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:
kichikuou
2026-09-16 08:44:23 +09:00
parent 647a5aa326
commit 3e32da1d37
40 changed files with 1475 additions and 266 deletions
+11 -5
View File
@@ -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
+12 -4
View File
@@ -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
+3 -1
View File
@@ -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/
+10 -3
View File
@@ -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/
+14 -4
View File
@@ -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
View File
@@ -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)
+20 -9
View File
@@ -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).
-5
View File
@@ -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
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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"
+462
View File
@@ -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
View File
@@ -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
}
+559
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+5 -5
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(&current_seq, 0);
SDL_AtomicSet(&current_loop, 0);
SDL_AtomicSet(&current_mark, 0);
sdl::SetAtomicInt(&current_seq, 0);
sdl::SetAtomicInt(&current_loop, 0);
sdl::SetAtomicInt(&current_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(&current_seq, seq);
sdl::SetAtomicInt(&current_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(&current_seq, 0);
sdl::SetAtomicInt(&current_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(&current_seq) != 0;
return sdl::GetAtomicInt(&current_seq) != 0;
}
void MAKOMidi::get_mark(int* mark, int* loop)
{
*mark = SDL_AtomicGet(&current_mark);
*loop = SDL_AtomicGet(&current_loop);
*mark = sdl::GetAtomicInt(&current_mark);
*loop = sdl::GetAtomicInt(&current_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(&current_loop, 0);
SDL_AtomicSet(&current_mark, 0);
sdl::SetAtomicInt(&current_loop, 0);
sdl::SetAtomicInt(&current_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(&current_loop, &current_mark)) {
SDL_AtomicCAS(&current_seq, current->seq(), 0);
sdl::CompareAndSwapAtomicInt(&current_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
View File
@@ -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();
+3 -3
View File
@@ -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; }
+1 -1
View File
@@ -3,7 +3,7 @@
#include <memory>
#include <string>
#include <SDL.h>
#include "sdl_compat.h"
struct DecodedChunk {
const Uint8* data;
+15 -1
View File
@@ -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
View File
@@ -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);
+4 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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__
+58
View File
@@ -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
View File
@@ -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
View File
@@ -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
```