Rev 219 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "MultiSpkFile.h"
#include "File.h"
#include "XspFile.h"
CMultiSpkFile::~CMultiSpkFile()
{
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *ms = node->Data();
if ( !ms )
continue;
if ( ms->sData )
{
delete ms->sData;
ms->sData = NULL;
}
if ( ms->pFile )
{
delete ms->pFile;
ms->pFile = NULL;
}
node->DeleteData();
}
}
void CMultiSpkFile::CreatePackage(SMultiSpkFile *p)
{
if ( !p )
return;
if ( p->iType == TYPE_XSP )
p->pFile = new CXspFile();
else if ( p->iType == TYPE_SPK )
p->pFile = new CSpkFile();
else if ( p->iType == TYPE_BASE )
p->pFile = new CBaseFile();
}
bool CMultiSpkFile::addFileNow(const Utils::WString &file, bool on )
{
if ( this->addFile(file, on) )
{
Utils::WString findFile = CFileIO(file).filename();
const SMultiSpkFile *p = this->findFile(findFile);
if ( p )
{
this->CreatePackage(const_cast<SMultiSpkFile*>(p));
if ( p->pFile->readFile(file, SPKREAD_ALL))
return true;
}
}
return false;
}
bool CMultiSpkFile::addFile(SMultiSpkFile *ms)
{
bool found = false;
// find if theres any files with the same filename
CListNode<SMultiSpkFile> *node;
for ( node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *it = node->Data();
if ( it->sName.Compare(ms->sName) )
{
m_lFiles.remove ( it );
found = true;
delete it;
break;
}
}
// now find if theres a file with the same script/author
for ( node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *it = node->Data();
if ( (it->sScriptAuthor.Compare(ms->sScriptAuthor)) && (it->sScriptName.Compare(ms->sScriptName)) )
{
m_lFiles.remove ( it );
found = true;
delete it;
break;
}
}
m_lFiles.push_back ( ms );
m_bChanged = true;
return true;
}
bool CMultiSpkFile::addFile(const Utils::WString &file, bool on)
{
// multipack
if (file.contains(L"::" ))
{
Utils::WString mfile = file.left(file.findPos(L"::"));
CMultiSpkFile spkfile;
if ( spkfile.readFile(mfile, false))
{
SMultiSpkFile *ms = new SMultiSpkFile;
ms->bRemove = false;
ms->sName = file.right((int)(file.length() - file.findPos(L"::")) - 2 );
ms->pFile = NULL;
ms->sData = NULL;
if ( spkfile.extractData(ms))
{
ms->bOn = on;
addFile(ms);
return true;
}
delete ms;
}
return false;
}
// check its a valid file
int check = CBaseFile::CheckFile(file);
if ( check == SPKFILE_MULTI )
{
CMultiSpkFile spkfile;
if ( spkfile.readFile(file, false))
{
CLinkList<SMultiSpkFile> *list = spkfile.GetFileList();
for ( SMultiSpkFile *it = list->First(); it; it = list->Next() )
{
SMultiSpkFile *ms = new SMultiSpkFile;
ms->bRemove = false;
ms->sName = it->sName;
ms->pFile = NULL;
ms->sData = NULL;
if (spkfile.extractData(ms))
{
ms->bOn = on;
addFile(ms);
}
else
delete ms;
}
return true;
}
return false;
}
else if ( check != SPKFILE_SINGLE && check != SPKFILE_SINGLESHIP )
return false;
CFileIO File(file);
if ( !File.startRead() ) return false;
// create entry
SMultiSpkFile *ms = new SMultiSpkFile;
ms->bRemove = false;
ms->pFile = NULL;
ms->sName = CFileIO(file).filename();
ms->bOn = on;
// read data
ms->sData = (char *)File.readAll((size_t *)&ms->lSize);
File.close();
CBaseFile *baseFile = NULL;
if ( check == SPKFILE_SINGLE )
baseFile = new CSpkFile;
else if ( check == SPKFILE_SINGLESHIP )
baseFile = new CXspFile;
if ( baseFile ) {
if ( baseFile->readFile(file, SPKREAD_VALUES)) {
ms->sScriptAuthor = baseFile->author();
ms->sScriptName = baseFile->name();
ms->sScriptVersion = baseFile->version();
ms->iType = baseFile->GetType();
}
delete baseFile;
}
addFile(ms);
return true;
}
SMultiSpkFile *CMultiSpkFile::addFileEntry(const Utils::WString &filename)
{
SMultiSpkFile *ms = new SMultiSpkFile;
ms->bRemove = false;
ms->pFile = NULL;
ms->sName = filename;
ms->bOn = true;
ms->iType = -1;
m_lFiles.push_back ( ms );
m_bChanged = true;
return ms;
}
bool CMultiSpkFile::writeFile(const Utils::WString &filename, CProgressInfo *progress)
{
CFileIO File(filename);
if ( !File.startWrite() ) return false;
// make sure we remove all packages that have been marked
this->RemovePackages();
Utils::WString data = _createData();
int comprLen = (int)((data.length() * sizeof(wchar_t)) / sizeof(char)), uncomprLen = comprLen;;
unsigned char *compr = NULL;
bool compressed = false;
int valueheader = m_SHeader.iCompression;
if ( valueheader == SPKCOMPRESS_7ZIP )
valueheader = SPKCOMPRESS_ZLIB;
if ( valueheader == SPKCOMPRESS_ZLIB )
{
comprLen = uncomprLen;
if ( comprLen < 100 )
comprLen = 200;
else if ( comprLen < 1000 )
comprLen *= 2;
compr = (unsigned char *)calloc((unsigned int)comprLen, 1);
int err = compress ( (unsigned char *)compr, (unsigned long *)&comprLen, (const unsigned char *)data.c_str(), (unsigned long)uncomprLen, 0 );
if ( err == Z_OK )
compressed = true;
}
if ( !compressed )
{
comprLen = uncomprLen;
compr = (unsigned char *)calloc((unsigned int)comprLen, 1);
memcpy ( compr, (const unsigned char *)data.c_str(), comprLen );
valueheader = SPKCOMPRESS_NONE;
}
// write the main header to the file
if ( !File.write("MSPKCycrow;%.2f;%d;%d;%d;%d\n", FILEVERSION, valueheader, data.length(), comprLen, (m_SHeader.bSelection) ? 1 : 0) ) return false;
// write the compressed data to file
File.writeSize(uncomprLen);
File.write(compr, comprLen);
free ( compr );
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() ) {
SMultiSpkFile *ms = node->Data();
if ( progress )
progress->UpdatePackage(ms);
if ( (!ms->sData) && (ms->pFile) )
ms->pFile->writeData(File, NULL);
else
File.write(ms->sData, ms->lSize);
}
return true;
}
void CMultiSpkFile::RemovePackages()
{
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
if ( !node->Data()->bRemove ) continue;
node->DeleteData();
}
m_lFiles.RemoveEmpty();
}
bool CMultiSpkFile::readFile(const Utils::WString &filename, bool bReadData)
{
CFileIO File(filename);
if ( !File.startRead() ) return false;
// first read the header
if ( !_parseHeader(File.readEndOfLineStr()) ) return false;
ClearFiles ();
int doneLen = 0;
// next read the data values for the spk files
if ( m_SHeader.lComprLen )
{
// read data to memory
unsigned long uncomprLen = File.readSize();
unsigned char *readData = File.read(m_SHeader.lComprLen);
// check for zlib compression
if ( m_SHeader.iCompression == SPKCOMPRESS_ZLIB ) {
// uncomress the data
unsigned char *uncompr = new unsigned char[uncomprLen];
int err = uncompress ( uncompr, &uncomprLen, readData, m_SHeader.lComprLen );
if (err == Z_OK)
{
if (m_SHeader.fVersion < 4.4f)
{
std::string data((char*)uncompr);
_readValues(std::wstring(data.begin(), data.end()));
}
else
{
const wchar_t* data = (const wchar_t*)uncompr;
_readValues(Utils::WString(data));
}
}
doneLen = uncomprLen;
delete uncompr;
}
else if ( m_SHeader.iCompression == SPKCOMPRESS_7ZIP ) {
long len = uncomprLen;
unsigned char *compr = LZMADecode_C ( readData, m_SHeader.lComprLen, (size_t *)&len, NULL );
if (compr)
{
if (m_SHeader.fVersion < 4.4f)
{
std::string data((char*)compr);
_readValues(std::wstring(data.begin(), data.end()));
}
else
{
const wchar_t* data = (const wchar_t*)compr;
_readValues(Utils::WString(data));
}
}
}
// no compression
else
{
if (m_SHeader.fVersion < 4.4f)
{
std::string data((char*)readData);
_readValues(std::wstring(data.begin(), data.end()));
}
else
{
const wchar_t* data = (const wchar_t*)readData;
_readValues(Utils::WString(data));
}
}
delete readData;
}
if ( bReadData ) {
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() ) {
SMultiSpkFile *ms = node->Data();
ms->sData = (char *)File.read(ms->lSize);
}
}
_sFilename = filename;
File.close();
m_bChanged = false;
return true;
}
unsigned int CMultiSpkFile::GetAvailableFiles()
{
unsigned int size = 0;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
if ( node->Data()->bRemove ) continue;
++size;
}
return size;
}
unsigned long CMultiSpkFile::GetFileSize()
{
unsigned int size = 0;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
if ( node->Data()->bRemove ) continue;
size += node->Data()->lSize;
}
return size;
}
void CMultiSpkFile::ClearFiles ()
{
m_lFiles.clear(true);
}
bool CMultiSpkFile::extractData(SMultiSpkFile *ms)
{
FILE *id = _wfopen (_sFilename.c_str(), L"rb");
if ( !id )
return false;
// skip past the header
CSpkFile::GetEndOfLine ( id, NULL, false );
// seek past the values
fseek ( id, 4, SEEK_CUR );
fseek ( id, m_SHeader.lComprLen, SEEK_CUR );
bool found = false;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *it = node->Data();
if ( ms->sName.Compare(it->sName) )
{
ms->lSize = it->lSize;
ms->sScriptAuthor = it->sScriptAuthor;
ms->sScriptName = it->sScriptName;
ms->sScriptVersion = it->sScriptVersion;
ms->bOn = it->bOn;
ms->iType = it->iType;
ms->sData = new char[ms->lSize];
if ( it->sData )
memcpy ( ms->sData, it->sData, ms->lSize );
else
fread ( ms->sData, sizeof(char), ms->lSize, id );
found = true;
break;
}
else
fseek ( id, it->lSize, SEEK_CUR );
}
fclose ( id );
return found;
}
bool CMultiSpkFile::ReadFileToMemory ( SMultiSpkFile *ms )
{
if ( ms->sData )
return true;
FILE *id = _wfopen(_sFilename.c_str(), L"rb");
if ( !id )
return false;
// skip past the header
CSpkFile::GetEndOfLine ( id, NULL, false );
// seek past the values
fseek ( id, 4, SEEK_CUR );
fseek ( id, m_SHeader.lComprLen, SEEK_CUR );
bool found = false;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *it = node->Data();
if ( it == ms )
{
ms->sData = new char[ms->lSize];
fread ( ms->sData, sizeof(char), ms->lSize, id );
found = true;
break;
}
else
fseek ( id, it->lSize, SEEK_CUR );
}
fclose ( id );
return found;
}
bool CMultiSpkFile::extractFile(const Utils::WString &file, const Utils::WString &dir)
{
const SMultiSpkFile *ms = findFile(file);
if ( !ms ) return false;
return extractFile(ms, dir);
}
bool CMultiSpkFile::extractFile(const SMultiSpkFile *ms, const Utils::WString &dir)
{
if ( !ms->sData )
{
if (!ReadFileToMemory(const_cast<SMultiSpkFile *>(ms)))
return false;
}
Utils::WString filename = dir;
if (!dir.empty())
filename += L"/";
filename += ms->sName;
FILE *id = _wfopen(filename.c_str(), L"wb");
if ( !id )
return false;
if(ms->sData)
fwrite(ms->sData, sizeof(char), ms->lSize, id );
fclose ( id );
return true;
}
bool CMultiSpkFile::extractAll(const Utils::WString &dir)
{
if (_sFilename.empty())
return false;
if ( !ReadAllFilesToMemory () )
return false;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *ms = node->Data();
if ( ms->bRemove ) continue;
if ( !ms->sData )
continue;
FILE *id = _wfopen((dir + L"/" + ms->sName).c_str(), L"wb" );
if ( !id )
continue;
fwrite ( ms->sData, sizeof(char), ms->lSize, id );
fclose ( id );
}
return true;
}
bool CMultiSpkFile::splitMulti(const Utils::WString &filename, const Utils::WString &destdir)
{
if ( !readFile(filename))
return false;
bool doneone = false;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *ms = node->Data();
if ( ms->bRemove ) continue;
if ( !ms->sData )
continue;
Utils::WString destfile = destdir;
if ( !destfile.empty() )
destfile += L"/";
destfile += ms->sName;
FILE *id = _wfopen(destfile.c_str(), L"wb");
if ( !id )
continue;
fwrite ( ms->sData, sizeof(char), ms->lSize, id );
fclose ( id );
doneone = true;
}
return doneone;
}
bool CMultiSpkFile::ReadAllFilesToMemory (CProgressInfo *progress)
{
// no file to read from
if (_sFilename.empty())
return false;
// now open the file
FILE *id = _wfopen(_sFilename.c_str(), L"rb");
if ( !id )
return false;
// read the header
CSpkFile::GetEndOfLine ( id, NULL, false );
// skip past values
fseek ( id, 4, SEEK_CUR );
fseek ( id, m_SHeader.lComprLen, SEEK_CUR );
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *ms = node->Data();
if ( ms->bRemove ) continue;
if ( progress )
progress->UpdatePackage(ms);
if ( !ms->sData )
{
ms->sData = new char[ms->lSize];
fread ( ms->sData, sizeof(char), ms->lSize, id );
}
else
fseek ( id, ms->lSize, SEEK_CUR );
}
return true;
}
bool CMultiSpkFile::removeFile(const Utils::WString &file)
{
int num = 0;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *it = node->Data();
if ( file.Compare(it->sName) )
return removeFile(num);
++num;
}
return false;
}
bool CMultiSpkFile::removeFile(const SMultiSpkFile *ms)
{
int num = 0;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *it = node->Data();
if ( it == ms )
return removeFile(num);
++num;
}
return false;
}
bool CMultiSpkFile::removeFile(int id)
{
if ( (id < 0) || (id >= m_lFiles.size()) )
return false;
SMultiSpkFile *file = m_lFiles.Get ( id );
m_lFiles.erase ( id + 1 );
if ( file )
delete file;
m_bChanged = true;
return true;
}
bool CMultiSpkFile::markRemoveFile(const Utils::WString &file)
{
int num = 0;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *it = node->Data();
if ( file.Compare(it->sName) )
return markRemoveFile(num);
num++;
}
return false;
}
bool CMultiSpkFile::markRemoveFile(SMultiSpkFile *ms)
{
int num = 0;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *it = node->Data();
if ( it == ms )
return markRemoveFile(num);
++num;
}
return false;
}
bool CMultiSpkFile::markRemoveFile ( int id )
{
if ( (id < 0) || (id >= m_lFiles.size()) )
return false;
SMultiSpkFile *file = m_lFiles.Get ( id );
file->bRemove = true;
m_bChanged = true;
return true;
}
void CMultiSpkFile::markRemoveAll()
{
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
node->Data()->bRemove = true;
m_bChanged = true;
}
bool CMultiSpkFile::ReadSpk( SMultiSpkFile *ms, int type )
{
if ( ms->pFile->IsFullyLoaded() )
return true;
// no file to read from
if (_sFilename.empty())
return false;
// now open the file
CFileIO File(_sFilename);
if ( !File.startRead() ) return false;
// read the header
File.readEndOfLineStr();
// skip past values
File.seek(4 + m_SHeader.lComprLen);
bool ret = false;
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() ) {
SMultiSpkFile *it = node->Data();
if ( it == ms )
{
if ( !ms->pFile )
{
if ( ms->iType == TYPE_XSP )
ms->pFile = new CXspFile;
else
ms->pFile = new CSpkFile;
}
ret = it->pFile->readFile(File, type, NULL);
break;
}
else
File.seek(it->lSize);
}
File.close();
return ret;
}
bool CMultiSpkFile::readAllPackages( int type, CLinkList<CBaseFile> *addToList )
{
// no file to read from
if (_sFilename.empty())
return false;
// now open the file
CFileIO File(_sFilename);
if ( !File.startRead() ) return false;
// read the header
File.readEndOfLineStr();
// skip past values
File.seek(4 + m_SHeader.lComprLen);
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() ) {
SMultiSpkFile *ms = node->Data();
CBaseFile *package = NULL;
if ( ms->iType == TYPE_XSP )
package = new CXspFile;
else
package = new CSpkFile;
if ( !addToList ) {
if ( ms->pFile ) {
delete package;
package = ms->pFile;
}
}
if ( !package->IsFullyLoaded() ) {
long tell = File.position();
package->readFile(File, type, NULL);
if ( addToList ) {
addToList->push_back(package);
package->SetEnabled(ms->bOn);
}
else if ( package )
ms->pFile = package;
// move to correct position in file for next stream of data
// should be fine, this is more of a failsafe
File.seekStart(tell);
}
File.seek(ms->lSize);
}
File.close();
return true;
}
const SMultiSpkFile *CMultiSpkFile::findFile(const Utils::WString &name) const
{
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *ms = node->Data();
if ( ms->sName.Compare(name) )
return ms;
}
return NULL;
}
void CMultiSpkFile::UpdatedPackage(CBaseFile *p)
{
SMultiSpkFile *s = this->findPackage(p);
if ( s )
{
if ( s->sData )
delete s->sData;
s->sData = (char *)p->createData((size_t*)&s->lSize);
}
}
SMultiSpkFile *CMultiSpkFile::findPackage(CBaseFile *p)
{
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *ms = node->Data();
if ( ms->pFile == p )
return ms;
}
return NULL;
}
SMultiSpkFile *CMultiSpkFile::findPackage(const Utils::WString &name, const Utils::WString &author)
{
for ( CListNode<SMultiSpkFile> *node = m_lFiles.Front(); node; node = node->next() )
{
SMultiSpkFile *ms = node->Data();
if ( !ms->pFile )
continue;
if (ms->pFile->name().Compare(name) && ms->pFile->author().Compare(author))
return ms;
}
return NULL;
}
Utils::WString CMultiSpkFile::_createData() const
{
Utils::WString ret;
if (!_sName.empty())
ret = L"Name: " + _sName + L"\n";
for (CListNode<SMultiSpkFile>* node = m_lFiles.Front(); node; node = node->next())
{
SMultiSpkFile* ms = node->Data();
if (ms->iType == TYPE_XSP)
ret += L"XspFile: ";
else
ret += L"SpkFile: ";
if (ms->bOn)
ret += L"1:";
else
ret += L"0:";
ret += ms->lSize;
ret += L":";
ret += ms->sName;
if (!ms->sScriptName.empty())
ret += L":" + ms->sScriptName + L"|" + ms->sScriptAuthor + L"|" + ms->sScriptVersion;
ret += L"\n";
}
return ret;
}
bool CMultiSpkFile::_parseHeader(const Utils::WString &header)
{
if (header.token(L";", 1) != L"MSPKCycrow")
return false;
m_SHeader.fVersion = header.token(L";", 2).toFloat();
if (m_SHeader.fVersion > FILEVERSION)
return false;
m_SHeader.iCompression = header.token(L";", 3).toInt();
m_SHeader.lUncomprLen = header.token(L";", 4).toLong();
m_SHeader.lComprLen = header.token(L";", 5).toLong();
m_SHeader.bSelection = (header.token(L";", 4).toInt()) ? true : false;
return true;
}
bool CMultiSpkFile::_parseValueLine(const Utils::WString &line)
{
Utils::WString first = line.token(L" ", 1);
Utils::WString rest = line.tokens(L" ", 2);
if (first == L"Name:")
_sName = rest;
else if (first == L"SpkFile:")
{
SMultiSpkFile* ms = new SMultiSpkFile;
ms->bRemove = false;
ms->pFile = NULL;
ms->bOn = (rest.token(L":", 1).toInt()) ? true : false;
ms->lSize = rest.token(L":", 2).toLong();
ms->sName = rest.token(L":", 3);
ms->sData = NULL;
ms->iType = TYPE_SPK;
Utils::WString r = rest.tokens(L":", 4);
if (!r.empty())
{
ms->sScriptName = r.token(L"|", 1);
ms->sScriptAuthor = r.token(L"|", 2);
ms->sScriptVersion = r.tokens(L"|", 3);
}
m_lFiles.push_back(ms);
}
else if (first == L"XspFile:")
{
SMultiSpkFile* ms = new SMultiSpkFile;
ms->bRemove = false;
ms->pFile = NULL;
ms->bOn = (rest.token(L":", 1).toInt()) ? true : false;
ms->lSize = rest.token(L":", 2).toLong();
ms->sName = rest.token(L":", 3);
ms->sData = NULL;
ms->iType = TYPE_XSP;
Utils::WString r = rest.tokens(L":", 4);
if (!r.empty())
{
ms->sScriptName = r.token(L"|", 1);
ms->sScriptAuthor = r.token(L"|", 2);
ms->sScriptVersion = r.tokens(L"|", 3);
}
m_lFiles.push_back(ms);
}
else if (first == L"BaseFile:")
{
SMultiSpkFile* ms = new SMultiSpkFile;
ms->bRemove = false;
ms->pFile = NULL;
ms->bOn = (rest.token(L":", 1).toInt()) ? true : false;
ms->lSize = rest.token(L":", 2).toLong();
ms->sName = rest.token(L":", 3);
ms->sData = NULL;
ms->iType = TYPE_BASE;
Utils::WString r = rest.tokens(L":", 4);
if (!r.empty())
{
ms->sScriptName = r.token(L"|", 1);
ms->sScriptAuthor = r.token(L"|", 2);
ms->sScriptVersion = r.tokens(L"|", 3);
}
m_lFiles.push_back(ms);
}
else
return false;
return true;
}
void CMultiSpkFile::_readValues(const Utils::WString &values)
{
std::vector<Utils::WString> lines;
size_t num = values.tokenise(L"\n", lines);
for (size_t i = 0; i < num; i++)
_parseValueLine(lines[i]);
}