Rev 1 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "CatFile.h"
#include <time.h>
#include "File.h"
#include "DirIO.h"
CCatFile::CCatFile ()
{
m_iDataType = CATFILE_NONE;
m_sData = NULL;
m_lSize = 0;
m_bCreate = false;
}
CCatFile::~CCatFile ()
{
for ( SInCatFile *c = m_lFiles.First(); c; c = m_lFiles.Next() )
{
if ( c->sData )
delete c->sData;
delete c;
}
m_lFiles.clear();
}
bool CCatFile::IsAddonDir(CyString dir)
{
CyString d = dir;
d = d.FindReplace("\\", "/");
d = d.GetToken("/", 1, 1);
if ( d.Compare("types") )
return true;
if ( d.Compare("t") )
return true;
if ( d.Compare("maps") )
return true;
if ( d.Compare("director") )
return true;
if ( d.Compare("cutscenes") )
return true;
return false;
}
bool CCatFile::Opened(int error, bool allowCreate)
{
if ( error == CATERR_NONE ) return true;
if ( allowCreate && error == CATERR_CREATED ) return true;
return false;
}
int CCatFile::Open ( CyString catfile, CyString addon, int readtype, bool create )
{
m_bCreate = false;
CyString datfile;
if ( !catfile.Right(4).Compare(".cat") )
{
datfile = catfile + ".dat";
catfile += ".cat";
}
else
datfile = catfile.Left ( -4 ) + ".dat";
if ( readtype == CATREAD_JUSTCONTENTS )
create = false;
// first check if the dat file exists and opens
bool created = false;
if ( readtype != CATREAD_JUSTCONTENTS )
{
FILE *id = fopen ( datfile.c_str(), "rb+" );
if ( !id )
{
if ( create )
created = true;
else
return CATERR_NODATFILE;
}
if ( !created && id )
fclose ( id );
}
// now open the cat file to read
FILE *cid = fopen ( catfile.c_str(), "rb" );
if ( !cid )
{
if ( create )
created = true;
else
return CATERR_NOCATFILE;
}
if ( created )
{
m_fCatFile.Open ( catfile );
m_fDatFile.Open ( datfile );
m_bCreate = true;
return CATERR_CREATED;
}
// find the file size
fseek ( cid, 0, SEEK_END );
size_t lFileSize = ftell ( cid );
fseek ( cid, 0, SEEK_SET );
if ( !lFileSize )
{
fclose ( cid );
return CATERR_FILEEMPTY;
}
// size must be multiples of 5
size_t size = lFileSize + ((lFileSize % 5) ? 5 - (lFileSize % 5) : 0);
// read cat to buffer
m_sData = new unsigned char[size + 1];
m_sData[lFileSize] = 0;
m_lSize = size;
fread ( m_sData, sizeof(unsigned char), m_lSize, cid );
if ( ferror(cid) )
{
RemoveData ();
fclose ( cid );
return CATERR_READCAT;
}
m_iDataType = CATFILE_READ;
fclose ( cid );
if ( readtype != CATREAD_CAT )
{
if ( !DecryptData () )
return CATERR_DECRYPT;
m_sData[lFileSize] = 0;
ReadFiles ();
}
m_sAddonDir = addon;
m_fCatFile.Open ( catfile );
if ( readtype != CATREAD_JUSTCONTENTS )
{
m_fDatFile.Open ( datfile );
// check the file size matches
long compare = 0;
FILE *id = fopen ( datfile.c_str(), "rb" );
if ( id )
{
fseek ( id, 0, SEEK_END );
compare = ftell ( id );
fclose ( id );
}
/*
SInCatFile *c = m_lFiles.Back ()->Data();
if ( (c->lSize + c->lOffset) != compare )
return CATERR_MISMATCH;
*/
}
if ( readtype >= CATREAD_DAT )
LoadDatFile ();
return CATERR_NONE;
}
bool CCatFile::RemoveFile ( CyString file )
{
if ( !m_sAddonDir.Empty() ) {
SInCatFile *f = FindData ( m_sAddonDir + "\\" + file );
if ( f )
RemoveFile(f);
}
{
SInCatFile *f = FindData ( CyString("addon\\") + file );
if ( f )
RemoveFile(f);
}
SInCatFile *f = FindData ( file );
if ( !f )
{
m_iError = CATERR_NOFILE;
return false;
}
return RemoveFile ( f );
}
void CCatFile::LoadDatFile ()
{
if ( m_fDatFile.NoFile() )
return;
FILE *id = fopen ( m_fDatFile.GetFullFilename().c_str(), "rb" );
if ( id )
{
for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SInCatFile *c = node->Data();
if ( c->sData )
delete c->sData;
c->sData = new unsigned char[c->lSize + 1];
size_t readAmount = fread ( c->sData, sizeof(unsigned char), c->lSize, id );
if ( readAmount != c->lSize )
break;
DecryptDAT(c->sData, c->lSize);
}
fclose ( id );
}
}
bool CCatFile::ReadFiles ()
{
int num = 0;
size_t offset = 0;
unsigned char *data = m_sData;
int spacePos = -1;
while ( m_sData[num] != '\0' )
{
if ( m_sData[num] == '\n' )
{
m_sData[num] = 0;
if ( spacePos != -1 )
{
CyString file;
int size = 0;
m_sData[spacePos] = '\0';
file = (char *)data;
data = (m_sData + (spacePos + 1));
if ( !CyString((char *)data).IsNumber() )
size = 0;
else
size = atoi((const char *)data);
if ( size )
{
SInCatFile *catfile = new SInCatFile;
catfile->lSize = size;
catfile->sFile = file;
catfile->lOffset = offset;
catfile->sData = 0;
offset += catfile->lSize;
m_lFiles.push_back ( catfile );
}
}
data = (m_sData + (num + 1));
spacePos = -1;
}
else if ( m_sData[num] == ' ' )
spacePos = num;
++num;
}
/*
CyString s((const char*)m_sData);
CyString *lines = s.SplitToken ( '\n', &num );
for ( int i = 1; i < num; i++ )
{
CyString l = lines[i];
if ( l.NumToken ( ' ' ) <= 1 )
continue;
SInCatFile *catfile = new SInCatFile;
catfile->lSize = l.GetToken ( l.NumToken ( ' ' ), ' ' ).ToLong();
catfile->sFile = l.GetToken ( 0, l.NumToken ( ' ' ) - 1, ' ' );
catfile->lOffset = offset;
catfile->sData = 0;
offset += catfile->lSize;
m_lFiles.push_back ( catfile );
}
*/
return true;
}
bool CCatFile::DecryptData ( unsigned char *data, size_t size )
{
unsigned char cl=0xDB, dh=0xDC, dl=0xDD, ah=0xDE, ch=0xDF, al;
unsigned char *ptr = data, *end = data + size;
for ( ptr = data; ptr < end; ptr += 5)
{
al=ch;
ch+=5;
*(ptr + 4) ^= al;
al=ah;
ah+=5;
*(ptr + 3) ^= al;
al=dl;
dl+=5;
*(ptr + 2) ^= al;
al=dh;
dh+=5;
*(ptr + 1) ^= al;
al=cl;
cl+=5;
*(ptr + 0) ^= al;
}
return true;
}
bool CCatFile::DecryptData ()
{
if ( !DecryptData ( m_sData, m_lSize ) )
return false;
m_iDataType = CATERR_DECRYPT;
return true;
}
void CCatFile::RemoveData ()
{
if ( m_sData )
delete m_sData;
m_sData = NULL;
m_iDataType = CATFILE_NONE;
m_lSize = 0;
}
bool CCatFile::ReadFileToData ( CyString filename )
{
SInCatFile *c = FindData ( filename );
return ReadFileToData ( c );
}
bool CCatFile::ReadFileToData ( SInCatFile *c )
{
if ( !c )
return false;
size_t size = 0;
c->sData = ReadData ( c, &size );
if ( c->sData )
return true;
return false;
}
unsigned char *CCatFile::ReadData ( SInCatFile *c, size_t *size )
{
*size = c->lSize;
FILE *id = fopen ( m_fDatFile.GetFullFilename().c_str(), "rb" );
if ( id )
{
fseek ( id, (long)c->lOffset, SEEK_SET );
unsigned char *data = new unsigned char[c->lSize + 1];
fread ( data, sizeof(unsigned char), c->lSize, id );
*size = c->lSize;
data[c->lSize] = '\0';
DecryptDAT(data, *size);
fclose ( id );
return data;
}
return NULL;
}
void CCatFile::DecryptDAT(unsigned char *buffer, size_t size)
{
for(unsigned char *pos=buffer, *end=buffer + size; pos < end; pos++){
*pos^=0x33;
}
}
unsigned char *CCatFile::ReadData ( CyString filename, size_t *size )
{
*size = 0;
if ( !m_fDatFile.NoFile() )
{
SInCatFile *c = FindData ( filename );
if ( c )
return ReadData ( c, size );
}
return NULL;
}
unsigned char *CCatFile::UnpackFile ( SInCatFile *c, size_t *size, bool forcepck )
{
*size = 0;
if ( !c )
return NULL;
if ( IsDataPCK ( (const unsigned char *)c->sData, c->lSize ) || forcepck )
return UnPCKData ( c->sData, c->lSize, size );
*size = c->lSize;
return c->sData;
}
bool CCatFile::RemoveFile ( SInCatFile *f )
{
if ( (m_bCreate) || (m_lFiles.empty()) )
return false;
if ( !f )
return false;
int err = m_fDatFile.TruncateFile ( f->lOffset, f->lSize );
if ( err == FILEERR_TOSMALL )
{
if ( (int)m_fDatFile.GetFilesize() > this->GetEndOffset() )
m_fDatFile.TruncateFile( this->GetEndOffset(), m_fDatFile.GetFilesize() - this->GetEndOffset() );
}
else if ( err != FILEERR_NONE )
return false;
// adjust the file positions
bool bAdjust = false;
int iOffset;
for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() ) {
int iNextOffset = node->Data()->lOffset;
if ( bAdjust ) {
node->Data()->lOffset = iOffset;
}
else if ( node->Data() == f ) {
bAdjust = true;
}
else if ( !bAdjust ) continue;
iOffset = iNextOffset;
}
// now just write the new cat file
m_lFiles.remove ( f );
WriteCatFile ();
return true;
}
bool CCatFile::WriteCatFile ()
{
if ( (m_bCreate) && (m_lFiles.empty()) )
return false;
CyString cat = m_fCatFile.GetFilename() + "\n";
for ( SInCatFile *f = m_lFiles.First(); f; f = m_lFiles.Next() )
{
if ( f->sFile.Empty() )
continue;
cat += f->sFile.findreplace("/", "\\") + " " + (long)f->lSize + "\n";
}
if ( !cat.Length() )
return false;
size_t len = cat.Length();
// if ( len % 5)
// len += (5 - (len % 5));
unsigned char *data = new unsigned char[len + 1];
memcpy ( data, cat.c_str(), cat.Length() );
for ( size_t i = len; i > cat.Length(); i-- )
data[i] = '\0';
DecryptData ( data, len );
m_fCatFile.WriteData ( (const char *)data, len );
return true;
}
bool CCatFile::CheckExtensionPck ( CyString filename )
{
CyString ext = filename.GetToken ( ".", filename.NumToken ( "." ) ).lower();
if ( ext == "xml" )
return true;
else if ( ext == "txt" )
return true;
else if ( ext == "bob" )
return true;
else if ( ext == "bod" )
return true;
return false;
}
bool CCatFile::CheckPackedExtension ( CyString filename )
{
CyString ext = filename.GetToken ( ".", filename.NumToken ( "." ) ).lower();
if ( ext == "pck" )
return true;
else if ( ext == "pbb" )
return true;
else if ( ext == "pbd" )
return true;
return false;
}
CyString CCatFile::PckChangeExtension ( CyString f )
{
CFileIO fo ( f );
CyString ext = fo.GetFileExtension ().lower();
if ( ext == "txt" )
return fo.ChangeFileExtension ( "pck" );
else if ( ext == "xml" )
return fo.ChangeFileExtension ( "pck" );
else if ( ext == "bob" )
return fo.ChangeFileExtension ( "pbb" );
else if ( ext == "bod" )
return fo.ChangeFileExtension ( "pbd" );
return f;
}
bool CCatFile::AppendFile ( CyString filename, CyString to, bool pck, bool bXor )
{
if ( (!m_bCreate) && (!m_fCatFile.Exists ()) )
return false;
if ( !m_sAddonDir.Empty() && CCatFile::IsAddonDir(to) ) {
to = m_sAddonDir + "\\" + to;
}
if ( filename.IsIn ( "::" ) )
return WriteFromCat ( filename.GetToken ( "::", 1, 1 ), filename.GetToken ( "::", 2, 2 ) );
// first open the file to check it exists
FILE *id = fopen ( filename.c_str(), "rb" );
if ( !id )
return false;
fclose ( id );
// then check if the file already exists
if ( !m_lFiles.empty() )
{
SInCatFile *f = FindData ( to );
if ( f )
{
if ( !RemoveFile ( f ) )
return false;
}
}
bool append = false;
SInCatFile *f = new SInCatFile;
f->sData = 0;
f->lOffset = this->GetEndOffset();
bool dofile = true;
if ( pck && CheckExtensionPck(filename) )
{
to = PckChangeExtension ( to );
if ( !m_lFiles.empty() )
{
SInCatFile *checkf = FindData ( to );
if ( checkf )
{
if ( !RemoveFile ( checkf ) )
return false;
}
}
}
if ( !m_lFiles.size() )
m_fDatFile.WipeFile();
// if ( CheckPackedExtension(to) && !CheckPackedExtension(filename) )
{
FILE *id = fopen ( filename.c_str(), "rb" );
if ( !id )
return false;
fseek ( id, 0, SEEK_END );
size_t size = ftell ( id );
fseek ( id, 0, SEEK_SET );
unsigned char *data = new unsigned char[size + 1];
fread ( data, sizeof(unsigned char), size, id );
data[size] = '\0';
fclose ( id );
if ( CheckPackedExtension(to) && !IsDataPCK ( data, size ) )
{
size_t newsize = 0;
unsigned char *newdata = PCKData ( data, size, &newsize, bXor );
this->DecryptDAT(newdata, newsize);
f->lSize = newsize;
append = m_fDatFile.AppendDataToPos ( (const char *)newdata, newsize, f->lOffset );
delete [] newdata;
}
else
{
this->DecryptDAT(data, size);
f->lSize = size;
append = m_fDatFile.AppendDataToPos ( (const char *)data, size, f->lOffset );
}
delete [] data;
}
if ( append )
{
m_bCreate = false;
f->sFile = to;
m_lFiles.push_back ( f );
WriteCatFile ();
}
else
delete f;
return true;
}
bool CCatFile::AddData ( CyString catfile, unsigned char *data, size_t size, CyString to, bool pck, bool create )
{
int err = Open ( catfile, "", CATREAD_CATDECRYPT, create );
if ( (err != CATERR_NONE) && (err != CATERR_CREATED) )
return false;
return AppendData ( data, size, to, pck );
}
int CCatFile::GetEndOffset()
{
if ( m_lFiles.empty() )
return 0;
else
return m_lFiles.Back()->Data()->lOffset + m_lFiles.Back()->Data()->lSize;
}
bool CCatFile::AppendData ( unsigned char *data, size_t size, CyString to, bool pck, bool bXor )
{
if ( (!m_bCreate) && (!m_fCatFile.Exists ()) )
return false;
if ( (size <= 0) || (!data) )
return false;
// then check if the file already exists
if ( !m_lFiles.empty() )
{
SInCatFile *f = FindData ( to );
if ( f )
{
if ( !RemoveFile ( f ) )
return false;
}
}
bool append = false;
if ( !m_lFiles.size() )
m_fDatFile.WipeFile();
SInCatFile *f = new SInCatFile;
f->sData = 0;
if ( m_lFiles.empty() )
f->lOffset = 0;
else
f->lOffset = m_fDatFile.GetFilesize();
// if file extension is packed but not the file, then pack it, "pck" forces it to be packed unless it already is
f->lSize = size;
if ( (((pck) && (CheckExtensionPck (to))) || ((CheckPackedExtension ( to )))) && (!IsDataPCK ( data, size )) )
{
to = PckChangeExtension ( to );
if ( !m_lFiles.empty() )
{
SInCatFile *f = FindData ( to );
if ( f )
{
if ( !RemoveFile ( f ) )
return false;
}
}
size_t newsize = 0;
unsigned char *d = PCKData ( data, size, &newsize, bXor );
f->lSize = newsize;
this->DecryptDAT(d, newsize);
append = m_fDatFile.AppendDataToPos ( (const char *)d, newsize, f->lOffset );
delete [] d;
}
else {
this->DecryptDAT(data, size);
append = m_fDatFile.AppendDataToPos ( (const char *)data, size, f->lOffset );
}
if ( append )
{
m_bCreate = false;
f->sFile = to;
m_lFiles.push_back ( f );
WriteCatFile ();
}
else
delete f;
return true;
}
CyString CCatFile::RenameFileExtension(SInCatFile *f)
{
CFileIO fo ( f->sFile );
CyString ext = fo.GetFileExtension ().lower();
if ( ext == "pck" )
{
CyString firstDir = f->sFile.FindReplace("/", "\\").GetToken("\\", 1, 1).lower();
if ( firstDir == "t" )
return fo.ChangeFileExtension ( "xml" );
else if ( firstDir == "director" )
return fo.ChangeFileExtension ( "xml" );
return fo.ChangeFileExtension ( "txt" );
}
else if ( ext == "pbb" )
return fo.ChangeFileExtension ( "bob" );
else if ( ext == "pbd" )
return fo.ChangeFileExtension ( "bod" );
return f->sFile;
}
void CCatFile::FindFiles(CyStringList *files, CyString filemask)
{
if ( m_lFiles.empty() )
return;
for ( SInCatFile *f = m_lFiles.First(); f; f = m_lFiles.Next() )
{
if ( filemask.WildMatch(f->sFile) )
files->PushBack(f->sFile);
}
}
bool CCatFile::ExtractAll(CyString dir)
{
if ( m_lFiles.empty() )
return false;
for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() )
{
if ( !ExtractFile(node->Data(), dir, true) )
return false;
}
return true;
}
bool CCatFile::ExtractFile ( CyString filename, CyString to, bool preserve )
{
if ( m_lFiles.empty() )
return false;
// check if file exists
if ( !m_sAddonDir.Empty() ) {
SInCatFile *f = FindData ( m_sAddonDir + "\\" + filename );
if ( f )
return ExtractFile ( f, to, preserve );
}
{
SInCatFile *f = FindData ( CyString("addon\\") + filename );
if ( f )
return ExtractFile ( f, to, preserve );
}
SInCatFile *f = FindData ( filename );
if ( !f )
{
m_iError = CATERR_NOFILE;
return false;
}
return ExtractFile ( f, to, preserve );
}
bool CCatFile::ExtractFile ( SInCatFile *f, CyString to, bool preserve )
{
unsigned char *data = 0;
size_t size = 0;
// load the data from file
ReadFileToData ( f );
data = UnpackFile ( f, &size, CheckPackedExtension(f->sFile) );
if ( !data )
{
m_iError = CATERR_CANTREAD;
return false;
}
CFileIO fo(CCatFile::RenameFileExtension(f));
// check for a file name
CyString checkFile = to;
CyString todir, tofile = to;
if ( checkFile.IsAnyIn("\\/") )
{
checkFile = checkFile.FindReplace ( "\\", "/" );
checkFile = checkFile.FindReplace ( "//", "/" );
tofile = checkFile.GetToken("/", checkFile.NumToken('/'));
if ( !checkFile.IsIn(".") || preserve )
{
tofile = fo.GetFilename();
todir = checkFile;
}
else
{
todir = CFileIO(checkFile).GetDir();
tofile = CFileIO(checkFile).GetFilename();
}
}
if ( tofile.Empty() )
{
if ( !tofile.Empty() )
tofile += "/";
tofile += fo.GetFilename();
todir = CFileIO(tofile).GetDir();
tofile = CFileIO(tofile).GetFilename();
}
if ( preserve )
{
if ( !fo.GetDir().Compare(todir.Right(- (int)fo.GetDir().Length())) )
{
if ( !todir.Empty() && todir.Right(1) != "/" )
todir += "/";
todir += fo.GetDir();
}
}
if ( tofile.Empty() )
tofile = fo.GetFilename();
// create the directory to extract to
if ( !todir.Empty() && !CDirIO(todir).Create() )
m_iError = CATERR_CANTCREATEDIR;
else
{
CyString file = todir;
if ( !file.Empty() )
file += "/";
file += tofile;
file = file.FindReplace("/", "\\");
file = file.FindReplace("\\\\", "\\");
FILE *id = fopen ( file.c_str(), "wb" );
if ( !id )
m_iError = CATERR_INVALIDDEST;
else
{
fwrite ( data, sizeof(char), size, id );
fclose ( id );
ClearError ();
delete data;
f->sData = 0;
return true;
}
}
delete data;
f->sData = 0;
return false;
}
CyString CCatFile::GetErrorString ()
{
switch ( m_iError )
{
case CATERR_NONE:
return NullString;
case CATERR_NODATFILE:
return "Unable to open Dat file";
case CATERR_NOCATFILE:
return "Unable to open Cat file";
case CATERR_FILEEMPTY:
return "Cat file is empty";
case CATERR_READCAT:
return "Unable to read from cat file";
case CATERR_DECRYPT:
return "Unable to decrypt cat file";
case CATERR_MISMATCH:
return "File size mismatch with Dat file";
case CATERR_NOFILE:
return "Unable to find file in archive";
case CATERR_CANTREAD:
return "Unable to read file in archive";
case CATERR_CANTCREATEDIR:
return "Unable to create destiantion directory";
case CATERR_INVALIDDEST:
return "Unable to write to destiantion file";
}
return "Invalid";
}
unsigned char *CompressPCKData ( unsigned char *buffer, size_t size, size_t *retsize, time_t mtime )
{
size_t newsize = (size * 2) + 20;
unsigned char *data = (unsigned char *)malloc ( sizeof(unsigned char) * newsize );
z_stream zs;
char flags=0;
// error(0);
unsigned char *d = data;
// if(m_pszComment && strlen(m_pszComment) > 0) flags|=GZ_F_COMMENT;
// if(m_pszFileName && strlen(m_pszFileName) > 0) flags|=GZ_F_FILENAME;
int pos = PCKHEADERSIZE;
*d = 0x1F; d++;
*d = 0x8B; d++;
*d = 8; d++;
*d = flags; d++;
memcpy(d, &mtime, sizeof(mtime));
d += 4;
*d = 0; d++;
*d = 11; d++;
// if(flags & GZ_F_FILENAME) put((const char *)m_pszFileName);
// if(flags & GZ_F_COMMENT) put((const char *)m_pszComment);
memset(&zs, 0, sizeof(zs));
zs.next_in=buffer;
zs.avail_in=(long)size;
int ret;
unsigned long ubound;
ret=deflateInit2(&zs, 9, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
if(ret!=Z_OK)
return false;
ubound=deflateBound(&zs, (unsigned long)size);
if ( newsize < ubound)
{
newsize += ubound;
data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
}
zs.next_out=d;
zs.avail_out=(unsigned int)newsize - pos;
while((ret=deflate(&zs, Z_FINISH))==Z_OK)
{
newsize += 1024;
data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
zs.next_out=data + zs.total_out;
zs.avail_out=(unsigned int)newsize - zs.total_out;
}
pos += zs.total_out;
deflateEnd(&zs);
unsigned long crc=crc32(0, NULL, 0);
crc=crc32(crc, buffer, (unsigned int)size);
int s = sizeof(crc) + sizeof(size);
if ( newsize < (size_t)(s + pos) )
{
newsize += (s + pos) - newsize;
data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
}
memcpy(&data[pos], &crc, sizeof(crc));
pos += sizeof(crc);
memcpy(&data[pos], &size, sizeof(size));
pos += sizeof(size);
newsize = pos;
unsigned char *retdata = NULL;
if ( ret == Z_STREAM_END )
{
*retsize = newsize;
retdata = new unsigned char[newsize];
memcpy ( retdata, data, newsize );
}
free ( data );
return retdata;
}
unsigned char *PCKData ( unsigned char *data, size_t oldsize, size_t *newsize, bool bXor )
{
unsigned char *newdata = CompressPCKData ( data, oldsize, newsize, time(NULL) );
if ( !bXor )
return newdata;
if ( newdata )
{
char magic = (char)clock(), m;
m=magic ^ 0xC8;
unsigned char *ptr = newdata, *end = newdata + *newsize;
// XOR encryption
if ( bXor )
{
for ( ; ptr < end; ptr++ )
(*ptr)^=magic;
}
unsigned char *finalData = new unsigned char[*newsize + 1];
finalData[0] = m;
memcpy ( finalData + 1, newdata, *newsize );
delete [] newdata;
(*newsize)++;
return finalData;
}
return NULL;
}
bool CCatFile::WriteFromCat ( CCatFile *fcat, CyString file )
{
// now find the file in the cat file
SInCatFile *getfile = fcat->FindData ( file );
if ( !getfile )
return false;
// read the dat from the cat file
size_t size = 0;
unsigned char *data = fcat->ReadData ( getfile, &size );
if ( !data )
return false;
// now check if it exists in this file
RemoveFile ( file );
// now write to the new file
if ( !m_fDatFile.AppendData ( (const char *)data, size ) )
return false;
// finally add to the list
SInCatFile *f = new SInCatFile;
f->sData = 0;
if ( m_lFiles.empty() )
f->lOffset = 0;
else
f->lOffset = m_lFiles.Back()->Data()->lOffset + m_lFiles.Back()->Data()->lSize;
f->sFile = file;
f->lSize = size;
m_lFiles.push_back ( f );
WriteCatFile ();
return true;
}
bool CCatFile::WriteFromCat ( CyString catfile, CyString file )
{
CCatFile fcat;
if ( fcat.Open ( catfile, "", CATREAD_CATDECRYPT, false ) )
return false;
return this->WriteFromCat(&fcat, file);
}
SInCatFile *CCatFile::FindData ( CyString filename )
{
if ( m_lFiles.empty() )
return NULL;
CyString check = filename;
check = check.FindReplace ( "\\", "/" );
for ( SInCatFile *c = m_lFiles.First(); c; c = m_lFiles.Next() )
{
CyString f = c->sFile;
f = f.FindReplace ( "\\", "/" );
if ( f.Compare(check) )
return c;
}
return NULL;
}
bool CCatFile::MarkRemoveFile( CyString file )
{
SInCatFile *f = FindData ( file );
if ( !f )
{
m_iError = CATERR_NOFILE;
return false;
}
return MarkRemoveFile ( f );
}
bool CCatFile::MarkRemoveFile ( SInCatFile *f )
{
f->bDelete = true;
return true;
}
void CCatFile::WriteFiles()
{
int count = 0;
CListNode<SInCatFile> *node;
for ( node = m_lFiles.Front(); node; node = node->next() )
{
if ( node->Data()->bDelete )
++count;
}
if ( !count )
return;
size_t *offset = new size_t[count * 2];
count = 0;
node = m_lFiles.Front();
while ( node )
{
CListNode<SInCatFile> *nextNode = node->next();
SInCatFile *f = node->Data();
if ( f->bDelete )
{
offset[count++] = f->lOffset;
offset[count++] = f->lSize;
m_lFiles.remove(node);
delete f;
}
node = nextNode;
}
if ( m_fDatFile.WritePartFile(offset, count) )
WriteCatFile();
}
CyStringList *CCatFile::GetTShipsEntries()
{
if ( this->ExtractFile("types/tships.pck", "tships.txt") )
{
CFileIO TShips("tships.txt");
if ( TShips.Exists() )
{
CyStringList *lines = TShips.ReadLinesStr();
if ( lines )
{
// remove any commands, blank lines, and start
// and put the id as the data
int entries = -1;
for ( SStringList *str = lines->Head(); str; str = str->next )
{
str->str.RemoveFirstSpace();
str->str.RemoveChar(9);
str->str.RemoveChar('\r');
str->str.RemoveChar('\n');
if ( str->str.Empty() )
{
str->remove = true;
continue;
}
if ( str->str[0] == '/' )
{
str->remove = true;
continue;
}
if ( entries == -1 )
{
entries = str->str.GetToken(";", 2, 2).ToInt();
str->remove = true;
continue;
}
// hopefully we now have a valid tships line
int num = -1;
while ( str->data.Empty() )
str->data = str->str.GetToken(";", num--, (num + 2));
}
}
// remove the tmp file
TShips.Remove();
if ( lines )
lines->RemoveMarked();
return lines;
}
}
return NULL;
}
CyString CCatFile::GetTShipsEntry(CyString id)
{
CyStringList *ships = this->GetTShipsEntries();
if ( ships )
{
SStringList *node = ships->FindData(id.upper());
if ( node )
return node->str;
}
return NullString;
}