diff --git a/.gitignore b/.gitignore index 442be304f..1df76208d 100644 --- a/.gitignore +++ b/.gitignore @@ -441,4 +441,5 @@ compile_commands.json CTestTestfile.cmake _deps */extract_assets_cmake* -/build* \ No newline at end of file +/build* +build.c diff --git a/BUILDING.md b/BUILDING.md index 0ab0d0d52..7606032ba 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -30,8 +30,8 @@ With the cmake build system you have two options for working on the project: #### Visual Studio To develop using Visual Studio you only need to use cmake to generate the solution file: ```powershell -# Generates Ship.sln at the root directory -& 'C:\Program Files\CMake\bin\cmake' -S . -G "Visual Studio 17 2022" -T v142 -A x64 +# Generates Ship.sln at `build/x64` +& 'C:\Program Files\CMake\bin\cmake' -S . -B "build/x64" -G "Visual Studio 17 2022" -T v142 -A x64 ``` #### Visual Studio Code or another editor @@ -53,7 +53,7 @@ cd "build/x64" ``` ## Linux -1. Requires `gcc, x11, curl, python3, sdl2, libpng, glew, ninja, cmake` +1. Requires `gcc >= 10, x11, curl, python3, sdl2 >= 2.0.22, libpng, glew >= 2.2, ninja, cmake, lld` **Important: For maximum performance make sure you have ninja build tools installed!** diff --git a/CMakeLists.txt b/CMakeLists.txt index 753c34523..d6b44d786 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,9 @@ add_compile_options($<$:/MP>) if (CMAKE_SYSTEM_NAME STREQUAL "Windows") include(cmake/automate-vcpkg.cmake) +set(VCPKG_TRIPLET x64-windows-static) +set(VCPKG_TARGET_TRIPLET x64-windows-static) + vcpkg_bootstrap() vcpkg_install_packages(zlib bzip2 libpng) endif() @@ -140,6 +143,7 @@ add_custom_target( COMMAND ${CMAKE_COMMAND} -E rm -f oot.otr COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${CMAKE_BINARY_DIR}/soh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter COMMENT "Running asset extraction..." DEPENDS ZAPD diff --git a/Dockerfile b/Dockerfile index e44a08286..1c23dafa4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,7 +46,8 @@ RUN curl -sLO https://libsdl.org/release/SDL2-${SDL2VER}.tar.gz && \ cd SDL2-${SDL2VER} && \ ./configure --build=x86_64-linux-gnu && \ make -j$(nproc) && make install && \ - rm ../SDL2-${SDL2VER}.tar.gz + rm ../SDL2-${SDL2VER}.tar.gz && \ + cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/ RUN \ ln -sf /proc/self/mounts /etc/mtab && \ diff --git a/libultraship/libultraship/CMakeLists.txt b/libultraship/libultraship/CMakeLists.txt index 8070d5f93..78331cbaf 100644 --- a/libultraship/libultraship/CMakeLists.txt +++ b/libultraship/libultraship/CMakeLists.txt @@ -456,6 +456,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") "NDEBUG" ">" "ENABLE_OPENGL;" + "SPDLOG_ACTIVE_LEVEL=0;" ) endif() ################################################################################ diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp index c80dc4be5..3e81f56fa 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_dxgi.cpp @@ -239,8 +239,8 @@ static LRESULT CALLBACK gfx_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_par dxgi.current_height = (uint32_t)(l_param >> 16); break; case WM_DESTROY: - Ship::ExecuteHooks(); - exit(0); + PostQuitMessage(0); + break; case WM_PAINT: if (dxgi.in_paint) { dxgi.recursive_paint_detected = true; @@ -378,6 +378,8 @@ static void gfx_dxgi_main_loop(void (*run_one_game_iter)(void)) { TranslateMessage(&msg); DispatchMessage(&msg); } + + Ship::ExecuteHooks(); } static void gfx_dxgi_get_dimensions(uint32_t *width, uint32_t *height) { diff --git a/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp b/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp index 8600aae1f..23cff4698 100644 --- a/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp +++ b/libultraship/libultraship/Lib/Fast3D/gfx_sdl2.cpp @@ -238,6 +238,8 @@ static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) { Ship::Switch::Exit(); #endif Ship::ExecuteHooks(); + + SDL_Quit(); } static void gfx_sdl_get_dimensions(uint32_t *width, uint32_t *height) { @@ -307,9 +309,8 @@ static void gfx_sdl_handle_events(void) { CVar_Save(); break; case SDL_QUIT: - Ship::ExecuteHooks(); - SDL_Quit(); // bandaid fix for linux window closing issue - exit(0); + is_running = false; + break; } } } diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index c842a4f92..edaf99c5f 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -1576,7 +1576,7 @@ set(ALL_FILES ################################################################################ # Target ################################################################################ -add_executable(${PROJECT_NAME} ${ALL_FILES}) +add_executable(${PROJECT_NAME} ${ALL_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc) if (CMAKE_SYSTEM_NAME STREQUAL "Windows") use_props(${PROJECT_NAME} "${CMAKE_CONFIGURATION_TYPES}" "${DEFAULT_CXX_PROPS}") @@ -1996,13 +1996,16 @@ endif() if(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch") -set_target_properties(soh PROPERTIES - APP_TITLE "Ship of Harkirian" - APP_AUTHOR "Ship" - APP_VERSION "3.0.0" - ICON "icon.jpg") - -nx_create_nro(soh) +nx_generate_nacp(Ship.nacp + NAME "Ship of Harkinian" + AUTHOR "Harbour Masters" + VERSION "3.1.0" +) + +nx_create_nro(soh + NACP Ship.nacp + ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg +) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/soh.nro DESTINATION . COMPONENT ship) diff --git a/soh/soh/OTRAudio.h b/soh/soh/OTRAudio.h index ee0ec46a2..a452fdb29 100644 --- a/soh/soh/OTRAudio.h +++ b/soh/soh/OTRAudio.h @@ -1,8 +1,9 @@ #pragma once static struct { + std::thread thread; std::condition_variable cv_to_thread, cv_from_thread; std::mutex mutex; - bool initialized; + bool running; bool processing; } audio; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index ebe06ff73..34729b91d 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -85,11 +85,73 @@ extern "C" void ResourceMgr_CacheDirectory(const char* resName); extern "C" SequenceData ResourceMgr_LoadSeqByName(const char* path); std::unordered_map ExtensionCache; +void OTRAudio_Thread() { + while (audio.running) { + { + std::unique_lock Lock(audio.mutex); + while (!audio.processing && audio.running) { + audio.cv_to_thread.wait(Lock); + } + + if (!audio.running) { + break; + } + } + std::unique_lock Lock(audio.mutex); + //AudioMgr_ThreadEntry(&gAudioMgr); + // 528 and 544 relate to 60 fps at 32 kHz 32000/60 = 533.333.. + // in an ideal world, one third of the calls should use num_samples=544 and two thirds num_samples=528 + //#define SAMPLES_HIGH 560 + //#define SAMPLES_LOW 528 + // PAL values + //#define SAMPLES_HIGH 656 + //#define SAMPLES_LOW 624 + + // 44KHZ values + #define SAMPLES_HIGH 752 + #define SAMPLES_LOW 720 + + #define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 ) + #define NUM_AUDIO_CHANNELS 2 + + int samples_left = AudioPlayer_Buffered(); + u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW; + + // 3 is the maximum authentic frame divisor. + s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3]; + for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) { + AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples); + } + + AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE)); + + audio.processing = false; + audio.cv_from_thread.notify_one(); + } +} + // C->C++ Bridge extern "C" void OTRAudio_Init() { // Precache all our samples, sequences, etc... ResourceMgr_CacheDirectory("audio"); + + if (!audio.running) { + audio.running = true; + audio.thread = std::thread(OTRAudio_Thread); + } +} + +extern "C" void OTRAudio_Exit() { + // Tell the audio thread to stop + { + std::unique_lock Lock(audio.mutex); + audio.running = false; + } + audio.cv_to_thread.notify_all(); + + // Wait until the audio thread quit + audio.thread.join(); } extern "C" void VanillaItemTable_Init() { @@ -267,6 +329,10 @@ extern "C" void InitOTR() { VanillaItemTable_Init(); } +extern "C" void DeinitOTR() { + OTRAudio_Exit(); +} + #ifdef _WIN32 extern "C" uint64_t GetFrequency() { LARGE_INTEGER nFreq; @@ -366,56 +432,10 @@ extern "C" void Graph_StartFrame() { // C->C++ Bridge extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { -#ifndef __SWITCH__ - if (!audio.initialized) { - audio.initialized = true; - std::thread([]() { - for (;;) { - { - std::unique_lock Lock(audio.mutex); - while (!audio.processing) { - audio.cv_to_thread.wait(Lock); - } - } - std::unique_lock Lock(audio.mutex); - //AudioMgr_ThreadEntry(&gAudioMgr); - // 528 and 544 relate to 60 fps at 32 kHz 32000/60 = 533.333.. - // in an ideal world, one third of the calls should use num_samples=544 and two thirds num_samples=528 - //#define SAMPLES_HIGH 560 - //#define SAMPLES_LOW 528 - // PAL values - //#define SAMPLES_HIGH 656 - //#define SAMPLES_LOW 624 - - // 44KHZ values - #define SAMPLES_HIGH 752 - #define SAMPLES_LOW 720 - - #define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 ) - #define NUM_AUDIO_CHANNELS 2 - - int samples_left = AudioPlayer_Buffered(); - u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW; - - // 3 is the maximum authentic frame divisor. - s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3]; - for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) { - AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples); - } - - AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE)); - - audio.processing = false; - audio.cv_from_thread.notify_one(); - } - }).detach(); - } - { std::unique_lock Lock(audio.mutex); audio.processing = true; } -#endif audio.cv_to_thread.notify_one(); std::vector> mtx_replacements; @@ -458,14 +478,12 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { last_fps = fps; last_update_rate = R_UPDATE_RATE; -#ifndef __SWITCH__ { std::unique_lock Lock(audio.mutex); while (audio.processing) { audio.cv_from_thread.wait(Lock); } } -#endif // OTRTODO: FIGURE OUT END FRAME POINT /* if (OTRGlobals::Instance->context->GetWindow()->lastScancode != -1) diff --git a/soh/src/code/graph.c b/soh/src/code/graph.c index cf0a53c76..f4dcf56e8 100644 --- a/soh/src/code/graph.c +++ b/soh/src/code/graph.c @@ -481,22 +481,6 @@ static void RunFrame() uint64_t ticksA, ticksB; ticksA = GetPerfCounter(); -#ifdef __SWITCH__ - #define SAMPLES_HIGH 752 - #define SAMPLES_LOW 720 - - #define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 ) - #define NUM_AUDIO_CHANNELS 2 - int samples_left = AudioPlayer_Buffered(); - u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW; - - s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3]; - for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) { - AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples); - } - - AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE)); -#endif Graph_StartFrame(); // TODO: Workaround for rumble being too long. Implement os thread functions. diff --git a/soh/src/code/main.c b/soh/src/code/main.c index fd02b016a..ccfdc2019 100644 --- a/soh/src/code/main.c +++ b/soh/src/code/main.c @@ -36,12 +36,14 @@ void Main_LogSystemHeap(void) { osSyncPrintf(VT_RST); } -void main(int argc, char** argv) +int main(int argc, char** argv) { GameConsole_Init(); InitOTR(); BootCommands_Init(); Main(0); + DeinitOTR(); + return 0; } void Main(void* arg) {