From 460bc045b92f18470b51c169994a937dbcbffcd7 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 21 Apr 2021 11:28:48 +0800 Subject: [PATCH] Added language path for premade files (#368) Added AppIface.msg file support to Hero Appearance mod. --- sfall/FalloutEngine/Functions.cpp | 2 +- sfall/FalloutEngine/Functions.h | 2 +- sfall/Modules/Graphics.cpp | 3 ++ sfall/Modules/HeroAppearance.cpp | 56 +++++++++++++++------- sfall/Modules/Message.cpp | 13 +++-- sfall/Modules/Message.h | 2 + sfall/Modules/MiscPatches.cpp | 2 + sfall/Modules/Premade.cpp | 79 +++++++++++++++++++++++++++---- sfall/Modules/Tiles.cpp | 2 +- 9 files changed, 129 insertions(+), 32 deletions(-) diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index 408b6a65..78755a4b 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -408,7 +408,7 @@ BYTE* __fastcall loadPCX(const char* file, long* width, long* height) { } } -long __fastcall get_game_config_string(const char* outValue, const char* section, const char* param) { +long __fastcall get_game_config_string(const char** outValue, const char* section, const char* param) { __asm { mov ebx, param; mov eax, FO_VAR_game_config; diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index b0ad07f3..af529a98 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -82,7 +82,7 @@ void __cdecl trans_buf_to_buf(BYTE* src, long width, long height, long src_width BYTE* __fastcall loadPCX(const char* file, long* width, long* height); -long __fastcall get_game_config_string(const char* outValue, const char* section, const char* param); +long __fastcall get_game_config_string(const char** outValue, const char* section, const char* param); // X-Macro for wrapper functions. #define WRAP_WATCOM_FUNC0(retType, name) \ diff --git a/sfall/Modules/Graphics.cpp b/sfall/Modules/Graphics.cpp index 9b2c1961..5e47d9f3 100644 --- a/sfall/Modules/Graphics.cpp +++ b/sfall/Modules/Graphics.cpp @@ -1205,6 +1205,9 @@ void Graphics::init() { SafeWrite8(0x50FB6B, '2'); // Set call DirectDrawCreate2 HookCall(0x44260C, game_init_hook); + // Patch HRP to show the mouse cursor over the window title + if (Graphics::mode == 5 && hrpVersionValid) SafeWrite8(HRPAddress(0x10027142), CodeType::JumpShort); + textureFilter = IniReader::GetConfigInt("Graphics", "TextureFilter", 1); dlogr(" Done", DL_INIT); } diff --git a/sfall/Modules/HeroAppearance.cpp b/sfall/Modules/HeroAppearance.cpp index a441cd53..861ee13f 100644 --- a/sfall/Modules/HeroAppearance.cpp +++ b/sfall/Modules/HeroAppearance.cpp @@ -700,21 +700,32 @@ void __stdcall HeroSelectWindow(int raceStyleFlag) { int oldFont = GetFont(); SetFont(0x67); - char titleText[16]; - // Get alternate text from ini if available - if (isStyle) { - IniReader::Translate("AppearanceMod", "StyleText", "Style", titleText, 16); + char *RaceStyleBtn, *DoneBtn; + fo::MessageList MsgList; + + if (fo::func::message_load(&MsgList, "game\\AppIface.msg") == 1) { + RaceStyleBtn = GetMsg(&MsgList, (isStyle) ? 101 : 100, 2); + DoneBtn = GetMsg(&MsgList, 102, 2); } else { - IniReader::Translate("AppearanceMod", "RaceText", "Race", titleText, 16); + // Get alternate text from ini if available (TODO: remove this in the future) + char titleText[16]; + char doneText[16]; + if (isStyle) { + IniReader::Translate("AppearanceMod", "StyleText", "Style", titleText, 16); + } else { + IniReader::Translate("AppearanceMod", "RaceText", "Race", titleText, 16); + } + IniReader::Translate("AppearanceMod", "DoneBtn", "Done", doneText, 16); + RaceStyleBtn = titleText; + DoneBtn = doneText; } BYTE textColour = fo::var::PeanutButter; // PeanutButter colour - palette offset stored in mem - DWORD titleTextWidth = fo::GetTextWidth(titleText); - fo::PrintText(titleText, textColour, 92 - titleTextWidth / 2, 10, titleTextWidth, 484, mainSurface); + DWORD titleTextWidth = fo::GetTextWidth(RaceStyleBtn); + fo::PrintText(RaceStyleBtn, textColour, 92 - titleTextWidth / 2, 10, titleTextWidth, 484, mainSurface); - IniReader::Translate("AppearanceMod", "DoneBtn", "Done", titleText, 16); - titleTextWidth = fo::GetTextWidth(titleText); - fo::PrintText(titleText, textColour, 80 - titleTextWidth / 2, 185, titleTextWidth, 484, mainSurface); + titleTextWidth = fo::GetTextWidth(DoneBtn); + fo::PrintText(DoneBtn, textColour, 80 - titleTextWidth / 2, 185, titleTextWidth, 484, mainSurface); surface_draw(484, 230, 484, 0, 0, mainSurface, 484, 0, 0, winSurface); fo::func::win_show(winRef); @@ -1215,18 +1226,27 @@ static void __declspec(naked) FixCharScrnBack() { int oldFont = GetFont(); SetFont(0x67); - char RaceText[8], StyleText[8]; - // Get alternate text from ini if available - IniReader::Translate("AppearanceMod", "RaceText", "Race", RaceText, 8); - IniReader::Translate("AppearanceMod", "StyleText", "Style", StyleText, 8); + char *RaceBtn, *StyleBtn; + fo::MessageList MsgList; - DWORD raceTextWidth = fo::GetTextWidth(RaceText); - DWORD styleTextWidth = fo::GetTextWidth(StyleText); + if (fo::func::message_load(&MsgList, "game\\AppIface.msg") == 1) { + RaceBtn = GetMsg(&MsgList, 100, 2); + StyleBtn = GetMsg(&MsgList, 101, 2); + } else { + // Get alternate text from ini if available (TODO: remove this in the future) + char RaceText[8], StyleText[8]; + IniReader::Translate("AppearanceMod", "RaceText", "Race", RaceText, 8); + IniReader::Translate("AppearanceMod", "StyleText", "Style", StyleText, 8); + RaceBtn = RaceText; + StyleBtn = StyleText; + } + DWORD raceTextWidth = fo::GetTextWidth(RaceBtn); + DWORD styleTextWidth = fo::GetTextWidth(StyleBtn); BYTE PeanutButter = fo::var::PeanutButter; // palette offset stored in mem - fo::PrintText(RaceText, PeanutButter, 372 - raceTextWidth / 2, 6, raceTextWidth, 640, charScrnBackSurface); - fo::PrintText(StyleText, PeanutButter, 372 - styleTextWidth / 2, 231, styleTextWidth, 640, charScrnBackSurface); + fo::PrintText(RaceBtn, PeanutButter, 372 - raceTextWidth / 2, 6, raceTextWidth, 640, charScrnBackSurface); + fo::PrintText(StyleBtn, PeanutButter, 372 - styleTextWidth / 2, 231, styleTextWidth, 640, charScrnBackSurface); SetFont(oldFont); } diff --git a/sfall/Modules/Message.cpp b/sfall/Modules/Message.cpp index 74d97749..33bcf507 100644 --- a/sfall/Modules/Message.cpp +++ b/sfall/Modules/Message.cpp @@ -53,6 +53,12 @@ const fo::MessageList* gameMsgFiles[] = { }; #undef CASTMSG +static char gameLanguage[128]; + +const char* Message::GameLanguage() { + return &gameLanguage[0]; +} + ExtraGameMessageListsMap gExtraGameMsgLists; static std::vector msgFileList; @@ -244,9 +250,10 @@ static void ClearScriptAddedExtraGameMsg() { // C++11 } static void FallbackEnglishLoadMsgFiles() { - char value[128]; - if (fo::func::get_game_config_string(value, "system", "language") && _stricmp(value, "english") != 0) { - HookCall(0x484B18, message_load_hook); + const char* lang; + if (fo::func::get_game_config_string(&lang, "system", "language")) { + strncpy_s(gameLanguage, lang, _TRUNCATE); + if (_stricmp(lang, "english") != 0) HookCall(0x484B18, message_load_hook); } } diff --git a/sfall/Modules/Message.h b/sfall/Modules/Message.h index 1e244000..a57d8244 100644 --- a/sfall/Modules/Message.h +++ b/sfall/Modules/Message.h @@ -59,6 +59,8 @@ public: void init(); void exit() override; + static const char* GameLanguage(); + static long AddExtraMsgFile(const char* msgName, long msgNumber); }; diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 4c0746d5..508d801a 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -815,6 +815,8 @@ void MiscPatches::init() { BlockCall(0x4425E6); // Patch out ereg call + SafeWrite8(0x4810AB, CodeType::JumpShort); // Disable selfrun + SimplePatch(0x440C2A, "Misc", "SpecialDeathGVAR", fo::GVAR_MODOC_SHITTY_DEATH); // Remove hardcoding for maps with IDs 19 and 37 diff --git a/sfall/Modules/Premade.cpp b/sfall/Modules/Premade.cpp index 5c1ae4b3..8451dc92 100644 --- a/sfall/Modules/Premade.cpp +++ b/sfall/Modules/Premade.cpp @@ -17,14 +17,73 @@ */ #include "..\main.h" -#include "..\FalloutEngine\Structs.h" +#include "..\FalloutEngine\Fallout2.h" +#include "Message.h" #include "Premade.h" namespace sfall { -fo::PremadeChar* premade; +static fo::PremadeChar* premade; + +static const char* __fastcall GetLangPremadePath(const char* premadePath) { + static char premadeLangPath[64]; // premade\\combat.bio + static bool isDefault = false; + static long len = 0; + + if (isDefault) return nullptr; + if (len == 0) { + len = std::strlen(Message::GameLanguage()); + if (len == 0 || len > 40) { + isDefault = true; + return nullptr; + } + isDefault = (_stricmp(Message::GameLanguage(), "english") == 0); + if (isDefault) return nullptr; + + std::strncpy(premadeLangPath, premadePath, 8); + std::strcpy(&premadeLangPath[8], Message::GameLanguage()); + } + std::strcpy(&premadeLangPath[8 + len], &premadePath[7]); + + return premadeLangPath; +} + +static const char* __fastcall PremadeGCD(const char* premadePath) { + const char* path = GetLangPremadePath(premadePath); + return (path && fo::func::db_access(path)) ? path : premadePath; +} + +static fo::DbFile* __fastcall PremadeBIO(const char* premadePath, const char* mode) { + premadePath = GetLangPremadePath(premadePath); + return (premadePath) ? fo::func::db_fopen(premadePath, mode) : nullptr; +} + +static void __declspec(naked) select_display_bio_hook() { + __asm { + push eax; + push edx; + mov ecx, eax; // premade path + call PremadeBIO; + test eax, eax; + jz default; + add esp, 8; + retn; +default: + pop edx; + pop eax; + jmp fo::funcoffs::db_fopen_; + } +} + +static void __declspec(naked) select_update_display_hook() { + __asm { + mov ecx, eax; // premade path + call PremadeGCD; + jmp fo::funcoffs::proto_dude_init_; + } +} void Premade::init() { auto premadePaths = IniReader::GetConfigList("misc", "PremadePaths", "", 512); @@ -39,17 +98,21 @@ void Premade::init() { dlog_f(" Failed: %s exceeds 11 characters\n", DL_INIT, premadePaths[i].c_str()); return; } - strcpy(premade[i].path, path.c_str()); + std::strcpy(premade[i].path, path.c_str()); premade[i].fid = atoi(premadeFids[i].c_str()); } - SafeWrite32(0x51C8D4, count); - SafeWrite32(0x4A7D76, (DWORD)premade); - SafeWrite32(0x4A8B1E, (DWORD)premade); - SafeWrite32(0x4A7E2C, (DWORD)&premade[0].fid); - strcpy((char*)0x50AF68, premade[0].path); + SafeWrite32(0x51C8D4, count); // _premade_total + SafeWrite32(0x4A7D76, (DWORD)premade); // select_update_display_ + SafeWrite32(0x4A8B1E, (DWORD)premade); // select_display_bio_ + SafeWrite32(0x4A7E2C, (DWORD)&premade[0].fid); // select_display_portrait_ + std::strcpy((char*)0x50AF68, premade[0].path); // for selfrun dlogr(" Done", DL_INIT); } + + // Add language path for premade GCD/BIO files + HookCall(0x4A8B44, select_display_bio_hook); + HookCall(0x4A7D91, select_update_display_hook); } } diff --git a/sfall/Modules/Tiles.cpp b/sfall/Modules/Tiles.cpp index 2078ebb9..e50d6474 100644 --- a/sfall/Modules/Tiles.cpp +++ b/sfall/Modules/Tiles.cpp @@ -208,7 +208,7 @@ static int __stdcall ArtInitHook() { if (listpos != tiles->total) { tiles->names = (char*)fo::func::mem_realloc(tiles->names, listpos * 13); for (long i = tiles->total; i < listpos; i++) { - sprintf_s(&tiles->names[i * 13], 12, "zzz%04d.frm", i - tiles->total); + sprintf(&tiles->names[i * 13], "zzz%04d.frm", i - tiles->total); } tiles->total = listpos; }