diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml index 20e7796..a5b6e93 100644 --- a/.github/workflows/emscripten.yml +++ b/.github/workflows/emscripten.yml @@ -8,6 +8,10 @@ env: jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + sdl-version: [2, 3] + name: Emscripten SDL${{ matrix.sdl-version }} steps: - uses: actions/checkout@v6 @@ -32,9 +36,11 @@ jobs: - name: Build run: | - mkdir -p out/wasm - cd out/wasm - emcmake cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_COMPILE_WARNING_AS_ERROR=YES ../../ + mkdir -p out/wasm-sdl${{ matrix.sdl-version }} + cd out/wasm-sdl${{ matrix.sdl-version }} + emcmake cmake -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_COMPILE_WARNING_AS_ERROR=YES \ + -DSYSTEM3_SDL_VERSION=${{ matrix.sdl-version }} ../.. make -j4 mkdir system3 mv system3.* system3/ @@ -42,5 +48,5 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v7 with: - name: system3-wasm - path: out/wasm/system3 + name: system3-wasm-sdl${{ matrix.sdl-version }} + path: out/wasm-sdl${{ matrix.sdl-version }}/system3 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2596e4c..beca257 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -3,12 +3,13 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 strategy: matrix: build-type: ["Debug", "Release"] + sdl-version: [2, 3] - name: Linux ${{ matrix.build-type }} + name: Linux SDL${{ matrix.sdl-version }} ${{ matrix.build-type }} steps: - uses: actions/checkout@v6 with: @@ -17,11 +18,18 @@ jobs: - name: Install Deps run: | sudo apt update - sudo apt install libsdl2-dev libsdl2-ttf-dev librtmidi-dev nlohmann-json3-dev ninja-build + sudo apt install librtmidi-dev nlohmann-json3-dev ninja-build + if [ "${{ matrix.sdl-version }}" = 2 ]; then + sudo apt install libsdl2-dev libsdl2-ttf-dev + else + sudo apt install libsdl3-dev libsdl3-ttf-dev + fi - name: Build run: | - cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ + -DSYSTEM3_SDL_VERSION=${{ matrix.sdl-version }} cmake --build build - name: Test diff --git a/.github/workflows/switch.yml b/.github/workflows/switch.yml index f32b917..4b01616 100644 --- a/.github/workflows/switch.yml +++ b/.github/workflows/switch.yml @@ -5,6 +5,7 @@ jobs: build: runs-on: ubuntu-latest container: devkitpro/devkita64:latest + name: Nintendo Switch SDL2 steps: - uses: actions/checkout@v6 @@ -15,7 +16,8 @@ jobs: run: | mkdir -p out/switch cd out/switch - /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake -DCMAKE_BUILD_TYPE=Release ../../ + /opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake \ + -DCMAKE_BUILD_TYPE=Release -DSYSTEM3_SDL_VERSION=2 ../../ make -j4 mkdir system3 mv system3.* system3/ diff --git a/.github/workflows/windows-msvc.yml b/.github/workflows/windows-msvc.yml index 7a50f0c..7747174 100644 --- a/.github/workflows/windows-msvc.yml +++ b/.github/workflows/windows-msvc.yml @@ -6,13 +6,20 @@ jobs: runs-on: windows-latest strategy: matrix: - arch: ["Win32", "x64"] include: - arch: "Win32" + sdl-version: 2 package: "system3-sdl2-msvc-32bit" - arch: "x64" + sdl-version: 2 package: "system3-sdl2-msvc-64bit" - name: MSVS ${{ matrix.arch }} + - arch: "Win32" + sdl-version: 3 + package: "system3-sdl3-msvc-32bit" + - arch: "x64" + sdl-version: 3 + package: "system3-sdl3-msvc-64bit" + name: MSVS SDL${{ matrix.sdl-version }} ${{ matrix.arch }} steps: - name: Checkout @@ -24,7 +31,7 @@ jobs: run: | mkdir out cd out - cmake -A ${{ matrix.arch }} -DCMAKE_COMPILE_WARNING_AS_ERROR=YES ../ + cmake -A ${{ matrix.arch }} -DCMAKE_COMPILE_WARNING_AS_ERROR=YES -DSYSTEM3_SDL_VERSION=${{ matrix.sdl-version }} ../ cmake --build . --config Release cmake --install . --prefix artifacts cp ../COPYING.txt artifacts/ diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 73d8445..b6ccab9 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -10,14 +10,23 @@ jobs: include: - sys: mingw32 package: "system3-sdl2-32bit" + sdl-version: 2 + sdl-deps: "SDL2:p SDL2_ttf:p" deps: "" - sys: ucrt64 package: "system3-sdl2-64bit" + sdl-version: 2 + sdl-deps: "SDL2:p SDL2_ttf:p" + deps: "nlohmann-json:p" + - sys: ucrt64 + package: "system3-sdl3-64bit" + sdl-version: 3 + sdl-deps: "sdl3:p sdl3-ttf:p" deps: "nlohmann-json:p" defaults: run: shell: msys2 {0} - name: MSYS2 ${{ matrix.sys }} + name: MSYS2 SDL${{ matrix.sdl-version }} ${{ matrix.sys }} steps: - name: Set up MSYS2 @@ -25,8 +34,7 @@ jobs: with: msystem: ${{ matrix.sys }} pacboy: >- - SDL2:p - SDL2_ttf:p + ${{ matrix.sdl-deps }} rtmidi:p ${{ matrix.deps }} @@ -48,7 +56,9 @@ jobs: - name: Build run: | - cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_COMPILE_WARNING_AS_ERROR=YES + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_COMPILE_WARNING_AS_ERROR=YES \ + -DSYSTEM3_SDL_VERSION=${{ matrix.sdl-version }} cmake --build build - name: Test diff --git a/CMakeLists.txt b/CMakeLists.txt index 968385d..123d9cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,24 @@ project(System3 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) include(CTest) +set(SYSTEM3_SDL_VERSION "2" CACHE STRING + "SDL major version (2 or 3)") +set_property(CACHE SYSTEM3_SDL_VERSION PROPERTY STRINGS 2 3) +if (NOT SYSTEM3_SDL_VERSION STREQUAL "2" AND + NOT SYSTEM3_SDL_VERSION STREQUAL "3") + message(FATAL_ERROR "SYSTEM3_SDL_VERSION must be 2 or 3") +endif() + +add_library(system3_sdl INTERFACE) +target_compile_definitions(system3_sdl INTERFACE + SYSTEM3_SDL_VERSION=${SYSTEM3_SDL_VERSION}) + +if (SYSTEM3_SDL_VERSION STREQUAL "2") + set(SYSTEM3_AUDIO_BACKEND src/generic/mako.cpp) +else() + set(SYSTEM3_AUDIO_BACKEND src/generic/mako_sdl3.cpp) +endif() + # Generates a static library from pkg_check_modules() result function(add_static_library name pkg) add_library(${name} INTERFACE) @@ -38,6 +56,9 @@ endif() if (ANDROID) + if (NOT SYSTEM3_SDL_VERSION STREQUAL "2") + message(FATAL_ERROR "Android currently supports SDL2 only") + endif() add_library(system3 SHARED) target_sources(system3 PRIVATE @@ -56,9 +77,14 @@ elseif (EMSCRIPTEN) src/emscripten/nact_emscripten.cpp src/emscripten/mako.cpp ) - set(LIBS -sUSE_SDL=2 -sUSE_SDL_TTF=2) - target_compile_options(system3 PRIVATE ${LIBS}) - target_link_options(system3 PRIVATE ${LIBS}) + set(LIBS + -sUSE_SDL=${SYSTEM3_SDL_VERSION} + -sUSE_SDL_TTF=${SYSTEM3_SDL_VERSION}) + if (SYSTEM3_SDL_VERSION STREQUAL "3") + list(APPEND LIBS -Wno-experimental) + endif() + target_compile_options(system3_sdl INTERFACE ${LIBS}) + target_link_options(system3_sdl INTERFACE ${LIBS}) target_link_libraries(system3 PRIVATE idbfs.js ymfm) # Without optimizations, Asyncify generates very large code. @@ -92,20 +118,43 @@ else() # NOT (ANDROID OR EMSCRIPTEN) if (MSVC) include(FetchContent) - FetchContent_Declare(sdl2 - URL https://github.com/libsdl-org/SDL/releases/download/release-2.32.10/SDL2-devel-2.32.10-VC.zip - URL_HASH SHA1=27f5179346a0b0db80c4dd1769c7c9d62b9a91f3) - FetchContent_MakeAvailable(sdl2) - find_package(SDL2 REQUIRED CONFIG PATHS ${sdl2_SOURCE_DIR}/cmake NO_DEFAULT_PATH) - get_target_property(SDL2_DLL SDL2::SDL2 IMPORTED_LOCATION) + if (SYSTEM3_SDL_VERSION STREQUAL "2") + FetchContent_Declare(sdl + URL https://github.com/libsdl-org/SDL/releases/download/release-2.32.10/SDL2-devel-2.32.10-VC.zip + URL_HASH SHA1=27f5179346a0b0db80c4dd1769c7c9d62b9a91f3) + FetchContent_MakeAvailable(sdl) + find_package(SDL2 REQUIRED CONFIG PATHS ${sdl_SOURCE_DIR}/cmake NO_DEFAULT_PATH) + set(SDL_TARGET SDL2::SDL2) + set(SDL_MAIN_TARGET SDL2::SDL2main) - FetchContent_Declare(sdl2_ttf - URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.24.0/SDL2_ttf-devel-2.24.0-VC.zip - URL_HASH SHA1=2d18b9a4fc2ec0eee80de2a946b088d4e6efd0ee) - FetchContent_MakeAvailable(sdl2_ttf) - find_package(SDL2_ttf REQUIRED CONFIG PATHS ${sdl2_ttf_SOURCE_DIR}/cmake NO_DEFAULT_PATH) - get_target_property(SDL2TTF_DLL SDL2_ttf::SDL2_ttf IMPORTED_LOCATION) - get_filename_component(SDL2TTF_LIBDIR ${SDL2TTF_DLL} DIRECTORY) + FetchContent_Declare(sdl_ttf + URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.24.0/SDL2_ttf-devel-2.24.0-VC.zip + URL_HASH SHA1=2d18b9a4fc2ec0eee80de2a946b088d4e6efd0ee) + FetchContent_MakeAvailable(sdl_ttf) + find_package(SDL2_ttf REQUIRED CONFIG PATHS ${sdl_ttf_SOURCE_DIR}/cmake NO_DEFAULT_PATH) + set(SDL_TTF_TARGET SDL2_ttf::SDL2_ttf) + else() + FetchContent_Declare(sdl + URL https://github.com/libsdl-org/SDL/releases/download/release-3.4.16/SDL3-devel-3.4.16-VC.zip + URL_HASH SHA1=edcf1f567837e7464cf80df402661b41acd78dbf) + FetchContent_MakeAvailable(sdl) + find_package(SDL3 REQUIRED CONFIG PATHS ${sdl_SOURCE_DIR}/cmake NO_DEFAULT_PATH) + set(SDL_TARGET SDL3::SDL3) + set(SDL_MAIN_TARGET "") + + FetchContent_Declare(sdl_ttf + URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-3.2.2/SDL3_ttf-devel-3.2.2-VC.zip + URL_HASH SHA1=3786bc016d89ca4cf9739e4d342ca29e3e29c137) + FetchContent_MakeAvailable(sdl_ttf) + find_package(SDL3_ttf REQUIRED CONFIG PATHS ${sdl_ttf_SOURCE_DIR}/cmake NO_DEFAULT_PATH) + set(SDL_TTF_TARGET SDL3_ttf::SDL3_ttf) + endif() + get_target_property(SDL_DLL ${SDL_TARGET} IMPORTED_LOCATION) + get_target_property(SDL_TTF_DLL ${SDL_TTF_TARGET} IMPORTED_LOCATION) + get_filename_component(SDL_LIBDIR ${SDL_DLL} DIRECTORY) + get_filename_component(SDL_TTF_LIBDIR ${SDL_TTF_DLL} DIRECTORY) + target_link_libraries(system3_sdl INTERFACE + ${SDL_MAIN_TARGET} ${SDL_TARGET} ${SDL_TTF_TARGET}) FetchContent_Declare(rtmidi URL https://github.com/thestk/rtmidi/archive/refs/tags/6.0.0.zip @@ -127,36 +176,40 @@ else() # NOT (ANDROID OR EMSCRIPTEN) target_compile_definitions(system3 PRIVATE _CRT_NONSTDC_NO_DEPRECATE _CRT_SECURE_NO_WARNINGS) target_compile_options(system3 PRIVATE /utf-8 /wd4244) - target_link_libraries(system3 PRIVATE SDL2::SDL2main SDL2::SDL2 SDL2_ttf::SDL2_ttf) add_custom_command(TARGET system3 POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - "$" "$" + "$" "$" COMMAND ${CMAKE_COMMAND} -E copy_if_different - "$" "$" + "$" "$" COMMENT "Copying SDL runtime DLLs" VERBATIM) set_target_properties(system3 PROPERTIES WIN32_EXECUTABLE TRUE - VS_DEBUGGER_ENVIRONMENT "PATH=${SDL2_LIBDIR}$${SDL2TTF_LIBDIR}$$ENV{PATH}") + VS_DEBUGGER_ENVIRONMENT "PATH=${SDL_LIBDIR}$${SDL_TTF_LIBDIR}$$ENV{PATH}") set_directory_properties(PROPERTIES VS_STARTUP_PROJECT system3) - install(FILES ${SDL2_DLL} ${SDL2TTF_DLL} DESTINATION .) + install(FILES ${SDL_DLL} ${SDL_TTF_DLL} DESTINATION .) install(TARGETS system3 RUNTIME DESTINATION .) else() include(FindPkgConfig) - pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2) - pkg_check_modules(SDL2TTF REQUIRED IMPORTED_TARGET SDL2_ttf) - if (MINGW) - add_static_library(sdl2 SDL2) - add_static_library(sdl2_ttf SDL2TTF) - # Workaround for linking error - set_property(TARGET sdl2_ttf PROPERTY INTERFACE_LINK_LIBRARIES - $) + if (SYSTEM3_SDL_VERSION STREQUAL "2") + pkg_check_modules(SDL REQUIRED IMPORTED_TARGET sdl2) + pkg_check_modules(SDLTTF REQUIRED IMPORTED_TARGET SDL2_ttf) else() - add_library(sdl2 ALIAS PkgConfig::SDL2) - add_library(sdl2_ttf ALIAS PkgConfig::SDL2TTF) + pkg_check_modules(SDL REQUIRED IMPORTED_TARGET sdl3) + pkg_check_modules(SDLTTF REQUIRED IMPORTED_TARGET sdl3-ttf) endif() - target_link_libraries(system3 PRIVATE sdl2 sdl2_ttf) + if (MINGW) + add_static_library(sdl SDL) + add_static_library(sdl_ttf SDLTTF) + # Workaround for linking error + set_property(TARGET sdl_ttf PROPERTY INTERFACE_LINK_LIBRARIES + $) + else() + add_library(sdl ALIAS PkgConfig::SDL) + add_library(sdl_ttf ALIAS PkgConfig::SDLTTF) + endif() + target_link_libraries(system3_sdl INTERFACE sdl sdl_ttf) find_package(nlohmann_json 3.2.0) if (nlohmann_json_FOUND) @@ -180,8 +233,8 @@ else() # NOT (ANDROID OR EMSCRIPTEN) if (CMAKE_SYSTEM_NAME STREQUAL "Windows") target_sources(system3 PRIVATE src/win/nact_win.cpp - src/generic/mako.cpp src/win/resource.rc + ${SYSTEM3_AUDIO_BACKEND} ) target_compile_definitions(system3 PRIVATE NOMINMAX) target_link_libraries(system3 PRIVATE winmm) @@ -191,7 +244,7 @@ else() # NOT (ANDROID OR EMSCRIPTEN) else() target_sources(system3 PRIVATE src/generic/nact_generic.cpp - src/generic/mako.cpp + ${SYSTEM3_AUDIO_BACKEND} ) set(RESOURCE_PATH ${CMAKE_INSTALL_PREFIX}/share/system3/) install(TARGETS system3 RUNTIME DESTINATION bin) @@ -243,6 +296,9 @@ if (ENABLE_DEBUGGER) endif() if(NINTENDO_SWITCH) + if (SYSTEM3_SDL_VERSION STREQUAL "3") + message(FATAL_ERROR "Nintendo Switch currently supports SDL2 only") + endif() set(RESOURCE_PATH romfs:/) nx_generate_nacp(system3.nacp @@ -269,6 +325,7 @@ endif() target_compile_definitions(system3 PRIVATE RESOURCE_PATH="${RESOURCE_PATH}") target_include_directories(system3 PRIVATE src src/sys) +target_link_libraries(system3 PRIVATE system3_sdl) if (BUILD_TESTING AND NOT ANDROID AND NOT EMSCRIPTEN AND NOT NINTENDO_SWITCH) add_subdirectory(test) diff --git a/README.md b/README.md index 7820aa0..3520d83 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# System3 for SDL2 +# System3 for SDL2/SDL3 -This is an SDL2 port of +This is an SDL port of [System3 for Win32](http://takeda-toshiya.my.coocan.jp/alice/) by Takeda Toshiya. It supports multiple platforms, including Android and Emscripten. @@ -124,6 +124,8 @@ the game ID. You need to specify the `game` option in `system3.ini`. ## Building from Source +SDL2 is used by default. Pass CMake option `-DSYSTEM3_SDL_VERSION=3` to select SDL3. + ### Linux (Debian, Ubuntu) ```bash @@ -131,11 +133,13 @@ $ git submodule update --init $ sudo apt install g++ cmake libsdl2-dev libsdl2-ttf-dev librtmidi-dev nlohmann-json3-dev $ mkdir -p out/debug $ cd out/debug -$ cmake -DCMAKE_BUILD_TYPE=Debug ../../ +$ cmake -DCMAKE_BUILD_TYPE=Debug -DSYSTEM3_SDL_VERSION=2 ../../ $ make $ sudo make install ``` +For SDL3, install `libsdl3-dev libsdl3-ttf-dev` and use `-DSYSTEM3_SDL_VERSION=3`. + ### MacOS ```bash @@ -143,11 +147,13 @@ $ git submodule update --init $ brew install cmake pkg-config sdl2 sdl2_ttf rtmidi nlohmann-json $ mkdir -p out/debug $ cd out/debug -$ cmake -DCMAKE_BUILD_TYPE=Debug ../../ +$ cmake -DCMAKE_BUILD_TYPE=Debug -DSYSTEM3_SDL_VERSION=2 ../../ $ make $ sudo make install ``` +For SDL3, install `sdl3 sdl3_ttf` and use `-DSYSTEM3_SDL_VERSION=3`. + ### Windows (MSYS2) ```bash @@ -155,10 +161,13 @@ $ git submodule update --init $ pacman -S make mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-SDL2 mingw-w64-ucrt-x86_64-SDL2_ttf mingw-w64-ucrt-x86_64-rtmidi mingw-w64-ucrt-x86_64-nlohmann-json $ mkdir -p out/debug $ cd out/debug -$ cmake -G"MSYS Makefiles" -DCMAKE_BUILD_TYPE=Debug ../../ +$ cmake -G"MSYS Makefiles" -DCMAKE_BUILD_TYPE=Debug -DSYSTEM3_SDL_VERSION=2 ../../ $ make ``` +For SDL3, install `mingw-w64-ucrt-x86_64-sdl3 mingw-w64-ucrt-x86_64-sdl3-ttf` +and use `-DSYSTEM3_SDL_VERSION=3`. + ### Windows (Microsoft Visual Studio) - Install Visual Studio 2026 with the **Desktop development with C++** @@ -168,14 +177,14 @@ $ make - Wait until the Output window reports `CMake generation finished.` - Select **Build > Build All** to build the project. -The executable and its runtime DLLs are generated under -`out/build/x64-Debug`: +The executable and its runtime DLLs are generated under the build directory +(`out/build/x64-Debug` for the default configuration): - `system3.exe` - `SDL2.dll` - `SDL2_ttf.dll` -To run a game, copy all three files to the game folder and run `system3.exe`. +To run a game, copy these files to the game folder, then run `system3.exe`. ### Emscripten @@ -183,10 +192,12 @@ To run a game, copy all three files to the game folder and run `system3.exe`. $ git submodule update --init $ mkdir -p out/wasm $ cd out/wasm -$ emcmake cmake -DCMAKE_BUILD_TYPE=Release ../../ +$ emcmake cmake -DCMAKE_BUILD_TYPE=Release -DSYSTEM3_SDL_VERSION=2 ../../ $ make ``` +For SDL3, use `-DSYSTEM3_SDL_VERSION=3`. + ### Android See [android/README.md](android/README.md). diff --git a/src/common.h b/src/common.h index ab1f351..6f54c56 100644 --- a/src/common.h +++ b/src/common.h @@ -41,9 +41,4 @@ enum CustomEvent { DISABLE_CD_MENU, }; -// resource.cpp -struct SDL_RWops; -SDL_RWops* open_resource(const char* name, const char* type); -SDL_RWops* open_file(const char* name); - #endif diff --git a/src/debugger/dap_frontend.cpp b/src/debugger/dap_frontend.cpp index 0a404d9..53dc1b5 100644 --- a/src/debugger/dap_frontend.cpp +++ b/src/debugger/dap_frontend.cpp @@ -3,7 +3,7 @@ #include "debugger/frontend.h" #include #include -#include +#include "sdl_compat.h" #include "nlohmann/json.hpp" #include "common.h" #include "encoding.h" diff --git a/src/emscripten/mako.cpp b/src/emscripten/mako.cpp index bf9fe80..f4bdffb 100644 --- a/src/emscripten/mako.cpp +++ b/src/emscripten/mako.cpp @@ -4,7 +4,7 @@ */ #include -#include +#include "sdl_compat.h" #include #include "mako.h" #include "fm/mako_ymfm.h" @@ -118,7 +118,7 @@ void MAKO::play_pcm(int page, int loops) std::vector buffer = amse.load(page); if (!buffer.empty()) { // AMSE形式 (乙女戦記) - uint32_t amse_size = SDL_SwapLE32(*reinterpret_cast(&buffer[8])); + uint32_t amse_size = sdl::Swap32LE(*reinterpret_cast(&buffer[8])); int samples = (amse_size - 12) * 2; int total = samples + 0x24; diff --git a/src/generic/mako.cpp b/src/generic/mako.cpp index 0eccf35..46e4c37 100644 --- a/src/generic/mako.cpp +++ b/src/generic/mako.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include "sdl_compat.h" #include "mako.h" #include "mako_midi.h" diff --git a/src/generic/mako_sdl3.cpp b/src/generic/mako_sdl3.cpp new file mode 100644 index 0000000..5ad7447 --- /dev/null +++ b/src/generic/mako_sdl3.cpp @@ -0,0 +1,462 @@ +/* + ALICE SOFT SYSTEM 3 for Win32 + + [ MAKO ] +*/ + +#include +#include +#include +#include +#include "sdl_compat.h" + +#include "mako.h" +#include "mako_midi.h" +#include "music_decoder.h" +#include "fm/mako_ymfm.h" +#include "config.h" +#include "dri.h" +#include "game_id.h" + +namespace { + +// Plays a single BGM file (MP3, OGG, or WAV, chosen by file extension), +// decoding on demand into an audio stream bound to the playback device. +class Music { +public: + // loops: number of times to play; 0 means loop forever. + Music(SDL_AudioDeviceID device, const std::string& path, int loops) + : decoder(create_music_decoder(path)), loops_(loops) + { + if (!decoder) + return; + const SDL_AudioSpec& src_spec = decoder->spec(); + SDL_AudioSpec device_spec; + SDL_GetAudioDeviceFormat(device, &device_spec, nullptr); + stream = SDL_CreateAudioStream(&src_spec, &device_spec); + if (!stream) { + WARNING("SDL_CreateAudioStream failed: %s", SDL_GetError()); + return; + } + playing = true; + SDL_SetAudioStreamGetCallback(stream, [](void* self, SDL_AudioStream*, int additional_amount, int) { + static_cast(self)->audio_callback(additional_amount); + }, this); + SDL_BindAudioStream(device, stream); + } + + ~Music() + { + if (stream) + SDL_DestroyAudioStream(stream); + } + + bool is_open() const { return stream != nullptr; } + + bool is_playing() const + { + SDL_LockAudioStream(stream); + bool result = playing; + SDL_UnlockAudioStream(stream); + return result; + } + +private: + void audio_callback(int additional_amount) + { + if (!playing) + return; + + while (SDL_GetAudioStreamAvailable(stream) < additional_amount && !input_finished) + decode(); + if (input_finished && SDL_GetAudioStreamAvailable(stream) <= 0) + playing = false; + } + + // Decodes and queues one chunk, handling EOF and decoder errors. + void decode() + { + constexpr int CHUNK_FRAMES = 1024; + + DecodedChunk chunk = decoder->decode(CHUNK_FRAMES); + + if (chunk.frames == 0) { + if (loops_ && --loops_ == 0) { + SDL_FlushAudioStream(stream); + input_finished = true; + } else { + decoder->seek_start(); + } + return; + } + const SDL_AudioSpec& spec = decoder->spec(); + int bytes = chunk.frames * SDL_AUDIO_BITSIZE(spec.format) / 8 * spec.channels; + if (!SDL_PutAudioStreamData(stream, chunk.data, bytes)) { + WARNING("SDL_PutAudioStreamData failed: %s", SDL_GetError()); + input_finished = true; + } + } + + std::unique_ptr decoder; + SDL_AudioStream* stream = nullptr; + int loops_; // number of times to play, 0 for infinite loop + bool playing = false; + bool input_finished = false; +}; + +#ifdef _WIN32 +// Per-game mapping from music numbers to CD tracks. This is necessary to +// forcibly change the sound device with a menu command. +// +// When the game uses "Z 100+x,y" command, the xth element of the array should +// be y. The array must be terminated with -1. +const int8_t RANCE41_tracks[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,1,-1}; +const int8_t RANCE42_tracks[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,1,-1}; +const int8_t DPSALL_tracks[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,5,1,2,3,-1}; +#endif + +class FmStream { +public: + FmStream(SDL_AudioDeviceID device, std::vector data) + : FmStream(device, std::move(data), device_format(device)) {} + ~FmStream() { SDL_DestroyAudioStream(stream); } // unbinds and stops fill() + + // FM music keeps playing until every channel has looped. + bool is_playing() { int mark, loop; get_mark(&mark, &loop); return !loop; } + + void get_mark(int* mark, int* loop) { + SDL_LockAudioStream(stream); + ymfm.get_mark(mark, loop); + SDL_UnlockAudioStream(stream); + } + +private: + FmStream(SDL_AudioDeviceID device, std::vector data, const SDL_AudioSpec& device_spec) + : ymfm(device_spec.freq, std::move(data)) + { + SDL_AudioSpec src_spec = { SDL_AUDIO_S16, 2, device_spec.freq }; + stream = SDL_CreateAudioStream(&src_spec, &device_spec); + SDL_SetAudioStreamGetCallback(stream, [](void* self, SDL_AudioStream*, int additional_amount, int) { + static_cast(self)->fill(additional_amount); + }, this); + SDL_BindAudioStream(device, stream); + } + + static SDL_AudioSpec device_format(SDL_AudioDeviceID device) { + SDL_AudioSpec spec; + SDL_GetAudioDeviceFormat(device, &spec, nullptr); + return spec; + } + + void fill(int additional_amount) { + const int CHUNK = 4096; // bytes; 1024 stereo S16 frames + int16_t buffer[CHUNK / 2]; + while (additional_amount > 0) { + int len = additional_amount < CHUNK ? additional_amount : CHUNK; + ymfm.Process(buffer, len / 4); + SDL_PutAudioStreamData(stream, buffer, len); + additional_amount -= len; + } + } + + MakoYmfm ymfm; + SDL_AudioStream* stream; +}; + +// The audio device. Each sound source (music, fm, pcm) creates its own +// SDL_AudioStream and binds it to this device. +SDL_AudioDeviceID g_device; + +std::unique_ptr music; +std::unique_ptr fm; +std::unique_ptr midi; + +// PCM playback. pcm_loops is the number of remaining plays, or -1 for an +// infinite loop. pcm_stream's get-callback re-feeds pcm_src. +SDL_AudioStream* pcm_stream; +std::vector pcm_src; +int pcm_loops; +bool pcm_input_finished; + +void SDLCALL pcm_audio_callback(void*, SDL_AudioStream* stream, int additional_amount, int /*total_amount*/) +{ + while (SDL_GetAudioStreamAvailable(stream) < additional_amount && !pcm_input_finished) { + if (pcm_loops == 0) { + SDL_FlushAudioStream(stream); + pcm_input_finished = true; + break; + } + SDL_PutAudioStreamData(stream, pcm_src.data(), static_cast(pcm_src.size())); + if (pcm_loops > 0) + pcm_loops--; + } +} + +} // namespace + +MAKO::MAKO(const Config& config, const GameId& game_id) : + use_fm(config.use_fm), + current_music(0), + next_loop(0), + game_id(game_id) +{ + if (!config.playlist.empty()) + load_playlist(config.playlist.c_str()); + +#ifdef _WIN32 + if (!is_cd_available()) { + SDL_Event event = {}; + event.user.type = sdl_custom_event_type; + event.user.code = DISABLE_CD_MENU; + SDL_PushEvent(&event); + } +#endif + + amus.open("AMUS.DAT"); + awav.open("AWAV.DAT"); + amse.open("AMSE.DAT"); + mda.open("AMUS.MDA"); + for (int i = 1; i <= 99; i++) + cd_track[i] = 0; + + SDL_InitSubSystem(SDL_INIT_AUDIO); + g_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr); + if (!g_device) + WARNING("Cannot open audio device: %s", SDL_GetError()); + + midi = std::make_unique(config.midi_device); + if (!midi->is_available()) + use_fm = true; +} + +MAKO::~MAKO() +{ + stop_music(); + stop_pcm(); + midi.reset(); + + if (g_device) + SDL_CloseAudioDevice(g_device); + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +bool MAKO::load_playlist(const char* path) +{ + FILE* fp = fopen(path, "r"); + if (!fp) { + WARNING("Cannot open %s", path); + return false; + } + char buf[256]; + while (fgets(buf, sizeof(buf) - 1, fp)) { + for (char *p = buf; *p; p++) { + if (*p == '\\') + *p = '/'; + else if (*p == '\r' || *p == '\n') + *p = '\0'; + } + playlist.push_back(buf[0] ? strdup(buf) : NULL); + } + fclose(fp); + return true; +} + +void MAKO::play_music(int page) +{ + if (current_music == page) + return; + + stop_music(); + + size_t track = page < 100 ? cd_track[page] : 0; + if (track && is_cd_available()) { + if (track >= playlist.size() || !playlist[track]) + return; + const char* file = playlist[track]; +#ifdef __ANDROID__ + // dr_mp3/stb_vorbis open the file via fopen, which requires an absolute + // path on Android. + char abspath[PATH_MAX]; + if (!realpath(file, abspath)) + return; + file = abspath; +#endif + music = std::make_unique(g_device, file, next_loop); + if (!music->is_open()) { + music.reset(); + return; + } + } else if (use_fm) { + std::vector data = amus.load(page); + if (data.empty()) + return; + fm = std::make_unique(g_device, std::move(data)); + } else if (midi->is_available()) { + if (!midi->play(game_id, amus, mda, page, next_loop)) + return; + } + current_music = page; + next_loop = 0; +} + +void MAKO::stop_music() +{ + music.reset(); + fm.reset(); + if (midi->is_available()) + midi->stop(); + current_music = 0; +} + +bool MAKO::check_music() +{ + if (fm) + return fm->is_playing(); + if (music) + return music->is_playing(); + return midi->is_playing(); +} + +#ifdef _WIN32 +void MAKO::select_sound(BGMDevice dev) +{ + int page = current_music; + int old_dev = (1 <= page && page <= 99 && cd_track[page]) ? BGM_CD : + use_fm ? BGM_FM : BGM_MIDI; + + switch (dev) { + case BGM_FM: + case BGM_MIDI: + for (int i = 1; i <= 99; i++) + cd_track[i] = 0; + if (midi->is_available()) + use_fm = dev == BGM_FM; + else + dev = BGM_FM; + break; + + case BGM_CD: + const int8_t* tracks; + + switch (game_id.game) { + case GameId::RANCE41: + tracks = RANCE41_tracks; + break; + case GameId::RANCE42: + tracks = RANCE42_tracks; + break; + case GameId::DPS_ALL: + tracks = DPSALL_tracks; + break; + + // For the following games, the default mapping (cd_track[i] = i) works. + case GameId::AYUMI_CD: + case GameId::FUNNYBEE_CD: + case GameId::ONLYYOU: + default: + tracks = nullptr; + } + + if (tracks) { + for (int i = 0; tracks[i] >= 0; i++) + cd_track[i + 1] = tracks[i]; + } else { + for (int i = 1; i <= 99; i++) + cd_track[i] = i; + } + break; + } + + if (dev != old_dev && page) { + stop_music(); + play_music(page); + } +} +#endif + +void MAKO::get_mark(int* mark, int* loop) +{ + if (fm) { + fm->get_mark(mark, loop); + return; + } + midi->get_mark(mark, loop); +} + +void MAKO::play_pcm(int page, int loops) +{ + stop_pcm(); + + SDL_AudioSpec device_spec; + SDL_GetAudioDeviceFormat(g_device, &device_spec, nullptr); + + SDL_AudioStream* stream = nullptr; + std::vector src; + + // WAV形式 (Only You) + std::vector data = awav.load(page); + if (!data.empty()) { + SDL_AudioSpec spec; + Uint8* wav; + Uint32 wavlen; + if (!SDL_LoadWAV_IO(SDL_IOFromConstMem(data.data(), static_cast(data.size())), 1, &spec, &wav, &wavlen)) { + WARNING("SDL_LoadWAV_IO failed: %s", SDL_GetError()); + return; + } + src.assign(wav, wav + wavlen); + SDL_free(wav); + stream = SDL_CreateAudioStream(&spec, &device_spec); + } else { + // AMSE形式 (乙女戦記) + data = amse.load(page); + if (data.empty()) + return; + uint32_t amse_size = SDL_Swap32LE(*reinterpret_cast(&data[8])); + // 4-bit PCM -> 8-bit PCM, mono, 8000Hz + for (uint32_t i = 12; i < amse_size; i++) { + src.push_back(data[i] & 0xf0); + src.push_back((data[i] & 0x0f) << 4); + } + SDL_AudioSpec src_spec = { SDL_AUDIO_U8, 1, 8000 }; + stream = SDL_CreateAudioStream(&src_spec, &device_spec); + } + if (!stream) { + WARNING("SDL_CreateAudioStream failed: %s", SDL_GetError()); + return; + } + + pcm_src = std::move(src); + pcm_loops = loops ? loops : -1; + pcm_input_finished = false; + pcm_stream = stream; + SDL_SetAudioStreamGetCallback(pcm_stream, pcm_audio_callback, nullptr); + SDL_BindAudioStream(g_device, pcm_stream); +} + +void MAKO::stop_pcm() +{ + // Destroy the stream first (unbinds and stops its get-callback), then it is + // safe to drop the source buffer the callback was reading. + if (pcm_stream) { + SDL_DestroyAudioStream(pcm_stream); + pcm_stream = nullptr; + } + pcm_src.clear(); + pcm_input_finished = false; +} + +bool MAKO::check_pcm() +{ + if (!pcm_stream) + return false; + SDL_LockAudioStream(pcm_stream); + bool playing = pcm_stream && + (!pcm_input_finished || SDL_GetAudioStreamAvailable(pcm_stream) > 0); + SDL_UnlockAudioStream(pcm_stream); + return playing; +} + +bool MAKO::is_cd_available() const +{ + return !playlist.empty(); +} diff --git a/src/resource.cpp b/src/resource.cpp index 80d992a..337fe6c 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -1,12 +1,14 @@ #include +#include +#include #ifdef _WIN32 #include #undef ERROR #endif -#include #include "common.h" +#include "sdl_compat.h" -SDL_RWops* open_resource(const char* name, const char* type) { +sdl::IOStream* open_resource(const char* name, const char* type) { #ifdef _WIN32 // On Windows, read from resource. HINSTANCE hInst = GetModuleHandle(NULL); @@ -16,25 +18,25 @@ SDL_RWops* open_resource(const char* name, const char* type) { WARNING("Cannot load resource %s (type: %s)", name, type); return NULL; } - return SDL_RWFromConstMem(LockResource(hGlobal), SizeofResource(hInst, hRes)); + return sdl::IOFromConstMem(LockResource(hGlobal), SizeofResource(hInst, hRes)); #else // On Android, read from APK assets. // On other platforms, read from a file under RESOURCE_PATH. char path[PATH_MAX]; snprintf(path, PATH_MAX, "%s%s/%s", RESOURCE_PATH, type, name); - return SDL_RWFromFile(path, "rb"); + return sdl::IOFromFile(path, "rb"); #endif } -SDL_RWops* open_file(const char* name) { +sdl::IOStream* open_file(const char* name) { #ifdef __ANDROID__ - // We cannot use SDL_RWFromFile() because it does not resolve relative - // paths using the current directory on Android. - FILE *fp = fopen(name, "rb"); - if (!fp) + // SDL_IOFromFile() treats relative paths as APK assets on Android, so use + // an absolute path to open files relative to the current game directory. + char path[PATH_MAX]; + if (!realpath(name, path)) return NULL; - return SDL_RWFromFP(fp, SDL_TRUE); + return sdl::IOFromFile(path, "rb"); #else - return SDL_RWFromFile(name, "rb"); + return sdl::IOFromFile(name, "rb"); #endif } diff --git a/src/sdl_compat.h b/src/sdl_compat.h new file mode 100644 index 0000000..025ee80 --- /dev/null +++ b/src/sdl_compat.h @@ -0,0 +1,559 @@ +#ifndef SYSTEM3_SDL_COMPAT_H_ +#define SYSTEM3_SDL_COMPAT_H_ + +#include +#include + +#ifndef SYSTEM3_SDL_VERSION +#define SYSTEM3_SDL_VERSION 2 +#endif + +#if SYSTEM3_SDL_VERSION == 2 +#include +#elif SYSTEM3_SDL_VERSION == 3 +#include +#else +#error "Unsupported SYSTEM3_SDL_VERSION" +#endif + +static_assert(SDL_MAJOR_VERSION == SYSTEM3_SDL_VERSION, + "The selected SDL headers do not match SYSTEM3_SDL_VERSION"); + +// windows.h defines CreateWindow as a macro. +#ifdef CreateWindow +#undef CreateWindow +#endif + +// Compatibility aliases and wrappers use SDL3 names when SDL2 and SDL3 use +// different names. +namespace sdl { + +#if SYSTEM3_SDL_VERSION == 2 +using Gamepad = SDL_GameController; +using PixelFormat = SDL_PixelFormatEnum; +using Mutex = SDL_mutex; +using AtomicInt = SDL_atomic_t; +using IOStream = SDL_RWops; +using GamepadButton = SDL_GameControllerButton; +using GamepadAxis = SDL_GameControllerAxis; + +inline constexpr Uint32 INIT_GAMEPAD = SDL_INIT_GAMECONTROLLER; +inline constexpr Uint32 EVENT_QUIT = SDL_QUIT; +inline constexpr Uint32 EVENT_KEY_UP = SDL_KEYUP; +inline constexpr Uint32 EVENT_MOUSE_MOTION = SDL_MOUSEMOTION; +inline constexpr Uint32 EVENT_MOUSE_WHEEL = SDL_MOUSEWHEEL; +inline constexpr Uint32 EVENT_FINGER_DOWN = SDL_FINGERDOWN; +inline constexpr Uint32 EVENT_FINGER_UP = SDL_FINGERUP; +inline constexpr Uint32 EVENT_FINGER_MOTION = SDL_FINGERMOTION; +inline constexpr SDL_WindowFlags WINDOW_FULLSCREEN = SDL_WINDOW_FULLSCREEN_DESKTOP; +inline constexpr SDL_AudioFormat AudioS16 = AUDIO_S16SYS; +inline constexpr GamepadButton GAMEPAD_BUTTON_DPAD_UP = SDL_CONTROLLER_BUTTON_DPAD_UP; +inline constexpr GamepadButton GAMEPAD_BUTTON_DPAD_DOWN = SDL_CONTROLLER_BUTTON_DPAD_DOWN; +inline constexpr GamepadButton GAMEPAD_BUTTON_DPAD_LEFT = SDL_CONTROLLER_BUTTON_DPAD_LEFT; +inline constexpr GamepadButton GAMEPAD_BUTTON_DPAD_RIGHT = SDL_CONTROLLER_BUTTON_DPAD_RIGHT; +inline constexpr GamepadButton GAMEPAD_BUTTON_SOUTH = SDL_CONTROLLER_BUTTON_A; +inline constexpr GamepadButton GAMEPAD_BUTTON_EAST = SDL_CONTROLLER_BUTTON_B; +inline constexpr GamepadButton GAMEPAD_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X; +inline constexpr GamepadButton GAMEPAD_BUTTON_NORTH = SDL_CONTROLLER_BUTTON_Y; +inline constexpr GamepadAxis GAMEPAD_AXIS_LEFTX = SDL_CONTROLLER_AXIS_LEFTX; +inline constexpr GamepadAxis GAMEPAD_AXIS_LEFTY = SDL_CONTROLLER_AXIS_LEFTY; + +#else +using Gamepad = SDL_Gamepad; +using PixelFormat = SDL_PixelFormat; +using Mutex = SDL_Mutex; +using AtomicInt = SDL_AtomicInt; +using IOStream = SDL_IOStream; +using GamepadButton = SDL_GamepadButton; +using GamepadAxis = SDL_GamepadAxis; + +inline constexpr Uint32 INIT_GAMEPAD = SDL_INIT_GAMEPAD; +inline constexpr Uint32 EVENT_QUIT = SDL_EVENT_QUIT; +inline constexpr Uint32 EVENT_KEY_UP = SDL_EVENT_KEY_UP; +inline constexpr Uint32 EVENT_MOUSE_MOTION = SDL_EVENT_MOUSE_MOTION; +inline constexpr Uint32 EVENT_MOUSE_WHEEL = SDL_EVENT_MOUSE_WHEEL; +inline constexpr Uint32 EVENT_FINGER_DOWN = SDL_EVENT_FINGER_DOWN; +inline constexpr Uint32 EVENT_FINGER_UP = SDL_EVENT_FINGER_UP; +inline constexpr Uint32 EVENT_FINGER_MOTION = SDL_EVENT_FINGER_MOTION; +inline constexpr SDL_WindowFlags WINDOW_FULLSCREEN = SDL_WINDOW_FULLSCREEN; +inline constexpr SDL_AudioFormat AudioS16 = SDL_AUDIO_S16; +inline constexpr GamepadButton GAMEPAD_BUTTON_DPAD_UP = SDL_GAMEPAD_BUTTON_DPAD_UP; +inline constexpr GamepadButton GAMEPAD_BUTTON_DPAD_DOWN = SDL_GAMEPAD_BUTTON_DPAD_DOWN; +inline constexpr GamepadButton GAMEPAD_BUTTON_DPAD_LEFT = SDL_GAMEPAD_BUTTON_DPAD_LEFT; +inline constexpr GamepadButton GAMEPAD_BUTTON_DPAD_RIGHT = SDL_GAMEPAD_BUTTON_DPAD_RIGHT; +inline constexpr GamepadButton GAMEPAD_BUTTON_SOUTH = SDL_GAMEPAD_BUTTON_SOUTH; +inline constexpr GamepadButton GAMEPAD_BUTTON_EAST = SDL_GAMEPAD_BUTTON_EAST; +inline constexpr GamepadButton GAMEPAD_BUTTON_WEST = SDL_GAMEPAD_BUTTON_WEST; +inline constexpr GamepadButton GAMEPAD_BUTTON_NORTH = SDL_GAMEPAD_BUTTON_NORTH; +inline constexpr GamepadAxis GAMEPAD_AXIS_LEFTX = SDL_GAMEPAD_AXIS_LEFTX; +inline constexpr GamepadAxis GAMEPAD_AXIS_LEFTY = SDL_GAMEPAD_AXIS_LEFTY; +#endif + +inline IOStream* IOFromFile(const char* path, const char* mode) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_RWFromFile(path, mode); +#else + return SDL_IOFromFile(path, mode); +#endif +} + +inline IOStream* IOFromConstMem(const void* data, size_t size) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_RWFromConstMem(data, static_cast(size)); +#else + return SDL_IOFromConstMem(data, size); +#endif +} + +inline size_t ReadIO(IOStream* stream, void* destination, size_t bytes) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_RWread(stream, destination, 1, bytes); +#else + return SDL_ReadIO(stream, destination, bytes); +#endif +} + +inline int64_t SeekIO(IOStream* stream, int64_t offset, int whence) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_RWseek(stream, offset, whence); +#else + return SDL_SeekIO(stream, offset, static_cast(whence)); +#endif +} + +inline bool CloseIO(IOStream* stream) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_RWclose(stream) == 0; +#else + return SDL_CloseIO(stream); +#endif +} + +inline int SetAtomicInt(AtomicInt* value, int desired) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_AtomicSet(value, desired); +#else + return SDL_SetAtomicInt(value, desired); +#endif +} + +inline int GetAtomicInt(AtomicInt* value) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_AtomicGet(value); +#else + return SDL_GetAtomicInt(value); +#endif +} + +inline bool CompareAndSwapAtomicInt(AtomicInt* value, int expected, int desired) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_AtomicCAS(value, expected, desired) == SDL_TRUE; +#else + return SDL_CompareAndSwapAtomicInt(value, expected, desired); +#endif +} + +inline bool SetCurrentThreadPriority(SDL_ThreadPriority priority) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_SetThreadPriority(priority) == 0; +#else + return SDL_SetCurrentThreadPriority(priority); +#endif +} + +inline int GetNumTouchFingers(SDL_TouchID touch_id) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_GetNumTouchFingers(touch_id); +#else + int count = 0; + SDL_Finger** fingers = SDL_GetTouchFingers(touch_id, &count); + SDL_free(fingers); + return count; +#endif +} + +inline SDL_TouchID GetTouchID(const SDL_TouchFingerEvent& event) +{ +#if SYSTEM3_SDL_VERSION == 2 + return event.touchId; +#else + return event.touchID; +#endif +} + +inline SDL_Scancode GetKeyScancode(const SDL_KeyboardEvent& event) +{ +#if SYSTEM3_SDL_VERSION == 2 + return event.keysym.scancode; +#else + return event.scancode; +#endif +} + +inline bool RenderCoordinatesFromWindow(SDL_Renderer* renderer, float window_x, + float window_y, float* x, float* y) +{ +#if SYSTEM3_SDL_VERSION == 2 + *x = window_x; + *y = window_y; + return true; +#else + return SDL_RenderCoordinatesFromWindow(renderer, window_x, window_y, x, y); +#endif +} + +inline bool RenderCoordinatesToWindow(SDL_Renderer* renderer, SDL_Window* window, + int* x, int* y) +{ +#if SYSTEM3_SDL_VERSION == 2 + float scale_x, scale_y; + SDL_RenderGetScale(renderer, &scale_x, &scale_y); + *x *= scale_x; + *y *= scale_y; + + int logical_width, logical_height; + SDL_RenderGetLogicalSize(renderer, &logical_width, &logical_height); + + int window_width, window_height; + SDL_GetWindowSize(window, &window_width, &window_height); + + *x += (window_width - logical_width * scale_x) / 2; + *y += (window_height - logical_height * scale_y) / 2; + return true; +#else + (void)window; + float window_x, window_y; + bool result = SDL_RenderCoordinatesToWindow( + renderer, *x, *y, &window_x, &window_y); + *x = window_x; + *y = window_y; + return result; +#endif +} + +inline bool GetGamepadButton(Gamepad* gamepad, GamepadButton button) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_GameControllerGetButton(gamepad, button) != 0; +#else + return SDL_GetGamepadButton(gamepad, button); +#endif +} + +inline Sint16 GetGamepadAxis(Gamepad* gamepad, GamepadAxis axis) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_GameControllerGetAxis(gamepad, axis); +#else + return SDL_GetGamepadAxis(gamepad, axis); +#endif +} + +inline bool ShowMessageBox(const SDL_MessageBoxData* data, int* button_id) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_ShowMessageBox(data, button_id) == 0; +#else + return SDL_ShowMessageBox(data, button_id); +#endif +} + +inline void HideCursor() +{ +#if SYSTEM3_SDL_VERSION == 2 + SDL_ShowCursor(SDL_DISABLE); +#else + SDL_HideCursor(); +#endif +} + +inline void ShowCursor() +{ +#if SYSTEM3_SDL_VERSION == 2 + SDL_ShowCursor(SDL_ENABLE); +#else + SDL_ShowCursor(); +#endif +} + +inline bool SaveBMP(SDL_Surface* surface, const char* path) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_SaveBMP(surface, path) == 0; +#else + return SDL_SaveBMP(surface, path); +#endif +} + +inline void FreeWAV(Uint8* data) +{ +#if SYSTEM3_SDL_VERSION == 2 + SDL_FreeWAV(data); +#else + SDL_free(data); +#endif +} + +inline Uint32 Swap32LE(Uint32 value) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_SwapLE32(value); +#else + return SDL_Swap32LE(value); +#endif +} + +inline SDL_Surface* CreateSurface(int width, int height, PixelFormat format) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_CreateRGBSurfaceWithFormat(0, width, height, + SDL_BITSPERPIXEL(format), format); +#else + SDL_Surface* surface = SDL_CreateSurface(width, height, format); + if (surface && format == SDL_PIXELFORMAT_INDEX8) + SDL_CreateSurfacePalette(surface); + return surface; +#endif +} + +inline SDL_Surface* CreateSurfaceWithMasks(int width, int height, int depth, + Uint32 red, Uint32 green, Uint32 blue, Uint32 alpha) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_CreateRGBSurface(0, width, height, depth, red, green, blue, alpha); +#else + return SDL_CreateSurface(width, height, + SDL_GetPixelFormatForMasks(depth, red, green, blue, alpha)); +#endif +} + +inline void DestroySurface(SDL_Surface* surface) +{ +#if SYSTEM3_SDL_VERSION == 2 + SDL_FreeSurface(surface); +#else + SDL_DestroySurface(surface); +#endif +} + +inline SDL_Palette* GetSurfacePalette(SDL_Surface* surface) +{ +#if SYSTEM3_SDL_VERSION == 2 + return surface->format->palette; +#else + return SDL_GetSurfacePalette(surface); +#endif +} + +inline SDL_Window* CreateWindow(const char* title, int width, int height, + SDL_WindowFlags flags) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + width, height, flags); +#else + // SDL3 uses physical pixels for window coordinates on platforms such as + // Windows. Scale the requested content size so that it has the same + // apparent size on a high-DPI display. On platforms whose window + // coordinates are already logical (macOS and Wayland), the content scale is + // 1 and HIGH_PIXEL_DENSITY provides the correspondingly larger backbuffer. + float scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + if (scale <= 0.0f) + scale = 1.0f; + width = static_cast(width * scale + 0.5f); + height = static_cast(height * scale + 0.5f); + return SDL_CreateWindow(title, width, height, + flags | SDL_WINDOW_HIGH_PIXEL_DENSITY); +#endif +} + +inline void SetWindowContentSize(SDL_Window* window, int width, int height) +{ +#if SYSTEM3_SDL_VERSION == 2 + SDL_SetWindowSize(window, width, height); +#else + // Convert content units to window coordinates. Dividing the window's + // display scale by its pixel density gives the platform content scale. + float pixel_density = SDL_GetWindowPixelDensity(window); + float scale = SDL_GetWindowDisplayScale(window); + if (pixel_density > 0.0f && scale > 0.0f) + scale /= pixel_density; + else + scale = 1.0f; + SDL_SetWindowSize(window, + static_cast(width * scale + 0.5f), + static_cast(height * scale + 0.5f)); +#endif +} + +inline bool SetWindowFullscreen(SDL_Window* window, bool fullscreen) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_SetWindowFullscreen( + window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0) == 0; +#else + return SDL_SetWindowFullscreen(window, fullscreen); +#endif +} + +inline SDL_Renderer* CreateRenderer(SDL_Window* window) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_CreateRenderer(window, -1, 0); +#else + return SDL_CreateRenderer(window, nullptr); +#endif +} + +inline bool SetRenderLogicalPresentation(SDL_Renderer* renderer, int width, int height) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_RenderSetLogicalSize(renderer, width, height) == 0; +#else + return SDL_SetRenderLogicalPresentation(renderer, width, height, + SDL_LOGICAL_PRESENTATION_LETTERBOX); +#endif +} + +inline SDL_Palette* CreatePalette(int colors) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_AllocPalette(colors); +#else + return SDL_CreatePalette(colors); +#endif +} + +inline void DestroyPalette(SDL_Palette* palette) +{ +#if SYSTEM3_SDL_VERSION == 2 + SDL_FreePalette(palette); +#else + SDL_DestroyPalette(palette); +#endif +} + +inline void DestroyCursor(SDL_Cursor* cursor) +{ +#if SYSTEM3_SDL_VERSION == 2 + SDL_FreeCursor(cursor); +#else + SDL_DestroyCursor(cursor); +#endif +} + +inline bool GetRectIntersection(const SDL_Rect* a, const SDL_Rect* b, SDL_Rect* result) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_IntersectRect(a, b, result) == SDL_TRUE; +#else + return SDL_GetRectIntersection(a, b, result); +#endif +} + +inline void GetRectUnion(const SDL_Rect* a, const SDL_Rect* b, SDL_Rect* result) +{ +#if SYSTEM3_SDL_VERSION == 2 + SDL_UnionRect(a, b, result); +#else + SDL_GetRectUnion(a, b, result); +#endif +} + +inline bool RenderTexture(SDL_Renderer* renderer, SDL_Texture* texture, + const SDL_Rect* source, const SDL_Rect* destination) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_RenderCopy(renderer, texture, source, destination) == 0; +#else + SDL_FRect src; + SDL_FRect dst; + const SDL_FRect* src_ptr = nullptr; + const SDL_FRect* dst_ptr = nullptr; + if (source) { + src = { static_cast(source->x), static_cast(source->y), + static_cast(source->w), static_cast(source->h) }; + src_ptr = &src; + } + if (destination) { + dst = { static_cast(destination->x), static_cast(destination->y), + static_cast(destination->w), static_cast(destination->h) }; + dst_ptr = &dst; + } + return SDL_RenderTexture(renderer, texture, src_ptr, dst_ptr); +#endif +} + +inline bool SetSurfaceColorKey(SDL_Surface* surface, bool enabled, Uint32 key) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_SetColorKey(surface, enabled ? SDL_TRUE : SDL_FALSE, key) == 0; +#else + return SDL_SetSurfaceColorKey(surface, enabled, key); +#endif +} + +inline bool SurfaceHasColorKey(SDL_Surface* surface) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_HasColorKey(surface) == SDL_TRUE; +#else + return SDL_SurfaceHasColorKey(surface); +#endif +} + +inline bool GetSurfaceColorKey(SDL_Surface* surface, Uint32* key) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_GetColorKey(surface, key) == 0; +#else + return SDL_GetSurfaceColorKey(surface, key); +#endif +} + +inline bool StretchSurface(SDL_Surface* source, const SDL_Rect* source_rect, + SDL_Surface* destination, SDL_Rect* destination_rect) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_SoftStretch(source, source_rect, destination, destination_rect) == 0; +#else + return SDL_StretchSurface(source, source_rect, destination, destination_rect, + SDL_SCALEMODE_NEAREST); +#endif +} + +inline bool FillSurfaceRect(SDL_Surface* surface, const SDL_Rect* rect, Uint32 color) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_FillRect(surface, rect, color) == 0; +#else + return SDL_FillSurfaceRect(surface, rect, color); +#endif +} + +inline bool FillSurfaceRects(SDL_Surface* surface, const SDL_Rect* rects, + int count, Uint32 color) +{ +#if SYSTEM3_SDL_VERSION == 2 + return SDL_FillRects(surface, rects, count, color) == 0; +#else + return SDL_FillSurfaceRects(surface, rects, count, color); +#endif +} + +} // namespace sdl + +sdl::IOStream* open_resource(const char* name, const char* type); +sdl::IOStream* open_file(const char* name); + +#endif // SYSTEM3_SDL_COMPAT_H_ diff --git a/src/sdlmain.cpp b/src/sdlmain.cpp index 352c0a9..da7dced 100644 --- a/src/sdlmain.cpp +++ b/src/sdlmain.cpp @@ -1,3 +1,6 @@ +#if SYSTEM3_SDL_VERSION == 3 +#include +#endif #include #include "common.h" #include "config.h" @@ -35,7 +38,9 @@ SDL_Window* create_window(const Config& config, const GameId& game_id) SDL_Init(SDL_INIT_VIDEO); SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); +#if SYSTEM3_SDL_VERSION == 2 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); +#endif #ifdef __EMSCRIPTEN__ // Stop SDL from calling emscripten_sleep() in functions that are called // indirectly, which does not work with ASYNCIFY_IGNORE_INDIRECT=1. For @@ -46,9 +51,10 @@ SDL_Window* create_window(const Config& config, const GameId& game_id) #ifdef __ANDROID__ SDL_SetHint(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1"); SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight"); - Uint32 flags = SDL_WINDOW_FULLSCREEN; + SDL_WindowFlags flags = SDL_WINDOW_FULLSCREEN; #else - Uint32 flags = config.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE; + SDL_WindowFlags flags = config.fullscreen ? + sdl::WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE; #endif #ifdef __EMSCRIPTEN__ @@ -57,7 +63,7 @@ SDL_Window* create_window(const Config& config, const GameId& game_id) #else const char *window_title = title.c_str(); #endif - return SDL_CreateWindow(window_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 400, flags); + return sdl::CreateWindow(window_title, 640, 400, flags); } } // namespace @@ -77,7 +83,7 @@ int main(int argc, char *argv[]) GameId game_id(config); g_window = create_window(config, game_id); - g_renderer = SDL_CreateRenderer(g_window, -1, 0); + g_renderer = sdl::CreateRenderer(g_window); sdl_custom_event_type = SDL_RegisterEvents(1); // system3 初期化 diff --git a/src/sys/ags.cpp b/src/sys/ags.cpp index 960aef6..217f0a4 100644 --- a/src/sys/ags.cpp +++ b/src/sys/ags.cpp @@ -20,7 +20,7 @@ const uint32 SCANLINE_ALPHA = 0x38; // 0-255 SDL_Texture* create_scanline_texture(SDL_Renderer* renderer, int width, int height) { - SDL_Surface* sf = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_ARGB8888); + SDL_Surface* sf = sdl::CreateSurface(width, height, SDL_PIXELFORMAT_ARGB8888); for (int y = 0; y < height; y++) { uint32* p = reinterpret_cast(surface_line(sf, y)); uint32 v = y % 2 ? (SCANLINE_ALPHA << 24) : 0; @@ -30,7 +30,7 @@ SDL_Texture* create_scanline_texture(SDL_Renderer* renderer, int width, int heig } SDL_Texture* tx = SDL_CreateTextureFromSurface(renderer, sf); SDL_SetTextureBlendMode(tx, SDL_BLENDMODE_BLEND); - SDL_FreeSurface(sf); + sdl::DestroySurface(sf); return tx; } @@ -48,19 +48,19 @@ AGS::AGS(const Config& config, const GameId& game_id) : game_id(game_id) window_height = screen_height = 400; } - SDL_SetWindowSize(g_window, window_width, window_height); - SDL_RenderSetLogicalSize(g_renderer, window_width, window_height); + sdl::SetWindowContentSize(g_window, window_width, window_height); + sdl::SetRenderLogicalPresentation(g_renderer, window_width, window_height); sdlTexture = SDL_CreateTexture(g_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, screen_width, screen_height); scanline_texture = NULL; // DIBSection 8bpp for(int i = 0; i < NR_SCREENS; i++) { - hBmpScreen[i] = SDL_CreateRGBSurfaceWithFormat(0, 640, 480, 8, SDL_PIXELFORMAT_INDEX8); + hBmpScreen[i] = sdl::CreateSurface(640, 480, SDL_PIXELFORMAT_INDEX8); vram[i] = reinterpret_cast(hBmpScreen[i]->pixels); } // All surfaces share the same palette. - screen_palette = hBmpScreen[SCREEN_FRONT]->format->palette; + screen_palette = sdl::GetSurfacePalette(hBmpScreen[SCREEN_FRONT]); SDL_SetSurfacePalette(hBmpScreen[SCREEN_BACK], screen_palette); if (!config.censor_list.empty()) @@ -77,17 +77,17 @@ AGS::AGS(const Config& config, const GameId& game_id) : game_id(game_id) if (!rw_font) sys_error("Cannot open default font"); } - hFont16 = TTF_OpenFontRW(rw_font, 0, 16); - SDL_RWseek(rw_font, 0, SEEK_SET); - hFont24 = TTF_OpenFontRW(rw_font, 0, 24); - SDL_RWseek(rw_font, 0, SEEK_SET); - hFont32 = TTF_OpenFontRW(rw_font, 0, 32); - SDL_RWseek(rw_font, 0, SEEK_SET); - hFont48 = TTF_OpenFontRW(rw_font, 0, 48); - SDL_RWseek(rw_font, 0, SEEK_SET); - hFont64 = TTF_OpenFontRW(rw_font, 0, 64); + hFont16 = ttf::OpenFontIO(rw_font, 16); + sdl::SeekIO(rw_font, 0, SEEK_SET); + hFont24 = ttf::OpenFontIO(rw_font, 24); + sdl::SeekIO(rw_font, 0, SEEK_SET); + hFont32 = ttf::OpenFontIO(rw_font, 32); + sdl::SeekIO(rw_font, 0, SEEK_SET); + hFont48 = ttf::OpenFontIO(rw_font, 48); + sdl::SeekIO(rw_font, 0, SEEK_SET); + hFont64 = ttf::OpenFontIO(rw_font, 64); if (!hFont16 || !hFont24 || !hFont32 || !hFont48 || !hFont64) { - sys_error("TTF_OpenFontRW failed: %s", TTF_GetError()); + sys_error("Cannot open font: %s", SDL_GetError()); } if (config.no_antialias) ags_setAntialiasedStringMode(0); @@ -120,7 +120,7 @@ AGS::AGS(const Config& config, const GameId& game_id) : game_id(game_id) acg.open("ACG.DAT"); // パレット - program_palette = SDL_AllocPalette(256); + program_palette = sdl::CreatePalette(256); program_palette->colors[0x00] = {0x00, 0x00, 0x00, 0xff}; program_palette->colors[0x01] = {0x00, 0x00, 0xaa, 0xff}; program_palette->colors[0x02] = {0xaa, 0x00, 0x00, 0xff}; @@ -199,7 +199,7 @@ AGS::~AGS() // カーソル開放 for(int i = 0; i < 10; i++) { if(hCursor[i]) { - SDL_FreeCursor(hCursor[i]); + sdl::DestroyCursor(hCursor[i]); } } @@ -210,13 +210,13 @@ AGS::~AGS() TTF_CloseFont(hFont32); TTF_CloseFont(hFont48); TTF_CloseFont(hFont64); - SDL_RWclose(rw_font); + sdl::CloseIO(rw_font); } - SDL_FreePalette(program_palette); + sdl::DestroyPalette(program_palette); for(int i = 0; i < NR_SCREENS; i++) { - SDL_FreeSurface(hBmpScreen[i]); + sdl::DestroySurface(hBmpScreen[i]); } SDL_DestroyTexture(sdlTexture); @@ -264,8 +264,8 @@ void AGS::invalidate_screen(int sx, int sy, int width, int height) { SDL_Rect rect = {sx, sy, width, height}; SDL_Rect screen_rect = {0, 0, screen_width, screen_height}; - SDL_IntersectRect(&rect, &screen_rect, &rect); - SDL_UnionRect(&dirty_rect, &rect, &dirty_rect); + sdl::GetRectIntersection(&rect, &screen_rect, &rect); + sdl::GetRectUnion(&dirty_rect, &rect, &dirty_rect); } void AGS::update_screen() @@ -288,7 +288,7 @@ void AGS::update_screen() dest.y = -scroll; src.h = dest.h = screen_height + scroll; } - SDL_RenderCopy(g_renderer, sdlTexture, &src, &dest); + sdl::RenderTexture(g_renderer, sdlTexture, &src, &dest); if (fade_level) { SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND); @@ -298,7 +298,7 @@ void AGS::update_screen() SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_NONE); } if (scanline_texture) - SDL_RenderCopy(g_renderer, scanline_texture, NULL, NULL); + sdl::RenderTexture(g_renderer, scanline_texture, NULL, NULL); SDL_RenderPresent(g_renderer); } @@ -314,25 +314,25 @@ void AGS::set_scanline_mode(bool enable) bool AGS::save_screenshot(const char* path) { - SDL_Surface* sf = SDL_CreateRGBSurface(0, screen_width, screen_height, 32, 0, 0, 0, 0); + SDL_Surface* sf = sdl::CreateSurfaceWithMasks(screen_width, screen_height, 32, 0, 0, 0, 0); SDL_BlitSurface(hBmpScreen[SCREEN_FRONT], NULL, sf, NULL); if (scanline_texture) { SDL_Renderer* renderer = SDL_CreateSoftwareRenderer(sf); SDL_Texture *tx = create_scanline_texture(renderer, screen_width, screen_height); - SDL_RenderCopy(renderer, tx, NULL, NULL); + sdl::RenderTexture(renderer, tx, NULL, NULL); SDL_DestroyTexture(tx); SDL_DestroyRenderer(renderer); } - bool ok = SDL_SaveBMP(sf, path) == 0; + bool ok = sdl::SaveBMP(sf, path); if (!ok) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "system3", SDL_GetError(), g_window); SDL_ClearError(); } - SDL_FreeSurface(sf); + sdl::DestroySurface(sf); return ok; } diff --git a/src/sys/ags.h b/src/sys/ags.h index 4395104..60ed1fc 100644 --- a/src/sys/ags.h +++ b/src/sys/ags.h @@ -16,7 +16,7 @@ #include "game_id.h" #include "cg.h" #include "dri.h" -#include +#include "ttf_compat.h" struct Config; @@ -99,7 +99,6 @@ public: // mouse void load_cursor(int page, uint8_t flags); void select_cursor(); - void translate_mouse_coords(int* x, int* y); int cursor_index; @@ -137,7 +136,7 @@ private: int fade_color = 0; // 0: black, 255: white // font - SDL_RWops* rw_font; + sdl::IOStream* rw_font; TTF_Font* hFont16; TTF_Font* hFont24; TTF_Font* hFont32; diff --git a/src/sys/ags_cursor.cpp b/src/sys/ags_cursor.cpp index 9d09993..3a5bf0a 100644 --- a/src/sys/ags_cursor.cpp +++ b/src/sys/ags_cursor.cpp @@ -72,7 +72,7 @@ void AGS::load_cursor(int page, uint8_t flags) } } if(hCursor[i]) { - SDL_FreeCursor(hCursor[i]); + sdl::DestroyCursor(hCursor[i]); } // TODO: fix amask/xmask values hCursor[i] = SDL_CreateCursor(amask, xmask, 32, 32, 2, 2); @@ -87,30 +87,3 @@ void AGS::select_cursor() SDL_SetCursor(hCursor[cursor_index - 1]); } } - -void AGS::translate_mouse_coords(int* x, int* y) -{ - // scale mouse x and y - float scalex, scaley; - SDL_RenderGetScale(g_renderer, &scalex, &scaley); - *x *= scalex; - *y *= scaley; - - // calculate window borders - int logw, logh; - SDL_RenderGetLogicalSize(g_renderer, &logw, &logh); - - float scalew, scaleh; - scalew = logw * scalex; - scaleh = logh * scaley; - - int winw, winh; - SDL_GetWindowSize(g_window, &winw, &winh); - - float border_left = (winw - scalew) / 2; - float border_top = (winh - scaleh) / 2; - - // offset x and y by window borders - *x += border_left; - *y += border_top; -} diff --git a/src/sys/ags_draw.cpp b/src/sys/ags_draw.cpp index e566870..3b6f435 100644 --- a/src/sys/ags_draw.cpp +++ b/src/sys/ags_draw.cpp @@ -19,14 +19,14 @@ namespace { const int MOSAIC_SIZE = 16; void mosaic(SDL_Surface* sf) { - SDL_Surface *tmp = SDL_CreateRGBSurfaceWithFormat( - 0, (sf->w + MOSAIC_SIZE - 1) / MOSAIC_SIZE, (sf->h + MOSAIC_SIZE - 1) / MOSAIC_SIZE, 8, SDL_PIXELFORMAT_INDEX8); - if (sf->format->palette) - SDL_SetSurfacePalette(tmp, sf->format->palette); + SDL_Surface *tmp = sdl::CreateSurface( + (sf->w + MOSAIC_SIZE - 1) / MOSAIC_SIZE, (sf->h + MOSAIC_SIZE - 1) / MOSAIC_SIZE, SDL_PIXELFORMAT_INDEX8); + if (sdl::GetSurfacePalette(sf)) + SDL_SetSurfacePalette(tmp, sdl::GetSurfacePalette(sf)); // NOTE: SDL_BlitScaled() does not support 8-bit surfaces. - SDL_SoftStretch(sf, NULL, tmp, NULL); - SDL_SoftStretch(tmp, NULL, sf, NULL); - SDL_FreeSurface(tmp); + sdl::StretchSurface(sf, NULL, tmp, NULL); + sdl::StretchSurface(tmp, NULL, sf, NULL); + sdl::DestroySurface(tmp); } } // namespace @@ -160,10 +160,10 @@ void AGS::copy_screen(ScreenId src, ScreenId dest, int sx, int sy, int ex, int e SDL_Surface* src_surface = hBmpScreen[src]; if (transparent_color >= 0) - SDL_SetColorKey(src_surface, SDL_TRUE, transparent_color); + sdl::SetSurfaceColorKey(src_surface, true, transparent_color); SDL_BlitSurface(src_surface, &srcrect, hBmpScreen[dest], &destrect); if (transparent_color >= 0) - SDL_SetColorKey(src_surface, SDL_FALSE, 0); + sdl::SetSurfaceColorKey(src_surface, false, 0); if (dest == SCREEN_FRONT) invalidate_screen(dx, dy, width, height); @@ -248,7 +248,7 @@ void AGS::draw_mesh(int sx, int sy, int width, int height) void AGS::box_fill(ScreenId dest, int sx, int sy, int ex, int ey, uint8 color) { SDL_Rect rect = {sx, sy, ex - sx + 1, ey - sy + 1}; - SDL_FillRect(hBmpScreen[dest], &rect, color); + sdl::FillSurfaceRect(hBmpScreen[dest], &rect, color); if(dest == SCREEN_FRONT) { invalidate_screen(sx, sy, ex - sx + 1, ey - sy + 1); } @@ -261,10 +261,10 @@ void AGS::box_line(ScreenId dest, int sx, int sy, int ex, int ey, uint8 color) SDL_Rect left = {sx, sy, 1, ey - sy + 1}; SDL_Rect right = {ex, sy, 1, ey - sy + 1}; - SDL_FillRect(hBmpScreen[dest], &top, color); - SDL_FillRect(hBmpScreen[dest], &bottom, color); - SDL_FillRect(hBmpScreen[dest], &left, color); - SDL_FillRect(hBmpScreen[dest], &right, color); + sdl::FillSurfaceRect(hBmpScreen[dest], &top, color); + sdl::FillSurfaceRect(hBmpScreen[dest], &bottom, color); + sdl::FillSurfaceRect(hBmpScreen[dest], &left, color); + sdl::FillSurfaceRect(hBmpScreen[dest], &right, color); if(dest == SCREEN_FRONT) { invalidate_screen(sx, sy, ex - sx + 1, ey - sy + 1); } @@ -273,7 +273,7 @@ void AGS::box_line(ScreenId dest, int sx, int sy, int ex, int ey, uint8 color) void AGS::draw_window(int sx, int sy, int ex, int ey, bool frame, uint8 frame_color, uint8 back_color) { SDL_Rect rect = {sx, sy, ex - sx + 1, ey - sy + 1}; - SDL_FillRect(hBmpScreen[SCREEN_FRONT], &rect, back_color); + sdl::FillSurfaceRect(hBmpScreen[SCREEN_FRONT], &rect, back_color); if (frame) { SDL_Rect rects[] = { @@ -282,7 +282,7 @@ void AGS::draw_window(int sx, int sy, int ex, int ey, bool frame, uint8 frame_co {sx + 1, sy + 1, 2, ey - sy - 1}, {ex - 2, sy + 1, 2, ey - sy - 1}, }; - SDL_FillRects(hBmpScreen[SCREEN_FRONT], rects, 4, frame_color); + sdl::FillSurfaceRects(hBmpScreen[SCREEN_FRONT], rects, 4, frame_color); box_line(SCREEN_FRONT, sx + 4, sy + 4, ex - 4, ey - 4, frame_color); } invalidate_screen(sx, sy, ex - sx + 1, ey - sy + 1); diff --git a/src/sys/ags_gl3.cpp b/src/sys/ags_gl3.cpp index cda29b1..96a3837 100644 --- a/src/sys/ags_gl3.cpp +++ b/src/sys/ags_gl3.cpp @@ -42,7 +42,7 @@ CG AGS::load_gl3(const std::vector& data, bool set_palette, int transpa // GL3展開 CG cg(sx * 8, sy, width * 8, height); if (transparent >= 0) { - SDL_SetColorKey(cg.surface(), SDL_TRUE, transparent | base); + sdl::SetSurfaceColorKey(cg.surface(), true, transparent | base); } uint8 cgdata[4][80][3]; int p = 0x36; @@ -128,4 +128,3 @@ CG AGS::load_gl3(const std::vector& data, bool set_palette, int transpa return cg; } - diff --git a/src/sys/ags_gm3.cpp b/src/sys/ags_gm3.cpp index 5c088ca..0d80fdc 100644 --- a/src/sys/ags_gm3.cpp +++ b/src/sys/ags_gm3.cpp @@ -25,7 +25,7 @@ CG AGS::load_gm3(const std::vector& data, int transparent, uint8_t flag // GM3展開 CG cg(sx * 8, sy * 2, width * 8, height * 2); if (transparent >= 0) { - SDL_SetColorKey(cg.surface(), SDL_TRUE, transparent | base); + sdl::SetSurfaceColorKey(cg.surface(), true, transparent | base); } uint8 cgdata[3][80][3]; int p = 0x36; @@ -115,4 +115,3 @@ CG AGS::load_gm3(const std::vector& data, int transparent, uint8_t flag return cg; } - diff --git a/src/sys/ags_pms.cpp b/src/sys/ags_pms.cpp index 89e4149..f53708e 100644 --- a/src/sys/ags_pms.cpp +++ b/src/sys/ags_pms.cpp @@ -81,7 +81,7 @@ CG AGS::load_pms(int page, const std::vector& data, bool set_palette, i // Extract pixel data CG cg(sx, sy, width, height); if (transparent >= 0) { - SDL_SetColorKey(cg.surface(), SDL_TRUE, transparent); + sdl::SetSurfaceColorKey(cg.surface(), true, transparent); } std::vector buf[3]; buf[0].resize(width); diff --git a/src/sys/ags_text.cpp b/src/sys/ags_text.cpp index 69e427a..529011e 100644 --- a/src/sys/ags_text.cpp +++ b/src/sys/ags_text.cpp @@ -30,8 +30,8 @@ int AGS::draw_text(ScreenId dest, int x, int y, std::u16string_view codes, int f case 48: font = hFont48; break; case 64: font = hFont64; break; } - int ascent = TTF_FontAscent(font); - int descent = TTF_FontDescent(font); + int ascent = ttf::GetFontAscent(font); + int descent = ttf::GetFontDescent(font); // Adjust dest_y if the font height is larger than the specified size. int dest_y = y - (ascent - descent - font_size) / 2; @@ -49,7 +49,7 @@ int AGS::draw_text(ScreenId dest, int x, int y, std::u16string_view codes, int f draw_char(dest, dest_x, dest_y, code, font, color); int miny, maxy, advance; - TTF_GlyphMetrics(font, code, NULL, NULL, &miny, &maxy, &advance); + ttf::GetGlyphMetrics(font, code, NULL, NULL, &miny, &maxy, &advance); // Some fonts report incorrect Ascent/Descent value so we need to fix them. if (miny < descent) descent = miny; if (maxy > ascent) ascent = maxy; @@ -79,7 +79,7 @@ void AGS::draw_char(ScreenId dest, int dest_x, int dest_y, uint16 code, TTF_Font } } - SDL_FreeSurface(fs); + sdl::DestroySurface(fs); } int AGS::nearest_color(int r, int g, int b) { @@ -128,7 +128,7 @@ void AGS::draw_char_antialias(ScreenId dest, int dest_x, int dest_y, uint16 code } } - SDL_FreeSurface(fs); + sdl::DestroySurface(fs); } void AGS::draw_gaiji(ScreenId dest, int dest_x, int dest_y, const uint8_t bitmap[32], int size, uint8 color) diff --git a/src/sys/ags_vsp.cpp b/src/sys/ags_vsp.cpp index 5c035c8..d2cd1bc 100644 --- a/src/sys/ags_vsp.cpp +++ b/src/sys/ags_vsp.cpp @@ -12,12 +12,12 @@ namespace { void trim(CG& cg, int w, int h) { - SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, w, h, 8, SDL_PIXELFORMAT_INDEX8); + SDL_Surface *sf = sdl::CreateSurface(w, h, SDL_PIXELFORMAT_INDEX8); SDL_SetSurfacePalette(sf, cg.palette()); - if (SDL_HasColorKey(cg.surface())) { + if (sdl::SurfaceHasColorKey(cg.surface())) { Uint32 key; - SDL_GetColorKey(cg.surface(), &key); - SDL_SetColorKey(sf, SDL_TRUE, key); + sdl::GetSurfaceColorKey(cg.surface(), &key); + sdl::SetSurfaceColorKey(sf, true, key); } SDL_Rect srcrect = { 0, 0, w, h }; SDL_BlitSurface(cg.surface(), &srcrect, sf, NULL); @@ -74,7 +74,7 @@ CG AGS::load_vsp(const std::vector& data, bool set_palette, int transpa // Gakuen Senki uses exact sx values rather than 8x. CG cg(game_id.is(GameId::GAKUEN_SENKI) ? sx : sx * 8, sy, width * 8, height); if (transparent >= 0) { - SDL_SetColorKey(cg.surface(), SDL_TRUE, transparent | base); + sdl::SetSurfaceColorKey(cg.surface(), true, transparent | base); } uint8 cgdata[4][2][480], mask = 0; int p = 0x3a; @@ -166,4 +166,3 @@ CG AGS::load_vsp(const std::vector& data, bool set_palette, int transpa } return cg; } - diff --git a/src/sys/ags_vsp2l.cpp b/src/sys/ags_vsp2l.cpp index ebadf23..be4dd05 100644 --- a/src/sys/ags_vsp2l.cpp +++ b/src/sys/ags_vsp2l.cpp @@ -39,7 +39,7 @@ CG AGS::load_vsp2l(const std::vector& data, int transparent, uint8_t fl // VSP2L展開 CG cg(sx * 8, sy * 2, width * 8, height * 2); if (transparent >= 0) { - SDL_SetColorKey(cg.surface(), SDL_TRUE, transparent | base); + sdl::SetSurfaceColorKey(cg.surface(), true, transparent | base); } uint8 cgdata[3][2][200], mask = 0; int p = 0x1a; diff --git a/src/sys/cg.h b/src/sys/cg.h index 193fd9e..9e24fef 100644 --- a/src/sys/cg.h +++ b/src/sys/cg.h @@ -8,7 +8,7 @@ #define _CG_H_ #include -#include +#include "sdl_compat.h" #include "common.h" inline uint8_t* surface_line(SDL_Surface* surface, int y) @@ -18,7 +18,7 @@ inline uint8_t* surface_line(SDL_Surface* surface, int y) struct SurfaceDeleter { void operator()(SDL_Surface* s) const noexcept { - if (s) SDL_FreeSurface(s); + if (s) sdl::DestroySurface(s); } }; @@ -29,14 +29,14 @@ struct CG { CG() = default; CG(int x, int y, int width, int height) - : surface_(SDL_CreateRGBSurfaceWithFormat(0, width, height, 8, SDL_PIXELFORMAT_INDEX8)), + : surface_(sdl::CreateSurface(width, height, SDL_PIXELFORMAT_INDEX8)), x(x), y(y) {} explicit operator bool() const noexcept { return static_cast(surface_); } SDL_Surface* surface() const { return surface_.get(); } int width() const { return surface_->w; } int height() const { return surface_->h; } - SDL_Palette* palette() const { return surface_->format->palette; } + SDL_Palette* palette() const { return sdl::GetSurfacePalette(surface_.get()); } }; #endif // _CG_H_ diff --git a/src/sys/dri.cpp b/src/sys/dri.cpp index 908bc21..d56767a 100644 --- a/src/sys/dri.cpp +++ b/src/sys/dri.cpp @@ -7,9 +7,9 @@ #include "dri.h" #include #include -#include #include "game_id.h" #include "fileio.h" +#include "sdl_compat.h" void Dri::open(const char* file_name) { @@ -185,37 +185,37 @@ std::vector Dri::load_mda(const GameId& game_id, int page) return {}; } - SDL_RWops* rw = open_resource(name, "mda"); + sdl::IOStream* rw = open_resource(name, "mda"); if (!rw) return {}; uint8 buf[4]; // ページの位置を取得 - SDL_RWread(rw, buf, 4, 1); + sdl::ReadIO(rw, buf, 4); int link_sector = buf[0] | (buf[1] << 8); int data_sector = buf[2] | (buf[3] << 8); if(page > (data_sector - link_sector) * 128 - 1) { // ページ番号不正 - SDL_RWclose(rw); + sdl::CloseIO(rw); return {}; } - SDL_RWseek(rw, (link_sector - 1) * 256 + (page - 1) * 2, RW_SEEK_SET); - SDL_RWread(rw, buf, 2, 1); + sdl::SeekIO(rw, (link_sector - 1) * 256 + (page - 1) * 2, SEEK_SET); + sdl::ReadIO(rw, buf, 2); int disk_index = buf[0]; int link_index = buf[1]; if(disk_index == 0 || disk_index == 0x1a) { // 欠番 - SDL_RWclose(rw); + sdl::CloseIO(rw); return {}; } // AMUS.MDA以外にリンクされている場合はリソースを開き直す if(disk_index == 2) { - SDL_RWclose(rw); + sdl::CloseIO(rw); switch (game_id.game) { case GameId::DPS_SG_FAHREN: name = "BMUS_FAH.MDA"; @@ -264,7 +264,7 @@ std::vector Dri::load_mda(const GameId& game_id, int page) return {}; } } else if(disk_index == 3) { - SDL_RWclose(rw); + sdl::CloseIO(rw); switch (game_id.game) { case GameId::TOUSHIN_HINT: name = "CMUS_T1.MDA"; @@ -281,27 +281,27 @@ std::vector Dri::load_mda(const GameId& game_id, int page) } } else if(disk_index != 1) { // AMUS.MDA以外にリンクされている場合は失敗 - SDL_RWclose(rw); + sdl::CloseIO(rw); return {}; } // データ取得 - SDL_RWseek(rw, link_index * 2, RW_SEEK_SET); - SDL_RWread(rw, buf, 4, 1); + sdl::SeekIO(rw, link_index * 2, SEEK_SET); + sdl::ReadIO(rw, buf, 4); int start_sector = buf[0] | (buf[1] << 8); int end_sector = buf[2] | (buf[3] << 8); int size = (end_sector - start_sector) * 256; if (size == 0) { // サイズ不正 - SDL_RWclose(rw); + sdl::CloseIO(rw); return {}; } std::vector buffer(size); - SDL_RWseek(rw, (start_sector - 1) * 256, RW_SEEK_SET); - SDL_RWread(rw, buffer.data(), size, 1); + sdl::SeekIO(rw, (start_sector - 1) * 256, SEEK_SET); + sdl::ReadIO(rw, buffer.data(), size); - SDL_RWclose(rw); + sdl::CloseIO(rw); return buffer; } diff --git a/src/sys/game_id.cpp b/src/sys/game_id.cpp index b3c775b..30900e9 100644 --- a/src/sys/game_id.cpp +++ b/src/sys/game_id.cpp @@ -1,5 +1,5 @@ #include -#include +#include "sdl_compat.h" #include "game_id.h" #include "fileio.h" #include "config.h" diff --git a/src/sys/mako_midi.cpp b/src/sys/mako_midi.cpp index 2bd0cc1..42b4c70 100644 --- a/src/sys/mako_midi.cpp +++ b/src/sys/mako_midi.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include "sdl_compat.h" #include #include "mako_midi.h" @@ -98,7 +98,7 @@ public: bool load_mml(const std::vector& data); void load_mda(const std::vector& data); void start_midi(); - bool play_midi(SDL_atomic_t* current_loop, SDL_atomic_t* current_mark); + bool play_midi(sdl::AtomicInt* current_loop, sdl::AtomicInt* current_mark); int seq() const { return seq_; } private: @@ -231,7 +231,7 @@ void Playback::start_midi() play_time = 0; } -bool Playback::play_midi(SDL_atomic_t* current_loop, SDL_atomic_t* current_mark) +bool Playback::play_midi(sdl::AtomicInt* current_loop, sdl::AtomicInt* current_mark) { // 経過時間の取得 Uint32 current_time = SDL_GetTicks(); @@ -272,7 +272,7 @@ bool Playback::play_midi(SDL_atomic_t* current_loop, SDL_atomic_t* current_mark) !play[6].loop_flag && !play[7].loop_flag && !play[8].loop_flag) { // 全チャンネルが再生停止 (ループしない曲) stop_midi(); - SDL_AtomicSet(current_loop, 1); + sdl::SetAtomicInt(current_loop, 1); return false; } play[i].wait_time = 1; @@ -284,7 +284,7 @@ bool Playback::play_midi(SDL_atomic_t* current_loop, SDL_atomic_t* current_mark) loop = play[j].loop_cnt; } } - SDL_AtomicSet(current_loop, loop); + sdl::SetAtomicInt(current_loop, loop); if(loop_ && loop >= loop_) { // 指定回数だけ再生完了 stop_midi(); @@ -311,7 +311,7 @@ bool Playback::play_midi(SDL_atomic_t* current_loop, SDL_atomic_t* current_mark) } play[i].note = 128; } else if(d0 == 0xe0) { - SDL_AtomicSet(current_mark, mml[i].next()); + sdl::SetAtomicInt(current_mark, mml[i].next()); } else if(d0 == 0xe1) { d1 = mml[i].next(); play[i].velocity += (d1 > 127) ? (d1 - 256) : d1; @@ -638,9 +638,9 @@ MAKOMidi::MAKOMidi(int device) thread = SDL_CreateThread(thread_main, "MAKOMidi", this); queue_mutex = SDL_CreateMutex(); } - SDL_AtomicSet(¤t_seq, 0); - SDL_AtomicSet(¤t_loop, 0); - SDL_AtomicSet(¤t_mark, 0); + sdl::SetAtomicInt(¤t_seq, 0); + sdl::SetAtomicInt(¤t_loop, 0); + sdl::SetAtomicInt(¤t_mark, 0); } MAKOMidi::~MAKOMidi() @@ -666,7 +666,7 @@ bool MAKOMidi::play(const GameId& game_id, Dri& amus, Dri& mda, int page, int lo auto playback = Playback::create(game_id, amus, mda, page, loop, seq); if (!playback) return false; - SDL_AtomicSet(¤t_seq, seq); + sdl::SetAtomicInt(¤t_seq, seq); SDL_LockMutex(queue_mutex); queue.push(std::make_unique(Command::PLAY, std::move(playback))); SDL_UnlockMutex(queue_mutex); @@ -675,7 +675,7 @@ bool MAKOMidi::play(const GameId& game_id, Dri& amus, Dri& mda, int page, int lo void MAKOMidi::stop() { - SDL_AtomicSet(¤t_seq, 0); + sdl::SetAtomicInt(¤t_seq, 0); SDL_LockMutex(queue_mutex); queue.push(std::make_unique(Command::STOP)); SDL_UnlockMutex(queue_mutex); @@ -683,13 +683,13 @@ void MAKOMidi::stop() bool MAKOMidi::is_playing() { - return SDL_AtomicGet(¤t_seq) != 0; + return sdl::GetAtomicInt(¤t_seq) != 0; } void MAKOMidi::get_mark(int* mark, int* loop) { - *mark = SDL_AtomicGet(¤t_mark); - *loop = SDL_AtomicGet(¤t_loop); + *mark = sdl::GetAtomicInt(¤t_mark); + *loop = sdl::GetAtomicInt(¤t_loop); } void MAKOMidi::thread_loop() @@ -708,8 +708,8 @@ void MAKOMidi::thread_loop() stop_midi(); current = std::move(cmd->playback); current->start_midi(); - SDL_AtomicSet(¤t_loop, 0); - SDL_AtomicSet(¤t_mark, 0); + sdl::SetAtomicInt(¤t_loop, 0); + sdl::SetAtomicInt(¤t_mark, 0); SDL_Delay(100); // ? break; case Command::STOP: @@ -726,7 +726,7 @@ void MAKOMidi::thread_loop() SDL_UnlockMutex(queue_mutex); if (current) { if (!current->play_midi(¤t_loop, ¤t_mark)) { - SDL_AtomicCAS(¤t_seq, current->seq(), 0); + sdl::CompareAndSwapAtomicInt(¤t_seq, current->seq(), 0); current.reset(); } } @@ -737,7 +737,7 @@ void MAKOMidi::thread_loop() // static int MAKOMidi::thread_main(void* data) { - SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); + sdl::SetCurrentThreadPriority(SDL_THREAD_PRIORITY_HIGH); MAKOMidi* mm = reinterpret_cast(data); mm->thread_loop(); diff --git a/src/sys/mako_midi.h b/src/sys/mako_midi.h index ab68adc..e4162c7 100644 --- a/src/sys/mako_midi.h +++ b/src/sys/mako_midi.h @@ -3,7 +3,7 @@ #include #include -#include +#include "sdl_compat.h" #include "common.h" struct GameId; @@ -24,11 +24,11 @@ public: private: struct Command; std::queue> queue; - SDL_mutex* queue_mutex = nullptr; + sdl::Mutex* queue_mutex = nullptr; SDL_Thread* thread = nullptr; - SDL_atomic_t current_seq; - SDL_atomic_t current_loop; - SDL_atomic_t current_mark; + sdl::AtomicInt current_seq; + sdl::AtomicInt current_loop; + sdl::AtomicInt current_mark; int next_seq = 0; void thread_loop(); diff --git a/src/sys/music_decoder.cpp b/src/sys/music_decoder.cpp index 52fc346..644ab2b 100644 --- a/src/sys/music_decoder.cpp +++ b/src/sys/music_decoder.cpp @@ -20,7 +20,7 @@ public: } initialized = true; spec_.freq = static_cast(mp3.sampleRate); - spec_.format = AUDIO_S16SYS; + spec_.format = sdl::AudioS16; spec_.channels = static_cast(mp3.channels); } @@ -60,7 +60,7 @@ public: } stb_vorbis_info info = stb_vorbis_get_info(vorbis); spec_.freq = static_cast(info.sample_rate); - spec_.format = AUDIO_S16SYS; + spec_.format = sdl::AudioS16; spec_.channels = static_cast(info.channels); } @@ -104,7 +104,7 @@ public: ~WavDecoder() override { if (data) - SDL_FreeWAV(data); + sdl::FreeWAV(data); } bool is_open() const override { return data_size > 0 && frame_size > 0; } diff --git a/src/sys/music_decoder.h b/src/sys/music_decoder.h index 6bdc222..4f148ba 100644 --- a/src/sys/music_decoder.h +++ b/src/sys/music_decoder.h @@ -3,7 +3,7 @@ #include #include -#include +#include "sdl_compat.h" struct DecodedChunk { const Uint8* data; diff --git a/src/sys/nact.cpp b/src/sys/nact.cpp index dbe1c1a..aa491b3 100644 --- a/src/sys/nact.cpp +++ b/src/sys/nact.cpp @@ -79,7 +79,8 @@ NACT::NACT(const Config& config, const GameId& game_id) init_windows(); init_text(); - SDL_Init(SDL_INIT_GAMECONTROLLER); + SDL_Init(sdl::INIT_GAMEPAD); +#if SYSTEM3_SDL_VERSION == 2 for (int i = 0; i < SDL_NumJoysticks(); ++i) { if (SDL_IsGameController(i)) { sdl_gamecontroller = SDL_GameControllerOpen(i); @@ -90,6 +91,19 @@ NACT::NACT(const Config& config, const GameId& game_id) } } } +#else + int count = 0; + SDL_JoystickID* ids = SDL_GetJoysticks(&count); + for (int i = 0; i < count; ++i) { + if (SDL_IsGamepad(ids[i])) { + sdl_gamecontroller = SDL_OpenGamepad(ids[i]); + if (sdl_gamecontroller) + break; + WARNING("Could not open gamepad %i: %s\n", i, SDL_GetError()); + } + } + SDL_free(ids); +#endif } NACT::~NACT() diff --git a/src/sys/nact.h b/src/sys/nact.h index 03b308d..38d1ab2 100644 --- a/src/sys/nact.h +++ b/src/sys/nact.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include "sdl_compat.h" #include "common.h" #include "config.h" #include "cg.h" @@ -22,6 +22,11 @@ #include "game_id.h" #include "scenario.h" +#ifdef _WIN32 +struct tagMSG; +typedef tagMSG MSG; +#endif + #define RND var[ 0] #define MAX_VERB 128 @@ -58,6 +63,10 @@ public: void set_skip_menu_state(bool enabled, bool checked); +#ifdef _WIN32 + void handle_windows_event(MSG* msg); +#endif + virtual uint16 cali() = 0; Scenario sco; @@ -270,7 +279,7 @@ protected: // input bool mouse_move_enabled = true; bool wait_keydown = true; // ウェイト時のキー受付 - SDL_GameController *sdl_gamecontroller = NULL; + sdl::Gamepad *sdl_gamecontroller = NULL; uint8 get_key(bool notify_texthook = true); void wait_key_release(uint8_t mask = 0xff); diff --git a/src/sys/nact_gakuenking.cpp b/src/sys/nact_gakuenking.cpp index 0bb8091..2b335dd 100644 --- a/src/sys/nact_gakuenking.cpp +++ b/src/sys/nact_gakuenking.cpp @@ -568,7 +568,7 @@ private: base = CELL_EMPTY; if (base == CELL_EMPTY) { SDL_Rect rect = { px, py, TILE_SIZE, TILE_SIZE }; - SDL_FillRect(composed_map.surface(), &rect, 0); + sdl::FillSurfaceRect(composed_map.surface(), &rect, 0); } else { draw_map_tile(base, px, py, false); } @@ -666,8 +666,7 @@ private: sheet_x, sheet_y, width, height, cg.width(), cg.height()); return std::nullopt; } - SDL_SetColorKey(cg.surface(), transparent ? SDL_TRUE : SDL_FALSE, - TRANSPARENT_COLOR); + sdl::SetSurfaceColorKey(cg.surface(), transparent, TRANSPARENT_COLOR); return SDL_Rect{ sheet_x, sheet_y, width, height }; } @@ -1472,7 +1471,7 @@ private: get_cursor(&seen_x, &seen_y); // Hides the system pointer and draws the cursor at (cx, cy) instead. auto show_cursor = [&] { - SDL_ShowCursor(SDL_DISABLE); + sdl::HideCursor(); draw_mouse_cursor(shape, cx, cy); cursor_drawn = true; }; @@ -1481,7 +1480,7 @@ private: auto hide_cursor = [&] { if (!cursor_drawn) return; - SDL_ShowCursor(SDL_ENABLE); + sdl::ShowCursor(); ags->copy_screen(SCREEN_BACK, SCREEN_FRONT, cx, cy, cx + MOUSE_CURSOR_SIZE - 1, cy + MOUSE_CURSOR_SIZE - 1, cx, cy); cursor_drawn = false; diff --git a/src/sys/nact_input.cpp b/src/sys/nact_input.cpp index 6676100..959560c 100644 --- a/src/sys/nact_input.cpp +++ b/src/sys/nact_input.cpp @@ -8,7 +8,6 @@ #include #undef ERROR #endif -#include #include "nact.h" #include "ags.h" #include "texthook.h" @@ -21,6 +20,7 @@ enum TouchState { }; extern SDL_Window* g_window; +extern SDL_Renderer* g_renderer; static int mousex, mousey, wheel; static TouchState touch_state = TOUCH_NONE; @@ -30,33 +30,37 @@ void NACT::handle_event(SDL_Event e) return; switch (e.type) { - case SDL_QUIT: + case sdl::EVENT_QUIT: show_quit_dialog(); break; #ifdef __ANDROID__ - case SDL_KEYUP: - if (e.key.keysym.scancode == SDL_SCANCODE_AC_BACK) { + case sdl::EVENT_KEY_UP: + if (sdl::GetKeyScancode(e.key) == SDL_SCANCODE_AC_BACK) { show_quit_dialog(); } break; #endif - case SDL_MOUSEMOTION: - mousex = e.motion.x * ags->screen_width / ags->window_width; - mousey = e.motion.y * ags->screen_height / ags->window_height; + case sdl::EVENT_MOUSE_MOTION: { + float render_x, render_y; + sdl::RenderCoordinatesFromWindow(g_renderer, e.motion.x, e.motion.y, + &render_x, &render_y); + mousex = render_x * ags->screen_width / ags->window_width; + mousey = render_y * ags->screen_height / ags->window_height; break; + } - case SDL_MOUSEWHEEL: + case sdl::EVENT_MOUSE_WHEEL: wheel += e.wheel.y * (e.wheel.direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1); break; - case SDL_FINGERDOWN: - case SDL_FINGERUP: - case SDL_FINGERMOTION: + case sdl::EVENT_FINGER_DOWN: + case sdl::EVENT_FINGER_UP: + case sdl::EVENT_FINGER_MOTION: mousex = e.tfinger.x * ags->screen_width; mousey = e.tfinger.y * ags->screen_height; - switch (SDL_GetNumTouchFingers(e.tfinger.touchId)) { + switch (sdl::GetNumTouchFingers(sdl::GetTouchID(e.tfinger))) { case 0: touch_state = TOUCH_NONE; break; @@ -116,7 +120,7 @@ uint8 NACT::get_key(bool notify_texthook) pump_events(); // キーボード&マウス - const Uint8* key = SDL_GetKeyboardState(NULL); + const auto* key = SDL_GetKeyboardState(NULL); Uint32 mouse = SDL_GetMouseState(NULL, NULL); if(key[SDL_SCANCODE_UP ] || key[SDL_SCANCODE_KP_8 ] ) val |= 0x01; @@ -131,14 +135,14 @@ uint8 NACT::get_key(bool notify_texthook) // マウス移動で方向入力はサポートしない if(sdl_gamecontroller) { - if(SDL_GameControllerGetButton(sdl_gamecontroller, SDL_CONTROLLER_BUTTON_DPAD_UP) || SDL_GameControllerGetAxis(sdl_gamecontroller, SDL_CONTROLLER_AXIS_LEFTY) <= -8000) val |= 0x01; - if(SDL_GameControllerGetButton(sdl_gamecontroller, SDL_CONTROLLER_BUTTON_DPAD_DOWN) || SDL_GameControllerGetAxis(sdl_gamecontroller, SDL_CONTROLLER_AXIS_LEFTY) >= 8000) val |= 0x02; - if(SDL_GameControllerGetButton(sdl_gamecontroller, SDL_CONTROLLER_BUTTON_DPAD_LEFT) || SDL_GameControllerGetAxis(sdl_gamecontroller, SDL_CONTROLLER_AXIS_LEFTX) <= -8000) val |= 0x04; - if(SDL_GameControllerGetButton(sdl_gamecontroller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) || SDL_GameControllerGetAxis(sdl_gamecontroller, SDL_CONTROLLER_AXIS_LEFTX) >= 8000) val |= 0x08; - if(SDL_GameControllerGetButton(sdl_gamecontroller, SDL_CONTROLLER_BUTTON_A)) val |= 0x10; - if(SDL_GameControllerGetButton(sdl_gamecontroller, SDL_CONTROLLER_BUTTON_B)) val |= 0x20; - if(SDL_GameControllerGetButton(sdl_gamecontroller, SDL_CONTROLLER_BUTTON_X)) val |= 0x40; - if(SDL_GameControllerGetButton(sdl_gamecontroller, SDL_CONTROLLER_BUTTON_Y)) val |= 0x80; + if(sdl::GetGamepadButton(sdl_gamecontroller, sdl::GAMEPAD_BUTTON_DPAD_UP) || sdl::GetGamepadAxis(sdl_gamecontroller, sdl::GAMEPAD_AXIS_LEFTY) <= -8000) val |= 0x01; + if(sdl::GetGamepadButton(sdl_gamecontroller, sdl::GAMEPAD_BUTTON_DPAD_DOWN) || sdl::GetGamepadAxis(sdl_gamecontroller, sdl::GAMEPAD_AXIS_LEFTY) >= 8000) val |= 0x02; + if(sdl::GetGamepadButton(sdl_gamecontroller, sdl::GAMEPAD_BUTTON_DPAD_LEFT) || sdl::GetGamepadAxis(sdl_gamecontroller, sdl::GAMEPAD_AXIS_LEFTX) <= -8000) val |= 0x04; + if(sdl::GetGamepadButton(sdl_gamecontroller, sdl::GAMEPAD_BUTTON_DPAD_RIGHT) || sdl::GetGamepadAxis(sdl_gamecontroller, sdl::GAMEPAD_AXIS_LEFTX) >= 8000) val |= 0x08; + if(sdl::GetGamepadButton(sdl_gamecontroller, sdl::GAMEPAD_BUTTON_SOUTH)) val |= 0x10; + if(sdl::GetGamepadButton(sdl_gamecontroller, sdl::GAMEPAD_BUTTON_EAST)) val |= 0x20; + if(sdl::GetGamepadButton(sdl_gamecontroller, sdl::GAMEPAD_BUTTON_WEST)) val |= 0x40; + if(sdl::GetGamepadButton(sdl_gamecontroller, sdl::GAMEPAD_BUTTON_NORTH)) val |= 0x80; } return val; @@ -160,7 +164,7 @@ void NACT::set_cursor(int x, int y) { if (!mouse_move_enabled) return; - ags->translate_mouse_coords(&x, &y); + sdl::RenderCoordinatesToWindow(g_renderer, g_window, &x, &y); SDL_WarpMouseInWindow(g_window, x, y); } @@ -186,7 +190,7 @@ void NACT::show_quit_dialog() buttons, }; int buttonid = 0; - if (SDL_ShowMessageBox(&messagebox_data, &buttonid) < 0) { + if (!sdl::ShowMessageBox(&messagebox_data, &buttonid)) { WARNING("error displaying message box"); buttonid = 1; } diff --git a/src/texthook.cpp b/src/texthook.cpp index d6aa845..1abf9b9 100644 --- a/src/texthook.cpp +++ b/src/texthook.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include "sdl_compat.h" #include "common.h" #include "texthook.h" #ifdef __EMSCRIPTEN__ diff --git a/src/ttf_compat.h b/src/ttf_compat.h new file mode 100644 index 0000000..f8eefc8 --- /dev/null +++ b/src/ttf_compat.h @@ -0,0 +1,58 @@ +#ifndef SYSTEM3_TTF_COMPAT_H_ +#define SYSTEM3_TTF_COMPAT_H_ + +#include "sdl_compat.h" + +#if SYSTEM3_SDL_VERSION == 2 +#include +#elif SYSTEM3_SDL_VERSION == 3 +#include +#else +#error "Unsupported SYSTEM3_SDL_VERSION" +#endif + +static_assert(SDL_TTF_MAJOR_VERSION == SYSTEM3_SDL_VERSION, + "SDL_ttf and SDL must use the same major version"); + +namespace ttf { + +inline TTF_Font* OpenFontIO(sdl::IOStream* stream, int point_size) +{ +#if SYSTEM3_SDL_VERSION == 2 + return TTF_OpenFontRW(stream, 0, point_size); +#else + return TTF_OpenFontIO(stream, false, point_size); +#endif +} + +inline int GetFontAscent(TTF_Font* font) +{ +#if SYSTEM3_SDL_VERSION == 2 + return TTF_FontAscent(font); +#else + return TTF_GetFontAscent(font); +#endif +} + +inline int GetFontDescent(TTF_Font* font) +{ +#if SYSTEM3_SDL_VERSION == 2 + return TTF_FontDescent(font); +#else + return TTF_GetFontDescent(font); +#endif +} + +inline bool GetGlyphMetrics(TTF_Font* font, Uint32 code, int* minx, int* maxx, + int* miny, int* maxy, int* advance) +{ +#if SYSTEM3_SDL_VERSION == 2 + return TTF_GlyphMetrics(font, code, minx, maxx, miny, maxy, advance) == 0; +#else + return TTF_GetGlyphMetrics(font, code, minx, maxx, miny, maxy, advance); +#endif +} + +} // namespace ttf + +#endif // SYSTEM3_TTF_COMPAT_H_ diff --git a/src/win/nact_win.cpp b/src/win/nact_win.cpp index 4e8a6a7..69c764e 100644 --- a/src/win/nact_win.cpp +++ b/src/win/nact_win.cpp @@ -3,7 +3,9 @@ #undef ERROR #include #include "nact.h" +#if SYSTEM3_SDL_VERSION == 2 #include "SDL_syswm.h" +#endif #include "encoding.h" #include "ags.h" #include "mako.h" @@ -19,12 +21,24 @@ namespace { bool auto_copy_enabled = false; HWND get_hwnd(SDL_Window* window) { +#if SYSTEM3_SDL_VERSION == 2 SDL_SysWMinfo info; SDL_VERSION(&info.version); SDL_GetWindowWMInfo(window, &info); return info.info.win.window; +#else + return (HWND)SDL_GetPointerProperty( + SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); +#endif } +#if SYSTEM3_SDL_VERSION == 3 +bool SDLCALL windows_message_hook(void* userdata, MSG* msg) { + static_cast(userdata)->handle_windows_event(msg); + return true; +} +#endif + void init_menu(bool mouse_move_enabled, const Config& config) { HINSTANCE hinst = (HINSTANCE)GetModuleHandle(NULL); @@ -146,7 +160,11 @@ void NACT::platform_initialize() { init_menu(mouse_move_enabled, config); init_console(config, game_id.sys_ver); +#if SYSTEM3_SDL_VERSION == 2 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); +#else + SDL_SetWindowsMessageHook(windows_message_hook, this); +#endif } void NACT::platform_finalize() @@ -194,13 +212,26 @@ bool NACT::handle_platform_event(const SDL_Event& e) return false; } - if (e.type != SDL_SYSWMEVENT) - return false; - const SDL_SysWMmsg* msg = e.syswm.msg; +#if SYSTEM3_SDL_VERSION == 2 + if (e.type == SDL_SYSWMEVENT) { + const SDL_SysWMmsg* syswm = e.syswm.msg; + MSG msg = {}; + msg.hwnd = syswm->msg.win.hwnd; + msg.message = syswm->msg.win.msg; + msg.wParam = syswm->msg.win.wParam; + msg.lParam = syswm->msg.win.lParam; + handle_windows_event(&msg); + return true; + } +#endif + return false; +} - switch (msg->msg.win.msg) { +void NACT::handle_windows_event(MSG* msg) +{ + switch (msg->message) { case WM_COMMAND: - switch (msg->msg.win.wParam) { + switch (msg->wParam) { case ID_SCREENSHOT: save_screenshot(ags); break; @@ -211,10 +242,10 @@ bool NACT::handle_platform_event(const SDL_Event& e) quit(0); break; case ID_SCREEN_WINDOW: - SDL_SetWindowFullscreen(g_window, 0); + sdl::SetWindowFullscreen(g_window, false); break; case ID_SCREEN_FULL: - SDL_SetWindowFullscreen(g_window, SDL_WINDOW_FULLSCREEN_DESKTOP); + sdl::SetWindowFullscreen(g_window, true); break; case ID_SCANLINE: ags->set_scanline_mode(!ags->get_scanline_mode()); @@ -252,5 +283,4 @@ bool NACT::handle_platform_event(const SDL_Event& e) } break; } - return true; } diff --git a/switch/README.md b/switch/README.md index 1092a93..e6da14f 100644 --- a/switch/README.md +++ b/switch/README.md @@ -12,7 +12,8 @@ git submodule update --init sudo (dkp-)pacman -S switch-dev switch-sdl2 switch-sdl2_ttf mkdir -p out/debug cd out/debug -/opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake -DCMAKE_BUILD_TYPE=Debug ../../ +/opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-cmake \ + -DCMAKE_BUILD_TYPE=Debug -DSYSTEM3_SDL_VERSION=2 ../../ make ```