openmohaa/code/fgame/archive.cpp
2024-09-20 23:08:53 +02:00

1197 lines
28 KiB
C++

/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// archive.cpp: OpenMoHAA Archiver
#include "glb_local.h"
#include "archive.h"
#include "level.h"
#include <lz77.h>
#ifdef GAME_DLL
# include "../fgame/entity.h"
#endif
enum {
ARC_NULL,
ARC_Vector,
ARC_Vec2,
ARC_Vec3,
ARC_Vec4,
ARC_Integer,
ARC_Unsigned,
ARC_Byte,
ARC_Char,
ARC_Short,
ARC_UnsignedShort,
ARC_Float,
ARC_Double,
ARC_Boolean,
ARC_String,
ARC_Raw,
ARC_Object,
ARC_ObjectPointer,
ARC_SafePointer,
ARC_EventPointer,
ARC_Quat,
ARC_Entity,
ARC_Bool,
ARC_Position,
ARC_Size,
ARC_NUMTYPES
};
static const char *typenames[] = {
"NULL", "vector", "vec2", "vec3", "vec4", "int", "unsigned", "byte",
"char", "short", "unsigned short", "float", "double", "qboolean", "string", "raw data",
"object", "objectpointer", "safepointer", "eventpointer", "quaternion", "entity", "bool", "position",
"size"};
#define ArchiveHeader (LittleLong(*(int *)"MHAA"))
#define ArchiveVersion 14 // This must be changed any time the format changes!
#define ArchiveInfo "OPENMOHAA Archive Version 14" // This must be changed any time the format changes!
ArchiveFile::ArchiveFile()
{
length = 0;
buffer = 0;
pos = 0;
bufferlength = 0;
writing = 0;
opened = 0;
}
ArchiveFile::~ArchiveFile()
{
Close();
}
void ArchiveFile::Close()
{
if (writing) {
gi.FS_WriteFile(filename.c_str(), buffer, length);
}
if (buffer) {
gi.Free((void *)buffer);
buffer = NULL;
}
writing = false;
filename = "";
length = 0;
pos = 0;
}
const char *ArchiveFile::Filename(void)
{
return filename.c_str();
}
qboolean ArchiveFile::Compress()
{
byte *tempbuf;
size_t out_len;
size_t tempbuf_len;
tempbuf_len = (length >> 6) + length + 27;
tempbuf = (byte *)gi.Malloc(tempbuf_len);
// Set the signature
tempbuf[0] = 'C';
tempbuf[1] = 'S';
tempbuf[2] = 'V';
tempbuf[3] = 'G';
*(size_t *)(tempbuf + 4) = length;
// Compress the data
if (g_lz77.Compress(buffer, length, tempbuf + 8, &out_len)) {
gi.Error(ERR_DROP, "Compression of SaveGame Failed!\n");
return false;
}
gi.Free(buffer);
buffer = tempbuf;
length = out_len + 8;
bufferlength = tempbuf_len;
return true;
}
size_t ArchiveFile::Length(void)
{
return length;
}
size_t ArchiveFile::Pos(void)
{
return pos - buffer;
}
size_t ArchiveFile::Tell(void)
{
return pos - buffer;
}
qboolean ArchiveFile::Seek(size_t newpos)
{
if (!buffer) {
return false;
}
if (newpos > length) {
return false;
}
pos = buffer + newpos;
return true;
}
qboolean ArchiveFile::OpenRead(const char *name)
{
byte *tempbuf;
assert(name);
assert(!buffer);
Close();
if (!name) {
return false;
}
length = gi.FS_ReadFile(name, (void **)&tempbuf, qtrue);
if (length == (size_t)(-1) || length == 0) {
return false;
}
// create our own space
buffer = (byte *)gi.Malloc(length);
bufferlength = length;
// copy the file over to our space
memcpy(buffer, tempbuf, length);
// free the file
gi.FS_FreeFile(tempbuf);
// set the file name
filename = name;
pos = buffer;
writing = false;
opened = true;
char FileHeader[4];
Read(FileHeader, sizeof(FileHeader));
if (FileHeader[0] != 'C' || FileHeader[1] != 'S' || FileHeader[2] != 'V' || FileHeader[3] != 'G') {
pos = buffer;
} else {
uint32_t new_len;
size_t iCSVGLength;
new_len = 0;
Read(&new_len, sizeof(uint32_t));
new_len = LittleLong(new_len);
tempbuf = (byte *)gi.Malloc(new_len);
if (g_lz77.Decompress(pos, length - 8, tempbuf, &iCSVGLength) || iCSVGLength != new_len) {
gi.Error(ERR_DROP, "Decompression of save game failed\n");
return false;
}
gi.Free(buffer);
buffer = tempbuf;
length = iCSVGLength;
bufferlength = length;
pos = buffer;
}
return true;
}
qboolean ArchiveFile::OpenWrite(const char *name)
{
this->length = 0;
// 4 MiB buffer
this->bufferlength = 4 * 1024 * 1024;
this->buffer = (byte *)gi.Malloc(bufferlength);
this->filename = name;
this->pos = buffer;
this->writing = true;
this->opened = true;
return true;
}
qboolean ArchiveFile::Read(void *dest, size_t size)
{
if (!size) {
return false;
}
if ((pos + size) > (buffer + length)) {
return false;
}
memcpy(dest, pos, size);
pos += size;
return true;
}
qboolean ArchiveFile::Write(const void *source, size_t size)
{
if ((pos + size) > (buffer + bufferlength)) {
byte *oldbuf;
do {
bufferlength *= 2;
} while ((pos + size) > (buffer + bufferlength));
oldbuf = buffer;
// reallocate a bigger buffer
buffer = (byte *)gi.Malloc(bufferlength);
memcpy(buffer, oldbuf, length);
// free the old buffer
gi.Free(oldbuf);
// set the position with the new buffer
pos = buffer + (pos - oldbuf);
}
memcpy(pos, source, size);
pos += size;
if (length < (pos - buffer)) {
length = (pos - buffer);
}
return true;
}
Archiver::Archiver()
{
archivemode = ARCHIVE_WRITE;
fileerror = false;
harderror = true;
Reset();
silent = false;
assert((sizeof(typenames) / sizeof(typenames[0])) == ARC_NUMTYPES);
}
Archiver::~Archiver()
{
if (archivemode != ARCHIVE_NONE) {
Close();
}
}
void Archiver::FileError(const char *fmt, ...)
{
va_list argptr;
char text[1024];
va_start(argptr, fmt);
Q_vsnprintf(text, sizeof(text), fmt, argptr);
va_end(argptr);
fileerror = true;
Close();
if (archivemode == ARCHIVE_READ) {
if (harderror) {
gi.Error(ERR_DROP, "Error while loading %s : %s\n", filename.c_str(), text);
} else if (!silent) {
gi.Printf("Error while loading %s : %s\n", filename.c_str(), text);
}
} else if (archivemode == ARCHIVE_WRITE) {
if (harderror) {
gi.Error(ERR_DROP, "Error while writing to %s : %s\n", filename.c_str(), text);
} else if (!silent) {
gi.Printf("Error while writing to %s : %s\n", filename.c_str(), text);
}
} else {
if (harderror) {
gi.Error(ERR_DROP, "Error while neither reading nor writing: %s\n", text);
} else {
gi.Printf("Error while neither reading nor writing: %s\n", text);
}
}
}
void Archiver::Close(void)
{
if (archivemode == ARCHIVE_NONE) {
// nothing to process
return;
}
if (archivemode == ARCHIVE_WRITE) {
int numobjects;
size_t pos;
// write out the number of classpointers
pos = archivefile.Tell();
archivefile.Seek(numclassespos);
numobjects = classpointerList.NumObjects();
ArchiveInteger(&numobjects);
// compress the file
archivefile.Seek(pos);
archivefile.Compress();
}
archivefile.Close();
if (archivemode == ARCHIVE_READ) {
int i, num;
pointer_fixup_t *fixup;
num = fixupList.NumObjects();
for (i = 1; i <= num; i++) {
fixup = fixupList.ObjectAt(i);
if (fixup->type == pointer_fixup_ptr) {
LightClass **fixupptr;
fixupptr = (LightClass **)fixup->ptr;
*fixupptr = classpointerList.ObjectAt(fixup->index);
} else if (fixup->type == pointer_fixup_normal) {
Class **fixupptr;
fixupptr = (Class **)fixup->ptr;
*fixupptr = static_cast<Class *>(classpointerList.ObjectAt(fixup->index));
} else if (fixup->type == pointer_fixup_safe) {
SafePtrBase *fixupptr;
fixupptr = (SafePtrBase *)fixup->ptr;
fixupptr->InitSafePtr(static_cast<Class *>(classpointerList.ObjectAt(fixup->index)));
}
delete fixup;
}
fixupList.FreeObjectList();
classpointerList.FreeObjectList();
}
archivemode = ARCHIVE_NONE;
}
/****************************************************************************************
File Read/Write functions
*****************************************************************************************/
qboolean Archiver::Read(const char *name, qboolean harderror)
{
unsigned header;
unsigned version;
str info;
int num;
int i;
Class *null;
this->harderror = harderror;
this->fileerror = false;
this->archivemode = ARCHIVE_READ;
this->filename = name;
if (!archivefile.OpenRead(filename.c_str())) {
if (harderror) {
FileError("Couldn't open file.");
}
return false;
}
ArchiveUnsigned(&header);
if (header != ArchiveHeader) {
archivefile.Close();
FileError("Not a valid MOHAA archive.");
return false;
}
ArchiveUnsigned(&version);
if (version > ArchiveVersion) {
archivefile.Close();
FileError("Archive is from version %u. Check http://www.x-null.net for an update.", version);
return false;
}
if (version < ArchiveVersion) {
archivefile.Close();
FileError("Archive is out of date.");
return false;
}
ArchiveString(&info);
gi.DPrintf("%s\n", info.c_str());
// setup out class pointers
ArchiveInteger(&num);
classpointerList.Resize(num);
null = NULL;
for (i = 1; i <= num; i++) {
classpointerList.AddObject(null);
}
return true;
}
qboolean Archiver::Create(const char *name, qboolean harderror)
{
unsigned header;
unsigned version;
str info;
int numZero = 0;
this->harderror = harderror;
this->fileerror = false;
this->archivemode = ARCHIVE_WRITE;
this->filename = name;
if (!archivefile.OpenWrite(filename.c_str())) {
FileError("Couldn't open file.");
return false;
}
header = ArchiveHeader;
ArchiveUnsigned(&header);
version = ArchiveVersion;
ArchiveUnsigned(&version);
info = ArchiveInfo;
ArchiveString(&info);
numclassespos = archivefile.Tell();
ArchiveInteger(&numZero);
Reset();
return true;
}
/****************************************************************************************
File Archive functions
*****************************************************************************************/
//#define ARCHIVE_USE_TYPES 1
template<typename v>
void ArchiveSwapValue(v* value) {
LittleSwap(value, sizeof(v));
}
template<typename v>
void ArchiveSwapValue(v* value, size_t size) {
for (size_t i = 0; i < size; i++) {
LittleSwap(&value[i], sizeof(value[i]));
}
}
template<>
void ArchiveSwapValue<Vector>(Vector* value) {
for (int i = 0; i < 3; i++) {
(*value)[i] = LittleFloat((*value)[i]);
}
}
template<>
void ArchiveSwapValue<Quat>(Quat* value) {
for (int i = 0; i < 4; i++) {
(*value)[i] = LittleFloat((*value)[i]);
}
}
#define ARCHIVE(func, type) \
void Archiver::Archive##func(type *v) \
{ \
int i; \
if (archivemode == ARCHIVE_WRITE) { \
type nv = *v; \
ArchiveSwapValue(&nv); \
ArchiveData(ARC_##func, &nv, sizeof(type)); \
} else { \
ArchiveData(ARC_##func, v, sizeof(type)); \
ArchiveSwapValue(v); \
} \
}
ARCHIVE(Vector, Vector);
ARCHIVE(Integer, int);
ARCHIVE(Unsigned, unsigned);
ARCHIVE(Byte, byte);
ARCHIVE(Char, char);
ARCHIVE(Short, short);
ARCHIVE(UnsignedShort, unsigned short);
ARCHIVE(Float, float);
ARCHIVE(Double, double);
ARCHIVE(Boolean, qboolean);
ARCHIVE(Quat, Quat);
ARCHIVE(Bool, bool);
ARCHIVE(Position, int);
ARCHIVE(Size, long);
void Archiver::ArchiveSvsTime(int *time)
{
#ifdef GAME_DLL
if (archivemode == ARCHIVE_READ) {
ArchiveInteger(time);
gi.AddSvsTimeFixup(time);
} else {
*time -= level.svsTime;
ArchiveInteger(time);
*time += level.svsTime;
}
#endif
}
void Archiver::ArchiveVec2(vec2_t vec)
{
int i;
if (archivemode == ARCHIVE_WRITE) {
vec2_t nv = { vec[0], vec[1] };
ArchiveSwapValue(nv, 2);
ArchiveData(ARC_Vec2, nv, sizeof(vec2_t));
} else {
ArchiveData(ARC_Vec2, vec, sizeof(vec2_t));
ArchiveSwapValue(vec, 2);
}
}
void Archiver::ArchiveVec3(vec3_t vec)
{
int i;
if (archivemode == ARCHIVE_WRITE) {
vec3_t nv = { vec[0], vec[1], vec[2]};
ArchiveSwapValue(nv, 3);
ArchiveData(ARC_Vec3, nv, sizeof(vec3_t));
} else {
ArchiveData(ARC_Vec3, vec, sizeof(vec3_t));
ArchiveSwapValue(vec, 3);
}
}
void Archiver::ArchiveVec4(vec4_t vec)
{
int i;
if (archivemode == ARCHIVE_WRITE) {
vec4_t nv = { vec[0], vec[1], vec[2], vec[3] };
ArchiveSwapValue(nv, 4);
ArchiveData(ARC_Vec4, nv, sizeof(vec4_t));
} else {
ArchiveData(ARC_Vec4, vec, sizeof(vec4_t));
ArchiveSwapValue(vec, 4);
}
}
void Archiver::ArchiveObjectPointer(LightClass **ptr)
{
int index = 0;
if (archivemode == ARCHIVE_READ) {
pointer_fixup_t *fixup;
ArchiveData(ARC_ObjectPointer, &index, sizeof(index));
index = LittleLong(index);
//
// see if the variable was NULL
//
if (index == ARCHIVE_NULL_POINTER) {
*ptr = NULL;
} else {
// init the pointer with NULL until we can fix it
*ptr = NULL;
fixup = new pointer_fixup_t;
fixup->ptr = (void **)ptr;
fixup->index = index;
fixup->type = pointer_fixup_ptr;
fixupList.AddObject(fixup);
}
} else {
if (*ptr) {
index = classpointerList.AddUniqueObject(*ptr);
} else {
index = ARCHIVE_NULL_POINTER;
}
index = LittleLong(index);
ArchiveData(ARC_ObjectPointer, &index, sizeof(index));
}
}
void Archiver::ArchiveObjectPointer(Class **ptr)
{
int index = 0;
if (archivemode == ARCHIVE_READ) {
pointer_fixup_t *fixup;
ArchiveData(ARC_ObjectPointer, &index, sizeof(index));
index = LittleLong(index);
//
// see if the variable was NULL
//
if (index == ARCHIVE_NULL_POINTER) {
*ptr = NULL;
} else {
// init the pointer with NULL until we can fix it
*ptr = NULL;
fixup = new pointer_fixup_t;
fixup->ptr = (void **)ptr;
fixup->index = index;
fixup->type = pointer_fixup_normal;
fixupList.AddObject(fixup);
}
} else {
if (*ptr) {
index = classpointerList.AddUniqueObject(*ptr);
} else {
index = ARCHIVE_NULL_POINTER;
}
index = LittleLong(index);
ArchiveData(ARC_ObjectPointer, &index, sizeof(index));
}
}
void Archiver::ArchiveSafePointer(SafePtrBase *ptr)
{
int index = 0;
if (archivemode == ARCHIVE_READ) {
pointer_fixup_t *fixup;
ArchiveData(ARC_SafePointer, &index, sizeof(index));
index = LittleLong(index);
//
// see if the variable was NULL
//
if (index == ARCHIVE_NULL_POINTER) {
ptr->InitSafePtr(NULL);
} else {
// init the pointer with NULL until we can fix it
ptr->InitSafePtr(NULL);
// Add new fixup
fixup = new pointer_fixup_t;
fixup->ptr = (void **)ptr;
fixup->index = index;
fixup->type = pointer_fixup_safe;
fixupList.AddObject(fixup);
}
} else {
if (ptr->Pointer()) {
Class *obj;
obj = ptr->Pointer();
index = classpointerList.AddUniqueObject(obj);
} else {
index = ARCHIVE_NULL_POINTER;
}
index = LittleLong(index);
ArchiveData(ARC_SafePointer, &index, sizeof(index));
}
}
void Archiver::ArchiveEventPointer(Event **ev)
{
int index;
#ifdef ARCHIVE_USE_TYPES
CheckType(ARC_EventPointer);
#endif
if (archivemode == ARCHIVE_READ) {
#ifndef NDEBUG
CheckRead();
#endif
ArchiveInteger(&index);
if (!fileerror) {
if (index == ARCHIVE_POINTER_VALID) {
*ev = new Event;
(*ev)->Archive(*this);
} else {
(*ev) = NULL;
}
}
} else {
#ifndef NDEBUG
CheckWrite();
#endif
if (*ev) {
index = ARCHIVE_POINTER_VALID;
} else {
index = ARCHIVE_NULL_POINTER;
}
ArchiveInteger(&index);
if (*ev) {
(*ev)->Archive(*this);
}
}
}
void Archiver::ArchiveRaw(void *data, size_t size)
{
ArchiveData(ARC_Raw, data, size);
}
void Archiver::ArchiveString(str *string)
{
#ifdef ARCHIVE_USE_TYPES
CheckType(ARC_String);
#endif
if (archivemode == ARCHIVE_READ) {
fileSize_t s;
char *data;
#ifndef NDEBUG
CheckRead();
#endif
if (!fileerror) {
s = ReadSize();
if (!fileerror) {
data = new char[s + 1];
if (data) {
if (s) {
archivefile.Read(data, s);
}
data[s] = 0;
*string = data;
delete[] data;
}
}
}
} else {
fileSize_t s;
#ifndef NDEBUG
CheckWrite();
#endif
WriteSize(string->length());
archivefile.Write(string->c_str(), string->length());
}
}
Class *Archiver::ReadObject(void)
{
ClassDef *cls;
Class *obj;
str classname;
size_t objstart;
size_t endpos;
int index;
size_t size;
qboolean isent;
int type;
CheckRead();
type = ReadType();
if ((type != ARC_Object) && (type != ARC_Entity)) {
FileError("Expecting %s or %s", typenames[ARC_Object], typenames[ARC_Entity]);
}
size = ReadSize();
ArchiveString(&classname);
cls = getClass(classname.c_str());
if (!cls) {
FileError("Invalid class %s.", classname.c_str());
}
#if defined(GAME_DLL)
isent = checkInheritance(&Entity::ClassInfo, cls);
if (type == ARC_Entity) {
if (!isent) {
FileError("Non-Entity class object '%s' saved as an Entity based object.", classname.c_str());
}
ArchiveInteger(&level.spawn_entnum);
//
// make sure to setup spawnflags properly
//
ArchiveInteger(&level.spawnflags);
} else if (isent) {
FileError("Entity class object '%s' saved as non-Entity based object.", classname.c_str());
}
#else
isent = false;
#endif
ArchiveInteger(&index);
objstart = archivefile.Pos();
obj = (Class *)cls->newInstance();
if (!obj) {
FileError("Failed to on new instance of class %s.", classname.c_str());
} else {
obj->Archive(*this);
}
if (!fileerror) {
endpos = archivefile.Pos();
if ((endpos - objstart) > size) {
FileError("Object read past end of object's data");
} else if ((endpos - objstart) < size) {
FileError("Object didn't read entire data from file");
}
}
//
// register this pointer with our list
//
classpointerList.AddObjectAt(index, obj);
return obj;
}
void Archiver::ArchiveObject(Class *obj)
{
str classname;
int index;
fileSize_t size;
qboolean isent;
if (archivemode == ARCHIVE_READ) {
ClassDef *cls;
size_t objstart;
size_t endpos;
int type;
CheckRead();
type = ReadType();
if ((type != ARC_Object) && (type != ARC_Entity)) {
FileError("Expecting %s or %s", typenames[ARC_Object], typenames[ARC_Entity]);
}
size = ReadSize();
ArchiveString(&classname);
cls = getClass(classname.c_str());
if (!cls) {
FileError("Invalid class %s.", classname.c_str());
}
if (obj->classinfo() != cls) {
FileError(
"Archive has a '%s' object, but was expecting a '%s' object.", classname.c_str(), obj->getClassname()
);
}
#if defined(GAME_DLL)
isent = obj->isSubclassOf(Entity);
if (type == ARC_Entity) {
int entnum;
if (!isent) {
FileError("Non-Entity class object '%s' saved as an Entity based object.", classname.c_str());
}
ArchiveInteger(&entnum);
((Entity *)obj)->entnum = entnum;
//
// make sure to setup spawnflags properly
//
ArchiveInteger(&level.spawnflags);
} else if (isent) {
FileError("Entity class object '%s' saved as non-Entity based object.", classname.c_str());
}
#else
isent = false;
#endif
ArchiveInteger(&index);
objstart = archivefile.Pos();
obj->Archive(*this);
if (!fileerror) {
endpos = archivefile.Pos();
if ((endpos - objstart) > size) {
FileError("Object read past end of object's data");
} else if ((endpos - objstart) < size) {
FileError("Object didn't read entire data from file");
}
}
//
// register this pointer with our list
//
classpointerList.AddObjectAt(index, obj);
} else {
long sizepos;
long objstart = 0;
long endpos;
assert(obj);
if (!obj) {
FileError("NULL object in WriteObject");
}
#if defined(GAME_DLL)
isent = obj->isSubclassOf(Entity);
#else
isent = false;
#endif
CheckWrite();
if (isent) {
WriteType(ARC_Entity);
} else {
WriteType(ARC_Object);
}
sizepos = archivefile.Tell();
size = 0;
WriteSize(size);
classname = obj->getClassname();
ArchiveString(&classname);
#if defined(GAME_DLL)
if (isent) {
// Write out the entity number
ArchiveInteger(&((Entity *)obj)->entnum);
//
// make sure to setup spawnflags properly
//
ArchiveInteger(&((Entity *)obj)->spawnflags);
}
#endif
// write out pointer index for this class pointer
index = classpointerList.AddUniqueObject(obj);
ArchiveInteger(&index);
if (!fileerror) {
objstart = archivefile.Tell();
obj->Archive(*this);
}
if (!fileerror) {
endpos = archivefile.Tell();
size = endpos - objstart;
archivefile.Seek(sizepos);
WriteSize(size);
if (!fileerror) {
archivefile.Seek(archivefile.Length());
}
}
}
}
void Archiver::ArchiveObject(SafePtrBase* obj)
{
ArchiveSafePointer(obj);
}
void Archiver::ArchiveObjectPosition(LightClass *obj)
{
int index = 0;
if (archivemode == ARCHIVE_READ) {
ArchivePosition(&index);
classpointerList.AddObjectAt(index, (Class *)obj);
} else {
index = classpointerList.AddUniqueObject((Class *)obj);
ArchivePosition(&index);
}
}
qboolean Archiver::ObjectPositionExists(void *obj)
{
return classpointerList.IndexOfObject((Class *)obj) != 0;
}
void Archiver::CheckRead(void)
{
assert(archivemode == ARCHIVE_READ);
if (!fileerror && (archivemode != ARCHIVE_READ)) {
FileError("File read during a write operation.");
}
}
void Archiver::CheckWrite(void)
{
assert(archivemode == ARCHIVE_WRITE);
if (!fileerror && (archivemode != ARCHIVE_WRITE)) {
FileError("File write during a read operation.");
}
}
qboolean Archiver::Read(str& name, qboolean harderror)
{
return Read(name.c_str(), harderror);
}
qboolean Archiver::Create(str& name, qboolean harderror)
{
return Create(name.c_str(), harderror);
}
qboolean Archiver::Loading(void)
{
return (archivemode == ARCHIVE_READ) ? qtrue : qfalse;
}
qboolean Archiver::Saving(void)
{
return (archivemode == ARCHIVE_WRITE) ? qtrue : qfalse;
}
qboolean Archiver::NoErrors(void)
{
return fileerror ? qfalse : qtrue;
}
size_t Archiver::Counter() const
{
return m_iNumBytesIO;
}
void Archiver::Reset()
{
m_iNumBytesIO = 0;
}
fileSize_t Archiver::ReadSize(void)
{
fileSize_t s;
s = 0;
if (!fileerror) {
archivefile.Read(&s, sizeof(s));
LittleSwap(&s, sizeof(s));
}
return s;
}
void Archiver::CheckSize(int type, fileSize_t size)
{
fileSize_t s;
if (!fileerror) {
s = ReadSize();
if (size != s) {
FileError("Invalid data size of %d on %s.", s, typenames[type]);
}
}
}
void Archiver::WriteSize(fileSize_t size)
{
LittleSwap(&size, sizeof(size));
archivefile.Write(&size, sizeof(fileSize_t));
}
int Archiver::ReadType(void)
{
int t;
if (!fileerror) {
archivefile.Read(&t, sizeof(t));
t = LittleLong(t);
return t;
}
return ARC_NULL;
}
void Archiver::WriteType(int type)
{
archivefile.Write(&type, sizeof(type));
}
void Archiver::CheckType(int type)
{
int t;
assert((type >= 0) && (type < ARC_NUMTYPES));
if (archivemode == ARCHIVE_READ) {
if (!fileerror) {
t = ReadType();
if (t != type) {
if (t < ARC_NUMTYPES) {
FileError("Expecting %s, Should be %s", typenames[type], typenames[t]);
assert(0);
}
else {
FileError("Expecting %s, Should be %i (Unknown type)", typenames[type], t);
}
}
}
} else {
int nt = LittleLong(type);
archivefile.Write(&nt, sizeof(nt));
}
}
void Archiver::ArchiveData(int type, void *data, size_t size)
{
#ifdef ARCHIVE_USE_TYPES
CheckType(type);
#endif
if (archivemode == ARCHIVE_READ) {
#ifndef NDEBUG
CheckRead();
#endif
if (!fileerror && size) {
m_iNumBytesIO += size;
archivefile.Read(data, size);
}
} else {
#ifndef NDEBUG
CheckWrite();
#endif
if (!fileerror && size) {
m_iNumBytesIO += size;
archivefile.Write(data, size);
}
}
}
void Archiver::ArchiveConfigString(int cs)
{
str s;
if (archivemode == ARCHIVE_READ) {
ArchiveString(&s);
gi.setConfigstring(cs, s.c_str());
} else {
s = gi.getConfigstring(cs);
ArchiveString(&s);
}
}
void Archiver::SetSilent(bool bSilent)
{
silent = bSilent;
}