Subversion Repositories spk

Rev

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