// Ryzom - MMORPG Framework
// 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 .
#include "db_reference_file.h"
#include "pds_table_buffer.h"
#include
using namespace NLMISC;
using namespace std;
/*
* Constructor
*/
CDBReferenceFile::CDBReferenceFile()
{
clear();
}
/*
* Destructor
*/
CDBReferenceFile::~CDBReferenceFile()
{
clear();
}
/*
* Clear initial setup
*/
void CDBReferenceFile::clear()
{
_Init = false;
close();
_Name.clear();
_Path.clear();
_Header.BaseIndex = 0;
_Header.EndIndex = 0;
_Header.OverIndex = 0;
_Header.RowSize = 0;
_Header.FullRowSize = 0;
_Header.Timestamp = 0;
_Mode = Read;
_DataStart = 0;
}
/*
* close file
*/
void CDBReferenceFile::close()
{
if (_File != NULL)
{
PDS_LOG_DEBUG(1)("CDBReferenceFile::clear(): closing file '%s%s' in %s mode", _Path.c_str(), _Name.c_str(), (_Mode == Read ? "Read" : "Update"));
// in update mode, postwrite to validate file
if (_Mode == Update)
{
postwrite();
}
}
CMixedStreamFile::close();
}
/*
* Setup file name and path
*/
void CDBReferenceFile::setup(const string& name, const string& path, uint32 baseIndex, uint32 overIndex, uint32 rowSize)
{
clear();
uint32 tableId;
uint32 refFile;
nlassert(isRefFile(name, tableId, refFile));
_Name = name;
_Path = path;
_Header.BaseIndex = baseIndex;
_Header.EndIndex = baseIndex;
_Header.OverIndex = overIndex;
_Header.RowSize = rowSize;
_Header.FullRowSize = rowSize + getRowHeaderSize();
_Header.Timestamp = CTableBuffer::getCommonStamp(); //CStampHandler::getStamp();
_Init = true;
}
/*
* Builds an empty file
*/
bool CDBReferenceFile::buildEmptyRef()
{
string filepath = _Path+_Name;
if (_File != NULL)
{
nlwarning("CDBReferenceFile::buildEmptyRef(): failed, file '%s' already open", filepath.c_str());
return false;
}
// check file doesn't exist yet
if (CFile::fileExists(filepath))
{
nlwarning("CDBReferenceFile::buildEmptyRef(): failed, file '%s' already exists", filepath.c_str());
return false;
}
if (!prewrite())
{
nlwarning("CDBReferenceFile::buildEmptyRef(): failed, cannot prewrite file '%s'", filepath.c_str());
close();
return false;
}
close();
return true;
}
/*
* Prewrite reference file.
* At least, read file header to known the base and index index in file
*/
bool CDBReferenceFile::prewrite(bool failIfNotExist)
{
// check file already open
if (_File != NULL)
return true;
string filepath = _Path+_Name;
// not?
// check file exists
if (!CFile::fileExists(filepath))
{
if (failIfNotExist)
{
nlwarning("CDBReferenceFile::prewrite(): failed, file '%s' does not exist", filepath.c_str());
return false;
}
// open file
if (!open(filepath.c_str(), "wb"))
{
nlwarning("CDBReferenceFile::prewrite(): failed, cannot open file '%s' for write", filepath.c_str());
return false;
}
// write header, set to output mode
setInOut(false);
}
else
{
// open file
if (!open(filepath.c_str(), "r+b"))
{
nlwarning("CDBReferenceFile::prewrite(): failed, cannot open file '%s' for read", filepath.c_str());
return false;
}
if (fseek(_File, 0, SEEK_SET) != 0)
{
nlwarning("CDBReferenceFile::prewrite(): failed, cannot seek start of '%s' to read header", filepath.c_str());
return false;
}
// read header, set to input mode
setInOut(true);
}
// serial header
try
{
if (!serialHeader())
{
nlwarning("CDBReferenceFile::prewrite(): failed, cannot read file '%s' header", filepath.c_str());
return false;
}
}
catch (const Exception& e)
{
nlwarning("CDBReferenceFile::prewrite(): failed, cannot read file '%s' header, exception '%s'", filepath.c_str(), e.what());
return false;
}
// write data, set to output mode
setInOut(false);
_Mode = Update;
//
PDS_LOG_DEBUG(1)("CDBReferenceFile::prewrite(): opened file '%s' in Update mode", filepath.c_str());
return true;
}
/*
* Postwrite reference file
* Mark file as valid, close file, flush anything still alive...
*/
bool CDBReferenceFile::postwrite()
{
if (_File == NULL)
return true;
string filepath = _Path+_Name;
if (fseek(_File, 0, SEEK_SET) != 0)
{
nlwarning("CDBReferenceFile::postwrite(): failed, cannot seek start of '%s' to read header", filepath.c_str());
return false;
}
// serial header
try
{
if (!serialHeader())
{
nlwarning("CDBReferenceFile::postwrite(): failed, cannot read file '%s' header", filepath.c_str());
return false;
}
}
catch (const Exception& e)
{
nlwarning("CDBReferenceFile::postwrite(): failed, cannot read file '%s' header, exception '%s'", filepath.c_str(), e.what());
return false;
}
return true;
}
/*
* Update Start/End Delta Ids
*/
bool CDBReferenceFile::updateDeltaIds(uint32 startId, uint32 endId)
{
if (_Header.StartDeltaId == 0 && _Header.EndDeltaId == 0)
{
_Header.StartDeltaId = startId;
_Header.EndDeltaId = endId;
return true;
}
if (_Header.EndDeltaId != startId && _Header.EndDeltaId != startId-1)
{
string filepath = _Path+_Name;
nlwarning("CDBReferenceFile::updateDeltaIds(): non consecutive delta ids, file '%s' end=%d, update start=%d", filepath.c_str(), _Header.EndDeltaId, startId);
return false;
}
_Header.EndDeltaId = endId;
return true;
}
/*
* Get Start/End Delta Ids
*/
void CDBReferenceFile::getUpdateDeltaIds(uint32& startId, uint32& endId)
{
startId = _Header.StartDeltaId;
endId = _Header.EndDeltaId;
}
/*
* Update a row in the reference file
* \param index is the absolute row index to update, not relative to file base index
* \param data is the data buffer to store in file
*/
bool CDBReferenceFile::update(uint32 index, const uint8* rowdata)
{
string filepath = _Path+_Name;
if (!prewrite())
{
nlwarning("CDBReferenceFile::update(): failed, failed to prewrite '%s'", filepath.c_str());
return false;
}
if (_Mode != Update)
{
nlwarning("CDBReferenceFile::update(): failed, file '%s' not opened in Update mode", filepath.c_str());
return false;
}
// check row belongs to file (not over OverIndex)
if (index >= _Header.OverIndex)
{
nlwarning("CDBReferenceFile::update(): failed, index '%d' is over file '%s' limit '%s'", index, filepath.c_str(), _Header.OverIndex);
return false;
}
// check row is not beyong physical file data
if (index >= _Header.EndIndex)
{
// not?
// increase file size by filling blank, fill row indices where needed)
// seek to end
if (fseek(_File, getSeekPos(_Header.EndIndex), SEEK_SET) != 0)
{
nlwarning("CDBReferenceFile::update(): failed, can't seek to end in file '%s'", filepath.c_str());
return false;
}
// allocate blank buffer
uint8* tempRowBuffer = new uint8[_Header.FullRowSize];
memset(tempRowBuffer, 0, _Header.FullRowSize);
// dump empty rows till we get to end index
while (_Header.EndIndex <= index)
{
// setup row index
*(uint32*)tempRowBuffer = index;
if (!writeBuffer(tempRowBuffer, _Header.FullRowSize))
{
nlwarning("CDBReferenceFile::update(): failed, can't increase file '%s' size", filepath.c_str());
delete tempRowBuffer;
return false;
}
++_Header.EndIndex;
}
}
// seek to row in file
if (fseek(_File, getSeekPos(index)+getRowHeaderSize(), SEEK_SET) != 0)
{
nlwarning("CDBReferenceFile::update(): failed, can't seek to index '%d' data in file '%s'", index, filepath.c_str());
return false;
}
// write data
if (!writeBuffer(rowdata, _Header.RowSize))
{
nlwarning("CDBReferenceFile::update(): failed, can't write index '%d' data in file '%s'", index, filepath.c_str());
return false;
}
return true;
}
/*
* Preload reference file.
* At least, read file header to known the base and index index in file
*/
bool CDBReferenceFile::preload()
{
// check file already open
if (_File != NULL)
return true;
// force read mode
_Mode = Read;
string filepath = _Path+_Name;
// file doesn't exist, do nothing
if (!CFile::fileExists(filepath))
return true;
// open file
if (!open(filepath.c_str(), "rb"))
{
nlwarning("CDBReferenceFile::preload(): failed, cannot open file '%s'", filepath.c_str());
return false;
}
//
PDS_LOG_DEBUG(1)("CDBReferenceFile::preload(): opened file '%s' in Read mode", filepath.c_str());
setInOut(true);
// serial header
try
{
if (!serialHeader())
{
nlwarning("CDBReferenceFile::preload(): failed, cannot read file '%s' header", filepath.c_str());
return false;
}
}
catch (const Exception& e)
{
nlwarning("CDBReferenceFile::preload(): failed, cannot read file '%s' header, exception '%s'", filepath.c_str(), e.what());
return false;
}
return true;
}
/*
* Read a row in the reference file
* \param index is the absolute row index to read, not relative to file base index
* \param data is the data buffer to store data read from file
*/
bool CDBReferenceFile::read(uint32 index, uint8* rowdata)
{
string filepath = _Path+_Name;
// preload will fail only if file exists and cannot be read
// preload returns true when everything ok or file doesn't exist
if (!preload())
{
nlwarning("CDBReferenceFile::read(): failed, failed to preload '%s'", filepath.c_str());
return false;
}
// check mode...
if (_Mode != Read)
{
nlwarning("CDBReferenceFile::read(): failed, file '%s' not opened in Read mode", filepath.c_str());
return false;
}
// check row belongs to file (not over OverIndex)
if (index >= _Header.OverIndex)
{
nlwarning("CDBReferenceFile::read(): failed, index '%d' is over file '%s' end '%d'", index, filepath.c_str(), _Header.OverIndex);
return false;
}
// check file opened or row is not beyond file end
if (_File == NULL || index >= _Header.EndIndex)
{
PDS_LOG_DEBUG(1)("CDBReferenceFile::read(): row '%d' is beyond file '%s' end '%d', row is empty", index, filepath.c_str(), _Header.EndIndex);
memset(rowdata, 0, _Header.RowSize);
return true;
}
// seek to row in file
if (fseek(_File, getSeekPos(index)+getRowHeaderSize(), SEEK_SET) != 0)
{
nlwarning("CDBReferenceFile::read(): failed, can't seek to index '%d' data in file '%s'", index, filepath.c_str());
return false;
}
// write data
if (!readBuffer(rowdata, _Header.RowSize))
{
nlwarning("CDBReferenceFile::read(): failed, can't read index '%d' data in file '%s'", index, filepath.c_str());
return false;
}
return true;
}
/*
* Serial file header
*/
bool CDBReferenceFile::serialHeader()
{
serialCheck(NELID("DbRf"));
uint version = serialVersion(0);
if (isReading())
{
// on reading, read header in a temp buffer
CRefHeader hdr;
serial(hdr);
// check header complies
if ((_Header.BaseIndex != 0 && hdr.BaseIndex != _Header.BaseIndex) || (isReading() && _Header.RowSize != 0 && hdr.RowSize != _Header.RowSize))
return false;
// compy temp to header
_Header = hdr;
}
else
{
serial(_Header);
}
serialCheck(NELID("Data"));
_DataStart = ftell(_File);
if (isReading())
{
// get file size to compute real EndIndex
uint32 filesize = CFile::getFileSize(_File);
uint32 numRows = (filesize-_DataStart) / _Header.FullRowSize;
// check exact number of rows in file...
if ((filesize-_DataStart) % _Header.FullRowSize != 0)
{
nlwarning("CDBReferenceFile::serialHeader(): failed, file doesn't contain an exact number of rows");
return false;
}
// compute exact end index
_Header.EndIndex = _Header.BaseIndex + numRows;
}
return true;
}