Rev 134 | Rev 156 | 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"// X2BOD/*#include "x2bc/x2bc_common/settings.h"#include "x2bc/x2bc_common/bob_dom.h"#include "x2bc/x2bc_common/bod_parser.h"#include "x2bc/x2bc_common/realfile_stream.h"*/#include "../Windows/libx2bc.h"//////////////////////////////////////////////////////////////////////// STATIC FUNCTIONS//////////////////////////////////////////////////////////////////////Utils::String C_File::GetDirectory(FileType eType, const Utils::String &filename, CBaseFile *file){switch (eType){case FILETYPE_SCRIPT:return "Scripts";case FILETYPE_TEXT:return "T";case FILETYPE_README:{if (file)return Utils::String("PluginManager/Readme/") + file->getNameValidFile();return "PluginManager/Readme";}case FILETYPE_MAP:return "Maps";case FILETYPE_MOD:return "Mods";case FILETYPE_UNINSTALL:return "PluginManager/Uninstall";case FILETYPE_SOUND:if (!filename.empty() && !CFileIO(filename).CheckFileExtension("wav"))return "Soundtrack";return "s";case FILETYPE_SOUNDTRACK: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 Utils::String::Null();}bool C_File::DoesTypeHaveExtraDir(int i){switch (i){case FILETYPE_EXTRA:case FILETYPE_SHIPSCENE:case FILETYPE_COCKPITSCENE:case FILETYPE_SHIPMODEL:case FILETYPE_SHIPOTHER:return true;}return false;}//////////////////////////////////////////////////////////////////////// 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(const Utils::String &filename){m_bDontDeleteData = false;m_bUsedMalloc = false;m_sData = NULL;Reset ();setFilename(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 ){return getDirectory(file);}Utils::String C_File::getDirectory(CBaseFile *file) const{if (IsFakePatch())return "";Utils::String sDir = _sDir;if ((m_iFileType == FILETYPE_MOD) && (sDir == "Patch"))return "PluginManager/Patch";if ((!sDir.empty()) && (m_iFileType != FILETYPE_README) && sDir != "."){Utils::String dir = sDir.findReplace("\\", "/");if (file){dir = dir.findReplace("$scriptname", file->getNameValidFile());dir = dir.findReplace("$scriptauthor", file->author());}return dir;}return C_File::GetDirectory(m_iFileType, _sName, file);}CyString C_File::GetNameDirectory(CBaseFile *file){return getNameDirectory(file);}Utils::String C_File::getNameDirectory(CBaseFile *file) const{Utils::String dir = getDirectory(file);if (!dir.empty())dir += "/";return (dir + _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 = FILETYPE_UNKNOWN;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;_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 = _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 ( !_sOriginalName.empty() ){textid = GetTextFileID(_sOriginalName);if ( textid >= 0 && textid <= 3 )return true;}return false;}bool C_File::isFileInAddon() const{switch (m_iFileType){case FILETYPE_SHIPMODEL:case FILETYPE_SHIPOTHER:case FILETYPE_SHIPSCENE:case FILETYPE_COCKPITSCENE:case FILETYPE_SOUND:return false;}return true;}bool C_File::IsFakePatch() const{if (m_iFileType != FILETYPE_MOD)return false;Utils::String name = _sName;if (name.token(".", 1).toLong())return true;if (name.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){setFilename(filename.ToString());}void C_File::setFilename(const Utils::String &filename){Utils::String file = filename.findReplace ( "\\", "/" ).findReplace ( "//", "/" );_sFullDir = file.tokens("/", 1, -2);_sName = file.token("/", -1);if ( _sFullDir.right(2) == "/." )_sFullDir = _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(filePointer());}bool C_File::ReadFromFile(CyString filename){return readFromFile(filename.ToString());}bool C_File::readFromFile(const Utils::String 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);}Utils::String C_File::filePointer() const{Utils::String fullfile = _sFullDir;if ( !fullfile.empty() )fullfile += "/";if ( !_sName.empty() )fullfile += _sName;return fullfile;}bool C_File::isExternalFile() const{if (!m_sData && !m_lDataSize){Utils::String file = this->filePointer();if (!file.empty() && CFileIO::Exists(file))return true;}return false;}/*Func: GetFilePointerDesc: Returns the file pointer nameJoins dir and name togetherWorks for relative paths as well*/CyString C_File::GetFilePointer (){return filePointer();}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(filePointer());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 (){Utils::String file = filePointer();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(filePointer());}bool C_File::ReadSignedFile(){if ( (m_iFileType != FILETYPE_SCRIPT) && (m_iFileType != FILETYPE_UNINSTALL) )return false;// check file pointerUtils::String file = filePointer();Utils::String ext = CFileIO(file).extension();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(filePointer());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->dir() != _sDir )return false;if ( file->name() != _sName ){// check if the base name matches, but only for certain file extensionsif (file->shouldCheckBaseName()){Utils::String baseName = CFileIO(file->GetName()).dir() + "/" + CFileIO(file->GetName()).baseName();Utils::String compareBaseName = CFileIO(_sName).dir() + "/" + CFileIO(_sName).baseName();if (baseName.Compare(compareBaseName))return true;}return false;}return true;}/*############################################################################################################################################################ Compression Functions ############################################################################################################################################################*/bool C_File::CompressFile ( CProgressInfo *progress ){Utils::String file = this->filePointer();if ( !CFileIO(this->filePointer()).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;unsigned char *uncompr = new unsigned char[m_lDataSize];memcpy(uncompr, m_sData, m_lDataSize);return uncompr;}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.ToString(), 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(filePointer(), cData, len);}bool C_File::writeToFile(const Utils::String &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.contains("::")) {Utils::String catfile = filename.token("::", 1);Utils::String file = filename.token( "::", 2);CCatFile newcat;return newcat.AddData ( catfile, data, len, file, true, true );}else {Utils::String filen = filename.findReplace ( "/", "\\" );filen = filen.findReplace ( "\\\\", "\\" );if ( len && data ) {CFileIO File(filen);if ( File.startWrite() ) ret = File.write(data, len);}}return ret;}bool C_File::writeToDir(const Utils::String &dir, CBaseFile *spkfile, bool includedir, const Utils::String &appendDir, unsigned char *data, long len){Utils::String fullfile = _getFullFileToDir(dir, includedir, spkfile);if (!appendDir.empty()){if (!fullfile.empty())fullfile += "/";fullfile += appendDir;}Utils::String fulldir = fullfile.tokens("/", 1, -2);if (!fulldir.empty()){if (!CDirIO(fulldir).Create())return false;}return writeToFile(fullfile, data, len);}Utils::String C_File::dataSizeString() const{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;}Utils::String 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 Utils::String::Null();}FileType 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 FILETYPE_UNKNOWN;}Utils::String FormatErrorString(int error, const Utils::String &rest){int max = 0;Utils::String *args = 0;if ( !rest.empty() )args = rest.tokenise("~", &max);Utils::String 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;}Utils::String 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 = filePointer();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(filePointer());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 ){bool isPCK = IsDataPCK(data, datasize);unsigned char *newData = data;unsigned char *tempData = NULL;if ( nocrypt ){tempData = new unsigned char[datasize];newData = tempData;memcpy(newData, data, datasize);unsigned char magic = newData[0] ^ 0xC8;for ( size_t i = 1; i < datasize; i++ )newData[i] ^= magic;++newData;--datasize;}// create data buffersize_t *uncomprLenSize = (size_t*)(newData + (datasize - 4));unsigned long uncomprLen = (unsigned long)*uncomprLenSize;if ( uncomprLen > (datasize * 100) ){if ( tempData ) delete []tempData;*len = 0;return NULL;}unsigned char *uncompr = new unsigned char[uncomprLen + 1];if ( !uncompr ) {if ( tempData ) delete []tempData;return NULL;}memset ( uncompr, 0, sizeof(uncompr) );// find header sizeunsigned char *buf = newData + PCKHEADERSIZE;// buf = data + (6 + sizeof(time_t));char flag = newData[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-newData) - 8);int err = uncompress2 ( uncompr, &uncomprLen, buf, bufSize );if ( err != Z_OK ){if ( tempData ) delete []tempData;delete uncompr;*len = 0;return NULL;}*len = uncomprLen;uncompr[uncomprLen] = '\0';if ( tempData ) delete []tempData;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;}Utils::String C_File::baseName() const{// remove any directoryUtils::String file = _sName.token("/", -1);// remove file extensionreturn file.remToken(".", -1);}CyString C_File::GetBaseName(){return baseName();}void C_File::CopyData(C_File *oldFile, bool includeData){SetFilename(oldFile->GetFullFilename());_sDir = oldFile->dir();m_tTime = oldFile->GetCreationTime();m_bShared = oldFile->IsShared();m_bSigned = oldFile->IsSigned();m_iFileType = oldFile->fileType();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){_sName = CFileIO(_sName).changeFileExtension(ext.ToString());return _sName;}Utils::String C_File::fileExt() const{return _sName.token(".", -1);}const Utils::String &C_File::changeFileExt(const Utils::String &ext){_sName = CFileIO(_sName).changeFileExtension(ext);return _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::shouldCheckBaseName() const{Utils::String ext = CFileIO(_sName).extension();if (ext == "xml" || ext == "txt" || ext == "pck")return true;if (ext == "bod" || ext == "bob" || ext == "pbb" || ext == "pbd")return true;return false;}unsigned int C_File::game() const{return _iGame;}bool C_File::isForGame(int game) const{if (game > 0){if (!_iGame || _iGame == GAME_ALLNEW)return true;return (_iGame & 1 << game) != 0;}return true;}int C_File::getForSingleGame() const{int checkGame = _iGame & ~GAME_ALLNEW;for (int i = 0; i < 31; ++i){if (checkGame == (1 << i))return i;}return 0;}void C_File::setGame(unsigned int i){if (i == 0 || i == 1 << 31)_iGame = 0;else_iGame = i;}unsigned char *C_File::BobDecompile(size_t *size){(*size) = 0;Utils::String fromFile = this->filePointer();if ( !CFileIO::Exists(fromFile) ) {if ( this->writeToFile(CPackages::tempDirectory() + "bob.tmp") ) {fromFile = CPackages::tempDirectory() + "bob.tmp";}}fromFile = fromFile.findReplace("/", "\\");Utils::String toFile = CPackages::tempDirectory() + "bod.tmp";toFile = toFile.findReplace("/", "\\");if ( CFileIO::Exists(fromFile) ) {if ( X2BC_BOB2BOD(fromFile.c_str(), toFile.c_str()) ) {CFileIO F(toFile);if ( F.exists() && F.startRead() ) {unsigned char *data = F.readAll(size);F.close();F.remove();return data;}}}if ( CFileIO::Exists(CPackages::tempDirectory() + "bob.tmp") )CFileIO(CPackages::tempDirectory() + "bob.tmp").remove();/*Settings settings;bob_dom_document doc(&settings);ibinaryrealfile is;otextrealfile os;os.open(CPackages::tempDirectory() + "tmp.tmp", filestream::create);is.open(this->filePointer().c_str(), filestream::rdonly);if(is.fail() || os.fail()) return NULL;bool bRes=doc.convert(is, os);for(bob_dom_document::ErrorIterator &it=doc.errors.begin(); it!=doc.errors.end(); ++it){//BOBError(*it);}if ( bRed ) {CFileIO F(CPackages::tempDirectory() + "tmp.tmp");if ( F.exists() && F.startRead() ) {unsigned char *data = F.readAll(size);F.close();F.remove();return data;}}*/return NULL;}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;}Utils::String C_File::_getFullFileToDir(const Utils::String &dir, bool includedir, CBaseFile *file) const{Utils::String fullfile = dir;if (includedir){Utils::String d = getDirectory(file);if (!d.empty()){if (!fullfile.empty())fullfile += "/";fullfile += d;}}if (!_sName.empty()){if (!fullfile.empty())fullfile += "/";fullfile += _sName;}fullfile = fullfile.findReplace("\\", "/");return fullfile;}