Changed the implementation of AutoQuickSave

* now it sets the number of pages used for quick saving.

Added related macros for controlling the save slot with scripts.
This commit is contained in:
NovaRain
2021-08-12 15:07:24 +08:00
parent 34ae106fa5
commit 5ef12b60c5
7 changed files with 179 additions and 102 deletions
+4 -4
View File
@@ -500,12 +500,12 @@ UseScrollingQuestsList=1
ExtraSaveSlots=0
;To use more than one save slot for quick saving (F6 key) without picking a slot beforehand, set the next two lines
;AutoQuickSave sets how many save slots on a page you want to use for quick saving (valid range: 1..10)
;AutoQuickSavePage is the page number to use (valid range: 1..1000) if ExtraSaveSlots is enabled
;The quick saves will be rotated from the first slot on the page to the n-th slot
;AutoQuickSave sets how many pages you want to use for quick saving (valid range: 1..10)
;AutoQuickSavePage is the page number to start at (valid range: 1..1000, if ExtraSaveSlots is enabled)
;The quick saves will be rotated from the first slot on the page to the last slot on the n-th page
;Set to 0 to disable. AutoQuickSave will use the current selected page if AutoQuickSavePage is disabled
AutoQuickSave=0
AutoQuickSavePage=0
AutoQuickSavePage=1
;Set to 1 to speed up the HP/AC counter animations
;Set to 2 to update the HP/AC counters instantly
+16 -7
View File
@@ -283,16 +283,10 @@
if (get_flags(obj1) bwand FLAG_MULTIHEX) then distance--; \
if (get_flags(obj2) bwand FLAG_MULTIHEX) then distance--
/* sfall metarule3 function macros */
// sets the number of days (range 1...127) for the Frank Horrigan encounter, or disable the encounter if days is set to 0
#define set_horrigan_days(day) metarule3(200, day, 0, 0)
// clears the keyboard input buffer, use it in the HOOK_KEYPRESS hook to clear keyboard events before calling functions that are waiting for keyboard input
#define clear_keyboard_buffer metarule3(201, 0, 0, 0)
// checks if the specified PID number exists in the list of registered protos
#define check_pid(pid) (get_proto_data(pid, 0) != -1)
/* sfall_funcX macros */
#define add_extra_msg_file(name) sfall_func1("add_extra_msg_file", name)
#define add_global_timer_event(time, fixedParam) sfall_func2("add_g_timer_event", time, fixedParam)
@@ -407,4 +401,19 @@
#define set_fake_trait_npc(npc, trait, active, image, desc) sfall_func5("set_fake_trait_npc", npc, trait, active, image, desc)
#define set_selectable_perk_npc(npc, perk, active, image, desc) sfall_func5("set_selectable_perk_npc", npc, perk, active, image, desc)
/* sfall metarule3 function macros */
// sets the number of days (range 1...127) for the Frank Horrigan encounter, or disable the encounter if days is set to 0
#define set_horrigan_days(day) metarule3(200, day, 0, 0)
// clears the keyboard input buffer, use it in the HOOK_KEYPRESS hook to clear keyboard events before calling functions that are waiting for keyboard input
#define clear_keyboard_buffer metarule3(201, 0, 0, 0)
// functions to control the save slot
// Note: page and slot here are 0-indexed instead of 1-indexed displayed in game
#define get_current_save_slot metarule3(1000, 0, 0, 0) // returns the amount: page + slot
#define set_current_save_slot(page, slot) metarule3(1001, page, slot, 0)
#define get_current_quick_save_page metarule3(1002, 0, 0, 0)
#define get_current_quick_save_slot metarule3(1003, 0, 0, 0)
#define set_current_quick_save_slot(page, slot, check) metarule3(1004, page, slot, check) // check: 1 - don't check slot when saving
#endif
+109 -68
View File
@@ -26,14 +26,23 @@
namespace sfall
{
DWORD LSPageOffset = 0;
static long LSPageOffset = 0;
static long LSButtDN = 0;
static BYTE* SaveLoadSurface = nullptr;
static const char* filename = "%s\\savegame\\slotdat.ini";
static void SavePageOffsets() {
long ExtraSaveSlots::GetSaveSlot() {
return LSPageOffset + fo::var::slot_cursor;
}
void ExtraSaveSlots::SetSaveSlot(long page, long slot) {
if (GetQuickSavePage() >= 0 && page >= 0 && page <= 9990) LSPageOffset = page - (page % 10);;
if (slot >= 0 && slot < 10) fo::var::slot_cursor = slot;
}
// save last slot position values to file
static long save_page_offsets() {
char SavePath[MAX_PATH], buffer[6];
sprintf_s(SavePath, MAX_PATH, filename, fo::var::patches);
@@ -43,16 +52,8 @@ static void SavePageOffsets() {
_itoa_s(LSPageOffset, buffer, 10);
WritePrivateProfileStringA("POSITION", "PageOffset", buffer, SavePath);
}
static void __declspec(naked) save_page_offsets(void) {
__asm {
// save last slot position values to file
call SavePageOffsets;
// restore original code
mov eax, dword ptr ds:[FO_VAR_lsgwin];
retn;
}
return fo::var::lsgwin; // restore original code
}
static void LoadPageOffsets() {
@@ -74,13 +75,12 @@ static void __declspec(naked) load_page_offsets(void) {
__asm {
// load last slot position values from file
call LoadPageOffsets;
// restore original code
mov edx, 0x50A480; // ASCII "SAV"
mov edx, 0x50A480; // ASCII "SAV" (restore original code)
retn;
}
}
static void CreateButtons() {
static long create_page_buttons() {
DWORD winRef = fo::var::lsgwin;
// left button -10 | X | Y | W | H |HOn |HOff |BDown |BUp |PicUp |PicDown |? |ButType
@@ -93,14 +93,8 @@ static void CreateButtons() {
fo::func::win_register_button(winRef, 248, 60, 24, 20, -1, 0x500, 0x551, 0x151, 0, 0, 0, 32);
// Set Number button
fo::func::win_register_button(winRef, 140, 60, 60, 20, -1, -1, 'p', -1, 0, 0, 0, 32);
}
static void __declspec(naked) create_page_buttons(void) {
__asm {
call CreateButtons;
mov eax, 0x65; // restore original code
retn;
}
return 101; // restore original value
}
static void SetPageNum() {
@@ -215,12 +209,12 @@ static long __fastcall CheckPage(long button) {
break;
case 0x14D: // right button
LSPageOffset += 10;
if (LSPageOffset > 9990) LSPageOffset -= 10000; // to the first page
if (LSPageOffset >= 10000) LSPageOffset -= 10000; // to the first page
__asm call fo::funcoffs::gsound_red_butt_press_;
break;
case 0x151: // fast right PGDN button
LSPageOffset += 100;
if (LSPageOffset > 9990) LSPageOffset -= 10000;
if (LSPageOffset >= 10000) LSPageOffset -= 10000;
__asm call fo::funcoffs::gsound_red_butt_press_;
break;
case 'p': // p/P button pressed - start SetPageNum func
@@ -237,15 +231,17 @@ static long __fastcall CheckPage(long button) {
static void __declspec(naked) check_page_buttons(void) {
__asm {
pushad;
push eax;
push ecx;
mov ecx, eax;
call CheckPage;
test eax, eax;
popad;
jnz CheckUp;
pop ecx;
pop eax;
jnz checkUp;
add dword ptr ds:[esp], 26; // set return to button pressed code
jmp fo::funcoffs::GetSlotList_; // reset page save list func
CheckUp:
checkUp:
// restore original code
cmp eax, 0x148; // up button
retn;
@@ -326,16 +322,16 @@ static void DrawPageText() {
static void __declspec(naked) draw_page_text(void) {
__asm {
pushad;
push eax;
call DrawPageText;
popad;
mov ebp, 0x57; // restore original code
pop eax;
mov ebp, 87; // restore original code
retn;
}
}
// add page num offset when reading and writing various save data files
static void __declspec(naked) AddPageOffset01(void) {
static void __declspec(naked) add_page_offset_hack1(void) {
__asm {
mov eax, dword ptr ds:[FO_VAR_slot_cursor]; // list position 0-9
add eax, LSPageOffset; // add page num offset
@@ -344,7 +340,7 @@ static void __declspec(naked) AddPageOffset01(void) {
}
// getting info for the 10 currently displayed save slots from save.dats
static void __declspec(naked) AddPageOffset02(void) {
static void __declspec(naked) add_page_offset_hack2(void) {
__asm {
push 0x50A514; // ASCII "SAVE.DAT"
lea eax, [ebx + 1];
@@ -355,7 +351,7 @@ static void __declspec(naked) AddPageOffset02(void) {
}
// printing current 10 slot numbers
static void __declspec(naked) AddPageOffset03(void) {
static void __declspec(naked) add_page_offset_hack3(void) {
__asm {
inc eax;
add eax, LSPageOffset; // add page num offset
@@ -366,32 +362,36 @@ static void __declspec(naked) AddPageOffset03(void) {
static void EnableSuperSaving() {
// save/load button setup func
MakeCalls(create_page_buttons, {0x47D80D});
MakeCall(0x47D80D, create_page_buttons); // LSGameStart_
// Draw button text
MakeCalls(draw_page_text, {0x47E6E8});
MakeCall(0x47E6E8, draw_page_text); // ShowSlotList_
// check save/load buttons
MakeCalls(check_page_buttons, {0x47BD49, 0x47CB1C});
MakeCalls(check_page_buttons, {0x47BD49, 0x47CB1C}); // SaveGame_, LoadGame_
// save current page and list positions to file on load/save scrn exit
MakeCalls(save_page_offsets, {0x47D828});
MakeCall(0x47D828, save_page_offsets); // LSGameEnd_
// load saved page and list positions from file
MakeCalls(load_page_offsets, {0x47B82B});
MakeCall(0x47B82B, load_page_offsets); // InitLoadSave_
// Add Load/Save page offset to Load/Save folder number
MakeCalls(AddPageOffset01, {
0x47B929, 0x47D8DB, 0x47D9B0, 0x47DA34, 0x47DABF, 0x47DB58, 0x47DBE9,
0x47DC9C, 0x47EC77, 0x47F5AB, 0x47F694, 0x47F6EB, 0x47F7FB, 0x47F892,
0x47FB86, 0x47FC3A, 0x47FCF2, 0x480117, 0x4801CF, 0x480234, 0x480310,
0x4803F3, 0x48049F, 0x480512, 0x4805F2, 0x480767, 0x4807E6, 0x480839,
0x4808D3
MakeCalls(add_page_offset_hack1, {
0x47B929, // SaveGame_
0x47D8DB, 0x47D9B0, 0x47DA34, 0x47DABF, 0x47DB58, 0x47DBE9, // SaveSlot_
0x47DC9C, // LoadSlot_
0x47EC77, // LoadTumbSlot_
0x47F5AB, 0x47F694, 0x47F6EB, 0x47F7FB, 0x47F892, // GameMap2Slot_
0x47FB86, 0x47FC3A, 0x47FCF2, // SlotMap2Game_
0x480117, 0x4801CF, 0x480234, 0x480310, // SaveBackup_
0x4803F3, 0x48049F, 0x480512, 0x4805F2, // RestoreSave_
0x480767, 0x4807E6, 0x480839, 0x4808D3 // EraseSave_
});
MakeJump(0x47E5E1, AddPageOffset02);
MakeJump(0x47E5E1, add_page_offset_hack2); // GetSlotList_
MakeCalls(AddPageOffset03, {0x47E756});
MakeCall(0x47E756, add_page_offset_hack3); // ShowSlotList_
}
static void GetSaveFileTime(char* filename, FILETIME* ftSlot) {
@@ -408,51 +408,77 @@ static void GetSaveFileTime(char* filename, FILETIME* ftSlot) {
};
}
static const char* commentFmt = "%02d/%02d/%d - %02d:%02d:%02d";
static const char* autoFmt = "AUTO: %02d/%02d/%d - %02d:%02d:%02d";
static const char* quickFmt = "QUICK: %02d/%02d/%d - %02d:%02d:%02d";
static void CreateSaveComment(char* bufstr) {
static void CreateSaveComment(char* bufstr, bool isAuto) {
SYSTEMTIME stUTC, stLocal;
GetSystemTime(&stUTC);
SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
char buf[30];
sprintf_s(buf, commentFmt, stLocal.wDay, stLocal.wMonth, stLocal.wYear, stLocal.wHour, stLocal.wMinute, stLocal.wSecond);
strcpy(bufstr, buf);
const char* fmt = (isAuto) ? autoFmt : quickFmt;
sprintf_s(bufstr, 30, fmt, stLocal.wDay, stLocal.wMonth, stLocal.wYear, stLocal.wHour, stLocal.wMinute, stLocal.wSecond);
}
static long autoQuickSave = 0;
static long quickSavePage = 0;
static long quickSavePageInit = -1;
static long quickSavePageCount;
static long currentPageCount;
static long quickSavePage = -1;
static long quickSaveSlot = 0;
static long dontCheckSlot = 0;
static bool qFirst = true;
static FILETIME ftPrevSlot;
static DWORD __stdcall QuickSaveGame(fo::DbFile* file, char* filename) {
long currSlot = fo::var::slot_cursor;
long currSlot = quickSaveSlot;
if (dontCheckSlot) {
if (file) fo::func::db_fclose(file);
} else {
// for quick feature
if (file) { // This slot is not empty
fo::func::db_fclose(file);
FILETIME ftCurrSlot;
GetSaveFileTime(filename, &ftCurrSlot);
if (currSlot == 0 || ftCurrSlot.dwHighDateTime > ftPrevSlot.dwHighDateTime ||
if (currSlot == 0 ||
ftCurrSlot.dwHighDateTime > ftPrevSlot.dwHighDateTime ||
(ftCurrSlot.dwHighDateTime == ftPrevSlot.dwHighDateTime && ftCurrSlot.dwLowDateTime > ftPrevSlot.dwLowDateTime))
{
ftPrevSlot.dwHighDateTime = ftCurrSlot.dwHighDateTime;
ftPrevSlot.dwLowDateTime = ftCurrSlot.dwLowDateTime;
if (++currSlot > autoQuickSave) {
currSlot = 0;
} else {
fo::var::slot_cursor = currSlot;
if (!qFirst && currSlot < 9) {
fo::var::slot_cursor = ++quickSaveSlot;
return 0x47B929; // check next slot
}
currSlot = 0; // set if currSlot >= 9
qFirst = false;
}
}
// next save slot
if (++quickSaveSlot >= 10) {
quickSaveSlot = 0;
// next page
if (quickSavePageCount > 1 && quickSavePageInit != -1) {
if (++currentPageCount >= quickSavePageCount) {
currentPageCount = 0;
quickSavePage = quickSavePageInit;
} else if (quickSavePage <= 9980) {
quickSavePage += 10;
}
}
}
}
// Save to slot
fo::var::slot_cursor = currSlot;
fo::LSData* saveData = (fo::LSData*)FO_VAR_LSData;
CreateSaveComment(saveData[currSlot].comment);
CreateSaveComment(saveData[currSlot].comment, dontCheckSlot != 0);
fo::var::quick_done = 1;
return 0x47B9A4; // normal return
@@ -472,13 +498,28 @@ static void __declspec(naked) SaveGame_hack0() {
static void __declspec(naked) SaveGame_hack1() {
__asm {
mov ds:[FO_VAR_slot_cursor], 0;
mov eax, quickSaveSlot;
mov ds:[FO_VAR_slot_cursor], eax;
mov eax, quickSavePage;
mov LSPageOffset, eax;
retn;
}
}
long ExtraSaveSlots::GetQuickSavePage() {
return quickSavePage;
}
long ExtraSaveSlots::GetQuickSaveSlot() {
return quickSaveSlot;
}
void ExtraSaveSlots::SetQuickSaveSlot(long page, long slot, long check) {
if (quickSavePage >= 0 && page >= 0 && page <= 9990) quickSavePage = page - (page % 10);
if (slot >= 0 && slot < 10) quickSaveSlot = slot;
dontCheckSlot = check;
}
void ExtraSaveSlots::init() {
bool extraSaveSlots = (IniReader::GetConfigInt("Misc", "ExtraSaveSlots", 0) != 0);
if (extraSaveSlots) {
@@ -487,19 +528,19 @@ void ExtraSaveSlots::init() {
dlogr(" Done", DL_INIT);
}
autoQuickSave = IniReader::GetConfigInt("Misc", "AutoQuickSave", 0);
if (autoQuickSave > 0) {
quickSavePageCount = IniReader::GetConfigInt("Misc", "AutoQuickSave", 0);
if (quickSavePageCount > 0) {
dlog("Applying auto quick save patch.", DL_INIT);
if (autoQuickSave > 10) autoQuickSave = 10;
autoQuickSave--; // reserved slot count
if (quickSavePageCount > 10) quickSavePageCount = 10;
quickSavePage = IniReader::GetConfigInt("Misc", "AutoQuickSavePage", 0);
quickSavePage = IniReader::GetConfigInt("Misc", "AutoQuickSavePage", 1);
if (quickSavePage > 1000) quickSavePage = 1000;
if (extraSaveSlots && quickSavePage > 0) {
quickSavePage = (quickSavePage - 1) * 10;
quickSavePageInit = quickSavePage;
MakeCall(0x47B923, SaveGame_hack1, 1);
} else {
} else { // for quickSavePage == 0
SafeWrite8(0x47B923, 0x89);
SafeWrite32(0x47B924, 0x5193B83D); // mov [slot_cursor], edi = 0
}
+7 -2
View File
@@ -23,13 +23,18 @@
namespace sfall
{
extern DWORD LSPageOffset;
class ExtraSaveSlots : public Module {
public:
const char* name() { return "SuperSave"; }
void init();
void exit() override;
static long GetSaveSlot();
static void SetSaveSlot(long page, long slot);
static long GetQuickSavePage();
static long GetQuickSaveSlot();
static void SetQuickSaveSlot(long page, long slot, long check);
};
}
+1 -2
View File
@@ -626,8 +626,7 @@ void Inject_InventoryMoveHook() {
Inject_SwitchHandHook();
MakeJump(0x4713A9, UseArmorHack); // old 0x4713A3
MakeJump(0x476491, DropIntoContainerHack);
MakeJump(0x471338, DropIntoContainerHandSlotHack);
MakeJump(0x4712AB, DropIntoContainerHandSlotHack);
MakeJumps(DropIntoContainerHandSlotHack, { 0x471338, 0x4712AB });
HookCall(0x471200, MoveInventoryHook);
HookCall(0x476549, DropAmmoIntoWeaponHook); // old 0x476588
HookCalls(InvenActionCursorObjDropHook, {
+1 -1
View File
@@ -123,7 +123,7 @@ void __stdcall SetInLoop(DWORD mode, LoopFlag flag) {
}
void GetSavePath(char* buf, char* ftype) {
sprintf(buf, "%s\\savegame\\slot%.2d\\sfall%s.sav", fo::var::patches, fo::var::slot_cursor + 1 + LSPageOffset, ftype); //add SuperSave Page offset
sprintf(buf, "%s\\savegame\\slot%.2d\\sfall%s.sav", fo::var::patches, ExtraSaveSlots::GetSaveSlot() + 1, ftype); //add SuperSave Page offset
}
static void __stdcall SaveGame2() {
+25 -2
View File
@@ -18,6 +18,7 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "ExtraSaveSlots.h"
#include "LoadGameHook.h"
#include "PartyControl.h"
@@ -36,7 +37,14 @@ static bool HorriganEncounterDisabled = false;
enum class MetaruleFunction : long {
SET_HORRIGAN_ENCOUNTER = 200, // sets the number of days for the Frank Horrigan encounter or disable encounter
CLEAR_KEYBOARD_BUFFER = 201, // clears the keyboard input buffer, should be used in the HOOK_KEYPRESS hook to clear keyboard events in some cases
EXT_PARTY_ORDER_ATTACK = 999
PARTY_ORDER_ATTACK = 999,
// save slot controls
GET_CURRENT_SAVE_SLOT = 1000,
SET_CURRENT_SAVE_SLOT = 1001,
GET_CURRENT_QSAVE_PAGE = 1002,
GET_CURRENT_QSAVE_SLOT = 1003,
SET_CURRENT_QSAVE_SLOT = 1004,
};
/*
@@ -63,9 +71,24 @@ static long __fastcall op_metarule3_ext(long metafunc, long* args) {
case MetaruleFunction::CLEAR_KEYBOARD_BUFFER:
__asm call fo::funcoffs::kb_clear_;
break;
case MetaruleFunction::EXT_PARTY_ORDER_ATTACK:
case MetaruleFunction::PARTY_ORDER_ATTACK:
PartyControl::OrderAttackPatch();
break;
case MetaruleFunction::GET_CURRENT_SAVE_SLOT:
result = ExtraSaveSlots::GetSaveSlot();
break;
case MetaruleFunction::SET_CURRENT_SAVE_SLOT:
ExtraSaveSlots::SetSaveSlot(args[0], args[1]);
break;
case MetaruleFunction::GET_CURRENT_QSAVE_PAGE:
result = ExtraSaveSlots::GetQuickSavePage();
break;
case MetaruleFunction::GET_CURRENT_QSAVE_SLOT:
result = ExtraSaveSlots::GetQuickSaveSlot();
break;
case MetaruleFunction::SET_CURRENT_QSAVE_SLOT:
ExtraSaveSlots::SetQuickSaveSlot(args[0], args[1], args[2]);
break;
default:
fo::func::debug_printf("\nOPCODE ERROR: metarule3(%d, ...) - metarule function number does not exist.\n > Script: %s, procedure %s.",
metafunc, fo::var::currentProgram->fileName, fo::func::findCurrentProc(fo::var::currentProgram));