Subversion Repositories spk

Rev

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