Rev 1 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
// File.cpp: implementation of the C_File class.
//
//////////////////////////////////////////////////////////////////////
#include "File.h"
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <iostream>
#include <fstream>
#include "spk.h"
#include "BaseFile.h"
#include "CatFile.h"
#include "DirIO.h"
#include "secure.h"
// LZMA compression
#include "lzma/Lzma86Enc.h"
#include "lzma/Lzma86Dec.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
void CProgressInfoDone::DoingFile ( C_File *file )
{
m_iDone = 0;
if ( !m_bDontDoMax )
{
if ( file->GetData() && file->GetDataSize() )
m_lMaxSize = file->GetDataSize();
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 ( CyString filename )
{
m_bDontDeleteData = false;
m_bUsedMalloc = false;
m_sData = NULL;
Reset ();
SetFilename ( filename );
}
C_File::C_File ( const char *filename )
{
m_bDontDeleteData = false;
m_bUsedMalloc = false;
m_sData = NULL;
Reset ();
SetFilename ( CyString(filename) );
}
C_File::~C_File()
{
DeleteData ();
if ( !m_sTmpFile.Empty() )
remove ( m_sTmpFile.c_str() );
}
/*
Func: GetDirectory()
Return: Directory String
Desc: Returns the directory the file goes into, based on m_sDir and Filetype
*/
CyString C_File::GetDirectory ( CBaseFile *file )
{
if ( IsFakePatch() )
return "";
if ( (m_iFileType == FILETYPE_MOD) && (m_sDir == "Patch") )
return "PluginManager/Patch";
if ( (!m_sDir.Empty()) && (m_iFileType != FILETYPE_README) && m_sDir != "." )
{
CyString dir = m_sDir.FindReplace ( "\\", "/" );
if ( file )
{
dir = dir.FindReplace ( "$scriptname", file->GetNameValidFile() );
dir = dir.FindReplace ( "$scriptauthor", file->GetAuthor() );
}
return dir;
}
switch ( m_iFileType )
{
case FILETYPE_SCRIPT:
return "Scripts";
case FILETYPE_TEXT:
return "T";
case FILETYPE_README:
{
if ( file )
return CyString("PluginManager/Readme/") + file->GetNameValidFile();
return "PluginManager/Readme";
}
case FILETYPE_MAP:
return "Maps";
case FILETYPE_MOD:
// if ( (file) && (file->IsPatch()) )
// return "Mods/Patch";
return "Mods";
case FILETYPE_UNINSTALL:
return "PluginManager/Uninstall";
case FILETYPE_SOUND:
if ( CFileIO(m_sName).CheckFileExtension("wav") )
return "s";
return "Soundtrack";
case FILETYPE_EXTRA:
return "PluginManager/Extras";
case FILETYPE_SCREEN:
return "loadscr";
case FILETYPE_ADVERT:
return "PluginManager/Graphics";
case FILETYPE_MISSION:
return "Director";
}
return NullString;
}
CyString C_File::GetNameDirectory ( CBaseFile *file )
{
CyString dir = GetDirectory( file );
if ( !dir.Empty() )
dir += "/";
return CyString(dir + m_sName).FindReplace ( "\\", "/" ).FindReplace( "//", "/");
}
/*
Func: Reset()
Desc: Resets the file data, clears all data inside
Clears the Data stream
*/
void C_File::Reset ()
{
m_iLastError = SPKERR_NONE;
m_bLoaded = false;
m_iFileType = -1;
m_lSize = 0;
m_iVersion = 0;
m_bSigned = false;
m_iUsed = 0;
m_tTime = 0;
m_lDataSize = m_lUncomprDataSize = 0;
DeleteData ();
m_iDataCompression = 0;
m_bSkip = false;
m_bShared = false;
m_bCompressedToFile = false;
m_bDisabled = false;
m_bUpdatedSignature = false;
m_iPos = -1;
m_iGame = 0;
}
bool C_File::UpdateSigned()
{
switch ( m_iFileType )
{
// text files, readmes, screenshots can be always considered to be 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->GetDir().Left(6).Compare("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::GetTextFileID(CyString filename)
{
if ( m_iFileType != FILETYPE_TEXT )
return -1;
if ( filename.Empty() )
filename = m_sName;
CyString textid;
if ( filename.IsIn("-L") || filename.IsIn("-l") )
textid = filename.GetToken("-", 1, 1);
else
textid = filename.GetToken(".", -1).Right(4);
return textid.ToInt();
}
bool C_File::IsAutoTextFile ()
{
int textid = GetTextFileID();
if ( textid == -1 ) return false;
if ( textid <= 3 )
return true;
// check for original name
if ( !m_sOriginalName.Empty() )
{
textid = GetTextFileID(m_sOriginalName);
if ( textid >= 0 && textid <= 3 )
return true;
}
return false;
}
bool C_File::IsFakePatch ()
{
if ( m_iFileType != FILETYPE_MOD )
return false;
if ( m_sName.GetToken ( 1, '.' ).ToInt() )
return true;
if ( m_sName.Left (10) == "FakePatch_" )
return true;
return false;
}
/*
########################################################################################################################
#################################### File Pointer Functions ####################################
########################################################################################################################
*/
/*
Func: 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 ( CyString filename )
{
CyString file = filename.FindReplace ( "\\", "/" ).FindReplace ( "//", "/" );
int tok = file.NumToken ( '/' );
m_sFullDir = file.GetToken ( "/", 1, -1 );
m_sName = file.GetToken ( "/", -1 );
if ( m_sFullDir.Right(2) == "/." )
m_sFullDir = m_sFullDir.Left(-2);
ReadFileSize ();
ReadLastModified ();
}
/*
Func: 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(GetFilePointer().c_str());
}
bool C_File::ReadFromFile (CyString filename)
{
FILE *id = fopen ( filename.c_str(), "rb" );
if ( !id )
{
m_iLastError = SPKERR_FILEOPEN;
return false;
}
if ( !m_lSize )
{
fseek ( id, 0, SEEK_END );
m_lSize = ftell ( id );
rewind ( id );
}
m_iDataCompression = SPKCOMPRESS_NONE;
m_lDataSize = m_lUncomprDataSize = m_lSize;
DeleteData ();
m_sData = new unsigned char[m_lSize];
if ( !m_sData ) { fclose ( id ); m_iLastError = SPKERR_MALLOC; return false; }
fread ( m_sData, sizeof(unsigned char), m_lSize, id );
if ( ferror(id) )
{
m_iLastError = SPKERR_FILEREAD;
DeleteData ();
m_lDataSize = 0;
fclose ( id );
return false;
}
m_iLastError = SPKERR_NONE;
struct stat fileStat;
if ( !stat(GetFilePointer().c_str(), &fileStat) )
m_tTime = fileStat.st_atime;
m_bLoaded = true;
fclose ( id );
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;
}
/*
Func: ReadFromFile
Accept: id - File Pointer Stream of open file
size - amount of data to read from file
dosize - Read the 4 character size
Func: Reads a data stream from a currently open file
Can be used to read directly from a SPK Package
dosize will read the initial 4 character uncompressed size if needed
*/
bool C_File::ReadFromFile ( FILE *id, long size, bool dosize )
{
// remove data
m_lDataSize = size ;
m_sData = new unsigned char[m_lDataSize];
if ( dosize )
{
unsigned char s[4];
fread ( s, sizeof(unsigned char), 4, id );
}
fread ( m_sData, sizeof(unsigned char), m_lDataSize, id );
if ( ferror (id) )
{
DeleteData ();
m_lDataSize = 0;
return false;
}
return true;
}
/*
Func: GetFilePointer
Desc: Returns the file pointer name
Joins dir and name together
Works for relative paths as well
*/
CyString C_File::GetFilePointer ()
{
CyString fullfile = m_sFullDir;
if ( !fullfile.Empty() )
fullfile += "/";
if ( !m_sName.Empty() )
fullfile += m_sName;
return fullfile;
}
void C_File::UpdateSignature()
{
m_sSignature = "";
bool deleteData = false;
if ( !m_sData )
{
if ( !ReadFromFile() )
return;
deleteData = true;
}
if ( CheckPCK() )
UnPCKFile();
m_bUpdatedSignature = true;
if ( !m_sData )
return;
size_t fPos = m_lDataSize;
if ( fPos > 700 )
fPos = 700;
unsigned char *data = m_sData + (m_lDataSize - fPos);
data[fPos - 1] = '\0';
CyString sData ((char *)data);
int pos = sData.FindPos("</codearray>", 0);
if ( pos != -1 )
{
sData = sData.Right(sData.Length() - pos);
pos = sData.FindPos("<signature>", 0);
int endpos = sData.FindPos("</signature>", 0);
if ( pos != -1 && endpos != -1 )
{
m_sSignature = sData.Mid(pos + 12, endpos - (pos + 12) + 1);
m_sSignature = m_sSignature.Remove('\n').Remove('\r');
}
}
if ( deleteData )
DeleteData();
}
/*
Func: ReadFileSize()
Return: Returns the file size read
Desc: Opens the file and seeks to the end
*/
long C_File::ReadFileSize ()
{
FILE *id = fopen ( GetFilePointer().c_str(), "rb" );
if ( id )
{
fseek ( id, 0, SEEK_END );
m_lSize = ftell ( id );
fclose ( id );
}
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 ()
{
CyString file = GetFilePointer();
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 ()
{
CyString filename = GetFilePointer();
if ( filename.Empty() )
return false;
FILE *id = fopen ( filename.c_str(), "rb+" );
if ( !id )
return false;
fclose ( id );
return true;
}
bool C_File::ReadSignedFile()
{
if ( (m_iFileType != FILETYPE_SCRIPT) && (m_iFileType != FILETYPE_UNINSTALL) )
return false;
// check file pointer
CyString file = GetFilePointer();
CyString ext = CFileIO(file).GetFileExtension();
if ( !ext.Compare("xml") && !ext.Compare("pck") )
return false;
if ( m_iDataCompression != SPKCOMPRESS_NONE )
return m_bSigned;
// check file extenstion
if ( (ext.Compare("pck")) || (CheckPCK()) )
{
size_t size = 0;
unsigned char *data = UnPCKFile ( &size );
if ( (data) && (size) )
return ::ReadSignedFromData ( data, (long)size );
}
else if ( (m_sData) && (m_iDataCompression == SPKCOMPRESS_NONE) )
return ::ReadSignedFromData ( m_sData, m_lDataSize );
else
{
FILE *id = fopen(file.c_str(), "rb");
if ( id )
{
CyString sRead;
char read[21];
fseek(id, 0, SEEK_END);
int endPos = ftell(id);
int pos = endPos;
// move to end of the file
while ( true )
{
// read blocks of 20 backwards
pos -= 20;
if ( pos < 0 )
pos = 0;
fseek(id, pos, SEEK_SET);
fread(read, sizeof(char), 20, id);
read[20] = '\0';
sRead = CyString(read) + sRead;
// find code array
if ( sRead.FindPos("codearray") != -1 )
break;
if ( pos <= 2 )
break;
}
fclose(id);
// now check the find read
if ( sRead.FindPos("signed") != -1 )
return true;
}
}
return false;
}
int C_File::ReadScriptVersion ()
{
if ( (m_iFileType != FILETYPE_SCRIPT) && (m_iFileType != FILETYPE_UNINSTALL) )
return 0;
// check file pointer
CyString file = GetFilePointer();
// check file extenstion
if ( (CFileIO(file).CheckFileExtension("pck")) || (CheckPCK()) )
{
size_t size = 0;
unsigned char *data = UnPCKFile ( &size );
if ( (data) && (size) )
m_iVersion = ::ReadScriptVersionFromData ( data, (long)size );
}
else if ( (m_sData) && (m_iDataCompression == SPKCOMPRESS_NONE) )
m_iVersion = ::ReadScriptVersionFromData ( m_sData, m_lDataSize );
else
{
FILE *id = fopen ( file.c_str(), "rb+" );
if ( id )
{
fclose ( id );
std::string line;
std::ifstream myfile ( file.c_str() );
if ( myfile.is_open() )
{
bool inscript = false;
while (! myfile.eof() )
{
std::getline ( myfile, line );
while ( line[0] == ' ' )
line.erase ( 0, 1 );
CyString sLine = line;
if ( !inscript )
{
if ( sLine.GetToken ( 1, '>' ) == "<script" )
inscript = true;
}
else
{
if ( sLine.GetToken ( 1, '>' ) == "<version" )
{
m_iVersion = sLine.GetToken ( 2, '>' ).GetToken ( 1, '<' ).ToInt();
break;
}
}
}
myfile.close();
}
}
}
return m_iVersion;
}
bool C_File::MatchFile ( C_File *file )
{
if ( file->GetFileType() != m_iFileType )
return false;
if ( file->GetDir() != m_sDir )
return false;
if ( file->GetName() != m_sName )
return false;
return true;
}
/*
########################################################################################################################
#################################### Compression Functions ####################################
########################################################################################################################
*/
bool C_File::CompressFile ( CProgressInfo *progress )
{
CyString file = this->GetFilePointer();
if ( !CFileIO(this->GetFilePointer()).Exists() )
{
if ( !this->WriteToFile("tempuncompr.dat", m_sData, m_lDataSize) )
return false;
file = "tempuncompr.dat";
}
bool ret = false;
FILE *fIn = fopen(file.c_str(), "rb");
if ( fIn )
{
FILE *fOut = fopen("tempcompr.dat", "wb");
if ( fOut )
{
int err;
if ( progress )
err = zlib_def(fIn, fOut, progress->GetDonePointer());
else
err = zlib_def(fIn, fOut, 0);
fclose(fOut);
if ( err == Z_OK )
{
DeleteData ();
FILE *id = fopen ( "tempcompr.dat", "rb" );
if ( id )
{
fseek ( id, 0, SEEK_END );
m_lDataSize = ftell ( id );
rewind ( id );
m_sData = new unsigned char[m_lDataSize];
if ( !m_sData ) { fclose ( id ); m_iLastError = SPKERR_MALLOC; }
else
{
fread ( m_sData, sizeof(unsigned char), m_lDataSize, id );
if ( ferror(id) )
{
m_iLastError = SPKERR_FILEREAD;
DeleteData ();
m_lDataSize = 0;
}
else
{
ret = true;
m_iLastError = SPKERR_NONE;
m_iDataCompression = SPKCOMPRESS_ZLIB;
}
}
fclose(id);
}
}
}
CFileIO("tempcompr.dat").Remove();
fclose(fIn);
}
CFileIO("tempuncompr.dat").Remove();
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);
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("pck") || this->CheckFileExt("cat") || this->CheckFileExt("dat") || this->CheckFileExt("pbb") || this->CheckFileExt("pbd")) )
compressionType = SPKCOMPRESS_LZMA;
// 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
return true;
}
return false;
}
bool C_File::UncompressData ( CProgressInfo *progress )
{
// no data to try to uncompress
if ( (!m_sData) || (!m_lDataSize) )
return false;
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;
return m_sData;
}
if ( m_iDataCompression == SPKCOMPRESS_ZLIB )
{
unsigned long uncomprLen = m_lUncomprDataSize;
unsigned char *uncompr = new unsigned char[m_lUncomprDataSize];
int err = uncompress ( uncompr, &uncomprLen, (const unsigned char *)m_sData, m_lDataSize );
if ( err == Z_OK )
{
*size = uncomprLen;
return uncompr;
}
if ( uncompr )
delete [] uncompr;
}
if ( m_iDataCompression == SPKCOMPRESS_7ZIP )
{
long len = m_lUncomprDataSize;
unsigned char *compr = LZMADecode_C ( m_sData, m_lDataSize, (size_t *)&len, NULL );
if ( compr )
{
*size = len;
return compr;
}
if ( compr )
delete [] compr;
}
else if ( m_iDataCompression == SPKCOMPRESS_LZMA )
{
size_t uncomprLen = m_lUncomprDataSize;
unsigned char *uncompr = new unsigned char[m_lUncomprDataSize];
SRes res = Lzma86_Decode(uncompr, &uncomprLen, m_sData, (size_t*)&m_lDataSize);
if ( res == SZ_OK )
{
*size = (long)uncomprLen;
return uncompr;
}
if ( uncompr )
delete [] uncompr;
}
return NULL;
}
bool C_File::UncompressToFile ( CyString toFile, CBaseFile *spkfile, bool includedir, CProgressInfo *progress )
{
#ifdef _INCLUDE7ZIP
if ( (!m_sData) || (!m_lDataSize) )
return false;
// if theres a tmp file, open it and check it still exists
if ( !m_sTmpFile.Empty() )
{
FILE *id = fopen ( m_sTmpFile.c_str(), "rb" );
if ( id )
{
fclose ( id );
return true;
}
m_sTmpFile = "";
}
// now uncompress to the file
CyString file = toFile;
if ( file.Empty() )
{
m_iTempNum++;
file = CyString("uncompr") + (long)m_iTempNum + ".tmp";
}
else
file = GetFullFileToDir ( file, includedir, spkfile );
FILE *id = fopen ( "compr.tmp", "wb" );
if ( !id )
return false;
fwrite ( m_sData, sizeof(unsigned char), m_lDataSize, id );
bool ret = false;
int err = ferror(id);
fclose ( id );
if ( !err )
{
if ( LZMADecodeFile ( "compr.tmp", file.c_str(), (CProgressInfo7Zip *)progress ) )
{
ret = true;
if ( toFile.Empty() )
m_sTmpFile = file;
}
}
remove ( "compr.tmp" );
return ret;
#else
return false;
#endif
}
bool C_File::WriteFilePointer ( unsigned char *cData, long len )
{
return WriteToFile ( GetFilePointer(), cData, len );
}
bool C_File::WriteToFile ( CyString filename, unsigned char *cData, long len )
{
unsigned char *data = cData;
if ( (!len) || (!data) )
{
len = m_lDataSize;
data = m_sData;
}
if ( (!len) || (!data) )
return false;
// check for cat file
if ( filename.IsIn ( "::" ) )
{
CyString catfile = filename.GetToken ( "::", 1, 1 );
CyString file = filename.GetToken ( "::", 2, 2 );
CCatFile newcat;
return newcat.AddData ( catfile, data, len, file, true, true );
}
else
{
CyString filen = filename.FindReplace ( "/", "\\" );
filen = filen.FindReplace ( "\\\\", "\\" );
FILE *id = fopen ( filen.c_str(), "wb" );
if ( !id )
return false;
fwrite ( data, sizeof(unsigned char), len, id );
bool ret = true;
if ( ferror(id) )
ret = false;
fclose ( id );
return ret;
}
return false;
}
CyString C_File::GetFullFileToDir ( CyString dir, bool includedir, CBaseFile *file )
{
CyString fullfile = dir;
if ( includedir )
{
CyString d = GetDirectory ( file );
if ( !d.Empty() )
{
if ( !fullfile.Empty() )
fullfile += "/";
fullfile += d;
}
}
if ( !m_sName.Empty() )
{
if ( !fullfile.Empty() )
fullfile += "/";
fullfile += m_sName;
}
fullfile = fullfile.FindReplace ( "\\", "/" );
return fullfile;
}
bool C_File::WriteToDir ( CyString &dir, CBaseFile *spkfile, bool includedir, CyString appendDir, unsigned char *data, long len )
{
CyString fullfile = GetFullFileToDir ( dir, includedir, spkfile );
if ( !appendDir.Empty() )
{
if ( !fullfile.Empty() )
fullfile += "/";
fullfile += appendDir;
}
CyString fulldir = fullfile.GetToken ( 1, fullfile.NumToken('/') - 2, '/' );
if ( !fulldir.Empty() )
{
if ( !CDirIO(fulldir).Create() )
return false;
}
return WriteToFile ( fullfile, data, len );
}
CyString C_File::GetDataSizeString ()
{
return SPK::GetSizeString ( m_lDataSize );
}
CyString C_File::GetUncompressedSizeString ()
{
return SPK::GetSizeString ( GetUncompressedDataSize() );
}
CyString C_File::GetCreationTimeString ()
{
if ( !m_tTime )
return NullString;
struct tm *currDate;
char dateString[100];
time_t n = m_tTime;
currDate = localtime(&n);
strftime(dateString, sizeof dateString, "(%d/%m/%Y) %H:%M", currDate);
return CyString(dateString);
}
bool C_File::CompareNew ( C_File *file )
{
if ( !m_iVersion )
ReadScriptVersion ();
// if version, check if its later 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;
}
CyString GetFileTypeString ( int type )
{
switch ( type )
{
case FILETYPE_SCRIPT:
return "Script";
case FILETYPE_TEXT:
return "Text";
case FILETYPE_README:
return "Readme";
case FILETYPE_MAP:
return "Map";
case FILETYPE_MOD:
return "Mod";
case FILETYPE_UNINSTALL:
return "Uninstall";
case FILETYPE_SOUND:
return "Sound";
case FILETYPE_EXTRA:
return "Extra";
case FILETYPE_SCREEN:
return "Screen";
case FILETYPE_ADVERT:
return "Advert";
case FILETYPE_MISSION:
return "Mission";
case FILETYPE_BACKUP:
return "Backup";
case FILETYPE_SHIPOTHER:
return "ShipOther";
case FILETYPE_SHIPMODEL:
return "ShipModel";
case FILETYPE_SHIPSCENE:
return "ShipScene";
case FILETYPE_COCKPITSCENE:
return "CockpitScene";
}
return NullString;
}
int GetFileTypeFromString ( CyString type )
{
CyString ltype = type.ToLower();
if ( ltype == "script" )
return FILETYPE_SCRIPT;
else if ( ltype == "text" )
return FILETYPE_TEXT;
else if ( ltype == "readme" )
return FILETYPE_README;
else if ( ltype == "map" )
return FILETYPE_MAP;
else if ( ltype == "mod" )
return FILETYPE_MOD;
else if ( ltype == "uninstall" )
return FILETYPE_UNINSTALL;
else if ( ltype == "sound" )
return FILETYPE_SOUND;
else if ( ltype == "extra" )
return FILETYPE_EXTRA;
else if ( ltype == "screen" )
return FILETYPE_SCREEN;
else if ( ltype == "advert" )
return FILETYPE_ADVERT;
else if ( ltype == "mission" )
return FILETYPE_MISSION;
else if ( ltype == "backup" )
return FILETYPE_BACKUP;
else if ( ltype == "shipother" )
return FILETYPE_SHIPOTHER;
else if ( ltype == "shipmodel" )
return FILETYPE_SHIPMODEL;
else if ( ltype == "shipscene" )
return FILETYPE_SHIPSCENE;
else if ( ltype == "cockpitscene" )
return FILETYPE_COCKPITSCENE;
return -1;
}
CyString FormatErrorString(int error, CyString rest)
{
int max = 0;
CyString *args = 0;
if ( !rest.Empty() )
args = rest.SplitToken('~', &max);
CyString errorStr;
switch ( error )
{
case SPKINSTALL_CREATEDIRECTORY:
errorStr = "Creating Directory: %1";
break;
case SPKINSTALL_CREATEDIRECTORY_FAIL:
errorStr = "Unable to Creating Directory: %1";
break;
case SPKINSTALL_WRITEFILE:
errorStr = "Writing File: %1";
break;
case SPKINSTALL_WRITEFILE_FAIL:
errorStr = "Unable to Write File: %1";
break;
case SPKINSTALL_DELETEFILE:
errorStr = "Deleting File: %1";
break;
case SPKINSTALL_DELETEFILE_FAIL:
errorStr = "Unable to delete File: %1";
break;
case SPKINSTALL_REMOVEDIR:
errorStr = "Removing Directory: %1";
break;
case SPKINSTALL_SKIPFILE:
errorStr = "Skipping File (older): %1";
break;
case SPKINSTALL_ENABLEFILE:
errorStr = "Enabled File: %1";
break;
case SPKINSTALL_DISABLEFILE:
errorStr = "Disabled File: %1";
break;
case SPKINSTALL_ENABLEFILE_FAIL:
errorStr = "Failed to enable File: %1";
break;
case SPKINSTALL_DISABLEFILE_FAIL:
errorStr = "Failed to disable File: %1";
break;
case SPKINSTALL_UNINSTALL_MOVE:
errorStr = "Moving uninstall file: %1";
break;
case SPKINSTALL_UNINSTALL_MOVE_FAIL:
errorStr = "Unable to Move uninstall file: %1";
break;
case SPKINSTALL_UNINSTALL_COPY:
errorStr = "Coping uninstall file: %1";
break;
case SPKINSTALL_UNINSTALL_COPY_FAIL:
errorStr = "Unable to Copy uninstall file: %1";
break;
case SPKINSTALL_UNINSTALL_REMOVE:
errorStr = "Removing Uninstall file: %1";
break;
case SPKINSTALL_UNINSTALL_REMOVE_FAIL:
errorStr = "Unable to remove Uninstall file: %1";
break;
case SPKINSTALL_SHARED:
errorStr = "Removing Unused Shared file: %1";
break;
case SPKINSTALL_SHARED_FAIL:
errorStr = "Unable to remove Unused Shared file: %1";
break;
case SPKINSTALL_ORIGINAL_BACKUP:
errorStr = "Backing up original file: %1";
break;
case SPKINSTALL_ORIGINAL_BACKUP_FAIL:
errorStr = "Unable to back up original file: %1";
break;
case SPKINSTALL_ORIGINAL_RESTORE:
errorStr = "Restoring original file: %1";
break;
case SPKINSTALL_ORIGINAL_RESTORE_FAIL:
errorStr = "Unable to restore original file: %1";
break;
case SPKINSTALL_FAKEPATCH:
errorStr = "Shifted fake patch: %1 to %2";
break;
case SPKINSTALL_FAKEPATCH_FAIL:
errorStr = "Unable to shift fake patch: %1 to %2";
break;
case SPKINSTALL_AUTOTEXT:
errorStr = "Shifted text file: %1 to %2";
break;
case SPKINSTALL_AUTOTEXT_FAIL:
errorStr = "Unable to shift text file: %1 to %2";
break;
case SPKINSTALL_MISSINGFILE:
errorStr = "File is missing: %1";
break;
case SPKINSTALL_ORPHANED:
errorStr = "Orphaned File removed: %1";
break;
case SPKINSTALL_ORPHANED_FAIL:
errorStr = "Unable to remove Orphaned File: %1";
break;
}
CyString ret = errorStr.Args(args, max);
CLEANSPLIT(args, max)
return ret;
}
bool C_File::CheckPCK ()
{
if ( (m_sData) && (m_lDataSize) && (m_iDataCompression == SPKCOMPRESS_NONE) )
return IsDataPCK ( m_sData, m_lDataSize );
CyString filename = GetFilePointer();
if ( !filename.Empty() )
{
FILE *id = fopen ( filename.c_str(), "rb+" );
if ( id )
{
unsigned char data[3];
fread ( data, sizeof(unsigned char), 3, id );
fclose ( id );
return IsDataPCK ( data, 3 );
}
}
if ( CheckFileExt("pck") || CheckFileExt("pbb") || CheckFileExt("pbd") )
return true;
return false;
}
bool C_File::PCKFile()
{
if ( !m_lDataSize || !m_sData )
{
if ( !this->ReadFromFile() )
return false;
}
if ( m_lDataSize && m_sData )
{
if ( !this->UncompressData() )
return false;
size_t size;
unsigned char *data = PCKData(m_sData, m_lDataSize, &size);
if ( data && size )
{
this->DeleteData();
m_lUncomprDataSize = m_lDataSize;
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() )
{
FILE *id = fopen ( GetFilePointer().c_str(), "rb+" );
if ( id )
{
fseek ( id, 0, SEEK_END );
datasize = ftell ( id );
rewind ( id );
data = new unsigned char[datasize];
fread ( data, sizeof(unsigned char), datasize, id );
fclose ( id );
}
}
if ( !data )
{
if ( !m_lDataSize )
{
if ( !this->ReadFromFile() )
return NULL;
}
datasize = m_lDataSize;
data = new unsigned char[datasize];
memcpy ( data, m_sData, datasize );
}
if ( data )
{
unsigned char *newdata = UnPCKData ( data, datasize, len, CheckPCK() );
delete data;
return newdata;
}
return NULL;
}
bool C_File::UnPCKFile()
{
if ( !m_lDataSize || !m_sData )
{
if ( !this->ReadFromFile() )
return false;
}
if ( m_lDataSize && m_sData )
{
if ( !this->UncompressData() )
return false;
size_t size;
unsigned char *data = UnPCKData(m_sData, m_lDataSize, &size, CheckPCK());
if ( data && size )
{
this->DeleteData();
m_lUncomprDataSize = m_lDataSize;
m_bUsedMalloc = false;
m_sData = new unsigned char[size];
memcpy(m_sData, data, size);
m_lDataSize = (long)size;
delete []data;
}
return true;
}
return false;
}
unsigned char *UnPCKFile ( const char *file, size_t *len, bool nocrypt )
{
FILE *id = fopen ( file, "rb" );
if ( !id )
return NULL;
fseek ( id, 0, SEEK_END );
size_t size = ftell ( id );
fseek ( id, 0, SEEK_SET );
unsigned char *data = new unsigned char[size];
fread ( data, sizeof(unsigned char), size, id );
if ( ferror(id) )
{
delete data;
data = NULL;
}
fclose ( id );
if ( data )
{
unsigned char *unData = UnPCKData ( data, size, len, nocrypt );
delete data;
return unData;
}
return NULL;
}
unsigned char *UnPCKData ( unsigned char *data, size_t datasize, size_t *len ) { return UnPCKData(data, datasize, len, IsDataPCK(data, datasize)); }
unsigned char *UnPCKData ( unsigned char *data, size_t datasize, size_t *len, bool nocrypt )
{
//IsDataPCK(data, datasize)
if ( nocrypt )
{
unsigned char magic = data[0] ^ 0xC8;
for ( size_t i = 1; i < datasize; i++ )
data[i] ^= magic;
++data;
--datasize;
}
// create data buffer
size_t *uncomprLenSize = (size_t*)(data + (datasize - 4));
unsigned long uncomprLen = (unsigned long)*uncomprLenSize;
if ( uncomprLen > (datasize * 100) )
{
*len = 0;
return NULL;
}
unsigned char *uncompr = new unsigned char[uncomprLen];
if ( !uncompr )
return NULL;
memset ( uncompr, 0, sizeof(uncompr) );
// find header size
unsigned char *buf = data + PCKHEADERSIZE;
// buf = data + (6 + sizeof(time_t));
char flag = data[3];
if ( flag & GZ_FLAG_EXTRA )
{
size_t xlen = *((short int*)(buf));
buf += xlen;
}
if ( flag & GZ_FLAG_FILENAME )
{
char *origname = (char*)(buf);
buf += strlen (origname) + 1;
}
if ( flag & GZ_FLAG_COMMENT )
{
char *comment = (char*)(buf);
buf += strlen(comment) + 1;
}
if ( flag & GZ_FLAG_HCRC )
buf += 2;
long bufSize = (long)(datasize - (buf-data) - 8);
int err = uncompress2 ( uncompr, &uncomprLen, buf, bufSize );
if ( err != Z_OK )
{
delete uncompr;
*len = 0;
return NULL;
}
*len = uncomprLen;
uncompr[uncomprLen - 1] = '\0';
return uncompr;
}
bool IsDataPCK ( const unsigned char *data, size_t size )
{
if ( size >=3 )
{
unsigned char magic=data[0] ^ 0xC8;
return ((data[1] ^ magic)==0x1F && (data[2] ^ magic)==0x8B);
}
else
return false;
}
bool ReadSignedFromData ( unsigned char *data, long size )
{
if ( IsDataPCK ( data, size ) )
{
size_t unpckSize = 0;
unsigned char *unpckData = UnPCKData ( data, size, &unpckSize, false );
if ( (unpckData) && (unpckSize) )
return ReadSignedFromData ( unpckData, (long)unpckSize );
return false;
}
// work backwards
int pos = size - 1;
// find the first tage
while ( pos > 0 )
{
while ( data[pos] != '>' && pos > 0 )
pos--;
char tag[100];
bool checked = false;
if ( data[pos] == '>' )
{
int pos2 = pos - 1;
// now find the front
while ( data[pos2] != '<' && pos2 > 0 )
pos2--;
// now get the tag
if ( data[pos2] == '<' )
{
checked = true;
memcpy(tag, data + pos2, pos - pos2);
tag[pos - pos2] = '\0';
// check if tag is signed
CyString sTag(tag);
if ( sTag.Compare("</signature") || sTag.Compare("<signature") )
return true;
// check if tag is codearray
if ( sTag.Compare("</codearray") || sTag.Compare("<codearray") )
return false;
pos = pos2;
}
--pos;
}
if ( !checked )
break;
}
return false;
}
int ReadScriptVersionFromData ( unsigned char *data, long size )
{
if ( IsDataPCK ( data, size ) )
{
size_t unpckSize = 0;
unsigned char *unpckData = UnPCKData ( data, size, &unpckSize );
if ( (unpckData) && (unpckSize) )
return ReadScriptVersionFromData ( unpckData, (long)unpckSize );
return 0;
}
int pos = 0;
bool found = false;
int iVersion = 0;
// skip past initial space
while ( !found )
{
while ( (pos < size) && (data[pos] == ' ') )
++pos;
if ( data[pos] == '<' )
{
CyString checktag;
while ( (pos < size) && (data[pos] != '>') )
{
checktag += (char)data[pos];
++pos;
}
++pos;
if ( checktag == "<version" )
{
CyString version;
while ( (pos < size) && (data[pos] != '<') )
{
version += (char)data[pos];
++pos;
}
iVersion = version.ToInt();
found = true;
break;
}
}
if ( found )
break;
// skip to next line
while ( (pos < size) && (data[pos] != '\n') )
++pos;
++pos;
if ( pos >= size )
break;
}
return iVersion;
}
CyString C_File::GetBaseName ()
{
// remove any directory
CyString file = m_sName.GetToken ( "/", m_sName.NumToken ( '/' ) );
// remove file extension
file = file.GetToken ( ".", 1, file.NumToken ( '.' ) - 1 );
return file;
}
void C_File::CopyData(C_File *oldFile, bool includeData)
{
SetFilename(oldFile->GetFullFilename());
m_sDir = oldFile->GetDir();
m_tTime = oldFile->GetCreationTime();
m_bShared = oldFile->IsShared();
m_bSigned = oldFile->IsSigned();
m_iFileType = oldFile->GetFileType();
m_lSize = oldFile->GetSize();
if ( includeData )
{
m_lDataSize = oldFile->GetDataSize();
if ( m_sData )
this->DeleteData();
m_sData = NULL;
if ( oldFile->GetData() && m_lDataSize )
{
m_sData = new unsigned char[m_lDataSize];
m_bUsedMalloc = false;
memcpy((char *)m_sData, (char *)oldFile->GetData(), m_lDataSize);
}
m_lUncomprDataSize = oldFile->GetUncompressedDataSize();
}
m_iDataCompression = oldFile->GetCompressionType();
}
float GetLibraryVersion() { return (float)LIBRARYVERSION; }
float GetFileFormatVersion() { return (float)FILEVERSION; }
CyString C_File::ChangeFileExt(CyString ext)
{
m_sName = CFileIO(m_sName).ChangeFileExtension(ext);
return m_sName;
}
bool C_File::CheckPackedExtension()
{
CyString ext = this->GetFileExt();
if ( ext == "pck" )
return true;
else if ( ext == "pbb" )
return true;
else if ( ext == "pbd" )
return true;
return false;
}
bool C_File::BobDecompile()
{
bool bRes=false;
CyStringList outData;
outData.PushBack("// Converted by SPK Libraries\n");
/*
bob_with_errors *e=0;
char *pszTime;
char *name="";
time_t tm=time(0);
pszTime=ctime(&tm);
pszTime[strlen(pszTime)-1]=0;
clearErrors();
os << "// Converted with x2bc from \"" << is.name() << "\" at " << pszTime << endl;
if(settings->convert()==false)
os << "// Raw mode - values are not converted" << endl;
os << endl;
*/
/*
switch(m_sData[0]){
case bob_dom_bob::hdr_begin:
{
bob_dom_bob *bob = new bob_dom_bob(NULL);
//bRes=bob->load(is);
//if(bRes) bRes=bob->toFile(os);
delete bob;
break;
}
case bob_dom_cut::hdr_begin:
{
bob_dom_cut *cut = new bob_dom_cut(NULL);
//bRes=cut->convert(is, os);
delete cut;
break;
}
default:
return false;
}
if(e){
for(bob_with_errors::ErrorIterator &it=e->errors.begin(); it!=e->errors.end(); ++it){
bob_error(it->severity, it->code, "%s->%s", name, it->text);
}
}
return bRes;
bob_dom_ibufferstream<unsigned char> is((const unsigned char *)m_sData, m_lDataSize);
bob_dom_otextrealfile os;
if(is.fail() || os.fail()) return false;
bool bRes=doc.convert(is, os);*/
return bRes;
}
bool C_File::BodCompile()
{
return true;
}
bool C_File::RenameScript(CyString baseName)
{
if ( !m_sData || !m_lDataSize )
{
if ( !this->ReadFromFile() )
return false;
}
// uncompress the file
if ( !this->UncompressData() )
return false;
// un pck the file
if ( this->CheckFileExt("pck") )
{
if ( !this->UnPCKFile() )
return false;
}
// now we should have the raw data
CyString data((const char *)m_sData);
data.Truncate(m_lDataSize);
data = data.FindReplace(this->GetBaseName(), baseName);
this->DeleteData();
m_sData = new unsigned char[data.Length()];
memcpy(m_sData, data.c_str(), data.Length());
m_lDataSize = (long)data.Length();
// repck the file
if ( this->CheckFileExt("pck") )
{
if ( !this->PCKFile() )
return false;
}
return true;
}