From e714c69f1d57ee36ce8a72b104b64b997885c9e2 Mon Sep 17 00:00:00 2001 From: Dario Date: Sun, 8 Dec 2024 19:39:23 -0300 Subject: [PATCH] Embedded player implementation. --- UnleashedRecomp/CMakeLists.txt | 3 + UnleashedRecomp/apu/driver/miniaudio_driver.h | 1 + UnleashedRecomp/apu/embedded_player.cpp | 142 ++++++++++++++++++ UnleashedRecomp/apu/embedded_player.h | 10 ++ UnleashedRecomp/exports.cpp | 30 ++-- UnleashedRecomp/ui/installer_wizard.cpp | 30 ++-- UnleashedRecompResources | 2 +- 7 files changed, 194 insertions(+), 24 deletions(-) create mode 100644 UnleashedRecomp/apu/embedded_player.cpp create mode 100644 UnleashedRecomp/apu/embedded_player.h diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 00455941..4751e858 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -95,6 +95,7 @@ set(SWA_GPU_CXX_SOURCES set(SWA_APU_CXX_SOURCES "apu/audio.cpp" + "apu/embedded_player.cpp" ) if (SWA_XAUDIO2) @@ -399,3 +400,5 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" ARRAY_NAME "g_xbox_color_correction" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_cursor.wav" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_cursor.wav" ARRAY_NAME "g_sys_worldmap_cursor") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_finaldecide.wav" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_finaldecide.wav" ARRAY_NAME "g_sys_worldmap_finaldecide") diff --git a/UnleashedRecomp/apu/driver/miniaudio_driver.h b/UnleashedRecomp/apu/driver/miniaudio_driver.h index c8f99bf4..d80c989b 100644 --- a/UnleashedRecomp/apu/driver/miniaudio_driver.h +++ b/UnleashedRecomp/apu/driver/miniaudio_driver.h @@ -1,2 +1,3 @@ #pragma once + #include diff --git a/UnleashedRecomp/apu/embedded_player.cpp b/UnleashedRecomp/apu/embedded_player.cpp new file mode 100644 index 00000000..d9f38a5f --- /dev/null +++ b/UnleashedRecomp/apu/embedded_player.cpp @@ -0,0 +1,142 @@ +#include + +#include +#include + +#include +#include + +enum EmbeddedSound +{ + EmbeddedSoundSysWorldMapCursor, + EmbeddedSoundSysWorldMapFinalDecide, + EmbeddedSoundCount, +}; + +static ma_engine g_audioEngine = {}; +static bool g_audioEngineInitialized = false; +static std::array g_embeddedSoundCache = {}; +static std::array g_embeddedDecoderCache = {}; +static const std::unordered_map g_embeddedSoundMap = +{ + { "sys_worldmap_cursor", EmbeddedSoundSysWorldMapCursor }, + { "sys_worldmap_finaldecide", EmbeddedSoundSysWorldMapFinalDecide }, +}; + +void InitEmbeddedSound(EmbeddedSound s) +{ + ma_sound &sound = g_embeddedSoundCache[s]; + ma_decoder &decoder = g_embeddedDecoderCache[s]; + const void *soundData = nullptr; + size_t soundDataSize = 0; + float volume = Config::EffectsVolume; + switch (s) + { + case EmbeddedSoundSysWorldMapCursor: + soundData = g_sys_worldmap_cursor; + soundDataSize = sizeof(g_sys_worldmap_cursor); + volume *= 0.259f; + break; + case EmbeddedSoundSysWorldMapFinalDecide: + soundData = g_sys_worldmap_finaldecide; + soundDataSize = sizeof(g_sys_worldmap_finaldecide); + volume *= 0.61f; + break; + default: + assert(false && "Unknown embedded sound."); + return; + } + + if (soundData != nullptr) + { + ma_result res; + res = ma_decoder_init_memory(soundData, soundDataSize, nullptr, &decoder); + if (res != MA_SUCCESS) + { + fprintf(stderr, "ma_decoder_init_memory failed with error code %d on embedded sound %d.\n", res, s); + return; + } + + res = ma_sound_init_from_data_source(&g_audioEngine, &decoder, MA_SOUND_FLAG_DECODE, nullptr, &sound); + if (res != MA_SUCCESS) + { + fprintf(stderr, "ma_sound_init_from_data_source failed with error code %d on embedded sound %d.\n", res, s); + return; + } + + ma_sound_set_volume(&sound, volume); + } +} + +void EmbeddedPlayer::Init() +{ + s_isActive = true; +} + +void EmbeddedPlayer::Play(const char *name) +{ + assert(s_isActive && "Playback shouldn't be requested if the Embedded Player isn't active."); + + auto it = g_embeddedSoundMap.find(name); + if (it == g_embeddedSoundMap.end()) + { + return; + } + + if (!g_audioEngineInitialized) + { + ma_engine_config engineConfig = ma_engine_config_init(); + ma_result res = ma_engine_init(&engineConfig, &g_audioEngine); + if (res != MA_SUCCESS) + { + fprintf(stderr, "ma_engine_init failed with error code %d.\n", res); + } + + // Do not attempt to initialize this again if it fails. + g_audioEngineInitialized = true; + } + + if (g_audioEngine.pDevice == nullptr) + { + return; + } + + ma_sound &sound = g_embeddedSoundCache[it->second]; + if (sound.pDataSource == nullptr) + { + InitEmbeddedSound(it->second); + } + + if (sound.pDataSource != nullptr) + { + ma_sound_seek_to_pcm_frame(&sound, 0); + ma_sound_start(&sound); + } +} + +void EmbeddedPlayer::Shutdown() +{ + for (ma_sound &sound : g_embeddedSoundCache) + { + if (sound.pDataSource != nullptr) + { + ma_sound_uninit(&sound); + } + } + + for (ma_decoder &decoder : g_embeddedDecoderCache) + { + if (decoder.pBackend != nullptr) + { + ma_decoder_uninit(&decoder); + } + } + + if (g_audioEngine.pDevice != nullptr) + { + ma_engine_uninit(&g_audioEngine); + } + + g_audioEngineInitialized = false; + s_isActive = false; +} diff --git a/UnleashedRecomp/apu/embedded_player.h b/UnleashedRecomp/apu/embedded_player.h new file mode 100644 index 00000000..40e241b7 --- /dev/null +++ b/UnleashedRecomp/apu/embedded_player.h @@ -0,0 +1,10 @@ +#pragma once + +struct EmbeddedPlayer +{ + inline static bool s_isActive = false; + + static void Init(); + static void Play(const char *name); + static void Shutdown(); +}; diff --git a/UnleashedRecomp/exports.cpp b/UnleashedRecomp/exports.cpp index e47eaefb..ee4eb0dd 100644 --- a/UnleashedRecomp/exports.cpp +++ b/UnleashedRecomp/exports.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,21 +9,24 @@ SWA_API void Game_PlaySound(const char* pName) { - // TODO: use own sound player. - if (InstallerWizard::s_isVisible) - return; + if (EmbeddedPlayer::s_isActive) + { + EmbeddedPlayer::Play(pName); + } + else + { + guest_stack_var soundPlayer; + GuestToHostFunction(sub_82B4DF50, soundPlayer.get(), ((be*)g_memory.Translate(0x83367900))->get(), 7, 0, 0); - guest_stack_var soundPlayer; - GuestToHostFunction(sub_82B4DF50, soundPlayer.get(), ((be*)g_memory.Translate(0x83367900))->get(), 7, 0, 0); + auto soundPlayerVtable = (be*)g_memory.Translate(*(be*)soundPlayer->get()); + uint32_t virtualFunction = *(soundPlayerVtable + 1); - auto soundPlayerVtable = (be*)g_memory.Translate(*(be*)soundPlayer->get()); - uint32_t virtualFunction = *(soundPlayerVtable + 1); - - size_t strLen = strlen(pName); - void* strAllocation = g_userHeap.Alloc(strLen + 1); - memcpy(strAllocation, pName, strLen + 1); - GuestToHostFunction(virtualFunction, soundPlayer->get(), strAllocation, 0); - g_userHeap.Free(strAllocation); + size_t strLen = strlen(pName); + void *strAllocation = g_userHeap.Alloc(strLen + 1); + memcpy(strAllocation, pName, strLen + 1); + GuestToHostFunction(virtualFunction, soundPlayer->get(), strAllocation, 0); + g_userHeap.Free(strAllocation); + } } SWA_API void Window_SetFullscreen(bool isEnabled) diff --git a/UnleashedRecomp/ui/installer_wizard.cpp b/UnleashedRecomp/ui/installer_wizard.cpp index e090e6bb..cecc2c03 100644 --- a/UnleashedRecomp/ui/installer_wizard.cpp +++ b/UnleashedRecomp/ui/installer_wizard.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -152,6 +153,7 @@ public: return; } + int newCursorIndex = -1; ImVec2 tapDirection = {}; switch (event->type) { @@ -168,7 +170,7 @@ public: break; case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: - g_currentCursorAccepted = true; + g_currentCursorAccepted = (g_currentCursorIndex >= 0); break; } @@ -189,7 +191,7 @@ public: tapDirection = { 0.0f, 1.0f }; break; case SDL_CONTROLLER_BUTTON_A: - g_currentCursorAccepted = true; + g_currentCursorAccepted = (g_currentCursorIndex >= 0); break; } @@ -215,14 +217,12 @@ public: case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEMOTION: { - g_currentCursorIndex = -1; - for (size_t i = 0; i < g_currentCursorRects.size(); i++) { auto ¤tRect = g_currentCursorRects[i]; if (ImGui::IsMouseHoveringRect(currentRect.first, currentRect.second, false)) { - g_currentCursorIndex = int(i); + newCursorIndex = int(i); if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_LEFT) { @@ -233,13 +233,17 @@ public: } } + if (newCursorIndex < 0) + { + g_currentCursorIndex = -1; + } + break; } } if (tapDirection.x != 0.0f || tapDirection.y != 0.0f) { - int newCursorIndex = -1; if (g_currentCursorIndex >= g_currentCursorRects.size() || g_currentCursorIndex < 0) { newCursorIndex = g_currentCursorDefault; @@ -278,13 +282,16 @@ public: } } } + } - if (newCursorIndex >= 0) + if (newCursorIndex >= 0) + { + if (g_currentCursorIndex != newCursorIndex) { - // TODO: Play sound. - - g_currentCursorIndex = newCursorIndex; + Game_PlaySound("sys_worldmap_cursor"); } + + g_currentCursorIndex = newCursorIndex; } } }; @@ -388,6 +395,7 @@ static bool PushCursorRect(ImVec2 min, ImVec2 max, bool &cursorPressed, bool mak { if (g_currentCursorAccepted) { + Game_PlaySound("sys_worldmap_finaldecide"); cursorPressed = true; g_currentCursorAccepted = false; } @@ -1411,6 +1419,7 @@ void InstallerWizard::Shutdown() bool InstallerWizard::Run(bool skipGame) { + EmbeddedPlayer::Init(); NFD_Init(); // Guarantee one controller is initialized. We'll rely on SDL's event loop to get the controller events. @@ -1443,6 +1452,7 @@ bool InstallerWizard::Run(bool skipGame) NFD_Quit(); InstallerWizard::Shutdown(); + EmbeddedPlayer::Shutdown(); return true; } diff --git a/UnleashedRecompResources b/UnleashedRecompResources index 3081bfae..f5352d15 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit 3081bfaec87550e3a085f1ac4048c3b637b5481d +Subproject commit f5352d15813026a1cdf44f67723f3c07567c9229