1868 lines
51 KiB
C++
1868 lines
51 KiB
C++
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek CryENGINE Source code
|
|
//
|
|
// File:CryPak.cpp
|
|
// Description: Implementation of the Crytek package files management
|
|
//
|
|
// History:
|
|
// -Jan 31,2001:Created by Honich Andrey
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
#include "CryPak.h"
|
|
#include <ilog.h>
|
|
#include <StringUtils.h>
|
|
|
|
/////////////////////////////////////////////////////
|
|
|
|
#ifndef _XBOX
|
|
#ifdef WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include "windows.h"
|
|
#endif
|
|
#else
|
|
#include <xtl.h>
|
|
#endif
|
|
|
|
#ifdef LINUX
|
|
#include <sys/dir.h>
|
|
#include <sys/io.h>
|
|
#else
|
|
# include <direct.h>
|
|
# include <io.h>
|
|
#endif
|
|
|
|
extern CMTSafeHeap* g_pSmallHeap;
|
|
extern CMTSafeHeap* g_pBigHeap;
|
|
|
|
//default values for pak vars
|
|
static const PakVars g_PakVars;
|
|
|
|
/////////////////////////////////////////////////////
|
|
// Initializes the crypak system;
|
|
// pVarPakPriority points to the variable, which is, when set to 1,
|
|
// signals that the files from pak should have higher priority than filesystem files
|
|
CCryPak::CCryPak(IMiniLog* pLog, PakVars* pPakVars):
|
|
m_pLog (pLog),
|
|
m_setCachedFiles (CCachedFileDataOrder(), CachedFileDataAllocator(g_pSmallHeap)),
|
|
m_bRememberOpenedFiles(false),
|
|
m_pPakVars (pPakVars?pPakVars:&g_PakVars),
|
|
m_mapMissingFiles ( std::less<string>(), MissingFileMapAllocator(g_pBigHeap) )
|
|
{
|
|
char szCurrentDir[0x800];
|
|
if (GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir))
|
|
{
|
|
// normalize it (lower-char with forward slashes and trailing slash)
|
|
char* p;
|
|
for (p = szCurrentDir; *p; ++p)
|
|
{
|
|
if (*p == g_cNonNativeSlash)
|
|
*p = g_cNativeSlash;
|
|
else
|
|
*p = tolower(*p);
|
|
}
|
|
// add the trailing slash if needed
|
|
#if defined(LINUX)
|
|
if (p > szCurrentDir && (p[-1] != g_cNativeSlash && p[-1] != g_cNonNativeSlash))
|
|
#else
|
|
if (p > szCurrentDir && p[-1] != g_cNativeSlash)
|
|
#endif
|
|
{
|
|
*p=g_cNativeSlash;
|
|
*++p = '\0';
|
|
}
|
|
m_strMasterCDRoot = szCurrentDir;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CCryPak::AddMod(const char* szMod)
|
|
{
|
|
// remember the prefix to use to conver the file names
|
|
string strPrepend = szMod;
|
|
for (string::iterator it = strPrepend.begin(); it != strPrepend.end(); ++it)
|
|
{
|
|
if (*it==g_cNonNativeSlash)
|
|
*it = g_cNativeSlash;
|
|
else
|
|
*it = tolower(*it);
|
|
}
|
|
#if defined(LINUX)
|
|
if (!strPrepend.empty() && (strPrepend[strPrepend.length()-1] != g_cNativeSlash && strPrepend[strPrepend.length()-1] != g_cNonNativeSlash))
|
|
#else
|
|
if (!strPrepend.empty() && strPrepend[strPrepend.length()-1] != g_cNativeSlash)
|
|
#endif
|
|
strPrepend += g_cNativeSlash;
|
|
|
|
std::vector<string>::iterator strit;
|
|
for (strit = m_arrMods.begin(); strit != m_arrMods.end(); ++strit)
|
|
{
|
|
string &sMOD = *strit;
|
|
if (stricmp(sMOD.c_str(),strPrepend.c_str())==0)
|
|
return; // already added
|
|
}
|
|
m_arrMods.push_back(strPrepend);
|
|
m_pLog->Log("Added MOD %s to crypak",strPrepend.c_str());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CCryPak::RemoveMod(const char* szMod)
|
|
{
|
|
std::vector<string>::iterator it;
|
|
for (it = m_arrMods.begin(); it != m_arrMods.end(); ++it)
|
|
{
|
|
string &sMOD = *it;
|
|
// NOTE: this check may not work with multiple mods, but
|
|
// however multiple mods are not supported...
|
|
if (strstr(sMOD.c_str(),szMod)==0)
|
|
{
|
|
m_arrMods.erase(it);
|
|
break;
|
|
}
|
|
} //it
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CCryPak::~CCryPak()
|
|
{
|
|
unsigned numFilesForcedToClose = 0;
|
|
// scan through all open files and close them
|
|
for (ZipPseudoFileArray::iterator itFile = m_arrOpenFiles.begin(); itFile != m_arrOpenFiles.end(); ++itFile)
|
|
if (itFile->GetFile())
|
|
{
|
|
itFile->Destruct();
|
|
++numFilesForcedToClose;
|
|
}
|
|
|
|
if (numFilesForcedToClose)
|
|
m_pLog->LogWarning ("\002%u files were forced to close", numFilesForcedToClose);
|
|
|
|
size_t numDatasForcedToDestruct = m_setCachedFiles.size();
|
|
for (size_t i = 0; i < numDatasForcedToDestruct; ++i)
|
|
if (m_setCachedFiles.empty())
|
|
assert (0);
|
|
else
|
|
delete *m_setCachedFiles.begin();
|
|
|
|
if (numDatasForcedToDestruct)
|
|
m_pLog->LogWarning ("\002%u cached file data blocks were forced to destruct; they still have references on them, crash possible", numDatasForcedToDestruct);
|
|
|
|
if (!m_arrArchives.empty())
|
|
m_pLog->LogError("\002 There are %d external references to archive objects: they have dangling pointers and will either lead to memory leaks or crashes", m_arrArchives.size());
|
|
|
|
if (!m_mapMissingFiles.empty())
|
|
{
|
|
FILE* f = fopen ("Missing Files Report.txt", "wt");
|
|
if (f)
|
|
{
|
|
for (MissingFileMap::iterator it = m_mapMissingFiles.begin(); it != m_mapMissingFiles.end(); ++it)
|
|
fprintf (f, "%d\t%s\n", it->second,it->first.c_str());
|
|
fclose(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// makes the path lower-case and removes the duplicate and non native slashes
|
|
// may make some other fool-proof stuff
|
|
// may NOT write beyond the string buffer (may not make it longer)
|
|
// returns: the pointer to the ending terminator \0
|
|
char* CCryPak::BeautifyPath(char* dst)
|
|
{
|
|
// make the path lower-letters and with native slashes
|
|
char*p,*q;
|
|
// there's a special case: two slashes at the beginning mean UNC filepath
|
|
p = q = dst;
|
|
if (*p == g_cNonNativeSlash || *p == g_cNativeSlash)
|
|
++p,++q; // start normalization/beautifications from the second symbol; if it's a slash, we'll add it, too
|
|
|
|
while (*p)
|
|
{
|
|
if (*p == g_cNonNativeSlash || *p == g_cNativeSlash)
|
|
{
|
|
*q = g_cNativeSlash;
|
|
++p,++q;
|
|
while(*p == g_cNonNativeSlash || *p == g_cNativeSlash)
|
|
++p; // skip the extra slashes
|
|
}
|
|
else
|
|
{
|
|
*q = tolower (*p);
|
|
++q,++p;
|
|
}
|
|
}
|
|
*q = '\0';
|
|
return q;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// given the source relative path, constructs the full path to the file according to the flags
|
|
const char* CCryPak::AdjustFileName(const char *src, char *dst, unsigned nFlags,bool *bFoundInPak)
|
|
{
|
|
// in many cases, the path will not be long, so there's no need to allocate so much..
|
|
// I'd use _alloca, but I don't like non-portable solutions. besides, it tends to confuse new developers. So I'm just using a big enough array
|
|
char szNewSrc[g_nMaxPath];
|
|
strcpy(szNewSrc, src);
|
|
BeautifyPath(szNewSrc);
|
|
if (!_fullpath (dst, szNewSrc, g_nMaxPath))
|
|
{
|
|
src = szNewSrc;
|
|
m_pLog->LogError("\002Cannot transform file name %s to absolute path, resorting to desparate measures!", src);
|
|
if (src[0] == '.' && (src[1] == g_cNativeSlash || src[1] == g_cNonNativeSlash))
|
|
src+=2;
|
|
#ifdef _XBOX
|
|
if (src[0] && src[1] != ':')
|
|
strcpy (dst, "d:\\");
|
|
dst += 3;
|
|
#endif
|
|
strcpy(dst, src);
|
|
size_t len = strlen(dst);
|
|
for (size_t n=0; dst[n]; n++)
|
|
{
|
|
if ( dst[n] == '\\' )
|
|
dst[n] = '/';
|
|
|
|
if (n > 8 && n+3 < len && dst[n] == '/' && dst[n+1] == '.' && dst[n+2] == '.')
|
|
{
|
|
size_t m = n+3;
|
|
n--;
|
|
while (dst[n] != '/')
|
|
{
|
|
n--;
|
|
if (!n)
|
|
break;
|
|
}
|
|
if (n)
|
|
{
|
|
memmove(&dst[n], &dst[m], len-m+1);
|
|
len -= m-n;
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
char* pEnd = BeautifyPath(dst);
|
|
|
|
#if defined(LINUX)
|
|
//we got to adjust the filename and fetch the case sensitive one
|
|
string adjustedFilename(dst);
|
|
adaptFilenameToLinux(adjustedFilename);
|
|
string fileName(adjustedFilename);
|
|
if(getFilenameNoCase(dst, fileName))
|
|
{
|
|
//file does exist, copy the real filename
|
|
strcpy(dst, fileName.c_str());
|
|
}
|
|
else
|
|
strcpy(dst, adjustedFilename.c_str());
|
|
if ((nFlags & FLAGS_ADD_TRAILING_SLASH) && pEnd > dst && (pEnd[-1]!=g_cNativeSlash && pEnd[-1]!=g_cNonNativeSlash))
|
|
#else
|
|
// p now points to the end of string
|
|
if ((nFlags & FLAGS_ADD_TRAILING_SLASH) && pEnd > dst && pEnd[-1]!=g_cNativeSlash)
|
|
#endif
|
|
{
|
|
*pEnd = g_cNativeSlash;
|
|
*++pEnd = '\0';
|
|
}
|
|
|
|
unsigned nLength = pEnd - dst;
|
|
|
|
if (bFoundInPak)
|
|
bFoundInPak=false;
|
|
|
|
if (nFlags & FLAGS_PATH_REAL)
|
|
return dst;
|
|
|
|
if (m_arrMods.empty())
|
|
return dst; // no mods - no need to search or modify the path
|
|
|
|
// now replace the root directory name (C:\Mastercd\)
|
|
// with the filesystem prefix ("" by default).
|
|
// try to search through the MOD directories, if it makes sense
|
|
#if defined(LINUX)
|
|
if (!(nFlags&FLAGS_IGNORE_MOD_DIRS) && nLength > m_strMasterCDRoot.length() && !comparePathNames(dst, m_strMasterCDRoot.c_str(), m_strMasterCDRoot.length()))
|
|
#else
|
|
if (!(nFlags&FLAGS_IGNORE_MOD_DIRS) && nLength > m_strMasterCDRoot.length() && !memcmp(dst, m_strMasterCDRoot.c_str(), m_strMasterCDRoot.length()))
|
|
#endif
|
|
{
|
|
// the previous mod prepend string length: as we move the string from through the loop,
|
|
// we keep the track of the actual path substring here.
|
|
size_t nPrevModLength = 0;
|
|
|
|
std::vector<string>::reverse_iterator it;
|
|
for (it = m_arrMods.rbegin(); it != m_arrMods.rend(); ++it)
|
|
{
|
|
string &strPrepend = *it;
|
|
memmove (dst+strPrepend.length()+m_strMasterCDRoot.length(), dst+m_strMasterCDRoot.length()+nPrevModLength, nLength-m_strMasterCDRoot.length()+1);
|
|
memcpy (dst+m_strMasterCDRoot.length(), strPrepend.c_str(), strPrepend.length());
|
|
|
|
// if there's only one MOD, there's actually no need to determine whether this file exists or not
|
|
//if (m_arrMods.size() == 1)
|
|
// return dst;
|
|
|
|
//m_pLog->Log("searching for %s",dst);
|
|
|
|
nPrevModLength = strPrepend.length();
|
|
if (GetFileAttributes (dst) != INVALID_FILE_ATTRIBUTES) // there's such file in FS
|
|
{
|
|
if (bFoundInPak)
|
|
*bFoundInPak=false;
|
|
break;
|
|
}
|
|
|
|
// [marco] NOTE: looks like there is a flow in the implementation of this in crypak -
|
|
// this function never finds the file in the pak even though the file is there,
|
|
// because of the prepended mod subdirs; however since the file is "not found"
|
|
// in the "mod paks", it is opened correctly afterwards regardless of this, because
|
|
// then it searches through zip files in reverse order and the mod paks are the latest
|
|
// paks opened, so the file is found, unless replaced by other files, which is kinda what
|
|
// we want to achieve with the pak system.
|
|
if (HasFileEntry(dst)) // there's such file in the pak
|
|
{
|
|
if (bFoundInPak)
|
|
*bFoundInPak=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (it == m_arrMods.rend())
|
|
{
|
|
if (nFlags & FLAGS_ONLY_MOD_DIRS)
|
|
return NULL; // we didn't find the corresponding file
|
|
else
|
|
{
|
|
string strPrepend; // = "";
|
|
// there's no mod where there is such a file - fallback to the "" mod
|
|
memmove (dst+strPrepend.length()+m_strMasterCDRoot.length(), dst+m_strMasterCDRoot.length()+nPrevModLength, nLength-m_strMasterCDRoot.length()+1);
|
|
memcpy (dst+m_strMasterCDRoot.length(), strPrepend.c_str(), strPrepend.length());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (nFlags & FLAGS_ONLY_MOD_DIRS) // only in MOD dirs, but no MODs found
|
|
return NULL;
|
|
|
|
return dst; // the last MOD scanned, or the absolute path outside MasterCD
|
|
}
|
|
/*
|
|
FILETIME CCryPak::GetFileTime(const char * szFileName)
|
|
{
|
|
FILETIME writetime;
|
|
memset(&writetime, 0, sizeof(writetime));
|
|
#ifdef WIN32
|
|
HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
::GetFileTime(hFile, NULL, NULL, &writetime);
|
|
CloseHandle(hFile);
|
|
}
|
|
#endif
|
|
return writetime;
|
|
}
|
|
*/
|
|
/*bool CCryPak::IsOutOfDate(const char * szCompiledName, const char * szMasterFile)
|
|
{
|
|
FILE * f = FOpen(szMasterFile,"rb");
|
|
if (f)
|
|
FClose(f);
|
|
else
|
|
return (false);
|
|
|
|
assert(f > m_OpenFiles.Num());
|
|
|
|
f = FOpen(szCompiledName,"rb");
|
|
if (f)
|
|
FClose(f);
|
|
else
|
|
return (true);
|
|
|
|
assert(f > m_OpenFiles.Num());
|
|
|
|
#ifdef WIN32
|
|
|
|
HANDLE status1 = CreateFile(szCompiledName,GENERIC_READ,FILE_SHARE_READ,
|
|
NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
|
|
|
|
HANDLE status2 = CreateFile(szMasterFile,GENERIC_READ,FILE_SHARE_READ,
|
|
NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
|
|
|
|
FILETIME writetime1,writetime2;
|
|
|
|
GetFileTime(status1,NULL,NULL,&writetime1);
|
|
GetFileTime(status2,NULL,NULL,&writetime2);
|
|
|
|
CloseHandle(status1);
|
|
CloseHandle(status2);
|
|
|
|
if (CompareFileTime(&writetime1,&writetime2)==-1)
|
|
return(true);
|
|
|
|
return (false);
|
|
#else
|
|
|
|
return (false);
|
|
|
|
#endif
|
|
}*/
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
FILE *CCryPak::FOpen(const char *pName, const char *szMode,char *szFileGamePath,int nLen)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
FILE *fp = NULL;
|
|
char szFullPathBuf[g_nMaxPath];
|
|
const char* szFullPath = AdjustFileName(pName, szFullPathBuf, 0);
|
|
if (nLen>g_nMaxPath)
|
|
nLen=g_nMaxPath;
|
|
strncpy(szFileGamePath,szFullPath,nLen);
|
|
fp = fopen (szFullPath, szMode);
|
|
|
|
if (fp)
|
|
RecordFile( pName );
|
|
else
|
|
OnMissingFile(pName);
|
|
return (fp);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
FILE *CCryPak::FOpen(const char *pName, const char *szMode,unsigned nFlags2)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
FILE *fp = NULL;
|
|
char szFullPathBuf[g_nMaxPath];
|
|
|
|
// get the priority into local variable to avoid it changing in the course of
|
|
// this function execution (?)
|
|
int nVarPakPriority = m_pPakVars->nPriority;
|
|
|
|
// maybe this file is in MOD dir - then it'll have the preference anyway
|
|
|
|
if (!m_arrMods.empty())
|
|
{
|
|
bool bFoundInPak;
|
|
const char* szModPath = AdjustFileName (pName, szFullPathBuf, FLAGS_ONLY_MOD_DIRS,&bFoundInPak);
|
|
|
|
// [marco] will have the preference, when outside the pak, ONLY if indevmode
|
|
// in non-devmode it always has preference inside the pak, so that we can use
|
|
// security checks - if the file is not found in the pak and we are not in
|
|
// devmode, do not open it
|
|
|
|
if (szModPath && (bFoundInPak || (!bFoundInPak && !nVarPakPriority)))
|
|
{
|
|
fp = fopen (szModPath, szMode);
|
|
if (fp)
|
|
{
|
|
RecordFile(pName);
|
|
return fp;
|
|
}
|
|
else
|
|
{
|
|
assert (0);
|
|
// this is strange - AdjustFileName() found a file, but fopen() didn't find one.. this should be impossible with FLAGS_ONLY_MOD_DIRS
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *szFullPath = AdjustFileName(pName, szFullPathBuf, 0);
|
|
|
|
if (!nVarPakPriority) // if the file system files have priority now..
|
|
{
|
|
fp = fopen (szFullPath, szMode);
|
|
if (fp)
|
|
{
|
|
RecordFile( pName );
|
|
return fp;
|
|
}
|
|
}
|
|
|
|
#if defined(LINUX)
|
|
const int _fmode = _O_BINARY;//it does not exist on linux
|
|
#endif
|
|
unsigned nFlags = (_fmode & (_O_TEXT|_O_BINARY))|_O_RDONLY;
|
|
|
|
// check the szMode
|
|
for (const char* pModeChar = szMode; *pModeChar; ++pModeChar)
|
|
switch (*pModeChar)
|
|
{
|
|
case 'r':
|
|
nFlags &= ~(_O_WRONLY|_O_RDWR);
|
|
// read mode is the only mode we can open the file in
|
|
break;
|
|
case 'w':
|
|
nFlags |= _O_WRONLY;
|
|
break;
|
|
case 'a':
|
|
nFlags |= _O_RDWR;
|
|
break;
|
|
case '+':
|
|
nFlags |= _O_RDWR;
|
|
break;
|
|
|
|
case 'b':
|
|
nFlags &= ~_O_TEXT;
|
|
nFlags |= _O_BINARY;
|
|
break;
|
|
case 't':
|
|
nFlags &= ~_O_BINARY;
|
|
nFlags |= _O_TEXT;
|
|
break;
|
|
|
|
case 'c':
|
|
case 'C':
|
|
nFlags |= CZipPseudoFile::_O_COMMIT_FLUSH_MODE;
|
|
break;
|
|
case 'n':
|
|
case 'N':
|
|
nFlags &= ~CZipPseudoFile::_O_COMMIT_FLUSH_MODE;
|
|
break;
|
|
|
|
case 'S':
|
|
nFlags |= _O_SEQUENTIAL;
|
|
break;
|
|
|
|
case 'R':
|
|
nFlags |= _O_RANDOM;
|
|
break;
|
|
|
|
case 'T':
|
|
nFlags |= _O_SHORT_LIVED;
|
|
break;
|
|
|
|
case 'D':
|
|
nFlags |= _O_TEMPORARY;
|
|
break;
|
|
|
|
case 'x':
|
|
case 'X':
|
|
nFlags2 |= FOPEN_HINT_DIRECT_OPERATION;
|
|
break;
|
|
}
|
|
|
|
if (nFlags & (_O_WRONLY|_O_RDWR))
|
|
{
|
|
// we need to open the file for writing, but we failed to do so.
|
|
// the only reason that can be is that there are no directories for that file.
|
|
// now create those dirs
|
|
if (!MakeDir (CryStringUtils::GetParentDirectory(string(szFullPath)).c_str()))
|
|
return NULL;
|
|
|
|
FILE *file = fopen (szFullPath, szMode);
|
|
if (file)
|
|
RecordFile( pName );
|
|
else
|
|
{
|
|
if (!(nFlags2&FOPEN_HINT_QUIET))
|
|
OnMissingFile(pName);
|
|
}
|
|
return file;
|
|
}
|
|
|
|
// try to open the pseudofile from one of the zips
|
|
CCachedFileData_AutoPtr pFileData = GetFileData (szFullPath);
|
|
if (!pFileData)
|
|
{
|
|
if (nVarPakPriority) // if the pak files had more priority, we didn't attempt fopen before- try it now
|
|
{
|
|
fp = fopen (szFullPath, szMode);
|
|
if (fp)
|
|
{
|
|
RecordFile( pName );
|
|
return fp;
|
|
}
|
|
}
|
|
if (!(nFlags2&FOPEN_HINT_QUIET))
|
|
OnMissingFile (pName);
|
|
return NULL; // we can't find such file in the pack files
|
|
}
|
|
|
|
RecordFile( pName );
|
|
|
|
size_t nFile;
|
|
// find the empty slot and open the file there; return the handle
|
|
for (nFile = 0; nFile < m_arrOpenFiles.size() && m_arrOpenFiles[nFile].GetFile(); ++nFile)
|
|
continue;
|
|
|
|
if (nFile == m_arrOpenFiles.size())
|
|
{
|
|
m_arrOpenFiles.resize (nFile+1);
|
|
}
|
|
|
|
#if defined(LINUX64)
|
|
if (pFileData != 0 && (nFlags2 & FOPEN_HINT_DIRECT_OPERATION))
|
|
#else
|
|
if (pFileData != NULL && (nFlags2 & FOPEN_HINT_DIRECT_OPERATION))
|
|
#endif
|
|
nFlags |= CZipPseudoFile::_O_DIRECT_OPERATION;
|
|
m_arrOpenFiles[nFile].Construct (pFileData, nFlags);
|
|
return (FILE*)(nFile + g_nPseudoFileIdxOffset); // the handle to the file
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// given the file name, searches for the file entry among the zip files.
|
|
// if there's such file in one of the zips, then creates (or used cached)
|
|
// CCachedFileData instance and returns it.
|
|
// The file data object may be created in this function,
|
|
// and it's important that the autoptr is returned: another thread may release the existing
|
|
// cached data before the function returns
|
|
// the path must be absolute normalized lower-case with forward-slashes
|
|
CCachedFileDataPtr CCryPak::GetFileData(const char* szName)
|
|
{
|
|
#if defined(LINUX)
|
|
replaceDoublePathFilename((char*)szName);
|
|
#endif
|
|
unsigned nNameLen = (unsigned)strlen(szName);
|
|
AUTO_LOCK(m_csZips);
|
|
// scan through registered pak files and try to find this file
|
|
for (ZipArray::reverse_iterator itZip = m_arrZips.rbegin(); itZip != m_arrZips.rend(); ++itZip)
|
|
{
|
|
size_t nBindRootLen = itZip->strBindRoot.length();
|
|
#if defined(LINUX)
|
|
if (nNameLen > nBindRootLen &&!comparePathNames(itZip->strBindRoot.c_str(), szName, nBindRootLen))
|
|
#else
|
|
if (nNameLen > nBindRootLen &&!memcmp(itZip->strBindRoot.c_str(), szName, nBindRootLen))
|
|
#endif
|
|
{
|
|
//const char *szDebug1=itZip->strBindRoot.c_str();
|
|
//const char *szDebug2=itZip->pZip->GetFilePath();
|
|
|
|
ZipDir::FileEntry* pFileEntry = itZip->pZip->FindFile (szName+nBindRootLen);
|
|
if (pFileEntry)
|
|
{
|
|
CCachedFileData Result(NULL, itZip->pZip, pFileEntry);
|
|
AUTO_LOCK(m_csCachedFiles);
|
|
|
|
CachedFileDataSet::iterator it = m_setCachedFiles.find(&Result);
|
|
if (it != m_setCachedFiles.end())
|
|
{
|
|
assert((*it)->GetFileEntry() == pFileEntry); // cached data
|
|
return *it;
|
|
}
|
|
else
|
|
return new CCachedFileData (this, itZip->pZip, pFileEntry);
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// tests if the given file path refers to an existing file inside registered (opened) packs
|
|
// the path must be absolute normalized lower-case with forward-slashes
|
|
bool CCryPak::HasFileEntry (const char* szPath)
|
|
{
|
|
unsigned nNameLen = (unsigned)strlen(szPath);
|
|
AUTO_LOCK(m_csZips);
|
|
// scan through registered pak files and try to find this file
|
|
for (ZipArray::reverse_iterator itZip = m_arrZips.rbegin(); itZip != m_arrZips.rend(); ++itZip)
|
|
{
|
|
size_t nBindRootLen = itZip->strBindRoot.length();
|
|
//const char *szDebug1=itZip->strBindRoot.c_str();
|
|
//const char *szDebug2=itZip->pZip->GetFilePath();
|
|
#if defined(LINUX)
|
|
if (nNameLen > nBindRootLen &&!comparePathNames(itZip->strBindRoot.c_str(), szPath, nBindRootLen))
|
|
#else
|
|
if (nNameLen > nBindRootLen &&!memcmp(itZip->strBindRoot.c_str(), szPath, nBindRootLen))
|
|
#endif
|
|
{
|
|
ZipDir::FileEntry* pFileEntry = itZip->pZip->FindFile (szPath+nBindRootLen);
|
|
if (pFileEntry)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
long CCryPak::FTell(FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].FTell();
|
|
else
|
|
return ftell(hFile);
|
|
}
|
|
|
|
// returns the path to the archive in which the file was opened
|
|
const char* CCryPak::GetFileArchivePath (FILE* hFile)
|
|
{
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].GetArchivePath();
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
FILETIME UnixTimeToFileTime(time_t t)
|
|
{
|
|
// Note that LONGLONG is a 64-bit value
|
|
#if defined(LINUX)
|
|
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000ll;
|
|
#else
|
|
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
|
|
#endif
|
|
return (FILETIME&)ll;
|
|
}
|
|
|
|
|
|
// returns the file modification time
|
|
FILETIME CCryPak::GetModificationTime(FILE* hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].GetModificationTime();
|
|
else
|
|
{
|
|
// TODO/TIME: implement retrieving FILETIME out of the real file handle
|
|
#if defined(LINUX)
|
|
struct stat64 st;
|
|
_fstat64(fileno(hFile), &st);
|
|
#else
|
|
struct __stat64 st;
|
|
_fstat64(_fileno(hFile), &st);
|
|
#endif
|
|
return UnixTimeToFileTime (st.st_mtime);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
unsigned CCryPak::FGetSize(FILE* hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].GetFileSize();
|
|
else
|
|
{
|
|
long nSave = ftell (hFile);
|
|
fseek (hFile, 0, SEEK_END);
|
|
long nSize = ftell(hFile);
|
|
fseek (hFile, nSave, SEEK_SET);
|
|
return nSize;
|
|
}
|
|
}
|
|
|
|
int CCryPak::FFlush(FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return 0;
|
|
else
|
|
return fflush(hFile);
|
|
}
|
|
|
|
int CCryPak::FSeek(FILE *hFile, long seek, int mode)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].FSeek(seek, mode);
|
|
else
|
|
return fseek(hFile, seek, mode);
|
|
}
|
|
|
|
size_t CCryPak::FWrite(void *data, size_t length, size_t elems, FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return 0;
|
|
else
|
|
return fwrite(data, length, elems, hFile);
|
|
}
|
|
|
|
size_t CCryPak::FRead(void *pData, size_t nSize, size_t nCount, FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].FRead(pData, nSize, nCount, hFile);
|
|
else
|
|
{
|
|
return fread(pData, nSize, nCount, hFile);
|
|
}
|
|
}
|
|
|
|
int CCryPak::FClose(FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
{
|
|
m_arrOpenFiles[nPseudoFile].Destruct();
|
|
return 0;
|
|
}
|
|
else
|
|
return fclose(hFile);
|
|
}
|
|
|
|
int CCryPak::FEof(FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].FEof();
|
|
else
|
|
return feof(hFile);
|
|
}
|
|
|
|
int CCryPak::FScanf(FILE *hFile, const char *format, ...)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
va_list arglist;
|
|
va_start(arglist, format);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].FScanfv(format, arglist);
|
|
else
|
|
return 0;//vfscanf(handle, format, arglist);
|
|
va_end(arglist);
|
|
}
|
|
|
|
int CCryPak::FPrintf(FILE *hFile, const char *szFormat, ...)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return 0; // we don't support it now
|
|
|
|
va_list arglist;
|
|
va_start(arglist, szFormat);
|
|
|
|
return vfprintf(hFile, szFormat, arglist);
|
|
}
|
|
|
|
char *CCryPak::FGets(char *str, int n, FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].FGets(str, n);
|
|
else
|
|
return fgets(str, n, hFile);
|
|
}
|
|
|
|
int CCryPak::Getc(FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].Getc();
|
|
else
|
|
return getc(hFile);
|
|
}
|
|
|
|
int CCryPak::Ungetc(int c, FILE *hFile)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
INT_PTR nPseudoFile = ((INT_PTR)hFile) - g_nPseudoFileIdxOffset;
|
|
if ((UINT_PTR)nPseudoFile < m_arrOpenFiles.size())
|
|
return m_arrOpenFiles[nPseudoFile].Ungetc(c);
|
|
else
|
|
return ungetc(c, hFile);
|
|
}
|
|
|
|
#ifndef _XBOX
|
|
const char *GetExtension (const char *in)
|
|
{
|
|
while (*in)
|
|
{
|
|
if (*in=='.')
|
|
return in;
|
|
in++;
|
|
}
|
|
return NULL;
|
|
}
|
|
#else
|
|
const char *GetExtension (const char *in);
|
|
#endif //_XBOX
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
intptr_t CCryPak::FindFirst(const char *pDir, struct _finddata_t *fd)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
char szFullPathBuf[g_nMaxPath];
|
|
|
|
//m_pLog->Log("Scanning %s",pDir);
|
|
//const char *szFullPath = AdjustFileName(pDir, szFullPathBuf, 0);
|
|
const char *szFullPath = AdjustFileName(pDir, szFullPathBuf,FLAGS_IGNORE_MOD_DIRS);
|
|
CCryPakFindData_AutoPtr pFindData = new CCryPakFindData(this, szFullPath);
|
|
if (pFindData->empty())
|
|
{
|
|
if (m_arrMods.empty())
|
|
return (-1); // no mods and no files found
|
|
}
|
|
|
|
// now scan mod folders as well
|
|
std::vector<string>::reverse_iterator it;
|
|
for (it = m_arrMods.rbegin(); it != m_arrMods.rend(); ++it)
|
|
{
|
|
string &strPrepend = *it;
|
|
string sModFolder=strPrepend+string(pDir);
|
|
const char *szFullPath = AdjustFileName(sModFolder.c_str(), szFullPathBuf,FLAGS_IGNORE_MOD_DIRS);
|
|
//m_pLog->Log("Scanning (2) %s",szFullPath);
|
|
pFindData->Scan(this,szFullPath);
|
|
} //it
|
|
|
|
if (pFindData->empty())
|
|
return (-1);
|
|
|
|
m_setFindData.insert (pFindData);
|
|
pFindData->Fetch(fd);
|
|
return (intptr_t)(CCryPakFindData*)pFindData;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CCryPak::FindNext(intptr_t handle, struct _finddata_t *fd)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
//if (m_setFindData.find ((CCryPakFindData*)handle) == m_setFindData.end())
|
|
// return -1; // invalid handle
|
|
|
|
if (((CCryPakFindData*)handle)->Fetch(fd))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int CCryPak::FindClose(intptr_t handle)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
m_setFindData.erase ((CCryPakFindData*)handle);
|
|
return 0;
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
bool CCryPak::OpenPack(const char* szBindRootIn, const char *szPath, unsigned nFlags)
|
|
{
|
|
char szFullPathBuf[g_nMaxPath];
|
|
|
|
const char *szFullPath = AdjustFileName(szPath, szFullPathBuf, nFlags|FLAGS_IGNORE_MOD_DIRS);
|
|
|
|
char szBindRootBuf[g_nMaxPath];
|
|
const char* szBindRoot = AdjustFileName(szBindRootIn, szBindRootBuf, FLAGS_ADD_TRAILING_SLASH|FLAGS_IGNORE_MOD_DIRS);
|
|
|
|
return OpenPackCommon(szBindRoot, szFullPath, nFlags);
|
|
}
|
|
|
|
bool CCryPak::OpenPack(const char *szPath, unsigned nFlags)
|
|
{
|
|
char szFullPathBuf[g_nMaxPath];
|
|
|
|
const char *szFullPath = AdjustFileName(szPath, szFullPathBuf, nFlags|FLAGS_IGNORE_MOD_DIRS);
|
|
string strBindRoot;
|
|
const char *pLastSlash = strrchr(szFullPath, g_cNativeSlash);
|
|
if (pLastSlash)
|
|
strBindRoot.assign (szFullPath, pLastSlash - szFullPath + 1);
|
|
else
|
|
{
|
|
m_pLog->LogError("\002Pak file %s has absolute path %s, which is strange", szPath, szFullPath);
|
|
//desc.strFileName = szZipPath;
|
|
}
|
|
|
|
return OpenPackCommon(strBindRoot.c_str(), szFullPath, nFlags);
|
|
}
|
|
|
|
bool CCryPak::OpenPackCommon(const char* szBindRoot, const char *szFullPath, unsigned nFlags)
|
|
{
|
|
AUTO_LOCK(m_csZips);
|
|
{
|
|
// try to find this - maybe the pack has already been opened
|
|
for (ZipArray::iterator it = m_arrZips.begin(); it != m_arrZips.end(); ++it)
|
|
if (!stricmp(it->pZip->GetFilePath(), szFullPath)
|
|
&&!stricmp(it->strBindRoot.c_str(), szBindRoot))
|
|
return true; // already opened
|
|
}
|
|
PackDesc desc;
|
|
desc.strBindRoot = szBindRoot;
|
|
|
|
desc.pArchive = OpenArchive (szFullPath, ICryArchive::FLAGS_OPTIMIZED_READ_ONLY|ICryArchive::FLAGS_ABSOLUTE_PATHS);
|
|
if (!desc.pArchive)
|
|
return false; // couldn't open the archive
|
|
|
|
if (desc.pArchive->GetClassId() == CryArchive::gClassId)
|
|
{
|
|
m_pLog->Log("Opening pack file %s",szFullPath);
|
|
desc.pZip = static_cast<CryArchive*>((ICryArchive*)desc.pArchive)->GetCache();
|
|
m_arrZips.push_back(desc);
|
|
return true;
|
|
}
|
|
else
|
|
return false; // don't support such objects yet
|
|
}
|
|
|
|
//int gg=1;
|
|
// after this call, the file will be unlocked and closed, and its contents won't be used to search for files
|
|
bool CCryPak::ClosePack(const char* pName, unsigned nFlags)
|
|
{
|
|
char szZipPathBuf[g_nMaxPath];
|
|
const char* szZipPath = AdjustFileName(pName, szZipPathBuf, nFlags);
|
|
AUTO_LOCK(m_csZips);
|
|
|
|
//if (strstr(szZipPath,"huggy_tweak_scripts"))
|
|
// gg=0;
|
|
|
|
for (ZipArray::iterator it = m_arrZips.begin(); it != m_arrZips.end(); ++it)
|
|
if (!stricmp(szZipPath, it->GetFullPath()))
|
|
{
|
|
// this is the pack with the given name - remove it, and if possible it will be deleted
|
|
// the zip is referenced from the archive and *it; the archive is referenced only from *it
|
|
//
|
|
// the pZip (cache) can be referenced from stream engine and pseudo-files.
|
|
// the archive can be referenced from outside
|
|
bool bResult = (it->pZip->NumRefs() == 2) && it->pArchive->NumRefs() == 1;
|
|
if (bResult)
|
|
{
|
|
m_pLog->Log("Closing pack file %s",szZipPath);
|
|
m_arrZips.erase (it);
|
|
}
|
|
return bResult;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CCryPak::OpenPacks(const char *pWildcardIn, unsigned nFlags)
|
|
{
|
|
char cWorkBuf[g_nMaxPath];
|
|
AdjustFileName(pWildcardIn, cWorkBuf, nFlags|FLAGS_COPY_DEST_ALWAYS);
|
|
string strBindRoot = CryStringUtils::GetParentDirectory<string>(cWorkBuf);
|
|
strBindRoot += g_cNativeSlash;
|
|
return OpenPacksCommon(strBindRoot.c_str(), cWorkBuf, nFlags);
|
|
}
|
|
|
|
bool CCryPak::OpenPacks(const char* szBindRoot, const char *pWildcardIn, unsigned nFlags)
|
|
{
|
|
char cWorkBuf[g_nMaxPath];
|
|
AdjustFileName(pWildcardIn, cWorkBuf, nFlags|FLAGS_COPY_DEST_ALWAYS);
|
|
char cBindRootBuf[g_nMaxPath];
|
|
const char* pBindRoot = AdjustFileName(szBindRoot, cBindRootBuf, nFlags|FLAGS_ADD_TRAILING_SLASH);
|
|
return OpenPacksCommon(pBindRoot, cWorkBuf, nFlags);
|
|
}
|
|
|
|
bool CCryPak::OpenPacksCommon(const char* szDir, char *cWork, unsigned nFlags)
|
|
{
|
|
__finddata64_t fd;
|
|
intptr_t h = _findfirst64 (cWork, &fd);
|
|
|
|
// where to copy the filenames to form the path in cWork
|
|
char* pDestName = strrchr (cWork, g_cNativeSlash);
|
|
#if defined(LINUX)
|
|
if (!pDestName)
|
|
pDestName = strrchr (cWork, g_cNonNativeSlash);
|
|
#endif
|
|
if (!pDestName)
|
|
pDestName = cWork;
|
|
else
|
|
++pDestName;
|
|
if (h != -1)
|
|
{
|
|
std::vector<string> files;
|
|
do {
|
|
strcpy (pDestName, fd.name);
|
|
std::string sfile = strlwr(cWork);
|
|
files.push_back(strlwr(cWork));
|
|
}
|
|
while(0 == _findnext64 (h, &fd));
|
|
|
|
// Open files in alphabet order.
|
|
std::sort( files.begin(),files.end() );
|
|
for (int i = 0; i < files.size(); i++)
|
|
{
|
|
OpenPackCommon(szDir, files[i].c_str(), nFlags);
|
|
}
|
|
|
|
_findclose (h);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CCryPak::ClosePacks(const char *pWildcardIn, unsigned nFlags)
|
|
{
|
|
bool bOk = true;
|
|
__finddata64_t fd;
|
|
char cWorkBuf[g_nMaxPath];
|
|
const char* cWork = AdjustFileName(pWildcardIn, cWorkBuf, nFlags);
|
|
intptr_t h = _findfirst64 (cWork, &fd);
|
|
string strDir = CryStringUtils::GetParentDirectory<string>(pWildcardIn);
|
|
if (h != -1)
|
|
{
|
|
do {
|
|
if (!ClosePack((strDir + "\\" + fd.name).c_str(), nFlags))
|
|
bOk = false;
|
|
} while(0 == _findnext64 (h, &fd));
|
|
_findclose (h);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CCryPak::InitPack(const char *szBasePath, unsigned nFlags)
|
|
{
|
|
string strPath = szBasePath;
|
|
if (szBasePath && szBasePath[0])
|
|
strPath += "\\*.cpk";
|
|
else
|
|
strPath += "*.cpk";
|
|
|
|
char cWorkBuf[g_nMaxPath];
|
|
const char* cWork = AdjustFileName(strPath.c_str(), cWorkBuf, nFlags);
|
|
|
|
bool bFind = false;
|
|
struct __finddata64_t fileinfo;
|
|
intptr_t handle = _findfirst64 (cWork, &fileinfo);
|
|
if (handle == -1)
|
|
return true;
|
|
do
|
|
{
|
|
if (szBasePath && szBasePath[0])
|
|
_snprintf(cWorkBuf, sizeof(cWorkBuf), "%s\\%s", szBasePath, fileinfo.name);
|
|
else
|
|
strcpy(cWorkBuf, fileinfo.name);
|
|
if (!OpenPack(cWorkBuf))
|
|
{
|
|
m_pLog->LogError("\003Cannot bind pak file %s", cWork);
|
|
}
|
|
} while (_findnext64( handle, &fileinfo ) != -1);
|
|
_findclose (handle);
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
bool CCryPak::Init(const char *szBasePath)
|
|
{
|
|
return InitPack(szBasePath);
|
|
}
|
|
|
|
void CCryPak::Release()
|
|
{
|
|
}
|
|
|
|
int CZipPseudoFile::FSeek (long nOffset, int nMode)
|
|
{
|
|
if (!m_pFileData)
|
|
return -1;
|
|
|
|
switch (nMode)
|
|
{
|
|
case SEEK_SET:
|
|
m_nCurSeek = nOffset;
|
|
break;
|
|
case SEEK_CUR:
|
|
m_nCurSeek += nOffset;
|
|
break;
|
|
case SEEK_END:
|
|
m_nCurSeek = GetFileSize() - nOffset;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
if (m_fDirect)
|
|
fseek (m_fDirect, m_nCurSeek + GetFile()->GetFileDataOffset(), SEEK_SET);
|
|
return 0;
|
|
}
|
|
|
|
size_t CZipPseudoFile::FRead (void* pDest, size_t nSize, size_t nCount, FILE* hFile)
|
|
{
|
|
if (!GetFile())
|
|
return 0;
|
|
|
|
size_t nTotal = nSize * nCount;
|
|
if (!nTotal || (unsigned)m_nCurSeek >= GetFileSize())
|
|
return 0;
|
|
|
|
if (nTotal > GetFileSize() - m_nCurSeek)
|
|
{
|
|
nTotal = GetFileSize() - m_nCurSeek;
|
|
if (nTotal < nSize)
|
|
return 0;
|
|
nTotal -= nTotal % nSize;
|
|
}
|
|
|
|
if (m_fDirect)
|
|
{
|
|
m_nCurSeek += nTotal;
|
|
//return fread (pDest, nSize, nCount, m_fDirect);
|
|
|
|
// FIX [Carsten]: nTotal holds the amount of bytes we need to read!
|
|
// Since we're in a zip archive it's very likely that we read more than actually belongs to hFile.
|
|
// Therefore nTotal was correct in the previous "if" branch in case of we're reading over the end of the file.
|
|
return fread (pDest, 1, nTotal, m_fDirect);
|
|
}
|
|
else
|
|
{
|
|
unsigned char * pSrc = (unsigned char *)GetFile()->GetData();
|
|
if (!pSrc)
|
|
return 0;
|
|
pSrc += m_nCurSeek;
|
|
|
|
if (!(m_nFlags & _O_TEXT))
|
|
memcpy(pDest, pSrc, nTotal);
|
|
else
|
|
{
|
|
unsigned char *itDest = (unsigned char *)pDest;
|
|
unsigned char *itSrc = pSrc, *itSrcEnd = pSrc + nTotal;
|
|
for (; itSrc != itSrcEnd; ++itSrc)
|
|
{
|
|
if (*pSrc != 0xd)
|
|
*(itDest++) = *itSrc;
|
|
}
|
|
m_nCurSeek += nTotal;
|
|
return itDest - (unsigned char*) pDest;
|
|
}
|
|
m_nCurSeek += nTotal;
|
|
}
|
|
return nTotal / nSize;
|
|
}
|
|
|
|
int CZipPseudoFile::FEof()
|
|
{
|
|
return (unsigned)m_nCurSeek >= GetFileSize();
|
|
}
|
|
|
|
int CZipPseudoFile::FScanfv (const char* szFormat, va_list args)
|
|
{
|
|
if (!GetFile())
|
|
return 0;
|
|
char *pSrc = (char *)GetFile()->GetData();
|
|
if (!pSrc)
|
|
return 0;
|
|
// now scan the pSrc+m_nCurSeek
|
|
return 0;
|
|
}
|
|
|
|
char* CZipPseudoFile::FGets(char* pBuf, int n)
|
|
{
|
|
if (!GetFile())
|
|
return NULL;
|
|
char *pData = (char *)GetFile()->GetData();
|
|
if (!pData)
|
|
return NULL;
|
|
int nn = 0;
|
|
int i;
|
|
for (i=0; i<n; i++)
|
|
{
|
|
if (i+m_nCurSeek == GetFileSize())
|
|
break;
|
|
char c = pData[i+m_nCurSeek];
|
|
if (c == 0xa)
|
|
{
|
|
pBuf[nn++] = c;
|
|
i++;
|
|
break;
|
|
}
|
|
else
|
|
if (c == 0xd)
|
|
continue;
|
|
pBuf[nn++] = c;
|
|
}
|
|
pBuf[nn] = 0;
|
|
m_nCurSeek += i;
|
|
if (m_nCurSeek == GetFileSize())
|
|
return NULL;
|
|
return pBuf;
|
|
}
|
|
|
|
int CZipPseudoFile::Getc()
|
|
{
|
|
if (!GetFile())
|
|
return EOF;
|
|
char *pData = (char *)GetFile()->GetData();
|
|
if (!pData)
|
|
return EOF;
|
|
int nn = 0;
|
|
int c = EOF;
|
|
int i;
|
|
for (i=0; i<1; i++)
|
|
{
|
|
if (i+m_nCurSeek == GetFileSize())
|
|
return c;
|
|
c = pData[i+m_nCurSeek];
|
|
break;
|
|
}
|
|
m_nCurSeek += i;
|
|
return c;
|
|
}
|
|
|
|
int CZipPseudoFile::Ungetc(int c)
|
|
{
|
|
if (m_nCurSeek <= 0)
|
|
return EOF;
|
|
m_nCurSeek--;
|
|
return c;
|
|
}
|
|
|
|
CCachedFileData::CCachedFileData(class CCryPak* pPak, ZipDir::Cache*pZip, ZipDir::FileEntry* pFileEntry)
|
|
{
|
|
m_pPak = pPak;
|
|
m_pFileData = NULL;
|
|
m_nRefCounter = 0;
|
|
m_pZip = pZip;
|
|
m_pFileEntry = pFileEntry;
|
|
|
|
if (pPak)
|
|
pPak->Register (this);
|
|
}
|
|
|
|
CCachedFileData::~CCachedFileData()
|
|
{
|
|
if (m_pPak)
|
|
m_pPak->Unregister (this);
|
|
|
|
// forced destruction
|
|
if (m_pFileData)
|
|
{
|
|
m_pZip->Free (m_pFileData);
|
|
m_pFileData = NULL;
|
|
}
|
|
|
|
m_pZip = NULL;
|
|
m_pFileEntry = NULL;
|
|
}
|
|
|
|
// return the data in the file, or NULL if error
|
|
void* CCachedFileData::GetData(bool bRefreshCache)
|
|
{
|
|
// first, do a "dirty" fast check without locking the critical section
|
|
// in most cases, the data's going to be already there, and if it's there,
|
|
// nobody's going to release it until this object is destructed.
|
|
if (bRefreshCache && !m_pFileData)
|
|
{
|
|
assert ((bool)m_pZip);
|
|
assert (m_pFileEntry && m_pZip->IsOwnerOf(m_pFileEntry));
|
|
|
|
// Then, lock it and check whether the data is still not there.
|
|
// if it's not, allocate memory and unpack the file
|
|
CCritSection& csCachedFileDataLock = m_pPak->GetCachedFileLock();
|
|
AUTO_LOCK(csCachedFileDataLock);
|
|
if (!m_pFileData)
|
|
{
|
|
m_pFileData = g_pBigHeap->Alloc (m_pFileEntry->desc.lSizeUncompressed, "CCachedFileData::GetData");
|
|
if (ZipDir::ZD_ERROR_SUCCESS != m_pZip->ReadFile (m_pFileEntry, NULL, m_pFileData))
|
|
{
|
|
g_pBigHeap->Free(m_pFileData);
|
|
m_pFileData = NULL;
|
|
}
|
|
}
|
|
}
|
|
return m_pFileData;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CCryPakFindData::Scan(class CCryPak*pPak, const char* szDir)
|
|
{
|
|
// get the priority into local variable to avoid it changing in the course of
|
|
// this function execution
|
|
int nVarPakPriority = pPak->m_pPakVars->nPriority;
|
|
|
|
if (!nVarPakPriority)
|
|
{
|
|
// first, find the file system files
|
|
ScanFS(pPak,szDir);
|
|
ScanZips(pPak, szDir);
|
|
}
|
|
else
|
|
{
|
|
// first, find the zip files
|
|
ScanZips(pPak, szDir);
|
|
ScanFS(pPak,szDir);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CCryPakFindData::CCryPakFindData (class CCryPak*pPak, const char* szDir)
|
|
{
|
|
Scan(pPak,szDir);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CCryPakFindData::ScanFS(CCryPak*pPak, const char *szDirIn)
|
|
{
|
|
//char cWork[CCryPak::g_nMaxPath];
|
|
//pPak->AdjustFileName(szDirIn, cWork);
|
|
|
|
__finddata64_t fd;
|
|
#ifdef WIN64
|
|
memset (&fd, 0, sizeof(fd));
|
|
#endif
|
|
intptr_t nFS = _findfirst64 (szDirIn, &fd);
|
|
if (nFS == -1)
|
|
return;
|
|
|
|
do
|
|
{
|
|
m_mapFiles.insert (FileMap::value_type(fd.name, FileDesc(&fd)));
|
|
}
|
|
while(0 == _findnext64(nFS, &fd));
|
|
|
|
_findclose (nFS);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CCryPakFindData::ScanZips (CCryPak* pPak, const char* szDir)
|
|
{
|
|
size_t nLen = strlen(szDir);
|
|
CAutoLock<CCritSection> __AL_Zips(pPak->m_csZips);
|
|
for (CCryPak::ZipArray::iterator it = pPak->m_arrZips.begin(); it != pPak->m_arrZips.end(); ++it)
|
|
{
|
|
size_t nBindRootLen = it->strBindRoot.length();
|
|
|
|
#if defined(LINUX)
|
|
if (nLen > nBindRootLen && !comparePathNames(szDir, it->strBindRoot.c_str(), nBindRootLen))
|
|
#else
|
|
if (nLen > nBindRootLen && !memcmp(szDir, it->strBindRoot.c_str(), nBindRootLen))
|
|
#endif
|
|
{
|
|
// first, find the files
|
|
{
|
|
ZipDir::FindFile fd (it->pZip);
|
|
for(fd.FindFirst (szDir + nBindRootLen); fd.GetFileEntry(); fd.FindNext())
|
|
m_mapFiles.insert (FileMap::value_type(fd.GetFileName(), FileDesc(fd.GetFileEntry())));
|
|
}
|
|
|
|
{
|
|
ZipDir::FindDir fd (it->pZip);
|
|
for (fd.FindFirst(szDir + nBindRootLen); fd.GetDirEntry(); fd.FindNext())
|
|
m_mapFiles.insert (FileMap::value_type(fd.GetDirName(), FileDesc ()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CCryPakFindData::empty() const
|
|
{
|
|
return m_mapFiles.empty();
|
|
}
|
|
|
|
bool CCryPakFindData::Fetch(_finddata_t* pfd)
|
|
{
|
|
if (m_mapFiles.empty())
|
|
return false;
|
|
|
|
FileMap::iterator it = m_mapFiles.begin();
|
|
memcpy(pfd->name, it->first.c_str(), min(sizeof(pfd->name), it->first.length()+1));
|
|
pfd->attrib = it->second.nAttrib;
|
|
pfd->size = it->second.nSize;
|
|
pfd->time_access = it->second.tAccess;
|
|
pfd->time_create = it->second.tCreate;
|
|
pfd->time_write = it->second.tWrite;
|
|
|
|
m_mapFiles.erase (it);
|
|
return true;
|
|
}
|
|
|
|
CCryPakFindData::FileDesc::FileDesc (struct _finddata_t* fd)
|
|
{
|
|
nSize = fd->size;
|
|
nAttrib = fd->attrib;
|
|
tAccess = fd->time_access;
|
|
tCreate = fd->time_create;
|
|
tWrite = fd->time_write;
|
|
}
|
|
|
|
// the conversions in this function imply that we don't support
|
|
// 64-bit file sizes or 64-bit time values
|
|
CCryPakFindData::FileDesc::FileDesc (struct __finddata64_t* fd)
|
|
{
|
|
nSize = (unsigned)fd->size;
|
|
nAttrib = fd->attrib;
|
|
tAccess = (time_t)fd->time_access;
|
|
tCreate = (time_t)fd->time_create;
|
|
tWrite = (time_t)fd->time_write;
|
|
}
|
|
|
|
|
|
CCryPakFindData::FileDesc::FileDesc (ZipDir::FileEntry* fe)
|
|
{
|
|
nSize = fe->desc.lSizeUncompressed;
|
|
#if defined(LINUX)
|
|
nAttrib = _A_IN_CRYPAK; // files in zip are read-only, and
|
|
#else
|
|
nAttrib = _A_RDONLY|_A_IN_CRYPAK; // files in zip are read-only, and
|
|
#endif
|
|
tAccess = -1;
|
|
tCreate = -1;
|
|
tWrite = -1; // we don't need it
|
|
}
|
|
|
|
CCryPakFindData::FileDesc::FileDesc ()
|
|
{
|
|
nSize = 0;
|
|
#if defined(LINUX)
|
|
nAttrib = _A_SUBDIR;
|
|
#else
|
|
nAttrib = _A_SUBDIR | _A_RDONLY;
|
|
#endif
|
|
tAccess = -1;
|
|
tCreate = -1;
|
|
tWrite = -1;
|
|
}
|
|
|
|
//! Puts the memory statistics into the given sizer object
|
|
//! According to the specifications in interface ICrySizer
|
|
void CCryPak::GetMemoryStatistics(ICrySizer *pSizer)
|
|
{
|
|
AUTO_LOCK(m_csZips);
|
|
size_t nSize = sizeof(*this) /*+ m_arrZips.capacity() * sizeof(ZipArray::value_type)*/;
|
|
for (ZipArray::iterator itZip = m_arrZips.begin(); itZip != m_arrZips.end(); ++itZip)
|
|
nSize += itZip->sizeofThis();
|
|
|
|
for (CryPakFindDataSet::iterator itFindData = m_setFindData.begin(); itFindData != m_setFindData.end(); ++itFindData)
|
|
nSize += sizeof(CryPakFindDataSet::value_type) + (*itFindData)->sizeofThis();
|
|
|
|
for (CachedFileDataSet::iterator itCachedData = m_setCachedFiles.begin(); itCachedData != m_setCachedFiles.end(); ++itCachedData)
|
|
nSize += sizeof(CachedFileDataSet::value_type) + (*itCachedData)->sizeofThis();
|
|
|
|
nSize += m_arrOpenFiles.capacity() * sizeof(CZipPseudoFile);
|
|
|
|
pSizer->AddObject(this, nSize);
|
|
}
|
|
|
|
size_t CCryPakFindData::sizeofThis()const
|
|
{
|
|
size_t nSize = sizeof(*this);
|
|
for (FileMap::const_iterator it = m_mapFiles.begin(); it != m_mapFiles.end(); ++it)
|
|
{
|
|
nSize += sizeof(FileMap::value_type);
|
|
nSize += it->first.capacity();
|
|
}
|
|
return nSize;
|
|
}
|
|
|
|
|
|
bool CCryPak::MakeDir(const char* szPath)
|
|
{
|
|
for (const char*p = szPath; *p; )
|
|
{
|
|
while (*p != g_cNonNativeSlash && *p != g_cNativeSlash && *p)
|
|
++p;
|
|
|
|
string strSubdir(szPath, p - szPath);
|
|
// check whether such file or dir exists
|
|
DWORD dwAttr = GetFileAttributes(strSubdir.c_str());
|
|
if (strSubdir.empty() || strSubdir[strSubdir.length()-1] == ':')
|
|
{
|
|
// this is the disk specification - do nothing, the disk should already exist
|
|
}
|
|
else
|
|
if (dwAttr == INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
if (_mkdir (strSubdir.c_str()))
|
|
return false; // couldn't create such directory
|
|
}
|
|
else
|
|
if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
// do nothing - such a directory already exists
|
|
}
|
|
else
|
|
{
|
|
// we can't create such a directory because there's already a file with such name
|
|
return false;
|
|
}
|
|
if (*p)
|
|
++p;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// open the physical archive file - creates if it doesn't exist
|
|
// returns NULL if it's invalid or can't open the file
|
|
ICryArchive* CCryPak::OpenArchive (const char* szPath, unsigned nFlags)
|
|
{
|
|
char szFullPathBuf[CCryPak::g_nMaxPath];
|
|
const char* szFullPath = AdjustFileName(szPath, szFullPathBuf, nFlags);
|
|
|
|
// if it's simple and read-only, it's assumed it's read-only
|
|
if (nFlags & ICryArchive::FLAGS_OPTIMIZED_READ_ONLY)
|
|
nFlags |= ICryArchive::FLAGS_READ_ONLY;
|
|
|
|
unsigned nFactoryFlags = 0;
|
|
if (nFlags & ICryArchive::FLAGS_DONT_COMPACT)
|
|
nFactoryFlags |= ZipDir::CacheFactory::FLAGS_DONT_COMPACT;
|
|
|
|
if (nFlags & ICryArchive::FLAGS_READ_ONLY)
|
|
nFactoryFlags |= ZipDir::CacheFactory::FLAGS_READ_ONLY;
|
|
|
|
if (nFlags & ICryArchive::FLAGS_CREATE_NEW)
|
|
nFactoryFlags |= ZipDir::CacheFactory::FLAGS_CREATE_NEW;
|
|
|
|
ICryArchive* pArchive = FindArchive(szFullPath);
|
|
const unsigned nFlagsMustMatch = ICryArchive::FLAGS_RELATIVE_PATHS_ONLY | ICryArchive::FLAGS_ABSOLUTE_PATHS | ICryArchive::FLAGS_READ_ONLY;
|
|
if (pArchive)
|
|
{
|
|
// check for compatibility
|
|
|
|
if (!(nFlags & ICryArchive::FLAGS_RELATIVE_PATHS_ONLY) && (pArchive->GetFlags() & ICryArchive::FLAGS_RELATIVE_PATHS_ONLY))
|
|
pArchive->ResetFlags(ICryArchive::FLAGS_RELATIVE_PATHS_ONLY);
|
|
|
|
// we found one
|
|
if (nFlags & ICryArchive::FLAGS_OPTIMIZED_READ_ONLY)
|
|
{
|
|
if (pArchive->GetClassId() == CryArchive::gClassId)
|
|
return pArchive; // we can return an optimized archive
|
|
|
|
//if (!(pArchive->GetFlags() & ICryArchive::FLAGS_READ_ONLY))
|
|
return NULL; // we can't let it open read-only optimized while it's open for RW access
|
|
}
|
|
else
|
|
{
|
|
if (!(nFlags & ICryArchive::FLAGS_READ_ONLY) && (pArchive->GetFlags() & ICryArchive::FLAGS_READ_ONLY))
|
|
{
|
|
// we don't support upgrading from ReadOnly to ReadWrite
|
|
return NULL;
|
|
}
|
|
|
|
return pArchive;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
string strBindRoot;
|
|
|
|
//if (!(nFlags & ICryArchive::FLAGS_RELATIVE_PATHS_ONLY))
|
|
strBindRoot = CryStringUtils::GetParentDirectory<string>(szFullPath);
|
|
|
|
try
|
|
{
|
|
ZipDir::CacheFactory factory (g_pBigHeap, ZipDir::ZD_INIT_FAST, nFactoryFlags);
|
|
if (nFlags & ICryArchive::FLAGS_OPTIMIZED_READ_ONLY)
|
|
return new CryArchive (this, strBindRoot, factory.New (szFullPath), nFlags);
|
|
else
|
|
return new CryArchiveRW(this, strBindRoot, factory.NewRW(szFullPath), nFlags);
|
|
}
|
|
catch(ZipDir::Error e)
|
|
{
|
|
m_pLog->LogError("can't create the archive \"%s\"==\"%s\": error %s (code %d) in %s at %s:%d. %s.", szPath, szFullPath, e.getError(), e.nError, e.szFunction, e.szFile, e.nLine, e.getDescription());
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Adds a new file to the zip or update an existing one
|
|
// adds a directory (creates several nested directories if needed)
|
|
// compression methods supported are 0 (store) and 8 (deflate) , compression level is 0..9 or -1 for default (like in zlib)
|
|
int CryArchiveRW::UpdateFile (const char* szRelativePath, void* pUncompressed, unsigned nSize, unsigned nCompressionMethod, int nCompressionLevel)
|
|
{
|
|
if (m_nFlags & FLAGS_READ_ONLY)
|
|
return ZipDir::ZD_ERROR_INVALID_CALL;
|
|
|
|
char szFullPath[CCryPak::g_nMaxPath];
|
|
const char*pPath = AdjustPath (szRelativePath, szFullPath);
|
|
if (!pPath)
|
|
return ZipDir::ZD_ERROR_INVALID_PATH;
|
|
return m_pCache->UpdateFile(pPath, pUncompressed, nSize, nCompressionMethod, nCompressionLevel);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// deletes the file from the archive
|
|
int CryArchiveRW::RemoveFile (const char* szRelativePath)
|
|
{
|
|
if (m_nFlags & FLAGS_READ_ONLY)
|
|
return ZipDir::ZD_ERROR_INVALID_CALL;
|
|
|
|
char szFullPath[CCryPak::g_nMaxPath];
|
|
const char*pPath = AdjustPath (szRelativePath, szFullPath);
|
|
if (!pPath)
|
|
return ZipDir::ZD_ERROR_INVALID_PATH;
|
|
return m_pCache->RemoveFile (pPath);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// deletes the directory, with all its descendants (files and subdirs)
|
|
int CryArchiveRW::RemoveDir (const char* szRelativePath)
|
|
{
|
|
if (m_nFlags & FLAGS_READ_ONLY)
|
|
return ZipDir::ZD_ERROR_INVALID_CALL;
|
|
|
|
char szFullPath[CCryPak::g_nMaxPath];
|
|
const char*pPath = AdjustPath (szRelativePath, szFullPath);
|
|
if (!pPath)
|
|
return ZipDir::ZD_ERROR_INVALID_PATH;
|
|
return m_pCache->RemoveDir(pPath);
|
|
}
|
|
|
|
int CryArchiveRW::RemoveAll()
|
|
{
|
|
return m_pCache->RemoveAll();
|
|
|
|
}
|
|
int CryArchive::RemoveAll()
|
|
{
|
|
return ZipDir::ZD_ERROR_INVALID_CALL;
|
|
|
|
}
|
|
|
|
void CCryPak::Register (ICryArchive* pArchive)
|
|
{
|
|
ArchiveArray::iterator it = std::lower_bound (m_arrArchives.begin(), m_arrArchives.end(), pArchive, CryArchiveSortByName());
|
|
m_arrArchives.insert (it, pArchive);
|
|
}
|
|
|
|
void CCryPak::Unregister (ICryArchive* pArchive)
|
|
{
|
|
ArchiveArray::iterator it;
|
|
if (m_arrArchives.size() < 16)
|
|
{
|
|
// for small array sizes, we'll use linear search
|
|
it = std::find (m_arrArchives.begin(), m_arrArchives.end(), pArchive);
|
|
}
|
|
else
|
|
it = std::lower_bound (m_arrArchives.begin(), m_arrArchives.end(), pArchive, CryArchiveSortByName());
|
|
|
|
if (it != m_arrArchives.end() && *it == pArchive)
|
|
m_arrArchives.erase (it);
|
|
else
|
|
assert (0); // unregistration of unregistered archive
|
|
}
|
|
|
|
ICryArchive* CCryPak::FindArchive (const char* szFullPath)
|
|
{
|
|
ArchiveArray::iterator it = std::lower_bound (m_arrArchives.begin(), m_arrArchives.end(), szFullPath, CryArchiveSortByName());
|
|
if (it != m_arrArchives.end() && !stricmp(szFullPath, (*it)->GetFullPath()))
|
|
return *it;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate)
|
|
// returns one of the Z_* errors (Z_OK upon success)
|
|
// MT-safe
|
|
int CCryPak::RawCompress (const void* pUncompressed, unsigned long* pDestSize, void* pCompressed, unsigned long nSrcSize, int nLevel)
|
|
{
|
|
return ZipDir::ZipRawCompress(g_pBigHeap, pUncompressed, pDestSize, pCompressed, nSrcSize, nLevel);
|
|
}
|
|
|
|
// Uncompresses raw (without wrapping) data that is compressed with method 8 (deflated) in the Zip file
|
|
// returns one of the Z_* errors (Z_OK upon success)
|
|
// This function just mimics the standard uncompress (with modification taken from unzReadCurrentFile)
|
|
// with 2 differences: there are no 16-bit checks, and
|
|
// it initializes the inflation to start without waiting for compression method byte, as this is the
|
|
// way it's stored into zip file
|
|
int CCryPak::RawUncompress (void* pUncompressed, unsigned long* pDestSize, const void* pCompressed, unsigned long nSrcSize)
|
|
{
|
|
return ZipDir::ZipRawUncompress(g_pBigHeap, pUncompressed, pDestSize, pCompressed, nSrcSize);
|
|
}
|
|
|
|
|
|
// returns the current game directory, with trailing slash (or empty string if it's right in MasterCD)
|
|
// this is used to support Resource Compiler which doesn't have access to this interface:
|
|
// in case all the contents is located in a subdirectory of MasterCD, this string is the subdirectory name with slash
|
|
/*const char* CCryPak::GetGameDir()
|
|
{
|
|
return m_strPrepend.c_str();
|
|
}
|
|
*/
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CCryPak::RecordFileOpen( bool bEnable )
|
|
{
|
|
m_bRememberOpenedFiles = bEnable;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CCryPak::EnumerateRecordedFiles( RecordedFilesEnumCallback enumCallback )
|
|
{
|
|
assert( enumCallback );
|
|
for (RecordedFilesSet::const_iterator it = m_recordedFilesSet.begin(); it != m_recordedFilesSet.end(); ++it)
|
|
{
|
|
enumCallback( (*it).c_str() );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CCryPak::RecordFile( const char *szFilename )
|
|
{
|
|
if (m_bRememberOpenedFiles)
|
|
{
|
|
m_recordedFilesSet.insert( szFilename );
|
|
}
|
|
}
|
|
|
|
|
|
void CCryPak::OnMissingFile (const char* szPath)
|
|
{
|
|
AUTO_LOCK(m_csMain);
|
|
if (m_pPakVars->nLogMissingFiles)
|
|
{
|
|
std::pair<MissingFileMap::iterator, bool> insertion = m_mapMissingFiles.insert (MissingFileMap::value_type(szPath,1));
|
|
if (m_pPakVars->nLogMissingFiles >= 2 && (insertion.second || m_pPakVars->nLogMissingFiles >= 3))
|
|
{
|
|
// insertion occured
|
|
char szFileName[64];
|
|
sprintf (szFileName, "MissingFiles%d.log", m_pPakVars->nLogMissingFiles);
|
|
FILE* f = fopen (szFileName, "at");
|
|
if (f)
|
|
{
|
|
fprintf (f, "%s\n", szPath);
|
|
fclose(f);
|
|
}
|
|
}
|
|
else
|
|
++insertion.first->second; // increase the count of missing files
|
|
}
|
|
}
|
|
|
|
static char* cry_strdup(const char* szSource)
|
|
{
|
|
size_t len = strlen(szSource);
|
|
char* szResult = (char*)malloc(len+1);
|
|
memcpy (szResult, szSource, len+1);
|
|
return szResult;
|
|
}
|
|
|
|
|
|
ICryPak::PakInfo* CCryPak::GetPakInfo()
|
|
{
|
|
AUTO_LOCK(m_csZips);
|
|
PakInfo* pResult = (PakInfo*)malloc (sizeof(PakInfo) + sizeof(PakInfo::Pak)*m_arrZips.size());
|
|
pResult->numOpenPaks = m_arrZips.size();
|
|
for (unsigned i = 0; i < m_arrZips.size(); ++i)
|
|
{
|
|
pResult->arrPaks[i].szBindRoot = cry_strdup(m_arrZips[i].strBindRoot.c_str());
|
|
pResult->arrPaks[i].szFilePath = cry_strdup(m_arrZips[i].GetFullPath());
|
|
pResult->arrPaks[i].nUsedMem = m_arrZips[i].sizeofThis();
|
|
}
|
|
return pResult;
|
|
}
|
|
|
|
void CCryPak::FreePakInfo (PakInfo* pPakInfo)
|
|
{
|
|
for (unsigned i = 0; i < pPakInfo->numOpenPaks; ++i)
|
|
{
|
|
free((void*)pPakInfo->arrPaks[i].szBindRoot);
|
|
free((void*)pPakInfo->arrPaks[i].szFilePath);
|
|
}
|
|
free(pPakInfo);
|
|
}
|