Rev 317 | 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#ifdef _XBODLIBRARY__#include "../Windows/libx2bc.h"#else#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"#endif//////////////////////////////////////////////////////////////////////// STATIC FUNCTIONS//////////////////////////////////////////////////////////////////////Utils::WString C_File::GetDirectory(FileType eType, const Utils::WString &filename, CBaseFile *file){switch (eType){case FILETYPE_SCRIPT:return L"scripts";case FILETYPE_TEXT:return L"t";case FILETYPE_README:{if (file)return Utils::WString(L"PluginManager/Readme/") + file->getNameValidFile();return L"PluginManager/Readme";}case FILETYPE_MAP:return L"maps";case FILETYPE_MOD:return L"mods";case FILETYPE_UNINSTALL:return L"PluginManager/Uninstall";case FILETYPE_SOUND:if (!filename.empty() && !CFileIO(filename).isFileExtension(L"wav"))return L"soundtrack";return L"s";case FILETYPE_SOUNDTRACK:return L"soundtrack";case FILETYPE_EXTRA:return L"PluginManager/Extras";case FILETYPE_SCREEN:return L"loadscr";case FILETYPE_ADVERT:return L"PluginManager/Graphics";case FILETYPE_MISSION:return L"director";}return Utils::WString::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::WString &filename){m_bDontDeleteData = false;m_bUsedMalloc = false;m_sData = NULL;Reset ();setFilename(filename);}C_File::~C_File(){DeleteData ();if (!_sTmpFile.empty())CFileIO::Remove(_sTmpFile);}long C_File::uncompressedDataSize() const{if (m_lUncomprDataSize)return m_lUncomprDataSize;if (m_lSize)return m_lSize;return m_lDataSize;}/*Func: getDirectory()Return: Directory StringDesc: Returns the directory the file goes into, based on m_sDir and Filetype*/Utils::WString C_File::getDirectory(CBaseFile *file) const{if (IsFakePatch())return L"";Utils::WString sDir = _sDir;if ((m_iFileType == FILETYPE_MOD) && (sDir == L"Patch"))return L"PluginManager/Patch";if ((!sDir.empty()) && (m_iFileType != FILETYPE_README) && sDir != L"."){Utils::WString dir = sDir.findReplace(L"\\", L"/");if (file){dir = dir.findReplace(L"$scriptname", file->getNameValidFile());dir = dir.findReplace(L"$scriptauthor", file->author());}return dir;}return C_File::GetDirectory(m_iFileType, _sName, file);}Utils::WString C_File::getNameDirectory(CBaseFile *file) const{Utils::WString dir = getDirectory(file);if (!dir.empty())dir += L"/";return (dir + _sName).findReplace(L"\\", L"/").findReplace(L"//", L"/");}/*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;_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->dir().left(6).Compare(L"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::textFileID(const Utils::WString &sFilename) const{if ( m_iFileType != FILETYPE_TEXT )return -1;Utils::WString filename = sFilename;if ( filename.empty() )filename = _sName;Utils::WString textid;if ( filename.contains(L"-L") || filename.contains(L"-l") )textid = filename.token(L"-", 1);elsetextid = filename.tokens(L".", 1, -2).right(4);if (textid.isNumber()) return textid.toInt();return -1;}bool C_File::isAutoTextFile (){int textid = textFileID();if ( textid == -1 ) return false;if ( textid == 4 )return true;// check for original nameif ( !_sOriginalName.empty() ){textid = textFileID(_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::WString name = _sName;if (name.token(L".", 1).toLong())return true;if (name.left(10) == L"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(const Utils::WString &filename){Utils::WString file = filename.findReplace ( L"\\", L"/" ).findReplace ( L"//", L"/" );_sFullDir = file.tokens(L"/", 1, -2);_sName = file.token(L"/", -1);if ( _sFullDir.right(2) == L"/." )_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(const Utils::WString 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, L"C_File::readFromFile() unable to malloc, %d (%hs)", 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, L"C_File::readFromFile() unable to read from file, %d (%hs)", 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: filePointerDesc: Returns the file pointer nameJoins dir and name togetherWorks for relative paths as well*/Utils::WString C_File::filePointer() const{Utils::WString fullfile = _sFullDir;if ( !fullfile.empty() )fullfile += L"/";if ( !_sName.empty() )fullfile += _sName;return fullfile;}bool C_File::isExternalFile() const{if (!m_sData && !m_lDataSize){Utils::WString file = this->filePointer();if (!file.empty() && CFileIO::Exists(file))return true;}return false;}void C_File::updateSignature(){_sSignature = L"";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';Utils::WString sData ((char *)data);int pos = sData.findPos(L"</codearray>", 0);if ( pos != -1 ){sData = sData.right(sData.length() - pos);pos = sData.findPos(L"<signature>", 0);int endpos = sData.findPos(L"</signature>", 0);if ( pos != -1 && endpos != -1 ){_sSignature = sData.mid(pos + 12, endpos - (pos + 12) + 1);_sSignature = _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::WString 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::WString file = filePointer();Utils::WString ext = CFileIO(file).extension();if ( !ext.Compare(L"xml") && !ext.Compare(L"pck") )return false;if ( m_iDataCompression != SPKCOMPRESS_NONE )return m_bSigned;// check file extenstionif ( (ext.Compare(L"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::WString baseName = CFileIO(file->name()).dir() + L"/" + CFileIO(file->name()).baseName();Utils::WString compareBaseName = CFileIO(_sName).dir() + L"/" + CFileIO(_sName).baseName();if (baseName.Compare(compareBaseName))return true;}return false;}return true;}/*############################################################################################################################################################ Compression Functions ############################################################################################################################################################*/bool C_File::CompressFile ( CProgressInfo *progress ){Utils::WString file = this->filePointer();if ( !CFileIO(file).exists() ){if ( !this->writeToFile(L"tempuncompr.dat", m_sData, m_lDataSize) )return false;file = L"tempuncompr.dat";}bool ret = false;#pragma warning(disable:4244)FILE *fIn = fopen(file.toFileUTF8().c_str(), "rb");#pragma warning(default:4244)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(L"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(L"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, size_t* outSize, CProgressInfo* progress, int level){if (outSize) *outSize = 0;size_t initialLen = m_lDataSize;if (initialLen < 100) initialLen = 200;else if (initialLen < 1000) initialLen *= 2;initialLen += 1000;switch (compressionType){case SPKCOMPRESS_ZLIB:{unsigned long comprLen = static_cast<unsigned long>(initialLen);unsigned char* compr = (unsigned char*)calloc(comprLen, 1);if (!compr) return NULL;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){unsigned long newLen = comprLen + (CHUNK * 2);unsigned char* newCompr = (unsigned char*)realloc(compr, newLen);if (!newCompr) { free(compr); return NULL; }compr = newCompr;comprLen = newLen;}}if (err == Z_OK && comprLen > 0){unsigned char* retData = new (std::nothrow) unsigned char[comprLen];if (!retData) { free(compr); return NULL; }memcpy(retData, compr, comprLen);free(compr);if (outSize) *outSize = static_cast<size_t>(comprLen);return retData;}free(compr);return NULL;}break;case SPKCOMPRESS_LZMA:{size_t comprLen = initialLen;unsigned char* compr = new (std::nothrow) unsigned char[comprLen];if (!compr) return NULL;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 && comprLen > 0){unsigned char* retData = new (std::nothrow) unsigned char[comprLen];if (!retData) { delete[] compr; return NULL; }memcpy(retData, compr, comprLen);delete[] compr;if (outSize) *outSize = comprLen;return retData;}delete[] 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(L"pck") || this->checkFileExt(L"cat") || this->checkFileExt(L"dat") || this->checkFileExt(L"pbb") || this->checkFileExt(L"pbd")) )compressionType = SPKCOMPRESS_LZMA;// dont compress cat/dat filesif (this->checkFileExt(L"cat") || this->checkFileExt(L"dat"))compressionType = SPKCOMPRESS_NONE;// 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;size_t comprLen = static_cast<size_t>(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;size_t bestSize = 0;unsigned char *bestCompr = NULL;for ( int i = 0; i < 2; i++ ){size_t 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 mode//TODO: file compression?return 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(const Utils::WString &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 fileUtils::String file = toFile;if (file.empty()){m_iTempNum++;file = "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(filePointer(), cData, len);}bool C_File::writeToFile(const Utils::WString &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(L"::")) {Utils::WString catfile = filename.token(L"::", 1);Utils::WString file = filename.token(L"::", 2);CCatFile newcat;return newcat.addData(catfile, data, len, file, true, true);}else {Utils::WString filen = filename.findReplace(L"/", L"\\");filen = filen.findReplace(L"\\\\", L"\\");if ( len && data ) {CFileIO File(filen);if ( File.startWrite() ) ret = File.write(data, len);}}return ret;}bool C_File::writeToDir(const Utils::WString &dir, CBaseFile *spkfile, bool includedir, const Utils::WString &appendDir, unsigned char *data, long len){Utils::WString fullfile = _getFullFileToDir(dir, includedir, spkfile);if (!appendDir.empty()){if (!fullfile.empty())fullfile += L"/";fullfile += appendDir;}Utils::WString fulldir = fullfile.tokens(L"/", 1, -2);if (!fulldir.empty()){if (!CDirIO(fulldir).create())return false;}return writeToFile(fullfile, data, len);}Utils::WString C_File::dataSizeString() const{return SPK::GetSizeString(m_lDataSize);}Utils::WString C_File::uncompressedSizeString() const{return SPK::GetSizeString(uncompressedDataSize());}Utils::WString C_File::creationTimeString() const{if ( !m_tTime )return Utils::WString::Null();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 Utils::WString(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::WString GetFileTypeString(int type){switch ( type ){case FILETYPE_SCRIPT:return L"Script";case FILETYPE_TEXT:return L"Text";case FILETYPE_README:return L"Readme";case FILETYPE_MAP:return L"Map";case FILETYPE_MOD:return L"Mod";case FILETYPE_UNINSTALL:return L"Uninstall";case FILETYPE_SOUND:return L"Sound";case FILETYPE_EXTRA:return L"Extra";case FILETYPE_SCREEN:return L"Screen";case FILETYPE_ADVERT:return L"Advert";case FILETYPE_MISSION:return L"Mission";case FILETYPE_BACKUP:return L"Backup";case FILETYPE_SHIPOTHER:return L"ShipOther";case FILETYPE_SHIPMODEL:return L"ShipModel";case FILETYPE_SHIPSCENE:return L"ShipScene";case FILETYPE_COCKPITSCENE:return L"CockpitScene";}return Utils::WString::Null();}FileType GetFileTypeFromString(const Utils::WString &type){Utils::WString ltype = type.lower();if ( ltype == L"script" || ltype == L"scriptpck")return FILETYPE_SCRIPT;else if ( ltype == L"text" )return FILETYPE_TEXT;else if ( ltype == L"readme" )return FILETYPE_README;else if ( ltype == L"map" )return FILETYPE_MAP;else if ( ltype == L"mod" )return FILETYPE_MOD;else if ( ltype == L"uninstall" )return FILETYPE_UNINSTALL;else if ( ltype == L"sound" )return FILETYPE_SOUND;else if ( ltype == L"extra" )return FILETYPE_EXTRA;else if ( ltype == L"screen" )return FILETYPE_SCREEN;else if ( ltype == L"advert" )return FILETYPE_ADVERT;else if ( ltype == L"mission" )return FILETYPE_MISSION;else if ( ltype == L"backup" )return FILETYPE_BACKUP;else if ( ltype == L"shipother" )return FILETYPE_SHIPOTHER;else if ( ltype == L"shipmodel" )return FILETYPE_SHIPMODEL;else if ( ltype == L"shipscene" )return FILETYPE_SHIPSCENE;else if ( ltype == L"cockpitscene" )return FILETYPE_COCKPITSCENE;return FILETYPE_UNKNOWN;}Utils::WString FormatErrorString(int error, const Utils::WString &rest){std::vector<Utils::WString> args;if (!rest.empty())rest.tokenise(L"~", args);Utils::WString errorStr;switch ( error ){case SPKINSTALL_CREATEDIRECTORY:errorStr = L"Creating Directory: %1";break;case SPKINSTALL_CREATEDIRECTORY_FAIL:errorStr = L"Unable to Creating Directory: %1";break;case SPKINSTALL_WRITEFILE:errorStr = L"Writing File: %1";break;case SPKINSTALL_WRITEFILE_FAIL:errorStr = L"Unable to Write File: %1";break;case SPKINSTALL_DELETEFILE:errorStr = L"Deleting File: %1";break;case SPKINSTALL_DELETEFILE_FAIL:errorStr = L"Unable to delete File: %1";break;case SPKINSTALL_REMOVEDIR:errorStr = L"Removing Directory: %1";break;case SPKINSTALL_SKIPFILE:errorStr = L"Skipping File (older): %1";break;case SPKINSTALL_ENABLEFILE:errorStr = L"Enabled File: %1";break;case SPKINSTALL_DISABLEFILE:errorStr = L"Disabled File: %1";break;case SPKINSTALL_ENABLEFILE_FAIL:errorStr = L"Failed to enable File: %1";break;case SPKINSTALL_DISABLEFILE_FAIL:errorStr = L"Failed to disable File: %1";break;case SPKINSTALL_UNINSTALL_MOVE:errorStr = L"Moving uninstall file: %1";break;case SPKINSTALL_UNINSTALL_MOVE_FAIL:errorStr = L"Unable to Move uninstall file: %1";break;case SPKINSTALL_UNINSTALL_COPY:errorStr = L"Coping uninstall file: %1";break;case SPKINSTALL_UNINSTALL_COPY_FAIL:errorStr = L"Unable to Copy uninstall file: %1";break;case SPKINSTALL_UNINSTALL_REMOVE:errorStr = L"Removing Uninstall file: %1";break;case SPKINSTALL_UNINSTALL_REMOVE_FAIL:errorStr = L"Unable to remove Uninstall file: %1";break;case SPKINSTALL_SHARED:errorStr = L"Removing Unused Shared file: %1";break;case SPKINSTALL_SHARED_FAIL:errorStr = L"Unable to remove Unused Shared file: %1";break;case SPKINSTALL_ORIGINAL_BACKUP:errorStr = L"Backing up original file: %1";break;case SPKINSTALL_ORIGINAL_BACKUP_FAIL:errorStr = L"Unable to back up original file: %1";break;case SPKINSTALL_ORIGINAL_RESTORE:errorStr = L"Restoring original file: %1";break;case SPKINSTALL_ORIGINAL_RESTORE_FAIL:errorStr = L"Unable to restore original file: %1";break;case SPKINSTALL_FAKEPATCH:errorStr = L"Shifted fake patch: %1 to %2";break;case SPKINSTALL_FAKEPATCH_FAIL:errorStr = L"Unable to shift fake patch: %1 to %2";break;case SPKINSTALL_AUTOTEXT:errorStr = L"Shifted text file: %1 to %2";break;case SPKINSTALL_AUTOTEXT_FAIL:errorStr = L"Unable to shift text file: %1 to %2";break;case SPKINSTALL_MISSINGFILE:errorStr = L"File is missing: %1";break;case SPKINSTALL_ORPHANED:errorStr = L"Orphaned File removed: %1";break;case SPKINSTALL_ORPHANED_FAIL:errorStr = L"Unable to remove Orphaned File: %1";break;}Utils::WString ret = errorStr.args(args);return ret;}bool C_File::CheckPCK (){if ( (m_sData) && (m_lDataSize) && (m_iDataCompression == SPKCOMPRESS_NONE) )return IsDataPCK ( m_sData, m_lDataSize );Utils::WString 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(L"pck") || checkFileExt(L"pbb") || checkFileExt(L"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 Utils::WString &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){if (!data || datasize < 8) { // Minimum size for header + lengthif (len) *len = 0;return nullptr;}bool isPCK = IsDataPCK(data, datasize);unsigned char* newData = data;unsigned char* tempData = nullptr;if (nocrypt) {tempData = new (std::nothrow) unsigned char[datasize];if (!tempData) { if (len) *len = 0; return nullptr; }memcpy(tempData, data, datasize);unsigned char magic = tempData[0] ^ 0xC8;for (size_t i = 1; i < datasize; i++)tempData[i] ^= magic;newData = tempData + 1;datasize -= 1;}if (datasize < 4) { // Not enough data for lengthif (tempData) delete[] tempData;if (len) *len = 0;return nullptr;}// Read uncompressed length safelysize_t lenPos = datasize - 4;unsigned long uncomprLen = 0;memcpy(&uncomprLen, newData + lenPos, 4);if (uncomprLen > datasize * 100 || uncomprLen == 0) {if (tempData) delete[] tempData;if (len) *len = 0;return nullptr;}unsigned char* uncompr = new (std::nothrow) unsigned char[uncomprLen + 1];if (!uncompr) {if (tempData) delete[] tempData;if (len) *len = 0;return nullptr;}memset(uncompr, 0, uncomprLen + 1);// Parse headersize_t bufOffset = PCKHEADERSIZE;if (datasize < bufOffset + 8) { // Not enough data for header and footerif (tempData) delete[] tempData;delete[] uncompr;if (len) *len = 0;return nullptr;}unsigned char flag = newData[3];unsigned char* buf = newData + bufOffset;if (flag & GZ_FLAG_EXTRA) {if (datasize < bufOffset + 2) { if (tempData) delete[] tempData; delete[] uncompr; if (len) *len = 0; return nullptr; }size_t xlen = *((short int*)(buf));buf += 2;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);if (bufSize <= 0) {if (tempData) delete[] tempData;delete[] uncompr;if (len) *len = 0;return nullptr;}int err = uncompress2(uncompr, &uncomprLen, buf, bufSize);if (err != Z_OK) {if (tempData) delete[] tempData;delete[] uncompr;if (len) *len = 0;return nullptr;}if (len) *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::WString sTag(tag);if ( sTag.Compare(L"</signature") || sTag.Compare(L"<signature") ) return true;if ( sTag.Compare(L"</codearray") || sTag.Compare(L"<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::WString sTag(tag);if ( !sTag.Compare(L"<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::WString C_File::baseName() const{// remove any directoryUtils::WString file = _sName.token(L"/", -1);// remove file extensionreturn file.remToken(L".", -1);}void C_File::CopyData(C_File *oldFile, bool includeData){setFilename(oldFile->fullFilename());_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->uncompressedDataSize();}m_iDataCompression = oldFile->GetCompressionType();}float GetLibraryVersion() { return (float)LIBRARYVERSION; }float GetFileFormatVersion() { return (float)FILEVERSION; }Utils::WString C_File::fileExt() const{return _sName.token(L".", -1);}bool C_File::checkFileExt(const Utils::WString& ext){if (fileExt().Compare(ext)) return true; return false;}const Utils::WString &C_File::changeFileExt(const Utils::WString &ext){_sName = CFileIO(_sName).changeFileExtension(ext);return _sName;}bool C_File::CheckPackedExtension(){Utils::WString ext = this->fileExt();if ( ext == L"pck" )return true;else if ( ext == L"pbb" )return true;else if ( ext == L"pbd" )return true;return false;}bool C_File::shouldCheckBaseName() const{Utils::WString ext = CFileIO(_sName).extension();if (ext == L"xml" || ext == L"txt" || ext == L"pck")return true;if (ext == L"bod" || ext == L"bob" || ext == L"pbb" || ext == L"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::WString fromFile = this->filePointer();if (!CFileIO::Exists(fromFile)){if (this->writeToFile(CPackages::tempDirectory() + L"bob.tmp"))fromFile = CPackages::tempDirectory() + L"bob.tmp";}fromFile = fromFile.findReplace(L"/", L"\\");Utils::WString toFile = CPackages::tempDirectory() + L"bod.tmp";toFile = toFile.findReplace(L"/", L"\\");if (CFileIO::Exists(fromFile)){#ifdef _XBODLIBRARY__if (X2BC_BOB2BOD(fromFile.toString().c_str(), toFile.toString().c_str())) {CFileIO F(toFile);if (F.exists() && F.startRead()) {unsigned char* data = F.readAll(size);F.close();F.remove();return data;}}#else// Use x2bc library to convert the fileSettings settings;bob_dom_document doc(&settings);ibinaryrealfile is;otextrealfile os;is.open(fromFile.toString().c_str(), filestream::rdonly);os.open(toFile.toString().c_str(), filestream::create);if (!is.fail() && !os.fail()){if (doc.convert(is, os)){CFileIO F(toFile);if (F.exists() && F.startRead()){unsigned char* data = F.readAll(size);F.close();F.remove();return data;}}}#endif}if (CFileIO::Exists(CPackages::tempDirectory() + L"bob.tmp"))CFileIO::Remove(CPackages::tempDirectory() + L"bob.tmp");return NULL;}unsigned char* C_File::BobCompile(size_t* size, bool isCut){(*size) = 0;if (!m_sData || !m_lDataSize){if (!this->ReadFromFile())return NULL;}Utils::WString toFile = CPackages::tempDirectory() + L"bob.tmp";toFile = toFile.findReplace(L"/", L"\\");CFileIO ToFile(toFile);Settings settings;bod_parser p(&settings);bool bRes;bod_parser::fileType type = isCut ? bod_parser::cutFile : bod_parser::bobFile;obinaryrealfile os;os.open(ToFile.fullFilename().toFileUTF8().c_str(), filestream::create);if (!os.bad()){bRes = p.compile((char *)m_sData, (size_t)m_lDataSize, type, os);for (bod_parser::iterator& it = p.errors.begin(); it != p.errors.end(); ++it) {; // check errors?}}os.close();CFileIO tmpFile(CPackages::tempDirectory() + L"bob.tmp");if (tmpFile.exists() && tmpFile.startRead()){unsigned char* data = tmpFile.readAll(size);tmpFile.close();tmpFile.remove();if (data)return data;}return NULL;}bool C_File::renameScript(const Utils::WString &baseName){if ( !m_sData || !m_lDataSize ){if ( !this->ReadFromFile() )return false;}// uncompress the fileif ( !this->UncompressData() )return false;// un pck the fileif (this->checkFileExt(L"pck")){if ( !this->UnPCKFile() )return false;}// now we should have the raw dataUtils::WString data((const char *)m_sData);data.truncate(m_lDataSize);data = data.findReplace(this->baseName(), 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(L"pck")){if ( !this->PCKFile() )return false;}return true;}Utils::WString C_File::_getFullFileToDir(const Utils::WString &dir, bool includedir, CBaseFile *file) const{Utils::WString fullfile = dir;if (includedir){Utils::WString d = getDirectory(file);if (!d.empty()){if (!fullfile.empty())fullfile += L"/";fullfile += d;}}if (!_sName.empty()){if (!fullfile.empty())fullfile += L"/";fullfile += _sName;}fullfile = fullfile.findReplace(L"\\", L"/");return fullfile;}