// ******************************************************************************** // *** C/C++ example (Visual C++) to read out .timeline files *** // *** Lists all entries in a .timeline file *** // *** *** // *** Last modified: 2012APR28 *** // *** (c) Christian Schnettelker 2011, 2012 *** // *** *** // *** Command line usage: Tango <timeline-file> *** // *** *** // *** www.finefiles.com mail@finefiles.com *** // ******************************************************************************** #include <stdio.h> #include <string.h> #include "ffBasic.h" #include "tlfBasic.h" #pragma warning(disable: 4996) // Visual C++: donīt warn because of fopen security ////////////////////////////////////////////////////////////////////////// // // Sub-function: check the main header // ////////////////////////////////////////////////////// bool ____checkMainHeader( FILE *fp ) { TLFMAINHEADER xTLFmainHeader = { 0 }; // Read the header if ( fseek(fp,0,SEEK_SET) != 0 ) return false; if ( fread(&xTLFmainHeader,sizeof(TLFMAINHEADER),1,fp) != 1 ) return false; // Check the data if ( xTLFmainHeader.cStart != 0x89 ) return false; if ( strncmp((char *)xTLFmainHeader.cSignature,__TLF__SIGNATURE,33) != 0 ) return false; if ( xTLFmainHeader.cCRLF[ 0 ] != 0x0D ) return false; if ( xTLFmainHeader.cCRLF[ 1 ] != 0x0A ) return false; if ( xTLFmainHeader.cEOF != 0x1A ) return false; if ( xTLFmainHeader.cLF != 0x0A ) return false; return true; // Main header is valid } ////////////////////////////////////////////////////////////////////////// // // Sub-function: check and read the index to pIndex // ////////////////////////////////////////////////////// bool ____readIndex( FILE *fp, TLFINDEX *pIndex ) { if ( fseek(fp,sizeof(TLFMAINHEADER),SEEK_SET) != 0 ) return false; if ( fread(pIndex,sizeof(TLFINDEX),1,fp) != 1 ) return false; if ( strncmp((char *)pIndex->xChunkTag.cSignature,"|III",4) != 0 ) return false; return true; // Index is valid } ////////////////////////////////////////////////////////////////////////// // // Sub-function: read a chunk of any type at fpChunkPos to pChunk // ////////////////////////////////////////////////////// bool ____readChunk( FILE *fp, TLFYMDCHUNK *pChunk, const fpos_t fpChunkPos ) { if ( fsetpos(fp,&fpChunkPos) != 0 ) return false; if ( fread(pChunk,sizeof(TLFYMDCHUNK),1,fp) != 1 ) return false; if ( pChunk->xChunkTag.cSignature[ 0 ] != '|' ) return false; return true; // Chunk is valid } ////////////////////////////////////////////////////////////////////////// // // Sub-function: read a year index at fpYearIndexPos to pYearIndex // ////////////////////////////////////////////////////// bool ____readYearIndex( FILE *fp, TLFYEARINDEX *pYearIndex, const fpos_t fpYearIndexPos ) { if ( fsetpos(fp,&fpYearIndexPos) != 0 ) return false; if ( fread(pYearIndex,sizeof(TLFYEARINDEX),1,fp) != 1 ) return false; if ( strncmp((char *)pYearIndex->xChunkTag.cSignature,"|IYI",4) != 0 ) return false; return true; // Year index is valid } ////////////////////////////////////////////////////////////////////////// // // Sub-function: read a month index at fpMonthIndexPos to pMonthIndex // ////////////////////////////////////////////////////// bool ____readMonthIndex( FILE *fp, TLFMONTHINDEX *pMonthIndex, const fpos_t fpMonthIndexPos ) { if ( fsetpos(fp,&fpMonthIndexPos) != 0 ) return false; if ( fread(pMonthIndex,sizeof(TLFMONTHINDEX),1,fp) != 1 ) return false; if ( strncmp((char *)pMonthIndex->xChunkTag.cSignature,"|IMI",4) != 0 ) return false; return true; // Month index is valid } ////////////////////////////////////////////////////////////////////////// // // Sub-function: read an entry chunk at fpEntryChunkPos to pEntryChunk // Additional return: sRoot and sFileName // ////////////////////////////////////////////////////// bool ____readEntryChunk( FILE *fp, TLFENTRYCHUNK *pEntryChunk, const fpos_t fpEntryChunkPos, wchar_t *swRoot, wchar_t *swFileName ) { // Read and check the chunk if ( fsetpos(fp,&fpEntryChunkPos) != 0 ) return false; if ( fread(pEntryChunk,sizeof(TLFENTRYCHUNK),1,fp) != 1 ) return false; if ( strncmp((char *)pEntryChunk->xChunkTag.cSignature,"|CEC",4) != 0 ) return false; // Security check if ( pEntryChunk->wRootLen >= MAX_PATH || pEntryChunk->wRootLen < 1 ) { puts( "Root length out of range in ____readEntryChunk()" ); return false; } if ( pEntryChunk->wFileNameLen >= MAX_PATH || pEntryChunk->wFileNameLen < 1 ) { puts( "FileName length out of range in ____readEntryChunk()" ); return false; } // Get swRoot and swFileName fpos_t fpFilePos = fpEntryChunkPos + sizeof( TLFENTRYCHUNK ); // Calculate the position if ( fsetpos(fp,&fpFilePos) != 0 ) return false; // Set the file pointer if ( fread(swRoot,2,pEntryChunk->wRootLen,fp) != pEntryChunk->wRootLen ) return false; // Read swRoot[ pEntryChunk->wRootLen ] = EOS; // Set EOS (End of string) fpFilePos = fpEntryChunkPos + sizeof( TLFENTRYCHUNK ) + ( 2 * pEntryChunk->wRootLen ); // Calculate the position if ( fsetpos(fp,&fpFilePos) != 0 ) return false; // Set the file pointer if ( fread(swFileName,2,pEntryChunk->wFileNameLen,fp) != pEntryChunk->wFileNameLen ) return false; // Read swFileName[ pEntryChunk->wFileNameLen ] = EOS; // Set EOS (End of string) return true; // Entry chunk is valid } ////////////////////////////////////////////////////////////////////////// // // main // ////////////////////////////////////////////////////// #define __QUIT fclose( fp ); return 0 int wmain( int argc, wchar_t *argv[] ) { // Open the source file FILE *fp = _wfopen( argv[ 1 ],L"rb" ); if ( fp == NULL ) { puts( "timeline file not found" ); return 0; } // First check the main header if ( !____checkMainHeader(fp) ) { puts( "This is not a valid finefiles timeline file" ); __QUIT; } // Check and load the (main)index TLFINDEX xTLFindex = { 0 }; if ( !____readIndex(fp,&xTLFindex) ) { puts( "Cannot read index" ); __QUIT; } // Start the readout loop now TLFYMDCHUNK xYearChunk = { 0 }; TLFYEARINDEX xYearIndex = { 0 }; TLFYMDCHUNK xMonthChunk = { 0 }; TLFMONTHINDEX xMonthIndex = { 0 }; TLFYMDCHUNK xDayChunk = { 0 }; TLFENTRYCHUNK xEntryChunk = { 0 }; wchar_T swEntryRoot[ MAX_PATH + 1 ] = L"", swEntryFileName[ MAX_PATH + 1 ] = L""; fpos_t fpFilePos = xTLFindex.i64Lower; // Start from the i64Lower pointer of the (main)index while( 1 ) { if ( fpFilePos == NULL ) break; // Reaching the end of the year chain? // Read next year chunk + year index if ( !____readChunk(fp,&xYearChunk,fpFilePos) ) { puts( "Error reading year chunk" ); __QUIT; } if ( !____readYearIndex(fp,&xYearIndex,xYearChunk.i64Lower) ) { puts( "Error reading year index" ); __QUIT; } // Read the month chunks for the current year (0 = unknow) for( unsigned int uMonth=0; uMonth<=12; uMonth++ ) if ( xYearIndex.i64Month[ uMonth ] ) { // Read next month chunk + month index if ( !____readChunk(fp,&xMonthChunk,xYearIndex.i64Month[ uMonth ]) ) { puts( "Error reading month chunk" ); __QUIT; } if ( !____readMonthIndex(fp,&xMonthIndex,xMonthChunk.i64Lower) ) { puts( "Error reading month index" ); __QUIT; } // Read the day chunks for the current month (0 = unknow) for( unsigned int uDays=0; uDays<=31; uDays++ ) if ( xMonthIndex.i64Days[ uDays ] ) { // read the next day chunk if ( !____readChunk(fp,&xDayChunk,xMonthIndex.i64Days[ uDays ]) ) { puts( "Error reading day chunk" ); __QUIT; } // Read entries in a loop // Display the content for demonstration using printf() fpFilePos = xDayChunk.i64Lower; // Set the file pointer to the start of the entry chain while( 1 ) { if ( fpFilePos == NULL ) break; // Reaching the end of the entry chain? if ( !____readEntryChunk(fp,&xEntryChunk,fpFilePos,swEntryRoot,swEntryFileName) ) { puts( "Error reading entry chunk" ); __QUIT; } printf( "%08I64d -> %04d/%02d/%02d -> %.50ls\r\n",fpFilePos,xEntryChunk.xDateTag.wYear, xEntryChunk.xDateTag.wMonth, xEntryChunk.xDateTag.wDay, swEntryFileName ); fpFilePos = xEntryChunk.i64Next; // Jump to the next entry (or NULL = finished) } } } fpFilePos = xYearChunk.i64Next; // Jump to the next year chunk (or NULL = finished) } // Job finished fclose( fp ); return 0; } /* eof */