Subversion Repositories spk

Rev

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