mirror of
https://github.com/dhewm/dhewm3-sdk.git
synced 2024-11-25 05:51:15 +00:00
Fix savegame-compatibility of scripts, increase BUILD_NUMBER
"Fix "t->c->value.argSize == func->parmTotal" Assertion in Scripts, #303" had broken old savegames because the script checksum (idProgram::CalculateChecksum()) changed, see #344. This is fixed now, also the BUILD_NUMBER is increased so old savegames can be identified for a workaround. Don't use this commit without the next one which will further modify the savegame format (for the new BUILD_NUMBER 1305)
This commit is contained in:
parent
372ae8bb73
commit
ef2ac00ebe
8 changed files with 213 additions and 81 deletions
|
@ -987,6 +987,25 @@ idVarDef *idCompiler::EmitFunctionParms( int op, idVarDef *func, int startarg, i
|
||||||
// need arg size seperate since script object may be NULL
|
// need arg size seperate since script object may be NULL
|
||||||
statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
|
statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
|
||||||
statement.c = SizeConstant( func->value.functionPtr->parmTotal );
|
statement.c = SizeConstant( func->value.functionPtr->parmTotal );
|
||||||
|
// DG: before changes I did to ParseFunctionDef(), func->value.functionPtr->parmTotal was 0
|
||||||
|
// if the function declaration/prototype has been parsed already, but the
|
||||||
|
// definition/implementation hadn't been parsed yet. That was wrong and sometimes
|
||||||
|
// (with debug game DLLs) lead to assertions in custom scripts, because the
|
||||||
|
// stack space reserved for function parameters was wrong.
|
||||||
|
// Now func->value.functionPtr->parmTotal is calculated when parsing the prototype,
|
||||||
|
// but func->value.functionPtr->parmSize[i] is still only calculated when parsing
|
||||||
|
// the implementation (as it's not needed before and so we can tell the cases apart here).
|
||||||
|
// However, savegames from before the change have script checksums
|
||||||
|
// (by idProgram::CalculateChecksum()) from statements with the wrong size, so
|
||||||
|
// loading them would fail as the checksum doesn't match.
|
||||||
|
// Setting this flag allows using the parmTotal argSize 0 when calculating the checksum
|
||||||
|
// so it matches the one from old savegames (unless something else has also changed in
|
||||||
|
// the script state so they really are incompatible). That's only done when actually
|
||||||
|
// loading old savegames (detected via BUILD_NUMBER/savegame.GetBuildNumber())
|
||||||
|
if ( op == OP_OBJECTCALL && func->value.functionPtr->parmTotal > 0
|
||||||
|
&& func->value.functionPtr->parmSize.Num() == 0 ) {
|
||||||
|
statement.flags = statement_t::FLAG_OBJECTCALL_IMPL_NOT_PARSED_YET;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
EmitOpcode( op, func, SizeConstant( size ) );
|
EmitOpcode( op, func, SizeConstant( size ) );
|
||||||
}
|
}
|
||||||
|
@ -2144,18 +2163,36 @@ void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DG: make sure parmSize gets calculated when parsing prototype (not just when parsing
|
// DG: make sure parmTotal gets calculated when parsing prototype (not just when parsing
|
||||||
// implementation) so calling this function/method before implementation has been parsed
|
// implementation) so calling this function/method before the implementation has been parsed
|
||||||
// works without getting Assertions in IdInterpreter::Execute() and ::LeaveFunction()
|
// works without getting Assertions in IdInterpreter::Execute() and ::LeaveFunction()
|
||||||
// ("st->c->value.argSize == func->parmTotal", "localstackUsed == localstackBase", see #303)
|
// ("st->c->value.argSize == func->parmTotal", "localstackUsed == localstackBase", see #303 and #344)
|
||||||
|
|
||||||
// calculate stack space used by parms
|
// calculate stack space used by parms
|
||||||
numParms = type->NumParameters();
|
numParms = type->NumParameters();
|
||||||
if( numParms != func->parmSize.Num() ) { // DG: make sure not to do this twice
|
if ( !CheckToken( "{" ) ) {
|
||||||
|
// it's just a prototype, so get the ; and move on
|
||||||
|
ExpectToken( ";" );
|
||||||
|
// DG: BUT only after calculating the stack space for the arguments because this
|
||||||
|
// function might be called before the implementation is parsed (see #303 and #344)
|
||||||
|
// which otherwise causes Assertions in IdInterpreter::Execute() and ::LeaveFunction()
|
||||||
|
// ("st->c->value.argSize == func->parmTotal", "localstackUsed == localstackBase")
|
||||||
|
func->parmTotal = 0;
|
||||||
|
for( i = 0; i < numParms; i++ ) {
|
||||||
|
parmType = type->GetParmType( i );
|
||||||
|
int size = parmType->Inherits( &type_object ) ? type_object.Size() : parmType->Size();
|
||||||
|
func->parmTotal += size;
|
||||||
|
// NOTE: Don't set func->parmSize[] yet, the workaround to keep compatibility
|
||||||
|
// with old savegames checks for func->parmSize.Num() == 0
|
||||||
|
// (see EmitFunctionParms() for more explanation of that workaround)
|
||||||
|
// Also not defining the parms yet, otherwise they're defined in a different order
|
||||||
|
// than before, so their .num is different which breaks compat with old savegames
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if it hasn't been parsed yet, parmSize.Num() should be 0..
|
|
||||||
assert( func->parmSize.Num() == 0 && "function had different number of arguments before?!" );
|
|
||||||
|
|
||||||
|
int totalSize = 0; // DG: totalsize might already have been calculated for the prototype, see a few lines above
|
||||||
func->parmSize.SetNum( numParms );
|
func->parmSize.SetNum( numParms );
|
||||||
for( i = 0; i < numParms; i++ ) {
|
for( i = 0; i < numParms; i++ ) {
|
||||||
parmType = type->GetParmType( i );
|
parmType = type->GetParmType( i );
|
||||||
|
@ -2164,8 +2201,11 @@ void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) {
|
||||||
} else {
|
} else {
|
||||||
func->parmSize[ i ] = parmType->Size();
|
func->parmSize[ i ] = parmType->Size();
|
||||||
}
|
}
|
||||||
func->parmTotal += func->parmSize[ i ];
|
totalSize += func->parmSize[ i ];
|
||||||
}
|
}
|
||||||
|
// DG: if parmTotal has been calculated before, it shouldn't have changed
|
||||||
|
assert((func->parmTotal == 0 || totalSize == func->parmTotal) && "function parameter sizes differ between protype vs implementation?!");
|
||||||
|
func->parmTotal = totalSize;
|
||||||
|
|
||||||
// define the parms
|
// define the parms
|
||||||
for( i = 0; i < numParms; i++ ) {
|
for( i = 0; i < numParms; i++ ) {
|
||||||
|
@ -2174,16 +2214,6 @@ void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) {
|
||||||
}
|
}
|
||||||
gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false );
|
gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DG: moved this down here so parmSize also gets calculated when parsing prototype
|
|
||||||
// check if this is a prototype or declaration
|
|
||||||
if ( !CheckToken( "{" ) ) {
|
|
||||||
// it's just a prototype, so get the ; and move on
|
|
||||||
ExpectToken( ";" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// DG end
|
|
||||||
|
|
||||||
oldscope = scope;
|
oldscope = scope;
|
||||||
scope = def;
|
scope = def;
|
||||||
|
|
|
@ -1634,7 +1634,9 @@ statement_t *idProgram::AllocStatement( void ) {
|
||||||
if ( statements.Num() >= statements.Max() ) {
|
if ( statements.Num() >= statements.Max() ) {
|
||||||
throw idCompileError( va( "Exceeded maximum allowed number of statements (%d)", statements.Max() ) );
|
throw idCompileError( va( "Exceeded maximum allowed number of statements (%d)", statements.Max() ) );
|
||||||
}
|
}
|
||||||
return statements.Alloc();
|
statement_t* ret = statements.Alloc();
|
||||||
|
ret->flags = 0; // DG: initialize the added flags (that are rarely set/used otherwise) to 0
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2005,7 +2007,7 @@ void idProgram::Save( idSaveGame *savefile ) const {
|
||||||
savefile->WriteByte( variables[i] );
|
savefile->WriteByte( variables[i] );
|
||||||
}
|
}
|
||||||
|
|
||||||
int checksum = CalculateChecksum();
|
int checksum = CalculateChecksum(false);
|
||||||
savefile->WriteInt( checksum );
|
savefile->WriteInt( checksum );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2039,9 +2041,11 @@ bool idProgram::Restore( idRestoreGame *savefile ) {
|
||||||
int saved_checksum, checksum;
|
int saved_checksum, checksum;
|
||||||
|
|
||||||
savefile->ReadInt( saved_checksum );
|
savefile->ReadInt( saved_checksum );
|
||||||
checksum = CalculateChecksum();
|
bool isOldSavegame = savefile->GetBuildNumber() <= 1304;
|
||||||
|
checksum = CalculateChecksum(isOldSavegame);
|
||||||
|
|
||||||
if ( saved_checksum != checksum ) {
|
if ( saved_checksum != checksum ) {
|
||||||
|
gameLocal.Warning( "WARNING: Real Script checksum didn't match the one from the savegame!");
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2053,7 +2057,7 @@ bool idProgram::Restore( idRestoreGame *savefile ) {
|
||||||
idProgram::CalculateChecksum
|
idProgram::CalculateChecksum
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
int idProgram::CalculateChecksum( void ) const {
|
int idProgram::CalculateChecksum( bool forOldSavegame ) const {
|
||||||
int i, result;
|
int i, result;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -2069,6 +2073,17 @@ int idProgram::CalculateChecksum( void ) const {
|
||||||
|
|
||||||
memset( statementList, 0, ( sizeof(statementBlock_t) * statements.Num() ) );
|
memset( statementList, 0, ( sizeof(statementBlock_t) * statements.Num() ) );
|
||||||
|
|
||||||
|
// DG hack: get the vardef for the argSize == 0 constant for savegame-compat
|
||||||
|
int constantZeroNum = -1;
|
||||||
|
if ( forOldSavegame ) {
|
||||||
|
for( idVarDef* def = GetDefList( "<IMMEDIATE>" ); def != NULL; def = def->Next() ) {
|
||||||
|
if ( def->Type() == ev_argsize && def->value.argSize == 0 ) {
|
||||||
|
constantZeroNum = def->num;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy info into new list, using the variable numbers instead of a pointer to the variable
|
// Copy info into new list, using the variable numbers instead of a pointer to the variable
|
||||||
for( i = 0; i < statements.Num(); i++ ) {
|
for( i = 0; i < statements.Num(); i++ ) {
|
||||||
statementList[i].op = statements[i].op;
|
statementList[i].op = statements[i].op;
|
||||||
|
@ -2084,7 +2099,15 @@ int idProgram::CalculateChecksum( void ) const {
|
||||||
statementList[i].b = -1;
|
statementList[i].b = -1;
|
||||||
}
|
}
|
||||||
if ( statements[i].c ) {
|
if ( statements[i].c ) {
|
||||||
|
// DG: old savegames wrongly assumed argSize 0 for some statements.
|
||||||
|
// So for the checksums to match we need to use the corresponding vardef num here
|
||||||
|
// See idCompiler::EmitFunctionParms() and ParseFunctionDef() for more details.
|
||||||
|
if ( forOldSavegame && statements[i].op == OP_OBJECTCALL
|
||||||
|
&& statements[i].flags == statement_t::FLAG_OBJECTCALL_IMPL_NOT_PARSED_YET ) {
|
||||||
|
statementList[i].c = constantZeroNum;
|
||||||
|
} else {
|
||||||
statementList[i].c = statements[i].c->num;
|
statementList[i].c = statements[i].c->num;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
statementList[i].c = -1;
|
statementList[i].c = -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -331,7 +331,7 @@ class idVarDef {
|
||||||
friend class idVarDefName;
|
friend class idVarDefName;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int num;
|
int num; // global index/ID of variable
|
||||||
varEval_t value;
|
varEval_t value;
|
||||||
idVarDef * scope; // function, namespace, or object the var was defined in
|
idVarDef * scope; // function, namespace, or object the var was defined in
|
||||||
int numUsers; // number of users if this is a constant
|
int numUsers; // number of users if this is a constant
|
||||||
|
@ -432,11 +432,19 @@ extern idVarDef def_boolean;
|
||||||
|
|
||||||
typedef struct statement_s {
|
typedef struct statement_s {
|
||||||
unsigned short op;
|
unsigned short op;
|
||||||
|
unsigned short flags; // DG: added this for ugly hacks
|
||||||
|
enum {
|
||||||
|
// op is OP_OBJECTCALL and when the statement was created the function/method
|
||||||
|
// implementation hasn't been parsed yet (only the declaration/prototype)
|
||||||
|
// see idCompiler::EmitFunctionParms() and idProgram::CalculateChecksum()
|
||||||
|
FLAG_OBJECTCALL_IMPL_NOT_PARSED_YET = 1,
|
||||||
|
};
|
||||||
|
// DG: moved linenumber and file up here to prevent wasting 8 bytes of padding on 64bit
|
||||||
|
unsigned short linenumber;
|
||||||
|
unsigned short file;
|
||||||
idVarDef *a;
|
idVarDef *a;
|
||||||
idVarDef *b;
|
idVarDef *b;
|
||||||
idVarDef *c;
|
idVarDef *c;
|
||||||
unsigned short linenumber;
|
|
||||||
unsigned short file;
|
|
||||||
} statement_t;
|
} statement_t;
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
|
@ -488,7 +496,7 @@ public:
|
||||||
// save games
|
// save games
|
||||||
void Save( idSaveGame *savefile ) const;
|
void Save( idSaveGame *savefile ) const;
|
||||||
bool Restore( idRestoreGame *savefile );
|
bool Restore( idRestoreGame *savefile );
|
||||||
int CalculateChecksum( void ) const; // Used to insure program code has not
|
int CalculateChecksum( bool forOldSavegame ) const; // Used to insure program code has not
|
||||||
// changed between savegames
|
// changed between savegames
|
||||||
|
|
||||||
void Startup( const char *defaultScript );
|
void Startup( const char *defaultScript );
|
||||||
|
|
|
@ -25,4 +25,4 @@ If you have questions concerning this license or the applicable additional terms
|
||||||
|
|
||||||
===========================================================================
|
===========================================================================
|
||||||
*/
|
*/
|
||||||
const int BUILD_NUMBER = 1304;
|
const int BUILD_NUMBER = 1305;
|
||||||
|
|
|
@ -427,6 +427,10 @@ void idGameLocal::SaveGame( idFile *f ) {
|
||||||
|
|
||||||
savegame.WriteBuildNumber( BUILD_NUMBER );
|
savegame.WriteBuildNumber( BUILD_NUMBER );
|
||||||
|
|
||||||
|
// DG: TODO: write other things (maybe internal savegame version so I don't have to bump BUILD_NUMBER AGAIN)
|
||||||
|
// and OS, architecture etc, see #344
|
||||||
|
// TODO: adjust InitFromSavegame() accordingly!
|
||||||
|
|
||||||
// go through all entities and threads and add them to the object list
|
// go through all entities and threads and add them to the object list
|
||||||
for( i = 0; i < MAX_GENTITIES; i++ ) {
|
for( i = 0; i < MAX_GENTITIES; i++ ) {
|
||||||
ent = entities[i];
|
ent = entities[i];
|
||||||
|
@ -1220,6 +1224,12 @@ bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWo
|
||||||
|
|
||||||
savegame.ReadBuildNumber();
|
savegame.ReadBuildNumber();
|
||||||
|
|
||||||
|
|
||||||
|
if(savegame.GetBuildNumber() >= 1305)
|
||||||
|
{
|
||||||
|
// TODO: read stuff additionally written in SaveGame()
|
||||||
|
}
|
||||||
|
|
||||||
// Create the list of all objects in the game
|
// Create the list of all objects in the game
|
||||||
savegame.CreateObjects();
|
savegame.CreateObjects();
|
||||||
|
|
||||||
|
|
|
@ -987,6 +987,25 @@ idVarDef *idCompiler::EmitFunctionParms( int op, idVarDef *func, int startarg, i
|
||||||
// need arg size seperate since script object may be NULL
|
// need arg size seperate since script object may be NULL
|
||||||
statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
|
statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
|
||||||
statement.c = SizeConstant( func->value.functionPtr->parmTotal );
|
statement.c = SizeConstant( func->value.functionPtr->parmTotal );
|
||||||
|
// DG: before changes I did to ParseFunctionDef(), func->value.functionPtr->parmTotal was 0
|
||||||
|
// if the function declaration/prototype has been parsed already, but the
|
||||||
|
// definition/implementation hadn't been parsed yet. That was wrong and sometimes
|
||||||
|
// (with debug game DLLs) lead to assertions in custom scripts, because the
|
||||||
|
// stack space reserved for function parameters was wrong.
|
||||||
|
// Now func->value.functionPtr->parmTotal is calculated when parsing the prototype,
|
||||||
|
// but func->value.functionPtr->parmSize[i] is still only calculated when parsing
|
||||||
|
// the implementation (as it's not needed before and so we can tell the cases apart here).
|
||||||
|
// However, savegames from before the change have script checksums
|
||||||
|
// (by idProgram::CalculateChecksum()) from statements with the wrong size, so
|
||||||
|
// loading them would fail as the checksum doesn't match.
|
||||||
|
// Setting this flag allows using the parmTotal argSize 0 when calculating the checksum
|
||||||
|
// so it matches the one from old savegames (unless something else has also changed in
|
||||||
|
// the script state so they really are incompatible). That's only done when actually
|
||||||
|
// loading old savegames (detected via BUILD_NUMBER/savegame.GetBuildNumber())
|
||||||
|
if ( op == OP_OBJECTCALL && func->value.functionPtr->parmTotal > 0
|
||||||
|
&& func->value.functionPtr->parmSize.Num() == 0 ) {
|
||||||
|
statement.flags = statement_t::FLAG_OBJECTCALL_IMPL_NOT_PARSED_YET;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
EmitOpcode( op, func, SizeConstant( size ) );
|
EmitOpcode( op, func, SizeConstant( size ) );
|
||||||
}
|
}
|
||||||
|
@ -2144,18 +2163,36 @@ void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DG: make sure parmSize gets calculated when parsing prototype (not just when parsing
|
// DG: make sure parmTotal gets calculated when parsing prototype (not just when parsing
|
||||||
// implementation) so calling this function/method before implementation has been parsed
|
// implementation) so calling this function/method before the implementation has been parsed
|
||||||
// works without getting Assertions in IdInterpreter::Execute() and ::LeaveFunction()
|
// works without getting Assertions in IdInterpreter::Execute() and ::LeaveFunction()
|
||||||
// ("st->c->value.argSize == func->parmTotal", "localstackUsed == localstackBase", see #303)
|
// ("st->c->value.argSize == func->parmTotal", "localstackUsed == localstackBase", see #303 and #344)
|
||||||
|
|
||||||
// calculate stack space used by parms
|
// calculate stack space used by parms
|
||||||
numParms = type->NumParameters();
|
numParms = type->NumParameters();
|
||||||
if( numParms != func->parmSize.Num() ) { // DG: make sure not to do this twice
|
if ( !CheckToken( "{" ) ) {
|
||||||
|
// it's just a prototype, so get the ; and move on
|
||||||
|
ExpectToken( ";" );
|
||||||
|
// DG: BUT only after calculating the stack space for the arguments because this
|
||||||
|
// function might be called before the implementation is parsed (see #303 and #344)
|
||||||
|
// which otherwise causes Assertions in IdInterpreter::Execute() and ::LeaveFunction()
|
||||||
|
// ("st->c->value.argSize == func->parmTotal", "localstackUsed == localstackBase")
|
||||||
|
func->parmTotal = 0;
|
||||||
|
for( i = 0; i < numParms; i++ ) {
|
||||||
|
parmType = type->GetParmType( i );
|
||||||
|
int size = parmType->Inherits( &type_object ) ? type_object.Size() : parmType->Size();
|
||||||
|
func->parmTotal += size;
|
||||||
|
// NOTE: Don't set func->parmSize[] yet, the workaround to keep compatibility
|
||||||
|
// with old savegames checks for func->parmSize.Num() == 0
|
||||||
|
// (see EmitFunctionParms() for more explanation of that workaround)
|
||||||
|
// Also not defining the parms yet, otherwise they're defined in a different order
|
||||||
|
// than before, so their .num is different which breaks compat with old savegames
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if it hasn't been parsed yet, parmSize.Num() should be 0..
|
|
||||||
assert( func->parmSize.Num() == 0 && "function had different number of arguments before?!" );
|
|
||||||
|
|
||||||
|
int totalSize = 0; // DG: totalsize might already have been calculated for the prototype, see a few lines above
|
||||||
func->parmSize.SetNum( numParms );
|
func->parmSize.SetNum( numParms );
|
||||||
for( i = 0; i < numParms; i++ ) {
|
for( i = 0; i < numParms; i++ ) {
|
||||||
parmType = type->GetParmType( i );
|
parmType = type->GetParmType( i );
|
||||||
|
@ -2164,8 +2201,11 @@ void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) {
|
||||||
} else {
|
} else {
|
||||||
func->parmSize[ i ] = parmType->Size();
|
func->parmSize[ i ] = parmType->Size();
|
||||||
}
|
}
|
||||||
func->parmTotal += func->parmSize[ i ];
|
totalSize += func->parmSize[ i ];
|
||||||
}
|
}
|
||||||
|
// DG: if parmTotal has been calculated before, it shouldn't have changed
|
||||||
|
assert((func->parmTotal == 0 || totalSize == func->parmTotal) && "function parameter sizes differ between protype vs implementation?!");
|
||||||
|
func->parmTotal = totalSize;
|
||||||
|
|
||||||
// define the parms
|
// define the parms
|
||||||
for( i = 0; i < numParms; i++ ) {
|
for( i = 0; i < numParms; i++ ) {
|
||||||
|
@ -2174,16 +2214,6 @@ void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) {
|
||||||
}
|
}
|
||||||
gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false );
|
gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DG: moved this down here so parmSize also gets calculated when parsing prototype
|
|
||||||
// check if this is a prototype or declaration
|
|
||||||
if ( !CheckToken( "{" ) ) {
|
|
||||||
// it's just a prototype, so get the ; and move on
|
|
||||||
ExpectToken( ";" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// DG end
|
|
||||||
|
|
||||||
oldscope = scope;
|
oldscope = scope;
|
||||||
scope = def;
|
scope = def;
|
||||||
|
|
|
@ -1634,7 +1634,9 @@ statement_t *idProgram::AllocStatement( void ) {
|
||||||
if ( statements.Num() >= statements.Max() ) {
|
if ( statements.Num() >= statements.Max() ) {
|
||||||
throw idCompileError( va( "Exceeded maximum allowed number of statements (%d)", statements.Max() ) );
|
throw idCompileError( va( "Exceeded maximum allowed number of statements (%d)", statements.Max() ) );
|
||||||
}
|
}
|
||||||
return statements.Alloc();
|
statement_t* ret = statements.Alloc();
|
||||||
|
ret->flags = 0; // DG: initialize the added flags (that are rarely set/used otherwise) to 0
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2005,7 +2007,7 @@ void idProgram::Save( idSaveGame *savefile ) const {
|
||||||
savefile->WriteByte( variables[i] );
|
savefile->WriteByte( variables[i] );
|
||||||
}
|
}
|
||||||
|
|
||||||
int checksum = CalculateChecksum();
|
int checksum = CalculateChecksum(false);
|
||||||
savefile->WriteInt( checksum );
|
savefile->WriteInt( checksum );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2039,9 +2041,11 @@ bool idProgram::Restore( idRestoreGame *savefile ) {
|
||||||
int saved_checksum, checksum;
|
int saved_checksum, checksum;
|
||||||
|
|
||||||
savefile->ReadInt( saved_checksum );
|
savefile->ReadInt( saved_checksum );
|
||||||
checksum = CalculateChecksum();
|
bool isOldSavegame = savefile->GetBuildNumber() <= 1304;
|
||||||
|
checksum = CalculateChecksum(isOldSavegame);
|
||||||
|
|
||||||
if ( saved_checksum != checksum ) {
|
if ( saved_checksum != checksum ) {
|
||||||
|
gameLocal.Warning( "WARNING: Real Script checksum didn't match the one from the savegame!");
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2053,7 +2057,7 @@ bool idProgram::Restore( idRestoreGame *savefile ) {
|
||||||
idProgram::CalculateChecksum
|
idProgram::CalculateChecksum
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
int idProgram::CalculateChecksum( void ) const {
|
int idProgram::CalculateChecksum( bool forOldSavegame ) const {
|
||||||
int i, result;
|
int i, result;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -2069,6 +2073,17 @@ int idProgram::CalculateChecksum( void ) const {
|
||||||
|
|
||||||
memset( statementList, 0, ( sizeof(statementBlock_t) * statements.Num() ) );
|
memset( statementList, 0, ( sizeof(statementBlock_t) * statements.Num() ) );
|
||||||
|
|
||||||
|
// DG hack: get the vardef for the argSize == 0 constant for savegame-compat
|
||||||
|
int constantZeroNum = -1;
|
||||||
|
if ( forOldSavegame ) {
|
||||||
|
for( idVarDef* def = GetDefList( "<IMMEDIATE>" ); def != NULL; def = def->Next() ) {
|
||||||
|
if ( def->Type() == ev_argsize && def->value.argSize == 0 ) {
|
||||||
|
constantZeroNum = def->num;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy info into new list, using the variable numbers instead of a pointer to the variable
|
// Copy info into new list, using the variable numbers instead of a pointer to the variable
|
||||||
for( i = 0; i < statements.Num(); i++ ) {
|
for( i = 0; i < statements.Num(); i++ ) {
|
||||||
statementList[i].op = statements[i].op;
|
statementList[i].op = statements[i].op;
|
||||||
|
@ -2084,7 +2099,15 @@ int idProgram::CalculateChecksum( void ) const {
|
||||||
statementList[i].b = -1;
|
statementList[i].b = -1;
|
||||||
}
|
}
|
||||||
if ( statements[i].c ) {
|
if ( statements[i].c ) {
|
||||||
|
// DG: old savegames wrongly assumed argSize 0 for some statements.
|
||||||
|
// So for the checksums to match we need to use the corresponding vardef num here
|
||||||
|
// See idCompiler::EmitFunctionParms() and ParseFunctionDef() for more details.
|
||||||
|
if ( forOldSavegame && statements[i].op == OP_OBJECTCALL
|
||||||
|
&& statements[i].flags == statement_t::FLAG_OBJECTCALL_IMPL_NOT_PARSED_YET ) {
|
||||||
|
statementList[i].c = constantZeroNum;
|
||||||
|
} else {
|
||||||
statementList[i].c = statements[i].c->num;
|
statementList[i].c = statements[i].c->num;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
statementList[i].c = -1;
|
statementList[i].c = -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,7 +317,7 @@ class idVarDef {
|
||||||
friend class idVarDefName;
|
friend class idVarDefName;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int num;
|
int num; // global index/ID of variable
|
||||||
varEval_t value;
|
varEval_t value;
|
||||||
idVarDef * scope; // function, namespace, or object the var was defined in
|
idVarDef * scope; // function, namespace, or object the var was defined in
|
||||||
int numUsers; // number of users if this is a constant
|
int numUsers; // number of users if this is a constant
|
||||||
|
@ -418,11 +418,19 @@ extern idVarDef def_boolean;
|
||||||
|
|
||||||
typedef struct statement_s {
|
typedef struct statement_s {
|
||||||
unsigned short op;
|
unsigned short op;
|
||||||
|
unsigned short flags; // DG: added this for ugly hacks
|
||||||
|
enum {
|
||||||
|
// op is OP_OBJECTCALL and when the statement was created the function/method
|
||||||
|
// implementation hasn't been parsed yet (only the declaration/prototype)
|
||||||
|
// see idCompiler::EmitFunctionParms() and idProgram::CalculateChecksum()
|
||||||
|
FLAG_OBJECTCALL_IMPL_NOT_PARSED_YET = 1,
|
||||||
|
};
|
||||||
|
// DG: moved linenumber and file up here to prevent wasting 8 bytes of padding on 64bit
|
||||||
|
unsigned short linenumber;
|
||||||
|
unsigned short file;
|
||||||
idVarDef *a;
|
idVarDef *a;
|
||||||
idVarDef *b;
|
idVarDef *b;
|
||||||
idVarDef *c;
|
idVarDef *c;
|
||||||
unsigned short linenumber;
|
|
||||||
unsigned short file;
|
|
||||||
} statement_t;
|
} statement_t;
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
|
@ -474,7 +482,7 @@ public:
|
||||||
// save games
|
// save games
|
||||||
void Save( idSaveGame *savefile ) const;
|
void Save( idSaveGame *savefile ) const;
|
||||||
bool Restore( idRestoreGame *savefile );
|
bool Restore( idRestoreGame *savefile );
|
||||||
int CalculateChecksum( void ) const; // Used to insure program code has not
|
int CalculateChecksum( bool forOldSavegame ) const; // Used to insure program code has not
|
||||||
// changed between savegames
|
// changed between savegames
|
||||||
|
|
||||||
void Startup( const char *defaultScript );
|
void Startup( const char *defaultScript );
|
||||||
|
|
Loading…
Reference in a new issue