Implemented controller LED timings for cutscenes (#83)

This commit is contained in:
Hyper 2025-01-16 15:08:57 +00:00 committed by GitHub
parent 666f93843d
commit 63d474ce91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 262 additions and 23 deletions

View file

@ -152,6 +152,7 @@ set(SWA_PATCHES_CXX_SOURCES
"patches/audio_patches.cpp" "patches/audio_patches.cpp"
"patches/camera_patches.cpp" "patches/camera_patches.cpp"
"patches/fps_patches.cpp" "patches/fps_patches.cpp"
"patches/inspire_patches.cpp"
"patches/misc_patches.cpp" "patches/misc_patches.cpp"
"patches/object_patches.cpp" "patches/object_patches.cpp"
"patches/player_patches.cpp" "patches/player_patches.cpp"

View file

@ -64,6 +64,11 @@
#include "SWA/HUD/Sonic/HudSonicStage.h" #include "SWA/HUD/Sonic/HudSonicStage.h"
#include "SWA/Inspire/InspireMovieOverlay.h" #include "SWA/Inspire/InspireMovieOverlay.h"
#include "SWA/Inspire/InspireMovieOverlayInfo.h" #include "SWA/Inspire/InspireMovieOverlayInfo.h"
#include "SWA/Inspire/InspireOpacityAnimationInfo.h"
#include "SWA/Inspire/InspireScene.h"
#include "SWA/Inspire/InspireSceneData.h"
#include "SWA/Inspire/InspireSceneInfo.h"
#include "SWA/Inspire/InspireTextureAnimationInfo.h"
#include "SWA/Inspire/InspireTextureOverlay.h" #include "SWA/Inspire/InspireTextureOverlay.h"
#include "SWA/Inspire/InspireTextureOverlayInfo.h" #include "SWA/Inspire/InspireTextureOverlayInfo.h"
#include "SWA/Movie/MovieDisplayer.h" #include "SWA/Movie/MovieDisplayer.h"

View file

@ -8,10 +8,10 @@ namespace SWA::Inspire
{ {
public: public:
Hedgehog::Base::CSharedString m_MovieName; Hedgehog::Base::CSharedString m_MovieName;
be<float> m_StartTime; be<float> m_Prepare;
be<float> m_FadeInStartTime; be<float> m_InStart;
be<float> m_FadeInEndTime; be<float> m_InEnd;
be<float> m_FadeOutStartTime; be<float> m_OutStart;
be<float> m_FadeOutEndTime; be<float> m_OutEnd;
}; };
} }

View file

@ -0,0 +1,13 @@
#pragma once
#include <SWA.inl>
namespace SWA::Inspire
{
class COpacityAnimationInfo
{
public:
be<float> m_Opacity;
be<float> m_Frame;
};
}

View file

@ -0,0 +1,21 @@
#pragma once
#include <SWA.inl>
namespace SWA::Inspire
{
struct SSceneData
{
be<float> Frame;
be<uint32_t> Cut;
bool IsPlaying;
SWA_INSERT_PADDING(0x177);
};
class CScene
{
public:
SWA_INSERT_PADDING(0xC0);
xpointer<SSceneData> m_pData;
};
}

View file

@ -0,0 +1,13 @@
#pragma once
#include <SWA.inl>
namespace SWA::Inspire
{
class CSceneData // : public Hedgehog::Database::CDatabaseData
{
public:
SWA_INSERT_PADDING(0x80);
Hedgehog::Base::CSharedString m_ResourceName;
};
}

View file

@ -0,0 +1,8 @@
#pragma once
#include <SWA.inl>
namespace SWA::Inspire
{
class CSceneInfo {};
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <SWA.inl>
namespace SWA::Inspire
{
class CTextureAnimationInfo
{
public:
SWA_INSERT_PADDING(0x10);
Hedgehog::Base::CSharedString m_MovieTex;
Hedgehog::Base::CSharedString m_MovieSfd;
SWA_INSERT_PADDING(0x08);
be<float> m_Prepare;
be<float> m_Start;
be<float> m_End;
be<float> m_Width;
be<float> m_Height;
};
}

View file

@ -11,7 +11,7 @@ namespace SWA::Inspire
{ {
public: public:
xpointer<void> m_pVftable; xpointer<void> m_pVftable;
boost::shared_ptr<CInspireTextureOverlayInfo> m_spInfo; boost::shared_ptr<CTextureOverlayInfo> m_spInfo;
xpointer<CScene> m_pScene; xpointer<CScene> m_pScene;
boost::shared_ptr<Hedgehog::Mirage::CTextureData> m_spTextureData; boost::shared_ptr<Hedgehog::Mirage::CTextureData> m_spTextureData;
}; };

View file

@ -4,12 +4,11 @@
namespace SWA::Inspire namespace SWA::Inspire
{ {
class CInspireTextureOverlayInfo class CTextureOverlayInfo
{ {
public: public:
Hedgehog::Base::CSharedString m_CameraName; Hedgehog::Base::CSharedString m_Picture;
be<uint32_t> m_Unk1; be<uint32_t> m_Start;
be<uint32_t> m_Unk2; be<uint32_t> m_End;
be<uint32_t> m_Unk3;
}; };
} }

View file

@ -4,5 +4,9 @@
namespace SWA::Sequence::Unit namespace SWA::Sequence::Unit
{ {
class CPlayMovieUnit : public CUnitBase {}; class CPlayMovieUnit : public CUnitBase
{
public:
Hedgehog::Base::CSharedString m_SceneName;
};
} }

View file

@ -29,12 +29,16 @@ namespace SWA::Sequence::Utility
SVertexData m_TopRight; SVertexData m_TopRight;
SVertexData m_BottomRight; SVertexData m_BottomRight;
SVertexData m_BottomLeft; SVertexData m_BottomLeft;
bool m_MaintainAspectRatio; bool m_Field1A4;
SWA_INSERT_PADDING(0x18); SWA_INSERT_PADDING(0x18);
be<float> m_TimeElapsed; be<float> m_TimeElapsed;
}; };
SWA_INSERT_PADDING(0x18); xpointer<void> m_pVftable;
Hedgehog::Base::CSharedString m_SceneName;
SWA_INSERT_PADDING(0x10);
xpointer<CRender> m_pRender; xpointer<CRender> m_pRender;
SWA_INSERT_PADDING(0x04);
xpointer<Hedgehog::Base::CSharedString> m_pResourceName;
}; };
} }

View file

@ -2,10 +2,11 @@
#include <gpu/video.h> #include <gpu/video.h>
#include <install/installer.h> #include <install/installer.h>
#include <kernel/function.h> #include <kernel/function.h>
#include <ui/game_window.h>
#include <patches/audio_patches.h>
#include <user/config.h>
#include <os/process.h> #include <os/process.h>
#include <patches/audio_patches.h>
#include <patches/inspire_patches.h>
#include <ui/game_window.h>
#include <user/config.h>
void App::Restart(std::vector<std::string> restartArgs) void App::Restart(std::vector<std::string> restartArgs)
{ {
@ -24,7 +25,7 @@ void App::Exit()
std::_Exit(0); std::_Exit(0);
} }
// CApplication::Ctor // SWA::CApplication
PPC_FUNC_IMPL(__imp__sub_824EB490); PPC_FUNC_IMPL(__imp__sub_824EB490);
PPC_FUNC(sub_824EB490) PPC_FUNC(sub_824EB490)
{ {
@ -37,7 +38,7 @@ PPC_FUNC(sub_824EB490)
static std::thread::id g_mainThreadId = std::this_thread::get_id(); static std::thread::id g_mainThreadId = std::this_thread::get_id();
// CApplication::Update // SWA::CApplication::Update
PPC_FUNC_IMPL(__imp__sub_822C1130); PPC_FUNC_IMPL(__imp__sub_822C1130);
PPC_FUNC(sub_822C1130) PPC_FUNC(sub_822C1130)
{ {
@ -47,6 +48,7 @@ PPC_FUNC(sub_822C1130)
if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX) if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX)
{ {
double targetDeltaTime = 1.0 / Config::FPS; double targetDeltaTime = 1.0 / Config::FPS;
if (abs(ctx.f1.f64 - targetDeltaTime) < 0.00001) if (abs(ctx.f1.f64 - targetDeltaTime) < 0.00001)
ctx.f1.f64 = targetDeltaTime; ctx.f1.f64 = targetDeltaTime;
} }
@ -65,6 +67,7 @@ PPC_FUNC(sub_822C1130)
GameWindow::Update(); GameWindow::Update();
AudioPatches::Update(App::s_deltaTime); AudioPatches::Update(App::s_deltaTime);
InspirePatches::Update();
__imp__sub_822C1130(ctx, base); __imp__sub_822C1130(ctx, base);
} }

View file

@ -8,6 +8,7 @@ public:
static inline bool s_isInit; static inline bool s_isInit;
static inline bool s_isMissingDLC; static inline bool s_isMissingDLC;
static inline bool s_isLoading; static inline bool s_isLoading;
static inline bool s_isWerehog;
static inline ELanguage s_language; static inline ELanguage s_language;

View file

@ -0,0 +1,134 @@
#include "inspire_patches.h"
#include <api/SWA.h>
#include <ui/game_window.h>
#include <ui/window_events.h>
#include <os/logger.h>
#include <app.h>
static SWA::Inspire::CScene* g_pScene;
static std::string g_sceneName;
static bool g_isFirstFrameChecked;
static uint32_t g_eventDispatchCount;
static std::array<std::string_view, 8> g_alwaysEvilSonic =
{
"evrt_m2_02", // Same As Ever
"evrt_s1_05", // Chun-nan Temple
"evrt_s3_04", // Holoskan Temple
"evrt_t0_02", // Shamaran Temple
"evrt_m7_02", // The Final Temple
"evrt_m7_04", // Congratulations
"evrt_m8_02", // The Egg Dragoon
"evrt_m8_03" // Planet's End
};
static std::unordered_map<std::string_view, std::pair<float, float>> g_evilSonicTimings =
{
{ "evrt_m0_01_05", { 8189.97f, 10821 } }, // Opening
{ "evrt_m0_06", { 0, 5104.07f } }, // A New Journey
{ "evrt_m1_02", { 1162.46f, 3513 } }, // The First Night
{ "evrt_m6_03", { 2445, 5744 } }, // No Reason
{ "evrt_m8_04", { 0, 2314 } } // Dark Gaia Appears
};
// SWA::Inspire::CScene
PPC_FUNC_IMPL(__imp__sub_82B98D80);
PPC_FUNC(sub_82B98D80)
{
__imp__sub_82B98D80(ctx, base);
g_pScene = (SWA::Inspire::CScene*)g_memory.Translate(ctx.r3.u32);
g_isFirstFrameChecked = false;
g_eventDispatchCount = 0;
}
// ~SWA::Inspire::CScene
PPC_FUNC_IMPL(__imp__sub_82B98D30);
PPC_FUNC(sub_82B98D30)
{
__imp__sub_82B98D30(ctx, base);
g_pScene = nullptr;
g_sceneName.clear();
SDL_User_EvilSonic(App::s_isWerehog);
}
PPC_FUNC_IMPL(__imp__sub_82B9BA98);
PPC_FUNC(sub_82B9BA98)
{
auto sceneName = (Hedgehog::Base::CSharedString*)g_memory.Translate(ctx.r5.u32);
g_sceneName = sceneName->c_str();
__imp__sub_82B9BA98(ctx, base);
}
void InspirePatches::DrawDebug()
{
if (!g_pScene)
{
ImGui::Text("There is no active scene.");
return;
}
ImGui::Text("Name: %s", g_sceneName.c_str());
ImGui::Text("Frame: %f", g_pScene->m_pData->Frame.get());
ImGui::Text("Cut: %d", g_pScene->m_pData->Cut.get());
static std::vector<float> g_loggedFrames{};
ImGui::Separator();
if (ImGui::Button("Log"))
g_loggedFrames.push_back(g_pScene->m_pData->Frame);
if (ImGui::Button("Clear"))
g_loggedFrames.clear();
if (g_loggedFrames.size())
{
ImGui::Separator();
for (auto& frame : g_loggedFrames)
ImGui::Text("%f", frame);
}
}
void InspirePatches::Update()
{
if (!g_pScene || !g_sceneName.size())
return;
if (!g_isFirstFrameChecked && std::find(g_alwaysEvilSonic.begin(), g_alwaysEvilSonic.end(), g_sceneName) != g_alwaysEvilSonic.end())
{
SDL_User_EvilSonic(true);
g_isFirstFrameChecked = true;
return;
}
auto findResult = g_evilSonicTimings.find(g_sceneName);
if (findResult != g_evilSonicTimings.end())
{
auto& timings = findResult->second;
auto& frame = g_pScene->m_pData->Frame;
if (!g_isFirstFrameChecked && timings.first > 0)
{
SDL_User_EvilSonic(false);
g_isFirstFrameChecked = true;
}
if (!g_eventDispatchCount && (frame > timings.first && frame < timings.second))
{
SDL_User_EvilSonic(true);
g_eventDispatchCount++;
}
else if (g_eventDispatchCount == 1 && frame > timings.second)
{
SDL_User_EvilSonic(false);
g_eventDispatchCount++;
}
}
}

View file

@ -0,0 +1,8 @@
#pragma once
class InspirePatches
{
public:
static void DrawDebug();
static void Update();
};

View file

@ -3,6 +3,7 @@
#include <ui/window_events.h> #include <ui/window_events.h>
#include <user/config.h> #include <user/config.h>
#include <os/logger.h> #include <os/logger.h>
#include <app.h>
static uint32_t g_lastEnemyScore; static uint32_t g_lastEnemyScore;
static uint32_t g_lastTrickScore; static uint32_t g_lastTrickScore;
@ -122,20 +123,24 @@ void SetXButtonHomingMidAsmHook(PPCRegister& r30)
r30.u32 = Config::HomingAttackOnBoost; r30.u32 = Config::HomingAttackOnBoost;
} }
// SWA::Player::CEvilSonicContext::Ctor // SWA::Player::CEvilSonicContext
PPC_FUNC_IMPL(__imp__sub_823B49D8); PPC_FUNC_IMPL(__imp__sub_823B49D8);
PPC_FUNC(sub_823B49D8) PPC_FUNC(sub_823B49D8)
{ {
__imp__sub_823B49D8(ctx, base); __imp__sub_823B49D8(ctx, base);
App::s_isWerehog = true;
SDL_User_EvilSonic(true); SDL_User_EvilSonic(true);
} }
// SWA::Player::CEvilSonicContext::Dtor // ~SWA::Player::CEvilSonicContext
PPC_FUNC_IMPL(__imp__sub_823B4590); PPC_FUNC_IMPL(__imp__sub_823B4590);
PPC_FUNC(sub_823B4590) PPC_FUNC(sub_823B4590)
{ {
__imp__sub_823B4590(ctx, base); __imp__sub_823B4590(ctx, base);
App::s_isWerehog = false;
SDL_User_EvilSonic(false); SDL_User_EvilSonic(false);
} }

View file

@ -29,11 +29,11 @@ inline void SDL_MoveEvent(SDL_Window* pWindow, int x, int y)
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }
inline void SDL_User_EvilSonic(bool isCtor) inline void SDL_User_EvilSonic(bool isEvil)
{ {
SDL_Event event{}; SDL_Event event{};
event.type = SDL_USER_EVILSONIC; event.type = SDL_USER_EVILSONIC;
event.user.code = isCtor; event.user.code = isEvil;
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }