1164 lines
21 KiB
C++
1164 lines
21 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /Quake 2 Engine/Sin/code/game/archive.cpp $
|
|
// $Revision:: 19 $
|
|
// $Author:: Aldie $
|
|
// $Date:: 11/12/98 3:46p $
|
|
//
|
|
// Copyright (C) 1997 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source is may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
// $Log:: /Quake 2 Engine/Sin/code/game/archive.cpp $
|
|
//
|
|
// 19 11/12/98 3:46p Aldie
|
|
// Fixed length check for files not found.
|
|
//
|
|
// 18 11/12/98 2:41p Markd
|
|
// added assert for pos in Read
|
|
//
|
|
// 17 11/12/98 12:19p Markd
|
|
// archive file was being loaded as TAG_GAME this was a VERY BAD thing.
|
|
//
|
|
// 16 11/12/98 2:31a Jimdose
|
|
// Added ReadFile. Archives now read from pak files
|
|
//
|
|
// 15 10/19/98 12:04a Jimdose
|
|
// made all code use fast checks for inheritance (no text lookups when
|
|
// possible)
|
|
// isSubclassOf no longer requires ::_classinfo()
|
|
//
|
|
// 14 10/10/98 1:26a Jimdose
|
|
// ReadObject no longer cancels objects events
|
|
//
|
|
// 13 10/07/98 11:41p Jimdose
|
|
// Got savegames working!!!
|
|
// Rewrote event archiving
|
|
//
|
|
// 12 9/22/98 7:18p Markd
|
|
// forgot to free up a list of objects when closing the file
|
|
//
|
|
// 11 9/22/98 3:58a Jimdose
|
|
// Incremented the archive version number
|
|
//
|
|
// 10 9/21/98 10:15p Markd
|
|
// Putting archiving and unarchiving functions in
|
|
//
|
|
// 9 9/21/98 4:21p Markd
|
|
// Put in archive functions and rewrote all archive routines
|
|
//
|
|
// 8 7/26/98 2:15a Jimdose
|
|
// ReadObject was casting a Class * as a Entity *. Not bad, but wrong.
|
|
//
|
|
// 7 6/11/98 7:18p Jimdose
|
|
// FileError now closes the file before exiting. This prevents an assertion
|
|
// when the object is deleted but the file is still open.
|
|
//
|
|
// 6 5/24/98 8:46p Jimdose
|
|
// Made a lot of functions more str-friendly.
|
|
// Got rid of a lot of char * based strings
|
|
// Cleaned up get spawn arg functions and sound functions
|
|
// sound functions now use consistant syntax
|
|
//
|
|
// 5 5/24/98 4:48p Jimdose
|
|
// Made char *'s const
|
|
//
|
|
// 4 5/09/98 8:04p Jimdose
|
|
// Create now creates the path if it doesn't exist
|
|
//
|
|
// 3 5/08/98 2:50p Jimdose
|
|
// fixed bugs
|
|
//
|
|
// 2 5/07/98 10:39p Jimdose
|
|
// created file
|
|
//
|
|
// 1 5/06/98 8:19p Jimdose
|
|
//
|
|
// DESCRIPTION:
|
|
// Class for archiving objects
|
|
//
|
|
#include "g_local.h"
|
|
#include "archive.h"
|
|
|
|
#define ARCHIVE_WRITE 0
|
|
#define ARCHIVE_READ 1
|
|
|
|
enum
|
|
{
|
|
ARC_NULL, ARC_Vector, 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_Event, ARC_Quat, ARC_Entity,
|
|
ARC_NUMTYPES
|
|
};
|
|
|
|
static const char *typenames[] =
|
|
{
|
|
"NULL", "vector", "int", "unsigned", "byte", "char", "short", "unsigned short",
|
|
"float", "double", "qboolean", "string", "raw data", "object", "objectpointer",
|
|
"safepointer", "event", "quaternion", "entity"
|
|
};
|
|
|
|
#define ArchiveHeader ( *( int * )"SIN\0" )
|
|
#define ArchiveVersion 2 // This must be changed any time the format changes!
|
|
#define ArchiveInfo "Sin Archive Version 2" // This must be changed any time the format changes!
|
|
|
|
CLASS_DECLARATION( Class, ReadFile, NULL );
|
|
|
|
ResponseDef ReadFile::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
ReadFile::ReadFile()
|
|
{
|
|
length = 0;
|
|
buffer = NULL;
|
|
pos = 0;
|
|
}
|
|
|
|
ReadFile::~ReadFile()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
void ReadFile::Close
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( buffer )
|
|
{
|
|
gi.TagFree( ( void * )buffer );
|
|
buffer = NULL;
|
|
}
|
|
|
|
filename = "";
|
|
length = 0;
|
|
pos = 0;
|
|
}
|
|
|
|
const char *ReadFile::Filename
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return filename.c_str();
|
|
}
|
|
|
|
size_t ReadFile::Length
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return length;
|
|
}
|
|
|
|
size_t ReadFile::Pos
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return pos - buffer;
|
|
}
|
|
|
|
qboolean ReadFile::Seek
|
|
(
|
|
size_t newpos
|
|
)
|
|
|
|
{
|
|
if ( !buffer )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( newpos < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( newpos > length )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pos = buffer + newpos;
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean ReadFile::Open
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
assert( name );
|
|
|
|
assert( !buffer );
|
|
Close();
|
|
|
|
if ( !name )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
length = gi.LoadFile( name, ( void ** )&buffer, 0 );
|
|
if ( length == ( size_t )( -1 ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
filename = name;
|
|
pos = buffer;
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean ReadFile::Read
|
|
(
|
|
void *dest,
|
|
size_t size
|
|
)
|
|
|
|
{
|
|
assert( dest );
|
|
assert( buffer );
|
|
assert( pos );
|
|
|
|
if ( !dest )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( size <= 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( ( pos + size ) > ( buffer + length ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memcpy( dest, pos, size );
|
|
pos += size;
|
|
|
|
return true;
|
|
}
|
|
|
|
CLASS_DECLARATION( Class, Archiver, NULL );
|
|
|
|
ResponseDef Archiver::Responses[] =
|
|
{
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
Archiver::Archiver()
|
|
{
|
|
file = NULL;
|
|
fileerror = false;
|
|
assert( ( sizeof( typenames ) / sizeof( typenames[ 0 ] ) ) == ARC_NUMTYPES );
|
|
}
|
|
|
|
Archiver::~Archiver()
|
|
{
|
|
if ( file )
|
|
{
|
|
Close();
|
|
}
|
|
|
|
readfile.Close();
|
|
}
|
|
|
|
void Archiver::FileError
|
|
(
|
|
const char *fmt,
|
|
...
|
|
)
|
|
|
|
{
|
|
va_list argptr;
|
|
char text[ 1024 ];
|
|
|
|
va_start( argptr, fmt );
|
|
vsprintf( text, fmt, argptr );
|
|
va_end( argptr );
|
|
|
|
fileerror = true;
|
|
Close();
|
|
if ( archivemode == ARCHIVE_READ )
|
|
{
|
|
gi.error( "Error while loading %s : %s\n", filename.c_str(), text );
|
|
}
|
|
else
|
|
{
|
|
gi.error( "Error while writing to %s : %s\n", filename.c_str(), text );
|
|
}
|
|
}
|
|
|
|
void Archiver::Close
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( file )
|
|
{
|
|
if ( archivemode == ARCHIVE_WRITE )
|
|
{
|
|
// write out the number of classpointers
|
|
fseek( file, numclassespos, SEEK_SET );
|
|
numclassespos = ftell( file );
|
|
WriteInteger( classpointerList.NumObjects() );
|
|
}
|
|
|
|
fclose( file );
|
|
file = NULL;
|
|
}
|
|
|
|
readfile.Close();
|
|
|
|
if ( archivemode == ARCHIVE_READ )
|
|
{
|
|
int i, num;
|
|
Class * classptr;
|
|
pointer_fixup_t *fixup;
|
|
|
|
num = fixupList.NumObjects();
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
fixup = fixupList.ObjectAt( i );
|
|
classptr = classpointerList.ObjectAt( fixup->index );
|
|
if ( fixup->type == pointer_fixup_normal )
|
|
{
|
|
Class ** fixupptr;
|
|
fixupptr = ( Class ** )fixup->ptr;
|
|
*fixupptr = classptr;
|
|
}
|
|
else if ( fixup->type == pointer_fixup_safe )
|
|
{
|
|
SafePtrBase * fixupptr;
|
|
fixupptr = ( SafePtrBase * )fixup->ptr;
|
|
fixupptr->InitSafePtr( classptr );
|
|
}
|
|
delete fixup;
|
|
}
|
|
fixupList.FreeObjectList();
|
|
classpointerList.FreeObjectList();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************************
|
|
|
|
File Read functions
|
|
|
|
*****************************************************************************************/
|
|
|
|
void Archiver::Read
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
unsigned header;
|
|
unsigned version;
|
|
str info;
|
|
int num;
|
|
int i;
|
|
Class *null;
|
|
|
|
assert( name );
|
|
if ( !name )
|
|
{
|
|
gi.error( "NULL pointer for filename in Archiver::Read.\n" );
|
|
}
|
|
|
|
fileerror = false;
|
|
|
|
archivemode = ARCHIVE_READ;
|
|
|
|
filename = name;
|
|
|
|
if ( !readfile.Open( filename.c_str() ) )
|
|
{
|
|
FileError( "Couldn't open file." );
|
|
}
|
|
|
|
header = ReadUnsigned();
|
|
if ( header != ArchiveHeader )
|
|
{
|
|
readfile.Close();
|
|
FileError( "Not a valid Sin archive." );
|
|
}
|
|
|
|
version = ReadUnsigned();
|
|
if ( version > ArchiveVersion )
|
|
{
|
|
readfile.Close();
|
|
FileError( "Archive is from version %.2f. Check http://www.ritual.com for an update.", version );
|
|
}
|
|
|
|
if ( version < ArchiveVersion )
|
|
{
|
|
readfile.Close();
|
|
FileError( "Archive is out of date." );
|
|
}
|
|
|
|
info = ReadString();
|
|
gi.dprintf( "%s\n", info.c_str() );
|
|
|
|
// setup out class pointers
|
|
num = ReadInteger();
|
|
classpointerList.Resize( num );
|
|
null = NULL;
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
classpointerList.AddObject( null );
|
|
}
|
|
}
|
|
|
|
inline void Archiver::CheckRead
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
assert( archivemode == ARCHIVE_READ );
|
|
if ( !fileerror && ( archivemode != ARCHIVE_READ ) )
|
|
{
|
|
FileError( "File read during a write operation." );
|
|
}
|
|
}
|
|
|
|
inline int Archiver::ReadType
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int t;
|
|
|
|
if ( !fileerror )
|
|
{
|
|
readfile.Read( &t, sizeof( t ) );
|
|
|
|
return t;
|
|
}
|
|
|
|
return ARC_NULL;
|
|
}
|
|
|
|
inline void Archiver::CheckType
|
|
(
|
|
int type
|
|
)
|
|
|
|
{
|
|
int t;
|
|
|
|
assert( ( type >= 0 ) && ( type < ARC_NUMTYPES ) );
|
|
|
|
if ( !fileerror )
|
|
{
|
|
t = ReadType();
|
|
if ( t != type )
|
|
{
|
|
FileError( "Expecting %s", typenames[ type ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
inline size_t Archiver::ReadSize
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
size_t s;
|
|
|
|
s = 0;
|
|
if ( !fileerror )
|
|
{
|
|
readfile.Read( &s, sizeof( s ) );
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
inline void Archiver::CheckSize
|
|
(
|
|
int type,
|
|
size_t size
|
|
)
|
|
|
|
{
|
|
size_t s;
|
|
|
|
if ( !fileerror )
|
|
{
|
|
s = ReadSize();
|
|
|
|
if ( size != s )
|
|
{
|
|
FileError( "Invalid data size of %d on %s.", s, typenames[ type ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void Archiver::ReadData
|
|
(
|
|
int type,
|
|
void *data,
|
|
size_t size
|
|
)
|
|
|
|
{
|
|
CheckRead();
|
|
CheckType( type );
|
|
CheckSize( type, size );
|
|
|
|
if ( !fileerror && size )
|
|
{
|
|
readfile.Read( data, size );
|
|
}
|
|
}
|
|
|
|
#define READ( func, type ) \
|
|
type Archiver::Read##func \
|
|
( \
|
|
void \
|
|
) \
|
|
\
|
|
{ \
|
|
type v; \
|
|
\
|
|
ReadData( ARC_##func, &v, sizeof( type ) ); \
|
|
\
|
|
return v; \
|
|
}
|
|
|
|
READ( Vector, Vector );
|
|
READ( Integer, int );
|
|
READ( Unsigned, unsigned );
|
|
READ( Byte, byte );
|
|
READ( Char, char );
|
|
READ( Short, short );
|
|
READ( UnsignedShort, unsigned short );
|
|
READ( Float, float );
|
|
READ( Double, double );
|
|
READ( Boolean, qboolean );
|
|
READ( Quat, Quat );
|
|
|
|
#define READPTR( func, type ) \
|
|
void Archiver::Read##func \
|
|
( \
|
|
type * v \
|
|
) \
|
|
\
|
|
{ \
|
|
ReadData( ARC_##func, v, sizeof( type ) ); \
|
|
}
|
|
|
|
READPTR( Vector, Vector );
|
|
READPTR( Integer, int );
|
|
READPTR( Unsigned, unsigned );
|
|
READPTR( Byte, byte );
|
|
READPTR( Char, char );
|
|
READPTR( Short, short );
|
|
READPTR( UnsignedShort, unsigned short );
|
|
READPTR( Float, float );
|
|
READPTR( Double, double );
|
|
READPTR( Boolean, qboolean );
|
|
READPTR( Quat, Quat );
|
|
|
|
void Archiver::ReadObjectPointer
|
|
(
|
|
Class ** ptr
|
|
)
|
|
|
|
{
|
|
int index;
|
|
pointer_fixup_t *fixup;
|
|
|
|
ReadData( ARC_ObjectPointer, &index, sizeof( index ) );
|
|
|
|
// Check for a NULL pointer
|
|
assert( ptr );
|
|
if ( !ptr )
|
|
{
|
|
FileError( "NULL pointer in ReadObjectPointer." );
|
|
}
|
|
|
|
//
|
|
// 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 );
|
|
}
|
|
}
|
|
|
|
void Archiver::ReadSafePointer
|
|
(
|
|
SafePtrBase * ptr
|
|
)
|
|
|
|
{
|
|
int index;
|
|
pointer_fixup_t *fixup;
|
|
|
|
ReadData( ARC_SafePointer, &index, sizeof( &index ) );
|
|
|
|
// Check for a NULL pointer
|
|
assert( ptr );
|
|
if ( !ptr )
|
|
{
|
|
FileError( "NULL pointer in ReadSafePointer." );
|
|
}
|
|
|
|
//
|
|
// 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 );
|
|
}
|
|
}
|
|
|
|
Event Archiver::ReadEvent
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
Event ev;
|
|
|
|
CheckRead();
|
|
CheckType( ARC_Event );
|
|
|
|
if ( !fileerror )
|
|
{
|
|
ev.Unarchive( *this );
|
|
}
|
|
|
|
return ev;
|
|
}
|
|
|
|
void Archiver::ReadEvent
|
|
(
|
|
Event * ev
|
|
)
|
|
|
|
{
|
|
CheckRead();
|
|
CheckType( ARC_Event );
|
|
|
|
if ( !fileerror )
|
|
{
|
|
ev->Unarchive( *this );
|
|
}
|
|
}
|
|
|
|
void Archiver::ReadRaw
|
|
(
|
|
void *data,
|
|
size_t size
|
|
)
|
|
|
|
{
|
|
ReadData( ARC_Raw, data, size );
|
|
}
|
|
|
|
str Archiver::ReadString
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
size_t s;
|
|
char *data;
|
|
str string;
|
|
|
|
CheckRead();
|
|
CheckType( ARC_String );
|
|
|
|
if ( !fileerror )
|
|
{
|
|
s = ReadSize();
|
|
if ( !fileerror )
|
|
{
|
|
data = new char[ s + 1 ];
|
|
if ( s )
|
|
{
|
|
readfile.Read( data, s );
|
|
}
|
|
data[ s ] = 0;
|
|
|
|
string = data;
|
|
|
|
delete [] data;
|
|
}
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
void Archiver::ReadString
|
|
(
|
|
str * string
|
|
)
|
|
|
|
{
|
|
*string = ReadString();
|
|
}
|
|
|
|
Class *Archiver::ReadObject
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
ClassDef *cls;
|
|
Class *obj;
|
|
str classname;
|
|
long objstart;
|
|
long 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();
|
|
classname = ReadString();
|
|
|
|
cls = getClass( classname.c_str() );
|
|
if ( !cls )
|
|
{
|
|
FileError( "Invalid class %s.", classname.c_str() );
|
|
}
|
|
|
|
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() );
|
|
}
|
|
|
|
game.force_entnum = true;
|
|
game.spawn_entnum = ReadInteger();
|
|
}
|
|
else if ( isent )
|
|
{
|
|
FileError( "Entity class object '%s' saved as non-Entity based object.", classname.c_str() );
|
|
}
|
|
|
|
index = ReadInteger();
|
|
objstart = readfile.Pos();
|
|
|
|
obj = ( Class * )cls->newInstance();
|
|
if ( !obj )
|
|
{
|
|
FileError( "Failed to on new instance of class %s.", classname.c_str() );
|
|
}
|
|
else
|
|
{
|
|
obj->Unarchive( *this );
|
|
}
|
|
|
|
if ( isent )
|
|
{
|
|
game.force_entnum = false;
|
|
}
|
|
|
|
if ( !fileerror )
|
|
{
|
|
endpos = readfile.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;
|
|
}
|
|
|
|
Class *Archiver::ReadObject
|
|
(
|
|
Class *obj
|
|
)
|
|
|
|
{
|
|
ClassDef *cls;
|
|
str classname;
|
|
long objstart;
|
|
long endpos;
|
|
int index;
|
|
size_t size;
|
|
int type;
|
|
qboolean isent;
|
|
|
|
CheckRead();
|
|
type = ReadType();
|
|
if ( ( type != ARC_Object ) && ( type != ARC_Entity ) )
|
|
{
|
|
FileError( "Expecting %s or %s", typenames[ ARC_Object ], typenames[ ARC_Entity ] );
|
|
}
|
|
|
|
size = ReadSize();
|
|
classname = ReadString();
|
|
|
|
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() );
|
|
}
|
|
|
|
isent = obj->isSubclassOf( Entity );
|
|
if ( type == ARC_Entity )
|
|
{
|
|
if ( !isent )
|
|
{
|
|
FileError( "Non-Entity class object '%s' saved as an Entity based object.", classname.c_str() );
|
|
}
|
|
|
|
( ( Entity * )obj )->SetEntNum( ReadInteger() );
|
|
}
|
|
else if ( isent )
|
|
{
|
|
FileError( "Entity class object '%s' saved as non-Entity based object.", classname.c_str() );
|
|
}
|
|
|
|
index = ReadInteger();
|
|
objstart = readfile.Pos();
|
|
|
|
obj->Unarchive( *this );
|
|
|
|
if ( !fileerror )
|
|
{
|
|
endpos = readfile.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;
|
|
}
|
|
|
|
/****************************************************************************************
|
|
|
|
File Write functions
|
|
|
|
*****************************************************************************************/
|
|
|
|
void Archiver::Create
|
|
(
|
|
const char *name
|
|
)
|
|
|
|
{
|
|
assert( name );
|
|
if ( !name )
|
|
{
|
|
gi.error( "NULL pointer for filename in Archiver::Create.\n" );
|
|
}
|
|
|
|
fileerror = false;
|
|
|
|
archivemode = ARCHIVE_WRITE;
|
|
|
|
filename = name;
|
|
|
|
gi.CreatePath( filename.c_str() );
|
|
file = fopen( filename.c_str(), "wb" );
|
|
if ( !file )
|
|
{
|
|
FileError( "Couldn't open file." );
|
|
}
|
|
|
|
WriteUnsigned( ArchiveHeader );
|
|
WriteUnsigned( ArchiveVersion );
|
|
WriteString( str( ArchiveInfo ) );
|
|
|
|
numclassespos = ftell( file );
|
|
WriteInteger( 0 );
|
|
}
|
|
|
|
inline void Archiver::CheckWrite
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
assert( archivemode == ARCHIVE_WRITE );
|
|
if ( !fileerror && ( archivemode != ARCHIVE_WRITE ) )
|
|
{
|
|
FileError( "File write during a read operation." );
|
|
}
|
|
}
|
|
|
|
inline void Archiver::WriteType
|
|
(
|
|
int type
|
|
)
|
|
|
|
{
|
|
fwrite( &type, sizeof( type ), 1, file );
|
|
}
|
|
|
|
inline void Archiver::WriteSize
|
|
(
|
|
size_t size
|
|
)
|
|
|
|
{
|
|
fwrite( &size, sizeof( size ), 1, file );
|
|
}
|
|
|
|
inline void Archiver::WriteData
|
|
(
|
|
int type,
|
|
const void *data,
|
|
size_t size
|
|
)
|
|
|
|
{
|
|
CheckWrite();
|
|
WriteType( type );
|
|
WriteSize( size );
|
|
|
|
if ( !fileerror && size )
|
|
{
|
|
fwrite( data, size, 1, file );
|
|
}
|
|
}
|
|
|
|
#define WRITE( func, type ) \
|
|
void Archiver::Write##func \
|
|
( \
|
|
type v \
|
|
) \
|
|
\
|
|
{ \
|
|
WriteData( ARC_##func, &v, sizeof( type ) ); \
|
|
}
|
|
|
|
WRITE( Vector, Vector & );
|
|
WRITE( Quat, Quat & );
|
|
WRITE( Integer, int );
|
|
WRITE( Unsigned, unsigned );
|
|
WRITE( Byte, byte );
|
|
WRITE( Char, char );
|
|
WRITE( Short, short );
|
|
WRITE( UnsignedShort, unsigned short );
|
|
WRITE( Float, float );
|
|
WRITE( Double, double );
|
|
WRITE( Boolean, qboolean );
|
|
|
|
void Archiver::WriteRaw
|
|
(
|
|
const void *data,
|
|
size_t size
|
|
)
|
|
|
|
{
|
|
WriteData( ARC_Raw, data, size );
|
|
}
|
|
|
|
void Archiver::WriteString
|
|
(
|
|
str &string
|
|
)
|
|
|
|
{
|
|
WriteData( ARC_String, string.c_str(), string.length() );
|
|
}
|
|
|
|
void Archiver::WriteObject
|
|
(
|
|
Class *obj
|
|
)
|
|
|
|
{
|
|
str classname;
|
|
long sizepos;
|
|
long objstart;
|
|
long endpos;
|
|
int index;
|
|
size_t size;
|
|
qboolean isent;
|
|
|
|
assert( obj );
|
|
if ( !obj )
|
|
{
|
|
FileError( "NULL object in WriteObject" );
|
|
}
|
|
|
|
isent = obj->isSubclassOf( Entity );
|
|
|
|
CheckWrite();
|
|
if ( isent )
|
|
{
|
|
WriteType( ARC_Entity );
|
|
}
|
|
else
|
|
{
|
|
WriteType( ARC_Object );
|
|
}
|
|
|
|
sizepos = ftell( file );
|
|
size = 0;
|
|
WriteSize( size );
|
|
|
|
classname = obj->getClassname();
|
|
WriteString( classname );
|
|
|
|
if ( isent )
|
|
{
|
|
// Write out the entity number
|
|
WriteInteger( ( ( Entity * )obj )->entnum );
|
|
}
|
|
|
|
// write out pointer index for this class pointer
|
|
index = classpointerList.AddUniqueObject( obj );
|
|
WriteInteger( index );
|
|
|
|
if ( !fileerror )
|
|
{
|
|
objstart = ftell( file );
|
|
obj->Archive( *this );
|
|
}
|
|
|
|
if ( !fileerror )
|
|
{
|
|
endpos = ftell( file );
|
|
size = endpos - objstart;
|
|
fseek( file, sizepos, SEEK_SET );
|
|
WriteSize( size );
|
|
|
|
if ( !fileerror )
|
|
{
|
|
fseek( file, endpos, SEEK_SET );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Archiver::WriteObjectPointer
|
|
(
|
|
Class * ptr
|
|
)
|
|
|
|
{
|
|
int index;
|
|
|
|
if ( ptr )
|
|
{
|
|
index = classpointerList.AddUniqueObject( ptr );
|
|
}
|
|
else
|
|
{
|
|
index = ARCHIVE_NULL_POINTER;
|
|
}
|
|
WriteData( ARC_ObjectPointer, &index, sizeof( index ) );
|
|
}
|
|
|
|
void Archiver::WriteSafePointer
|
|
(
|
|
Class * ptr
|
|
)
|
|
|
|
{
|
|
int index;
|
|
|
|
if ( ptr )
|
|
{
|
|
index = classpointerList.AddUniqueObject( ptr );
|
|
}
|
|
else
|
|
{
|
|
index = ARCHIVE_NULL_POINTER;
|
|
}
|
|
WriteData( ARC_SafePointer, &index, sizeof( index ) );
|
|
}
|
|
|
|
void Archiver::WriteEvent
|
|
(
|
|
Event &ev
|
|
)
|
|
|
|
{
|
|
CheckWrite();
|
|
WriteType( ARC_Event );
|
|
|
|
//FIXME!!!! Make this handle null events
|
|
if ( &ev == NULL )
|
|
{
|
|
NullEvent.Archive( *this );
|
|
}
|
|
else
|
|
{
|
|
ev.Archive( *this );
|
|
}
|
|
}
|