Subversion Repositories spk

Rev

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