diff --git a/sfall_testing/gl_test_add_extra_msg_file.ssl b/sfall_testing/gl_test_add_extra_msg_file.ssl new file mode 100644 index 00000000..eb1131f9 --- /dev/null +++ b/sfall_testing/gl_test_add_extra_msg_file.ssl @@ -0,0 +1,26 @@ +#include "test_utils.h" +#include "sfall.h" + +// to test, copy test_extra_msg_file.msg into /text/English/game/ + +procedure start begin + variable auto_id; + variable duplicate_auto_id; + + display_msg("Testing add_extra_msg_file..."); + + call assertEquals("metarule exists", metarule_exist("add_extra_msg_file"), 1); + + auto_id := add_extra_msg_file("test_extra_msg_file.msg"); + call assertNotEquals("auto id is not load error", auto_id, -2); + call assertTrue("auto id lower bound", auto_id >= 0x3000); + call assertTrue("auto id upper bound", auto_id <= 0x3FFF); + call assertEquals("auto message", message_str_game(auto_id, 100), "auto extra msg"); + + duplicate_auto_id := add_extra_msg_file("TEST_EXTRA_MSG_FILE.MSG"); + call assertEquals("duplicate file returns existing id", duplicate_auto_id, auto_id); + + call assertEquals("missing file", add_extra_msg_file("missing_extra_msg_file.msg"), -2); + + call report_test_results("add_extra_msg_file"); +end diff --git a/sfall_testing/test_extra_msg_file.msg b/sfall_testing/test_extra_msg_file.msg new file mode 100644 index 00000000..536ef281 --- /dev/null +++ b/sfall_testing/test_extra_msg_file.msg @@ -0,0 +1 @@ +{100}{}{auto extra msg} diff --git a/src/message.cc b/src/message.cc index 260a335d..c6597516 100644 --- a/src/message.cc +++ b/src/message.cc @@ -1,5 +1,6 @@ #include "message.h" +#include #include #include #include @@ -37,6 +38,7 @@ struct MessageListRepositoryState { std::array protoMessageLists; std::unordered_map persistentMessageLists; std::unordered_map temporaryMessageLists; + std::unordered_map tempMessageListPaths; int nextTemporaryMessageListId = kFirstTemporaryMessageListId; }; @@ -47,6 +49,15 @@ static int _message_load_field(File* file, char* str); static MessageList* messageListRepositoryLoad(const char* path); +static std::string messageListRepositoryNormalizePath(const char* path) +{ + std::string normalizedPath(path != nullptr ? path : ""); + std::transform(normalizedPath.begin(), normalizedPath.end(), normalizedPath.begin(), [](unsigned char ch) { + return static_cast(tolower(ch)); + }); + return normalizedPath; +} + // 0x50B79C static char _Error_1[] = "Error"; @@ -702,6 +713,7 @@ void messageListRepositoryReset() delete pair.second; } _messageListRepositoryState->temporaryMessageLists.clear(); + _messageListRepositoryState->tempMessageListPaths.clear(); _messageListRepositoryState->nextTemporaryMessageListId = kFirstTemporaryMessageListId; } @@ -733,28 +745,17 @@ void messageListRepositorySetProtoMessageList(int protoMessageList, MessageList* _messageListRepositoryState->protoMessageLists[protoMessageList] = messageList; } -int messageListRepositoryAddExtra(int messageListId, const char* path) +int messageListRepositoryAddExtra(const char* path) { - if (messageListId != 0) { - // CE: Probably there is a bug in Sfall, when |messageListId| is - // non-zero, it is enforced to be within persistent id range. That is - // the scripting engine is allowed to add persistent message lists. - // Everything added/changed by scripting engine should be temporary by - // design. - if (messageListId < kFirstPersistentMessageListId || messageListId > kLastPersistentMessageListId) { - return -1; - } + std::string normalizedPath = messageListRepositoryNormalizePath(path); - // CE: Sfall stores both persistent and temporary message lists in - // one map, however since we've passed check above, we should only - // check in persistent message lists. - if (_messageListRepositoryState->persistentMessageLists.find(messageListId) != _messageListRepositoryState->persistentMessageLists.end()) { - return 0; - } - } else { - if (_messageListRepositoryState->nextTemporaryMessageListId > kLastTemporaryMessageListId) { - return -3; - } + auto it = _messageListRepositoryState->tempMessageListPaths.find(normalizedPath); + if (it != _messageListRepositoryState->tempMessageListPaths.end()) { + return it->second; + } + + if (_messageListRepositoryState->nextTemporaryMessageListId > kLastTemporaryMessageListId) { + return -3; } MessageList* messageList = messageListRepositoryLoad(path); @@ -762,12 +763,11 @@ int messageListRepositoryAddExtra(int messageListId, const char* path) return -2; } - if (messageListId == 0) { - messageListId = _messageListRepositoryState->nextTemporaryMessageListId++; - } - + int messageListId = _messageListRepositoryState->nextTemporaryMessageListId++; _messageListRepositoryState->temporaryMessageLists[messageListId] = messageList; + _messageListRepositoryState->tempMessageListPaths[normalizedPath] = messageListId; + return messageListId; } diff --git a/src/message.h b/src/message.h index 7b9131fc..e17472af 100644 --- a/src/message.h +++ b/src/message.h @@ -89,7 +89,7 @@ void messageListRepositoryReset(); void messageListRepositoryExit(); void messageListRepositorySetStandardMessageList(int messageListId, MessageList* messageList); void messageListRepositorySetProtoMessageList(int messageListId, MessageList* messageList); -int messageListRepositoryAddExtra(int messageListId, const char* path); +int messageListRepositoryAddExtra(const char* path); char* messageListRepositoryGetMsg(int messageListId, int messageId); } // namespace fallout diff --git a/src/sfall_metarules.cc b/src/sfall_metarules.cc index ea91b780..9b5f11a2 100644 --- a/src/sfall_metarules.cc +++ b/src/sfall_metarules.cc @@ -18,6 +18,7 @@ #include "interpreter.h" #include "inventory.h" #include "memory.h" +#include "message.h" #include "object.h" #include "platform_compat.h" #include "scripts.h" @@ -46,6 +47,7 @@ static void mf_get_text_width(Program* program, int args); static void mf_intface_redraw(Program* program, int args); static void mf_loot_obj(Program* program, int args); static void mf_message_box(Program* program, int args); +static void mf_add_extra_msg_file(Program* program, int args); static void mf_metarule_exist(Program* program, int args); static void mf_obj_under_cursor(Program* program, int args); static void mf_opcode_exists(Program* program, int args); @@ -75,7 +77,7 @@ static const MetaruleInfo* currentMetarule() // TODO: argument validation, standard error return value // TODO: reduce code complexity using something like MetaruleContext in sfall const MetaruleInfo kMetarules[] = { - // {"add_extra_msg_file", mf_add_extra_msg_file, 1, 2, -1, {ARG_STRING, ARG_INT}}, + { "add_extra_msg_file", mf_add_extra_msg_file, 1, 2, -1, { ARG_STRING, ARG_INT } }, // {"add_iface_tag", mf_add_iface_tag, 0, 0}, // {"add_g_timer_event", mf_add_g_timer_event, 2, 2, -1, {ARG_INT, ARG_INT}}, // {"add_trait", mf_add_trait, 1, 1, -1, {ARG_INT}}, @@ -312,6 +314,30 @@ void mf_metarule_exist(Program* program, int args) programStackPushInteger(program, 0); } +void mf_add_extra_msg_file(Program* program, int args) +{ + if (args == 2) { + programFatalError("op_sfall_func: '%s': explicit fileNumber is not supported in Fallout 2 CE", currentMetarule()->name); + } + + const char* fileName = programStackPopString(program); + + char path[COMPAT_MAX_PATH]; + snprintf(path, sizeof(path), "%s\\%s", "game", fileName); + + int result = messageListRepositoryAddExtra(path); + switch (result) { + case -2: + programPrintError("%s() - error loading message file.", currentMetarule()->name); + break; + case -3: + programPrintError("%s() - the limit of adding message files has been exceeded.", currentMetarule()->name); + break; + } + + programStackPushInteger(program, result); +} + void mf_opcode_exists(Program* program, int args) { int opcode = programStackPopInteger(program);