From 27eab0af66c2d2e5ca774cbaf93783a3e7344826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo?= Date: Thu, 12 Dec 2024 16:17:12 -0300 Subject: [PATCH] Installer sounds and embedded player. (#29) * Embedded player implementation. * Rework embedded sound player to support simultaneous playback. * Add more embedded sounds. * Update submodule. * Update. * Move engine initialization. * Use guest audio configuration values in embedded player. * Miniaudio submodule on dev branch. * Implement libvorbis. * Update resources submodule. --------- Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> --- .gitmodules | 3 + UnleashedRecomp/CMakeLists.txt | 14 +- UnleashedRecomp/apu/audio.cpp | 27 ++ .../apu/driver/miniaudio_driver.cpp | 3 - UnleashedRecomp/apu/driver/miniaudio_driver.h | 1 + UnleashedRecomp/apu/embedded_player.cpp | 289 ++++++++++++++++++ UnleashedRecomp/apu/embedded_player.h | 10 + UnleashedRecomp/exports.cpp | 30 +- UnleashedRecomp/stdafx.cpp | 6 + UnleashedRecomp/stdafx.h | 2 + UnleashedRecomp/ui/installer_wizard.cpp | 53 ++-- UnleashedRecompResources | 2 +- thirdparty/miniaudio | 1 + vcpkg.json | 4 +- 14 files changed, 404 insertions(+), 41 deletions(-) create mode 100644 UnleashedRecomp/apu/embedded_player.cpp create mode 100644 UnleashedRecomp/apu/embedded_player.h create mode 160000 thirdparty/miniaudio diff --git a/.gitmodules b/.gitmodules index 1accc7a..8e88cd7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "thirdparty/msdf-atlas-gen"] path = thirdparty/msdf-atlas-gen url = https://github.com/Chlumsky/msdf-atlas-gen.git +[submodule "thirdparty/miniaudio"] + path = thirdparty/miniaudio + url = https://github.com/mackron/miniaudio diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 9881d46..e179973 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -96,6 +96,7 @@ set(SWA_GPU_CXX_SOURCES set(SWA_APU_CXX_SOURCES "apu/audio.cpp" + "apu/embedded_player.cpp" ) if (SWA_XAUDIO2) @@ -163,6 +164,8 @@ set_source_files_properties(${LIBMSPACK_C_SOURCES} PROPERTIES SKIP_PRECOMPILE_HE set(SMOLV_SOURCE_DIR "${SWA_THIRDPARTY_ROOT}/ShaderRecomp/thirdparty/smol-v/source") set(BC_DIFF_SOURCE_DIR "${SWA_TOOLS_ROOT}/bc_diff") +set(MINIAUDIO_INCLUDE_DIRS "${SWA_THIRDPARTY_ROOT}/miniaudio") + set(SWA_USER_CXX_SOURCES "user/achievement_data.cpp" "user/config.cpp" @@ -220,7 +223,7 @@ find_package(imgui CONFIG REQUIRED) find_package(magic_enum CONFIG REQUIRED) find_package(unofficial-tiny-aes-c CONFIG REQUIRED) find_package(nfd CONFIG REQUIRED) -find_path(MINIAUDIO_INCLUDE_DIRS "miniaudio.h") +find_package(Vorbis CONFIG REQUIRED) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) add_custom_command(TARGET UnleashedRecomp POST_BUILD @@ -260,6 +263,7 @@ target_link_libraries(UnleashedRecomp PRIVATE unofficial::tiny-aes-c::tiny-aes-c nfd::nfd msdf-atlas-gen::msdf-atlas-gen + Vorbis::vorbisfile ) target_include_directories(UnleashedRecomp PRIVATE @@ -401,3 +405,11 @@ 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.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_cursor.ogg" ARRAY_NAME "g_sys_worldmap_cursor") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_finaldecide.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_finaldecide.ogg" ARRAY_NAME "g_sys_worldmap_finaldecide") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausecansel.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausecansel.ogg" ARRAY_NAME "g_sys_actstg_pausecansel") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausecursor.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausecursor.ogg" ARRAY_NAME "g_sys_actstg_pausecursor") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausedecide.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausedecide.ogg" ARRAY_NAME "g_sys_actstg_pausedecide") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinclose.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinclose.ogg" ARRAY_NAME "g_sys_actstg_pausewinclose") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinopen.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinopen.ogg" ARRAY_NAME "g_sys_actstg_pausewinopen") + diff --git a/UnleashedRecomp/apu/audio.cpp b/UnleashedRecomp/apu/audio.cpp index c234c5a..76dac7c 100644 --- a/UnleashedRecomp/apu/audio.cpp +++ b/UnleashedRecomp/apu/audio.cpp @@ -1,11 +1,25 @@ #include + +#include + #include "audio.h" #include "cpu/code_cache.h" #define AUDIO_DRIVER_KEY (uint32_t)('DAUD') +// Use to dump raw audio captures to the game folder. +//#define AUDIO_DUMP_SAMPLES_PATH "audio.pcm" + +#ifdef AUDIO_DUMP_SAMPLES_PATH +std::ofstream g_audioDumpStream; +#endif + uint32_t XAudioRegisterRenderDriverClient(XLPDWORD callback, XLPDWORD driver) { +#ifdef AUDIO_DUMP_SAMPLES_PATH + g_audioDumpStream.open(AUDIO_DUMP_SAMPLES_PATH, std::ios::binary); +#endif + *driver = AUDIO_DRIVER_KEY; XAudioRegisterClient(KeFindHostFunction(*callback), callback[1]); return 0; @@ -18,6 +32,19 @@ uint32_t XAudioUnregisterRenderDriverClient(DWORD driver) uint32_t XAudioSubmitRenderDriverFrame(uint32_t driver, void* samples) { +#ifdef AUDIO_DUMP_SAMPLES_PATH + static uint32_t xaudioSamplesBuffer[XAUDIO_NUM_SAMPLES * XAUDIO_NUM_CHANNELS]; + for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) + { + for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++) + { + xaudioSamplesBuffer[i * XAUDIO_NUM_CHANNELS + j] = std::byteswap(((uint32_t *)samples)[j * XAUDIO_NUM_SAMPLES + i]); + } + } + + g_audioDumpStream.write((const char *)(xaudioSamplesBuffer), sizeof(xaudioSamplesBuffer)); +#endif + XAudioSubmitFrame(samples); return 0; } diff --git a/UnleashedRecomp/apu/driver/miniaudio_driver.cpp b/UnleashedRecomp/apu/driver/miniaudio_driver.cpp index a5b6dd2..fb4ba39 100644 --- a/UnleashedRecomp/apu/driver/miniaudio_driver.cpp +++ b/UnleashedRecomp/apu/driver/miniaudio_driver.cpp @@ -4,9 +4,6 @@ #include #include -#define MINIAUDIO_IMPLEMENTATION -#include - static PPCFunc* g_clientCallback{}; static DWORD g_clientCallbackParam{}; // pointer in guest memory static ma_device g_audioDevice{}; diff --git a/UnleashedRecomp/apu/driver/miniaudio_driver.h b/UnleashedRecomp/apu/driver/miniaudio_driver.h index c8f99bf..d80c989 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 0000000..2fb899d --- /dev/null +++ b/UnleashedRecomp/apu/embedded_player.cpp @@ -0,0 +1,289 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#pragma region libvorbis +static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libvorbis* pVorbis; + + (void)pUserData; + + pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libvorbis* pVorbis; + + (void)pUserData; + + pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend; + + (void)pUserData; + + ma_libvorbis_uninit(pVorbis, pAllocationCallbacks); + ma_free(pVorbis, pAllocationCallbacks); +} + +static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend; + + (void)pUserData; + + return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis = +{ + ma_decoding_backend_init__libvorbis, + ma_decoding_backend_init_file__libvorbis, + NULL, /* onInitFileW() */ + NULL, /* onInitMemory() */ + ma_decoding_backend_uninit__libvorbis +}; +#pragma endregion + +enum class EmbeddedSound +{ + SysWorldMapCursor, + SysWorldMapFinalDecide, + SysActStgPauseCansel, + SysActStgPauseCursor, + SysActStgPauseDecide, + SysActStgPauseWinClose, + SysActStgPauseWinOpen, + Count, +}; + +struct EmbeddedSoundData +{ + static const int SimultaneousLimit = 4; + std::array, SimultaneousLimit> sounds; + std::array, SimultaneousLimit> decoders; + int oldestIndex = 0; +}; + +static ma_engine g_audioEngine = {}; +static std::array g_embeddedSoundData = {}; +static const std::unordered_map g_embeddedSoundMap = +{ + { "sys_worldmap_cursor", EmbeddedSound::SysWorldMapCursor }, + { "sys_worldmap_finaldecide", EmbeddedSound::SysWorldMapFinalDecide }, + { "sys_actstg_pausecansel", EmbeddedSound::SysActStgPauseCansel }, + { "sys_actstg_pausecursor", EmbeddedSound::SysActStgPauseCursor }, + { "sys_actstg_pausedecide", EmbeddedSound::SysActStgPauseDecide }, + { "sys_actstg_pausewinclose", EmbeddedSound::SysActStgPauseWinClose }, + { "sys_actstg_pausewinopen", EmbeddedSound::SysActStgPauseWinOpen }, +}; + +static void PlayEmbeddedSound(EmbeddedSound s) +{ + EmbeddedSoundData &data = g_embeddedSoundData[size_t(s)]; + int pickedIndex = -1; + for (int i = 0; (i < EmbeddedSoundData::SimultaneousLimit) && (pickedIndex < 0); i++) + { + if (data.sounds[i] == nullptr) + { + // The sound hasn't been created yet, create it and pick it. + const void *soundData = nullptr; + size_t soundDataSize = 0; + switch (s) + { + case EmbeddedSound::SysWorldMapCursor: + soundData = g_sys_worldmap_cursor; + soundDataSize = sizeof(g_sys_worldmap_cursor); + break; + case EmbeddedSound::SysWorldMapFinalDecide: + soundData = g_sys_worldmap_finaldecide; + soundDataSize = sizeof(g_sys_worldmap_finaldecide); + break; + case EmbeddedSound::SysActStgPauseCansel: + soundData = g_sys_actstg_pausecansel; + soundDataSize = sizeof(g_sys_actstg_pausecansel); + break; + case EmbeddedSound::SysActStgPauseCursor: + soundData = g_sys_actstg_pausecursor; + soundDataSize = sizeof(g_sys_actstg_pausecursor); + break; + case EmbeddedSound::SysActStgPauseDecide: + soundData = g_sys_actstg_pausedecide; + soundDataSize = sizeof(g_sys_actstg_pausedecide); + break; + case EmbeddedSound::SysActStgPauseWinClose: + soundData = g_sys_actstg_pausewinclose; + soundDataSize = sizeof(g_sys_actstg_pausewinclose); + break; + case EmbeddedSound::SysActStgPauseWinOpen: + soundData = g_sys_actstg_pausewinopen; + soundDataSize = sizeof(g_sys_actstg_pausewinopen); + break; + default: + assert(false && "Unknown embedded sound."); + return; + } + + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis + }; + + ma_decoder_config decoderConfig = ma_decoder_config_init_default(); + decoderConfig.pCustomBackendUserData = NULL; + decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; + decoderConfig.customBackendCount = std::size(pCustomBackendVTables); + + ma_result res; + data.decoders[i] = std::make_unique(); + res = ma_decoder_init_memory(soundData, soundDataSize, &decoderConfig, data.decoders[i].get()); + if (res != MA_SUCCESS) + { + fprintf(stderr, "ma_decoder_init_memory failed with error code %d.\n", res); + return; + } + + data.sounds[i] = std::make_unique(); + res = ma_sound_init_from_data_source(&g_audioEngine, data.decoders[i].get(), MA_SOUND_FLAG_DECODE, nullptr, data.sounds[i].get()); + if (res != MA_SUCCESS) + { + fprintf(stderr, "ma_sound_init_from_data_source failed with error code %d.\n", res); + return; + } + + pickedIndex = i; + } + else if (ma_sound_at_end(data.sounds[i].get())) + { + // A sound has reached the end, pick it. + pickedIndex = i; + } + } + + if (pickedIndex < 0) + { + // No free slots are available, pick the oldest one. + pickedIndex = data.oldestIndex; + data.oldestIndex = (data.oldestIndex + 1) % EmbeddedSoundData::SimultaneousLimit; + } + + if (data.sounds[pickedIndex] != nullptr) + { + ma_sound_set_volume(data.sounds[pickedIndex].get(), Config::EffectsVolume); + ma_sound_seek_to_pcm_frame(data.sounds[pickedIndex].get(), 0); + ma_sound_start(data.sounds[pickedIndex].get()); + } +} + +void EmbeddedPlayer::Init() +{ + ma_engine_config engineConfig = ma_engine_config_init(); + engineConfig.channels = XAUDIO_NUM_CHANNELS; + engineConfig.sampleRate = XAUDIO_SAMPLES_HZ; + + 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); + } + + 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_audioEngine.pDevice == nullptr) + { + return; + } + + PlayEmbeddedSound(it->second); +} + +void EmbeddedPlayer::Shutdown() +{ + for (EmbeddedSoundData &data : g_embeddedSoundData) + { + for (auto &sound : data.sounds) + { + if (sound != nullptr) + { + if (sound->pDataSource != nullptr) + { + ma_sound_uninit(sound.get()); + } + + sound.reset(); + } + } + + for (auto &decoder : data.decoders) + { + if (decoder != nullptr) + { + if (decoder->pBackend != nullptr) + { + ma_decoder_uninit(decoder.get()); + } + + decoder.reset(); + } + } + } + + if (g_audioEngine.pDevice != nullptr) + { + ma_engine_uninit(&g_audioEngine); + } + + s_isActive = false; +} diff --git a/UnleashedRecomp/apu/embedded_player.h b/UnleashedRecomp/apu/embedded_player.h new file mode 100644 index 0000000..40e241b --- /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 e47eaef..ee4eb0d 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/stdafx.cpp b/UnleashedRecomp/stdafx.cpp index 7a2e063..90aaf59 100644 --- a/UnleashedRecomp/stdafx.cpp +++ b/UnleashedRecomp/stdafx.cpp @@ -1,4 +1,10 @@ #define STB_IMAGE_IMPLEMENTATION #include +#define MINIAUDIO_IMPLEMENTATION +#include + +#define ma_offset_pcm_frames_ptr (char*)ma_offset_pcm_frames_ptr +#include + #include "stdafx.h" diff --git a/UnleashedRecomp/stdafx.h b/UnleashedRecomp/stdafx.h index 011046d..a28caba 100644 --- a/UnleashedRecomp/stdafx.h +++ b/UnleashedRecomp/stdafx.h @@ -34,6 +34,8 @@ #include #include #include +#include +#include using Microsoft::WRL::ComPtr; diff --git a/UnleashedRecomp/ui/installer_wizard.cpp b/UnleashedRecomp/ui/installer_wizard.cpp index 5e4e85e..45e3e68 100644 --- a/UnleashedRecomp/ui/installer_wizard.cpp +++ b/UnleashedRecomp/ui/installer_wizard.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -133,7 +134,7 @@ static WizardPage g_currentPage = g_firstPage; static std::string g_currentMessagePrompt = ""; static bool g_currentMessagePromptConfirmation = false; static int g_currentMessageResult = -1; -static bool g_currentMessageUpdateRemaining = false; +static bool g_filesPickerSkipUpdate = false; static ImVec2 g_joypadAxis = {}; static int g_currentCursorIndex = -1; static int g_currentCursorDefault = 0; @@ -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++) + for (size_t i = 0; i < g_currentCursorRects.size() && !g_filesPickerSkipUpdate; 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; } @@ -880,7 +888,7 @@ static bool ShowFilesPicker(std::list &filePaths) const nfdpathset_t *pathSet; nfdresult_t result = NFD_OpenDialogMultipleU8(&pathSet, nullptr, 0, nullptr); - g_currentMessageUpdateRemaining = true; + g_filesPickerSkipUpdate = true; if (result == NFD_OKAY) { @@ -900,7 +908,7 @@ static bool ShowFoldersPicker(std::list &folderPaths) const nfdpathset_t *pathSet; nfdresult_t result = NFD_PickFolderMultipleU8(&pathSet, nullptr); - g_currentMessageUpdateRemaining = true; + g_filesPickerSkipUpdate = true; if (result == NFD_OKAY) { @@ -1004,6 +1012,8 @@ static void DrawLanguagePicker() static void DrawSourcePickers() { + g_filesPickerSkipUpdate = false; + bool buttonPressed = false; std::list paths; if (g_currentPage == WizardPage::SelectGameAndUpdate || g_currentPage == WizardPage::SelectDLC) @@ -1293,17 +1303,16 @@ static void DrawBorders() static void DrawMessagePrompt() { - if (g_currentMessagePrompt.empty()) - { - return; - } - - if (g_currentMessageUpdateRemaining) + if (g_filesPickerSkipUpdate) { // If a blocking function like the files picker is called, we must wait one update before actually showing // the message box, as a lot of time has passed since the last real update. Otherwise, animations will play // too quickly and input glitches might happen. - g_currentMessageUpdateRemaining = false; + return; + } + + if (g_currentMessagePrompt.empty()) + { return; } @@ -1410,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. @@ -1442,6 +1452,7 @@ bool InstallerWizard::Run(bool skipGame) NFD_Quit(); InstallerWizard::Shutdown(); + EmbeddedPlayer::Shutdown(); return true; } diff --git a/UnleashedRecompResources b/UnleashedRecompResources index a9019c0..7179a84 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit a9019c0ab4f8990cbb09916df57e33d2dbed13e0 +Subproject commit 7179a84509ac565edd07bddeee131fb229e0e99f diff --git a/thirdparty/miniaudio b/thirdparty/miniaudio new file mode 160000 index 0000000..12a8d4e --- /dev/null +++ b/thirdparty/miniaudio @@ -0,0 +1 @@ +Subproject commit 12a8d4e4911c5ab4f4c089b4d039433975ed8a66 diff --git a/vcpkg.json b/vcpkg.json index e93e121..6771cfa 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -23,7 +23,7 @@ }, "magic-enum", "nativefiledialog-extended", - "miniaudio", - "freetype" + "freetype", + "libvorbis" ] }