hlBool CVPKFile::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const { const VPKDirectoryItem *pDirectoryItem = static_cast<const VPKDirectoryItem *>(pFile->GetData()); if(pDirectoryItem->pDirectoryEntry->uiEntryLength != 0) { if(pDirectoryItem->pDirectoryEntry->uiArchiveIndex == HL_VPK_NO_ARCHIVE) { if(pDirectoryItem->lpPreloadData != 0) { pStream = new Streams::CMemoryStream(const_cast<hlVoid *>(pDirectoryItem->lpPreloadData), pDirectoryItem->pDirectoryEntry->uiEntryLength); } else { return hlFalse; } } else if(this->lpArchives[pDirectoryItem->pDirectoryEntry->uiArchiveIndex].pMapping != 0) { if(pDirectoryItem->pDirectoryEntry->uiPreloadBytes != 0) { Mapping::CView *pView = 0; if(!this->lpArchives[pDirectoryItem->pDirectoryEntry->uiArchiveIndex].pMapping->Map(pView, pDirectoryItem->pDirectoryEntry->uiEntryOffset, pDirectoryItem->pDirectoryEntry->uiEntryLength)) { return hlFalse; } hlUInt uiBufferSize = pDirectoryItem->pDirectoryEntry->uiEntryLength + pDirectoryItem->pDirectoryEntry->uiPreloadBytes; hlByte *lpBuffer = new hlByte[uiBufferSize]; memcpy(lpBuffer, pDirectoryItem->lpPreloadData, pDirectoryItem->pDirectoryEntry->uiPreloadBytes); memcpy(lpBuffer + pDirectoryItem->pDirectoryEntry->uiPreloadBytes, pView->GetView(), pDirectoryItem->pDirectoryEntry->uiEntryLength); this->lpArchives[pDirectoryItem->pDirectoryEntry->uiArchiveIndex].pMapping->Unmap(pView); pStream = new Streams::CMemoryStream(lpBuffer, uiBufferSize); } else { pStream = new Streams::CMappingStream(*this->lpArchives[pDirectoryItem->pDirectoryEntry->uiArchiveIndex].pMapping, pDirectoryItem->pDirectoryEntry->uiEntryOffset, pDirectoryItem->pDirectoryEntry->uiEntryLength); } } else { return hlFalse; } } else if(pDirectoryItem->pDirectoryEntry->uiPreloadBytes != 0) { pStream = new Streams::CMemoryStream(const_cast<hlVoid *>(pDirectoryItem->lpPreloadData), pDirectoryItem->pDirectoryEntry->uiPreloadBytes); } else { pStream = new Streams::CNullStream(); } return hlTrue; }
hlBool CSGAFile::CSGADirectory<TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile, TSGAFileHeader>::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const { const TSGAFile &File = this->lpFiles[pFile->GetID()]; if(File.uiType == 0) { pStream = new Streams::CMappingStream(*this->File.pMapping, this->File.pHeader->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk); return hlTrue; } else { #if USE_ZLIB Mapping::CView *pFileDataView = 0; if(this->File.pMapping->Map(pFileDataView, this->File.pHeader->uiFileDataOffset + File.uiOffset, File.uiSizeOnDisk)) { hlBool bResult = hlFalse; hlByte *lpInflateBuffer = new hlByte[File.uiSize]; uLongf iInflateSize = File.uiSize; switch(uncompress(lpInflateBuffer, &iInflateSize, static_cast<const hlByte *>(pFileDataView->GetView()), (uLong)File.uiSizeOnDisk)) { case Z_OK: pStream = new Streams::CMemoryStream(lpInflateBuffer, iInflateSize); bResult = hlTrue; break; case Z_MEM_ERROR: delete []lpInflateBuffer; LastError.SetErrorMessage("Deflate Error: Z_MEM_ERROR."); break; case Z_BUF_ERROR: delete []lpInflateBuffer; LastError.SetErrorMessage("Deflate Error: Z_BUF_ERROR."); break; case Z_DATA_ERROR: delete []lpInflateBuffer; LastError.SetErrorMessage("Deflate Error: Z_DATA_ERROR."); break; default: delete []lpInflateBuffer; LastError.SetErrorMessage("Deflate Error: Unknown."); break; } this->File.pMapping->Unmap(pFileDataView); return bResult; } #endif return hlFalse; } }
CPackage *CPackage::AutoOpen(Mapping::CMapping &Mapping, hlUInt uiMode) { Mapping::CView *lpView = 0; if (!Mapping.Map(lpView, 0, 512 > Mapping.GetMappingSize() ? Mapping.GetMappingSize() : 512)) { return 0; } CPackage *lpPackage = PackageFromMemory(lpView->GetView(), lpView->GetLength()); Mapping.Unmap(lpView); if (lpPackage != 0) { if (!lpPackage->Open(Mapping, uiMode)) { delete lpPackage; lpPackage = 0; } } return lpPackage; }
hlBool CZIPFile::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const { const ZIPFileHeader *pDirectoryItem = static_cast<const ZIPFileHeader *>(pFile->GetData()); if(pDirectoryItem->uiCompressionMethod != 0) { LastError.SetErrorMessageFormated("Compression format %#.2x not supported.", pDirectoryItem->uiCompressionMethod); return hlFalse; } if(pDirectoryItem->uiDiskNumberStart != this->pEndOfCentralDirectoryRecord->uiNumberOfThisDisk) { LastError.SetErrorMessageFormated("File resides on disk %u.", pDirectoryItem->uiDiskNumberStart); return hlFalse; } Mapping::CView *pDirectoryEnrtyView = 0; if(!this->pMapping->Map(pDirectoryEnrtyView, pDirectoryItem->uiRelativeOffsetOfLocalHeader, sizeof(ZIPLocalFileHeader))) { return hlFalse; } const ZIPLocalFileHeader DirectoryEntry = *static_cast<const ZIPLocalFileHeader *>(pDirectoryEnrtyView->GetView()); this->pMapping->Unmap(pDirectoryEnrtyView); if(DirectoryEntry.uiSignature != HL_ZIP_LOCAL_FILE_HEADER_SIGNATURE) { LastError.SetErrorMessageFormated("Invalid file data offset.", pDirectoryItem->uiDiskNumberStart); return hlFalse; } pStream = new Streams::CMappingStream(*this->pMapping, pDirectoryItem->uiRelativeOffsetOfLocalHeader + sizeof(ZIPLocalFileHeader) + DirectoryEntry.uiFileNameLength + DirectoryEntry.uiExtraFieldLength, DirectoryEntry.uiUncompressedSize); return hlTrue; }
hlBool CWADFile::GetLumpInfo(const CDirectoryFile &File, hlUInt &uiWidth, hlUInt &uiHeight, hlUInt &uiPaletteSize, hlUInt uiMipmap) const { const WADLump &Lump = this->lpLumps[File.GetID()]; if(Lump.iCompression) { LastError.SetErrorMessageFormated("Error reading lump: compression format %#.2x not supported.", Lump.iCompression); return hlFalse; } if(Lump.iType == 0x42) { if(uiMipmap > 0) { LastError.SetErrorMessageFormated("Error reading lump: invalid mipmap level %u.", uiMipmap); return hlFalse; } } else if(Lump.iType == 0x43) { if(uiMipmap > 3) { LastError.SetErrorMessageFormated("Error reading lump: invalid mipmap level %u.", uiMipmap); return hlFalse; } } else { LastError.SetErrorMessageFormated("Error reading lump: lump type %#.2x not supported.", Lump.iType); return hlFalse; } WADLumpInfo &Info = this->lpLumpInfo[File.GetID()]; if(Info.uiWidth && Info.uiHeight && Info.uiPaletteSize) { uiWidth = Info.uiWidth; uiHeight = Info.uiHeight; uiPaletteSize = Info.uiPaletteSize; } else { Mapping::CView *pView = 0; if(!this->pMapping->Map(pView, Lump.uiOffset, Lump.uiDiskLength)) { return hlFalse; } hlByte *lpData = (hlByte *)pView->GetView(); // Type 0x42 has no name, type 0x43 does. Are these flags? if(Lump.iType == 0x42) { // Get Width. uiWidth = *(hlUInt *)lpData; lpData += 4; // Get Height. uiHeight = *(hlUInt *)lpData; lpData += 4; // Scan past pixel data. lpData += (uiWidth * uiHeight); // Get palette size. uiPaletteSize = (hlUInt)(*(hlUInt16 *)lpData); } else if(Lump.iType == 0x43) { // Scan past name. lpData += 16; // Get Width. uiWidth = *(hlUInt *)lpData; lpData += 4; // Get Height. uiHeight = *(hlUInt *)lpData; lpData += 4; // Scan past pixel offset. lpData += 16; hlUInt uiPixelSize = uiWidth * uiHeight; switch(uiMipmap) { case 1: lpData += (uiPixelSize); break; case 2: lpData += (uiPixelSize) + (uiPixelSize / 4); break; case 3: lpData += (uiPixelSize) + (uiPixelSize / 4) + (uiPixelSize / 16); break; } // Scan past data. lpData += (uiPixelSize) + (uiPixelSize / 4) + (uiPixelSize / 16) + (uiPixelSize / 64); // Get palette size. uiPaletteSize = (hlUInt)(*(hlUInt16 *)lpData); } this->pMapping->Unmap(pView); Info.uiWidth = uiWidth; Info.uiHeight = uiHeight; Info.uiPaletteSize = uiPaletteSize; } switch(uiMipmap) { case 1: uiWidth /= 2; uiHeight /= 2; break; case 2: uiWidth /= 4; uiHeight /= 4; break; case 3: uiWidth /= 8; uiHeight /= 8; break; } return hlTrue; }
hlBool CSGAFile::CSGADirectory<TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile, TSGAFileHeader>::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const { const TSGAFile &File = this->lpFiles[pFile->GetID()]; #if !USE_ZLIB if(File.uiType != 0) { eValidation = HL_VALIDATES_ASSUMED_OK; return hlTrue; } #endif Mapping::CView *pFileHeaderDataView = 0; if(this->File.pMapping->Map(pFileHeaderDataView, this->File.pHeader->uiFileDataOffset + File.uiOffset - sizeof(TSGAFileHeader), File.uiSizeOnDisk + sizeof(TSGAFileHeader))) { hlULong uiChecksum = 0; const TSGAFileHeader* pFileHeader = static_cast<const TSGAFileHeader*>(pFileHeaderDataView->GetView()); const hlByte* lpBuffer = reinterpret_cast<const hlByte *>(pFileHeader) + sizeof(TSGAFileHeader); #if USE_ZLIB hlByte *lpInflateBuffer = 0; if(File.uiType != 0) { lpInflateBuffer = new hlByte[File.uiSize]; uLongf iInflateSize = File.uiSize; switch(uncompress(lpInflateBuffer, &iInflateSize, lpBuffer, static_cast<uLong>(File.uiSizeOnDisk))) { case Z_OK: lpBuffer = lpInflateBuffer; break; default: delete []lpInflateBuffer; lpInflateBuffer = 0; eValidation = HL_VALIDATES_ERROR; break; } } if(File.uiType == 0 || lpInflateBuffer != 0) #endif { hlULongLong uiTotalBytes = 0, uiFileBytes = File.uiSize; hlBool bCancel = hlFalse; hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel); while(uiTotalBytes < uiFileBytes) { if(bCancel) { eValidation = HL_VALIDATES_CANCELED; break; } hlUInt uiBufferSize = static_cast<hlUInt>(uiTotalBytes + HL_SGA_CHECKSUM_LENGTH <= uiFileBytes ? HL_SGA_CHECKSUM_LENGTH : uiFileBytes - uiTotalBytes); uiChecksum = CRC32(lpBuffer, uiBufferSize, uiChecksum); lpBuffer += uiBufferSize; uiTotalBytes += static_cast<hlULongLong>(uiBufferSize); hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel); } } #if USE_ZLIB delete []lpInflateBuffer; #endif eValidation = static_cast<hlULong>(pFileHeader->uiCRC32) == uiChecksum ? HL_VALIDATES_OK : HL_VALIDATES_CORRUPT; this->File.pMapping->Unmap(pFileHeaderDataView); } else { eValidation = HL_VALIDATES_ERROR; } return hlTrue; }
hlBool CSGAFile::CSGADirectory<TSGADirectoryHeader, TSGASection, TSGAFolder, TSGAFile, TSGAFileHeader>::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const { if(pItem->GetID() != HL_ID_INVALID) { switch(pItem->GetType()) { case HL_ITEM_FOLDER: { const CDirectoryFolder *pFolder = static_cast<const CDirectoryFolder *>(pItem); switch(eAttribute) { case HL_SGA_ITEM_SECTION_ALIAS: { for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++) { if(pFolder->GetID() >= this->lpSections[i].uiFolderStartIndex && pFolder->GetID() < this->lpSections[i].uiFolderEndIndex) { hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpAlias); return hlTrue; } } return hlFalse; } case HL_SGA_ITEM_SECTION_NAME: { for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++) { if(pFolder->GetID() >= this->lpSections[i].uiFolderStartIndex && pFolder->GetID() < this->lpSections[i].uiFolderEndIndex) { hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpName); return hlTrue; } } return hlFalse; } } break; } case HL_ITEM_FILE: { const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem); const TSGAFile &File = this->lpFiles[pFile->GetID()]; switch(eAttribute) { case HL_SGA_ITEM_SECTION_ALIAS: { for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++) { if(pFile->GetID() >= this->lpSections[i].uiFileStartIndex && pFile->GetID() < this->lpSections[i].uiFileEndIndex) { hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpAlias); return hlTrue; } } return hlFalse; } case HL_SGA_ITEM_SECTION_NAME: { for(hlUInt i = 0; i < this->pDirectoryHeader->uiSectionCount; i++) { if(pFile->GetID() >= this->lpSections[i].uiFileStartIndex && pFile->GetID() < this->lpSections[i].uiFileEndIndex) { hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], this->lpSections[i].lpName); return hlTrue; } } return hlFalse; } case HL_SGA_ITEM_MODIFIED: { time_t Time = (time_t)File.uiTimeModified; tm *pTime = localtime(&Time); hlChar lpTime[128]; strftime(lpTime, sizeof(lpTime), "%c", pTime); hlAttributeSetString(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], lpTime); return hlTrue; } case HL_SGA_ITEM_TYPE: { hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], File.uiType, hlFalse); return hlTrue; } case HL_SGA_ITEM_CRC: { Mapping::CView *pFileHeaderView = 0; if(this->File.pMapping->Map(pFileHeaderView, this->File.pHeader->uiFileDataOffset + File.uiOffset - sizeof(TSGAFileHeader), sizeof(TSGAFileHeader))) { const TSGAFileHeader* pFileHeader = static_cast<const TSGAFileHeader *>(pFileHeaderView->GetView()); hlAttributeSetUnsignedInteger(&Attribute, CSGAFile::lpItemAttributeNames[eAttribute], pFileHeader->uiCRC32, hlTrue); this->File.pMapping->Unmap(pFileHeaderView); return hlTrue; } return hlFalse; } } break; } } } return hlFalse; }
hlBool CVBSPFile::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const { if(pFile->GetData()) { const ZIPFileHeader *pDirectoryItem = static_cast<const ZIPFileHeader *>(pFile->GetData()); if(pDirectoryItem->uiCompressionMethod != 0) { LastError.SetErrorMessageFormated("Compression format %#.2x not supported.", pDirectoryItem->uiCompressionMethod); return hlFalse; } if(pDirectoryItem->uiDiskNumberStart != this->pEndOfCentralDirectoryRecord->uiNumberOfThisDisk) { LastError.SetErrorMessageFormated("File resides on disk %u.", pDirectoryItem->uiDiskNumberStart); return hlFalse; } Mapping::CView *pDirectoryEnrtyView = 0; if(!this->pMapping->Map(pDirectoryEnrtyView, this->pHeader->lpLumps[HL_VBSP_LUMP_PAKFILE].uiOffset + pDirectoryItem->uiRelativeOffsetOfLocalHeader, sizeof(ZIPLocalFileHeader))) { return hlFalse; } const ZIPLocalFileHeader DirectoryEntry = *static_cast<const ZIPLocalFileHeader *>(pDirectoryEnrtyView->GetView()); this->pMapping->Unmap(pDirectoryEnrtyView); if(DirectoryEntry.uiSignature != HL_VBSP_ZIP_LOCAL_FILE_HEADER_SIGNATURE) { LastError.SetErrorMessageFormated("Invalid file data offset %u.", pDirectoryItem->uiDiskNumberStart); return hlFalse; } pStream = new Streams::CMappingStream(*this->pMapping, this->pHeader->lpLumps[HL_VBSP_LUMP_PAKFILE].uiOffset + pDirectoryItem->uiRelativeOffsetOfLocalHeader + sizeof(ZIPLocalFileHeader) + DirectoryEntry.uiFileNameLength + DirectoryEntry.uiExtraFieldLength, DirectoryEntry.uiUncompressedSize); } else if(pFile->GetID() < HL_VBSP_LUMP_COUNT) { pStream = new Streams::CMappingStream(*this->pMapping, this->pHeader->lpLumps[pFile->GetID()].uiOffset, this->pHeader->lpLumps[pFile->GetID()].uiLength); } else { hlUInt uiID = pFile->GetID() - HL_VBSP_LUMP_COUNT; Mapping::CView *pLumpView = 0; if(!this->pMapping->Map(pLumpView, this->pHeader->lpLumps[uiID].uiOffset, this->pHeader->lpLumps[uiID].uiLength)) { return hlFalse; } hlUInt uiBufferSize = sizeof(LMPHeader) + this->pHeader->lpLumps[uiID].uiLength; hlByte *lpBuffer = new hlByte[uiBufferSize]; LMPHeader *pLMPHeader = (LMPHeader *)lpBuffer; pLMPHeader->iLumpOffset = sizeof(LMPHeader); pLMPHeader->iLumpID = uiID; pLMPHeader->iLumpVersion = this->pHeader->lpLumps[uiID].uiVersion; pLMPHeader->iLumpLength = this->pHeader->lpLumps[uiID].uiLength; pLMPHeader->iMapRevision = this->pHeader->iMapRevision; memcpy(lpBuffer + sizeof(LMPHeader), pLumpView->GetView(), this->pHeader->lpLumps[uiID].uiLength); pStream = new Streams::CMemoryStream(lpBuffer, uiBufferSize); this->pMapping->Unmap(pLumpView); } return hlTrue; }
hlBool CVBSPFile::MapDataStructures() { if(sizeof(VBSPHeader) > this->pMapping->GetMappingSize()) { LastError.SetErrorMessage("Invalid file: the file map is too small for it's header."); return hlFalse; } if(!this->pMapping->Map(this->pHeaderView, 0, sizeof(VBSPHeader))) { return hlFalse; } this->pHeader = static_cast<const VBSPHeader *>(this->pHeaderView->GetView()); if(memcmp(this->pHeader->lpSignature, "VBSP", 4) != 0) { LastError.SetErrorMessage("Invalid file: the file's signature does not match."); return hlFalse; } // Versions: // 19-20: Source // 21: Source - The lump version property was moved to the start of the struct. // 0x00040014: Dark Messiah - Looks like the 32 bit version has been split into two 16 bit fields. if((this->pHeader->iVersion < 19 || this->pHeader->iVersion > 21) && this->pHeader->iVersion != 0x00040014) { LastError.SetErrorMessageFormated("Invalid VBSP version (v%i): you have a version of a VBSP file that HLLib does not know how to read. Check for product updates.", this->pHeader->iVersion); return hlFalse; } if(this->pHeader->iVersion >= 21 && this->pHeader->iVersion != 0x00040014) { VBSPHeader *pHeader = new VBSPHeader; memcpy(pHeader, this->pHeader, sizeof(VBSPHeader)); for(hlUInt i = 0; i < HL_VBSP_LUMP_COUNT; i++) { pHeader->lpLumps[i].uiVersion = this->pHeader->lpLumps[i].uiOffset; pHeader->lpLumps[i].uiOffset = this->pHeader->lpLumps[i].uiLength; pHeader->lpLumps[i].uiLength = this->pHeader->lpLumps[i].uiVersion; } this->pHeader = pHeader; } if(sizeof(ZIPEndOfCentralDirectoryRecord) <= this->pHeader->lpLumps[HL_VBSP_LUMP_PAKFILE].uiLength) { hlUInt uiTest, uiOffset = this->pHeader->lpLumps[HL_VBSP_LUMP_PAKFILE].uiOffset; while(uiOffset < this->pHeader->lpLumps[HL_VBSP_LUMP_PAKFILE].uiOffset + this->pHeader->lpLumps[HL_VBSP_LUMP_PAKFILE].uiLength - sizeof(uiTest)) { Mapping::CView *pTestView = 0; if(!this->pMapping->Map(pTestView, uiOffset, sizeof(hlUInt))) { return hlFalse; } uiTest = *(hlUInt *)pTestView->GetView(); this->pMapping->Unmap(pTestView); switch(uiTest) { case HL_VBSP_ZIP_END_OF_CENTRAL_DIRECTORY_RECORD_SIGNATURE: { if(!this->pMapping->Map(pTestView, uiOffset, sizeof(ZIPEndOfCentralDirectoryRecord))) { return hlFalse; } const ZIPEndOfCentralDirectoryRecord EndOfCentralDirRecord = *static_cast<const ZIPEndOfCentralDirectoryRecord *>(pTestView->GetView()); this->pMapping->Unmap(pTestView); if(!this->pMapping->Map(this->pEndOfCentralDirectoryRecordView, uiOffset, sizeof(ZIPEndOfCentralDirectoryRecord) + EndOfCentralDirRecord.uiCommentLength)) { return hlFalse; } this->pEndOfCentralDirectoryRecord = static_cast<const ZIPEndOfCentralDirectoryRecord *>(this->pEndOfCentralDirectoryRecordView->GetView()); if(!this->pMapping->Map(this->pFileHeaderView, this->pHeader->lpLumps[HL_VBSP_LUMP_PAKFILE].uiOffset + this->pEndOfCentralDirectoryRecord->uiStartOfCentralDirOffset, this->pEndOfCentralDirectoryRecord->uiCentralDirectorySize)) { return hlFalse; } return hlTrue; } case HL_VBSP_ZIP_FILE_HEADER_SIGNATURE: { if(!this->pMapping->Map(pTestView, uiOffset, sizeof(ZIPFileHeader))) { return hlFalse; } const ZIPFileHeader FileHeader = *static_cast<const ZIPFileHeader *>(pTestView->GetView()); this->pMapping->Unmap(pTestView); uiOffset += sizeof(ZIPFileHeader) + FileHeader.uiFileNameLength + FileHeader.uiExtraFieldLength + FileHeader.uiFileCommentLength; break; } case HL_VBSP_ZIP_LOCAL_FILE_HEADER_SIGNATURE: { if(!this->pMapping->Map(pTestView, uiOffset, sizeof(ZIPLocalFileHeader))) { return hlFalse; } const ZIPLocalFileHeader LocalFileHeader = *static_cast<const ZIPLocalFileHeader *>(pTestView->GetView()); this->pMapping->Unmap(pTestView); uiOffset += sizeof(ZIPLocalFileHeader) + LocalFileHeader.uiFileNameLength + LocalFileHeader.uiExtraFieldLength + LocalFileHeader.uiCompressedSize; break; } default: { LastError.SetErrorMessageFormated("Invalid file: unknown ZIP section signature %#.8x.", uiTest); return hlFalse; } } } LastError.SetErrorMessage("Invalid file: unexpected end of file while scanning for end of ZIP central directory record."); return hlFalse; } return hlTrue; }
hlBool CZIPFile::MapDataStructures() { if(sizeof(ZIPEndOfCentralDirectoryRecord) > this->pMapping->GetMappingSize()) { LastError.SetErrorMessage("Invalid file: the file map is too small for it's header."); return hlFalse; } hlUInt uiTest; hlULongLong uiOffset = 0, uiLength = this->pMapping->GetMappingSize(); while(uiOffset < uiLength - sizeof(uiTest)) { Mapping::CView *pTestView = 0; if(!this->pMapping->Map(pTestView, uiOffset, sizeof(hlUInt))) { return hlFalse; } uiTest = *(hlUInt *)pTestView->GetView(); this->pMapping->Unmap(pTestView); switch(uiTest) { case HL_ZIP_END_OF_CENTRAL_DIRECTORY_RECORD_SIGNATURE: { if(!this->pMapping->Map(pTestView, uiOffset, sizeof(ZIPEndOfCentralDirectoryRecord))) { return hlFalse; } const ZIPEndOfCentralDirectoryRecord EndOfCentralDirRecord = *static_cast<const ZIPEndOfCentralDirectoryRecord *>(pTestView->GetView()); this->pMapping->Unmap(pTestView); if(!this->pMapping->Map(this->pEndOfCentralDirectoryRecordView, uiOffset, sizeof(ZIPEndOfCentralDirectoryRecord) + EndOfCentralDirRecord.uiCommentLength)) { return hlFalse; } this->pEndOfCentralDirectoryRecord = static_cast<const ZIPEndOfCentralDirectoryRecord *>(this->pEndOfCentralDirectoryRecordView->GetView()); if(!this->pMapping->Map(this->pFileHeaderView, this->pEndOfCentralDirectoryRecord->uiStartOfCentralDirOffset, this->pEndOfCentralDirectoryRecord->uiCentralDirectorySize)) { return hlFalse; } return hlTrue; } case HL_ZIP_FILE_HEADER_SIGNATURE: { if(!this->pMapping->Map(pTestView, uiOffset, sizeof(ZIPFileHeader))) { return hlFalse; } const ZIPFileHeader FileHeader = *static_cast<const ZIPFileHeader *>(pTestView->GetView()); this->pMapping->Unmap(pTestView); uiOffset += static_cast<hlULongLong>(sizeof(ZIPFileHeader) + FileHeader.uiFileNameLength + FileHeader.uiExtraFieldLength + FileHeader.uiFileCommentLength); break; } case HL_ZIP_LOCAL_FILE_HEADER_SIGNATURE: { if(!this->pMapping->Map(pTestView, uiOffset, sizeof(ZIPLocalFileHeader))) { return hlFalse; } const ZIPLocalFileHeader LocalFileHeader = *static_cast<const ZIPLocalFileHeader *>(pTestView->GetView()); this->pMapping->Unmap(pTestView); uiOffset += static_cast<hlULongLong>(sizeof(ZIPLocalFileHeader) + LocalFileHeader.uiFileNameLength + LocalFileHeader.uiExtraFieldLength + LocalFileHeader.uiCompressedSize); break; } default: { LastError.SetErrorMessageFormated("Invalid file: unknown section signature %#.8x.", uiTest); return hlFalse; } } } LastError.SetErrorMessage("Invalid file: unexpected end of file while scanning for end of central directory record."); return hlFalse; }
hlBool CGCFFile::DefragmentInternal() { hlBool bError = hlFalse, bCancel = hlFalse; hlUInt uiFilesDefragmented = 0, uiFilesTotal = 0; hlULongLong uiBytesDefragmented = 0, uiBytesTotal = 0; // Check the current fragmentation state. { hlUInt uiBlocksFragmented = 0; hlUInt uiBlocksUsed = 0; for(hlUInt i = 0; i < this->pDirectoryHeader->uiItemCount; i++) { if(this->lpDirectoryEntries[i].uiDirectoryFlags & HL_GCF_FLAG_FILE) { hlUInt uiFileBlocksFragmented = 0; hlUInt uiFileBlocksUsed = 0; this->GetItemFragmentation(i, uiFileBlocksFragmented, uiFileBlocksUsed); uiBlocksFragmented += uiFileBlocksFragmented; uiBlocksUsed += uiFileBlocksUsed; uiFilesTotal++; uiBytesTotal += static_cast<hlULongLong>(uiFileBlocksUsed * this->pDataBlockHeader->uiBlockSize); } } // If there are no data blocks to defragment, and we don't want to sort the data blocks // lexicographically, then we're done. if((uiBlocksFragmented == 0 && !bForceDefragment) || uiBlocksUsed == 0) { hlDefragmentProgress(0, uiFilesTotal, uiFilesTotal, uiBytesTotal, uiBytesTotal, &bCancel); return hlTrue; } } Mapping::CView *pCurrentView = 0, *pIncrementedView = 0; hlUInt uiIncrement = 0; //hlUInt uiDataBlockTerminator = this->pDataBlockHeader->uiBlockCount >= 0x0000ffff ? 0xffffffff : 0x0000ffff; hlUInt uiDataBlockTerminator = this->pFragmentationMapHeader->uiTerminator == 0 ? 0x0000ffff : 0xffffffff; hlByte *lpDataBlock = new hlByte[this->pDataBlockHeader->uiBlockSize]; // Step through each data block in each file as it appears in the directory and defragment (order each data // block sequentially). This is a slow brute force approach, but since related half-life 2 files // often start with the same name we *may* expect a performance boost from odering files lexicographically // (as they appear in the directory). That's my justification at least... for(hlUInt i = 0; i < this->pDirectoryHeader->uiItemCount && !bError && !bCancel; i++) { if(this->lpDirectoryEntries[i].uiDirectoryFlags & HL_GCF_FLAG_FILE) { hlUInt uiCurrentBlockEntryIndex = this->lpDirectoryMapEntries[i].uiFirstBlockIndex; while(uiCurrentBlockEntryIndex != this->pDataBlockHeader->uiBlockCount) { hlUInt uiCurrentBlockEntrySize = 0; hlUInt uiCurrentDataBlockIndex = this->lpBlockEntries[uiCurrentBlockEntryIndex].uiFirstDataBlockIndex; hlUInt uiLastDataBlockIndex = this->pDataBlockHeader->uiBlockCount; while(uiCurrentDataBlockIndex < uiDataBlockTerminator && uiCurrentBlockEntrySize < this->lpBlockEntries[uiCurrentBlockEntryIndex].uiFileDataSize) { hlUInt uiNextDataBlockIndex = this->lpFragmentationMap[uiCurrentDataBlockIndex].uiNextDataBlockIndex; // If this data block is not ordered sequentially, swap it with the sequential data block. if(uiCurrentDataBlockIndex != uiIncrement) { // Make sure we can map the two data blocks before we alter any tables. if(this->pMapping->Map(pCurrentView, static_cast<hlULongLong>(this->pDataBlockHeader->uiFirstBlockOffset) + static_cast<hlULongLong>(uiCurrentDataBlockIndex) * static_cast<hlULongLong>(this->pDataBlockHeader->uiBlockSize), static_cast<hlULongLong>(this->pDataBlockHeader->uiBlockSize)) && this->pMapping->Map(pIncrementedView, static_cast<hlULongLong>(this->pDataBlockHeader->uiFirstBlockOffset) + static_cast<hlULongLong>(uiIncrement) * static_cast<hlULongLong>(this->pDataBlockHeader->uiBlockSize), static_cast<hlULongLong>(this->pDataBlockHeader->uiBlockSize))) { // Search to see if the sequential data block is in use, we only need to check // files after ours in the directory because everything before is sequential. hlBool bFound = hlFalse; for(hlUInt j = i; j < this->pDirectoryHeader->uiItemCount && !bFound; j++) { if(this->lpDirectoryEntries[j].uiDirectoryFlags & HL_GCF_FLAG_FILE) { hlUInt uiIncrementedBlockEntryIndex = this->lpDirectoryMapEntries[j].uiFirstBlockIndex; while(uiIncrementedBlockEntryIndex != this->pDataBlockHeader->uiBlockCount && !bFound) { hlUInt uiIncrementedDataBlockIndex = this->lpBlockEntries[uiIncrementedBlockEntryIndex].uiFirstDataBlockIndex; if(uiIncrementedDataBlockIndex == uiIncrement) { // The sequential data block is the first data block in a block entry, // update the tables preserving the sequence for the file we are fragmenting. this->lpBlockEntries[uiIncrementedBlockEntryIndex].uiFirstDataBlockIndex = uiCurrentDataBlockIndex; this->lpFragmentationMap[uiCurrentDataBlockIndex].uiNextDataBlockIndex = this->lpFragmentationMap[uiIncrementedDataBlockIndex].uiNextDataBlockIndex; // We found it. bFound = hlTrue; break; } else { // The sequential data block is in the middle of a block entry, // update the tables preserving the sequence for the file we are fragmenting. hlUInt uiIncrementedBlockEntrySize = 0; hlUInt uiIncrementedLastDataBlockIndex = this->pDataBlockHeader->uiBlockCount; while(uiIncrementedDataBlockIndex < uiDataBlockTerminator && uiIncrementedBlockEntrySize < this->lpBlockEntries[uiIncrementedBlockEntryIndex].uiFileDataSize) { if(uiIncrementedDataBlockIndex == uiIncrement) { // If the data blocks are side by side, prevent circular maps. if(uiIncrement != uiNextDataBlockIndex) { this->lpFragmentationMap[uiIncrementedLastDataBlockIndex].uiNextDataBlockIndex = uiCurrentDataBlockIndex; } this->lpFragmentationMap[uiCurrentDataBlockIndex].uiNextDataBlockIndex = this->lpFragmentationMap[uiIncrementedDataBlockIndex].uiNextDataBlockIndex; // We found it. bFound = hlTrue; break; } uiIncrementedLastDataBlockIndex = uiIncrementedDataBlockIndex; uiIncrementedDataBlockIndex = this->lpFragmentationMap[uiIncrementedDataBlockIndex].uiNextDataBlockIndex; uiIncrementedBlockEntrySize += this->pDataBlockHeader->uiBlockSize; } } uiIncrementedBlockEntryIndex = this->lpBlockEntries[uiIncrementedBlockEntryIndex].uiNextBlockEntryIndex; } } } // Swap the data blocks if necessary. if(bFound) { memcpy(lpDataBlock, pIncrementedView->GetView(), this->pDataBlockHeader->uiBlockSize); } memcpy(const_cast<hlVoid *>(pIncrementedView->GetView()), pCurrentView->GetView(), this->pDataBlockHeader->uiBlockSize); if(!this->pMapping->Commit(*pIncrementedView)) { // At this point the GCF will be corrupt and require validating by Steam for repair. bError = hlTrue; } if(bFound) { memcpy(const_cast<hlVoid *>(pCurrentView->GetView()), lpDataBlock, this->pDataBlockHeader->uiBlockSize); if(!this->pMapping->Commit(*pCurrentView)) { // At this point the GCF will be corrupt and require validating by Steam for repair. bError = hlTrue; } } // Update the tables preserving the sequence of the file we are defragmenting. if(uiLastDataBlockIndex == this->pDataBlockHeader->uiBlockCount) { this->lpBlockEntries[uiCurrentBlockEntryIndex].uiFirstDataBlockIndex = uiIncrement; } else { this->lpFragmentationMap[uiLastDataBlockIndex].uiNextDataBlockIndex = uiIncrement; } if(uiIncrement != this->pDataBlockHeader->uiBlockCount) { // If the data blocks are side by side, prevent circular maps. if(uiIncrement != uiNextDataBlockIndex) { this->lpFragmentationMap[uiIncrement].uiNextDataBlockIndex = uiNextDataBlockIndex; } else { this->lpFragmentationMap[uiIncrement].uiNextDataBlockIndex = uiCurrentDataBlockIndex; } } } else { bError = hlTrue; break; } } // Move to the next data block. uiLastDataBlockIndex = uiIncrement; uiIncrement++; uiCurrentDataBlockIndex = this->lpFragmentationMap[uiLastDataBlockIndex].uiNextDataBlockIndex; uiCurrentBlockEntrySize += this->pDataBlockHeader->uiBlockSize; // Update the progress. uiBytesDefragmented += static_cast<hlULongLong>(this->pDataBlockHeader->uiBlockSize); hlDefragmentProgress(this->lpDirectoryItems != 0 ? static_cast<CDirectoryFile *>(this->lpDirectoryItems[i]) : 0, uiFilesDefragmented, uiFilesTotal, uiBytesDefragmented, uiBytesTotal, &bCancel); if(bCancel) { break; } } if(bError || bCancel) { break; } // Update the tables to make sure the last data block points to nothing. // It would seem that this is only necessary if there is an eror in the GCF // in which case we should just let it be (Steam knows better)... /*if(uiLastDataBlockIndex != this->pDataBlockHeader->uiBlockCount) { this->lpFragmentationMap[uiLastDataBlockIndex].uiNextDataBlockIndex = uiDataBlockTerminator; }*/ uiCurrentBlockEntryIndex = this->lpBlockEntries[uiCurrentBlockEntryIndex].uiNextBlockEntryIndex; } uiFilesDefragmented++; } } if(!bError && !bCancel) { // Store the first unused fragmentation map entry. if(uiIncrement < this->pFragmentationMapHeader->uiBlockCount) { // Note: usually if there are no unused data blocks, uiFirstUnusedEntry is 0, but // sometimes it isn't (and I don't know why) so I'm unsure if I should set it to // 0 in the else case. this->pFragmentationMapHeader->uiFirstUnusedEntry = uiIncrement; this->pFragmentationMapHeader->uiChecksum = this->pFragmentationMapHeader->uiBlockCount + this->pFragmentationMapHeader->uiFirstUnusedEntry + this->pFragmentationMapHeader->uiTerminator; } // Fill in the unused fragmentation map entries with uiBlockCount. for(hlUInt i = uiIncrement; i < this->pFragmentationMapHeader->uiBlockCount; i++) { this->lpFragmentationMap[i].uiNextDataBlockIndex = this->pFragmentationMapHeader->uiBlockCount; } } else { hlByte *lpTouched = new hlByte[this->pFragmentationMapHeader->uiBlockCount]; memset(lpTouched, 0, sizeof(hlByte) * this->pFragmentationMapHeader->uiBlockCount); // Figure out which fragmentation map entries are used. for(hlUInt i = 0; i < this->pDirectoryHeader->uiItemCount; i++) { if(this->lpDirectoryEntries[i].uiDirectoryFlags & HL_GCF_FLAG_FILE) { hlUInt uiBlockEntryIndex = this->lpDirectoryMapEntries[i].uiFirstBlockIndex; while(uiBlockEntryIndex != this->pDataBlockHeader->uiBlockCount) { hlUInt uiBlockEntrySize = 0; hlUInt uiDataBlockIndex = this->lpBlockEntries[uiBlockEntryIndex].uiFirstDataBlockIndex; while(uiDataBlockIndex < uiDataBlockTerminator && uiBlockEntrySize < this->lpBlockEntries[uiBlockEntryIndex].uiFileDataSize) { lpTouched[uiDataBlockIndex] = hlTrue; uiDataBlockIndex = this->lpFragmentationMap[uiDataBlockIndex].uiNextDataBlockIndex; uiBlockEntrySize += this->pDataBlockHeader->uiBlockSize; } uiBlockEntryIndex = this->lpBlockEntries[uiBlockEntryIndex].uiNextBlockEntryIndex; } } } // Fill in the unused fragmentation map entries with uiBlockCount. hlBool bFirst = hlFalse; for(hlUInt i = 0; i < this->pFragmentationMapHeader->uiBlockCount; i++) { if(!lpTouched[i]) { if(!bFirst) { // Store the first unused fragmentation map entry. this->pFragmentationMapHeader->uiFirstUnusedEntry = i; this->pFragmentationMapHeader->uiChecksum = this->pFragmentationMapHeader->uiBlockCount + this->pFragmentationMapHeader->uiFirstUnusedEntry + this->pFragmentationMapHeader->uiTerminator; bFirst = hlTrue; } this->lpFragmentationMap[i].uiNextDataBlockIndex = this->pFragmentationMapHeader->uiBlockCount; } } delete []lpTouched; } delete []lpDataBlock; this->pMapping->Unmap(pCurrentView); this->pMapping->Unmap(pIncrementedView); // Commit header changes to mapping. this->pMapping->Commit(*this->pHeaderView, (hlUInt)((const hlByte *)this->lpBlockEntries - (const hlByte *)this->pHeaderView->GetView()), sizeof(GCFBlockEntry) * this->pBlockEntryHeader->uiBlockCount); this->pMapping->Commit(*this->pHeaderView, (hlUInt)((const hlByte *)this->pFragmentationMapHeader - (const hlByte *)this->pHeaderView->GetView()), sizeof(GCFFragmentationMapHeader)); this->pMapping->Commit(*this->pHeaderView, (hlUInt)((const hlByte *)this->lpFragmentationMap - (const hlByte *)this->pHeaderView->GetView()), sizeof(GCFFragmentationMap) * this->pFragmentationMapHeader->uiBlockCount); return !bError; }