Subversion Repositories spk

Rev

Rev 84 | Rev 96 | 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 ) {
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
 
146
	m_fCatFile.Open ( catfile );
147
 
51 cycrow 148
	if ( readtype != CATREAD_JUSTCONTENTS ) {
1 cycrow 149
		m_fDatFile.Open ( datfile );
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 );*/
434
	return UnPCKData(c->sData, c->lSize, size, (iFiletype == FILETYPE_PCK) ? true : false);
1 cycrow 435
}
436
 
437
bool CCatFile::RemoveFile ( SInCatFile *f )
438
{
439
	if ( (m_bCreate) || (m_lFiles.empty()) )
440
		return false;
441
 
442
	if ( !f )
443
		return false;
444
 
445
	int err = m_fDatFile.TruncateFile ( f->lOffset, f->lSize );
446
	if ( err == FILEERR_TOSMALL )
447
	{
448
		if ( (int)m_fDatFile.GetFilesize() > this->GetEndOffset() )
449
			m_fDatFile.TruncateFile( this->GetEndOffset(), m_fDatFile.GetFilesize() - this->GetEndOffset() );
450
	}
451
	else if ( err != FILEERR_NONE )
452
		return false;
453
 
16 cycrow 454
	// adjust the file positions
40 cycrow 455
	int iOffset = -1;
16 cycrow 456
	for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() ) {
40 cycrow 457
		if ( node->Data() == f ) {
458
			iOffset = node->Data()->lOffset;
459
		}
460
		else if ( iOffset >= 0 ) {
16 cycrow 461
			node->Data()->lOffset = iOffset;
40 cycrow 462
			iOffset += node->Data()->lSize;
16 cycrow 463
		}
464
	}
465
 
1 cycrow 466
	// now just write the new cat file
467
	m_lFiles.remove ( f );
53 cycrow 468
	m_bCatChanged = true;
1 cycrow 469
 
470
	return true;
471
}
472
 
473
bool CCatFile::WriteCatFile ()
474
{
53 cycrow 475
	if ( (m_bCreate) && (m_lFiles.empty()) ) return false;
1 cycrow 476
 
85 cycrow 477
	Utils::String cat = m_fDatFile.filename() + "\n";
53 cycrow 478
	for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() ) {
479
		if ( !node->Data() ) continue;
480
		if ( node->Data()->sFile.Empty() ) continue;
481
		Utils::String str = node->Data()->sFile.ToString();
482
		cat += str.findReplace("/", "\\").findReplace("\\\\", "\\") + " " + (long)node->Data()->lSize + "\n";
1 cycrow 483
	}
484
 
53 cycrow 485
	if ( !cat.length() ) return false;
1 cycrow 486
 
53 cycrow 487
	// make sure the len is in multiples of 5, otherwise decryptData could cause heap problems
488
	size_t len = cat.length() + ((cat.length() % 5) ? 5 - (cat.length() % 5) : 0);
1 cycrow 489
 
53 cycrow 490
	bool ret = false;
491
	try {
492
		unsigned char *data = new unsigned char[len + 1];
493
		memcpy(data, cat.c_str(), cat.length() * sizeof(unsigned char));
494
		for ( size_t i = len; i >= cat.length(); i-- ) data[i] = '\0';
495
		this->DecryptData(data, len);
1 cycrow 496
 
53 cycrow 497
		bool ret = m_fCatFile.write(data, cat.length());
498
		delete []data;
499
	}
500
	catch(std::exception &e) {
501
		CLog::logf(CLog::Log_IO, 2, "CCatFile::WriteCatFile() unable to malloc, %d (%s)", len + 1, e.what());
502
		return false;
503
	}
1 cycrow 504
 
53 cycrow 505
	m_fCatFile.close();
506
	m_bCatChanged = false;
507
	return ret;
1 cycrow 508
}
509
 
510
bool CCatFile::CheckExtensionPck ( CyString filename )
511
{
512
	CyString ext = filename.GetToken ( ".", filename.NumToken  ( "." ) ).lower();
513
 
514
	if ( ext == "xml" )
515
		return true;
516
	else if ( ext == "txt" )
517
		return true;
518
	else if ( ext == "bob" )
519
		return true;
520
	else if ( ext == "bod" )
521
		return true;
522
 
523
	return false;
524
}
525
 
58 cycrow 526
bool CCatFile::CheckPackedExtension(const Utils::String &sFilename)
1 cycrow 527
{
58 cycrow 528
	Utils::String ext = CFileIO(sFilename).extension().lower();
1 cycrow 529
 
530
	if ( ext == "pck" )
531
		return true;
532
	else if ( ext == "pbb" )
533
		return true;
534
	else if ( ext == "pbd" )
535
		return true;
536
 
537
	return false;
538
}
539
 
540
CyString CCatFile::PckChangeExtension ( CyString f )
541
{
542
	CFileIO fo ( f );
543
	CyString ext = fo.GetFileExtension ().lower();
544
	if ( ext == "txt" )
545
		return fo.ChangeFileExtension ( "pck" );
546
	else if ( ext == "xml" )
547
		return fo.ChangeFileExtension ( "pck" );
548
	else if ( ext == "bob" )
549
		return fo.ChangeFileExtension ( "pbb" );
550
	else if ( ext == "bod" )
551
		return fo.ChangeFileExtension ( "pbd" );
552
 
553
	return f;
554
}
555
 
52 cycrow 556
bool CCatFile::AppendFile(const Utils::String &filename, const Utils::String &sTo, bool pck, bool bXor, Utils::String *sChangeTo)
1 cycrow 557
{
52 cycrow 558
	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.GetFilename().ToString().c_str(), sTo.c_str(), (pck) ? "Yes" : "No", (bXor) ? "Yes" : "No");
559
 
560
	if ( filename.isin ( "::" ) ) return WriteFromCat ( filename.token("::", 1), filename.token("::", 2));
53 cycrow 561
	if ( (!m_bCreate) && (!m_fDatFile.exists ()) ) {
52 cycrow 562
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() Cat File: %s, doesn't exist, quitting...", m_fCatFile.GetFullFilename().ToString().c_str());
1 cycrow 563
		return false;
52 cycrow 564
	}
565
	CyString to = sTo;
1 cycrow 566
	if ( !m_sAddonDir.Empty() && CCatFile::IsAddonDir(to) ) {
52 cycrow 567
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() changing destination to included addon fir, %s => %s", to, m_sAddonDir + "/" + to);
1 cycrow 568
		to = m_sAddonDir + "\\" + to;
569
	}
570
 
42 cycrow 571
	// change the file extension and remove the file again
572
	if ( pck && CheckExtensionPck(filename) ) {
52 cycrow 573
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() changing file extension for packed file, %s => %s", to.c_str(), PckChangeExtension(to).ToString().c_str());
574
		to = PckChangeExtension(to);
1 cycrow 575
	}
52 cycrow 576
	if ( sChangeTo ) *sChangeTo = to.ToString();
1 cycrow 577
 
42 cycrow 578
	if ( !m_lFiles.empty() ) {
579
		SInCatFile *checkf = FindData ( to );
580
		if ( checkf ) {
52 cycrow 581
			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());
582
			if ( !RemoveFile(checkf) ) {
583
				CLog::logf(CLog::Log_IO, 1, "CCatFile::AppendFile() unable to remove existing file, quitting...");
584
				return false;
585
			}
42 cycrow 586
		}
587
	}
588
 
1 cycrow 589
	bool append = false;
590
 
591
	SInCatFile *f = new SInCatFile;
592
	f->sData = 0;
593
	f->lOffset = this->GetEndOffset();
85 cycrow 594
 
1 cycrow 595
	bool dofile = true;
596
 
52 cycrow 597
	if ( !m_lFiles.size() )	{
598
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() no existing files found, wipeing the existing dat file");
1 cycrow 599
		m_fDatFile.WipeFile();
52 cycrow 600
	}
1 cycrow 601
 
52 cycrow 602
	CFileIO File(filename);
603
	if ( !File.startRead() ) {
604
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() unable to open file, %s, for reading", filename.c_str());
605
		return false;
606
	}
607
 
608
	CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() reading file data, %s:%d", filename.c_str(), File.fileSize());
609
	unsigned char *data = File.readAll();
610
	File.close();
1 cycrow 611
 
52 cycrow 612
	// check if we need to pack the file
613
	int iFileType = this->_checkFiletype(data, 3);
614
	CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() preparing to append file data, Type=%d", iFileType);
58 cycrow 615
	if ( iFileType == FILETYPE_PLAIN && CheckPackedExtension(to.ToString()) ) {
52 cycrow 616
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() plain text file needs to packed");
617
		size_t newsize = 0;
618
		unsigned char *newdata = PCKData(data, File.fileSize(), &newsize, bXor);
619
		CLog::logf(CLog::Log_IO, 1, "CCatFile::AppendFile() file has been packed, %d => %d", File.fileSize(), newsize);
1 cycrow 620
 
52 cycrow 621
		f->lSize = newsize;
622
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() Decrypting data");
623
		this->DecryptDAT(newdata, f->lSize);
624
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() appending data to Dat file, Offset:%d", f->lOffset);
625
		append = m_fDatFile.AppendDataToPos ( (const char *)newdata, newsize, f->lOffset );
1 cycrow 626
 
52 cycrow 627
		delete [] newdata;
1 cycrow 628
	}
52 cycrow 629
	else  {
630
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() Decrypting data");
631
		this->DecryptDAT(data, File.fileSize());
632
		f->lSize = File.fileSize();
633
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() appending data to Dat file, Offset:%d", f->lOffset);
634
		append = m_fDatFile.AppendDataToPos ( (const char *)data, f->lSize, f->lOffset );
635
	}
1 cycrow 636
 
52 cycrow 637
	CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() cleaning up memory");
638
	delete [] data;
639
 
53 cycrow 640
	m_fDatFile.close();
641
 
52 cycrow 642
	if ( append ) {
643
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() append complete, adding file into cat list");
1 cycrow 644
		m_bCreate = false;
645
		f->sFile = to;
646
		m_lFiles.push_back ( f );
52 cycrow 647
		CLog::logf(CLog::Log_IO, 3, "CCatFile::AppendFile() writing the new cat file");
53 cycrow 648
		m_bCatChanged = true;
1 cycrow 649
	}
52 cycrow 650
	else {
651
		CLog::logf(CLog::Log_IO, 2, "CCatFile::AppendFile() appending failed, cleaning up file data");
1 cycrow 652
		delete f;
52 cycrow 653
	}
1 cycrow 654
 
53 cycrow 655
	CLog::logf(CLog::Log_IO, 4, "CCatFile::AppendFile() function complete");
656
 
657
	return append;
1 cycrow 658
}
659
 
660
bool CCatFile::AddData ( CyString catfile, unsigned char *data, size_t size, CyString to, bool pck, bool create )
661
{
662
	int err = Open ( catfile, "", CATREAD_CATDECRYPT, create );
663
	if ( (err != CATERR_NONE) && (err != CATERR_CREATED) )
664
		return false;
665
 
666
	return AppendData ( data, size, to, pck );
667
}
668
 
669
int CCatFile::GetEndOffset()
670
{
671
	if ( m_lFiles.empty() )
672
		return 0;
84 cycrow 673
	else {
674
		for(SInCatFile *f = m_lFiles.First(); f; f = m_lFiles.Next()) {
675
			f->lOffset;
676
		}
1 cycrow 677
		return m_lFiles.Back()->Data()->lOffset + m_lFiles.Back()->Data()->lSize;
84 cycrow 678
	}
1 cycrow 679
}
680
 
681
 
682
bool CCatFile::AppendData ( unsigned char *data, size_t size, CyString to, bool pck, bool bXor )
683
{
52 cycrow 684
	if ( (!m_bCreate) && (!m_fCatFile.exists ()) )
1 cycrow 685
		return false;
686
 
687
	if ( (size <= 0) || (!data) )
688
		return false;
689
 
690
	// then check if the file already exists
691
	if ( !m_lFiles.empty() )
692
	{
693
		SInCatFile *f = FindData ( to );
694
		if ( f )
695
		{
696
			if ( !RemoveFile ( f ) )
697
				return false;
698
		}
699
	}
700
 
701
	bool append = false;
702
 
703
	if ( !m_lFiles.size() )
704
		m_fDatFile.WipeFile();
705
 
706
	SInCatFile *f = new SInCatFile;
707
	f->sData = 0;
708
	if ( m_lFiles.empty() )
709
		f->lOffset = 0;
710
	else
711
		f->lOffset = m_fDatFile.GetFilesize();
712
 
713
	// if file extension is packed but not the file, then pack it, "pck" forces it to be packed unless it already is
714
 
715
	f->lSize = size;
58 cycrow 716
	if ( (((pck) && (CheckExtensionPck (to.ToString()))) || ((CheckPackedExtension ( to.ToString() )))) && (!IsDataPCK ( data, size )) )
1 cycrow 717
	{
718
		to = PckChangeExtension ( to );
719
 
720
		if ( !m_lFiles.empty() )
721
		{
722
			SInCatFile *f = FindData ( to );
723
			if ( f )
724
			{
725
				if ( !RemoveFile ( f ) )
726
					return false;
727
			}
728
		}
729
		size_t newsize = 0;
730
		unsigned char *d = PCKData ( data, size, &newsize, bXor );
731
 
732
		f->lSize = newsize;
733
		this->DecryptDAT(d, newsize);
734
		append = m_fDatFile.AppendDataToPos ( (const char *)d, newsize, f->lOffset );
735
 
736
		delete [] d;
737
	}
738
	else {
739
		this->DecryptDAT(data, size);
740
		append = m_fDatFile.AppendDataToPos ( (const char *)data, size, f->lOffset );
741
	}
742
 
58 cycrow 743
	m_fDatFile.close();
744
	if ( append ) {
1 cycrow 745
		m_bCreate = false;
746
		f->sFile = to;
747
		m_lFiles.push_back ( f );
53 cycrow 748
		m_bCatChanged = true;
1 cycrow 749
	}
750
	else
751
		delete f;
752
 
753
	return true;
754
}
755
 
756
CyString CCatFile::RenameFileExtension(SInCatFile *f)
757
{
758
	CFileIO fo ( f->sFile );
759
	CyString ext = fo.GetFileExtension ().lower();
760
	if ( ext == "pck" )
761
	{
762
		CyString firstDir = f->sFile.FindReplace("/", "\\").GetToken("\\", 1, 1).lower();
763
 
764
		if ( firstDir == "t" )
765
			return fo.ChangeFileExtension ( "xml" );
766
		else if ( firstDir == "director" )
767
			return fo.ChangeFileExtension ( "xml" );
768
		return fo.ChangeFileExtension ( "txt" );
769
	}
770
	else if ( ext == "pbb" )
771
		return fo.ChangeFileExtension ( "bob" );
772
	else if ( ext == "pbd" )
773
		return fo.ChangeFileExtension ( "bod" );
774
	return f->sFile;
775
}
776
 
777
void CCatFile::FindFiles(CyStringList *files, CyString filemask)
778
{
779
	if ( m_lFiles.empty() )
780
		return;
781
 
782
	for ( SInCatFile *f = m_lFiles.First(); f; f = m_lFiles.Next() )
783
	{
784
		if ( filemask.WildMatch(f->sFile) )
785
			files->PushBack(f->sFile);
786
	}
787
}
788
 
789
bool CCatFile::ExtractAll(CyString dir)
790
{
791
	if ( m_lFiles.empty() )
792
		return false;
793
 
794
	for ( CListNode<SInCatFile> *node = m_lFiles.Front(); node; node = node->next() )
795
	{
796
		if ( !ExtractFile(node->Data(), dir, true) )
797
			return false;
798
	}
799
 
800
	return true;
801
}
802
bool CCatFile::ExtractFile ( CyString filename, CyString to, bool preserve )
803
{
804
	if ( m_lFiles.empty() )
805
		return false;
806
 
807
	// check if file exists
808
	if ( !m_sAddonDir.Empty() ) {
809
		SInCatFile *f = FindData ( m_sAddonDir + "\\" + filename );
810
		if ( f )
811
			return ExtractFile ( f, to, preserve );
812
	}
813
	{
814
		SInCatFile *f = FindData ( CyString("addon\\") + filename );
815
		if ( f )
816
			return ExtractFile ( f, to, preserve );
817
	}
818
 
819
	SInCatFile *f = FindData ( filename );
820
	if ( !f )
821
	{
822
		m_iError = CATERR_NOFILE;
823
		return false;
824
	}
825
 
826
	return ExtractFile ( f, to, preserve );
827
}
828
 
41 cycrow 829
int CCatFile::_checkFiletype(const unsigned char *pBuffer, int iSize)
830
{
831
	if(iSize >= 3) {
832
		unsigned char magic = pBuffer[0] ^ 0xC8;
833
		if( (pBuffer[1] ^ magic) == 0x1F && (pBuffer[2] ^ magic) == 0x8B) {
834
			return FILETYPE_PCK;
835
		}
836
	}
837
 
838
	if ( iSize >= 2 && (pBuffer[0] == 0x1F && pBuffer[1] == 0x8B) ) {
839
		return FILETYPE_DEFLATE;
840
	}	
841
 
842
	return FILETYPE_PLAIN;
843
}
844
 
1 cycrow 845
bool CCatFile::ExtractFile ( SInCatFile *f, CyString to, bool preserve )
846
{
847
	unsigned char *data = 0;
848
	size_t size = 0;
849
 
850
	// load the data from file
851
	ReadFileToData ( f );
852
 
41 cycrow 853
	data = this->UnpackFile(f, &size);
52 cycrow 854
	if ( !data ) {
1 cycrow 855
		m_iError = CATERR_CANTREAD;
856
		return false;
857
	}
858
 
859
	CFileIO fo(CCatFile::RenameFileExtension(f));
860
	// check for a file name
861
	CyString checkFile = to;
862
	CyString todir, tofile = to;
863
	if ( checkFile.IsAnyIn("\\/") )
864
	{
865
		checkFile = checkFile.FindReplace ( "\\", "/" );
866
		checkFile = checkFile.FindReplace ( "//", "/" );
867
		tofile = checkFile.GetToken("/", checkFile.NumToken('/'));
868
 
869
		if ( !checkFile.IsIn(".") || preserve )
870
		{
871
			tofile = fo.GetFilename();
872
			todir = checkFile;
873
		}
874
		else
875
		{
876
			todir = CFileIO(checkFile).GetDir();
877
			tofile = CFileIO(checkFile).GetFilename();
878
		}
879
	}
880
 
881
	if ( tofile.Empty() )
882
	{
883
		if ( !tofile.Empty() )
884
			tofile += "/";
885
		tofile += fo.GetFilename();
886
		todir = CFileIO(tofile).GetDir();
887
		tofile = CFileIO(tofile).GetFilename();
888
	}
889
 
890
	if ( preserve )
891
	{
892
		if ( !fo.GetDir().Compare(todir.Right(- (int)fo.GetDir().Length())) )
893
		{
894
			if ( !todir.Empty() && todir.Right(1) != "/" )
895
				todir += "/";
896
			todir += fo.GetDir();
897
		}
898
	}
899
 
900
	if ( tofile.Empty() )
901
		tofile = fo.GetFilename();
902
 
52 cycrow 903
	bool bWritten = false;
904
 
1 cycrow 905
	// create the directory to extract to
906
	if ( !todir.Empty() && !CDirIO(todir).Create() )
907
		m_iError = CATERR_CANTCREATEDIR;
908
	else
909
	{
910
		CyString file = todir;
911
		if ( !file.Empty() )
912
			file += "/";
913
		file += tofile;
914
		file = file.FindReplace("/", "\\");
915
		file = file.FindReplace("\\\\", "\\");
916
 
52 cycrow 917
		CFileIO File(file.c_str());
918
		if ( !File.startWrite() ) m_iError = CATERR_INVALIDDEST;
919
		else bWritten = File.write(data, size);
1 cycrow 920
	}
921
 
922
	delete data;
923
	f->sData = 0;
924
 
52 cycrow 925
	if ( bWritten ) this->ClearError();
926
 
927
	return bWritten;
1 cycrow 928
}
929
 
85 cycrow 930
const Utils::String &CCatFile::internalDatFilename() const
931
{
932
	return _sReadFilename;
933
}
934
 
1 cycrow 935
CyString CCatFile::GetErrorString ()
936
{
937
	switch ( m_iError )
938
	{
939
		case CATERR_NONE:
940
			return NullString;
941
		case CATERR_NODATFILE:
942
			return "Unable to open Dat file";
943
		case CATERR_NOCATFILE:
944
			return "Unable to open Cat file";
945
		case CATERR_FILEEMPTY:
946
			return "Cat file is empty";
947
		case CATERR_READCAT:
948
			return "Unable to read from cat file";
949
		case CATERR_DECRYPT:
950
			return "Unable to decrypt cat file";
951
		case CATERR_MISMATCH:
952
			return "File size mismatch with Dat file";
953
		case CATERR_NOFILE:
954
			return "Unable to find file in archive";
955
		case CATERR_CANTREAD:
956
			return "Unable to read file in archive";
957
		case CATERR_CANTCREATEDIR:
958
			return "Unable to create destiantion directory";
959
		case CATERR_INVALIDDEST:
960
			return "Unable to write to destiantion file";
961
	}
962
 
963
	return "Invalid";
964
}
965
 
966
 
967
unsigned char *CompressPCKData ( unsigned char *buffer, size_t size, size_t *retsize, time_t mtime )
968
{
969
	size_t newsize = (size * 2) + 20;
970
	unsigned char *data = (unsigned char *)malloc ( sizeof(unsigned char) * newsize );
971
 
972
	z_stream zs;
973
	char flags=0;
974
 
975
//	error(0);
976
 
977
	unsigned char *d = data;
978
//	if(m_pszComment && strlen(m_pszComment) > 0) flags|=GZ_F_COMMENT;
979
//	if(m_pszFileName && strlen(m_pszFileName) > 0) flags|=GZ_F_FILENAME;
980
 
981
	int pos = PCKHEADERSIZE;
982
	*d = 0x1F; 		d++;
983
	*d = 0x8B; 		d++;
984
	*d = 8;			d++;
985
	*d = flags; 	d++;
986
	memcpy(d, &mtime, sizeof(mtime));
987
	d += 4;
988
	*d = 0; 		d++;
989
	*d = 11; 		d++;
990
//	if(flags & GZ_F_FILENAME) put((const char *)m_pszFileName);
991
//	if(flags & GZ_F_COMMENT) put((const char *)m_pszComment);
992
 
993
	memset(&zs, 0, sizeof(zs));
994
	zs.next_in=buffer;
995
	zs.avail_in=(long)size;
996
 
997
	int ret;
998
	unsigned long ubound;
999
	ret=deflateInit2(&zs, 9, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
1000
	if(ret!=Z_OK)
1001
		return false;
1002
 
1003
	ubound=deflateBound(&zs, (unsigned long)size);
1004
	if ( newsize < ubound)
1005
	{
1006
		newsize += ubound;
1007
		data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
1008
	}
1009
 
1010
 
1011
	zs.next_out=d;
1012
	zs.avail_out=(unsigned int)newsize - pos;
1013
 
1014
	while((ret=deflate(&zs, Z_FINISH))==Z_OK)
1015
	{
1016
		newsize += 1024;
1017
		data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
1018
		zs.next_out=data + zs.total_out;
1019
		zs.avail_out=(unsigned int)newsize - zs.total_out;
1020
	}
1021
	pos += zs.total_out;
1022
 
1023
	deflateEnd(&zs);
1024
 
1025
	unsigned long crc=crc32(0, NULL, 0);
1026
	crc=crc32(crc, buffer, (unsigned int)size);
1027
 
1028
	int s = sizeof(crc) + sizeof(size);
1029
	if ( newsize < (size_t)(s + pos) )
1030
	{
1031
		newsize += (s + pos) - newsize;
1032
		data = (unsigned char *)realloc ( data, sizeof(unsigned char) * newsize );
1033
	}
1034
 
1035
	memcpy(&data[pos], &crc, sizeof(crc));
1036
	pos += sizeof(crc);
1037
	memcpy(&data[pos], &size, sizeof(size));
1038
	pos += sizeof(size);
1039
 
1040
	newsize = pos;
1041
 
1042
	unsigned char *retdata = NULL;
1043
	if ( ret == Z_STREAM_END )
1044
	{
1045
		*retsize = newsize;
1046
		retdata = new unsigned char[newsize];
1047
		memcpy ( retdata, data, newsize );
1048
	}
1049
	free ( data );
1050
 
1051
	return retdata;
1052
}
1053
 
1054
unsigned char *PCKData ( unsigned char *data, size_t oldsize, size_t *newsize, bool bXor )
1055
{
1056
	unsigned char *newdata = CompressPCKData ( data, oldsize, newsize, time(NULL) );
1057
	if ( !bXor )
1058
		return newdata;
1059
 
1060
	if ( newdata )
1061
	{
1062
		char magic = (char)clock(), m;
1063
		m=magic ^ 0xC8;
1064
 
1065
		unsigned char *ptr = newdata, *end = newdata + *newsize;
1066
		// XOR encryption
1067
		if ( bXor )
1068
		{
1069
			for ( ; ptr < end; ptr++ )
1070
				(*ptr)^=magic;
1071
		}
1072
 
1073
		unsigned char *finalData = new unsigned char[*newsize + 1];
1074
		finalData[0] = m;
1075
		memcpy ( finalData + 1, newdata, *newsize );
1076
		delete [] newdata;
1077
		(*newsize)++;
1078
		return finalData;
1079
	}
1080
 
1081
	return NULL;
1082
}
1083
 
1084
bool CCatFile::WriteFromCat ( CCatFile *fcat, CyString file )
1085
{
1086
	// now find the file in the cat file
1087
	SInCatFile *getfile = fcat->FindData ( file );
1088
	if ( !getfile )
1089
		return false;
1090
 
1091
	// read the dat from the cat file
1092
	size_t size = 0;
53 cycrow 1093
	unsigned char *data = fcat->readData ( getfile, &size );
1 cycrow 1094
	if ( !data )
1095
		return false;
1096
 
1097
	// now check if it exists in this file
1098
	RemoveFile ( file );
1099
 
58 cycrow 1100
	int offset = (m_lFiles.empty()) ? 0 : (m_lFiles.Back()->Data()->lOffset + m_lFiles.Back()->Data()->lSize);
1 cycrow 1101
	// now write to the new file
58 cycrow 1102
	if ( getfile->bDecrypted ) this->DecryptDAT(getfile->sData, getfile->lSize);
1103
	getfile->bDecrypted = false;
1104
	if ( !m_fDatFile.AppendDataToPos((const char *)getfile->sData, getfile->lSize, offset) ) return false;
1105
    m_fDatFile.close();
1 cycrow 1106
 
1107
	// finally add to the list
1108
	SInCatFile *f = new SInCatFile;
1109
	f->sData = 0;
1110
	if ( m_lFiles.empty() )
1111
		f->lOffset = 0;
1112
	else
1113
		f->lOffset = m_lFiles.Back()->Data()->lOffset + m_lFiles.Back()->Data()->lSize;
1114
	f->sFile = file;
1115
	f->lSize = size;
1116
	m_lFiles.push_back ( f );
53 cycrow 1117
	m_bCatChanged = true;
1 cycrow 1118
 
1119
	return true;
1120
}
1121
 
1122
bool CCatFile::WriteFromCat ( CyString catfile, CyString file )
1123
{
1124
	CCatFile fcat;
1125
	if ( fcat.Open ( catfile, "", CATREAD_CATDECRYPT, false ) )
1126
		return false;
1127
 
1128
	return this->WriteFromCat(&fcat, file);
1129
}
1130
 
1131
SInCatFile *CCatFile::FindData ( CyString filename )
1132
{
1133
	if ( m_lFiles.empty() )
1134
		return NULL;
1135
 
1136
	CyString check = filename;
1137
	check = check.FindReplace ( "\\", "/" );
1138
	for ( SInCatFile *c = m_lFiles.First(); c; c = m_lFiles.Next() )
1139
	{
1140
		CyString f = c->sFile;
1141
		f = f.FindReplace ( "\\", "/" );
1142
		if ( f.Compare(check) )
1143
			return c;
1144
	}
1145
	return NULL;
1146
}
1147
 
1148
bool CCatFile::MarkRemoveFile( CyString file )
1149
{
1150
	SInCatFile *f = FindData ( file );
1151
	if ( !f )
1152
	{
1153
		m_iError = CATERR_NOFILE;
1154
		return false;
1155
	}
1156
 
1157
	return MarkRemoveFile ( f );
1158
}
1159
bool CCatFile::MarkRemoveFile ( SInCatFile *f )
1160
{
1161
	f->bDelete = true;
1162
	return true;
1163
}
1164
 
1165
CyStringList *CCatFile::GetTShipsEntries()
1166
{
1167
	if ( this->ExtractFile("types/tships.pck", "tships.txt") )
1168
	{
1169
		CFileIO TShips("tships.txt");
52 cycrow 1170
		if ( TShips.exists() )
1 cycrow 1171
		{
1172
			CyStringList *lines = TShips.ReadLinesStr();
1173
			if ( lines )
1174
			{
1175
				// remove any commands, blank lines, and start
1176
				// and put the id as the data
1177
				int entries = -1;
1178
				for ( SStringList *str = lines->Head(); str; str = str->next )
1179
				{
1180
					str->str.RemoveFirstSpace();
1181
					str->str.RemoveChar(9);
1182
					str->str.RemoveChar('\r');
1183
					str->str.RemoveChar('\n');
1184
					if ( str->str.Empty() )
1185
					{
1186
						str->remove = true;
1187
						continue;
1188
					}
1189
 
1190
					if ( str->str[0] == '/' )
1191
					{
1192
						str->remove = true;
1193
						continue;
1194
					}
1195
 
1196
					if ( entries == -1 )
1197
					{
1198
						entries = str->str.GetToken(";", 2, 2).ToInt();
1199
						str->remove = true;
1200
						continue;
1201
					}
1202
 
1203
					// hopefully we now have a valid tships line
1204
					int num = -1;
1205
					while ( str->data.Empty() )
1206
						str->data = str->str.GetToken(";", num--, (num + 2));
1207
				}
1208
			}
1209
			// remove the tmp file
52 cycrow 1210
			TShips.remove();
1 cycrow 1211
 
1212
			if ( lines )
1213
				lines->RemoveMarked();
1214
			return lines;
1215
		}
1216
	}
1217
 
1218
	return NULL;
1219
}
1220
 
1221
CyString CCatFile::GetTShipsEntry(CyString id)
1222
{
1223
	CyStringList *ships = this->GetTShipsEntries();
1224
	if ( ships )
1225
	{
1226
		SStringList *node = ships->FindData(id.upper());
1227
		if ( node )
1228
			return node->str;
1229
	}
1230
	return NullString;
1231
}
1232