Compare commits

...
Author SHA1 Message Date
Mike Klaas 9e3e94ffd9 PR findings 2026-06-21 22:05:31 -07:00
github-actions[bot] 25e006cffd chore: auto-format with clang-format 2026-06-22 04:56:15 +00:00
Mike Klaas 3edd00d85f clarify which are transient 2026-06-21 21:53:16 -07:00
Mike Klaas 2bfa10411c better cleaup 2026-06-21 21:43:39 -07:00
Mike Klaas a9eddc1705 Expose transient message lists.
Some message lists are only loaded while a given screen is active.  Sfall still lets scripts access them, though scripts need to be careful to only do so when the windows are active.
2026-06-21 21:22:35 -07:00
10 changed files with 115 additions and 48 deletions
+30 -7
View File
@@ -112,6 +112,8 @@ typedef enum EditorFolder {
EDITOR_FOLDER_KILLS,
} EditorFolder;
static void characterEditorMessageListReset();
enum {
EDITOR_DERIVED_STAT_ARMOR_CLASS,
EDITOR_DERIVED_STAT_ACTION_POINTS,
@@ -587,6 +589,12 @@ static int gCharacterEditorOptionalTraitBtns[TRAIT_COUNT];
// 0x5700E8 mesg
static MessageListItem gCharacterEditorMessageListItem;
static void characterEditorMessageListReset()
{
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_EDITOR, nullptr);
messageListFree(&gCharacterEditorMessageList);
}
// 0x5700F8 old_str1
static char gCharacterEditorCardTitle[48];
@@ -1284,18 +1292,35 @@ static int characterEditorWindowInit()
if (!messageListLoad(&gCharacterEditorMessageList, path)) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_EDITOR, &gCharacterEditorMessageList);
fid = buildFid(OBJ_TYPE_INTERFACE, (gCharacterEditorIsCreationMode ? 169 : 177), 0, 0, 0);
if (!_editorBackgroundFrmImage.lock(fid)) {
messageListFree(&gCharacterEditorMessageList);
characterEditorMessageListReset();
return -1;
}
if (karmaInit() == -1) {
_editorBackgroundFrmImage.unlock();
characterEditorMessageListReset();
if (gCharacterEditorIsoWasEnabled) {
isoEnable();
}
colorCycleEnable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
return -1;
}
if (genericReputationInit() == -1) {
karmaFree();
_editorBackgroundFrmImage.unlock();
characterEditorMessageListReset();
if (gCharacterEditorIsoWasEnabled) {
isoEnable();
}
colorCycleEnable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
return -1;
}
@@ -1318,11 +1343,9 @@ static int characterEditorWindowInit()
while (--i >= 0) {
_editorFrmImages[i].unlock();
}
return -1;
_editorBackgroundFrmImage.unlock();
messageListFree(&gCharacterEditorMessageList);
characterEditorMessageListReset();
if (gCharacterEditorIsoWasEnabled) {
isoEnable();
@@ -1360,7 +1383,7 @@ static int characterEditorWindowInit()
_editorBackgroundFrmImage.unlock();
messageListFree(&gCharacterEditorMessageList);
characterEditorMessageListReset();
if (gCharacterEditorIsoWasEnabled) {
isoEnable();
}
@@ -1389,7 +1412,7 @@ static int characterEditorWindowInit()
_editorBackgroundFrmImage.unlock();
messageListFree(&gCharacterEditorMessageList);
characterEditorMessageListReset();
if (gCharacterEditorIsoWasEnabled) {
isoEnable();
}
@@ -1869,7 +1892,7 @@ static void characterEditorWindowFree()
// SFALL: Custom town reputation.
customTownReputationFree();
messageListFree(&gCharacterEditorMessageList);
characterEditorMessageListReset();
interfaceBarRefresh();
+12 -1
View File
@@ -86,6 +86,8 @@ typedef enum GameDialogReviewWindowButtonFrm {
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_COUNT,
} GameDialogReviewWindowButtonFrm;
static void partyMemberCustomizationMessageListReset();
typedef enum GameDialogReaction {
GAME_DIALOG_REACTION_GOOD = 49,
GAME_DIALOG_REACTION_NEUTRAL = 50,
@@ -583,6 +585,12 @@ static int gGameDialogReviewWindowOldFont;
// 0x58F470 gdialog_buttons
static int _gdialog_buttons[9];
static void partyMemberCustomizationMessageListReset()
{
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_CUSTOM, nullptr);
messageListFree(&gCustomMessageList);
}
// 0x58F4C8 oldFont
static int _oldFont;
@@ -3966,10 +3974,12 @@ int partyMemberCustomizationWindowInit()
if (!messageListLoad(&gCustomMessageList, "game\\custom.msg")) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_CUSTOM, &gCustomMessageList);
FrmImage backgroundFrmImage;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 391, 0, 0, 0);
if (!backgroundFrmImage.lock(backgroundFid)) {
partyMemberCustomizationMessageListReset();
return -1;
}
@@ -4081,6 +4091,7 @@ int partyMemberCustomizationWindowInit()
void partyMemberCustomizationWindowFree()
{
if (gGameDialogWindow == -1) {
partyMemberCustomizationMessageListReset();
return;
}
@@ -4118,7 +4129,7 @@ void partyMemberCustomizationWindowFree()
windowDestroy(gGameDialogWindow);
gGameDialogWindow = -1;
messageListFree(&gCustomMessageList);
partyMemberCustomizationMessageListReset();
}
// 0x449B3C
+2
View File
@@ -937,6 +937,7 @@ static int inventoryMessageListInit()
if (!messageListLoad(&gInventoryMessageList, path))
return -1;
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_INVENTORY, &gInventoryMessageList);
return 0;
}
@@ -944,6 +945,7 @@ static int inventoryMessageListInit()
// 0x46E7A0
static int inventoryMessageListFree()
{
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_INVENTORY, nullptr);
messageListFree(&gInventoryMessageList);
return 0;
}
+18 -6
View File
@@ -120,6 +120,8 @@ typedef enum LoadSaveScrollDirection {
LOAD_SAVE_SCROLL_DIRECTION_DOWN,
} LoadSaveScrollDirection;
static void loadSaveMessageListReset();
typedef struct LoadSaveSlotData {
char signature[24];
short versionMinor;
@@ -330,6 +332,12 @@ static char _str1[COMPAT_MAX_PATH];
// 0x6145FC str
static char _str[COMPAT_MAX_PATH];
static void loadSaveMessageListReset()
{
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_LSGAME, nullptr);
messageListFree(&gLoadSaveMessageList);
}
// 0x614700 lsgbuf
static unsigned char* gLoadSaveWindowBuffer;
@@ -459,6 +467,7 @@ int lsgSaveGame(int mode)
if (!messageListLoad(&gLoadSaveMessageList, path)) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_LSGAME, &gLoadSaveMessageList);
_snapshotBuf = nullptr;
int v6 = _QuickSnapShot();
@@ -476,6 +485,7 @@ int lsgSaveGame(int mode)
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
if (v6 != -1) {
loadSaveMessageListReset();
return 1;
}
@@ -491,7 +501,7 @@ int lsgSaveGame(int mode)
};
showDialogBox(_str0, body, 1, 169, 116, _colorTable[32328], nullptr, _colorTable[32328], DIALOG_BOX_LARGE);
messageListFree(&gLoadSaveMessageList);
loadSaveMessageListReset();
return -1;
}
@@ -1092,6 +1102,7 @@ int lsgLoadGame(int mode)
if (!messageListLoad(&gLoadSaveMessageList, path)) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_LSGAME, &gLoadSaveMessageList);
if (window != -1) {
windowDestroy(window);
@@ -1103,7 +1114,7 @@ int lsgLoadGame(int mode)
strcpy(_str1, getmsg(&gLoadSaveMessageList, &messageListItem, 135));
showDialogBox(_str0, body, 1, 169, 116, _colorTable[32328], nullptr, _colorTable[32328], DIALOG_BOX_LARGE);
messageListFree(&gLoadSaveMessageList);
loadSaveMessageListReset();
mapNewMap();
_game_user_wants_to_quit = GAME_QUIT_REQUEST_MAIN_MENU;
@@ -1597,10 +1608,11 @@ static int lsgWindowInit(int windowType)
if (!messageListLoad(&gLoadSaveMessageList, _str)) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_LSGAME, &gLoadSaveMessageList);
_snapshot = (unsigned char*)internal_malloc(61632);
if (_snapshot == nullptr) {
messageListFree(&gLoadSaveMessageList);
loadSaveMessageListReset();
fontSetCurrent(gLoadSaveWindowOldFont);
return -1;
}
@@ -1652,7 +1664,7 @@ static int lsgWindowInit(int windowType)
_loadsaveFrmImages[index].unlock();
}
internal_free(_snapshot);
messageListFree(&gLoadSaveMessageList);
loadSaveMessageListReset();
fontSetCurrent(gLoadSaveWindowOldFont);
if (windowType != LOAD_SAVE_WINDOW_TYPE_LOAD_GAME_FROM_MAIN_MENU) {
@@ -1678,7 +1690,7 @@ static int lsgWindowInit(int windowType)
if (gLoadSaveWindow == -1) {
// FIXME: Leaking frms.
internal_free(_snapshot);
messageListFree(&gLoadSaveMessageList);
loadSaveMessageListReset();
fontSetCurrent(gLoadSaveWindowOldFont);
if (windowType != LOAD_SAVE_WINDOW_TYPE_LOAD_GAME_FROM_MAIN_MENU) {
@@ -1816,7 +1828,7 @@ static int lsgWindowFree(int windowType)
windowDestroy(gLoadSaveWindow);
fontSetCurrent(gLoadSaveWindowOldFont);
messageListFree(&gLoadSaveMessageList);
loadSaveMessageListReset();
for (int index = 0; index < LOAD_SAVE_FRM_COUNT; index++) {
_loadsaveFrmImages[index].unlock();
+2 -4
View File
@@ -183,13 +183,12 @@ bool messageListInit(MessageList* messageList)
// 0x484964 message_exit
bool messageListFree(MessageList* messageList)
{
int i;
MessageListItem* entry;
if (messageList == nullptr) {
return false;
}
int i;
MessageListItem* entry;
for (i = 0; i < messageList->entries_num; i++) {
entry = &(messageList->entries[i]);
@@ -212,7 +211,6 @@ bool messageListFree(MessageList* messageList)
return true;
}
// message_load
// 0x484AA4 message_load
bool messageListLoad(MessageList* messageList, const char* path)
{
+13 -23
View File
@@ -12,41 +12,31 @@ namespace fallout {
// CE: Working with standard message lists is tricky in Sfall. Many message
// lists are initialized only for the duration of appropriate modal window. This
// is not documented in Sfall and shifts too much responsibility to scripters
// (who should check game mode before accessing volatile message lists). For now
// CE only exposes persistent standard message lists:
// - combat.msg
// - combatai.msg
// - scrname.msg
// - misc.msg
// - item.msg
// - map.msg
// - proto.msg
// - script.msg
// - skill.msg
// - stat.msg
// - trait.msg
// - worldmap.msg
// (who should check game mode before accessing volatile message lists). CE
// always exposes persistent standard message lists and additionally exposes
// some volatile lists for the duration of their UI lifetime:
enum StandardMessageList {
STANDARD_MESSAGE_LIST_COMBAT,
STANDARD_MESSAGE_LIST_COMBAT_AI,
STANDARD_MESSAGE_LIST_COMBAT, // transient
STANDARD_MESSAGE_LIST_COMBAT_AI, // transient
STANDARD_MESSAGE_LIST_SCRNAME,
STANDARD_MESSAGE_LIST_MISC,
STANDARD_MESSAGE_LIST_CUSTOM,
STANDARD_MESSAGE_LIST_INVENTORY,
STANDARD_MESSAGE_LIST_CUSTOM, // transient
STANDARD_MESSAGE_LIST_INVENTORY, // transient
STANDARD_MESSAGE_LIST_ITEM,
STANDARD_MESSAGE_LIST_LSGAME,
STANDARD_MESSAGE_LIST_LSGAME, // transient
STANDARD_MESSAGE_LIST_MAP,
STANDARD_MESSAGE_LIST_OPTIONS,
STANDARD_MESSAGE_LIST_OPTIONS, // transient
STANDARD_MESSAGE_LIST_PERK,
STANDARD_MESSAGE_LIST_PIPBOY,
STANDARD_MESSAGE_LIST_QUESTS,
STANDARD_MESSAGE_LIST_PIPBOY, // transient
STANDARD_MESSAGE_LIST_QUESTS, // transient
STANDARD_MESSAGE_LIST_PROTO,
STANDARD_MESSAGE_LIST_SCRIPT,
STANDARD_MESSAGE_LIST_SKILL,
STANDARD_MESSAGE_LIST_SKILLDEX,
STANDARD_MESSAGE_LIST_SKILLDEX, // transient
STANDARD_MESSAGE_LIST_STAT,
STANDARD_MESSAGE_LIST_TRAIT,
STANDARD_MESSAGE_LIST_WORLDMAP,
STANDARD_MESSAGE_LIST_EDITOR, // transient; yes, this is supposed to be at the end rather than alphabetical
STANDARD_MESSAGE_LIST_COUNT,
};
+16 -6
View File
@@ -43,6 +43,7 @@ typedef enum OptionsWindowFrm {
static int optionsWindowInit();
static int optionsWindowFree();
static void _ShadeScreen(bool preserveWorldState);
static void optionsMessageListReset();
// 0x48FC0C
static const int gPauseWindowFrmIds[PAUSE_WINDOW_FRM_COUNT] = {
@@ -85,6 +86,12 @@ static bool gOptionsWindowIsoWasEnabled;
static FrmImage _optionsFrmImages[OPTIONS_WINDOW_FRM_COUNT];
static void optionsMessageListReset()
{
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_OPTIONS, nullptr);
messageListFree(&gPreferencesMessageList);
}
// 0x48FC50 do_optionsFunc
int showOptions()
{
@@ -136,6 +143,7 @@ int showOptions()
case 502:
// PREFERENCES
doPreferences(false);
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_OPTIONS, &gPreferencesMessageList);
break;
case KEY_PLUS:
case KEY_EQUAL:
@@ -182,6 +190,7 @@ static int optionsWindowInit()
if (!messageListLoad(&gPreferencesMessageList, path)) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_OPTIONS, &gPreferencesMessageList);
for (int index = 0; index < OPTIONS_WINDOW_FRM_COUNT; index++) {
int fid = buildFid(OBJ_TYPE_INTERFACE, gOptionsWindowFrmIds[index], 0, 0, 0);
@@ -190,7 +199,7 @@ static int optionsWindowInit()
_optionsFrmImages[index].unlock();
}
messageListFree(&gPreferencesMessageList);
optionsMessageListReset();
return -1;
}
@@ -208,7 +217,7 @@ static int optionsWindowInit()
_optionsFrmImages[index].unlock();
}
messageListFree(&gPreferencesMessageList);
optionsMessageListReset();
return -1;
}
@@ -236,7 +245,7 @@ static int optionsWindowInit()
_optionsFrmImages[index].unlock();
}
messageListFree(&gPreferencesMessageList);
optionsMessageListReset();
return -1;
}
@@ -304,7 +313,7 @@ static int optionsWindowFree()
{
windowDestroy(gOptionsWindow);
fontSetCurrent(gOptionsWindowOldFont);
messageListFree(&gPreferencesMessageList);
optionsMessageListReset();
for (int index = 0; index < OPTIONS_WINDOW_BUTTONS_COUNT; index++) {
internal_free(_opbtns[index]);
@@ -365,6 +374,7 @@ int showPause(bool preserveWorldState)
// FIXME: Leaking graphics.
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_OPTIONS, &gPreferencesMessageList);
int pauseWindowX = (screenGetWidth() - frmImages[PAUSE_WINDOW_FRM_BACKGROUND].getWidth()) / 2;
int pauseWindowY = (screenGetHeight() - frmImages[PAUSE_WINDOW_FRM_BACKGROUND].getHeight()) / 2;
@@ -383,7 +393,7 @@ int showPause(bool preserveWorldState)
256,
WINDOW_MODAL | WINDOW_DONT_MOVE_TOP);
if (window == -1) {
messageListFree(&gPreferencesMessageList);
optionsMessageListReset();
debugPrint("\n** Error opening pause window! **\n");
return -1;
@@ -477,7 +487,7 @@ int showPause(bool preserveWorldState)
}
windowDestroy(window);
messageListFree(&gPreferencesMessageList);
optionsMessageListReset();
if (!preserveWorldState) {
if (gameMouseWasVisible) {
+6
View File
@@ -595,6 +595,7 @@ int pipboyMessageListInit()
if (!(messageListLoad(&gPipboyMessageList, path))) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_PIPBOY, &gPipboyMessageList);
return 0;
}
@@ -602,6 +603,7 @@ int pipboyMessageListInit()
void pipboyMessageListFree()
{
if (gPipboyMessageList.entries != nullptr) {
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_PIPBOY, nullptr);
messageListFree(&gPipboyMessageList);
}
messageListInit(&gPipboyMessageList);
@@ -2697,6 +2699,7 @@ static int questInit()
File* stream = fileOpen("data\\quests.txt", "rt");
if (stream == nullptr) {
messageListFree(&gQuestsMessageList);
return -1;
}
@@ -2764,12 +2767,14 @@ static int questInit()
qsort(gQuestDescriptions, gQuestsCount, sizeof(*gQuestDescriptions), questDescriptionCompare);
fileClose(stream);
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_QUESTS, &gQuestsMessageList);
return 0;
err:
fileClose(stream);
messageListFree(&gQuestsMessageList);
return -1;
}
@@ -2784,6 +2789,7 @@ static void questFree()
gQuestsCount = 0;
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_QUESTS, nullptr);
messageListFree(&gQuestsMessageList);
}
+11 -1
View File
@@ -115,6 +115,7 @@ int _SavePrefs(bool save);
static int preferencesWindowInit();
static int preferencesWindowFree();
static void _DoThing(int eventCode);
static void preferencesMessageListReset();
// 0x48FBD0 row1Ytab
static const int _row1Ytab[PRIMARY_PREF_COUNT] = {
@@ -231,6 +232,12 @@ static MessageList gPreferencesMessageList;
// 0x663840 optnmesg
static MessageListItem gPreferencesMessageListItem;
static void preferencesMessageListReset()
{
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_OPTIONS, nullptr);
messageListFree(&gPreferencesMessageList);
}
// 0x6638C8 text_delay_back
static double gPreferencesTextBaseDelay2;
@@ -990,6 +997,7 @@ static int preferencesWindowInit()
if (!messageListLoad(&gPreferencesMessageList, path)) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_OPTIONS, &gPreferencesMessageList);
_oldFont = fontGetCurrent();
@@ -1001,6 +1009,7 @@ static int preferencesWindowInit()
while (--i >= 0) {
_preferencesFrmImages[i].unlock();
}
preferencesMessageListReset();
return -1;
}
}
@@ -1019,6 +1028,7 @@ static int preferencesWindowInit()
for (i = 0; i < PREFERENCES_WINDOW_FRM_COUNT; i++) {
_preferencesFrmImages[i].unlock();
}
preferencesMessageListReset();
return -1;
}
@@ -1210,7 +1220,7 @@ static int preferencesWindowFree()
fontSetCurrent(_oldFont);
messageListFree(&gPreferencesMessageList);
preferencesMessageListReset();
touch_set_touchscreen_mode(false);
return 0;
+5
View File
@@ -166,6 +166,7 @@ static int skilldexWindowInit()
if (!messageListLoad(&gSkilldexMessageList, path)) {
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SKILLDEX, &gSkilldexMessageList);
int frmIndex;
for (frmIndex = 0; frmIndex < SKILLDEX_FRM_COUNT; frmIndex++) {
@@ -180,6 +181,7 @@ static int skilldexWindowInit()
_skilldexFrmImages[frmIndex].unlock();
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SKILLDEX, nullptr);
messageListFree(&gSkilldexMessageList);
return -1;
@@ -218,6 +220,7 @@ static int skilldexWindowInit()
_skilldexFrmImages[index].unlock();
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SKILLDEX, nullptr);
messageListFree(&gSkilldexMessageList);
return -1;
@@ -241,6 +244,7 @@ static int skilldexWindowInit()
_skilldexFrmImages[index].unlock();
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SKILLDEX, nullptr);
messageListFree(&gSkilldexMessageList);
return -1;
@@ -403,6 +407,7 @@ static void skilldexWindowFree()
_skilldexFrmImages[index].unlock();
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SKILLDEX, nullptr);
messageListFree(&gSkilldexMessageList);
fontSetCurrent(gSkilldexWindowOldFont);