Add load/save global vars (#296)

This commit is contained in:
Alexander Batalov 2023-06-03 08:06:49 +03:00 committed by GitHub
parent 37e4822ed5
commit cf5f865a23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 206 additions and 43 deletions

View File

@ -341,8 +341,8 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
// SFALL // SFALL
premadeCharactersInit(); premadeCharactersInit();
if (!sfallGlobalVarsInit()) { if (!sfall_gl_vars_init()) {
debugPrint("Failed on sfallGlobalVarsInit"); debugPrint("Failed on sfall_gl_vars_init");
return -1; return -1;
} }
@ -409,7 +409,7 @@ void gameReset()
_init_options_menu(); _init_options_menu();
// SFALL // SFALL
sfallGlobalVarsReset(); sfall_gl_vars_reset();
sfallListsReset(); sfallListsReset();
messageListRepositoryReset(); messageListRepositoryReset();
sfallArraysReset(); sfallArraysReset();
@ -425,7 +425,7 @@ void gameExit()
sfall_gl_scr_exit(); sfall_gl_scr_exit();
sfallArraysExit(); sfallArraysExit();
sfallListsExit(); sfallListsExit();
sfallGlobalVarsExit(); sfall_gl_vars_exit();
premadeCharactersExit(); premadeCharactersExit();
tileDisable(); tileDisable();

View File

@ -14,6 +14,7 @@
#include "interpreter_lib.h" #include "interpreter_lib.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "platform_compat.h" #include "platform_compat.h"
#include "sfall_global_scripts.h"
#include "svga.h" #include "svga.h"
namespace fallout { namespace fallout {
@ -3011,6 +3012,15 @@ Program* runScript(char* name)
// 0x46E1EC // 0x46E1EC
void _updatePrograms() void _updatePrograms()
{ {
// CE: Implementation is different. Sfall inserts global scripts into
// program list upon creation, so engine does not diffirentiate between
// global and normal scripts. Global scripts in CE are not part of program
// list, so we need a separate call to continue execution (usually
// non-critical calls scheduled from managed windows). One more thing to
// note is that global scripts in CE cannot handle conditional/timed procs
// (which are not used anyway).
sfall_gl_scr_update(_cpuBurstSize);
ProgramListNode* curr = gInterpreterProgramListHead; ProgramListNode* curr = gInterpreterProgramListHead;
while (curr != NULL) { while (curr != NULL) {
ProgramListNode* next = curr->next; ProgramListNode* next = curr->next;

View File

@ -46,6 +46,7 @@
#include "scripts.h" #include "scripts.h"
#include "settings.h" #include "settings.h"
#include "sfall_global_scripts.h" #include "sfall_global_scripts.h"
#include "sfall_global_vars.h"
#include "skill.h" #include "skill.h"
#include "stat.h" #include "stat.h"
#include "svga.h" #include "svga.h"
@ -1588,6 +1589,73 @@ static int lsgPerformSaveGame()
fileClose(_flptr); fileClose(_flptr);
// SFALL: Save sfallgv.sav.
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
strcat(_gmpath, "sfallgv.sav");
_flptr = fileOpen(_gmpath, "wb");
if (_flptr != NULL) {
do {
if (!sfall_gl_vars_save(_flptr)) {
debugPrint("LOADSAVE (SFALL): ** Error saving global vars **\n");
break;
}
// TODO: For now fill remaining sections with zeros to that Sfall
// can successfully read our global vars and skip the rest.
int nextObjectId = 0;
if (fileWrite(&nextObjectId, sizeof(nextObjectId), 1, _flptr) != 1) {
debugPrint("LOADSAVE (SFALL): ** Error saving next object id **\n");
break;
}
int addedYears = 0;
if (fileWrite(&addedYears, sizeof(addedYears), 1, _flptr) != 1) {
debugPrint("LOADSAVE (SFALL): ** Error saving added years **\n");
break;
}
int fakeTraitsCount = 0;
if (fileWrite(&fakeTraitsCount, sizeof(fakeTraitsCount), 1, _flptr) != 1) {
debugPrint("LOADSAVE (SFALL): ** Error saving fake traits **\n");
break;
}
int fakePerksCount = 0;
if (fileWrite(&fakePerksCount, sizeof(fakePerksCount), 1, _flptr) != 1) {
debugPrint("LOADSAVE (SFALL): ** Error saving fake perks **\n");
break;
}
int fakeSelectablePerksCount = 0;
if (fileWrite(&fakeSelectablePerksCount, sizeof(fakeSelectablePerksCount), 1, _flptr) != 1) {
debugPrint("LOADSAVE (SFALL): ** Error saving fake selectable perks **\n");
break;
}
int arraysCountOld = 0;
if (fileWrite(&arraysCountOld, sizeof(arraysCountOld), 1, _flptr) != 1) {
debugPrint("LOADSAVE (SFALL): ** Error saving arrays (old fmt) **\n");
break;
}
int arraysCountNew = 0;
if (fileWrite(&arraysCountNew, sizeof(arraysCountNew), 1, _flptr) != 1) {
debugPrint("LOADSAVE (SFALL): ** Error saving arrays (new fmt) **\n");
break;
}
int drugPidsCount = 0;
if (fileWrite(&drugPidsCount, sizeof(drugPidsCount), 1, _flptr) != 1) {
debugPrint("LOADSAVE (SFALL): ** Error saving drug pids **\n");
break;
}
} while (0);
fileClose(_flptr);
}
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1); snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
_MapDirErase(_gmpath, "BAK"); _MapDirErase(_gmpath, "BAK");
@ -1663,6 +1731,24 @@ static int lsgLoadGameInSlot(int slot)
debugPrint("LOADSAVE: Total load data read: %ld bytes.\n", fileTell(_flptr)); debugPrint("LOADSAVE: Total load data read: %ld bytes.\n", fileTell(_flptr));
fileClose(_flptr); fileClose(_flptr);
// SFALL: Load sfallgv.sav.
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
strcat(_gmpath, "sfallgv.sav");
_flptr = fileOpen(_gmpath, "rb");
if (_flptr != NULL) {
do {
if (!sfall_gl_vars_load(_flptr)) {
debugPrint("LOADSAVE (SFALL): ** Error loading global vars **\n");
break;
}
// TODO: For now silently ignore remaining sections.
} while (0);
fileClose(_flptr);
}
snprintf(_str, sizeof(_str), "%s\\", "MAPS"); snprintf(_str, sizeof(_str), "%s\\", "MAPS");
_MapDirErase(_str, "BAK"); _MapDirErase(_str, "BAK");
_proto_dude_update_gender(); _proto_dude_update_gender();

View File

@ -210,4 +210,11 @@ bool sfall_gl_scr_is_loaded(Program* program)
return true; return true;
} }
void sfall_gl_scr_update(int burstSize)
{
for (auto& scr : state->globalScripts) {
_interpret(scr.program, burstSize);
}
}
} // namespace fallout } // namespace fallout

View File

@ -17,6 +17,7 @@ void sfall_gl_scr_process_worldmap();
void sfall_gl_scr_set_repeat(Program* program, int frames); void sfall_gl_scr_set_repeat(Program* program, int frames);
void sfall_gl_scr_set_type(Program* program, int type); void sfall_gl_scr_set_type(Program* program, int type);
bool sfall_gl_scr_is_loaded(Program* program); bool sfall_gl_scr_is_loaded(Program* program);
void sfall_gl_scr_update(int burstSize);
} // namespace fallout } // namespace fallout

View File

@ -10,72 +10,127 @@ struct SfallGlobalVarsState {
std::unordered_map<uint64_t, int> vars; std::unordered_map<uint64_t, int> vars;
}; };
static bool sfallGlobalVarsStore(uint64_t key, int value); #pragma pack(push)
static bool sfallGlobalVarsFetch(uint64_t key, int& value); #pragma pack(8)
static SfallGlobalVarsState* _state; /// Matches Sfall's `GlobalVar` to maintain binary compatibility.
struct GlobalVarEntry {
uint64_t key;
int32_t value;
int32_t unused;
};
bool sfallGlobalVarsInit() #pragma pack(pop)
static bool sfall_gl_vars_store(uint64_t key, int value);
static bool sfall_gl_vars_fetch(uint64_t key, int& value);
static SfallGlobalVarsState* sfall_gl_vars_state = nullptr;
bool sfall_gl_vars_init()
{ {
_state = new (std::nothrow) SfallGlobalVarsState(); sfall_gl_vars_state = new (std::nothrow) SfallGlobalVarsState();
if (_state == nullptr) { if (sfall_gl_vars_state == nullptr) {
return false; return false;
} }
return true; return true;
} }
void sfallGlobalVarsReset() void sfall_gl_vars_reset()
{ {
_state->vars.clear(); sfall_gl_vars_state->vars.clear();
} }
void sfallGlobalVarsExit() void sfall_gl_vars_exit()
{ {
if (_state != nullptr) { if (sfall_gl_vars_state != nullptr) {
delete _state; delete sfall_gl_vars_state;
_state = nullptr; sfall_gl_vars_state = nullptr;
} }
} }
bool sfallGlobalVarsStore(const char* key, int value) bool sfall_gl_vars_save(File* stream)
{
int count = static_cast<int>(sfall_gl_vars_state->vars.size());
if (fileWrite(&count, sizeof(count), 1, stream) != 1) {
return false;
}
GlobalVarEntry entry = { 0 };
for (auto& pair : sfall_gl_vars_state->vars) {
entry.key = pair.first;
entry.value = pair.second;
if (fileWrite(&entry, sizeof(entry), 1, stream) != 1) {
return false;
}
}
return true;
}
bool sfall_gl_vars_load(File* stream)
{
int count;
if (fileRead(&count, sizeof(count), 1, stream) != 1) {
return false;
}
sfall_gl_vars_state->vars.reserve(count);
GlobalVarEntry entry;
while (count > 0) {
if (fileRead(&entry, sizeof(entry), 1, stream) != 1) {
return false;
}
sfall_gl_vars_state->vars[entry.key] = static_cast<int>(entry.value);
count--;
}
return true;
}
bool sfall_gl_vars_store(const char* key, int value)
{ {
if (strlen(key) != 8) { if (strlen(key) != 8) {
return false; return false;
} }
uint64_t numericKey = *(reinterpret_cast<const uint64_t*>(key)); uint64_t numericKey = *(reinterpret_cast<const uint64_t*>(key));
return sfallGlobalVarsStore(numericKey, value); return sfall_gl_vars_store(numericKey, value);
} }
bool sfallGlobalVarsStore(int key, int value) bool sfall_gl_vars_store(int key, int value)
{ {
return sfallGlobalVarsStore(static_cast<uint64_t>(key), value); return sfall_gl_vars_store(static_cast<uint64_t>(key), value);
} }
bool sfallGlobalVarsFetch(const char* key, int& value) bool sfall_gl_vars_fetch(const char* key, int& value)
{ {
if (strlen(key) != 8) { if (strlen(key) != 8) {
return false; return false;
} }
uint64_t numericKey = *(reinterpret_cast<const uint64_t*>(key)); uint64_t numericKey = *(reinterpret_cast<const uint64_t*>(key));
return sfallGlobalVarsFetch(numericKey, value); return sfall_gl_vars_fetch(numericKey, value);
} }
bool sfallGlobalVarsFetch(int key, int& value) bool sfall_gl_vars_fetch(int key, int& value)
{ {
return sfallGlobalVarsFetch(static_cast<uint64_t>(key), value); return sfall_gl_vars_fetch(static_cast<uint64_t>(key), value);
} }
static bool sfallGlobalVarsStore(uint64_t key, int value) static bool sfall_gl_vars_store(uint64_t key, int value)
{ {
auto it = _state->vars.find(key); auto it = sfall_gl_vars_state->vars.find(key);
if (it == _state->vars.end()) { if (it == sfall_gl_vars_state->vars.end()) {
_state->vars.emplace(key, value); sfall_gl_vars_state->vars.emplace(key, value);
} else { } else {
if (value == 0) { if (value == 0) {
_state->vars.erase(it); sfall_gl_vars_state->vars.erase(it);
} else { } else {
it->second = value; it->second = value;
} }
@ -84,10 +139,10 @@ static bool sfallGlobalVarsStore(uint64_t key, int value)
return true; return true;
} }
static bool sfallGlobalVarsFetch(uint64_t key, int& value) static bool sfall_gl_vars_fetch(uint64_t key, int& value)
{ {
auto it = _state->vars.find(key); auto it = sfall_gl_vars_state->vars.find(key);
if (it == _state->vars.end()) { if (it == sfall_gl_vars_state->vars.end()) {
return false; return false;
} }

View File

@ -1,15 +1,19 @@
#ifndef FALLOUT_SFALL_GLOBAL_VARS_H_ #ifndef FALLOUT_SFALL_GLOBAL_VARS_H_
#define FALLOUT_SFALL_GLOBAL_VARS_H_ #define FALLOUT_SFALL_GLOBAL_VARS_H_
#include "db.h"
namespace fallout { namespace fallout {
bool sfallGlobalVarsInit(); bool sfall_gl_vars_init();
void sfallGlobalVarsReset(); void sfall_gl_vars_reset();
void sfallGlobalVarsExit(); void sfall_gl_vars_exit();
bool sfallGlobalVarsStore(const char* key, int value); bool sfall_gl_vars_save(File* stream);
bool sfallGlobalVarsStore(int key, int value); bool sfall_gl_vars_load(File* stream);
bool sfallGlobalVarsFetch(const char* key, int& value); bool sfall_gl_vars_store(const char* key, int value);
bool sfallGlobalVarsFetch(int key, int& value); bool sfall_gl_vars_store(int key, int value);
bool sfall_gl_vars_fetch(const char* key, int& value);
bool sfall_gl_vars_fetch(int key, int& value);
} // namespace fallout } // namespace fallout

View File

@ -167,9 +167,9 @@ static void opSetGlobalVar(Program* program)
if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) { if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
const char* key = programGetString(program, variable.opcode, variable.integerValue); const char* key = programGetString(program, variable.opcode, variable.integerValue);
sfallGlobalVarsStore(key, value.integerValue); sfall_gl_vars_store(key, value.integerValue);
} else if (variable.opcode == VALUE_TYPE_INT) { } else if (variable.opcode == VALUE_TYPE_INT) {
sfallGlobalVarsStore(variable.integerValue, value.integerValue); sfall_gl_vars_store(variable.integerValue, value.integerValue);
} }
} }
@ -181,9 +181,9 @@ static void opGetGlobalInt(Program* program)
int value = 0; int value = 0;
if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) { if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
const char* key = programGetString(program, variable.opcode, variable.integerValue); const char* key = programGetString(program, variable.opcode, variable.integerValue);
sfallGlobalVarsFetch(key, value); sfall_gl_vars_fetch(key, value);
} else if (variable.opcode == VALUE_TYPE_INT) { } else if (variable.opcode == VALUE_TYPE_INT) {
sfallGlobalVarsFetch(variable.integerValue, value); sfall_gl_vars_fetch(variable.integerValue, value);
} }
programStackPushInteger(program, value); programStackPushInteger(program, value);