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();
else
m_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 String
Desc: 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 inside
Clears 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 signed
case 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 signed
case 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 case
case FILETYPE_EXTRA:
if ( this->dir().left(6).Compare(L"extras") )
m_bSigned = true;
else
m_bSigned = false;
break;
// script files need to check
case FILETYPE_SCRIPT:
case FILETYPE_UNINSTALL:
m_bSigned = this->ReadSignedFile();
break;
// mission files
case 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 );
else
delete [] 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);
else
textid = 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 name
if ( !_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: SetFilename
Accept: filename - String for the filename of disk
Desc: Sets the file pointer
Reads the file size and last modifed time to store in the class
Splits 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: ReadFromFile
Return: Boolean - Returns true if read was successfull
Desc: Reads data from file pointer into data stream
As 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: ReadFromData
Accept: data - The data stream to read
size - The length of the data stream
Return: Boolean - Return true if successfull
Desc: Copys data to the data stream in the file
Used 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: filePointer
Desc: Returns the file pointer name
Joins dir and name together
Works 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 read
Desc: 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 returns
Uses seperate rountines for Windows and Linux
*/
time_t C_File::ReadLastModified ()
{
Utils::WString file = filePointer();
if ( file.empty() )
return m_tTime;
#ifndef _WIN32
struct stat attrib; // create a file attribute structure
stat ( file.c_str(), &attrib);
m_tTime = attrib.st_mtime;
#else
#endif
return 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 pointer
Utils::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 extenstion
if ( (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 extensions
if (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());
else
err = 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 compress
if ( (!m_sData) || (!m_lDataSize) )
return false;
// laready compressed to correct type
if ( compressionType == m_iDataCompression )
return true;
// otherwise, lets recompress the file
// first we uncompress the data
if ( !this->UncompressData(progress) )
return false;
// next we compress to new type
if ( !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 compress
if ( (!m_sData) || (!m_lDataSize) )
return false;
// if comopression is set to noe, dont bother
if (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 instead
if ( 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 files
if (this->checkFileExt(L"cat") || this->checkFileExt(L"dat"))
compressionType = SPKCOMPRESS_NONE;
// if its already compressed, no need to compress again
if ( compressionType == m_iDataCompression )
return true;
// no need to change the compression
if ( 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 compression
if ( comprLen > 500000000 )
return this->CompressFile(progress);
// best compression, attempt to compress the file multiple times until we get the best one
if ( 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 it
else
delete 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 uncompress
if ( (!m_sData) || (!m_lDataSize) ) {
return (m_lSize) ? false : true;
}
if ( m_bCompressedToFile )
return false;
// if comopression is set to none, dont bother
if ( 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 uncompress
if ( (!m_sData) || (!m_lDataSize) )
return NULL;
//if ( m_bCompressedToFile )
// return NULL;
// if comopression is set to none, dont bother
if ( 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 _INCLUDE7ZIP
if ( (!m_sData) || (!m_lDataSize) )
return false;
// if theres a tmp file, open it and check it still exists
if ( !m_sTmpFile.empty() )
{
if ( CFileIO::Exists(m_sTmpFile) ) return true;
m_sTmpFile = "";
}
// now uncompress to the file
Utils::String file = toFile;
if (file.empty())
{
m_iTempNum++;
file = "uncompr" + (long)m_iTempNum + ".tmp";
}
else
file = _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;
#else
return 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 file
if ( 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 version
if ( (m_iVersion) && (file->GetVersion()) )
{
if ( m_iVersion > file->GetVersion() )
return false;
}
// now check for last modified time
if ( (m_tTime) && (file->GetLastModified()) )
{
if ( m_tTime > file->GetLastModified() )
return false;
}
// assume same or newer
return 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 + length
if (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 length
if (tempData) delete[] tempData;
if (len) *len = 0;
return nullptr;
}
// Read uncompressed length safely
size_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 header
size_t bufOffset = PCKHEADERSIZE;
if (datasize < bufOffset + 8) { // Not enough data for header and footer
if (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);
}
else
return 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 backwards
int pos = size - 1;
int max = size - 5000;
if ( max < 0 ) max = 0;
// find the first tage
while ( pos > max ) {
while ( data[pos] != '>' && pos > max ) pos--;
if ( data[pos] != '>' ) break;
// now find the front
int 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 file
int iMax = (size > 5000) ? 5000 : size;
int pos = 0;
bool found = false;
// skip past initial space
char tag[21];
while ( pos < iMax )
{
// skip past whitespace
while ( (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 found
if ( (pos - iStartPos) > 20 ) continue;
// check if the tag is what we're looking for
memcpy(tag, data + iStartPos, pos - iStartPos);
tag[pos - iStartPos] = '\0';
Utils::WString sTag(tag);
if ( !sTag.Compare(L"<version") ) continue;
//extract the tag data
iStartPos = 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 directory
Utils::WString file = _sName.token(L"/", -1);
// remove file extension
return 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 file
Settings 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 file
if ( !this->UncompressData() )
return false;
// un pck the file
if (this->checkFileExt(L"pck"))
{
if ( !this->UnPCKFile() )
return false;
}
// now we should have the raw data
Utils::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 file
if (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;
}