Serious-Engine/Sources/Engine/Base/Stream.cpp
Daniel Gibson 72edf1c720 Commented out unused functions and variables
many unused functions and variables are now commented out

You'll still get tons of warnings, which should mostly fall in one of
the following categories:
1. Unnecessary variables or values generated from .es scripts
2. Pointers assigned to from functions with side-effects: DO NOT REMOVE!
   Like CEntity *penNew = CreateEntity_t(...); - even if penNew isn't
   used, CreateEntity() must be called there!
2016-05-09 18:51:03 +02:00

1616 lines
43 KiB
C++
Executable File

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include "Engine/StdH.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// !!! FIXME : rcg10162001 Need this anymore, since _findfirst() is abstracted?
#ifdef PLATFORM_WIN32
#include <io.h>
#endif
#include <Engine/Base/Protection.h>
#include <Engine/Base/Stream.h>
#include <Engine/Base/Memory.h>
#include <Engine/Base/Console.h>
#include <Engine/Base/ErrorReporting.h>
#include <Engine/Base/ListIterator.inl>
#include <Engine/Base/ProgressHook.h>
#include <Engine/Base/Unzip.h>
#include <Engine/Base/CRC.h>
#include <Engine/Base/Shell.h>
#include <Engine/Base/FileSystem.h>
#include <Engine/Templates/NameTable_CTFileName.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/DynamicStackArray.cpp>
#include <Engine/Templates/Stock_CTextureData.h>
#include <Engine/Templates/Stock_CModelData.h>
// default size of page used for stream IO operations (4Kb)
ULONG _ulPageSize = 0;
// maximum length of file that can be saved (default: 128Mb)
ULONG _ulMaxLenghtOfSavingFile = (1UL<<20)*128;
INDEX fil_bPreferZips = FALSE;
// set if current thread has currently enabled stream handling
THREADLOCAL(BOOL, _bThreadCanHandleStreams, FALSE);
// list of currently opened streams
ULONG _ulVirtuallyAllocatedSpace = 0;
ULONG _ulVirtuallyAllocatedSpaceTotal = 0;
THREADLOCAL(CListHead *, _plhOpenedStreams, NULL);
// global string with application path
CTFileName _fnmApplicationPath;
// global string with filename of the started application
CTFileName _fnmApplicationExe;
// global string with user-specific writable directory.
CTFileName _fnmUserDir;
// global string with current MOD path
CTFileName _fnmMod;
// global string with current name (the parameter that is passed on cmdline)
CTString _strModName;
// global string with url to be shown to users that don't have the mod installed
// (should be set by game.dll)
CTString _strModURL;
// global string with current MOD extension (for adding to dlls)
CTString _strModExt;
// global string with CD path (for minimal installations)
CTFileName _fnmCDPath;
// include/exclude lists for base dir writing/browsing
CDynamicStackArray<CTFileName> _afnmBaseWriteInc;
CDynamicStackArray<CTFileName> _afnmBaseWriteExc;
CDynamicStackArray<CTFileName> _afnmBaseBrowseInc;
CDynamicStackArray<CTFileName> _afnmBaseBrowseExc;
// list of paths or patterns that are not included when making CRCs for network connection
// this is used to enable connection between different localized versions
CDynamicStackArray<CTFileName> _afnmNoCRC;
// load a filelist
static BOOL LoadFileList(CDynamicStackArray<CTFileName> &afnm, const CTFileName &fnmList)
{
afnm.PopAll();
try {
CTFileStream strm;
strm.Open_t(fnmList);
while(!strm.AtEOF()) {
CTString strLine;
strm.GetLine_t(strLine);
strLine.TrimSpacesLeft();
strLine.TrimSpacesRight();
if (strLine!="") {
afnm.Push() = strLine;
}
}
return TRUE;
} catch(char *strError) {
CPrintF("%s\n", strError);
return FALSE;
}
}
extern BOOL FileMatchesList(CDynamicStackArray<CTFileName> &afnm, const CTFileName &fnm)
{
for(INDEX i=0; i<afnm.Count(); i++) {
if (fnm.Matches(afnm[i]) || fnm.HasPrefix(afnm[i])) {
return TRUE;
}
}
return FALSE;
}
static CTFileName _fnmApp;
void InitStreams(void)
{
// obtain information about system
// !!! FIXME: Move this into an abstraction of some sort...
#ifdef PLATFORM_WIN32
SYSTEM_INFO siSystemInfo;
GetSystemInfo( &siSystemInfo);
// and remember page size
_ulPageSize = siSystemInfo.dwPageSize*16; // cca. 64kB on WinNT/Win95
#else
_ulPageSize = PAGESIZE;
#endif
// keep a copy of path for setting purposes
_fnmApp = _fnmApplicationPath;
// if no mod defined yet
if (_fnmMod=="") {
// check for 'default mod' file
LoadStringVar(CTString("DefaultMod.txt"), _fnmMod);
}
CPrintF(TRANSV("Current mod: %s\n"),
(_fnmMod=="") ? TRANS("<none>") :
(const char *) (CTString&)_fnmMod);
// if there is a mod active
if (_fnmMod!="") {
// load mod's include/exclude lists
CPrintF(TRANSV("Loading mod include/exclude lists...\n"));
BOOL bOK = FALSE;
bOK |= LoadFileList(_afnmBaseWriteInc , CTString("BaseWriteInclude.lst"));
bOK |= LoadFileList(_afnmBaseWriteExc , CTString("BaseWriteExclude.lst"));
bOK |= LoadFileList(_afnmBaseBrowseInc, CTString("BaseBrowseInclude.lst"));
bOK |= LoadFileList(_afnmBaseBrowseExc, CTString("BaseBrowseExclude.lst"));
// if none found
if (!bOK) {
// the mod is not valid
_fnmMod = CTString("");
CPrintF(TRANSV("Error: MOD not found!\n"));
// if mod is ok
} else {
// remember mod name (the parameter that is passed on cmdline)
_strModName = _fnmMod;
_strModName.DeleteChar(_strModName.Length()-1);
_strModName = CTFileName(_strModName).FileName();
}
}
// find eventual extension for the mod's dlls
_strModExt = "";
LoadStringVar(CTString("ModExt.txt"), _strModExt);
CPrintF(TRANSV("Loading group files...\n"));
CDynamicArray<CTString> *files = NULL;
// for each group file in base directory
files = _pFileSystem->FindFiles(_fnmApplicationPath, "*.gro");
int max = files->Count();
int i;
// for each .gro file in the directory
for (i = 0; i < max; i++) {
// add it to active set
UNZIPAddArchive( _fnmApplicationPath+((*files)[i]) );
}
delete files;
// if there is a mod active
if (_fnmMod!="") {
// for each group file in mod directory
files = _pFileSystem->FindFiles(_fnmApplicationPath+_fnmMod, "*.gro");
max = files->Count();
for (i = 0; i < max; i++) {
UNZIPAddArchive( _fnmApplicationPath + _fnmMod + ((*files)[i]) );
}
delete files;
}
// if there is a CD path
if (_fnmCDPath!="") {
// for each group file on the CD
files = _pFileSystem->FindFiles(_fnmCDPath, "*.gro");
max = files->Count();
for (i = 0; i < max; i++) {
UNZIPAddArchive( _fnmCDPath + ((*files)[i]) );
}
delete files;
// if there is a mod active
if (_fnmMod!="") {
// for each group file in mod directory
files = _pFileSystem->FindFiles(_fnmCDPath+_fnmMod, "*.gro");
max = files->Count();
for (i = 0; i < max; i++) {
UNZIPAddArchive( _fnmCDPath + _fnmMod + ((*files)[i]) );
}
delete files;
}
}
// try to
try {
// read the zip directories
UNZIPReadDirectoriesReverse_t();
// if failed
} catch( char *strError) {
// report warning
CPrintF( TRANS("There were group file errors:\n%s"), strError);
}
CPrintF("\n");
const char *dirsep = CFileSystem::GetDirSeparator();
LoadFileList(_afnmNoCRC, CTFILENAME("Data") + CTString(dirsep) + CTString("NoCRC.lst"));
_pShell->SetINDEX(CTString("sys")+"_iCPU"+"Misc", 1);
}
void EndStreams(void)
{
}
void UseApplicationPath(void)
{
_fnmApplicationPath = _fnmApp;
}
void IgnoreApplicationPath(void)
{
_fnmApplicationPath = CTString("");
}
/////////////////////////////////////////////////////////////////////////////
// Helper functions
/* Static function enable stream handling. */
void CTStream::EnableStreamHandling(void)
{
ASSERT(!_bThreadCanHandleStreams && _plhOpenedStreams == NULL);
_bThreadCanHandleStreams = TRUE;
_plhOpenedStreams = new CListHead;
}
/* Static function disable stream handling. */
void CTStream::DisableStreamHandling(void)
{
ASSERT(_bThreadCanHandleStreams && _plhOpenedStreams != NULL);
_bThreadCanHandleStreams = FALSE;
delete _plhOpenedStreams;
_plhOpenedStreams = NULL;
}
/*
* Throw an exception of formatted string.
*/
void CTStream::Throw_t(const char *strFormat, ...) // throws char *
{
const SLONG slBufferSize = 256;
char strFormatBuffer[slBufferSize];
static char *strBuffer = NULL;
// ...and yes, you are screwed if you call this in a catch block and
// try to access the previous text again.
delete[] strBuffer;
strBuffer = new char[slBufferSize];
// add the stream description to the format string
_snprintf(strFormatBuffer, slBufferSize, "%s (%s)", strFormat, (const char *) strm_strStreamDescription);
// format the message in buffer
va_list arg;
va_start(arg, strFormat); // variable arguments start after this argument
_vsnprintf(strBuffer, slBufferSize, strFormatBuffer, arg);
va_end(arg);
throw strBuffer;
}
/////////////////////////////////////////////////////////////////////////////
// Binary access methods
/* Get CRC32 of stream */
ULONG CTStream::GetStreamCRC32_t(void)
{
// remember where stream is now
SLONG slOldPos = GetPos_t();
// go to start of file
SetPos_t(0);
// get size of file
SLONG slFileSize = GetStreamSize();
ULONG ulCRC;
CRC_Start(ulCRC);
// for each block in file
const SLONG slBlockSize = 4096;
for(SLONG slPos=0; slPos<slFileSize; slPos+=slBlockSize) {
// read the block
UBYTE aubBlock[slBlockSize];
SLONG slThisBlockSize = Min(slFileSize-slPos, slBlockSize);
Read_t(aubBlock, slThisBlockSize);
// checksum it
CRC_AddBlock(ulCRC, aubBlock, slThisBlockSize);
}
// restore position
SetPos_t(slOldPos);
CRC_Finish(ulCRC);
return ulCRC;
}
/////////////////////////////////////////////////////////////////////////////
// Text access methods
/* Get a line of text from file. */
// throws char *
void CTStream::GetLine_t(char *strBuffer, SLONG slBufferSize, char cDelimiter /*='\n'*/ )
{
// check parameters
ASSERT(strBuffer!=NULL && slBufferSize>0);
// check that the stream can be read
ASSERT(IsReadable());
// letters slider
INDEX iLetters = 0;
// test if EOF reached
if(AtEOF()) {
ThrowF_t(TRANS("EOF reached, file %s"), (const char *) strm_strStreamDescription);
}
// get line from istream
FOREVER
{
char c;
Read_t(&c, 1);
if(AtEOF()) {
// cut off
strBuffer[ iLetters] = 0;
break;
}
// don't read "\r" characters but rather act like they don't exist
if( c != '\r') {
strBuffer[ iLetters] = c;
// stop reading when delimiter loaded
if( strBuffer[ iLetters] == cDelimiter) {
// convert delimiter to zero
strBuffer[ iLetters] = 0;
// jump over delimiter
//Seek_t(1, SD_CUR);
break;
}
// jump to next destination letter
iLetters++;
}
// test if maximum buffer lenght reached
if( iLetters==slBufferSize) {
return;
}
}
}
void CTStream::GetLine_t(CTString &strLine, char cDelimiter/*='\n'*/) // throw char *
{
char strBuffer[1024];
GetLine_t(strBuffer, sizeof(strBuffer)-1, cDelimiter);
strLine = strBuffer;
}
/* Put a line of text into file. */
void CTStream::PutLine_t(const char *strBuffer) // throws char *
{
// check parameters
ASSERT(strBuffer!=NULL);
// check that the stream is writteable
ASSERT(IsWriteable());
// get string length
INDEX iStringLength = strlen(strBuffer);
// put line into stream
Write_t(strBuffer, iStringLength);
// write "\r\n" into stream
Write_t("\r\n", 2);
}
void CTStream::PutString_t(const char *strString) // throw char *
{
// check parameters
ASSERT(strString!=NULL);
// check that the stream is writteable
ASSERT(IsWriteable());
// get string length
INDEX iStringLength = strlen(strString);
// put line into stream
for( INDEX iLetter=0; iLetter<iStringLength; iLetter++)
{
if (*strString=='\n') {
// write "\r\n" into stream
Write_t("\r\n", 2);
strString++;
} else {
Write_t(strString++, 1);
}
}
}
void CTStream::FPrintF_t(const char *strFormat, ...) // throw char *
{
const SLONG slBufferSize = 2048;
char strBuffer[slBufferSize];
// format the message in buffer
va_list arg;
va_start(arg, strFormat); // variable arguments start after this argument
_vsnprintf(strBuffer, slBufferSize, strFormat, arg);
va_end(arg);
// print the buffer
PutString_t(strBuffer);
}
/////////////////////////////////////////////////////////////////////////////
// Chunk reading/writing methods
CChunkID CTStream::GetID_t(void) // throws char *
{
CChunkID cidToReturn;
Read_t( &cidToReturn.cid_ID[0], CID_LENGTH);
return( cidToReturn);
}
CChunkID CTStream::PeekID_t(void) // throw char *
{
// read the chunk id
CChunkID cidToReturn;
Read_t( &cidToReturn.cid_ID[0], CID_LENGTH);
// return the stream back
Seek_t(-CID_LENGTH, SD_CUR);
return( cidToReturn);
}
void CTStream::ExpectID_t(const CChunkID &cidExpected) // throws char *
{
CChunkID cidToCompare;
Read_t( &cidToCompare.cid_ID[0], CID_LENGTH);
if( !(cidToCompare == cidExpected))
{
ThrowF_t(TRANS("Chunk ID validation failed.\nExpected ID \"%s\" but found \"%s\"\n"),
cidExpected.cid_ID, cidToCompare.cid_ID);
}
}
void CTStream::ExpectKeyword_t(const CTString &strKeyword) // throw char *
{
// check that the keyword is present
const INDEX total = (INDEX)strlen(strKeyword);
for(INDEX iKeywordChar=0; iKeywordChar<total; iKeywordChar++) {
SBYTE chKeywordChar;
(*this)>>chKeywordChar;
if (chKeywordChar!=strKeyword[iKeywordChar]) {
ThrowF_t(TRANS("Expected keyword %s not found"), (const char *) strKeyword);
}
}
}
SLONG CTStream::GetSize_t(void) // throws char *
{
SLONG chunkSize;
Read_t( (char *) &chunkSize, sizeof( SLONG));
return( chunkSize);
}
void CTStream::ReadRawChunk_t(void *pvBuffer, SLONG slSize) // throws char *
{
Read_t((char *)pvBuffer, slSize);
}
void CTStream::ReadChunk_t(void *pvBuffer, SLONG slExpectedSize) // throws char *
{
if( slExpectedSize != GetSize_t())
throw TRANS("Chunk size not equal as expected size");
Read_t((char *)pvBuffer, slExpectedSize);
}
void CTStream::ReadFullChunk_t(const CChunkID &cidExpected, void *pvBuffer,
SLONG slExpectedSize) // throws char *
{
ExpectID_t( cidExpected);
ReadChunk_t( pvBuffer, slExpectedSize);
};
void* CTStream::ReadChunkAlloc_t(SLONG slSize) // throws char *
{
UBYTE *buffer;
SLONG chunkSize;
if( slSize != 0)
chunkSize = slSize;
else
chunkSize = GetSize_t(); // throws char *
buffer = (UBYTE *) AllocMemory( chunkSize);
if( buffer == NULL)
throw TRANS("ReadChunkAlloc: Unable to allocate needed amount of memory.");
Read_t((char *)buffer, chunkSize); // throws char *
return buffer;
}
void CTStream::ReadStream_t(CTStream &strmOther) // throw char *
{
// implement this !!!! @@@@
}
void CTStream::WriteID_t(const CChunkID &cidSave) // throws char *
{
Write_t( &cidSave.cid_ID[0], CID_LENGTH);
}
void CTStream::WriteSize_t(SLONG slSize) // throws char *
{
Write_t( (char *)&slSize, sizeof( SLONG));
}
void CTStream::WriteRawChunk_t(void *pvBuffer, SLONG slSize) // throws char *
{
Write_t( (char *)pvBuffer, slSize);
}
void CTStream::WriteChunk_t(void *pvBuffer, SLONG slSize) // throws char *
{
WriteSize_t( slSize);
WriteRawChunk_t( pvBuffer, slSize);
}
void CTStream::WriteFullChunk_t(const CChunkID &cidSave, void *pvBuffer,
SLONG slSize) // throws char *
{
WriteID_t( cidSave); // throws char *
WriteChunk_t( pvBuffer, slSize); // throws char *
}
void CTStream::WriteStream_t(CTStream &strmOther) // throw char *
{
// implement this !!!! @@@@
}
// whether or not the given pointer is coming from this stream (mainly used for exception handling)
BOOL CTStream::PointerInStream(void* pPointer)
{
// safe to return FALSE, we're using virtual functions anyway
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// filename dictionary operations
// enable dictionary in writable file from this point
void CTStream::DictionaryWriteBegin_t(const CTFileName &fnmImportFrom, SLONG slImportOffset)
{
ASSERT(strm_slDictionaryPos==0);
ASSERT(strm_dmDictionaryMode == DM_NONE);
strm_ntDictionary.SetAllocationParameters(100, 5, 5);
strm_ctDictionaryImported = 0;
// if importing an existing dictionary to start with
if (fnmImportFrom!="") {
// open that file
CTFileStream strmOther;
strmOther.Open_t(fnmImportFrom);
// read the dictionary in that stream
strmOther.ReadDictionary_intenal_t(slImportOffset);
// copy the dictionary here
CopyDictionary(strmOther);
// write dictionary importing data
WriteID_t("DIMP"); // dictionary import
*this<<fnmImportFrom<<slImportOffset;
// remember how many filenames were imported
strm_ctDictionaryImported = strm_afnmDictionary.Count();
}
// write dictionary position chunk id
WriteID_t("DPOS"); // dictionary position
// remember where position will be placed
strm_slDictionaryPos = GetPos_t();
// leave space for position
*this<<SLONG(0);
// start dictionary
strm_dmDictionaryMode = DM_ENABLED;
}
// write the dictionary (usually at the end of file)
void CTStream::DictionaryWriteEnd_t(void)
{
ASSERT(strm_dmDictionaryMode == DM_ENABLED);
ASSERT(strm_slDictionaryPos>0);
// remember the dictionary position chunk position
SLONG slDictPos = strm_slDictionaryPos;
// mark that now saving dictionary
strm_slDictionaryPos = -1;
// remember where dictionary begins
SLONG slDictBegin = GetPos_t();
// start dictionary processing
strm_dmDictionaryMode = DM_PROCESSING;
WriteID_t("DICT"); // dictionary
// write number of used filenames
INDEX ctFileNames = strm_afnmDictionary.Count();
INDEX ctFileNamesNew = ctFileNames-strm_ctDictionaryImported;
*this<<ctFileNamesNew;
// for each filename
for(INDEX iFileName=strm_ctDictionaryImported; iFileName<ctFileNames; iFileName++) {
// write it to disk
*this<<strm_afnmDictionary[iFileName];
}
WriteID_t("DEND"); // dictionary end
// remember where is end of dictionary
SLONG slContinue = GetPos_t();
// write the position back to dictionary position chunk
SetPos_t(slDictPos);
*this<<slDictBegin;
// stop dictionary processing
strm_dmDictionaryMode = DM_NONE;
strm_ntDictionary.Clear();
strm_afnmDictionary.Clear();
// return to end of dictionary
SetPos_t(slContinue);
strm_slDictionaryPos=0;
}
// read the dictionary from given offset in file (internal function)
void CTStream::ReadDictionary_intenal_t(SLONG slOffset)
{
// remember where to continue loading
SLONG slContinue = GetPos_t();
// go to dictionary beginning
SetPos_t(slOffset);
// start dictionary processing
strm_dmDictionaryMode = DM_PROCESSING;
ExpectID_t("DICT"); // dictionary
// read number of new filenames
INDEX ctFileNamesOld = strm_afnmDictionary.Count();
INDEX ctFileNamesNew;
*this>>ctFileNamesNew;
// if there are any new filenames
if (ctFileNamesNew>0) {
// create that much space
strm_afnmDictionary.Push(ctFileNamesNew);
// for each filename
for(INDEX iFileName=ctFileNamesOld; iFileName<ctFileNamesOld+ctFileNamesNew; iFileName++) {
// read it
*this>>strm_afnmDictionary[iFileName];
}
}
ExpectID_t("DEND"); // dictionary end
// remember where end of dictionary is
strm_slDictionaryPos = GetPos_t();
// return to continuing position
SetPos_t(slContinue);
}
// copy filename dictionary from another stream
void CTStream::CopyDictionary(CTStream &strmOther)
{
strm_afnmDictionary = strmOther.strm_afnmDictionary;
for (INDEX i=0; i<strm_afnmDictionary.Count(); i++) {
strm_ntDictionary.Add(&strm_afnmDictionary[i]);
}
}
SLONG CTStream::DictionaryReadBegin_t(void)
{
ASSERT(strm_dmDictionaryMode == DM_NONE);
ASSERT(strm_slDictionaryPos==0);
strm_ntDictionary.SetAllocationParameters(100, 5, 5);
SLONG slImportOffset = 0;
// if there is imported dictionary
if (PeekID_t()==CChunkID("DIMP")) { // dictionary import
// read dictionary importing data
ExpectID_t("DIMP"); // dictionary import
CTFileName fnmImportFrom;
*this>>fnmImportFrom>>slImportOffset;
// open that file
CTFileStream strmOther;
strmOther.Open_t(fnmImportFrom);
// read the dictionary in that stream
strmOther.ReadDictionary_intenal_t(slImportOffset);
// copy the dictionary here
CopyDictionary(strmOther);
}
// if the dictionary is not here
if (PeekID_t()!=CChunkID("DPOS")) { // dictionary position
// do nothing
return 0;
}
// read dictionary position
ExpectID_t("DPOS"); // dictionary position
SLONG slDictBeg;
*this>>slDictBeg;
// read the dictionary from that offset in file
ReadDictionary_intenal_t(slDictBeg);
// stop dictionary processing - go to dictionary using
strm_dmDictionaryMode = DM_ENABLED;
// return offset of dictionary for later cross-file importing
if (slImportOffset!=0) {
return slImportOffset;
} else {
return slDictBeg;
}
}
void CTStream::DictionaryReadEnd_t(void)
{
if (strm_dmDictionaryMode == DM_ENABLED) {
ASSERT(strm_slDictionaryPos>0);
// just skip the dictionary (it was already read)
SetPos_t(strm_slDictionaryPos);
strm_slDictionaryPos=0;
strm_dmDictionaryMode = DM_NONE;
strm_ntDictionary.Clear();
// for each filename
INDEX ctFileNames = strm_afnmDictionary.Count();
for(INDEX iFileName=0; iFileName<ctFileNames; iFileName++) {
CTFileName &fnm = strm_afnmDictionary[iFileName];
// if not preloaded
if (fnm.fnm_pserPreloaded==NULL) {
// skip
continue;
}
// free preloaded instance
CTString strExt = fnm.FileExt();
if (strExt==".tex") {
_pTextureStock->Release((CTextureData*)fnm.fnm_pserPreloaded);
} else if (strExt==".mdl") {
_pModelStock->Release((CModelData*)fnm.fnm_pserPreloaded);
}
}
strm_afnmDictionary.Clear();
}
}
void CTStream::DictionaryPreload_t(void)
{
INDEX ctFileNames = strm_afnmDictionary.Count();
// for each filename
for(INDEX iFileName=0; iFileName<ctFileNames; iFileName++) {
// preload it
CTFileName &fnm = strm_afnmDictionary[iFileName];
CTString strExt = fnm.FileExt();
CallProgressHook_t(FLOAT(iFileName)/ctFileNames);
try {
if (strExt==".tex") {
fnm.fnm_pserPreloaded = _pTextureStock->Obtain_t(fnm);
} else if (strExt==".mdl") {
fnm.fnm_pserPreloaded = _pModelStock->Obtain_t(fnm);
}
} catch (char *strError) {
CPrintF( TRANS("Cannot preload %s: %s\n"), (const char *) (CTString&)fnm, strError);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// General construction/destruction
/* Default constructor. */
CTStream::CTStream(void) : strm_ntDictionary(*new CNameTable_CTFileName)
{
strm_strStreamDescription = "";
strm_slDictionaryPos = 0;
strm_dmDictionaryMode = DM_NONE;
}
/* Destructor. */
CTStream::~CTStream(void)
{
strm_ntDictionary.Clear();
strm_afnmDictionary.Clear();
delete &strm_ntDictionary;
}
/////////////////////////////////////////////////////////////////////////////
// File stream opening/closing methods
/*
* Default constructor.
*/
CTFileStream::CTFileStream(void)
{
fstrm_pFile = NULL;
// mark that file is created for writing
fstrm_bReadOnly = TRUE;
fstrm_iZipHandle = -1;
fstrm_iZipLocation = 0;
fstrm_pubZipBuffer = NULL;
}
/*
* Destructor.
*/
CTFileStream::~CTFileStream(void)
{
// close stream
if (fstrm_pFile != NULL || fstrm_iZipHandle!=-1) {
Close();
}
}
/*
* Open an existing file.
*/
// throws char *
void CTFileStream::Open_t(const CTFileName &fnFileName, CTStream::OpenMode om/*=OM_READ*/)
{
// if current thread has not enabled stream handling
if (!_bThreadCanHandleStreams) {
// error
::ThrowF_t(TRANS("Cannot open file `%s', stream handling is not enabled for this thread"),
(const char *) (CTString&)fnFileName);
}
// check parameters
ASSERT(strlen(fnFileName)>0);
// check that the file is not open
ASSERT(fstrm_pFile==NULL && fstrm_iZipHandle==-1);
// expand the filename to full path
CTFileName fnmFullFileName;
INDEX iFile = ExpandFilePath((om == OM_READ)?EFP_READ:EFP_WRITE, fnFileName, fnmFullFileName);
// if read only mode requested
if( om == OM_READ) {
// initially, no physical file
fstrm_pFile = NULL;
// if zip file
if( iFile==EFP_MODZIP || iFile==EFP_BASEZIP) {
// open from zip
fstrm_iZipHandle = UNZIPOpen_t(fnmFullFileName);
fstrm_slZipSize = UNZIPGetSize(fstrm_iZipHandle);
// load the file from the zip in the buffer
fstrm_pubZipBuffer = new UBYTE[fstrm_slZipSize];
UNZIPReadBlock_t(fstrm_iZipHandle, fstrm_pubZipBuffer, 0, fstrm_slZipSize);
// if it is a physical file
} else if (iFile==EFP_FILE) {
// open file in read only mode
fstrm_pFile = fopen(fnmFullFileName, "rb");
}
fstrm_bReadOnly = TRUE;
// if write mode requested
} else if( om == OM_WRITE) {
// open file for reading and writing
fstrm_pFile = fopen(fnmFullFileName, "rb+");
fstrm_bReadOnly = FALSE;
// if unknown mode
} else {
FatalError(TRANS("File stream opening requested with unknown open mode: %d\n"), om);
}
// if openning operation was not successfull
if(fstrm_pFile == NULL && fstrm_iZipHandle==-1) {
// throw exception
Throw_t(TRANS("Cannot open file `%s' (%s)"), (const char *) (CTString&)fnmFullFileName,
strerror(errno));
}
// if file opening was successfull, set stream description to file name
strm_strStreamDescription = fnmFullFileName;
// add this newly opened file into opened stream list
_plhOpenedStreams->AddTail( strm_lnListNode);
}
static void MakeSureDirectoryPathExists(const CTFileName &fnmFullFileName)
{
STUBBED("!!! FIXME: get the code back in from Ryan's original port.");
}
/*
* Create a new file or overwrite existing.
*/
void CTFileStream::Create_t(const CTFileName &fnFileName,
enum CTStream::CreateMode cm) // throws char *
{
(void)cm; // OBSOLETE!
CTFileName fnFileNameAbsolute = fnFileName;
fnFileNameAbsolute.SetAbsolutePath();
// if current thread has not enabled stream handling
if (!_bThreadCanHandleStreams) {
// error
::ThrowF_t(TRANS("Cannot create file `%s', stream handling is not enabled for this thread"),
(const char *) (CTString&)fnFileNameAbsolute);
}
CTFileName fnmFullFileName;
INDEX iFile = ExpandFilePath(EFP_WRITE, fnFileNameAbsolute, fnmFullFileName);
// check parameters
ASSERT(strlen(fnFileNameAbsolute)>0);
// check that the file is not open
ASSERT(fstrm_pFile == NULL);
// create the directory for the new file if it doesn't exist yet
MakeSureDirectoryPathExists(fnmFullFileName);
// open file stream for writing (destroy file context if file existed before)
fstrm_pFile = fopen(fnmFullFileName, "wb+");
// if not successfull
if(fstrm_pFile == NULL)
{
// throw exception
Throw_t(TRANS("Cannot create file `%s' (%s)"), (const char *) (CTString&)fnmFullFileName,
strerror(errno));
}
// if file creation was successfull, set stream description to file name
strm_strStreamDescription = fnFileNameAbsolute;
// mark that file is created for writing
fstrm_bReadOnly = FALSE;
// add this newly created file into opened stream list
_plhOpenedStreams->AddTail( strm_lnListNode);
}
/*
* Close an open file.
*/
void CTFileStream::Close(void)
{
// if file is not open
if (fstrm_pFile==NULL && fstrm_iZipHandle==-1) {
ASSERT(FALSE);
return;
}
// clear stream description
strm_strStreamDescription = "";
// remove file from list of curently opened streams
strm_lnListNode.Remove();
// if file on disk
if (fstrm_pFile != NULL) {
// close file
fclose( fstrm_pFile);
fstrm_pFile = NULL;
// if file in zip
} else if (fstrm_iZipHandle>=0) {
// close zip entry
UNZIPClose(fstrm_iZipHandle);
fstrm_iZipHandle = -1;
delete[] fstrm_pubZipBuffer;
_ulVirtuallyAllocatedSpace -= fstrm_slZipSize;
//CPrintF("Freed virtual memory with size ^c00ff00%d KB^C (now %d KB)\n", (fstrm_slZipSize / 1000), (_ulVirtuallyAllocatedSpace / 1000));
}
// clear dictionary vars
strm_dmDictionaryMode = DM_NONE;
strm_ntDictionary.Clear();
strm_afnmDictionary.Clear();
strm_slDictionaryPos=0;
}
/* Get CRC32 of stream */
ULONG CTFileStream::GetStreamCRC32_t(void)
{
// if file on disk
if (fstrm_pFile != NULL) {
// use base class implementation (really calculates the CRC)
return CTStream::GetStreamCRC32_t();
// if file in zip
} else if (fstrm_iZipHandle >=0) {
return UNZIPGetCRC(fstrm_iZipHandle);
} else {
ASSERT(FALSE);
return 0;
}
}
/* Read a block of data from stream. */
void CTFileStream::Read_t(void *pvBuffer, SLONG slSize)
{
if(fstrm_iZipHandle != -1) {
memcpy(pvBuffer, fstrm_pubZipBuffer + fstrm_iZipLocation, slSize);
fstrm_iZipLocation += slSize;
return;
}
fread(pvBuffer, slSize, 1, fstrm_pFile);
}
/* Write a block of data to stream. */
void CTFileStream::Write_t(const void *pvBuffer, SLONG slSize)
{
if(fstrm_bReadOnly || fstrm_iZipHandle != -1) {
throw "Stream is read-only!";
}
fwrite(pvBuffer, slSize, 1, fstrm_pFile);
}
/* Seek in stream. */
void CTFileStream::Seek_t(SLONG slOffset, enum SeekDir sd)
{
if(fstrm_iZipHandle != -1) {
switch(sd) {
case SD_BEG: fstrm_iZipLocation = slOffset; break;
case SD_CUR: fstrm_iZipLocation += slOffset; break;
case SD_END: fstrm_iZipLocation = GetSize_t() + slOffset; break;
}
} else {
fseek(fstrm_pFile, slOffset, sd);
}
}
/* Set absolute position in stream. */
void CTFileStream::SetPos_t(SLONG slPosition)
{
Seek_t(slPosition, SD_BEG);
}
/* Get absolute position in stream. */
SLONG CTFileStream::GetPos_t(void)
{
if(fstrm_iZipHandle != -1) {
return fstrm_iZipLocation;
} else {
return ftell(fstrm_pFile);
}
}
/* Get size of stream */
SLONG CTFileStream::GetStreamSize(void)
{
if(fstrm_iZipHandle != -1) {
return UNZIPGetSize(fstrm_iZipHandle);
} else {
long lCurrentPos = ftell(fstrm_pFile);
fseek(fstrm_pFile, 0, SD_END);
long lRet = ftell(fstrm_pFile);
fseek(fstrm_pFile, lCurrentPos, SD_BEG);
return lRet;
}
}
/* Check if file position points to the EOF */
BOOL CTFileStream::AtEOF(void)
{
if(fstrm_iZipHandle != -1) {
return fstrm_iZipLocation >= fstrm_slZipSize;
} else {
int eof = feof(fstrm_pFile);
return eof != 0;
}
}
// whether or not the given pointer is coming from this stream (mainly used for exception handling)
BOOL CTFileStream::PointerInStream(void* pPointer)
{
// we're not using virtual allocation buffers so it's fine to return FALSE here.
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// Memory stream construction/destruction
/*
* Create dynamically resizing stream for reading/writing.
*/
CTMemoryStream::CTMemoryStream(void)
{
// if current thread has not enabled stream handling
if (!_bThreadCanHandleStreams) {
// error
::FatalError(TRANS("Can create memory stream, stream handling is not enabled for this thread"));
}
mstrm_ctLocked = 0;
mstrm_bReadable = TRUE;
mstrm_bWriteable = TRUE;
mstrm_slLocation = 0;
// set stream description
strm_strStreamDescription = "dynamic memory stream";
// add this newly created memory stream into opened stream list
_plhOpenedStreams->AddTail( strm_lnListNode);
// allocate amount of memory needed to hold maximum allowed file length (when saving)
mstrm_pubBuffer = new UBYTE[_ulMaxLenghtOfSavingFile];
mstrm_pubBufferEnd = mstrm_pubBuffer + _ulMaxLenghtOfSavingFile;
mstrm_pubBufferMax = mstrm_pubBuffer;
}
/*
* Create static stream from given buffer.
*/
CTMemoryStream::CTMemoryStream(void *pvBuffer, SLONG slSize,
CTStream::OpenMode om /*= CTStream::OM_READ*/)
{
// if current thread has not enabled stream handling
if (!_bThreadCanHandleStreams) {
// error
::FatalError(TRANS("Can create memory stream, stream handling is not enabled for this thread"));
}
// allocate amount of memory needed to hold maximum allowed file length (when saving)
mstrm_pubBuffer = new UBYTE[_ulMaxLenghtOfSavingFile];
mstrm_pubBufferEnd = mstrm_pubBuffer + _ulMaxLenghtOfSavingFile;
mstrm_pubBufferMax = mstrm_pubBuffer + slSize;
// copy given block of memory into memory file
memcpy( mstrm_pubBuffer, pvBuffer, slSize);
mstrm_ctLocked = 0;
mstrm_bReadable = TRUE;
mstrm_slLocation = 0;
// if stram is opened in read only mode
if( om == OM_READ)
{
mstrm_bWriteable = FALSE;
}
// otherwise, write is enabled
else
{
mstrm_bWriteable = TRUE;
}
// set stream description
strm_strStreamDescription = "dynamic memory stream";
// add this newly created memory stream into opened stream list
_plhOpenedStreams->AddTail( strm_lnListNode);
}
/* Destructor. */
CTMemoryStream::~CTMemoryStream(void)
{
ASSERT(mstrm_ctLocked==0);
delete[] mstrm_pubBuffer;
// remove memory stream from list of curently opened streams
strm_lnListNode.Remove();
}
/////////////////////////////////////////////////////////////////////////////
// Memory stream buffer operations
/*
* Lock the buffer contents and it's size.
*/
void CTMemoryStream::LockBuffer(void **ppvBuffer, SLONG *pslSize)
{
mstrm_ctLocked++;
ASSERT(mstrm_ctLocked>0);
*ppvBuffer = mstrm_pubBuffer;
*pslSize = GetSize_t();
}
/*
* Unlock buffer.
*/
void CTMemoryStream::UnlockBuffer()
{
mstrm_ctLocked--;
ASSERT(mstrm_ctLocked>=0);
}
/////////////////////////////////////////////////////////////////////////////
// Memory stream overrides from CTStream
BOOL CTMemoryStream::IsReadable(void)
{
return mstrm_bReadable && (mstrm_ctLocked==0);
}
BOOL CTMemoryStream::IsWriteable(void)
{
return mstrm_bWriteable && (mstrm_ctLocked==0);
}
BOOL CTMemoryStream::IsSeekable(void)
{
return TRUE;
}
/* Read a block of data from stream. */
void CTMemoryStream::Read_t(void *pvBuffer, SLONG slSize)
{
memcpy(pvBuffer, mstrm_pubBuffer + mstrm_slLocation, slSize);
mstrm_slLocation += slSize;
}
/* Write a block of data to stream. */
void CTMemoryStream::Write_t(const void *pvBuffer, SLONG slSize)
{
memcpy(mstrm_pubBuffer + mstrm_slLocation, pvBuffer, slSize);
mstrm_slLocation += slSize;
if(mstrm_pubBuffer + mstrm_slLocation > mstrm_pubBufferMax) {
mstrm_pubBufferMax = mstrm_pubBuffer + mstrm_slLocation;
}
}
/* Seek in stream. */
void CTMemoryStream::Seek_t(SLONG slOffset, enum SeekDir sd)
{
switch(sd) {
case SD_BEG: mstrm_slLocation = slOffset; break;
case SD_CUR: mstrm_slLocation += slOffset; break;
case SD_END: mstrm_slLocation = GetStreamSize() + slOffset; break;
}
}
/* Set absolute position in stream. */
void CTMemoryStream::SetPos_t(SLONG slPosition)
{
mstrm_slLocation = slPosition;
}
/* Get absolute position in stream. */
SLONG CTMemoryStream::GetPos_t(void)
{
return mstrm_slLocation;
}
/* Get size of stream. */
SLONG CTMemoryStream::GetSize_t(void)
{
return GetStreamSize();
}
/* Get size of stream */
SLONG CTMemoryStream::GetStreamSize(void)
{
return mstrm_pubBufferMax - mstrm_pubBuffer;
}
/* Get CRC32 of stream */
ULONG CTMemoryStream::GetStreamCRC32_t(void)
{
return CTStream::GetStreamCRC32_t();
}
/* Check if file position points to the EOF */
BOOL CTMemoryStream::AtEOF(void)
{
return mstrm_slLocation >= GetStreamSize();
}
// whether or not the given pointer is coming from this stream (mainly used for exception handling)
BOOL CTMemoryStream::PointerInStream(void* pPointer)
{
return pPointer >= mstrm_pubBuffer && pPointer < mstrm_pubBufferEnd;
}
// Test if a file exists.
BOOL FileExists(const CTFileName &fnmFile)
{
// if no file
if (fnmFile=="") {
// it doesn't exist
return FALSE;
}
// try to
try {
// open the file for reading
CTFileStream strmFile;
strmFile.Open_t(fnmFile);
// if successful, it means that it exists,
return TRUE;
// if failed, it means that it doesn't exist
} catch (char *strError) {
(void) strError;
return FALSE;
}
}
// Test if a file exists for writing.
// (this is can be diferent than normal FileExists() if a mod uses basewriteexclude.lst
BOOL FileExistsForWriting(const CTFileName &fnmFile)
{
// if no file
if (fnmFile=="") {
// it doesn't exist
return FALSE;
}
// expand the filename to full path for writing
CTFileName fnmFullFileName;
INDEX iFile = ExpandFilePath(EFP_WRITE, fnmFile, fnmFullFileName);
// check if it exists
FILE *f = fopen(fnmFullFileName, "rb");
if (f!=NULL) {
fclose(f);
return TRUE;
} else {
return FALSE;
}
}
// Get file timestamp
SLONG GetFileTimeStamp_t(const CTFileName &fnm)
{
// expand the filename to full path
CTFileName fnmExpanded;
INDEX iFile = ExpandFilePath(EFP_READ, fnm, fnmExpanded);
if (iFile!=EFP_FILE) {
return FALSE;
}
int file_handle;
// try to open file for reading
file_handle = _open( fnmExpanded, _O_RDONLY | _O_BINARY);
if(file_handle==-1) {
ThrowF_t(TRANS("Cannot open file '%s' for reading"), (const char *) CTString(fnm));
return -1;
}
struct stat statFileStatus;
// get file status
fstat( file_handle, &statFileStatus);
_close( file_handle);
ASSERT(statFileStatus.st_mtime<=time(NULL));
return statFileStatus.st_mtime;
}
// Get CRC32 of a file
ULONG GetFileCRC32_t(const CTFileName &fnmFile) // throw char *
{
// open the file
CTFileStream fstrm;
fstrm.Open_t(fnmFile);
// return the checksum
return fstrm.GetStreamCRC32_t();
}
// Test if a file is read only (also returns FALSE if file does not exist)
BOOL IsFileReadOnly(const CTFileName &fnm)
{
// expand the filename to full path
CTFileName fnmExpanded;
INDEX iFile = ExpandFilePath(EFP_READ, fnm, fnmExpanded);
if (iFile!=EFP_FILE) {
return FALSE;
}
int file_handle;
// try to open file for reading
file_handle = _open( fnmExpanded, _O_RDONLY | _O_BINARY);
if(file_handle==-1) {
return FALSE;
}
struct stat statFileStatus;
// get file status
fstat( file_handle, &statFileStatus);
_close( file_handle);
ASSERT(statFileStatus.st_mtime<=time(NULL));
return !(statFileStatus.st_mode&_S_IWRITE);
}
// Delete a file
BOOL RemoveFile(const CTFileName &fnmFile)
{
// expand the filename to full path
CTFileName fnmExpanded;
INDEX iFile = ExpandFilePath(EFP_WRITE, fnmFile, fnmExpanded);
if (iFile==EFP_FILE) {
int ires = remove(fnmExpanded);
return ires==0;
} else {
return FALSE;
}
}
static BOOL IsFileReadable_internal(CTFileName &fnmFullFileName)
{
FILE *pFile = fopen(fnmFullFileName, "rb");
if (pFile!=NULL) {
fclose(pFile);
return TRUE;
} else {
return FALSE;
}
}
// check for some file extensions that can be substituted
static BOOL SubstExt_internal(CTFileName &fnmFullFileName)
{
if (fnmFullFileName.FileExt()==".mp3") {
fnmFullFileName = fnmFullFileName.NoExt()+".ogg";
return TRUE;
} else if (fnmFullFileName.FileExt()==".ogg") {
fnmFullFileName = fnmFullFileName.NoExt()+".mp3";
return TRUE;
} else {
return TRUE;
}
}
static INDEX ExpandFilePath_read(ULONG ulType, const CTFileName &fnmFile, CTFileName &fnmExpanded)
{
// search for the file in zips
INDEX iFileInZip = UNZIPGetFileIndex(fnmFile);
//const BOOL userdir_not_basedir = (_fnmUserDir != _fnmApplicationPath);
// if a mod is active
if (_fnmMod!="") {
// first try in the mod's dir
if (!fil_bPreferZips) {
fnmExpanded = _fnmApplicationPath+_fnmMod+fnmFile;
if (IsFileReadable_internal(fnmExpanded)) {
return EFP_FILE;
}
}
// if not disallowing zips
if (!(ulType&EFP_NOZIPS)) {
// if exists in mod's zip
if (iFileInZip>=0 && UNZIPIsFileAtIndexMod(iFileInZip)) {
// use that one
fnmExpanded = fnmFile;
return EFP_MODZIP;
}
}
// try in the mod's dir after
if (fil_bPreferZips) {
fnmExpanded = _fnmApplicationPath+_fnmMod+fnmFile;
if (IsFileReadable_internal(fnmExpanded)) {
return EFP_FILE;
}
}
}
// try in the app's base dir
if (!fil_bPreferZips) {
CTFileName fnmAppPath = _fnmApplicationPath;
fnmAppPath.SetAbsolutePath();
if(fnmFile.HasPrefix(fnmAppPath)) {
fnmExpanded = fnmFile;
} else {
fnmExpanded = _fnmApplicationPath+fnmFile;
}
if (IsFileReadable_internal(fnmExpanded)) {
return EFP_FILE;
}
}
// if not disallowing zips
if (!(ulType&EFP_NOZIPS)) {
// if exists in any zip
if (iFileInZip>=0) {
// use that one
fnmExpanded = fnmFile;
return EFP_BASEZIP;
}
}
// try in the app's base dir
if (fil_bPreferZips) {
fnmExpanded = _fnmApplicationPath+fnmFile;
if (IsFileReadable_internal(fnmExpanded)) {
return EFP_FILE;
}
}
// finally, try in the CD path
if (_fnmCDPath!="") {
// if a mod is active
if (_fnmMod!="") {
// first try in the mod's dir
fnmExpanded = _fnmCDPath+_fnmMod+fnmFile;
if (IsFileReadable_internal(fnmExpanded)) {
return EFP_FILE;
}
}
fnmExpanded = _fnmCDPath+fnmFile;
if (IsFileReadable_internal(fnmExpanded)) {
return EFP_FILE;
}
}
return EFP_NONE;
}
// rcg01042002 User dir and children may need to be created on the fly...
static void VerifyDirsExist(const char *_path)
{
char *path = (char *) AllocMemory(strlen(_path) + 1);
strcpy(path, _path);
const char *dirsep = CFileSystem::GetDirSeparator();
// skip first dirsep. This assumes an absolute path and some other
// fundamentals of how a filepath is specified.
char *ptr = strstr(path, dirsep);
ASSERT(ptr != NULL);
if (ptr == NULL)
return;
for (ptr = strstr(ptr+1, dirsep); ptr != NULL; ptr = strstr(ptr+1, dirsep)) {
char ch = *ptr;
*ptr = '\0'; // terminate the path.
if (!_pFileSystem->IsDirectory(path)) {
if (_pFileSystem->Exists(path)) {
CPrintF("Expected %s to be a directory, but it's a file!\n", path);
break;
} else {
CPrintF("Creating directory %s ...\n", path);
_mkdir(path);
if (!_pFileSystem->IsDirectory(path)) {
CPrintF("Creation of directory %s FAILED!\n", path);
break;
}
}
}
*ptr = ch; // put path char back...
}
FreeMemory(path);
}
// Expand a file's filename to full path
INDEX ExpandFilePath(ULONG ulType, const CTFileName &fnmFile, CTFileName &fnmExpanded)
{
CTFileName fnmFileAbsolute = fnmFile;
fnmFileAbsolute.SetAbsolutePath();
// if writing
if (ulType&EFP_WRITE) {
// if should write to mod dir
if (_fnmMod!="" && (!FileMatchesList(_afnmBaseWriteInc, fnmFileAbsolute) || FileMatchesList(_afnmBaseWriteExc, fnmFileAbsolute))) {
// do that
fnmExpanded = _fnmApplicationPath+_fnmMod+fnmFileAbsolute;
fnmExpanded.SetAbsolutePath();
VerifyDirsExist(fnmExpanded.FileDir());
return EFP_FILE;
// if should not write to mod dir
} else {
// write to base dir
fnmExpanded = _fnmApplicationPath+fnmFileAbsolute;
fnmExpanded.SetAbsolutePath();
VerifyDirsExist(fnmExpanded.FileDir());
return EFP_FILE;
}
// if reading
} else if (ulType&EFP_READ) {
// check for expansions for reading
INDEX iRes = ExpandFilePath_read(ulType, fnmFileAbsolute, fnmExpanded);
// if not found
if (iRes==EFP_NONE) {
//check for some file extensions that can be substituted
CTFileName fnmSubst = fnmFileAbsolute;
if (SubstExt_internal(fnmSubst)) {
iRes = ExpandFilePath_read(ulType, fnmSubst, fnmExpanded);
}
}
fnmExpanded.SetAbsolutePath();
if (iRes!=EFP_NONE) {
return iRes;
}
// in other cases
} else {
ASSERT(FALSE);
fnmExpanded = _fnmApplicationPath+fnmFileAbsolute;
fnmExpanded.SetAbsolutePath();
return EFP_FILE;
}
fnmExpanded = _fnmApplicationPath+fnmFileAbsolute;
fnmExpanded.SetAbsolutePath();
return EFP_NONE;
}