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.
This commit is contained in:
NovaRain
2019-02-19 11:55:38 +08:00
parent fe5a148d7f
commit 8613a4fb8b
9 changed files with 122 additions and 83 deletions
+10 -5
View File
@@ -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
+4
View File
@@ -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)
+2
View File
@@ -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, ...);
+75 -52
View File
@@ -37,6 +37,8 @@ static bool femaleCheck = false; // flag for check female dialog file
static DWORD format;
static bool cutsPatch = false;
static std::vector<std::string> patchFiles;
static void CheckPlayerGender() {
isFemale = fo::HeroIsFemale();
@@ -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);
+11 -9
View File
@@ -284,11 +284,11 @@ really_end:
static DWORD RetryCombatLastAP;
static DWORD RetryCombatMinAP;
static void __declspec(naked) RetryCombatHook() {
using namespace fo;
using namespace Fields;
__asm {
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;
@@ -296,12 +296,14 @@ process:
call fo::funcoffs::process_bk_;
jmp process;
next:
mov eax, [esi+0x40];
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);
}
}
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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<long>(arrayVar.key.type) > 4 || arrayVar.key.intVal == 0) { // partial compatibility with 3.4
if (arrayVar.key.intVal == 0 || static_cast<long>(arrayVar.key.type) >= 4) { // partial compatibility with 3.4
arrayVar.key.intVal = static_cast<long>(arrayVar.key.type);
arrayVar.key.type = DataType::INT;
}
+4 -1
View File
@@ -145,10 +145,13 @@ typedef ArraysMap::iterator array_itr;
typedef std::pair<DWORD, sArrayVar> array_pair;
// auto-incremented ID
extern DWORD nextarrayid;
extern DWORD nextArrayID;
extern DWORD arraysBehavior;
// temp arrays: set of arrayId
extern std::set<DWORD> tempArrays;
// saved arrays: arrayKey => arrayId
extern ArrayKeysMap savedArrays;
+3 -3
View File
@@ -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)