Fixed and improved Use32BitHeadGraphics option:

* Fixed the position of the talking head texture when the game res is
larger than 640x480.

* Added the support to use textures without the need of patching
talking head FRM files.

Fixed a bug in item highlighting that item keeps its outline if player
picked it up while holding the highlight key.
This commit is contained in:
NovaRain
2019-04-20 22:49:02 +08:00
parent 533020cd8b
commit 3798e5239d
12 changed files with 323 additions and 171 deletions
+3 -1
View File
@@ -78,7 +78,9 @@ GraphicsHeight=0
;GPU is faster, but requires v2.0 pixel shader support
GPUBlt=0
;Set to 1 to allow using 32 bit graphics for talking heads
;Set to 1 to allow using 32-bit .png textures for talking heads
;The texture files should be placed in art/heads/<name of the talking head FRM file>/ (w/o extension)
;The files in the folder should be numbered according to the number of frames in the talking head FRM file (e.g. 0.png - 10.png)
;Requires DX9 graphics mode and v2.0 pixel shader support (see GPUBlt option)
Use32BitHeadGraphics=0
Binary file not shown.
+6
View File
@@ -112,6 +112,11 @@ procedure GameModeChangeHandler begin
end
end
procedure InventoryMoveHandler begin
// remove item outline when player picks up the item
if (isHighlight and get_sfall_arg == 7) then set_outline(get_sfall_arg, 0);
end
procedure start begin
if game_loaded and (sfall_ver_major >= 4) then begin
call InitConfigs;
@@ -130,5 +135,6 @@ procedure start begin
register_hook_proc(HOOK_KEYPRESS, KeyPressHandler);
register_hook_proc(HOOK_COMBATTURN, CombatTurnHandler);
register_hook_proc(HOOK_GAMEMODECHANGE, GameModeChangeHandler);
register_hook_proc(HOOK_INVENTORYMOVE, InventoryMoveHandler);
end
end
+2 -1
View File
@@ -288,7 +288,8 @@
#define set_outline(obj, color) sfall_func2("set_outline", obj, color)
#define set_rest_heal_time(time) sfall_func1("set_rest_heal_time", time)
#define set_rest_mode(mode) sfall_func1("set_rest_mode", mode)
#define set_unique_id(object) sfall_func1("set_unique_id", object)
#define set_unique_id(obj) sfall_func1("set_unique_id", obj)
#define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1)
#define set_unjam_locks_time(time) sfall_func1("set_unjam_locks_time", time)
#define spatial_radius(obj) sfall_func1("spatial_radius", obj)
#define tile_refresh_display sfall_func0("tile_refresh_display")
+1 -1
View File
@@ -18,7 +18,7 @@
// because the compiler builds the better/optimized code when calling the engine functions
WRAP_WATCOM_FFUNC4(long, _word_wrap, const char*, text, int, maxWidth, DWORD*, buf, BYTE*, count)
WRAP_WATCOM_FFUNC7(long, createWindow, const char*, winName, long, x, long, y, long, width, long, height, long, bgColorIndex, long, flags)
WRAP_WATCOM_FFUNC3(long, frame_ptr, FrmFrameData*, frm, long, rotation, long, frame)
WRAP_WATCOM_FFUNC3(FrmSubframeData*, frame_ptr, FrmFrameData*, frm, long, frame, long, direction)
WRAP_WATCOM_FFUNC7(void, make_straight_path_func, fo::GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func)
WRAP_WATCOM_FFUNC3(long, object_under_mouse, long, crSwitch, long, inclDude, long, elevation)
+3 -1
View File
@@ -287,7 +287,7 @@ struct FrmFile {
};
//structures for holding frms loaded with fallout2 functions
#pragma pack(2)
#pragma pack(1)
typedef class FrmSubframeData {
public:
WORD width;
@@ -295,8 +295,10 @@ public:
DWORD size;
WORD x;
WORD y;
BYTE data[1]; // begin frame data
} FrmSubframeData;
#pragma pack(2)
typedef class FrmFrameData {
public:
DWORD version; //version num
+3
View File
@@ -19,6 +19,7 @@
#define FO_VAR_background_volume 0x518E88
#define FO_VAR_bboxslot 0x5970E0
#define FO_VAR_bckgnd 0x5707A4
#define FO_VAR_bk_disabled 0x6AC780
#define FO_VAR_black_palette 0x663FD0
#define FO_VAR_bottom_line 0x664524
#define FO_VAR_btable 0x59E944
@@ -43,6 +44,7 @@
#define FO_VAR_curr_pc_stat 0x6681AC
#define FO_VAR_curr_stack 0x59E96C
#define FO_VAR_cursor_line 0x664514
#define FO_VAR_dialogue_head 0x518850
#define FO_VAR_dialogue_state 0x518714
#define FO_VAR_dialogue_switch_mode 0x518718
#define FO_VAR_dialog_target 0x518848
@@ -56,6 +58,7 @@
#define FO_VAR_elevation 0x631D2C
#define FO_VAR_Experience_ 0x6681B4
#define FO_VAR_fallout_game_time 0x51C720
#define FO_VAR_fidgetFID 0x5186F4
#define FO_VAR_flptr 0x614808
#define FO_VAR_folder_card_desc 0x5705CC
#define FO_VAR_folder_card_fid 0x5705B0
+1
View File
@@ -46,6 +46,7 @@ VAR_(Educated, DWORD)
VAR_(elevation, DWORD)
VAR_(Experience_, DWORD)
VAR_(fallout_game_time, DWORD)
VAR_(fidgetFID, DWORD)
VAR_(flptr, DWORD)
VAR_(folder_card_desc, DWORD)
VAR_(folder_card_fid, DWORD)
+142 -86
View File
@@ -27,9 +27,6 @@
#endif
#endif
#include <d3d9.h>
#include <d3dx9.h>
#include <ddraw.h>
#include <math.h>
#include <stdio.h>
#include <vector>
@@ -54,7 +51,9 @@ typedef IDirect3D9* (_stdcall *D3DCreateProc)(UINT version);
static DWORD ResWidth;
static DWORD ResHeight;
static DWORD GPUBlt;
DWORD Graphics::GPUBlt;
DWORD Graphics::mode;
static BYTE* titlesBuffer = nullptr;
static DWORD movieHeight = 0;
@@ -94,51 +93,65 @@ static IDirect3DTexture9* movieTex = 0;
static ID3DXEffect* gpuBltEffect;
static const char* gpuEffect=
"texture image;\n"
"texture palette;\n"
"texture head;\n"
"sampler s0 = sampler_state { texture=<image>; MAGFILTER=POINT; MINFILTER=POINT; };\n"
"sampler s1 = sampler_state { texture=<palette>; MAGFILTER=POINT; MINFILTER=POINT; };\n"
"sampler s2 = sampler_state { texture=<head>; MAGFILTER=POINT; MINFILTER=POINT; };\n"
"float2 size;\n"
"float2 corner;\n"
"float4 P0( in float2 Tex : TEXCOORD0 ) : COLOR0 {\n"
" float3 result = tex1D(s1, tex2D(s0, Tex).a);\n"
" return float4(result.b, result.g, result.r, 1);\n"
"}\n"
"float4 P1( in float2 Tex : TEXCOORD0 ) : COLOR0 {\n"
" float backdrop = tex2D(s0, Tex).a;\n"
" float3 result;\n"
" if(abs(backdrop-(48.0/255.0))<0.001) {\n"
//" float2 size = float2(388.0/640.0, 200.0/480.0);\n"
//" float2 corner = float2(126.0/640.0, 14.0/480.0);\n"
" result = tex2D(s2, saturate((Tex-corner)/size));\n"
" } else {\n"
" result = tex1D(s1, backdrop);\n"
" result = float3(result.b, result.g, result.r);\n"
" }\n"
" return float4(result.r, result.g, result.b, 1);\n"
"}\n"
"technique T0\n"
"{\n"
" pass p0 { PixelShader = compile ps_2_0 P0(); }\n"
"}\n"
"technique T1\n"
"{\n"
" pass p1 { PixelShader = compile ps_2_0 P1(); }\n"
"}\n"
;
"texture image;"
"texture palette;"
"texture head;"
"texture highlight;"
"sampler s0 = sampler_state { texture=<image>; };"
"sampler s1 = sampler_state { texture=<palette>; };"
"sampler s2 = sampler_state { texture=<head>; minFilter=linear; magFilter=linear; addressU=clamp; addressV=clamp; };"
"sampler s3 = sampler_state { texture=<highlight>; minFilter=linear; magFilter=linear; addressU=clamp; addressV=clamp; };"
"float2 size;"
"float2 corner;"
"float2 sizehl;"
"float2 cornerhl;"
"int showhl;"
// shader for displaying head textures
"float4 P1( in float2 Tex : TEXCOORD0 ) : COLOR0 {"
" float backdrop = tex2D(s0, Tex).a;"
" float3 result;"
" if(abs(backdrop - 1.0) < 0.001) {" // (48.0 / 255.0) // 48 - key index color
" result = tex2D(s2, saturate((Tex - corner) / size));"
" } else {"
" result = tex1D(s1, backdrop);"
" result = float3(result.b, result.g, result.r);"
" }"
// blend highlights
"if (showhl) {"
"float4 h = tex2D(s3, saturate((Tex - cornerhl) / sizehl));"
"result = saturate(result + h.rgb);" // saturate(result * (1 - h.a) * h.rgb * h.a)"
"}"
" return float4(result.r, result.g, result.b, 1);"
"}"
"technique T1"
"{"
" pass p1 { PixelShader = compile ps_2_0 P1(); }"
"}"
"float4 P0( in float2 Tex : TEXCOORD0 ) : COLOR0 {"
" float3 result = tex1D(s1, tex2D(s0, Tex).a);"
" return float4(result.b, result.g, result.r, 1);"
"}"
"technique T0"
"{"
" pass p0 { PixelShader = compile ps_2_0 P0(); }"
"}";
static D3DXHANDLE gpuBltBuf;
static D3DXHANDLE gpuBltPalette;
static D3DXHANDLE gpuBltHead;
static D3DXHANDLE gpuBltHeadSize;
static D3DXHANDLE gpuBltHeadCorner;
static D3DXHANDLE gpuBltHighlight;
static D3DXHANDLE gpuBltHighlightSize;
static D3DXHANDLE gpuBltHighlightCorner;
static D3DXHANDLE gpuBltShowHighlight;
static float rcpres[2];
DWORD Graphics::mode;
struct sShader {
ID3DXEffect* Effect;
bool Active;
@@ -176,10 +189,23 @@ void GetFalloutWindowInfo(DWORD* width, DWORD* height, HWND* wnd) {
*wnd = window;
}
long Graphics::GetGameWidthRes() {
return fo::var::scr_size.offx - (fo::var::scr_size.x + 1);
}
long Graphics::GetGameHeightRes() {
return fo::var::scr_size.offy - (fo::var::scr_size.y + 1);
}
int _stdcall GetShaderVersion() {
return ShaderVersion;
}
static void rcpresInit() {
rcpres[0] = 1.0f / (float)Graphics::GetGameWidthRes();
rcpres[1] = 1.0f / (float)Graphics::GetGameHeightRes();
}
void _stdcall SetShaderMode(DWORD d, DWORD mode) {
if (d >= shaders.size() || !shaders[d].Effect) return;
if (mode & 0x80000000) {
@@ -190,7 +216,7 @@ void _stdcall SetShaderMode(DWORD d, DWORD mode) {
}
int _stdcall LoadShader(const char* path) {
if ((Graphics::mode < 4) || (strstr(path, "..")) || (strstr(path, ":"))) return -1;
if (!Graphics::mode || strstr(path, "..") || strstr(path, ":")) return -1;
char buf[MAX_PATH];
sprintf(buf, "%s\\shaders\\%s", fo::var::patches, path);
for (DWORD d = 0; d < shaders.size(); d++) {
@@ -296,17 +322,24 @@ static void ResetDevice(bool CreateNew) {
d3d9Device->GetDeviceCaps(&caps);
ShaderVersion = ((caps.PixelShaderVersion & 0x0000ff00) >> 8) * 10 + (caps.PixelShaderVersion & 0xff);
if (GPUBlt == 2) {
if (ShaderVersion < 20) GPUBlt = 0;
if (Graphics::GPUBlt == 2) {
if (ShaderVersion < 20) Graphics::GPUBlt = 0;
}
if (GPUBlt) {
if (Graphics::GPUBlt) {
D3DXCreateEffect(d3d9Device, gpuEffect, strlen(gpuEffect), 0, 0, 0, 0, &gpuBltEffect, 0);
gpuBltBuf = gpuBltEffect->GetParameterByName(0, "image");
gpuBltPalette = gpuBltEffect->GetParameterByName(0, "palette");
// for head textures
gpuBltHead = gpuBltEffect->GetParameterByName(0, "head");
gpuBltHeadSize = gpuBltEffect->GetParameterByName(0, "size");
gpuBltHeadCorner = gpuBltEffect->GetParameterByName(0, "corner");
gpuBltHighlight = gpuBltEffect->GetParameterByName(0, "highlight");
gpuBltHighlightSize = gpuBltEffect->GetParameterByName(0, "sizehl");
gpuBltHighlightCorner = gpuBltEffect->GetParameterByName(0, "cornerhl");
gpuBltShowHighlight = gpuBltEffect->GetParameterByName(0, "showhl");
Graphics::SetDefaultTechnique();
}
} else {
d3d9Device->Reset(&params);
@@ -320,14 +353,14 @@ static void ResetDevice(bool CreateNew) {
ShaderVertices[2].x = ResWidth - 0.5f;
ShaderVertices[3].y = ResHeight - 0.5f;
ShaderVertices[3].x = ResWidth - 0.5f;
if (d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_DYNAMIC, GPUBlt ? D3DFMT_A8 : D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &Tex, 0) != D3D_OK) {
if (d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_DYNAMIC, Graphics::GPUBlt ? D3DFMT_A8 : D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &Tex, 0) != D3D_OK) {
d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &Tex, 0);
GPUBlt = 0;
Graphics::GPUBlt = 0;
dlog(" Error: D3DFMT_A8 unsupported texture format. Now CPU is used to convert the palette.", DL_MAIN);
}
d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &sTex1, 0);
d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &sTex2, 0);
if (GPUBlt) {
if (Graphics::GPUBlt) {
d3d9Device->CreateTexture(256, 1, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &gpuPalette, 0);
gpuBltEffect->SetTexture(gpuBltBuf, Tex);
gpuBltEffect->SetTexture(gpuBltPalette, gpuPalette);
@@ -451,7 +484,7 @@ void RefreshGraphics() {
d3d9Device->SetStreamSource(0, vBuffer, 0, sizeof(MyVertex));
d3d9Device->SetRenderTarget(0, sSurf1);
if (GPUBlt && shaders.size()) {
if (Graphics::GPUBlt && shaders.size()) {
UINT unused;
gpuBltEffect->Begin(&unused, 0);
gpuBltEffect->BeginPass(0);
@@ -484,13 +517,13 @@ void RefreshGraphics() {
d3d9Device->SetStreamSource(0, vBuffer2, 0, sizeof(MyVertex));
d3d9Device->SetRenderTarget(0, backbuffer);
if (GPUBlt && !shaders.size()) {
if (Graphics::GPUBlt && !shaders.size()) {
UINT unused;
gpuBltEffect->Begin(&unused, 0);
gpuBltEffect->BeginPass(0);
}
d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
if (GPUBlt) {
if (Graphics::GPUBlt) {
gpuBltEffect->EndPass();
gpuBltEffect->End();
}
@@ -551,19 +584,45 @@ void _stdcall PlayMovieFrame() {
Present();
}
void _stdcall SetHeadTex(IDirect3DTexture9* tex, int width, int height, int xoff, int yoff) {
if (tex) {
gpuBltEffect->SetTexture(gpuBltHead, tex);
float size[2];
size[0] = ((float)width)*rcpres[0];
size[1] = ((float)height)*rcpres[1];
gpuBltEffect->SetFloatArray(gpuBltHeadSize, size, 2);
size[0] = (126.0f + xoff + (388 - width) / 2)*rcpres[0];
size[1] = (14.0f + yoff + (200 - height) / 2)*rcpres[1];
gpuBltEffect->SetFloatArray(gpuBltHeadCorner, size, 2);
gpuBltEffect->SetTechnique("T1");
} else
gpuBltEffect->SetTechnique("T0");
void Graphics::SetHighlightTexture(IDirect3DTexture9* htex) {
gpuBltEffect->SetTexture(gpuBltHighlight, htex);
float size[2];
size[0] = 388.0f * rcpres[0];
size[1] = 200.0f * rcpres[1];
gpuBltEffect->SetFloatArray(gpuBltHighlightSize, size, 2);
int xPos = ((GetGameWidthRes() - 640) / 2);
size[0] = (126.0f + xPos) * rcpres[0];
int h = GetGameHeightRes();
int yPos = (h > 480) ? ((h - 480) / 2) - 33 : 14;
size[1] = (float)yPos * rcpres[1];
gpuBltEffect->SetFloatArray(gpuBltHighlightCorner, size, 2);
}
void Graphics::SetHeadTex(IDirect3DTexture9* tex, int width, int height, int xoff, int yoff, int showHighlight) {
gpuBltEffect->SetInt(gpuBltShowHighlight, showHighlight);
gpuBltEffect->SetTexture(gpuBltHead, tex);
float size[2];
size[0] = (float)width * rcpres[0];
size[1] = (float)height * rcpres[1];
gpuBltEffect->SetFloatArray(gpuBltHeadSize, size, 2);
// adjust head texture position for HRP 4.1.8
int h = GetGameHeightRes();
if (h > 480) yoff += ((h - 480) / 2) - 47;
xoff += ((GetGameWidthRes() - 640) / 2);
size[0] = (126.0f + xoff + ((388 - width) / 2)) * rcpres[0];
size[1] = (14.0f + yoff + ((200 - height) / 2)) * rcpres[1];
gpuBltEffect->SetFloatArray(gpuBltHeadCorner, size, 2);
gpuBltEffect->SetTechnique("T1");
}
void Graphics::SetDefaultTechnique() {
gpuBltEffect->SetTechnique("T0");
}
void GraphicsResetOnGameLoad() {
@@ -604,7 +663,7 @@ public:
if (c == 0 || b + c > 256) return DDERR_INVALIDPARAMS;
CopyMemory(&palette[b], d, c * 4);
if (GPUBlt) {
if (Graphics::GPUBlt) {
if (gpuPalette) {
D3DLOCKED_RECT rect;
if (!FAILED(gpuPalette->LockRect(0, &rect, 0, D3DLOCK_DISCARD))) {
@@ -674,7 +733,7 @@ public:
Tex->LockRect(0, &dRect, a, 0);
DWORD width = ResWidth;
int pitch = dRect.Pitch;
if (GPUBlt) {
if (Graphics::GPUBlt) {
char* pBits = (char*)dRect.pBits;
if (subTitlesShow) {
subTitlesShow = false;
@@ -703,7 +762,7 @@ public:
if (y < yoffset || y > bottom) {
int yyp = (y - yoffset) * pitch;
int yw = y * width;
for (DWORD x = 0; x < width; x++) {
for (DWORD x = 0; x < width; x++) {
((DWORD*)dRect.pBits)[yyp + x] = palette[titlesBuffer[yw + x]];
}
}
@@ -729,13 +788,13 @@ public:
d3d9Device->SetStreamSource(0, vBuffer2, 0, sizeof(MyVertex));
d3d9Device->SetTexture(0, Tex);
d3d9Device->BeginScene();
if (GPUBlt) {
if (Graphics::GPUBlt) {
UINT unused;
gpuBltEffect->Begin(&unused, 0);
gpuBltEffect->BeginPass(0);
}
d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
if (GPUBlt) {
if (Graphics::GPUBlt) {
gpuBltEffect->EndPass();
gpuBltEffect->End();
}
@@ -798,7 +857,7 @@ public:
Tex->LockRect(0, &dRect, 0, 0);
int pitch = dRect.Pitch;
DWORD width = ResWidth;
if (GPUBlt) {
if (Graphics::GPUBlt) {
char* pBits = (char*)dRect.pBits;
if (IsPlayMovie) { // for subtitles
subTitlesShow = true;
@@ -984,15 +1043,15 @@ public:
return DD_OK;
}
HRESULT _stdcall SetDisplayMode(DWORD, DWORD, DWORD) { return DD_OK; }
HRESULT _stdcall SetDisplayMode(DWORD, DWORD, DWORD) { return DD_OK; }
HRESULT _stdcall WaitForVerticalBlank(DWORD, HANDLE) { UNUSEDFUNCTION; }
};
HRESULT _stdcall FakeDirectDrawCreate2_Init(void*, IDirectDraw** b, void*) {
dlog("Initializing Direct3D...", DL_MAIN);
ResWidth = *(DWORD*)0x4CAD6B;
ResHeight = *(DWORD*)0x4CAD66;
ResWidth = *(DWORD*)0x4CAD6B; // 640
ResHeight = *(DWORD*)0x4CAD66; // 480
if (!d3d9) {
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
@@ -1022,17 +1081,14 @@ HRESULT _stdcall FakeDirectDrawCreate2_Init(void*, IDirectDraw** b, void*) {
gWidth = ResWidth;
gHeight = ResHeight;
}
GPUBlt = GetConfigInt("Graphics", "GPUBlt", 0);
if (!GPUBlt || GPUBlt > 2) GPUBlt = 2; //Swap them around to keep compatibility with old ddraw.ini's
else if (GPUBlt == 2) GPUBlt = 0;
Graphics::GPUBlt = GetConfigInt("Graphics", "GPUBlt", 0);
if (!Graphics::GPUBlt || Graphics::GPUBlt > 2) Graphics::GPUBlt = 2; // Swap them around to keep compatibility with old ddraw.ini's
else if (Graphics::GPUBlt == 2) Graphics::GPUBlt = 0; // Use CPU
if (Graphics::mode == 5) {
ScrollWindowKey = GetConfigInt("Input", "WindowScrollKey", 0);
} else ScrollWindowKey = 0;
rcpres[0] = 1.0f / (float)gWidth;
rcpres[1] = 1.0f / (float)gHeight;
*b = (IDirectDraw*)new FakeDirectDraw2();
dlogr(" Done.", DL_MAIN);
@@ -1042,13 +1098,11 @@ HRESULT _stdcall FakeDirectDrawCreate2_Init(void*, IDirectDraw** b, void*) {
static double fadeMulti;
static __declspec(naked) void palette_fade_to_hook() {
__asm {
//pushf;
push ebx;
fild[esp];
push ebx; // _fade_steps
fild [esp];
fmul fadeMulti;
fistp[esp];
fistp [esp];
pop ebx;
//popf;
jmp fo::funcoffs::fadeSystemPalette_;
}
}
@@ -1058,7 +1112,7 @@ void Graphics::init() {
if (Graphics::mode != 4 && Graphics::mode != 5) {
Graphics::mode = 0;
}
if (Graphics::mode == 4 || Graphics::mode == 5) {
if (Graphics::mode) {
dlog("Applying DX9 graphics patch.", DL_INIT);
#define _DLL_NAME "d3dx9_43.dll"
HMODULE h = LoadLibraryEx(_DLL_NAME, 0, LOAD_LIBRARY_AS_DATAFILE);
@@ -1072,6 +1126,7 @@ void Graphics::init() {
SafeWrite8(0x50FB6B, '2'); // Set call DirectDrawCreate2
dlogr(" Done", DL_INIT);
}
fadeMulti = GetConfigInt("Graphics", "FadeMultiplier", 100);
if (fadeMulti != 100) {
dlog("Applying fade patch.", DL_INIT);
@@ -1080,13 +1135,14 @@ void Graphics::init() {
dlogr(" Done", DL_INIT);
}
if (Graphics::mode > 3) {
if (Graphics::mode) {
LoadGameHook::OnGameReset() += GraphicsResetOnGameLoad;
LoadGameHook::OnAfterGameInit() += rcpresInit;
}
}
void Graphics::exit() {
if (Graphics::mode != 0) {
if (Graphics::mode) {
if (titlesBuffer) delete[] titlesBuffer;
CoUninitialize();
}
+12
View File
@@ -18,6 +18,10 @@
#pragma once
#include <d3d9.h>
#include <d3dx9.h>
#include <ddraw.h>
#include "Module.h"
namespace sfall
@@ -30,6 +34,14 @@ public:
void exit() override;
static DWORD mode;
static DWORD GPUBlt;
static long GetGameWidthRes();
static long GetGameHeightRes();
static void SetHighlightTexture(IDirect3DTexture9* htex);
static void SetHeadTex(IDirect3DTexture9* tex, int width, int height, int xoff, int yoff, int showHighlight);
static void SetDefaultTechnique();
};
int _stdcall GetShaderVersion();
+149 -81
View File
@@ -16,26 +16,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <d3d9.h>
#include <d3dx9.h>
#include <stdio.h>
#include <unordered_map>
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "Graphics.h"
#include "LoadGameHook.h"
#include "TalkingHeads.h"
namespace sfall
{
void _stdcall SetHeadTex(IDirect3DTexture9* tex, int width, int height, int xoff, int yoff);
extern IDirect3DDevice9* d3d9Device;
static std::unordered_map<__int64, IDirect3DTexture9**> texMap;
typedef std::unordered_map<__int64, IDirect3DTexture9**> :: iterator tex_itr;
typedef std::unordered_map<__int64, IDirect3DTexture9**> :: const_iterator tex_citr;
static BYTE overridden=0;
#pragma pack(push, 1)
struct Frm {
@@ -66,129 +60,203 @@ struct Frm {
};
};
};
struct Frame {
WORD width;
WORD height;
DWORD size;
WORD xoffset;
WORD yoffset;
BYTE data[1];
};
#pragma pack(pop)
static Frame* FramePointer(const Frm* frm, int frameno) {
Frame* result;
__asm {
mov eax, frm;
mov edx, frameno;
xor ebx, ebx;
call fo::funcoffs::frame_ptr_;
mov result, eax;
}
return result;
struct TextureData {
IDirect3DTexture9** textures;
BYTE showHighlights;
BYTE bakedBackground;
int frames;
TextureData(IDirect3DTexture9** tex, BYTE show, BYTE baked, int frames)
: textures(tex), showHighlights(show), bakedBackground(baked), frames(frames) {}
};
typedef std::unordered_map<__int64, TextureData> :: iterator tex_itr;
typedef std::unordered_map<__int64, TextureData> :: const_iterator tex_citr;
static std::unordered_map<__int64, TextureData> texMap;
static IDirect3DTexture9* texHighlight = nullptr;
static const char* headSuffix[] = { "gv", "gf", "gn", "ng", "nf", "nb", "bn", "bf", "bv", "gp", "np", "bp" };
static BYTE showHighlights;
/* Head FID
0-000-1000-00000000-0000-000000000000
ID3 Type ID2 ID1 .lst index
*/
static bool GetHeadFrmName(char* name) {
int headFid = fo::var::fidgetFID;
int index = headFid & 0xFFF;
if (index >= fo::var::art[fo::OBJ_TYPE_HEAD].total) return true;
int ID2 = (headFid & 0xFF0000) >> 16;
if (ID2 > 11) return true;
int ID1 = (headFid & 0xF000) >> 12;
//if (ID1 > 3) ID1 = 3;
const char* headLst = fo::var::art[fo::OBJ_TYPE_HEAD].names;
_snprintf(name, 8, "%s%s%d", &headLst[13 * index], headSuffix[ID2], ID1);
return false;
}
static void LoadFrm(Frm* frm) {
static void StrAppend(char* buf, const char* str, int pos) {
int i = 0;
while (pos < MAX_PATH && str[i]) buf[pos++] = str[i++];
buf[pos] = str[i]; // copy '\0'
}
static bool LoadFrm(Frm* frm) {
if (!frm->key && GetHeadFrmName(frm->path)) {
frm->broken = 1;
return false;
}
tex_citr itr = texMap.find(frm->key);
if (itr == texMap.end()) {
//Load textures
// Loading head frames textures
*(DWORD*)FO_VAR_bk_disabled = 1;
char buf[MAX_PATH];
int pathLen = sprintf(buf, "%s\\art\\heads\\%s\\", fo::var::patches, frm->path);
IDirect3DTexture9** textures = new IDirect3DTexture9*[frm->frames];
for (int i = 0; i < frm->frames; i++) {
sprintf(buf, "%s\\art\\heads\\%s\\%d.png", fo::var::patches, frm->path, i);
sprintf(&buf[pathLen], "%d.png", i);
if (FAILED(D3DXCreateTextureFromFileExA(d3d9Device, buf, 0, 0, 1, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, 0, 0, &textures[i]))) {
for (int j = 0; j < i; j++) textures[j]->Release();
delete[] textures;
frm->broken = 1;
return;
*(DWORD*)FO_VAR_bk_disabled = 0;
return false;
}
fo::func::process_bk(); // eliminate lag when loading textures
}
if (frm->magic != 0xABCD) { // frm file not patched
StrAppend(buf, "highlight.off", pathLen);
if (GetFileAttributes(buf) != INVALID_FILE_ATTRIBUTES) frm->showHighlights = 1; // disable all highlights
if (!frm->showHighlights && texHighlight) {
StrAppend(buf, "highlight.on", pathLen);
if (GetFileAttributes(buf) != INVALID_FILE_ATTRIBUTES) frm->showHighlights = 2; // show textured highlights
}
StrAppend(buf, "background.off", pathLen);
if (GetFileAttributes(buf) != INVALID_FILE_ATTRIBUTES) frm->bakedBackground = 1; // fills entire frame surface with a key-color
}
frm->textures = textures;
texMap[frm->key] = textures;
texMap.emplace(std::piecewise_construct, std::forward_as_tuple(frm->key),
std::forward_as_tuple(textures, frm->showHighlights, frm->bakedBackground, frm->frames));
*(DWORD*)FO_VAR_bk_disabled = 0;
} else {
//Use preloaded textures
frm->textures = itr->second;
// Use preloaded textures
frm->textures = itr->second.textures;
frm->showHighlights = itr->second.showHighlights;
frm->bakedBackground = itr->second.bakedBackground;
}
//mask image
// make mask image
for (int i = 0; i < frm->frames; i++) {
Frame* frame = FramePointer(frm, i);
fo::FrmSubframeData* frame = fo::func::frame_ptr((fo::FrmFrameData*)frm, i, 0);
if (frm->bakedBackground) {
memset(frame->data, 0x30, frame->size);
memset(frame->data, 255, frame->size);
} else {
for (DWORD j = 0; j < frame->size; j++) {
if (frame->data[j]) frame->data[j] = 0x30;
if (frame->data[j]) frame->data[j] = 255; // set index color
}
}
}
frm->loaded = 1;
return true;
}
static void _stdcall DrawFrmHookInternal(Frm* frm, int frameno) {
if (!frm) return;
if (frm->magic == 0xabcd && !frm->broken) {
if (!frm->loaded) LoadFrm(frm);
if (frm->broken) return;
Frame* frame = FramePointer(frm, frameno);
SetHeadTex(frm->textures[frameno], frame->width, frame->height, frame->xoffset + frm->xshift, frame->yoffset + frm->yshift);
overridden = !frm->showHighlights;
} else overridden = 0;
static void __fastcall DrawHeadFrame(Frm* frm, int frameno) {
if (frm && !frm->broken) {
if (!frm->loaded && !LoadFrm(frm)) goto loadFail;
fo::FrmSubframeData* frame = fo::func::frame_ptr((fo::FrmFrameData*)frm, frameno, 0);
Graphics::SetHeadTex(frm->textures[frameno], frame->width, frame->height, frame->x + frm->xshift, frame->y + frm->yshift, (frm->showHighlights == 2));
showHighlights = (((char)frm->showHighlights) > 0);
return;
}
loadFail:
showHighlights = 0; // show vanilla highlights
Graphics::SetDefaultTechnique();
}
static const DWORD gdDisplayFrameRet = 0x44AD06;
static void __declspec(naked) DrawFrmHook() {
static void __declspec(naked) gdDisplayFrame_hack() {
__asm {
push edx;
push eax;
push edx;
push eax;
call DrawFrmHookInternal;
pop eax;
pop edx;
sub esp, 0x38;
mov esi, eax;
jmp gdDisplayFrameRet;
mov ecx, eax; // frm file
call DrawHeadFrame; // edx - frameno
pop eax;
pop edx;
sub esp, 0x38;
mov esi, eax;
jmp gdDisplayFrameRet;
}
}
static const DWORD EndSpeechHookRet=0x447299;
void __declspec(naked) EndSpeechHook() {
void __declspec(naked) gdDestroyHeadWindow_hack() {
__asm {
push label;
push ebx;
push ecx;
push edx;
push edi;
push ebp;
jmp EndSpeechHookRet;
label:
xor eax, eax;
push eax;
push eax;
push eax;
push eax;
push eax;
call SetHeadTex;
call Graphics::SetDefaultTechnique;
pop ebp;
pop edi;
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static void __declspec(naked) TransTalkHook() {
__asm {
cmp overridden, 0;
jne skip;
jmp fo::funcoffs::talk_to_translucent_trans_buf_to_buf_;
test showHighlights, 0xFF;
jnz skip;
jmp fo::funcoffs::talk_to_translucent_trans_buf_to_buf_;
skip:
retn 0x18;
}
}
void TalkingHeads::init() {
if (GetConfigInt("Graphics", "Use32BitHeadGraphics", 0)) {
HookCall(0x44AFB4, &TransTalkHook);
HookCall(0x44B00B, &TransTalkHook);
MakeJump(0x44AD01, DrawFrmHook);
MakeJump(0x447294, EndSpeechHook);
static void __declspec(naked) gdialogInitFromScript_hook() {
__asm {
cmp dword ptr ds:[FO_VAR_dialogue_head], -1;
jnz noScroll;
jmp fo::funcoffs::tile_scroll_to_;
noScroll:
retn;
}
}
static void TalkingHeadsInit() {
if (!Graphics::GPUBlt) return;
HookCalls(TransTalkHook, {0x44AFB4, 0x44B00B});
MakeJump(0x44AD01, gdDisplayFrame_hack); // Draw Frm
MakeJump(0x4472F8, gdDestroyHeadWindow_hack);
// Load highlights texture
char buf[MAX_PATH];
sprintf_s(buf, "%s\\art\\stex\\highlight.png", fo::var::patches);
if (!FAILED(D3DXCreateTextureFromFileExA(d3d9Device, buf, 0, 0, 1, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, 0, 0, &texHighlight))) {
Graphics::SetHighlightTexture(texHighlight);
}
}
void TalkingHeads::init() {
// Disable centering the screen if NPC has talking head
HookCall(0x445224, gdialogInitFromScript_hook);
if (Graphics::mode && GetConfigInt("Graphics", "Use32BitHeadGraphics", 0)) {
LoadGameHook::OnAfterGameInit() += TalkingHeadsInit;
}
}
void TalkingHeads::exit() {
if (!texMap.empty()) {
for (auto& frm : texMap) {
for (int i = 0; i < frm.second.frames; i++) {
frm.second.textures[i]->Release();
}
delete[] frm.second.textures;
}
}
if (texHighlight) texHighlight->Release();
}
}
+1
View File
@@ -7,6 +7,7 @@ class TalkingHeads : public Module {
public:
const char* name() { return "TalkingHeads"; }
void init();
void exit() override;
};
}