Subversion Repositories spk

Rev

Rev 283 | Rev 317 | 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
#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, 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);
                                        }
                                        else
                                                break;
                                }       

                                // if its compressed ok, remove old data and use new one
                                if ( 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);
                                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 )
                                {
                                        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 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;

        unsigned long comprLen = 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;
                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 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 )
{
        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 buffer
        size_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 size
        unsigned 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);
        }
        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;
        /*
        (*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) ) {
                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;
                        }
                }
        }

        if ( CFileIO::Exists(CPackages::tempDirectory() + L"bob.tmp") )
                CFileIO::Remove(CPackages::tempDirectory() + L"bob.tmp");
                */


        /*
        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;


        Utils::WStringList outData;
        outData.pushBack(L"// 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(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;
}