Fixed the screenshot for sfall DX9 mode

* Now the screenshots are saved in PNG format.

[TEST] Changed the DirectX 9 device creation mode.
This commit is contained in:
NovaRain
2022-02-10 13:39:31 +08:00
parent 6e10f624a3
commit 6b14d4f86e
7 changed files with 150 additions and 27 deletions
+1
View File
@@ -76,6 +76,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
@@ -235,6 +235,7 @@ FUNC(game_exit_, 0x442C34)
FUNC(game_get_global_var_, 0x443C68)
FUNC(game_help_, 0x443F74)
FUNC(game_reset_, 0x442B84)
FUNC(game_screendump_, 0x443EF0)
FUNC(game_set_global_var_, 0x443C98)
FUNC(game_time_, 0x4A3330)
FUNC(game_time_date_, 0x4A3338)
+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)
const DWORD scrdumpBufAddr[] = {0x4C8FD1, 0x4C900D};
sf::SafeWriteBatch<DWORD>(FO_VAR_screen_buffer, scrdumpBufAddr);
}
+128 -6
View File
@@ -22,6 +22,7 @@
#include "..\version.h"
#include "LoadGameHook.h"
#include "ScriptShaders.h"
#include "Movies.h"
#include "..\Game\render.h"
@@ -64,6 +65,7 @@ DWORD Graphics::mode;
bool Graphics::PlayAviMovie = false;
bool Graphics::AviMovieWidthFit = false;
static bool dShowMovies;
static bool DeviceLost = false;
static char textureFilter; // 1 - auto, 2 - force
@@ -199,12 +201,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;
@@ -234,7 +238,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;
@@ -246,7 +250,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 PALCOLOR[256];
@@ -1333,6 +1337,118 @@ static __declspec(naked) void palette_fade_to_hook() {
jmp fo::funcoffs::fadeSystemPalette_;
}
}
/*
#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() {
Graphics::mode = IniReader::GetConfigInt("Graphics", "Mode", 0);
@@ -1347,7 +1463,7 @@ void Graphics::init() {
if (!h) {
dlogr(" Failed", DL_INIT);
MessageBoxA(0, "You have selected DirectX graphics mode, but " _DLL_NAME " is missing.\n"
"Switch back to mode 0, or install an up to date version of DirectX 9.0c.", 0, MB_TASKMODAL | MB_ICONERROR);
"Switch back to DirectDraw (Mode=0), or install an up to date version of DirectX 9.0c.", 0, MB_TASKMODAL | MB_ICONERROR);
#undef _DLL_NAME
ExitProcess(-1);
}
@@ -1359,6 +1475,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 (hrpVersionValid) {
// Patch HRP to show the mouse cursor over the window title
if (Graphics::mode == 5) SafeWrite8(HRPAddress(0x10027142), CodeType::JumpShort);
@@ -1369,6 +1489,8 @@ void Graphics::init() {
textureFilter = IniReader::GetConfigInt("Graphics", "TextureFilter", 1);
dlogr(" Done", DL_INIT);
dShowMovies = Movies::DirectShowMovies();
}
if (Graphics::mode == 5) {
moveWindowKey[0] = IniReader::GetConfigInt("Input", "WindowScrollKey", 0);
-4
View File
@@ -239,10 +239,6 @@ void HookScripts::HookScriptClear() {
void HookScripts::init() {
injectAllHooks = isDebug && (IniReader::GetIntDefaultConfig("Debugging", "InjectAllGameHooks", 0) != 0);
if (injectAllHooks) dlogr("Injecting all game hooks.", DL_HOOK|DL_INIT);
// for (int i = 0; i < HOOK_COUNT; i++) {
// dlog_f("injectHooks[%d]: id(%d), func(0x%x), state(%d)\n", DL_INIT, i, injectHooks[i].id, injectHooks[i].inject, injectHooks[i].injectState);
// }
}
}
+17 -16
View File
@@ -86,6 +86,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 (std::vector<IDirect3DSurface9*>::iterator it = surfaces.begin(); it != surfaces.end(); ++it) {
@@ -562,6 +563,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 changes the callback procedure to display mve frames. */
return true;
}
return false;
}
void Movies::init() {
dlog("Applying movie patch.", DL_INIT);
@@ -581,26 +598,10 @@ void Movies::init() {
IniReader::GetConfigString("Misc", optName, "", &MoviePaths[index], 65);
}
}
dlog(".", DL_INIT);
const DWORD movieListAddr[] = {0x44E6AE, 0x44E721, 0x44E75E, 0x44E78A}; // gmovie_play_
SafeWriteBatch<DWORD>((DWORD)MoviePtrs, movieListAddr);
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 != 0) {
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
@@ -26,6 +26,8 @@ public:
static const char* name() { return "Movies"; }
static void init();
//static void exit();
static bool DirectShowMovies();
};
static const int MaxMovies = 32;