/* 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 "stdh.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // default size of page used for stream IO operations (4Kb) ULONG _ulPageSize = 0; // maximum lenght of file that can be saved (default: 128Mb) ULONG _ulMaxLenghtOfSavingFile = (1UL<<20)*128; extern INDEX fil_bPreferZips = FALSE; // set if current thread has currently enabled stream handling static _declspec(thread) BOOL _bThreadCanHandleStreams = FALSE; // list of currently opened streams static _declspec(thread) CListHead *_plhOpenedStreams = NULL; ULONG _ulVirtuallyAllocatedSpace = 0; ULONG _ulVirtuallyAllocatedSpaceTotal = 0; // global string with application path CTFileName _fnmApplicationPath; // global string with filename of the started application CTFileName _fnmApplicationExe; // 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 _afnmBaseWriteInc; CDynamicStackArray _afnmBaseWriteExc; CDynamicStackArray _afnmBaseBrowseInc; CDynamicStackArray _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 _afnmNoCRC; // load a filelist static BOOL LoadFileList(CDynamicStackArray &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 &afnm, const CTFileName &fnm) { for(INDEX i=0; i"):(CTString&)_fnmMod); // if there is a mod active if (_fnmMod!="") { // load mod's include/exclude lists CPrintF(TRANS("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(TRANS("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(TRANS("Loading group files...\n")); // for each group file in base directory struct _finddata_t c_file; long hFile; hFile = _findfirst(_fnmApplicationPath+"*.gro", &c_file); BOOL bOK = (hFile!=-1); while(bOK) { if (CTString(c_file.name).Matches("*.gro")) { // add it to active set UNZIPAddArchive(_fnmApplicationPath+c_file.name); } bOK = _findnext(hFile, &c_file)==0; } _findclose( hFile ); // if there is a mod active if (_fnmMod!="") { // for each group file in mod directory struct _finddata_t c_file; long hFile; hFile = _findfirst(_fnmApplicationPath+_fnmMod+"*.gro", &c_file); BOOL bOK = (hFile!=-1); while(bOK) { if (CTString(c_file.name).Matches("*.gro")) { // add it to active set UNZIPAddArchive(_fnmApplicationPath+_fnmMod+c_file.name); } bOK = _findnext(hFile, &c_file)==0; } _findclose( hFile ); } // if there is a CD path if (_fnmCDPath!="") { // for each group file on the CD struct _finddata_t c_file; long hFile; hFile = _findfirst(_fnmCDPath+"*.gro", &c_file); BOOL bOK = (hFile!=-1); while(bOK) { if (CTString(c_file.name).Matches("*.gro")) { // add it to active set UNZIPAddArchive(_fnmCDPath+c_file.name); } bOK = _findnext(hFile, &c_file)==0; } _findclose( hFile ); // if there is a mod active if (_fnmMod!="") { // for each group file in mod directory struct _finddata_t c_file; long hFile; hFile = _findfirst(_fnmCDPath+_fnmMod+"*.gro", &c_file); BOOL bOK = (hFile!=-1); while(bOK) { if (CTString(c_file.name).Matches("*.gro")) { // add it to active set UNZIPAddArchive(_fnmCDPath+_fnmMod+c_file.name); } bOK = _findnext(hFile, &c_file)==0; } _findclose( hFile ); } } // 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"); LoadFileList(_afnmNoCRC, CTFILENAME("Data\\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; } int CTStream::ExceptionFilter(DWORD dwCode, _EXCEPTION_POINTERS *pExceptionInfoPtrs) { // If the exception is not a page fault, exit. if( dwCode != EXCEPTION_ACCESS_VIOLATION) { return EXCEPTION_CONTINUE_SEARCH; } // obtain access violation virtual address UBYTE *pIllegalAdress = (UBYTE *)pExceptionInfoPtrs->ExceptionRecord->ExceptionInformation[1]; CTStream *pstrmAccessed = NULL; // search for stream that was accessed FOREACHINLIST( CTStream, strm_lnListNode, (*_plhOpenedStreams), itStream) { // if access violation happened inside curently testing stream if(itStream.Current().PointerInStream(pIllegalAdress)) { // remember accesed stream ptr pstrmAccessed = &itStream.Current(); // stream found, stop searching break; } } // if none of our streams was accessed, real access violation occured if( pstrmAccessed == NULL) { // so continue default exception handling return EXCEPTION_CONTINUE_SEARCH; } // Continue execution where the page fault occurred return EXCEPTION_CONTINUE_EXECUTION; } /* * Static function to report fatal exception error. */ void CTStream::ExceptionFatalError(void) { FatalError( GetWindowsError( GetLastError()) ); } /* * Throw an exception of formatted string. */ void CTStream::Throw_t(char *strFormat, ...) // throws char * { const SLONG slBufferSize = 256; char strFormatBuffer[slBufferSize]; char strBuffer[slBufferSize]; // add the stream description to the format string _snprintf(strFormatBuffer, slBufferSize, "%s (%s)", strFormat, 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); 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; slPos0); // 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"), 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>chKeywordChar; if (chKeywordChar!=strKeyword[iKeywordChar]) { ThrowF_t(TRANS("Expected keyword %s not found"), 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<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; // 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>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>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; iFileNameRelease((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; iFileNameObtain_t(fnm); } else if (strExt==".mdl") { fnm.fnm_pserPreloaded = _pModelStock->Obtain_t(fnm); } } catch (char *strError) { CPrintF( TRANS("Cannot preload %s: %s\n"), (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"), (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 = (UBYTE*)VirtualAlloc(NULL, fstrm_slZipSize, MEM_COMMIT, PAGE_READWRITE); UNZIPReadBlock_t(fstrm_iZipHandle, (UBYTE*)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)"), (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); } /* * 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"), (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)"), (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; VirtualFree(fstrm_pubZipBuffer, 0, MEM_RELEASE); _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 = (UBYTE*)VirtualAlloc(NULL, _ulMaxLenghtOfSavingFile, MEM_COMMIT, PAGE_READWRITE); 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 = (UBYTE*)VirtualAlloc(NULL, _ulMaxLenghtOfSavingFile, MEM_COMMIT, PAGE_READWRITE); 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); VirtualFree(mstrm_pubBuffer, 0, MEM_RELEASE); // 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"), 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); // 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; } // 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(); return EFP_FILE; // if should not write to mod dir } else { // write to base dir fnmExpanded = _fnmApplicationPath+fnmFileAbsolute; fnmExpanded.SetAbsolutePath(); 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; }