Subversion Repositories spk

Rev

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