/* 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 "Dependency.h" #include // adjust file path automatically for application path prefix void AdjustFilePath_t(CTFileName &fnm) { // if filename contains a colon or double backslash if (strchr(fnm, ':')!=NULL ||strstr(fnm, "\\\\")!=NULL) { // it must be prefixed with application path fnm.RemoveApplicationPath_t(); } } // class constructor CDependInfo::CDependInfo(CTFileName fnFileName, CTFileName fnParent) { // copy file name di_fnFileName = fnFileName; di_fnParent = fnParent; } BOOL CDependInfo::IsFileOnDiskUpdated(void) { int file_handle; // try to open file for reading file_handle = _open( _fnmApplicationPath + di_fnFileName, _O_RDONLY | _O_BINARY); // mark as it is not updated BOOL bUpdated = FALSE; // if opened succesefully if( file_handle != -1) { struct stat statFileStatus; // get file status fstat( file_handle, &statFileStatus); ASSERT(statFileStatus.st_mtime<=time(NULL)); // if last modification time is same as remembered in dependency info object if( difftime( statFileStatus.st_mtime, di_tTime) >= 0) { // mark file as updated bUpdated = TRUE; } } if(file_handle!=-1) { _close(file_handle); } return bUpdated; } // if given file allready has its own DependInfo object linked in list BOOL CDependencyList::ExistsInList(CListHead &lh, CTFileName fnTestName) const { // for all members in depend list FOREACHINLIST( CDependInfo, di_Node, lh, itDependInfo) { // if this DependInfo is describing searching file if( itDependInfo->di_fnFileName == fnTestName) { // return true return TRUE; } } // if none of linked DepenInfo objects represent our testing file return FALSE; } // extract all dependencies from list void CDependencyList::ExtractDependencies() { int file_handle; volatile ULONG ulDFNM = 'NFD'; volatile ULONG ulTFNM = 'NFT'; volatile ULONG ulEFNM = 'NFE'; char strDFNM[]="DFNM"; char strTFNM[]="TFNM"; char strEFNM[]="EFNM"; ulDFNM |= ULONG('M')<<24; ulTFNM |= ULONG('M')<<24; ulEFNM |= ULONG('M')<<24; // for all list members CListHead lhToProcess; lhToProcess.MoveList(dl_ListHead); while(!lhToProcess.IsEmpty()) { CDependInfo *pdi = LIST_HEAD(lhToProcess, CDependInfo, di_Node); pdi->di_Node.Remove(); dl_ListHead.AddTail(pdi->di_Node); CTFileName fnFileName = pdi->di_fnFileName; // try to open file for reading file_handle = _open( _fnmApplicationPath + fnFileName, _O_RDONLY | _O_BINARY); // if an error occured if( file_handle == -1) { // if file is not available remove it from list //FatalError( "File %s can't be opened!", (CTString&)(_fnmApplicationPath + fnFileName)); printf( "warning, cannot open: %s (referenced from %s)\n", (CTString&)(fnFileName), (CTString&)(pdi->di_fnParent)); delete pdi; } // if file is opened properly else { struct stat statFileStatus; // get file status fstat( file_handle, &statFileStatus); pdi->di_tTime = statFileStatus.st_mtime; ASSERT(pdi->di_tTime<=time(NULL)); // get size SLONG ulSize = statFileStatus.st_size; // read file into memory char *pFileInMemory = (char *) AllocMemory( ulSize); // must be allocated properly ASSERT( pFileInMemory != NULL); // read file if( _read( file_handle, pFileInMemory, ulSize) != ulSize) { FatalError( "Fatal error ocured while reading file: %s.", (CTString&)pdi->di_fnFileName); } if(file_handle!=-1) { _close(file_handle); } // find all file name indentifiers in memory file ("EFNM" or "DFNM") for( INDEX charCt=0; charCt 254) { continue; } // if found file name does not yet exists in dependacy list if( (fnTestName!="") && (!ExistsInList(dl_ListHead, fnTestName)) && (!ExistsInList(lhToProcess, fnTestName))) { // create new depend info object CDependInfo *pDI = new CDependInfo( fnTestName, fnFileName); // add it at tail of dependency list lhToProcess.AddTail( pDI->di_Node); } } // if we found file name inside data file (notice little-big indian convetion) else if( ulMemValue == ulDFNM) { char chrFileName[ 256]; INDEX iStringLenght = *(INDEX *)(pFileInMemory + charCt + 4); if( iStringLenght > 254 || iStringLenght<0) { continue; } memcpy( chrFileName, pFileInMemory + charCt + 8, iStringLenght); chrFileName[ iStringLenght] = 0; // create full file name CTFileName fnTestName = CTString(chrFileName); if( fnTestName!="" && (!ExistsInList(dl_ListHead, fnTestName)) && (!ExistsInList(lhToProcess, fnTestName))) { // create new depend info object // NOTICE: string containing file name starts after ULONG determing its lenght (+8!) CDependInfo *pDI = new CDependInfo( fnTestName, fnFileName); // add it at tail of dependency list lhToProcess.AddTail( pDI->di_Node); } } // if we found file name inside text file (notice little-big indian convetion) else if( ulMemValue == ulTFNM) { // after describing long and one space we will find file name pchrDependentFile = pFileInMemory + charCt + 4 + 1; // copy file name from the file, until newline char chrFileName[ 256]; char *chrSrc = pchrDependentFile; char *chrDst = chrFileName; while(*chrSrc!='\n' && *chrSrc!='\r' && chrSrc-pchrDependentFile<254) { *chrDst++ = *chrSrc++; } *chrDst = 0; CTFileName fnTestName = CTString(chrFileName); if( strlen(fnTestName) > 254) { continue; } // if found file name does not yet exists in dependacy list if( (fnTestName!="") && (!ExistsInList(dl_ListHead, fnTestName)) && (!ExistsInList(lhToProcess, fnTestName))) { // create new depend info object CDependInfo *pDI = new CDependInfo( fnTestName, fnFileName); // add it at tail of dependency list lhToProcess.AddTail( pDI->di_Node); } } } // free file from memory FreeMemory(pFileInMemory); } } } // substracts given dependecy list from this void CDependencyList::Substract( CDependencyList &dlToSubstract) { // for all files in this dependency FORDELETELIST( CDependInfo, di_Node, dl_ListHead, itThis) { // see if file exists in other dependency FOREACHINLIST( CDependInfo, di_Node, dlToSubstract.dl_ListHead, itOther) { // if we found same file in other dependency list if( itThis->di_fnFileName == itOther->di_fnFileName) { // if file is updated if( itOther->IsUpdated( *itThis)) { // remove it from list delete &itThis.Current(); break; } // if file to substract is newer than this one else if( itThis->IsOlder( *itOther)) { FatalError( "File \"%s\" is newer in substracting dependency file.", (CTString&)itThis->di_fnFileName); } } } } } // create list from ascii file void CDependencyList::ImportASCII( CTFileName fnAsciiFile) { char chrOneLine[ 256]; CTFileStream file; int file_handle; // try to try { // open file list for reading file.Open_t( fnAsciiFile); } catch( char *pError) { FatalError( "Error opening file %s. Error: %s.", (CTString&)fnAsciiFile, pError); } // loop loading lines until EOF reached ("catched") FOREVER { try { // load one line from file file.GetLine_t( chrOneLine, 256); // create file name from loaded line CTFileName fnFileName = CTString(chrOneLine); AdjustFilePath_t(fnFileName); // try to open file for reading file_handle = _open( _fnmApplicationPath+fnFileName, _O_RDONLY | _O_BINARY); // if opened succesefully if( file_handle != -1) { // create new depend info object CDependInfo *pDI = new CDependInfo( fnFileName, fnAsciiFile); struct stat statFileStatus; // get file status fstat( file_handle, &statFileStatus); // obtain last modification time pDI->di_tTime = statFileStatus.st_mtime; ASSERT(pDI->di_tTime<=time(NULL)); // add it at tail of dependency list dl_ListHead.AddTail( pDI->di_Node); // close file _close( file_handle); } else { CPrintF("cannot open file '%s'\n", chrOneLine); } } // error, EOF catched catch( char *pFinished) { if (!file.AtEOF()) { CPrintF("%s\n", pFinished); } break; } } } // remove updated files from list void CDependencyList::RemoveUpdatedFiles() { // for all list members FORDELETELIST( CDependInfo, di_Node, dl_ListHead, itDependInfo) { // if file is updated if( itDependInfo->IsFileOnDiskUpdated()) { // remove it from list delete &itDependInfo.Current(); } } } // clear dependency list void CDependencyList::Clear( void) { // for all list members FORDELETELIST( CDependInfo, di_Node, dl_ListHead, itDependInfo) { // delete member delete &itDependInfo.Current(); } } // export list members into ascii file in form sutable for archivers void CDependencyList::ExportASCII_t( CTFileName fnAsciiFile) { CTFileStream strmFile; char line[ 256]; // try to try { // create exporting text file strmFile.Create_t( fnAsciiFile); } catch( char *pError) { FatalError( "Error creating file %s. Error: %s.", (CTString&)fnAsciiFile, pError); } // for all members in depend list FOREACHINLIST( CDependInfo, di_Node, dl_ListHead, itDependInfo) { // prepare line of text sprintf( line, "%s\n", (CTString&)itDependInfo->di_fnFileName); // write text line into file strmFile.Write_t( line, strlen( line)); } } // dynamic array of translation pairs CDynamicArray _atpPairs; // add one string to array static void AddStringForTranslation(const CTString &str) { // for each existing pair _atpPairs.Lock(); INDEX ct = _atpPairs.Count(); for(INDEX i=0; i 254 || iStrLen<0) { continue; } memcpy( achr, pub + 8, iStrLen); achr[ iStrLen] = 0; // get it CTString str = achr; // add it AddStringForTranslation(str); // if text translatable string is here } else if (ul=='SRTT') { // after describing long and one space we will find file name char *pchrStart = (char*)pub + 4 + 1; // copy file name from the file, until newline char chrString[ 256]; char *chrSrc = pchrStart; char *chrDst = chrString; while(*chrSrc!='\n' && *chrSrc!='\r' && chrSrc-pchrStart<254) { *chrDst++ = *chrSrc++; } *chrDst = 0; CTString str = CTString(chrString); if( strlen(str) > 254) { continue; } // add it AddStringForTranslation(str); } } // free file from memory FreeMemory(pubFile); } // count used pairs _atpPairs.Lock(); INDEX ctUsedPairs = 0; {INDEX ct = _atpPairs.Count(); for(INDEX i=0; i\n"); WriteTranslationString_t(strm, tp.tp_strDst); }} _atpPairs.Unlock(); // write eof token WriteTranslationToken_t(strm, "\n\\0\\$\n"); } // read operation void CDependencyList::Read_t( CTStream *istrFile) { // count of files INDEX ctFiles; // expect file ID istrFile->ExpectID_t( CChunkID( "DEPD")); // read number of entries in list *istrFile >> ctFiles; // load all list members for( INDEX i=0; iGetDescription()); // read dependency object pDI->Read_t( istrFile); // add it at tail of dependency list dl_ListHead.AddTail( pDI->di_Node); } } // write opertaion void CDependencyList::Write_t( CTStream *ostrFile) { // write file ID ostrFile->WriteID_t( CChunkID( "DEPD")); // write number of entries in list *ostrFile << dl_ListHead.Count(); // for all members in dependency list FOREACHINLIST( CDependInfo, di_Node, dl_ListHead, itDependInfo) { // write one depend info object itDependInfo->Write_t( ostrFile); } } // make a new directory recursively static void MakeDirectory_t(const CTFileName &fnm) { if (fnm=="") { return; } // remove trailing backslash CTFileName fnmDir = fnm; ((char *)(const char*)fnmDir)[strlen(fnmDir)-1] = 0; // get the path part CTFileName fnmDirPath = fnmDir.FileDir(); // if there is a path part if (fnmDirPath!="") { // create that first MakeDirectory_t(fnmDirPath); } // try to create the directory int iRes = _mkdir(_fnmApplicationPath+fnmDir); }