Replace all usage of LoadUnlisted with fo::FrmFile

- Move cached FRM/PCX loading to new ExtraArt module
- Use the same FRM cache for all unlisted FRM needs
- Minor refactoring
This commit is contained in:
phobos2077
2024-05-08 20:44:33 +02:00
parent 344a64f9d2
commit bf3f5d2930
15 changed files with 276 additions and 335 deletions
+1 -101
View File
@@ -409,7 +409,7 @@ fo::GameObject* __fastcall MultiHexMoveIsBlocking(fo::GameObject* source, long d
// Returns the terrain type of the sub-tile at the specified coordinates on the world map
long wmGetTerrainType(long xPos, long yPos) {
long* terrainId;
long* terrainId{};
__asm {
lea ebx, terrainId;
mov edx, yPos;
@@ -667,105 +667,5 @@ void RefreshGNW(bool skipOwner) {
fo::var::setInt(FO_VAR_doing_refresh_all) = 0;
}
//////////////////////////// UNLISTED FRM FUNCTIONS ////////////////////////////
static bool LoadFrmHeader(fo::UnlistedFrm *frmHeader, fo::DbFile* frmStream) {
if (fo::func::db_freadInt(frmStream, &frmHeader->version) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frmHeader->FPS) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frmHeader->actionFrame) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frmHeader->numFrames) == -1)
return false;
else if (fo::func::db_freadShortCount(frmStream, frmHeader->xCentreShift, 6) == -1)
return false;
else if (fo::func::db_freadShortCount(frmStream, frmHeader->yCentreShift, 6) == -1)
return false;
else if (fo::func::db_freadIntCount(frmStream, frmHeader->oriOffset, 6) == -1)
return false;
else if (fo::func::db_freadInt(frmStream, &frmHeader->frameAreaSize) == -1)
return false;
return true;
}
static bool LoadFrmFrame(fo::UnlistedFrm::Frame *frame, fo::DbFile* frmStream) {
//FRMframe *frameHeader = (FRMframe*)frameMEM;
//BYTE* frameBuff = frame + sizeof(FRMframe);
if (fo::func::db_freadShort(frmStream, &frame->width) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frame->height) == -1)
return false;
else if (fo::func::db_freadInt(frmStream, &frame->size) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frame->x) == -1)
return false;
else if (fo::func::db_freadShort(frmStream, &frame->y) == -1)
return false;
frame->indexBuff = new BYTE[frame->size];
if (fo::func::db_fread(frame->indexBuff, 1, frame->size, frmStream) != frame->size)
return false;
return true;
}
fo::UnlistedFrm *LoadUnlistedFrm(const char *frmName, unsigned int folderRef) {
if (folderRef > fo::OBJ_TYPE_SKILLDEX) return nullptr;
const char *artfolder = fo::var::art[folderRef].path; // address of art type name
char frmPath[MAX_PATH];
if (fo::var::use_language) {
sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s\\%s", (const char*)fo::var::language, artfolder, frmName);
} else {
sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName);
}
fo::UnlistedFrm *frm = new fo::UnlistedFrm;
auto frmStream = fo::func::db_fopen(frmPath, "rb");
if (!frmStream && fo::var::use_language) {
sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName);
frmStream = fo::func::db_fopen(frmPath, "rb");
}
if (frmStream != nullptr) {
if (!LoadFrmHeader(frm, frmStream)) {
fo::func::db_fclose(frmStream);
delete frm;
return nullptr;
}
DWORD oriOffset_1st = frm->oriOffset[0];
DWORD oriOffset_new = 0;
frm->frames = new fo::UnlistedFrm::Frame[6 * frm->numFrames];
for (int ori = 0; ori < 6; ori++) {
if (ori == 0 || frm->oriOffset[ori] != oriOffset_1st) {
frm->oriOffset[ori] = oriOffset_new;
for (int fNum = 0; fNum < frm->numFrames; fNum++) {
if (!LoadFrmFrame(&frm->frames[oriOffset_new + fNum], frmStream)) {
fo::func::db_fclose(frmStream);
delete frm;
return nullptr;
}
}
oriOffset_new += frm->numFrames;
} else {
frm->oriOffset[ori] = 0;
}
}
fo::func::db_fclose(frmStream);
} else {
delete frm;
return nullptr;
}
return frm;
}
}
}
+14 -2
View File
@@ -33,6 +33,20 @@ namespace fo
namespace util
{
// To safely unlock cache entries after using art_ptr_lock and similar functions
struct ArtCacheLock {
DWORD entryPtr = 0;
ArtCacheLock() { }
ArtCacheLock(DWORD _lock) : entryPtr(_lock) { }
~ArtCacheLock() {
if (entryPtr != 0) {
fo::func::art_ptr_unlock(entryPtr);
entryPtr = 0;
}
}
};
__inline void DisplayPrint(const std::string& str) {
fo::func::display_print(str.c_str());
}
@@ -191,7 +205,5 @@ void RedrawObject(fo::GameObject* obj);
// Redraws all windows
void RefreshGNW(bool skipOwner = false);
fo::UnlistedFrm *LoadUnlistedFrm(const char *frmName, unsigned int folderRef);
}
}
+15 -67
View File
@@ -29,8 +29,8 @@ namespace sfall
struct Rectangle {
long x, y, width, height;
long right() { return x + (width - 1); }
long bottom() { return y + (height - 1); }
long right() const { return x + (width - 1); }
long bottom() const { return y + (height - 1); }
};
}
@@ -208,25 +208,25 @@ struct GameObject {
long rads;
long poison;
inline bool IsDead() {
inline bool IsDead() const {
return ((damageFlags & DamageFlag::DAM_DEAD) != 0);
}
inline bool IsNotDead() {
inline bool IsNotDead() const {
return ((damageFlags & DamageFlag::DAM_DEAD) == 0);
}
inline bool IsActive() {
inline bool IsActive() const {
return ((damageFlags & (DamageFlag::DAM_KNOCKED_OUT | DamageFlag::DAM_LOSE_TURN)) == 0);
}
inline bool IsNotActive() {
inline bool IsNotActive() const {
return ((damageFlags & (DamageFlag::DAM_KNOCKED_OUT | DamageFlag::DAM_LOSE_TURN)) != 0);
}
inline bool IsActiveNotDead() {
inline bool IsActiveNotDead() const {
return ((damageFlags & (DamageFlag::DAM_DEAD | DamageFlag::DAM_KNOCKED_OUT | DamageFlag::DAM_LOSE_TURN)) == 0);
}
inline bool IsNotActiveOrDead() {
inline bool IsNotActiveOrDead() const {
return ((damageFlags & (DamageFlag::DAM_DEAD | DamageFlag::DAM_KNOCKED_OUT | DamageFlag::DAM_LOSE_TURN)) != 0);
}
inline bool IsFleeing() {
inline bool IsFleeing() const {
return ((combatState & CombatStateFlag::InFlee) != 0);
}
@@ -255,23 +255,23 @@ struct GameObject {
GameObject* owner; // not saved
long scriptIndex;
inline char Type() {
inline char Type() const {
return (protoId >> 24);
}
inline char TypeFid() {
inline char TypeFid() const {
return ((artFid >> 24) & 0x0F);
}
inline bool IsCritter() {
inline bool IsCritter() const {
return (Type() == fo::ObjType::OBJ_TYPE_CRITTER);
}
inline bool IsNotCritter() {
inline bool IsNotCritter() const {
return (Type() != fo::ObjType::OBJ_TYPE_CRITTER);
}
inline bool IsItem() {
inline bool IsItem() const {
return (Type() == fo::ObjType::OBJ_TYPE_ITEM);
}
inline bool IsNotItem() {
inline bool IsNotItem() const {
return (Type() != fo::ObjType::OBJ_TYPE_ITEM);
}
};
@@ -498,58 +498,6 @@ struct TileFrmFile : public FrmHeaderData {
static_assert(sizeof(TileFrmFile) == 2954, "Incorrect TileFrmFile definition.");
// structures for loading unlisted frms
struct UnlistedFrm {
DWORD version;
WORD FPS;
WORD actionFrame;
WORD numFrames;
WORD xCentreShift[6];
WORD yCentreShift[6];
DWORD oriOffset[6];
DWORD frameAreaSize;
struct Frame {
WORD width;
WORD height;
DWORD size;
WORD x;
WORD y;
BYTE *indexBuff;
Frame() {
width = 0;
height = 0;
size = 0;
x = 0;
y = 0;
indexBuff = nullptr;
}
~Frame() {
if (indexBuff != nullptr)
delete[] indexBuff;
}
} *frames;
UnlistedFrm() {
version = 0;
FPS = 0;
actionFrame = 0;
numFrames = 0;
for (int i = 0; i < 6; i++) {
xCentreShift[i] = 0;
yCentreShift[i] = 0;
oriOffset[i] = 0;
}
frameAreaSize = 0;
frames = nullptr;
}
~UnlistedFrm() {
if (frames != nullptr) delete[] frames;
}
};
//for holding a message
struct MessageNode {
long number;
+1
View File
@@ -18,6 +18,7 @@ VAR_(BlueColor, BYTE)
VAR_(bottom_line, DWORD)
VAR_(btable, DWORD)
VAR_(btncnt, DWORD)
VAR_(buf_length_2, long)
VARD(cap, fo::AIcap) // dynamic array
VAR_(carCurrentArea, DWORD)
VAR_(carGasAmount, long) // from 0 to 80000
+5 -8
View File
@@ -6,6 +6,7 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "..\Modules\ExtraArt.h"
#include "..\Modules\LoadGameHook.h"
#include "Init.h"
@@ -22,7 +23,7 @@ namespace sf = sfall;
static const long width = 640; // art
static long scr_width = 639;
static fo::UnlistedFrm* altDialogArt;
static fo::FrmFile* altDialogArt;
bool Dialog::DIALOG_SCRN_ART_FIX = true;
bool Dialog::DIALOG_SCRN_BACKGROUND = false;
@@ -172,20 +173,16 @@ static bool loadAltDialogArt = false;
static void __cdecl talk_to_refresh_background_window_hook_buf_to_buf(BYTE* src, long w, long h, long srcWidth, BYTE* dst, long dstWidth) {
if (!loadAltDialogArt) {
loadAltDialogArt = true;
altDialogArt = fo::util::LoadUnlistedFrm("HR_ALLTLK.frm", fo::ArtType::OBJ_TYPE_INTRFACE);
altDialogArt = sf::LoadUnlistedFrmCached("HR_ALLTLK.frm", fo::ArtType::OBJ_TYPE_INTRFACE);
}
if (altDialogArt) {
src = altDialogArt->frames->indexBuff;
srcWidth = altDialogArt->frames->width;
src = altDialogArt->frameData[0].data;
srcWidth = altDialogArt->frameData[0].width;
}
fo::func::buf_to_buf(src, w, h, srcWidth, dst, dstWidth);
}
static void UnloadDialogArt() {
if (altDialogArt) {
delete altDialogArt;
altDialogArt = nullptr;
}
loadAltDialogArt = false;
}
+15 -19
View File
@@ -6,6 +6,7 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "..\Modules\ExtraArt.h"
#include "..\Modules\LoadGameHook.h"
#include "Init.h"
@@ -45,22 +46,20 @@ static class Panels {
long rightBarID;
void LoadFRMImage(char* name, long winId) {
auto* frm = fo::util::LoadUnlistedFrm(name, fo::ArtType::OBJ_TYPE_INTRFACE);
if (!frm) return;
fo::FrmFile* frm = sf::LoadUnlistedFrmCached(name, fo::ArtType::OBJ_TYPE_INTRFACE);
if (frm == nullptr) return;
fo::Window* win = fo::func::GNW_find(winId);
long width = win->width;
if (width > frm->frames->width) width = frm->frames->width;
BYTE* scr = frm->frames->indexBuff;
if (width > frm->frameData[0].width) width = frm->frameData[0].width;
BYTE* scr = frm->frameData[0].data;
// set the position to the right side
if (!IFaceBar::IFACE_BAR_SIDES_ORI && win->wRect.left <= 0) scr += (frm->frames->width - win->width);
if (IFaceBar::IFACE_BAR_SIDES_ORI && win->wRect.left > xPosition) scr += (frm->frames->width - win->width);
if (!IFaceBar::IFACE_BAR_SIDES_ORI && win->wRect.left <= 0) scr += (frm->frameData[0].width - win->width);
if (IFaceBar::IFACE_BAR_SIDES_ORI && win->wRect.left > xPosition) scr += (frm->frameData[0].width - win->width);
fo::func::cscale(scr, width, frm->frames->height, frm->frames->width, win->surface, win->width, win->height, win->width);
delete frm;
fo::func::cscale(scr, width, frm->frameData[0].height, frm->frameData[0].width, win->surface, win->width, win->height, win->width);
}
public:
@@ -205,24 +204,21 @@ static long __cdecl InterfaceArt(BYTE* scr, long w, long h, long srcWidth, BYTE*
char file[33];
std::sprintf(file, "HR_IFACE_%i%s.frm", IFaceBar::IFACE_BAR_WIDTH, ((expandAPBar) ? "E" : ""));
auto* frm = fo::util::LoadUnlistedFrm(file, fo::ArtType::OBJ_TYPE_INTRFACE);
if (frm && frm->frames->width == IFaceBar::IFACE_BAR_WIDTH) {
h = frm->frames->height;
fo::FrmFile* frm = sf::LoadUnlistedFrmCached(file, fo::ArtType::OBJ_TYPE_INTRFACE);
if (frm != nullptr && frm->frameData[0].width == IFaceBar::IFACE_BAR_WIDTH) {
h = frm->frameData[0].height;
if (h > 100) h = 100;
fo::func::buf_to_buf(frm->frames->indexBuff, frm->frames->width, h, frm->frames->width, dst, IFaceBar::IFACE_BAR_WIDTH);
delete frm;
fo::func::buf_to_buf(frm->frameData[0].data, frm->frameData[0].width, h, frm->frameData[0].width, dst, IFaceBar::IFACE_BAR_WIDTH);
return 0;
}
// no required file, use the default one provided by HRP
if (!frm) frm = fo::util::LoadUnlistedFrm(((expandAPBar) ? "HR_IFACE_800E.frm" : "HR_IFACE_800.frm"), fo::ArtType::OBJ_TYPE_INTRFACE);
if (frm == nullptr) frm = sf::LoadUnlistedFrmCached(((expandAPBar) ? "HR_IFACE_800E.frm" : "HR_IFACE_800.frm"), fo::ArtType::OBJ_TYPE_INTRFACE);
if (frm) {
if (frm != nullptr) {
// scale the 800px wide interface to the width of IFACE_BAR_WIDTH
InterfaceArtScale(frm->frames->indexBuff, frm->frames->width, frm->frames->height, dst, h);
delete frm;
InterfaceArtScale(frm->frameData[0].data, frm->frameData[0].width, frm->frameData[0].height, dst, h);
} else {
// scale the vanilla interface to 640px wide (640-460=180)
InterfaceArtScale(scr, w, h, dst, h);
+1 -1
View File
@@ -24,7 +24,7 @@ static long __fastcall CreateWin(long height, long yPos, long xPos, long width,
if (!setPosition[mode]) {
setPosition[mode] = true;
long x = (Setting::ScreenWidth() - width) / 2;
long y = (fo::var::getInt(FO_VAR_buf_length_2) - height) / 2;
long y = (fo::var::buf_length_2 - height) / 2;
if (y < 0) y = 0;
fo::var::iscr_data[mode].x = x;
fo::var::iscr_data[mode].y = y;
+19 -23
View File
@@ -7,6 +7,7 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "..\Modules\ExtraArt.h"
#include "..\Modules\LoadGameHook.h"
#include "..\Modules\Graphics.h"
#include "..\Modules\MainMenu.h"
@@ -31,8 +32,8 @@ bool MainMenuScreen::SCALE_BUTTONS_AND_TEXT_MENU; // if the value is false and U
long MainMenuScreen::MENU_BG_OFFSET_X = 30;
long MainMenuScreen::MENU_BG_OFFSET_Y = 19;
static fo::UnlistedFrm* mainBackgroundFrm;
static fo::UnlistedFrm* btnBackgroundFrm;
static fo::FrmFile* mainBackgroundFrm;
static fo::FrmFile* btnBackgroundFrm;
static long mainmenuWidth = 640;
@@ -49,9 +50,9 @@ static void __cdecl main_menu_create_hook_buf_to_buf(BYTE* src, long sw, long sh
dstW = w;
if (mainBackgroundFrm) {
src = mainBackgroundFrm->frames->indexBuff;
sh = mainBackgroundFrm->frames->height;
sw = mainBackgroundFrm->frames->width;
src = mainBackgroundFrm->frameData[0].data;
sh = mainBackgroundFrm->frameData[0].height;
sw = mainBackgroundFrm->frameData[0].width;
}
bool stretch = (MainMenuScreen::MAIN_MENU_SIZE == 1 || MainMenuScreen::MAIN_MENU_SIZE == 2);
@@ -81,13 +82,13 @@ static void __cdecl main_menu_create_hook_buf_to_buf(BYTE* src, long sw, long sh
if (y < 0) y = 0;
dst += (y * dstW) + x;
sh = btnBackgroundFrm->frames->height;
sw = btnBackgroundFrm->frames->width;
sh = btnBackgroundFrm->frameData[0].height;
sw = btnBackgroundFrm->frameData[0].width;
if (MainMenuScreen::SCALE_BUTTONS_AND_TEXT_MENU) {
fo::func::trans_cscale(btnBackgroundFrm->frames->indexBuff, sw, sh, sw, dst, (long)(sw * scaleFactor), (long)(sh * scaleFactor), dstW);
fo::func::trans_cscale(btnBackgroundFrm->frameData[0].data, sw, sh, sw, dst, (long)(sw * scaleFactor), (long)(sh * scaleFactor), dstW);
} else {
fo::func::trans_buf_to_buf(btnBackgroundFrm->frames->indexBuff, sw, sh, sw, dst, dstW); // direct copy
fo::func::trans_buf_to_buf(btnBackgroundFrm->frameData[0].data, sw, sh, sw, dst, dstW); // direct copy
}
}
}
@@ -101,13 +102,13 @@ static long __fastcall main_menu_create_hook_add_win(long h, long y, long color,
sf::Graphics::BackgroundClearColor(0);
if (MainMenuScreen::USE_HIRES_IMAGES) {
if (!mainBackgroundFrm) {
mainBackgroundFrm = fo::util::LoadUnlistedFrm("HR_MAINMENU.frm", fo::ArtType::OBJ_TYPE_INTRFACE);
btnBackgroundFrm = fo::util::LoadUnlistedFrm("HR_MENU_BG.frm", fo::ArtType::OBJ_TYPE_INTRFACE);
if (mainBackgroundFrm == nullptr) {
mainBackgroundFrm = sf::LoadUnlistedFrmCached("HR_MAINMENU.frm", fo::ArtType::OBJ_TYPE_INTRFACE);
btnBackgroundFrm = sf::LoadUnlistedFrmCached("HR_MENU_BG.frm", fo::ArtType::OBJ_TYPE_INTRFACE);
}
if (mainBackgroundFrm) {
sw = mainBackgroundFrm->frames->width;
sh = mainBackgroundFrm->frames->height;
if (mainBackgroundFrm != nullptr) {
sw = mainBackgroundFrm->frameData[0].width;
sh = mainBackgroundFrm->frameData[0].height;
w = sw;
h = sh;
}
@@ -274,14 +275,9 @@ static void __declspec(naked) main_menu_create_hook_register_button() {
}
static void FreeMainMenuImages() {
if (mainBackgroundFrm) {
delete mainBackgroundFrm;
mainBackgroundFrm = nullptr;
}
if (btnBackgroundFrm) {
delete btnBackgroundFrm;
btnBackgroundFrm = nullptr;
}
// Reset FRM pointers so they can be loaded again after Art cache is reset.
mainBackgroundFrm = nullptr;
btnBackgroundFrm = nullptr;
if (buttonImageData) {
delete[] buttonImageData;
buttonImageData = nullptr;
+105
View File
@@ -0,0 +1,105 @@
/*
* sfall
* Copyright (C) 2008-2024 The sfall team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "..\FalloutEngine\Structs.h"
#include "LoadGameHook.h"
#include "ExtraArt.h"
namespace sfall
{
typedef std::unordered_map<std::string, fo::FrmFile*> TFrmCache;
typedef std::unordered_map<std::string, PcxFile> TPcxCache;
static TFrmCache frmFileCache;
static TPcxCache pcxFileCache;
static PcxFile LoadPcxFile(const char* file) {
PcxFile pcx;
pcx.pixelData = fo::func::loadPCX(file, &pcx.width, &pcx.height, fo::var::pal);
if (pcx.pixelData == nullptr) return PcxFile();
fo::func::datafileConvertData(pcx.pixelData, fo::var::pal, pcx.width, pcx.height);
return pcx;
}
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;
}
frmFileCache.emplace(file, frmPtr);
}
return frmPtr;
}
PcxFile LoadPcxFileCached(const char* file) {
auto cacheHit = pcxFileCache.find(file);
if (cacheHit != pcxFileCache.end()) {
return cacheHit->second;
}
return pcxFileCache.emplace(file, LoadPcxFile(file)).first->second;
}
fo::FrmFile* LoadUnlistedFrmCached(const char* frmName, unsigned int folderRef) {
if (folderRef > fo::OBJ_TYPE_SKILLDEX) return nullptr;
const char* artfolder = fo::var::art[folderRef].path; // address of art type name
char frmPath[MAX_PATH];
if (fo::var::use_language) {
sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s\\%s", (const char*)fo::var::language, artfolder, frmName);
}
else {
sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName);
}
fo::FrmFile* frm = LoadFrmFileCached(frmPath);
if (frm == nullptr && fo::var::use_language) {
sprintf_s(frmPath, MAX_PATH, "art\\%s\\%s", artfolder, frmName);
frm = LoadFrmFileCached(frmPath);
}
return frm;
}
static void ClearInterfaceArtCache() {
for (auto &pair : pcxFileCache) {
fo::func::freePtr_invoke(pair.second.pixelData);
}
pcxFileCache.clear();
for (auto &pair : frmFileCache) {
fo::func::mem_free(pair.second);
}
frmFileCache.clear();
}
void ExtraArt::init() {
LoadGameHook::OnGameReset() += []() {
ClearInterfaceArtCache();
};
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* sfall
* Copyright (C) 2008-2024 The sfall team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Module.h"
namespace fo
{
struct FrmFile;
}
namespace sfall
{
struct PcxFile {
unsigned char* pixelData;
long width;
long height;
PcxFile() : pixelData(nullptr), width(0), height(0) {}
};
class ExtraArt : public Module {
public:
const char* name() { return "ExtraArt"; }
void init();
};
fo::FrmFile* LoadFrmFileCached(const char* file);
PcxFile LoadPcxFileCached(const char* file);
fo::FrmFile* LoadUnlistedFrmCached(const char* file, unsigned int folderRef);
}
+8 -12
View File
@@ -21,6 +21,7 @@
#include "..\Translate.h"
#include "Inventory.h"
#include "ExtraArt.h"
#include "LoadGameHook.h"
#include "LoadOrder.h"
#include "PartyControl.h"
@@ -585,10 +586,9 @@ static void DrawCharNote(bool style, int winRef, DWORD xPosWin, DWORD yPosWin, B
BYTE *PadSurface = new BYTE [280 * 168];
surface_draw(280, 168, widthBG, xPosBG, yPosBG, BGSurface, 280, 0, 0, PadSurface);
fo::UnlistedFrm *frm = fo::util::LoadUnlistedFrm((style) ? "AppStyle.frm" : "AppRace.frm", fo::OBJ_TYPE_SKILLDEX);
if (frm) {
fo::util::DrawToSurface(frm->frames[0].width, frm->frames[0].height, 0, 0, frm->frames[0].width, frm->frames[0].indexBuff, 136, 37, 280, 168, PadSurface, 0); // cover buttons pics bottom
delete frm;
fo::FrmFile *frm = LoadUnlistedFrmCached((style) ? "AppStyle.frm" : "AppRace.frm", fo::OBJ_TYPE_SKILLDEX);
if (frm != nullptr) {
fo::util::DrawToSurface(frm->frameData[0].width, frm->frameData[0].height, 0, 0, frm->frameData[0].width, frm->frameData[0].data, 136, 37, 280, 168, PadSurface, 0); // cover buttons pics bottom
}
int oldFont = GetFont(); // store current font
@@ -653,7 +653,7 @@ static void __stdcall DrawCharNoteNewChar(bool type) {
void __stdcall HeroSelectWindow(int raceStyleFlag) {
if (!HeroAppearance::appModEnabled) return;
fo::UnlistedFrm *frm = fo::util::LoadUnlistedFrm("AppHeroWin.frm", fo::OBJ_TYPE_INTRFACE);
fo::FrmFile *frm = LoadUnlistedFrmCached("AppHeroWin.frm", fo::OBJ_TYPE_INTRFACE);
if (frm == nullptr) {
fo::func::debug_printf("\nApperanceMod: art\\intrface\\AppHeroWin.frm file not found.");
return;
@@ -665,7 +665,6 @@ void __stdcall HeroSelectWindow(int raceStyleFlag) {
int winRef = fo::func::win_add(resWidth / 2 - 242, (resHeight - 100) / 2 - 65, 484, 230, 100, 0x4);
if (winRef == -1) {
delete frm;
return;
}
@@ -677,8 +676,7 @@ void __stdcall HeroSelectWindow(int raceStyleFlag) {
BYTE *winSurface = fo::func::win_get_buf(winRef);
BYTE *mainSurface = new BYTE [484 * 230];
surface_draw(484, 230, 484, 0, 0, frm->frames[0].indexBuff, 484, 0, 0, mainSurface);
delete frm;
surface_draw(484, 230, 484, 0, 0, frm->frameData[0].data, 484, 0, 0, mainSurface);
DWORD MenuUObj, MenuDObj;
BYTE *MenuUSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 299), 0, 0, &MenuUObj); // MENUUP Frm
@@ -1149,11 +1147,9 @@ static void __declspec(naked) FixCharScrnBack() {
if (charScrnBackSurface == nullptr) {
charScrnBackSurface = new BYTE [640 * 480];
fo::UnlistedFrm *frm = fo::util::LoadUnlistedFrm((fo::var::glblmode) ? "AppChCrt.frm" : "AppChEdt.frm", fo::OBJ_TYPE_INTRFACE);
fo::FrmFile *frm = LoadUnlistedFrmCached((fo::var::glblmode) ? "AppChCrt.frm" : "AppChEdt.frm", fo::OBJ_TYPE_INTRFACE);
if (frm != nullptr) {
surface_draw(640, 480, 640, 0, 0, frm->frames[0].indexBuff, 640, 0, 0, charScrnBackSurface);
delete frm;
surface_draw(640, 480, 640, 0, 0, frm->frameData[0].data, 640, 0, 0, charScrnBackSurface);
} else {
BYTE* oldCharScrnBackSurface = fo::var::bckgnd; // char screen background frm surface
+28 -37
View File
@@ -22,6 +22,7 @@
#include "..\FalloutEngine\Fallout2.h"
#include "..\SimplePatch.h"
#include "..\Utils.h"
#include "ExtraArt.h"
#include "Graphics.h"
#include "LoadGameHook.h"
#include "Worldmap.h"
@@ -96,13 +97,21 @@ fo::Window* Interface::GetWindow(long winType) {
return (winID > 0) ? fo::func::GNW_find(winID) : nullptr;
}
static BYTE* LoadInterfaceFrmData(fo::FrmFile** frm, const char* frmName) {
if (*frm == nullptr) {
*frm = LoadUnlistedFrmCached(frmName, fo::ArtType::OBJ_TYPE_INTRFACE);
if (*frm == nullptr) {
return nullptr;
}
}
return (*frm)->frameData[0].data;
}
static BYTE movePointBackground[16 * 9 * 5];
static fo::UnlistedFrm* ifaceFrm = nullptr;
static fo::FrmFile* ifaceFrm = nullptr;
static void* LoadIfaceFrm() {
ifaceFrm = fo::util::LoadUnlistedFrm("IFACE_E.frm", fo::OBJ_TYPE_INTRFACE);
if (!ifaceFrm) return nullptr;
return ifaceFrm->frames[0].indexBuff;
return LoadInterfaceFrmData(&ifaceFrm, "IFACE_E.frm");
}
static void __declspec(naked) intface_init_hook_lock() {
@@ -1104,15 +1113,15 @@ static void UIAnimationSpeedPatch() {
SimplePatch<BYTE>(&addrs[4], 2, "Misc", "PipboyTimeAnimDelay", 50, 0, 127);
}
static fo::UnlistedFrm* barterTallFrm = nullptr;
static fo::UnlistedFrm* tradeTallFrm = nullptr;
static fo::UnlistedFrm* inventoryTallFrms[3] = { nullptr, nullptr, nullptr };
static const char* inventoryTallFrmNames[3] = { "invbox_473.frm", "use_472.frm", "loot_472.frm"};
static fo::FrmFile* barterTallFrm = nullptr;
static fo::FrmFile* tradeTallFrm = nullptr;
constexpr long numTallFrms = fo::INVENTORY_WINDOW_TYPE_TRADE;
static fo::FrmFile* inventoryTallFrms[numTallFrms] = { nullptr, nullptr, nullptr };
static const char* inventoryTallFrmNames[numTallFrms] = { "invbox_473.frm", "use_472.frm", "loot_472.frm"};
static DWORD findInventoryWindowTypeByFid(DWORD fid) {
fid &= 0xFFF;
for (int i = 0; i < 3; ++i) {
for (int i = 0; i < numTallFrms; ++i) {
if (fid == fo::var::iscr_data[i].artIndex)
return i;
}
@@ -1123,45 +1132,25 @@ static BYTE* __fastcall inventory_get_art_data(DWORD fid) {
DWORD windowType = findInventoryWindowTypeByFid(fid);
if (windowType > fo::INVENTORY_WINDOW_TYPE_LOOT) return nullptr;
fo::UnlistedFrm** frm = &inventoryTallFrms[windowType];
if (*frm == nullptr) {
*frm = fo::util::LoadUnlistedFrm(inventoryTallFrmNames[windowType], fo::ArtType::OBJ_TYPE_INTRFACE);
if (*frm == nullptr) {
return nullptr;
}
}
return (*frm)->frames[0].indexBuff;
return LoadInterfaceFrmData(&inventoryTallFrms[windowType], inventoryTallFrmNames[windowType]);
}
static BYTE* __fastcall gdialog_barter_get_art_data() {
fo::UnlistedFrm** frm;
const char* frmName;
if (fo::var::dialog_target_is_party) {
frm = &tradeTallFrm;
frmName = "trade_238.frm";
return LoadInterfaceFrmData(&tradeTallFrm, "trade_238.frm");
}
else {
frm = &barterTallFrm;
frmName = "barter_239.frm";
}
if (*frm == nullptr) {
*frm = fo::util::LoadUnlistedFrm(frmName, fo::ArtType::OBJ_TYPE_INTRFACE);
if (*frm == nullptr) {
return nullptr;
}
}
return (*frm)->frames[0].indexBuff;
return LoadInterfaceFrmData(&barterTallFrm, "barter_239.frm");
}
static DWORD __fastcall gdialog_barter_get_art_height() {
fo::UnlistedFrm** frm = fo::var::dialog_target_is_party
fo::FrmFile** frm = fo::var::dialog_target_is_party
? &tradeTallFrm
: &barterTallFrm;
if (*frm == nullptr) {
return 0;
}
return (*frm)->frames[0].height;
return (*frm)->frameData[0].height;
}
// replace art data for dialog barter window
@@ -1346,12 +1335,14 @@ void Interface::init() {
ammoBarXPos -= 2;
}
}
ExpandedBarterPatch();
LoadGameHook::OnGameInit() += []() {
// Needs to be invoked in OnGameInit when screen height is already known.
ExpandedBarterPatch();
};
ExpandedInventoryPatch();
}
void Interface::exit() {
if (ifaceFrm) delete ifaceFrm;
if (dotStyle) delete[] dotStyle;
}
+12 -64
View File
@@ -17,10 +17,12 @@
*/
#include "..\..\..\FalloutEngine\AsmMacros.h"
#include "..\..\..\FalloutEngine\EngineUtils.h"
#include "..\..\..\FalloutEngine\Fallout2.h"
#include "..\..\..\InputFuncs.h"
#include "..\..\BarBoxes.h"
#include "..\..\ExtraArt.h"
#include "..\..\LoadGameHook.h"
#include "..\..\ScriptExtender.h"
#include "..\..\Interface.h"
@@ -502,70 +504,29 @@ struct FrameData {
}
// Data from PCX file.
FrameData(BYTE* data, long w, long h) {
pixelData = data;
width = (short)w;
height = (short)h;
FrameData(PcxFile pcx) {
pixelData = pcx.pixelData;
width = (short)pcx.width;
height = (short)pcx.height;
}
};
static FrameData LoadPCXFile(const char* file) {
long w, h;
BYTE* pixelData = fo::func::loadPCX(file, &w, &h, fo::var::pal);
if (pixelData == nullptr) return FrameData();
fo::func::datafileConvertData(pixelData, fo::var::pal, w, h);
return FrameData(pixelData, w, h);
}
static bool IsPCXFile(const char* file) {
const char* pos = strrchr(file, '.');
return pos && _stricmp(++pos, "PCX") == 0;
}
typedef std::unordered_map<std::string, fo::FrmFile*> TFRMCache;
typedef std::unordered_map<std::string, FrameData> TPCXCache;
static TFRMCache frmFileCache;
static TPCXCache pcxFileCache;
//static fo::FrmFile* LoadArtFileCached(const char* file, long frame, long direction, fo::FrmFrameData* &framePtr, bool checkPCX) {
static FrameData LoadFrameDataCached(const char* file, long frame, long direction) {
if (IsPCXFile(file)) {
auto cacheHit = pcxFileCache.find(file);
if (cacheHit != pcxFileCache.end()) {
return cacheHit->second;
}
return pcxFileCache.emplace(file, LoadPCXFile(file)).first->second;
return LoadPcxFileCached(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;
}
frmFileCache.emplace(file, frmPtr);
}
return (frmPtr != nullptr)
fo::FrmFile* frmPtr = LoadFrmFileCached(file);
return frmPtr != nullptr
? FrameData(frmPtr, direction, frame)
: FrameData();
}
void ClearInterfaceArtCache() {
for (auto &pair : pcxFileCache) {
fo::func::freePtr_invoke(pair.second.pixelData);
}
pcxFileCache.clear();
for (auto &pair : frmFileCache) {
fo::func::mem_free(pair.second);
}
frmFileCache.clear();
}
static long GetArtFIDFile(long fid, char* outFilePath) {
long direction = 0;
long _fid = fid & 0xFFFFFFF;
@@ -590,20 +551,7 @@ static long GetArtFIDFile(long fid, char* outFilePath) {
return direction;
}
struct ArtCacheLock {
DWORD entryPtr = 0;
ArtCacheLock() {}
ArtCacheLock(DWORD _lock) : entryPtr(_lock) {}
~ArtCacheLock() {
if (entryPtr != 0) {
fo::func::art_ptr_unlock(entryPtr);
entryPtr = 0;
}
}
};
static FrameData LockFrameData(unsigned long fid, ArtCacheLock& lock, long direction, long frame) {
static FrameData LockFrameData(unsigned long fid, fo::util::ArtCacheLock& lock, long direction, long frame) {
long objType = (fid >> 24) & 0xF;
if (direction < 0) {
// If direction is not specified, take it from FID.
@@ -623,7 +571,7 @@ static long DrawImage(OpcodeContext& ctx, bool isScaled) {
return 0;
}
FrameData frm;
ArtCacheLock cacheLock;
fo::util::ArtCacheLock cacheLock;
bool isID = ctx.arg(0).isInt();
long frame = ctx.arg(1).rawValue();
@@ -740,7 +688,7 @@ static long InterfaceDrawImage(OpcodeContext& ctx, fo::Window* ifaceWin) {
if (size > 2) h = sArray->val[2].intVal;
}
}
ArtCacheLock cacheLock;
fo::util::ArtCacheLock cacheLock;
FrameData frm;
if (isID) { // art id
long fid = ctx.arg(1).rawValue();
-1
View File
@@ -293,7 +293,6 @@ void Opcodes::InitNew() {
PipboyAvailableRestore();
ForceEncounterRestore(); // restore if the encounter did not happen
ResetIniCache();
ClearInterfaceArtCache();
};
if (int unsafe = IniReader::GetIntDefaultConfig("Debugging", "AllowUnsafeScripting", 0)) {
+2
View File
@@ -39,6 +39,7 @@
#include "Modules\Elevators.h"
#include "Modules\EngineTweaks.h"
#include "Modules\Explosions.h"
#include "Modules\ExtraArt.h"
#include "Modules\ExtraSaveSlots.h"
#include "Modules\FileSystem.h"
#include "Modules\Graphics.h"
@@ -117,6 +118,7 @@ static void InitModules() {
manager.add<LoadOrder>();
manager.add<LoadGameHook>();
manager.add<MainLoopHook>();
manager.add<ExtraArt>();
manager.add<EngineTweaks>();
manager.add<Books>();