Subversion Repositories spk

Rev

Rev 124 | Rev 127 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 cycrow 1
#include "CatFile.h"
2
#include <time.h>
3
#include "File.h"
4
#include "DirIO.h"
65 cycrow 5
#include "logging/Log.h"
1 cycrow 6
 
53 cycrow 7
CCatFile::CCatFile () : m_bCatChanged(false)
1 cycrow 8
{
9
	m_iDataType = CATFILE_NONE;
10
	m_sData = NULL;
11
	m_lSize = 0;
12
	m_bCreate = false;
124 cycrow 13
	_lFiles = new std::vector<SInCatFile *>;
1 cycrow 14
}
15
 
16
CCatFile::~CCatFile ()
17
{
52 cycrow 18
	m_fDatFile.close();
19
	m_fCatFile.close();
53 cycrow 20
 
21
	if ( m_bCatChanged ) {
22
		WriteCatFile ();
23
		m_fCatFile.close();
24
	}
25
 
85 cycrow 26
	_clearFiles();
124 cycrow 27
	delete _lFiles;
85 cycrow 28
 
29
	if ( m_sData ) delete []m_sData;
30
}
31
 
32
void CCatFile::_clearFiles()
33
{
124 cycrow 34
	for(auto itr = _lFiles->begin(); itr != _lFiles->end(); itr++)
1 cycrow 35
	{
124 cycrow 36
		if ((*itr)->sData )
37
			delete [](*itr)->sData;
38
		delete (*itr);
1 cycrow 39
	}
53 cycrow 40
 
124 cycrow 41
	_lFiles->clear();
1 cycrow 42
}
43
 
44
bool CCatFile::IsAddonDir(CyString dir)
45
{
125 cycrow 46
	Utils::String d = dir.ToString();
47
	d = d.findReplace("\\", "/");
48
	d = d.token("/", 1);
1 cycrow 49
 
50
	if ( d.Compare("types") )
51
		return true;
52
	if ( d.Compare("t") )
53
		return true;
54
	if ( d.Compare("maps") )
55
		return true;
56
	if ( d.Compare("director") )
57
		return true;
58
	if ( d.Compare("cutscenes") )
59
		return true;
60
	return false;
61
}
62
 
63
bool CCatFile::Opened(int error, bool allowCreate) 
64
{
65
	if ( error == CATERR_NONE ) return true;
66
	if ( allowCreate && error == CATERR_CREATED ) return true;
67
	return false;
68
}
125 cycrow 69
int CCatFile::open(const Utils::String &sCatfile, const Utils::String &addon, int readtype, bool create)
1 cycrow 70
{
125 cycrow 71
	Utils::String catfile = sCatfile;
51 cycrow 72
 
1 cycrow 73
	m_bCreate = false;
51 cycrow 74
	Utils::String datfile;
75
	if ( !catfile.right(4).Compare(".cat") ) {
1 cycrow 76
		datfile = catfile + ".dat";
77
		catfile += ".cat";
78
	}
51 cycrow 79
	else datfile = catfile.left(-4) + ".dat";
1 cycrow 80
 
51 cycrow 81
	if ( readtype == CATREAD_JUSTCONTENTS ) create = false;
1 cycrow 82
 
83
	// first check if the dat file exists and opens
84
	bool created = false;
51 cycrow 85
	if ( readtype != CATREAD_JUSTCONTENTS ) {
85 cycrow 86
		if ( !CFileIO::Exists(datfile) ) {
51 cycrow 87
			if ( create ) created = true;
88
			else		  return CATERR_NODATFILE;
1 cycrow 89
		}
90
	}
91
 
92
	// now open the cat file to read
82 cycrow 93
	CFileIO File(catfile);
94
	if ( !File.startRead() ) {
51 cycrow 95
		if ( create ) created = true;
96
		else		  return CATERR_NOCATFILE;
1 cycrow 97
	}
98
 
51 cycrow 99
	if ( created ) {
118 cycrow 100
		m_fCatFile.open(catfile);
101
		m_fDatFile.open(datfile);
1 cycrow 102
		m_bCreate = true;
103
		return CATERR_CREATED;
104
	}
105
 
106
	// find the file size
82 cycrow 107
	if ( !File.fileSize() ) {
51 cycrow 108
		File.close();
1 cycrow 109
		return CATERR_FILEEMPTY;
110
	}
111
 
112
	// size must be multiples of 5
82 cycrow 113
	size_t size = File.fileSize() + ((File.fileSize() % 5) ? 5 - (File.fileSize() % 5) : 0);
1 cycrow 114
 
115
	// read cat to buffer
51 cycrow 116
	try {
53 cycrow 117
		if ( m_sData ) delete [] m_sData;
51 cycrow 118
		m_sData = new unsigned char[size + 1];
119
	}
120
	catch (std::exception &e) {
121
		CLog::logf(CLog::Log_IO, 2, "Memory Exception error, unable to alloc enough memory to store file data, %d (%s)", size + 1, e.what());
122
		return CATERR_MALLOC;
123
	}
124
 
1 cycrow 125
	m_lSize = size;
51 cycrow 126
	try {
82 cycrow 127
		File.read(m_sData, m_lSize);
51 cycrow 128
	} catch (std::exception &e) {
129
		CLog::logf(CLog::Log_IO, 3, "CCatFile::Open() unable to read from cat file, %s", e.what());
1 cycrow 130
		RemoveData ();
51 cycrow 131
		File.close();
1 cycrow 132
		return CATERR_READCAT;
133
	}
134
 
51 cycrow 135
	m_sData[size] = 0;
1 cycrow 136
	m_iDataType = CATFILE_READ;
51 cycrow 137
	File.close();
1 cycrow 138
 
51 cycrow 139
	if ( readtype != CATREAD_CAT ) {
140
		if ( !DecryptData () ) return CATERR_DECRYPT;
82 cycrow 141
		m_sData[File.fileSize()] = 0;
85 cycrow 142
		readFiles ();
1 cycrow 143
	}
144
 
125 cycrow 145
	_sAddonDir = addon;
1 cycrow 146
 
118 cycrow 147
	m_fCatFile.open ( catfile );
1 cycrow 148
 
51 cycrow 149
	if ( readtype != CATREAD_JUSTCONTENTS ) {
118 cycrow 150
		m_fDatFile.open ( datfile );
1 cycrow 151
 
152
		// check the file size matches
153
		long compare = 0;
82 cycrow 154
		if ( m_fDatFile.startRead() ) {
155
			compare = m_fDatFile.fileSize();
156
			m_fDatFile.close();
1 cycrow 157
		}
124 cycrow 158
 
159
		SInCatFile *c = _lFiles->back();
160
		if (c && (c->lSize + c->lOffset) != compare ) {
85 cycrow 161
//			return CATERR_MISMATCH;
162
		}
1 cycrow 163
	}
164
 
51 cycrow 165
	if ( readtype >= CATREAD_DAT ) LoadDatFile ();
1 cycrow 166
 
167
	return CATERR_NONE;
168
}
169
 
124 cycrow 170
bool CCatFile::removeFile(const Utils::String &file)
1 cycrow 171
{
124 cycrow 172
	if ( !_sAddonDir.empty() ) {
173
		SInCatFile *f = findData(_sAddonDir + "\\" + file);
1 cycrow 174
		if ( f )
124 cycrow 175
			removeFile(f);
1 cycrow 176
	}
177
	{
124 cycrow 178
		SInCatFile *f = findData("addon\\" + file);
1 cycrow 179
		if ( f )
124 cycrow 180
			removeFile(f);
1 cycrow 181
	}
124 cycrow 182
	SInCatFile *f = findData(file);
1 cycrow 183
	if ( !f )
184
	{
185
		m_iError = CATERR_NOFILE;
186
		return false;
187
	}
188
 
124 cycrow 189
	return removeFile(f);
1 cycrow 190
}
191
 
192
void CCatFile::LoadDatFile ()
193
{
194
	if ( m_fDatFile.NoFile() )
195
		return;
196
 
124 cycrow 197
	if ( m_fDatFile.startRead() ) 
198
	{
199
		for(auto itr = _lFiles->cbegin(); itr != _lFiles->cend(); ++itr)
1 cycrow 200
		{
124 cycrow 201
			SInCatFile *c = *itr;
51 cycrow 202
			if ( c->sData )	delete c->sData;
1 cycrow 203
 
51 cycrow 204
			try {
205
				c->sData = new unsigned char[c->lSize + 1];
206
			}
207
			catch (std::exception &e) {
208
				CLog::logf(CLog::Log_IO, 2, "CCatFile::LoadDatFile() unable to malloc data storage: %s, %d (%s)", c->sFile.c_str(), c->lSize, e.what());
209
				continue;
210
			}
211
 
212
			try {
82 cycrow 213
				m_fDatFile.read(c->sData, c->lSize);
51 cycrow 214
			}
215
			catch(std::exception &e) {
216
				CLog::logf(CLog::Log_IO, 2, "CCatFile::LoadDatFile() unable to read file data: %s, %d (%s)", c->sFile.c_str(), c->lSize, e.what());
217
				continue;
218
			}
58 cycrow 219
			c->bDecrypted = false;
51 cycrow 220
			this->DecryptDAT(c);
1 cycrow 221
		}
82 cycrow 222
		m_fDatFile.close();
1 cycrow 223
	}
224
}
225
 
85 cycrow 226
bool CCatFile::readFiles ()
1 cycrow 227
{
228
	size_t offset = 0;
85 cycrow 229
	int max = 0;
1 cycrow 230
 
85 cycrow 231
	Utils::String *lines = Utils::String(m_sData).tokenise("\n", &max);
232
 
233
	_clearFiles();
234
 
235
	if ( lines && max ) {
236
		int start = 1;
237
		_sReadFilename = lines[0];
238
		if ( _sReadFilename.token(" ", -1).isNumber() ) {
239
			_sReadFilename.clear();
240
			start = 0;
241
		}
242
 
243
		for(int i = start; i < max; i++) {
244
			Utils::String l = lines[i];
245
 
246
			SInCatFile *catfile = new SInCatFile;
247
 
248
			catfile->lSize = static_cast<size_t>(l.token(" ", -1).toLong());
249
			catfile->sFile = l.tokens(" ", 1, -2);
250
			catfile->lOffset = offset;
251
			catfile->sData = 0;
252
 
253
			offset += catfile->lSize;
254
 
124 cycrow 255
			_lFiles->push_back(catfile);
85 cycrow 256
		}
257
	}
258
 
259
	CLEANSPLIT(lines, max);
260
/*
1 cycrow 261
	unsigned char *data = m_sData;
262
	int spacePos = -1;
53 cycrow 263
	while ( m_sData[num] != '\0' ) {
264
		if ( m_sData[num] == '\n' ) {
1 cycrow 265
			m_sData[num] = 0;
266
			if ( spacePos != -1 )
267
			{
268
				CyString file;
269
				int size = 0;
270
 
271
				m_sData[spacePos] = '\0';
272
				file = (char *)data;
273
				data = (m_sData + (spacePos + 1));
274
				if ( !CyString((char *)data).IsNumber() )
275
					size = 0;
276
				else
277
					size = atoi((const char *)data);
278
 
279
				if ( size )
280
				{
281
					SInCatFile *catfile = new SInCatFile;
282
 
283
					catfile->lSize = size;
284
					catfile->sFile = file;
285
					catfile->lOffset = offset;
286
					catfile->sData = 0;
287
 
288
					offset += catfile->lSize;
289
 
290
					m_lFiles.push_back ( catfile );
291
				}
292
			}
293
			data = (m_sData + (num + 1));
294
			spacePos = -1;
295
		}
296
		else if ( m_sData[num] == ' ' )
297
			spacePos = num;
298
		++num;
299
	}
85 cycrow 300
*/
1 cycrow 301
	return true;
302
}
303
 
304
bool CCatFile::DecryptData ( unsigned char *data, size_t size )
305
{
306
	unsigned char cl=0xDB, dh=0xDC, dl=0xDD, ah=0xDE, ch=0xDF, al;
307
	unsigned char *ptr = data, *end = data + size;
308
	for ( ptr = data; ptr < end; ptr += 5)
309
	{
310
		al=ch;
311
		ch+=5;
312
		*(ptr + 4) ^= al;
313
		al=ah;
314
		ah+=5;
315
		*(ptr + 3) ^= al;
316
		al=dl;
317
		dl+=5;
318
		*(ptr + 2) ^= al;
319
		al=dh;
320
		dh+=5;
321
		*(ptr + 1) ^= al;
322
		al=cl;
323
		cl+=5;
324
		*(ptr + 0) ^= al;
325
	}
326
 
327
	return true;
328
}
329
 
330
bool CCatFile::DecryptData ()
331
{
51 cycrow 332
	if ( !DecryptData ( m_sData, m_lSize ) ) return false;
1 cycrow 333
 
334
	m_iDataType = CATERR_DECRYPT;
335
 
336
	return true;
337
}
338
 
339
void CCatFile::RemoveData ()
340
{
53 cycrow 341
	delete m_sData;
1 cycrow 342
	m_sData = NULL;
343
	m_iDataType = CATFILE_NONE;
344
	m_lSize = 0;
345
}
346
 
347
bool CCatFile::ReadFileToData ( CyString filename )
348
{
349
	SInCatFile *c = FindData ( filename );
350
	return ReadFileToData ( c );
351
}
352
 
353
bool CCatFile::ReadFileToData ( SInCatFile *c )
354
{
41 cycrow 355
	if ( !c ) return false;
1 cycrow 356
	size_t size = 0;
53 cycrow 357
	if ( !c->sData ) c->sData = readData ( c, &size );
41 cycrow 358
	if ( c->sData )	return true;
1 cycrow 359
	return false;
360
}
361
 
53 cycrow 362
unsigned char *CCatFile::readData ( SInCatFile *c, size_t *size )
1 cycrow 363
{
364
	*size = c->lSize;
365
 
53 cycrow 366
	if ( !c->sData ) {
367
		if ( m_fDatFile.startRead() ) {
368
			m_fDatFile.seek(c->lOffset);
369
			c->sData = m_fDatFile.read(c->lSize);
58 cycrow 370
			c->bDecrypted = false;
53 cycrow 371
			m_fDatFile.close();
51 cycrow 372
 
53 cycrow 373
			if ( !c->sData ) (*size) = 0;
374
			else {
375
				c->sData[c->lSize] = '\0';
58 cycrow 376
				DecryptDAT(c);
53 cycrow 377
			}
51 cycrow 378
		}
1 cycrow 379
	}
380
 
53 cycrow 381
	return c->sData;
1 cycrow 382
}
383
 
41 cycrow 384
void CCatFile::DecryptDAT(SInCatFile *pFile) 
385
{
386
	if ( pFile->bDecrypted ) return;
387
	pFile->bDecrypted = true;
388
	this->DecryptDAT(pFile->sData, pFile->lSize);
389
}
1 cycrow 390
void CCatFile::DecryptDAT(unsigned char *buffer, size_t size)
391
{
392
	for(unsigned char *pos=buffer, *end=buffer + size; pos < end; pos++){
393
		*pos^=0x33;
394
	}
395
}
53 cycrow 396
unsigned char *CCatFile::readData ( CyString filename, size_t *size )
1 cycrow 397
{
398
	*size = 0;
399
 
53 cycrow 400
	if ( !m_fDatFile.NoFile() ) {
1 cycrow 401
		SInCatFile *c = FindData ( filename );
53 cycrow 402
		if ( c ) return readData ( c, size );
1 cycrow 403
	}
404
 
405
	return NULL;
406
}
407
 
41 cycrow 408
unsigned char *CCatFile::UnpackFile ( SInCatFile *c, size_t *size)
1 cycrow 409
{
410
	*size = 0;
411
	if ( !c )
412
		return NULL;
413
 
41 cycrow 414
	this->DecryptDAT(c);
1 cycrow 415
 
41 cycrow 416
	int iFiletype = this->_checkFiletype(c->sData, 3);
417
 
418
	// plain file
419
	if ( iFiletype == FILETYPE_PLAIN ) {
420
		*size = c->lSize;
421
		return c->sData;
422
	}
423
 
424
	//otherwise unpack it
425
 
426
//	if ( IsDataPCK ( (const unsigned char *)c->sData, c->lSize ) || forcepck )
427
	/*int iOffset = 0;
428
	if ( iFiletype == FILETYPE_PCK ) {
429
		iOffset = 1;
430
		for(size_t i=0; i < c->lSize; i++){
431
			c->sData[i] ^= magic;
432
		}
433
	}
434
 
435
 
436
	return UnPCKData ( c->sData + iOffset, c->lSize - iOffset, size );*/
96 cycrow 437
	unsigned char *data = UnPCKData(c->sData, c->lSize, size, false);
438
	if ( !data ) data = UnPCKData(c->sData, c->lSize, size, true);
439
	return data;
1 cycrow 440
}
441
 
124 cycrow 442
bool CCatFile::removeFile(SInCatFile *f)
1 cycrow 443
{
124 cycrow 444
	if ( (m_bCreate) || (_lFiles->empty()) )
1 cycrow 445
		return false;
446
 
447
	if ( !f )
448
		return false;
449
 
450
	int err = m_fDatFile.TruncateFile ( f->lOffset, f->lSize );
451
	if ( err == FILEERR_TOSMALL )
452
	{
453
		if ( (int)m_fDatFile.GetFilesize() > this->GetEndOffset() )
454
			m_fDatFile.TruncateFile( this->GetEndOffset(), m_fDatFile.GetFilesize() - this->GetEndOffset() );
455
	}
456
	else if ( err != FILEERR_NONE )
457
		return false;
458
 
16 cycrow 459
	// adjust the file positions
40 cycrow 460
	int iOffset = -1;
124 cycrow 461
	for (auto itr = _lFiles->cbegin(); itr != _lFiles->cend(); ++itr)
462
	{
463
		SInCatFile *c = *itr;
464
		if (c == f) {
465
			iOffset = c->lOffset;
40 cycrow 466
		}
467
		else if ( iOffset >= 0 ) {
124 cycrow 468
			c->lOffset = iOffset;
469
			iOffset += c->lSize;
16 cycrow 470
		}
471
	}
472
 
1 cycrow 473
	// now just write the new cat file
124 cycrow 474
	auto findItr = std::find(_lFiles->begin(), _lFiles->end(), f);
475
	if (findItr != _lFiles->end())
476
		_lFiles->erase(findItr);
53 cycrow 477
	m_bCatChanged = true;
1 cycrow 478
 
479
	return true;
480
}
481
 
482
bool CCatFile::WriteCatFile ()
483
{
124 cycrow 484
	if ( (m_bCreate) && (_lFiles->empty()) ) return false;
1 cycrow 485
 
85 cycrow 486
	Utils::String cat = m_fDatFile.filename() + "\n";
124 cycrow 487
	for (auto itr = _lFiles->cbegin(); itr != _lFiles->cend(); ++itr)
488
	{
489
		SInCatFile *c = *itr;
490
		if ( !c ) continue;
491
		if ( c->sFile.empty() ) continue;
492
		Utils::String str = c->sFile;
493
		cat += str.findReplace("/", "\\").findReplace("\\\\", "\\") + " " + (long)c->lSize + "\n";
1 cycrow 494
	}
495
 
53 cycrow 496
	if ( !cat.length() ) return false;
1 cycrow 497
 
53 cycrow 498
	// make sure the len is in multiples of 5, otherwise decryptData could cause heap problems
499
	size_t len = cat.length() + ((cat.length() % 5) ? 5 - (cat.length() % 5) : 0);
1 cycrow 500
 
53 cycrow 501
	bool ret = false;
502
	try {
503
		unsigned char *data = new unsigned char[len + 1];
504
		memcpy(data, cat.c_str(), cat.length() * sizeof(unsigned char));
505
		for ( size_t i = len; i >= cat.length(); i-- ) data[i] = '\0';
506
		this->DecryptData(data, len);
1 cycrow 507
 
53 cycrow 508
		bool ret = m_fCatFile.write(data, cat.length());
509
		delete []data;
510
	}
511
	catch(std::exception &e) {
512
		CLog::logf(CLog::Log_IO, 2, "CCatFile::WriteCatFile() unable to malloc, %d (%s)", len + 1, e.what());
513
		return false;
514
	}
1 cycrow 515
 
53 cycrow 516
	m_fCatFile.close();
517
	m_bCatChanged = false;
518
	return ret;
1 cycrow 519
}
520
 
124 cycrow 521
std::vector<SInCatFile *> *CCatFile::GetFiles() const
1 cycrow 522
{
124 cycrow 523
	return _lFiles;
524
}
1 cycrow 525
 
124 cycrow 526
bool CCatFile::checkExtensionPck(const Utils::String &filename) const
527
{	
528
	Utils::String ext = CFileIO(filename).extension().lower();
1 cycrow 529
	if ( ext == "xml" )
530
		return true;
531
	else if ( ext == "txt" )
532
		return true;
533
	else if ( ext == "bob" )
534
		return true;
535
	else if ( ext == "bod" )
536
		return true;
537
 
538
	return false;
539
}
540
 
58 cycrow 541
bool CCatFile::CheckPackedExtension(const Utils::String &sFilename)
1 cycrow 542
{
58 cycrow 543
	Utils::String ext = CFileIO(sFilename).extension().lower();
1 cycrow 544
 
545
	if ( ext == "pck" )
546
		return true;
547
	else if ( ext == "pbb" )
548
		return true;
549
	else if ( ext == "pbd" )
550
		return true;
551
 
552
	return false;
553
}
554
 
124 cycrow 555
Utils::String CCatFile::PckChangeExtension(const Utils::String &f)
1 cycrow 556
{
557
	CFileIO fo ( f );
119 cycrow 558
	Utils::String ext = fo.extension ().lower();
1 cycrow 559
	if ( ext == "txt" )
124 cycrow 560
		return fo.changeFileExtension("pck");
1 cycrow 561
	else if ( ext == "xml" )
124 cycrow 562
		return fo.changeFileExtension("pck");
1 cycrow 563
	else if ( ext == "bob" )
124 cycrow 564
		return fo.changeFileExtension("pbb");
1 cycrow 565
	else if ( ext == "bod" )
124 cycrow 566
		return fo.changeFileExtension("pbd");
1 cycrow 567
 
568
	return f;
569
}
570
 
52 cycrow 571
bool CCatFile::AppendFile(const Utils::String &filename, const Utils::String &sTo, bool pck, bool bXor, Utils::String *sChangeTo)
1 cycrow 572
{
111 cycrow 573
	CLog::logf(CLog::Log_IO, 1, "CCatFile::AppendFile() Adding file, %s, into cat file, %s::%s [PCK:%s XOR:%s]", filename.c_str(), this->m_fCatFile.filename().c_str(), sTo.c_str(), (pck) ? "Yes" : "No", (bXor) ? "Yes" : "No");
52 cycrow 574
 
575
	if ( filename.isin ( "::" ) ) return WriteFromCat ( filename.token("::", 1), filename.token("::", 2));
53 cycrow 576
	if ( (!m_bCreate) && (!m_fDatFile.exists ()) ) {
102 cycrow 577
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() Cat File: %s, doesn't exist, quitting...", m_fCatFile.fullFilename().c_str());
1 cycrow 578
		return false;
52 cycrow 579
	}
124 cycrow 580
	Utils::String to = sTo;
581
	if ( !_sAddonDir.empty() && CCatFile::IsAddonDir(to) ) {
582
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() changing destination to included addon fir, %s => %s", to, _sAddonDir + "/" + to);
583
		to = _sAddonDir + "\\" + to;
1 cycrow 584
	}
585
 
42 cycrow 586
	// change the file extension and remove the file again
124 cycrow 587
	if ( pck && checkExtensionPck(filename) ) {
588
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() changing file extension for packed file, %s => %s", to.c_str(), PckChangeExtension(to).c_str());
52 cycrow 589
		to = PckChangeExtension(to);
1 cycrow 590
	}
124 cycrow 591
	if ( sChangeTo ) *sChangeTo = to;
1 cycrow 592
 
124 cycrow 593
	if ( !_lFiles->empty() ) {
42 cycrow 594
		SInCatFile *checkf = FindData ( to );
595
		if ( checkf ) {
124 cycrow 596
			CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() removing existing filechanging file extension for packed file, %s => %s", to.c_str(), PckChangeExtension(to).c_str());
597
			if ( !removeFile(checkf) ) {
52 cycrow 598
				CLog::logf(CLog::Log_IO, 1, "CCatFile::AppendFile() unable to remove existing file, quitting...");
599
				return false;
600
			}
42 cycrow 601
		}
602
	}
603
 
1 cycrow 604
	bool append = false;
605
 
606
	SInCatFile *f = new SInCatFile;
607
	f->sData = 0;
608
	f->lOffset = this->GetEndOffset();
85 cycrow 609
 
1 cycrow 610
	bool dofile = true;
611
 
124 cycrow 612
	if ( _lFiles->empty() )	{
52 cycrow 613
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() no existing files found, wipeing the existing dat file");
1 cycrow 614
		m_fDatFile.WipeFile();
52 cycrow 615
	}
1 cycrow 616
 
52 cycrow 617
	CFileIO File(filename);
618
	if ( !File.startRead() ) {
619
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() unable to open file, %s, for reading", filename.c_str());
620
		return false;
621
	}
622
 
623
	CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() reading file data, %s:%d", filename.c_str(), File.fileSize());
624
	unsigned char *data = File.readAll();
625
	File.close();
1 cycrow 626
 
52 cycrow 627
	// check if we need to pack the file
628
	int iFileType = this->_checkFiletype(data, 3);
629
	CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() preparing to append file data, Type=%d", iFileType);
124 cycrow 630
	if ( iFileType == FILETYPE_PLAIN && CheckPackedExtension(to) ) {
52 cycrow 631
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() plain text file needs to packed");
632
		size_t newsize = 0;
633
		unsigned char *newdata = PCKData(data, File.fileSize(), &newsize, bXor);
634
		CLog::logf(CLog::Log_IO, 1, "CCatFile::AppendFile() file has been packed, %d => %d", File.fileSize(), newsize);
1 cycrow 635
 
52 cycrow 636
		f->lSize = newsize;
637
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() Decrypting data");
638
		this->DecryptDAT(newdata, f->lSize);
639
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() appending data to Dat file, Offset:%d", f->lOffset);
640
		append = m_fDatFile.AppendDataToPos ( (const char *)newdata, newsize, f->lOffset );
1 cycrow 641
 
52 cycrow 642
		delete [] newdata;
1 cycrow 643
	}
52 cycrow 644
	else  {
645
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() Decrypting data");
646
		this->DecryptDAT(data, File.fileSize());
647
		f->lSize = File.fileSize();
648
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() appending data to Dat file, Offset:%d", f->lOffset);
649
		append = m_fDatFile.AppendDataToPos ( (const char *)data, f->lSize, f->lOffset );
650
	}
1 cycrow 651
 
52 cycrow 652
	CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() cleaning up memory");
653
	delete [] data;
654
 
53 cycrow 655
	m_fDatFile.close();
656
 
52 cycrow 657
	if ( append ) {
658
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() append complete, adding file into cat list");
1 cycrow 659
		m_bCreate = false;
660
		f->sFile = to;
124 cycrow 661
		_lFiles->push_back(f);
52 cycrow 662
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() writing the new cat file");
53 cycrow 663
		m_bCatChanged = true;
1 cycrow 664
	}
52 cycrow 665
	else {
666
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() appending failed, cleaning up file data");
1 cycrow 667
		delete f;
52 cycrow 668
	}
1 cycrow 669
 
53 cycrow 670
	CLog::logf(CLog::Log_IO, 4, "CCatFile::AppendFile() function complete");
671
 
672
	return append;
1 cycrow 673
}
674
 
675
bool CCatFile::AddData ( CyString catfile, unsigned char *data, size_t size, CyString to, bool pck, bool create )
676
{
125 cycrow 677
	int err = open(catfile.ToString(), "", CATREAD_CATDECRYPT, create);
1 cycrow 678
	if ( (err != CATERR_NONE) && (err != CATERR_CREATED) )
679
		return false;
680
 
681
	return AppendData ( data, size, to, pck );
682
}
683
 
684
int CCatFile::GetEndOffset()
685
{
124 cycrow 686
	if ( _lFiles->empty() )
1 cycrow 687
		return 0;
84 cycrow 688
	else {
124 cycrow 689
		return _lFiles->back()->lOffset + _lFiles->back()->lSize;
84 cycrow 690
	}
1 cycrow 691
}
692
 
124 cycrow 693
size_t CCatFile::GetNumFiles() const 
694
{ 
695
	return _lFiles->size(); 
696
}
1 cycrow 697
 
124 cycrow 698
SInCatFile *CCatFile::GetFile(unsigned int num) const 
699
{ 
700
	return (num >= 0 && num < _lFiles->size()) ? _lFiles->at(num) : NULL; 
701
}
702
 
703
bool CCatFile::AppendData ( unsigned char *data, size_t size, CyString sTo, bool pck, bool bXor )
1 cycrow 704
{
52 cycrow 705
	if ( (!m_bCreate) && (!m_fCatFile.exists ()) )
1 cycrow 706
		return false;
707
 
708
	if ( (size <= 0) || (!data) )
709
		return false;
710
 
124 cycrow 711
	Utils::String to = sTo.ToString();
712
 
1 cycrow 713
	// then check if the file already exists
124 cycrow 714
	if ( !_lFiles->empty() )
1 cycrow 715
	{
716
		SInCatFile *f = FindData ( to );
717
		if ( f )
718
		{
124 cycrow 719
			if (!removeFile ( f ))
1 cycrow 720
				return false;
721
		}
722
	}
723
 
724
	bool append = false;
725
 
124 cycrow 726
	if (_lFiles->empty())
1 cycrow 727
		m_fDatFile.WipeFile();
728
 
729
	SInCatFile *f = new SInCatFile;
730
	f->sData = 0;
124 cycrow 731
	if (_lFiles->empty())
1 cycrow 732
		f->lOffset = 0;
733
	else
734
		f->lOffset = m_fDatFile.GetFilesize();
735
 
736
	// if file extension is packed but not the file, then pack it, "pck" forces it to be packed unless it already is
737
 
738
	f->lSize = size;
124 cycrow 739
	if ( (((pck) && (checkExtensionPck (to))) || ((CheckPackedExtension (to)))) && (!IsDataPCK ( data, size )) )
1 cycrow 740
	{
741
		to = PckChangeExtension ( to );
742
 
124 cycrow 743
		if (!_lFiles->empty())
1 cycrow 744
		{
745
			SInCatFile *f = FindData ( to );
746
			if ( f )
747
			{
124 cycrow 748
				if (!removeFile(f))
1 cycrow 749
					return false;
750
			}
751
		}
752
		size_t newsize = 0;
753
		unsigned char *d = PCKData ( data, size, &newsize, bXor );
754
 
755
		f->lSize = newsize;
756
		this->DecryptDAT(d, newsize);
757
		append = m_fDatFile.AppendDataToPos ( (const char *)d, newsize, f->lOffset );
758
 
759
		delete [] d;
760
	}
761
	else {
762
		this->DecryptDAT(data, size);
763
		append = m_fDatFile.AppendDataToPos ( (const char *)data, size, f->lOffset );
764
	}
765
 
58 cycrow 766
	m_fDatFile.close();
767
	if ( append ) {
1 cycrow 768
		m_bCreate = false;
769
		f->sFile = to;
124 cycrow 770
		_lFiles->push_back(f);
53 cycrow 771
		m_bCatChanged = true;
1 cycrow 772
	}
773
	else
774
		delete f;
775
 
776
	return true;
777
}
778
 
124 cycrow 779
Utils::String CCatFile::RenameFileExtension(SInCatFile *f)
1 cycrow 780
{
124 cycrow 781
	CFileIO fo(f->sFile);
125 cycrow 782
	Utils::String ext = fo.extension().lower();
1 cycrow 783
	if ( ext == "pck" )
784
	{
124 cycrow 785
		Utils::String firstDir = f->sFile.findReplace("/", "\\").token("\\", 1).lower();
1 cycrow 786
 
787
		if ( firstDir == "t" )
124 cycrow 788
			return fo.changeFileExtension ( "xml" );
1 cycrow 789
		else if ( firstDir == "director" )
124 cycrow 790
			return fo.changeFileExtension ( "xml" );
791
		return fo.changeFileExtension ( "txt" );
1 cycrow 792
	}
793
	else if ( ext == "pbb" )
124 cycrow 794
		return fo.changeFileExtension ( "bob" );
1 cycrow 795
	else if ( ext == "pbd" )
124 cycrow 796
		return fo.changeFileExtension ( "bod" );
1 cycrow 797
	return f->sFile;
798
}
799
 
800
void CCatFile::FindFiles(CyStringList *files, CyString filemask)
801
{
124 cycrow 802
	if (_lFiles->empty())
1 cycrow 803
		return;
804
 
124 cycrow 805
	for (auto itr = _lFiles->cbegin(); itr != _lFiles->cend(); ++itr)
1 cycrow 806
	{
124 cycrow 807
		if ( filemask.WildMatch((*itr)->sFile) )
808
			files->PushBack(CyString((*itr)->sFile));
1 cycrow 809
	}
810
}
811
 
812
bool CCatFile::ExtractAll(CyString dir)
813
{
124 cycrow 814
	if (_lFiles->empty())
1 cycrow 815
		return false;
816
 
124 cycrow 817
	for (auto itr = _lFiles->cbegin(); itr != _lFiles->cend(); ++itr)
1 cycrow 818
	{
124 cycrow 819
		if ( !ExtractFile(*itr, dir, true) )
1 cycrow 820
			return false;
821
	}
822
 
823
	return true;
824
}
825
bool CCatFile::ExtractFile ( CyString filename, CyString to, bool preserve )
826
{
124 cycrow 827
	if (_lFiles->empty())
1 cycrow 828
		return false;
829
 
830
	// check if file exists
124 cycrow 831
	if ( !_sAddonDir.empty() ) {
832
		SInCatFile *f = FindData ( CyString(_sAddonDir) + "\\" + filename );
1 cycrow 833
		if ( f )
834
			return ExtractFile ( f, to, preserve );
835
	}
836
	{
837
		SInCatFile *f = FindData ( CyString("addon\\") + filename );
838
		if ( f )
839
			return ExtractFile ( f, to, preserve );
840
	}
841
 
842
	SInCatFile *f = FindData ( filename );
843
	if ( !f )
844
	{
845
		m_iError = CATERR_NOFILE;
846
		return false;
847
	}
848
 
849
	return ExtractFile ( f, to, preserve );
850
}
851
 
41 cycrow 852
int CCatFile::_checkFiletype(const unsigned char *pBuffer, int iSize)
853
{
854
	if(iSize >= 3) {
855
		unsigned char magic = pBuffer[0] ^ 0xC8;
856
		if( (pBuffer[1] ^ magic) == 0x1F && (pBuffer[2] ^ magic) == 0x8B) {
857
			return FILETYPE_PCK;
858
		}
859
	}
860
 
861
	if ( iSize >= 2 && (pBuffer[0] == 0x1F && pBuffer[1] == 0x8B) ) {
862
		return FILETYPE_DEFLATE;
863
	}	
864
 
865
	return FILETYPE_PLAIN;
866
}
867
 
1 cycrow 868
bool CCatFile::ExtractFile ( SInCatFile *f, CyString to, bool preserve )
869
{
870
	unsigned char *data = 0;
871
	size_t size = 0;
872
 
873
	// load the data from file
874
	ReadFileToData ( f );
875
 
41 cycrow 876
	data = this->UnpackFile(f, &size);
52 cycrow 877
	if ( !data ) {
1 cycrow 878
		m_iError = CATERR_CANTREAD;
879
		return false;
880
	}
881
 
882
	CFileIO fo(CCatFile::RenameFileExtension(f));
883
	// check for a file name
884
	CyString checkFile = to;
885
	CyString todir, tofile = to;
886
	if ( checkFile.IsAnyIn("\\/") )
887
	{
888
		checkFile = checkFile.FindReplace ( "\\", "/" );
889
		checkFile = checkFile.FindReplace ( "//", "/" );
890
		tofile = checkFile.GetToken("/", checkFile.NumToken('/'));
891
 
892
		if ( !checkFile.IsIn(".") || preserve )
893
		{
111 cycrow 894
			tofile = fo.filename();
1 cycrow 895
			todir = checkFile;
896
		}
897
		else
898
		{
102 cycrow 899
			todir = CFileIO(checkFile).dir();
900
			tofile = CFileIO(checkFile).filename();
1 cycrow 901
		}
902
	}
903
 
904
	if ( tofile.Empty() )
905
	{
906
		if ( !tofile.Empty() )
907
			tofile += "/";
111 cycrow 908
		tofile += fo.filename();
102 cycrow 909
		todir = CFileIO(tofile).dir();
910
		tofile = CFileIO(tofile).filename();
1 cycrow 911
	}
912
 
913
	if ( preserve )
914
	{
102 cycrow 915
		if ( !fo.dir().Compare(todir.Right(- (int)fo.dir().length()).ToString()) )
1 cycrow 916
		{
917
			if ( !todir.Empty() && todir.Right(1) != "/" )
918
				todir += "/";
102 cycrow 919
			todir += fo.dir();
1 cycrow 920
		}
921
	}
922
 
923
	if ( tofile.Empty() )
111 cycrow 924
		tofile = fo.filename();
1 cycrow 925
 
52 cycrow 926
	bool bWritten = false;
927
 
1 cycrow 928
	// create the directory to extract to
929
	if ( !todir.Empty() && !CDirIO(todir).Create() )
930
		m_iError = CATERR_CANTCREATEDIR;
931
	else
932
	{
933
		CyString file = todir;
934
		if ( !file.Empty() )
935
			file += "/";
936
		file += tofile;
937
		file = file.FindReplace("/", "\\");
938
		file = file.FindReplace("\\\\", "\\");
939
 
52 cycrow 940
		CFileIO File(file.c_str());
941
		if ( !File.startWrite() ) m_iError = CATERR_INVALIDDEST;
942
		else bWritten = File.write(data, size);
1 cycrow 943
	}
944
 
945
	delete data;
946
	f->sData = 0;
947
 
124 cycrow 948
	if ( bWritten ) this->clearError();
52 cycrow 949
 
950
	return bWritten;
1 cycrow 951
}
952
 
85 cycrow 953
const Utils::String &CCatFile::internalDatFilename() const
954
{
955
	return _sReadFilename;
956
}
957
 
124 cycrow 958
Utils::String CCatFile::getErrorString () const
1 cycrow 959
{
960
	switch ( m_iError )
961
	{
962
		case CATERR_NONE:
124 cycrow 963
			return Utils::String::Null();
1 cycrow 964
		case CATERR_NODATFILE:
965
			return "Unable to open Dat file";
966
		case CATERR_NOCATFILE:
967
			return "Unable to open Cat file";
968
		case CATERR_FILEEMPTY:
969
			return "Cat file is empty";
970
		case CATERR_READCAT:
971
			return "Unable to read from cat file";
972
		case CATERR_DECRYPT:
973
			return "Unable to decrypt cat file";
974
		case CATERR_MISMATCH:
975
			return "File size mismatch with Dat file";
976
		case CATERR_NOFILE:
977
			return "Unable to find file in archive";
978
		case CATERR_CANTREAD:
979
			return "Unable to read file in archive";
980
		case CATERR_CANTCREATEDIR:
981
			return "Unable to create destiantion directory";
982
		case CATERR_INVALIDDEST:
983
			return "Unable to write to destiantion file";
984
	}
985
 
986
	return "Invalid";
987
}
988
 
989
 
990
unsigned char *CompressPCKData ( unsigned char *buffer, size_t size, size_t *retsize, time_t mtime )
991
{
992
	size_t newsize = (size * 2) + 20;
993
	unsigned char *data = (unsigned char *)malloc ( sizeof(unsigned char) * newsize );
994
 
995
	z_stream zs;
996
	char flags=0;
997
 
998
//	error(0);
999
 
1000
	unsigned char *d = data;
1001
//	if(m_pszComment && strlen(m_pszComment) > 0) flags|=GZ_F_COMMENT;
1002
//	if(m_pszFileName && strlen(m_pszFileName) > 0) flags|=GZ_F_FILENAME;
1003
 
1004
	int pos = PCKHEADERSIZE;
1005
	*d = 0x1F; 		d++;
1006
	*d = 0x8B; 		d++;
1007
	*d = 8;			d++;
1008
	*d = flags; 	d++;
1009
	memcpy(d, &mtime, sizeof(mtime));
1010
	d += 4;
1011
	*d = 0; 		d++;
1012
	*d = 11; 		d++;
1013
//	if(flags & GZ_F_FILENAME) put((const char *)m_pszFileName);
1014
//	if(flags & GZ_F_COMMENT) put((const char *)m_pszComment);
1015
 
1016
	memset(&zs, 0, sizeof(zs));
1017
	zs.next_in=buffer;
1018
	zs.avail_in=(long)size;
1019
 
1020
	int ret;
1021
	unsigned long ubound;
1022
	ret=deflateInit2(&zs, 9, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
1023
	if(ret!=Z_OK)
1024
		return false;
1025
 
1026
	ubound=deflateBound(&zs, (unsigned long)size);
1027
	if ( newsize < ubound)
1028
	{
1029
		newsize += ubound;
1030
		data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
1031
	}
1032
 
1033
 
1034
	zs.next_out=d;
1035
	zs.avail_out=(unsigned int)newsize - pos;
1036
 
1037
	while((ret=deflate(&zs, Z_FINISH))==Z_OK)
1038
	{
1039
		newsize += 1024;
1040
		data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
1041
		zs.next_out=data + zs.total_out;
1042
		zs.avail_out=(unsigned int)newsize - zs.total_out;
1043
	}
1044
	pos += zs.total_out;
1045
 
1046
	deflateEnd(&zs);
1047
 
1048
	unsigned long crc=crc32(0, NULL, 0);
1049
	crc=crc32(crc, buffer, (unsigned int)size);
1050
 
1051
	int s = sizeof(crc) + sizeof(size);
1052
	if ( newsize < (size_t)(s + pos) )
1053
	{
1054
		newsize += (s + pos) - newsize;
1055
		data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
1056
	}
1057
 
1058
	memcpy(&data[pos], &crc, sizeof(crc));
1059
	pos += sizeof(crc);
1060
	memcpy(&data[pos], &size, sizeof(size));
1061
	pos += sizeof(size);
1062
 
1063
	newsize = pos;
1064
 
1065
	unsigned char *retdata = NULL;
1066
	if ( ret == Z_STREAM_END )
1067
	{
1068
		*retsize = newsize;
1069
		retdata = new unsigned char[newsize];
1070
		memcpy ( retdata, data, newsize );
1071
	}
1072
	free ( data );
1073
 
1074
	return retdata;
1075
}
1076
 
1077
unsigned char *PCKData ( unsigned char *data, size_t oldsize, size_t *newsize, bool bXor )
1078
{
1079
	unsigned char *newdata = CompressPCKData ( data, oldsize, newsize, time(NULL) );
1080
	if ( !bXor )
1081
		return newdata;
1082
 
1083
	if ( newdata )
1084
	{
1085
		char magic = (char)clock(), m;
1086
		m=magic ^ 0xC8;
1087
 
1088
		unsigned char *ptr = newdata, *end = newdata + *newsize;
1089
		// XOR encryption
1090
		if ( bXor )
1091
		{
1092
			for ( ; ptr < end; ptr++ )
1093
				(*ptr)^=magic;
1094
		}
1095
 
1096
		unsigned char *finalData = new unsigned char[*newsize + 1];
1097
		finalData[0] = m;
1098
		memcpy ( finalData + 1, newdata, *newsize );
1099
		delete [] newdata;
1100
		(*newsize)++;
1101
		return finalData;
1102
	}
1103
 
1104
	return NULL;
1105
}
1106
 
1107
bool CCatFile::WriteFromCat ( CCatFile *fcat, CyString file )
1108
{
1109
	// now find the file in the cat file
1110
	SInCatFile *getfile = fcat->FindData ( file );
1111
	if ( !getfile )
1112
		return false;
1113
 
1114
	// read the dat from the cat file
1115
	size_t size = 0;
53 cycrow 1116
	unsigned char *data = fcat->readData ( getfile, &size );
1 cycrow 1117
	if ( !data )
1118
		return false;
1119
 
1120
	// now check if it exists in this file
124 cycrow 1121
	removeFile(file.ToString());
1 cycrow 1122
 
124 cycrow 1123
	int offset = (_lFiles->empty()) ? 0 : (_lFiles->back()->lOffset + _lFiles->back()->lSize);
1 cycrow 1124
	// now write to the new file
58 cycrow 1125
	if ( getfile->bDecrypted ) this->DecryptDAT(getfile->sData, getfile->lSize);
1126
	getfile->bDecrypted = false;
1127
	if ( !m_fDatFile.AppendDataToPos((const char *)getfile->sData, getfile->lSize, offset) ) return false;
1128
    m_fDatFile.close();
1 cycrow 1129
 
1130
	// finally add to the list
1131
	SInCatFile *f = new SInCatFile;
1132
	f->sData = 0;
124 cycrow 1133
	if (_lFiles->empty())
1 cycrow 1134
		f->lOffset = 0;
1135
	else
124 cycrow 1136
		f->lOffset = _lFiles->back()->lOffset + _lFiles->back()->lSize;
1137
	f->sFile = file.ToString();
1 cycrow 1138
	f->lSize = size;
124 cycrow 1139
	_lFiles->push_back(f);
53 cycrow 1140
	m_bCatChanged = true;
1 cycrow 1141
 
1142
	return true;
1143
}
1144
 
1145
bool CCatFile::WriteFromCat ( CyString catfile, CyString file )
1146
{
1147
	CCatFile fcat;
125 cycrow 1148
	if ( fcat.open(catfile.ToString(), "", CATREAD_CATDECRYPT, false) )
1 cycrow 1149
		return false;
1150
 
1151
	return this->WriteFromCat(&fcat, file);
1152
}
1153
 
124 cycrow 1154
SInCatFile *CCatFile::FindData(CyString filename)
1 cycrow 1155
{
124 cycrow 1156
	return findData(filename.ToString());
1157
}
1158
 
1159
SInCatFile *CCatFile::findData(const Utils::String &filename) const
1160
{
1161
	if (_lFiles->empty())
1 cycrow 1162
		return NULL;
1163
 
124 cycrow 1164
	Utils::String check = filename;
1165
	check = check.findReplace("\\", "/");
1166
	for (auto itr = _lFiles->cbegin(); itr != _lFiles->cend(); ++itr)
1 cycrow 1167
	{
124 cycrow 1168
		Utils::String f = (*itr)->sFile;
1169
		f = f.findReplace("\\", "/");
1170
		if (f.Compare(check))
1171
			return *itr;
1 cycrow 1172
	}
1173
	return NULL;
1174
}
1175
 
1176
bool CCatFile::MarkRemoveFile( CyString file )
1177
{
1178
	SInCatFile *f = FindData ( file );
1179
	if ( !f )
1180
	{
1181
		m_iError = CATERR_NOFILE;
1182
		return false;
1183
	}
1184
 
1185
	return MarkRemoveFile ( f );
1186
}
1187
bool CCatFile::MarkRemoveFile ( SInCatFile *f )
1188
{
1189
	f->bDelete = true;
1190
	return true;
1191
}