/* * HLLib * Copyright (C) 2006 Ryan Gregg * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later * version. */ #include "HLLib.h" #include "GCFStream.h" using namespace HLLib; using namespace HLLib::Streams; CGCFStream::CGCFStream(CGCFFile &GCFFile, hlUInt uiFileID) : bOpened(hlFalse), uiMode(HL_MODE_INVALID), GCFFile(GCFFile), uiFileID(uiFileID), pView(0), uiPointer(0), uiLength(0) { } CGCFStream::~CGCFStream() { this->Close(); } HLStreamType CGCFStream::GetType() const { return HL_STREAM_GCF; } const CGCFFile &CGCFStream::GetPackage() const { return this->GCFFile; } const hlChar *CGCFStream::GetFileName() const { return this->GCFFile.lpDirectoryNames + this->GCFFile.lpDirectoryEntries[this->uiFileID].uiNameOffset; } hlBool CGCFStream::GetOpened() const { return this->bOpened; } hlUInt CGCFStream::GetMode() const { return this->uiMode; } hlBool CGCFStream::Open(hlUInt uiMode) { this->Close(); if(!this->GCFFile.GetOpened()) { LastError.SetErrorMessage("GCF file not opened."); return hlFalse; } if((uiMode & (HL_MODE_READ | HL_MODE_WRITE)) == 0) { LastError.SetErrorMessageFormated("Invalid open mode (%#.8x).", uiMode); return hlFalse; } if((uiMode & HL_MODE_READ) != 0 && (this->GCFFile.pMapping->GetMode() & HL_MODE_READ) == 0) { LastError.SetErrorMessage("GCF file does not have read permissions."); return hlFalse; } if((uiMode & HL_MODE_WRITE) != 0 && (this->GCFFile.pMapping->GetMode() & HL_MODE_WRITE) == 0) { LastError.SetErrorMessage("GCF file does not have write permissions."); return hlFalse; } this->uiPointer = 0; this->uiLength = (uiMode & HL_MODE_READ) ? this->GCFFile.lpDirectoryEntries[this->uiFileID].uiItemSize : 0; this->bOpened = hlTrue; this->uiMode = uiMode; this->uiBlockEntryIndex = this->GCFFile.lpDirectoryMapEntries[this->uiFileID].uiFirstBlockIndex; this->uiBlockEntryOffset = 0; this->uiDataBlockIndex = this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFirstDataBlockIndex; this->uiDataBlockOffset = 0; return hlTrue; } hlVoid CGCFStream::Close() { this->bOpened = hlFalse; this->uiMode = HL_MODE_INVALID; this->GCFFile.pMapping->Unmap(this->pView); this->uiPointer = 0; this->uiLength = 0; } hlUInt CGCFStream::GetStreamSize() const { return this->uiLength; } hlUInt CGCFStream::GetStreamPointer() const { return this->uiPointer; } hlUInt CGCFStream::Seek(hlLong iOffset, HLSeekMode eSeekMode) { if(!this->bOpened) { return 0; } switch(eSeekMode) { case HL_SEEK_BEGINNING: this->uiPointer = 0; break; case HL_SEEK_CURRENT: break; case HL_SEEK_END: this->uiPointer = this->uiLength; break; } hlLong iPointer = (hlLong)this->uiPointer + iOffset; if(iPointer < 0) { iPointer = 0; } if(iPointer > (hlLong)this->uiLength) { iPointer = (hlLong)this->uiLength; } this->uiPointer = (hlUInt)iPointer; return this->uiPointer; } hlBool CGCFStream::Read(hlChar &cChar) { if(!this->bOpened) { return 0; } if((this->uiMode & HL_MODE_READ) == 0) { LastError.SetErrorMessage("Stream not in read mode."); return 0; } if(this->uiPointer < this->uiLength) { if(!this->Map(this->uiPointer)) { return 0; } hlUInt uiViewPointer = this->uiPointer - (this->uiBlockEntryOffset + this->uiDataBlockOffset); hlUInt uiViewBytes = this->pView->GetLength() - uiViewPointer; if(uiViewBytes >= 1) { cChar = *((hlChar *)this->pView->GetView() + uiViewPointer); this->uiPointer++; return 1; } } return 0; } hlUInt CGCFStream::Read(hlVoid *lpData, hlUInt uiBytes) { if(!this->bOpened) { return 0; } if((this->uiMode & HL_MODE_READ) == 0) { LastError.SetErrorMessage("Stream not in read mode."); return 0; } if(this->uiPointer == this->uiLength) { return 0; } else { hlUInt uiOffset = 0; while(uiBytes && this->uiPointer < this->uiLength) { if(!this->Map(this->uiPointer)) { break; } hlUInt uiViewPointer = this->uiPointer - (this->uiBlockEntryOffset + this->uiDataBlockOffset); hlUInt uiViewBytes = this->pView->GetLength() - uiViewPointer; if(uiViewBytes >= uiBytes) { memcpy((hlByte *)lpData + uiOffset, (hlByte *)this->pView->GetView() + uiViewPointer, uiBytes); this->uiPointer += uiBytes; uiOffset += uiBytes; break; } else { memcpy((hlByte *)lpData + uiOffset, (hlByte *)this->pView->GetView() + uiViewPointer, uiViewBytes); this->uiPointer += uiViewBytes; uiOffset += uiViewBytes; uiBytes -= uiViewBytes; } } return uiOffset; } } hlBool CGCFStream::Write(hlChar cChar) { if(!this->bOpened) { return 0; } if((this->uiMode & HL_MODE_WRITE) == 0) { LastError.SetErrorMessage("Stream not in write mode."); return 0; } if(this->uiPointer < this->GCFFile.lpDirectoryEntries[this->uiFileID].uiItemSize) { if(!this->Map(this->uiPointer)) { return 0; } hlUInt uiViewPointer = this->uiPointer - (this->uiBlockEntryOffset + this->uiDataBlockOffset); hlUInt uiViewBytes = this->pView->GetLength() - uiViewPointer; if(uiViewBytes >= 1) { *((hlChar *)this->pView->GetView() + uiViewPointer) = cChar; this->uiPointer++; if(this->uiPointer > this->uiLength) { this->uiLength = this->uiPointer; } return 1; } } return 0; } hlUInt CGCFStream::Write(const hlVoid *lpData, hlUInt uiBytes) { if(!this->bOpened) { return 0; } if((this->uiMode & HL_MODE_WRITE) == 0) { LastError.SetErrorMessage("Stream not in write mode."); return 0; } if(this->uiPointer == this->GCFFile.lpDirectoryEntries[this->uiFileID].uiItemSize) { return 0; } else { hlUInt uiOffset = 0; while(uiBytes && this->uiPointer < this->GCFFile.lpDirectoryEntries[this->uiFileID].uiItemSize) { if(!this->Map(this->uiPointer)) { break; } hlUInt uiViewPointer = this->uiPointer - (this->uiBlockEntryOffset + this->uiDataBlockOffset); hlUInt uiViewBytes = this->pView->GetLength() - uiViewPointer; if(uiViewBytes >= uiBytes) { memcpy((hlByte *)this->pView->GetView() + uiViewPointer, (hlByte *)lpData + uiOffset, uiBytes); this->uiPointer += uiBytes; uiOffset += uiBytes; break; } else { memcpy((hlByte *)this->pView->GetView() + uiViewPointer, (hlByte *)lpData + uiOffset, uiViewBytes); this->uiPointer += uiViewBytes; uiOffset += uiViewBytes; uiBytes -= uiViewBytes; } } if(this->uiPointer > this->uiLength) { this->uiLength = this->uiPointer; } return uiOffset; } } hlBool CGCFStream::Map(hlUInt uiPointer) { if(uiPointer < this->uiBlockEntryOffset + this->uiDataBlockOffset) { this->uiBlockEntryIndex = this->GCFFile.lpDirectoryMapEntries[this->uiFileID].uiFirstBlockIndex; this->uiBlockEntryOffset = 0; this->uiDataBlockIndex = this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFirstDataBlockIndex; this->uiDataBlockOffset = 0; } hlUInt uiLength = this->uiDataBlockOffset + this->GCFFile.pDataBlockHeader->uiBlockSize > this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize ? this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize - this->uiDataBlockOffset : this->GCFFile.pDataBlockHeader->uiBlockSize; //hlUInt uiDataBlockTerminator = this->pDataBlockHeader->uiBlockCount >= 0x0000ffff ? 0xffffffff : 0x0000ffff; hlUInt uiDataBlockTerminator = this->GCFFile.pFragmentationMapHeader->uiTerminator == 0 ? 0x0000ffff : 0xffffffff; while((uiPointer >= this->uiBlockEntryOffset + this->uiDataBlockOffset + uiLength) && (this->uiBlockEntryIndex != this->GCFFile.pDataBlockHeader->uiBlockCount)) { // Loop through each data block fragment. while((uiPointer >= this->uiBlockEntryOffset + this->uiDataBlockOffset + uiLength) && (this->uiDataBlockIndex < uiDataBlockTerminator && this->uiDataBlockOffset < this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize)) { // Get the next data block fragment. this->uiDataBlockIndex = this->GCFFile.lpFragmentationMap[this->uiDataBlockIndex].uiNextDataBlockIndex; this->uiDataBlockOffset += this->GCFFile.pDataBlockHeader->uiBlockSize; uiLength = this->uiDataBlockOffset + this->GCFFile.pDataBlockHeader->uiBlockSize > this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize ? this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize - this->uiDataBlockOffset : this->GCFFile.pDataBlockHeader->uiBlockSize; } if(this->uiDataBlockOffset >= this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize) { // Get the next data block. this->uiBlockEntryOffset += this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize; this->uiBlockEntryIndex = this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiNextBlockEntryIndex; this->uiDataBlockOffset = 0; if(this->uiBlockEntryIndex != this->GCFFile.pDataBlockHeader->uiBlockCount) { this->uiDataBlockIndex = this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFirstDataBlockIndex; } uiLength = this->uiDataBlockOffset + this->GCFFile.pDataBlockHeader->uiBlockSize > this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize ? this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize - this->uiDataBlockOffset : this->GCFFile.pDataBlockHeader->uiBlockSize; } } if(this->uiBlockEntryIndex == this->GCFFile.pDataBlockHeader->uiBlockCount || this->uiDataBlockIndex >= uiDataBlockTerminator) { if(this->uiBlockEntryOffset + this->uiDataBlockOffset < this->GCFFile.lpDirectoryEntries[this->uiFileID].uiItemSize) { LastError.SetErrorMessageFormated("Unexpected end of GCF stream (%u B of %u B). Has the GCF file been completely acquired?", this->uiBlockEntryOffset + this->uiDataBlockOffset, this->GCFFile.lpDirectoryEntries[this->uiFileID].uiItemSize); } this->GCFFile.pMapping->Unmap(this->pView); return hlFalse; } if(this->pView) { if(this->pView->GetAllocationOffset() == this->GCFFile.pDataBlockHeader->uiFirstBlockOffset + this->uiDataBlockIndex * this->GCFFile.pDataBlockHeader->uiBlockSize) { return hlTrue; } } //uiLength = this->uiDataBlockOffset + this->GCFFile.pDataBlockHeader->uiBlockSize > this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize ? this->GCFFile.lpBlockEntries[this->uiBlockEntryIndex].uiFileDataSize - this->uiDataBlockOffset : this->GCFFile.pDataBlockHeader->uiBlockSize; return this->GCFFile.pMapping->Map(this->pView, this->GCFFile.pDataBlockHeader->uiFirstBlockOffset + this->uiDataBlockIndex * this->GCFFile.pDataBlockHeader->uiBlockSize, uiLength); }