|
|
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as
|
|
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// 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 Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
#include "stdmisc.h"
|
|
|
|
#ifndef NL_OS_WINDOWS
|
|
|
|
#include <errno.h>
|
|
|
|
#endif
|
|
|
|
#include "nel/misc/file.h"
|
|
|
|
#include "nel/misc/debug.h"
|
|
|
|
#include "nel/misc/big_file.h"
|
|
|
|
#include "nel/misc/path.h"
|
|
|
|
#include "nel/misc/command.h"
|
|
|
|
#include "nel/misc/sstring.h"
|
|
|
|
#include "nel/misc/xml_pack.h"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
#define NLMISC_DONE_FILE_OPENED 40
|
|
|
|
|
|
|
|
namespace NLMISC
|
|
|
|
{
|
|
|
|
|
|
|
|
typedef std::list<uint64> TFileAccessTimes; // list of times at which a given file is opened for reading
|
|
|
|
typedef CHashMap<std::string,TFileAccessTimes> TFileAccessLog; // map from file name to read access times
|
|
|
|
typedef NLMISC::CSynchronized<TFileAccessLog> TSynchronizedFileAccessLog;
|
|
|
|
|
|
|
|
static TSynchronizedFileAccessLog IFileAccessLog("IFileAccessLog");
|
|
|
|
static bool IFileAccessLoggingEnabled= false;
|
|
|
|
static uint64 IFileAccessLogStartTime= 0;
|
|
|
|
|
|
|
|
uint32 CIFile::_NbBytesSerialized = 0;
|
|
|
|
uint32 CIFile::_NbBytesLoaded = 0;
|
|
|
|
uint32 CIFile::_ReadFromFile = 0;
|
|
|
|
uint32 CIFile::_ReadingFromFile = 0;
|
|
|
|
uint32 CIFile::_FileOpened = 0;
|
|
|
|
uint32 CIFile::_FileRead = 0;
|
|
|
|
CSynchronized<std::deque<std::string> > CIFile::_OpenedFiles("");
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
CIFile::CIFile() : IStream(true)
|
|
|
|
{
|
|
|
|
_F = NULL;
|
|
|
|
_Cache = NULL;
|
|
|
|
_ReadPos = 0;
|
|
|
|
_FileSize = 0;
|
|
|
|
_BigFileOffset = 0;
|
|
|
|
_IsInBigFile = false;
|
|
|
|
_IsInXMLPackFile = false;
|
|
|
|
_CacheFileOnOpen = false;
|
|
|
|
_IsAsyncLoading = false;
|
|
|
|
_AllowBNPCacheFileOnOpen= true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
CIFile::CIFile(const std::string &path, bool text) : IStream(true)
|
|
|
|
{
|
|
|
|
_F=NULL;
|
|
|
|
_Cache = NULL;
|
|
|
|
_ReadPos = 0;
|
|
|
|
_FileSize = 0;
|
|
|
|
_BigFileOffset = 0;
|
|
|
|
_IsInBigFile = false;
|
|
|
|
_IsInXMLPackFile = false;
|
|
|
|
_CacheFileOnOpen = false;
|
|
|
|
_IsAsyncLoading = false;
|
|
|
|
_AllowBNPCacheFileOnOpen= true;
|
|
|
|
open(path, text);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
CIFile::~CIFile()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::loadIntoCache()
|
|
|
|
{
|
|
|
|
const uint32 READPACKETSIZE = 64 * 1024;
|
|
|
|
const uint32 INTERPACKETSLEEP = 5;
|
|
|
|
|
|
|
|
_Cache = new uint8[_FileSize];
|
|
|
|
if(!_IsAsyncLoading)
|
|
|
|
{
|
|
|
|
_ReadingFromFile += _FileSize;
|
|
|
|
int read = (int)fread (_Cache, _FileSize, 1, _F);
|
|
|
|
_FileRead++;
|
|
|
|
_ReadingFromFile -= _FileSize;
|
|
|
|
_ReadFromFile += read * _FileSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uint index= 0;
|
|
|
|
while(index<_FileSize)
|
|
|
|
{
|
|
|
|
if( _NbBytesLoaded + (_FileSize-index) > READPACKETSIZE )
|
|
|
|
{
|
|
|
|
sint n= READPACKETSIZE-_NbBytesLoaded;
|
|
|
|
n= max(n, 1);
|
|
|
|
_ReadingFromFile += n;
|
|
|
|
int read = (int)fread (_Cache+index, n, 1, _F);
|
|
|
|
_FileRead++;
|
|
|
|
_ReadingFromFile -= n;
|
|
|
|
_ReadFromFile += read * n;
|
|
|
|
index+= n;
|
|
|
|
|
|
|
|
nlSleep (INTERPACKETSLEEP);
|
|
|
|
_NbBytesLoaded= 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uint n= _FileSize-index;
|
|
|
|
_ReadingFromFile += n;
|
|
|
|
int read = (int)fread (_Cache+index, n, 1, _F);
|
|
|
|
_FileRead++;
|
|
|
|
_ReadingFromFile -= n;
|
|
|
|
_ReadFromFile += read * n;
|
|
|
|
_NbBytesLoaded+= n;
|
|
|
|
index+= n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
bool CIFile::open(const std::string &path, bool text)
|
|
|
|
{
|
|
|
|
// Log opened files
|
|
|
|
{
|
|
|
|
CSynchronized<deque<string> >::CAccessor fileOpened(&_OpenedFiles);
|
|
|
|
fileOpened.value().push_front (path);
|
|
|
|
if (fileOpened.value().size () > NLMISC_DONE_FILE_OPENED)
|
|
|
|
fileOpened.value().resize (NLMISC_DONE_FILE_OPENED);
|
|
|
|
_FileOpened++;
|
|
|
|
}
|
|
|
|
|
|
|
|
close();
|
|
|
|
|
|
|
|
// can't open empty filename
|
|
|
|
if(path.empty ())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// IFile Access Log management
|
|
|
|
if (IFileAccessLoggingEnabled)
|
|
|
|
{
|
|
|
|
// get the current time
|
|
|
|
uint64 timeNow = NLMISC::CTime::getPerformanceTime();
|
|
|
|
|
|
|
|
// get a handle for the container
|
|
|
|
TSynchronizedFileAccessLog::CAccessor synchronizedFileAccesLog(&IFileAccessLog);
|
|
|
|
TFileAccessLog& fileAccessLog= synchronizedFileAccesLog.value();
|
|
|
|
|
|
|
|
// add the current time to the container entry for the given path (creating a new container entry if required)
|
|
|
|
fileAccessLog[path].push_back(timeNow);
|
|
|
|
}
|
|
|
|
|
|
|
|
char mode[3];
|
|
|
|
mode[0] = 'r';
|
|
|
|
mode[1] = 'b'; // No more reading in text mode
|
|
|
|
mode[2] = '\0';
|
|
|
|
|
|
|
|
_FileName = path;
|
|
|
|
_ReadPos = 0;
|
|
|
|
|
|
|
|
// Bigfile or xml pack access requested ?
|
|
|
|
string::size_type pos;
|
|
|
|
if ((pos = path.find('@')) != string::npos)
|
|
|
|
{
|
|
|
|
// check for a double @ to identify XML pack file
|
|
|
|
if (pos+1 < path.size() && path[pos+1] == '@')
|
|
|
|
{
|
|
|
|
// xml pack file
|
|
|
|
_IsInXMLPackFile = true;
|
|
|
|
|
|
|
|
if(_AllowBNPCacheFileOnOpen)
|
|
|
|
{
|
|
|
|
_F = CXMLPack::getInstance().getFile(path, _FileSize, _BigFileOffset, _CacheFileOnOpen, _AlwaysOpened);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool dummy;
|
|
|
|
_F = CXMLPack::getInstance().getFile (path, _FileSize, _BigFileOffset, dummy, _AlwaysOpened);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// bnp file
|
|
|
|
_IsInBigFile = true;
|
|
|
|
if(_AllowBNPCacheFileOnOpen)
|
|
|
|
{
|
|
|
|
_F = CBigFile::getInstance().getFile (path, _FileSize, _BigFileOffset, _CacheFileOnOpen, _AlwaysOpened);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool dummy;
|
|
|
|
_F = CBigFile::getInstance().getFile (path, _FileSize, _BigFileOffset, dummy, _AlwaysOpened);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(_F != NULL)
|
|
|
|
{
|
|
|
|
// Start to load the bigfile or xml file at the file offset.
|
|
|
|
nlfseek64 (_F, _BigFileOffset, SEEK_SET);
|
|
|
|
|
|
|
|
// Load into cache ?
|
|
|
|
if (_CacheFileOnOpen)
|
|
|
|
{
|
|
|
|
// load file in the cache
|
|
|
|
loadIntoCache();
|
|
|
|
|
|
|
|
if (!_AlwaysOpened)
|
|
|
|
{
|
|
|
|
fclose (_F);
|
|
|
|
_F = NULL;
|
|
|
|
}
|
|
|
|
return (_Cache != NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_IsInBigFile = false;
|
|
|
|
_IsInXMLPackFile = false;
|
|
|
|
_BigFileOffset = 0;
|
|
|
|
_AlwaysOpened = false;
|
|
|
|
_F = fopen (path.c_str(), mode);
|
|
|
|
if (_F != NULL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
THIS CODE REPLACED BY SADGE BECAUSE SOMETIMES
|
|
|
|
ftell() RETRUNS 0 FOR NO GOOD REASON - LEADING TO CLIENT CRASH
|
|
|
|
|
|
|
|
nlfseek64 (_F, 0, SEEK_END);
|
|
|
|
_FileSize = ftell(_F);
|
|
|
|
nlfseek64 (_F, 0, SEEK_SET);
|
|
|
|
nlassert(_FileSize==filelength(fileno(_F)));
|
|
|
|
|
|
|
|
THE FOLLOWING WORKS BUT IS NOT PORTABLE
|
|
|
|
_FileSize=filelength(fileno(_F));
|
|
|
|
*/
|
|
|
|
_FileSize=CFile::getFileSize (_F);
|
|
|
|
if (_FileSize == 0)
|
|
|
|
{
|
|
|
|
nlwarning ("FILE: Size of file '%s' is 0", path.c_str());
|
|
|
|
fclose (_F);
|
|
|
|
_F = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nlwarning("Failed to open file '%s', error %u : %s", path.c_str(), errno, strerror(errno));
|
|
|
|
_FileSize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((_CacheFileOnOpen) && (_F != NULL))
|
|
|
|
{
|
|
|
|
// load file in the cache
|
|
|
|
loadIntoCache();
|
|
|
|
|
|
|
|
fclose (_F);
|
|
|
|
_F = NULL;
|
|
|
|
return (_Cache != NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (_F != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::setCacheFileOnOpen (bool newState)
|
|
|
|
{
|
|
|
|
_CacheFileOnOpen = newState;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::setAsyncLoading (bool newState)
|
|
|
|
{
|
|
|
|
_IsAsyncLoading = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::close()
|
|
|
|
{
|
|
|
|
if (_CacheFileOnOpen)
|
|
|
|
{
|
|
|
|
if (_Cache)
|
|
|
|
{
|
|
|
|
delete[] _Cache;
|
|
|
|
_Cache = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_IsInBigFile || _IsInXMLPackFile)
|
|
|
|
{
|
|
|
|
if (!_AlwaysOpened)
|
|
|
|
{
|
|
|
|
if (_F)
|
|
|
|
{
|
|
|
|
fclose (_F);
|
|
|
|
_F = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_F)
|
|
|
|
{
|
|
|
|
fclose (_F);
|
|
|
|
_F = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nlassert(_Cache == NULL);
|
|
|
|
resetPtrTable();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::flush()
|
|
|
|
{
|
|
|
|
if (_CacheFileOnOpen)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_F)
|
|
|
|
{
|
|
|
|
fflush (_F);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::getline (char *buffer, uint32 bufferSize)
|
|
|
|
{
|
|
|
|
if (bufferSize == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
uint read = 0;
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
if (read == bufferSize - 1)
|
|
|
|
{
|
|
|
|
*buffer = '\0';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// read one byte
|
|
|
|
serialBuffer ((uint8 *)buffer, 1);
|
|
|
|
}
|
|
|
|
catch (EFile &)
|
|
|
|
{
|
|
|
|
*buffer = '\0';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*buffer == '\n')
|
|
|
|
{
|
|
|
|
*buffer = '\0';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip '\r' char
|
|
|
|
if (*buffer != '\r')
|
|
|
|
{
|
|
|
|
buffer++;
|
|
|
|
read++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
bool CIFile::eof ()
|
|
|
|
{
|
|
|
|
return _ReadPos >= (sint32)_FileSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::serialBuffer(uint8 *buf, uint len) throw(EReadError)
|
|
|
|
{
|
|
|
|
if (len == 0)
|
|
|
|
return;
|
|
|
|
// Check the read pos
|
|
|
|
if ((_ReadPos < 0) || ((_ReadPos+len) > _FileSize))
|
|
|
|
throw EReadError (_FileName);
|
|
|
|
if ((_CacheFileOnOpen) && (_Cache == NULL))
|
|
|
|
throw EFileNotOpened (_FileName);
|
|
|
|
if ((!_CacheFileOnOpen) && (_F == NULL))
|
|
|
|
throw EFileNotOpened (_FileName);
|
|
|
|
|
|
|
|
if (_IsAsyncLoading)
|
|
|
|
{
|
|
|
|
_NbBytesSerialized += len;
|
|
|
|
if (_NbBytesSerialized > 64 * 1024)
|
|
|
|
{
|
|
|
|
// give up time slice
|
|
|
|
nlSleep (0);
|
|
|
|
_NbBytesSerialized = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_CacheFileOnOpen)
|
|
|
|
{
|
|
|
|
memcpy (buf, _Cache + _ReadPos, len);
|
|
|
|
_ReadPos += len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int read;
|
|
|
|
_ReadingFromFile += len;
|
|
|
|
read=(int)fread(buf, len, 1, _F);
|
|
|
|
_FileRead++;
|
|
|
|
_ReadingFromFile -= len;
|
|
|
|
_ReadFromFile += /*read **/ len;
|
|
|
|
if (read != 1 /*< (int)len*/)
|
|
|
|
throw EReadError(_FileName);
|
|
|
|
_ReadPos += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::serialBit(bool &bit) throw(EReadError)
|
|
|
|
{
|
|
|
|
// Simple for now.
|
|
|
|
uint8 v=bit;
|
|
|
|
serialBuffer(&v, 1);
|
|
|
|
bit=(v!=0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
bool CIFile::seek (sint32 offset, IStream::TSeekOrigin origin) const throw(EStream)
|
|
|
|
{
|
|
|
|
if ((_CacheFileOnOpen) && (_Cache == NULL))
|
|
|
|
return false;
|
|
|
|
if ((!_CacheFileOnOpen) && (_F == NULL))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (origin)
|
|
|
|
{
|
|
|
|
case IStream::begin:
|
|
|
|
_ReadPos = offset;
|
|
|
|
break;
|
|
|
|
case IStream::current:
|
|
|
|
_ReadPos = _ReadPos + offset;
|
|
|
|
break;
|
|
|
|
case IStream::end:
|
|
|
|
_ReadPos = _FileSize + offset;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nlstop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_CacheFileOnOpen)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// seek in the file. NB: if not in bigfile, _BigFileOffset==0.
|
|
|
|
if (nlfseek64(_F, _BigFileOffset+_ReadPos, SEEK_SET) != 0)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
sint32 CIFile::getPos () const throw(EStream)
|
|
|
|
{
|
|
|
|
return _ReadPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
std::string CIFile::getStreamName() const
|
|
|
|
{
|
|
|
|
return _FileName;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::allowBNPCacheFileOnOpen(bool newState)
|
|
|
|
{
|
|
|
|
_AllowBNPCacheFileOnOpen= newState;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::dump (std::vector<std::string> &result)
|
|
|
|
{
|
|
|
|
CSynchronized<deque<string> >::CAccessor acces(&_OpenedFiles);
|
|
|
|
|
|
|
|
const deque<string> &openedFile = acces.value();
|
|
|
|
|
|
|
|
// Resize the destination array
|
|
|
|
result.clear ();
|
|
|
|
result.reserve (openedFile.size ());
|
|
|
|
|
|
|
|
// Add the waiting strings
|
|
|
|
deque<string>::const_reverse_iterator ite = openedFile.rbegin ();
|
|
|
|
while (ite != openedFile.rend ())
|
|
|
|
{
|
|
|
|
result.push_back (*ite);
|
|
|
|
|
|
|
|
// Next task
|
|
|
|
ite++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void CIFile::clearDump ()
|
|
|
|
{
|
|
|
|
CSynchronized<deque<string> >::CAccessor acces(&_OpenedFiles);
|
|
|
|
acces.value().clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
uint CIFile::getDbgStreamSize() const
|
|
|
|
{
|
|
|
|
return getFileSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
// ======================================================================================================
|
|
|
|
// ======================================================================================================
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
COFile::COFile() : IStream(false)
|
|
|
|
{
|
|
|
|
_F=NULL;
|
|
|
|
_FileName = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
COFile::COFile(const std::string &path, bool append, bool text, bool useTempFile) : IStream(false)
|
|
|
|
{
|
|
|
|
_F=NULL;
|
|
|
|
open(path, append, text, useTempFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
COFile::~COFile()
|
|
|
|
{
|
|
|
|
internalClose(false);
|
|
|
|
}
|
|
|
|
// ======================================================================================================
|
|
|
|
bool COFile::open(const std::string &path, bool append, bool text, bool useTempFile)
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
|
|
|
|
// can't open empty filename
|
|
|
|
if(path.empty ())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
_FileName = path;
|
|
|
|
_TempFileName.clear();
|
|
|
|
|
|
|
|
char mode[3];
|
|
|
|
mode[0] = (append)?'a':'w';
|
|
|
|
// ACE: NEVER SAVE IN TEXT MODE!!! mode[1] = (text)?'\0':'b';
|
|
|
|
mode[1] = 'b';
|
|
|
|
mode[2] = '\0';
|
|
|
|
|
|
|
|
string fileToOpen = path;
|
|
|
|
if (useTempFile)
|
|
|
|
{
|
|
|
|
CFile::getTemporaryOutputFilename (path, _TempFileName);
|
|
|
|
fileToOpen = _TempFileName;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if appending to file and using a temporary file, copycat temporary file from original...
|
|
|
|
if (append && useTempFile && CFile::fileExists(_FileName))
|
|
|
|
{
|
|
|
|
// open fails if can't copy original content
|
|
|
|
if (!CFile::copyFile(_TempFileName.c_str(), _FileName.c_str()))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_F=fopen(fileToOpen.c_str(), mode);
|
|
|
|
|
|
|
|
return _F!=NULL;
|
|
|
|
}
|
|
|
|
// ======================================================================================================
|
|
|
|
void COFile::close()
|
|
|
|
{
|
|
|
|
internalClose(true);
|
|
|
|
}
|
|
|
|
// ======================================================================================================
|
|
|
|
void COFile::internalClose(bool success)
|
|
|
|
{
|
|
|
|
if(_F)
|
|
|
|
{
|
|
|
|
fclose(_F);
|
|
|
|
|
|
|
|
// Temporary filename ?
|
|
|
|
if (!_TempFileName.empty())
|
|
|
|
{
|
|
|
|
// Delete old
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
// Bug under windows, sometimes the file is not deleted
|
|
|
|
uint retry = 1000;
|
|
|
|
while (--retry)
|
|
|
|
{
|
|
|
|
if (CFile::fileExists(_FileName))
|
|
|
|
CFile::deleteFile (_FileName);
|
|
|
|
|
|
|
|
if (CFile::moveFile (_FileName.c_str(), _TempFileName.c_str()))
|
|
|
|
break;
|
|
|
|
nlSleep (0);
|
|
|
|
}
|
|
|
|
if (!retry)
|
|
|
|
throw ERenameError (_FileName, _TempFileName);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
CFile::deleteFile (_TempFileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
_F=NULL;
|
|
|
|
}
|
|
|
|
resetPtrTable();
|
|
|
|
}
|
|
|
|
// ======================================================================================================
|
|
|
|
void COFile::flush()
|
|
|
|
{
|
|
|
|
if(_F)
|
|
|
|
{
|
|
|
|
fflush(_F);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
void COFile::serialBuffer(uint8 *buf, uint len) throw(EWriteError)
|
|
|
|
{
|
|
|
|
if(!_F)
|
|
|
|
throw EFileNotOpened(_FileName);
|
|
|
|
if(fwrite(buf, len, 1, _F) != 1)
|
|
|
|
// if(fwrite(buf, 1, len, _F) != len)
|
|
|
|
{
|
|
|
|
if (ferror(_F) && errno == 28 /*ENOSPC*/)
|
|
|
|
{
|
|
|
|
throw EDiskFullError(_FileName);
|
|
|
|
}
|
|
|
|
throw EWriteError(_FileName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ======================================================================================================
|
|
|
|
void COFile::serialBit(bool &bit) throw(EWriteError)
|
|
|
|
{
|
|
|
|
// Simple for now.
|
|
|
|
uint8 v=bit;
|
|
|
|
serialBuffer(&v, 1);
|
|
|
|
}
|
|
|
|
// ======================================================================================================
|
|
|
|
bool COFile::seek (sint32 offset, IStream::TSeekOrigin origin) const throw(EStream)
|
|
|
|
{
|
|
|
|
if (_F)
|
|
|
|
{
|
|
|
|
int origin_c = SEEK_SET;
|
|
|
|
switch (origin)
|
|
|
|
{
|
|
|
|
case IStream::begin:
|
|
|
|
origin_c=SEEK_SET;
|
|
|
|
break;
|
|
|
|
case IStream::current:
|
|
|
|
origin_c=SEEK_CUR;
|
|
|
|
break;
|
|
|
|
case IStream::end:
|
|
|
|
origin_c=SEEK_END;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nlstop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nlfseek64 (_F, offset, origin_c)!=0)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// ======================================================================================================
|
|
|
|
sint32 COFile::getPos () const throw(EStream)
|
|
|
|
{
|
|
|
|
if (_F)
|
|
|
|
{
|
|
|
|
return ftell (_F);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
std::string COFile::getStreamName() const
|
|
|
|
{
|
|
|
|
return _FileName;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
// ======================================================================================================
|
|
|
|
// ======================================================================================================
|
|
|
|
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
NLMISC_CATEGORISED_COMMAND(nel, iFileAccessLogStart, "Start file access logging", "")
|
|
|
|
{
|
|
|
|
if (!args.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
IFileAccessLoggingEnabled= true;
|
|
|
|
if (IFileAccessLogStartTime==0)
|
|
|
|
{
|
|
|
|
uint64 timeNow = NLMISC::CTime::getPerformanceTime();
|
|
|
|
IFileAccessLogStartTime= timeNow;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
NLMISC_CATEGORISED_COMMAND(nel, iFileAccessLogStop, "Stop file access logging", "")
|
|
|
|
{
|
|
|
|
if (!args.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
IFileAccessLoggingEnabled= false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
NLMISC_CATEGORISED_COMMAND(nel, iFileAccessLogClear, "Clear file access logs", "")
|
|
|
|
{
|
|
|
|
if (!args.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
TSynchronizedFileAccessLog::CAccessor(&IFileAccessLog).value().clear();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================================================================================================
|
|
|
|
NLMISC_CATEGORISED_COMMAND(nel, iFileAccessLogDisplay, "Display file access logs", "")
|
|
|
|
{
|
|
|
|
if (!args.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
log.displayNL("-- FILE ACCESS LOG BEGIN --");
|
|
|
|
|
|
|
|
TSynchronizedFileAccessLog::CAccessor fileAccesLog(&IFileAccessLog);
|
|
|
|
TFileAccessLog::const_iterator it= fileAccesLog.value().begin();
|
|
|
|
TFileAccessLog::const_iterator itEnd= fileAccesLog.value().end();
|
|
|
|
uint32 count=0;
|
|
|
|
while (it!=itEnd)
|
|
|
|
{
|
|
|
|
uint32 numTimes= (uint32)it->second.size();
|
|
|
|
CSString fileName= it->first;
|
|
|
|
if (fileName.contains("@"))
|
|
|
|
{
|
|
|
|
log.display("%d,%s,%s,",numTimes,fileName.splitTo("@").c_str(),fileName.splitFrom("@").c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
log.display("%d,,%s,",numTimes,fileName.c_str());
|
|
|
|
}
|
|
|
|
TFileAccessTimes::const_iterator atIt= it->second.begin();
|
|
|
|
TFileAccessTimes::const_iterator atItEnd=it->second.end();
|
|
|
|
while (atIt!=atItEnd)
|
|
|
|
{
|
|
|
|
uint64 delta= (*atIt-IFileAccessLogStartTime);
|
|
|
|
log.display("%"NL_I64"u,",delta);
|
|
|
|
++atIt;
|
|
|
|
}
|
|
|
|
log.displayNL("");
|
|
|
|
++count;
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
log.displayNL("-- FILE ACCESS LOG END (%d Unique Files Accessed) --",count);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|