mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
Implement sfall add_extra_message_file (#327)
https://sfall-team.github.io/sfall/sfall-funcx-macros/#add_extra_msg_file For now we're choosing to not support the 2-arg form. We could return `-1` if the script tries to do this, but if so and it doesn't check for an error, that's likely worse than just crashing at this point. It's essentially an unimplemented opcode.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
#include "test_utils.h"
|
||||
#include "sfall.h"
|
||||
|
||||
// to test, copy test_extra_msg_file.msg into <game folder>/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
|
||||
@@ -0,0 +1 @@
|
||||
{100}{}{auto extra msg}
|
||||
+24
-24
@@ -1,5 +1,6 @@
|
||||
#include "message.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -37,6 +38,7 @@ struct MessageListRepositoryState {
|
||||
std::array<MessageList*, PROTO_MESSAGE_LIST_COUNT> protoMessageLists;
|
||||
std::unordered_map<int, MessageList*> persistentMessageLists;
|
||||
std::unordered_map<int, MessageList*> temporaryMessageLists;
|
||||
std::unordered_map<std::string, int> 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<char>(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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+27
-1
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user