mirror of
https://github.com/kichikuou/system3-sdl2.git
synced 2026-09-22 14:54:35 +03:00
Add SDL3 support alongside SDL2
Add optional SDL3 support while keeping SDL2 as the default. SDL3 builds must be selected explicitly with the SYSTEM3_SDL_VERSION CMake option. Centralize most SDL2/SDL3 API differences behind compatibility interfaces. Audio playback uses a dedicated SDL3 backend based on SDL3 audio streams, implemented separately in mako_sdl3.cpp. Android and Nintendo Switch remain on SDL2 for now.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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
|
||||
|
||||
+91
-34
@@ -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
|
||||
"$<TARGET_FILE:SDL2::SDL2>" "$<TARGET_FILE_DIR:system3>"
|
||||
"$<TARGET_FILE:${SDL_TARGET}>" "$<TARGET_FILE_DIR:system3>"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"$<TARGET_FILE:SDL2_ttf::SDL2_ttf>" "$<TARGET_FILE_DIR:system3>"
|
||||
"$<TARGET_FILE:${SDL_TTF_TARGET}>" "$<TARGET_FILE_DIR:system3>"
|
||||
COMMENT "Copying SDL runtime DLLs"
|
||||
VERBATIM)
|
||||
set_target_properties(system3 PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
VS_DEBUGGER_ENVIRONMENT "PATH=${SDL2_LIBDIR}$<SEMICOLON>${SDL2TTF_LIBDIR}$<SEMICOLON>$ENV{PATH}")
|
||||
VS_DEBUGGER_ENVIRONMENT "PATH=${SDL_LIBDIR}$<SEMICOLON>${SDL_TTF_LIBDIR}$<SEMICOLON>$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
|
||||
$<LINK_GROUP:RESCAN,${SDL2TTF_STATIC_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
|
||||
$<LINK_GROUP:RESCAN,${SDLTTF_STATIC_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)
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "debugger/frontend.h"
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "common.h"
|
||||
#include "encoding.h"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include <emscripten.h>
|
||||
#include "mako.h"
|
||||
#include "fm/mako_ymfm.h"
|
||||
@@ -118,7 +118,7 @@ void MAKO::play_pcm(int page, int loops)
|
||||
std::vector<uint8_t> buffer = amse.load(page);
|
||||
if (!buffer.empty()) {
|
||||
// AMSE形式 (乙女戦記)
|
||||
uint32_t amse_size = SDL_SwapLE32(*reinterpret_cast<uint32_t*>(&buffer[8]));
|
||||
uint32_t amse_size = sdl::Swap32LE(*reinterpret_cast<uint32_t*>(&buffer[8]));
|
||||
int samples = (amse_size - 12) * 2;
|
||||
int total = samples + 0x24;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <limits.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#include "mako.h"
|
||||
#include "mako_midi.h"
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
ALICE SOFT SYSTEM 3 for Win32
|
||||
|
||||
[ MAKO ]
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <limits.h>
|
||||
#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<Music*>(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<MusicDecoder> 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<uint8_t> 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<uint8_t> 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<FmStream*>(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> music;
|
||||
std::unique_ptr<FmStream> fm;
|
||||
std::unique_ptr<MAKOMidi> 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<uint8_t> 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<int>(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<MAKOMidi>(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<Music>(g_device, file, next_loop);
|
||||
if (!music->is_open()) {
|
||||
music.reset();
|
||||
return;
|
||||
}
|
||||
} else if (use_fm) {
|
||||
std::vector<uint8_t> data = amus.load(page);
|
||||
if (data.empty())
|
||||
return;
|
||||
fm = std::make_unique<FmStream>(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<uint8_t> src;
|
||||
|
||||
// WAV形式 (Only You)
|
||||
std::vector<uint8_t> data = awav.load(page);
|
||||
if (!data.empty()) {
|
||||
SDL_AudioSpec spec;
|
||||
Uint8* wav;
|
||||
Uint32 wavlen;
|
||||
if (!SDL_LoadWAV_IO(SDL_IOFromConstMem(data.data(), static_cast<int>(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<uint32_t*>(&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();
|
||||
}
|
||||
+13
-11
@@ -1,12 +1,14 @@
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#undef ERROR
|
||||
#endif
|
||||
#include <SDL.h>
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,559 @@
|
||||
#ifndef SYSTEM3_SDL_COMPAT_H_
|
||||
#define SYSTEM3_SDL_COMPAT_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef SYSTEM3_SDL_VERSION
|
||||
#define SYSTEM3_SDL_VERSION 2
|
||||
#endif
|
||||
|
||||
#if SYSTEM3_SDL_VERSION == 2
|
||||
#include <SDL.h>
|
||||
#elif SYSTEM3_SDL_VERSION == 3
|
||||
#include <SDL3/SDL.h>
|
||||
#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<int>(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<SDL_IOWhence>(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<int>(width * scale + 0.5f);
|
||||
height = static_cast<int>(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<int>(width * scale + 0.5f),
|
||||
static_cast<int>(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<float>(source->x), static_cast<float>(source->y),
|
||||
static_cast<float>(source->w), static_cast<float>(source->h) };
|
||||
src_ptr = &src;
|
||||
}
|
||||
if (destination) {
|
||||
dst = { static_cast<float>(destination->x), static_cast<float>(destination->y),
|
||||
static_cast<float>(destination->w), static_cast<float>(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_
|
||||
+10
-4
@@ -1,3 +1,6 @@
|
||||
#if SYSTEM3_SDL_VERSION == 3
|
||||
#include <SDL3/SDL_main.h>
|
||||
#endif
|
||||
#include <string>
|
||||
#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 初期化
|
||||
|
||||
+29
-29
@@ -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<uint32*>(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<uint8_t(*)[640]>(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;
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -16,7 +16,7 @@
|
||||
#include "game_id.h"
|
||||
#include "cg.h"
|
||||
#include "dri.h"
|
||||
#include <SDL_ttf.h>
|
||||
#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;
|
||||
|
||||
+1
-28
@@ -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;
|
||||
}
|
||||
|
||||
+16
-16
@@ -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);
|
||||
|
||||
+1
-2
@@ -42,7 +42,7 @@ CG AGS::load_gl3(const std::vector<uint8_t>& 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<uint8_t>& data, bool set_palette, int transpa
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -25,7 +25,7 @@ CG AGS::load_gm3(const std::vector<uint8_t>& 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<uint8_t>& data, int transparent, uint8_t flag
|
||||
|
||||
return cg;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ CG AGS::load_pms(int page, const std::vector<uint8_t>& 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<uint8_t> buf[3];
|
||||
buf[0].resize(width);
|
||||
|
||||
@@ -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)
|
||||
|
||||
+5
-6
@@ -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<uint8_t>& 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<uint8_t>& data, bool set_palette, int transpa
|
||||
}
|
||||
return cg;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ CG AGS::load_vsp2l(const std::vector<uint8_t>& 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;
|
||||
|
||||
+4
-4
@@ -8,7 +8,7 @@
|
||||
#define _CG_H_
|
||||
|
||||
#include <memory>
|
||||
#include <SDL.h>
|
||||
#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<bool>(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_
|
||||
|
||||
+16
-16
@@ -7,9 +7,9 @@
|
||||
#include "dri.h"
|
||||
#include <memory>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "game_id.h"
|
||||
#include "fileio.h"
|
||||
#include "sdl_compat.h"
|
||||
|
||||
void Dri::open(const char* file_name)
|
||||
{
|
||||
@@ -185,37 +185,37 @@ std::vector<uint8> 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<uint8> 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<uint8> 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<uint8_t> 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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "game_id.h"
|
||||
#include "fileio.h"
|
||||
#include "config.h"
|
||||
|
||||
+18
-18
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include <RtMidi.h>
|
||||
|
||||
#include "mako_midi.h"
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
bool load_mml(const std::vector<uint8_t>& data);
|
||||
void load_mda(const std::vector<uint8_t>& 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<MAKOMidi::Command>(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<MAKOMidi::Command>(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<MAKOMidi*>(data);
|
||||
mm->thread_loop();
|
||||
|
||||
+5
-5
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "common.h"
|
||||
|
||||
struct GameId;
|
||||
@@ -24,11 +24,11 @@ public:
|
||||
private:
|
||||
struct Command;
|
||||
std::queue<std::unique_ptr<Command>> 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();
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
}
|
||||
initialized = true;
|
||||
spec_.freq = static_cast<int>(mp3.sampleRate);
|
||||
spec_.format = AUDIO_S16SYS;
|
||||
spec_.format = sdl::AudioS16;
|
||||
spec_.channels = static_cast<Uint8>(mp3.channels);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
}
|
||||
stb_vorbis_info info = stb_vorbis_get_info(vorbis);
|
||||
spec_.freq = static_cast<int>(info.sample_rate);
|
||||
spec_.format = AUDIO_S16SYS;
|
||||
spec_.format = sdl::AudioS16;
|
||||
spec_.channels = static_cast<Uint8>(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; }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
|
||||
struct DecodedChunk {
|
||||
const Uint8* data;
|
||||
|
||||
+15
-1
@@ -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()
|
||||
|
||||
+11
-2
@@ -14,7 +14,7 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
#include <SDL.h>
|
||||
#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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
+27
-23
@@ -8,7 +8,6 @@
|
||||
#include <windows.h>
|
||||
#undef ERROR
|
||||
#endif
|
||||
#include <SDL_syswm.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "sdl_compat.h"
|
||||
#include "common.h"
|
||||
#include "texthook.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef SYSTEM3_TTF_COMPAT_H_
|
||||
#define SYSTEM3_TTF_COMPAT_H_
|
||||
|
||||
#include "sdl_compat.h"
|
||||
|
||||
#if SYSTEM3_SDL_VERSION == 2
|
||||
#include <SDL_ttf.h>
|
||||
#elif SYSTEM3_SDL_VERSION == 3
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
#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_
|
||||
+38
-8
@@ -3,7 +3,9 @@
|
||||
#undef ERROR
|
||||
#include <time.h>
|
||||
#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<NACT*>(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;
|
||||
}
|
||||
|
||||
+2
-1
@@ -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
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user