#include "system/xstring.h"
#include "system/xstdio.h"
#include "misc/dbfsutil.h"

/*################################################################
#
# dbfs_crypt
#
#################################################################*/

void dbfs_crypt( BYTE *data, DWORD length)
{
	BYTE i = 0xff;
	for( ; length; length--)
	 	*(data++) ^= i--;
}


/*################################################################
#
# dbfs_copydata
#
#################################################################*/

static int dbfs_copydata( FILE *out, FILE *in, DWORD length)
{
	BYTE *buff = (BYTE *)xmalloc( 16384);
	if( buff == NULL)
		return( FALSE);

	while( length > 16384)
	{
		if( fread( buff, sizeof( BYTE), 16384, in) != 16384)
		{
			xfree( buff);
			return( FALSE);
		}
		if( fwrite( buff, sizeof( BYTE), 16384, out) != 16384)
		{
			xfree( buff);
			return( FALSE);
		}
		length -= 16384;
	}
	if( length > 0)
	{
		if( fread( buff, sizeof( BYTE), length, in) != length)
		{
			xfree( buff);
			return( FALSE);
		}
		if( fwrite( buff, sizeof( BYTE), length, out) != length)
		{
			xfree( buff);
			return( FALSE);
		}
	}
	xfree( buff);
  return( TRUE);
}

/*################################################################
#
# dbfs_makewrite
#
#################################################################*/

int dbfs_makewrite( DBFS *dbfs)
{
	strcpy( dbfs->writename, tmpnam( NULL));
	if( (dbfs->writefile = fopen( dbfs->writename, "wb")) == NULL)
		return( FALSE);

	if( dbfs->startdata != 0)
	{
		fseek( dbfs->readfile, 0, SEEK_SET);
		dbfs_copydata( dbfs->writefile, dbfs->readfile, dbfs->startdata);
	}
	dbfs->flags |= DBFSFLAG_MODIFIED;
	return( TRUE);
}

/*################################################################
#
# dbfs_rewrite
#
#################################################################*/

int dbfs_rewrite( DBFS *dbfs)
{
	DBFILE *dbfile;
	FILE *newfile;
	char newname[256];

	strcpy( newname, tmpnam( NULL));
	if( (newfile = fopen( newname, "wb")) == NULL)
		return( FALSE);

	if( dbfs->startdata != 0)
	{
		fseek( dbfs->writefile, 0, SEEK_SET);
		dbfs_copydata( newfile, dbfs->writefile, dbfs->startdata);
	}

	dbfile = dbfs->filelist;
	while( dbfile != NULL)
	{
		if( dbfile->flags & DBFILEFLAG_MODIFIED)
		{
			if( !(dbfile->flags & DBFILEFLAG_NULLFILE))
			{
				fseek( dbfs->writefile, dbfile->filepos, SEEK_SET);
				dbfile->filepos = ftell( newfile);
				if( !dbfs_copydata( newfile, dbfs->writefile, dbfile->flength))
					return( FALSE);
			}
		}
	}
	fclose( dbfs->writefile);
	remove( dbfs->writename);
	strcpy( dbfs->writename, newname);
	dbfs->writefile = newfile;
  return( TRUE);
}

/*################################################################
#
# dbfs_update
#
#################################################################*/

int dbfs_update( DBFS *dbfs)
{
	DBFILE *dbfile;

	fseek( dbfs->writefile, 0, SEEK_END);

	// Copy all none-modified data to new file

	dbfile = dbfs->filelist;
	while( dbfile != NULL)
	{
		if( !(dbfile->flags & DBFILEFLAG_MODIFIED))
		{
			if( !(dbfile->flags & DBFILEFLAG_NULLFILE))
			{
				fseek( dbfs->readfile, dbfile->filepos, SEEK_SET);
				dbfile->filepos = ftell( dbfs->writefile);
				if( !dbfs_copydata( dbfs->writefile, dbfs->readfile, dbfile->flength))
					return( FALSE);
			}
		}
		dbfile = dbfile->next;
	}

	// Write table of contents

	dbfs->starttoc = ftell( dbfs->writefile);
	dbfile = dbfs->filelist;
	while( dbfile != NULL)
	{
		BYTE slen = strlen( dbfile->name);

		dbfile->flags &= ~DBFILEFLAG_MODIFIED;

    fw_byte( dbfs->writefile, slen);
		dbfs_crypt( (BYTE *)dbfile->name, (DWORD)slen);
		fwrite( &dbfile->name, sizeof( BYTE), slen , dbfs->writefile);
		dbfs_crypt( (BYTE *)dbfile->name, (DWORD)slen);
    fwle_dword( dbfs->writefile, dbfile->flags);
    fwle_dword( dbfs->writefile, dbfile->flength);
    fwle_dword( dbfs->writefile, dbfile->plength);
    fwle_dword( dbfs->writefile, dbfile->length);
    fwle_dword( dbfs->writefile, dbfile->crc);
    fwle_dword( dbfs->writefile, dbfile->filepos - dbfs->startdata);
		dbfile = dbfile->next;
	}

  fwle_dword( dbfs->writefile, dbfs->starttoc - (ftell( dbfs->writefile) + ( DBFS_IDSIZE + 2*sizeof( WORD) + sizeof( DWORD))));
  fwle_word( dbfs->writefile, dbfs->files);
  fwle_word( dbfs->writefile, DBFS_VERSION);
	fwrite( DBFS_ID, sizeof( BYTE), DBFS_IDSIZE, dbfs->writefile);


	fclose( dbfs->readfile);
	remove( dbfs->readname);

	fclose( dbfs->writefile);
	if( rename( dbfs->writename, dbfs->readname) != 0)
	{
		DWORD flen;
		dbfs->readfile = fopen( dbfs->readname, "wb");
		dbfs->writefile = fopen( dbfs->writename, "rb");
		fseek( dbfs->writefile, 0, SEEK_END);
		flen = ftell( dbfs->writefile);
		fseek( dbfs->writefile, 0, SEEK_SET);
		dbfs_copydata( dbfs->readfile, dbfs->writefile, flen);
		fclose( dbfs->readfile);
		fclose( dbfs->writefile);
	}
	remove( dbfs->writename);
	dbfs->readfile = fopen( dbfs->readname, "r+b");

	dbfs->flags &= ~DBFILEFLAG_MODIFIED;
	return( TRUE);
}
	
