[HRP] Fixed the screenshot for sfall DX9 mode

* Now the screenshots are saved in PNG format.

[HRP] Adjusted the position of movie subtitles.

[TEST] Changed the DirectX 9 device creation mode.
This commit is contained in:
NovaRain
2022-02-10 12:13:26 +08:00
parent a7e6365d84
commit 1c9866a693
10 changed files with 184 additions and 54 deletions
+2 -1
View File
@@ -95,6 +95,7 @@ Use32BitHeadGraphics=0
;Set to 1 to automatically search for alternative AVI video files when Fallout tries to play the game movies
;Set to 2 to force AVI videos to fit the screen width
;Requires DX9 graphics mode
;The recommended video codec is Xvid
AllowDShowMovies=0
;Fade effect time percentage modifier
@@ -331,7 +332,7 @@ Movie17=credits.mve
;To change the limit of the distance away from the player to which you're allowed to scroll the local maps, uncomment the next two lines
;Defaults are 480 in the x direction and 400 in the y direction.
;Not compatible with the res patch!
;Not compatible with the hi-res patch!
;LocalMapXLimit=480
;LocalMapYLimit=400
+1 -1
View File
@@ -183,7 +183,7 @@ void Render::init() {
// Custom implementation of the GNW_win_refresh function
sf::MakeJump(0x4D6FD9, GNW_win_refresh_hack, 1);
// Replace _screendump_buf with _screen_buffer for creating screenshots
// Replace _screendump_buf with _screen_buffer for creating screenshots (for sfall DX9 mode with HRP by Mash)
sf::SafeWriteBatch<DWORD>(FO_VAR_screen_buffer, {0x4C8FD1, 0x4C900D});
}
+3 -3
View File
@@ -32,7 +32,7 @@ static void __fastcall SetMovieSize() {
if (MoviesScreen::MOVIE_SIZE == 1 || bW > sWidth || bH > sHeight) {
long aspectW = sWidth;
long aspectH = sHeight - subtitleHeight;
long aspectH = sHeight;
long x = 0;
long y = 0;
Image::GetAspectSize(bW, bH, &x, &y, aspectW, aspectH);
@@ -100,7 +100,7 @@ static void __cdecl movieShowFrame(IDirectDrawSurface7* surface, int bW, int bH,
if (!win) return;
DDSURFACEDESC2 desc;
desc.dwSize = sizeof(DDSURFACEDESC);
desc.dwSize = sizeof(DDSURFACEDESC2);
surface->Lock(0, &desc, DDLOCK_WAIT, 0);
BYTE* dst = win->surface + movieToSize.left + (movieToSize.top * win->width);
@@ -117,7 +117,7 @@ static long __fastcall SubtitleAdjustPosition(long yTop, long yBottom) {
if (MoviesScreen::MOVIE_SIZE == 0 || Setting::ScreenHeight() == 480) return yTop;
long y = fo::var::getInt(FO_VAR_lastMovieY);
y += fo::var::getInt(FO_VAR_lastMovieH) + 4;
y += fo::var::getInt(FO_VAR_lastMovieH);// + 4;
if (yTop >= y) return yTop;
+129 -5
View File
@@ -21,6 +21,7 @@
#include "..\WinProc.h"
#include "LoadGameHook.h"
#include "ScriptShaders.h"
#include "Movies.h"
#include "SubModules\DirectDraw.h"
#include "SubModules\WindowRender.h"
@@ -69,6 +70,7 @@ bool Graphics::IsWindowedMode;
bool Graphics::PlayAviMovie = false;
bool Graphics::AviMovieWidthFit = false;
static bool dShowMovies;
bool DeviceLost = false;
static char textureFilter; // 1 - auto, 2 - force
@@ -185,12 +187,14 @@ static void ResetDevice(bool create) {
static D3DFORMAT textureFormat = D3DFMT_X8R8G8B8;
if (create) {
DWORD mThreadFlags = (dShowMovies) ? D3DCREATE_MULTITHREADED : 0;
dlog("Creating D3D9 Device...", DL_MAIN);
if (FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_PUREDEVICE | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &params, &d3d9Device))) {
if (FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_HARDWARE_VERTEXPROCESSING | mThreadFlags, &params, &d3d9Device))) { //D3DCREATE_PUREDEVICE | D3DCREATE_FPU_PRESERVE
MessageBoxA(window, "Failed to create hardware vertex processing device.\nUsing software vertex processing instead.",
"sfall DX9", MB_TASKMODAL | MB_ICONWARNING);
"sfall DirectX 9", MB_TASKMODAL | MB_ICONWARNING);
software = true;
d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &params, &d3d9Device);
d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING | mThreadFlags, &params, &d3d9Device); //D3DCREATE_FPU_PRESERVE
}
D3DCAPS9 caps;
@@ -224,7 +228,7 @@ static void ResetDevice(bool create) {
textureFormat = (A8IsSupported) ? D3DFMT_A8 : D3DFMT_L8; // D3DFMT_A8 - not supported on some older video cards
} else {
MessageBoxA(window, "Failed to create shader effects.\nSwitching to CPU for the palette conversion.",
"sfall DX9", MB_TASKMODAL | MB_ICONWARNING);
"sfall DirectX 9", MB_TASKMODAL | MB_ICONWARNING);
if (mainTex) SAFERELEASE(mainTex); // release D3DFMT_A8 format texture
Graphics::GPUBlt = 0;
A8IsSupported = false;
@@ -236,7 +240,7 @@ static void ResetDevice(bool create) {
d3d9Device->CreateTexture(ResWidth, ResHeight, 1, 0, textureFormat, D3DPOOL_SYSTEMMEM, &mainTex, 0);
MessageBoxA(window, "Texture format error.\nGPU does not support the D3DFMT_L8 texture format.\nNow CPU is used to convert the palette.\n"
"Set 'GPUBlt' option to CPU to bypass this warning message.",
"sfall DX9", MB_TASKMODAL | MB_ICONWARNING);
"sfall DirectX 9", MB_TASKMODAL | MB_ICONWARNING);
Graphics::GPUBlt = 0;
}
if (Graphics::GPUBlt == 0) palette = new DirectDraw::PALCOLOR[256];
@@ -649,7 +653,9 @@ public:
int height = mveDesc.dwHeight;
if (d != 0) { // scale
//mveScaleSurface = (BYTE*)fo::var::getInt(FO_VAR_screen_buffer);
if (!mveScaleSurface) mveScaleSurface = new BYTE[ResWidth * ResHeight];
width = dst->right - dst->left;
height = dst->bottom - dst->top;
@@ -1186,6 +1192,118 @@ void Graphics::BackgroundClearColor(long indxColor) {
std::memset(desc.lpSurface, indxColor, ResWidth * ResHeight);
}
}
/*
#pragma pack(push, 1)
struct BMPHEADER {
BITMAPFILEHEADER bFile;
BITMAPINFOHEADER bInfo;
};
#pragma pack(pop)
*/
long __stdcall SaveScreen(const char* file) {
IDirect3DSurface9* surface;
d3d9Device->CreateOffscreenPlainSurface(gWidth, gHeight, D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, 0);
d3d9Device->GetRenderTargetData(backBuffer, surface);
LPD3DXBUFFER buffer;
D3DXCreateBuffer(gWidth * gHeight * 2, &buffer);
D3DXSaveSurfaceToFileInMemory(&buffer, D3DXIFF_PNG, surface, 0, 0);
//D3DXSaveSurfaceToFileA(file, D3DXIFF_PNG, surface, 0, 0); // slow save
HANDLE hFile = CreateFileA(file, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
bool resultOK = (hFile != INVALID_HANDLE_VALUE);
if (resultOK) {
DWORD dwWritten;
WriteFile(hFile, buffer->GetBufferPointer(), buffer->GetBufferSize(), &dwWritten, 0);
CloseHandle(hFile);
}
surface->Release();
buffer->Release();
/* BMP 24-bit
long bmpExtraSize = gWidth * 3 % 4;
if (bmpExtraSize != 0) bmpExtraSize = 4 - bmpExtraSize;
DWORD sizeImage = gWidth * gHeight * 3;
sizeImage += gHeight * bmpExtraSize;
BMPHEADER bmpHeader;
std::memset(&bmpHeader, 0, sizeof(BMPHEADER));
bmpHeader.bFile.bfType = 0x4D42;
bmpHeader.bFile.bfOffBits = sizeof(BMPHEADER);
bmpHeader.bInfo.biSize = sizeof(BITMAPINFOHEADER);
bmpHeader.bInfo.biWidth = gWidth;
bmpHeader.bInfo.biHeight = 0 - gHeight;
bmpHeader.bInfo.biPlanes = 1;
bmpHeader.bInfo.biBitCount = 24;
bmpHeader.bInfo.biCompression = BI_RGB;
bmpHeader.bInfo.biSizeImage = sizeImage;
BYTE* bmpImageData = new BYTE[sizeImage];
BYTE* dData = bmpImageData;
D3DLOCKED_RECT lockRect;
surface->LockRect(&lockRect, 0, 0);
BYTE* lockData = (BYTE*)lockRect.pBits;
// 32-bit to 24-bit
for (size_t h = 0; h < gHeight; h++) {
BYTE* sData = lockData;
for (size_t x = 0; x < gWidth; x++) {
*dData++ = *sData++;
*dData++ = *sData++;
*dData++ = *sData++;
sData++;
}
lockData += lockRect.Pitch;
dData += bmpExtraSize;
}
surface->UnlockRect();
surface->Release();
HANDLE hFile = CreateFileA(file, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
bool resultOK = (hFile != INVALID_HANDLE_VALUE);
if (resultOK) {
DWORD dwWritten;
WriteFile(hFile, &bmpHeader, sizeof(BMPHEADER), &dwWritten, 0);
WriteFile(hFile, bmpImageData, sizeImage, &dwWritten, 0);
CloseHandle(hFile);
}
delete[] bmpImageData;*/
return (resultOK) ? 0 : 1;
}
long __stdcall game_screendump_hook() {
char fileName[16];
for (size_t i = 0; i < 100000; i++) {
std::sprintf(fileName, "scr%.5d.png", i); // scr#####.png
HANDLE hFile = CreateFileA(fileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) {
return SaveScreen(fileName);
}
CloseHandle(hFile);
}
return 1;
}
static __declspec(naked) void dump_screen_hack_replacement() {
__asm {
push ecx;
push edx;
call fo::funcoffs::game_screendump_; // call ds:[FO_VAR_screendump_func];
pop edx;
pop ecx;
retn;
}
}
void Graphics::init() {
int gMode = IniReader::GetConfigInt("Graphics", "Mode", 0);
@@ -1215,6 +1333,10 @@ void Graphics::init() {
MakeJump(fo::funcoffs::GNW95_SetPaletteEntries_ + 1, GNW95_SetPaletteEntries_replacement); // 0x4CB311
MakeJump(fo::funcoffs::GNW95_SetPalette_, GNW95_SetPalette_replacement); // 0x4CB568
// Replace the screenshot saving implementation for sfall DirectX 9
HookCall(0x443EF3, game_screendump_hook);
MakeJump(0x4C8F4C, dump_screen_hack_replacement);
if (HRP::Setting::VersionIsValid) {
// Patch HRP to show the mouse cursor over the window title
if (Graphics::mode == 5) SafeWrite8(HRP::Setting::GetAddress(0x10027142), CodeType::JumpShort);
@@ -1226,6 +1348,8 @@ void Graphics::init() {
textureFilter = IniReader::GetConfigInt("Graphics", "TextureFilter", 1);
dlogr(" Done", DL_INIT);
dShowMovies = Movies::DirectShowMovies();
LoadGameHook::OnGameReset() += []() {
ForceGraphicsRefresh(0); // disable refresh
};
-1
View File
@@ -41,7 +41,6 @@ static DWORD __fastcall CalcDeathAnimHook_Script(DWORD damage, fo::GameObject* t
DWORD animDeath = fo::func::pick_death(attacker, target, weapon, damage, animation, hitBack); // vanilla pick death
if (registerHookDeathAnim2) {
//argCount = 5;
args[4] = animDeath;
RunHookScript(HOOK_DEATHANIM2);
@@ -1,4 +1,3 @@
#include "..\..\FalloutEngine\Fallout2.h"
#include "..\..\SafeWrite.h"
#include "..\HookScripts.h"
+17 -15
View File
@@ -2,21 +2,23 @@
namespace sfall
{
void InitMiscHookScripts();
void SourceUseSkillOnInit();
void Inject_BarterPriceHook();
void Inject_UseSkillOnHook();
void Inject_UseSkillHook();
void Inject_StealCheckHook();
void Inject_SneakCheckHook();
void Inject_WithinPerceptionHook();
void Inject_CarTravelHook();
void Inject_SetGlobalVarHook();
void Inject_RestTimerHook();
void Inject_ExplosiveTimerHook();
void Inject_EncounterHook();
void Inject_RollCheckHook();
void InitMiscHookScripts();
void SourceUseSkillOnInit();
void Inject_BarterPriceHook();
void Inject_UseSkillOnHook();
void Inject_UseSkillHook();
void Inject_StealCheckHook();
void Inject_SneakCheckHook();
void Inject_WithinPerceptionHook();
void Inject_CarTravelHook();
void Inject_SetGlobalVarHook();
void Inject_RestTimerHook();
void Inject_ExplosiveTimerHook();
void Inject_EncounterHook();
void Inject_RollCheckHook();
long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);
long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);
}
+13 -11
View File
@@ -2,17 +2,19 @@
namespace sfall
{
void InitObjectHookScripts();
void Inject_UseObjOnHook();
void Inject_UseObjHook();
void Inject_UseAnimateObjHook();
void Inject_DescriptionObjHook();
void Inject_SetLightingHook();
void Inject_ScriptProcedureHook();
void Inject_ScriptProcedureHook2();
void Inject_AdjustPoisonHook();
void Inject_AdjustRadsHook();
void InitObjectHookScripts();
void Inject_UseObjOnHook();
void Inject_UseObjHook();
void Inject_UseAnimateObjHook();
void Inject_DescriptionObjHook();
void Inject_SetLightingHook();
void Inject_ScriptProcedureHook();
void Inject_ScriptProcedureHook2();
void Inject_AdjustPoisonHook();
void Inject_AdjustRadsHook();
long UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target);
long UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target);
}
+17 -16
View File
@@ -85,6 +85,7 @@ private:
lpAllocInfo->dwFlags |= VMR9AllocFlag_TextureSurface; // | VMR9AllocFlag_DXVATarget;
lpAllocInfo->Pool = D3DPOOL_SYSTEMMEM;
//lpAllocInfo->Format = D3DFMT_X8R8G8B8;
// Ask the VMR-9 to allocate the surfaces for us.
for (auto &surface : surfaces) SAFERELEASE(surface);
@@ -557,6 +558,22 @@ static __declspec(naked) void LostFocus() {
}
}
/*
WIP: Task
Implement subtitle output from the need to play an mve file in the background.
*/
bool Movies::DirectShowMovies() {
int allowDShowMovies = IniReader::GetConfigInt("Graphics", "AllowDShowMovies", 0);
if (allowDShowMovies > 0) {
Graphics::AviMovieWidthFit = (allowDShowMovies >= 2);
MakeJump(0x44E690, gmovie_play_hack);
HookCall(0x44E993, gmovie_play_hook_stop);
/* NOTE: At this address 0x487781 (movieStart_), HRP by Mash changes the callback procedure to display mve frames. */
return true;
}
return false;
}
void Movies::init() {
dlog("Applying movie patch.", DL_INIT);
@@ -576,25 +593,9 @@ void Movies::init() {
IniReader::GetConfigString("Misc", optName, "", &MoviePaths[index], 65);
}
}
dlog(".", DL_INIT);
SafeWriteBatch<DWORD>((DWORD)MoviePtrs, {0x44E6AE, 0x44E721, 0x44E75E, 0x44E78A}); // gmovie_play_
MakeCall(0x44E896, gmovie_play_hack_subpal, 2);
MakeCall(0x45A1C9, op_play_gmovie_hack);
dlog(".", DL_INIT);
/*
WIP: Task
Implement subtitle output from the need to play an mve file in the background.
*/
if (Graphics::mode >= 4) {
int allowDShowMovies = IniReader::GetConfigInt("Graphics", "AllowDShowMovies", 0);
if (allowDShowMovies > 0) {
Graphics::AviMovieWidthFit = (allowDShowMovies >= 2);
MakeJump(0x44E690, gmovie_play_hack);
HookCall(0x44E993, gmovie_play_hook_stop);
/* NOTE: At this address 0x487781 (movieStart_), HRP changes the callback procedure to display mve frames. */
}
}
dlogr(" Done", DL_INIT);
DWORD days = SimplePatch<DWORD>(0x4A36EC, "Misc", "MovieTimer_artimer4", 360, 0);
+2
View File
@@ -28,6 +28,8 @@ public:
const char* name() { return "Movies"; }
void init();
//void exit() override;
static bool DirectShowMovies();
};
static constexpr int MaxMovies = 32;