From 122a85528a81457ac41934679b4a1e7877df01f5 Mon Sep 17 00:00:00 2001 From: jonwil Date: Wed, 11 Oct 2023 06:41:59 +1000 Subject: [PATCH] Implement WindowVideo (#1009) --- src/CMakeLists.txt | 1 + src/game/client/gui/gamewindow.h | 2 + src/game/client/gui/windowvideomanager.cpp | 300 +++++++++++++++++++++ src/game/client/gui/windowvideomanager.h | 106 ++++++++ src/game/client/ingameui.h | 6 +- src/hooker/setuphooks_zh.cpp | 5 + 6 files changed, 417 insertions(+), 3 deletions(-) create mode 100644 src/game/client/gui/windowvideomanager.cpp create mode 100644 src/game/client/gui/windowvideomanager.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b48ce8051..583bfa372 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -130,6 +130,7 @@ set(GAMEENGINE_SRC game/client/gui/processanimatewindow.cpp game/client/gui/shell/shell.cpp game/client/gui/windowlayout.cpp + game/client/gui/windowvideomanager.cpp game/client/gui/wininstancedata.cpp game/client/ingameui.cpp game/client/input/keyboard.cpp diff --git a/src/game/client/gui/gamewindow.h b/src/game/client/gui/gamewindow.h index 7e67aa1ec..51c5fa92c 100644 --- a/src/game/client/gui/gamewindow.h +++ b/src/game/client/gui/gamewindow.h @@ -337,6 +337,7 @@ class GameWindow : public MemoryPoolObject int Win_Set_Enabled_Color(int index, int color); int Win_Set_Enabled_Border_Color(int index, int color); + const Image *Win_Get_Enabled_Image(int index) { return m_instData.m_enabledDrawData[index].image; } int Win_Get_Enabled_Color(int index) { return m_instData.m_enabledDrawData[index].color; } int Win_Get_Enabled_Border_Color(int index) { return m_instData.m_enabledDrawData[index].borderColor; } @@ -344,6 +345,7 @@ class GameWindow : public MemoryPoolObject int Win_Set_Disabled_Color(int index, int color); int Win_Set_Disabled_Border_Color(int index, int color); + const Image *Win_Get_Disabled_Image(int index) { return m_instData.m_disabledDrawData[index].image; } int Win_Get_Disabled_Color(int index) { return m_instData.m_disabledDrawData[index].color; } int Win_Get_Disabled_Border_Color(int index) { return m_instData.m_disabledDrawData[index].borderColor; } diff --git a/src/game/client/gui/windowvideomanager.cpp b/src/game/client/gui/windowvideomanager.cpp new file mode 100644 index 000000000..0f4343f07 --- /dev/null +++ b/src/game/client/gui/windowvideomanager.cpp @@ -0,0 +1,300 @@ +/** + * @file + * + * @author Jonathan Wilson + * + * @brief Window Video Manager + * + * @copyright Thyme 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 + * 2 of the License, or (at your option) any later version. + * A full copy of the GNU General Public License can be found in + * LICENSE + */ +#include "windowvideomanager.h" +#include "display.h" + +WindowVideo::WindowVideo() : + m_playType(WINDOW_PLAY_MOVIE_ONCE), + m_win(nullptr), + m_videoBuffer(nullptr), + m_videoStream(nullptr), + m_state(WINDOW_VIDEO_STATE_STOP) +{ +} + +WindowVideo::~WindowVideo() +{ + if (m_win != nullptr) { + m_win->Win_Get_Instance_Data()->Set_VideoBuffer(nullptr); + } + + m_win = nullptr; + delete m_videoBuffer; + delete m_videoStream; +} + +void WindowVideo::Init(GameWindow *win, + Utf8String movie_name, + WindowVideoPlayType play_type, + VideoBuffer *video_buffer, + VideoStream *video_stream) +{ + m_win = win; + m_movieName = movie_name; + m_playType = play_type; + m_videoBuffer = video_buffer; + m_videoStream = video_stream; + m_state = WINDOW_VIDEO_STATE_PLAY; + + if (m_win != nullptr) { + m_win->Win_Get_Instance_Data()->Set_VideoBuffer(m_videoBuffer); + } +} + +void WindowVideo::Set_Window_State(WindowVideoStates state) +{ + m_state = state; + + if (m_state == WINDOW_VIDEO_STATE_STOP && m_win != nullptr) { + m_win->Win_Get_Instance_Data()->Set_VideoBuffer(nullptr); + } + + if (m_state == WINDOW_VIDEO_STATE_PLAY || m_state == WINDOW_VIDEO_STATE_PAUSE) { + if (m_win != nullptr) { + m_win->Win_Get_Instance_Data()->Set_VideoBuffer(m_videoBuffer); + } + } +} + +WindowVideoManager::WindowVideoManager() : m_stopAllMovies(false), m_pauseAllMovies(false) {} + +WindowVideoManager::~WindowVideoManager() +{ + for (auto it = m_playingVideos.begin(); it != m_playingVideos.end(); it++) { + delete it->second; + } + + m_playingVideos.clear(); +} + +void WindowVideoManager::Init() +{ + m_playingVideos.clear(); + m_stopAllMovies = false; + m_pauseAllMovies = false; +} + +void WindowVideoManager::Reset() +{ + for (auto it = m_playingVideos.begin(); it != m_playingVideos.end(); it++) { + delete it->second; + } + + m_playingVideos.clear(); + m_stopAllMovies = false; + m_pauseAllMovies = false; +} + +void WindowVideoManager::Update() +{ + if (!m_pauseAllMovies && !m_stopAllMovies) { + for (auto it = m_playingVideos.begin(); it != m_playingVideos.end(); it++) { + WindowVideo *win_video = it->second; + + if (win_video == nullptr) { + captainslog_dbgassert(false, "There's No WindowVideo in the m_playingVideos list"); + return; + } + + GameWindow *win = win_video->Get_Win(); + + if (win_video->Get_State() == WINDOW_VIDEO_STATE_HIDDEN && !win->Win_Is_Hidden()) { + Resume_Movie(win); + } + + if (win_video->Get_State() == WINDOW_VIDEO_STATE_PLAY && win->Win_Is_Hidden()) { + Hide_Movie(win); + } + + if (win_video->Get_State() == WINDOW_VIDEO_STATE_PLAY) { + VideoStream *stream = win_video->Get_Video_Stream(); + VideoBuffer *buffer = win_video->Get_Video_Buffer(); + + if (stream != nullptr) { + if (buffer != nullptr) { + if (stream->Is_Frame_Ready()) { + stream->Decompress_Frame(); + stream->Render_Frame(buffer); + stream->Next_Frame(); + + if (stream->Frame_Index() == 0) { + if (win_video->Get_Play_Type() != WINDOW_PLAY_MOVIE_ONCE) { + if (win_video->Get_Play_Type() == WINDOW_PLAY_MOVIE_SHOW_LAST_FRAME) { + Pause_Movie(win); + } + } else { + Stop_Movie(win); + } + } + } + } + } + } + } + } +} + +void WindowVideoManager::Load_Movie(GameWindow *win, Utf8String movie_name, WindowVideoPlayType play_type) +{ + Stop_And_Remove_Movie(win); + VideoStream *stream = g_theVideoPlayer->Open(movie_name); + + if (stream != nullptr) { + VideoBuffer *buffer = g_theDisplay->Create_VideoBuffer(); + + if (buffer != nullptr && buffer->Allocate(stream->Width(), stream->Height())) { + WindowVideo *win_video = new WindowVideo(); + win_video->Init(win, movie_name, play_type, buffer, stream); + m_playingVideos[win] = win_video; + m_pauseAllMovies = false; + m_stopAllMovies = false; + } else { + if (buffer != nullptr) { + delete buffer; + } + + if (stream != nullptr) { + stream->Close(); + } + } + } +} + +void WindowVideoManager::Pause_Movie(GameWindow *win) +{ + auto it = m_playingVideos.find(win); + + if (it != m_playingVideos.end()) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + win_video->Set_Window_State(WINDOW_VIDEO_STATE_PAUSE); + } + } +} + +void WindowVideoManager::Hide_Movie(GameWindow *win) +{ + auto it = m_playingVideos.find(win); + + if (it != m_playingVideos.end()) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + win_video->Set_Window_State(WINDOW_VIDEO_STATE_HIDDEN); + } + } +} + +void WindowVideoManager::Resume_Movie(GameWindow *win) +{ + auto it = m_playingVideos.find(win); + + if (it != m_playingVideos.end()) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + win_video->Set_Window_State(WINDOW_VIDEO_STATE_PLAY); + } + } + + m_pauseAllMovies = false; + m_stopAllMovies = false; +} + +void WindowVideoManager::Stop_Movie(GameWindow *win) +{ + auto it = m_playingVideos.find(win); + + if (it != m_playingVideos.end()) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + win_video->Set_Window_State(WINDOW_VIDEO_STATE_STOP); + } + } +} + +void WindowVideoManager::Stop_And_Remove_Movie(GameWindow *win) +{ + auto it = m_playingVideos.find(win); + + if (it != m_playingVideos.end()) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + delete win_video; + } + + m_playingVideos.erase(it); + } +} + +void WindowVideoManager::Stop_All_Movies() +{ + for (auto it = m_playingVideos.begin(); it != m_playingVideos.end(); it++) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + win_video->Set_Window_State(WINDOW_VIDEO_STATE_STOP); + } + } + + m_stopAllMovies = true; + m_pauseAllMovies = false; +} + +void WindowVideoManager::Pause_All_Movies() +{ + for (auto it = m_playingVideos.begin(); it != m_playingVideos.end(); it++) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + win_video->Set_Window_State(WINDOW_VIDEO_STATE_PAUSE); + } + } + + m_pauseAllMovies = true; + m_stopAllMovies = false; +} + +void WindowVideoManager::Resume_All_Movies() +{ + for (auto it = m_playingVideos.begin(); it != m_playingVideos.end(); it++) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + win_video->Set_Window_State(WINDOW_VIDEO_STATE_PLAY); + } + } + + m_pauseAllMovies = false; + m_stopAllMovies = false; +} + +WindowVideoStates WindowVideoManager::Get_Win_State(GameWindow *win) +{ + auto it = m_playingVideos.find(win); + + if (it != m_playingVideos.end()) { + WindowVideo *win_video = it->second; + + if (win_video != nullptr) { + return win_video->Get_State(); + } + } + + return WINDOW_VIDEO_STATE_STOP; +} diff --git a/src/game/client/gui/windowvideomanager.h b/src/game/client/gui/windowvideomanager.h new file mode 100644 index 000000000..275c634db --- /dev/null +++ b/src/game/client/gui/windowvideomanager.h @@ -0,0 +1,106 @@ +/** + * @file + * + * @author Jonathan Wilson + * + * @brief Window Video Manager + * + * @copyright Thyme 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 + * 2 of the License, or (at your option) any later version. + * A full copy of the GNU General Public License can be found in + * LICENSE + */ +#pragma once +#include "always.h" +#include "gamewindow.h" +#include "videobuffer.h" +#include "videoplayer.h" +#include "videostream.h" + +enum WindowVideoPlayType +{ + WINDOW_PLAY_MOVIE_ONCE, + WINDOW_PLAY_MOVIE_LOOP, + WINDOW_PLAY_MOVIE_SHOW_LAST_FRAME, + WINDOW_PLAY_MOVIE_COUNT, +}; + +enum WindowVideoStates +{ + WINDOW_VIDEO_STATE_START, + WINDOW_VIDEO_STATE_STOP, + WINDOW_VIDEO_STATE_PAUSE, + WINDOW_VIDEO_STATE_PLAY, + WINDOW_VIDEO_STATE_HIDDEN, + WINDOW_VIDEO_STATE_COUNT, +}; + +class WindowVideo +{ +public: + WindowVideo(); + ~WindowVideo(); + + WindowVideoPlayType Get_Play_Type() const { return m_playType; } + WindowVideoStates Get_State() const { return m_state; } + VideoBuffer *Get_Video_Buffer() const { return m_videoBuffer; } + VideoStream *Get_Video_Stream() const { return m_videoStream; } + GameWindow *Get_Win() const { return m_win; } + + void Init(GameWindow *win, + Utf8String movie_name, + WindowVideoPlayType play_type, + VideoBuffer *video_buffer, + VideoStream *video_stream); + + void Set_Window_State(WindowVideoStates state); + +private: + WindowVideoPlayType m_playType; + GameWindow *m_win; + VideoBuffer *m_videoBuffer; + VideoStream *m_videoStream; + Utf8String m_movieName; + WindowVideoStates m_state; +}; + +class WindowVideoManager : public SubsystemInterface +{ +public: + WindowVideoManager(); + virtual ~WindowVideoManager() override; + virtual void Init() override; + virtual void Reset() override; + virtual void Update() override; + + WindowVideoStates Get_Win_State(GameWindow *win); + void Hide_Movie(GameWindow *win); + void Load_Movie(GameWindow *win, Utf8String movie_name, WindowVideoPlayType play_type); + void Pause_All_Movies(); + void Pause_Movie(GameWindow *win); + void Resume_All_Movies(); + void Resume_Movie(GameWindow *win); + void Stop_All_Movies(); + void Stop_And_Remove_Movie(GameWindow *win); + void Stop_Movie(GameWindow *win); + +#ifdef GAME_DLL + WindowVideoManager *Hook_Ctor() { return new (this) WindowVideoManager; } +#endif + +private: + struct HashConstGameWindowPtr + { + size_t operator()(const GameWindow *object) const { return reinterpret_cast(object); } + }; + +#ifdef THYME_USE_STLPORT + std::hash_map m_playingVideos; +#else + std::unordered_map m_playingVideos; +#endif + bool m_stopAllMovies; + bool m_pauseAllMovies; +}; diff --git a/src/game/client/ingameui.h b/src/game/client/ingameui.h index 9ca1db47a..b46ac0b3d 100644 --- a/src/game/client/ingameui.h +++ b/src/game/client/ingameui.h @@ -29,7 +29,7 @@ class CommandButton; class Drawable; class VideoBuffer; -class VideoStreamInterface; +class VideoStream; class GameWindow; class PopupMessageData; class WindowLayout; @@ -272,9 +272,9 @@ class InGameUI : public SubsystemInterface, public SnapShot int m_doubleClickCounter; // not 100% identified yet Coord3D m_radiusDecalPos; // not 100% identified yet VideoBuffer *m_videoBuffer; - VideoStreamInterface *m_videoStream; + VideoStream *m_videoStream; VideoBuffer *m_cameoVideoBuffer; - VideoStreamInterface *m_cameoVideoStream; + VideoStream *m_cameoVideoStream; UIMessage m_uiMessages[6]; std::map> m_superweapons[16]; Coord2D m_superweaponPosition; diff --git a/src/hooker/setuphooks_zh.cpp b/src/hooker/setuphooks_zh.cpp index d4c8750e6..e2171a7fc 100644 --- a/src/hooker/setuphooks_zh.cpp +++ b/src/hooker/setuphooks_zh.cpp @@ -221,6 +221,7 @@ #include "win32gameengine.h" #include "win32localfilesystem.h" #include "win32mouse.h" +#include "windowvideomanager.h" #include "wininstancedata.h" #include "worldheightmap.h" #include "wwstring.h" @@ -3497,4 +3498,8 @@ void Setup_Hooks() Hook_Any(0x005A9160, AnimateWindowManager::Hook_Ctor); Hook_Any(0x005A9830, AnimateWindowManager::Register_Game_Window); Hook_Any(0x005A9990, AnimateWindowManager::Reverse_Animate_Window); + + // windowvideomanager.h + Hook_Any(0x005A9D00, WindowVideoManager::Hook_Ctor); + Hook_Any(0x005AA400, WindowVideoManager::Load_Movie); }