Subversion Repositories spk

Rev

Rev 82 | Rev 85 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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