From 8613a4fb8b85972635854113c6db619e31df56f5 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 19 Feb 2019 11:55:38 +0800 Subject: [PATCH] Added ExtraPatches section to ddraw.ini for setting multiple custom paths for game files (from Mr.Stalin, #206) Refactored some code of DataLoadOrderPatch to work with new ExtraPatches. Removed MultiPatches from ddraw.ini, now it's always enabled. Minor edits to MiscPatches.cpp and Arrays.cpp. Updated version number. --- artifacts/ddraw.ini | 15 ++-- sfall/FalloutEngine/Functions.cpp | 4 + sfall/FalloutEngine/Functions.h | 2 + sfall/Modules/LoadOrder.cpp | 129 +++++++++++++++++------------ sfall/Modules/MiscPatches.cpp | 40 ++++----- sfall/Modules/Objects.cpp | 2 +- sfall/Modules/Scripting/Arrays.cpp | 2 +- sfall/Modules/Scripting/Arrays.h | 5 +- sfall/version.h | 6 +- 9 files changed, 122 insertions(+), 83 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index ba694096..3d0bb23c 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -1,5 +1,5 @@ ;sfall configuration settings -;v4.1.5 +;v4.1.6 [Main] ;Change to 1 if you want to use command line args to tell sfall to use another ini file. @@ -8,6 +8,14 @@ UseCommandLine=0 ;Uncomment and point to a file to get alternate translations for some sfall messages ;TranslationsINI=./Translations.ini +;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[ExtraPatches] +;This section allows you to set multiple paths to folders containing mods or patches that will be loaded by the game +;The priority of read files will be higher than the files in patchXXX.dat. If DataLoadOrderPatch is enabled, the data load order will be: +;master_patches > critter_patches > PatchFile99 - PatchFile0 > patchXXX.dat > ... +;Paths to folders and Fallout .dat files are supported. The available range for PatchFile option names is from 0 to 99 +;PatchFile0=mods\RP_data + ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Sound] ;Sets the number of allowed simultaneous sound effects @@ -200,11 +208,8 @@ WorldMapSlots=0 UseFileSystemOverride=0 ;To use a patch file other than patch000.dat, uncomment the next line and add your new file name -;If you want to check for multiple patch files, you can include a %d in the file name (sprintf syntax) -;By default, only the first patch file found will be used. -;If you want to load multiple patch files (up to 1000) at once, uncomment the MultiPatches line and set it to 1 +;If you want to load multiple patch files (up to 1000) at once, you can include a %d in the file name (sprintf syntax) ;PatchFile=patch%03d.dat -;MultiPatches=0 ;Set to 1 to use the modified data load order for the engine to find game data ;Original: patchXXX.dat > critter_patches > critter_dat > f2_res_patches > f2_res_dat > master_patches > master_dat diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index 316e654b..d891082b 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -160,6 +160,10 @@ long __stdcall db_get_file_list(const char* searchMask, char* * *fileList) { WRAP_WATCOM_CALL2(db_get_file_list_, searchMask, fileList) } +long __stdcall db_init(const char* path_dat, const char* path_patches) { + WRAP_WATCOM_CALL3(db_init_, path_dat, 0, path_patches) +} + // Check fallout paths for file long __stdcall CheckFile(char *fileName, DWORD *sizeOut) { WRAP_WATCOM_CALL2(db_dir_entry_, fileName, sizeOut) diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index 91f0e9be..af899736 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -76,6 +76,8 @@ long __stdcall db_fwriteByteCount(DbFile* file, const BYTE* cptr, long count); // returns number of elements in *fileList long __stdcall db_get_file_list(const char* searchMask, char* * *fileList); +long __stdcall db_init(const char* path_dat, const char* path_patches); + // prints message to debug.log file void __declspec() debug_printf(const char* fmt, ...); diff --git a/sfall/Modules/LoadOrder.cpp b/sfall/Modules/LoadOrder.cpp index 309e5915..4f65a04c 100644 --- a/sfall/Modules/LoadOrder.cpp +++ b/sfall/Modules/LoadOrder.cpp @@ -37,6 +37,8 @@ static bool femaleCheck = false; // flag for check female dialog file static DWORD format; static bool cutsPatch = false; +static std::vector patchFiles; + static void CheckPlayerGender() { isFemale = fo::HeroIsFemale(); @@ -66,7 +68,7 @@ static void __declspec(naked) scr_get_dialog_msg_file_hack1() { push msgFemaleFolder; jmp scr_get_dialog_msg_file_Back; default: - push FO_VAR_aDialogS_msg; // default "dialog\%s.msg" + push FO_VAR_aDialogS_msg; // default "dialog\%s.msg" jmp scr_get_dialog_msg_file_Back; } } @@ -99,81 +101,102 @@ static void __declspec(naked) gnw_main_hack() { } } -static void __declspec(naked) removeDatabase() { - __asm { - cmp eax, -1 - je end - mov ebx, ds:[FO_VAR_paths] - mov ecx, ebx -nextPath: - mov edx, [esp+0x104+4+4] // path_patches - mov eax, [ebx] // database.path - call fo::funcoffs::stricmp_ - test eax, eax // found path? - jz skip // Yes - mov ecx, ebx - mov ebx, [ebx+0xC] // database.next - jmp nextPath -skip: - mov eax, [ebx+0xC] // database.next - mov [ecx+0xC], eax // database.next - xchg ebx, eax - cmp eax, ecx - jne end - mov ds:[FO_VAR_paths], ebx -end: - retn +static fo::PathNode* __fastcall RemoveDatabase(const char* pathPatches) { + auto paths = fo::var::paths; // curr.node (beginning of the load order) + auto _paths = paths; // prev.node + + while (paths) { + if (_stricmp(paths->path, pathPatches) == 0) { // found path + fo::PathNode* nextPaths = paths->next; // pointer to the node of the next path +// TODO: need to check if this condition is used correctly + if (paths != _paths) + _paths->next = nextPaths; // replace the pointer in the previous node, removing the current(found) path from the load order + else // if the current node is equal to the previous node + fo::var::paths = nextPaths; // set the next node at the beginning of the load order + return paths; // return the pointer of the current removed node (save the pointer) + } + _paths = paths; // prev.node <- curr.node + paths = paths->next; // take a pointer to the next path from the current node } + return nullptr; // it's possible that this will create an exceptional situation for the game, although such a situation should not arise } +// Remove master_patches from the load order static void __declspec(naked) game_init_databases_hack1() { __asm { - call removeDatabase - mov ds:[FO_VAR_master_db_handle], eax - retn + cmp eax, -1; + je skip; + mov ecx, [esp + 0x104 + 4]; // path_patches + call RemoveDatabase; +skip: + mov ds:[FO_VAR_master_db_handle], eax; // the pointer of master_patches node will be saved here + retn; } } +// Remove critter_patches from the load order static void __declspec(naked) game_init_databases_hack2() { __asm { - cmp eax, -1 - je end - mov eax, ds:[FO_VAR_master_db_handle] - mov eax, [eax] // eax = master_patches.path - call fo::funcoffs::xremovepath_ - dec eax // remove path (critter_patches == master_patches)? - jz end // Yes - inc eax - call removeDatabase + cmp eax, -1; + je end; + mov eax, ds:[FO_VAR_master_db_handle]; // pointer to master_patches node + mov eax, [eax]; // eax = master_patches.path + call fo::funcoffs::xremovepath_; + dec eax; // remove path (critter_patches == master_patches)? + jz end; // Yes (jump if 0) + mov ecx, [esp + 0x104 + 4]; // path_patches + call RemoveDatabase; end: - mov ds:[FO_VAR_critter_db_handle], eax - retn + mov ds:[FO_VAR_critter_db_handle], eax; // the pointer of critter_patches node will be saved here + retn; } } -static void __declspec(naked) game_init_databases_hook() { -// eax = _master_db_handle - __asm { - mov ecx, ds:[FO_VAR_critter_db_handle] - mov edx, ds:[FO_VAR_paths] - jecxz skip - mov [ecx+0xC], edx // critter_patches.next->_paths - mov edx, ecx -skip: - mov [eax+0xC], edx // master_patches.next - mov ds:[FO_VAR_paths], eax - retn +static void __stdcall InitExtraPatches() { + for (auto it = patchFiles.begin(); it != patchFiles.end(); it++) { + fo::func::db_init(it->c_str(), 0); + } +} + +static void __fastcall game_init_databases_hook() { + fo::PathNode* master_patches; + __asm mov master_patches, eax; // eax = _master_db_handle + + if (!patchFiles.empty()) InitExtraPatches(); + + fo::PathNode* critter_patches = (fo::PathNode*)fo::var::critter_db_handle; + fo::PathNode* paths = fo::var::paths; // beginning of the load order + // insert master_patches/critter_patches at the beginning of the load order + if (critter_patches) { + critter_patches->next = paths; // critter_patches.next -> paths + paths = critter_patches; + } + master_patches->next = paths; // master_patches.next -> paths + fo::var::paths = master_patches; // set master_patches node at the beginning of the load order +} + +static void GetExtraPatches() { + char patchFile[12] = "PatchFile"; + for (int i = 0; i < 100; i++) { + _itoa(i, &patchFile[9], 10); + auto patch = GetConfigString("ExtraPatches", patchFile, "", MAX_PATH); + if (patch.empty() || GetFileAttributes(patch.c_str()) == INVALID_FILE_ATTRIBUTES) continue; + patchFiles.push_back(patch); } } void LoadOrder::init() { + GetExtraPatches(); + if (GetConfigInt("Misc", "DataLoadOrderPatch", 0)) { dlog("Applying data load order patch.", DL_INIT); MakeCall(0x444259, game_init_databases_hack1); MakeCall(0x4442F1, game_init_databases_hack2); - HookCall(0x44436D, &game_init_databases_hook); - SafeWrite8(0x4DFAEC, 0x1D); // error correction + HookCall(0x44436D, game_init_databases_hook); + SafeWrite8(0x4DFAEC, 0x1D); // error correction (ecx > ebx) dlogr(" Done", DL_INIT); + } else if (!patchFiles.empty()) { + HookCall(0x44436D, InitExtraPatches); } femaleMsgs = GetConfigInt("Misc", "FemaleDialogMsgs", 0); diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index ca765b69..d0f9bc8a 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -284,25 +284,27 @@ really_end: static DWORD RetryCombatLastAP; static DWORD RetryCombatMinAP; static void __declspec(naked) RetryCombatHook() { + using namespace fo; + using namespace Fields; __asm { - mov RetryCombatLastAP, 0; + mov RetryCombatLastAP, 0; retry: - mov eax, esi; - xor edx, edx; call fo::funcoffs::combat_ai_; process: - cmp dword ptr ds:[FO_VAR_combat_turn_running], 0; - jle next; + cmp dword ptr ds:[FO_VAR_combat_turn_running], 0; + jle next; call fo::funcoffs::process_bk_; - jmp process; + jmp process; next: - mov eax, [esi+0x40]; - cmp eax, RetryCombatMinAP; - jl end; - cmp eax, RetryCombatLastAP; - je end; - mov RetryCombatLastAP, eax; - jmp retry; + mov eax, [esi + movePoints]; + cmp eax, RetryCombatMinAP; + jl end; + cmp eax, RetryCombatLastAP; + je end; + mov RetryCombatLastAP, eax; + mov eax, esi; + xor edx, edx; + jmp retry; end: retn; } @@ -619,19 +621,19 @@ void DontTurnOffSneakIfYouRunPatch() { void CombatProcFix() { //Ray's combat_p_proc fix dlog("Applying combat_p_proc fix.", DL_INIT); - SafeWrite32(0x0425253, ((DWORD)&Combat_p_procFix) - 0x0425257); - SafeWrite8(0x0424dbc, 0xE9); - SafeWrite32(0x0424dbd, 0x00000034); + HookCall(0x425252, Combat_p_procFix); + SafeWrite8(0x424DBC, 0xE9); + SafeWrite32(0x424DBD, 0x00000034); dlogr(" Done", DL_INIT); } void MultiPatchesPatch() { - if (GetConfigInt("Misc", "MultiPatches", 0)) { + //if (GetConfigInt("Misc", "MultiPatches", 0)) { dlog("Applying load multiple patches patch.", DL_INIT); SafeWrite8(0x444354, 0x90); // Change step from 2 to 1 SafeWrite8(0x44435C, 0xC4); // Disable check dlogr(" Done", DL_INIT); - } + //} } void PlayIdleAnimOnReloadPatch() { @@ -655,7 +657,7 @@ void ApplyNpcExtraApPatch() { RetryCombatMinAP = GetConfigInt("Misc", "NPCsTryToSpendExtraAP", 0); if (RetryCombatMinAP > 0) { dlog("Applying retry combat patch.", DL_INIT); - HookCall(0x422B94, &RetryCombatHook); + HookCall(0x422B94, RetryCombatHook); // combat_turn_ dlogr(" Done", DL_INIT); } } diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index dbe9c9eb..e8a566e2 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -33,7 +33,7 @@ long Objects::uniqueID = UniqueID::Start; // saving to sfallgv.sav // Assigns a new unique identifier to an object if it has not been previously assigned // the identifier is saved with the object in the saved game and this can used in various script long Objects::SetObjectUniqueID(fo::GameObject* obj) { - if (obj->id > UniqueID::Start || obj == fo::var::obj_dude) return obj->id; // dude id = 1800. TODO: perhaps his id needs to be changed to 0x10000000 + if (obj->id > UniqueID::Start || obj == fo::var::obj_dude) return obj->id; // dude id = 18000 if ((DWORD)uniqueID >= UniqueID::End) uniqueID = UniqueID::Start; obj->id = ++uniqueID; diff --git a/sfall/Modules/Scripting/Arrays.cpp b/sfall/Modules/Scripting/Arrays.cpp index 23d47e58..aa5f8785 100644 --- a/sfall/Modules/Scripting/Arrays.cpp +++ b/sfall/Modules/Scripting/Arrays.cpp @@ -243,7 +243,7 @@ bool LoadArrays(HANDLE h) { sArrayVar arrayVar; for (DWORD i = 0; i < count; i++) { if (LoadArrayElement(&arrayVar.key, h)) return true; - if (static_cast(arrayVar.key.type) > 4 || arrayVar.key.intVal == 0) { // partial compatibility with 3.4 + if (arrayVar.key.intVal == 0 || static_cast(arrayVar.key.type) >= 4) { // partial compatibility with 3.4 arrayVar.key.intVal = static_cast(arrayVar.key.type); arrayVar.key.type = DataType::INT; } diff --git a/sfall/Modules/Scripting/Arrays.h b/sfall/Modules/Scripting/Arrays.h index 322e9815..41138b72 100644 --- a/sfall/Modules/Scripting/Arrays.h +++ b/sfall/Modules/Scripting/Arrays.h @@ -145,10 +145,13 @@ typedef ArraysMap::iterator array_itr; typedef std::pair array_pair; // auto-incremented ID -extern DWORD nextarrayid; +extern DWORD nextArrayID; + extern DWORD arraysBehavior; + // temp arrays: set of arrayId extern std::set tempArrays; + // saved arrays: arrayKey => arrayId extern ArrayKeysMap savedArrays; diff --git a/sfall/version.h b/sfall/version.h index d676a1f2..eec1920f 100644 --- a/sfall/version.h +++ b/sfall/version.h @@ -24,10 +24,10 @@ #define VERSION_MAJOR 4 #define VERSION_MINOR 1 -#define VERSION_BUILD 5 +#define VERSION_BUILD 6 #define VERSION_REV 0 -#define VERSION_STRING "4.1.5" +#define VERSION_STRING "4.1.6" -#define CHECK_VAL (4) +//#define CHECK_VAL (4)