1 /*
2 * HLLib
3 * Copyright (C) 2006-2012 Ryan Gregg
4
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later
9 * version.
10 */
11
12 #include "HLLib.h"
13 #include "SGAFile.h"
14 #include "Streams.h"
15 #include "Checksum.h"
16 #include "Utility.h"
17
18 #if USE_ZLIB
19 # ifdef _WIN32
20 # define ZLIB_WINAPI
21 # endif
22 # include <zlib.h>
23 #endif
24
25 using namespace HLLib;
26
27 #define HL_SGA_CHECKSUM_LENGTH 0x00008000
28
29 const char *CSGAFile::lpAttributeNames[] = { "Major Version", "Minor Version", "File MD5", "Name", "Header MD5" };
30 const char *CSGAFile::lpItemAttributeNames[] = { "Section Alias", "Section Name", "Modified", "Type", "CRC", "Verification" };
31 const char *CSGAFile::lpVerificationNames[] = { "None", "CRC", "CRC Blocks", "MD5 Blocks", "SHA1 Blocks" };
32
CSGAFile()33 CSGAFile::CSGAFile() : CPackage(), pHeaderView(0), pHeader(0), pDirectory(0)
34 {
35
36 }
37
~CSGAFile()38 CSGAFile::~CSGAFile()
39 {
40 this->Close();
41 }
42
GetType() const43 HLPackageType CSGAFile::GetType() const
44 {
45 return HL_PACKAGE_SGA;
46 }
47
GetExtension() const48 const hlChar *CSGAFile::GetExtension() const
49 {
50 return "sga";
51 }
52
GetDescription() const53 const hlChar *CSGAFile::GetDescription() const
54 {
55 return "Archive File";
56 }
57
MapDataStructures()58 hlBool CSGAFile::MapDataStructures()
59 {
60 hlULongLong uiMaxHeaderSize = std::max(sizeof(SGAHeader4), sizeof(SGAHeader6));
61 if(uiMaxHeaderSize > this->pMapping->GetMappingSize())
62 {
63 LastError.SetErrorMessage("Invalid file: the file map is too small for it's header.");
64 return hlFalse;
65 }
66
67 if(!this->pMapping->Map(this->pHeaderView, 0, uiMaxHeaderSize))
68 {
69 return hlFalse;
70 }
71 this->pHeader = static_cast<const SGAHeaderBase *>(this->pHeaderView->GetView());
72
73 if(memcmp(this->pHeader->lpSignature, "_ARCHIVE", 8) != 0)
74 {
75 LastError.SetErrorMessage("Invalid file: the file's signature does not match.");
76 return hlFalse;
77 }
78
79 if((this->pHeader->uiMajorVersion != 4 || this->pHeader->uiMinorVersion != 0) &&
80 (this->pHeader->uiMajorVersion != 5 || this->pHeader->uiMinorVersion != 0) &&
81 (this->pHeader->uiMajorVersion != 6 || this->pHeader->uiMinorVersion != 0) &&
82 (this->pHeader->uiMajorVersion != 7 || this->pHeader->uiMinorVersion != 0))
83 {
84 LastError.SetErrorMessageFormated("Invalid SGA version (v%hu.%hu): you have a version of a SGA file that HLLib does not know how to read. Check for product updates.", this->pHeader->uiMajorVersion, this->pHeader->uiMinorVersion);
85 return hlFalse;
86 }
87
88 switch(this->pHeader->uiMajorVersion)
89 {
90 case 4:
91 if(static_cast<const CSGADirectory4::SGAHeader*>(this->pHeader)->uiHeaderLength > this->pMapping->GetMappingSize())
92 {
93 LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header.");
94 return hlFalse;
95 }
96
97 this->pDirectory = new CSGADirectory4(*this);
98 break;
99 case 5:
100 if(static_cast<const CSGADirectory5::SGAHeader*>(this->pHeader)->uiHeaderLength > this->pMapping->GetMappingSize())
101 {
102 LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header.");
103 return hlFalse;
104 }
105
106 this->pDirectory = new CSGADirectory5(*this);
107 break;
108 case 6:
109 if(static_cast<const CSGADirectory6::SGAHeader*>(this->pHeader)->uiHeaderLength > this->pMapping->GetMappingSize())
110 {
111 LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header.");
112 return hlFalse;
113 }
114
115 this->pDirectory = new CSGADirectory6(*this);
116 break;
117 case 7:
118 if(static_cast<const CSGADirectory7::SGAHeader*>(this->pHeader)->uiHeaderLength > this->pMapping->GetMappingSize())
119 {
120 LastError.SetErrorMessage("Invalid file: the file map is too small for it's extended header.");
121 return hlFalse;
122 }
123
124 this->pDirectory = new CSGADirectory7(*this);
125 break;
126 default:
127 assert(false);
128 return hlFalse;
129 }
130
131 if(!this->pDirectory->MapDataStructures())
132 {
133 return hlFalse;
134 }
135
136 return hlTrue;
137 }
138
UnmapDataStructures()139 hlVoid CSGAFile::UnmapDataStructures()
140 {
141 delete this->pDirectory;
142 this->pDirectory = 0;
143
144 this->pHeader = 0;
145
146 this->pMapping->Unmap(this->pHeaderView);
147 }
148
CreateRoot()149 CDirectoryFolder *CSGAFile::CreateRoot()
150 {
151 return this->pDirectory->CreateRoot();
152 }
153
GetItemAttributeInternal(const CDirectoryItem * pItem,HLPackageAttribute eAttribute,HLAttribute & Attribute) const154 hlBool CSGAFile::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
155 {
156 return this->pDirectory->GetItemAttributeInternal(pItem, eAttribute, Attribute);
157 }
158
GetFileExtractableInternal(const CDirectoryFile * pFile,hlBool & bExtractable) const159 hlBool CSGAFile::GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const
160 {
161 return this->pDirectory->GetFileExtractableInternal(pFile, bExtractable);
162 }
163
GetFileValidationInternal(const CDirectoryFile * pFile,HLValidation & eValidation) const164 hlBool CSGAFile::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
165 {
166 return this->pDirectory->GetFileValidationInternal(pFile, eValidation);
167 }
168
GetFileSizeInternal(const CDirectoryFile * pFile,hlUInt & uiSize) const169 hlBool CSGAFile::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
170 {
171 return this->pDirectory->GetFileSizeInternal(pFile, uiSize);
172 }
173
GetFileSizeOnDiskInternal(const CDirectoryFile * pFile,hlUInt & uiSize) const174 hlBool CSGAFile::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
175 {
176 return this->pDirectory->GetFileSizeOnDiskInternal(pFile, uiSize);
177 }
178
CreateStreamInternal(const CDirectoryFile * pFile,Streams::IStream * & pStream) const179 hlBool CSGAFile::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const
180 {
181 return this->pDirectory->CreateStreamInternal(pFile, pStream);
182 }
183
ReleaseStreamInternal(Streams::IStream & Stream) const184 hlVoid CSGAFile::ReleaseStreamInternal(Streams::IStream &Stream) const
185 {
186 return this->pDirectory->ReleaseStreamInternal(Stream);
187 }
188
189
GetAttributeCountInternal() const190 hlUInt CSGAFile::GetAttributeCountInternal() const
191 {
192 return HL_SGA_PACKAGE_COUNT;
193 }
194
GetAttributeNameInternal(HLPackageAttribute eAttribute) const195 const hlChar *CSGAFile::GetAttributeNameInternal(HLPackageAttribute eAttribute) const
196 {
197 if(eAttribute < HL_SGA_PACKAGE_COUNT)
198 {
199 return this->lpAttributeNames[eAttribute];
200 }
201
202 return 0;
203 }
204
GetAttributeInternal(HLPackageAttribute eAttribute,HLAttribute & Attribute) const205 hlBool CSGAFile::GetAttributeInternal(HLPackageAttribute eAttribute, HLAttribute &Attribute) const
206 {
207 hlChar lpBuffer[64];
208 switch(eAttribute)
209 {
210 case HL_SGA_PACKAGE_VERSION_MAJOR:
211 hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiMajorVersion, hlFalse);
212 return hlTrue;
213 case HL_SGA_PACKAGE_VERSION_MINOR:
214 hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiMinorVersion, hlFalse);
215 return hlTrue;
216 case HL_SGA_PACKAGE_MD5_FILE:
217 if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5)
218 {
219 BufferToHexString(static_cast<const SGAHeader4 *>(this->pHeader)->lpFileMD5, 16, lpBuffer, sizeof(lpBuffer));
220 hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer);
221 return hlTrue;
222 }
223 return hlFalse;
224 case HL_SGA_PACKAGE_NAME:
225 if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5)
226 {
227 WStringToString(static_cast<const SGAHeader4 *>(this->pHeader)->lpName, lpBuffer, sizeof(lpBuffer));
228 hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer);
229 return hlTrue;
230 }
231 if(this->pHeader->uiMajorVersion >= 6 && this->pHeader->uiMajorVersion <= 6)
232 {
233 WStringToString(static_cast<const SGAHeader6 *>(this->pHeader)->lpName, lpBuffer, sizeof(lpBuffer));
234 hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer);
235 return hlTrue;
236 }
237 return hlFalse;
238 case HL_SGA_PACKAGE_MD5_HEADER:
239 if(this->pHeader->uiMajorVersion >= 4 && this->pHeader->uiMajorVersion <= 5)
240 {
241 BufferToHexString(static_cast<const SGAHeader4 *>(this->pHeader)->lpHeaderMD5, 16, lpBuffer, sizeof(lpBuffer));
242 hlAttributeSetString(&Attribute, this->lpAttributeNames[eAttribute], lpBuffer);
243 return hlTrue;
244 }
245 return hlFalse;
246 default:
247 return hlFalse;
248 }
249 }
250
GetItemAttributeCountInternal() const251 hlUInt CSGAFile::GetItemAttributeCountInternal() const
252 {
253 return HL_SGA_ITEM_COUNT;
254 }
255
GetItemAttributeNameInternal(HLPackageAttribute eAttribute) const256 const hlChar *CSGAFile::GetItemAttributeNameInternal(HLPackageAttribute eAttribute) const
257 {
258 if(eAttribute < HL_SGA_ITEM_COUNT)
259 {
260 return this->lpItemAttributeNames[eAttribute];
261 }
262
263 return 0;
264 }
265
~ISGADirectory()266 CSGAFile::ISGADirectory::~ISGADirectory()
267 {
268
269 }
270
271 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
CSGASpecializedDirectory(CSGAFile & File)272 CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0)
273 {
274
275 }
276
277 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
CSGASpecializedDirectory(CSGAFile & File)278 CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile4>::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0)
279 {
280
281 }
282
283 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
CSGASpecializedDirectory(CSGAFile & File)284 CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile6>::CSGASpecializedDirectory(CSGAFile& File) : File(File), pHeaderDirectoryView(0), pDirectoryHeader(0), lpSections(0), lpFolders(0), lpFiles(0), lpStringTable(0)
285 {
286
287 }
288
289 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
CSGADirectory(CSGAFile & File)290 CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CSGADirectory(CSGAFile& File) : CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>(File)
291 {
292
293 }
294
295 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
~CSGADirectory()296 CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::~CSGADirectory()
297 {
298 this->UnmapDataStructures();
299 }
300
301 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
MapDataStructures()302 hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::MapDataStructures()
303 {
304 if(!this->File.pMapping->Map(this->pHeaderDirectoryView, sizeof(SGAHeader), static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength))
305 {
306 return hlFalse;
307 }
308
309 this->pDirectoryHeader = static_cast<const SGADirectoryHeader *>(this->pHeaderDirectoryView->GetView());
310
311 if(this->pDirectoryHeader->uiSectionCount > 0 && this->pDirectoryHeader->uiSectionOffset + sizeof(SGASection) * this->pDirectoryHeader->uiSectionCount > static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength)
312 {
313 LastError.SetErrorMessage("Invalid file: the file map is too small for section data.");
314 return hlFalse;
315 }
316 if(this->pDirectoryHeader->uiFolderCount > 0 && this->pDirectoryHeader->uiFolderOffset + sizeof(SGAFolder) * this->pDirectoryHeader->uiFolderCount > static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength)
317 {
318 LastError.SetErrorMessage("Invalid file: the file map is too small for folder data.");
319 return hlFalse;
320 }
321 if(this->pDirectoryHeader->uiFileCount > 0 && this->pDirectoryHeader->uiFileOffset + sizeof(SGAFile) * this->pDirectoryHeader->uiFileCount > static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength)
322 {
323 LastError.SetErrorMessage("Invalid file: the file map is too small for file data.");
324 return hlFalse;
325 }
326 if(this->pDirectoryHeader->uiStringTableOffset > static_cast<const SGAHeader *>(this->File.pHeader)->uiHeaderLength)
327 {
328 LastError.SetErrorMessage("Invalid file: the file map is too small for string table data.");
329 return hlFalse;
330 }
331
332 this->lpSections = reinterpret_cast<const SGASection *>(reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiSectionOffset);
333 this->lpFolders = reinterpret_cast<const SGAFolder *>(reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiFolderOffset);
334 this->lpFiles = reinterpret_cast<const SGAFile *>(reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiFileOffset);
335 this->lpStringTable = reinterpret_cast<const hlChar *>(reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiStringTableOffset);
336
337 return hlTrue;
338 }
339
340 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
UnmapDataStructures()341 hlVoid CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::UnmapDataStructures()
342 {
343 this->pDirectoryHeader = 0;
344 this->lpSections = 0;
345 this->lpFolders = 0;
346 this->lpFiles = 0;
347 this->lpStringTable = 0;
348
349 this->File.pMapping->Unmap(this->pHeaderDirectoryView);
350 }
351
352 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
CreateRoot()353 CDirectoryFolder *CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CreateRoot()
354 {
355 CDirectoryFolder *pRoot = new CDirectoryFolder(&File);
356
357 for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
358 {
359 CDirectoryFolder* pSection;
360 // Check if folder exists.
361 CDirectoryItem *pItem = pRoot->GetItem(this->lpSections[i].lpAlias);
362 if(pItem == 0 || pItem->GetType() == HL_ITEM_FILE)
363 {
364 // It doesn't, create it.
365 pSection = pRoot->AddFolder(this->lpSections[i].lpAlias);
366 }
367 else
368 {
369 // It does, use it.
370 pSection = static_cast<CDirectoryFolder *>(pItem);
371 }
372 this->CreateFolder(pSection, this->lpSections[i].uiFolderRootIndex);
373 }
374
375 return pRoot;
376 }
377
378 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
CreateFolder(CDirectoryFolder * pParent,hlUInt uiFolderIndex)379 hlVoid CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CreateFolder(CDirectoryFolder *pParent, hlUInt uiFolderIndex)
380 {
381 const hlChar* lpName = this->lpStringTable + this->lpFolders[uiFolderIndex].uiNameOffset;
382 if(*lpName != '\0')
383 {
384 // Strip parent folder names.
385 const hlChar* lpTemp = strrchr(lpName, '/');
386 if(lpTemp != 0)
387 {
388 lpName = lpTemp + 1;
389 }
390 lpTemp = strrchr(lpName, '\\');
391 if(lpTemp != 0)
392 {
393 lpName = lpTemp + 1;
394 }
395 // Check if folder exists.
396 CDirectoryItem *pItem = pParent->GetItem(lpName);
397 if(pItem == 0 || pItem->GetType() == HL_ITEM_FILE)
398 {
399 // It doesn't, create it.
400 pParent = pParent->AddFolder(lpName);
401 }
402 else
403 {
404 // It does, use it.
405 pParent = static_cast<CDirectoryFolder *>(pItem);
406 }
407 }
408 for(hlUInt i = this->lpFolders[uiFolderIndex].uiFolderStartIndex; i < this->lpFolders[uiFolderIndex].uiFolderEndIndex; i++)
409 {
410 CreateFolder(pParent, i);
411 }
412 for(hlUInt i = this->lpFolders[uiFolderIndex].uiFileStartIndex; i < this->lpFolders[uiFolderIndex].uiFileEndIndex; i++)
413 {
414 const hlChar* lpName = this->lpStringTable + this->lpFiles[i].uiNameOffset;
415 pParent->AddFile(lpName, i);
416 }
417 }
418
419 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
GetItemAttributeInternal(const CDirectoryItem * pItem,HLPackageAttribute eAttribute,HLAttribute & Attribute) const420 hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile4>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
421 {
422 if(pItem->GetID() != HL_ID_INVALID)
423 {
424 switch(pItem->GetType())
425 {
426 case HL_ITEM_FILE:
427 {
428 const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
429 const SGAFile &File = this->lpFiles[pFile->GetID()];
430 switch(eAttribute)
431 {
432 case HL_SGA_ITEM_CRC:
433 {
434 Mapping::CView *pFileHeaderView = 0;
435 if(this->File.pMapping->Map(pFileHeaderView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset - sizeof(SGAFileHeader), sizeof(SGAFileHeader)))
436 {
437 const SGAFileHeader* pFileHeader = static_cast<const SGAFileHeader *>(pFileHeaderView->GetView());
438 hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], pFileHeader->uiCRC32, hlTrue);
439 this->File.pMapping->Unmap(pFileHeaderView);
440 return hlTrue;
441 }
442 return hlFalse;
443 }
444 case HL_SGA_ITEM_VERIFICATION:
445 {
446 hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], CSGAFile::lpVerificationNames[CSGAFile::VERIFICATION_CRC]);
447 return hlTrue;
448 }
449 }
450 break;
451 }
452 }
453 }
454 return hlFalse;
455 }
456
457 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
GetItemAttributeInternal(const CDirectoryItem * pItem,HLPackageAttribute eAttribute,HLAttribute & Attribute) const458 hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile6>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
459 {
460 if(pItem->GetID() != HL_ID_INVALID)
461 {
462 switch(pItem->GetType())
463 {
464 case HL_ITEM_FILE:
465 {
466 const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
467 const SGAFile &File = this->lpFiles[pFile->GetID()];
468 switch(eAttribute)
469 {
470 case HL_SGA_ITEM_CRC:
471 {
472 hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], File.uiCRC32, hlTrue);
473 return hlTrue;
474 }
475 case HL_SGA_ITEM_VERIFICATION:
476 {
477 hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], CSGAFile::lpVerificationNames[CSGAFile::VERIFICATION_CRC]);
478 return hlTrue;
479 }
480 }
481 break;
482 }
483 }
484 }
485 return hlFalse;
486 }
487
488 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
GetItemAttributeInternal(const CDirectoryItem * pItem,HLPackageAttribute eAttribute,HLAttribute & Attribute) const489 hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
490 {
491 if(pItem->GetID() != HL_ID_INVALID)
492 {
493 switch(pItem->GetType())
494 {
495 case HL_ITEM_FILE:
496 {
497 const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
498 const SGAFile &File = this->lpFiles[pFile->GetID()];
499 switch(eAttribute)
500 {
501 case HL_SGA_ITEM_CRC:
502 {
503 hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], File.uiCRC32, hlTrue);
504 return hlTrue;
505 }
506 case HL_SGA_ITEM_VERIFICATION:
507 {
508 hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], CSGAFile::lpVerificationNames[File.uiDummy0 < CSGAFile::VERIFICATION_COUNT ? File.uiDummy0 : CSGAFile::VERIFICATION_NONE]);
509 return hlTrue;
510 }
511 }
512 break;
513 }
514 }
515 }
516 return hlFalse;
517 }
518
519 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
GetItemAttributeInternal(const CDirectoryItem * pItem,HLPackageAttribute eAttribute,HLAttribute & Attribute) const520 hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
521 {
522 if(pItem->GetID() != HL_ID_INVALID)
523 {
524 switch(pItem->GetType())
525 {
526 case HL_ITEM_FOLDER:
527 {
528 const CDirectoryFolder *pFolder = static_cast<const CDirectoryFolder *>(pItem);
529 switch(eAttribute)
530 {
531 case HL_SGA_ITEM_SECTION_ALIAS:
532 {
533 for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
534 {
535 if(pFolder->GetID() >= this->lpSections[i].uiFolderStartIndex && pFolder->GetID() < this->lpSections[i].uiFolderEndIndex)
536 {
537 hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpAlias);
538 return hlTrue;
539 }
540 }
541 return hlFalse;
542 }
543 case HL_SGA_ITEM_SECTION_NAME:
544 {
545 for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
546 {
547 if(pFolder->GetID() >= this->lpSections[i].uiFolderStartIndex && pFolder->GetID() < this->lpSections[i].uiFolderEndIndex)
548 {
549 hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpName);
550 return hlTrue;
551 }
552 }
553 return hlFalse;
554 }
555 }
556 break;
557 }
558 case HL_ITEM_FILE:
559 {
560 const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
561 const SGAFile &File = this->lpFiles[pFile->GetID()];
562 switch(eAttribute)
563 {
564 case HL_SGA_ITEM_SECTION_ALIAS:
565 {
566 for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
567 {
568 if(pFile->GetID() >= this->lpSections[i].uiFileStartIndex && pFile->GetID() < this->lpSections[i].uiFileEndIndex)
569 {
570 hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpAlias);
571 return hlTrue;
572 }
573 }
574 return hlFalse;
575 }
576 case HL_SGA_ITEM_SECTION_NAME:
577 {
578 for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++)
579 {
580 if(pFile->GetID() >= this->lpSections[i].uiFileStartIndex && pFile->GetID() < this->lpSections[i].uiFileEndIndex)
581 {
582 hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpName);
583 return hlTrue;
584 }
585 }
586 return hlFalse;
587 }
588 case HL_SGA_ITEM_MODIFIED:
589 {
590 time_t Time = (time_t)File.uiTimeModified;
591 tm *pTime = localtime(&Time);
592
593 hlChar lpTime[128];
594 strftime(lpTime, sizeof(lpTime), "%c", pTime);
595
596 hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], lpTime);
597 return hlTrue;
598 }
599 case HL_SGA_ITEM_TYPE:
600 {
601 hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], File.uiType, hlFalse);
602 return hlTrue;
603 }
604 }
605 break;
606 }
607 }
608 }
609 return CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetItemAttributeInternal(pItem, eAttribute, Attribute);
610 }
611
612 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
GetFileExtractableInternal(const CDirectoryFile * pFile,hlBool & bExtractable) const613 hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const
614 {
615 #if !USE_ZLIB
616 const SGAFile &File = this->lpFiles[pFile->GetID()];
617
618 bExtractable = File.uiType == 0;
619 #else
620 bExtractable = true;
621 #endif
622
623 return hlTrue;
624 }
625
626 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
GetFileValidationInternal(const CDirectoryFile * pFile,HLValidation & eValidation) const627 hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile4>::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
628 {
629 const SGAFile &File = this->lpFiles[pFile->GetID()];
630
631 #if !USE_ZLIB
632 if(File.uiType != 0)
633 {
634 eValidation = HL_VALIDATES_ASSUMED_OK;
635 return hlTrue;
636 }
637 #endif
638
639 Mapping::CView *pFileHeaderDataView = 0;
640 if(this->File.pMapping->Map(pFileHeaderDataView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset - sizeof(SGAFileHeader), File.uiSizeOnDisk + sizeof(SGAFileHeader)))
641 {
642 hlULong uiChecksum = 0;
643 const SGAFileHeader* pFileHeader = static_cast<const SGAFileHeader*>(pFileHeaderDataView->GetView());
644 const hlByte* lpBuffer = reinterpret_cast<const hlByte *>(pFileHeader) + sizeof(SGAFileHeader);
645 #if USE_ZLIB
646 hlByte *lpInflateBuffer = 0;
647 if(File.uiType != 0)
648 {
649 lpInflateBuffer = new hlByte[File.uiSize];
650 uLongf iInflateSize = File.uiSize;
651 switch(uncompress(lpInflateBuffer, &iInflateSize, lpBuffer, static_cast<uLong>(File.uiSizeOnDisk)))
652 {
653 case Z_OK:
654 lpBuffer = lpInflateBuffer;
655 break;
656 default:
657 delete []lpInflateBuffer;
658 lpInflateBuffer = 0;
659 eValidation = HL_VALIDATES_ERROR;
660 break;
661 }
662 }
663 if(File.uiType == 0 || lpInflateBuffer != 0)
664 #endif
665 {
666 hlULongLong uiTotalBytes = 0, uiFileBytes = File.uiSize;
667
668 hlBool bCancel = hlFalse;
669 hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
670
671 while(uiTotalBytes < uiFileBytes)
672 {
673 if(bCancel)
674 {
675 eValidation = HL_VALIDATES_CANCELED;
676 break;
677 }
678
679 hlUInt uiBufferSize = static_cast<hlUInt>(uiTotalBytes + HL_SGA_CHECKSUM_LENGTH <= uiFileBytes ? HL_SGA_CHECKSUM_LENGTH : uiFileBytes - uiTotalBytes);
680 uiChecksum = CRC32(lpBuffer, uiBufferSize, uiChecksum);
681
682 lpBuffer += uiBufferSize;
683 uiTotalBytes += static_cast<hlULongLong>(uiBufferSize);
684
685 hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
686 }
687 }
688 #if USE_ZLIB
689 delete []lpInflateBuffer;
690 #endif
691 if(eValidation == HL_VALIDATES_ASSUMED_OK)
692 {
693 eValidation = static_cast<hlULong>(pFileHeader->uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT;
694 }
695
696 this->File.pMapping->Unmap(pFileHeaderDataView);
697 }
698 else
699 {
700 eValidation = HL_VALIDATES_ERROR;
701 }
702
703 return hlTrue;
704 }
705
706 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder>
GetFileValidationInternal(const CDirectoryFile * pFile,HLValidation & eValidation) const707 hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, CSGAFile::SGAFile6>::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
708 {
709 const SGAFile &File = this->lpFiles[pFile->GetID()];
710
711 Mapping::CView *pFileHeaderDataView = 0;
712 if(this->File.pMapping->Map(pFileHeaderDataView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk))
713 {
714 hlULong uiChecksum = 0;
715 const hlByte* lpBuffer = reinterpret_cast<const hlByte *>(pFileHeaderDataView->GetView());
716 hlULongLong uiTotalBytes = 0, uiFileBytes = File.uiSizeOnDisk;
717
718 hlBool bCancel = hlFalse;
719 hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
720
721 while(uiTotalBytes < uiFileBytes)
722 {
723 if(bCancel)
724 {
725 eValidation = HL_VALIDATES_CANCELED;
726 break;
727 }
728
729 hlUInt uiBufferSize = static_cast<hlUInt>(uiTotalBytes + HL_SGA_CHECKSUM_LENGTH <= uiFileBytes ? HL_SGA_CHECKSUM_LENGTH : uiFileBytes - uiTotalBytes);
730 uiChecksum = CRC32(lpBuffer, uiBufferSize, uiChecksum);
731
732 lpBuffer += uiBufferSize;
733 uiTotalBytes += static_cast<hlULongLong>(uiBufferSize);
734
735 hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
736 }
737 if(eValidation == HL_VALIDATES_ASSUMED_OK)
738 {
739 eValidation = static_cast<hlULong>(File.uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT;
740 }
741
742 this->File.pMapping->Unmap(pFileHeaderDataView);
743 }
744 else
745 {
746 eValidation = HL_VALIDATES_ERROR;
747 }
748
749 return hlTrue;
750 }
751
752 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
GetFileValidationInternal(const CDirectoryFile * pFile,HLValidation & eValidation) const753 hlBool CSGAFile::CSGASpecializedDirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
754 {
755 const SGAFile &File = this->lpFiles[pFile->GetID()];
756
757 Mapping::CView *pFileHeaderDataView = 0;
758 if(this->File.pMapping->Map(pFileHeaderDataView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk))
759 {
760 hlULong uiChecksum = 0;
761 const hlByte* lpBuffer = reinterpret_cast<const hlByte *>(pFileHeaderDataView->GetView());
762 hlULongLong uiTotalBytes = 0, uiFileBytes = File.uiSizeOnDisk;
763 hlULongLong uiBlockSize = this->pDirectoryHeader->uiBlockSize;
764 if(uiBlockSize == 0)
765 {
766 uiBlockSize = HL_SGA_CHECKSUM_LENGTH;
767 }
768
769 Checksum* checksum = 0;
770 switch(File.uiDummy0)
771 {
772 case CSGAFile::VERIFICATION_CRC_BLOCKS:
773 checksum = new CRC32Checksum();
774 break;
775 case CSGAFile::VERIFICATION_MD5_BLOCKS:
776 checksum = new MD5Checksum();
777 break;
778 case CSGAFile::VERIFICATION_SHA1_BLOCKS:
779 checksum = new SHA1Checksum();
780 break;
781 }
782 const hlByte *lpHashTable = reinterpret_cast<const hlByte *>(this->pDirectoryHeader) + this->pDirectoryHeader->uiHashTableOffset + File.uiHashOffset;
783
784 hlBool bCancel = hlFalse;
785 hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
786
787 while(uiTotalBytes < uiFileBytes)
788 {
789 if(bCancel)
790 {
791 eValidation = HL_VALIDATES_CANCELED;
792 break;
793 }
794
795 hlUInt uiBufferSize = static_cast<hlUInt>(uiTotalBytes + uiBlockSize <= uiFileBytes ? uiBlockSize : uiFileBytes - uiTotalBytes);
796 uiChecksum = CRC32(lpBuffer, uiBufferSize, uiChecksum);
797 if(checksum != 0)
798 {
799 checksum->Initialize();
800 checksum->Update(lpBuffer, uiBufferSize);
801 if(!checksum->Finalize(lpHashTable))
802 {
803 eValidation = HL_VALIDATES_CORRUPT;
804 break;
805 }
806 lpHashTable += checksum->GetDigestSize();
807 }
808
809 lpBuffer += uiBufferSize;
810 uiTotalBytes += static_cast<hlULongLong>(uiBufferSize);
811
812 hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
813 }
814 if(eValidation == HL_VALIDATES_ASSUMED_OK)
815 {
816 eValidation = static_cast<hlULong>(File.uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT;
817 }
818
819 delete checksum;
820
821 this->File.pMapping->Unmap(pFileHeaderDataView);
822 }
823 else
824 {
825 eValidation = HL_VALIDATES_ERROR;
826 }
827
828 return hlTrue;
829 }
830
831 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
GetFileSizeInternal(const CDirectoryFile * pFile,hlUInt & uiSize) const832 hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
833 {
834 const SGAFile &File = this->lpFiles[pFile->GetID()];
835
836 uiSize = File.uiSize;
837
838 return hlTrue;
839 }
840
841 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
GetFileSizeOnDiskInternal(const CDirectoryFile * pFile,hlUInt & uiSize) const842 hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
843 {
844 const SGAFile &File = this->lpFiles[pFile->GetID()];
845
846 uiSize = File.uiSizeOnDisk;
847
848 return hlTrue;
849 }
850
851 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
CreateStreamInternal(const CDirectoryFile * pFile,Streams::IStream * & pStream) const852 hlBool CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const
853 {
854 const SGAFile &File = this->lpFiles[pFile->GetID()];
855
856 if(File.uiType == 0)
857 {
858 pStream = new Streams::CMappingStream(*this->File.pMapping, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk);
859 return hlTrue;
860 }
861 else
862 {
863 #if USE_ZLIB
864 Mapping::CView *pFileDataView = 0;
865 if(this->File.pMapping->Map(pFileDataView, static_cast<const SGAHeader *>(this->File.pHeader)->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk))
866 {
867 hlBool bResult = hlFalse;
868 hlByte *lpInflateBuffer = new hlByte[File.uiSize];
869 uLongf iInflateSize = File.uiSize;
870 switch(uncompress(lpInflateBuffer, &iInflateSize, static_cast<const hlByte *>(pFileDataView->GetView()), (uLong)File.uiSizeOnDisk))
871 {
872 case Z_OK:
873 pStream = new Streams::CMemoryStream(lpInflateBuffer, iInflateSize);
874 bResult = hlTrue;
875 break;
876 case Z_MEM_ERROR:
877 delete []lpInflateBuffer;
878 LastError.SetErrorMessage("Deflate Error: Z_MEM_ERROR.");
879 break;
880 case Z_BUF_ERROR:
881 delete []lpInflateBuffer;
882 LastError.SetErrorMessage("Deflate Error: Z_BUF_ERROR.");
883 break;
884 case Z_DATA_ERROR:
885 delete []lpInflateBuffer;
886 LastError.SetErrorMessage("Deflate Error: Z_DATA_ERROR.");
887 break;
888 default:
889 delete []lpInflateBuffer;
890 LastError.SetErrorMessage("Deflate Error: Unknown.");
891 break;
892 }
893 this->File.pMapping->Unmap(pFileDataView);
894 return bResult;
895 }
896 #endif
897 return hlFalse;
898 }
899 }
900
901 template<typename TSGAHeader, typename TSGADirectoryHeader, typename TSGASection, typename TSGAFolder, typename TSGAFile>
ReleaseStreamInternal(Streams::IStream & Stream) const902 hlVoid CSGAFile::CSGADirectory<TSGAHeader, TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile>::ReleaseStreamInternal(Streams::IStream &Stream) const
903 {
904 if(Stream.GetType() == HL_STREAM_MEMORY)
905 {
906 delete []static_cast<const hlByte *>(static_cast<Streams::CMemoryStream &>(Stream).GetBuffer());
907 }
908 }