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 }