From 648bffedc6b2decc20b3f8619fce255c00985c9e Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 28 Jul 2020 17:08:44 +0800 Subject: [PATCH] Added volume control support for avi movies Fixed implementation bugs when playing avi movies. Added a couple of engine variable pointers. --- artifacts/ddraw.ini | 3 +- sfall/BarBoxes.cpp | 2 +- sfall/FalloutEngine.cpp | 46 +++++++++++++-- sfall/FalloutEngine.h | 21 ++++++- sfall/Graphics.cpp | 60 ++++++++++---------- sfall/Graphics.h | 7 ++- sfall/Interface.cpp | 4 +- sfall/MiscPatches.cpp | 2 +- sfall/Movies.cpp | 122 ++++++++++++++++++++++++---------------- sfall/SafeWrite.h | 1 + 10 files changed, 177 insertions(+), 91 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index c659bc05..7a40328d 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -267,7 +267,8 @@ DataLoadOrderPatch=1 ;FemaleDefaultModel=hfjmps ;To change the various ingame movies, modify the next 17 lines -;Most of these can also be changed ingame via script. +;You can also define additional movies by adding Movie18 - Movie32 lines +;Most of these can also be changed ingame via script Movie1=iplogo.mve Movie2=intro.mve Movie3=elder.mve diff --git a/sfall/BarBoxes.cpp b/sfall/BarBoxes.cpp index 1716e558..9ec74721 100644 --- a/sfall/BarBoxes.cpp +++ b/sfall/BarBoxes.cpp @@ -185,7 +185,7 @@ void __stdcall BarBoxes_SetText(int box, const char* text, DWORD color) { clr = *ptr_GoodColor; break; case 7: - clr = *(BYTE*)_DullPinkColor; + clr = *ptr_DullPinkColor; break; default: clr = *ptr_GreenColor; diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index aeb72138..183bd247 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -61,6 +61,7 @@ TGameObj** ptr_dialog_target = reinterpret_cast(_dialog_tar DWORD* ptr_dialog_target_is_party = reinterpret_cast(_dialog_target_is_party); const DWORD* ptr_dialogueBackWindow = reinterpret_cast(_dialogueBackWindow); DWORD* ptr_drugInfoList = reinterpret_cast(_drugInfoList); +BYTE* ptr_DullPinkColor = reinterpret_cast(_DullPinkColor); const DWORD* ptr_edit_win = reinterpret_cast(_edit_win); DWORD* ptr_Educated = reinterpret_cast(_Educated); DWORD* ptr_elevation = reinterpret_cast(_elevation); @@ -145,6 +146,7 @@ DWORD* ptr_name_font = reinterpret_cast(_name_font); DWORD* ptr_name_sort_list = reinterpret_cast(_name_sort_list); DWORD* ptr_num_game_global_vars = reinterpret_cast(_num_game_global_vars); DWORD* ptr_num_map_global_vars = reinterpret_cast(_num_map_global_vars); +DWORD* ptr_num_windows = reinterpret_cast(_num_windows); TGameObj** ptr_obj_dude = reinterpret_cast(_obj_dude); DWORD* ptr_objectTable = reinterpret_cast(_objectTable); DWORD* ptr_objItemOutlineState = reinterpret_cast(_objItemOutlineState); @@ -226,7 +228,7 @@ DWORD* ptr_title_font = reinterpret_cast(_title_font); DWORD* ptr_trait_data = reinterpret_cast(_trait_data); DWORD* ptr_view_page = reinterpret_cast(_view_page); DWORD* ptr_wd_obj = reinterpret_cast(_wd_obj); -DWORD* ptr_window = reinterpret_cast(_window); // total 50 WINinfo* +WINinfo** ptr_window = reinterpret_cast(_window); // array of 50 WINinfo* BYTE* ptr_WhiteColor = reinterpret_cast(_WhiteColor); DWORD* ptr_wmAreaInfoList = reinterpret_cast(_wmAreaInfoList); const DWORD* ptr_wmBkWin = reinterpret_cast(_wmBkWin); @@ -426,6 +428,7 @@ const DWORD gmouse_set_cursor_ = 0x44C840; const DWORD gmovie_play_ = 0x44E690; const DWORD GNW_do_bk_process_ = 0x4C8D1C; const DWORD GNW_find_ = 0x4D7888; +const DWORD GNW_win_refresh_ = 0x4D6FD8; const DWORD GNW95_process_message_ = 0x4C9CF0; const DWORD gsnd_build_weapon_sfx_name_ = 0x451760; const DWORD gsound_background_pause_ = 0x450B50; @@ -557,6 +560,7 @@ const DWORD mouse_hide_ = 0x4CA534; const DWORD mouse_in_ = 0x4CA8C8; const DWORD mouse_show_ = 0x4CA34C; const DWORD move_inventory_ = 0x474708; +const DWORD movieStop_ = 0x487150; const DWORD movieUpdate_ = 0x487BEC; const DWORD new_obj_id_ = 0x4A386C; const DWORD NixHotLines_ = 0x4999C0; @@ -741,8 +745,10 @@ const DWORD win_disable_button_ = 0x4D94D0; const DWORD win_draw_ = 0x4D6F5C; const DWORD win_draw_rect_ = 0x4D6F80; const DWORD win_enable_button_ = 0x4D9474; +const DWORD win_fill_ = 0x4D6CC8; const DWORD win_get_buf_ = 0x4D78B0; const DWORD win_get_top_win_ = 0x4D78CC; +const DWORD win_height_ = 0x4D7934; const DWORD win_hide_ = 0x4D6E64; const DWORD win_line_ = 0x4D6B24; const DWORD win_print_ = 0x4D684C; @@ -750,6 +756,7 @@ const DWORD win_register_button_ = 0x4D8260; const DWORD win_register_button_disable_ = 0x4D8674; const DWORD win_register_button_sound_func_ = 0x4D87F8; const DWORD win_show_ = 0x4D6DAC; +const DWORD win_width_ = 0x4D7918; const DWORD windowDisplayBuf_ = 0x4B8EF0; const DWORD windowDisplayTransBuf_ = 0x4B8F64; const DWORD windowGetBuffer_ = 0x4B82DC; @@ -1470,6 +1477,10 @@ FrmFrameData* __fastcall FramePtr(FrmHeaderData* frm, long frame, long direction WRAP_WATCOM_FCALL3(frame_ptr_, frm, frame, direction) } +void __fastcall GNWWinRefresh(WINinfo* win, BoundRect* rect, long* buffer) { + WRAP_WATCOM_FCALL3(GNW_win_refresh_, win, rect, buffer) +} + void __stdcall MapDirErase(const char* folder, const char* ext) { WRAP_WATCOM_CALL2(MapDirErase_, folder, ext) } @@ -1805,9 +1816,9 @@ long GetScriptLocalVars(long sid) { // Returns window ID by x/y coordinate (hidden windows are ignored) long __fastcall GetTopWindowID(long xPos, long yPos) { WINinfo* win = nullptr; - long countWin = *(DWORD*)_num_windows - 1; + long countWin = *ptr_num_windows - 1; for (int n = countWin; n >= 0; n--) { - win = (WINinfo*)ptr_window[n]; + win = ptr_window[n]; if (xPos >= win->wRect.left && xPos <= win->wRect.right && yPos >= win->wRect.top && yPos <= win->wRect.bottom) { if (!(win->flags & WinFlags::Hidden)) { break; @@ -2004,6 +2015,24 @@ void DrawToSurface(long width, long height, long fromX, long fromY, long fromWid } } +// Fills the specified non-scripted interface window with black color +void ClearWindow(DWORD winID, bool refresh) { + __asm { + xor ebx, ebx; + push ebx; + mov eax, winID; + call win_height_; + push eax; + mov eax, winID; + call win_width_; + mov ecx, eax; + mov edx, ebx; + mov eax, winID; //dword ptr ds:[_GNWWin]; + call win_fill_; + } + if (refresh) WinDraw(winID); +} + //--------------------------------------------------------- // print text to surface void __stdcall PrintText(char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface) { @@ -2110,12 +2139,21 @@ DWORD __stdcall GetMaxCharWidth() { // return charWidth; } -void __stdcall RedrawObject(TGameObj* obj) { +void RedrawObject(TGameObj* obj) { BoundRect rect; ObjBound(obj, &rect); TileRefreshRect(&rect, obj->elevation); } +// Redraws all interface windows +void RefreshGNW() { + *(DWORD*)_doing_refresh_all = 1; + for (size_t i = 0; i < *ptr_num_windows; i++) { + GNWWinRefresh(ptr_window[i], ptr_scr_size, 0); + } + *(DWORD*)_doing_refresh_all = 0; +} + /////////////////////////////////////////////////////////////////UNLISTED FRM FUNCTIONS////////////////////////////////////////////////////////////// static bool LoadFrmHeader(UNLSTDfrm *frmHeader, DbFile* frmStream) { diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index 02a3f969..4b52fc29 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -85,6 +85,7 @@ #define _dialogueWindow 0x518744 #define _display_win 0x631E4C #define _displayMapList 0x41B560 +#define _doing_refresh_all 0x6ADF38 #define _dropped_explosive 0x5190E0 #define _drugInfoList 0x5191CC #define _edit_win 0x57060C @@ -119,6 +120,7 @@ #define _gmouse_current_cursor 0x518C0C #define _gmovie_played_list 0x596C78 #define _GNW_win_init_flag 0x51E3E0 +#define _GNWWin 0x5195B8 #define _gsound_initialized 0x518E30 #define _gsound_speech_tag 0x518E54 #define _hit_location_penalty 0x510954 @@ -351,6 +353,7 @@ extern TGameObj** ptr_dialog_target; extern DWORD* ptr_dialog_target_is_party; extern const DWORD* ptr_dialogueBackWindow; extern DWORD* ptr_drugInfoList; +extern BYTE* ptr_DullPinkColor; extern const DWORD* ptr_edit_win; extern DWORD* ptr_Educated; extern DWORD* ptr_elevation; @@ -435,6 +438,7 @@ extern DWORD* ptr_name_font; extern DWORD* ptr_name_sort_list; extern DWORD* ptr_num_game_global_vars; extern DWORD* ptr_num_map_global_vars; +extern DWORD* ptr_num_windows; extern TGameObj** ptr_obj_dude; extern DWORD* ptr_objectTable; extern DWORD* ptr_objItemOutlineState; @@ -517,7 +521,7 @@ extern DWORD* ptr_title_font; extern DWORD* ptr_trait_data; extern DWORD* ptr_view_page; extern DWORD* ptr_wd_obj; -extern DWORD* ptr_window; // total 50 WINinfo* +extern WINinfo** ptr_window; // array of 50 WINinfo* extern BYTE* ptr_WhiteColor; extern DWORD* ptr_wmAreaInfoList; extern const DWORD* ptr_wmBkWin; @@ -700,6 +704,7 @@ extern const DWORD gmouse_set_cursor_; extern const DWORD gmovie_play_; extern const DWORD GNW_do_bk_process_; extern const DWORD GNW_find_; +extern const DWORD GNW_win_refresh_; extern const DWORD GNW95_process_message_; extern const DWORD gsnd_build_weapon_sfx_name_; extern const DWORD gsound_background_pause_; @@ -837,6 +842,7 @@ extern const DWORD mouse_hide_; extern const DWORD mouse_in_; extern const DWORD mouse_show_; extern const DWORD move_inventory_; +extern const DWORD movieStop_; extern const DWORD movieUpdate_; extern const DWORD new_obj_id_; extern const DWORD NixHotLines_; @@ -1024,8 +1030,10 @@ extern const DWORD win_disable_button_; extern const DWORD win_draw_; extern const DWORD win_draw_rect_; extern const DWORD win_enable_button_; +extern const DWORD win_fill_; extern const DWORD win_get_buf_; extern const DWORD win_get_top_win_; +extern const DWORD win_height_; extern const DWORD win_hide_; extern const DWORD win_line_; extern const DWORD win_print_; @@ -1033,6 +1041,7 @@ extern const DWORD win_register_button_; extern const DWORD win_register_button_disable_; extern const DWORD win_register_button_sound_func_; extern const DWORD win_show_; +extern const DWORD win_width_; extern const DWORD windowDisplayBuf_; extern const DWORD windowDisplayTransBuf_; extern const DWORD windowGetBuffer_; @@ -1336,6 +1345,8 @@ long __stdcall NewObjId(); FrmFrameData* __fastcall FramePtr(FrmHeaderData* frm, long frame, long direction); +void __fastcall GNWWinRefresh(WINinfo* win, BoundRect* rect, long* buffer); + void __stdcall MapDirErase(const char* folder, const char* ext); void __stdcall MouseGetPosition(long* outX, long* outY); @@ -1501,6 +1512,9 @@ void DrawToSurface(long width, long height, long fromX, long fromY, long fromWid void DrawToSurface(long width, long height, long fromX, long fromY, long fromWidth, BYTE* fromSurf, long toX, long toY, long toWidth, long toHeight, BYTE* toSurf); +// Fills the specified non-scripted interface window with black color +void ClearWindow(DWORD winID, bool refresh = true); + // Print text to surface void __stdcall PrintText(char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface); void __stdcall PrintTextFM(char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface); @@ -1526,6 +1540,9 @@ DWORD __stdcall GetCharGapWidth(); DWORD __stdcall GetMaxCharWidth(); // Redraw the given object on screen (does not always redraws the whole object) -void __stdcall RedrawObject(TGameObj* obj); +void RedrawObject(TGameObj* obj); + +// Redraws all interface windows +void RefreshGNW(); UNLSTDfrm *LoadUnlistedFrm(char *frmName, unsigned int folderRef); diff --git a/sfall/Graphics.cpp b/sfall/Graphics.cpp index a3341db7..7449d8c7 100644 --- a/sfall/Graphics.cpp +++ b/sfall/Graphics.cpp @@ -84,7 +84,7 @@ static DWORD ShaderVersion; IDirect3D9* d3d9 = 0; IDirect3DDevice9* d3d9Device = 0; -static IDirect3DTexture9* Tex = 0; +static IDirect3DTexture9* mainTex = 0; static IDirect3DTexture9* sTex1 = 0; static IDirect3DTexture9* sTex2 = 0; @@ -378,8 +378,8 @@ 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) { - d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &Tex, 0); + if (d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_DYNAMIC, GPUBlt ? D3DFMT_A8 : D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &mainTex, 0) != D3D_OK) { + d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &mainTex, 0); GPUBlt = 0; dlog(" Error: D3DFMT_A8 unsupported texture format. Now CPU is used to convert the palette.", DL_MAIN); } @@ -387,7 +387,7 @@ static void ResetDevice(bool createNew) { d3d9Device->CreateTexture(ResWidth, ResHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &sTex2, 0); if (GPUBlt) { d3d9Device->CreateTexture(256, 1, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &gpuPalette, 0); - gpuBltEffect->SetTexture(gpuBltBuf, Tex); + gpuBltEffect->SetTexture(gpuBltBuf, mainTex); gpuBltEffect->SetTexture(gpuBltPalette, gpuPalette); } @@ -421,7 +421,7 @@ static void ResetDevice(bool createNew) { vBuffer2->Unlock(); d3d9Device->SetFVF(_VERTEXFORMAT); - d3d9Device->SetTexture(0, Tex); + d3d9Device->SetTexture(0, mainTex); d3d9Device->SetStreamSource(0, vBuffer, 0, sizeof(VertexFormat)); //d3d9Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); // default false @@ -484,7 +484,7 @@ static void Present() { dlogr("Present: DEVICELOST", DL_MAIN); #endif d3d9Device->SetTexture(0, 0); - SAFERELEASE(Tex) + SAFERELEASE(mainTex) SAFERELEASE(backbuffer); SAFERELEASE(sSurf1); SAFERELEASE(sSurf2); @@ -520,7 +520,7 @@ void RefreshGraphics() { d3d9Device->StretchRect(sSurf1, 0, sSurf2, 0, D3DTEXF_NONE); // copy: sSurf1 to sSurf2 d3d9Device->SetTexture(0, sTex2); } else { - d3d9Device->SetTexture(0, Tex); + d3d9Device->SetTexture(0, mainTex); } for (int d = shadersSize - 1; d >= 0; d--) { if (!shaders[d].Effect || !shaders[d].Active) continue; @@ -543,6 +543,7 @@ void RefreshGraphics() { d3d9Device->SetStreamSource(0, vBuffer2, 0, sizeof(VertexFormat)); d3d9Device->SetRenderTarget(0, backbuffer); + if (GPUBlt && !shadersSize) { gpuBltEffect->Begin(&unused, 0); gpuBltEffect->BeginPass(0); @@ -614,29 +615,30 @@ void Gfx_SetMovieTexture(IDirect3DTexture9* tex) { void Gfx_ShowMovieFrame() { //d3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 0, 255), 1.0f, 0); // for debbuging + if (!PlayAviMovie) return; + d3d9Device->BeginScene(); if (GPUBlt && shadersSize) { d3d9Device->SetTexture(0, sTex2); } else { - d3d9Device->SetTexture(0, Tex); + d3d9Device->SetTexture(0, mainTex); } d3d9Device->SetStreamSource(0, vBuffer2, 0, sizeof(VertexFormat)); d3d9Device->SetRenderTarget(0, backbuffer); - // TODO: The commented code sometimes for some unknown reason crashes the game when playing videos - //if (GPUBlt /*&& !shadersSize*/) { - // UINT unused; - // gpuBltEffect->Begin(&unused, 0); - // gpuBltEffect->BeginPass(0); - //} + if (GPUBlt) { + UINT passes; + gpuBltEffect->Begin(&passes, 0); + gpuBltEffect->BeginPass(0); + } d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); - //if (GPUBlt /*&& !shadersSize*/) { - // gpuBltEffect->EndPass(); - // gpuBltEffect->End(); - //} + if (GPUBlt) { + gpuBltEffect->EndPass(); + gpuBltEffect->End(); + } - // for movie + // for avi movie d3d9Device->SetTexture(0, movieTex); d3d9Device->SetStreamSource(0, movieBuffer, 0, sizeof(VertexFormat)); d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); @@ -735,9 +737,8 @@ public: primaryDDSurface->SetPalette(0); // update } if (!PlayAviMovie) { + //dlog("\nSetEntries: -> RefreshGraphics", DL_INIT); RefreshGraphics(); - } else { - Gfx_ShowMovieFrame(); } return DD_OK; } @@ -797,7 +798,7 @@ public: BYTE* lockTarget = ((FakeSurface2*)b)->lockTarget; D3DLOCKED_RECT dRect; - Tex->LockRect(0, &dRect, a, 0); + mainTex->LockRect(0, &dRect, a, 0); DWORD width = movieDesc.lPitch; // the current size of the width of the mve movie int pitch = dRect.Pitch; if (GPUBlt) { @@ -851,11 +852,11 @@ public: // } //} } - Tex->UnlockRect(0); + mainTex->UnlockRect(0); if (!DeviceLost) { d3d9Device->SetStreamSource(0, vBuffer2, 0, sizeof(VertexFormat)); - d3d9Device->SetTexture(0, Tex); + d3d9Device->SetTexture(0, mainTex); d3d9Device->BeginScene(); if (GPUBlt) { UINT unused; @@ -923,7 +924,7 @@ public: if (a) return DD_OK; // prevents executing the function when called from outside of sfall D3DLOCKED_RECT dRect; - Tex->LockRect(0, &dRect, 0, 0); + mainTex->LockRect(0, &dRect, 0, 0); DWORD* pBits = (DWORD*)dRect.pBits; int pitch = dRect.Pitch / 4; @@ -936,7 +937,7 @@ public: pBits[yp + x] = palette[lockTarget[yw + x]]; } } - Tex->UnlockRect(0); + mainTex->UnlockRect(0); return DD_OK; } @@ -959,7 +960,7 @@ public: } if (!DeviceLost) { D3DLOCKED_RECT dRect; - Tex->LockRect(0, &dRect, 0, 0); + mainTex->LockRect(0, &dRect, 0, 0); int pitch = dRect.Pitch; DWORD width = ResWidth; if (GPUBlt) { @@ -1031,9 +1032,10 @@ start2: } } } - Tex->UnlockRect(0); + mainTex->UnlockRect(0); if (!IsPlayMovie && !PlayAviMovie) { subTitlesShow = false; + //dlog("\nUnlock: -> RefreshGraphics", DL_INIT); RefreshGraphics(); }; } @@ -1074,7 +1076,7 @@ public: SAFERELEASE(backbuffer); SAFERELEASE(sSurf1); SAFERELEASE(sSurf2); - SAFERELEASE(Tex); + SAFERELEASE(mainTex); SAFERELEASE(sTex1); SAFERELEASE(sTex2); SAFERELEASE(vBuffer); diff --git a/sfall/Graphics.h b/sfall/Graphics.h index eb27001a..2e5ad98c 100644 --- a/sfall/Graphics.h +++ b/sfall/Graphics.h @@ -36,10 +36,14 @@ void GraphicsExit(); long Gfx_GetGameWidthRes(); long Gfx_GetGameHeightRes(); + void Gfx_SetHeadTex(IDirect3DTexture9* tex, int width, int height, int xoff, int yoff); void Gfx_SetHeadTechnique(); void Gfx_SetDefaultTechnique(); +void Gfx_ShowMovieFrame(); +void Gfx_SetMovieTexture(IDirect3DTexture9* tex); + int __stdcall GetShaderVersion(); int __stdcall LoadShader(const char*); void __stdcall ActivateShader(DWORD); @@ -56,6 +60,3 @@ void __stdcall SetShaderTexture(DWORD d, const char* param, DWORD value); void RefreshGraphics(); HWND GetFalloutWindowInfo(RECT* rect); - -void Gfx_SetMovieTexture(IDirect3DTexture9* tex); -void Gfx_ShowMovieFrame(); diff --git a/sfall/Interface.cpp b/sfall/Interface.cpp index 8ed7b696..39ee7397 100644 --- a/sfall/Interface.cpp +++ b/sfall/Interface.cpp @@ -475,12 +475,12 @@ static void SpeedInterfaceCounterAnimsPatch() { static bool IFACE_BAR_MODE = false; static long gmouse_handle_event_hook() { - long countWin = *(DWORD*)_num_windows; + long countWin = *ptr_num_windows; long ifaceWin = *ptr_interfaceWindow; WINinfo* win = nullptr; for (int n = 1; n < countWin; n++) { - win = (WINinfo*)ptr_window[n]; + win = ptr_window[n]; if ((win->wID == ifaceWin || (win->flags & WinFlags::ScriptWindow && !(win->flags & WinFlags::Transparent))) // also check scripted windows && !(win->flags & WinFlags::Hidden)) { RECT *rect = &win->wRect; diff --git a/sfall/MiscPatches.cpp b/sfall/MiscPatches.cpp index 18846246..8c5beb81 100644 --- a/sfall/MiscPatches.cpp +++ b/sfall/MiscPatches.cpp @@ -344,7 +344,7 @@ static void EncounterTableSizePatch() { static void DisablePipboyAlarmPatch() { if (GetConfigInt("Misc", "DisablePipboyAlarm", 0)) { dlog("Applying Disable Pip-Boy alarm button patch.", DL_INIT); - SafeWrite8(0x499518, 0xC3); + SafeWrite8(0x499518, CODETYPE_Ret); SafeWrite8(0x443601, 0x0); dlogr(" Done", DL_INIT); } diff --git a/sfall/Movies.cpp b/sfall/Movies.cpp index e5b3c807..dcb0cc79 100644 --- a/sfall/Movies.cpp +++ b/sfall/Movies.cpp @@ -22,10 +22,14 @@ #include "main.h" #include "FalloutEngine.h" + #include "Graphics.h" +#include "Sound.h" #include "Movies.h" +static DWORD MoviePtrs[MaxMovies]; + class CAllocator : public IVMRSurfaceAllocator9, IVMRImagePresenter9 { #define SAFERELEASE(a) { if (a) { a->Release(); a = nullptr; } } @@ -132,7 +136,6 @@ private: } HRESULT __stdcall PresentImage(DWORD_PTR dwUserID, VMR9PresentationInfo *lpPresInfo) { - //dlog_f("\nPresent Image %d", DL_INIT, isStartPresenting); if (isStartPresenting) { IDirect3DTexture9* tex; lpPresInfo->lpSurf->GetContainer(IID_IDirect3DTexture9, (LPVOID*)&tex); @@ -192,18 +195,34 @@ struct sDSTexture { IVMRSurfaceAllocatorNotify9 *pAlloc; IMediaControl *pControl; IMediaSeeking *pSeek; + IBasicAudio *pAudio; } movieInterface; +enum AviState : long { + AVISTATE_Stop, + AVISTATE_ReadyToPlay, + AVISTATE_Playing +}; + +static AviState aviPlayState; +static DWORD backgroundVolume = 0; + void PlayMovie(sDSTexture* movie) { movie->pControl->Run(); + movie->pAudio->put_Volume( + CalculateVolumeDB(*ptr_master_volume, (backgroundVolume) ? backgroundVolume : *ptr_background_volume) + ); } void PauseMovie(sDSTexture* movie) { movie->pControl->Pause(); } -void StopMovie(sDSTexture* movie) { - movie->pControl->Stop(); +void StopMovie() { + aviPlayState = AVISTATE_Stop; + movieInterface.pControl->Stop(); + Gfx_SetMovieTexture(0); + if (*(DWORD*)_subtitles == 0) RefreshGNW(); // Note: it is only necessary when the game is loaded } /* void RewindMovie(sDSTexture* movie) { @@ -227,6 +246,7 @@ DWORD FreeMovie(sDSTexture* movie) { if (movie->pConfig) movie->pConfig->Release(); if (movie->pVmr) movie->pVmr->Release(); if (movie->pGraph) movie->pGraph->Release(); + if (movie->pAudio) movie->pAudio->Release(); return 0; } @@ -271,6 +291,8 @@ DWORD CreateDSGraph(wchar_t* path, sDSTexture* movie) { hr = movie->pGraph->AddFilter(movie->pVmr, L"VMR9"); if (hr != S_OK) return FreeMovie(movie); + movie->pGraph->QueryInterface(IID_IBasicAudio, (void**)&movie->pAudio); + dlog("\nStart rendering file.", DL_INIT); hr = movie->pGraph->RenderFile(path, nullptr); if (hr != S_OK) dlog_f(" ERROR: %d", DL_INIT, hr); @@ -282,12 +304,24 @@ static __int64 endMoviePosition; // Movie play looping static DWORD PlayMovieLoop() { - //dlog("\nPlay Movie Loop.", DL_INIT); + static bool onlyOnce = false; - if (*(DWORD*)_subtitles) __asm call movieUpdate_; // for reading subtitles when playing mve + if (aviPlayState == AVISTATE_ReadyToPlay) { + aviPlayState = AVISTATE_Playing; + PlayMovie(&movieInterface); + onlyOnce = false; + } + + if (*(DWORD*)_subtitles) { + __asm call movieUpdate_; // for reading subtitles when playing mve + if (!onlyOnce) { + onlyOnce = true; + ClearWindow(*(DWORD*)_GNWWin); + } + } if (GetAsyncKeyState(VK_ESCAPE)) { - StopMovie(&movieInterface); + StopMovie(); return 0; // break play } @@ -297,7 +331,7 @@ static DWORD PlayMovieLoop() { movieInterface.pSeek->GetCurrentPosition(&pos); bool isPlayEnd = (endMoviePosition == pos); - if (isPlayEnd) StopMovie(&movieInterface); + if (isPlayEnd) StopMovie(); return !isPlayEnd; // 0 - for breaking play } @@ -320,8 +354,6 @@ static void __declspec(naked) gmovie_play_hook_input() { } } -static DWORD backgroundVolume = 0; - static DWORD __fastcall PreparePlayMovie(const DWORD id) { static long isNotWMR = -1; // Verify that the VMR exists on this system @@ -339,19 +371,20 @@ static DWORD __fastcall PreparePlayMovie(const DWORD id) { // Get file path in unicode wchar_t path[MAX_PATH]; + long pos = 0; + char* master_patches = *ptr_patches; - DWORD len = 0; - while (master_patches[len]) { - path[len] = master_patches[len]; len++; - } - path[len] = 0; - wcscat_s(path, L"\\art\\cuts\\"); - len = wcslen(path); - char* movie = &MoviePaths[id * 65] - len; - while (movie[len]) { - path[len] = movie[len]; len++; - } - wcscpy_s(&path[len - 3], 5, L"avi"); + while (pos < MAX_PATH && master_patches[pos]) path[pos] = master_patches[pos++]; + if ((pos + 10) >= MAX_PATH) return 0; + + wcscpy(&path[pos], L"\\art\\cuts\\"); + pos += 10; + + char* movie = (char*)(MoviePtrs[id] - pos); + while (pos < MAX_PATH && movie[pos]) path[pos] = movie[pos++]; + if (pos >= MAX_PATH) return 0; + + wcscpy(&path[pos - 3], L"avi"); // Check for existance of file if (GetFileAttributesW(path) & FILE_ATTRIBUTE_DIRECTORY) return 0; // also file not found @@ -362,28 +395,18 @@ static DWORD __fastcall PreparePlayMovie(const DWORD id) { HookCall(0x44E949, gmovie_play_hook_input); // block get_input_ (if subtitles are disabled then mve videos will not be played) HookCall(0x44E937, gmovie_play_hook); // looping call moviePlaying_ - // patching gmovie_play_ for disabled game subtitles - if (*(DWORD*)_subtitles == 0) { - SafeWrite8(0x4CB850, 0xC3); // GNW95_ShowRect_ blocking image rendering from the 'descSurface' surface when subtitles are disabled (optional) - } else { - //SafeWrite8(0x486654, 0xC3); // blocking movie_MVE_ShowFrame_ - //SafeWrite8(0x486900, 0xC3); // blocking movieShowFrame_ - SafeWrite8(0x4F5F40, 0xC3); // blocking sfShowFrame_ for disabling the display of mve video frames + // patching MVE_rmStepMovie_ for game subtitles + if (*(DWORD*)_subtitles) { + SafeWrite8(0x4F5F40, CODETYPE_Ret); // blocking sfShowFrame_ for disabling the display of mve video frames #ifdef NDEBUG // mute sound because mve file is still being played to get subtitles backgroundVolume = GsoundBackgroundVolumeGetSet(0); #endif } - - //WIP - //SafeWrite32(0x4C73B2, 0x2E); - //BlockCall(0x48827E); - //BlockCall(0x44E92B); - movieInterface.pSeek->GetStopPosition(&endMoviePosition); - PlayMovie(&movieInterface); + aviPlayState = AVISTATE_ReadyToPlay; - return 1; // play AVI + return 1; // for playing AVI } static void __stdcall PlayMovieRestore() { @@ -394,14 +417,12 @@ static void __stdcall PlayMovieRestore() { SafeWrite32(0x44E938, 0x3934C); // call moviePlaying_ SafeWrite32(0x44E94A, 0x7A22A); // call get_input_ - if (*(DWORD*)_subtitles == 0) { - SafeWrite8(0x4CB850, 0x53); // GNW95_ShowRect_ - } else { + if (*(DWORD*)_subtitles) { SafeWrite8(0x4F5F40, 0x53); // push ebx if (backgroundVolume) backgroundVolume = GsoundBackgroundVolumeGetSet(backgroundVolume); // restore volume } - Gfx_SetMovieTexture(0); + aviPlayState = AVISTATE_Stop; FreeMovie(&movieInterface); } @@ -420,14 +441,14 @@ static void __declspec(naked) gmovie_play_hack() { pop edx; pop ecx; jz failPlayAvi; - push offset return; // return to here label + push offset return; // return to here "return" label failPlayAvi: push ebx; push ecx; push esi; push edi; push ebp; - jmp gmovie_play_addr; + jmp gmovie_play_addr; // continue return: push ecx; call PlayMovieRestore; @@ -437,11 +458,19 @@ return: } } +static void __declspec(naked) gmovie_play_hook_stop() { + __asm { + cmp aviPlayState, AVISTATE_Playing; + jne skip; + call StopMovie; +skip: + jmp movieStop_; + } +} + /////////////////////////////////////////////////////////////////////////////// char MoviePaths[MaxMovies * 65]; - -static DWORD MoviePtrs[MaxMovies]; static DWORD Artimer1DaysCheckTimer; static void __declspec(naked) Artimer1DaysCheckHack() { @@ -493,13 +522,11 @@ void MoviesInit() { /* WIP: Task - Necessary to implement setting the volume according to the volume control in Fallout settings. - Add fade effects and brightness adjustment for videos. Implement subtitle output from the need to play an mve file in the background. - Fix minor bugs. */ if (GraphicsMode != 0 && GetConfigInt("Graphics", "AllowDShowMovies", 0)) { MakeJump(0x44E690, gmovie_play_hack); + HookCall(0x44E993, gmovie_play_hook_stop); /* NOTE: At this address 0x487781, HRP changes the callback procedure to display mve frames. */ } dlogr(" Done", DL_INIT); @@ -517,6 +544,5 @@ void MoviesInit() { dlogr(" Done", DL_INIT); } - // Should be AFTER the PlayMovieHook setup above SkipOpeningMoviesPatch(); } diff --git a/sfall/SafeWrite.h b/sfall/SafeWrite.h index 8fdfbe24..b36439b7 100644 --- a/sfall/SafeWrite.h +++ b/sfall/SafeWrite.h @@ -1,6 +1,7 @@ #pragma once enum CodeType : BYTE { + CODETYPE_Ret = 0xC3, CODETYPE_Call = 0xE8, CODETYPE_Jump = 0xE9, CODETYPE_Nop = 0x90,