#include "dblreader.h"

INT* pDBLChunkHeaders = NULL;
INT iCurrentChunk = 0;
INT iFirstChunkOffset = 0;
BOOL bDumpData = FALSE;

dbl_chunk_header_s* ReadChunkHeader( FILE* pFile, INT iOffset, dbl_header_s* pDBLHeader ) {
	INT iByte;
	dbl_chunk_header_s* pCurrentChunkHeader;

	if( (INT)pDBLHeader != DBL_CHUNK_NODBLHEADER ) {
		if( iCurrentChunk > pDBLHeader->iChunksNum ) return FALSE;
	}

	iByte = 0;
	pCurrentChunkHeader = (dbl_chunk_header_s*)malloc( sizeof(dbl_chunk_header_s) );

	fseek( pFile, iOffset, 0 );
	fread( &iByte, 2, 1, pFile );
	if( feof(pFile) ) return FALSE;
	pCurrentChunkHeader->iDataChunkType = iByte;

	fread( &iByte, 2, 1, pFile );
	pCurrentChunkHeader->iFlag = iByte;

	fread( &iByte, 4, 1, pFile );
	pCurrentChunkHeader->iDataChunkSize = iByte;
	iByte = 0;

	fread( &iByte, 2, 1, pFile );
	pCurrentChunkHeader->iDataChunkVer = iByte;
	iByte = 0;

	return pCurrentChunkHeader;
}

INT ReadDBLHeader( FILE* pFile, dbl_header_s* pHeader ) {
	INT iByte, iHeaderOffset;

	iByte = 0; iHeaderOffset = 0;
	fseek( pFile, iHeaderOffset, 0 );
	fseek( pFile, 0, 0 );
	memset( pHeader->dblId, 0, 64 );

	iByte = 0x30;
	while( iByte >= 0x30 && iByte <= 0x39 ) { // Read till it's not a number
		fread( &iByte, 1, 1, pFile );
		strcat( pHeader->dblId, (char*)&iByte );
	}
	if( pHeader->dblId[0] < 0x30 || pHeader->dblId[1] > 0x39 ) return 0;
	iHeaderOffset = atoi( pHeader->dblId ); // We're reading the offset to the DBL header first
	fseek( pFile, iHeaderOffset+8, 0 ); // G+G = GG
	memset( pHeader->dblId, 0, 64 );

	iByte = 1;
	while( iByte != 0x00 ) { // Reading the header until the string is terminated
		fread( &iByte, 1, 1, pFile );
		strcat( pHeader->dblId, (char*)&iByte );
	}
	iByte = 0;

	fseek( pFile, iHeaderOffset+24, 0 ); //32
	fread( &iByte, 2, 1, pFile );
	pHeader->iChunksNum = iByte; // Get the number of data chunks in this DBL from the static offset
	iByte = 0;
	
	return iHeaderOffset+32; // Return the offset to the first ever data-chunk header
}

BOOL UnmergeDBL( const char* dblFname, const char* output ) {
	FILE* pDBLFile;
	FILE* pChunkFile;
	dbl_chunk_header_s* pCurrentChunkHeader;
	INT iNextOffset, iPostDataOffset, i, y;
	BYTE iByte;
	char szFilename[256];

	pDBLFile = 0;
	pChunkFile = 0;
	pCurrentChunkHeader = 0;
	iNextOffset = 0; iPostDataOffset = 0; iByte = 0;

	pDBLFile = fopen( dblFname, "rb" );
	if( !pDBLFile ) return FALSE;

	for( i = 0; i < iCurrentChunk; i++ ) {
		sprintf( szFilename, (i+1 < 10) ? "%s\\DATA0%i.DBL" : "%s\\DATA%i.DBL", output, i+1 );
		pChunkFile = fopen( szFilename, "wb" );
		if( bDumpData ) printf( "Attempting to unmerge ch # %i\n", i );
		if( !pChunkFile ) {
			if( bDumpData ) printf( "* Couldn't open file %s for writing\n\n", szFilename );
			return FALSE;
		}

		pCurrentChunkHeader = (dbl_chunk_header_s*)(pDBLChunkHeaders[i]);
		if( !pCurrentChunkHeader ) {
			if( bDumpData ) printf( "* Unable to access ch header # %i, probably hasn't been parsed?\n\n", i );
			break;
		}

		iByte = 0x30;
		fwrite( &iByte, 1, 1, pChunkFile );
		iByte = 0;
		for( y = 0; y < 7; y++ ) fwrite( &iByte, 1, 1, pChunkFile );
		fputs( "DB", pChunkFile );
		for( y = 0; y < 14; y++ ) fwrite( &iByte, 1, 1, pChunkFile );
		iByte = 1; fwrite( &iByte, 1, 1, pChunkFile );
		iByte = 0; fwrite( &iByte, 1, 1, pChunkFile );
		for( y = 0; y < 6; y++ ) fwrite( &iByte, 1, 1, pChunkFile );

		switch( pCurrentChunkHeader->iFlag ) {
			case 0x0E:
				iPostDataOffset = FLAG_0E;
			break;
			case 0x06:
				iPostDataOffset = FLAG_06;
			break;
			case 0x00:
				iPostDataOffset = FLAG_00;
			break;
			case 0x0B:
				iPostDataOffset = FLAG_0B;
			break;
			default:
				printf( "Unknown chunk-header flag 0x%02x!\n\tChunk #%i @ 0x%02x bytes\n\n", pCurrentChunkHeader->iFlag, iCurrentChunk+1, iNextOffset );
				return FALSE;
			break;
		}

		if( i == 0 ) iNextOffset = iFirstChunkOffset;
		fseek( pDBLFile, iNextOffset, 0 );
		for( y = 0; y < pCurrentChunkHeader->iDataChunkSize+iPostDataOffset; y++ ) {
			fread( &iByte, 1, 1, pDBLFile );
			fwrite( &iByte, 1, 1, pChunkFile );
		}
		iNextOffset = pCurrentChunkHeader->iDataFinalOffset;
		fclose( pChunkFile );
		if( bDumpData ) printf( "Finished writing ch-DBL # %i, %i bytes written\n\n\n\n", i, y );
	}

	fclose( pDBLFile );

	return TRUE;
}

BOOL CheckDBLId( dbl_header_s* header ) {
	if( strcmp(header->dblId, "DB") &&
		strcmp(header->dblId, "WIN32") &&
		strcmp(header->dblId, "PS2") &&
		strcmp(header->dblId, "DX") &&
		strcmp(header->dblId, "XBOX") ) return FALSE;
	return TRUE;
}

BOOL ReadDBLFile( const char* file ) {
	dbl_chunk_header_s* pCurrentChunkHeader;
	FILE* pDBLFile;
	INT iByte, iDataOffset, iPostDataOffset, i;
	dbl_header_s* pDBLHeader;

	iCurrentChunk = 0;
	pCurrentChunkHeader = NULL;
	pDBLFile = NULL;
	iByte = 0; iDataOffset = 0; iPostDataOffset = 0; iFirstChunkOffset = 0;
	pDBLHeader = (dbl_header_s*)malloc( sizeof(dbl_header_s) );
	iCurrentChunk = 0;

	if( bDumpData ) printf( "Attempting to read DBL file: %s\n", file );

	pDBLFile = fopen( file, "rb" );
	if( !pDBLFile ) {
		if( bDumpData ) printf( "* Couldn't open DBL file %s for reading\n", file );
		return FALSE;
	}
	pDBLHeader->dblId = (char*)malloc( 64 );

	iFirstChunkOffset = ReadDBLHeader( pDBLFile, pDBLHeader );
	if( !iFirstChunkOffset ) {
		printf( "Couldn't locate the DBL header offset!\n\n" );
		return FALSE;
	}
	if( !CheckDBLId(pDBLHeader) ) {
		printf( "* Wrong/unrecognized DBL ID: %s\n\n", pDBLHeader->dblId );
		return FALSE;
	}

	if( bDumpData ) printf( "Located first ch header at 0x%02x\n", iFirstChunkOffset );
	pDBLChunkHeaders = (INT*)malloc( pDBLHeader->iChunksNum*4 );

	iDataOffset = iFirstChunkOffset;
	while( iDataOffset ) { // Start processing data chunks
		pCurrentChunkHeader = ReadChunkHeader( pDBLFile, iDataOffset, pDBLHeader );
		if( !pCurrentChunkHeader ) break;
		switch( pCurrentChunkHeader->iFlag ) {
			case 0x0E:
				iPostDataOffset = FLAG_0E;
			break;
			case 0x06:
				iPostDataOffset = FLAG_06;
			break;
			case 0x00:
				iPostDataOffset = FLAG_00;
			break;
			case 0x0B:
				iPostDataOffset = FLAG_0B;
			break;
			default:
				printf( "Unknown chunk-header flag 0x%02x!\n\tChunk #%i @ 0x%02x bytes\n\n", pCurrentChunkHeader->iFlag, iCurrentChunk+1, iDataOffset );
				return FALSE;
			break;
		}

		iDataOffset = iFirstChunkOffset + pCurrentChunkHeader->iDataChunkSize+iPostDataOffset;

		if( iCurrentChunk > 0 ) {
			for( i = iCurrentChunk-1; i >= 0; i-- ) {
				if( !pDBLChunkHeaders[i] ) break;
				switch( ((dbl_chunk_header_s*)(pDBLChunkHeaders[i]))->iFlag ) {
					case 0x0E:
						iPostDataOffset = FLAG_0E;
					break;
					case 0x06:
						iPostDataOffset = FLAG_06;
					break;
					case 0x00:
						iPostDataOffset = FLAG_00;
					break;
					case 0x0B:
						iPostDataOffset = FLAG_0B;
					break;
				}
				iDataOffset += ((dbl_chunk_header_s*)(pDBLChunkHeaders[i]))->iDataChunkSize+iPostDataOffset; // Remove -22
			}
		}
		pCurrentChunkHeader->iDataFinalOffset = iDataOffset;
		pDBLChunkHeaders[iCurrentChunk] = (INT)pCurrentChunkHeader;

		iCurrentChunk++;
		if( iCurrentChunk > pDBLHeader->iChunksNum ) break;
	}

	fclose( pDBLFile );

	return TRUE;
}