mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-11-24 03:10:27 +01:00
582 lines
17 KiB
C++
582 lines
17 KiB
C++
/* 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 <Engine/Base/TranslationPair.h>
|
|
|
|
// 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<ulSize-4; charCt++)
|
|
{
|
|
ULONG ulMemValue = *((ULONG *)(pFileInMemory + charCt));
|
|
char *pchrDependentFile;
|
|
// if we found file name inside exe file (notice little-big indian convetion)
|
|
if( ulMemValue == ulEFNM)
|
|
{
|
|
// after describing long we will find file name
|
|
pchrDependentFile = pFileInMemory + charCt + 4;
|
|
// create full file name
|
|
CTFileName fnTestName = CTString(pchrDependentFile);
|
|
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);
|
|
}
|
|
}
|
|
// 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<CTranslationPair> _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<ct; i++) {
|
|
// if it is that one
|
|
if (strcmp(_atpPairs[i].tp_strSrc, str)==0) {
|
|
// just mark it as used
|
|
_atpPairs[i].m_bUsed = TRUE;
|
|
// don't search any more
|
|
_atpPairs.Unlock();
|
|
return;
|
|
}
|
|
}
|
|
_atpPairs.Unlock();
|
|
|
|
// if not found, add it
|
|
CTranslationPair &tp = *_atpPairs.New(1);
|
|
tp.tp_strSrc = str;
|
|
tp.tp_strDst = CTString("$TODO$") + str;
|
|
tp.m_bUsed = TRUE;
|
|
}
|
|
|
|
static void WriteTranslationToken_t(CTStream &strm, CTString str)
|
|
{
|
|
strm.PutString_t(str);
|
|
}
|
|
static void WriteTranslationString_t(CTStream &strm, CTString str)
|
|
{
|
|
const char *s = str;
|
|
INDEX iLen=strlen(s);
|
|
for (INDEX i=0; i<iLen; i++) {
|
|
char c = s[i];
|
|
switch(c) {
|
|
case '\n':
|
|
strm<<UBYTE('\\')<<UBYTE('n')<<UBYTE('\n');
|
|
break;
|
|
case '\\':
|
|
strm<<UBYTE('\\')<<UBYTE('\\');
|
|
break;
|
|
default:
|
|
strm<<UBYTE(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
// extract translation strings from all files in list
|
|
void CDependencyList::ExtractTranslations_t( const CTFileName &fnTranslations)
|
|
{
|
|
_atpPairs.Clear();
|
|
|
|
// read original translation table
|
|
ReadTranslationTable_t(_atpPairs, fnTranslations);
|
|
|
|
// for each file in list
|
|
FOREACHINLIST( CDependInfo, di_Node, dl_ListHead, itDependInfo) {
|
|
CDependInfo &di = *itDependInfo;
|
|
// open the file
|
|
CTFileStream strm;
|
|
strm.Open_t(di.di_fnFileName);
|
|
// load it in memory
|
|
SLONG slSize = strm.GetStreamSize();
|
|
UBYTE *pubFile = (UBYTE *)AllocMemory(slSize);
|
|
strm.Read_t(pubFile, slSize);
|
|
|
|
// for each byte in file
|
|
for(INDEX iByte=0; iByte<slSize-4; iByte++) {
|
|
UBYTE *pub = pubFile+iByte;
|
|
ULONG ul = *((ULONG*)pub);
|
|
|
|
BYTESWAP(ul);
|
|
|
|
// if exe translatable string is here
|
|
if (ul=='SRTE') {
|
|
// get it
|
|
CTString str = (char*)(pub+4);
|
|
// add it
|
|
AddStringForTranslation(str);
|
|
// if data translatable string is here
|
|
} else if (ul=='SRTD') {
|
|
char achr[ 256];
|
|
INDEX iStrLen = *(INDEX *)(pub + 4);
|
|
BYTESWAP(iStrLen);
|
|
if( iStrLen > 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<ct; i++) {
|
|
if(_atpPairs[i].m_bUsed) {
|
|
ctUsedPairs++;
|
|
}
|
|
}}
|
|
_atpPairs.Unlock();
|
|
|
|
// create output file
|
|
CTFileStream strm;
|
|
strm.Create_t(fnTranslations);
|
|
// write number of used pairs
|
|
CTString strNo;
|
|
strNo.PrintF("%d", ctUsedPairs);
|
|
WriteTranslationString_t(strm, strNo);
|
|
WriteTranslationToken_t(strm, "\n");
|
|
// for each used pair
|
|
_atpPairs.Lock();
|
|
{INDEX ct = _atpPairs.Count();
|
|
for(INDEX i=0; i<ct; i++) {
|
|
CTranslationPair &tp = _atpPairs[i];
|
|
if(!tp.m_bUsed) {
|
|
continue;
|
|
}
|
|
// write it, as source and destination, with tags in front
|
|
WriteTranslationToken_t(strm, "\n\\0\\<\n");
|
|
WriteTranslationString_t(strm, tp.tp_strSrc);
|
|
WriteTranslationToken_t(strm, "\n\\0\\>\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; i<ctFiles; i++)
|
|
{
|
|
// create new depend info object
|
|
CDependInfo *pDI = new CDependInfo( CTString(""), istrFile->GetDescription());
|
|
// 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);
|
|
}
|
|
|