Rev 16 | 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";}elsedatfile = catfile.Left ( -4 ) + ".dat";if ( readtype == CATREAD_JUSTCONTENTS )create = false;// first check if the dat file exists and opensbool created = false;if ( readtype != CATREAD_JUSTCONTENTS ){FILE *id = fopen ( datfile.c_str(), "rb+" );if ( !id ){if ( create )created = true;elsereturn CATERR_NODATFILE;}if ( !created && id )fclose ( id );}// now open the cat file to readFILE *cid = fopen ( catfile.c_str(), "rb" );if ( !cid ){if ( create )created = true;elsereturn CATERR_NOCATFILE;}if ( created ){m_fCatFile.Open ( catfile );m_fDatFile.Open ( datfile );m_bCreate = true;return CATERR_CREATED;}// find the file sizefseek ( 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 5size_t size = lFileSize + ((lFileSize % 5) ? 5 - (lFileSize % 5) : 0);// read cat to bufferm_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 matcheslong 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;elsesize = 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 positionsbool 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 filem_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 existsFILE *id = fopen ( filename.c_str(), "rb" );if ( !id )return false;fclose ( id );// then check if the file already existsif ( !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 ();}elsedelete 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;elsereturn 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 existsif ( !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;elsef->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 isf->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 ();}elsedelete 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 existsif ( !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 fileReadFileToData ( 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 nameCyString 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 toif ( !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 encryptionif ( 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 fileSInCatFile *getfile = fcat->FindData ( file );if ( !getfile )return false;// read the dat from the cat filesize_t size = 0;unsigned char *data = fcat->ReadData ( getfile, &size );if ( !data )return false;// now check if it exists in this fileRemoveFile ( file );// now write to the new fileif ( !m_fDatFile.AppendData ( (const char *)data, size ) )return false;// finally add to the listSInCatFile *f = new SInCatFile;f->sData = 0;if ( m_lFiles.empty() )f->lOffset = 0;elsef->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 dataint 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 lineint num = -1;while ( str->data.Empty() )str->data = str->str.GetToken(";", num--, (num + 2));}}// remove the tmp fileTShips.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;}