Files
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

2037 lines
51 KiB
C++

#include "StuffHeaders.hpp"
#include "Database.hpp"
#include <GameOS\ToolOS.hpp>
#include <GameOS\Network.hpp>
#include <mbstring.h>
#define __mbsrchr(s,c) (char*)_mbsrchr((const unsigned char*)(s),(c))
#if 1
#define DATABASE_LOG(string) LOG_BLOCK("Database::" string)
#else
#define DATABASE_LOG(string)
#endif
extern volatile int ProcessingError;
extern void _stdcall ExitGameOS();
//===========================================================================//
// File: Database.cpp //
// Project: GameOS //
// Contents: Database functionality //
//---------------------------------------------------------------------------//
// //
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 02/20/99 AG Created //
// //
//===========================================================================//
HGOSHEAP Stuff::g_DatabaseHeap = NULL;
bool(__cdecl *NoCDMessageFunction)() = NULL;
#define RATIO 0.95f
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
static DWORD GenerateHash(const char* name)
{
DWORD hash=0;
while (*name)
{
hash=(hash<<4) + (*name&15);
if (hash&0xf0000000)
hash=hash^(hash>>28);
name++;
}
return hash|0x80000000;
}
//
//###########################################################################
//############################## Record ###############################
//###########################################################################
//
class Stuff::Record
{
public:
Record(
const RecordHandle *handle,
DWORD record_hash,
BYTE name_length
);
Record(
const RecordHandle *handle,
DWORD record_hash,
BYTE name_length,
Record *record
);
Record(
__int64 last_modified,
DWORD data_length,
DWORD real_length,
DWORD offset,
WORD id,
BYTE name_length,
MemoryStream *stream
);
~Record()
{
Check_Object(this); Verify(!m_referenceCount);
if (m_deleteData && m_buffer) delete m_buffer;
}
void Save(
DatabaseHandle *handle,
MemoryStream *record_stream,
MemoryStream *data_stream
);
void SaveNoChange(
DatabaseHandle *handle,
MemoryStream *record_stream,
MemoryStream *data_stream
);
void LoadData(DatabaseHandle *handle);
void LoadDataNoDecompress(DatabaseHandle *handle);
void UnloadData();
void AbandonData();
void Unhook(const RecordHandle *handle);
__int64
m_lastModified; // Time record was last modifyed
BYTE
*m_buffer; // the buffer data has been uncompressed into
Record
*m_nextIDRecord, // offset to chain of records that share the same hash
*m_nextNameRecord; // offset to chain of records that share the same hash
DWORD
m_hash, // Hash value
m_realLength, // length of the compressed block - if zero then data is not compressed
m_dataLength,
m_dataOffset; // possibly compressed data
#if defined(_ARMOR)
DWORD m_referenceCount;
#endif
WORD
m_ID; // ID
bool
m_deleteData;
BYTE
m_nameLength;
char
m_name[1];
enum {
e_FixedLength = sizeof(__int64) + 3*sizeof(DWORD) + sizeof(WORD) + sizeof(BYTE)
};
void TestInstance() const
{}
};
//
//###########################################################################
//############################# Database ##############################
//###########################################################################
//
class Stuff::Database
{
public:
enum {
e_DataBlockSize=1021,
e_Tag = 'DBV#',
e_Version = 4,
e_FixedLength = 4*sizeof(DWORD) + 2*sizeof(WORD)
};
Database(DatabaseHandle *handle, DWORD content_version);
Database(DatabaseHandle *handle, DWORD content_version, HGOSFILE file);
Database(DatabaseHandle *handle, DWORD content_version, MemoryStream *stream);
~Database();
void LoadRecords(MemoryStream *stream, bool preload);
void Save();
void ScatterSave();
DatabaseHandle
*m_handle;
DWORD
m_tag, // Magic number to identity file
m_formatVersion, // Format Version number
m_contentVersion, // Content Version number
m_indexSize;
WORD
m_numberOfRecords, // Number of used records in database
m_nextRecordID; // Sequential index assigned in ADD
Stuff::Record
*m_idOffsets[e_DataBlockSize], // Offsets to DatabaseRecords ( sorted by index )
*m_nameOffsets[e_DataBlockSize]; // Offsets to DatabaseRecords ( sorted by HASH )
void TestInstance() const
{Verify(m_tag == e_Tag && m_formatVersion <= e_Version);}
static int
s_FilesOpened;
};
//
//###########################################################################
//############################## Record ###############################
//###########################################################################
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Record::Record(
const RecordHandle *handle,
DWORD record_hash,
BYTE name_length
)
{
Check_Pointer(this);
Check_Object(handle);
DatabaseHandle *db_handle = handle->m_databaseHandle;
Check_Object(db_handle);
Database *db = db_handle->m_dataBase;
Check_Object(db);
//
//------------------
// Set up the record
//------------------
//
m_ID = handle->m_ID;
Verify(m_ID <= db->m_numberOfRecords+1);
m_hash = record_hash;
m_nameLength = name_length;
m_dataLength = handle->m_length;
m_lastModified = gos_GetTimeDate();
#if defined(_ARMOR)
m_referenceCount = 0;
#endif
//
//----------------------------------------------------
// If there is data to write out, put it in the buffer
//----------------------------------------------------
//
if (m_dataLength>0)
{
m_buffer = new(db_handle->m_heap) BYTE[m_dataLength];
Check_Pointer(m_buffer);
memcpy(m_buffer, handle->m_data, m_dataLength);
m_deleteData = true;
}
else
{
m_deleteData = false;
m_buffer = NULL;
}
m_dataOffset = -1;
m_realLength = m_dataLength;
//
//------------------
// Store in database
//------------------
//
DWORD index=m_ID % Database::e_DataBlockSize;
m_nextIDRecord = db->m_idOffsets[index];
db->m_idOffsets[index] = this;
if(handle->m_name)
memcpy(m_name, handle->m_name, name_length+1);
else
*m_name = 0;
index = record_hash % Database::e_DataBlockSize;
m_nextNameRecord = db->m_nameOffsets[index];
db->m_nameOffsets[index] = this;
//
//----------------------------
// Increase the database count
//----------------------------
//
db->m_numberOfRecords++;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Record::Record(
const RecordHandle *handle,
DWORD record_hash,
BYTE name_length,
Record *record
)
{
Check_Pointer(this);
Check_Object(handle);
DatabaseHandle *db_handle = handle->m_databaseHandle;
Check_Object(db_handle);
Database *db = db_handle->m_dataBase;
Check_Object(db);
//
//------------------
// Set up the record
//------------------
//
m_ID = handle->m_ID;
Verify(m_ID == record->m_ID);
m_hash = record_hash;
m_nameLength = name_length;
m_dataLength = handle->m_length;
m_lastModified = gos_GetTimeDate();
#if defined(_ARMOR)
m_referenceCount = 0;
#endif
//
//------------------------------------------------------------------
// If there is data to write out, put it in the buffer. If the data
// hasn't changed, don't let the old record delete the memory
//------------------------------------------------------------------
//
if (m_dataLength>0)
{
if (record->m_buffer == handle->m_data)
{
Verify(record->m_dataLength == handle->m_length);
m_buffer = record->m_buffer;
Verify(!record->m_referenceCount);
m_deleteData = record->m_deleteData;
record->m_deleteData = false;
}
else
{
m_buffer = new(db_handle->m_heap) BYTE[m_dataLength];
Check_Pointer(m_buffer);
memcpy(m_buffer, handle->m_data, m_dataLength);
m_deleteData = true;
}
}
else
{
m_deleteData = false;
m_buffer = NULL;
}
m_dataOffset = -1;
m_realLength = m_dataLength;
//
//------------------
// Store in database
//------------------
//
DWORD index=m_ID % Database::e_DataBlockSize;
m_nextIDRecord = db->m_idOffsets[index];
db->m_idOffsets[index] = this;
if(handle->m_name)
memcpy(m_name, handle->m_name, name_length+1);
else
*m_name = 0;
index = record_hash % Database::e_DataBlockSize;
m_nextNameRecord = db->m_nameOffsets[index];
db->m_nameOffsets[index] = this;
//
//----------------------------
// Increase the database count
//----------------------------
//
db->m_numberOfRecords++;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Record::Record(
__int64 last_modified,
DWORD data_length,
DWORD real_length,
DWORD offset,
WORD id,
BYTE name_length,
MemoryStream *stream
)
{
Check_Pointer(this);
Check_Object(stream);
//
//------------------------------
// Read the data from the stream
//------------------------------
//
m_lastModified = last_modified;
m_dataLength = data_length;
m_realLength = real_length;
m_dataOffset = offset;
m_ID = id;
m_nameLength = name_length;
#if defined(_ARMOR)
m_referenceCount = 0;
#endif
if(m_nameLength > 0)
stream->ReadBytes(m_name, m_nameLength);
m_name[m_nameLength] = '\0';
//
//---------------------------------------------------------------
// Set up the buffer to indicate the data hasn't been read in yet
//---------------------------------------------------------------
//
m_buffer = NULL;
m_deleteData = false;
m_hash = GenerateHash(m_name);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Record::Save(
DatabaseHandle *handle,
MemoryStream *record_stream,
MemoryStream *data_stream
)
{
Check_Pointer(this);
Check_Object(handle);
Check_Object(record_stream);
Check_Object(data_stream);
//
//--------------------------------
// Write out the modification time
//--------------------------------
//
record_stream->WriteBytes(&m_lastModified, sizeof(m_lastModified));
//
//-----------------------------------------------------------------
// If this is a zero length record, just write out the empty record
//-----------------------------------------------------------------
//
if (!m_realLength)
{
Verify(!m_dataLength);
*record_stream << (DWORD)0 << (DWORD)0 << (DWORD)0;
*record_stream << m_ID << m_nameLength;
if(m_nameLength > 0)
record_stream->WriteBytes(m_name, m_nameLength);
return;
}
//
//-------------------------------------------------
// Load the buffer if it hasn't already been loaded
//-------------------------------------------------
//
if (!m_buffer)
LoadData(handle);
//
//----------------------------------------------------------
// If the offset is -1, we will need to test for compression
//----------------------------------------------------------
//
#if defined(_ARMOR)
BYTE *buffer = new(handle->m_heap) BYTE[m_dataLength+4];
*Cast_Pointer(int*,&buffer[m_dataLength]) = '%y!P';
#else
BYTE *buffer = new(handle->m_heap) BYTE[m_dataLength];
#endif
m_realLength = gos_LZCompress(buffer, m_buffer, m_dataLength, m_dataLength);
Verify(m_realLength <= m_dataLength);
Verify(*Cast_Pointer(int*,&buffer[m_dataLength]) == '%y!P');
if (!m_realLength || ((Scalar)m_realLength)/m_dataLength > RATIO)
{
delete buffer;
buffer = NULL;
m_realLength = m_dataLength;
}
//
//-----------------------------------------------------
// Write the record to the record portion of the stream
//-----------------------------------------------------
//
*record_stream << m_dataLength << m_realLength;
m_dataOffset = data_stream->GetIndex();
*record_stream << m_dataOffset;
*record_stream << m_ID << m_nameLength;
if(m_nameLength > 0)
record_stream->WriteBytes(m_name, m_nameLength);
//
//-----------------------------------
// Write the data to the data portion
//-----------------------------------
//
Verify(m_realLength>0);
if (buffer)
{
Verify(m_realLength < m_dataLength);
data_stream->WriteBytes(buffer, m_realLength);
delete buffer;
}
else
{
Verify(m_realLength == m_dataLength);
data_stream->WriteBytes(m_buffer, m_realLength);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Record::SaveNoChange(
DatabaseHandle *handle,
MemoryStream *record_stream,
MemoryStream *data_stream
)
{
Check_Pointer(this);
Check_Object(handle);
Check_Object(record_stream);
Check_Object(data_stream);
//
//--------------------------------
// Write out the modification time
//--------------------------------
//
record_stream->WriteBytes(&m_lastModified, sizeof(m_lastModified));
//
//-----------------------------------------------------------------
// If this is a zero length record, just write out the empty record
//-----------------------------------------------------------------
//
if (!m_realLength)
{
Verify(!m_dataLength);
*record_stream << (DWORD)0 << (DWORD)0 << (DWORD)0;
*record_stream << m_ID << m_nameLength;
if(m_nameLength > 0)
record_stream->WriteBytes(m_name, m_nameLength);
return;
}
//
//-------------------------------------------------
// Load the buffer if it hasn't already been loaded
//-------------------------------------------------
//
if (!m_buffer)
LoadDataNoDecompress(handle);
//
//-----------------------------------------------------
// Write the record to the record portion of the stream
//-----------------------------------------------------
//
*record_stream << m_dataLength << m_realLength;
m_dataOffset = data_stream->GetIndex();
*record_stream << m_dataOffset;
*record_stream << m_ID << m_nameLength;
if(m_nameLength > 0)
record_stream->WriteBytes(m_name, m_nameLength);
data_stream->WriteBytes(m_buffer, m_realLength);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Record::LoadData(DatabaseHandle *handle)
{
Check_Pointer(this);
Verify(!m_buffer && m_dataOffset != -1);
Check_Object(handle);
DATABASE_LOG("Load Data");
//
//----------------------------------------
// Read in the data stored in the gos file
//----------------------------------------
//
Database *db = handle->m_dataBase;
Check_Object(db);
BYTE *buffer = NULL;
if (handle->m_fileHandle)
{
DATABASE_LOG("Load Data::Read File");
gos_SeekFile(handle->m_fileHandle, FROMSTART, db->m_indexSize+m_dataOffset);
buffer = m_buffer = new(handle->m_heap) BYTE[m_realLength];
m_deleteData = true;
gos_ReadFile(handle->m_fileHandle, m_buffer, m_realLength);
}
else
{
m_buffer = handle->m_fileAddress + db->m_indexSize + m_dataOffset;
Verify(!m_deleteData);
}
//
//-------------------------------
// Decompress the file if need be
//-------------------------------
//
if (m_realLength < m_dataLength)
{
DATABASE_LOG("Load Data::Decompress");
m_deleteData = true;
BYTE *new_buffer = new(handle->m_heap) BYTE[m_dataLength];
#if defined(_ARMOR)
DWORD bytes =
#endif
gos_LZDecompress(new_buffer, m_buffer, m_realLength);
Verify(bytes == m_dataLength);
if (buffer)
{
Verify(buffer == m_buffer);
delete buffer;
}
m_buffer = new_buffer;
}
else
Verify(m_realLength == m_dataLength);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Record::LoadDataNoDecompress(DatabaseHandle *handle)
{
Check_Pointer(this);
Verify(!m_buffer && m_dataOffset != -1);
Check_Object(handle);
//
//----------------------------------------
// Read in the data stored in the gos file
//----------------------------------------
//
Database *db = handle->m_dataBase;
Check_Object(db);
BYTE *buffer = NULL;
if (handle->m_fileHandle)
{
gos_SeekFile(handle->m_fileHandle, FROMSTART, db->m_indexSize+m_dataOffset);
buffer = m_buffer = new(handle->m_heap) BYTE[m_realLength];
m_deleteData = true;
gos_ReadFile(handle->m_fileHandle, m_buffer, m_realLength);
}
else
{
m_buffer = handle->m_fileAddress + db->m_indexSize + m_dataOffset;
Verify(!m_deleteData);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Record::UnloadData()
{
Check_Object(this);
Verify(m_referenceCount < 2);
//
//--------------------------------------
// Delete the data if it has been loaded
//--------------------------------------
//
if (m_deleteData && m_dataOffset != -1)
{
Check_Pointer(m_buffer);
delete m_buffer;
m_buffer = NULL;
m_deleteData = false;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Record::AbandonData()
{
Check_Object(this);
Verify(m_referenceCount == 1);
//
//--------------------------------------
// Delete the data if it has been loaded
//--------------------------------------
//
if (m_deleteData && m_dataOffset != -1)
{
Check_Pointer(m_buffer);
m_buffer = NULL;
m_deleteData = false;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Record::Unhook(const RecordHandle *handle)
{
Check_Pointer(this);
Check_Object(handle);
DatabaseHandle *db_handle = handle->m_databaseHandle;
Check_Object(db_handle);
Database *db = db_handle->m_dataBase;
Check_Object(db);
Check_Object(this);
Verify(m_referenceCount == 1);
//
//--------------------------------------------
// Make sure that we can find the record by ID
//--------------------------------------------
//
Record* record;
DWORD index = m_ID % Database::e_DataBlockSize;
#ifdef _ARMOR
record = db->m_idOffsets[index];
while (record)
{
Check_Object(record);
if (record->m_ID == m_ID)
break;
record = record->m_nextIDRecord;
}
Verify(this == record);
#endif
//
//-------------------------------------------
// Now remove the record from the index database
//-------------------------------------------
//
record = db->m_idOffsets[index];
if (record == this)
db->m_idOffsets[index] = m_nextIDRecord;
else
{
while (record)
{
Check_Object(record);
if (record->m_nextIDRecord == this)
{
record->m_nextIDRecord = m_nextIDRecord;
break;
}
record = record->m_nextIDRecord;
}
}
//
//------------------------------------
// Now remove from hash index database
//------------------------------------
//
index = m_hash % Database::e_DataBlockSize;
record = db->m_nameOffsets[index];
if (record == this)
db->m_nameOffsets[index] = m_nextNameRecord;
else
{
while (record)
{
Check_Object(record);
if (record->m_nextNameRecord == this)
{
record->m_nextNameRecord = m_nextNameRecord;
break;
}
record = record->m_nextNameRecord;
}
}
db->m_numberOfRecords--;
}
//
//###########################################################################
//########################### RecordHandle ############################
//###########################################################################
//
static Record *NextIteratedRecord = NULL;
#if defined(_ARMOR)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RecordHandle::RecordHandle()
{
Check_Object(this);
m_record = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RecordHandle::~RecordHandle()
{
Check_Object(this);
if (m_record)
{
Check_Object(m_record);
Verify(m_record->m_referenceCount > 0);
--const_cast<Record*>(m_record)->m_referenceCount;
}
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RecordHandle::Add()
{
Check_Object(this);
Verify(!m_record);
//
//------------------
// Find our database
//------------------
//
Check_Object(m_databaseHandle);
m_databaseHandle->m_dirtyFlag = true;
Database* db = m_databaseHandle->m_dataBase;
Check_Object(db);
//
//-------------------------------------------
// Make sure the record doesn't already exist
//-------------------------------------------
//
Verify(!m_record);
#ifdef _ARMOR
if (m_name)
{
RecordHandle dup_check = *this;
if (dup_check.FindName(false))
STOP(( "Duplicate Record" ));
}
#endif
//
//---------------------
// Set the record index
//---------------------
//
m_ID=db->m_nextRecordID++;
//
//---------------------------------------------------
// Figure out how long the name is and its hash value
//---------------------------------------------------
//
DWORD record_hash;
BYTE name_length;
if (m_name)
{
record_hash = GenerateHash(m_name);
Verify(strlen(m_name)<256);
name_length = static_cast<BYTE>(strlen(m_name));
Verify(name_length > 0);
}
else
{
record_hash = 0;
name_length = 0;
}
//
//------------------
// Set up the record
//------------------
//
Verify(!m_record);
Record *data =
new(new(m_databaseHandle->m_heap) BYTE[sizeof(*m_record) + name_length])
Record(this, record_hash, name_length);
Check_Object(data);
m_data = data->m_buffer;
if (m_name)
m_name = data->m_name;
m_timeStamp = data->m_lastModified;
//
//------------------
// Update statistics
//------------------
//
m_record = data;
#if defined(_ARMOR)
const_cast<Record*>(m_record)->m_referenceCount = 1;
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RecordHandle::Replace()
{
Check_Object(this);
//
//------------------
// Find our database
//------------------
//
Check_Object(m_databaseHandle);
m_databaseHandle->m_dirtyFlag = true;
//
//----------------------
// Unhook the old record
//----------------------
//
Check_Object(m_record);
const_cast<Record*>(m_record)->Unhook(this);
//
//---------------------------------------------------
// Figure out how long the name is and its hash value
//---------------------------------------------------
//
DWORD record_hash;
BYTE name_length;
if (m_name)
{
record_hash = GenerateHash(m_name);
Verify(strlen(m_name) < 256);
name_length = static_cast<BYTE>(strlen(m_name));
Verify(name_length > 0);
}
else
{
record_hash = 0;
name_length = 0;
}
//
//------------------
// Set up the record
//------------------
//
#if defined(_ARMOR)
Verify(m_record->m_referenceCount == 1);
const_cast<Record*>(m_record)->m_referenceCount = 0;
#endif
Record *data =
new(new(m_databaseHandle->m_heap) BYTE[sizeof(*m_record) + name_length])
Record(this, record_hash, name_length, const_cast<Record*>(m_record));
Check_Object(data);
m_data = data->m_buffer;
if (m_name)
m_name = data->m_name;
m_timeStamp = data->m_lastModified;
//
//------------------
// Update statistics
//------------------
//
delete const_cast<Record*>(m_record);
m_record = data;
#if defined(_ARMOR)
const_cast<Record*>(m_record)->m_referenceCount = 1;
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RecordHandle::Delete()
{
Check_Object(this);
//
//------------------
// Find our database
//------------------
//
Check_Object(m_databaseHandle);
m_databaseHandle->m_dirtyFlag = true;
//
//----------------------
// Delete the old record
//----------------------
//
Check_Object(m_record);
const_cast<Record*>(m_record)->Unhook(this);
#if defined(_ARMOR)
const_cast<Record*>(m_record)->m_referenceCount = 0;
#endif
delete const_cast<Record*>(m_record);
m_record = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool RecordHandle::FindID(bool load)
{
Check_Object(this);
Check_Object(m_databaseHandle);
DATABASE_LOG("Find ID");
Database* db = m_databaseHandle->m_dataBase;
Check_Object(db);
DWORD index=m_ID % Database::e_DataBlockSize;
Record* record = db->m_idOffsets[index];
while (record)
{
Check_Object(record);
if (record->m_ID==m_ID)
{
m_length = record->m_dataLength;
if (load && !record->m_buffer && record->m_dataOffset!=-1 && record->m_dataLength>0)
record->LoadData(m_databaseHandle);
m_data = record->m_buffer;
m_name = record->m_name;
m_record = record;
#if defined(_ARMOR)
++const_cast<Record*>(m_record)->m_referenceCount;
#endif
m_timeStamp = record->m_lastModified;
return true;
}
record = record->m_nextIDRecord;
}
m_record = NULL;
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool RecordHandle::FindName(bool load)
{
Check_Object(this);
Check_Object(m_databaseHandle);
DATABASE_LOG("Find Name");
Database* db = m_databaseHandle->m_dataBase;
Check_Object(db);
Check_Pointer(m_name);
DWORD hash = GenerateHash(m_name);
DWORD index = hash % Database::e_DataBlockSize;
Record* record = db->m_nameOffsets[index];
while (record)
{
Check_Object(record);
if (record->m_hash == hash && !_stricmp(m_name,record->m_name))
{
m_length = record->m_dataLength;
if (load && !record->m_buffer && record->m_dataOffset!=-1 && record->m_dataLength>0)
record->LoadData(m_databaseHandle);
m_data = record->m_buffer;
m_ID = record->m_ID;
m_name = record->m_name;
m_record = record;
#if defined(_ARMOR)
++const_cast<Record*>(m_record)->m_referenceCount;
#endif
m_timeStamp = record->m_lastModified;
return true;
}
record = record->m_nextNameRecord;
}
m_record = NULL;
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RecordHandle::LoadData()
{
Check_Object(this);
Check_Object(m_record);
Check_Object(m_databaseHandle);
if (!m_record->m_buffer && m_record->m_dataOffset != -1 && m_record->m_dataLength>0)
const_cast<Record*>(m_record)->LoadData(m_databaseHandle);
m_data = m_record->m_buffer;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RecordHandle::UnloadData()
{
Check_Object(this);
Check_Object(m_record);
Check_Object(m_databaseHandle);
Verify(m_databaseHandle->m_fileHandle || !m_record->m_deleteData || m_record->m_dataOffset == -1);
const_cast<Record*>(m_record)->UnloadData();
m_data = m_record->m_buffer;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RecordHandle::AbandonData()
{
Check_Object(this);
Check_Object(m_record);
Check_Object(m_databaseHandle);
const_cast<Record*>(m_record)->AbandonData();
m_data = m_record->m_buffer;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int RecordHandle::GetDiskOffset()
{
Check_Object(this);
Check_Object(m_record);
Check_Object(m_databaseHandle);
Database* db = m_databaseHandle->m_dataBase;
Check_Object(db);
Verify(m_record->m_realLength == m_record->m_dataLength);
return db->m_indexSize + m_record->m_dataOffset;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DWORD RecordHandle::GetRealRecordSize()
{
Check_Object(this);
Check_Object(m_record);
Check_Object(m_databaseHandle);
return m_record->m_realLength;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RecordHandle::First()
{
Check_Object(this);
Check_Object(m_databaseHandle);
Database* db = m_databaseHandle->m_dataBase;
Check_Object(db);
m_record = NULL;
for (DWORD i=0; i<Database::e_DataBlockSize; i++)
{
NextIteratedRecord = db->m_idOffsets[i];
if (NextIteratedRecord)
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool RecordHandle::ReadAndNext()
{
Check_Object(this);
Check_Object(m_databaseHandle);
Database* db = m_databaseHandle->m_dataBase;
Check_Object(db);
#if defined(_ARMOR)
if (m_record)
--const_cast<Record*>(m_record)->m_referenceCount;
#endif
if (!NextIteratedRecord)
{
m_record = NULL;
return false;
}
Record* &data = NextIteratedRecord;
Check_Object(data);
m_ID = data->m_ID;
m_length = data->m_dataLength;
m_data = NULL;
m_name = data->m_name;
m_record = data;
#if defined(_ARMOR)
++const_cast<Record*>(m_record)->m_referenceCount;
#endif
m_timeStamp = data->m_lastModified;
data = data->m_nextIDRecord;
if (!data)
{
DWORD index=m_ID % Database::e_DataBlockSize;
while (++index < Database::e_DataBlockSize)
{
data = db->m_idOffsets[index];
if (data)
return true;
}
data = NULL;
return true;
}
NextIteratedRecord = data;
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RecordHandle::TestInstance() const
{
}
//
//###########################################################################
//############################# Database ##############################
//###########################################################################
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int Database::s_FilesOpened = 0;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Database::Database(DatabaseHandle *handle, DWORD content_version)
{
Check_Pointer(this);
Check_Object(handle);
m_handle = handle;
m_tag = e_Tag;
m_formatVersion = e_Version;
m_contentVersion = content_version;
m_nextRecordID = 0;
m_numberOfRecords = 0;
m_indexSize = 0;
memset(m_idOffsets, 0, sizeof(m_idOffsets));
memset(m_nameOffsets, 0, sizeof(m_nameOffsets));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Database::Database(DatabaseHandle *handle, DWORD content_version, HGOSFILE file)
{
Check_Pointer(this);
Check_Object(handle);
Check_Pointer(file);
//
//----------------------------
// Read in the database header
//----------------------------
//
m_handle = handle;
gos_ReadFile(file, &m_tag, sizeof(m_tag));
if (m_tag != e_Tag)
STOP(("Invalid or Corrupt file"));
gos_ReadFile(file, &m_formatVersion, sizeof(m_formatVersion));
if (m_formatVersion > e_Version)
STOP(("Application must be recompiled to use this database"));
gos_ReadFile(file, &m_contentVersion, sizeof(m_contentVersion));
if (m_formatVersion < e_Version || m_contentVersion < content_version)
{
gos_CloseFile(file);
handle->m_fileHandle = NULL;
m_handle = handle;
m_tag = e_Tag;
m_formatVersion = e_Version;
m_contentVersion = content_version;
m_nextRecordID = 0;
m_numberOfRecords = 0;
m_indexSize = 0;
memset(m_idOffsets, 0, sizeof(m_idOffsets));
memset(m_nameOffsets, 0, sizeof(m_nameOffsets));
return;
}
gos_ReadFile(file, &m_indexSize, sizeof(m_indexSize));
gos_ReadFile(file, &m_numberOfRecords, sizeof(m_numberOfRecords));
gos_ReadFile(file, &m_nextRecordID, sizeof(m_nextRecordID));
//
//------------------------------
// Now read in the chunk of data
//------------------------------
//
DWORD record_size = m_indexSize - e_FixedLength;
BYTE *buffer = new(handle->m_heap) BYTE[record_size];
Check_Pointer(buffer);
gos_ReadFile(file, buffer, record_size);
MemoryStream stream(buffer, record_size);
LoadRecords(&stream, false);
handle->m_fileLength = gos_SeekFile(file, FROMEND, 0);
delete buffer;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Database::Database(DatabaseHandle *handle, DWORD content_version, MemoryStream *stream)
{
Check_Pointer(this);
Check_Object(stream);
Check_Pointer(handle);
//
//----------------------------
// Read in the database header
//----------------------------
//
m_handle = handle;
*stream >> m_tag;
if (m_tag != e_Tag)
STOP(("Invalid or Corrupt file"));
*stream >> m_formatVersion;
if (m_formatVersion > e_Version)
STOP(("Application must be recompiled to use this database"));
*stream >> m_contentVersion;
if (m_formatVersion < e_Version || m_contentVersion < content_version)
STOP(("Obsolete database"));
*stream >> m_indexSize;
*stream >> m_numberOfRecords >> m_nextRecordID;
LoadRecords(stream, true);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Database::LoadRecords(MemoryStream *stream, bool preload)
{
Check_Object(this);
Check_Object(stream);
//
//-----------------------------------------------------------
// Now create all the records and hook them into the database
//-----------------------------------------------------------
//
memset(m_idOffsets, 0, sizeof(m_idOffsets));
memset(m_nameOffsets, 0, sizeof(m_nameOffsets));
for (WORD i=0; i<m_numberOfRecords; ++i)
{
__int64 last_modified;
stream->ReadBytes(&last_modified, sizeof(last_modified));
DWORD data_length, real_length, offset;
*stream >> data_length >> real_length >> offset;
WORD id;
BYTE name_length;
*stream >> id >> name_length;
Record *record =
new(new(m_handle->m_heap) BYTE[sizeof(Record) + name_length]) Record(
last_modified,
data_length,
real_length,
offset,
id,
name_length,
stream
);
Check_Object(record);
DWORD index=record->m_ID % Database::e_DataBlockSize;
record->m_nextIDRecord = m_idOffsets[index];
m_idOffsets[index] = record;
index = record->m_hash % Database::e_DataBlockSize;
record->m_nextNameRecord = m_nameOffsets[index];
m_nameOffsets[index] = record;
//
//--------------------------------------------------------
// If we are to preload, set the memory buffer accordingly
//--------------------------------------------------------
//
if (preload && record->m_realLength == record->m_dataLength)
record->m_buffer = m_handle->m_fileAddress + m_indexSize + record->m_dataOffset;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Database::Save()
{
Check_Object(this);
//
//----------------------------------------------------------------------
// First we have to spin through all the records to find out the size of
// memory we'll have to allocate
//----------------------------------------------------------------------
//
DWORD data_size=0;
DWORD record_size=Database::e_FixedLength;
WORD i;
for (i=0; i<Database::e_DataBlockSize; ++i)
{
Record* record = m_idOffsets[i];
while (record)
{
Check_Object(record);
record_size += Record::e_FixedLength + record->m_nameLength;
data_size += record->m_dataLength;
record = record->m_nextIDRecord;
}
}
//
//----------------------------------------------------------------------
// Allocate space in the destination stream to hold the entire database,
// then make a second memorystream rooted where the data should start
// writing to
//----------------------------------------------------------------------
//
DynamicMemoryStream db_stream(record_size + data_size);
MemoryStream
record_stream(
static_cast<BYTE*>(db_stream.GetPointer()),
record_size
);
MemoryStream
data_stream(
static_cast<BYTE*>(db_stream.GetPointer())+record_size,
data_size
);
//
//----------------------------------
// Now write out the database header
//----------------------------------
//
record_stream << m_tag << m_formatVersion << m_contentVersion;
record_stream << record_size << m_numberOfRecords << m_nextRecordID;
#if 0
//
//----------------------
// Write out the records
//----------------------
//
for (i=0; i<Database::e_DataBlockSize; ++i)
{
Record* record = m_idOffsets[i];
while (record)
{
Check_Object(record);
record->Save(m_handle, &record_stream, &data_stream);
Verify(!record->m_buffer || record->m_deleteData);
record = record->m_nextIDRecord;
}
}
#endif
for (i = 0; i < m_numberOfRecords; ++i)
{
Stuff::RecordHandle record;
record.m_databaseHandle = m_handle;
record.m_ID = i;
#if defined(_ARMOR)
bool result =
#endif
record.FindID(true);
Verify(result);
Verify(record.m_record != NULL);
Record* test_record = const_cast<Record*>(record.m_record);
test_record->Save(m_handle, &record_stream, &data_stream);
Verify(!test_record->m_buffer || test_record->m_deleteData);
}
Verify(record_stream.GetBytesRemaining() == 0);
Verify(data_stream.GetBytesRemaining() >= 0);
DWORD real_length = record_size + data_stream.GetBytesUsed();
//
//------------------------------------------------------
// Now we tell the handle to actually write out the data
//------------------------------------------------------
//
m_handle->Save(static_cast<BYTE*>(db_stream.GetPointer()), real_length);
m_indexSize = record_size;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Database::ScatterSave()
{
Check_Object(this);
//
//----------------------------------------------------------------------
// First we have to spin through all the records to find out the size of
// memory we'll have to allocate
//----------------------------------------------------------------------
//
DWORD data_size=0;
DWORD record_size=Database::e_FixedLength;
WORD i;
for (i=0; i<Database::e_DataBlockSize; ++i)
{
Record* record = m_idOffsets[i];
while (record)
{
Check_Object(record);
record_size += Record::e_FixedLength + record->m_nameLength;
data_size += record->m_dataLength;
record = record->m_nextIDRecord;
}
}
//
//----------------------------------------------------------------------
// Allocate space in the destination stream to hold the entire database,
// then make a second memorystream rooted where the data should start
// writing to
//----------------------------------------------------------------------
//
DynamicMemoryStream db_stream(record_size + data_size);
MemoryStream
record_stream(
static_cast<BYTE*>(db_stream.GetPointer()),
record_size
);
MemoryStream
data_stream(
static_cast<BYTE*>(db_stream.GetPointer())+record_size,
data_size
);
//
//----------------------------------
// Now write out the database header
//----------------------------------
//
record_stream << m_tag << m_formatVersion << m_contentVersion;
record_stream << record_size << m_numberOfRecords << m_nextRecordID;
//
//----------------------
// Write out the records
//----------------------
//
WORD start_value = (WORD)Random::GetLessThan(m_numberOfRecords);
Stuff::RecordHandle record;
record.m_databaseHandle = m_handle;
for (i=start_value; i<m_numberOfRecords; ++i)
{
record.m_ID = i;
#if defined(_ARMOR)
bool result =
#endif
record.FindID(true);
Verify(result);
Verify(record.m_record != NULL);
Record* test_record = const_cast<Record*>(record.m_record);
Check_Object(test_record);
test_record->SaveNoChange(m_handle, &record_stream, &data_stream);
Verify(!test_record->m_buffer || test_record->m_deleteData);
}
for (i=0; i < start_value; ++i)
{
record.m_ID = i;
#if defined(_ARMOR)
bool result =
#endif
record.FindID(true);
Verify(result);
Verify(record.m_record != NULL);
Record* test_record = const_cast<Record*>(record.m_record);
Check_Object(test_record);
test_record->SaveNoChange(m_handle, &record_stream, &data_stream);
Verify(!test_record->m_buffer || test_record->m_deleteData);
}
Verify(record_stream.GetBytesRemaining() == 0);
Verify(data_stream.GetBytesRemaining() >= 0);
DWORD real_length = record_size + data_stream.GetBytesUsed();
//
//------------------------------------------------------
// Now we tell the handle to actually write out the data
//------------------------------------------------------
//
m_handle->Save(static_cast<BYTE*>(db_stream.GetPointer()), real_length);
m_indexSize = record_size;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Database::~Database()
{
Check_Object(this);
for (int i=0; i<e_DataBlockSize; ++i)
{
Record *record = m_idOffsets[i];
while (record)
{
Check_Object(record);
Record *next = record->m_nextIDRecord;
delete record;
record = next;
}
}
}
//
//###########################################################################
//########################## DatabaseHandle ###########################
//###########################################################################
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DatabaseHandle::DatabaseHandle(
const char* filename,
bool read_only,
DWORD content_version
)
{
//
//-----------------------------------------------------
// Create the database heap if it doesn't already exist
//-----------------------------------------------------
//
m_fileName = filename;
if (!Database::s_FilesOpened++)
g_DatabaseHeap = gos_CreateMemoryHeap("Database", 0, Stuff::g_LibraryHeap);
const char* root_name = __mbsrchr(filename, '\\');
if (!root_name)
root_name = filename;
else
++root_name;
m_heap = gos_CreateMemoryHeap(const_cast<char*>(root_name), 0, g_DatabaseHeap);
m_dirtyFlag = false;
m_contentVersion = content_version;
//
//-------------------------------------
// Disable the file hooks for this part
//-------------------------------------
//
void (__stdcall *old_get_hook)(const char*, BYTE**, DWORD*) = Environment.HookGetFile;
Environment.HookGetFile = NULL;
bool (__stdcall *old_exist_hook)(const char*) = Environment.HookDoesFileExist;
Environment.HookDoesFileExist = NULL;
gos_PushCurrentHeap(m_heap);
//
//------------------------
// Initialize the pointers
//------------------------
//
m_readOnly = read_only;
m_MMHandle = NULL;
m_fileAddress = NULL;
m_fileLength = 0;
m_fileHandle = NULL;
//
//-----------------------------
// If we find the file, open it
//-----------------------------
//
if (gos_DoesFileExist(filename))
{
gos_OpenFile(&m_fileHandle, filename, READONLY);
m_dataBase = new(m_heap) Database(this, content_version, m_fileHandle);
}
//
//-----------------------------------------------------------------------------------
// Otherwise, we have to look in the registry for the CD key to try and find the file
// there
//-----------------------------------------------------------------------------------
//
else if (read_only)
{
char cd_path[256];
DWORD size=sizeof(cd_path);
gos_LoadDataFromRegistry(
"CDPath",
cd_path,
&size,
TRUE
);
if (size>0)
{
size--;
Verify(strlen(cd_path) == size);
m_fileName=MString(cd_path)+m_fileName;
bool res=true;
while(!gos_DoesFileExist(m_fileName) && res)
{
if(NoCDMessageFunction)
res=NoCDMessageFunction();
if(!res || !NoCDMessageFunction)
{
#ifdef LAB_ONLY
STOP(("Can't open %s", filename));
#endif
ProcessingError=1;
ExitGameOS();
}
}
gos_OpenFile(&m_fileHandle, m_fileName, READONLY);
m_dataBase = new(m_heap) Database(this, content_version, m_fileHandle);
}
else
STOP(("Can't open %s", filename));
}
//
//------------------------------------------
// The file doesn't exist, so create it here
//------------------------------------------
//
else
m_dataBase = new(m_heap) Database(this, content_version);
//
//-------------------------------------
// Disable the file hooks for this part
//-------------------------------------
//
gos_PopCurrentHeap();
Environment.HookGetFile = old_get_hook;
Environment.HookDoesFileExist = old_exist_hook;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DatabaseHandle::DatabaseHandle(
const char* filename,
MemoryStream *stream,
DWORD content_version
)
{
//
//-----------------------------------------------------
// Create the database heap if it doesn't already exist
//-----------------------------------------------------
//
m_fileName = filename;
if (!Database::s_FilesOpened++)
g_DatabaseHeap = gos_CreateMemoryHeap("Database", 0, Stuff::g_LibraryHeap);
const char* root_name = __mbsrchr(filename, '\\');
if (!root_name)
root_name = filename;
else
++root_name;
m_heap = gos_CreateMemoryHeap(const_cast<char*>(root_name), 0, g_DatabaseHeap);
m_dirtyFlag = false;
m_contentVersion = content_version;
m_readOnly = false;
m_fileHandle = NULL;
m_MMHandle = NULL;
m_fileAddress = (unsigned char*)stream->GetPointer();
m_fileLength = stream->GetSize();
m_dataBase = new(m_heap) Database(this, content_version, stream);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DatabaseHandle::~DatabaseHandle()
{
Check_Object(this);
//
//---------------------------------------------------------------
// If we are closing a read only file, release the memmap handle,
// otherwise delete the database object
//---------------------------------------------------------------
//
Check_Object(m_dataBase);
delete m_dataBase;
//
//--------------------
// Close off the files
//--------------------
//
if (m_fileHandle)
{
Verify(!m_MMHandle);
gos_CloseFile(m_fileHandle);
}
else if (m_MMHandle)
gos_CloseMemoryMappedFile(m_MMHandle);
//
//----------------------------------------------------
// Delete the memory heap if this is the last database
//----------------------------------------------------
//
gos_DestroyMemoryHeap(m_heap);
if (--Database::s_FilesOpened == 0)
gos_DestroyMemoryHeap(g_DatabaseHeap);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DatabaseHandle::Save()
{
Check_Object(this);
//
//--------------------------------------
// If there were no changes, we are done
//--------------------------------------
//
if (!m_dirtyFlag)
return;
m_dataBase->Save();
m_dirtyFlag = false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DatabaseHandle::ScatterSave()
{
Check_Object(this);
m_dataBase->ScatterSave();
m_dirtyFlag = false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DatabaseHandle::SaveAs(const char* filename)
{
Check_Object(this);
m_fileName = filename;
m_dataBase->Save();
m_dirtyFlag = false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DatabaseHandle::Save(BYTE *address, DWORD length)
{
Check_Object(this);
//
//--------------------------------------
// If we are using a file, write to that
//--------------------------------------
//
if (m_fileHandle)
gos_CloseFile(m_fileHandle);
gos_OpenFile(&m_fileHandle, m_fileName, READWRITE);
gos_SeekFile(m_fileHandle, FROMSTART, 0);
gos_WriteFile(m_fileHandle, address, length);
gos_CloseFile(m_fileHandle);
gos_OpenFile(&m_fileHandle, m_fileName, READONLY);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DatabaseHandle::SpewLoadedRecords()
{
Check_Object(this);
//
//----------------------
// Write out the records
//----------------------
//
#if defined(LAB_ONLY)
for (DWORD i=0; i<Database::e_DataBlockSize; ++i)
{
Record* record = m_dataBase->m_idOffsets[i];
while (record)
{
Check_Object(record);
if (record->m_deleteData)
SPEWALWAYS((NULL, "%d\t%s", record->m_dataLength, record->m_name));
record = record->m_nextIDRecord;
}
}
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DatabaseHandle::UnloadRecords()
{
Check_Object(this);
//
//----------------------
// Write out the records
//----------------------
//
for (DWORD i=0; i<Database::e_DataBlockSize; ++i)
{
Record* record = m_dataBase->m_idOffsets[i];
while (record)
{
Check_Object(record);
record->UnloadData();
record = record->m_nextIDRecord;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
WORD DatabaseHandle::GetNumberOfRecords()
{
Check_Object(this);
return m_dataBase->m_numberOfRecords;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
__int64 DatabaseHandle::CalculateCRC(int key)
{
Check_Object(this);
//SPEW(("jerryeds", "START DATABASE CRC-----------------------------"));
__int64 total_crc = 0;
//__int64 acumulator = 0;
//size_t acumulator_size = 0;
size_t bytes_left = m_fileLength - m_dataBase->m_indexSize;
WORD *word_data;
BYTE *temp_buf = NULL;
if (m_fileAddress)
word_data = Cast_Pointer(WORD*, m_fileAddress + m_dataBase->m_indexSize);
else
{
temp_buf = new BYTE[bytes_left];
gos_SeekFile(m_fileHandle, FROMSTART, m_dataBase->m_indexSize);
gos_ReadFile(m_fileHandle, temp_buf, bytes_left);
word_data = Cast_Pointer(WORD*, temp_buf);
}
Check_Pointer(word_data);
#if 0
__int64 local_acumulator = 0;
while (bytes_left > 0)
{
if (bytes_left > 1)
{
Check_Pointer(word_data);
acumulator += *word_data;
local_acumulator += *word_data;
word_data += 1;
acumulator_size += sizeof(WORD);
bytes_left -= sizeof(WORD);
}
else
{
BYTE *sub_data = (BYTE*)word_data;
Check_Pointer(sub_data);
acumulator += *sub_data;
local_acumulator += *sub_data;
acumulator_size += sizeof(BYTE);
bytes_left -= sizeof(BYTE);
}
}
//SPEW(("jerryeds", "%s : %u",handle.m_name, local_acumulator ));
// every 2 meg we xor with the crc...
if (acumulator_size)
{
total_crc = total_crc ^ acumulator;
}
//SPEW(("jerryeds", "STOP DATABASE CRC-----------------------------"));
#endif
MD5_CTX resource_context;
GUID resource_hash;
unsigned char * buff=(unsigned char *) &resource_hash;
MD5Init(&resource_context);
MD5Update(&resource_context, (unsigned char *) &key, 4);
MD5Update(&resource_context, (unsigned char *) word_data, bytes_left);
MD5Final(buff, &resource_context);
__int64 first_half = (__int64)buff[0];
__int64 second_half = (__int64)buff[8];
total_crc = first_half ^ second_half;
if (temp_buf)
delete temp_buf;
return total_crc;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
__int64 DatabaseHandle::CalculateDateCRC()
{
return gos_FileTimeStamp(m_fileName);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DatabaseHandle::TestInstance() const
{
}