Subversion Repositories spk

Rev

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