diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 43d1a1c3..f6fe51c6 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -6,6 +6,7 @@ UseCommandLine=0 ;Uncomment and point to a file to get alternate translations for some sfall messages +;This file can be placed in text\\ for sfall to get the translations depending on the game language ;TranslationsINI=sfall\Translations.ini ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index d7e36843..c362c6b8 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -508,18 +508,52 @@ long __fastcall fo_get_game_config_string(const char** outValue, const char* sec static MSGNode messageBuf; -const char* GetMessageStr(const MSGList* fileAddr, long messageId) { - return fo_getmsg(fileAddr, &messageBuf, messageId); +const char* GetMessageStr(const MSGList* file, long messageId) { + return fo_getmsg(file, &messageBuf, messageId); } -const char* MessageSearch(const MSGList* fileAddr, long messageId) { +const char* MessageSearch(const MSGList* file, long messageId) { messageBuf.number = messageId; - if (fo_message_search(fileAddr, &messageBuf) == 1) { + if (fo_message_search(file, &messageBuf) == 1) { return messageBuf.message; } return nullptr; } +MSGNode* GetMsgNode(MSGList* msgList, int msgNum) { + if (msgList != nullptr && msgList->numMsgs > 0) { + MSGNode *msgNode = msgList->nodes; + long last = msgList->numMsgs - 1; + long first = 0; + long mid; + + // Use Binary Search to find msg + while (first <= last) { + mid = (first + last) / 2; + if (msgNum > msgNode[mid].number) + first = mid + 1; + else if (msgNum < msgNode[mid].number) + last = mid - 1; + else + return &msgNode[mid]; + } + } + return nullptr; +} + +// Alternative version of getmsg_ function +char* GetMsg(MSGList* msgList, int msgNum, int msgType) { + MSGNode *msgNode = GetMsgNode(msgList, msgNum); + if (msgNode) { + if (msgType == 2) { + return msgNode->message; + } else if (msgType == 1) { + return msgNode->audio; + } + } + return nullptr; +} + Queue* QueueFind(TGameObj* object, long type) { if (*ptr_queue) { Queue* queue = *ptr_queue; @@ -981,14 +1015,23 @@ static bool LoadFrmFrame(UNLSTDfrm::Frame *frame, DbFile* frmStream) { UNLSTDfrm *LoadUnlistedFrm(char *frmName, unsigned int folderRef) { if (folderRef > OBJ_TYPE_SKILLDEX) return nullptr; - char *artfolder = ptr_art[folderRef].path; // address of art type name - char FrmPath[MAX_PATH]; + const char *artfolder = ptr_art[folderRef].path; // address of art type name + char frmPath[MAX_PATH]; - sprintf_s(FrmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName); + if (*ptr_use_language) { + sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s\\%s", (const char*)ptr_language, artfolder, frmName); + } else { + sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName); + } UNLSTDfrm *frm = new UNLSTDfrm; - DbFile* frmStream = fo_xfopen(FrmPath, "rb"); + DbFile* frmStream = fo_db_fopen(frmPath, "rb"); + + if (!frmStream && *ptr_use_language) { + sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName); + frmStream = fo_db_fopen(frmPath, "rb"); + } if (frmStream != nullptr) { if (!LoadFrmHeader(frm, frmStream)) { diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index a2eee11c..b5e17167 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -33,6 +33,7 @@ // Global variable offsets // TODO: probably need to hide these by moving inside implementation file +#define FO_VAR_aDialogS_msg 0x50DBE8 #define FO_VAR_aiInfoList 0x510948 #define FO_VAR_ambient_light 0x51923C #define FO_VAR_anim_set 0x54CC14 @@ -42,6 +43,9 @@ #define FO_VAR_art_vault_guy_num 0x5108A4 #define FO_VAR_art_vault_person_nums 0x5108A8 #define FO_VAR_artCritterFidShouldRunData 0x56CAF0 +#define FO_VAR_aTextSCuts 0x501A8C +#define FO_VAR_aTextSCutsS 0x503530 +#define FO_VAR_aTextSCutsSS 0x50B01C #define FO_VAR_background_volume 0x518E88 #define FO_VAR_bboxslot 0x5970E0 #define FO_VAR_bckgnd 0x5707A4 @@ -154,6 +158,7 @@ #define FO_VAR_itemButtonItems 0x5970F8 #define FO_VAR_itemCurrentItem 0x518F78 #define FO_VAR_kb_lock_flags 0x51E2EA +#define FO_VAR_language 0x56C970 // aka _darn_foreign_sub_path #define FO_VAR_last_buttons 0x51E2AC #define FO_VAR_last_button_winID 0x51E404 #define FO_VAR_last_level 0x5707B4 @@ -293,6 +298,7 @@ #define FO_VAR_title_color 0x56D750 #define FO_VAR_title_font 0x56D748 #define FO_VAR_trait_data 0x51DB84 +#define FO_VAR_use_language 0x510898 // aka _darn_foreigners #define FO_VAR_view_page 0x664520 #define FO_VAR_wd_obj 0x59E98C #define FO_VAR_window 0x6ADE58 @@ -505,6 +511,10 @@ const char* GetMessageStr(const MSGList* fileAddr, long messageId); // similar to GetMessageStr, but returns nullptr when no message is found const char* MessageSearch(const MSGList* fileAddr, long messageId); +MSGNode* GetMsgNode(MSGList* msgList, int msgNum); + +char* GetMsg(MSGList* msgList, int msgNum, int msgType); + Queue* QueueFind(TGameObj* object, long type); // returns weapon animation code diff --git a/sfall/FalloutVars_def.h b/sfall/FalloutVars_def.h index e347b533..d8981684 100644 --- a/sfall/FalloutVars_def.h +++ b/sfall/FalloutVars_def.h @@ -90,6 +90,7 @@ PTR_(inventry_message_file, MSGList) PTR_(itemButtonItems, ItemButtonItem) // array of 2 ItemButtonItem, 0 - left, 1 - right PTR_(itemCurrentItem, long) // 0 - left, 1 - right PTR_(kb_lock_flags, DWORD) +PTR_(language, char) // array of 32 char PTR_(last_buttons, DWORD) PTR_(last_button_winID, DWORD) PTR_(last_level, DWORD) @@ -212,7 +213,8 @@ PTR_(text_width, DWORD) PTR_(tile, DWORD) PTR_(title_color, DWORD) PTR_(title_font, DWORD) -PTR_(trait_data, TraitInfo) // // array of size == TRAIT_count +PTR_(trait_data, TraitInfo) // array of size == TRAIT_count +PTR_(use_language, DWORD) PTR_(view_page, DWORD) PTR_(wd_obj, DWORD) PTR_(window, WINinfo*) // array of 50 WINinfo* diff --git a/sfall/IniReader.cpp b/sfall/IniReader.cpp index aac33fa4..47a5e6d3 100644 --- a/sfall/IniReader.cpp +++ b/sfall/IniReader.cpp @@ -20,7 +20,7 @@ #include "IniReader.h" -DWORD IniReader_modifiedIni; +DWORD modifiedIni; static const char* ddrawIni = ".\\ddraw.ini"; static char ini[65] = ".\\"; @@ -107,5 +107,5 @@ int SetConfigInt(const char* section, const char* setting, int value) { } void IniReader_Init() { - IniReader_modifiedIni = GetConfigInt("Main", "ModifiedIni", 0); + modifiedIni = GetConfigInt("Main", "ModifiedIni", 0); } diff --git a/sfall/IniReader.h b/sfall/IniReader.h index dd77ff4c..88ef381e 100644 --- a/sfall/IniReader.h +++ b/sfall/IniReader.h @@ -18,7 +18,7 @@ #pragma once -extern DWORD IniReader_modifiedIni; +extern DWORD modifiedIni; void IniReader_Init(); diff --git a/sfall/LoadOrder.cpp b/sfall/LoadOrder.cpp index 7b5b0bf5..aa581545 100644 --- a/sfall/LoadOrder.cpp +++ b/sfall/LoadOrder.cpp @@ -283,6 +283,32 @@ artNotExist: } } +static DbFile* __fastcall LoadFont(const char* font, const char* mode) { + char file[128]; + const char* lang; + if (fo_get_game_config_string(&lang, "system", "language") && _stricmp(lang, "english") != 0) { + std::sprintf(file, "%s\\%s", lang, font); + return fo_db_fopen(file, mode); + } + return nullptr; +} + +void __declspec(naked) load_font_hook() { + __asm { + mov ebp, edx; + mov ebx, eax; + mov ecx, eax; + call LoadFont; + test eax, eax; + jz default; + retn; +default: + mov edx, ebp; + mov eax, ebx; + jmp db_fopen_; + } +} + void LoadOrder_OnGameLoad() { savPrototypes.clear(); RemoveSavFiles(); @@ -318,4 +344,11 @@ void LoadOrder_Init() { MakeCall(0x47FB80, SlotMap2Game_hack); // load game MakeCall(0x47FBBF, SlotMap2Game_hack_attr, 1); dlogr(" Done", DL_INIT); + + // Load fonts based on the game language + const DWORD loadFontAddr[] = { + 0x4D5621, // load_font_ + 0x441D58 // FMLoadFont_ + }; + HookCalls(load_font_hook, loadFontAddr); } diff --git a/sfall/Message.cpp b/sfall/Message.cpp index f84143fc..3e873d43 100644 --- a/sfall/Message.cpp +++ b/sfall/Message.cpp @@ -48,7 +48,7 @@ const MSGList* gameMsgFiles[] = { }; #undef CASTMSG -static char gameLanguage[41]; // max length of language string is 40 +static char gameLanguage[32]; // (max length of language string is 40) const char* Message_GameLanguage() { return &gameLanguage[0]; @@ -82,39 +82,6 @@ noFile: } } -MSGNode* GetMsgNode(MSGList* msgList, int msgRef) { - if (msgList != nullptr && msgList->numMsgs > 0) { - MSGNode *MsgNode = msgList->nodes; - long last = msgList->numMsgs - 1; - long first = 0; - long mid; - - // Use Binary Search to find msg - while (first <= last) { - mid = (first + last) / 2; - if (msgRef > MsgNode[mid].number) - first = mid + 1; - else if (msgRef < MsgNode[mid].number) - last = mid - 1; - else - return &MsgNode[mid]; - } - } - return nullptr; -} - -char* GetMsg(MSGList* msgList, int msgRef, int msgNum) { - MSGNode *msgNode = GetMsgNode(msgList, msgRef); - if (msgNode) { - if (msgNum == 2) { - return msgNode->message; - } else if (msgNum == 1) { - return msgNode->audio; - } - } - return nullptr; -} - void ReadExtraGameMsgFiles() { if (!msgFileList.empty()) { int number = 0; diff --git a/sfall/Message.h b/sfall/Message.h index 41bc7f20..01482007 100644 --- a/sfall/Message.h +++ b/sfall/Message.h @@ -21,7 +21,7 @@ #include #include "main.h" -#include "FalloutEngine.h" +//#include "FalloutEngine.h" #define MSG_FILE_COMBAT (0x56D368) #define MSG_FILE_AI (0x56D510) @@ -58,6 +58,3 @@ void ClearScriptAddedExtraGameMsg(); const char* Message_GameLanguage(); long __stdcall Message_AddExtraMsgFile(const char* msgName, long msgNumber); - -MSGNode* GetMsgNode(MSGList* msgList, int msgRef); -char* GetMsg(MSGList* msgList, int msgRef, int msgNum); diff --git a/sfall/MiscPatches.cpp b/sfall/MiscPatches.cpp index 58f3197c..28925bd7 100644 --- a/sfall/MiscPatches.cpp +++ b/sfall/MiscPatches.cpp @@ -23,7 +23,7 @@ #include "ScriptExtender.h" -static char mapName[17] = {}; +static char mapName[16] = {}; static char patchName[65] = {}; static char versionString[65] = {}; @@ -711,7 +711,7 @@ static void EngineOptimizationPatches() { void MiscPatches_Init() { EngineOptimizationPatches(); - if (GetConfigString("Misc", "StartingMap", "", mapName, 17)) { + if (GetConfigString("Misc", "StartingMap", "", mapName, 16)) { dlog("Applying starting map patch.", DL_INIT); SafeWrite32(0x480AAA, (DWORD)&mapName); dlogr(" Done", DL_INIT); diff --git a/sfall/PlayerModel.cpp b/sfall/PlayerModel.cpp index 8576eb91..410edb33 100644 --- a/sfall/PlayerModel.cpp +++ b/sfall/PlayerModel.cpp @@ -21,19 +21,19 @@ #include "Logging.h" #include "SafeWrite.h" -static char startMaleModelName[65] = {}; +static char startMaleModelName[33] = {}; char defaultMaleModelName[65] = {}; -static char startFemaleModelName[65] = {}; +static char startFemaleModelName[33] = {}; char defaultFemaleModelName[65] = {}; void PlayerModel_Init() { - if (GetConfigString("Misc", "MaleStartModel", "", startMaleModelName, 65)) { + if (GetConfigString("Misc", "MaleStartModel", "", startMaleModelName, 33)) { dlog("Applying male start model patch.", DL_INIT); SafeWrite32(0x418B88, (DWORD)&startMaleModelName); dlogr(" Done", DL_INIT); } - if (GetConfigString("Misc", "FemaleStartModel", "", startFemaleModelName, 65)) { + if (GetConfigString("Misc", "FemaleStartModel", "", startFemaleModelName, 33)) { dlog("Applying female start model patch.", DL_INIT); SafeWrite32(0x418BAB, (DWORD)&startFemaleModelName); dlogr(" Done", DL_INIT); diff --git a/sfall/Premade.cpp b/sfall/Premade.cpp index 40242dc3..97a76a69 100644 --- a/sfall/Premade.cpp +++ b/sfall/Premade.cpp @@ -23,14 +23,14 @@ static PremadeChar* premade; static const char* __fastcall GetLangPremadePath(const char* premadePath) { - static char premadeLangPath[65]; // premade\\combat.bio + static char premadeLangPath[56]; // 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) { + if (len == 0 || len >= 32) { isDefault = true; return nullptr; } diff --git a/sfall/ScriptOps/InterfaceOps.hpp b/sfall/ScriptOps/InterfaceOps.hpp index 81df47c3..a93247e7 100644 --- a/sfall/ScriptOps/InterfaceOps.hpp +++ b/sfall/ScriptOps/InterfaceOps.hpp @@ -568,16 +568,27 @@ static FrmFile* __stdcall LoadArtFile(const char* file, long frame, long directi return frmPtr; } -static long __stdcall GetArtFIDFile(long fid, const char* &file) { +static long __stdcall GetArtFIDFile(long fid, char* outFilePath) { long direction = 0; long _fid = fid & 0xFFFFFFF; - file = fo_art_get_name(_fid); // .frm + + const char* artPathName = fo_art_get_name(_fid); // \art\type\file.frm + if (_fid >> 24 == OBJ_TYPE_CRITTER) { direction = (fid >> 28); - if (direction > 0 && !fo_db_access(file)) { - file = fo_art_get_name(fid); // .fr# + if (direction > 0 && !fo_db_access(artPathName)) { + artPathName = fo_art_get_name(fid); // .fr# + } + } else { + if (*ptr_use_language) { + const char* _artPathName = std::strchr(artPathName, '\\'); + if (_artPathName && *(++_artPathName)) { + sprintf_s(outFilePath, MAX_PATH, "art\\%s\\%s", (const char*)ptr_language, _artPathName); + if (fo_db_access(outFilePath)) return direction; + } } } + std::strcpy(outFilePath, artPathName); return direction; } @@ -594,7 +605,9 @@ static long __stdcall DrawImage(OpcodeHandler& opHandler, bool isScaled, const c long fid = opHandler.arg(0).rawValue(); if (fid == -1) return -1; - direction = GetArtFIDFile(fid, file); + char fileBuf[MAX_PATH]; + direction = GetArtFIDFile(fid, fileBuf); + file = fileBuf; } else { file = opHandler.arg(0).strValue(); // path to frm/pcx file } @@ -664,7 +677,10 @@ static long __stdcall InterfaceDrawImage(OpcodeHandler& opHandler, WINinfo* ifac if (fid == -1) return -1; useShift = (((fid & 0xF000000) >> 24) == OBJ_TYPE_CRITTER); - direction = GetArtFIDFile(fid, file); + + char fileBuf[MAX_PATH]; + direction = GetArtFIDFile(fid, fileBuf); + file = fileBuf; } else { file = opHandler.arg(1).strValue(); // path to frm/pcx file } diff --git a/sfall/ScriptOps/MiscOps.hpp b/sfall/ScriptOps/MiscOps.hpp index 17f18d90..0e8cb722 100644 --- a/sfall/ScriptOps/MiscOps.hpp +++ b/sfall/ScriptOps/MiscOps.hpp @@ -524,7 +524,7 @@ fail: static void __declspec(naked) op_modified_ini() { __asm { - mov edx, IniReader_modifiedIni; + mov edx, modifiedIni; _J_RET_VAL_TYPE(VAR_TYPE_INT); } }