mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-09-22 22:48:08 +03:00
Compare commits
87
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1918350b19 | ||
|
|
9504ee7b65 | ||
|
|
f033a507d3 | ||
|
|
fbeb9a780e | ||
|
|
8c69f53073 | ||
|
|
cc96c85381 | ||
|
|
a2618319d9 | ||
|
|
30fd5768da | ||
|
|
15a769d83c | ||
|
|
253de6adc8 | ||
|
|
a0daa3261c | ||
|
|
98e9d8bcfb | ||
|
|
b7854aaec1 | ||
|
|
905eb34590 | ||
|
|
8f22566adb | ||
|
|
48ded43e2a | ||
|
|
571bec63a2 | ||
|
|
345c017c22 | ||
|
|
205004086b | ||
|
|
6820bcfcb8 | ||
|
|
5ce1494b59 | ||
|
|
dd45e566f6 | ||
|
|
1ccc7630c1 | ||
|
|
6456f59c37 | ||
|
|
f748e8f678 | ||
|
|
a9e23508b7 | ||
|
|
c3bd978f02 | ||
|
|
50a6b17c5e | ||
|
|
0d49619366 | ||
|
|
da3cc5d900 | ||
|
|
1785e93fcc | ||
|
|
a77e849f82 | ||
|
|
978dbdbc3b | ||
|
|
e3c168c5a6 | ||
|
|
9f8c2af842 | ||
|
|
5d8b1c5632 | ||
|
|
30188c3a4f | ||
|
|
a627a8ea3d | ||
|
|
0cf9c1fe8d | ||
|
|
0887c44292 | ||
|
|
27a0c89a73 | ||
|
|
5c827225ab | ||
|
|
bc37ba6256 | ||
|
|
ab4bc2d8d1 | ||
|
|
f26321c5e0 | ||
|
|
1d897c72d0 | ||
|
|
172c76b4e2 | ||
|
|
100584f896 | ||
|
|
975f618b53 | ||
|
|
0c8bab0c64 | ||
|
|
0192fd3d8e | ||
|
|
d69fc5c6a3 | ||
|
|
68a39843e2 | ||
|
|
e3d7b60785 | ||
|
|
2bb891ada8 | ||
|
|
f109fe11f5 | ||
|
|
73b3377b77 | ||
|
|
9400572502 | ||
|
|
a1e063c4b2 | ||
|
|
602c55a5e5 | ||
|
|
9624192a41 | ||
|
|
5c7436e2b1 | ||
|
|
6c9844697f | ||
|
|
210c0a13e4 | ||
|
|
3ebb1faece | ||
|
|
286f150510 | ||
|
|
a194cf10a6 | ||
|
|
9587d350ec | ||
|
|
02f62d51bb | ||
|
|
54b98ec3e4 | ||
|
|
8cde827ee6 | ||
|
|
a5d67b067c | ||
|
|
5cb70110b9 | ||
|
|
e3dcadd5dd | ||
|
|
ff31d03153 | ||
|
|
63886d7d00 | ||
|
|
c119c2f969 | ||
|
|
fb63d42ec1 | ||
|
|
6f631d073d | ||
|
|
49dca57fbd | ||
|
|
c8987da470 | ||
|
|
0dfb3eb378 | ||
|
|
269132fede | ||
|
|
651fc21a19 | ||
|
|
03f86bc4d7 | ||
|
|
4ffab8e4e9 | ||
|
|
1593452146 |
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Install Deps
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libwebp-dev libportmidi-dev libcjson-dev asciidoctor
|
||||
sudo apt install libsdl2-dev libfreetype-dev libsdl2-mixer-dev libwebp-dev libportmidi-dev libcjson-dev asciidoctor gettext
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@@ -27,3 +27,16 @@ jobs:
|
||||
- name: Test
|
||||
run: ctest --output-on-failure
|
||||
working-directory: out/${{ matrix.build-type }}
|
||||
|
||||
- name: Verify xsystem35.pot is up to date
|
||||
if: matrix.build-type == 'Debug'
|
||||
run: |
|
||||
xgettext --default-domain=xsystem35 --directory=. \
|
||||
--keyword=_ --keyword=N_ \
|
||||
--files-from=po/POTFILES.in \
|
||||
--output=/tmp/fresh.pot
|
||||
if ! diff <(grep '^msgid' po/xsystem35.pot | sort) \
|
||||
<(grep '^msgid' /tmp/fresh.pot | sort); then
|
||||
echo "::error::po/xsystem35.pot is out of date. Build the 'pot' target and commit the changes under po/."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
msystem: ${{ matrix.sys }}
|
||||
pacboy: >-
|
||||
SDL2:p
|
||||
SDL2_ttf:p
|
||||
freetype:p
|
||||
SDL2_mixer:p
|
||||
libwebp:p
|
||||
${{ matrix.deps }}
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## 2.20.0 - 2026-09-11
|
||||
- Improved font rendering to more closely match the original engine. On
|
||||
Windows, MS Gothic and MS Mincho are now used by default when available.
|
||||
- The experimental `enable_zb` option has been removed. (#44)
|
||||
- SACT: Message skipping now also skips text animation, effects, waits, and
|
||||
related input waits.
|
||||
- SACT: BGM now honors the loop count specified by the game.
|
||||
- SACT: Fixed timer handling in 楽園行.
|
||||
- The popup menu and dialogs can now be operated with the keyboard.
|
||||
- Fixed mouse coordinates changing unexpectedly when automatic mouse movement
|
||||
is disabled in System 3.8/3.9 games.
|
||||
- Android: The status bar now stays hidden after returning to the game. (#81)
|
||||
- Debugger: Added an `info cache` command for viewing cache usage and hit rates.
|
||||
|
||||
## 2.19.1 - 2026-08-02
|
||||
- Fixed an issue that prevented moving upward during battles in Rance 4.
|
||||
|
||||
## 2.19.0 - 2026-07-19
|
||||
- Replaced the GTK-based popup menu and dialogs with a pure-SDL implementation.
|
||||
- On Android, the popup menu opens with a three-finger tap.
|
||||
- Android: Added installation directly from CD-ROM images.
|
||||
- Implemented volume control panel.
|
||||
- Added `mute_on_unfocus` option to mute audio while the window is unfocused.
|
||||
|
||||
## 2.18.0 - 2026-06-23
|
||||
- Added support for the intro demo of Daiakuji.
|
||||
- Replaced the bundled mincho font with IPA Mincho.
|
||||
|
||||
+25
-22
@@ -5,7 +5,7 @@ set(CMAKE_C_STANDARD 99)
|
||||
enable_testing()
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
set(XSYSTEM35_VERSION "2.18.0")
|
||||
set(XSYSTEM35_VERSION "2.20.0")
|
||||
|
||||
include(CheckSymbolExists)
|
||||
include(FetchContent)
|
||||
@@ -55,6 +55,8 @@ check_symbol_exists(mmap "sys/mman.h" HAVE_MMAP)
|
||||
check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION)
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
option(JSPI "Use JavaScript Promise Integration instead of Asyncify" OFF)
|
||||
|
||||
function(add_emscripten_library name option)
|
||||
add_library(${name} INTERFACE)
|
||||
set_target_properties(${name} PROPERTIES
|
||||
@@ -63,14 +65,14 @@ if (EMSCRIPTEN)
|
||||
endfunction()
|
||||
add_emscripten_library(zlib -sUSE_ZLIB=1)
|
||||
add_emscripten_library(sdl2 -sUSE_SDL=2)
|
||||
add_emscripten_library(sdl2_ttf -sUSE_SDL_TTF=2)
|
||||
add_emscripten_library(freetype2 -sUSE_FREETYPE=1)
|
||||
set(DEFAULT_FONT_PATH /fonts/)
|
||||
fetch_webp()
|
||||
set(HAVE_WEBP 1)
|
||||
|
||||
elseif (ANDROID)
|
||||
add_library(sdl2 ALIAS SDL2)
|
||||
add_library(sdl2_ttf ALIAS SDL2_ttf)
|
||||
add_library(freetype2 ALIAS freetype)
|
||||
add_library(sdl2_mixer ALIAS SDL2_mixer)
|
||||
find_library(ndk_log log)
|
||||
find_library(ndk_zlib z)
|
||||
@@ -87,24 +89,18 @@ else() # non-emscripten, non-android
|
||||
add_library(zlib ALIAS ZLIB::ZLIB)
|
||||
|
||||
include(FindPkgConfig)
|
||||
optional_pkg_check_modules(GTK3 IMPORTED_TARGET gtk+-3.0)
|
||||
if (GTK3_FOUND)
|
||||
set(ENABLE_GTK 1)
|
||||
endif()
|
||||
|
||||
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2)
|
||||
pkg_check_modules(SDL2TTF REQUIRED IMPORTED_TARGET SDL2_ttf)
|
||||
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2>=2.18.0)
|
||||
pkg_check_modules(FREETYPE REQUIRED IMPORTED_TARGET freetype2)
|
||||
pkg_check_modules(SDL2MIXER REQUIRED IMPORTED_TARGET SDL2_mixer)
|
||||
if (WIN32)
|
||||
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} -lstdc++>)
|
||||
add_static_library(freetype2 FREETYPE)
|
||||
# harfbuzz and graphite2, which the static freetype depends on, are C++ libraries.
|
||||
target_link_libraries(freetype2 INTERFACE -lstdc++)
|
||||
add_static_library(sdl2_mixer SDL2MIXER)
|
||||
else()
|
||||
add_library(sdl2 ALIAS PkgConfig::SDL2)
|
||||
add_library(sdl2_ttf ALIAS PkgConfig::SDL2TTF)
|
||||
add_library(freetype2 ALIAS PkgConfig::FREETYPE)
|
||||
add_library(sdl2_mixer ALIAS PkgConfig::SDL2MIXER)
|
||||
set(DEFAULT_FONT_PATH ${CMAKE_INSTALL_PREFIX}/share/xsystem35/fonts/)
|
||||
endif()
|
||||
@@ -153,14 +149,19 @@ else() # non-emscripten, non-android
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Menu
|
||||
# i18n support
|
||||
#
|
||||
# Translate via libintl if available; otherwise fall back to a built-in catalog
|
||||
# compiled from the .po files.
|
||||
#
|
||||
# To add a language, create po/<lang>.po and list <lang> here.
|
||||
set(NLS_LANGUAGES ja)
|
||||
|
||||
if (ENABLE_GTK)
|
||||
# i18n support (currently only menus are translated)
|
||||
if (NOT EMSCRIPTEN AND NOT ANDROID AND NOT WIN32)
|
||||
include(FindIntl)
|
||||
include(FindGettext)
|
||||
if (Intl_FOUND AND GETTEXT_FOUND)
|
||||
set(ENABLE_NLS 1)
|
||||
set(HAVE_LIBINTL 1)
|
||||
add_compile_definitions(LOCALEDIR="${CMAKE_INSTALL_PREFIX}/share/locale")
|
||||
include_directories(${Intl_INCLUDE_DIRS})
|
||||
link_libraries(${Intl_LIBRARIES})
|
||||
@@ -227,9 +228,11 @@ add_subdirectory(fonts)
|
||||
if (NOT ANDROID AND NOT EMSCRIPTEN)
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
if (ENABLE_NLS)
|
||||
add_subdirectory(po)
|
||||
endif()
|
||||
add_subdirectory(po)
|
||||
|
||||
add_subdirectory(modules)
|
||||
target_link_libraries(xsystem35 PRIVATE modules)
|
||||
|
||||
if (NOT ANDROID AND NOT EMSCRIPTEN)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
@@ -105,7 +105,7 @@ See [xsystem35 command manual](doc/xsystem35.6.adoc) for detailed usage.
|
||||
### Linux (Debian / Ubuntu)
|
||||
|
||||
```bash
|
||||
$ sudo apt install build-essential cmake libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libwebp-dev libportmidi-dev libcjson-dev asciidoctor
|
||||
$ sudo apt install build-essential cmake libsdl2-dev libsdl2-mixer-dev libfreetype-dev libwebp-dev libportmidi-dev libcjson-dev asciidoctor
|
||||
$ mkdir -p out/debug
|
||||
$ cd out/debug
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Debug ../../
|
||||
@@ -117,7 +117,7 @@ $ make && make install
|
||||
[Homebrew](https://brew.sh/) is required.
|
||||
|
||||
```bash
|
||||
$ brew install cmake pkg-config sdl2 sdl2_mixer sdl2_ttf webp portmidi cjson asciidoctor
|
||||
$ brew install cmake pkg-config sdl2 sdl2_mixer freetype webp portmidi cjson asciidoctor
|
||||
$ mkdir -p out/debug
|
||||
$ cd out/debug
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Debug ../../
|
||||
@@ -129,7 +129,7 @@ $ make && make install
|
||||
[MSYS2](https://www.msys2.org) is required.
|
||||
|
||||
```bash
|
||||
$ pacman -S cmake 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-SDL2_mixer mingw-w64-ucrt-x86_64-libwebp mingw-w64-ucrt-x86_64-portmidi mingw-w64-ucrt-x86_64-cjson
|
||||
$ pacman -S cmake mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-SDL2 mingw-w64-ucrt-x86_64-freetype mingw-w64-ucrt-x86_64-SDL2_mixer mingw-w64-ucrt-x86_64-libwebp mingw-w64-ucrt-x86_64-portmidi mingw-w64-ucrt-x86_64-cjson
|
||||
$ mkdir -p out/debug
|
||||
$ cd out/debug
|
||||
$ cmake -G"MSYS Makefiles" -DCMAKE_BUILD_TYPE=Debug ../../
|
||||
|
||||
+22
-2
@@ -16,13 +16,31 @@ Alternatively, you can install `xsystem35-sdl2` using [Obtainium](https://github
|
||||
|
||||
## Usage
|
||||
### Basic Usage
|
||||
You can install games either from a prepared ZIP file or directly from a CD-ROM
|
||||
image.
|
||||
|
||||
#### Installing from a ZIP
|
||||
1. Create a ZIP file containing all the game files and BGM files (see
|
||||
[below](#preparing-a-zip) for details), and transfer it to your device.
|
||||
2. Open the app. Tap the option menu (three dots in the top right corner) and
|
||||
select "Install from ZIP".
|
||||
3. Select the ZIP file you created in step 1.
|
||||
4. The game will start. To simulate a right-click, tap the black bars on either
|
||||
the left or right, or top or bottom of the screen.
|
||||
4. The game will start.
|
||||
|
||||
#### Installing from a CD image
|
||||
1. Transfer the CD image file(s) to your device.
|
||||
2. Open the app. Tap the option menu and select "Install from CD image".
|
||||
3. Select the image file and, when needed, the matching metadata file at the
|
||||
same time. The supported combinations are:
|
||||
- `.iso`
|
||||
- `.bin` or `.img` with matching `.cue`
|
||||
- `.bin` or `.img` with matching `.ccd`
|
||||
- `.mdf` with matching `.mds`
|
||||
|
||||
In many Android file pickers, tapping a file immediately selects only that
|
||||
one file. Long-press a file first to enter multi-select mode, then select
|
||||
both the image file and its metadata file.
|
||||
4. The game will start.
|
||||
|
||||
### Preparing a ZIP
|
||||
- Include all files from the `GAMEDATA` folder (such as `.ALD` files and
|
||||
@@ -41,6 +59,8 @@ Note: This ZIP format is also compatible with
|
||||
[Kichikuou on Web](http://kichikuou.github.io/web/).
|
||||
|
||||
### Miscellaneous
|
||||
- To simulate a right-click, tap the black bars on either the left or right,
|
||||
or top or bottom of the screen.
|
||||
- You can export or import save files via the game list's option menu.
|
||||
- To uninstall a game, long-tap its title in the game list.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ android {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
versionCode 36
|
||||
versionName "2.18.0" + gitRevision()
|
||||
versionName "2.20.0" + gitRevision()
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
|
||||
@@ -85,6 +85,7 @@ dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
}
|
||||
|
||||
static String gitRevision() {
|
||||
|
||||
@@ -11,9 +11,10 @@ FetchContent_Declare(
|
||||
URL_HASH SHA1=ce98fa93e31836a751feca374ab28a0770b63c16
|
||||
)
|
||||
FetchContent_Declare(
|
||||
SDL_ttf
|
||||
URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.22.0/SDL2_ttf-2.22.0.tar.gz
|
||||
URL_HASH SHA1=da5e86b601ad299a697878fab1af6f3be47b529d
|
||||
freetype
|
||||
URL https://downloads.sourceforge.net/project/freetype/freetype2/2.14.3/freetype-2.14.3.tar.gz
|
||||
https://download.savannah.gnu.org/releases/freetype/freetype-2.14.3.tar.gz
|
||||
URL_HASH SHA1=a911f65c6355ddcf7aaba46e250dbc00f67e1678
|
||||
)
|
||||
FetchContent_Declare(
|
||||
SDL_mixer
|
||||
@@ -21,9 +22,12 @@ FetchContent_Declare(
|
||||
URL_HASH SHA1=a58c69f9d00e44833b9e00e1adb58d85759ca499
|
||||
)
|
||||
|
||||
set(SDL2TTF_SAMPLES OFF CACHE BOOL "Build the SDL2_ttf sample program(s)" FORCE)
|
||||
set(SDL2TTF_INSTALL OFF CACHE BOOL "Enable SDL2_ttf install target" FORCE)
|
||||
set(SDL2TTF_VENDORED ON CACHE BOOL "Use vendored third-party libraries" FORCE)
|
||||
# Only FreeType's own rasterizer is needed, none of its optional dependencies.
|
||||
set(FT_DISABLE_ZLIB ON CACHE BOOL "Disable use of system zlib" FORCE)
|
||||
set(FT_DISABLE_BZIP2 ON CACHE BOOL "Disable use of system libbz2" FORCE)
|
||||
set(FT_DISABLE_PNG ON CACHE BOOL "Disable use of system libpng" FORCE)
|
||||
set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "Disable use of harfbuzz" FORCE)
|
||||
set(FT_DISABLE_BROTLI ON CACHE BOOL "Disable use of brotli" FORCE)
|
||||
|
||||
set(SDL2MIXER_OPUS OFF CACHE BOOL "Enable Opus music" FORCE)
|
||||
set(SDL2MIXER_FLAC OFF CACHE BOOL "Enable FLAC music" FORCE)
|
||||
@@ -33,7 +37,7 @@ set(SDL2MIXER_WAVPACK OFF CACHE BOOL "Enable WavPack music" FORCE)
|
||||
set(SDL2MIXER_SAMPLES OFF CACHE BOOL "Build the SDL2_mixer sample program(s)" FORCE)
|
||||
set(SDL2MIXER_INSTALL OFF CACHE BOOL "Enable SDL2_mixer install target" FORCE)
|
||||
|
||||
FetchContent_MakeAvailable(SDL SDL_ttf SDL_mixer)
|
||||
FetchContent_MakeAvailable(SDL freetype SDL_mixer)
|
||||
|
||||
# The main CMakeLists.txt of xsystem35
|
||||
add_subdirectory(${PROJECT_ROOT_DIR} xsystem35)
|
||||
@@ -47,8 +51,7 @@ file(COPY_FILE ${PROJECT_ROOT_DIR}/COPYING ${ASSETS_DIR}/licenses/xsystem35)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/MTLc3m.txt ${ASSETS_DIR}/licenses/MTLc3m)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/mincho.txt ${ASSETS_DIR}/licenses/mincho)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/nanojpeg.txt ${ASSETS_DIR}/licenses/nanojpeg)
|
||||
file(COPY_FILE ${PROJECT_ROOT_DIR}/licenses/microui.txt ${ASSETS_DIR}/licenses/microui)
|
||||
file(COPY_FILE ${sdl_SOURCE_DIR}/LICENSE.txt ${ASSETS_DIR}/licenses/SDL)
|
||||
file(COPY_FILE ${sdl_ttf_SOURCE_DIR}/LICENSE.txt ${ASSETS_DIR}/licenses/SDL_ttf)
|
||||
file(COPY_FILE ${sdl_ttf_SOURCE_DIR}/external/freetype/docs/GPLv2.TXT ${ASSETS_DIR}/licenses/freetype)
|
||||
file(COPY_FILE ${sdl_ttf_SOURCE_DIR}/external/harfbuzz/COPYING ${ASSETS_DIR}/licenses/harfbuzz)
|
||||
file(COPY_FILE ${freetype_SOURCE_DIR}/docs/GPLv2.TXT ${ASSETS_DIR}/licenses/freetype)
|
||||
file(COPY_FILE ${sdl_mixer_SOURCE_DIR}/LICENSE.txt ${ASSETS_DIR}/licenses/SDL_mixer)
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<!-- Example of setting SDL hints from AndroidManifest.xml:
|
||||
<meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
|
||||
-->
|
||||
<meta-data android:name="SDL_ENV.SDL_IOS_ORIENTATIONS" android:value="LandscapeLeft LandscapeRight"/>
|
||||
|
||||
<activity android:name=".LauncherActivity"
|
||||
android:exported="true">
|
||||
@@ -78,6 +79,7 @@
|
||||
|
||||
<activity android:name=".GameActivity"
|
||||
android:label="@string/app_name"
|
||||
android:process=":game"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||
android:theme="@style/AppTheme"
|
||||
|
||||
@@ -0,0 +1,566 @@
|
||||
/* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
package io.github.kichikuou.xsystem35
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.os.ParcelFileDescriptor
|
||||
import java.io.Closeable
|
||||
import java.io.EOFException
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.OutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
import java.util.Locale
|
||||
|
||||
internal class CdImageReader private constructor(
|
||||
private val image: RandomAccessImage,
|
||||
private val tracks: List<TrackInfo?>,
|
||||
) : Closeable {
|
||||
fun readSector(sector: Int, buffer: ByteArray) {
|
||||
readDataFully(sector.toLong() * ISO_SECTOR_SIZE, buffer, 0, ISO_SECTOR_SIZE)
|
||||
}
|
||||
|
||||
fun readDataFully(offset: Long, buffer: ByteArray, bufferOffset: Int, length: Int) {
|
||||
// ISO9660 always sees logical 2048-byte data sectors even when the
|
||||
// image stores each sector as a raw 2352-byte CD-ROM block.
|
||||
var logicalOffset = offset
|
||||
var outOffset = bufferOffset
|
||||
var remaining = length
|
||||
val sectorBuffer = ByteArray(ISO_SECTOR_SIZE)
|
||||
while (remaining > 0) {
|
||||
val sector = (logicalOffset / ISO_SECTOR_SIZE).toInt()
|
||||
val offsetInSector = (logicalOffset % ISO_SECTOR_SIZE).toInt()
|
||||
val count = minOf(remaining, ISO_SECTOR_SIZE - offsetInSector)
|
||||
readSectorPayload(sector, sectorBuffer)
|
||||
sectorBuffer.copyInto(buffer, outOffset, offsetInSector, offsetInSector + count)
|
||||
logicalOffset += count
|
||||
outOffset += count
|
||||
remaining -= count
|
||||
}
|
||||
}
|
||||
|
||||
fun audioTracks(): List<Int> {
|
||||
return tracks.mapIndexedNotNull { trackNumber, track ->
|
||||
if (track?.isAudio == true) trackNumber else null
|
||||
}
|
||||
}
|
||||
|
||||
fun extractAudioTrack(trackNumber: Int, output: OutputStream) {
|
||||
val track = tracks.getOrNull(trackNumber)
|
||||
if (track?.isAudio != true) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
val dataSize = track.numSectors.toLong() * RAW_AUDIO_SECTOR_SIZE
|
||||
// CD-DA sectors are already 44.1kHz stereo 16-bit PCM; wrapping the
|
||||
// raw sector stream in a WAV header makes it playable by SDL_mixer.
|
||||
writeWaveHeader(output, dataSize)
|
||||
val buffer = ByteArray(track.blockSize * 16)
|
||||
var sector = 0
|
||||
while (sector < track.numSectors) {
|
||||
val sectorsToRead = minOf(16, track.numSectors - sector)
|
||||
val bytesToRead = sectorsToRead * track.blockSize
|
||||
image.readFully(track.offset + sector.toLong() * track.blockSize, buffer, 0, bytesToRead)
|
||||
output.write(buffer, 0, sectorsToRead * RAW_AUDIO_SECTOR_SIZE)
|
||||
sector += sectorsToRead
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
image.close()
|
||||
}
|
||||
|
||||
private fun readSectorPayload(sector: Int, buffer: ByteArray) {
|
||||
val track = findDataTrack(sector)
|
||||
val offset = track.offset + (sector - track.startSector).toLong() * track.blockSize + track.blockOffset
|
||||
image.readFully(offset, buffer, 0, ISO_SECTOR_SIZE)
|
||||
}
|
||||
|
||||
private fun findDataTrack(sector: Int): TrackInfo {
|
||||
for (track in tracks) {
|
||||
if (track == null || track.isAudio) {
|
||||
continue
|
||||
}
|
||||
if (sector >= track.startSector && sector < track.startSector + track.numSectors) {
|
||||
return track
|
||||
}
|
||||
}
|
||||
throw InstallFailureException(R.string.invalid_iso9660_image)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ISO_SECTOR_SIZE = 2048
|
||||
private const val RAW_AUDIO_SECTOR_SIZE = 2352
|
||||
private const val WAVE_HEADER_SIZE = 44
|
||||
private const val MDS_MODE_AUDIO = 0xa9
|
||||
private const val MDS_MODE_MODE1 = 0xaa
|
||||
|
||||
fun open(
|
||||
contentResolver: ContentResolver,
|
||||
files: List<SelectedInstallFile>,
|
||||
tempDir: File,
|
||||
): CdImageReader {
|
||||
val selected = selectImageFiles(files)
|
||||
val image = RandomAccessImage.open(contentResolver, selected.image, tempDir)
|
||||
try {
|
||||
if (selected.metadata == null) {
|
||||
return CdImageReader(image, listOf(null, TrackInfo.iso(image.size)))
|
||||
}
|
||||
val metadataName = selected.metadata.displayName.lowercase(Locale.US)
|
||||
val tracks = when {
|
||||
metadataName.endsWith(".cue") -> {
|
||||
val cueText = readMetadataText(contentResolver, selected.metadata)
|
||||
parseCue(cueText, image.size)
|
||||
}
|
||||
metadataName.endsWith(".ccd") -> {
|
||||
val ccdText = readMetadataText(contentResolver, selected.metadata)
|
||||
parseCcd(ccdText, image.size)
|
||||
}
|
||||
metadataName.endsWith(".mds") -> {
|
||||
val mdsBytes = readMetadataBytes(contentResolver, selected.metadata)
|
||||
parseMds(mdsBytes)
|
||||
}
|
||||
else -> throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
return CdImageReader(image, tracks)
|
||||
} catch (e: Exception) {
|
||||
image.close()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
internal fun openForFile(
|
||||
imageFile: File,
|
||||
metadataName: String? = null,
|
||||
metadataBytes: ByteArray? = null,
|
||||
): CdImageReader {
|
||||
val image = RandomAccessImage.open(imageFile)
|
||||
try {
|
||||
if (metadataName == null) {
|
||||
return CdImageReader(image, listOf(null, TrackInfo.iso(image.size)))
|
||||
}
|
||||
val tracks = when {
|
||||
metadataName.lowercase(Locale.US).endsWith(".cue") -> {
|
||||
parseCue(metadataBytes?.toString(Charsets.UTF_8) ?: "", image.size)
|
||||
}
|
||||
metadataName.lowercase(Locale.US).endsWith(".ccd") -> {
|
||||
parseCcd(metadataBytes?.toString(Charsets.UTF_8) ?: "", image.size)
|
||||
}
|
||||
metadataName.lowercase(Locale.US).endsWith(".mds") -> {
|
||||
parseMds(metadataBytes ?: ByteArray(0))
|
||||
}
|
||||
else -> throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
return CdImageReader(image, tracks)
|
||||
} catch (e: Exception) {
|
||||
image.close()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectImageFiles(files: List<SelectedInstallFile>): SelectedImageFiles {
|
||||
if (files.isEmpty()) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
val isoFiles = files.filter { it.displayName.lowercase(Locale.US).endsWith(".iso") }
|
||||
if (isoFiles.size == 1 && files.size == 1) {
|
||||
return SelectedImageFiles(isoFiles.single(), null)
|
||||
}
|
||||
|
||||
val imageFiles = files.filter {
|
||||
val name = it.displayName.lowercase(Locale.US)
|
||||
name.endsWith(".bin") || name.endsWith(".img") || name.endsWith(".mdf")
|
||||
}
|
||||
val metadataFiles = files.filter {
|
||||
val name = it.displayName.lowercase(Locale.US)
|
||||
name.endsWith(".cue") || name.endsWith(".ccd") || name.endsWith(".mds")
|
||||
}
|
||||
if (imageFiles.size == 1 && metadataFiles.isEmpty() && files.size == 1 ||
|
||||
imageFiles.isEmpty() && metadataFiles.size == 1 && files.size == 1
|
||||
) {
|
||||
throw InstallFailureException(R.string.missing_cd_image_metadata)
|
||||
}
|
||||
if (imageFiles.size != 1 || metadataFiles.size != 1 || files.size != 2) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
val imageBase = imageFiles.single().baseName()
|
||||
val metadataBase = metadataFiles.single().baseName()
|
||||
if (imageBase != metadataBase) {
|
||||
throw InstallFailureException(R.string.missing_cd_image_metadata)
|
||||
}
|
||||
if (!isSupportedMetadataForImage(imageFiles.single(), metadataFiles.single())) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
return SelectedImageFiles(imageFiles.single(), metadataFiles.single())
|
||||
}
|
||||
|
||||
private fun isSupportedMetadataForImage(
|
||||
image: SelectedInstallFile,
|
||||
metadata: SelectedInstallFile,
|
||||
): Boolean {
|
||||
val imageName = image.displayName.lowercase(Locale.US)
|
||||
val metadataName = metadata.displayName.lowercase(Locale.US)
|
||||
return if (imageName.endsWith(".mdf")) {
|
||||
metadataName.endsWith(".mds")
|
||||
} else {
|
||||
metadataName.endsWith(".cue") || metadataName.endsWith(".ccd")
|
||||
}
|
||||
}
|
||||
|
||||
private fun readMetadataText(
|
||||
contentResolver: ContentResolver,
|
||||
file: SelectedInstallFile,
|
||||
): String {
|
||||
return contentResolver.openInputStream(file.uri)?.bufferedReader()?.use {
|
||||
it.readText()
|
||||
} ?: throw InstallFailureException(R.string.cd_image_read_error)
|
||||
}
|
||||
|
||||
private fun readMetadataBytes(
|
||||
contentResolver: ContentResolver,
|
||||
file: SelectedInstallFile,
|
||||
): ByteArray {
|
||||
return contentResolver.openInputStream(file.uri)?.use {
|
||||
it.readBytes()
|
||||
} ?: throw InstallFailureException(R.string.cd_image_read_error)
|
||||
}
|
||||
|
||||
private fun parseCue(cueText: String, imageSize: Long): List<TrackInfo?> {
|
||||
val cueTracks = mutableListOf<CueTrack?>()
|
||||
var currentTrack: Int? = null
|
||||
for (line in cueText.lines()) {
|
||||
val fields = line.trim().split(Regex("\\s+"))
|
||||
if (fields.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
when (fields[0].uppercase(Locale.US)) {
|
||||
"TRACK" -> {
|
||||
if (fields.size < 3) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
currentTrack = fields[1].toIntOrNull()
|
||||
?: throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
while (cueTracks.size <= currentTrack) {
|
||||
cueTracks.add(null)
|
||||
}
|
||||
cueTracks[currentTrack] = when (fields[2].uppercase(Locale.US)) {
|
||||
"MODE1/2048" -> CueTrack(false, 2048, 0)
|
||||
"MODE1/2352" -> CueTrack(false, 2352, 16)
|
||||
"AUDIO" -> CueTrack(true, 2352, 0)
|
||||
else -> throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
}
|
||||
"INDEX" -> {
|
||||
val trackNumber = currentTrack ?: continue
|
||||
if (fields.size < 3) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
val indexNumber = fields[1].toIntOrNull()
|
||||
?: throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
cueTracks[trackNumber]?.index?.put(indexNumber, indexToSector(fields[2]))
|
||||
}
|
||||
}
|
||||
}
|
||||
return makeTrackInfo(cueTracks, imageSize)
|
||||
}
|
||||
|
||||
private fun parseCcd(ccdText: String, imageSize: Long): List<TrackInfo?> {
|
||||
val cueTracks = mutableListOf<CueTrack?>()
|
||||
var currentTrack: Int? = null
|
||||
for (line in ccdText.lines()) {
|
||||
val trimmed = line.trim()
|
||||
val trackMatch = Regex("""\[TRACK ([0-9]+)]""").matchEntire(trimmed)
|
||||
if (trackMatch != null) {
|
||||
currentTrack = trackMatch.groupValues[1].toInt()
|
||||
while (cueTracks.size <= currentTrack) {
|
||||
cueTracks.add(null)
|
||||
}
|
||||
cueTracks[currentTrack] = CueTrack(false, 2352, 16)
|
||||
continue
|
||||
}
|
||||
val trackNumber = currentTrack ?: continue
|
||||
val keyValue = trimmed.split("=", limit = 2)
|
||||
if (keyValue.size != 2) {
|
||||
continue
|
||||
}
|
||||
when (keyValue[0].uppercase(Locale.US)) {
|
||||
"MODE" -> if (keyValue[1] == "0") {
|
||||
cueTracks[trackNumber] = CueTrack(true, 2352, 0, cueTracks[trackNumber]?.index ?: mutableMapOf())
|
||||
}
|
||||
"INDEX 0" -> cueTracks[trackNumber]?.index?.put(
|
||||
0,
|
||||
keyValue[1].toIntOrNull() ?: throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
)
|
||||
"INDEX 1" -> cueTracks[trackNumber]?.index?.put(
|
||||
1,
|
||||
keyValue[1].toIntOrNull() ?: throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
)
|
||||
}
|
||||
}
|
||||
return makeTrackInfo(cueTracks, imageSize)
|
||||
}
|
||||
|
||||
private fun parseMds(mdsBytes: ByteArray): List<TrackInfo?> {
|
||||
if (mdsBytes.size < 0x70 ||
|
||||
String(mdsBytes, 0, 16, Charsets.US_ASCII).trimEnd('\u0000') != "MEDIA DESCRIPTOR"
|
||||
) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
val entries = mdsBytes[0x62].toInt() and 0xff
|
||||
if (0x70 + entries * 0x58 > mdsBytes.size) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
|
||||
val tracks = MutableList<TrackInfo?>(100) { null }
|
||||
for (i in 0 until entries) {
|
||||
val trackOffset = 0x70 + i * 0x50
|
||||
val extraOffset = 0x70 + entries * 0x50 + i * 8
|
||||
val mode = mdsBytes[trackOffset].toInt() and 0xff
|
||||
val trackNumber = mdsBytes[trackOffset + 0x04].toInt() and 0xff
|
||||
val sectorSize = readLittleEndianShort(mdsBytes, trackOffset + 0x10)
|
||||
// MDS offsets are treated as 32-bit file offsets, so images
|
||||
// beyond 4GB are unsupported.
|
||||
val imageOffset = readLittleEndianInt(mdsBytes, trackOffset + 0x28)
|
||||
val sectors = readLittleEndianInt(mdsBytes, extraOffset + 0x04).toInt()
|
||||
if (trackNumber >= tracks.size) {
|
||||
continue
|
||||
}
|
||||
tracks[trackNumber] = when (mode) {
|
||||
MDS_MODE_AUDIO -> TrackInfo(
|
||||
isAudio = true,
|
||||
offset = imageOffset,
|
||||
blockSize = sectorSize,
|
||||
blockOffset = 0,
|
||||
startSector = 0,
|
||||
numSectors = sectors,
|
||||
)
|
||||
MDS_MODE_MODE1 -> TrackInfo(
|
||||
isAudio = false,
|
||||
offset = imageOffset,
|
||||
blockSize = sectorSize,
|
||||
blockOffset = 16,
|
||||
startSector = 0,
|
||||
numSectors = sectors,
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
if (tracks.getOrNull(1)?.isAudio != false) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
return tracks
|
||||
}
|
||||
|
||||
private fun makeTrackInfo(cueTracks: List<CueTrack?>, imageSize: Long): List<TrackInfo?> {
|
||||
val tracks = MutableList<TrackInfo?>(cueTracks.size) { null }
|
||||
var offset = 0L
|
||||
var startSector = 0
|
||||
for (trackNumber in 1 until cueTracks.size) {
|
||||
val cueTrack = cueTracks[trackNumber] ?: continue
|
||||
val index1 = cueTrack.index[1]
|
||||
?: throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
// INDEX 0 describes a pregap, but a zero INDEX 0 is treated as absent.
|
||||
cueTrack.index[0]?.takeIf { it != 0 }?.let { index0 ->
|
||||
val gap = index1 - index0
|
||||
if (gap > 0) {
|
||||
startSector += gap
|
||||
offset += gap.toLong() * cueTrack.blockSize
|
||||
}
|
||||
}
|
||||
val nextTrack = cueTracks.drop(trackNumber + 1).firstOrNull { it != null }
|
||||
val nextStart = nextTrack?.let { it.index[0]?.takeIf { index -> index != 0 } ?: it.index[1] }
|
||||
val numSectors = if (nextStart != null) {
|
||||
nextStart - index1
|
||||
} else {
|
||||
((imageSize - offset) / cueTrack.blockSize).toInt()
|
||||
}
|
||||
if (numSectors <= 0) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
tracks[trackNumber] = TrackInfo(
|
||||
isAudio = cueTrack.isAudio,
|
||||
offset = offset,
|
||||
blockSize = cueTrack.blockSize,
|
||||
blockOffset = cueTrack.blockOffset,
|
||||
startSector = startSector,
|
||||
numSectors = numSectors,
|
||||
)
|
||||
startSector += numSectors
|
||||
offset += numSectors.toLong() * cueTrack.blockSize
|
||||
}
|
||||
return tracks
|
||||
}
|
||||
|
||||
private fun indexToSector(index: String): Int {
|
||||
val parts = index.split(":").map {
|
||||
it.toIntOrNull() ?: throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
if (parts.size != 3) {
|
||||
throw InstallFailureException(R.string.unsupported_cd_image)
|
||||
}
|
||||
return parts[0] * 60 * 75 + parts[1] * 75 + parts[2]
|
||||
}
|
||||
|
||||
private fun writeWaveHeader(output: OutputStream, dataSize: Long) {
|
||||
val header = ByteArray(WAVE_HEADER_SIZE)
|
||||
writeAscii(header, 0, "RIFF")
|
||||
writeLittleEndianInt(header, 4, dataSize + 36)
|
||||
writeAscii(header, 8, "WAVE")
|
||||
writeAscii(header, 12, "fmt ")
|
||||
writeLittleEndianInt(header, 16, 16)
|
||||
writeLittleEndianShort(header, 20, 1)
|
||||
writeLittleEndianShort(header, 22, 2)
|
||||
writeLittleEndianInt(header, 24, 44100)
|
||||
writeLittleEndianInt(header, 28, 44100 * 2 * 2)
|
||||
writeLittleEndianShort(header, 32, 2 * 2)
|
||||
writeLittleEndianShort(header, 34, 16)
|
||||
writeAscii(header, 36, "data")
|
||||
writeLittleEndianInt(header, 40, dataSize)
|
||||
output.write(header)
|
||||
}
|
||||
|
||||
private fun writeAscii(buffer: ByteArray, offset: Int, value: String) {
|
||||
value.toByteArray(Charsets.US_ASCII).copyInto(buffer, offset)
|
||||
}
|
||||
|
||||
private fun writeLittleEndianShort(buffer: ByteArray, offset: Int, value: Int) {
|
||||
buffer[offset] = value.toByte()
|
||||
buffer[offset + 1] = (value shr 8).toByte()
|
||||
}
|
||||
|
||||
private fun writeLittleEndianInt(buffer: ByteArray, offset: Int, value: Long) {
|
||||
buffer[offset] = value.toByte()
|
||||
buffer[offset + 1] = (value shr 8).toByte()
|
||||
buffer[offset + 2] = (value shr 16).toByte()
|
||||
buffer[offset + 3] = (value shr 24).toByte()
|
||||
}
|
||||
|
||||
private fun readLittleEndianShort(buffer: ByteArray, offset: Int): Int {
|
||||
return (buffer[offset].toInt() and 0xff) or
|
||||
((buffer[offset + 1].toInt() and 0xff) shl 8)
|
||||
}
|
||||
|
||||
private fun readLittleEndianInt(buffer: ByteArray, offset: Int): Long {
|
||||
return (buffer[offset].toLong() and 0xff) or
|
||||
((buffer[offset + 1].toLong() and 0xff) shl 8) or
|
||||
((buffer[offset + 2].toLong() and 0xff) shl 16) or
|
||||
((buffer[offset + 3].toLong() and 0xff) shl 24)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class SelectedImageFiles(
|
||||
val image: SelectedInstallFile,
|
||||
val metadata: SelectedInstallFile?,
|
||||
)
|
||||
|
||||
private data class CueTrack(
|
||||
val isAudio: Boolean,
|
||||
val blockSize: Int,
|
||||
val blockOffset: Int,
|
||||
val index: MutableMap<Int, Int> = mutableMapOf(),
|
||||
)
|
||||
|
||||
private data class TrackInfo(
|
||||
val isAudio: Boolean,
|
||||
val offset: Long,
|
||||
val blockSize: Int,
|
||||
val blockOffset: Int,
|
||||
val startSector: Int,
|
||||
val numSectors: Int,
|
||||
) {
|
||||
companion object {
|
||||
fun iso(imageSize: Long): TrackInfo {
|
||||
return TrackInfo(
|
||||
isAudio = false,
|
||||
offset = 0,
|
||||
blockSize = CdImageReader.ISO_SECTOR_SIZE,
|
||||
blockOffset = 0,
|
||||
startSector = 0,
|
||||
numSectors = (imageSize / CdImageReader.ISO_SECTOR_SIZE).toInt(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RandomAccessImage private constructor(
|
||||
private val channel: FileChannel,
|
||||
private val descriptor: ParcelFileDescriptor?,
|
||||
private val tempFile: File?,
|
||||
) : Closeable {
|
||||
val size: Long = channel.size()
|
||||
|
||||
fun readFully(offset: Long, buffer: ByteArray, bufferOffset: Int, length: Int) {
|
||||
val byteBuffer = ByteBuffer.wrap(buffer, bufferOffset, length)
|
||||
var pos = offset
|
||||
while (byteBuffer.hasRemaining()) {
|
||||
val read = channel.read(byteBuffer, pos)
|
||||
if (read < 0) {
|
||||
throw EOFException("Unexpected end of CD image")
|
||||
}
|
||||
pos += read
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
channel.close()
|
||||
descriptor?.close()
|
||||
tempFile?.delete()
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun open(file: File): RandomAccessImage {
|
||||
return RandomAccessImage(FileInputStream(file).channel, null, null)
|
||||
}
|
||||
|
||||
fun open(
|
||||
contentResolver: ContentResolver,
|
||||
file: SelectedInstallFile,
|
||||
tempDir: File,
|
||||
): RandomAccessImage {
|
||||
val descriptor = contentResolver.openFileDescriptor(file.uri, "r")
|
||||
?: throw InstallFailureException(R.string.cd_image_read_error)
|
||||
try {
|
||||
val channel = FileInputStream(descriptor.fileDescriptor).channel
|
||||
channel.size()
|
||||
return RandomAccessImage(channel, descriptor, null)
|
||||
} catch (e: Exception) {
|
||||
descriptor.close()
|
||||
}
|
||||
|
||||
// Some SAF providers expose the URI as a pipe. Copy those to an
|
||||
// app-private temp file so the CD reader can use positional reads.
|
||||
val tempFile = File.createTempFile("cdimage-", ".img", tempDir)
|
||||
try {
|
||||
contentResolver.openInputStream(file.uri)?.use { input ->
|
||||
tempFile.outputStream().buffered().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
} ?: throw InstallFailureException(R.string.cd_image_read_error)
|
||||
return RandomAccessImage(FileInputStream(tempFile).channel, null, tempFile)
|
||||
} catch (e: Exception) {
|
||||
tempFile.delete()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun SelectedInstallFile.baseName(): String {
|
||||
return displayName.substringBeforeLast('.').lowercase(Locale.US)
|
||||
}
|
||||
@@ -20,6 +20,7 @@ package io.github.kichikuou.xsystem35
|
||||
import android.app.AlertDialog
|
||||
import android.media.MediaPlayer
|
||||
import android.os.Bundle
|
||||
import android.os.Process
|
||||
import android.text.InputType
|
||||
import android.util.Log
|
||||
import android.widget.EditText
|
||||
@@ -44,6 +45,8 @@ class GameActivity : SDLActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
gameRoot = File(intent.getStringExtra(EXTRA_GAME_ROOT)!!)
|
||||
// Workaround for https://github.com/libsdl-org/SDL/issues/8995
|
||||
SDLActivity.setWindowStyle(true)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
@@ -56,6 +59,18 @@ class GameActivity : SDLActivity() {
|
||||
midi.onActivityResume()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
try {
|
||||
super.onDestroy()
|
||||
} finally {
|
||||
try {
|
||||
midi.release()
|
||||
} finally {
|
||||
Process.killProcess(Process.myPid())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<String> {
|
||||
return arrayOf("SDL2", "xsystem35")
|
||||
}
|
||||
@@ -75,7 +90,6 @@ class GameActivity : SDLActivity() {
|
||||
return
|
||||
}
|
||||
File(gameRoot, Launcher.TITLE_FILE).writeText(str)
|
||||
Launcher.updateGameList()
|
||||
}
|
||||
|
||||
private fun textInputDialog(msg: String, oldVal: String, maxLen: Int, result: Array<String?>) {
|
||||
@@ -154,11 +168,21 @@ class GameActivity : SDLActivity() {
|
||||
}
|
||||
|
||||
private class MidiPlayer {
|
||||
private enum class State {
|
||||
STOPPED,
|
||||
PLAYING,
|
||||
PAUSED,
|
||||
RELEASED,
|
||||
}
|
||||
|
||||
private val player = MediaPlayer()
|
||||
private var playing = false
|
||||
private var playerPaused = false
|
||||
private var state = State.STOPPED
|
||||
|
||||
fun start(path: String, loop: Boolean) {
|
||||
if (state == State.RELEASED) {
|
||||
return
|
||||
}
|
||||
state = State.STOPPED
|
||||
try {
|
||||
player.apply {
|
||||
reset()
|
||||
@@ -167,7 +191,7 @@ private class MidiPlayer {
|
||||
prepare()
|
||||
start()
|
||||
}
|
||||
playing = true
|
||||
state = State.PLAYING
|
||||
} catch (e: IOException) {
|
||||
Log.e("midiStart", "Cannot play midi", e)
|
||||
player.reset()
|
||||
@@ -175,27 +199,41 @@ private class MidiPlayer {
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
if (playing && player.isPlaying) {
|
||||
player.stop()
|
||||
playing = false
|
||||
when (state) {
|
||||
State.PLAYING, State.PAUSED -> {
|
||||
player.stop()
|
||||
state = State.STOPPED
|
||||
}
|
||||
State.STOPPED, State.RELEASED -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
fun currentPosition(): Int {
|
||||
return if (playing) player.currentPosition else 0
|
||||
return when (state) {
|
||||
State.PLAYING, State.PAUSED -> player.currentPosition
|
||||
State.STOPPED, State.RELEASED -> 0
|
||||
}
|
||||
}
|
||||
|
||||
fun onActivityStop() {
|
||||
if (playing && player.isPlaying) {
|
||||
if (state == State.PLAYING && player.isPlaying) {
|
||||
player.pause()
|
||||
playerPaused = true
|
||||
state = State.PAUSED
|
||||
}
|
||||
}
|
||||
|
||||
fun onActivityResume() {
|
||||
if (playerPaused) {
|
||||
if (state == State.PAUSED) {
|
||||
player.start()
|
||||
playerPaused = false
|
||||
state = State.PLAYING
|
||||
}
|
||||
}
|
||||
|
||||
fun release() {
|
||||
if (state == State.RELEASED) {
|
||||
return
|
||||
}
|
||||
state = State.RELEASED
|
||||
player.release()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
package io.github.kichikuou.xsystem35
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.UTFDataFormatException
|
||||
import java.nio.charset.Charset
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
data class SelectedInstallFile(
|
||||
val uri: Uri,
|
||||
val displayName: String,
|
||||
)
|
||||
|
||||
class GameInstaller(private val store: GameStore) {
|
||||
suspend fun installZip(input: InputStream, progressCallback: suspend (String) -> Unit): File {
|
||||
val dir = store.createDirForGame()
|
||||
var committed = false
|
||||
try {
|
||||
val gameDir = extractFiles(input, dir, progressCallback)
|
||||
committed = true
|
||||
return gameDir
|
||||
} finally {
|
||||
if (!committed && !dir.deleteRecursively()) {
|
||||
Log.w("launcher", "Failed to delete incomplete install directory: $dir")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun installCdImage(
|
||||
files: List<SelectedInstallFile>,
|
||||
contentResolver: ContentResolver,
|
||||
tempDir: File,
|
||||
progressCallback: suspend (String) -> Unit
|
||||
): File {
|
||||
val dir = store.createDirForGame()
|
||||
var committed = false
|
||||
try {
|
||||
val gameDir = extractCdImage(files, contentResolver, tempDir, dir, progressCallback)
|
||||
committed = true
|
||||
return gameDir
|
||||
} finally {
|
||||
if (!committed && !dir.deleteRecursively()) {
|
||||
Log.w("launcher", "Failed to delete incomplete install directory: $dir")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal suspend fun installCdImageForTest(
|
||||
cdImage: CdImageReader,
|
||||
progressCallback: suspend (String) -> Unit
|
||||
): File {
|
||||
val dir = store.createDirForGame()
|
||||
var committed = false
|
||||
try {
|
||||
val gameDir = extractCdImage(cdImage, dir, progressCallback)
|
||||
committed = true
|
||||
return gameDir
|
||||
} finally {
|
||||
if (!committed && !dir.deleteRecursively()) {
|
||||
Log.w("launcher", "Failed to delete incomplete install directory: $dir")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun extractFiles(
|
||||
input: InputStream,
|
||||
outDir: File,
|
||||
progressCallback: suspend (String) -> Unit
|
||||
): File {
|
||||
val configWriter = GameConfigWriter()
|
||||
val hadDecodeError = forEachZipEntrySuspending(input) { zipEntry, zip ->
|
||||
Log.i("extractFiles", zipEntry.name)
|
||||
if (zipEntry.isDirectory)
|
||||
return@forEachZipEntrySuspending
|
||||
val resolvedPath = resolveOutputPath(outDir, zipEntry.name)
|
||||
val path = resolvedPath.file
|
||||
path.parentFile?.mkdirs()
|
||||
progressCallback(resolvedPath.relativePath)
|
||||
FileOutputStream(path).buffered().use {
|
||||
zip.copyTo(it)
|
||||
}
|
||||
configWriter.maybeAdd(resolvedPath.relativePath)
|
||||
}
|
||||
if (!configWriter.ready) {
|
||||
if (hadDecodeError)
|
||||
throw InstallFailureException(R.string.unsupported_zip)
|
||||
throw InstallFailureException(R.string.cannot_find_ald)
|
||||
}
|
||||
configWriter.write(outDir)
|
||||
return configWriter.gameDir?.let { File(outDir, it) } ?: outDir
|
||||
}
|
||||
|
||||
private suspend fun extractCdImage(
|
||||
files: List<SelectedInstallFile>,
|
||||
contentResolver: ContentResolver,
|
||||
tempDir: File,
|
||||
outDir: File,
|
||||
progressCallback: suspend (String) -> Unit
|
||||
): File {
|
||||
progressCallback("metadata parsing")
|
||||
CdImageReader.open(contentResolver, files, tempDir).use { cdImage ->
|
||||
return extractCdImage(cdImage, outDir, progressCallback)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun extractCdImage(
|
||||
cdImage: CdImageReader,
|
||||
outDir: File,
|
||||
progressCallback: suspend (String) -> Unit
|
||||
): File {
|
||||
val fs = Iso9660FileSystem(cdImage)
|
||||
val gameData = fs.findGameDataDirectory()
|
||||
?: throw InstallFailureException(R.string.cannot_find_game_data_directory)
|
||||
var foundAld = false
|
||||
fs.extractDirectory(gameData, "GAMEDATA") { path, input ->
|
||||
val resolvedPath = resolveOutputPath(outDir, path)
|
||||
resolvedPath.file.parentFile?.mkdirs()
|
||||
progressCallback(resolvedPath.relativePath)
|
||||
FileOutputStream(resolvedPath.file).buffered().use {
|
||||
input.copyTo(it)
|
||||
}
|
||||
if (File(path).name.matches(""".*?s[a-z]\.ald""".toRegex(RegexOption.IGNORE_CASE))) {
|
||||
foundAld = true
|
||||
}
|
||||
}
|
||||
if (!foundAld) {
|
||||
throw InstallFailureException(R.string.cannot_find_ald)
|
||||
}
|
||||
File(outDir, Launcher.GAMEDIR_FILE).writeText("GAMEDATA")
|
||||
extractAudioTracks(cdImage, File(outDir, "GAMEDATA"), progressCallback)
|
||||
return File(outDir, "GAMEDATA")
|
||||
}
|
||||
|
||||
private suspend fun extractAudioTracks(
|
||||
cdImage: CdImageReader,
|
||||
gameDir: File,
|
||||
progressCallback: suspend (String) -> Unit
|
||||
) {
|
||||
val audioTracks = cdImage.audioTracks()
|
||||
if (audioTracks.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val cddaDir = File(gameDir, "cdda")
|
||||
cddaDir.mkdirs()
|
||||
val playlist = arrayOfNulls<String>(audioTracks.maxOrNull() ?: 0)
|
||||
for (track in audioTracks) {
|
||||
val relativePath = "cdda/track%02d.wav".format(track)
|
||||
val outputFile = File(gameDir, relativePath)
|
||||
progressCallback(relativePath)
|
||||
FileOutputStream(outputFile).buffered().use {
|
||||
cdImage.extractAudioTrack(track, it)
|
||||
}
|
||||
playlist[track - 1] = relativePath
|
||||
}
|
||||
File(gameDir, Launcher.PLAYLIST_FILE).writeText(playlist.joinToString("\n") { it ?: "" }.trimEnd('\n'))
|
||||
}
|
||||
}
|
||||
|
||||
internal class InstallFailureException(val msgId: Int) : Exception()
|
||||
|
||||
// A helper class which generates GAMEDIR_FILE and PLAYLIST_FILE.
|
||||
private class GameConfigWriter {
|
||||
var ready = false
|
||||
private set
|
||||
var gameDir: String? = null
|
||||
private set
|
||||
private val aldRegex = """.*?s[a-z]\.ald""".toRegex(RegexOption.IGNORE_CASE)
|
||||
private val audioRegex = """((\d+).*|.*?(\d+))\.(wav|mp3|ogg)""".toRegex(RegexOption.IGNORE_CASE)
|
||||
private val audioFiles: Array<String?> = arrayOfNulls(100)
|
||||
|
||||
fun maybeAdd(path: String) {
|
||||
val name = File(path).name
|
||||
aldRegex.matchEntire(name)?.let {
|
||||
gameDir = File(path).parent
|
||||
ready = true
|
||||
}
|
||||
audioRegex.matchEntire(name)?.let {
|
||||
val track = it.groupValues[2].toIntOrNull() ?: it.groupValues[3].toInt()
|
||||
if (0 < track && track <= audioFiles.size)
|
||||
audioFiles[track - 1] = path
|
||||
}
|
||||
}
|
||||
|
||||
fun write(outDir: File) {
|
||||
// Generate GAMEDIR_FILE
|
||||
gameDir?.let {
|
||||
File(outDir, Launcher.GAMEDIR_FILE).writeText(it)
|
||||
}
|
||||
// Generate PLAYLIST_FILE
|
||||
val absGameDir = gameDir?.let { File(outDir, it) } ?: outDir
|
||||
val playlistFile = File(absGameDir, Launcher.PLAYLIST_FILE)
|
||||
if (!playlistFile.exists()) {
|
||||
val prefixToRemove = gameDir?.let { "$it/" } ?: ""
|
||||
val playlist = audioFiles.joinToString("\n") {
|
||||
it?.removePrefix(prefixToRemove) ?: ""
|
||||
}.trimEnd('\n')
|
||||
playlistFile.writeText(playlist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @property file Safe canonical output path.
|
||||
* @property relativePath Path relative to the install root after canonicalization.
|
||||
*/
|
||||
internal data class ResolvedOutputPath(val file: File, val relativePath: String)
|
||||
|
||||
internal fun resolveOutputPath(baseDir: File, relativePath: String): ResolvedOutputPath {
|
||||
if (File(relativePath).isAbsolute) {
|
||||
throw IOException("Output path is absolute: $relativePath")
|
||||
}
|
||||
val canonicalBase = baseDir.canonicalFile
|
||||
val file = File(canonicalBase, relativePath).canonicalFile
|
||||
if (!isFileInsideDirectory(file, canonicalBase)) {
|
||||
throw IOException("Output path is outside target directory: $relativePath")
|
||||
}
|
||||
val basePath = canonicalBase.path + File.separator
|
||||
val canonicalRelativePath = file.path.removePrefix(basePath)
|
||||
return ResolvedOutputPath(file, canonicalRelativePath)
|
||||
}
|
||||
|
||||
private fun isFileInsideDirectory(file: File, directory: File): Boolean {
|
||||
return file.path.startsWith(directory.path + File.separator)
|
||||
}
|
||||
|
||||
internal fun forEachZipEntry(input: InputStream, action: (ZipEntry, ZipInputStream) -> Unit): Boolean =
|
||||
runBlocking {
|
||||
forEachZipEntrySuspending(input) { zipEntry, zip ->
|
||||
action(zipEntry, zip)
|
||||
}
|
||||
}
|
||||
|
||||
internal suspend fun forEachZipEntrySuspending(
|
||||
input: InputStream,
|
||||
action: suspend (ZipEntry, ZipInputStream) -> Unit
|
||||
): Boolean {
|
||||
val zip = if (Build.VERSION.SDK_INT >= 24) {
|
||||
ZipInputStream(input.buffered(), Charset.forName("Shift_JIS"))
|
||||
} else {
|
||||
ZipInputStream(input.buffered())
|
||||
}
|
||||
var hadDecodeError = false
|
||||
zip.use {
|
||||
while (true) {
|
||||
try {
|
||||
val zipEntry = zip.nextEntry ?: break
|
||||
action(zipEntry, zip)
|
||||
} catch (e: UTFDataFormatException) {
|
||||
// Attempted to read Shift_JIS zip in Android < 7
|
||||
Log.w("forEachZipEntry", "UTFDataFormatException: skipping a zip entry")
|
||||
zip.closeEntry()
|
||||
hadDecodeError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return hadDecodeError
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
package io.github.kichikuou.xsystem35
|
||||
|
||||
import android.util.Log
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.io.UTFDataFormatException
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
class GameStore(private val rootDir: File) {
|
||||
data class Entry(val path: File, val title: String, val timestamp: Long)
|
||||
|
||||
private val gameList = arrayListOf<Entry>()
|
||||
val games: List<Entry>
|
||||
get() = gameList
|
||||
val titles: List<String>
|
||||
get() = gameList.map(Entry::title)
|
||||
|
||||
init {
|
||||
updateGameList()
|
||||
}
|
||||
|
||||
fun uninstall(id: Int) {
|
||||
gameList[id].path.deleteRecursively()
|
||||
gameList.removeAt(id)
|
||||
}
|
||||
|
||||
fun updateGameList() {
|
||||
var saveDirFound = false
|
||||
gameList.clear()
|
||||
for (path in rootDir.listFiles() ?: emptyArray()) {
|
||||
if (!path.isDirectory)
|
||||
continue
|
||||
if (path.name == Launcher.SAVE_DIR) {
|
||||
saveDirFound = true
|
||||
continue
|
||||
}
|
||||
try {
|
||||
val gameDirFile = File(path, Launcher.GAMEDIR_FILE)
|
||||
val gamePath = if (gameDirFile.exists()) File(path, gameDirFile.readText()) else path
|
||||
val titleFile = File(gamePath, Launcher.TITLE_FILE)
|
||||
val title = titleFile.readText()
|
||||
gameList.add(Entry(gamePath, title, titleFile.lastModified()))
|
||||
migratePlaylist(path)
|
||||
} catch (e: IOException) {
|
||||
// Incomplete game installation. Delete it.
|
||||
path.deleteRecursively()
|
||||
}
|
||||
}
|
||||
gameList.sortByDescending(Entry::timestamp)
|
||||
if (!saveDirFound) {
|
||||
File(rootDir, Launcher.SAVE_DIR).mkdir()
|
||||
}
|
||||
}
|
||||
|
||||
fun createDirForGame(): File {
|
||||
var i = 0
|
||||
while (true) {
|
||||
val f = File(rootDir, i++.toString())
|
||||
if (!f.exists() && f.mkdir()) {
|
||||
return f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Throws IOException
|
||||
fun exportSaveData(output: OutputStream) {
|
||||
ZipOutputStream(output.buffered()).use { zip ->
|
||||
for (path in File(rootDir, Launcher.SAVE_DIR).listFiles() ?: emptyArray()) {
|
||||
if (path.isDirectory || path.name.endsWith(".asd."))
|
||||
continue
|
||||
val pathInZip = "${Launcher.SAVE_DIR}/${path.name}"
|
||||
Log.i("exportSaveData", pathInZip)
|
||||
zip.putNextEntry(ZipEntry(pathInZip))
|
||||
path.inputStream().buffered().use {
|
||||
it.copyTo(zip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun importSaveData(input: InputStream): Int? {
|
||||
try {
|
||||
var imported = false
|
||||
forEachZipEntry(input) { zipEntry, zip ->
|
||||
// Process only files directly under save/
|
||||
if (zipEntry.isDirectory || !zipEntry.name.startsWith("save/") ||
|
||||
zipEntry.name.count{it == '/'} != 1)
|
||||
return@forEachZipEntry
|
||||
val path = resolveOutputPath(rootDir, zipEntry.name).file
|
||||
Log.i("importSaveData", zipEntry.name)
|
||||
FileOutputStream(path).buffered().use {
|
||||
zip.copyTo(it)
|
||||
}
|
||||
imported = true
|
||||
}
|
||||
return if (imported) null else R.string.no_data_to_import
|
||||
} catch (e: UTFDataFormatException) {
|
||||
// Attempted to read Shift_JIS zip in Android < 7
|
||||
return R.string.unsupported_zip
|
||||
} catch (e: IOException) {
|
||||
Log.e("launcher", "Failed to extract ZIP", e)
|
||||
return R.string.zip_extraction_error
|
||||
}
|
||||
}
|
||||
|
||||
fun clearSaveData(): Boolean {
|
||||
var success = true
|
||||
File(rootDir, Launcher.SAVE_DIR).listFiles()?.forEach { file ->
|
||||
if (!file.deleteRecursively()) success = false
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
// Xsystem35-sdl2 2.3.0 - 2.11.1 used playlist2.txt. Rename it to playlist.txt.
|
||||
private fun migratePlaylist(dir: File) {
|
||||
val oldPlaylist = File(dir, Launcher.OLD_PLAYLIST_FILE)
|
||||
if (oldPlaylist.exists()) {
|
||||
oldPlaylist.renameTo(File(dir, Launcher.PLAYLIST_FILE))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
package io.github.kichikuou.xsystem35
|
||||
|
||||
import java.io.InputStream
|
||||
import java.nio.charset.Charset
|
||||
import java.util.Locale
|
||||
|
||||
internal class Iso9660FileSystem(private val image: CdImageReader) {
|
||||
private val descriptor = readBestVolumeDescriptor()
|
||||
|
||||
data class Entry(
|
||||
val name: String,
|
||||
val extent: Int,
|
||||
val size: Int,
|
||||
val isDirectory: Boolean,
|
||||
)
|
||||
|
||||
fun findGameDataDirectory(): Entry? {
|
||||
return listDirectory(descriptor.rootDirectory).firstOrNull {
|
||||
it.isDirectory && it.name.uppercase(Locale.US) == "GAMEDATA"
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun extractDirectory(
|
||||
directory: Entry,
|
||||
outputPath: String,
|
||||
writeFile: suspend (String, InputStream) -> Unit,
|
||||
) {
|
||||
for (entry in listDirectory(directory)) {
|
||||
val childPath = "$outputPath/${entry.name}"
|
||||
if (entry.isDirectory) {
|
||||
extractDirectory(entry, childPath, writeFile)
|
||||
} else {
|
||||
writeFile(childPath, fileInputStream(entry))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun listDirectory(directory: Entry): List<Entry> {
|
||||
val bytes = ByteArray(directory.size)
|
||||
image.readDataFully(
|
||||
directory.extent.toLong() * CdImageReader.ISO_SECTOR_SIZE,
|
||||
bytes,
|
||||
0,
|
||||
bytes.size
|
||||
)
|
||||
val entries = mutableListOf<Entry>()
|
||||
var offset = 0
|
||||
while (offset < bytes.size) {
|
||||
val length = bytes[offset].toInt() and 0xff
|
||||
if (length == 0) {
|
||||
offset = ((offset / CdImageReader.ISO_SECTOR_SIZE) + 1) * CdImageReader.ISO_SECTOR_SIZE
|
||||
continue
|
||||
}
|
||||
parseDirectoryRecord(bytes, offset, length)?.let { entries.add(it) }
|
||||
offset += length
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
private fun parseDirectoryRecord(bytes: ByteArray, offset: Int, length: Int): Entry? {
|
||||
if (length < 34) {
|
||||
throw InstallFailureException(R.string.invalid_iso9660_image)
|
||||
}
|
||||
val nameLength = bytes[offset + 32].toInt() and 0xff
|
||||
if (33 + nameLength > length) {
|
||||
throw InstallFailureException(R.string.invalid_iso9660_image)
|
||||
}
|
||||
val rawName = bytes.copyOfRange(offset + 33, offset + 33 + nameLength)
|
||||
if (rawName.size == 1 && (rawName[0].toInt() == 0 || rawName[0].toInt() == 1)) {
|
||||
return null
|
||||
}
|
||||
val name = descriptor.decodeName(rawName)
|
||||
val extent = readLittleEndianInt(bytes, offset + 2)
|
||||
val size = readLittleEndianInt(bytes, offset + 10)
|
||||
val flags = bytes[offset + 25].toInt() and 0xff
|
||||
return Entry(name, extent, size, flags and 0x02 != 0)
|
||||
}
|
||||
|
||||
private fun fileInputStream(entry: Entry): InputStream {
|
||||
return object : InputStream() {
|
||||
private var pos = 0
|
||||
|
||||
override fun read(): Int {
|
||||
val buffer = ByteArray(1)
|
||||
val read = read(buffer, 0, 1)
|
||||
return if (read < 0) -1 else buffer[0].toInt() and 0xff
|
||||
}
|
||||
|
||||
override fun read(buffer: ByteArray, offset: Int, length: Int): Int {
|
||||
if (pos >= entry.size) {
|
||||
return -1
|
||||
}
|
||||
val count = minOf(length, entry.size - pos)
|
||||
image.readDataFully(
|
||||
entry.extent.toLong() * CdImageReader.ISO_SECTOR_SIZE + pos,
|
||||
buffer,
|
||||
offset,
|
||||
count
|
||||
)
|
||||
pos += count
|
||||
return count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readBestVolumeDescriptor(): VolumeDescriptor {
|
||||
var primary: VolumeDescriptor? = null
|
||||
var joliet: VolumeDescriptor? = null
|
||||
val sector = ByteArray(CdImageReader.ISO_SECTOR_SIZE)
|
||||
var sectorNumber = 0x10
|
||||
while (true) {
|
||||
image.readSector(sectorNumber++, sector)
|
||||
if (String(sector, 1, 5, Charsets.US_ASCII) != "CD001") {
|
||||
throw InstallFailureException(R.string.invalid_iso9660_image)
|
||||
}
|
||||
when (sector[0].toInt() and 0xff) {
|
||||
1 -> primary = VolumeDescriptor(parseRootDirectory(sector), false)
|
||||
2 -> if (isJolietDescriptor(sector)) {
|
||||
joliet = VolumeDescriptor(parseRootDirectory(sector), true)
|
||||
}
|
||||
255 -> return joliet ?: primary
|
||||
?: throw InstallFailureException(R.string.invalid_iso9660_image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseRootDirectory(sector: ByteArray): Entry {
|
||||
return Entry(
|
||||
name = "",
|
||||
extent = readLittleEndianInt(sector, 156 + 2),
|
||||
size = readLittleEndianInt(sector, 156 + 10),
|
||||
isDirectory = true,
|
||||
)
|
||||
}
|
||||
|
||||
private fun isJolietDescriptor(sector: ByteArray): Boolean {
|
||||
return sector[88] == 0x25.toByte() &&
|
||||
sector[89] == 0x2f.toByte() &&
|
||||
(sector[90] == 0x40.toByte() || sector[90] == 0x43.toByte() || sector[90] == 0x45.toByte())
|
||||
}
|
||||
|
||||
private data class VolumeDescriptor(
|
||||
val rootDirectory: Entry,
|
||||
val joliet: Boolean,
|
||||
) {
|
||||
fun decodeName(rawName: ByteArray): String {
|
||||
val charset = if (joliet) Charsets.UTF_16BE else Charset.forName("Shift_JIS")
|
||||
return String(rawName, charset).substringBefore(";")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readLittleEndianInt(bytes: ByteArray, offset: Int): Int {
|
||||
return (bytes[offset].toInt() and 0xff) or
|
||||
((bytes[offset + 1].toInt() and 0xff) shl 8) or
|
||||
((bytes[offset + 2].toInt() and 0xff) shl 16) or
|
||||
((bytes[offset + 3].toInt() and 0xff) shl 24)
|
||||
}
|
||||
@@ -17,18 +17,15 @@
|
||||
*/
|
||||
package io.github.kichikuou.xsystem35
|
||||
|
||||
import android.os.Build
|
||||
import android.content.ContentResolver
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.*
|
||||
import java.nio.charset.Charset
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
private var gLauncher: Launcher? = null
|
||||
|
||||
@@ -39,7 +36,16 @@ interface LauncherObserver {
|
||||
fun onInstallFailure(msgId: Int)
|
||||
}
|
||||
|
||||
class Launcher private constructor(private val rootDir: File) {
|
||||
sealed class InstallState {
|
||||
object Idle : InstallState()
|
||||
data class Installing(val progress: String?) : InstallState()
|
||||
data class Succeeded(val path: File, val archiveName: String?) : InstallState()
|
||||
data class Failed(val msgId: Int) : InstallState()
|
||||
}
|
||||
|
||||
data class InstallResult(val path: File, val archiveName: String?)
|
||||
|
||||
class Launcher private constructor(rootDir: File) {
|
||||
companion object {
|
||||
const val SAVE_DIR = "save"
|
||||
const val TITLE_FILE = "title.txt"
|
||||
@@ -54,234 +60,118 @@ class Launcher private constructor(private val rootDir: File) {
|
||||
return gLauncher!!
|
||||
}
|
||||
|
||||
fun updateGameList() {
|
||||
gLauncher?.updateGameList()
|
||||
}
|
||||
}
|
||||
|
||||
data class Entry(val path: File, val title: String, val timestamp: Long)
|
||||
val games = arrayListOf<Entry>()
|
||||
private val store = GameStore(rootDir)
|
||||
private val installer = GameInstaller(store)
|
||||
val games: List<GameStore.Entry>
|
||||
get() = store.games
|
||||
val titles: List<String>
|
||||
get() = games.map(Entry::title)
|
||||
get() = store.titles
|
||||
var observer: LauncherObserver? = null
|
||||
var isInstalling = false
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
private var installJob: Job? = null
|
||||
var installState: InstallState = InstallState.Idle
|
||||
private set
|
||||
|
||||
init {
|
||||
updateGameList()
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun install(input: InputStream, archiveName: String?) {
|
||||
val dir = createDirForGame()
|
||||
isInstalling = true
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
try {
|
||||
val gameDir = withContext(Dispatchers.IO) {
|
||||
extractFiles(input, dir) { msg ->
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
observer?.onInstallProgress(msg)
|
||||
fun installZip(input: InputStream, archiveName: String?) {
|
||||
if (installJob?.isActive == true) {
|
||||
input.close()
|
||||
return
|
||||
}
|
||||
startInstallJob {
|
||||
val gameDir = withContext(Dispatchers.IO) {
|
||||
input.use {
|
||||
installer.installZip(it) { msg ->
|
||||
withContext(Dispatchers.Main) {
|
||||
setInstallProgress(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
observer?.onInstallSuccess(gameDir, archiveName)
|
||||
} catch (e: InstallFailureException) {
|
||||
observer?.onInstallFailure(e.msgId)
|
||||
} catch (e: Exception) {
|
||||
Log.e("launcher", "Failed to extract ZIP", e)
|
||||
observer?.onInstallFailure(R.string.zip_extraction_error)
|
||||
}
|
||||
isInstalling = false
|
||||
InstallResult(gameDir, archiveName)
|
||||
}
|
||||
}
|
||||
|
||||
fun installCdImage(
|
||||
files: List<SelectedInstallFile>,
|
||||
contentResolver: ContentResolver,
|
||||
tempDir: File,
|
||||
) {
|
||||
if (installJob?.isActive == true) {
|
||||
return
|
||||
}
|
||||
startInstallJob {
|
||||
val gameDir = withContext(Dispatchers.IO) {
|
||||
installer.installCdImage(files, contentResolver, tempDir) { msg ->
|
||||
withContext(Dispatchers.Main) {
|
||||
setInstallProgress(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
InstallResult(gameDir, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startInstallJob(block: suspend () -> InstallResult) {
|
||||
if (installJob?.isActive == true) {
|
||||
return
|
||||
}
|
||||
installState = InstallState.Installing(null)
|
||||
installJob = scope.launch {
|
||||
try {
|
||||
val result = block()
|
||||
setInstallSucceeded(result)
|
||||
} catch (e: InstallFailureException) {
|
||||
setInstallFailed(e.msgId)
|
||||
} catch (e: Exception) {
|
||||
Log.e("launcher", "Failed to install game", e)
|
||||
setInstallFailed(R.string.install_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun consumeInstallResult() {
|
||||
if (installState is InstallState.Succeeded || installState is InstallState.Failed) {
|
||||
installState = InstallState.Idle
|
||||
}
|
||||
}
|
||||
|
||||
private fun setInstallProgress(progress: String) {
|
||||
installState = InstallState.Installing(progress)
|
||||
observer?.onInstallProgress(progress)
|
||||
}
|
||||
|
||||
private fun setInstallSucceeded(result: InstallResult) {
|
||||
installState = InstallState.Succeeded(result.path, result.archiveName)
|
||||
observer?.onInstallSuccess(result.path, result.archiveName)
|
||||
}
|
||||
|
||||
private fun setInstallFailed(msgId: Int) {
|
||||
installState = InstallState.Failed(msgId)
|
||||
observer?.onInstallFailure(msgId)
|
||||
}
|
||||
|
||||
fun uninstall(id: Int) {
|
||||
games[id].path.deleteRecursively()
|
||||
games.removeAt(id)
|
||||
store.uninstall(id)
|
||||
observer?.onGameListChange()
|
||||
}
|
||||
|
||||
private fun updateGameList() {
|
||||
var saveDirFound = false
|
||||
games.clear()
|
||||
for (path in rootDir.listFiles() ?: emptyArray()) {
|
||||
if (!path.isDirectory)
|
||||
continue
|
||||
if (path.name == SAVE_DIR) {
|
||||
saveDirFound = true
|
||||
continue
|
||||
}
|
||||
try {
|
||||
val gameDirFile = File(path, GAMEDIR_FILE)
|
||||
val gamePath = if (gameDirFile.exists()) File(path, gameDirFile.readText()) else path
|
||||
val titleFile = File(gamePath, TITLE_FILE)
|
||||
val title = titleFile.readText()
|
||||
games.add(Entry(gamePath, title, titleFile.lastModified()))
|
||||
migratePlaylist(path)
|
||||
} catch (e: IOException) {
|
||||
// Incomplete game installation. Delete it.
|
||||
path.deleteRecursively()
|
||||
}
|
||||
}
|
||||
games.sortByDescending(Entry::timestamp)
|
||||
if (!saveDirFound) {
|
||||
File(rootDir, SAVE_DIR).mkdir()
|
||||
}
|
||||
fun refreshGameList() {
|
||||
store.updateGameList()
|
||||
observer?.onGameListChange()
|
||||
}
|
||||
|
||||
private fun createDirForGame(): File {
|
||||
var i = 0
|
||||
while (true) {
|
||||
val f = File(rootDir, i++.toString())
|
||||
if (!f.exists() && f.mkdir()) {
|
||||
return f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Throws IOException
|
||||
fun exportSaveData(output: OutputStream) {
|
||||
ZipOutputStream(output.buffered()).use { zip ->
|
||||
for (path in File(rootDir, SAVE_DIR).listFiles() ?: emptyArray()) {
|
||||
if (path.isDirectory || path.name.endsWith(".asd."))
|
||||
continue
|
||||
val pathInZip = "${SAVE_DIR}/${path.name}"
|
||||
Log.i("exportSaveData", pathInZip)
|
||||
zip.putNextEntry(ZipEntry(pathInZip))
|
||||
path.inputStream().buffered().use {
|
||||
it.copyTo(zip)
|
||||
}
|
||||
}
|
||||
}
|
||||
store.exportSaveData(output)
|
||||
}
|
||||
|
||||
fun importSaveData(input: InputStream): Int? {
|
||||
try {
|
||||
var imported = false
|
||||
forEachZipEntry(input) { zipEntry, zip ->
|
||||
// Process only files directly under save/
|
||||
if (zipEntry.isDirectory || !zipEntry.name.startsWith("save/") ||
|
||||
zipEntry.name.count{it == '/'} != 1)
|
||||
return@forEachZipEntry
|
||||
Log.i("importSaveData", zipEntry.name)
|
||||
FileOutputStream(File(rootDir, zipEntry.name)).buffered().use {
|
||||
zip.copyTo(it)
|
||||
}
|
||||
imported = true
|
||||
}
|
||||
return if (imported) null else R.string.no_data_to_import
|
||||
} catch (e: UTFDataFormatException) {
|
||||
// Attempted to read Shift_JIS zip in Android < 7
|
||||
return R.string.unsupported_zip
|
||||
} catch (e: IOException) {
|
||||
Log.e("launcher", "Failed to extract ZIP", e)
|
||||
return R.string.zip_extraction_error
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractFiles(input: InputStream, outDir: File, progressCallback: (String) -> Unit): File {
|
||||
val configWriter = GameConfigWriter()
|
||||
val hadDecodeError = forEachZipEntry(input) { zipEntry, zip ->
|
||||
Log.i("extractFiles", zipEntry.name)
|
||||
val path = File(outDir, zipEntry.name)
|
||||
if (zipEntry.isDirectory)
|
||||
return@forEachZipEntry
|
||||
path.parentFile?.mkdirs()
|
||||
progressCallback(zipEntry.name)
|
||||
FileOutputStream(path).buffered().use {
|
||||
zip.copyTo(it)
|
||||
}
|
||||
configWriter.maybeAdd(zipEntry.name)
|
||||
}
|
||||
if (!configWriter.ready) {
|
||||
if (hadDecodeError)
|
||||
throw InstallFailureException(R.string.unsupported_zip)
|
||||
throw InstallFailureException(R.string.cannot_find_ald)
|
||||
}
|
||||
configWriter.write(outDir)
|
||||
return configWriter.gameDir?.let { File(outDir, it) } ?: outDir
|
||||
}
|
||||
|
||||
// Xsystem35-sdl2 2.3.0 - 2.11.1 used playlist2.txt. Rename it to playlist.txt.
|
||||
private fun migratePlaylist(dir: File) {
|
||||
val oldPlaylist = File(dir, OLD_PLAYLIST_FILE)
|
||||
if (oldPlaylist.exists()) {
|
||||
oldPlaylist.renameTo(File(dir, PLAYLIST_FILE))
|
||||
}
|
||||
}
|
||||
|
||||
class InstallFailureException(val msgId: Int) : Exception()
|
||||
|
||||
// A helper class which generates GAMEDIR_FILE and PLAYLIST_FILE.
|
||||
private class GameConfigWriter {
|
||||
var ready = false
|
||||
private set
|
||||
var gameDir: String? = null
|
||||
private set
|
||||
private val aldRegex = """.*?s[a-z]\.ald""".toRegex(RegexOption.IGNORE_CASE)
|
||||
private val audioRegex = """((\d+).*|.*?(\d+))\.(wav|mp3|ogg)""".toRegex(RegexOption.IGNORE_CASE)
|
||||
private val audioFiles: Array<String?> = arrayOfNulls(100)
|
||||
|
||||
fun maybeAdd(path: String) {
|
||||
val name = File(path).name
|
||||
aldRegex.matchEntire(name)?.let {
|
||||
gameDir = File(path).parent
|
||||
ready = true
|
||||
}
|
||||
audioRegex.matchEntire(name)?.let {
|
||||
val track = it.groupValues[2].toIntOrNull() ?: it.groupValues[3].toInt()
|
||||
if (0 < track && track <= audioFiles.size)
|
||||
audioFiles[track - 1] = path
|
||||
}
|
||||
}
|
||||
|
||||
fun write(outDir: File) {
|
||||
// Generate GAMEDIR_FILE
|
||||
gameDir?.let {
|
||||
File(outDir, GAMEDIR_FILE).writeText(it)
|
||||
}
|
||||
// Generate PLAYLIST_FILE
|
||||
val absGameDir = gameDir?.let { File(outDir, it) } ?: outDir
|
||||
val playlistFile = File(absGameDir, PLAYLIST_FILE)
|
||||
if (!playlistFile.exists()) {
|
||||
val prefixToRemove = gameDir?.let { "$it/" } ?: ""
|
||||
val playlist = audioFiles.joinToString("\n") {
|
||||
it?.removePrefix(prefixToRemove) ?: ""
|
||||
}.trimEnd('\n')
|
||||
playlistFile.writeText(playlist)
|
||||
}
|
||||
}
|
||||
return store.importSaveData(input)
|
||||
}
|
||||
|
||||
fun clearSaveData(): Boolean {
|
||||
var success = true
|
||||
File(rootDir, SAVE_DIR).listFiles()?.forEach { file ->
|
||||
if (!file.deleteRecursively()) success = false
|
||||
}
|
||||
return success
|
||||
return store.clearSaveData()
|
||||
}
|
||||
}
|
||||
|
||||
private fun forEachZipEntry(input: InputStream, action: (ZipEntry, ZipInputStream) -> Unit): Boolean {
|
||||
val zip = if (Build.VERSION.SDK_INT >= 24) {
|
||||
ZipInputStream(input.buffered(), Charset.forName("Shift_JIS"))
|
||||
} else {
|
||||
ZipInputStream(input.buffered())
|
||||
}
|
||||
var hadDecodeError = false
|
||||
zip.use {
|
||||
while (true) {
|
||||
try {
|
||||
val zipEntry = zip.nextEntry ?: break
|
||||
action(zipEntry, zip)
|
||||
} catch (e: UTFDataFormatException) {
|
||||
// Attempted to read Shift_JIS zip in Android < 7
|
||||
Log.w("forEachZipEntry", "UTFDataFormatException: skipping a zip entry")
|
||||
zip.closeEntry()
|
||||
hadDecodeError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return hadDecodeError
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ import android.widget.Toast
|
||||
import java.io.*
|
||||
|
||||
private const val CONTENT_TYPE_ZIP = "application/zip"
|
||||
private const val INSTALL_REQUEST = 1
|
||||
private const val ZIP_INSTALL_REQUEST = 1
|
||||
private const val SAVEDATA_EXPORT_REQUEST = 2
|
||||
private const val SAVEDATA_IMPORT_REQUEST = 3
|
||||
private const val STATE_PROGRESS_TEXT = "progressText"
|
||||
private const val CD_IMAGE_INSTALL_REQUEST = 4
|
||||
|
||||
class LauncherActivity : Activity(), LauncherObserver {
|
||||
private lateinit var launcher: Launcher
|
||||
@@ -48,9 +48,7 @@ class LauncherActivity : Activity(), LauncherObserver {
|
||||
|
||||
launcher = Launcher.getInstance(filesDir)
|
||||
launcher.observer = this
|
||||
if (launcher.isInstalling) {
|
||||
showProgressDialog(savedInstanceState)
|
||||
}
|
||||
renderInstallState(launcher.installState)
|
||||
|
||||
onGameListChange()
|
||||
val listView = findViewById<ListView>(R.id.list)
|
||||
@@ -70,11 +68,9 @@ class LauncherActivity : Activity(), LauncherObserver {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
progressDialog?.let {
|
||||
outState.putCharSequence(STATE_PROGRESS_TEXT, it.findViewById<TextView>(R.id.text).text)
|
||||
}
|
||||
super.onSaveInstanceState(outState)
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
launcher.refreshGameList()
|
||||
}
|
||||
|
||||
private fun onListItemClick(position: Int) {
|
||||
@@ -100,7 +96,15 @@ class LauncherActivity : Activity(), LauncherObserver {
|
||||
R.id.install_from_zip -> {
|
||||
val i = Intent(Intent.ACTION_GET_CONTENT)
|
||||
i.type = CONTENT_TYPE_ZIP
|
||||
startActivityForResult(Intent.createChooser(i, getString(R.string.choose_a_file)), INSTALL_REQUEST)
|
||||
startActivityForResult(Intent.createChooser(i, getString(R.string.choose_a_file)), ZIP_INSTALL_REQUEST)
|
||||
true
|
||||
}
|
||||
R.id.install_from_cd_image -> {
|
||||
val i = Intent(Intent.ACTION_GET_CONTENT)
|
||||
i.type = "*/*"
|
||||
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
startActivityForResult(Intent.createChooser(i, getString(R.string.choose_cd_image_files)),
|
||||
CD_IMAGE_INSTALL_REQUEST)
|
||||
true
|
||||
}
|
||||
R.id.export_savedata -> {
|
||||
@@ -150,14 +154,20 @@ class LauncherActivity : Activity(), LauncherObserver {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode != RESULT_OK)
|
||||
return
|
||||
val uri = data?.data ?: return
|
||||
when (requestCode) {
|
||||
INSTALL_REQUEST -> {
|
||||
ZIP_INSTALL_REQUEST -> {
|
||||
val uri = data?.data ?: return
|
||||
val input = contentResolver.openInputStream(uri) ?: return
|
||||
showProgressDialog()
|
||||
launcher.install(input, getArchiveName(uri))
|
||||
launcher.installZip(input, getArchiveName(uri))
|
||||
renderInstallState(launcher.installState)
|
||||
}
|
||||
CD_IMAGE_INSTALL_REQUEST -> {
|
||||
val files = getSelectedInstallFiles(data)
|
||||
launcher.installCdImage(files, contentResolver, cacheDir)
|
||||
renderInstallState(launcher.installState)
|
||||
}
|
||||
SAVEDATA_EXPORT_REQUEST -> try {
|
||||
val uri = data?.data ?: return
|
||||
launcher.exportSaveData(contentResolver.openOutputStream(uri)!!)
|
||||
Toast.makeText(this, R.string.save_data_export_success, Toast.LENGTH_SHORT).show()
|
||||
} catch (e: IOException) {
|
||||
@@ -165,6 +175,7 @@ class LauncherActivity : Activity(), LauncherObserver {
|
||||
errorDialog(R.string.save_data_export_error)
|
||||
}
|
||||
SAVEDATA_IMPORT_REQUEST -> {
|
||||
val uri = data?.data ?: return
|
||||
val input = contentResolver.openInputStream(uri) ?: return
|
||||
val errMsgId = launcher.importSaveData(input)
|
||||
if (errMsgId == null) {
|
||||
@@ -184,17 +195,15 @@ class LauncherActivity : Activity(), LauncherObserver {
|
||||
}
|
||||
|
||||
override fun onInstallProgress(path: String) {
|
||||
progressDialog?.findViewById<TextView>(R.id.text)?.text = getString(R.string.install_progress, path)
|
||||
renderInstallState(launcher.installState)
|
||||
}
|
||||
|
||||
override fun onInstallSuccess(path: File, archiveName: String?) {
|
||||
dismissProgressDialog()
|
||||
startGame(path, archiveName)
|
||||
renderInstallState(launcher.installState)
|
||||
}
|
||||
|
||||
override fun onInstallFailure(msgId: Int) {
|
||||
dismissProgressDialog()
|
||||
errorDialog(msgId)
|
||||
renderInstallState(launcher.installState)
|
||||
}
|
||||
|
||||
private fun startGame(path: File, archiveName: String?) {
|
||||
@@ -210,16 +219,35 @@ class LauncherActivity : Activity(), LauncherObserver {
|
||||
launcher.uninstall(id)
|
||||
}
|
||||
|
||||
private fun showProgressDialog(savedInstanceState: Bundle? = null) {
|
||||
progressDialog = Dialog(this)
|
||||
progressDialog!!.apply {
|
||||
setTitle(R.string.install_dialog_title)
|
||||
setCancelable(false)
|
||||
setContentView(R.layout.progress_dialog)
|
||||
savedInstanceState?.let {
|
||||
findViewById<TextView>(R.id.text)?.text = it.getCharSequence(STATE_PROGRESS_TEXT)
|
||||
private fun renderInstallState(state: InstallState) {
|
||||
when (state) {
|
||||
InstallState.Idle -> dismissProgressDialog()
|
||||
is InstallState.Installing -> showProgressDialog(state.progress)
|
||||
is InstallState.Succeeded -> {
|
||||
dismissProgressDialog()
|
||||
startGame(state.path, state.archiveName)
|
||||
launcher.consumeInstallResult()
|
||||
}
|
||||
show()
|
||||
is InstallState.Failed -> {
|
||||
dismissProgressDialog()
|
||||
errorDialog(state.msgId)
|
||||
launcher.consumeInstallResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showProgressDialog(progress: String? = null) {
|
||||
if (progressDialog == null) {
|
||||
progressDialog = Dialog(this)
|
||||
progressDialog!!.apply {
|
||||
setTitle(R.string.install_dialog_title)
|
||||
setCancelable(false)
|
||||
setContentView(R.layout.progress_dialog)
|
||||
show()
|
||||
}
|
||||
}
|
||||
progress?.let {
|
||||
progressDialog?.findViewById<TextView>(R.id.text)?.text = getString(R.string.install_progress, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,13 +264,33 @@ class LauncherActivity : Activity(), LauncherObserver {
|
||||
}
|
||||
|
||||
private fun getArchiveName(uri: Uri): String? {
|
||||
val fname = getDisplayName(uri) ?: return null
|
||||
return if (fname.endsWith(".zip", true)) fname.dropLast(4) else fname
|
||||
}
|
||||
|
||||
private fun getSelectedInstallFiles(data: Intent?): List<SelectedInstallFile> {
|
||||
val files = mutableListOf<SelectedInstallFile>()
|
||||
val clipData = data?.clipData
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
val uri = clipData.getItemAt(i).uri
|
||||
files.add(SelectedInstallFile(uri, getDisplayName(uri) ?: uri.lastPathSegment ?: ""))
|
||||
}
|
||||
} else {
|
||||
data?.data?.let { uri ->
|
||||
files.add(SelectedInstallFile(uri, getDisplayName(uri) ?: uri.lastPathSegment ?: ""))
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
private fun getDisplayName(uri: Uri): String? {
|
||||
val cursor = contentResolver.query(uri, null, null, null, null, null)
|
||||
cursor?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val column = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
if (column >= 0) {
|
||||
val fname = it.getString(column)
|
||||
return if (fname.endsWith(".zip", true)) fname.dropLast(4) else fname
|
||||
return it.getString(column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,9 @@ class LicensesMenuActivity : Activity() {
|
||||
Entry("xsystem35-sdl2", "xsystem35", "https://github.com/kichikuou/xsystem35-sdl2"),
|
||||
Entry("SDL", "SDL", "https://www.libsdl.org/"),
|
||||
Entry("SDL_mixer", "SDL_mixer", "https://github.com/libsdl-org/SDL_mixer"),
|
||||
Entry("SDL_ttf", "SDL_ttf", "https://github.com/libsdl-org/SDL_ttf"),
|
||||
Entry("NanoJPEG", "nanojpeg", "https://keyj.emphy.de/nanojpeg/"),
|
||||
Entry("microui", "microui", "https://github.com/rxi/microui"),
|
||||
Entry("FreeType", "freetype", "https://freetype.org/"),
|
||||
Entry("HarfBuzz", "harfbuzz", "https://harfbuzz.github.io/"),
|
||||
Entry("MotoyaLCedar W3 mono", "MTLc3m", "https://github.com/aosp-mirror/platform_frameworks_base/tree/lollipop-release/data/fonts"),
|
||||
Entry("IPA明朝", "mincho", "https://moji.or.jp/ipafont/"),
|
||||
)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
<item
|
||||
android:id="@+id/install_from_zip"
|
||||
android:title="@string/action_install_from_zip" />
|
||||
<item
|
||||
android:id="@+id/install_from_cd_image"
|
||||
android:title="@string/action_install_from_cd_image" />
|
||||
<item
|
||||
android:id="@+id/export_savedata"
|
||||
android:title="@string/action_export_save_data" />
|
||||
@@ -18,4 +21,4 @@
|
||||
<item
|
||||
android:id="@+id/licenses"
|
||||
android:title="@string/action_licenses" />
|
||||
</menu>
|
||||
</menu>
|
||||
|
||||
@@ -4,17 +4,24 @@
|
||||
<string name="action_export_save_data">セーブデータをエクスポート</string>
|
||||
<string name="action_help">使い方</string>
|
||||
<string name="action_import_save_data">セーブデータをインポート</string>
|
||||
<string name="action_install_from_cd_image">CDイメージからインストール</string>
|
||||
<string name="action_install_from_zip">ZIPからインストール</string>
|
||||
<string name="action_licenses">オープンソースライセンス</string>
|
||||
<string name="action_clear_save_data">セーブデータをクリア</string>
|
||||
<string name="cancel">キャンセル</string>
|
||||
<string name="cannot_find_ald">System 3.x のファイル (*.ald) が見つかりません。</string>
|
||||
<string name="cannot_find_game_data_directory">CDイメージ内に GAMEDATA ディレクトリが見つかりません。</string>
|
||||
<string name="cd_image_read_error">選択された CD イメージファイルを読み取れません。</string>
|
||||
<string name="choose_a_file">ファイルを選択</string>
|
||||
<string name="choose_cd_image_files">CDイメージファイルを選択</string>
|
||||
<string name="clear_save_data_confirm">セーブデータをすべて削除します。元には戻せません。本当によろしいですか?</string>
|
||||
<string name="confirm">確認</string>
|
||||
<string name="error_dialog_title">エラー</string>
|
||||
<string name="install_dialog_title">インストール中…</string>
|
||||
<string name="install_error">ゲームのインストールに失敗しました。</string>
|
||||
<string name="install_progress">%s を展開中</string>
|
||||
<string name="invalid_iso9660_image">有効な ISO9660 イメージではありません。</string>
|
||||
<string name="missing_cd_image_metadata">CD イメージファイルと対応するメタデータファイルを一緒に選択してください。ファイル選択画面ではファイルを長押しすると複数選択モードに入れます。</string>
|
||||
<string name="no_data_to_import">ZIP アーカイブ中に save/ フォルダが見つかりません。</string>
|
||||
<string name="save_data_export_error">セーブデータのエクスポートに失敗しました。</string>
|
||||
<string name="save_data_export_success">エクスポートに成功しました。</string>
|
||||
@@ -22,6 +29,7 @@
|
||||
<string name="save_data_clear_success">セーブデータをクリアしました。</string>
|
||||
<string name="save_data_clear_error">セーブデータのクリアに失敗しました。</string>
|
||||
<string name="uninstall_dialog_message">「%s」をアンインストールしますか?</string>
|
||||
<string name="unsupported_cd_image">この CD イメージ形式にはまだ対応していません。現在は .iso、.bin/.img + .cue/.ccd、.mdf/.mds に対応しています。</string>
|
||||
<string name="unsupported_zip">この形式の ZIP はサポートしていません。</string>
|
||||
<string name="zip_extraction_error">ZIP の展開に失敗しました。</string>
|
||||
<string name="usage">
|
||||
@@ -41,4 +49,4 @@
|
||||
<p>3. この画面の右上のメニューボタンをタップして「ZIPからインストール」を選択し、転送したZIPを選びます。</p>
|
||||
]]>
|
||||
</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -4,16 +4,23 @@
|
||||
<string name="action_export_save_data">Export Save Files</string>
|
||||
<string name="action_help">Help</string>
|
||||
<string name="action_import_save_data">Import Save Files</string>
|
||||
<string name="action_install_from_cd_image">Install from CD image</string>
|
||||
<string name="action_install_from_zip">Install from ZIP</string>
|
||||
<string name="action_licenses">Open source licenses</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="cannot_find_ald">Cannot find System 3.x game files (*.ald).</string>
|
||||
<string name="cannot_find_game_data_directory">Cannot find GAMEDATA directory in the CD image.</string>
|
||||
<string name="cd_image_read_error">Cannot read the selected CD image file.</string>
|
||||
<string name="choose_a_file">Choose a file</string>
|
||||
<string name="choose_cd_image_files">Choose CD image files</string>
|
||||
<string name="clear_save_data_confirm">Are you sure you want to clear all save files? This cannot be undone.</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="error_dialog_title">Error</string>
|
||||
<string name="install_dialog_title">Installing…</string>
|
||||
<string name="install_error">Failed to install game.</string>
|
||||
<string name="install_progress">Extracting %s</string>
|
||||
<string name="invalid_iso9660_image">This file is not a valid ISO9660 image.</string>
|
||||
<string name="missing_cd_image_metadata">Select a matching metadata file with the CD image file. In the file picker, long-press a file to enter multi-select mode.</string>
|
||||
<string name="no_data_to_import">No save/ folder in the ZIP archive.</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="save_data_clear_error">Failed to clear save files.</string>
|
||||
@@ -22,6 +29,7 @@
|
||||
<string name="save_data_import_success">Imported successfully.</string>
|
||||
<string name="save_data_export_success">Exported successfully.</string>
|
||||
<string name="uninstall_dialog_message">Uninstall \"%s\"?</string>
|
||||
<string name="unsupported_cd_image">This CD image format is not supported yet. Currently only .iso, .bin/.img + .cue/.ccd, and .mdf/.mds are supported.</string>
|
||||
<string name="unsupported_zip">This type of ZIP is not supported.</string>
|
||||
<string name="zip_extraction_error">Failed to extract ZIP.</string>
|
||||
<string name="usage">
|
||||
|
||||
@@ -0,0 +1,714 @@
|
||||
/* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
package io.github.kichikuou.xsystem35
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class Iso9660FileSystemTest {
|
||||
@get:Rule
|
||||
val tmp = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun extractsGameDataDirectoryFromPrimaryDescriptor() = runBlocking {
|
||||
val iso = SyntheticIsoBuilder()
|
||||
.addGameDataFile("SA.ALD;1", "ald".toByteArray())
|
||||
.addGameDataFile("SUB/FILE.TXT;1", "text".toByteArray())
|
||||
.writeTo(tmp.newFile("game.iso"))
|
||||
|
||||
CdImageReader.openForFile(iso).use { reader ->
|
||||
val fs = Iso9660FileSystem(reader)
|
||||
val gameData = fs.findGameDataDirectory()
|
||||
?: throw AssertionError("GAMEDATA directory was not found")
|
||||
val extracted = mutableMapOf<String, ByteArray>()
|
||||
|
||||
fs.extractDirectory(gameData, "GAMEDATA") { path, input ->
|
||||
extracted[path] = input.readBytes()
|
||||
}
|
||||
|
||||
assertArrayEquals("ald".toByteArray(), extracted["GAMEDATA/SA.ALD"])
|
||||
assertArrayEquals("text".toByteArray(), extracted["GAMEDATA/SUB/FILE.TXT"])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun decodesPrimaryDescriptorNamesAsShiftJis() = runBlocking {
|
||||
val name = "表.TXT;1"
|
||||
val iso = SyntheticIsoBuilder()
|
||||
.addGameDataFile(name, "sjis".toByteArray())
|
||||
.writeTo(tmp.newFile("sjis.iso"))
|
||||
|
||||
CdImageReader.openForFile(iso).use { reader ->
|
||||
val fs = Iso9660FileSystem(reader)
|
||||
val gameData = fs.findGameDataDirectory()
|
||||
?: throw AssertionError("GAMEDATA directory was not found")
|
||||
val extracted = mutableMapOf<String, ByteArray>()
|
||||
|
||||
fs.extractDirectory(gameData, "GAMEDATA") { path, input ->
|
||||
extracted[path] = input.readBytes()
|
||||
}
|
||||
|
||||
assertArrayEquals("sjis".toByteArray(), extracted["GAMEDATA/表.TXT"])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun prefersJolietDescriptorOverPrimaryDescriptor() = runBlocking {
|
||||
val iso = SyntheticIsoBuilder()
|
||||
.addGameDataFile("PRIMARY.TXT;1", "primary".toByteArray())
|
||||
.addJolietGameDataFile("日本語.TXT;1", "joliet".toByteArray())
|
||||
.writeTo(tmp.newFile("joliet.iso"))
|
||||
|
||||
CdImageReader.openForFile(iso).use { reader ->
|
||||
val fs = Iso9660FileSystem(reader)
|
||||
val gameData = fs.findGameDataDirectory()
|
||||
?: throw AssertionError("GAMEDATA directory was not found")
|
||||
val extracted = mutableMapOf<String, ByteArray>()
|
||||
|
||||
fs.extractDirectory(gameData, "GAMEDATA") { path, input ->
|
||||
extracted[path] = input.readBytes()
|
||||
}
|
||||
|
||||
assertFalse(extracted.containsKey("GAMEDATA/PRIMARY.TXT"))
|
||||
assertArrayEquals("joliet".toByteArray(), extracted["GAMEDATA/日本語.TXT"])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rejectsInvalidVolumeDescriptorSignature() {
|
||||
val iso = tmp.newFile("invalid.iso")
|
||||
iso.writeBytes(ByteArray(18 * CdImageReader.ISO_SECTOR_SIZE))
|
||||
|
||||
val e = assertInstallFailure(R.string.invalid_iso9660_image) {
|
||||
CdImageReader.openForFile(iso).use { Iso9660FileSystem(it) }
|
||||
}
|
||||
assertEquals(R.string.invalid_iso9660_image, e.msgId)
|
||||
}
|
||||
}
|
||||
|
||||
class CdImageReaderTest {
|
||||
@get:Rule
|
||||
val tmp = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun readsIsoSectorAt2048ByteOffset() {
|
||||
val sector0 = sectorFilledWith(0x10)
|
||||
val sector1 = sectorFilledWith(0x20)
|
||||
val image = tmp.newFile("plain.iso")
|
||||
image.writeBytes(sector0 + sector1)
|
||||
|
||||
CdImageReader.openForFile(image).use { reader ->
|
||||
val buffer = ByteArray(CdImageReader.ISO_SECTOR_SIZE)
|
||||
|
||||
reader.readSector(1, buffer)
|
||||
|
||||
assertArrayEquals(sector1, buffer)
|
||||
assertTrue(reader.audioTracks().isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cueMode12048ReadsDataSector() {
|
||||
val payload = sectorFilledWith(0x31)
|
||||
val image = tmp.newFile("mode2048.bin")
|
||||
image.writeBytes(payload)
|
||||
|
||||
CdImageReader.openForFile(image, "mode2048.cue", cue("MODE1/2048").toByteArray()).use { reader ->
|
||||
val buffer = ByteArray(CdImageReader.ISO_SECTOR_SIZE)
|
||||
|
||||
reader.readSector(0, buffer)
|
||||
|
||||
assertArrayEquals(payload, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cueMode12352ReadsPayloadAtOffset16() {
|
||||
val payload = sectorFilledWith(0x42)
|
||||
val image = tmp.newFile("mode2352.bin")
|
||||
image.writeBytes(rawDataSector(payload, 0x11))
|
||||
|
||||
CdImageReader.openForFile(image, "mode2352.cue", cue("MODE1/2352").toByteArray()).use { reader ->
|
||||
val buffer = ByteArray(CdImageReader.ISO_SECTOR_SIZE)
|
||||
|
||||
reader.readSector(0, buffer)
|
||||
|
||||
assertArrayEquals(payload, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cuePregapAdvancesDataTrackStart() {
|
||||
val pregapPayload = sectorFilledWith(0x21)
|
||||
val dataPayload = sectorFilledWith(0x22)
|
||||
val image = tmp.newFile("pregap.bin")
|
||||
image.writeBytes(rawDataSector(pregapPayload, 0x01) + rawDataSector(dataPayload, 0x02))
|
||||
val metadata = """
|
||||
FILE "pregap.bin" BINARY
|
||||
TRACK 01 MODE1/2352
|
||||
INDEX 00 00:00:00
|
||||
INDEX 01 00:00:01
|
||||
""".trimIndent()
|
||||
|
||||
CdImageReader.openForFile(image, "pregap.cue", metadata.toByteArray()).use { reader ->
|
||||
val buffer = ByteArray(CdImageReader.ISO_SECTOR_SIZE)
|
||||
|
||||
reader.readSector(1, buffer)
|
||||
|
||||
assertArrayEquals(dataPayload, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun extractsCueAudioTrackAsWav() {
|
||||
val dataPayload = sectorFilledWith(0x33)
|
||||
val audio = ByteArray(RAW_AUDIO_SECTOR_SIZE) { it.toByte() }
|
||||
val image = tmp.newFile("audio.bin")
|
||||
image.writeBytes(rawDataSector(dataPayload, 0) + audio)
|
||||
val metadata = """
|
||||
FILE "audio.bin" BINARY
|
||||
TRACK 01 MODE1/2352
|
||||
INDEX 01 00:00:00
|
||||
TRACK 02 AUDIO
|
||||
INDEX 01 00:00:01
|
||||
""".trimIndent()
|
||||
|
||||
CdImageReader.openForFile(image, "audio.cue", metadata.toByteArray()).use { reader ->
|
||||
val output = ByteArrayOutputStream()
|
||||
|
||||
assertEquals(listOf(2), reader.audioTracks())
|
||||
reader.extractAudioTrack(2, output)
|
||||
|
||||
val wav = output.toByteArray()
|
||||
assertEquals("RIFF", wav.decodeAscii(0, 4))
|
||||
assertEquals(RAW_AUDIO_SECTOR_SIZE + 36, wav.readLeInt(4))
|
||||
assertEquals("WAVE", wav.decodeAscii(8, 4))
|
||||
assertEquals("data", wav.decodeAscii(36, 4))
|
||||
assertEquals(RAW_AUDIO_SECTOR_SIZE, wav.readLeInt(40))
|
||||
assertEquals(44100, wav.readLeInt(24))
|
||||
assertEquals(2, wav.readLeShort(22))
|
||||
assertEquals(16, wav.readLeShort(34))
|
||||
assertArrayEquals(audio, wav.copyOfRange(44, 44 + RAW_AUDIO_SECTOR_SIZE))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rejectsUnsupportedCueMode() {
|
||||
val image = tmp.newFile("unsupported.bin")
|
||||
image.writeBytes(ByteArray(CdImageReader.ISO_SECTOR_SIZE))
|
||||
|
||||
assertInstallFailure(R.string.unsupported_cd_image) {
|
||||
CdImageReader.openForFile(image, "unsupported.cue", cue("MODE2/2352").toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ccdUsesMode0ForAudioAndMode1PayloadOffsetForData() {
|
||||
val dataPayload = sectorFilledWith(0x52)
|
||||
val audio = ByteArray(RAW_AUDIO_SECTOR_SIZE) { (255 - it).toByte() }
|
||||
val image = tmp.newFile("disc.img")
|
||||
image.writeBytes(rawDataSector(dataPayload, 0x08) + audio)
|
||||
val metadata = """
|
||||
[TRACK 1]
|
||||
MODE=1
|
||||
INDEX 1=0
|
||||
[TRACK 2]
|
||||
MODE=0
|
||||
INDEX 1=1
|
||||
""".trimIndent()
|
||||
|
||||
CdImageReader.openForFile(image, "disc.ccd", metadata.toByteArray()).use { reader ->
|
||||
val buffer = ByteArray(CdImageReader.ISO_SECTOR_SIZE)
|
||||
val output = ByteArrayOutputStream()
|
||||
|
||||
reader.readSector(0, buffer)
|
||||
reader.extractAudioTrack(2, output)
|
||||
|
||||
assertArrayEquals(dataPayload, buffer)
|
||||
assertEquals(listOf(2), reader.audioTracks())
|
||||
assertArrayEquals(audio, output.toByteArray().copyOfRange(44, 44 + RAW_AUDIO_SECTOR_SIZE))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mdsMapsModeAAToDataAndModeA9ToAudio() {
|
||||
val dataPayload = sectorFilledWith(0x61)
|
||||
val audio = ByteArray(RAW_AUDIO_SECTOR_SIZE) { (it * 3).toByte() }
|
||||
val image = tmp.newFile("disc.mdf")
|
||||
image.writeBytes(rawDataSector(dataPayload, 0x09) + audio)
|
||||
|
||||
CdImageReader.openForFile(image, "disc.mds", mds(track1Sectors = 1, track2Sectors = 1)).use { reader ->
|
||||
val buffer = ByteArray(CdImageReader.ISO_SECTOR_SIZE)
|
||||
val output = ByteArrayOutputStream()
|
||||
|
||||
reader.readSector(0, buffer)
|
||||
reader.extractAudioTrack(2, output)
|
||||
|
||||
assertArrayEquals(dataPayload, buffer)
|
||||
assertEquals(listOf(2), reader.audioTracks())
|
||||
assertArrayEquals(audio, output.toByteArray().copyOfRange(44, 44 + RAW_AUDIO_SECTOR_SIZE))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rejectsMdsWithoutMediaDescriptorSignature() {
|
||||
val image = tmp.newFile("bad.mdf")
|
||||
image.writeBytes(ByteArray(RAW_AUDIO_SECTOR_SIZE))
|
||||
|
||||
assertInstallFailure(R.string.unsupported_cd_image) {
|
||||
CdImageReader.openForFile(image, "bad.mds", ByteArray(0x70))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GameInstallerCdImageTest {
|
||||
@get:Rule
|
||||
val tmp = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun installsDataOnlyIso() = runBlocking {
|
||||
val root = tmp.newFolder("root")
|
||||
val iso = SyntheticIsoBuilder()
|
||||
.addGameDataFile("SA.ALD;1", "ald".toByteArray())
|
||||
.addGameDataFile("TITLE.TXT;1", "title".toByteArray())
|
||||
.writeTo(tmp.newFile("game.iso"))
|
||||
|
||||
CdImageReader.openForFile(iso).use { reader ->
|
||||
val gameDir = GameInstaller(GameStore(root)).installCdImageForTest(reader) {}
|
||||
|
||||
assertEquals(File(root, "0/GAMEDATA").canonicalFile, gameDir.canonicalFile)
|
||||
assertEquals("GAMEDATA", File(root, "0/${Launcher.GAMEDIR_FILE}").readText())
|
||||
assertEquals("ald", File(gameDir, "SA.ALD").readText())
|
||||
assertEquals("title", File(gameDir, "TITLE.TXT").readText())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun installsCueAudioTrackAndPlaylist() = runBlocking {
|
||||
val root = tmp.newFolder("root")
|
||||
val isoSector = SyntheticIsoBuilder()
|
||||
.addGameDataFile("SA.ALD;1", "ald".toByteArray())
|
||||
.toByteArray()
|
||||
val dataSectors = isoSector.toList().chunked(CdImageReader.ISO_SECTOR_SIZE)
|
||||
.joinToByteArray { rawDataSector(it.toByteArray(), 0) }
|
||||
val audio = ByteArray(RAW_AUDIO_SECTOR_SIZE) { (it and 0x7f).toByte() }
|
||||
val image = tmp.newFile("audio.bin")
|
||||
image.writeBytes(dataSectors + audio)
|
||||
val dataSectorCount = isoSector.size / CdImageReader.ISO_SECTOR_SIZE
|
||||
val metadata = """
|
||||
FILE "audio.bin" BINARY
|
||||
TRACK 01 MODE1/2352
|
||||
INDEX 01 00:00:00
|
||||
TRACK 02 AUDIO
|
||||
INDEX 01 ${dataSectorCount.toMsf()}
|
||||
""".trimIndent()
|
||||
|
||||
CdImageReader.openForFile(image, "audio.cue", metadata.toByteArray()).use { reader ->
|
||||
val gameDir = GameInstaller(GameStore(root)).installCdImageForTest(reader) {}
|
||||
|
||||
val wav = File(gameDir, "cdda/track02.wav")
|
||||
assertTrue(wav.exists())
|
||||
assertEquals("\ncdda/track02.wav", File(gameDir, Launcher.PLAYLIST_FILE).readText())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rejectsIsoWithoutGameDataDirectoryAndDeletesIncompleteInstall() = runBlocking {
|
||||
val root = tmp.newFolder("root")
|
||||
val iso = SyntheticIsoBuilder(includeGameData = false).writeTo(tmp.newFile("nogamedata.iso"))
|
||||
|
||||
CdImageReader.openForFile(iso).use { reader ->
|
||||
assertInstallFailure(R.string.cannot_find_game_data_directory) {
|
||||
runBlocking {
|
||||
GameInstaller(GameStore(root)).installCdImageForTest(reader) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertFalse(File(root, "0").exists())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rejectsIsoWithoutAldAndDeletesIncompleteInstall() = runBlocking {
|
||||
val root = tmp.newFolder("root")
|
||||
val iso = SyntheticIsoBuilder()
|
||||
.addGameDataFile("README.TXT;1", "readme".toByteArray())
|
||||
.writeTo(tmp.newFile("noald.iso"))
|
||||
|
||||
CdImageReader.openForFile(iso).use { reader ->
|
||||
assertInstallFailure(R.string.cannot_find_ald) {
|
||||
runBlocking {
|
||||
GameInstaller(GameStore(root)).installCdImageForTest(reader) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertFalse(File(root, "0").exists())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resolveOutputPathRejectsTraversal() {
|
||||
val base = tmp.newFolder("out")
|
||||
|
||||
assertThrowsIOException {
|
||||
resolveOutputPath(base, "../evil")
|
||||
}
|
||||
assertThrowsIOException {
|
||||
resolveOutputPath(base, File(base.parentFile, "evil").absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SyntheticIsoBuilder(
|
||||
private val includeGameData: Boolean = true,
|
||||
) {
|
||||
private val primaryFiles = mutableMapOf<String, ByteArray>()
|
||||
private val jolietFiles = mutableMapOf<String, ByteArray>()
|
||||
|
||||
fun addGameDataFile(path: String, content: ByteArray): SyntheticIsoBuilder {
|
||||
primaryFiles[path] = content
|
||||
return this
|
||||
}
|
||||
|
||||
fun addJolietGameDataFile(path: String, content: ByteArray): SyntheticIsoBuilder {
|
||||
jolietFiles[path] = content
|
||||
return this
|
||||
}
|
||||
|
||||
fun writeTo(file: File): File {
|
||||
file.writeBytes(toByteArray())
|
||||
return file
|
||||
}
|
||||
|
||||
fun toByteArray(): ByteArray {
|
||||
val sectors = MutableList(80) { ByteArray(CdImageReader.ISO_SECTOR_SIZE) }
|
||||
var nextSector = 30
|
||||
val primaryTree = buildTree(primaryFiles, Charset.forName("Shift_JIS"))
|
||||
val primaryRoot = writeTree(sectors, primaryTree, ROOT_SECTOR, GAMEDATA_SECTOR, nextSector, false)
|
||||
nextSector = primaryRoot.nextSector
|
||||
|
||||
writeVolumeDescriptor(sectors[16], 1, ROOT_SECTOR, CdImageReader.ISO_SECTOR_SIZE, false)
|
||||
var terminatorSector = 17
|
||||
if (jolietFiles.isNotEmpty()) {
|
||||
val jolietTree = buildTree(jolietFiles, Charsets.UTF_16BE)
|
||||
writeTree(sectors, jolietTree, JOLIET_ROOT_SECTOR, JOLIET_GAMEDATA_SECTOR, nextSector, true)
|
||||
writeVolumeDescriptor(sectors[17], 2, JOLIET_ROOT_SECTOR, CdImageReader.ISO_SECTOR_SIZE, true)
|
||||
terminatorSector = 18
|
||||
}
|
||||
writeTerminator(sectors[terminatorSector])
|
||||
return sectors.flattenToByteArray()
|
||||
}
|
||||
|
||||
private fun buildTree(files: Map<String, ByteArray>, charset: Charset): DirectoryNode {
|
||||
val root = DirectoryNode("")
|
||||
if (!includeGameData) {
|
||||
return root
|
||||
}
|
||||
val gameData = DirectoryNode("GAMEDATA")
|
||||
root.directories["GAMEDATA"] = gameData
|
||||
for ((path, content) in files) {
|
||||
var directory = gameData
|
||||
val parts = path.split("/")
|
||||
for (part in parts.dropLast(1)) {
|
||||
directory = directory.directories.getOrPut(part.substringBefore(";")) {
|
||||
DirectoryNode(part.substringBefore(";"))
|
||||
}
|
||||
}
|
||||
val rawName = parts.last().toByteArray(charset)
|
||||
directory.files.add(FileNode(rawName, content))
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
private fun writeTree(
|
||||
sectors: MutableList<ByteArray>,
|
||||
root: DirectoryNode,
|
||||
rootSector: Int,
|
||||
gameDataSector: Int,
|
||||
firstFileSector: Int,
|
||||
joliet: Boolean,
|
||||
): TreeWriteResult {
|
||||
var nextSector = firstFileSector
|
||||
root.extent = rootSector
|
||||
root.size = CdImageReader.ISO_SECTOR_SIZE
|
||||
if (includeGameData) {
|
||||
root.directories["GAMEDATA"]!!.extent = gameDataSector
|
||||
root.directories["GAMEDATA"]!!.size = CdImageReader.ISO_SECTOR_SIZE
|
||||
}
|
||||
assignDirectorySectors(root, gameDataSector + 1)
|
||||
nextSector = writeFileContents(sectors, root, nextSector)
|
||||
writeDirectorySector(sectors[rootSector], root, root, joliet)
|
||||
if (includeGameData) {
|
||||
writeDirectorySectors(sectors, root.directories["GAMEDATA"]!!, root, joliet)
|
||||
}
|
||||
return TreeWriteResult(nextSector)
|
||||
}
|
||||
|
||||
private fun assignDirectorySectors(directory: DirectoryNode, nextDirectorySector: Int): Int {
|
||||
var sector = nextDirectorySector
|
||||
for (child in directory.directories.values) {
|
||||
if (child.name == "GAMEDATA") {
|
||||
sector = assignDirectorySectors(child, sector)
|
||||
continue
|
||||
}
|
||||
child.extent = sector++
|
||||
child.size = CdImageReader.ISO_SECTOR_SIZE
|
||||
sector = assignDirectorySectors(child, sector)
|
||||
}
|
||||
return sector
|
||||
}
|
||||
|
||||
private fun writeFileContents(
|
||||
sectors: MutableList<ByteArray>,
|
||||
directory: DirectoryNode,
|
||||
firstFileSector: Int,
|
||||
): Int {
|
||||
var sector = firstFileSector
|
||||
for (child in directory.directories.values) {
|
||||
sector = writeFileContents(sectors, child, sector)
|
||||
}
|
||||
for (file in directory.files) {
|
||||
file.extent = sector
|
||||
file.size = file.content.size
|
||||
val sectorCount = (file.content.size + CdImageReader.ISO_SECTOR_SIZE - 1) /
|
||||
CdImageReader.ISO_SECTOR_SIZE
|
||||
for (i in 0 until sectorCount) {
|
||||
file.content.copyInto(
|
||||
sectors[sector + i],
|
||||
0,
|
||||
i * CdImageReader.ISO_SECTOR_SIZE,
|
||||
minOf(file.content.size, (i + 1) * CdImageReader.ISO_SECTOR_SIZE)
|
||||
)
|
||||
}
|
||||
sector += maxOf(1, sectorCount)
|
||||
}
|
||||
return sector
|
||||
}
|
||||
|
||||
private fun writeDirectorySectors(
|
||||
sectors: MutableList<ByteArray>,
|
||||
directory: DirectoryNode,
|
||||
parent: DirectoryNode,
|
||||
joliet: Boolean,
|
||||
) {
|
||||
writeDirectorySector(sectors[directory.extent], directory, parent, joliet)
|
||||
for (child in directory.directories.values) {
|
||||
writeDirectorySectors(sectors, child, directory, joliet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeDirectorySector(
|
||||
sector: ByteArray,
|
||||
directory: DirectoryNode,
|
||||
parent: DirectoryNode,
|
||||
joliet: Boolean,
|
||||
) {
|
||||
var offset = 0
|
||||
offset += writeRecord(sector, offset, byteArrayOf(0), directory.extent, directory.size, true)
|
||||
offset += writeRecord(sector, offset, byteArrayOf(1), parent.extent, parent.size, true)
|
||||
for (child in directory.directories.values) {
|
||||
val name = if (joliet) child.name.toByteArray(Charsets.UTF_16BE) else child.name.toByteArray()
|
||||
offset += writeRecord(sector, offset, name, child.extent, child.size, true)
|
||||
}
|
||||
for (file in directory.files) {
|
||||
offset += writeRecord(sector, offset, file.rawName, file.extent, file.size, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeVolumeDescriptor(
|
||||
sector: ByteArray,
|
||||
type: Int,
|
||||
rootExtent: Int,
|
||||
rootSize: Int,
|
||||
joliet: Boolean,
|
||||
) {
|
||||
sector[0] = type.toByte()
|
||||
"CD001".toByteArray(Charsets.US_ASCII).copyInto(sector, 1)
|
||||
sector[6] = 1
|
||||
if (joliet) {
|
||||
sector[88] = 0x25
|
||||
sector[89] = 0x2f
|
||||
sector[90] = 0x45
|
||||
}
|
||||
writeRecord(sector, 156, byteArrayOf(0), rootExtent, rootSize, true)
|
||||
}
|
||||
|
||||
private fun writeTerminator(sector: ByteArray) {
|
||||
sector[0] = 255.toByte()
|
||||
"CD001".toByteArray(Charsets.US_ASCII).copyInto(sector, 1)
|
||||
sector[6] = 1
|
||||
}
|
||||
|
||||
private fun writeRecord(
|
||||
buffer: ByteArray,
|
||||
offset: Int,
|
||||
rawName: ByteArray,
|
||||
extent: Int,
|
||||
size: Int,
|
||||
isDirectory: Boolean,
|
||||
): Int {
|
||||
val length = 33 + rawName.size + if (rawName.size % 2 == 0) 1 else 0
|
||||
buffer[offset] = length.toByte()
|
||||
buffer.writeLeInt(offset + 2, extent)
|
||||
buffer.writeLeInt(offset + 10, size)
|
||||
buffer[offset + 25] = if (isDirectory) 0x02 else 0x00
|
||||
buffer[offset + 32] = rawName.size.toByte()
|
||||
rawName.copyInto(buffer, offset + 33)
|
||||
return length
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ROOT_SECTOR = 20
|
||||
private const val GAMEDATA_SECTOR = 21
|
||||
private const val JOLIET_ROOT_SECTOR = 24
|
||||
private const val JOLIET_GAMEDATA_SECTOR = 25
|
||||
}
|
||||
}
|
||||
|
||||
private data class DirectoryNode(
|
||||
val name: String,
|
||||
val directories: MutableMap<String, DirectoryNode> = linkedMapOf(),
|
||||
val files: MutableList<FileNode> = mutableListOf(),
|
||||
var extent: Int = 0,
|
||||
var size: Int = 0,
|
||||
)
|
||||
|
||||
private data class FileNode(
|
||||
val rawName: ByteArray,
|
||||
val content: ByteArray,
|
||||
var extent: Int = 0,
|
||||
var size: Int = 0,
|
||||
)
|
||||
|
||||
private data class TreeWriteResult(val nextSector: Int)
|
||||
|
||||
private const val RAW_AUDIO_SECTOR_SIZE = 2352
|
||||
|
||||
private fun cue(mode: String): String = """
|
||||
FILE "image.bin" BINARY
|
||||
TRACK 01 $mode
|
||||
INDEX 01 00:00:00
|
||||
""".trimIndent()
|
||||
|
||||
private fun mds(track1Sectors: Int, track2Sectors: Int): ByteArray {
|
||||
val bytes = ByteArray(0x70 + 2 * 0x50 + 2 * 8)
|
||||
"MEDIA DESCRIPTOR".toByteArray(Charsets.US_ASCII).copyInto(bytes, 0)
|
||||
bytes[0x62] = 2
|
||||
val track1 = 0x70
|
||||
bytes[track1] = 0xaa.toByte()
|
||||
bytes[track1 + 0x04] = 1
|
||||
bytes.writeLeShort(track1 + 0x10, RAW_AUDIO_SECTOR_SIZE)
|
||||
bytes.writeLeInt(track1 + 0x28, 0)
|
||||
bytes.writeLeInt(0x70 + 2 * 0x50 + 0x04, track1Sectors)
|
||||
val track2 = 0x70 + 0x50
|
||||
bytes[track2] = 0xa9.toByte()
|
||||
bytes[track2 + 0x04] = 2
|
||||
bytes.writeLeShort(track2 + 0x10, RAW_AUDIO_SECTOR_SIZE)
|
||||
bytes.writeLeInt(track2 + 0x28, track1Sectors * RAW_AUDIO_SECTOR_SIZE)
|
||||
bytes.writeLeInt(0x70 + 2 * 0x50 + 8 + 0x04, track2Sectors)
|
||||
return bytes
|
||||
}
|
||||
|
||||
private fun sectorFilledWith(value: Int): ByteArray {
|
||||
return ByteArray(CdImageReader.ISO_SECTOR_SIZE) { value.toByte() }
|
||||
}
|
||||
|
||||
private fun rawDataSector(payload: ByteArray, prefixValue: Int): ByteArray {
|
||||
val sector = ByteArray(RAW_AUDIO_SECTOR_SIZE) { prefixValue.toByte() }
|
||||
payload.copyInto(sector, 16, 0, CdImageReader.ISO_SECTOR_SIZE)
|
||||
return sector
|
||||
}
|
||||
|
||||
private fun Int.toMsf(): String {
|
||||
val minutes = this / (60 * 75)
|
||||
val seconds = this / 75 % 60
|
||||
val frames = this % 75
|
||||
return "%02d:%02d:%02d".format(minutes, seconds, frames)
|
||||
}
|
||||
|
||||
private fun Iterable<Byte>.toByteArray(): ByteArray {
|
||||
val result = ByteArray(count())
|
||||
forEachIndexed { index, byte -> result[index] = byte }
|
||||
return result
|
||||
}
|
||||
|
||||
private fun List<ByteArray>.flattenToByteArray(): ByteArray {
|
||||
val result = ByteArray(sumOf { it.size })
|
||||
var offset = 0
|
||||
for (bytes in this) {
|
||||
bytes.copyInto(result, offset)
|
||||
offset += bytes.size
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun List<List<Byte>>.joinToByteArray(transform: (List<Byte>) -> ByteArray): ByteArray {
|
||||
return map(transform).flattenToByteArray()
|
||||
}
|
||||
|
||||
private fun ByteArray.decodeAscii(offset: Int, length: Int): String {
|
||||
return String(this, offset, length, Charsets.US_ASCII)
|
||||
}
|
||||
|
||||
private fun ByteArray.readLeShort(offset: Int): Int {
|
||||
return (this[offset].toInt() and 0xff) or ((this[offset + 1].toInt() and 0xff) shl 8)
|
||||
}
|
||||
|
||||
private fun ByteArray.readLeInt(offset: Int): Int {
|
||||
return readLeShort(offset) or (readLeShort(offset + 2) shl 16)
|
||||
}
|
||||
|
||||
private fun ByteArray.writeLeShort(offset: Int, value: Int) {
|
||||
this[offset] = value.toByte()
|
||||
this[offset + 1] = (value shr 8).toByte()
|
||||
}
|
||||
|
||||
private fun ByteArray.writeLeInt(offset: Int, value: Int) {
|
||||
writeLeShort(offset, value)
|
||||
writeLeShort(offset + 2, value shr 16)
|
||||
}
|
||||
|
||||
private fun assertInstallFailure(expectedMsgId: Int, block: () -> Unit): InstallFailureException {
|
||||
try {
|
||||
block()
|
||||
} catch (e: InstallFailureException) {
|
||||
assertEquals(expectedMsgId, e.msgId)
|
||||
return e
|
||||
}
|
||||
fail("Expected InstallFailureException")
|
||||
throw AssertionError()
|
||||
}
|
||||
|
||||
private fun assertThrowsIOException(block: () -> Unit) {
|
||||
try {
|
||||
block()
|
||||
} catch (e: java.io.IOException) {
|
||||
return
|
||||
}
|
||||
fail("Expected IOException")
|
||||
}
|
||||
+1
-4
@@ -1,17 +1,14 @@
|
||||
#cmakedefine PACKAGE "@PACKAGE@"
|
||||
#define VERSION "@XSYSTEM35_VERSION@"
|
||||
|
||||
#cmakedefine CACHE_TOTALSIZE @CACHE_TOTALSIZE@
|
||||
|
||||
#cmakedefine ENABLE_DEBUGGER @ENABLE_DEBUGGER@
|
||||
#cmakedefine ENABLE_GTK @ENABLE_GTK@
|
||||
#cmakedefine ENABLE_MIDI_SDLMIXER @ENABLE_MIDI_SDLMIXER@
|
||||
#cmakedefine ENABLE_MIDI_PORTMIDI @ENABLE_MIDI_PORTMIDI@
|
||||
#cmakedefine ENABLE_NLS @ENABLE_NLS@
|
||||
|
||||
#cmakedefine DEFAULT_PLAYLIST_PATH "@DEFAULT_PLAYLIST_PATH@"
|
||||
#cmakedefine JOY_DEVICE "@JOY_DEVICE@"
|
||||
|
||||
#cmakedefine HAVE_LIBINTL @HAVE_LIBINTL@
|
||||
#cmakedefine HAVE_GETLOGIN @HAVE_GETLOGIN@
|
||||
#cmakedefine HAVE_MMAP @HAVE_MMAP@
|
||||
#cmakedefine HAVE_SIGACTION @HAVE_SIGACTION@
|
||||
|
||||
@@ -98,6 +98,9 @@ finds System 3.x game files (*.ALD) from the current directory.
|
||||
*-noimagecursor*::
|
||||
Disable custom mouse cursor images.
|
||||
|
||||
*-mute_on_unfocus*::
|
||||
Mute audio while the game window is not in focus.
|
||||
|
||||
*-version*::
|
||||
Print the version number and exit.
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2024 rxi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -12,8 +12,8 @@ or in your `.xsys35rc` file. For example:
|
||||
xsystem35 -censor misc/censor/kichikuou.txt
|
||||
```
|
||||
|
||||
The list files contain image numbers (integers), one per line. Lines starting
|
||||
with `#` are treated as comments.
|
||||
The list files contain image numbers (integers), one per line. A `#` starts a
|
||||
comment that continues to the end of the line.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# Censor list for ランス -光をもとめて- (Rance: The Quest for Hikari)
|
||||
# Images marked with a * might be allowed in an 18+ stream.
|
||||
5
|
||||
6
|
||||
8
|
||||
22
|
||||
26
|
||||
27
|
||||
29
|
||||
32
|
||||
39
|
||||
40
|
||||
41
|
||||
51
|
||||
56
|
||||
57
|
||||
75
|
||||
76
|
||||
77
|
||||
78 # *
|
||||
80
|
||||
82
|
||||
89
|
||||
91
|
||||
96 # *
|
||||
97 # *
|
||||
98
|
||||
99
|
||||
101
|
||||
208
|
||||
@@ -0,0 +1,59 @@
|
||||
# Censor list for ランスII (Rance II)
|
||||
# Images marked with a * might be allowed in an 18+ stream.
|
||||
26
|
||||
39 # *
|
||||
40
|
||||
41
|
||||
77
|
||||
78
|
||||
86
|
||||
98
|
||||
103
|
||||
104
|
||||
121
|
||||
122
|
||||
123
|
||||
124
|
||||
130
|
||||
131
|
||||
132
|
||||
133
|
||||
134
|
||||
140
|
||||
141
|
||||
142
|
||||
148
|
||||
156
|
||||
157
|
||||
158
|
||||
159
|
||||
161
|
||||
166
|
||||
171
|
||||
172
|
||||
185
|
||||
187
|
||||
191
|
||||
195
|
||||
197
|
||||
200
|
||||
201
|
||||
234
|
||||
236
|
||||
245
|
||||
246
|
||||
247
|
||||
272
|
||||
274
|
||||
275
|
||||
276
|
||||
278
|
||||
280
|
||||
281
|
||||
282
|
||||
284
|
||||
289
|
||||
292
|
||||
299
|
||||
300
|
||||
302
|
||||
@@ -0,0 +1,121 @@
|
||||
# Censor list for Rance 3
|
||||
# Images marked with a * might be allowed in an 18+ stream.
|
||||
1 # *
|
||||
2 # *
|
||||
3
|
||||
6
|
||||
8
|
||||
9
|
||||
32 # *
|
||||
53 # *
|
||||
54
|
||||
70
|
||||
71
|
||||
72
|
||||
73
|
||||
75
|
||||
77 # *
|
||||
78
|
||||
79
|
||||
112 # *
|
||||
113
|
||||
114
|
||||
115
|
||||
116
|
||||
117
|
||||
118 # *
|
||||
150
|
||||
151
|
||||
152
|
||||
153
|
||||
154
|
||||
155
|
||||
166
|
||||
167
|
||||
180
|
||||
181
|
||||
183
|
||||
184
|
||||
185
|
||||
186
|
||||
187
|
||||
188
|
||||
189
|
||||
190
|
||||
195
|
||||
196
|
||||
208
|
||||
209
|
||||
210
|
||||
211
|
||||
212
|
||||
213
|
||||
215
|
||||
216
|
||||
217
|
||||
218
|
||||
219
|
||||
231
|
||||
232
|
||||
233
|
||||
234
|
||||
235
|
||||
236
|
||||
238
|
||||
239
|
||||
260
|
||||
261
|
||||
263
|
||||
264
|
||||
265
|
||||
266
|
||||
268
|
||||
269
|
||||
330
|
||||
331
|
||||
332
|
||||
334
|
||||
335
|
||||
336
|
||||
337
|
||||
338
|
||||
339
|
||||
420
|
||||
421
|
||||
422
|
||||
423
|
||||
424
|
||||
425
|
||||
426
|
||||
427
|
||||
428
|
||||
429
|
||||
440
|
||||
441
|
||||
442
|
||||
443
|
||||
444
|
||||
445
|
||||
447
|
||||
473
|
||||
474
|
||||
475
|
||||
476
|
||||
477
|
||||
479
|
||||
519 # *
|
||||
521 # *
|
||||
523
|
||||
524
|
||||
525
|
||||
526
|
||||
530
|
||||
533 # *
|
||||
534
|
||||
535
|
||||
536
|
||||
537
|
||||
570
|
||||
571
|
||||
572
|
||||
581
|
||||
@@ -0,0 +1,168 @@
|
||||
# Censor list for Rance IV
|
||||
# Images marked with a * might be allowed in an 18+ stream.
|
||||
1
|
||||
2
|
||||
10
|
||||
20
|
||||
26
|
||||
28
|
||||
29
|
||||
30 # *
|
||||
31
|
||||
32
|
||||
33
|
||||
34
|
||||
35
|
||||
36
|
||||
37
|
||||
38
|
||||
39 # *
|
||||
40
|
||||
41
|
||||
42
|
||||
43
|
||||
44
|
||||
45
|
||||
46
|
||||
48
|
||||
49
|
||||
75
|
||||
76
|
||||
77
|
||||
78
|
||||
79
|
||||
122
|
||||
126
|
||||
140
|
||||
142
|
||||
148
|
||||
149
|
||||
162 # *
|
||||
163
|
||||
164
|
||||
165 # *
|
||||
166
|
||||
167
|
||||
168
|
||||
169 # *
|
||||
170
|
||||
171
|
||||
172
|
||||
176
|
||||
177
|
||||
178
|
||||
185
|
||||
186
|
||||
188
|
||||
189
|
||||
205
|
||||
207
|
||||
217 # *
|
||||
220
|
||||
221
|
||||
222
|
||||
223
|
||||
228
|
||||
241
|
||||
242
|
||||
251
|
||||
252
|
||||
253
|
||||
254
|
||||
260
|
||||
261
|
||||
262 # *
|
||||
264 # *
|
||||
265
|
||||
266
|
||||
267
|
||||
268
|
||||
269
|
||||
280
|
||||
281
|
||||
283
|
||||
284
|
||||
285
|
||||
286 # *
|
||||
287
|
||||
288
|
||||
292
|
||||
294
|
||||
299
|
||||
370
|
||||
371
|
||||
372
|
||||
373
|
||||
376
|
||||
378
|
||||
382
|
||||
384
|
||||
385 # *
|
||||
386
|
||||
387
|
||||
388
|
||||
389
|
||||
414 # *
|
||||
416
|
||||
428 # *
|
||||
531 # *
|
||||
532
|
||||
533
|
||||
534 # *
|
||||
535
|
||||
550
|
||||
551
|
||||
552
|
||||
553
|
||||
554
|
||||
555
|
||||
562 # *
|
||||
565
|
||||
615
|
||||
625
|
||||
626
|
||||
711
|
||||
723
|
||||
1055
|
||||
1056
|
||||
1057
|
||||
1058
|
||||
1059
|
||||
1060
|
||||
1061
|
||||
1062
|
||||
1545 # *
|
||||
1546 # *
|
||||
1547 # *
|
||||
1548 # *
|
||||
1549 # *
|
||||
1653 # *
|
||||
1654 # *
|
||||
1655 # *
|
||||
1656 # *
|
||||
1657 # *
|
||||
1658 # *
|
||||
1659 # *
|
||||
1660 # *
|
||||
1661 # *
|
||||
1662 # *
|
||||
1663 # *
|
||||
1664 # *
|
||||
1665 # *
|
||||
1728
|
||||
1729
|
||||
2004
|
||||
2005
|
||||
2006
|
||||
2007
|
||||
2008
|
||||
2009
|
||||
2010
|
||||
2011
|
||||
2012
|
||||
2013
|
||||
2014
|
||||
2015
|
||||
2016
|
||||
2017
|
||||
2018
|
||||
@@ -12,7 +12,7 @@ static void Init() {
|
||||
int p1 = getCaliValue(); /* ISys3x */
|
||||
int p2 = getCaliValue(); /* IWinMsg */
|
||||
int p3 = getCaliValue(); /* ITimer */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ static void Init() {
|
||||
|
||||
static void ExistKeyFile() {
|
||||
const char *p1 = sl_getString(0);
|
||||
int *p2 = getCaliVariable();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *p2 = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
@@ -25,8 +25,8 @@ static void ExistKeyFile() {
|
||||
|
||||
static void CheckProtectFile() {
|
||||
const char *p1 = sl_getString(0);
|
||||
int *p2 = getCaliVariable();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *p2 = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
@@ -35,8 +35,8 @@ static void CheckProtectFile() {
|
||||
|
||||
static void CreateKeyFile() {
|
||||
const char *p1 = sl_getString(0);
|
||||
int *p2 = getCaliVariable();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *p2 = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
|
||||
+13
-13
@@ -135,7 +135,7 @@ static void Create() {
|
||||
height: surface の高さ
|
||||
bpp : surface の深さ(24bppのみサポート)
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int width = getCaliValue();
|
||||
int height = getCaliValue();
|
||||
int bpp = getCaliValue();
|
||||
@@ -162,7 +162,7 @@ static void CreatePixelOnly() {
|
||||
height: surface の高さ
|
||||
bpp : surface の深さ(24bpp only)
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int width = getCaliValue();
|
||||
int height = getCaliValue();
|
||||
int bpp = getCaliValue();
|
||||
@@ -188,7 +188,7 @@ static void CreateAMapOnly() {
|
||||
width : surface の幅
|
||||
height: surface の高さ
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int width = getCaliValue();
|
||||
int height = getCaliValue();
|
||||
surface_t *s;
|
||||
@@ -214,7 +214,7 @@ static void IsSurface() {
|
||||
var : 結果を返す変数。surface ならば 1, !surface ならば 0
|
||||
*/
|
||||
int p1 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = sf_get(p1) ? 1 : 0;
|
||||
|
||||
@@ -229,7 +229,7 @@ static void IsPixel() {
|
||||
var : 結果を返す変数。pixel ならば 1, !pixel ならば 0
|
||||
*/
|
||||
int p1 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
surface_t *s;
|
||||
|
||||
s = sf_get(p1);
|
||||
@@ -251,7 +251,7 @@ static void IsAlpha() {
|
||||
var : 結果を返す変数。alpha ならば 1, !alpha ならば 0
|
||||
*/
|
||||
int p1 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
surface_t *s;
|
||||
|
||||
s = sf_get(p1);
|
||||
@@ -273,7 +273,7 @@ static void GetWidth() {
|
||||
var : 結果を返す変数。
|
||||
*/
|
||||
int p1 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
surface_t *s;
|
||||
|
||||
s = sf_get(p1);
|
||||
@@ -295,7 +295,7 @@ static void GetHeight() {
|
||||
var : 結果を返す変数。
|
||||
*/
|
||||
int p1 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
surface_t *s;
|
||||
|
||||
s = sf_get(p1);
|
||||
@@ -310,7 +310,7 @@ static void GetHeight() {
|
||||
}
|
||||
|
||||
static void GetCreatedSurface() { /* not used ? */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("Gpx.GetCreatedSurface %p:", var);
|
||||
}
|
||||
@@ -322,7 +322,7 @@ static void LoadCG() {
|
||||
var : 作成した surface の番号を返す変数
|
||||
p1 : 読み込む CG の番号
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int p1 = getCaliValue();
|
||||
|
||||
*var = load_cg_main(p1 -1);
|
||||
@@ -331,14 +331,14 @@ static void LoadCG() {
|
||||
}
|
||||
|
||||
static void GetCGPosX() { /* not useed ? */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int p1 = getCaliValue();
|
||||
|
||||
TRACE_UNIMPLEMENTED("Gpx.GetCgPosX %p,%d:", var, p1);
|
||||
}
|
||||
|
||||
static void GetCGPosY() { /* not useed ? */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int p1 = getCaliValue();
|
||||
|
||||
TRACE_UNIMPLEMENTED("Gpx.GetCgPosY %p,%d:", var, p1);
|
||||
@@ -1117,7 +1117,7 @@ static void EffectCopy() {
|
||||
int sw = getCaliValue();
|
||||
int sh = getCaliValue();
|
||||
int time = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
TRACE("Gpx.EffectCopy %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%p:", no, dx, dy, ss1, sx1, sy1, ss2, sx2, sy2, sw, sh, time, var);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ void gpx_effect(int no,
|
||||
surface_t *src, int sx, int sy,
|
||||
int width, int height,
|
||||
int time,
|
||||
int *endtype) {
|
||||
vmvar_t *endtype) {
|
||||
surface_t *write = nact->ags.dib;
|
||||
if (!gr_clip(dst, &dx, &dy, &width, &height, write, &wx, &wy)) return;
|
||||
if (!gr_clip(src, &sx, &sy, &width, &height, write, &wx, &wy)) return;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef __GLEFFECTCOPY_H__
|
||||
#define __GLEFFECTCOPY_H__
|
||||
|
||||
#include "portab.h"
|
||||
#include "surface.h"
|
||||
|
||||
extern void gpx_effect(int no,
|
||||
@@ -9,6 +10,6 @@ extern void gpx_effect(int no,
|
||||
surface_t *src, int sx, int sy,
|
||||
int width, int height,
|
||||
int time,
|
||||
int *endtype);
|
||||
vmvar_t *endtype);
|
||||
|
||||
#endif /* __GLEFFECTCOPY_H__ */
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ static void RandMTGet() {
|
||||
var: 結果を返す変数
|
||||
*/
|
||||
int num = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
if (num == 0 || num == 1) {
|
||||
*var = num;
|
||||
@@ -83,7 +83,7 @@ static void RandMTGetNumTable() {
|
||||
|
||||
var: 乱数を格納する変数
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = (int)(genrand() * numtblmax) + 1;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ static bool valid;
|
||||
static int action;
|
||||
|
||||
static void Init() {
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
int p2 = getCaliValue(); /* ISys3x */
|
||||
int p3 = getCaliValue();
|
||||
int p4 = getCaliValue();
|
||||
@@ -30,7 +30,7 @@ static void Init() {
|
||||
}
|
||||
|
||||
static void Start() {
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
int p2 = getCaliValue();
|
||||
|
||||
char *fname_utf8 = sjis2utf(svar_get(p2));
|
||||
@@ -52,7 +52,7 @@ static void SetValid() {
|
||||
}
|
||||
|
||||
static void GetValid() {
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
|
||||
*p1 = valid;
|
||||
|
||||
@@ -68,7 +68,7 @@ static void SetAction() {
|
||||
}
|
||||
|
||||
static void GetAction() {
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
|
||||
*p1 = action;
|
||||
|
||||
|
||||
+25
-26
@@ -22,7 +22,7 @@ night_t nightprv;
|
||||
|
||||
|
||||
static void Init(void) { /* 0 */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int p1 = getCaliValue(); /* ISys3xCG */
|
||||
int p2 = getCaliValue(); /* ISys3xDIB */
|
||||
int p3 = getCaliValue(); /* ISys3xMsgString */
|
||||
@@ -56,8 +56,7 @@ static void InitGame() { /* 1 */
|
||||
|
||||
nact->msgout = ntmsg_add;
|
||||
nact->ags.eventcb = ntev_callback;
|
||||
nact->callback = ntev_main;
|
||||
|
||||
|
||||
nt_gr_init();
|
||||
ntmsg_init();
|
||||
nt_sstr_init();
|
||||
@@ -214,7 +213,7 @@ static void SetSelMode(void) { /* 18 */
|
||||
|
||||
// キー入力待ち後、改ページ
|
||||
static void AnalyzeMessage(void) { /* 19 */
|
||||
int *var = getCaliVariable(); // 入力されたキー
|
||||
vmvar_t *var = getCaliVariable(); // 入力されたキー
|
||||
|
||||
*var = ntmsg_ana();
|
||||
|
||||
@@ -298,7 +297,7 @@ static void ScreenCG(void) { /* 27 */
|
||||
}
|
||||
|
||||
static void RunGameMain(void) { /* 28 */
|
||||
int *p1 = getCaliVariable(); // result
|
||||
vmvar_t *p1 = getCaliVariable(); // result
|
||||
int p2 = getCaliValue(); // month
|
||||
int p3 = getCaliValue(); // day
|
||||
int p4 = getCaliValue(); // day of week
|
||||
@@ -314,7 +313,7 @@ static void RunGameMain(void) { /* 28 */
|
||||
}
|
||||
|
||||
static void CheckNewGame(void) { /* 29 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
|
||||
*p1 = 0;
|
||||
|
||||
@@ -348,9 +347,9 @@ static void SetDate(void) { /* 33 */
|
||||
}
|
||||
|
||||
static void GetDate(void) { /* 34 */
|
||||
int *p1 = getCaliVariable(); // month
|
||||
int *p2 = getCaliVariable(); // day
|
||||
int *p3 = getCaliVariable(); // day of weeek
|
||||
vmvar_t *p1 = getCaliVariable(); // month
|
||||
vmvar_t *p2 = getCaliVariable(); // day
|
||||
vmvar_t *p3 = getCaliVariable(); // day of weeek
|
||||
|
||||
*p1 = night.Month;
|
||||
*p2 = night.Day;
|
||||
@@ -364,7 +363,7 @@ static void SelectGameLevel(void) { /* 35 */
|
||||
}
|
||||
|
||||
static void RunEventDungeon(void) { /* 36 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
int p2 = getCaliValue();
|
||||
|
||||
*p1 = 1;
|
||||
@@ -479,7 +478,7 @@ static void RunSoundMode(void) { /* 48 */
|
||||
}
|
||||
|
||||
static void RunMapEditor(void) { /* 49 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.RunMapEditor %p:", p1);
|
||||
}
|
||||
@@ -496,21 +495,21 @@ static void VisualListAdd(void) { /* 51 */
|
||||
}
|
||||
|
||||
static void GetLocalCountCG(void) { /* 52 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
int p2 = getCaliValue();
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.GetLocalCountCG %p,%d:", p1, p2);
|
||||
}
|
||||
|
||||
static void PlayMemory(void) { /* 53 */
|
||||
int *p1 = getCaliVariable(); // 回想ページ
|
||||
int *p2 = getCaliVariable(); // 回想RESULT
|
||||
vmvar_t *p1 = getCaliVariable(); // 回想ページ
|
||||
vmvar_t *p2 = getCaliVariable(); // 回想RESULT
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.PlayMemory %p,%p:", p1, p2);
|
||||
}
|
||||
|
||||
static void GetEventFlagTotal(void) { /* 54 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
int p2 = getCaliValue();
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.GetEventFlagTotal %p,%d:", p1, p2);
|
||||
@@ -529,25 +528,25 @@ static void GetPlayerName(void) { /* 56 */
|
||||
}
|
||||
|
||||
static void SaveGame(void) { /* 57 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.SaveGame %p:", p1);
|
||||
}
|
||||
|
||||
static void LoadGame(void) { /* 58 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.LoadGame %p:", p1);
|
||||
}
|
||||
|
||||
static void ExistSaveData(void) { /* 59 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.ExistSaveData %p:", p1);
|
||||
}
|
||||
|
||||
static void ExistStartData(void) { /* 60 */
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.ExistStartData %p:", p1);
|
||||
}
|
||||
@@ -581,13 +580,13 @@ static void DebugScenario(void) { /* 66 */
|
||||
}
|
||||
|
||||
static void GetDLLTime(void) { /* 67 */
|
||||
int *p1 = getCaliVariable();
|
||||
int *p2 = getCaliVariable();
|
||||
int *p3 = getCaliVariable();
|
||||
int *p4 = getCaliVariable();
|
||||
int *p5 = getCaliVariable();
|
||||
int *p6 = getCaliVariable();
|
||||
int *p7 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
vmvar_t *p2 = getCaliVariable();
|
||||
vmvar_t *p3 = getCaliVariable();
|
||||
vmvar_t *p4 = getCaliVariable();
|
||||
vmvar_t *p5 = getCaliVariable();
|
||||
vmvar_t *p6 = getCaliVariable();
|
||||
vmvar_t *p7 = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("NIGHTDLL.GetDLLTime %p,%p,%p,%p,%p,%p,%p:", p1, p2, p3, p4, p5, p6, p7);
|
||||
}
|
||||
|
||||
@@ -53,11 +53,6 @@ static void cb_waitkey_selection(agsevent_t *e) {
|
||||
}
|
||||
|
||||
void ntev_callback(agsevent_t *e) {
|
||||
// menu open中は無視
|
||||
if (nact->popupmenu_opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->type == AGSEVENT_KEY_PRESS && e->code == KEY_CTRL) {
|
||||
night.waitskiplv = 2;
|
||||
night.waitkey = e->code;
|
||||
@@ -90,16 +85,5 @@ void ntev_callback(agsevent_t *e) {
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
system35のメインループからで呼ばれるコールバック
|
||||
*/
|
||||
void ntev_main() {
|
||||
// デフォルトのコールバックのうち、ここで必要なものだけ処理。
|
||||
if (nact->popupmenu_opened) {
|
||||
menu_gtkmainiteration();
|
||||
if (nact->is_quit) sys_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define __NT_EVENT_H__
|
||||
|
||||
extern void ntev_callback(agsevent_t *e);
|
||||
extern void ntev_main();
|
||||
|
||||
|
||||
#endif /* __NT_EVENT_H__ */
|
||||
|
||||
@@ -55,19 +55,17 @@ static void ntmain(struct _scoadr inadr) {
|
||||
while (!nact->is_quit && !is_yield_requested()) {
|
||||
scheduler_on_command();
|
||||
//SACT_DEBUG("%d:%x", sl_getPage(), sl_getIndex());
|
||||
if (!nact->popupmenu_opened) {
|
||||
exec_command();
|
||||
if (sl_getPage() == inadr.page &&
|
||||
sl_getIndex() == inadr.index) {
|
||||
// ~E%05dからの戻り
|
||||
if (nact->fnc_return_value == 0) {
|
||||
return;
|
||||
} else {
|
||||
scono = nact->fnc_return_value;
|
||||
}
|
||||
curadr = scene2adr(scono);
|
||||
sl_callFar2(curadr.page -1, curadr.index);
|
||||
exec_command();
|
||||
if (sl_getPage() == inadr.page &&
|
||||
sl_getIndex() == inadr.index) {
|
||||
// ~E%05dからの戻り
|
||||
if (nact->fnc_return_value == 0) {
|
||||
return;
|
||||
} else {
|
||||
scono = nact->fnc_return_value;
|
||||
}
|
||||
curadr = scene2adr(scono);
|
||||
sl_callFar2(curadr.page -1, curadr.index);
|
||||
}
|
||||
nact->callback();
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ static void Init() {
|
||||
int p1 = getCaliValue(); /* ISys3x */
|
||||
int p2 = getCaliValue(); /* IWinMsg */
|
||||
int p3 = getCaliValue(); /* ITimer */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ static void Get() {
|
||||
var: 結果を返す変数
|
||||
*/
|
||||
int num = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
if (num == 0 || num == 1) {
|
||||
*var = num;
|
||||
@@ -65,7 +65,7 @@ static void Get() {
|
||||
static void GetNoOverlap() {
|
||||
int min = getCaliValue();
|
||||
int n = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = (int)(genrand() * n) + min;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ static void CreateChannel(void) {
|
||||
static void Prepare(void) {
|
||||
int ch = getCaliValue();
|
||||
int no = getCaliValue();
|
||||
int *result = getCaliVariable();
|
||||
vmvar_t *result = getCaliVariable();
|
||||
|
||||
if (ch == 0) {
|
||||
current_no = no;
|
||||
@@ -97,7 +97,7 @@ static void PlayPosSample(void) {
|
||||
|
||||
static void IsPlay(void) {
|
||||
int ch = getCaliValue();
|
||||
int *result = getCaliVariable();
|
||||
vmvar_t *result = getCaliVariable();
|
||||
|
||||
if (ch == 0) {
|
||||
*result = current_no && musbgm_isplaying(current_no);
|
||||
@@ -127,10 +127,10 @@ static void Restart(void) {
|
||||
|
||||
static void GetPlayPos(void) {
|
||||
int ch = getCaliValue();
|
||||
int *hour = getCaliVariable();
|
||||
int *min = getCaliVariable();
|
||||
int *sec = getCaliVariable();
|
||||
int *msec = getCaliVariable();
|
||||
vmvar_t *hour = getCaliVariable();
|
||||
vmvar_t *min = getCaliVariable();
|
||||
vmvar_t *sec = getCaliVariable();
|
||||
vmvar_t *msec = getCaliVariable();
|
||||
if (ch == 0) {
|
||||
int time = musbgm_getpos(current_no);
|
||||
*hour = time / 360000;
|
||||
@@ -149,8 +149,8 @@ static void GetPlayPos(void) {
|
||||
|
||||
static void GetPlayPosSample(void) {
|
||||
int p1 = getCaliValue();
|
||||
int *var1 = getCaliVariable();
|
||||
int *var2 = getCaliVariable();
|
||||
vmvar_t *var1 = getCaliVariable();
|
||||
vmvar_t *var2 = getCaliVariable();
|
||||
TRACE_UNIMPLEMENTED("S3xMusic.GetPlayPosSample %d, %p, %p:", p1, var1, var2);
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ static void FadeVolume(void) {
|
||||
|
||||
static void IsFade(void) {
|
||||
int ch = getCaliValue();
|
||||
int *result = getCaliVariable();
|
||||
vmvar_t *result = getCaliVariable();
|
||||
*result = 0;
|
||||
TRACE_UNIMPLEMENTED("S3xMusic.IsFade %d => %d:", ch, *result);
|
||||
}
|
||||
|
||||
+69
-60
@@ -33,6 +33,7 @@
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
#include "ald_manager.h"
|
||||
#include "audio_meta.h"
|
||||
#include "input.h"
|
||||
#include "msgskip.h"
|
||||
#include "xsystem35.h"
|
||||
@@ -286,8 +287,11 @@ static void DrawEffect() {
|
||||
if (sact.version >= 110) {
|
||||
wEffectkey = getCaliValue();
|
||||
}
|
||||
|
||||
sp_eupdate(wType, wEffectTime, wEffectkey);
|
||||
|
||||
if (sact.waitskiplv > 1 || msgskip_isSkipping())
|
||||
sp_update_all(true);
|
||||
else
|
||||
sp_eupdate(wType, wEffectTime, wEffectkey);
|
||||
|
||||
TRACE("SACT.DrawEffect %d,%d,%d:", wType, wEffectTime, wEffectkey);
|
||||
}
|
||||
@@ -304,7 +308,10 @@ static void DrawEffectAlphaMap() {
|
||||
int wEffectTime = getCaliValue();
|
||||
int wEffectKey = getCaliValue();
|
||||
|
||||
sp_eupdate_amap(nIndexAlphaMap, wEffectTime, wEffectKey);
|
||||
if (sact.waitskiplv > 1 || msgskip_isSkipping())
|
||||
sp_update_all(true);
|
||||
else
|
||||
sp_eupdate_amap(nIndexAlphaMap, wEffectTime, wEffectKey);
|
||||
|
||||
TRACE("SACT.DrawEffectAlphaMap %d,%d,%d:", nIndexAlphaMap, wEffectTime, wEffectKey);
|
||||
}
|
||||
@@ -622,9 +629,10 @@ static void QuakeSprite() {
|
||||
if (sact.version >= 110) {
|
||||
nfKeyEnable = getCaliValue();
|
||||
}
|
||||
|
||||
sp_quake_sprite(wType, wAmplitudeX, wAmplitudeY, wCount, nfKeyEnable);
|
||||
|
||||
|
||||
if (!msgskip_isSkipping())
|
||||
sp_quake_sprite(wType, wAmplitudeX, wAmplitudeY, wCount, nfKeyEnable);
|
||||
|
||||
TRACE("SACT.QuakeSprite %d,%d,%d,%d:", wType, wAmplitudeX, wAmplitudeY, wCount);
|
||||
}
|
||||
|
||||
@@ -636,7 +644,7 @@ static void QuakeSprite() {
|
||||
*/
|
||||
static void QuerySpriteIsExist() {
|
||||
int wNum = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = sp_exists(wNum) ? 1 : 0;
|
||||
|
||||
@@ -654,10 +662,10 @@ static void QuerySpriteIsExist() {
|
||||
*/
|
||||
static void QuerySpriteInfo() {
|
||||
int wNum = getCaliValue();
|
||||
int *vType = getCaliVariable();
|
||||
int *vCG1 = getCaliVariable();
|
||||
int *vCG2 = getCaliVariable();
|
||||
int *vCG3 = getCaliVariable();
|
||||
vmvar_t *vType = getCaliVariable();
|
||||
vmvar_t *vCG1 = getCaliVariable();
|
||||
vmvar_t *vCG2 = getCaliVariable();
|
||||
vmvar_t *vCG3 = getCaliVariable();
|
||||
|
||||
sp_query_info(wNum, vType, vCG1, vCG2, vCG3);
|
||||
|
||||
@@ -672,7 +680,7 @@ static void QuerySpriteInfo() {
|
||||
*/
|
||||
static void QuerySpriteShow() {
|
||||
int wNum = getCaliValue();
|
||||
int *vShow = getCaliVariable();
|
||||
vmvar_t *vShow = getCaliVariable();
|
||||
|
||||
sp_query_show(wNum, vShow);
|
||||
|
||||
@@ -688,8 +696,8 @@ static void QuerySpriteShow() {
|
||||
*/
|
||||
static void QuerySpritePos() {
|
||||
int wNum = getCaliValue();
|
||||
int *vX = getCaliVariable();
|
||||
int *vY = getCaliVariable();
|
||||
vmvar_t *vX = getCaliVariable();
|
||||
vmvar_t *vY = getCaliVariable();
|
||||
|
||||
sp_query_pos(wNum, vX, vY);
|
||||
|
||||
@@ -705,8 +713,8 @@ static void QuerySpritePos() {
|
||||
*/
|
||||
static void QuerySpriteSize() {
|
||||
int wNum = getCaliValue();
|
||||
int *vWidth = getCaliVariable();
|
||||
int *vHeight = getCaliVariable();
|
||||
vmvar_t *vWidth = getCaliVariable();
|
||||
vmvar_t *vHeight = getCaliVariable();
|
||||
|
||||
sp_query_size(wNum, vWidth, vHeight);
|
||||
|
||||
@@ -722,8 +730,8 @@ static void QuerySpriteSize() {
|
||||
*/
|
||||
static void QueryTextPos() {
|
||||
int wNum = getCaliValue();
|
||||
int *vX = getCaliVariable();
|
||||
int *vY = getCaliVariable();
|
||||
vmvar_t *vX = getCaliVariable();
|
||||
vmvar_t *vY = getCaliVariable();
|
||||
|
||||
sp_query_textpos(wNum, vX, vY);
|
||||
|
||||
@@ -762,7 +770,7 @@ static void CG_Reset() {
|
||||
*/
|
||||
static void CG_QueryType() {
|
||||
int wNumCG = getCaliValue();
|
||||
int *vType = getCaliVariable();
|
||||
vmvar_t *vType = getCaliVariable();
|
||||
|
||||
*vType = scg_querytype(wNumCG);
|
||||
|
||||
@@ -778,8 +786,8 @@ static void CG_QueryType() {
|
||||
*/
|
||||
static void CG_QuerySize() {
|
||||
int wNumCG = getCaliValue();
|
||||
int *vWidth = getCaliVariable();
|
||||
int *vHeight = getCaliVariable();
|
||||
vmvar_t *vWidth = getCaliVariable();
|
||||
vmvar_t *vHeight = getCaliVariable();
|
||||
|
||||
scg_querysize(wNumCG, vWidth, vHeight);
|
||||
|
||||
@@ -794,7 +802,7 @@ static void CG_QuerySize() {
|
||||
*/
|
||||
static void CG_QueryBpp() {
|
||||
int wNumCG = getCaliValue();
|
||||
int *vBpp = getCaliVariable();
|
||||
vmvar_t *vBpp = getCaliVariable();
|
||||
|
||||
*vBpp = scg_querybpp(wNumCG);
|
||||
|
||||
@@ -809,7 +817,7 @@ static void CG_QueryBpp() {
|
||||
*/
|
||||
static void CG_ExistAlphaMap() {
|
||||
int wNumCG = getCaliValue();
|
||||
int *vMask = getCaliVariable();
|
||||
vmvar_t *vMask = getCaliVariable();
|
||||
|
||||
*vMask = scg_existalphamap(wNumCG) ? 1 : 0;
|
||||
|
||||
@@ -1021,7 +1029,7 @@ static void CG_PartCopy() {
|
||||
* @param vKey: 入力されたキー
|
||||
*/
|
||||
static void WaitKeySimple() {
|
||||
int *vKey = getCaliVariable();
|
||||
vmvar_t *vKey = getCaliVariable();
|
||||
|
||||
TRACE("SACT.WaitKeySimple %d:", vKey);
|
||||
|
||||
@@ -1071,10 +1079,10 @@ static void WaitKeyMessage() {
|
||||
* @param vRsv2: 予約
|
||||
*/
|
||||
static void WaitKeySprite() {
|
||||
int *vOK = getCaliVariable();
|
||||
int *vRND = getCaliVariable();
|
||||
int *vRsv1 = getCaliVariable();
|
||||
int *vRsv2 = getCaliVariable();
|
||||
vmvar_t *vOK = getCaliVariable();
|
||||
vmvar_t *vRND = getCaliVariable();
|
||||
vmvar_t *vRsv1 = getCaliVariable();
|
||||
vmvar_t *vRsv2 = getCaliVariable();
|
||||
|
||||
TRACE("SACT.WaitKeySprite %p,%p,%p,%p:", vOK, vRND, vRsv1, vRsv2);
|
||||
|
||||
@@ -1092,7 +1100,7 @@ static void WaitKeySprite() {
|
||||
static void PeekKey() {
|
||||
static int prevKeyCode = NUM_KEYCODES;
|
||||
int nKeyCode = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
|
||||
// This function is called successively with different nKeyCodes.
|
||||
// Only the first call hits the scheduler.
|
||||
@@ -1121,8 +1129,8 @@ static void WaitMsgSkipKeyUp() {
|
||||
* @param wTime: タイムアウト時間 (1/100sec)
|
||||
*/
|
||||
static void WaitKeySimpleTimeOut() {
|
||||
int *vRND = getCaliVariable();
|
||||
int *vD03 = getCaliVariable();
|
||||
vmvar_t *vRND = getCaliVariable();
|
||||
vmvar_t *vD03 = getCaliVariable();
|
||||
int wTime = getCaliValue();
|
||||
|
||||
sact.waittype = KEYWAIT_SIMPLE;
|
||||
@@ -1153,11 +1161,11 @@ static void WaitKeySimpleTimeOut() {
|
||||
* @param wTime: タイムアウト時間 (1/100sec)
|
||||
*/
|
||||
static void WaitKeySpriteTimeOut() {
|
||||
int *vOK = getCaliVariable();
|
||||
int *vRND = getCaliVariable();
|
||||
int *vD01 = getCaliVariable();
|
||||
int *vD02 = getCaliVariable();
|
||||
int *vD03 = getCaliVariable();
|
||||
vmvar_t *vOK = getCaliVariable();
|
||||
vmvar_t *vRND = getCaliVariable();
|
||||
vmvar_t *vD01 = getCaliVariable();
|
||||
vmvar_t *vD02 = getCaliVariable();
|
||||
vmvar_t *vD03 = getCaliVariable();
|
||||
int wTime = getCaliValue();
|
||||
|
||||
sp_keywait(vOK, vRND, vD01, vD02, vD03, wTime);
|
||||
@@ -1171,7 +1179,7 @@ static void WaitKeySpriteTimeOut() {
|
||||
* @param vSkip:
|
||||
*/
|
||||
static void QueryMessageSkip() {
|
||||
int *vSkip = getCaliVariable();
|
||||
vmvar_t *vSkip = getCaliVariable();
|
||||
|
||||
*vSkip = msgskip_isSkipping() ? 1 : 0;
|
||||
|
||||
@@ -1219,7 +1227,7 @@ static void MessageOutput() {
|
||||
int wMessageSpeed = getCaliValue();
|
||||
int wMessageLineSpace = getCaliValue();
|
||||
int wMessageAlign = 0;
|
||||
int *vMessageLength = NULL;
|
||||
vmvar_t *vMessageLength = NULL;
|
||||
|
||||
if (sact.version >= 110) {
|
||||
wMessageAlign = getCaliValue();
|
||||
@@ -1266,7 +1274,7 @@ static void MessageOutputEx() {
|
||||
int wRubySize = getCaliValue();
|
||||
int wRubyFont = getCaliValue();
|
||||
int wRubyLineSpace = getCaliValue();
|
||||
int *vLength = NULL;
|
||||
vmvar_t *vLength = NULL;
|
||||
|
||||
if (sact.version >= 120) {
|
||||
vLength = getCaliVariable();
|
||||
@@ -1311,7 +1319,7 @@ static void MessageClear() {
|
||||
* @param wResult: 結果を返す変数
|
||||
*/
|
||||
static void MessageIsEmpty() {
|
||||
int *wResult = getCaliVariable();
|
||||
vmvar_t *wResult = getCaliVariable();
|
||||
|
||||
*wResult = smsg_is_empty() ? 1 : 0;
|
||||
|
||||
@@ -1325,7 +1333,7 @@ static void MessageIsEmpty() {
|
||||
* @param nTopStringNum: バッファを取得する文字列変数の最初
|
||||
*/
|
||||
static void MessagePeek() {
|
||||
int *vCount = getCaliVariable();
|
||||
vmvar_t *vCount = getCaliVariable();
|
||||
int nTopStringNum = getCaliValue();
|
||||
|
||||
*vCount = smsg_peek(nTopStringNum);
|
||||
@@ -1388,7 +1396,7 @@ static void MenuAdd() {
|
||||
* @param nAlign: 行そろえ (0:左, 1:中央, 2: 右) (1.1~)
|
||||
*/
|
||||
static void MenuOpen() {
|
||||
int *wMenuResult = getCaliVariable();
|
||||
vmvar_t *wMenuResult = getCaliVariable();
|
||||
int wNum = getCaliValue();
|
||||
int wChoiceSize = getCaliValue();
|
||||
int wMenuOutSpc = getCaliValue();
|
||||
@@ -1464,7 +1472,7 @@ static void Numeral_SetCG() {
|
||||
static void Numeral_GetCG() {
|
||||
int nNum = getCaliValue();
|
||||
int nIndex = getCaliValue();
|
||||
int *vCG = getCaliVariable();
|
||||
vmvar_t *vCG = getCaliVariable();
|
||||
|
||||
sp_num_getcg(nNum, nIndex, vCG);
|
||||
|
||||
@@ -1497,8 +1505,8 @@ static void Numeral_SetPos() {
|
||||
*/
|
||||
static void Numeral_GetPos() {
|
||||
int nNum = getCaliValue();
|
||||
int *vX = getCaliVariable();
|
||||
int *vY = getCaliVariable();
|
||||
vmvar_t *vX = getCaliVariable();
|
||||
vmvar_t *vY = getCaliVariable();
|
||||
|
||||
sp_num_getpos(nNum, vX, vY);
|
||||
|
||||
@@ -1528,7 +1536,7 @@ static void Numeral_SetSpan() {
|
||||
*/
|
||||
static void Numeral_GetSpan() {
|
||||
int nNum = getCaliValue();
|
||||
int *vSpan = getCaliVariable();
|
||||
vmvar_t *vSpan = getCaliVariable();
|
||||
|
||||
sp_num_getspan(nNum, vSpan);
|
||||
|
||||
@@ -1596,7 +1604,7 @@ static void TimerSet() {
|
||||
*/
|
||||
static void TimerGet() {
|
||||
int wTimerID = getCaliValue();
|
||||
int *vRND = getCaliVariable();
|
||||
vmvar_t *vRND = getCaliVariable();
|
||||
|
||||
*vRND = stimer_get(wTimerID);
|
||||
|
||||
@@ -1615,7 +1623,7 @@ static void TimerWait() {
|
||||
|
||||
int msec = (wCount - stimer_get(wTimerID)) * 10;
|
||||
if (msec > 0)
|
||||
sys_keywait(msec, KEYWAIT_CTRL_CANCELABLE);
|
||||
sys_keywait(msec, KEYWAIT_CTRL_CANCELABLE | KEYWAIT_SKIPPABLE);
|
||||
|
||||
TRACE("SACT.TimerWait %d,%d:", wTimerID, wCount);
|
||||
}
|
||||
@@ -1628,7 +1636,7 @@ static void TimerWait() {
|
||||
static void Wait() {
|
||||
int wCount = getCaliValue();
|
||||
|
||||
sys_keywait(wCount * 10, KEYWAIT_CTRL_CANCELABLE);
|
||||
sys_keywait(wCount * 10, KEYWAIT_CTRL_CANCELABLE | KEYWAIT_SKIPPABLE);
|
||||
|
||||
TRACE("SACT.Wait %d:", wCount);
|
||||
}
|
||||
@@ -1696,7 +1704,7 @@ static void SoundWait() {
|
||||
*/
|
||||
static void SoundWaitKey() {
|
||||
int wNum = getCaliValue();
|
||||
int *vKey = getCaliVariable();
|
||||
vmvar_t *vKey = getCaliVariable();
|
||||
|
||||
ssnd_waitkey(wNum, vKey);
|
||||
|
||||
@@ -1797,7 +1805,7 @@ static void SpriteSoundOB() {
|
||||
*/
|
||||
static void MusicCheck() {
|
||||
int wNum = getCaliValue();
|
||||
int *vRND = getCaliVariable();
|
||||
vmvar_t *vRND = getCaliVariable();
|
||||
|
||||
*vRND = ald_exists(DRIFILE_BGM, wNum - 1) ? 1 : 0;
|
||||
|
||||
@@ -1812,7 +1820,7 @@ static void MusicCheck() {
|
||||
*/
|
||||
static void MusicGetLength() {
|
||||
int wNum = getCaliValue();
|
||||
int *vRND = getCaliVariable();
|
||||
vmvar_t *vRND = getCaliVariable();
|
||||
|
||||
*vRND = musbgm_getlen(wNum);
|
||||
|
||||
@@ -1827,7 +1835,7 @@ static void MusicGetLength() {
|
||||
*/
|
||||
static void MusicGetPos() {
|
||||
int wNum = getCaliValue();
|
||||
int *vRND = getCaliVariable();
|
||||
vmvar_t *vRND = getCaliVariable();
|
||||
|
||||
*vRND = musbgm_getpos(wNum);
|
||||
|
||||
@@ -1845,8 +1853,9 @@ static void MusicPlay() {
|
||||
int wNum = getCaliValue();
|
||||
int wFadeTime = getCaliValue();
|
||||
int wVolume = getCaliValue();
|
||||
bgi_t *bgi = bgi_find(wNum);
|
||||
|
||||
musbgm_play(wNum, wFadeTime, wVolume, 0);
|
||||
musbgm_play(wNum, wFadeTime, wVolume, bgi ? bgi->loopno : 0);
|
||||
|
||||
TRACE("SACT.MusicPlay %d,%d,%d:", wNum, wFadeTime, wVolume);
|
||||
}
|
||||
@@ -1937,7 +1946,7 @@ static void MusicWaitPos() {
|
||||
*/
|
||||
static void SoundGetLinkNum() {
|
||||
int wNum = getCaliValue();
|
||||
int *vRND = getCaliVariable();
|
||||
vmvar_t *vRND = getCaliVariable();
|
||||
|
||||
*vRND = ssnd_getlinknum(wNum);
|
||||
|
||||
@@ -1957,7 +1966,7 @@ static void SoundGetLinkNum() {
|
||||
* pos = ((pos2-pos1) / (val2-val1)) * (val-val1) + pos1
|
||||
*/
|
||||
static void ChartPos() {
|
||||
int *pos = getCaliVariable();
|
||||
vmvar_t *pos = getCaliVariable();
|
||||
int pos1 = getCaliValue();
|
||||
int pos2 = getCaliValue();
|
||||
int val1 = getCaliValue();
|
||||
@@ -2002,7 +2011,7 @@ static void Maze_Create() {
|
||||
* SACT.Maze_Get (1.0~)
|
||||
*/
|
||||
static void Maze_Get() {
|
||||
int *p1 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
|
||||
@@ -2013,7 +2022,7 @@ static void Maze_Get() {
|
||||
* SACT.EncryptWORD (1.0~)
|
||||
*/
|
||||
static void EncryptWORD() {
|
||||
int *array = getCaliVariable();
|
||||
vmvar_t *array = getCaliVariable();
|
||||
int num = getCaliValue();
|
||||
int key = getCaliValue();
|
||||
|
||||
@@ -2026,7 +2035,7 @@ static void EncryptWORD() {
|
||||
* SACT.DecryptWORD (1.0~)
|
||||
*/
|
||||
static void DecryptWORD() {
|
||||
int *array = getCaliVariable();
|
||||
vmvar_t *array = getCaliVariable();
|
||||
int num = getCaliValue();
|
||||
int key = getCaliValue();
|
||||
|
||||
@@ -2092,7 +2101,7 @@ static void XMenuRegister() {
|
||||
*/
|
||||
static void XMenuGetNum() {
|
||||
int nRegiNum = getCaliValue();
|
||||
int *vMenuID = getCaliVariable();
|
||||
vmvar_t *vMenuID = getCaliVariable();
|
||||
|
||||
*vMenuID = spxm_getnum(nRegiNum);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "portab.h"
|
||||
|
||||
// グラフ用チャート作成
|
||||
void schart_pos(int *pos, int pos1, int pos2, int val1, int val2, int val) {
|
||||
void schart_pos(vmvar_t *pos, int pos1, int pos2, int val1, int val2, int val) {
|
||||
if (val1 == val2) {
|
||||
*pos = 0;
|
||||
} else {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#ifndef __SACTCHART_H__
|
||||
#define __SACTCHART_H__
|
||||
|
||||
void schart_pos(int *pos, int pos1, int pos2, int val1, int val2, int val);
|
||||
#include "portab.h"
|
||||
|
||||
void schart_pos(vmvar_t *pos, int pos1, int pos2, int val1, int val2, int val);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
適当でいいとおもう
|
||||
*/
|
||||
|
||||
void scryp_encrypt_word(int *array, int num, int key) {
|
||||
void scryp_encrypt_word(vmvar_t *array, int num, int key) {
|
||||
WARNING("NOT IMPLEMENTED");
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
#ifndef __SACTCRYPT_H__
|
||||
#define __SACTCRYPT_H__
|
||||
|
||||
void scryp_encrypt_word(int *array, int num, int key);
|
||||
#include "portab.h"
|
||||
|
||||
void scryp_encrypt_word(vmvar_t *array, int num, int key);
|
||||
void scryp_decrypt_word(int *array, int num, int key);
|
||||
void scryp_encrypt_str(int strno, int key);
|
||||
void scryp_decrypt_str(int strno, int key);
|
||||
|
||||
@@ -97,7 +97,11 @@ static void draw_log() {
|
||||
if (0 == strcmp(str, "\n")) {
|
||||
SDL_BlitSurface(hline, NULL, main_surface, &(SDL_Rect){0, y + FONTSIZE/2, main_surface->w, 3});
|
||||
} else {
|
||||
FontSpec font_spec = { .type = cur < 6 ? FONT_MINCHO : FONT_GOTHIC, .size = FONTSIZE };
|
||||
FontSpec font_spec = {
|
||||
.type = cur < 6 ? FONT_MINCHO : FONT_GOTHIC,
|
||||
.weight = cur < 6 ? FONT_WEIGHT_NORMAL : FONT_WEIGHT_BOLD,
|
||||
.size = FONTSIZE
|
||||
};
|
||||
gfx_drawString(0, y, str, 255, font_spec);
|
||||
}
|
||||
y += FONTSIZE;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "system.h"
|
||||
#include "nact.h"
|
||||
#include "input.h"
|
||||
#include "msgskip.h"
|
||||
#include "sactsound.h"
|
||||
#include "music.h"
|
||||
#include "sact.h"
|
||||
@@ -112,7 +113,7 @@ void ssnd_wait(int no) {
|
||||
}
|
||||
|
||||
// 指定の効果音が終了するか、キーが押されるまで待つ
|
||||
void ssnd_waitkey(int no, int *res) {
|
||||
void ssnd_waitkey(int no, vmvar_t *res) {
|
||||
int slot = slt_find(no);
|
||||
|
||||
if (slot == -1) {
|
||||
@@ -120,7 +121,7 @@ void ssnd_waitkey(int no, int *res) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sact.waitskiplv > 1) {
|
||||
if (sact.waitskiplv > 1 || msgskip_isSkipping()) {
|
||||
*res = SYS35KEY_RET;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
#ifndef __SACTSOUND_H__
|
||||
#define __SACTSOUND_H__
|
||||
|
||||
#include "portab.h"
|
||||
|
||||
void ssnd_init(void);
|
||||
void ssnd_play(int no);
|
||||
void ssnd_stop(int no, int fadetime);
|
||||
void ssnd_wait(int no);
|
||||
void ssnd_waitkey(int no, int *res);
|
||||
void ssnd_waitkey(int no, vmvar_t *res);
|
||||
void ssnd_prepare(int no);
|
||||
void ssnd_prepareLRrev(int no);
|
||||
void ssnd_playLRrev(int no);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "sacttimer.h"
|
||||
#include "system.h"
|
||||
|
||||
#define MAX_TIMER 10
|
||||
#define MAX_TIMER 128
|
||||
#define TICKS_PER_CENTISECOND 10
|
||||
|
||||
uint32_t ticks_base[MAX_TIMER];
|
||||
|
||||
@@ -455,7 +455,7 @@ bool sp_exists(int wNum) {
|
||||
}
|
||||
|
||||
// スプライトのタイプと何番のCGがセットされているかの取得
|
||||
bool sp_query_info(int wNum, int *vtype, int *vcg1, int *vcg2, int *vcg3) {
|
||||
bool sp_query_info(int wNum, vmvar_t *vtype, vmvar_t *vcg1, vmvar_t *vcg2, vmvar_t *vcg3) {
|
||||
sprite_t *sp;
|
||||
|
||||
if (wNum >= SPRITEMAX) goto errexit;
|
||||
@@ -479,7 +479,7 @@ bool sp_query_info(int wNum, int *vtype, int *vcg1, int *vcg2, int *vcg3) {
|
||||
}
|
||||
|
||||
// スプライトの表示状態の取得
|
||||
bool sp_query_show(int wNum, int *vShow) {
|
||||
bool sp_query_show(int wNum, vmvar_t *vShow) {
|
||||
if (wNum >= SPRITEMAX) goto errexit;
|
||||
if (sact.sp[wNum]->type == SPRITE_NONE) goto errexit;
|
||||
|
||||
@@ -492,7 +492,7 @@ bool sp_query_show(int wNum, int *vShow) {
|
||||
}
|
||||
|
||||
// スプライトの表示位置の取得
|
||||
bool sp_query_pos(int wNum, int *vx, int *vy) {
|
||||
bool sp_query_pos(int wNum, vmvar_t *vx, vmvar_t *vy) {
|
||||
if (wNum >= SPRITEMAX) goto errexit;
|
||||
if (sact.sp[wNum]->type == SPRITE_NONE) goto errexit;
|
||||
|
||||
@@ -507,7 +507,7 @@ bool sp_query_pos(int wNum, int *vx, int *vy) {
|
||||
}
|
||||
|
||||
// スプライトの大きさの取得
|
||||
bool sp_query_size(int wNum, int *vw, int *vh) {
|
||||
bool sp_query_size(int wNum, vmvar_t *vw, vmvar_t *vh) {
|
||||
sprite_t *sp;
|
||||
|
||||
if (wNum >= SPRITEMAX) goto errexit;
|
||||
@@ -528,7 +528,7 @@ bool sp_query_size(int wNum, int *vw, int *vh) {
|
||||
}
|
||||
|
||||
// テキストスプライトの現在の文字表示位置の取得
|
||||
bool sp_query_textpos(int wNum, int *vx, int *vy) {
|
||||
bool sp_query_textpos(int wNum, vmvar_t *vx, vmvar_t *vy) {
|
||||
if (wNum >= SPRITEMAX) goto errexit;
|
||||
sprite_t *sp = sact.sp[wNum];
|
||||
if (sp->type != SPRITE_MSG) goto errexit;
|
||||
@@ -551,7 +551,7 @@ void sp_num_setcg(int nNum, int nIndex, int nCG) {
|
||||
}
|
||||
|
||||
// NumeralXXXのCGの取得
|
||||
void sp_num_getcg(int nNum, int nIndex, int *vCG) {
|
||||
void sp_num_getcg(int nNum, int nIndex, vmvar_t *vCG) {
|
||||
SP_ASSERT_NO(nNum);
|
||||
|
||||
*vCG = sact.sp[nNum]->numeral.cg[nIndex];
|
||||
@@ -566,7 +566,7 @@ void sp_num_setpos(int nNum, int nX, int nY) {
|
||||
}
|
||||
|
||||
// NumeralXXXの位置の取得
|
||||
void sp_num_getpos(int nNum, int *vX, int *vY) {
|
||||
void sp_num_getpos(int nNum, vmvar_t *vX, vmvar_t *vY) {
|
||||
SP_ASSERT_NO(nNum);
|
||||
|
||||
*vX = sact.sp[nNum]->numeral.pos.x;
|
||||
@@ -581,7 +581,7 @@ void sp_num_setspan(int nNum, int nSpan) {
|
||||
}
|
||||
|
||||
// NumeralXXXのスパンの取得
|
||||
void sp_num_getspan(int nNum, int *vSpan) {
|
||||
void sp_num_getspan(int nNum, vmvar_t *vSpan) {
|
||||
SP_ASSERT_NO(nNum);
|
||||
|
||||
*vSpan = sact.sp[nNum]->numeral.span;
|
||||
|
||||
+11
-10
@@ -24,6 +24,7 @@
|
||||
#ifndef __SPRITE_H__
|
||||
#define __SPRITE_H__
|
||||
|
||||
#include "portab.h"
|
||||
#include "sact.h"
|
||||
|
||||
#define DEFAULT_UPDATE sp_draw
|
||||
@@ -51,17 +52,17 @@ void sp_set_animeinterval(int wNum, int wTime);
|
||||
bool sp_is_insprite(sprite_t *sp, int x, int y);
|
||||
void sp_set_blendrate(int wNum, int wCount, int rate);
|
||||
bool sp_exists(int wNum);
|
||||
bool sp_query_info(int wNum, int *vtype, int *vcg1, int *vcg2, int *vcg3);
|
||||
bool sp_query_show(int wNum, int *vShow);
|
||||
bool sp_query_pos(int wNum, int *vx, int *vy);
|
||||
bool sp_query_size(int wNum, int *vw, int *vh);
|
||||
bool sp_query_textpos(int wNum, int *vx, int *vy);
|
||||
bool sp_query_info(int wNum, vmvar_t *vtype, vmvar_t *vcg1, vmvar_t *vcg2, vmvar_t *vcg3);
|
||||
bool sp_query_show(int wNum, vmvar_t *vShow);
|
||||
bool sp_query_pos(int wNum, vmvar_t *vx, vmvar_t *vy);
|
||||
bool sp_query_size(int wNum, vmvar_t *vw, vmvar_t *vh);
|
||||
bool sp_query_textpos(int wNum, vmvar_t *vx, vmvar_t *vy);
|
||||
void sp_num_setcg(int nNum, int nIndex, int nCG);
|
||||
void sp_num_getcg(int nNum, int nIndex, int *vCG);
|
||||
void sp_num_getcg(int nNum, int nIndex, vmvar_t *vCG);
|
||||
void sp_num_setpos(int nNum, int nX, int nY);
|
||||
void sp_num_getpos(int nNum, int *vX, int *vY);
|
||||
void sp_num_getpos(int nNum, vmvar_t *vX, vmvar_t *vY);
|
||||
void sp_num_setspan(int nNum, int nSpan);
|
||||
void sp_num_getspan(int nNUm, int *vSpan);
|
||||
void sp_num_getspan(int nNUm, vmvar_t *vSpan);
|
||||
void sp_exp_clear(void);
|
||||
void sp_exp_add(int nNumSP1, int nNumSP2);
|
||||
void sp_exp_del(int wNum);
|
||||
@@ -85,7 +86,7 @@ void sp_draw_dmap(void* data, void* userdata);
|
||||
// in sprite_msg.c
|
||||
void smsg_add(const char *msg);
|
||||
void smsg_newline(int wNum, int size);
|
||||
void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wFont, int wSpeed, int wLineSpace, int wAlign, int wRSize, int wRFont, int wRLineSpace, int *wLength);
|
||||
void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wFont, int wSpeed, int wLineSpace, int wAlign, int wRSize, int wRFont, int wRLineSpace, vmvar_t *wLength);
|
||||
void smsg_clear(int wNum);
|
||||
bool smsg_is_empty();
|
||||
int smsg_peek(int nTopStringNum);
|
||||
@@ -133,7 +134,7 @@ void sp_eupdate(int type, int time, int key);
|
||||
void sp_quake_sprite(int wType, int wAmplitudeX, int wAmplitude, int wCount, int cancel);
|
||||
|
||||
// in sprite_keywait.c
|
||||
void sp_keywait(int *vOK, int *vRND, int *vRsv1, int *vRsv2, int *vRsv3, int timeout);
|
||||
void sp_keywait(vmvar_t *vOK, vmvar_t *vRND, vmvar_t *vRsv1, vmvar_t *vRsv2, vmvar_t *vRsv3, int timeout);
|
||||
|
||||
// in screen_quake.c
|
||||
void sp_quake_screen(int type, int p1, int p2, int time, int cancel);
|
||||
|
||||
@@ -39,11 +39,6 @@
|
||||
@param cancel: キー抜け(0:なし, 1:あり)
|
||||
*/
|
||||
void sp_eupdate(int type, int time, int cancel) {
|
||||
if (sact.waitskiplv > 1) {
|
||||
sp_update_all(true);
|
||||
return;
|
||||
}
|
||||
|
||||
sp_update_all(false);
|
||||
|
||||
enum effect_type effect = from_sact_effect(type);
|
||||
|
||||
@@ -426,11 +426,6 @@ static void cb_waitkey_backlog(agsevent_t *e) {
|
||||
X|SDL のイベントディスパッチャからくる最初の場所
|
||||
*/
|
||||
void spev_callback(agsevent_t *e) {
|
||||
// menu open中は無視
|
||||
if (nact->popupmenu_opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sact.waittype != KEYWAIT_BACKLOG) {
|
||||
if (e->type == AGSEVENT_KEY_PRESS && e->code == KEY_CTRL) {
|
||||
sact.waitskiplv = 2;
|
||||
|
||||
@@ -113,7 +113,7 @@ static bool waitcond(int endtime) {
|
||||
@param vD03: タイムアウトした場合=1, しない場合=0
|
||||
@param wTime: タイムアウト時間 (1/100sec)
|
||||
*/
|
||||
void sp_keywait(int *vOK, int *vRND, int *vD01, int *vD02, int *vD03, int timeout) {
|
||||
void sp_keywait(vmvar_t *vOK, vmvar_t *vRND, vmvar_t *vD01, vmvar_t *vD02, vmvar_t *vD03, int timeout) {
|
||||
int curtime, endtime;
|
||||
|
||||
// とりあえず全更新
|
||||
@@ -171,4 +171,3 @@ void sp_keywait(int *vOK, int *vRND, int *vD01, int *vD02, int *vD03, int timeou
|
||||
sact.waittype = KEYWAIT_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,9 +30,11 @@
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
#include "ags.h"
|
||||
#include "gfx.h"
|
||||
#include "nact.h"
|
||||
#include "variable.h"
|
||||
#include "input.h"
|
||||
#include "msgskip.h"
|
||||
#include "sact.h"
|
||||
#include "sprite.h"
|
||||
#include "drawtext.h"
|
||||
@@ -236,11 +238,12 @@ void smsg_newline(int wNum, int size) {
|
||||
@param wRLineSpace: ルビと本文の文字間隔
|
||||
@param vLength: ???
|
||||
*/
|
||||
void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wFont, int wSpeed, int wLineSpace, int wAlign, int wRSize, int wRFont, int wRLineSpace, int *wLength) {
|
||||
void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wFont, int wSpeed, int wLineSpace, int wAlign, int wRSize, int wRFont, int wRLineSpace, vmvar_t *wLength) {
|
||||
char *msg;
|
||||
sprite_t *sp;
|
||||
int len = 0; // 処理した文字数?
|
||||
bool needupdate = false;
|
||||
bool has_batched_output = false;
|
||||
bool flush_batched_output = wSpeed > 0;
|
||||
SDL_Rect uparea = {0,0,0,0};
|
||||
|
||||
// wRSize == 0 -> ルビ無し(SACT.MessageOutputからの呼出)
|
||||
@@ -250,7 +253,7 @@ void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wF
|
||||
if (!is_messagesprite(wNum)) return;
|
||||
|
||||
// MessageSkip中は文字送り速度を最大に
|
||||
if (sact.waitskiplv > 1) wSpeed = 0;
|
||||
if (sact.waitskiplv > 1 || msgskip_isSkipping()) wSpeed = 0;
|
||||
|
||||
// shortcut
|
||||
sp = sact.sp[wNum];
|
||||
@@ -302,7 +305,7 @@ void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wF
|
||||
mbuf,
|
||||
wColorR, wColorG, wColorB);
|
||||
|
||||
needupdate = true;
|
||||
has_batched_output = true;
|
||||
|
||||
append_to_log(mbuf);
|
||||
|
||||
@@ -313,7 +316,7 @@ void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wF
|
||||
cw,
|
||||
wSize + wRSize + wRLineSpace);
|
||||
sp_update_clipped();
|
||||
needupdate = false;
|
||||
has_batched_output = false;
|
||||
|
||||
// keywait
|
||||
delta = sys_get_ticks() - wcnt;
|
||||
@@ -333,11 +336,17 @@ void smsg_out(int wNum, int wSize, int wColorR, int wColorG, int wColorB, int wF
|
||||
// バッファリング中の文字のクリア
|
||||
sact.msgbuf[0] = '\0';
|
||||
|
||||
// Waitなしの出力は最後にupdate
|
||||
if (needupdate) {
|
||||
// Register the update area after rendering without waits.
|
||||
if (has_batched_output) {
|
||||
uparea.w = sp->width;
|
||||
uparea.h = min(sp->height, uparea.y - sp->u.msg.dspcur.y + wLineSpace + wLineSpace + wRSize);
|
||||
uparea.h = min(sp->height - uparea.y,
|
||||
sp->u.msg.dspcur.y - uparea.y + wSize + wRSize + wRLineSpace);
|
||||
sp_updateme_part(sp, uparea.x, uparea.y, uparea.w, uparea.h);
|
||||
// Present now if waiting was skipped.
|
||||
if (flush_batched_output) {
|
||||
sp_update_clipped();
|
||||
gfx_updateScreen();
|
||||
}
|
||||
}
|
||||
|
||||
// ????
|
||||
@@ -408,7 +417,7 @@ int smsg_keywait(int wNum1, int wNum2, int msglen) {
|
||||
struct markinfo minfo[6];
|
||||
int i = 0, j, maxstep;
|
||||
|
||||
if (sact.waitskiplv > 0) {
|
||||
if (sact.waitskiplv > 0 || msgskip_isSkipping()) {
|
||||
sys_getInputInfo();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -117,11 +117,4 @@ void spev_main() {
|
||||
|
||||
e.type = AGSEVENT_TIMER;
|
||||
tevent_callback(&e);
|
||||
|
||||
// デフォルトのコールバックのうち、ここで必要なものだけ
|
||||
// 処理。(VAコマンドcallbackはなし)
|
||||
if (nact->popupmenu_opened) {
|
||||
menu_gtkmainiteration();
|
||||
if (nact->is_quit) sys_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
+83
-83
@@ -43,10 +43,10 @@ static void GetAtArray(void) { /* 0 */
|
||||
type: 演算の種類
|
||||
vResult: 演算結果を返す変数
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int type = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i, j;
|
||||
|
||||
TRACE("ShArray.GetAtArray %p,%d,%d,%p:", vAry, cnt, type, vResult);
|
||||
@@ -88,8 +88,8 @@ static void AddAtArray(void) { /* 1 */
|
||||
vAry2: 配列2
|
||||
cnt : 個数
|
||||
*/
|
||||
int *vAry1 = getCaliVariable();
|
||||
int *vAry2 = getCaliVariable();
|
||||
vmvar_t *vAry1 = getCaliVariable();
|
||||
vmvar_t *vAry2 = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int i;
|
||||
|
||||
@@ -114,8 +114,8 @@ static void SubAtArray(void) { /* 2 */
|
||||
vAry2: 配列2
|
||||
cnt : 個数
|
||||
*/
|
||||
int *vAry1 = getCaliVariable();
|
||||
int *vAry2 = getCaliVariable();
|
||||
vmvar_t *vAry1 = getCaliVariable();
|
||||
vmvar_t *vAry2 = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int i;
|
||||
|
||||
@@ -140,8 +140,8 @@ static void MulAtArray(void) { /* 3 */
|
||||
vAry2: 配列2
|
||||
cnt : 個数
|
||||
*/
|
||||
int *vAry1 = getCaliVariable();
|
||||
int *vAry2 = getCaliVariable();
|
||||
vmvar_t *vAry1 = getCaliVariable();
|
||||
vmvar_t *vAry2 = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int i;
|
||||
|
||||
@@ -166,8 +166,8 @@ static void DivAtArray(void) { /* 4 */
|
||||
vAry2: 配列2
|
||||
cnt : 個数
|
||||
*/
|
||||
int *vAry1 = getCaliVariable();
|
||||
int *vAry2 = getCaliVariable();
|
||||
vmvar_t *vAry1 = getCaliVariable();
|
||||
vmvar_t *vAry2 = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int i;
|
||||
|
||||
@@ -196,8 +196,8 @@ static void MinAtArray(void) { /* 5 */
|
||||
vAry2: 配列2
|
||||
cnt : 個数
|
||||
*/
|
||||
int *vAry1 = getCaliVariable();
|
||||
int *vAry2 = getCaliVariable();
|
||||
vmvar_t *vAry1 = getCaliVariable();
|
||||
vmvar_t *vAry2 = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int i;
|
||||
|
||||
@@ -219,8 +219,8 @@ static void MaxAtArray(void) { /* 6 */
|
||||
vAry2: 配列2
|
||||
cnt : 個数
|
||||
*/
|
||||
int *vAry1 = getCaliVariable();
|
||||
int *vAry2 = getCaliVariable();
|
||||
vmvar_t *vAry1 = getCaliVariable();
|
||||
vmvar_t *vAry2 = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int i;
|
||||
|
||||
@@ -242,7 +242,7 @@ static void AndNumArray(void) { /* 7 */
|
||||
cnt : 個数
|
||||
val : ANDをとる値
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int i;
|
||||
@@ -263,7 +263,7 @@ static void OrNumArray(void) { /* 8 */
|
||||
cnt : 個数
|
||||
val : ORをとる値
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int i;
|
||||
@@ -284,7 +284,7 @@ static void XorNumArray(void) { /* 9 */
|
||||
cnt : 個数
|
||||
val : XORをとる値
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int i;
|
||||
@@ -307,10 +307,10 @@ static void SetEquArray(void) { /* 10 */
|
||||
val : 比較する値
|
||||
vResults : 結果を格納する配列
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.SetEquArray %p,%d,%d,%p:", vAry, cnt, val, vResults);
|
||||
@@ -331,10 +331,10 @@ static void SetNotArray(void) { /* 11 */
|
||||
val : 比較する値
|
||||
vResults : 結果を格納する配列
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.SetNotArray %p,%d,%d,%p:", vAry, cnt, val, vResults);
|
||||
@@ -354,10 +354,10 @@ static void SetLowArray(void) { /* 12 */
|
||||
val : 閾値
|
||||
vResult: 結果を返す変数
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.SetLowArray %p,%d,%d,%p:", vAry, cnt, val, vResults);
|
||||
@@ -377,10 +377,10 @@ static void SetHighArray(void) { /* 13 */
|
||||
val : 閾値
|
||||
vResults: 結果を返す変数
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.SetHighArray %p,%d,%d,%p:", vAry, cnt, val, vResults);
|
||||
@@ -403,11 +403,11 @@ static void SetRangeArray(void) { /* 14 */
|
||||
min < vAry < max の時 vResults = 1;
|
||||
それ以外 vResults = 0;
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.SetRangeArray %p,%d,%d,%d,%p:", vAry, cnt, min, max, vResults);
|
||||
@@ -430,11 +430,11 @@ static void SetAndEquArray(void) { /* 15 */
|
||||
vResults : 結果を代入する配列
|
||||
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int mask = getCaliValue();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.SetAndEquArray: %p,%d,%d,%d,%p:", vAry, mask, cnt, val, vResults);
|
||||
@@ -455,10 +455,10 @@ static void AndEquArray(void) { /* 16 */
|
||||
val : 比較する値
|
||||
vResults : 結果を代入する配列
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.AndEquArray %p,%d,%d,%p:", vAry, cnt, val, vResults);
|
||||
@@ -479,10 +479,10 @@ static void AndNotArray(void) { /* 17 */
|
||||
val : 比較する値
|
||||
vResults : 結果を代入する配列
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.AndNotArray %p,%d,%d,%p:", vAry, cnt, val, vResults);
|
||||
@@ -503,10 +503,10 @@ static void AndLowArray(void) { /* 18 */
|
||||
min : 最小値
|
||||
vResults : 結果を代入する配列
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.AndLowArray: %d,%d,%d,%d:", vAry, cnt, min, vResults);
|
||||
@@ -527,10 +527,10 @@ static void AndHighArray(void) { /* 19 */
|
||||
max : 最小値
|
||||
vResults : 結果を代入する配列
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.AndHighArray: %p,%d,%d,%p:", vAry, cnt, max, vResults);
|
||||
@@ -552,11 +552,11 @@ static void AndRangeArray(void) { /* 20 */
|
||||
max: 最大値
|
||||
vResults: 結果を返す配列
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.AndRangeArray %d,%d,%d,%d,%d:", vAry, cnt, min, max, vResults);
|
||||
@@ -579,11 +579,11 @@ static void AndAndEquArray(void) { /* 21 */
|
||||
vResults : 結果を代入する配列
|
||||
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int mask = getCaliValue();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.AndAndEquArray: %d,%d,%d,%d,%d:", vAry, mask, cnt, val, vResults);
|
||||
@@ -612,10 +612,10 @@ static void OrNotArray(void) { /* 23 */
|
||||
val : 比較する値
|
||||
vResults: 結果を書き込む変数
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResults = getCaliVariable();
|
||||
vmvar_t *vResults = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.OrNotArray %p,%d,%d,%p:", vAry, cnt, val, vResults);
|
||||
@@ -674,10 +674,10 @@ static void EnumEquArray(void) { /* 28 */
|
||||
val : 比較する値
|
||||
vResult: 一致する個数を返す変数
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.EnumEquArray %p,%d,%d,%p:", vAry, cnt, val, vResult);
|
||||
@@ -713,12 +713,12 @@ static void EnumEquNotArray2(void) { /* 30 */
|
||||
val2: 配列2と比較する値
|
||||
vResult: 条件に一致する数を返す変数
|
||||
*/
|
||||
int *vAry1 = getCaliVariable();
|
||||
int *vAry2 = getCaliVariable();
|
||||
vmvar_t *vAry1 = getCaliVariable();
|
||||
vmvar_t *vAry2 = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val1 = getCaliValue();
|
||||
int val2 = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.EnumEquNotArray2 %p,%p,%d,%d,%d,%p:", vAry1, vAry2, cnt, val1, val2, vResult);
|
||||
@@ -742,10 +742,10 @@ static void EnumNotArray(void) { /* 31 */
|
||||
val: 比較する値
|
||||
vResult: 等しくないものの数
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.EnumNotArray %p, %d, %d, %p:", vAry, cnt, val, vResult);
|
||||
@@ -799,11 +799,11 @@ static void EnumRangeArray(void) { /* 35 */
|
||||
max : 最大値
|
||||
vResult: 一致した数を返す変数
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.EnumRangeArray %d,%d,%d,%d,%d:", vAry, cnt, min, max, vResult);
|
||||
@@ -830,11 +830,11 @@ static void GrepEquArray(void) { /* 36 */
|
||||
vMatch: 一致したインデックス
|
||||
vResult: 一つでも val と同じ値があれば 1
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vMatch = getCaliVariable();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vMatch = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.GrepEquArray %p,%d,%d,%p,%p:", vAry, cnt, val, vMatch, vResult);
|
||||
@@ -862,11 +862,11 @@ static void GrepNotArray(void) { /* 37 */
|
||||
vMatch: 一致するindex
|
||||
vResult: 一つでも val と同じ値があれば 1
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int val = getCaliValue();
|
||||
int *vMatch = getCaliVariable();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vMatch = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.GrepNotArray %p,%d,%d,%p,%p:", vAry, cnt, val, vMatch, vResult);
|
||||
@@ -930,11 +930,11 @@ static void GrepLowArray(void) { /* 41 */
|
||||
vMatch: 一致したインデックス
|
||||
vResult: 一つでも val と同じ値があれば 1
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int *vMatch = getCaliVariable();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vMatch = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.GrepLowArray: %p,%d,%d,%p,%p:", vAry, cnt, min, vMatch, vResult);
|
||||
@@ -962,11 +962,11 @@ static void GrepHighArray(void) { /* 42 */
|
||||
vMatch: 一致したインデックス
|
||||
vResult: 一つでも val と同じ値があれば 1
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
int *vMatch = getCaliVariable();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vMatch = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.GrepHighArray: %p,%d,%d,%p,%p:", vAry, cnt, max, vMatch, vResult);
|
||||
@@ -995,12 +995,12 @@ static void GrepRangeArray(void) { /* 43 */
|
||||
vMatch: 一致したインデックス
|
||||
vResult: 一つでも val と同じ値があれば 1
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
int *vMatch = getCaliVariable();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vMatch = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShArray.GrepRangeArray %p,%d,%d,%d,%p,%p:", vAry, cnt, max, min, vMatch, vResult);
|
||||
@@ -1031,13 +1031,13 @@ static void GrepLowOrderArray(void) { /* 44 */
|
||||
vLastMatch: 最小値を示す配列のindex
|
||||
vResult: 最小値が見つかれば 1, 見つからなければ 0
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
int *v1 = getCaliVariable();
|
||||
int *vLastMatch = getCaliVariable();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *v1 = getCaliVariable();
|
||||
vmvar_t *vLastMatch = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i, j, k = 0;
|
||||
|
||||
TRACE("ShArray.GrepLowOrderArray %p,%d,%d,%d,%p,%p,%p:", vAry, cnt, min, max, v1, vLastMatch, vResult);
|
||||
@@ -1082,13 +1082,13 @@ static void GrepHighOrderArray(void) { /* 45 */
|
||||
vLastMatch: 最大値を示す配列のindex
|
||||
vResult: 最大値が見つかれば 1, 見つからなければ 0
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
int *v1 = getCaliVariable();
|
||||
int *vLastMatch = getCaliVariable();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *v1 = getCaliVariable();
|
||||
vmvar_t *vLastMatch = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i, j, k = 0;
|
||||
|
||||
TRACE("ShArray.GrepHighOrderArray %p,%d,%d,%d,%p,%p,%p:", vAry, cnt, min, max, v1, vLastMatch, vResult);
|
||||
@@ -1119,7 +1119,7 @@ static void GrepHighOrderArray(void) { /* 45 */
|
||||
}
|
||||
|
||||
static void ChangeEquArray(void) { /* 46 */
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int src = getCaliValue();
|
||||
int dst = getCaliValue();
|
||||
@@ -1172,7 +1172,7 @@ static void ChangeRangeArray(void) { /* 50 */
|
||||
max : 最大値
|
||||
val : 置き換える値
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int min = getCaliValue();
|
||||
int max = getCaliValue();
|
||||
@@ -1202,12 +1202,12 @@ static void CopyArrayToRect(void) { /* 51 */
|
||||
dw : コピー先 width
|
||||
dh : コピー先 height
|
||||
*/
|
||||
int *vSrc = getCaliVariable();
|
||||
vmvar_t *vSrc = getCaliVariable();
|
||||
int sw = getCaliValue();
|
||||
int sh = getCaliValue();
|
||||
int sx = getCaliValue();
|
||||
int sy = getCaliValue();
|
||||
int *vDst = getCaliVariable();
|
||||
vmvar_t *vDst = getCaliVariable();
|
||||
int dw = getCaliValue();
|
||||
int dh = getCaliValue();
|
||||
int x, y;
|
||||
@@ -1236,10 +1236,10 @@ static void CopyRectToArray(void) { /* 52 */
|
||||
dx : コピー先 x
|
||||
dy : コピー先 y
|
||||
*/
|
||||
int *vSrc = getCaliVariable();
|
||||
vmvar_t *vSrc = getCaliVariable();
|
||||
int sw = getCaliValue();
|
||||
int sh = getCaliValue();
|
||||
int *vDst = getCaliVariable();
|
||||
vmvar_t *vDst = getCaliVariable();
|
||||
int dw = getCaliValue();
|
||||
int dh = getCaliValue();
|
||||
int dx = getCaliValue();
|
||||
@@ -1266,10 +1266,10 @@ static void ChangeSecretArray(void) { /* 53 */
|
||||
type: 機能番号
|
||||
vResult: 結果を返す変数
|
||||
*/
|
||||
int *vAry = getCaliVariable();
|
||||
vmvar_t *vAry = getCaliVariable();
|
||||
int cnt = getCaliValue();
|
||||
int type = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
static uint16_t key[4] = { 0x7A7A, 0xADAD, 0xBCBC, 0xCECE }; /* key */
|
||||
|
||||
TRACE("ShArray.ChangeSecretArray %p,%d,%d,%p:", vAry, cnt, type, vResult);
|
||||
|
||||
+19
-19
@@ -50,7 +50,7 @@ static int64_t div64(int64_t a1, int64_t a2) {
|
||||
return a1 / a2;
|
||||
}
|
||||
|
||||
static int64_t get32(int *var) {
|
||||
static int64_t get32(vmvar_t *var) {
|
||||
return var[0] + mul64(var[1], 0x10000);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ static void SetIntNum16(void) { /* 1 */
|
||||
|
||||
var: 数値の入った変数
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
accumulator = mul64(*var, numbase);
|
||||
|
||||
@@ -82,7 +82,7 @@ static void SetIntNum16(void) { /* 1 */
|
||||
}
|
||||
|
||||
static void SetIntNum32(void) { /* 2 */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
accumulator = mul64(get32(var), numbase);
|
||||
|
||||
@@ -95,7 +95,7 @@ static void GetIntNum16(void) { /* 3 */
|
||||
|
||||
var: 数値をいれる変数
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int64_t i;
|
||||
|
||||
i = div64(accumulator, numbase);
|
||||
@@ -110,7 +110,7 @@ static void GetIntNum16(void) { /* 3 */
|
||||
}
|
||||
|
||||
static void GetIntNum32(void) { /* 4 */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int64_t i = div64(accumulator, numbase);
|
||||
|
||||
var[0] = i & 0xFFFF;
|
||||
@@ -126,7 +126,7 @@ static void AddIntNum16(void) { /* 5 */
|
||||
|
||||
var: 足す数の入った変数
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
accumulator += mul64(*var, numbase);
|
||||
|
||||
@@ -134,7 +134,7 @@ static void AddIntNum16(void) { /* 5 */
|
||||
}
|
||||
|
||||
static void AddIntNum32(void) { /* 6 */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
accumulator += mul64(get32(var), numbase);
|
||||
|
||||
@@ -148,7 +148,7 @@ static void SubIntNum16(void) { /* 7 */
|
||||
}
|
||||
|
||||
static void SubIntNum32(void) { /* 8 */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
accumulator -= mul64(get32(var), numbase);
|
||||
|
||||
@@ -161,7 +161,7 @@ static void MulIntNum16(void) { /* 9 */
|
||||
|
||||
var: 掛ける数の入った変数
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
accumulator *= mul64(*var, numbase);
|
||||
|
||||
@@ -180,7 +180,7 @@ static void DivIntNum16(void) { /* 11 */
|
||||
|
||||
var: 足す数の入った変数
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int64_t i;
|
||||
|
||||
i = mul64(*var, numbase);
|
||||
@@ -204,9 +204,9 @@ static void CmpIntNum16(void) { /* 13 */
|
||||
}
|
||||
|
||||
static void CmpIntNum32(void) { /* 14 */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int op = getCaliValue();
|
||||
int *result = getCaliVariable();
|
||||
vmvar_t *result = getCaliVariable();
|
||||
|
||||
int64_t val = mul64(get32(var), numbase);
|
||||
|
||||
@@ -230,8 +230,8 @@ static void GetLengthNum16(void) { /* 15 */
|
||||
var: 数値
|
||||
vResult: 数値の桁数を返す変数
|
||||
*/
|
||||
int *var = getCaliVariable();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
|
||||
if (*var >= 10000) {
|
||||
*vResult = 5;
|
||||
@@ -270,7 +270,7 @@ static void NumToRate(void) { /* 17 */
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int flag = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
i = (p1 * p3) / p2;
|
||||
@@ -302,7 +302,7 @@ static void NumToRateNum(void) { /* 18 */
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int flag = getCaliValue();
|
||||
int *vResult = getCaliVariable();
|
||||
vmvar_t *vResult = getCaliVariable();
|
||||
int i;
|
||||
|
||||
i = (p1 * p2) / p3;
|
||||
@@ -333,7 +333,7 @@ static void SetRandomSeed() {
|
||||
|
||||
static void GetRandomNumA() {
|
||||
int num = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
if (num == 0 || num == 1) {
|
||||
*var = num;
|
||||
@@ -351,7 +351,7 @@ static void NumToBit() {
|
||||
var: 値を返す変数
|
||||
*/
|
||||
int beki = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int i, j = 1;
|
||||
|
||||
if (beki < 17) {
|
||||
@@ -379,7 +379,7 @@ static void BitToNum() {
|
||||
var: 値を返す変数
|
||||
*/
|
||||
int val = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int i;
|
||||
|
||||
TRACE("ShCalc.BitToNum %d,%p:", val, var);
|
||||
|
||||
+12
-12
@@ -59,8 +59,8 @@ struct animsrc {
|
||||
static struct animsrc src[SLOT]; /* アニメーション各コマ転送元 */
|
||||
|
||||
struct _s0 {
|
||||
int *dst_p1;
|
||||
int *dst_p2;
|
||||
vmvar_t *dst_p1;
|
||||
vmvar_t *dst_p2;
|
||||
int dw_1000A188;
|
||||
};
|
||||
static struct _s0 s0[SLOT];
|
||||
@@ -84,7 +84,7 @@ struct _s2 {
|
||||
};
|
||||
static struct _s2 s2[SLOT];
|
||||
|
||||
static int* add_p5[SLOT]; /* どこまでアニメーションのコマが進んだか */
|
||||
static vmvar_t *add_p5[SLOT]; /* どこまでアニメーションのコマが進んだか */
|
||||
|
||||
static void copy_sprite(int sx, int sy, int width, int height, int dx, int dy, int r, int g, int b) {
|
||||
SDL_Surface *sf = nact->ags.dib->sdl_surface;
|
||||
@@ -115,8 +115,8 @@ static void ChangeEquColor() {
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int p4 = getCaliValue();
|
||||
int *p5 = getCaliVariable();
|
||||
int *p6 = getCaliVariable();
|
||||
vmvar_t *p5 = getCaliVariable();
|
||||
vmvar_t *p6 = getCaliVariable();
|
||||
int p7 = getCaliValue(); /* ISurface */
|
||||
|
||||
TRACE_UNIMPLEMENTED("ShGraph.ChangeEquColor %d,%d,%d,%d,%p,%p,%d:", p1, p2, p3, p4, p5, p6, p7);
|
||||
@@ -137,8 +137,8 @@ static void ChangeNotColor() {
|
||||
int y0 = getCaliValue();
|
||||
int width = getCaliValue();
|
||||
int height = getCaliValue();
|
||||
int *src = getCaliVariable(); /* r, g, b */
|
||||
int *dst = getCaliVariable(); /* r, g, b */
|
||||
vmvar_t *src = getCaliVariable(); /* r, g, b */
|
||||
vmvar_t *dst = getCaliVariable(); /* r, g, b */
|
||||
int p7 = getCaliValue(); /* ISurface */
|
||||
surface_t *dib;
|
||||
int x, y;
|
||||
@@ -216,7 +216,7 @@ static void ResetAnimeData() {
|
||||
}
|
||||
|
||||
memset(s1, 0, sizeof(struct _s1) * SLOT);
|
||||
memset(add_p5, 0, sizeof(int *) * SLOT);
|
||||
memset(add_p5, 0, sizeof(vmvar_t *) * SLOT);
|
||||
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ static void SetAnimeSrc() {
|
||||
int h = getCaliValue();
|
||||
int uw = getCaliValue();
|
||||
int uh = getCaliValue();
|
||||
int *pal = getCaliVariable();
|
||||
vmvar_t *pal = getCaliVariable();
|
||||
int r, g, b;
|
||||
|
||||
TRACE("ShGraph.SetAnimeSrc %d,%d,%d,%d,%d,%d,%d,%p:", no, x0, y0, w, h, uw, uh, pal);
|
||||
@@ -276,8 +276,8 @@ static void SetAnimeDst() {
|
||||
p6: 描画先オフセット追加分 (h) 10000が中央
|
||||
*/
|
||||
int no = getCaliValue();
|
||||
int *p1 = getCaliVariable();
|
||||
int *p2 = getCaliVariable();
|
||||
vmvar_t *p1 = getCaliVariable();
|
||||
vmvar_t *p2 = getCaliVariable();
|
||||
int p3 = getCaliValue();
|
||||
int p4 = getCaliValue();
|
||||
int p5 = getCaliValue();
|
||||
@@ -313,7 +313,7 @@ static void AddAnimeData() {
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int p4 = getCaliValue();
|
||||
int *p5 = getCaliVariable();
|
||||
vmvar_t *p5 = getCaliVariable();
|
||||
int p6 = getCaliValue();
|
||||
int i;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "xsystem35.h"
|
||||
#include "modules.h"
|
||||
#include "input.h"
|
||||
#include "menu.h"
|
||||
#include "input_modal.h"
|
||||
|
||||
// キー変換テーブル
|
||||
#define KEYMAP_MAX 8
|
||||
@@ -47,7 +47,7 @@ static void OutputMessageBox(void) { /* 0 */
|
||||
int p2 = getCaliValue();
|
||||
int title = getCaliValue();
|
||||
int msg = getCaliValue();
|
||||
int *res = getCaliVariable();
|
||||
vmvar_t *res = getCaliVariable();
|
||||
int ISys3xSystem = getCaliValue();
|
||||
|
||||
char *title_utf8 = toUTF8(svar_get(title));
|
||||
@@ -64,10 +64,10 @@ static void OutputMessageBox(void) { /* 0 */
|
||||
static void InputListNum(void) { /* 1 */
|
||||
int flags = getCaliValue();
|
||||
int title = getCaliValue();
|
||||
int *val = getCaliVariable();
|
||||
vmvar_t *val = getCaliVariable();
|
||||
int minval = getCaliValue();
|
||||
int maxval = getCaliValue();
|
||||
int *res = getCaliVariable();
|
||||
vmvar_t *res = getCaliVariable();
|
||||
int ISys3xSystem = getCaliValue();
|
||||
|
||||
INPUTNUM_PARAM ni_param = {
|
||||
@@ -77,12 +77,11 @@ static void InputListNum(void) { /* 1 */
|
||||
.title = toUTF8(svar_get(title)),
|
||||
};
|
||||
|
||||
menu_inputnumber(&ni_param);
|
||||
if (ni_param.value < 0) {
|
||||
*res = 0;
|
||||
} else {
|
||||
if (input_modal_number(&ni_param)) {
|
||||
*val = (uint16_t)ni_param.value;
|
||||
*res = 1;
|
||||
} else {
|
||||
*res = 0;
|
||||
}
|
||||
|
||||
free(ni_param.title);
|
||||
@@ -152,7 +151,7 @@ static void SetKeyStatus(void) {
|
||||
*/
|
||||
static void GetKeyStatus(void) {
|
||||
int no = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
int i;
|
||||
|
||||
if (no >= KEYMAP_MAX) {
|
||||
|
||||
@@ -268,7 +268,7 @@ static void wavPlayRing() {
|
||||
*/
|
||||
int start = getCaliValue();
|
||||
int cnt = getCaliValue();
|
||||
int *cur = getCaliVariable();
|
||||
vmvar_t *cur = getCaliVariable();
|
||||
|
||||
mus_wav_play(start + (*cur % cnt), 1);
|
||||
*cur = (*cur + 1) % cnt;
|
||||
@@ -321,7 +321,7 @@ static void wavIsPlay() {
|
||||
*result: 0なら停止中、!0なら再生中
|
||||
*/
|
||||
int slot = getCaliValue();
|
||||
int *result = getCaliVariable();
|
||||
vmvar_t *result = getCaliVariable();
|
||||
|
||||
*result = mus_wav_get_playposition(slot);
|
||||
|
||||
@@ -338,7 +338,7 @@ static void wavIsPlayRange() {
|
||||
*/
|
||||
int slot = getCaliValue();
|
||||
int range = getCaliValue();
|
||||
int *result = getCaliVariable();
|
||||
vmvar_t *result = getCaliVariable();
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = slot; i < (slot + range); i++) {
|
||||
|
||||
@@ -127,7 +127,7 @@ static void SetStringNum16(void) {
|
||||
p2: 変換された数値を格納する変数
|
||||
*/
|
||||
int st = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
const char *str = svar_get(st);
|
||||
char _dst[100];
|
||||
char *dst = _dst;
|
||||
@@ -160,7 +160,7 @@ static void SetStringNum16(void) {
|
||||
|
||||
static void SetStringNum32(void) {
|
||||
int p1 = getCaliValue();
|
||||
int *p2 = getCaliVariable();
|
||||
vmvar_t *p2 = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("ShString.SetStringNum32: %d,%p:", p1, p2);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ static void Init() {
|
||||
int p1 = getCaliValue();
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
if (!nact->files.alk[0]) {
|
||||
WARNING("dDEMO.alk not found");
|
||||
|
||||
@@ -13,7 +13,7 @@ static void Init() {
|
||||
int p1 = getCaliValue();
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ static void Init() {
|
||||
int p1 = getCaliValue();
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
|
||||
@@ -31,8 +31,11 @@
|
||||
#include "ags.h"
|
||||
#include "font.h"
|
||||
|
||||
static FontType ftype;
|
||||
static int fsize; // フォントの大きさ
|
||||
static FontSpec dt_spec = {
|
||||
.type = FONT_GOTHIC,
|
||||
.weight = FONT_WEIGHT_BOLD,
|
||||
.size = 12
|
||||
};
|
||||
|
||||
/**
|
||||
* 次に描く文字のフォントの種類と大きさを設定
|
||||
@@ -48,8 +51,8 @@ void dt_setfont(FontType type, int size) {
|
||||
}
|
||||
#endif
|
||||
|
||||
ftype = type;
|
||||
fsize = size;
|
||||
dt_spec.type = type;
|
||||
dt_spec.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +69,7 @@ void dt_setfont(FontType type, int size) {
|
||||
* @return: 実際に描画した幅
|
||||
*/
|
||||
int dt_drawtext_col(SDL_Surface *sf, int x, int y, char *buf, int r, int g, int b) {
|
||||
SDL_Surface *glyph = ags_drawStringToSurface(buf, r, g, b, (FontSpec){ .type = ftype, .size = fsize });
|
||||
SDL_Surface *glyph = ags_drawStringToSurface(buf, r, g, b, dt_spec);
|
||||
if (glyph == NULL) return 0;
|
||||
|
||||
SDL_Rect rect = {x, y, glyph->w, glyph->h};
|
||||
|
||||
@@ -220,7 +220,8 @@ void scg_create_text(int wNumCG, int wSize, int wR, int wG, int wB, int wText) {
|
||||
// 勝手に出ていいのかな?
|
||||
if (svar_length(wText) == 0) return;
|
||||
|
||||
SDL_Surface *glyph = ags_drawStringToSurface(svar_get(wText), wR, wG, wB, (FontSpec){ .size = wSize });
|
||||
FontSpec spec = { .type = FONT_GOTHIC, .weight = FONT_WEIGHT_BOLD, .size = wSize };
|
||||
SDL_Surface *glyph = ags_drawStringToSurface(svar_get(wText), wR, wG, wB, spec);
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, glyph->w, wSize, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_SetSurfaceBlendMode(glyph, SDL_BLENDMODE_NONE);
|
||||
@@ -244,7 +245,8 @@ void scg_create_textnum(int wNumCG, int wSize, int wR, int wG, int wB, int wFigs
|
||||
}
|
||||
sprintf(s, ss, wValue);
|
||||
|
||||
SDL_Surface *glyph = ags_drawStringToSurface(s, wR, wG, wB, (FontSpec){ .size = wSize });
|
||||
FontSpec spec = { .type = FONT_GOTHIC, .weight = FONT_WEIGHT_BOLD, .size = wSize };
|
||||
SDL_Surface *glyph = ags_drawStringToSurface(s, wR, wG, wB, spec);
|
||||
|
||||
SDL_Surface *sf = SDL_CreateRGBSurfaceWithFormat(0, glyph->w, wSize, 32, SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_SetSurfaceBlendMode(glyph, SDL_BLENDMODE_NONE);
|
||||
@@ -331,7 +333,7 @@ int scg_querytype(int wNumCG) {
|
||||
}
|
||||
|
||||
// CGの大きさを取得
|
||||
bool scg_querysize(int wNumCG, int *w, int *h) {
|
||||
bool scg_querysize(int wNumCG, vmvar_t *w, vmvar_t *h) {
|
||||
if (wNumCG >= (CGMAX -1)) goto errexit;
|
||||
if (!cg_store || !cg_store[wNumCG]) goto errexit;
|
||||
if (cg_store[wNumCG]->sf == NULL) goto errexit;
|
||||
|
||||
@@ -55,7 +55,7 @@ void scg_partcopy(int wNumDstCG, int wNumSrcCG, int wX, int wY, int wWidth, int
|
||||
void scg_freeall(void);
|
||||
void scg_free(int cg);
|
||||
int scg_querytype(int wNumCG);
|
||||
bool scg_querysize(int wNumCG, int *w, int *h);
|
||||
bool scg_querysize(int wNumCG, vmvar_t *w, vmvar_t *h);
|
||||
int scg_querybpp(int wNumCG);
|
||||
bool scg_existalphamap(int wNumCG);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ static void Init() {
|
||||
int p1 = getCaliValue(); /* ISys3x */
|
||||
int p2 = getCaliValue(); /* IWinMsg */
|
||||
int p3 = getCaliValue(); /* ITimer */
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 0;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ static void Init() {
|
||||
int p1 = getCaliValue();
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
TRACE_UNIMPLEMENTED("nDEMOE.Init %p:", var);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ static void Init() {
|
||||
int p1 = getCaliValue();
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
|
||||
+12
-12
@@ -83,9 +83,9 @@ static void MakeMapDraw() {
|
||||
int dstY = getCaliValue();
|
||||
int posX = getCaliValue();
|
||||
int posY = getCaliValue();
|
||||
int *a1 = getCaliVariable();
|
||||
int *a2 = getCaliVariable();
|
||||
int *a3 = getCaliVariable();
|
||||
vmvar_t *a1 = getCaliVariable();
|
||||
vmvar_t *a2 = getCaliVariable();
|
||||
vmvar_t *a3 = getCaliVariable();
|
||||
|
||||
for (int y = 0; y < window_height; y++) {
|
||||
for (int x = 0; x < window_width; x++) {
|
||||
@@ -178,9 +178,9 @@ static void TempMapLoadToShadow() {
|
||||
WARNING("non-array destination variable");
|
||||
return;
|
||||
}
|
||||
int *a1 = v_resolveRef(&r1);
|
||||
int *a2 = v_resolveRef(&r2);
|
||||
int *a3 = v_resolveRef(&r3);
|
||||
vmvar_t *a1 = v_resolveRef(&r1);
|
||||
vmvar_t *a2 = v_resolveRef(&r2);
|
||||
vmvar_t *a3 = v_resolveRef(&r3);
|
||||
uint16_t *p = mapdata[map];
|
||||
for (int i = 0; i < size; i++)
|
||||
*a1++ = SDL_SwapLE16(*p++);
|
||||
@@ -207,9 +207,9 @@ static void TempMapSaveToShadow() {
|
||||
WARNING("non-array source variable");
|
||||
return;
|
||||
}
|
||||
int *a1 = v_resolveRef(&r1);
|
||||
int *a2 = v_resolveRef(&r2);
|
||||
int *a3 = v_resolveRef(&r3);
|
||||
vmvar_t *a1 = v_resolveRef(&r1);
|
||||
vmvar_t *a2 = v_resolveRef(&r2);
|
||||
vmvar_t *a3 = v_resolveRef(&r3);
|
||||
uint16_t *p = mapdata[map];
|
||||
for (int i = 0; i < size; i++)
|
||||
*p++ = SDL_SwapLE16(*a1++);
|
||||
@@ -296,13 +296,13 @@ static void TempMapFileLoad() {
|
||||
}
|
||||
|
||||
static void CalcMoveDiffer() {
|
||||
int *dx = getCaliVariable();
|
||||
int *dy = getCaliVariable();
|
||||
vmvar_t *dx = getCaliVariable();
|
||||
vmvar_t *dy = getCaliVariable();
|
||||
int moveL = getCaliValue();
|
||||
int moveU = getCaliValue();
|
||||
int moveR = getCaliValue();
|
||||
int moveD = getCaliValue();
|
||||
int *pt = getCaliVariable();
|
||||
vmvar_t *pt = getCaliVariable();
|
||||
int duration = getCaliValue();
|
||||
|
||||
int t = min(*pt, duration);
|
||||
|
||||
@@ -41,7 +41,7 @@ static void Init() {
|
||||
int p1 = getCaliValue();
|
||||
int p2 = getCaliValue();
|
||||
int p3 = getCaliValue();
|
||||
int *var = getCaliVariable();
|
||||
vmvar_t *var = getCaliVariable();
|
||||
|
||||
*var = 1;
|
||||
|
||||
|
||||
+41
-10
@@ -1,11 +1,42 @@
|
||||
# This creates a target "translations"
|
||||
gettext_create_translations(xsystem35.pot ALL ja.po)
|
||||
# Translation catalogs, built from the po/<lang>.po files listed in
|
||||
# NLS_LANGUAGES. On platforms with libintl they are compiled to .mo files and
|
||||
# loaded at runtime; otherwise they are compiled into the executable as a
|
||||
# built-in catalog (see src/nls.c).
|
||||
|
||||
add_custom_target(pot
|
||||
xgettext --default-domain=${PACKAGE} --directory=${CMAKE_SOURCE_DIR}
|
||||
--keyword=_ --keyword=N_
|
||||
--files-from=${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in
|
||||
--copyright-holder='Masaki Chikama'
|
||||
--output=${CMAKE_CURRENT_SOURCE_DIR}/xsystem35.pot
|
||||
)
|
||||
add_dependencies(translations pot)
|
||||
if (HAVE_LIBINTL)
|
||||
# Compile the .po files into .mo catalogs for libintl.
|
||||
list(TRANSFORM NLS_LANGUAGES APPEND ".po" OUTPUT_VARIABLE po_files)
|
||||
gettext_create_translations(xsystem35.pot ALL ${po_files})
|
||||
|
||||
# The `pot` target re-extracts strings from POTFILES.in into xsystem35.pot.
|
||||
# A normal build does not re-extract, so after adding/changing a _()/N_()
|
||||
# string build this target and translate the new entries. CI fails if
|
||||
# xsystem35.pot is out of date.
|
||||
add_custom_target(pot
|
||||
xgettext --default-domain=${PACKAGE} --directory=${PROJECT_SOURCE_DIR}
|
||||
--keyword=_ --keyword=N_
|
||||
--files-from=${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in
|
||||
--output=${CMAKE_CURRENT_SOURCE_DIR}/xsystem35.pot
|
||||
)
|
||||
else()
|
||||
# Generate a header with the built-in translation tables (nls_catalog.h),
|
||||
# included by src/nls.c. xsystem35 is made to depend on it and to look in
|
||||
# this build directory for the header.
|
||||
set(po_files "")
|
||||
foreach (lang IN LISTS NLS_LANGUAGES)
|
||||
list(APPEND po_files ${CMAKE_CURRENT_SOURCE_DIR}/${lang}.po)
|
||||
endforeach()
|
||||
set(nls_header ${CMAKE_CURRENT_BINARY_DIR}/nls_catalog.h)
|
||||
add_custom_command(
|
||||
OUTPUT ${nls_header}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DPO_DIR=${CMAKE_CURRENT_SOURCE_DIR}
|
||||
"-DLANGUAGES=${NLS_LANGUAGES}"
|
||||
-DOUTPUT=${nls_header}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/generate_nls_catalog.cmake
|
||||
DEPENDS ${po_files} ${CMAKE_CURRENT_SOURCE_DIR}/generate_nls_catalog.cmake
|
||||
VERBATIM)
|
||||
add_custom_target(generate_nls_catalog DEPENDS ${nls_header})
|
||||
add_dependencies(xsystem35 generate_nls_catalog)
|
||||
target_include_directories(xsystem35 PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ja
|
||||
@@ -1,317 +0,0 @@
|
||||
# Makefile for PO directory in any package using GNU gettext.
|
||||
# Copyright (C) 1995-1997, 2000-2002 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
|
||||
#
|
||||
# This file can be copied and used freely without restrictions. It can
|
||||
# be used in projects which are not available under the GNU General Public
|
||||
# License but which still want to provide support for the GNU gettext
|
||||
# functionality.
|
||||
# Please note that the actual code of GNU gettext is covered by the GNU
|
||||
# General Public License and is *not* in the public domain.
|
||||
|
||||
PACKAGE = @PACKAGE@
|
||||
VERSION = @VERSION@
|
||||
|
||||
SHELL = /bin/sh
|
||||
@SET_MAKE@
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
datadir = @datadir@
|
||||
localedir = $(datadir)/locale
|
||||
gettextsrcdir = $(datadir)/gettext/po
|
||||
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
MKINSTALLDIRS = @MKINSTALLDIRS@
|
||||
mkinstalldirs = $(SHELL) `case "$(MKINSTALLDIRS)" in /*) echo "$(MKINSTALLDIRS)" ;; *) echo "$(top_builddir)/$(MKINSTALLDIRS)" ;; esac`
|
||||
|
||||
GMSGFMT = @GMSGFMT@
|
||||
MSGFMT = @MSGFMT@
|
||||
XGETTEXT = @XGETTEXT@
|
||||
MSGMERGE = msgmerge
|
||||
MSGMERGE_UPDATE = @MSGMERGE@ --update
|
||||
MSGINIT = msginit
|
||||
MSGCONV = msgconv
|
||||
MSGFILTER = msgfilter
|
||||
|
||||
POFILES = @POFILES@
|
||||
GMOFILES = @GMOFILES@
|
||||
UPDATEPOFILES = @UPDATEPOFILES@
|
||||
DUMMYPOFILES = @DUMMYPOFILES@
|
||||
DISTFILES.common = Makefile.in.in Makevars remove-potcdate.sin \
|
||||
$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3)
|
||||
DISTFILES = $(DISTFILES.common) POTFILES.in $(DOMAIN).pot \
|
||||
$(POFILES) $(GMOFILES) \
|
||||
$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3)
|
||||
|
||||
POTFILES = \
|
||||
|
||||
CATALOGS = @CATALOGS@
|
||||
|
||||
# Makevars gets inserted here. (Don't remove this line!)
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-update
|
||||
|
||||
.po.mo:
|
||||
@echo "$(MSGFMT) -c -o $@ $<"; \
|
||||
$(MSGFMT) -c -o t-$@ $< && mv t-$@ $@
|
||||
|
||||
.po.gmo:
|
||||
@lang=`echo $* | sed -e 's,.*/,,'`; \
|
||||
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
|
||||
echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o $${lang}.gmo $${lang}.po"; \
|
||||
cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo
|
||||
|
||||
.sin.sed:
|
||||
sed -e '/^#/d' $< > t-$@
|
||||
mv t-$@ $@
|
||||
|
||||
|
||||
all: all-@USE_NLS@
|
||||
|
||||
all-yes: $(CATALOGS)
|
||||
all-no:
|
||||
|
||||
# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update',
|
||||
# otherwise packages like GCC can not be built if only parts of the source
|
||||
# have been downloaded.
|
||||
|
||||
$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed
|
||||
$(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
|
||||
--add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) \
|
||||
--files-from=$(srcdir)/POTFILES.in \
|
||||
--copyright-holder='$(COPYRIGHT_HOLDER)'
|
||||
test ! -f $(DOMAIN).po || { \
|
||||
if test -f $(srcdir)/$(DOMAIN).pot; then \
|
||||
sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \
|
||||
sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \
|
||||
if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \
|
||||
rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \
|
||||
else \
|
||||
rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \
|
||||
mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
|
||||
fi; \
|
||||
else \
|
||||
mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
|
||||
fi; \
|
||||
}
|
||||
|
||||
$(srcdir)/$(DOMAIN).pot:
|
||||
$(MAKE) $(DOMAIN).pot-update
|
||||
|
||||
$(POFILES): $(srcdir)/$(DOMAIN).pot
|
||||
@lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \
|
||||
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
|
||||
echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \
|
||||
cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot
|
||||
|
||||
|
||||
install: install-exec install-data
|
||||
install-exec:
|
||||
install-data: install-data-@USE_NLS@
|
||||
if test "$(PACKAGE)" = "gettext"; then \
|
||||
$(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \
|
||||
for file in $(DISTFILES.common); do \
|
||||
$(INSTALL_DATA) $(srcdir)/$$file \
|
||||
$(DESTDIR)$(gettextsrcdir)/$$file; \
|
||||
done; \
|
||||
else \
|
||||
: ; \
|
||||
fi
|
||||
install-data-no: all
|
||||
install-data-yes: all
|
||||
$(mkinstalldirs) $(DESTDIR)$(datadir)
|
||||
@catalogs='$(CATALOGS)'; \
|
||||
for cat in $$catalogs; do \
|
||||
cat=`basename $$cat`; \
|
||||
lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
|
||||
dir=$(localedir)/$$lang/LC_MESSAGES; \
|
||||
$(mkinstalldirs) $(DESTDIR)$$dir; \
|
||||
if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \
|
||||
$(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \
|
||||
echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \
|
||||
for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
|
||||
if test -n "$$lc"; then \
|
||||
if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
|
||||
link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
|
||||
mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
|
||||
mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
|
||||
(cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
|
||||
for file in *; do \
|
||||
if test -f $$file; then \
|
||||
ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
|
||||
fi; \
|
||||
done); \
|
||||
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
|
||||
else \
|
||||
if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
|
||||
:; \
|
||||
else \
|
||||
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
|
||||
mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
|
||||
fi; \
|
||||
fi; \
|
||||
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
|
||||
ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
|
||||
ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
|
||||
cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
|
||||
echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \
|
||||
fi; \
|
||||
done; \
|
||||
done
|
||||
|
||||
install-strip: install
|
||||
|
||||
installdirs: installdirs-exec installdirs-data
|
||||
installdirs-exec:
|
||||
installdirs-data: installdirs-data-@USE_NLS@
|
||||
if test "$(PACKAGE)" = "gettext"; then \
|
||||
$(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \
|
||||
else \
|
||||
: ; \
|
||||
fi
|
||||
installdirs-data-no:
|
||||
installdirs-data-yes:
|
||||
$(mkinstalldirs) $(DESTDIR)$(datadir)
|
||||
@catalogs='$(CATALOGS)'; \
|
||||
for cat in $$catalogs; do \
|
||||
cat=`basename $$cat`; \
|
||||
lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
|
||||
dir=$(localedir)/$$lang/LC_MESSAGES; \
|
||||
$(mkinstalldirs) $(DESTDIR)$$dir; \
|
||||
for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
|
||||
if test -n "$$lc"; then \
|
||||
if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
|
||||
link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
|
||||
mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
|
||||
mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
|
||||
(cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
|
||||
for file in *; do \
|
||||
if test -f $$file; then \
|
||||
ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
|
||||
fi; \
|
||||
done); \
|
||||
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
|
||||
else \
|
||||
if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
|
||||
:; \
|
||||
else \
|
||||
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
|
||||
mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
|
||||
fi; \
|
||||
fi; \
|
||||
fi; \
|
||||
done; \
|
||||
done
|
||||
|
||||
# Define this as empty until I found a useful application.
|
||||
installcheck:
|
||||
|
||||
uninstall: uninstall-exec uninstall-data
|
||||
uninstall-exec:
|
||||
uninstall-data: uninstall-data-@USE_NLS@
|
||||
if test "$(PACKAGE)" = "gettext"; then \
|
||||
for file in $(DISTFILES.common); do \
|
||||
rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
|
||||
done; \
|
||||
else \
|
||||
: ; \
|
||||
fi
|
||||
uninstall-data-no:
|
||||
uninstall-data-yes:
|
||||
catalogs='$(CATALOGS)'; \
|
||||
for cat in $$catalogs; do \
|
||||
cat=`basename $$cat`; \
|
||||
lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
|
||||
for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \
|
||||
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
|
||||
done; \
|
||||
done
|
||||
|
||||
check: all
|
||||
|
||||
dvi info tags TAGS ID:
|
||||
|
||||
mostlyclean:
|
||||
rm -f remove-potcdate.sed
|
||||
rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po
|
||||
rm -fr *.o
|
||||
|
||||
clean: mostlyclean
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile Makefile.in POTFILES *.mo
|
||||
|
||||
maintainer-clean: distclean
|
||||
@echo "This command is intended for maintainers to use;"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
rm -f $(GMOFILES)
|
||||
|
||||
distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
|
||||
dist distdir:
|
||||
$(MAKE) update-po
|
||||
@$(MAKE) dist2
|
||||
# This is a separate target because 'update-po' must be executed before.
|
||||
dist2: $(DISTFILES)
|
||||
dists="$(DISTFILES)"; \
|
||||
if test -f $(srcdir)/ChangeLog; then dists="$$dists ChangeLog"; fi; \
|
||||
if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \
|
||||
for file in $$dists; do \
|
||||
if test -f $$file; then \
|
||||
cp -p $$file $(distdir); \
|
||||
else \
|
||||
cp -p $(srcdir)/$$file $(distdir); \
|
||||
fi; \
|
||||
done
|
||||
|
||||
update-po: Makefile
|
||||
$(MAKE) $(DOMAIN).pot-update
|
||||
$(MAKE) $(UPDATEPOFILES)
|
||||
$(MAKE) update-gmo
|
||||
|
||||
# General rule for updating PO files.
|
||||
|
||||
.nop.po-update:
|
||||
@lang=`echo $@ | sed -e 's/\.po-update$$//'`; \
|
||||
if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; fi; \
|
||||
tmpdir=`pwd`; \
|
||||
echo "$$lang:"; \
|
||||
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
|
||||
echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \
|
||||
cd $(srcdir); \
|
||||
if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \
|
||||
if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
|
||||
rm -f $$tmpdir/$$lang.new.po; \
|
||||
else \
|
||||
if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
|
||||
:; \
|
||||
else \
|
||||
echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi; \
|
||||
else \
|
||||
echo "msgmerge for $$lang.po failed!" 1>&2; \
|
||||
rm -f $$tmpdir/$$lang.new.po; \
|
||||
fi
|
||||
|
||||
$(DUMMYPOFILES):
|
||||
|
||||
update-gmo: Makefile $(GMOFILES)
|
||||
@:
|
||||
|
||||
Makefile: Makefile.in.in $(top_builddir)/config.status POTFILES.in
|
||||
cd $(top_builddir) \
|
||||
&& CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
|
||||
$(SHELL) ./config.status
|
||||
|
||||
force:
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make not to export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
# Makefile variables for PO directory in any package using GNU gettext.
|
||||
|
||||
# Usually the message domain is the same as the package name.
|
||||
DOMAIN = $(PACKAGE)
|
||||
|
||||
# These two variables depend on the location of this directory.
|
||||
subdir = po
|
||||
top_builddir = ..
|
||||
|
||||
# These options get passed to xgettext.
|
||||
XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
|
||||
|
||||
# This is the copyright holder that gets inserted into the header of the
|
||||
# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
|
||||
# package. (Note that the msgstr strings, extracted from the package's
|
||||
# sources, belong to the copyright holder of the package.) Translators are
|
||||
# expected to transfer the copyright for their translations to this person
|
||||
# or entity, or to disclaim their copyright. The empty string stands for
|
||||
# the public domain; in this case the translators are expected to disclaim
|
||||
# their copyright.
|
||||
COPYRIGHT_HOLDER = Masaki Chikama
|
||||
|
||||
# This is the email address or URL to which the translators shall report
|
||||
# bugs in the untranslated strings:
|
||||
# - Strings which are not entire sentences, see the maintainer guidelines
|
||||
# in the GNU gettext documentation, section 'Preparing Strings'.
|
||||
# - Strings which use unclear terms or require additional context to be
|
||||
# understood.
|
||||
# - Strings which make invalid assumptions about notation of date, time or
|
||||
# money.
|
||||
# - Pluralisation problems.
|
||||
# - Incorrect English spelling.
|
||||
# - Incorrect formatting.
|
||||
# It can be your email address, or a mailing list address where translators
|
||||
# can write to without being subscribed, or the URL of a web page through
|
||||
# which the translators can contact you.
|
||||
MSGID_BUGS_ADDRESS = chikama@nabal.aist-nara.ac.jp
|
||||
|
||||
# This is the list of locale categories, beyond LC_MESSAGES, for which the
|
||||
# message catalogs shall be used. It is usually empty.
|
||||
EXTRA_LOCALE_CATEGORIES =
|
||||
+2
-4
@@ -1,5 +1,3 @@
|
||||
./src/menu.c
|
||||
./src/menu_gui.c
|
||||
./src/menu_gui_volval.c
|
||||
./src/s39init.c
|
||||
|
||||
./src/input_modal.c
|
||||
./src/volume.c
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Generate a C header with the built-in translation catalogs, for the NLS used
|
||||
# on platforms without libintl. It defines one table per language plus a
|
||||
# registry (nls_catalogs) mapping language codes to tables, all as file-local
|
||||
# statics, and is included by src/nls.c.
|
||||
#
|
||||
# .po and C share the same string-escape conventions (\n, \", \\, ...), so the
|
||||
# quoted bodies are copied through verbatim and multi-line entries become
|
||||
# adjacent C string literals, which the compiler concatenates.
|
||||
#
|
||||
# Expected variables: PO_DIR, LANGUAGES (list), OUTPUT
|
||||
|
||||
set(tables "")
|
||||
set(registry "")
|
||||
|
||||
foreach(lang IN LISTS LANGUAGES)
|
||||
file(STRINGS "${PO_DIR}/${lang}.po" lines ENCODING UTF-8)
|
||||
|
||||
set(entries "")
|
||||
set(cur_id "")
|
||||
set(cur_str "")
|
||||
set(state none)
|
||||
|
||||
# Appends the current msgid/msgstr pair to `entries`, skipping the header
|
||||
# entry (empty msgid).
|
||||
macro(flush_entry)
|
||||
if(NOT cur_id STREQUAL "" AND NOT cur_id STREQUAL "\"\"")
|
||||
string(APPEND entries "\t{ ${cur_id}, ${cur_str} },\n")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
foreach(line IN LISTS lines)
|
||||
if(line MATCHES "^msgid \"(.*)\"$")
|
||||
flush_entry()
|
||||
set(cur_id "\"${CMAKE_MATCH_1}\"")
|
||||
set(cur_str "")
|
||||
set(state id)
|
||||
elseif(line MATCHES "^msgstr \"(.*)\"$")
|
||||
set(cur_str "\"${CMAKE_MATCH_1}\"")
|
||||
set(state str)
|
||||
elseif(line MATCHES "^\"(.*)\"$")
|
||||
if(state STREQUAL id)
|
||||
set(cur_id "${cur_id} \"${CMAKE_MATCH_1}\"")
|
||||
elseif(state STREQUAL str)
|
||||
set(cur_str "${cur_str} \"${CMAKE_MATCH_1}\"")
|
||||
endif()
|
||||
else()
|
||||
set(state none)
|
||||
endif()
|
||||
endforeach()
|
||||
flush_entry()
|
||||
|
||||
string(APPEND tables
|
||||
"static const struct nls_entry nls_table_${lang}[] = {\n"
|
||||
"${entries}"
|
||||
"\t{ 0, 0 }, // terminator\n"
|
||||
"};\n\n")
|
||||
string(APPEND registry "\t{ \"${lang}\", nls_table_${lang} },\n")
|
||||
endforeach()
|
||||
|
||||
file(WRITE "${OUTPUT}"
|
||||
"// Generated from the .po files. Do not edit.\n"
|
||||
"#include \"nls.h\"\n"
|
||||
"\n"
|
||||
"${tables}"
|
||||
"static const struct nls_catalog nls_catalogs[] = {\n"
|
||||
"${registry}"
|
||||
"\t{ 0, 0 }, // terminator\n"
|
||||
"};\n")
|
||||
@@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xsystem35 1.7.3pre3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-07-14 18:38+0900\n"
|
||||
"PO-Revision-Date: 2004-08-24 14:40+0900\n"
|
||||
"POT-Creation-Date: 2026-07-19 12:40+0900\n"
|
||||
"PO-Revision-Date: 2026-06-14 17:09+0900\n"
|
||||
"Last-Translator: CHIKAMA Masaki <chika@8ne.sakura.ne.jp>\n"
|
||||
"Language-Team: Japanese <chika@8ne.sakura.ne.jp>\n"
|
||||
"Language: ja\n"
|
||||
@@ -17,143 +17,74 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/menu_gui.c:127 src/menu_gui.c:137 src/menu_gui.c:147
|
||||
msgid "0"
|
||||
msgstr ""
|
||||
#: src/menu.c:136 src/menu.c:139
|
||||
msgid "Message Skip"
|
||||
msgstr "文字送り"
|
||||
|
||||
#: src/menu_gui.c:374 src/menu_gui.c:658
|
||||
msgid "About"
|
||||
msgstr "情報"
|
||||
#: src/menu.c:144
|
||||
msgid "Mouse Movement"
|
||||
msgstr "マウスカーソル移動"
|
||||
|
||||
#: src/menu_gui.c:593
|
||||
msgid "CDROM-audio"
|
||||
msgstr "CDROMオーディオ"
|
||||
#: src/menu.c:148 src/volume.c:198
|
||||
msgid "Sound Settings"
|
||||
msgstr "サウンド設定"
|
||||
|
||||
#: src/menu_gui.c:171 src/menu_gui.c:266
|
||||
#: src/menu.c:153 src/menu.c:221
|
||||
msgid "Restart"
|
||||
msgstr "再起動"
|
||||
|
||||
#: src/menu.c:158 src/menu.c:216
|
||||
msgid "Quit"
|
||||
msgstr "終了"
|
||||
|
||||
#: src/menu.c:197 src/input_modal.c:292 src/input_modal.c:485
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
|
||||
#: src/menu_gui.c:306 src/menu_gui.c:673
|
||||
msgid "Exit"
|
||||
msgstr "終了"
|
||||
#: src/menu.c:203
|
||||
msgid "Any unsaved progress will be lost."
|
||||
msgstr "保存していない進行状況は失われます。"
|
||||
|
||||
#: src/menu_gui.c:317
|
||||
msgid "Exit System35 ?"
|
||||
msgstr "System35を終了しますか?"
|
||||
#: src/menu.c:216
|
||||
msgid "Quit game?"
|
||||
msgstr "ゲームを終了しますか?"
|
||||
|
||||
#: src/menu_gui.c:400
|
||||
msgid "Information1"
|
||||
msgstr "情報1"
|
||||
#: src/menu.c:221
|
||||
msgid "Restart game?"
|
||||
msgstr "ゲームを再起動しますか?"
|
||||
|
||||
#: src/menu_gui.c:410
|
||||
msgid "Information2"
|
||||
msgstr "情報2"
|
||||
#: src/input_modal.c:268
|
||||
msgid "Enter a string"
|
||||
msgstr "文字を入力してください"
|
||||
|
||||
#: src/menu_gui.c:420
|
||||
msgid "Information3"
|
||||
msgstr "情報3"
|
||||
|
||||
#: src/menu_gui.c:60
|
||||
msgid "InputNumber"
|
||||
msgstr "数値入力"
|
||||
|
||||
#: src/menu_gui.c:214
|
||||
msgid "InputString"
|
||||
msgstr "文字列入力"
|
||||
|
||||
#: src/menu.c:69
|
||||
#: src/input_modal.c:280
|
||||
#, c-format
|
||||
msgid "MAX %d charater"
|
||||
msgid "Up to %d characters"
|
||||
msgstr "最大 %d 文字"
|
||||
|
||||
#: src/menu_gui.c:241
|
||||
msgid "MAX charater"
|
||||
msgstr "最大文字数"
|
||||
|
||||
#: src/menu_gui.c:621
|
||||
msgid "MIDI-audio"
|
||||
msgstr "MIDIオーディオ"
|
||||
|
||||
#: src/menu_gui.c:764
|
||||
#, fuzzy
|
||||
msgid "MessageBox"
|
||||
msgstr "メッセージスキップ"
|
||||
|
||||
#: src/menu_gui.c:485
|
||||
msgid "MessageSkip"
|
||||
msgstr "メッセージスキップ"
|
||||
|
||||
#: src/menu_gui.c:774
|
||||
#, fuzzy
|
||||
msgid "Messge"
|
||||
msgstr "メッセージスキップ"
|
||||
|
||||
#: src/menu_gui.c:521
|
||||
msgid "MouseAutoMove"
|
||||
msgstr "マウスカーソル移動"
|
||||
|
||||
#: src/menu_gui.c:338
|
||||
msgid "No"
|
||||
msgstr "いいえ"
|
||||
|
||||
#: src/menu_gui.c:250
|
||||
msgid "Notice) HANKAKU is not available"
|
||||
msgstr "註)全角文字のみ有効"
|
||||
|
||||
#: src/menu_gui.c:505 src/menu_gui.c:541 src/menu_gui.c:585 src/menu_gui.c:613
|
||||
#: src/menu_gui.c:641
|
||||
msgid "OFF"
|
||||
msgstr "無効"
|
||||
|
||||
#: src/menu_gui.c:164 src/menu_gui.c:273 src/menu_gui.c:426 src/menu_gui.c:781
|
||||
#: src/input_modal.c:288 src/input_modal.c:478 src/input_modal.c:483
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: src/menu_gui.c:497 src/menu_gui.c:533 src/menu_gui.c:577 src/menu_gui.c:605
|
||||
#: src/menu_gui.c:633
|
||||
msgid "ON"
|
||||
msgstr "有効"
|
||||
#: src/input_modal.c:434
|
||||
msgid "Enter a number"
|
||||
msgstr "数値を入力してください"
|
||||
|
||||
#: src/menu_gui.c:565
|
||||
msgid "PCM-audio"
|
||||
msgstr "PCMオーディオ"
|
||||
#: src/volume.c:106
|
||||
msgid "BGM"
|
||||
msgstr "BGM"
|
||||
|
||||
#: src/menu_gui.c:557 src/menu_gui_volval.c:43
|
||||
msgid "VolumeValance"
|
||||
msgstr "ボリュームバランス"
|
||||
#: src/volume.c:107
|
||||
msgid "Sound effects"
|
||||
msgstr "効果音"
|
||||
|
||||
#: src/menu_gui.c:331
|
||||
msgid "Yes"
|
||||
msgstr "はい"
|
||||
|
||||
#: src/menu_gui.c:97 src/menu_gui.c:745
|
||||
msgid "default"
|
||||
msgstr "既定値"
|
||||
|
||||
#: src/menu_gui_volval.c:122
|
||||
msgid "large"
|
||||
msgstr "大"
|
||||
|
||||
#: src/menu_gui.c:117
|
||||
msgid "max"
|
||||
msgstr "最大値"
|
||||
|
||||
#: src/menu_gui.c:107
|
||||
msgid "min"
|
||||
msgstr "最小値"
|
||||
|
||||
#: src/menu_gui_volval.c:89
|
||||
#: src/volume.c:189
|
||||
msgid "mute"
|
||||
msgstr "ミュート"
|
||||
|
||||
#: src/menu_gui_volval.c:114
|
||||
msgid "small"
|
||||
msgstr "小"
|
||||
#: src/volume.c:223
|
||||
msgid "Close"
|
||||
msgstr "閉じる"
|
||||
|
||||
#: src/menu_gui.c:225
|
||||
msgid "title"
|
||||
msgstr "タイトル"
|
||||
|
||||
#: src/menu_gui_volval.c:41
|
||||
msgid "window1"
|
||||
msgstr ""
|
||||
#~ msgid "Volume"
|
||||
#~ msgstr "ボリューム"
|
||||
|
||||
+35
-105
@@ -1,5 +1,5 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR Masaki Chikama
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-07-14 18:38+0900\n"
|
||||
"POT-Creation-Date: 2026-07-19 12:40+0900\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -17,141 +17,71 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/menu.c:69
|
||||
#, c-format
|
||||
msgid "MAX %d charater"
|
||||
#: src/menu.c:136 src/menu.c:139
|
||||
msgid "Message Skip"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:60
|
||||
msgid "InputNumber"
|
||||
#: src/menu.c:144
|
||||
msgid "Mouse Movement"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:97 src/menu_gui.c:745
|
||||
msgid "default"
|
||||
#: src/menu.c:148 src/volume.c:198
|
||||
msgid "Sound Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:107
|
||||
msgid "min"
|
||||
#: src/menu.c:153 src/menu.c:221
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:117
|
||||
msgid "max"
|
||||
#: src/menu.c:158 src/menu.c:216
|
||||
msgid "Quit"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:127 src/menu_gui.c:137 src/menu_gui.c:147
|
||||
msgid "0"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:164 src/menu_gui.c:273 src/menu_gui.c:426 src/menu_gui.c:781
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:171 src/menu_gui.c:266
|
||||
#: src/menu.c:197 src/input_modal.c:292 src/input_modal.c:485
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:214
|
||||
msgid "InputString"
|
||||
#: src/menu.c:203
|
||||
msgid "Any unsaved progress will be lost."
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:225
|
||||
msgid "title"
|
||||
#: src/menu.c:216
|
||||
msgid "Quit game?"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:241
|
||||
msgid "MAX charater"
|
||||
#: src/menu.c:221
|
||||
msgid "Restart game?"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:250
|
||||
msgid "Notice) HANKAKU is not available"
|
||||
#: src/input_modal.c:268
|
||||
msgid "Enter a string"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:306 src/menu_gui.c:673
|
||||
msgid "Exit"
|
||||
#: src/input_modal.c:280
|
||||
#, c-format
|
||||
msgid "Up to %d characters"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:317
|
||||
msgid "Exit System35 ?"
|
||||
#: src/input_modal.c:288 src/input_modal.c:478 src/input_modal.c:483
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:331
|
||||
msgid "Yes"
|
||||
#: src/input_modal.c:434
|
||||
msgid "Enter a number"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:338
|
||||
msgid "No"
|
||||
#: src/volume.c:106
|
||||
msgid "BGM"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:374 src/menu_gui.c:658
|
||||
msgid "About"
|
||||
#: src/volume.c:107
|
||||
msgid "Sound effects"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:400
|
||||
msgid "Information1"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:410
|
||||
msgid "Information2"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:420
|
||||
msgid "Information3"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:485
|
||||
msgid "MessageSkip"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:497 src/menu_gui.c:533 src/menu_gui.c:577 src/menu_gui.c:605
|
||||
#: src/menu_gui.c:633
|
||||
msgid "ON"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:505 src/menu_gui.c:541 src/menu_gui.c:585 src/menu_gui.c:613
|
||||
#: src/menu_gui.c:641
|
||||
msgid "OFF"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:521
|
||||
msgid "MouseAutoMove"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:557 src/menu_gui_volval.c:43
|
||||
msgid "VolumeValance"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:565
|
||||
msgid "PCM-audio"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:593
|
||||
msgid "CDROM-audio"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:621
|
||||
msgid "MIDI-audio"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:764
|
||||
msgid "MessageBox"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui.c:774
|
||||
msgid "Messge"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui_volval.c:41
|
||||
msgid "window1"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui_volval.c:89
|
||||
#: src/volume.c:189
|
||||
msgid "mute"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui_volval.c:114
|
||||
msgid "small"
|
||||
msgstr ""
|
||||
|
||||
#: src/menu_gui_volval.c:122
|
||||
msgid "large"
|
||||
#: src/volume.c:223
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
+58
-42
@@ -1,8 +1,27 @@
|
||||
# src_lib contains sources that can be unit-tested without linking the
|
||||
# rest of xsystem35. They may reference sys_* and other small hooks;
|
||||
# tests provide stub definitions for those.
|
||||
add_library(src_lib STATIC
|
||||
ald_manager.c
|
||||
alpha_plane.c
|
||||
audio_meta.c
|
||||
bmp.c
|
||||
cache.c
|
||||
cali.c
|
||||
dri.c
|
||||
filecheck.c
|
||||
font.c
|
||||
gameresource.c
|
||||
hankaku.c
|
||||
mmap.c
|
||||
msgqueue.c
|
||||
pms.c
|
||||
profile.c
|
||||
qnt.c
|
||||
scenario.c
|
||||
utfsjis.c
|
||||
variable.c
|
||||
vsp.c
|
||||
)
|
||||
target_compile_options(src_lib PRIVATE -Wno-pointer-sign -Wall)
|
||||
target_include_directories(src_lib PRIVATE .)
|
||||
@@ -22,12 +41,6 @@ target_link_libraries(xsystem35 PRIVATE src_lib)
|
||||
|
||||
target_sources(xsystem35 PRIVATE
|
||||
ags.c
|
||||
ald_manager.c
|
||||
alpha_plane.c
|
||||
bgi.c
|
||||
bmp.c
|
||||
cache.c
|
||||
cali.c
|
||||
cdrom.bgm.c
|
||||
cg.c
|
||||
cmd2F.c
|
||||
@@ -57,22 +70,22 @@ target_sources(xsystem35 PRIVATE
|
||||
cmdy.c
|
||||
cmdz.c
|
||||
cursor.c
|
||||
dri.c
|
||||
ecopy.c
|
||||
effect.c
|
||||
event.c
|
||||
filecheck.c
|
||||
font.c
|
||||
hacks.c
|
||||
gfx_draw.c
|
||||
gfx_image.c
|
||||
gfx_video.c
|
||||
image.c
|
||||
input.c
|
||||
input_modal.c
|
||||
jpeg.c
|
||||
menu.c
|
||||
message.c
|
||||
microui/microui.c
|
||||
midi.c
|
||||
mmap.c
|
||||
modal.c
|
||||
msgskip.c
|
||||
mt19937-1.c
|
||||
music.c
|
||||
@@ -80,18 +93,13 @@ target_sources(xsystem35 PRIVATE
|
||||
music_midi.c
|
||||
nact.c
|
||||
network.c
|
||||
pms.c
|
||||
profile.c
|
||||
qnt.c
|
||||
s39ain.c
|
||||
savedata.c
|
||||
scenario.c
|
||||
scheduler.c
|
||||
selection.c
|
||||
system.c
|
||||
texthook.c
|
||||
variable.c
|
||||
vsp.c
|
||||
volume.c
|
||||
xsystem35.c
|
||||
)
|
||||
|
||||
@@ -118,16 +126,6 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
target_sources(xsystem35 PRIVATE menu_emscripten.c)
|
||||
elseif (ANDROID)
|
||||
target_sources(xsystem35 PRIVATE menu_android.c)
|
||||
elseif (ENABLE_GTK)
|
||||
target_sources(xsystem35 PRIVATE menu.c menu_callback.c menu_gui.c s39init.c)
|
||||
else ()
|
||||
target_sources(xsystem35 PRIVATE menu_sdl.c editor.c)
|
||||
endif()
|
||||
|
||||
if (HAVE_WEBP)
|
||||
target_sources(xsystem35 PRIVATE webp.c)
|
||||
target_link_libraries(xsystem35 PRIVATE WebP)
|
||||
@@ -139,12 +137,17 @@ if (ENABLE_DEBUGGER)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
target_sources(src_lib PRIVATE win/resources.c)
|
||||
target_sources(xsystem35 PRIVATE
|
||||
win/dialog.c win/resources.rc win/resources.c win/menubar.c win/console.c)
|
||||
win/dialog.c win/resources.rc win/menubar.c win/console.c)
|
||||
endif()
|
||||
|
||||
target_link_libraries(src_lib PRIVATE sdl2 zlib)
|
||||
target_link_libraries(xsystem35 PRIVATE m zlib sdl2 sdl2_ttf)
|
||||
if (NOT HAVE_LIBINTL)
|
||||
target_sources(xsystem35 PRIVATE nls.c)
|
||||
endif()
|
||||
|
||||
target_link_libraries(src_lib PRIVATE sdl2 freetype2 zlib)
|
||||
target_link_libraries(xsystem35 PRIVATE m zlib sdl2)
|
||||
if (TARGET sdl2_mixer)
|
||||
target_link_libraries(xsystem35 PRIVATE sdl2_mixer)
|
||||
endif()
|
||||
@@ -155,18 +158,28 @@ if (EMSCRIPTEN)
|
||||
# Without optimizations, Asyncify generates very large code.
|
||||
list(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG "-O1")
|
||||
|
||||
if (JSPI)
|
||||
target_link_options(xsystem35 PRIVATE
|
||||
-sJSPI
|
||||
-sJSPI_IMPORTS=wait_vsync
|
||||
)
|
||||
else()
|
||||
target_link_options(xsystem35 PRIVATE
|
||||
-sASYNCIFY=1
|
||||
-sASYNCIFY_IGNORE_INDIRECT=1
|
||||
-sASYNCIFY_REMOVE=SDL_Delay
|
||||
-sASYNCIFY_IMPORTS=wait_vsync
|
||||
# Functions that call an asynchronous function via a function pointer must
|
||||
# be added to ASYNCIFY_ADD, even if it had a direct call to another
|
||||
# asynchronous function, so that the indirect call itself is instrumented.
|
||||
-sASYNCIFY_ADD=commands2F60,nact_main,send_agsevent,cb_waitkey_sprite,modal_run
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_options(xsystem35 PRIVATE
|
||||
-sENVIRONMENT=web
|
||||
-sMODULARIZE=1
|
||||
-sEXPORT_ES6=1
|
||||
-sASYNCIFY=1
|
||||
-sASYNCIFY_IGNORE_INDIRECT=1
|
||||
-sASYNCIFY_REMOVE=SDL_Delay
|
||||
-sASYNCIFY_IMPORTS=wait_vsync
|
||||
# Functions that call an asynchronous function via a function pointer must
|
||||
# be added to ASYNCIFY_ADD, even if it had a direct call to another
|
||||
# asynchronous function, so that the indirect call itself is instrumented.
|
||||
-sASYNCIFY_ADD=commands2F60,nact_main,send_agsevent,cb_waitkey_sprite
|
||||
-sALLOW_MEMORY_GROWTH=1
|
||||
-sNO_EXIT_RUNTIME=1
|
||||
-sEXPORTED_FUNCTIONS=_main,_malloc
|
||||
@@ -181,9 +194,6 @@ else() # non-emscripten, non-android
|
||||
if (TARGET cJSON)
|
||||
target_link_libraries(xsystem35 PRIVATE cJSON)
|
||||
endif()
|
||||
if (GTK3_FOUND)
|
||||
target_link_libraries(xsystem35 PRIVATE PkgConfig::GTK3)
|
||||
endif()
|
||||
if (ENABLE_MIDI_PORTMIDI)
|
||||
target_link_libraries(xsystem35 PRIVATE ${PORTMIDI})
|
||||
endif()
|
||||
@@ -192,11 +202,17 @@ else() # non-emscripten, non-android
|
||||
|
||||
add_executable(src_tests
|
||||
src_tests.c
|
||||
cache_test.c
|
||||
font_test.c
|
||||
gameresource_test.c
|
||||
hankaku_test.c
|
||||
qnt_test.c
|
||||
variable_test.c
|
||||
)
|
||||
target_compile_options(src_tests PRIVATE -Wno-pointer-sign -Wall)
|
||||
target_link_libraries(src_tests PRIVATE src_lib)
|
||||
target_compile_definitions(src_tests PRIVATE
|
||||
TEST_FONT_DIR="${PROJECT_SOURCE_DIR}/fonts")
|
||||
target_link_libraries(src_tests PRIVATE src_lib sdl2)
|
||||
add_test(NAME src_tests COMMAND src_tests)
|
||||
configure_file(testdata/test.gr ${CMAKE_CURRENT_BINARY_DIR}/testdata/test.gr COPYONLY)
|
||||
file(COPY testdata DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
@@ -125,7 +125,7 @@ bool ags_check_param_xy(int *x, int *y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ags_init(const char *render_driver, bool enable_zb) {
|
||||
void ags_init(const char *render_driver) {
|
||||
nact->ags.mouse_warp_enabled = true;
|
||||
nact->ags.world_width = SYS35_DEFAULT_WIDTH;
|
||||
nact->ags.world_height = SYS35_DEFAULT_HEIGHT;
|
||||
@@ -138,8 +138,7 @@ void ags_init(const char *render_driver, bool enable_zb) {
|
||||
nact->ags.font_type = FONT_GOTHIC;
|
||||
nact->ags.text_decoration_type = 0;
|
||||
nact->ags.text_decoration_color = 0;
|
||||
nact->ags.enable_zb = enable_zb;
|
||||
nact->ags.font_weight = enable_zb ? FONT_WEIGHT_BOLD : FONT_WEIGHT_NORMAL;
|
||||
nact->ags.font_weight = FONT_WEIGHT_BOLD;
|
||||
|
||||
gfx_Initialize(render_driver);
|
||||
event_init();
|
||||
@@ -193,7 +192,7 @@ void ags_setViewArea(int x, int y, int width, int height) {
|
||||
nact->ags.view_area.y = y;
|
||||
nact->ags.view_area.w = width;
|
||||
nact->ags.view_area.h = height;
|
||||
gfx_setWindowSize(width, height);
|
||||
gfx_setViewSize(width, height);
|
||||
}
|
||||
|
||||
void ags_setWindowTitle(const char *title_utf8) {
|
||||
@@ -202,20 +201,33 @@ void ags_setWindowTitle(const char *title_utf8) {
|
||||
gfx_setWindowTitle(buf);
|
||||
}
|
||||
|
||||
void ags_getDIBInfo(DispInfo *info) {
|
||||
info->width = nact->ags.world_width;
|
||||
info->height = nact->ags.world_height;
|
||||
info->depth = nact->ags.world_depth;
|
||||
}
|
||||
|
||||
void ags_getViewAreaInfo(DispInfo *info) {
|
||||
gfx_getWindowInfo(NULL, NULL, &info->depth);
|
||||
info->width = nact->ags.view_area.w;
|
||||
info->height = nact->ags.view_area.h;
|
||||
}
|
||||
|
||||
void ags_getWindowInfo(DispInfo *info) {
|
||||
gfx_getWindowInfo(&info->width, &info->height, &info->depth);
|
||||
void ags_getDisplayInfo(enum ags_display display, int *width, int *height, int *depth) {
|
||||
SDL_DisplayMode dm;
|
||||
switch (display) {
|
||||
case AGS_DISPLAY_CURRENT:
|
||||
SDL_GetCurrentDisplayMode(0, &dm);
|
||||
*width = dm.w;
|
||||
*height = dm.h;
|
||||
*depth = SDL_BITSPERPIXEL(dm.format);
|
||||
break;
|
||||
case AGS_DISPLAY_DIB:
|
||||
*width = nact->ags.world_width;
|
||||
*height = nact->ags.world_height;
|
||||
*depth = nact->ags.world_depth;
|
||||
break;
|
||||
case AGS_DISPLAY_VIEW_AREA:
|
||||
SDL_GetCurrentDisplayMode(0, &dm);
|
||||
*width = nact->ags.view_area.w;
|
||||
*height = nact->ags.view_area.h;
|
||||
*depth = SDL_BITSPERPIXEL(dm.format);
|
||||
break;
|
||||
case AGS_DISPLAY_DESKTOP:
|
||||
SDL_GetDesktopDisplayMode(0, &dm);
|
||||
*width = dm.w;
|
||||
*height = dm.h;
|
||||
*depth = SDL_BITSPERPIXEL(dm.format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ags_setExposeSwitch(bool expose) {
|
||||
@@ -466,7 +478,7 @@ int ags_drawString(int x, int y, const char *src, int col, int size, SDL_Rect *r
|
||||
SDL_Surface *ags_drawStringToSurface(const char *str, int r, int g, int b, FontSpec font) {
|
||||
char *utf8 = toUTF8(str);
|
||||
SDL_Color color = {r, g, b, 255};
|
||||
SDL_Surface *sf = font_render_text(font, utf8, color);
|
||||
SDL_Surface *sf = font_render_text(font, utf8, color, true);
|
||||
free(utf8);
|
||||
return sf;
|
||||
}
|
||||
@@ -579,7 +591,7 @@ void ags_alpha_copyArea(int sx, int sy, int w, int h, int dx, int dy) {
|
||||
alpha_copy_area(nact->ags.dib, sx, sy, w, h, dx, dy);
|
||||
}
|
||||
|
||||
void ags_alpha_getPixel(int x, int y, int *pic) {
|
||||
void ags_alpha_getPixel(int x, int y, vmvar_t *pic) {
|
||||
if (nact->ags.world_depth == 8) return;
|
||||
|
||||
if (!ags_check_param_xy(&x, &y)) {
|
||||
@@ -716,10 +728,10 @@ void ags_setCursorLocation(int x, int y, bool is_dibgeo, bool for_selection) {
|
||||
}
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (!for_selection) {
|
||||
if (!sl_is_s380 && !for_selection) {
|
||||
// We can't move the actual cursor in the browser, but can change the
|
||||
// internal mouse coordinates. This can help with keyboard/gamepad
|
||||
// navigation.
|
||||
// navigation in Toushin Toshi 2.
|
||||
event_set_mouse_internal_location(x, y);
|
||||
EM_ASM({ xsystem35.shell.showMouseMoveEffect($0, $1); }, x, y);
|
||||
sys_sleep(cursor_move_time);
|
||||
@@ -733,11 +745,11 @@ void ags_setCursorLocation(int x, int y, bool is_dibgeo, bool for_selection) {
|
||||
for (int i = 1; i < 8; i++) {
|
||||
int xi = ((dx*i*i*i) >> 9) - ((3*dx*i*i)>> 6) + ((3*dx*i) >> 3) + p.x;
|
||||
int yi = ((dy*i*i*i) >> 9) - ((3*dy*i*i)>> 6) + ((3*dy*i) >> 3) + p.y;
|
||||
event_set_mouse_location(xi, yi);
|
||||
gfx_warpMouse(xi, yi);
|
||||
sys_sleep(cursor_move_time / 7);
|
||||
}
|
||||
event_set_mouse_location(x, y);
|
||||
} else if (!for_selection) {
|
||||
gfx_warpMouse(x, y);
|
||||
} else if (!sl_is_s380 && !for_selection) {
|
||||
event_set_mouse_internal_location(x, y);
|
||||
sys_sleep(cursor_move_time);
|
||||
}
|
||||
|
||||
@@ -102,11 +102,12 @@ typedef struct {
|
||||
uint32_t index;
|
||||
} PixelColor;
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
int depth;
|
||||
} DispInfo;
|
||||
enum ags_display {
|
||||
AGS_DISPLAY_CURRENT,
|
||||
AGS_DISPLAY_DIB,
|
||||
AGS_DISPLAY_VIEW_AREA,
|
||||
AGS_DISPLAY_DESKTOP,
|
||||
};
|
||||
|
||||
struct _ags {
|
||||
SDL_Color pal[256]; /* system palette */
|
||||
@@ -128,7 +129,6 @@ struct _ags {
|
||||
int text_decoration_color;
|
||||
|
||||
bool mouse_warp_enabled;
|
||||
bool enable_zb;
|
||||
bool noantialias; /* antialias を使用しない */
|
||||
bool noimagecursor; /* リソースファイルのカーソルを読みこまない */
|
||||
};
|
||||
@@ -137,7 +137,7 @@ typedef struct _ags ags_t;
|
||||
extern SDL_Surface *main_surface;
|
||||
|
||||
/* 初期化関係 */
|
||||
void ags_init(const char *render_driver, bool enable_zb);
|
||||
void ags_init(const char *render_driver);
|
||||
void ags_remove(void);
|
||||
void ags_reset(void);
|
||||
|
||||
@@ -145,9 +145,7 @@ void ags_reset(void);
|
||||
extern void ags_setWorldSize(int width, int height, int depth);
|
||||
extern void ags_setViewArea(int x, int y, int width, int height);
|
||||
extern void ags_setWindowTitle(const char *title_utf8);
|
||||
extern void ags_getDIBInfo(DispInfo *info);
|
||||
extern void ags_getWindowInfo(DispInfo *info);
|
||||
extern void ags_getViewAreaInfo(DispInfo *info);
|
||||
extern void ags_getDisplayInfo(enum ags_display display, int *width, int *height, int *depth);
|
||||
extern bool ags_check_param(int *x, int *y, int *w, int *h);
|
||||
extern bool ags_check_param_xy(int *x, int *y);
|
||||
extern surface_t *ags_getDIB();
|
||||
@@ -199,7 +197,7 @@ extern void ags_alpha_uppercut(int sx, int sy, int w, int h, int s, int d);
|
||||
extern void ags_alpha_lowercut(int sx, int sy, int w, int h, int s, int d);
|
||||
extern void ags_alpha_setLevel(int x, int y, int w, int h, int lv);
|
||||
extern void ags_alpha_copyArea(int sx, int sy, int w, int h, int dx, int dy);
|
||||
extern void ags_alpha_getPixel(int x, int y, int *pic);
|
||||
extern void ags_alpha_getPixel(int x, int y, vmvar_t *pic);
|
||||
extern void ags_alpha_setPixel(int x, int y, int w, int h, uint8_t *b);
|
||||
|
||||
/* fader */
|
||||
|
||||
+37
-7
@@ -33,18 +33,34 @@
|
||||
/* drifiles object */
|
||||
static drifiles *dri[DRIFILETYPEMAX];
|
||||
|
||||
/* cache handler for dri file */
|
||||
static cacher *cacheid;
|
||||
static Cache *dri_cache;
|
||||
|
||||
#ifndef ALD_CACHE_SIZE
|
||||
#define ALD_CACHE_SIZE (10 << 20)
|
||||
#endif
|
||||
|
||||
static uint32_t int_hash(const void *key) {
|
||||
return (uint32_t)*(const int *)key;
|
||||
}
|
||||
|
||||
static bool int_equal(const void *a, const void *b) {
|
||||
return *(const int *)a == *(const int *)b;
|
||||
}
|
||||
|
||||
/*
|
||||
* free dridata
|
||||
* dfile: dridata to be free
|
||||
*/
|
||||
static void ald_free(dridata *dfile) {
|
||||
static void ald_free(void *data) {
|
||||
dridata *dfile = data;
|
||||
free(dfile->data_raw);
|
||||
free(dfile);
|
||||
}
|
||||
|
||||
static bool ald_is_pinned(const void *data) {
|
||||
return ((const dridata *)data)->refcnt != 0;
|
||||
}
|
||||
|
||||
bool ald_is_linked(DRIFILETYPE type, int no) {
|
||||
if (type >= DRIFILETYPEMAX || !dri[type])
|
||||
return false;
|
||||
@@ -80,11 +96,12 @@ dridata *ald_getdata(DRIFILETYPE type, int no) {
|
||||
if (dri[type]->mmapped) return dri_getdata(dri[type], no);
|
||||
|
||||
/* not mmapped */
|
||||
if (NULL == (ddata = (dridata *)cache_foreach(cacheid, (type << 16) + no))) {
|
||||
int key = (type << 16) + no;
|
||||
if (NULL == (ddata = cache_get(dri_cache, &key))) {
|
||||
ddata = dri_getdata(dri[type], no);
|
||||
if (ddata != NULL) {
|
||||
ddata->refcnt = 0;
|
||||
cache_insert(cacheid, (type << 16) + no, (void *)ddata, ddata->size, &(ddata->refcnt));
|
||||
ddata->cached = cache_insert(dri_cache, &key, ddata, ddata->size) == CACHE_INSERT_OK;
|
||||
}
|
||||
}
|
||||
if (ddata != NULL)
|
||||
@@ -105,6 +122,8 @@ void ald_freedata(dridata *data) {
|
||||
free(data);
|
||||
} else {
|
||||
data->refcnt--;
|
||||
if (!data->cached && data->refcnt == 0)
|
||||
ald_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,8 +131,15 @@ void ald_init(int type, const char **file, int cnt, bool use_mmap) {
|
||||
if (type >= DRIFILETYPEMAX || cnt <= 0)
|
||||
return;
|
||||
dri[type] = dri_init(file, cnt, use_mmap);
|
||||
if (!dri[type]->mmapped) {
|
||||
cacheid = cache_new(ald_free);
|
||||
if (!dri[type]->mmapped && !dri_cache) {
|
||||
CacheOps ops = {
|
||||
.key_size = sizeof(int),
|
||||
.hash = int_hash,
|
||||
.equal = int_equal,
|
||||
.destroy = ald_free,
|
||||
.is_pinned = ald_is_pinned,
|
||||
};
|
||||
dri_cache = cache_new((size_t)ALD_CACHE_SIZE, &ops);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,3 +148,7 @@ int ald_get_maxno(DRIFILETYPE type) {
|
||||
return 0;
|
||||
return dri[type]->maxno;
|
||||
}
|
||||
|
||||
CacheStats ald_get_cache_stats(void) {
|
||||
return cache_get_stats(dri_cache);
|
||||
}
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@
|
||||
#define __ALD_MANAGER__
|
||||
|
||||
#include "portab.h"
|
||||
#include "cache.h"
|
||||
#include "dri.h"
|
||||
|
||||
#define DRIFILETYPEMAX 7
|
||||
@@ -44,6 +45,6 @@ bool ald_exists(DRIFILETYPE type, int no);
|
||||
dridata *ald_getdata(DRIFILETYPE type, int no);
|
||||
void ald_freedata(dridata *data);
|
||||
int ald_get_maxno(DRIFILETYPE type);
|
||||
CacheStats ald_get_cache_stats(void);
|
||||
|
||||
#endif /* !__ALD_MANAGER__ */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* bgi.c: BGI (BGM information) parser
|
||||
* audio_meta.c: BGI (BGM information) / WAI (wave information) parser
|
||||
*
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
@@ -23,10 +23,12 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
#include "bgi.h"
|
||||
#include "audio_meta.h"
|
||||
#include "LittleEndian.h"
|
||||
|
||||
#define BGI_MAX 100
|
||||
|
||||
@@ -82,3 +84,77 @@ bgi_t *bgi_find(int no) {
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int *wai_channels;
|
||||
static int wai_count;
|
||||
|
||||
static void wai_unload(void) {
|
||||
free(wai_channels);
|
||||
wai_channels = NULL;
|
||||
wai_count = 0;
|
||||
}
|
||||
|
||||
bool wai_load(const char *path) {
|
||||
wai_unload();
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
uint8_t header[24];
|
||||
if (fread(header, 1, sizeof(header), fp) != sizeof(header) ||
|
||||
header[0] != 'X' || header[1] != 'I' || header[2] != '2' || header[3] != '\0')
|
||||
{
|
||||
WARNING("not WAI file");
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = LittleEndian_getDW(header, 8);
|
||||
if (count <= 0) {
|
||||
WARNING("invalid WAI record count: %d", count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
int version = LittleEndian_getDW(header, 12);
|
||||
if (version != 3) {
|
||||
WARNING("unsupported WAI version: %d", version);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
int *channels = calloc(count, sizeof(*channels));
|
||||
if (!channels) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t record[12];
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (fread(record, 1, sizeof(record), fp) != sizeof(record)) {
|
||||
WARNING("truncated WAI file");
|
||||
free(channels);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
channels[i] = LittleEndian_getDW(record, 8);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
wai_channels = channels;
|
||||
wai_count = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wai_loaded(void) {
|
||||
return wai_channels != NULL;
|
||||
}
|
||||
|
||||
int wai_mixch(int no) {
|
||||
if (!wai_channels || no < 0 || no >= wai_count)
|
||||
return -1;
|
||||
return wai_channels[no];
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* bgi.c: BGI (BGM information) parser
|
||||
* audio_meta.h: BGI (BGM information) / WAI (wave information) parser
|
||||
*
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
@@ -35,4 +35,8 @@ typedef struct {
|
||||
extern bool bgi_read(const char *path);
|
||||
extern bgi_t *bgi_find(int no);
|
||||
|
||||
bool wai_load(const char *path);
|
||||
bool wai_loaded(void);
|
||||
int wai_mixch(int no);
|
||||
|
||||
#endif /* _BGI_H__ */
|
||||
@@ -23,8 +23,9 @@
|
||||
#include "portab.h"
|
||||
#include "nact.h"
|
||||
#include "bgm.h"
|
||||
#include "bgi.h"
|
||||
#include "audio_meta.h"
|
||||
#include "system.h"
|
||||
#include "music_private.h"
|
||||
|
||||
bool musbgm_init(DRIFILETYPE type, int base_no) {
|
||||
if (type == DRIFILE_BGM)
|
||||
@@ -87,3 +88,7 @@ void musbgm_wait(int no, int timeout) {
|
||||
sys_wait_vsync();
|
||||
}
|
||||
}
|
||||
|
||||
void musbgm_reapply_valance(void) {
|
||||
EM_ASM({ xsystem35.cdPlayer.setValance($0); }, prv.volval[BGM_VOLVAL_CH]);
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@ int musbgm_getlen(int no);
|
||||
bool musbgm_isplaying(int no);
|
||||
void musbgm_stopall(int time);
|
||||
void musbgm_wait(int no, int timeout);
|
||||
void musbgm_reapply_valance(void);
|
||||
|
||||
#endif /* __BGM_H__ */
|
||||
|
||||
+19
-6
@@ -32,17 +32,27 @@
|
||||
#include "system.h"
|
||||
#include "nact.h"
|
||||
#include "bgm.h"
|
||||
#include "bgi.h"
|
||||
#include "audio_meta.h"
|
||||
#include "music_private.h"
|
||||
#include "ald_manager.h"
|
||||
|
||||
static DRIFILETYPE dri_type;
|
||||
static int base_no;
|
||||
static int current_no;
|
||||
static int current_vol = 100; // game-requested volume (0-100)
|
||||
static Mix_Music *mix_music;
|
||||
static dridata* dfile;
|
||||
static Uint32 start_time;
|
||||
|
||||
static void apply_music_volume(int vol) {
|
||||
current_vol = vol;
|
||||
Mix_VolumeMusic(current_vol * prv.volval[BGM_VOLVAL_CH] * MIX_MAX_VOLUME / (100 * 100));
|
||||
}
|
||||
|
||||
void musbgm_reapply_valance(void) {
|
||||
apply_music_volume(current_vol);
|
||||
}
|
||||
|
||||
static void free_music() {
|
||||
current_no = 0;
|
||||
if (mix_music) {
|
||||
@@ -101,11 +111,14 @@ bool musbgm_play(int no, int time, int vol, int loop_count) {
|
||||
if (!bgm_load(no))
|
||||
return false;
|
||||
|
||||
// We don't use the loop information in the BGI file, but SDL_mixer
|
||||
// understands loop info in the WAVE's "smpl" chunk.
|
||||
// We don't use the loop positions in the BGI file; SDL_mixer instead
|
||||
// understands loop positions in the WAVE's "smpl" chunk.
|
||||
|
||||
Mix_VolumeMusic(vol * MIX_MAX_VOLUME / 100);
|
||||
if (Mix_FadeInMusic(mix_music, loop_count == 0 ? -1 : loop_count, time * 10) != 0) {
|
||||
apply_music_volume(vol);
|
||||
// SDL_mixer counts repeats after the first play, while System 3.x counts
|
||||
// the total number of plays. In both APIs, an infinite loop is special.
|
||||
int loops = loop_count == 0 ? -1 : loop_count - 1;
|
||||
if (Mix_FadeInMusic(mix_music, loops, time * 10) != 0) {
|
||||
free_music();
|
||||
return false;
|
||||
}
|
||||
@@ -124,7 +137,7 @@ void musbgm_fade(int no, int time, int vol) {
|
||||
return;
|
||||
|
||||
// SDL_mixer doesn't provide arbitrary fading, so just set the volume immediately.
|
||||
Mix_VolumeMusic(vol * MIX_MAX_VOLUME / 100);
|
||||
apply_music_volume(vol);
|
||||
}
|
||||
|
||||
int musbgm_getpos(int no) {
|
||||
|
||||
+162
-115
@@ -1,8 +1,5 @@
|
||||
/*
|
||||
* cache.c general cache manager
|
||||
*
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,130 +16,180 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
/* $Id: cache.c,v 1.5 2003/07/21 23:06:47 chikama Exp $ */
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include "portab.h"
|
||||
#include "cache.h"
|
||||
|
||||
/* maximum cache size (in MB) */
|
||||
#ifndef CACHE_TOTALSIZE
|
||||
#define CACHE_TOTALSIZE 20
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int totalsize; /* total size in cache */
|
||||
static int dummyfalse = 0; /* dummy in_use flag */
|
||||
static int dummytrue = 1; /* dummy in_use flag */
|
||||
#define CACHE_BUCKETS 1024
|
||||
|
||||
/*
|
||||
* static methods
|
||||
*/
|
||||
static void remove_in_cache(cacher *id);
|
||||
typedef struct CacheEntry {
|
||||
struct CacheEntry *hash_next;
|
||||
struct CacheEntry *lru_prev;
|
||||
struct CacheEntry *lru_next;
|
||||
uint32_t hash;
|
||||
size_t cost;
|
||||
void *data;
|
||||
unsigned char key[];
|
||||
} CacheEntry;
|
||||
|
||||
/*
|
||||
* Remove data in cache
|
||||
* id: cache handler
|
||||
*/
|
||||
static void remove_in_cache(cacher *id) {
|
||||
cacheinfo *ip = id->top;
|
||||
cacheinfo *ic = ip->next;
|
||||
if (!ic)
|
||||
return;
|
||||
|
||||
while(ic->next != NULL) {
|
||||
if (!*ic->in_use) {
|
||||
totalsize -= ic->size;
|
||||
ip->next = ic->next;
|
||||
id->free_(ic->data);
|
||||
free(ic);
|
||||
} else {
|
||||
ip = ic;
|
||||
}
|
||||
ic = ip->next;
|
||||
}
|
||||
return;
|
||||
struct Cache {
|
||||
CacheOps ops;
|
||||
CacheEntry *buckets[CACHE_BUCKETS];
|
||||
CacheEntry *lru_head;
|
||||
CacheEntry *lru_tail;
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
size_t count;
|
||||
size_t hits;
|
||||
size_t misses;
|
||||
};
|
||||
|
||||
static bool is_pinned(const Cache *cache, const CacheEntry *entry) {
|
||||
return cache->ops.is_pinned && cache->ops.is_pinned(entry->data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create new cache object
|
||||
* delcallback: callback function for delete cache data object
|
||||
* return: new cache handler
|
||||
*/
|
||||
cacher *cache_new(void *delcallback) {
|
||||
cacher *c = calloc(1, sizeof(cacher));
|
||||
|
||||
c->top = calloc(1, sizeof(cacheinfo));
|
||||
c->top->next = NULL;
|
||||
c->top->in_use = &dummytrue;
|
||||
c->free_ = delcallback;
|
||||
return c;
|
||||
static void lru_remove(Cache *cache, CacheEntry *entry) {
|
||||
if (entry->lru_prev)
|
||||
entry->lru_prev->lru_next = entry->lru_next;
|
||||
else
|
||||
cache->lru_head = entry->lru_next;
|
||||
if (entry->lru_next)
|
||||
entry->lru_next->lru_prev = entry->lru_prev;
|
||||
else
|
||||
cache->lru_tail = entry->lru_prev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert data to cache
|
||||
* id : cache handler
|
||||
* key : data key
|
||||
* data : data to be cached
|
||||
* size : data size
|
||||
* in_use: in_use mark pointer, if in_use is nonzero, dont remove from cache
|
||||
*/
|
||||
void cache_insert(cacher *id, int key, void *data, int size, int *in_use) {
|
||||
cacheinfo *i = id->top;
|
||||
|
||||
if (CACHE_TOTALSIZE <= (totalsize >> 20)) {
|
||||
remove_in_cache(id);
|
||||
}
|
||||
|
||||
while(i->next != NULL) {
|
||||
i = i->next;
|
||||
}
|
||||
|
||||
i->key = key;
|
||||
i->data = data;
|
||||
i->size = size;
|
||||
i->next = calloc(1, sizeof(cacheinfo));
|
||||
i->next->next = NULL;
|
||||
if (in_use) {
|
||||
i->in_use = in_use;
|
||||
} else {
|
||||
i->in_use = &dummyfalse;
|
||||
}
|
||||
totalsize += size;
|
||||
static void lru_prepend(Cache *cache, CacheEntry *entry) {
|
||||
entry->lru_prev = NULL;
|
||||
entry->lru_next = cache->lru_head;
|
||||
if (cache->lru_head)
|
||||
cache->lru_head->lru_prev = entry;
|
||||
else
|
||||
cache->lru_tail = entry;
|
||||
cache->lru_head = entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search data in cache
|
||||
* id : cache handler
|
||||
* key: data search key
|
||||
* return: pointer to cached data
|
||||
*/
|
||||
void *cache_foreach(cacher *id, int key) {
|
||||
cacheinfo *i = id->top;
|
||||
|
||||
while(i != NULL) {
|
||||
if (i->key == key) {
|
||||
return i->data;
|
||||
}
|
||||
i = i->next;
|
||||
static void remove_entry(Cache *cache, CacheEntry *entry) {
|
||||
CacheEntry **link = &cache->buckets[entry->hash % CACHE_BUCKETS];
|
||||
while (*link != entry)
|
||||
link = &(*link)->hash_next;
|
||||
*link = entry->hash_next;
|
||||
lru_remove(cache, entry);
|
||||
cache->size -= entry->cost;
|
||||
cache->count--;
|
||||
cache->ops.destroy(entry->data);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
static CacheEntry *find_entry(Cache *cache, const void *key, uint32_t hash) {
|
||||
for (CacheEntry *entry = cache->buckets[hash % CACHE_BUCKETS]; entry;
|
||||
entry = entry->hash_next) {
|
||||
if (entry->hash == hash && cache->ops.equal(entry->key, key))
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cache_clear(cacher *id) {
|
||||
cacheinfo *ic = id->top->next;
|
||||
cacheinfo *next;
|
||||
|
||||
while (ic != NULL) {
|
||||
next = ic->next;
|
||||
if (ic->data) {
|
||||
totalsize -= ic->size;
|
||||
id->free_(ic->data);
|
||||
}
|
||||
free(ic);
|
||||
ic = next;
|
||||
}
|
||||
id->top->next = NULL;
|
||||
Cache *cache_new(size_t capacity, const CacheOps *ops) {
|
||||
if (!ops || !ops->key_size || !ops->hash || !ops->equal || !ops->destroy)
|
||||
return NULL;
|
||||
Cache *cache = calloc(1, sizeof(Cache));
|
||||
if (!cache)
|
||||
return NULL;
|
||||
cache->ops = *ops;
|
||||
cache->capacity = capacity;
|
||||
return cache;
|
||||
}
|
||||
|
||||
void cache_destroy(Cache *cache) {
|
||||
if (!cache)
|
||||
return;
|
||||
while (cache->lru_tail)
|
||||
remove_entry(cache, cache->lru_tail);
|
||||
free(cache);
|
||||
}
|
||||
|
||||
void *cache_get(Cache *cache, const void *key) {
|
||||
if (!cache || !key)
|
||||
return NULL;
|
||||
CacheEntry *entry = find_entry(cache, key, cache->ops.hash(key));
|
||||
if (!entry) {
|
||||
cache->misses++;
|
||||
return NULL;
|
||||
}
|
||||
cache->hits++;
|
||||
lru_remove(cache, entry);
|
||||
lru_prepend(cache, entry);
|
||||
return entry->data;
|
||||
}
|
||||
|
||||
CacheInsertResult cache_insert(Cache *cache, const void *key, void *data, size_t cost) {
|
||||
if (!cache || !key || !data)
|
||||
return CACHE_INSERT_NOMEM;
|
||||
uint32_t hash = cache->ops.hash(key);
|
||||
if (find_entry(cache, key, hash))
|
||||
return CACHE_INSERT_EXISTS;
|
||||
if (cost > cache->capacity)
|
||||
return CACHE_INSERT_FULL;
|
||||
|
||||
CacheEntry *entry = malloc(sizeof(CacheEntry) + cache->ops.key_size);
|
||||
if (!entry)
|
||||
return CACHE_INSERT_NOMEM;
|
||||
|
||||
for (CacheEntry *old = cache->lru_tail, *prev;
|
||||
old && cache->size > cache->capacity - cost; old = prev) {
|
||||
prev = old->lru_prev;
|
||||
if (!is_pinned(cache, old))
|
||||
remove_entry(cache, old);
|
||||
}
|
||||
if (cache->size > cache->capacity - cost) {
|
||||
free(entry);
|
||||
return CACHE_INSERT_FULL;
|
||||
}
|
||||
|
||||
entry->hash = hash;
|
||||
entry->cost = cost;
|
||||
entry->data = data;
|
||||
memcpy(entry->key, key, cache->ops.key_size);
|
||||
unsigned bucket = hash % CACHE_BUCKETS;
|
||||
entry->hash_next = cache->buckets[bucket];
|
||||
cache->buckets[bucket] = entry;
|
||||
lru_prepend(cache, entry);
|
||||
cache->size += cost;
|
||||
cache->count++;
|
||||
return CACHE_INSERT_OK;
|
||||
}
|
||||
|
||||
bool cache_remove(Cache *cache, const void *key) {
|
||||
if (!cache || !key)
|
||||
return false;
|
||||
CacheEntry *entry = find_entry(cache, key, cache->ops.hash(key));
|
||||
if (!entry || is_pinned(cache, entry))
|
||||
return false;
|
||||
remove_entry(cache, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t cache_clear(Cache *cache) {
|
||||
if (!cache)
|
||||
return 0;
|
||||
for (CacheEntry *entry = cache->lru_tail, *prev; entry; entry = prev) {
|
||||
prev = entry->lru_prev;
|
||||
if (!is_pinned(cache, entry))
|
||||
remove_entry(cache, entry);
|
||||
}
|
||||
return cache->count;
|
||||
}
|
||||
|
||||
CacheStats cache_get_stats(const Cache *cache) {
|
||||
if (!cache)
|
||||
return (CacheStats){0};
|
||||
return (CacheStats){
|
||||
.count = cache->count,
|
||||
.size = cache->size,
|
||||
.capacity = cache->capacity,
|
||||
.hits = cache->hits,
|
||||
.misses = cache->misses,
|
||||
};
|
||||
}
|
||||
|
||||
+46
-28
@@ -1,8 +1,5 @@
|
||||
/*
|
||||
* cache.h general cache manager
|
||||
*
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,33 +16,54 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
/* $Id: cache.h,v 1.2 2003/07/21 23:06:47 chikama Exp $ */
|
||||
#ifndef XSYSTEM35_CACHE_H
|
||||
#define XSYSTEM35_CACHE_H
|
||||
|
||||
#ifndef __CASHE__
|
||||
#define __CASHE__
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "portab.h"
|
||||
typedef struct Cache Cache;
|
||||
|
||||
/* cache controlr infomartion */
|
||||
struct _cacheinfo {
|
||||
int key; /* key of data */
|
||||
int size; /* data size */
|
||||
struct _cacheinfo *next; /* next data */
|
||||
int *in_use; /* if *in_use is nonzero, dont remove from cache */
|
||||
void *data; /* real data */
|
||||
};
|
||||
typedef struct _cacheinfo cacheinfo;
|
||||
typedef struct {
|
||||
size_t key_size;
|
||||
uint32_t (*hash)(const void *key);
|
||||
bool (*equal)(const void *a, const void *b);
|
||||
void (*destroy)(void *data);
|
||||
bool (*is_pinned)(const void *data);
|
||||
} CacheOps;
|
||||
|
||||
/* cache handler */
|
||||
struct _cacher {
|
||||
void (*free_)(void *); /* free data callback */
|
||||
struct _cacheinfo *top; /* pointer to data */
|
||||
};
|
||||
typedef struct _cacher cacher;
|
||||
typedef enum {
|
||||
CACHE_INSERT_OK,
|
||||
CACHE_INSERT_EXISTS,
|
||||
CACHE_INSERT_FULL,
|
||||
CACHE_INSERT_NOMEM,
|
||||
} CacheInsertResult;
|
||||
|
||||
extern cacher *cache_new(void *delcallback);
|
||||
extern void cache_insert(cacher *id, int key, void *data, int size, int *in_use);
|
||||
extern void *cache_foreach(cacher *id, int key);
|
||||
extern void cache_clear(cacher *id);
|
||||
typedef struct {
|
||||
size_t count;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
size_t hits;
|
||||
size_t misses;
|
||||
} CacheStats;
|
||||
|
||||
#endif /* !__CASHE__ */
|
||||
Cache *cache_new(size_t capacity, const CacheOps *ops);
|
||||
void cache_destroy(Cache *cache);
|
||||
|
||||
/* The returned pointer is owned by the cache and is invalidated by removal. */
|
||||
void *cache_get(Cache *cache, const void *key);
|
||||
|
||||
/* Ownership of data is transferred only when CACHE_INSERT_OK is returned. */
|
||||
CacheInsertResult cache_insert(Cache *cache, const void *key, void *data, size_t cost);
|
||||
|
||||
/* Pinned entries cannot be removed. */
|
||||
bool cache_remove(Cache *cache, const void *key);
|
||||
|
||||
/* Removes all unpinned entries and returns the number of entries left. */
|
||||
size_t cache_clear(Cache *cache);
|
||||
|
||||
/* Returns zero-filled statistics when cache is NULL. */
|
||||
CacheStats cache_get_stats(const Cache *cache);
|
||||
|
||||
#endif /* XSYSTEM35_CACHE_H */
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2026 <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "unittest.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
int value;
|
||||
bool pinned;
|
||||
} TestValue;
|
||||
|
||||
static int destroyed;
|
||||
|
||||
static uint32_t int_hash(const void *key) {
|
||||
return (uint32_t)*(const int *)key;
|
||||
}
|
||||
|
||||
static bool int_equal(const void *a, const void *b) {
|
||||
return *(const int *)a == *(const int *)b;
|
||||
}
|
||||
|
||||
static void destroy_value(void *data) {
|
||||
destroyed++;
|
||||
free(data);
|
||||
}
|
||||
|
||||
static bool value_is_pinned(const void *data) {
|
||||
return ((const TestValue *)data)->pinned;
|
||||
}
|
||||
|
||||
static TestValue *new_value(int value) {
|
||||
TestValue *data = malloc(sizeof(TestValue));
|
||||
ASSERT_TRUE(data);
|
||||
data->value = value;
|
||||
data->pinned = false;
|
||||
return data;
|
||||
}
|
||||
|
||||
void cache_test(void) {
|
||||
CacheOps ops = {
|
||||
.key_size = sizeof(int),
|
||||
.hash = int_hash,
|
||||
.equal = int_equal,
|
||||
.destroy = destroy_value,
|
||||
.is_pinned = value_is_pinned,
|
||||
};
|
||||
Cache *cache = cache_new(2, &ops);
|
||||
ASSERT_TRUE(cache);
|
||||
destroyed = 0;
|
||||
CacheStats stats = cache_get_stats(cache);
|
||||
ASSERT_EQUAL(stats.count, 0);
|
||||
ASSERT_EQUAL(stats.size, 0);
|
||||
ASSERT_EQUAL(stats.capacity, 2);
|
||||
ASSERT_EQUAL(stats.hits, 0);
|
||||
ASSERT_EQUAL(stats.misses, 0);
|
||||
|
||||
// A lookup makes k1 most-recently used, so inserting k3 evicts k2.
|
||||
int k1 = 1, k2 = 2, k3 = 3, k4 = 4, k5 = 5;
|
||||
ASSERT_EQUAL(cache_insert(cache, &k1, new_value(1), 1), CACHE_INSERT_OK);
|
||||
ASSERT_EQUAL(cache_insert(cache, &k2, new_value(2), 1), CACHE_INSERT_OK);
|
||||
stats = cache_get_stats(cache);
|
||||
ASSERT_EQUAL(stats.count, 2);
|
||||
ASSERT_EQUAL(stats.size, 2);
|
||||
ASSERT_EQUAL(stats.capacity, 2);
|
||||
ASSERT_EQUAL(((TestValue *)cache_get(cache, &k1))->value, 1);
|
||||
ASSERT_EQUAL(cache_insert(cache, &k3, new_value(3), 1), CACHE_INSERT_OK);
|
||||
ASSERT_NULL(cache_get(cache, &k2));
|
||||
stats = cache_get_stats(cache);
|
||||
ASSERT_EQUAL(stats.hits, 1);
|
||||
ASSERT_EQUAL(stats.misses, 1);
|
||||
|
||||
// Pinned entries survive both capacity eviction and cache_clear().
|
||||
((TestValue *)cache_get(cache, &k1))->pinned = true;
|
||||
ASSERT_EQUAL(cache_insert(cache, &k4, new_value(4), 1), CACHE_INSERT_OK);
|
||||
ASSERT_NULL(cache_get(cache, &k3));
|
||||
((TestValue *)cache_get(cache, &k4))->pinned = true;
|
||||
|
||||
// Insertion fails when every remaining entry is pinned.
|
||||
TestValue *rejected = new_value(5);
|
||||
ASSERT_EQUAL(cache_insert(cache, &k5, rejected, 1), CACHE_INSERT_FULL);
|
||||
ASSERT_EQUAL(((TestValue *)cache_get(cache, &k1))->value, 1);
|
||||
ASSERT_EQUAL(((TestValue *)cache_get(cache, &k4))->value, 4);
|
||||
ASSERT_EQUAL(cache_clear(cache), 2);
|
||||
ASSERT_EQUAL(destroyed, 2);
|
||||
free(rejected);
|
||||
|
||||
// Once unpinned, cache_clear() destroys the remaining entries.
|
||||
((TestValue *)cache_get(cache, &k1))->pinned = false;
|
||||
((TestValue *)cache_get(cache, &k4))->pinned = false;
|
||||
ASSERT_EQUAL(cache_clear(cache), 0);
|
||||
ASSERT_EQUAL(destroyed, 4);
|
||||
cache_destroy(cache);
|
||||
|
||||
stats = cache_get_stats(NULL);
|
||||
ASSERT_EQUAL(stats.count, 0);
|
||||
ASSERT_EQUAL(stats.size, 0);
|
||||
ASSERT_EQUAL(stats.capacity, 0);
|
||||
ASSERT_EQUAL(stats.hits, 0);
|
||||
ASSERT_EQUAL(stats.misses, 0);
|
||||
}
|
||||
+9
-9
@@ -24,10 +24,10 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "portab.h"
|
||||
#include "system.h"
|
||||
#include "cali.h"
|
||||
#include "variable.h"
|
||||
#include "scenario.h"
|
||||
#include "nact.h"
|
||||
#include "xsystem35.h"
|
||||
|
||||
#define OP_AND 0x74
|
||||
#define OP_OR 0x75
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
#define CALI_DEPTH_MAX 256
|
||||
|
||||
static int *getVar(int c0, struct VarRef *ref) {
|
||||
static vmvar_t *getVar(int c0, struct VarRef *ref) {
|
||||
int addr = sl_getIndex();
|
||||
int var;
|
||||
if ((c0 & 0x40) == 0) {
|
||||
@@ -63,7 +63,7 @@ static int *getVar(int c0, struct VarRef *ref) {
|
||||
c1 = sl_getc();
|
||||
var = c0 << 8 | c1;
|
||||
int index = getCaliValue();
|
||||
int *store = v_ref_indexed(var, index, ref);
|
||||
vmvar_t *store = v_ref_indexed(var, index, ref);
|
||||
if (!store)
|
||||
WARNING("%03d:%05x: Out of bounds index access: %s[%d]", sl_getPage(), addr, v_name(var), index);
|
||||
return store;
|
||||
@@ -74,15 +74,15 @@ static int *getVar(int c0, struct VarRef *ref) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
int *store = v_ref(var, ref);
|
||||
vmvar_t *store = v_ref(var, ref);
|
||||
if (!store)
|
||||
WARNING("%03d:%05x: Out of bounds array access: %s", sl_getPage(), addr, v_name(var));
|
||||
return store;
|
||||
}
|
||||
|
||||
// Returns a pointer to the variable
|
||||
int *getCaliVariable(void) {
|
||||
int *c0 = getVar(sl_getc(), NULL);
|
||||
vmvar_t *getCaliVariable(void) {
|
||||
vmvar_t *c0 = getVar(sl_getc(), NULL);
|
||||
if (sl_getc() != OP_END) {
|
||||
SYSERROR("Invalid variable expression at %03d:%05x", sl_getPage(), sl_getIndex());
|
||||
}
|
||||
@@ -98,7 +98,7 @@ bool getCaliArray(struct VarRef *ref) {
|
||||
}
|
||||
|
||||
// For variable assignment commands
|
||||
int *getVariable(void) {
|
||||
vmvar_t *getVariable(void) {
|
||||
return getVar(sl_getc(), NULL);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ int getCaliValue(void) {
|
||||
while ((c0 = sl_getc()) != OP_END) {
|
||||
if (c0 & 0x80) { // variable
|
||||
int c1 = sl_getcAt(sl_getIndex());
|
||||
int *t;
|
||||
vmvar_t *t;
|
||||
if (c0 == 0xc0) {
|
||||
if (c1 == OP_C0_INDEX || c1 >= 0x34) goto l_var;
|
||||
c1 = sl_getc();
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* s39init.h System39.ini read
|
||||
* cali.h 計算式の評価
|
||||
*
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
@@ -19,17 +19,17 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
/* $Id: s39init.h,v 1.1 2003/04/25 17:23:55 chikama Exp $ */
|
||||
|
||||
#ifndef __S39INIT_H__
|
||||
#define __S39INIT_H__
|
||||
#ifndef __CALI_H__
|
||||
#define __CALI_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "portab.h"
|
||||
|
||||
bool s39ini_init(void);
|
||||
void s39ini_winopen(void);
|
||||
void s39ini_winclose(void);
|
||||
void s39ini_setvol(void);
|
||||
void s39ini_remove(void);
|
||||
struct VarRef;
|
||||
|
||||
#endif
|
||||
int getCaliValue(void);
|
||||
vmvar_t *getCaliVariable(void);
|
||||
bool getCaliArray(struct VarRef *ref);
|
||||
vmvar_t *getVariable(void);
|
||||
|
||||
#endif /* !__CALI_H__ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user