Rev 58 | Rev 96 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
// File.cpp: implementation of the C_File class.////////////////////////////////////////////////////////////////////////#include "File.h"#include <time.h>#include <sys/types.h>#include <sys/stat.h>#ifndef _WIN32#include <unistd.h>#endif#include <iostream>#include <fstream>#include "spk.h"#include "BaseFile.h"#include "CatFile.h"#include "DirIO.h"#include "secure.h"// LZMA compression#include "lzma/Lzma86Enc.h"#include "lzma/Lzma86Dec.h"//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////void CProgressInfoDone::DoingFile ( C_File *file ){m_iDone = 0;if ( !m_bDontDoMax ){if ( file->GetData() && file->GetDataSize() )m_lMaxSize = file->GetDataSize();elsem_lMaxSize = file->GetSize();}m_pOnFile = file;}void CProgressInfoDone::DoingPackage ( SMultiSpkFile *file ){m_iDone = 0;if ( !m_bDontDoMax )m_lMaxSize = file->lSize;m_pOnPackage = file;}int C_File::m_iTempNum = 0;C_File::C_File(){m_bDontDeleteData = false;m_bUsedMalloc = false;m_sData = NULL;Reset();}C_File::C_File ( CyString filename ){m_bDontDeleteData = false;m_bUsedMalloc = false;m_sData = NULL;Reset ();SetFilename ( filename );}C_File::C_File ( const char *filename ){m_bDontDeleteData = false;m_bUsedMalloc = false;m_sData = NULL;Reset ();SetFilename ( CyString(filename) );}C_File::~C_File(){DeleteData ();if ( !m_sTmpFile.Empty() )remove ( m_sTmpFile.c_str() );}/*Func: GetDirectory()Return: Directory StringDesc: Returns the directory the file goes into, based on m_sDir and Filetype*/CyString C_File::GetDirectory ( CBaseFile *file ){if ( IsFakePatch() )return "";if ( (m_iFileType == FILETYPE_MOD) && (m_sDir == "Patch") )return "PluginManager/Patch";if ( (!m_sDir.Empty()) && (m_iFileType != FILETYPE_README) && m_sDir != "." ){CyString dir = m_sDir.FindReplace ( "\\", "/" );if ( file ){dir = dir.FindReplace ( "$scriptname", file->GetNameValidFile() );dir = dir.FindReplace ( "$scriptauthor", CyString(file->author()) );}return dir;}switch ( m_iFileType ){case FILETYPE_SCRIPT:return "Scripts";case FILETYPE_TEXT:return "T";case FILETYPE_README:{if ( file )return CyString("PluginManager/Readme/") + file->GetNameValidFile();return "PluginManager/Readme";}case FILETYPE_MAP:return "Maps";case FILETYPE_MOD:// if ( (file) && (file->IsPatch()) )// return "Mods/Patch";return "Mods";case FILETYPE_UNINSTALL:return "PluginManager/Uninstall";case FILETYPE_SOUND:if ( CFileIO(m_sName).CheckFileExtension("wav") )return "s";return "Soundtrack";case FILETYPE_EXTRA:return "PluginManager/Extras";case FILETYPE_SCREEN:return "loadscr";case FILETYPE_ADVERT:return "PluginManager/Graphics";case FILETYPE_MISSION:return "Director";}return NullString;}CyString C_File::GetNameDirectory ( CBaseFile *file ){CyString dir = GetDirectory( file );if ( !dir.Empty() )dir += "/";return CyString(dir + m_sName).FindReplace ( "\\", "/" ).FindReplace( "//", "/");}/*Func: Reset()Desc: Resets the file data, clears all data insideClears the Data stream*/void C_File::Reset (){m_iLastError = SPKERR_NONE;m_bLoaded = false;m_iFileType = -1;m_lSize = 0;m_iVersion = 0;m_bSigned = false;m_iUsed = 0;m_tTime = 0;m_lDataSize = m_lUncomprDataSize = 0;DeleteData ();m_iDataCompression = 0;m_bSkip = false;m_bShared = false;m_bCompressedToFile = false;m_bDisabled = false;m_bUpdatedSignature = false;m_iPos = -1;m_iGame = 0;}bool C_File::UpdateSigned(){switch ( m_iFileType ){// text files, readmes, screenshots can be always considered to be signedcase FILETYPE_TEXT:case FILETYPE_README:case FILETYPE_SCREEN:case FILETYPE_ADVERT:case FILETYPE_SOUND:case FILETYPE_BACKUP:m_bSigned = true;break;// mods, maps are always not signedcase FILETYPE_MOD:case FILETYPE_MAP:case FILETYPE_SHIPOTHER:case FILETYPE_SHIPMODEL:case FILETYPE_SHIPSCENE:case FILETYPE_COCKPITSCENE:m_bSigned = false;break;// extra files are a special casecase FILETYPE_EXTRA:if ( this->GetDir().Left(6).Compare("extras") )m_bSigned = true;elsem_bSigned = false;break;// script files need to checkcase FILETYPE_SCRIPT:case FILETYPE_UNINSTALL:m_bSigned = this->ReadSignedFile();break;// mission filescase FILETYPE_MISSION:m_bSigned = false;break;}return m_bSigned;}/*Func: DeleteData()Desc: Clears the data stream, uses free() if created by malloc, otherwise use delete.*/void C_File::DeleteData (){if ( m_sData ){if ( m_bUsedMalloc )free ( m_sData );elsedelete [] m_sData;m_bDontDeleteData = false;m_sData = NULL;m_bLoaded = false;m_lDataSize = 0;}m_bUsedMalloc = false;m_iDataCompression = SPKCOMPRESS_NONE;}int C_File::GetTextFileID(CyString filename){if ( m_iFileType != FILETYPE_TEXT )return -1;if ( filename.Empty() )filename = m_sName;CyString textid;if ( filename.IsIn("-L") || filename.IsIn("-l") )textid = filename.GetToken("-", 1, 1);elsetextid = filename.GetToken(".", -1).Right(4);if ( textid.IsNumber() ) return textid.ToInt();return -1;}bool C_File::IsAutoTextFile (){int textid = GetTextFileID();if ( textid == -1 ) return false;if ( textid <= 3 )return true;// check for original nameif ( !m_sOriginalName.Empty() ){textid = GetTextFileID(m_sOriginalName);if ( textid >= 0 && textid <= 3 )return true;}return false;}bool C_File::IsFakePatch (){if ( m_iFileType != FILETYPE_MOD )return false;if ( m_sName.GetToken ( 1, '.' ).ToInt() )return true;if ( m_sName.Left (10) == "FakePatch_" )return true;return false;}/*############################################################################################################################################################ File Pointer Functions ############################################################################################################################################################*//*Func: SetFilenameAccept: filename - String for the filename of diskDesc: Sets the file pointerReads the file size and last modifed time to store in the classSplits up the filename and dir path*/void C_File::SetFilename ( CyString filename ){CyString file = filename.FindReplace ( "\\", "/" ).FindReplace ( "//", "/" );int tok = file.NumToken ( '/' );m_sFullDir = file.GetToken ( "/", 1, -1 );m_sName = file.GetToken ( "/", -1 );if ( m_sFullDir.Right(2) == "/." )m_sFullDir = m_sFullDir.Left(-2);ReadFileSize ();ReadLastModified ();}/*Func: ReadFromFileReturn: Boolean - Returns true if read was successfullDesc: Reads data from file pointer into data streamAs its read from a file, there will be no compression, so its set to None*/bool C_File::ReadFromFile (){return this->ReadFromFile(GetFilePointer().c_str());}bool C_File::ReadFromFile (CyString filename){CFileIO File(filename);if ( !File.startRead() ) {m_iLastError = SPKERR_FILEOPEN;return false;}m_iDataCompression = SPKCOMPRESS_NONE;m_lDataSize = m_lUncomprDataSize = m_lSize;DeleteData ();m_sData = File.readAll((size_t *)&m_lSize);if ( !m_sData ) {m_iLastError = SPKERR_MALLOC;m_lDataSize = 0;return false;}m_lDataSize = m_lSize;m_tTime = File.modifiedTime();m_iLastError = SPKERR_NONE;m_bLoaded = true;File.close();return true;}/*Func: ReadFromDataAccept: data - The data stream to readsize - The length of the data streamReturn: Boolean - Return true if successfullDesc: Copys data to the data stream in the fileUsed when data is already loaded into memory*/bool C_File::ReadFromData ( char *data, long size ){DeleteData ();m_lDataSize = size ;m_sData = new unsigned char[m_lDataSize];memcpy ( m_sData, data, size );return true;}bool C_File::readFromFile(CFileIO &File){return readFromFile(File, File.fileSize(), false);}bool C_File::readFromFile(CFileIO &File, int iSize, bool bDoSize){if ( !File.isOpened() ) File.startRead();if ( !File.isOpened() ) return false;m_lDataSize = iSize;try {m_sData = new unsigned char[m_lDataSize];}catch(std::exception &e) {CLog::logf(CLog::Log_IO, 2, "C_File::readFromFile() unable to malloc, %d (%s)", m_lDataSize, e.what());return false;}if ( bDoSize ) File.readSize();try {File.read(m_sData, m_lDataSize);}catch(std::exception &e) {CLog::logf(CLog::Log_IO, 2, "C_File::readFromFile() unable to read from file, %d (%s)", m_lDataSize, e.what());DeleteData ();m_lDataSize = 0;return false;}return true;}void C_File::copyData(const unsigned char *data, size_t size){m_lDataSize = (long)size;delete m_sData;m_sData = new unsigned char[size];memcpy(m_sData, data, size);}/*Func: GetFilePointerDesc: Returns the file pointer nameJoins dir and name togetherWorks for relative paths as well*/CyString C_File::GetFilePointer (){CyString fullfile = m_sFullDir;if ( !fullfile.Empty() )fullfile += "/";if ( !m_sName.Empty() )fullfile += m_sName;return fullfile;}void C_File::UpdateSignature(){m_sSignature = "";bool deleteData = false;if ( !m_sData ){if ( !ReadFromFile() )return;deleteData = true;}if ( CheckPCK() )UnPCKFile();m_bUpdatedSignature = true;if ( !m_sData )return;size_t fPos = m_lDataSize;if ( fPos > 700 )fPos = 700;unsigned char *data = m_sData + (m_lDataSize - fPos);data[fPos - 1] = '\0';CyString sData ((char *)data);int pos = sData.FindPos("</codearray>", 0);if ( pos != -1 ){sData = sData.Right(sData.Length() - pos);pos = sData.FindPos("<signature>", 0);int endpos = sData.FindPos("</signature>", 0);if ( pos != -1 && endpos != -1 ){m_sSignature = sData.Mid(pos + 12, endpos - (pos + 12) + 1);m_sSignature = m_sSignature.Remove('\n').Remove('\r');}}if ( deleteData )DeleteData();}/*Func: ReadFileSize()Return: Returns the file size readDesc: Opens the file and seeks to the end*/long C_File::ReadFileSize (){CFileIO File(GetFilePointer());if ( File.exists() ) m_lSize = File.fileSize();m_lUncomprDataSize = m_lSize;return m_lSize;}/*Func: ReadLastModifed()Desc: Reads the last modified time of the file and returnsUses seperate rountines for Windows and Linux*/time_t C_File::ReadLastModified (){CyString file = GetFilePointer();if ( file.Empty() )return m_tTime;#ifndef _WIN32struct stat attrib; // create a file attribute structurestat ( file.c_str(), &attrib);m_tTime = attrib.st_mtime;#else#endifreturn m_tTime;}bool C_File::CheckValidFilePointer (){return CFileIO::Exists(GetFilePointer().ToString());}bool C_File::ReadSignedFile(){if ( (m_iFileType != FILETYPE_SCRIPT) && (m_iFileType != FILETYPE_UNINSTALL) )return false;// check file pointerCyString file = GetFilePointer();CyString ext = CFileIO(file).GetFileExtension();if ( !ext.Compare("xml") && !ext.Compare("pck") )return false;if ( m_iDataCompression != SPKCOMPRESS_NONE )return m_bSigned;// check file extenstionif ( (ext.Compare("pck")) || (CheckPCK()) ){size_t size = 0;unsigned char *data = UnPCKFile ( &size );if ( (data) && (size) ) {bool ret = ::ReadSignedFromData ( data, (long)size );//delete data;return ret;}}else if ( (m_sData) && (m_iDataCompression == SPKCOMPRESS_NONE) )return ::ReadSignedFromData ( m_sData, m_lDataSize );else{unsigned char pData[5001];CFileIO File(file);int iAmount = (File.fileSize() > 5000) ? 5000 : File.fileSize();if ( File.startRead() ) {File.seekStart(File.fileSize() - iAmount);if ( File.read(pData, iAmount, true) ) {return ::ReadSignedFromData(pData, iAmount);}}}return false;}int C_File::ReadScriptVersion (){if ( (m_iFileType != FILETYPE_SCRIPT) && (m_iFileType != FILETYPE_UNINSTALL) ) return 0;if ( (m_sData) && (m_iDataCompression == SPKCOMPRESS_NONE) )m_iVersion = ::ReadScriptVersionFromData ( m_sData, m_lDataSize );else {CFileIO File(GetFilePointer());if ( File.startRead() ) {int iRead = (File.fileSize() > 5000) ? 5000 : File.fileSize();unsigned char *data = File.read(iRead);if ( data ) {m_iVersion = ::ReadScriptVersionFromData(data, iRead);delete []data;}}}return m_iVersion;}bool C_File::MatchFile ( C_File *file ){if ( file->GetFileType() != m_iFileType )return false;if ( file->GetDir() != m_sDir )return false;if ( file->GetName() != m_sName )return false;return true;}/*############################################################################################################################################################ Compression Functions ############################################################################################################################################################*/bool C_File::CompressFile ( CProgressInfo *progress ){CyString file = this->GetFilePointer();if ( !CFileIO(this->GetFilePointer()).exists() ){if ( !this->WriteToFile("tempuncompr.dat", m_sData, m_lDataSize) )return false;file = "tempuncompr.dat";}bool ret = false;FILE *fIn = fopen(file.c_str(), "rb");if ( fIn ){FILE *fOut = fopen("tempcompr.dat", "wb");if ( fOut ){int err;if ( progress )err = zlib_def(fIn, fOut, progress->GetDonePointer());elseerr = zlib_def(fIn, fOut, 0);fclose(fOut);if ( err == Z_OK ){DeleteData ();CFileIO File("tempcompr.dat");File.setAutoDelete(true);if ( File.startRead() ) {m_sData = File.readAll((size_t *)&m_lDataSize);if ( !m_sData ) {m_iLastError = SPKERR_FILEREAD;m_lDataSize = 0;}else {m_iLastError = SPKERR_NONE;m_iDataCompression = SPKCOMPRESS_ZLIB;ret = true;}}File.close();}}fclose(fIn);}CFileIO::Remove("tempuncompr.dat");return ret;}bool C_File::ChangeCompression ( int compressionType, CProgressInfo *progress ){// no data to try to compressif ( (!m_sData) || (!m_lDataSize) )return false;// laready compressed to correct typeif ( compressionType == m_iDataCompression )return true;// otherwise, lets recompress the file// first we uncompress the dataif ( !this->UncompressData(progress) )return false;// next we compress to new typeif ( !this->CompressData(compressionType, progress) )return false;return true;}unsigned char *C_File::CompressToData(int compressionType, unsigned long *outSize, CProgressInfo *progress, int level){unsigned long comprLen = m_lDataSize;if ( comprLen < 100 ) comprLen = 200;else if ( comprLen < 1000 ) comprLen *= 2;comprLen += 1000;switch(compressionType){case SPKCOMPRESS_ZLIB:{unsigned char *compr = (unsigned char *)calloc(comprLen, 1);int err = Z_NOTENOUGH_BUF;while ( err == Z_NOTENOUGH_BUF ){err = compress2 ( compr, &comprLen, (const unsigned char *)m_sData, m_lDataSize, (progress) ? progress->GetDonePointer() : 0, level );if ( err == Z_NOTENOUGH_BUF ){comprLen += (CHUNK * 2);compr = (unsigned char *)realloc(compr, comprLen);}elsebreak;}// if its compressed ok, remove old data and use new oneif ( err == Z_OK ){unsigned char *retData = new unsigned char[comprLen];(*outSize) = comprLen;memcpy(retData, compr, comprLen);free(compr);return retData;}free(compr);return NULL;}break;case SPKCOMPRESS_LZMA:{unsigned char *compr = (unsigned char *)malloc(comprLen);SRes res = Lzma86_Encode((Byte *)compr, (size_t *)&comprLen, (const Byte *)m_sData, (size_t)m_lDataSize, level, LZMA_DICT, SZ_FILTER_NO, (progress) ? progress->GetDonePointer() : NULL);if ( res == SZ_OK ){unsigned char *retData = new unsigned char[comprLen];(*outSize) = comprLen;memcpy(retData, compr, comprLen);free(compr);return retData;}free(compr);return NULL;}break;}return NULL;}bool C_File::CompressData ( int compressionType, CProgressInfo *progress, int level ){// no data to try to compressif ( (!m_sData) || (!m_lDataSize) )return false;// if comopression is set to noe, dont botherif ( compressionType == SPKCOMPRESS_NONE )return true;if ( compressionType == SPKCOMPRESS_7ZIP )compressionType = SPKCOMPRESS_LZMA;// if its zlib, and we are trying to compress pcked files (ie already zlib compression) then switch to lzma insteadif ( compressionType == SPKCOMPRESS_ZLIB && (this->CheckFileExt("pck") || this->CheckFileExt("cat") || this->CheckFileExt("dat") || this->CheckFileExt("pbb") || this->CheckFileExt("pbd")) )compressionType = SPKCOMPRESS_LZMA;// if its already compressed, no need to compress againif ( compressionType == m_iDataCompression )return true;// no need to change the compressionif ( m_iDataCompression != SPKCOMPRESS_NONE )return true;m_lUncomprDataSize = m_lDataSize;unsigned long comprLen = m_lDataSize;if ( comprLen < 100 )comprLen = 200;else if ( comprLen < 1000 )comprLen *= 2;comprLen += 1000;// > 500mb (do file compressionif ( comprLen > 500000000 )return this->CompressFile(progress);// best compression, attempt to compress the file multiple times until we get the best oneif ( compressionType == SPKCOMPRESS_BEST ){int compress[] = { SPKCOMPRESS_ZLIB, SPKCOMPRESS_LZMA };int bestCompress = -1;unsigned int bestSize = 0;unsigned char *bestCompr = NULL;for ( int i = 0; i < 2; i++ ){unsigned long checkSize = 0;unsigned char *compr = this->CompressToData(compress[i], &checkSize, progress, level);if ( compr ){if ( checkSize < bestSize || bestCompress == -1 ){if ( bestCompr )delete bestCompr;bestCompr = compr;bestCompress = compress[i];bestSize = checkSize;}// not the best, no need to keep itelsedelete compr;}}if ( bestCompress != -1 && bestCompr ){DeleteData ();m_sData = bestCompr;m_bUsedMalloc = false;m_lDataSize = bestSize;m_iDataCompression = bestCompress;return true;}if ( bestCompr )delete bestCompr;}if ( compressionType == SPKCOMPRESS_ZLIB || compressionType == SPKCOMPRESS_LZMA ){unsigned char *compr = this->CompressToData(compressionType, &comprLen, progress, level);if ( compr ){DeleteData ();m_sData = compr;m_bUsedMalloc = false;m_lDataSize = comprLen;m_iDataCompression = compressionType;}// we shall always return true, files can be added in non compressed modereturn true;}return false;}bool C_File::UncompressData ( CProgressInfo *progress ){// no data to try to uncompressif ( (!m_sData) || (!m_lDataSize) ) {return (m_lSize) ? false : true;}if ( m_bCompressedToFile )return false;// if comopression is set to none, dont botherif ( m_iDataCompression == SPKCOMPRESS_NONE )return true;if ( m_iDataCompression == SPKCOMPRESS_ZLIB ){unsigned long uncomprLen = m_lUncomprDataSize;unsigned char *uncompr = new unsigned char[m_lUncomprDataSize];int err = uncompress ( uncompr, &uncomprLen, (const unsigned char *)m_sData, m_lDataSize );if ( err == Z_OK ){DeleteData ();m_iDataCompression = SPKCOMPRESS_NONE;m_lDataSize = uncomprLen;m_sData = uncompr;return true;}if ( uncompr )delete [] uncompr;}else if ( m_iDataCompression == SPKCOMPRESS_LZMA ){size_t uncomprLen = m_lUncomprDataSize;unsigned char *uncompr = new unsigned char[m_lUncomprDataSize];SRes res = Lzma86_Decode(uncompr, &uncomprLen, m_sData, (size_t*)&m_lDataSize);if ( res == SZ_OK ){DeleteData ();m_iDataCompression = SPKCOMPRESS_NONE;m_lDataSize = (long)uncomprLen;m_sData = new unsigned char[m_lDataSize];memcpy(m_sData, uncompr, m_lDataSize);delete uncompr;return true;}if ( uncompr )delete [] uncompr;}else if ( m_iDataCompression == SPKCOMPRESS_7ZIP ){long len = m_lUncomprDataSize;unsigned char *compr = LZMADecode_C ( (unsigned char *)m_sData, m_lDataSize, (size_t*)&len, NULL );if ( compr ){DeleteData ();m_sData = compr;m_lDataSize = len;m_iDataCompression = SPKCOMPRESS_NONE;return true;}}return false;}unsigned char *C_File::UncompressData ( long *size, CProgressInfo *progress ){// no data to try to uncompressif ( (!m_sData) || (!m_lDataSize) )return NULL;//if ( m_bCompressedToFile )// return NULL;// if comopression is set to none, dont botherif ( m_iDataCompression == SPKCOMPRESS_NONE ){*size = m_lDataSize;return m_sData;}if ( m_iDataCompression == SPKCOMPRESS_ZLIB ){unsigned long uncomprLen = m_lUncomprDataSize;unsigned char *uncompr = new unsigned char[m_lUncomprDataSize];int err = uncompress ( uncompr, &uncomprLen, (const unsigned char *)m_sData, m_lDataSize );if ( err == Z_OK ){*size = uncomprLen;return uncompr;}if ( uncompr )delete [] uncompr;}if ( m_iDataCompression == SPKCOMPRESS_7ZIP ){long len = m_lUncomprDataSize;unsigned char *compr = LZMADecode_C ( m_sData, m_lDataSize, (size_t *)&len, NULL );if ( compr ){*size = len;return compr;}if ( compr )delete [] compr;}else if ( m_iDataCompression == SPKCOMPRESS_LZMA ){size_t uncomprLen = m_lUncomprDataSize;unsigned char *uncompr = new unsigned char[m_lUncomprDataSize];SRes res = Lzma86_Decode(uncompr, &uncomprLen, m_sData, (size_t*)&m_lDataSize);if ( res == SZ_OK ){*size = (long)uncomprLen;return uncompr;}if ( uncompr )delete [] uncompr;}return NULL;}bool C_File::UncompressToFile ( CyString toFile, CBaseFile *spkfile, bool includedir, CProgressInfo *progress ){#ifdef _INCLUDE7ZIPif ( (!m_sData) || (!m_lDataSize) )return false;// if theres a tmp file, open it and check it still existsif ( !m_sTmpFile.Empty() ){if ( CFileIO::Exists(m_sTmpFile) ) return true;m_sTmpFile = "";}// now uncompress to the fileCyString file = toFile;if ( file.Empty() ){m_iTempNum++;file = CyString("uncompr") + (long)m_iTempNum + ".tmp";}elsefile = GetFullFileToDir ( file, includedir, spkfile );CFileIO File("compr.tmp");if ( !File.startWrite() ) return false;if ( !File.write(m_sData, m_lDataSize) ) return false;File.close();if ( LZMADecodeFile ( "compr.tmp", file.c_str(), (CProgressInfo7Zip *)progress ) ){ret = true;if ( toFile.Empty() )m_sTmpFile = file;}CFileIO::Remove("compr.tmp");return ret;#elsereturn false;#endif}bool C_File::WriteFilePointer ( unsigned char *cData, long len ){return WriteToFile ( GetFilePointer(), cData, len );}bool C_File::WriteToFile ( CyString filename, unsigned char *cData, long len ){unsigned char *data = cData;if ( (!len) || (!data) ) {len = m_lDataSize;data = m_sData;}if ( (!len) || (!data) ) {if ( m_lSize ) return false;}bool ret = false;// check for cat fileif ( filename.IsIn ( "::" ) ) {Utils::String catfile = filename.GetToken ( "::", 1, 1 ).ToString();Utils::String file = filename.GetToken ( "::", 2, 2 ).ToString();CCatFile newcat;return newcat.AddData ( catfile, data, len, file, true, true );}else {Utils::String filen = filename.FindReplace ( "/", "\\" ).ToString();filen = filen.findReplace ( "\\\\", "\\" );if ( len && data ) {CFileIO File(filen);if ( File.startWrite() ) ret = File.write(data, len);}}return ret;}CyString C_File::GetFullFileToDir ( CyString dir, bool includedir, CBaseFile *file ){CyString fullfile = dir;if ( includedir ){CyString d = GetDirectory ( file );if ( !d.Empty() ){if ( !fullfile.Empty() )fullfile += "/";fullfile += d;}}if ( !m_sName.Empty() ){if ( !fullfile.Empty() )fullfile += "/";fullfile += m_sName;}fullfile = fullfile.FindReplace ( "\\", "/" );return fullfile;}bool C_File::WriteToDir ( CyString &dir, CBaseFile *spkfile, bool includedir, CyString appendDir, unsigned char *data, long len ){CyString fullfile = GetFullFileToDir ( dir, includedir, spkfile );if ( !appendDir.Empty() ){if ( !fullfile.Empty() )fullfile += "/";fullfile += appendDir;}CyString fulldir = fullfile.GetToken ( 1, fullfile.NumToken('/') - 2, '/' );if ( !fulldir.Empty() ){if ( !CDirIO(fulldir).Create() )return false;}return WriteToFile ( fullfile, data, len );}CyString C_File::GetDataSizeString (){return SPK::GetSizeString ( m_lDataSize );}CyString C_File::GetUncompressedSizeString (){return SPK::GetSizeString ( GetUncompressedDataSize() );}CyString C_File::GetCreationTimeString (){if ( !m_tTime )return NullString;struct tm *currDate;char dateString[100];time_t n = m_tTime;currDate = localtime(&n);strftime(dateString, sizeof dateString, "(%d/%m/%Y) %H:%M", currDate);return CyString(dateString);}bool C_File::CompareNew ( C_File *file ){if ( !m_iVersion )ReadScriptVersion ();// if version, check if its later versionif ( (m_iVersion) && (file->GetVersion()) ){if ( m_iVersion > file->GetVersion() )return false;}// now check for last modified timeif ( (m_tTime) && (file->GetLastModified()) ){if ( m_tTime > file->GetLastModified() )return false;}// assume same or newerreturn true;}CyString GetFileTypeString ( int type ){switch ( type ){case FILETYPE_SCRIPT:return "Script";case FILETYPE_TEXT:return "Text";case FILETYPE_README:return "Readme";case FILETYPE_MAP:return "Map";case FILETYPE_MOD:return "Mod";case FILETYPE_UNINSTALL:return "Uninstall";case FILETYPE_SOUND:return "Sound";case FILETYPE_EXTRA:return "Extra";case FILETYPE_SCREEN:return "Screen";case FILETYPE_ADVERT:return "Advert";case FILETYPE_MISSION:return "Mission";case FILETYPE_BACKUP:return "Backup";case FILETYPE_SHIPOTHER:return "ShipOther";case FILETYPE_SHIPMODEL:return "ShipModel";case FILETYPE_SHIPSCENE:return "ShipScene";case FILETYPE_COCKPITSCENE:return "CockpitScene";}return NullString;}int GetFileTypeFromString ( CyString type ){CyString ltype = type.ToLower();if ( ltype == "script" )return FILETYPE_SCRIPT;else if ( ltype == "text" )return FILETYPE_TEXT;else if ( ltype == "readme" )return FILETYPE_README;else if ( ltype == "map" )return FILETYPE_MAP;else if ( ltype == "mod" )return FILETYPE_MOD;else if ( ltype == "uninstall" )return FILETYPE_UNINSTALL;else if ( ltype == "sound" )return FILETYPE_SOUND;else if ( ltype == "extra" )return FILETYPE_EXTRA;else if ( ltype == "screen" )return FILETYPE_SCREEN;else if ( ltype == "advert" )return FILETYPE_ADVERT;else if ( ltype == "mission" )return FILETYPE_MISSION;else if ( ltype == "backup" )return FILETYPE_BACKUP;else if ( ltype == "shipother" )return FILETYPE_SHIPOTHER;else if ( ltype == "shipmodel" )return FILETYPE_SHIPMODEL;else if ( ltype == "shipscene" )return FILETYPE_SHIPSCENE;else if ( ltype == "cockpitscene" )return FILETYPE_COCKPITSCENE;return -1;}CyString FormatErrorString(int error, CyString rest){int max = 0;CyString *args = 0;if ( !rest.Empty() )args = rest.SplitToken('~', &max);CyString errorStr;switch ( error ){case SPKINSTALL_CREATEDIRECTORY:errorStr = "Creating Directory: %1";break;case SPKINSTALL_CREATEDIRECTORY_FAIL:errorStr = "Unable to Creating Directory: %1";break;case SPKINSTALL_WRITEFILE:errorStr = "Writing File: %1";break;case SPKINSTALL_WRITEFILE_FAIL:errorStr = "Unable to Write File: %1";break;case SPKINSTALL_DELETEFILE:errorStr = "Deleting File: %1";break;case SPKINSTALL_DELETEFILE_FAIL:errorStr = "Unable to delete File: %1";break;case SPKINSTALL_REMOVEDIR:errorStr = "Removing Directory: %1";break;case SPKINSTALL_SKIPFILE:errorStr = "Skipping File (older): %1";break;case SPKINSTALL_ENABLEFILE:errorStr = "Enabled File: %1";break;case SPKINSTALL_DISABLEFILE:errorStr = "Disabled File: %1";break;case SPKINSTALL_ENABLEFILE_FAIL:errorStr = "Failed to enable File: %1";break;case SPKINSTALL_DISABLEFILE_FAIL:errorStr = "Failed to disable File: %1";break;case SPKINSTALL_UNINSTALL_MOVE:errorStr = "Moving uninstall file: %1";break;case SPKINSTALL_UNINSTALL_MOVE_FAIL:errorStr = "Unable to Move uninstall file: %1";break;case SPKINSTALL_UNINSTALL_COPY:errorStr = "Coping uninstall file: %1";break;case SPKINSTALL_UNINSTALL_COPY_FAIL:errorStr = "Unable to Copy uninstall file: %1";break;case SPKINSTALL_UNINSTALL_REMOVE:errorStr = "Removing Uninstall file: %1";break;case SPKINSTALL_UNINSTALL_REMOVE_FAIL:errorStr = "Unable to remove Uninstall file: %1";break;case SPKINSTALL_SHARED:errorStr = "Removing Unused Shared file: %1";break;case SPKINSTALL_SHARED_FAIL:errorStr = "Unable to remove Unused Shared file: %1";break;case SPKINSTALL_ORIGINAL_BACKUP:errorStr = "Backing up original file: %1";break;case SPKINSTALL_ORIGINAL_BACKUP_FAIL:errorStr = "Unable to back up original file: %1";break;case SPKINSTALL_ORIGINAL_RESTORE:errorStr = "Restoring original file: %1";break;case SPKINSTALL_ORIGINAL_RESTORE_FAIL:errorStr = "Unable to restore original file: %1";break;case SPKINSTALL_FAKEPATCH:errorStr = "Shifted fake patch: %1 to %2";break;case SPKINSTALL_FAKEPATCH_FAIL:errorStr = "Unable to shift fake patch: %1 to %2";break;case SPKINSTALL_AUTOTEXT:errorStr = "Shifted text file: %1 to %2";break;case SPKINSTALL_AUTOTEXT_FAIL:errorStr = "Unable to shift text file: %1 to %2";break;case SPKINSTALL_MISSINGFILE:errorStr = "File is missing: %1";break;case SPKINSTALL_ORPHANED:errorStr = "Orphaned File removed: %1";break;case SPKINSTALL_ORPHANED_FAIL:errorStr = "Unable to remove Orphaned File: %1";break;}CyString ret = errorStr.Args(args, max);CLEANSPLIT(args, max)return ret;}bool C_File::CheckPCK (){if ( (m_sData) && (m_lDataSize) && (m_iDataCompression == SPKCOMPRESS_NONE) )return IsDataPCK ( m_sData, m_lDataSize );Utils::String filename = GetFilePointer().ToString();if ( !filename.empty() ) {CFileIO File(filename);if ( File.startRead() ) {unsigned char data[4];if ( File.read(data, 3) ) return IsDataPCK ( data, 3 );}}if ( CheckFileExt("pck") || CheckFileExt("pbb") || CheckFileExt("pbd") ) return true;return false;}bool C_File::PCKFile(){if ( !m_lDataSize || !m_sData ){if ( !this->ReadFromFile() )return false;}if ( m_lDataSize && m_sData ){if ( !this->UncompressData() )return false;size_t size;unsigned char *data = PCKData(m_sData, m_lDataSize, &size, false);if ( data && size ){m_lUncomprDataSize = m_lDataSize;this->DeleteData();m_bUsedMalloc = false;m_lDataSize = (long)size;m_sData = new unsigned char[size];memcpy(m_sData, data, size);delete [] data;}return true;}return false;}unsigned char *C_File::UnPCKFile ( size_t *len ){unsigned char *data = NULL;size_t datasize = 0;if ( CheckValidFilePointer() ){CFileIO File(GetFilePointer().ToString());if ( File.startRead() ) data = File.readAll(&datasize);}if ( !data ){if ( !m_lDataSize ){if ( !this->ReadFromFile() )return NULL;}datasize = m_lDataSize;data = new unsigned char[datasize];memcpy ( data, m_sData, datasize );}if ( data ){unsigned char *newdata = UnPCKData ( data, datasize, len, CheckPCK() );delete data;return newdata;}return NULL;}bool C_File::UnPCKFile(){if ( !m_lDataSize || !m_sData ){if ( !this->ReadFromFile() )return false;}if ( m_lDataSize && m_sData ){if ( !this->UncompressData() )return false;size_t size;unsigned char *data = UnPCKData(m_sData, m_lDataSize, &size, CheckPCK());if ( data && size ){this->DeleteData();m_lUncomprDataSize = m_lDataSize;m_bUsedMalloc = false;m_sData = new unsigned char[size];memcpy(m_sData, data, size);m_lDataSize = (long)size;delete []data;return true;}}return false;}unsigned char *UnPCKFile ( const char *file, size_t *len, bool nocrypt ){CFileIO File(file);if ( !File.startRead() ) return NULL;size_t size;unsigned char *data = File.readAll(&size);if ( data ) {unsigned char *unData = UnPCKData ( data, size, len, nocrypt );delete data;return unData;}return NULL;}unsigned char *UnPCKData ( unsigned char *data, size_t datasize, size_t *len ) { return UnPCKData(data, datasize, len, IsDataPCK(data, datasize)); }unsigned char *UnPCKData ( unsigned char *data, size_t datasize, size_t *len, bool nocrypt ){//IsDataPCK(data, datasize)if ( nocrypt ){unsigned char magic = data[0] ^ 0xC8;for ( size_t i = 1; i < datasize; i++ )data[i] ^= magic;++data;--datasize;}// create data buffersize_t *uncomprLenSize = (size_t*)(data + (datasize - 4));unsigned long uncomprLen = (unsigned long)*uncomprLenSize;if ( uncomprLen > (datasize * 100) ){*len = 0;return NULL;}unsigned char *uncompr = new unsigned char[uncomprLen + 1];if ( !uncompr )return NULL;memset ( uncompr, 0, sizeof(uncompr) );// find header sizeunsigned char *buf = data + PCKHEADERSIZE;// buf = data + (6 + sizeof(time_t));char flag = data[3];if ( flag & GZ_FLAG_EXTRA ){size_t xlen = *((short int*)(buf));buf += xlen;}if ( flag & GZ_FLAG_FILENAME ){char *origname = (char*)(buf);buf += strlen (origname) + 1;}if ( flag & GZ_FLAG_COMMENT ){char *comment = (char*)(buf);buf += strlen(comment) + 1;}if ( flag & GZ_FLAG_HCRC )buf += 2;long bufSize = (long)(datasize - (buf-data) - 8);int err = uncompress2 ( uncompr, &uncomprLen, buf, bufSize );if ( err != Z_OK ){delete uncompr;*len = 0;return NULL;}*len = uncomprLen;uncompr[uncomprLen] = '\0';return uncompr;}bool IsDataPCK ( const unsigned char *data, size_t size ){if ( size >=3 ){unsigned char magic=data[0] ^ 0xC8;return ((data[1] ^ magic)==0x1F && (data[2] ^ magic)==0x8B);}elsereturn false;}bool ReadSignedFromData ( unsigned char *data, long size ){bool ret = false;if ( IsDataPCK ( data, size ) ) {size_t unpckSize = 0;unsigned char *unpckData = UnPCKData ( data, size, &unpckSize, false );if ( (unpckData) && (unpckSize) ) {ret = ReadSignedFromData ( unpckData, (long)unpckSize );delete unpckData;}return ret;}char tag[10000];// work backwardsint pos = size - 1;int max = size - 5000;if ( max < 0 ) max = 0;// find the first tagewhile ( pos > max ) {while ( data[pos] != '>' && pos > max ) pos--;if ( data[pos] != '>' ) break;// now find the frontint pos2 = pos - 1;while ( data[pos2] != '<' && pos2 > max ) pos2--;if ( data[pos2] != '<' ) break;memcpy(tag, data + pos2, pos - pos2);tag[pos - pos2] = '\0';Utils::String sTag(tag);if ( sTag.Compare("</signature") || sTag.Compare("<signature") ) return true;if ( sTag.Compare("</codearray") || sTag.Compare("<codearray") ) return false;pos = pos2 - 1;}return false;}int ReadScriptVersionFromData ( unsigned char *data, long size ){int iVersion = 0;if ( IsDataPCK ( data, size ) ){size_t unpckSize = 0;unsigned char *unpckData = UnPCKData ( data, size, &unpckSize );if ( (unpckData) && (unpckSize) ) {iVersion = ReadScriptVersionFromData ( unpckData, (long)unpckSize );delete unpckData;}return iVersion;}// only check the beginning of the fileint iMax = (size > 5000) ? 5000 : size;int pos = 0;bool found = false;// skip past initial spacechar tag[21];while ( pos < iMax ){// skip past whitespacewhile ( (pos < iMax) && (data[pos] == ' ') ) ++pos;// find the first '<'while ( (pos < iMax) && (data[pos] != '<') ) ++pos;if ( data[pos] != '<' ) break; //not found// find end tag '>'int iStartPos = pos;while ( (pos < iMax) && (data[pos] != '>') ) ++pos;if ( data[pos] != '>' ) break; //not foundif ( (pos - iStartPos) > 20 ) continue;// check if the tag is what we're looking formemcpy(tag, data + iStartPos, pos - iStartPos);tag[pos - iStartPos] = '\0';Utils::String sTag(tag);if ( !sTag.Compare("<version") ) continue;//extract the tag dataiStartPos = pos + 1;while ( (pos < iMax) && (data[pos] != '<') ) ++pos;if ( (pos - iStartPos) > 20 ) continue;memcpy(tag, data + iStartPos, pos - iStartPos);tag[pos - iStartPos] = '\0';return atoi(tag);}return iVersion;}CyString C_File::GetBaseName (){// remove any directoryCyString file = m_sName.GetToken ( "/", m_sName.NumToken ( '/' ) );// remove file extensionfile = file.GetToken ( ".", 1, file.NumToken ( '.' ) - 1 );return file;}void C_File::CopyData(C_File *oldFile, bool includeData){SetFilename(oldFile->GetFullFilename());m_sDir = oldFile->GetDir();m_tTime = oldFile->GetCreationTime();m_bShared = oldFile->IsShared();m_bSigned = oldFile->IsSigned();m_iFileType = oldFile->GetFileType();m_lSize = oldFile->GetSize();if ( includeData ){m_lDataSize = oldFile->GetDataSize();if ( m_sData )this->DeleteData();m_sData = NULL;if ( oldFile->GetData() && m_lDataSize ){m_sData = new unsigned char[m_lDataSize];m_bUsedMalloc = false;memcpy((char *)m_sData, (char *)oldFile->GetData(), m_lDataSize);}m_lUncomprDataSize = oldFile->GetUncompressedDataSize();}m_iDataCompression = oldFile->GetCompressionType();}float GetLibraryVersion() { return (float)LIBRARYVERSION; }float GetFileFormatVersion() { return (float)FILEVERSION; }CyString C_File::ChangeFileExt(CyString ext){m_sName = CFileIO(m_sName).ChangeFileExtension(ext);return m_sName;}bool C_File::CheckPackedExtension(){CyString ext = this->GetFileExt();if ( ext == "pck" )return true;else if ( ext == "pbb" )return true;else if ( ext == "pbd" )return true;return false;}bool C_File::BobDecompile(){bool bRes=false;CyStringList outData;outData.PushBack("// Converted by SPK Libraries\n");/*bob_with_errors *e=0;char *pszTime;char *name="";time_t tm=time(0);pszTime=ctime(&tm);pszTime[strlen(pszTime)-1]=0;clearErrors();os << "// Converted with x2bc from \"" << is.name() << "\" at " << pszTime << endl;if(settings->convert()==false)os << "// Raw mode - values are not converted" << endl;os << endl;*//*switch(m_sData[0]){case bob_dom_bob::hdr_begin:{bob_dom_bob *bob = new bob_dom_bob(NULL);//bRes=bob->load(is);//if(bRes) bRes=bob->toFile(os);delete bob;break;}case bob_dom_cut::hdr_begin:{bob_dom_cut *cut = new bob_dom_cut(NULL);//bRes=cut->convert(is, os);delete cut;break;}default:return false;}if(e){for(bob_with_errors::ErrorIterator &it=e->errors.begin(); it!=e->errors.end(); ++it){bob_error(it->severity, it->code, "%s->%s", name, it->text);}}return bRes;bob_dom_ibufferstream<unsigned char> is((const unsigned char *)m_sData, m_lDataSize);bob_dom_otextrealfile os;if(is.fail() || os.fail()) return false;bool bRes=doc.convert(is, os);*/return bRes;}bool C_File::BodCompile(){return true;}bool C_File::RenameScript(CyString baseName){if ( !m_sData || !m_lDataSize ){if ( !this->ReadFromFile() )return false;}// uncompress the fileif ( !this->UncompressData() )return false;// un pck the fileif ( this->CheckFileExt("pck") ){if ( !this->UnPCKFile() )return false;}// now we should have the raw dataCyString data((const char *)m_sData);data.Truncate(m_lDataSize);data = data.FindReplace(this->GetBaseName(), baseName);this->DeleteData();m_sData = new unsigned char[data.Length()];memcpy(m_sData, data.c_str(), data.Length());m_lDataSize = (long)data.Length();// repck the fileif ( this->CheckFileExt("pck") ){if ( !this->PCKFile() )return false;}return true;}