611 lines
17 KiB
C++
611 lines
17 KiB
C++
/*
|
|
* HLLib
|
|
* Copyright (C) 2006-2010 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 "NCFFile.h"
|
|
#include "Streams.h"
|
|
#include "Checksum.h"
|
|
#include "Utility.h"
|
|
|
|
using namespace HLLib;
|
|
|
|
#define HL_NCF_FLAG_FILE 0x00004000 // The item is a file.
|
|
#define HL_NCF_FLAG_ENCRYPTED 0x00000100 // The item is encrypted.
|
|
#define HL_NCF_FLAG_BACKUP_LOCAL 0x00000040 // Backup the item before overwriting it.
|
|
#define HL_NCF_FLAG_COPY_LOCAL 0x0000000a // The item is to be copied to the disk.
|
|
#define HL_NCF_FLAG_COPY_LOCAL_NO_OVERWRITE 0x00000001 // Don't overwrite the item if copying it to the disk and the item already exists.
|
|
|
|
const char *CNCFFile::lpAttributeNames[] = { "Version", "Cache ID", "Last Version Played" };
|
|
const char *CNCFFile::lpItemAttributeNames[] = { "Encrypted", "Copy Locally", "Overwrite Local Copy", "Backup Local Copy", "Flags" };
|
|
|
|
CNCFFile::CNCFFile() : CPackage(), lpRootPath(0), pHeaderView(0)
|
|
{
|
|
this->pHeader = 0;
|
|
|
|
this->pDirectoryHeader = 0;
|
|
this->lpDirectoryEntries = 0;
|
|
this->lpDirectoryNames = 0;
|
|
this->lpDirectoryInfo1Entries = 0;
|
|
this->lpDirectoryInfo2Entries = 0;
|
|
this->lpDirectoryCopyEntries = 0;
|
|
this->lpDirectoryLocalEntries = 0;
|
|
|
|
this->pUnknownHeader = 0;
|
|
this->lpUnknownEntries = 0;
|
|
|
|
this->pChecksumHeader = 0;
|
|
this->pChecksumMapHeader = 0;
|
|
this->lpChecksumMapEntries = 0;
|
|
this->lpChecksumEntries = 0;
|
|
}
|
|
|
|
CNCFFile::~CNCFFile()
|
|
{
|
|
this->Close();
|
|
}
|
|
|
|
HLPackageType CNCFFile::GetType() const
|
|
{
|
|
return HL_PACKAGE_NCF;
|
|
}
|
|
|
|
const hlChar *CNCFFile::GetExtension() const
|
|
{
|
|
return "ncf";
|
|
}
|
|
|
|
const hlChar *CNCFFile::GetDescription() const
|
|
{
|
|
return "Half-Life No Cache File";
|
|
}
|
|
|
|
const hlChar *CNCFFile::GetRootPath() const
|
|
{
|
|
return this->lpRootPath;
|
|
}
|
|
|
|
hlVoid CNCFFile::SetRootPath(const hlChar *lpRootPath)
|
|
{
|
|
if(!this->GetOpened())
|
|
{
|
|
return;
|
|
}
|
|
|
|
delete []this->lpRootPath;
|
|
this->lpRootPath = 0;
|
|
|
|
if(lpRootPath == 0 || *lpRootPath == '\0')
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->lpRootPath = new hlChar[strlen(lpRootPath) + 1];
|
|
strcpy(this->lpRootPath, lpRootPath);
|
|
}
|
|
|
|
hlBool CNCFFile::MapDataStructures()
|
|
{
|
|
//
|
|
// Determine the size of the header and validate it.
|
|
//
|
|
|
|
hlUInt uiHeaderSize = 0;
|
|
|
|
// Header.
|
|
|
|
if(sizeof(NCFHeader) > 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(NCFHeader)))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
this->pHeader = (NCFHeader *)this->pHeaderView->GetView();
|
|
|
|
hlBool bNull = hlTrue;
|
|
for(hlByte *pTest = (hlByte *)this->pHeader; pTest < (hlByte *)this->pHeader + sizeof(NCFHeader); pTest++)
|
|
{
|
|
if(*pTest != 0)
|
|
{
|
|
bNull = hlFalse;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(bNull)
|
|
{
|
|
LastError.SetErrorMessage("Invalid file: the file's header is null (contains no data).");
|
|
return hlFalse;
|
|
}
|
|
|
|
if(this->pHeader->uiMajorVersion != 2 || this->pHeader->uiMinorVersion != 1)
|
|
{
|
|
LastError.SetErrorMessageFormated("Invalid NCF version (v%u.%u): you have a version of a NCF file that HLLib does not know how to read. Check for product updates.", this->pHeader->uiMajorVersion, this->pHeader->uiMinorVersion);
|
|
return hlFalse;
|
|
}
|
|
|
|
uiHeaderSize += sizeof(NCFHeader);
|
|
|
|
// Directory.
|
|
|
|
if(!this->pMapping->Map(this->pHeaderView, uiHeaderSize, sizeof(NCFDirectoryHeader)))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
this->pDirectoryHeader = (NCFDirectoryHeader *)this->pHeaderView->GetView();
|
|
|
|
uiHeaderSize += this->pDirectoryHeader->uiDirectorySize;/*sizeof(NCFDirectoryHeader);
|
|
+ this->pDirectoryHeader->uiItemCount * sizeof(NCFDirectoryEntry)
|
|
+ this->pDirectoryHeader->uiNameSize
|
|
+ this->pDirectoryHeader->uiInfo1Count * sizeof(NCFDirectoryInfo1Entry)
|
|
+ this->pDirectoryHeader->uiItemCount * sizeof(NCFDirectoryInfo2Entry)
|
|
+ this->pDirectoryHeader->uiCopyCount * sizeof(NCFDirectoryCopyEntry)
|
|
+ this->pDirectoryHeader->uiLocalCount * sizeof(NCFDirectoryLocalEntry);*/
|
|
|
|
uiHeaderSize += sizeof(NCFUnknownHeader);
|
|
|
|
uiHeaderSize += this->pDirectoryHeader->uiItemCount * sizeof(NCFUnknownEntry);
|
|
|
|
// Checksums.
|
|
|
|
if(!this->pMapping->Map(this->pHeaderView, uiHeaderSize, sizeof(NCFChecksumHeader)))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
this->pChecksumHeader = (NCFChecksumHeader *)this->pHeaderView->GetView();
|
|
|
|
uiHeaderSize += sizeof(NCFChecksumHeader) + this->pChecksumHeader->uiChecksumSize;
|
|
|
|
//
|
|
// Map the header.
|
|
//
|
|
|
|
if(!this->pMapping->Map(this->pHeaderView, 0, uiHeaderSize))
|
|
{
|
|
return hlFalse;
|
|
}
|
|
|
|
this->pHeader = (NCFHeader *)this->pHeaderView->GetView();
|
|
|
|
this->pDirectoryHeader = (NCFDirectoryHeader *)((hlByte *)this->pHeader + sizeof(NCFHeader));
|
|
|
|
this->lpDirectoryEntries = (NCFDirectoryEntry *)((hlByte *)this->pDirectoryHeader + sizeof(NCFDirectoryHeader));
|
|
|
|
this->lpDirectoryNames = (hlChar *)((hlByte *)this->lpDirectoryEntries + sizeof(NCFDirectoryEntry) * this->pDirectoryHeader->uiItemCount);
|
|
|
|
this->lpDirectoryInfo1Entries = (NCFDirectoryInfo1Entry *)((hlByte *)this->lpDirectoryNames + this->pDirectoryHeader->uiNameSize);
|
|
this->lpDirectoryInfo2Entries = (NCFDirectoryInfo2Entry *)((hlByte *)this->lpDirectoryInfo1Entries + sizeof(NCFDirectoryInfo1Entry) * this->pDirectoryHeader->uiInfo1Count);
|
|
|
|
this->lpDirectoryCopyEntries = (NCFDirectoryCopyEntry *)((hlByte *)this->lpDirectoryInfo2Entries + sizeof(NCFDirectoryInfo2Entry) * this->pDirectoryHeader->uiItemCount);
|
|
this->lpDirectoryLocalEntries = (NCFDirectoryLocalEntry *)((hlByte *)this->lpDirectoryCopyEntries + sizeof(NCFDirectoryCopyEntry) * this->pDirectoryHeader->uiCopyCount);
|
|
|
|
this->pUnknownHeader = (NCFUnknownHeader *)((hlByte *)this->pDirectoryHeader + this->pDirectoryHeader->uiDirectorySize);
|
|
this->lpUnknownEntries = (NCFUnknownEntry *)((hlByte *)this->pUnknownHeader + sizeof(NCFUnknownHeader));
|
|
|
|
this->pChecksumHeader = (NCFChecksumHeader *)((hlByte *)this->lpUnknownEntries + this->pDirectoryHeader->uiItemCount * sizeof(NCFUnknownEntry));
|
|
this->pChecksumMapHeader = (NCFChecksumMapHeader *)((hlByte *)(this->pChecksumHeader) + sizeof(NCFChecksumHeader));
|
|
|
|
this->lpChecksumMapEntries = (NCFChecksumMapEntry *)((hlByte *)(this->pChecksumMapHeader) + sizeof(NCFChecksumMapHeader));
|
|
this->lpChecksumEntries = (NCFChecksumEntry *)((hlByte *)(this->lpChecksumMapEntries) + sizeof(NCFChecksumMapEntry) * this->pChecksumMapHeader->uiItemCount);
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlVoid CNCFFile::UnmapDataStructures()
|
|
{
|
|
delete []this->lpRootPath;
|
|
this->lpRootPath = 0;
|
|
|
|
this->pHeader = 0;
|
|
|
|
this->pDirectoryHeader = 0;
|
|
this->lpDirectoryEntries = 0;
|
|
this->lpDirectoryNames = 0;
|
|
this->lpDirectoryInfo1Entries = 0;
|
|
this->lpDirectoryInfo2Entries = 0;
|
|
this->lpDirectoryCopyEntries = 0;
|
|
this->lpDirectoryLocalEntries = 0;
|
|
|
|
this->pUnknownHeader = 0;
|
|
this->lpUnknownEntries = 0;
|
|
|
|
this->pChecksumHeader = 0;
|
|
this->pChecksumMapHeader = 0;
|
|
this->lpChecksumMapEntries = 0;
|
|
this->lpChecksumEntries = 0;
|
|
|
|
this->pMapping->Unmap(this->pHeaderView);
|
|
}
|
|
|
|
CDirectoryFolder *CNCFFile::CreateRoot()
|
|
{
|
|
CDirectoryFolder *pRoot = new CDirectoryFolder("root", 0, 0, this, 0);
|
|
|
|
this->CreateRoot(pRoot);
|
|
|
|
return pRoot;
|
|
}
|
|
|
|
hlVoid CNCFFile::CreateRoot(CDirectoryFolder *pFolder)
|
|
{
|
|
// Get the first directory item.
|
|
hlUInt uiIndex = this->lpDirectoryEntries[pFolder->GetID()].uiFirstIndex;
|
|
|
|
// Loop through directory items.
|
|
while(uiIndex && uiIndex != 0xffffffff)
|
|
{
|
|
// Check if the item is a folder.
|
|
if((this->lpDirectoryEntries[uiIndex].uiDirectoryFlags & HL_NCF_FLAG_FILE) == 0)
|
|
{
|
|
// Add the directory item to the current folder.
|
|
CDirectoryFolder *pSubFolder = pFolder->AddFolder(this->lpDirectoryNames + this->lpDirectoryEntries[uiIndex].uiNameOffset, uiIndex);
|
|
|
|
// Build the new folder.
|
|
this->CreateRoot(pSubFolder);
|
|
}
|
|
else
|
|
{
|
|
// Add the directory item to the current folder.
|
|
pFolder->AddFile(this->lpDirectoryNames + this->lpDirectoryEntries[uiIndex].uiNameOffset, uiIndex);
|
|
}
|
|
|
|
// Get the next directory item.
|
|
uiIndex = this->lpDirectoryEntries[uiIndex].uiNextIndex;
|
|
}
|
|
}
|
|
|
|
hlUInt CNCFFile::GetAttributeCountInternal() const
|
|
{
|
|
return HL_NCF_PACKAGE_COUNT;
|
|
}
|
|
|
|
const hlChar *CNCFFile::GetAttributeNameInternal(HLPackageAttribute eAttribute) const
|
|
{
|
|
if(eAttribute < HL_NCF_PACKAGE_COUNT)
|
|
{
|
|
return this->lpAttributeNames[eAttribute];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
hlBool CNCFFile::GetAttributeInternal(HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
switch(eAttribute)
|
|
{
|
|
case HL_NCF_PACKAGE_VERSION:
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiMinorVersion, hlFalse);
|
|
return hlTrue;
|
|
case HL_NCF_PACKAGE_ID:
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiCacheID, hlFalse);
|
|
return hlTrue;
|
|
case HL_NCF_PACKAGE_LAST_VERSION_PLAYED:
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpAttributeNames[eAttribute], this->pHeader->uiLastVersionPlayed, hlFalse);
|
|
return hlTrue;
|
|
default:
|
|
return hlFalse;
|
|
}
|
|
}
|
|
|
|
hlUInt CNCFFile::GetItemAttributeCountInternal() const
|
|
{
|
|
return HL_NCF_ITEM_COUNT;
|
|
}
|
|
|
|
const hlChar *CNCFFile::GetItemAttributeNameInternal(HLPackageAttribute eAttribute) const
|
|
{
|
|
if(eAttribute < HL_NCF_ITEM_COUNT)
|
|
{
|
|
return this->lpItemAttributeNames[eAttribute];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
hlBool CNCFFile::GetItemAttributeInternal(const CDirectoryItem *pItem, HLPackageAttribute eAttribute, HLAttribute &Attribute) const
|
|
{
|
|
switch(pItem->GetType())
|
|
{
|
|
case HL_ITEM_FILE:
|
|
{
|
|
const CDirectoryFile *pFile = static_cast<const CDirectoryFile *>(pItem);
|
|
switch(eAttribute)
|
|
{
|
|
case HL_NCF_ITEM_ENCRYPTED:
|
|
{
|
|
hlAttributeSetBoolean(&Attribute, this->lpItemAttributeNames[eAttribute], (this->lpDirectoryEntries[pFile->GetID()].uiDirectoryFlags & HL_NCF_FLAG_ENCRYPTED) != 0);
|
|
return hlTrue;
|
|
}
|
|
case HL_NCF_ITEM_COPY_LOCAL:
|
|
{
|
|
hlAttributeSetBoolean(&Attribute, this->lpItemAttributeNames[eAttribute], (this->lpDirectoryEntries[pFile->GetID()].uiDirectoryFlags & HL_NCF_FLAG_COPY_LOCAL) != 0);
|
|
return hlTrue;
|
|
}
|
|
case HL_NCF_ITEM_OVERWRITE_LOCAL:
|
|
{
|
|
hlAttributeSetBoolean(&Attribute, this->lpItemAttributeNames[eAttribute], (this->lpDirectoryEntries[pFile->GetID()].uiDirectoryFlags & HL_NCF_FLAG_COPY_LOCAL_NO_OVERWRITE) == 0);
|
|
return hlTrue;
|
|
}
|
|
case HL_NCF_ITEM_BACKUP_LOCAL:
|
|
{
|
|
hlAttributeSetBoolean(&Attribute, this->lpItemAttributeNames[eAttribute], (this->lpDirectoryEntries[pFile->GetID()].uiDirectoryFlags & HL_NCF_FLAG_BACKUP_LOCAL) != 0);
|
|
return hlTrue;
|
|
}
|
|
case HL_NCF_ITEM_FLAGS:
|
|
{
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpItemAttributeNames[eAttribute], this->lpDirectoryEntries[pFile->GetID()].uiDirectoryFlags, hlTrue);
|
|
return hlTrue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case HL_ITEM_FOLDER:
|
|
{
|
|
const CDirectoryFolder *pFolder = static_cast<const CDirectoryFolder *>(pItem);
|
|
switch(eAttribute)
|
|
{
|
|
case HL_NCF_ITEM_FLAGS:
|
|
{
|
|
hlAttributeSetUnsignedInteger(&Attribute, this->lpItemAttributeNames[eAttribute], this->lpDirectoryEntries[pFolder->GetID()].uiDirectoryFlags, hlTrue);
|
|
return hlTrue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return hlFalse;
|
|
}
|
|
|
|
hlBool CNCFFile::GetFileExtractableInternal(const CDirectoryFile *pFile, hlBool &bExtractable) const
|
|
{
|
|
bExtractable = hlFalse;
|
|
|
|
if(this->lpRootPath != 0)
|
|
{
|
|
hlChar lpTemp[512];
|
|
this->GetPath(pFile, lpTemp, sizeof(lpTemp));
|
|
|
|
hlUInt uiSize;
|
|
if(HLLib::GetFileSize(lpTemp, uiSize))
|
|
{
|
|
if(uiSize >= this->lpDirectoryEntries[pFile->GetID()].uiItemSize)
|
|
{
|
|
bExtractable = hlTrue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(this->lpDirectoryEntries[pFile->GetID()].uiItemSize == 0)
|
|
{
|
|
bExtractable = hlTrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlBool CNCFFile::GetFileValidationInternal(const CDirectoryFile *pFile, HLValidation &eValidation) const
|
|
{
|
|
if(this->lpRootPath != 0)
|
|
{
|
|
hlChar lpTemp[512];
|
|
this->GetPath(pFile, lpTemp, sizeof(lpTemp));
|
|
|
|
hlUInt uiSize;
|
|
if(HLLib::GetFileSize(lpTemp, uiSize))
|
|
{
|
|
if(uiSize < this->lpDirectoryEntries[pFile->GetID()].uiItemSize)
|
|
{
|
|
eValidation = HL_VALIDATES_INCOMPLETE;
|
|
}
|
|
else if(this->lpDirectoryEntries[pFile->GetID()].uiDirectoryFlags & HL_NCF_FLAG_ENCRYPTED)
|
|
{
|
|
// No way of checking, assume it's ok.
|
|
eValidation = HL_VALIDATES_ASSUMED_OK;
|
|
}
|
|
else if(this->lpDirectoryEntries[pFile->GetID()].uiChecksumIndex == 0xffffffff)
|
|
{
|
|
// File has no checksum.
|
|
eValidation = HL_VALIDATES_ASSUMED_OK;
|
|
}
|
|
else
|
|
{
|
|
Streams::CFileStream Stream = Streams::CFileStream(lpTemp);
|
|
|
|
if(Stream.Open(HL_MODE_READ))
|
|
{
|
|
eValidation = HL_VALIDATES_OK;
|
|
|
|
hlULongLong uiTotalBytes = 0, uiFileBytes = Stream.GetStreamSize();
|
|
hlUInt uiBufferSize;
|
|
hlByte *lpBuffer = new hlByte[this->pDirectoryHeader->uiChecksumDataLength];
|
|
|
|
const NCFChecksumMapEntry *pChecksumMapEntry = this->lpChecksumMapEntries + this->lpDirectoryEntries[pFile->GetID()].uiChecksumIndex;
|
|
|
|
hlBool bCancel = hlFalse;
|
|
hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
|
|
|
|
hlUInt i = 0;
|
|
while((uiBufferSize = Stream.Read(lpBuffer, this->pDirectoryHeader->uiChecksumDataLength)) != 0)
|
|
{
|
|
if(bCancel)
|
|
{
|
|
// User canceled.
|
|
eValidation = HL_VALIDATES_CANCELED;
|
|
break;
|
|
}
|
|
|
|
if(i >= pChecksumMapEntry->uiChecksumCount)
|
|
{
|
|
// Something bad happened.
|
|
eValidation = HL_VALIDATES_ERROR;
|
|
break;
|
|
}
|
|
|
|
hlULong uiChecksum = Adler32(lpBuffer, uiBufferSize) ^ CRC32(lpBuffer, uiBufferSize);
|
|
if(uiChecksum != this->lpChecksumEntries[pChecksumMapEntry->uiFirstChecksumIndex + i].uiChecksum)
|
|
{
|
|
eValidation = HL_VALIDATES_CORRUPT;
|
|
break;
|
|
}
|
|
|
|
uiTotalBytes += static_cast<hlULongLong>(uiBufferSize);
|
|
|
|
hlValidateFileProgress(const_cast<CDirectoryFile *>(pFile), uiTotalBytes, uiFileBytes, &bCancel);
|
|
|
|
i++;
|
|
}
|
|
|
|
delete []lpBuffer;
|
|
|
|
Stream.Close();
|
|
}
|
|
else
|
|
{
|
|
eValidation = HL_VALIDATES_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not found.
|
|
if(this->lpDirectoryEntries[pFile->GetID()].uiItemSize != 0)
|
|
{
|
|
eValidation = HL_VALIDATES_INCOMPLETE;
|
|
}
|
|
else
|
|
{
|
|
eValidation = HL_VALIDATES_OK;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eValidation = HL_VALIDATES_ASSUMED_OK;
|
|
}
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlBool CNCFFile::GetFileSizeInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
|
|
{
|
|
uiSize = this->lpDirectoryEntries[pFile->GetID()].uiItemSize;
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlBool CNCFFile::GetFileSizeOnDiskInternal(const CDirectoryFile *pFile, hlUInt &uiSize) const
|
|
{
|
|
uiSize = 0;
|
|
|
|
if(this->lpRootPath != 0)
|
|
{
|
|
hlChar lpTemp[512];
|
|
this->GetPath(pFile, lpTemp, sizeof(lpTemp));
|
|
|
|
HLLib::GetFileSize(lpTemp, uiSize);
|
|
}
|
|
|
|
return hlTrue;
|
|
}
|
|
|
|
hlBool CNCFFile::CreateStreamInternal(const CDirectoryFile *pFile, Streams::IStream *&pStream) const
|
|
{
|
|
if(!bReadEncrypted && this->lpDirectoryEntries[pFile->GetID()].uiDirectoryFlags & HL_NCF_FLAG_ENCRYPTED)
|
|
{
|
|
LastError.SetErrorMessage("File is encrypted.");
|
|
return hlFalse;
|
|
}
|
|
|
|
if(this->lpRootPath != 0)
|
|
{
|
|
hlChar lpTemp[512];
|
|
this->GetPath(pFile, lpTemp, sizeof(lpTemp));
|
|
|
|
hlUInt uiSize;
|
|
if(HLLib::GetFileSize(lpTemp, uiSize))
|
|
{
|
|
if(uiSize >= this->lpDirectoryEntries[pFile->GetID()].uiItemSize)
|
|
{
|
|
pStream = new Streams::CFileStream(lpTemp);
|
|
return hlTrue;
|
|
}
|
|
else
|
|
{
|
|
LastError.SetErrorMessage("File is incomplete.");
|
|
return hlFalse;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(this->lpDirectoryEntries[pFile->GetID()].uiItemSize == 0)
|
|
{
|
|
// Fake an empty stream.
|
|
pStream = new Streams::CNullStream();
|
|
return hlTrue;
|
|
}
|
|
else
|
|
{
|
|
LastError.SetErrorMessage("File not found.");
|
|
return hlFalse;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LastError.SetErrorMessage("NCF files are indexes and do not contain any file data.");
|
|
return hlFalse;
|
|
}
|
|
}
|
|
|
|
hlVoid CNCFFile::GetPath(const CDirectoryFile *pFile, hlChar *lpPath, hlUInt uiPathSize) const
|
|
{
|
|
hlChar *lpTemp = new hlChar[uiPathSize];
|
|
|
|
strncpy(lpPath, pFile->GetName(), uiPathSize);
|
|
lpPath[uiPathSize - 1] = '\0';
|
|
|
|
const CDirectoryItem *pItem = pFile->GetParent();
|
|
while(pItem)
|
|
{
|
|
strcpy(lpTemp, lpPath);
|
|
|
|
if(pItem->GetParent() == 0)
|
|
{
|
|
strncpy(lpPath, this->lpRootPath, uiPathSize);
|
|
}
|
|
else
|
|
{
|
|
strncpy(lpPath, pItem->GetName(), uiPathSize);
|
|
}
|
|
lpPath[uiPathSize - 1] = '\0';
|
|
|
|
strncat(lpPath, PATH_SEPARATOR_STRING, uiPathSize - strlen(lpPath) - 1);
|
|
|
|
strncat(lpPath, lpTemp, uiPathSize - strlen(lpPath) - 1);
|
|
|
|
pItem = pItem->GetParent();
|
|
}
|
|
|
|
delete []lpTemp;
|
|
}
|