Subversion Repositories spk

Rev

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