From 9a7a795b8235c688883607c84e134622ffe867ef Mon Sep 17 00:00:00 2001 From: phobos2077 Date: Thu, 9 May 2024 19:48:48 +0200 Subject: [PATCH] ActionPointsBar: language agnostic FRM - Use one small FRM with just AP bar background instead of having 2 big FRMs per language (for HRP and not) - It works by simply writing into iface window buffer right after it was written with iface.frm contents - Uses position relative to right edge of the iface panel --- artifacts/ddraw.ini | 2 +- .../dat_en/art/intrface/iface_apbar_e.frm | Bin 0 -> 2466 bytes sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/FalloutEngine/Variables_def.h | 1 + sfall/HRP/InterfaceBar.cpp | 9 +-- sfall/HRP/InterfaceBar.h | 2 - sfall/Modules/ExtraArt.cpp | 66 +++++++++++++----- sfall/Modules/ExtraArt.h | 17 +++++ sfall/Modules/Interface.cpp | 40 +++++------ 9 files changed, 91 insertions(+), 47 deletions(-) create mode 100644 artifacts/sfall_dat/dat_en/art/intrface/iface_apbar_e.frm diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 0620d724..e667ae17 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -110,7 +110,7 @@ FadeMultiplier=100 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Interface] ;Set to 1 to expand the number of action points displayed on the interface bar -;Requires new IFACE_E.frm and HR_IFACE_E.frm files in art\intrface\ (included in sfall.dat) to display correctly +;Requires new iface_apbar_e.frm in art\intrface\ (included in sfall.dat) to display correctly ;The minimum supported version of the hi-res patch is v4.1.8 ActionPointsBar=0 diff --git a/artifacts/sfall_dat/dat_en/art/intrface/iface_apbar_e.frm b/artifacts/sfall_dat/dat_en/art/intrface/iface_apbar_e.frm new file mode 100644 index 0000000000000000000000000000000000000000..43da42dec4faeb6120757ff340454151d41c4a39 GIT binary patch literal 2466 zcmZQzU|?YY0Y(r(8sUL<9E*Q98B!oIbIn13`oik zpA<}!pWhi2;0HDtWG+m_nKNfVf?y0Lz=rdJtoQTd1pz-lUWj6dLccS9e&=|3dC&Qs z<2`qQ_uRR2e$IYQr>l81=nAa?M% zVBcyyQog58AFsX;931THd;B;^Cj?jf`bvWgIOtpBd)gOd{^`@zL8q%vpY}ao4dPc< zS682|K3)0X0Z0;y)Pu?@AD^nzRaGGQs+#KR>T`a6hy2dC%sMm6`OxyyW##4K=gu8k zv10a$6}7YcRyfx>uUO#+VmSNFnssKyio?s79DgO$Q0*8&a?bxIjyLj<#cG)p;;isPP3d2Eps}&+zBLB>$Dt1VV9~sbO_|UigV{= zoR-%vpLOPpAFtnG=UHc*&z&o05n^UJ8+>SmytCX2x%1~|%R3#aUEy-xWyLw?TBpP3 zo?2k z@cG%ZooWv`pLaQbzSgOBd9A$s`PnOKYh`gu)zvY~FB`In43cp$M z=aZ5WlV>~Ok(zbr4Agnjva@C#nze$DmzURXw(}vs*|NUJc}`v65I^qce0cf!l;orn z3CZ%#v(Bd^I-f{daprtVa$-+;)wJ9mdASwA3DIhgT@+%IVPf0%Em^Aw^KB-wC=kbG`cWBnELo3es@$&k~ z`OR7(FL#EUKjl34#dBxQtXTdyB_+`@$t8J(JjhVLBo`NuyB!mgX3sj0OUh-%d6$%A zrxQu%ooAm8MoB(S%>8Kx%tn7oj2>y@>z$@@Lu5clUp&%Z?-%?*M*ex+}wV0&MRi02Zd@< zV)CKcE-5KVNr@nLrz9sPCeNOof=kLp-UZ~!F)3vQSPEqHit{Nc*rk>qnuT!QET`p%m!IMFyWltb(27~|=jAVO z@Lu5IJta5$jElTWN=izy%lX+Z=Rr0np9i@=B_&1vF>a~zV9%u_C!cqbPjLY`A!YXL z^B{v#&V#+1l9GIW1z03G`8?R&DajxykecLVsA8;Ahig}uoAYo&oVWb&p;?FcAMx_~ zom$~HYxemR{tH~Z{HM;#JFfs0BInPaKfg?V_G}P+{`_qD6p(YyFQ4rKDh9Agt&n$d zIq#Apf8JT%1r+5jDd!K(o-OZk{=Cb1CwZ_r$>*JC%YzJbIll~Q&}?~d*qlFqz7`bn zAjMduX014%ath+SQ?m{&KQxOUR7D+jp5+J1dFRiYo2SUjIiHy=?*a;!+7*5fce_B{ zbbf_1ZmAV6@-Ffz=g+UGT`?Qvh4ZtWoo3GlyR-I?GsxrTXV3C;0VS35E+B>rNbxME z+48ejoOf}ltz7|=TCoCMs&>|y^C?j0%{l~fEy&?=e#=)JJ|vd{0_O6d`o+aXezwc( z<%eg<&jx`*%RnZ}yUbc~_%LoMXZhJKF0)9CkWX3(`G%_VQVWTxQG9 zo_%=Mp;<1oU1rZdG;0|X!)T- zhl7Jb4Gt$iXFtC~hvnquPsyD*frameData[0].width == IFaceBar::IFACE_BAR_WIDTH) { @@ -214,7 +213,7 @@ static long __cdecl InterfaceArt(BYTE* scr, long w, long h, long srcWidth, BYTE* } // no required file, use the default one provided by HRP - if (frm == nullptr) frm = sf::LoadUnlistedFrmCached(((expandAPBar) ? "HR_IFACE_800E.frm" : "HR_IFACE_800.frm"), fo::ArtType::OBJ_TYPE_INTRFACE); + if (frm == nullptr) frm = sf::LoadUnlistedFrmCached("HR_IFACE_800.frm", fo::ArtType::OBJ_TYPE_INTRFACE); if (frm != nullptr) { // scale the 800px wide interface to the width of IFACE_BAR_WIDTH @@ -466,10 +465,6 @@ static void __declspec(naked) intface_update_ammo_lights_hook() { //////////////////////////////////////////////////////////////////////////////// -void IFaceBar::SetExpandAPBar() { - expandAPBar = true; -} - void IFaceBar::Hide() { InterfaceHide(fo::var::getInt(FO_VAR_interfaceWindow)); } diff --git a/sfall/HRP/InterfaceBar.h b/sfall/HRP/InterfaceBar.h index 095231b8..591f8459 100644 --- a/sfall/HRP/InterfaceBar.h +++ b/sfall/HRP/InterfaceBar.h @@ -25,8 +25,6 @@ public: static long display_width; static char* display_string_buf; - static void SetExpandAPBar(); - static void Hide(); static void Show(); }; diff --git a/sfall/Modules/ExtraArt.cpp b/sfall/Modules/ExtraArt.cpp index 20024b88..4ad22d1f 100644 --- a/sfall/Modules/ExtraArt.cpp +++ b/sfall/Modules/ExtraArt.cpp @@ -41,15 +41,25 @@ static PcxFile LoadPcxFile(const char* file) { return pcx; } +static fo::FrmFile* LoadFrmFile(const char* file) { + fo::FrmFile* frmPtr = nullptr; + if (fo::func::load_frame(file, &frmPtr)) { + frmPtr = nullptr; + } + return frmPtr; +} + +void UnloadFrmFile(fo::FrmFile* frm) { + fo::func::mem_free(frm); +} + fo::FrmFile* LoadFrmFileCached(const char* file) { fo::FrmFile* frmPtr = nullptr; auto cacheHit = frmFileCache.find(file); if (cacheHit != frmFileCache.end()) { frmPtr = cacheHit->second; } else { - if (fo::func::load_frame(file, &frmPtr)) { - frmPtr = nullptr; - } + frmPtr = LoadFrmFile(file); frmFileCache.emplace(file, frmPtr); } return frmPtr; @@ -64,7 +74,6 @@ PcxFile LoadPcxFileCached(const char* file) { } static void GetUnlistedFrmPath(const char* frmName, unsigned int folderRef, bool useLanguage, char* pathBuf) { - const char* artfolder = fo::var::art[folderRef].path; // address of art type name if (useLanguage) { sprintf_s(pathBuf, MAX_PATH, "art\\%s\\%s\\%s", (const char*)fo::var::language, artfolder, frmName); @@ -73,10 +82,9 @@ static void GetUnlistedFrmPath(const char* frmName, unsigned int folderRef, bool } } -bool UnlistedFrmExists(const char* frmName, unsigned int folderRef) { +static bool CheckUnlistedFrm(const char* frmName, unsigned int folderRef, char* frmPath) { if (folderRef > fo::OBJ_TYPE_SKILLDEX) return nullptr; - char frmPath[MAX_PATH]; GetUnlistedFrmPath(frmName, folderRef, fo::var::use_language != 0, frmPath); bool exists = fo::func::db_access(frmPath); @@ -87,19 +95,21 @@ bool UnlistedFrmExists(const char* frmName, unsigned int folderRef) { return exists; } -fo::FrmFile* LoadUnlistedFrmCached(const char* frmName, unsigned int folderRef) { - if (folderRef > fo::OBJ_TYPE_SKILLDEX) return nullptr; - +bool UnlistedFrmExists(const char* frmName, unsigned int folderRef) { char frmPath[MAX_PATH]; + return CheckUnlistedFrm(frmName, folderRef, frmPath); +} - GetUnlistedFrmPath(frmName, folderRef, fo::var::use_language != 0, frmPath); +fo::FrmFile* LoadUnlistedFrm(const char* frmName, unsigned int folderRef) { + char frmPath[MAX_PATH]; + if (!CheckUnlistedFrm(frmName, folderRef, frmPath)) return nullptr; + return LoadFrmFile(frmPath); +} - fo::FrmFile* frm = LoadFrmFileCached(frmPath); - if (frm == nullptr && fo::var::use_language) { - GetUnlistedFrmPath(frmName, folderRef, false, frmPath); - frm = LoadFrmFileCached(frmPath); - } - return frm; +fo::FrmFile* LoadUnlistedFrmCached(const char* frmName, unsigned int folderRef) { + char frmPath[MAX_PATH]; + if (!CheckUnlistedFrm(frmName, folderRef, frmPath)) return nullptr; + return LoadFrmFileCached(frmPath); } static void ClearInterfaceArtCache() { @@ -109,11 +119,33 @@ static void ClearInterfaceArtCache() { pcxFileCache.clear(); for (auto &pair : frmFileCache) { - fo::func::mem_free(pair.second); + UnloadFrmFile(pair.second); } frmFileCache.clear(); } +TempFrmHandle::TempFrmHandle(fo::FrmFile* frm) : _frm(frm) { +} + +TempFrmHandle::TempFrmHandle(TempFrmHandle&& other) : _frm(other._frm) { + other._frm = nullptr; +} + +TempFrmHandle::~TempFrmHandle() { + if (_frm == nullptr) return; + UnloadFrmFile(_frm); + _frm = nullptr; +} + +bool TempFrmHandle::IsValid() { + return _frm != nullptr; +} + +const fo::FrmFile& TempFrmHandle::Frm() const { + assert(_frm != nullptr); + return *_frm; +} + void ExtraArt::init() { LoadGameHook::OnGameReset() += []() { ClearInterfaceArtCache(); diff --git a/sfall/Modules/ExtraArt.h b/sfall/Modules/ExtraArt.h index 6f6a68c7..1f81b23c 100644 --- a/sfall/Modules/ExtraArt.h +++ b/sfall/Modules/ExtraArt.h @@ -36,16 +36,33 @@ struct PcxFile { PcxFile() : pixelData(nullptr), width(0), height(0) {} }; +class TempFrmHandle { +public: + TempFrmHandle(fo::FrmFile* frm); + TempFrmHandle(const TempFrmHandle&) = delete; + TempFrmHandle(TempFrmHandle&&); + TempFrmHandle& operator=(TempFrmHandle) = delete; + ~TempFrmHandle(); + + bool IsValid(); + const fo::FrmFile& Frm() const; +private: + fo::FrmFile* _frm; +}; + class ExtraArt : public Module { public: const char* name() { return "ExtraArt"; } void init(); }; +// TODO: more robust caching, similar to how art_ptr_lock works. fo::FrmFile* LoadFrmFileCached(const char* file); PcxFile LoadPcxFileCached(const char* file); bool UnlistedFrmExists(const char* frmName, unsigned int folderRef); +fo::FrmFile* LoadUnlistedFrm(const char* frmName, unsigned int folderRef); fo::FrmFile* LoadUnlistedFrmCached(const char* file, unsigned int folderRef); +void UnloadFrmFile(fo::FrmFile* frm); } diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index 551bb60a..51e95f4e 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -126,25 +126,29 @@ struct InterfaceCustomFrm { }; static BYTE movePointBackground[16 * 9 * 5]; -static InterfaceCustomFrm ifaceFrm{ "IFACE_E.frm" }; -static void* LoadIfaceFrm() { - return ifaceFrm.LoadFrmData(); +static void DrawExtendedApBar() { + const char* const ifaceApBarFrm = "iface_apbar_e.frm"; // 183x13 at 266,10 (x = width - 374) + TempFrmHandle frmHandle{ LoadUnlistedFrm(ifaceApBarFrm, fo::ArtType::OBJ_TYPE_INTRFACE) }; + if (!frmHandle.IsValid()) return; + + DWORD ifaceWin = fo::var::interfaceWindow; + fo::Window* win = fo::func::GNW_find(ifaceWin); + if (win == nullptr) return; + + constexpr int destOffsetRight = 374; + constexpr int destOffsetTop = 10; + const fo::FrmFrameData& srcFrame = frmHandle.Frm().frameData[0]; + BYTE* dest = fo::var::interfaceBuffer + (win->width * (destOffsetTop + 1)) - destOffsetRight; + fo::func::buf_to_buf((BYTE*)srcFrame.data, srcFrame.width, srcFrame.height, srcFrame.width, dest, win->width); } -static void __declspec(naked) intface_init_hook_lock() { +static void __declspec(naked) intface_init_hook_unlock_iface_frm() { __asm { pushadc; - call LoadIfaceFrm; - test eax, eax; - jz skip; - pop ecx; - add esp, 8; - mov dword ptr [ecx], 0; - retn; -skip: + call DrawExtendedApBar; popadc; - jmp fo::funcoffs::art_ptr_lock_data_; + jmp fo::funcoffs::art_ptr_unlock_; } } @@ -175,14 +179,10 @@ static void APBarRectPatch() { } static void ActionPointsBarPatch() { - HRP::IFaceBar::SetExpandAPBar(); - dlog("Applying expanded action points bar patch.", DL_INIT); if (HRP::Setting::ExternalEnabled()) { // check valid data - if (HRP::Setting::VersionIsValid && !_stricmp((const char*)HRP::Setting::GetAddress(HRP_VAR_HR_IFACE_FRM_STR), "HR_IFACE_%i.frm")) { - SafeWriteStr(HRP::Setting::GetAddress(HRP_VAR_HR_IFACE_FRM_STR + 11), "E.frm"); // patching HRP - } else { + if (!HRP::Setting::VersionIsValid) { dlogr(" Incorrect HRP version!", DL_INIT); return; } @@ -190,13 +190,14 @@ static void ActionPointsBarPatch() { } else { APBarRectPatch(); } + // intface_init_ SafeWriteBatch((DWORD)&movePointBackground, {0x45E343, 0x45EE3F}); // intface_update_move_points_ SafeWriteBatch(16, {0x45EE55, 0x45EE7B, 0x45EE82, 0x45EE9C, 0x45EEA0}); SafeWriteBatch(9276 - (54 / 2), {0x45EE33, 0x45EEC8, 0x45EF16}); - HookCall(0x45D918, intface_init_hook_lock); + HookCall(0x45D962, intface_init_hook_unlock_iface_frm); MakeCall(0x45E356, intface_init_hack); MakeJump(0x45EE38, intface_update_move_points_hack, 1); dlogr(" Done", DL_INIT); @@ -1412,7 +1413,6 @@ void Interface::init() { ExpandedInventoryPatch(); }; LoadGameHook::OnGameReset() += []() { - ifaceFrm.Reset(); barterTallFrm.Reset(); tradeTallFrm.Reset(); for (size_t i = 0; i < inventoryTallFrms.size(); ++i) {