// ********************************************************************************
// *** 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 */