mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Compare commits
263
Commits
1.0.0
...
04333ad689
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04333ad689 | ||
|
|
960c97ecc9 | ||
|
|
e61f4b3c42 | ||
|
|
03f9e7e87e | ||
|
|
87e4bebb12 | ||
|
|
3a0dc49340 | ||
|
|
e93cddad76 | ||
|
|
461eca1a85 | ||
|
|
6ea8af95ba | ||
|
|
167d75d092 | ||
|
|
275d71712e | ||
|
|
d68baa74a0 | ||
|
|
267f7bb762 | ||
|
|
addc26ca45 | ||
|
|
bc745035e1 | ||
|
|
11ad7a583a | ||
|
|
9654950ab4 | ||
|
|
021187752b | ||
|
|
5cc163ab16 | ||
|
|
e14b4bca79 | ||
|
|
02f29b6c6d | ||
|
|
fa09b0059f | ||
|
|
4233c7d294 | ||
|
|
b30a107fda | ||
|
|
bf3430dbad | ||
|
|
135cae216f | ||
|
|
05820dc536 | ||
|
|
e76762d11d | ||
|
|
5fb3c49f75 | ||
|
|
5d032d739e | ||
|
|
e3bf653165 | ||
|
|
a19c200870 | ||
|
|
5cbc697f23 | ||
|
|
4e20a499d4 | ||
|
|
0d4eb2a2a7 | ||
|
|
d800e12f59 | ||
|
|
b1f8948b74 | ||
|
|
ebc319994e | ||
|
|
9bb871d9fd | ||
|
|
62d38aaf59 | ||
|
|
5bb36c7524 | ||
|
|
4dc9792632 | ||
|
|
a7e973c0a9 | ||
|
|
33f1295237 | ||
|
|
e699fb0aa2 | ||
|
|
4e0dd82776 | ||
|
|
77d9e77053 | ||
|
|
d65a1e6d9b | ||
|
|
68e8257c27 | ||
|
|
8e339d1537 | ||
|
|
a2524bafd5 | ||
|
|
f146320e36 | ||
|
|
09a0e3329a | ||
|
|
b68e1622fd | ||
|
|
433e3be3e6 | ||
|
|
d5b27a0a47 | ||
|
|
a38c55602d | ||
|
|
b3bcd79bae | ||
|
|
f0c4db718d | ||
|
|
d17fc30731 | ||
|
|
ca2254360c | ||
|
|
81f9113906 | ||
|
|
d63e71e85d | ||
|
|
6d05546c2c | ||
|
|
e260b82828 | ||
|
|
c0cdf4f070 | ||
|
|
74461f5b9c | ||
|
|
d8479ad80a | ||
|
|
e8bd5ab036 | ||
|
|
05fee4ba83 | ||
|
|
bf3b3416fe | ||
|
|
fb3d0bbd3e | ||
|
|
507b65fc9c | ||
|
|
24df15b9e9 | ||
|
|
fab789df65 | ||
|
|
3012faa6ff | ||
|
|
6dbb5baf19 | ||
|
|
db8279ebc3 | ||
|
|
250bc82818 | ||
|
|
4843e4e87a | ||
|
|
1051294247 | ||
|
|
fa98a76c42 | ||
|
|
d073875d94 | ||
|
|
2b67a9e000 | ||
|
|
b62edb7188 | ||
|
|
85547ef4f4 | ||
|
|
f108b7f436 | ||
|
|
90c3ab5008 | ||
|
|
7b9565fe25 | ||
|
|
4dcc50d6ca | ||
|
|
5c47ad5aae | ||
|
|
062713f70e | ||
|
|
0e8bd36407 | ||
|
|
28554137cc | ||
|
|
32024bc805 | ||
|
|
ec9bd95dc9 | ||
|
|
8dee7d8658 | ||
|
|
0f73ded483 | ||
|
|
847987ec1b | ||
|
|
ec4b608877 | ||
|
|
12735d5c8c | ||
|
|
22d58d977a | ||
|
|
20e0d00661 | ||
|
|
a706a41dcb | ||
|
|
bf0b2bb44f | ||
|
|
916daec6b2 | ||
|
|
1fcccb766c | ||
|
|
aa112849de | ||
|
|
8ca1a31ccc | ||
|
|
2c622bb331 | ||
|
|
c01a27583c | ||
|
|
ced4daa331 | ||
|
|
9eee82c13b | ||
|
|
f53ec98d53 | ||
|
|
32f4253948 | ||
|
|
cb0b969492 | ||
|
|
0030e28471 | ||
|
|
224db2f9cc | ||
|
|
33d07c190e | ||
|
|
a4dd6a42df | ||
|
|
da268288c3 | ||
|
|
f535223988 | ||
|
|
f9087eb7ac | ||
|
|
636743478c | ||
|
|
a2f087b7f9 | ||
|
|
07bd9c7438 | ||
|
|
028c907a12 | ||
|
|
c45bb3a736 | ||
|
|
484f383826 | ||
|
|
af12e19859 | ||
|
|
e83cb1c91e | ||
|
|
4c4a62e1ed | ||
|
|
f01e41be12 | ||
|
|
6bb00a268c | ||
|
|
9ed1f52627 | ||
|
|
59789c0a8a | ||
|
|
13d0c51351 | ||
|
|
46bdbb05ef | ||
|
|
234b04b460 | ||
|
|
ae9544a1ac | ||
|
|
6509dca693 | ||
|
|
bf455de35d | ||
|
|
511f6a4bfa | ||
|
|
4239345289 | ||
|
|
955950198e | ||
|
|
63ab438391 | ||
|
|
1dd37559bb | ||
|
|
f52f4bd1f0 | ||
|
|
1b87d3f9a5 | ||
|
|
15f70f29a0 | ||
|
|
df6998c0f9 | ||
|
|
2f436ce5bf | ||
|
|
c4f89d040a | ||
|
|
93c790dcc6 | ||
|
|
88f199a8f2 | ||
|
|
28c8c277c9 | ||
|
|
e5087f0757 | ||
|
|
6dc62bffc8 | ||
|
|
0ea5d90797 | ||
|
|
d20f52cf79 | ||
|
|
dcbd149409 | ||
|
|
1655c8a8e0 | ||
|
|
2a99c75e4d | ||
|
|
70336546ed | ||
|
|
cb24db29c2 | ||
|
|
03ef51e76a | ||
|
|
a0d78a024c | ||
|
|
dc105aeb16 | ||
|
|
c877e261c5 | ||
|
|
39c0094ce8 | ||
|
|
93ca13a50b | ||
|
|
c540eb9796 | ||
|
|
753e202cea | ||
|
|
f884a58a77 | ||
|
|
2bc9a3d2cb | ||
|
|
1e29930f4c | ||
|
|
887eebf31d | ||
|
|
96d46069ce | ||
|
|
9192bc80c5 | ||
|
|
003ab16c72 | ||
|
|
03062d5d61 | ||
|
|
0bf5aaa8c8 | ||
|
|
fe952354c5 | ||
|
|
f43ca3a31a | ||
|
|
52f2948e19 | ||
|
|
92b2dd1990 | ||
|
|
f8ce39a324 | ||
|
|
9562e0d1c3 | ||
|
|
623b3d3407 | ||
|
|
39886c30bd | ||
|
|
1ad496c28d | ||
|
|
039f1c9a2e | ||
|
|
caa593d00b | ||
|
|
38610d93bb | ||
|
|
65fa898d9f | ||
|
|
547e8158a2 | ||
|
|
197b89620c | ||
|
|
ecc04bc9bb | ||
|
|
65ff73ca06 | ||
|
|
2ad0de6663 | ||
|
|
5f318e859e | ||
|
|
efddc4f128 | ||
|
|
d93bb91d72 | ||
|
|
49ee259d2c | ||
|
|
3c362e9db6 | ||
|
|
1ad7cbb2c5 | ||
|
|
a476b1d892 | ||
|
|
d4121032db | ||
|
|
9474e321e1 | ||
|
|
d130f38e74 | ||
|
|
e66fe928e5 | ||
|
|
134168d864 | ||
|
|
6e755b21ef | ||
|
|
a5d488564a | ||
|
|
205e0a3ed0 | ||
|
|
b758a04760 | ||
|
|
9c962e3467 | ||
|
|
02129edc10 | ||
|
|
5ac79311b9 | ||
|
|
75cfc9f18d | ||
|
|
eaa7f3d8d0 | ||
|
|
0d7ef9b577 | ||
|
|
4a7fc4d12a | ||
|
|
e662955a1d | ||
|
|
a3c23387f4 | ||
|
|
f23125cb77 | ||
|
|
30ac11e74b | ||
|
|
f40132497c | ||
|
|
7540712de1 | ||
|
|
6a75bc5a6b | ||
|
|
a1a809faef | ||
|
|
3929de81f1 | ||
|
|
65ccb29423 | ||
|
|
9f3af7b256 | ||
|
|
249bc06f53 | ||
|
|
621eb49be8 | ||
|
|
5f13b3e9d0 | ||
|
|
15cfc5463b | ||
|
|
ed2239fb1b | ||
|
|
5980e49a96 | ||
|
|
a6871623f2 | ||
|
|
477bbdb949 | ||
|
|
0baa493f79 | ||
|
|
2c51bec001 | ||
|
|
d6b5bde80c | ||
|
|
2510fa6b9b | ||
|
|
8fecf72281 | ||
|
|
93ce40bcdd | ||
|
|
4f2d367b19 | ||
|
|
74bdbd4f37 | ||
|
|
2c8fc425db | ||
|
|
f59158648f | ||
|
|
484b643f02 | ||
|
|
d1fdab4cde | ||
|
|
43c6a2804c | ||
|
|
180c81e5f5 | ||
|
|
e29739f0be | ||
|
|
1e682bad09 | ||
|
|
0a4981533b | ||
|
|
998ab23dab | ||
|
|
dc8342cbab | ||
|
|
8d9eb931f6 | ||
|
|
1621378ba1 |
@@ -0,0 +1,194 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
linux-build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
build-type: ["debug", "release"]
|
||||
|
||||
name: Linux ${{ matrix.build-type }}
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install flex bison meson libfreetype-dev libglew-dev libffi-dev libpng-dev libsndfile-dev libturbojpeg0-dev libwebp-dev libsdl2-dev zlib1g-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p out/${{ matrix.build-type }}
|
||||
meson -Dbuildtype=${{ matrix.build-type }} out/${{ matrix.build-type }}
|
||||
ninja -C out/${{ matrix.build-type }}
|
||||
|
||||
- name: Test
|
||||
run: out/${{ matrix.build-type }}/src/xsystem4 test/Run/test.ain
|
||||
|
||||
flatpak-build:
|
||||
name: Flatpak
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
|
||||
options: --privileged
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: xsystem4-linux-x86_64-nightly.flatpak
|
||||
manifest-path: technology.haniwa.xsystem4.yml
|
||||
cache-key: flatpak-builder-${{ github.sha }}
|
||||
|
||||
windows-build:
|
||||
runs-on: windows-latest
|
||||
name: Windows
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
install: >-
|
||||
base-devel
|
||||
git
|
||||
nasm
|
||||
diffutils
|
||||
zip
|
||||
mingw-w64-x86_64-gcc
|
||||
mingw-w64-x86_64-meson
|
||||
mingw-w64-x86_64-pkg-config
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-freetype
|
||||
mingw-w64-x86_64-libjpeg-turbo
|
||||
mingw-w64-x86_64-libwebp
|
||||
mingw-w64-x86_64-libpng
|
||||
mingw-w64-x86_64-libsndfile
|
||||
mingw-w64-x86_64-glew
|
||||
|
||||
- name: Cache FFmpeg
|
||||
id: cache-ffmpeg
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: FFmpeg/destdir
|
||||
key: ${{ runner.os }}-ffmpeg-n7.1.2
|
||||
|
||||
- name: Build FFmpeg
|
||||
if: ${{ steps.cache-ffmpeg.outputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
git clone --depth 1 --branch n7.1.2 https://github.com/FFmpeg/FFmpeg.git
|
||||
cd FFmpeg
|
||||
./configure --disable-everything \
|
||||
--enable-decoder=mpegvideo \
|
||||
--enable-decoder=mpeg1video \
|
||||
--enable-decoder=mpeg2video \
|
||||
--enable-decoder=mp2 \
|
||||
--enable-parser=mpegaudio \
|
||||
--enable-parser=mpegvideo \
|
||||
--enable-demuxer=mpegps \
|
||||
--enable-demuxer=mpegts \
|
||||
--enable-demuxer=mpegtsraw \
|
||||
--enable-demuxer=mpegvideo \
|
||||
--enable-decoder=vc1 \
|
||||
--enable-decoder=wmapro \
|
||||
--enable-parser=vc1 \
|
||||
--enable-hwaccel=vc1_d3d11va \
|
||||
--enable-hwaccel=vc1_d3d11va2 \
|
||||
--enable-hwaccel=vc1_dxva2 \
|
||||
--enable-demuxer=asf \
|
||||
--enable-protocol=file \
|
||||
--enable-filter=scale \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--extra-libs=-static \
|
||||
--extra-cflags=--static \
|
||||
--prefix=/mingw64
|
||||
make -j$(nproc)
|
||||
mkdir destdir
|
||||
DESTDIR=destdir make install
|
||||
cd ..
|
||||
|
||||
- name: Install FFmpeg
|
||||
run: |
|
||||
cp -a FFmpeg/destdir/. /
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir debug
|
||||
meson debug -Dbuildtype=debug
|
||||
ninja -C debug
|
||||
mkdir release
|
||||
meson release -Dbuildtype=release
|
||||
ninja -C release
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
debug/src/xsystem4.exe test/Run/test.ain
|
||||
release/src/xsystem4.exe test/Run/test.ain
|
||||
|
||||
- name: Copy licenses
|
||||
run: |
|
||||
ls /mingw64/share/licenses
|
||||
mkdir dist
|
||||
cp -r /mingw64/share/licenses dist/
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
cp release/src/xsystem4.exe COPYING README.md game_compatibility.md dist/
|
||||
cp debug/src/xsystem4.exe dist/xsystem4-debug.exe
|
||||
cp /mingw64/bin/glew32.dll dist/
|
||||
cp extra/*.lnk dist/
|
||||
cp -r fonts dist/
|
||||
cp -r shaders dist/
|
||||
mv dist xsystem4
|
||||
zip -r -9 xsystem4-windows-x86_64-nightly.zip xsystem4
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xsystem4
|
||||
path: xsystem4-windows-x86_64-nightly.zip
|
||||
|
||||
nightly:
|
||||
name: Update nightly release
|
||||
needs: [windows-build, flatpak-build]
|
||||
if: ${{ github.repository == 'nunuhara/xsystem4' && github.ref == 'refs/heads/master' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download release artifacts
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Update nightly tag
|
||||
uses: richardsimko/update-tag@v1
|
||||
with:
|
||||
tag_name: nightly
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Release nightly
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
prerelease: true
|
||||
name: nightly
|
||||
tag_name: nightly
|
||||
files: |
|
||||
xsystem4-windows-x86_64-nightly.zip
|
||||
xsystem4-linux-x86_64-nightly.flatpak
|
||||
fail_on_unmatched_files: true
|
||||
@@ -79,10 +79,13 @@ target_sources(xsystem4 PRIVATE
|
||||
src/3d/collision.c
|
||||
src/3d/debug.c
|
||||
src/3d/model.c
|
||||
src/3d/mpr.c
|
||||
src/3d/parser.c
|
||||
src/3d/particle.c
|
||||
src/3d/reign.c
|
||||
src/3d/renderer.c
|
||||
src/3d/s3de.c
|
||||
src/3d/save.c
|
||||
|
||||
src/dungeon/dgn.c
|
||||
src/dungeon/dtx.c
|
||||
@@ -98,7 +101,10 @@ target_sources(xsystem4 PRIVATE
|
||||
src/parts/construction.c
|
||||
src/parts/debug.c
|
||||
src/parts/flash.c
|
||||
src/parts/flat.c
|
||||
src/parts/input.c
|
||||
src/parts/layoutbox.c
|
||||
src/parts/message.c
|
||||
src/parts/motion.c
|
||||
src/parts/parts.c
|
||||
src/parts/render.c
|
||||
@@ -107,6 +113,7 @@ target_sources(xsystem4 PRIVATE
|
||||
|
||||
src/hll/ACXLoader.c
|
||||
src/hll/ADVSYS.c
|
||||
src/hll/AFAFactory.c
|
||||
src/hll/AliceLogo.c
|
||||
src/hll/AliceLogo2.c
|
||||
src/hll/AliceLogo3.c
|
||||
@@ -131,6 +138,7 @@ target_sources(xsystem4 PRIVATE
|
||||
src/hll/DALKEDemo.c
|
||||
src/hll/Data.c
|
||||
src/hll/DataFile.c
|
||||
src/hll/Discord.c
|
||||
src/hll/DrawDungeon.c
|
||||
src/hll/DrawEffect.c
|
||||
src/hll/DrawGraph.c
|
||||
@@ -155,6 +163,7 @@ target_sources(xsystem4 PRIVATE
|
||||
src/hll/InputString.c
|
||||
src/hll/KiwiSoundEngine.c
|
||||
src/hll/LoadCG.c
|
||||
src/hll/MADLoader.c
|
||||
src/hll/MainEXFile.c
|
||||
src/hll/MainSurface.c
|
||||
src/hll/MamanyoDemo.c
|
||||
@@ -170,6 +179,8 @@ target_sources(xsystem4 PRIVATE
|
||||
src/hll/MusicSystem.c
|
||||
src/hll/NewFont.c
|
||||
src/hll/OutputLog.c
|
||||
src/hll/P3MapSprite.c
|
||||
src/hll/P3SquareSprite.c
|
||||
src/hll/PassRegister.c
|
||||
src/hll/PastelChime2.c
|
||||
src/hll/PartsEngine.c
|
||||
@@ -211,6 +222,7 @@ target_sources(xsystem4 PRIVATE
|
||||
src/hll/vmSurface.c
|
||||
src/hll/vmSystem.c
|
||||
src/hll/vmTimer.c
|
||||
src/hll/ValueEncryption.c
|
||||
src/hll/VSFile.c
|
||||
)
|
||||
|
||||
|
||||
Binary file not shown.
+15
-2
@@ -77,8 +77,21 @@ issues, please let me know or open a pull request to update this table.
|
||||
| ランス・クエスト | Supported | |
|
||||
| ランス・クエスト マグナム | Supported | |
|
||||
| 母娘乱館 | Unknown | |
|
||||
| パステルチャイム3 バインドシーカー | Unsupported | |
|
||||
| パステルチャイム3 バインドシーカー | Supported | |
|
||||
| パスチャ3 プラスコンテンツ | Supported | |
|
||||
| どらぺこ! ~おねだりドラゴンとおっぱい勇者~ | Unsupported | |
|
||||
| ランス01 光をもとめて | Supported | |
|
||||
| Rance 01 - Quest for Hikari (EN) | Supported | |
|
||||
| All games released after Rance 01 | Unsupported | |
|
||||
| ランスIX-ヘルマン革命- | Supported | |
|
||||
| Rance IX - The Helmanian Revolution (EN) | Unknown | |
|
||||
| 武想少女隊ぶれいど☆ブライダーズ | Supported | |
|
||||
| イブニクル | Unsupported | |
|
||||
| Evenicle (EN) | Unsupported | |
|
||||
| ランス03 リーザス陥落 | Unsupported | |
|
||||
| Rance 03 -The Fall of Leazas- (EN) | Unsupported | |
|
||||
| 妻みぐい3 | Unsupported | |
|
||||
| は~とふるママン | Unsupported | |
|
||||
| 超昂神騎エクシール | Unsupported | |
|
||||
| Beat Valkyrie Ixseal (EN) | Unsupported | |
|
||||
| Rance X -決戦- | Unsupported | |
|
||||
| All games released after Rance X | Unsupported | |
|
||||
|
||||
+6
-1
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define AUDIO_NO_METADATA -1
|
||||
|
||||
struct archive_data;
|
||||
|
||||
void audio_init(void);
|
||||
@@ -78,6 +80,8 @@ int wav_get_volume(int id);
|
||||
int bgm_get_volume(int id);
|
||||
int wav_get_time_length(int id);
|
||||
int bgm_get_time_length(int id);
|
||||
int wav_get_data_length(int no);
|
||||
int bgm_get_data_length(int no);
|
||||
int wav_get_group_num(int id);
|
||||
//int bgm_get_group_num(int id);
|
||||
int wav_prepare_from_file(int id, char *filename);
|
||||
@@ -86,6 +90,7 @@ int bgm_prepare_from_file(int id, char *filename);
|
||||
int wav_get_group_num_from_data_num(int no);
|
||||
|
||||
// Takes ownership of dfile.
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile);
|
||||
int wav_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile);
|
||||
int bgm_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile);
|
||||
|
||||
#endif /* SYSTEM4_AUDIO_H */
|
||||
|
||||
@@ -72,6 +72,7 @@ enum effect {
|
||||
EFFECT_BLUR_FADEOUT = 50,
|
||||
EFFECT_BLUR_CROSSFADE = 51,
|
||||
EFFECT_2ROT_ZOOM_BLEND_BLUR = 52,
|
||||
EFFECT_VWAVE_SCROLL_CROSSFADE = 55,
|
||||
EFFECT_VWAVE_CROSSFADE = 60,
|
||||
NR_EFFECTS
|
||||
};
|
||||
|
||||
+2
-8
@@ -163,21 +163,15 @@ float gfx_render_textf(Texture *dst, float x, int y, char *msg, struct text_styl
|
||||
void gfx_render_dash_text(Texture *dst, struct text_style *ts);
|
||||
void gfx_draw_text_to_amap(Texture *dst, int x, int y, char *text);
|
||||
void gfx_draw_text_to_pmap(Texture *dst, int x, int y, char *text);
|
||||
float gfx_size_char_unstyled(struct text_style *ts, const char *ch);
|
||||
float gfx_size_char(struct text_style *ts, const char *ch);
|
||||
float gfx_size_char_kerning(struct text_style *ts, uint32_t code, uint32_t code_next);
|
||||
float gfx_size_text(struct text_style *ts, const char *text);
|
||||
float gfx_get_actual_font_size(unsigned face, float size);
|
||||
float gfx_get_actual_font_size_round_down(unsigned face, float size);
|
||||
|
||||
static inline float text_style_width(struct text_style *ts, const char *ch)
|
||||
{
|
||||
return (gfx_size_char(ts, ch) + (ts->bold_width * 2) + ts->edge_left + ts->edge_right)
|
||||
* ts->scale_x;
|
||||
}
|
||||
|
||||
static inline float text_style_height(struct text_style *ts)
|
||||
{
|
||||
return ts->size + (ts->bold_width * 2) + ts->edge_up + ts->edge_down;
|
||||
return ts->size + ceilf(ts->bold_width)*2 + ts->edge_up + ts->edge_down;
|
||||
}
|
||||
|
||||
#endif /* SYSTEM4_FONT_H */
|
||||
|
||||
@@ -77,6 +77,9 @@ void gfx_fini(void);
|
||||
|
||||
Texture *gfx_main_surface(void);
|
||||
void gfx_set_window_logical_size(int w, int h);
|
||||
bool gfx_set_fullscreen(bool enable);
|
||||
bool gfx_is_fullscreen(void);
|
||||
void gfx_toggle_fullscreen(void);
|
||||
void gfx_update_screen_scale(void);
|
||||
void gfx_set_wait_vsync(bool wait);
|
||||
float gfx_get_frame_rate(void);
|
||||
@@ -181,6 +184,8 @@ void gfx_copy_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx,
|
||||
void gfx_fill_with_alpha(Texture *dst, int x, int y, int w, int h, int r, int g, int b, int a);
|
||||
void gfx_copy_stretch_with_alpha_map(Texture *dst, int dx, int dy, int dw, int dh, Texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_grayscale_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_grayscale_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_draw_line(Texture *dst, int x0, int y0, int x1, int y1, int r, int g, int b);
|
||||
void gfx_draw_line_to_amap(Texture *dst, int x0, int y0, int x1, int y1, int a);
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width, bool blend);
|
||||
|
||||
@@ -153,5 +153,7 @@ void register_input_handler(void(*handler)(const char*));
|
||||
void clear_input_handler(void);
|
||||
void register_editing_handler(void(*handler)(const char*, int, int));
|
||||
void clear_editing_handler(void);
|
||||
void register_key_handler(void(*handler)(int));
|
||||
void clear_key_handler(void);
|
||||
|
||||
#endif /* SYSTEM4_INPUT_H */
|
||||
|
||||
+3
-2
@@ -59,8 +59,9 @@ struct channel;
|
||||
enum asset_type;
|
||||
|
||||
struct channel *channel_open(enum asset_type type, int no);
|
||||
// Takes ownership of dfile.
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile);
|
||||
// Takes ownership of dfile. Metadata is not applied if metadata_no is negative.
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile,
|
||||
enum asset_type type, int metadata_no);
|
||||
struct channel *channel_open_file(const char *path);
|
||||
void channel_close(struct channel *ch);
|
||||
int channel_play(struct channel *ch);
|
||||
|
||||
@@ -23,6 +23,7 @@ struct page;
|
||||
struct string;
|
||||
|
||||
// parts.c
|
||||
void PE_enable_multi_controller(void);
|
||||
bool PE_Init(void);
|
||||
void PE_Reset(void);
|
||||
void PE_Update(int passed_time, bool message_window_show);
|
||||
@@ -36,6 +37,7 @@ bool PE_SetPartsCG_by_string_index(int parts_no, struct string *cg_no,
|
||||
int sprite_deform, int state);
|
||||
void PE_GetPartsCGName(int parts_no, struct string **cg_name, int state);
|
||||
bool PE_SetPartsCGSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
void PE_GetPartsCGSurfaceArea(int parts_no, int *x, int *y, int *w, int *h, int state);
|
||||
int PE_GetPartsCGNumber(int parts_no, int state);
|
||||
bool PE_SetLoopCG_by_index(int parts_no, int cg_no, int nr_frames, int frame_time, int state);
|
||||
bool PE_SetLoopCG(int parts_no, struct string *cg_name, int start_no, int nr_frames,
|
||||
@@ -67,6 +69,7 @@ bool PE_SetNumeralSurfaceArea(int parts_no, int x, int y, int w, int h, int stat
|
||||
void PE_ReleaseParts(int parts_no);
|
||||
void PE_ReleaseAllParts(void);
|
||||
void PE_ReleaseAllPartsWithoutSystem(void);
|
||||
void PE_ReleaseAllWithoutSystem(struct page **erase_number_list);
|
||||
void PE_SetPos(int parts_no, int x, int y);
|
||||
int PE_GetPartsX(int parts_no);
|
||||
int PE_GetPartsY(int parts_no);
|
||||
@@ -94,6 +97,7 @@ bool PE_SetPartsGroupNumber(int parts_no, int group_no);
|
||||
void PE_SetPartsMessageWindowShowLink(int parts_no, bool message_window_show_link);
|
||||
bool PE_GetPartsMessageWindowShowLink(int parts_no);
|
||||
void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate);
|
||||
int PE_GetSpeedupRateByMessageSkip(int parts_no);
|
||||
void PE_SetPartsMagX(int parts_no, float scale_x);
|
||||
float PE_GetPartsMagX(int parts_no);
|
||||
void PE_SetPartsMagY(int parts_no, float scale_y);
|
||||
@@ -102,9 +106,12 @@ void PE_SetPartsRotateX(int parts_no, float rot_x);
|
||||
void PE_SetPartsRotateY(int parts_no, float rot_y);
|
||||
void PE_SetPartsRotateZ(int parts_no, float rot_z);
|
||||
float PE_GetPartsRotateZ(int parts_no);
|
||||
void PE_SetPartsAlphaClipperPartsNumber(int parts_no, int alpha_clipper_parts_no);
|
||||
void PE_SetPartsPixelDecide(int PartsNumber, bool pixel_decide);
|
||||
bool PE_SetThumbnailReductionSize(int reduction_size);
|
||||
bool PE_SetThumbnailMode(bool Mode);
|
||||
void PE_SetComponentType(int parts_no, int type, int state);
|
||||
int PE_GetComponentType(int parts_no, int state);
|
||||
void PE_SetInputState(int parts_no, int state);
|
||||
int PE_GetInputState(int parts_no);
|
||||
bool PE_SetPartsRectangleDetectionSize(int parts_no, int w, int h, int state);
|
||||
@@ -112,9 +119,27 @@ bool PE_SetPartsCGDetectionSize(int parts_no, struct string *cg_name, int state)
|
||||
bool PE_Save(struct page **buffer);
|
||||
bool PE_SaveWithoutHideParts(struct page **buffer);
|
||||
bool PE_Load(struct page **buffer);
|
||||
int PE_AddController(int index);
|
||||
void PE_RemoveController(struct page **erase_number_list, int index);
|
||||
bool PE_CreateParts3DLayerPluginID(int parts_no, int state);
|
||||
int PE_GetParts3DLayerPluginID(int parts_no, int state);
|
||||
bool PE_ReleaseParts3DLayerPluginID(int parts_no, int state);
|
||||
// GUIEngine
|
||||
int PE_GetFreeNumber(void);
|
||||
bool PE_IsExist(int parts_no);
|
||||
// PartsFunc interface
|
||||
void PE_set_active_controller(int controller_no);
|
||||
int PE_get_active_controller(void);
|
||||
int PE_get_system_controller(void);
|
||||
int PE_get_nr_controllers(void);
|
||||
void PE_parts_set_want_save(int parts_no, bool want_save);
|
||||
bool PE_save_thumbnail(struct string *filename, int reduction_factor);
|
||||
bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state);
|
||||
int PE_get_movie_sprite(int parts_no, int state);
|
||||
float PE_parts_get_absolute_x(int parts_no);
|
||||
float PE_parts_get_absolute_y(int parts_no);
|
||||
int PE_parts_get_absolute_z(int parts_no);
|
||||
void PE_parts_set_lock_input_state(int parts_no, bool lock);
|
||||
|
||||
// construction.c
|
||||
bool PE_AddCreateToPartsConstructionProcess(int parts_no, int w, int h, int state);
|
||||
@@ -125,12 +150,16 @@ bool PE_AddFillToPartsConstructionProcess(int parts_no, int x, int y, int w, int
|
||||
bool PE_AddFillAlphaColorToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int a, int state);
|
||||
bool PE_AddFillAMapToPartsConstructionProcess(int parts_no, int x, int y, int w, int h, int a, int state);
|
||||
bool PE_AddFillWithAlphaToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int a, int state);
|
||||
bool PE_AddDrawRectToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int state);
|
||||
bool PE_AddDrawCutCGToPartsConstructionProcess(int parts_no, struct string *cg_name,
|
||||
int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int interp_type, int state);
|
||||
bool PE_AddCopyCutCGToPartsConstructionProcess(int parts_no, struct string *cg_name,
|
||||
int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int interp_type, int state);
|
||||
bool PE_AddGrayFilterToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
bool full_size, int state);
|
||||
bool PE_AddCopyTextToPartsConstructionProcess(int parts_no, int x, int y, struct string *text,
|
||||
int type, int size, int r, int g, int b, float bold_weight,
|
||||
int edge_r, int edge_g, int edge_b, float edge_weight,
|
||||
@@ -145,8 +174,11 @@ bool PE_SetPartsConstructionSurfaceArea(int parts_no, int x, int y, int w, int h
|
||||
|
||||
// input.c
|
||||
void PE_UpdateInputState(int passed_time);
|
||||
void PE_SetPassCursor(int parts_no, bool pass);
|
||||
bool PE_GetPartsPassCursor(int parts_no);
|
||||
void PE_SetClickable(int parts_no, bool clickable);
|
||||
bool PE_GetPartsClickable(int parts_no);
|
||||
void PE_SetDrag(int parts_no, bool enable);
|
||||
void PE_SetPartsGroupDecideOnCursor(int group_no, bool decide_on_cursor);
|
||||
void PE_SetPartsGroupDecideClick(int group_no, bool decide_click);
|
||||
void PE_SetOnCursorShowLinkPartsNumber(int parts_no, int link_parts_no);
|
||||
@@ -159,6 +191,19 @@ void PE_EndInput(void);
|
||||
int PE_GetClickPartsNumber(void);
|
||||
bool PE_IsCursorIn(int parts_no, int mouse_x, int mouse_y, int state);
|
||||
|
||||
// message.c
|
||||
void PE_ReleaseMessage(void);
|
||||
void PE_PopMessage(void);
|
||||
int PE_GetMessageType(void);
|
||||
int PE_GetMessagePartsNumber(void);
|
||||
int PE_GetMessageDelegateIndex(void);
|
||||
int PE_GetMessageVariableCount(void);
|
||||
int PE_GetMessageVariableType(int index);
|
||||
int PE_GetMessageVariableInt(int index);
|
||||
float PE_GetMessageVariableFloat(int index);
|
||||
bool PE_GetMessageVariableBool(int index);
|
||||
void PE_GetMessageVariableString(int index, struct string **out);
|
||||
|
||||
// motion.c
|
||||
void PE_AddMotionPos(int parts_no, int begin_x, int begin_y, int end_x, int end_y, int begin_t, int end_t);
|
||||
void PE_AddMotionPos_curve(int parts_no, int begin_x, int begin_y, int end_x, int end_y,
|
||||
@@ -205,9 +250,11 @@ void PE_SeekEndMotion(void);
|
||||
void PE_UpdateMotionTime(int time, bool skip);
|
||||
bool PE_IsMotion(void);
|
||||
int PE_GetMotionEndTime(void);
|
||||
void PE_PauseMotion(bool pause);
|
||||
|
||||
// text.c
|
||||
bool PE_SetText(int parts_no, struct string *text, int state);
|
||||
struct string *PE_GetText(int parts_no, int state);
|
||||
bool PE_AddPartsText(int parts_no, struct string *text, int state);
|
||||
bool PE_SetPartsTextSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetFont(int parts_no, int type, int size, int r, int g, int b, float bold_weight, int edge_r, int edge_g, int edge_b, float edge_weight, int state);
|
||||
@@ -221,6 +268,7 @@ bool PE_SetTextCharSpace(int parts_no, int char_space, int state);
|
||||
bool PE_SetTextLineSpace(int parts_no, int line_space, int state);
|
||||
|
||||
// flash.c
|
||||
bool PE_ExistsFlashFile(struct string *flash_filename);
|
||||
bool PE_SetPartsFlash(int parts_no, struct string *flash_filename, int state);
|
||||
bool PE_IsPartsFlashEnd(int parts_no, int state);
|
||||
int PE_GetPartsFlashCurrentFrameNumber(int parts_no, int state);
|
||||
@@ -232,4 +280,37 @@ bool PE_StartPartsFlash(int parts_no, int state);
|
||||
bool PE_GoFramePartsFlash(int parts_no, int frame_no, int state);
|
||||
int PE_GetPartsFlashEndFrame(int parts_no, int state);
|
||||
|
||||
// flat.c
|
||||
bool PE_ExistsFlatFile(struct string *filename);
|
||||
bool PE_SetPartsFlat(int parts_no, struct string *filename, int state);
|
||||
bool PE_IsPartsFlatEnd(int parts_no, int state);
|
||||
int PE_GetPartsFlatCurrentFrameNumber(int parts_no, int state);
|
||||
bool PE_BackPartsFlatBeginFrame(int parts_no, int state);
|
||||
bool PE_StepPartsFlatFinalFrame(int parts_no, int state);
|
||||
bool PE_SetPartsFlatSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetPartsFlatAndStop(int parts_no, struct string *filename, int state);
|
||||
bool PE_StopPartsFlat(int parts_no, int state);
|
||||
bool PE_StartPartsFlat(int parts_no, int state);
|
||||
bool PE_GoFramePartsFlat(int parts_no, int frame_no, int state);
|
||||
int PE_GetPartsFlatEndFrame(int parts_no, int state);
|
||||
|
||||
// layoutbox.c
|
||||
void PE_SetLayoutBoxLayoutType(int parts_no, int type);
|
||||
int PE_GetLayoutBoxLayoutType(int parts_no);
|
||||
void PE_SetLayoutBoxReturn(int parts_no, bool return_flag, int return_size);
|
||||
bool PE_IsLayoutBoxReturn(int parts_no);
|
||||
int PE_GetLayoutBoxReturnSize(int parts_no);
|
||||
void PE_SetLayoutBoxAlign(int parts_no, int align);
|
||||
int PE_GetLayoutBoxAlign(int parts_no);
|
||||
void PE_SetComponentMargin(int parts_no, int top, int bottom, int left, int right);
|
||||
int PE_GetComponentMarginTop(int parts_no);
|
||||
int PE_GetComponentMarginBottom(int parts_no);
|
||||
int PE_GetComponentMarginLeft(int parts_no);
|
||||
int PE_GetComponentMarginRight(int parts_no);
|
||||
void PE_set_layoutbox_padding(int parts_no, int top, int bottom, int left, int right);
|
||||
int PE_get_layoutbox_padding_top(int parts_no);
|
||||
int PE_get_layoutbox_padding_bottom(int parts_no);
|
||||
int PE_get_layoutbox_padding_left(int parts_no);
|
||||
int PE_get_layoutbox_padding_right(int parts_no);
|
||||
|
||||
#endif /* SYSTEM4_PARTS_H */
|
||||
|
||||
+106
-10
@@ -1669,6 +1669,9 @@ uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self, const plm_vlc_uint_t *tabl
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// plm_demux implementation
|
||||
//
|
||||
// xsystem4 change: The demuxer accepts MPEG-1 and MPEG-2 Systems pack/PES headers.
|
||||
// The video and audio decoders still only support MPEG-1 Video and MP2 Audio.
|
||||
|
||||
static const int PLM_START_PACK = 0xBA;
|
||||
static const int PLM_START_END = 0xB9;
|
||||
@@ -1685,6 +1688,7 @@ struct plm_demux_t {
|
||||
double duration;
|
||||
|
||||
int start_code;
|
||||
int is_mpeg2;
|
||||
int has_pack_header;
|
||||
int has_system_header;
|
||||
int has_headers;
|
||||
@@ -1738,19 +1742,52 @@ int plm_demux_has_headers(plm_demux_t *self) {
|
||||
}
|
||||
|
||||
self->start_code = PLM_START_PACK;
|
||||
if (!plm_buffer_has(self->buffer, 64)) {
|
||||
return FALSE;
|
||||
}
|
||||
self->start_code = -1;
|
||||
|
||||
if (plm_buffer_read(self->buffer, 4) != 0x02) {
|
||||
if (!plm_buffer_has(self->buffer, 8)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->system_clock_ref = plm_demux_decode_time(self);
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 22); // mux_rate * 50
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
size_t byte_index = self->buffer->bit_index >> 3;
|
||||
int marker = self->buffer->bytes[byte_index];
|
||||
if ((marker & 0xf0) == 0x20) {
|
||||
if (!plm_buffer_has(self->buffer, 64)) {
|
||||
return FALSE;
|
||||
}
|
||||
self->start_code = -1;
|
||||
|
||||
plm_buffer_skip(self->buffer, 4); // MPEG-1 marker
|
||||
self->system_clock_ref = plm_demux_decode_time(self);
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 22); // mux_rate * 50
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
}
|
||||
else if ((marker & 0xc0) == 0x40) {
|
||||
// MPEG-2 pack headers have a fixed 10-byte part followed by up to
|
||||
// seven stuffing bytes. Read the stuffing length before consuming
|
||||
// anything so that a partial input can be retried.
|
||||
if (!plm_buffer_has(self->buffer, 80)) {
|
||||
return FALSE;
|
||||
}
|
||||
byte_index = self->buffer->bit_index >> 3;
|
||||
int stuffing_length = self->buffer->bytes[byte_index + 9] & 0x07;
|
||||
if (!plm_buffer_has(self->buffer, (10 + stuffing_length) << 3)) {
|
||||
return FALSE;
|
||||
}
|
||||
self->start_code = -1;
|
||||
self->is_mpeg2 = TRUE;
|
||||
|
||||
plm_buffer_skip(self->buffer, 2); // MPEG-2 marker
|
||||
self->system_clock_ref = plm_demux_decode_time(self);
|
||||
plm_buffer_skip(self->buffer, 9); // SCR extension
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 22); // program mux rate
|
||||
plm_buffer_skip(self->buffer, 2); // marker bits
|
||||
plm_buffer_skip(self->buffer, 5); // reserved
|
||||
plm_buffer_skip(self->buffer, 3); // stuffing length
|
||||
plm_buffer_skip(self->buffer, stuffing_length << 3);
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->has_pack_header = TRUE;
|
||||
}
|
||||
@@ -2085,6 +2122,65 @@ plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int type) {
|
||||
|
||||
self->next_packet.type = type;
|
||||
self->next_packet.length = plm_buffer_read(self->buffer, 16);
|
||||
self->next_packet.pts = PLM_PACKET_INVALID_TS;
|
||||
|
||||
if (self->is_mpeg2) {
|
||||
// MPEG-2 PES packets have three fixed header bytes followed by a
|
||||
// variable-length optional header. This decoder only needs PTS; all
|
||||
// other optional fields can be skipped using the header length.
|
||||
if (self->next_packet.length < 3 ||
|
||||
plm_buffer_read(self->buffer, 2) != 0x02
|
||||
) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
plm_buffer_skip(self->buffer, 6); // scrambling and control flags
|
||||
int pts_dts_marker = plm_buffer_read(self->buffer, 2);
|
||||
plm_buffer_skip(self->buffer, 6); // remaining optional-field flags
|
||||
int header_length = plm_buffer_read(self->buffer, 8);
|
||||
if (
|
||||
self->next_packet.length < (size_t)(3 + header_length) ||
|
||||
!plm_buffer_has(self->buffer, header_length << 3)
|
||||
) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int header_bytes_read = 0;
|
||||
if (pts_dts_marker == 0x02) {
|
||||
if (header_length < 5 || plm_buffer_read(self->buffer, 4) != 0x02) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
self->next_packet.pts = plm_demux_decode_time(self);
|
||||
header_bytes_read = 5;
|
||||
}
|
||||
else if (pts_dts_marker == 0x03) {
|
||||
if (header_length < 10 || plm_buffer_read(self->buffer, 4) != 0x03) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
self->next_packet.pts = plm_demux_decode_time(self);
|
||||
if (plm_buffer_read(self->buffer, 4) != 0x01) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
plm_demux_decode_time(self); // DTS
|
||||
header_bytes_read = 10;
|
||||
}
|
||||
else if (pts_dts_marker != 0x00) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->next_packet.pts != PLM_PACKET_INVALID_TS) {
|
||||
self->last_decoded_pts = self->next_packet.pts;
|
||||
}
|
||||
plm_buffer_skip(self->buffer, (header_length - header_bytes_read) << 3);
|
||||
self->next_packet.length -= 3 + header_length;
|
||||
return plm_demux_get_packet(self);
|
||||
}
|
||||
|
||||
self->next_packet.length -= plm_buffer_skip_bytes(self->buffer, 0xff); // stuffing
|
||||
|
||||
// skip P-STD
|
||||
|
||||
+83
-8
@@ -19,6 +19,7 @@
|
||||
|
||||
#define RE_NR_BACK_CGS 16
|
||||
#define RE_NR_INSTANCE_TARGETS 2
|
||||
#define RE_NR_LIGHT_PARAMS 41 // .lit parameter count
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
@@ -27,10 +28,14 @@
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct hash_table;
|
||||
struct billboard_texture;
|
||||
struct iarray_reader;
|
||||
struct iarray_writer;
|
||||
|
||||
enum RE_plugin_version {
|
||||
RE_REIGN_PLUGIN, // Toushin Toshi 3
|
||||
RE_TAPIR_PLUGIN, // Rance Quest
|
||||
RE_SEAL_PLUGIN, // Rance 9
|
||||
};
|
||||
|
||||
enum RE_instance_type {
|
||||
@@ -56,6 +61,29 @@ enum RE_fog_type {
|
||||
RE_FOG_LIGHT_SCATTERING = 2,
|
||||
};
|
||||
|
||||
// Indices into light_params. Vector entries name their first component.
|
||||
enum seal_light_param {
|
||||
SEAL_LP_TONEMAP_EXPOSURE_BIAS = 0,
|
||||
SEAL_LP_TONEMAP_WHITE_POINT = 1,
|
||||
SEAL_LP_TONEMAP_A = 2,
|
||||
SEAL_LP_TONEMAP_B = 3,
|
||||
SEAL_LP_TONEMAP_C = 4,
|
||||
SEAL_LP_TONEMAP_D = 5,
|
||||
SEAL_LP_TONEMAP_E = 6,
|
||||
SEAL_LP_TONEMAP_F = 7,
|
||||
SEAL_LP_HEMI_VEC = 8, // x,y,z
|
||||
SEAL_LP_HEMI_SKY_COLOR = 11, // r,g,b
|
||||
SEAL_LP_HEMI_MID_COLOR = 14, // r,g,b
|
||||
SEAL_LP_HEMI_GROUND_COLOR = 17, // r,g,b
|
||||
SEAL_LP_LS_BETA_R = 20,
|
||||
SEAL_LP_LS_BETA_M = 21,
|
||||
SEAL_LP_LS_G = 22,
|
||||
SEAL_LP_LS_DISTANCE = 23,
|
||||
SEAL_LP_LS_LIGHT_VEC = 24, // x,y,z
|
||||
SEAL_LP_LS_LIGHT_COLOR = 27, // r,g,b
|
||||
SEAL_LP_LS_SUN_COLOR = 30, // r,g,b
|
||||
};
|
||||
|
||||
enum RE_draw_type {
|
||||
RE_DRAW_TYPE_NORMAL = 0,
|
||||
RE_DRAW_TYPE_ADDITIVE = 1,
|
||||
@@ -64,15 +92,10 @@ enum RE_draw_type {
|
||||
|
||||
enum RE_draw_options {
|
||||
RE_DRAW_OPTION_EDGE = 0,
|
||||
RE_DRAW_OPTION_LIGHTING = 1,
|
||||
RE_DRAW_OPTION_MAX
|
||||
};
|
||||
|
||||
enum RE_draw_edge_mode {
|
||||
RE_DRAW_EDGE_NONE = 0,
|
||||
RE_DRAW_EDGE_CHARACTERS_ONLY = 1,
|
||||
RE_DRAW_EDGE_ALL = 2,
|
||||
};
|
||||
|
||||
struct RE_options {
|
||||
int anti_aliasing;
|
||||
int wait_vsync;
|
||||
@@ -83,6 +106,12 @@ struct RE_camera {
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
float quake_pitch, quake_yaw;
|
||||
// Per-frame full override from an 3de "camera" object. Cleared at the
|
||||
// start of every RE_build_model tick and re-asserted by s3de_effect_update
|
||||
// if any active camera object selects this frame.
|
||||
bool override_active;
|
||||
vec3 override_pos;
|
||||
float override_pitch, override_yaw, override_roll;
|
||||
};
|
||||
|
||||
struct RE_back_cg {
|
||||
@@ -98,15 +127,17 @@ struct RE_back_cg {
|
||||
|
||||
struct RE_plugin {
|
||||
struct draw_plugin plugin;
|
||||
enum RE_plugin_version version;
|
||||
int sprite;
|
||||
int nr_instances;
|
||||
struct RE_instance **instances;
|
||||
struct archive *aar;
|
||||
struct hash_table *model_cache;
|
||||
struct hash_table *pae_cache;
|
||||
struct hash_table *effect_cache;
|
||||
struct RE_renderer *renderer;
|
||||
struct RE_camera camera;
|
||||
mat4 proj_transform;
|
||||
int viewport_x, viewport_y, viewport_width, viewport_height;
|
||||
|
||||
struct RE_back_cg back_cg[RE_NR_BACK_CGS];
|
||||
|
||||
@@ -148,30 +179,43 @@ struct RE_plugin {
|
||||
|
||||
// TapirEngine
|
||||
bool suspended;
|
||||
|
||||
// SealEngine
|
||||
int mag_speed;
|
||||
float light_params[RE_NR_LIGHT_PARAMS];
|
||||
float edge_length;
|
||||
float edge_reduction_rate;
|
||||
vec3 edge_color;
|
||||
};
|
||||
|
||||
struct RE_instance {
|
||||
struct RE_plugin *plugin;
|
||||
struct model *model;
|
||||
struct particle_effect *effect;
|
||||
struct s3de_effect *s3de_effect;
|
||||
struct motion *motion;
|
||||
struct motion *next_motion;
|
||||
struct height_detector *height_detector;
|
||||
|
||||
enum RE_instance_type type;
|
||||
struct string *name;
|
||||
int target[RE_NR_INSTANCE_TARGETS];
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
vec3 scale;
|
||||
float alpha;
|
||||
float grayscale_rate;
|
||||
bool draw;
|
||||
bool draw_edge;
|
||||
bool draw_shadow;
|
||||
bool make_shadow;
|
||||
float shadow_volume_bone_radius;
|
||||
bool draw_bump;
|
||||
float fps;
|
||||
bool use_mag_speed;
|
||||
bool motion_blend;
|
||||
float motion_blend_rate;
|
||||
vec3 diffuse; // color multiplier for meshes, light color for lights
|
||||
vec3 ambient;
|
||||
vec3 column_pos;
|
||||
float column_height;
|
||||
@@ -180,11 +224,15 @@ struct RE_instance {
|
||||
|
||||
// Billboards
|
||||
enum RE_draw_type draw_type;
|
||||
vec3 vertex_pos[4];
|
||||
vec2 vertex_uv[4];
|
||||
struct billboard_texture **billboard_frames;
|
||||
int nr_billboard_frames;
|
||||
|
||||
// Lights
|
||||
vec3 vec;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
float *light_params;
|
||||
|
||||
// Private
|
||||
bool local_transform_needs_update;
|
||||
@@ -198,6 +246,9 @@ struct RE_instance {
|
||||
int texture_animation_index;
|
||||
};
|
||||
|
||||
#define RE_MAX_PLUGINS 2
|
||||
struct RE_plugin *RE_get_plugin(int handle);
|
||||
|
||||
struct RE_plugin *RE_plugin_new(enum RE_plugin_version version);
|
||||
void RE_plugin_free(struct RE_plugin *plugin);
|
||||
bool RE_plugin_bind(struct RE_plugin *plugin, int sprite);
|
||||
@@ -205,18 +256,22 @@ bool RE_plugin_unbind(struct RE_plugin *plugin);
|
||||
bool RE_plugin_suspend(struct RE_plugin *plugin);
|
||||
bool RE_plugin_resume(struct RE_plugin *plugin);
|
||||
bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms);
|
||||
void RE_update_model(struct RE_plugin *plugin);
|
||||
bool RE_set_viewport(struct RE_plugin *plugin, int x, int y, int width, int height);
|
||||
bool RE_set_projection(struct RE_plugin *plugin, float width, float height, float near, float far, float deg);
|
||||
int RE_create_instance(struct RE_plugin *plugin);
|
||||
bool RE_release_instance(struct RE_plugin *plugin, int instance);
|
||||
|
||||
bool RE_instance_set_type(struct RE_instance *instance, int type);
|
||||
bool RE_instance_data_exists(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_motion_exists(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load_motion(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_free_next_motion(struct RE_instance *instance);
|
||||
bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show);
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z);
|
||||
bool RE_instance_set_vertex_uv(struct RE_instance *instance, int index, float u, float v);
|
||||
int RE_instance_get_bone_index(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instance, int bone, vec3 offset, vec3 out);
|
||||
float RE_instance_calc_height(struct RE_instance *instance, float x, float z);
|
||||
@@ -227,6 +282,11 @@ bool RE_instance_find_path(struct RE_instance *instance, vec3 start, vec3 goal);
|
||||
const vec3 *RE_instance_get_path_line(struct RE_instance *instance, int *nr_path_points);
|
||||
bool RE_instance_optimize_path_line(struct RE_instance *instance);
|
||||
bool RE_instance_calc_path_finder_intersect_eye_vec(struct RE_instance *instance, int mouse_x, int mouse_y, vec3 out);
|
||||
bool RE_plugin_transform_pos_to_view_pos(struct RE_plugin *plugin, float x, float y, float z, int *view_x, int *view_y);
|
||||
bool RE_plugin_get_camera_z_vector(struct RE_plugin *plugin, vec3 out);
|
||||
void RE_plugin_reset_light_param(struct RE_plugin *plugin);
|
||||
bool RE_plugin_set_fog_type(struct RE_plugin *plugin, int type);
|
||||
int RE_plugin_get_fog_type(struct RE_plugin *plugin);
|
||||
|
||||
int RE_motion_get_state(struct motion *motion);
|
||||
bool RE_motion_set_state(struct motion *motion, int state);
|
||||
@@ -306,4 +366,19 @@ bool RE_back_cg_set_name(struct RE_back_cg *bcg, struct string *name, struct arc
|
||||
void RE_render(struct sact_sprite *sp);
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose);
|
||||
|
||||
// save.c
|
||||
void RE_plugin_serialize(struct RE_plugin *plugin, struct iarray_writer *w);
|
||||
void RE_plugin_deserialize(struct RE_plugin *plugin, struct iarray_reader *r, int version);
|
||||
|
||||
// Exposed for PartsEngine 3DLayer
|
||||
int ReignEngine_create_plugin(enum RE_plugin_version version);
|
||||
bool ReignEngine_ReleasePlugin(int handle);
|
||||
bool ReignEngine_BindPlugin(int handle, int sprite);
|
||||
|
||||
// Exposed for MarmotModelEngine
|
||||
void ReignEngine_update_models(void);
|
||||
|
||||
// debug.c
|
||||
void re_debug_init(void);
|
||||
|
||||
#endif /* SYSTEM4_REIGN_H */
|
||||
|
||||
@@ -118,6 +118,8 @@ bool sact_DX_GetUsePower2Texture(void);
|
||||
void sact_DX_SetUsePower2Texture(bool use);
|
||||
|
||||
extern struct text_style text_sprite_ts;
|
||||
int StoatSpriteEngine_SP_SetDrawMethod(int sp_no, int method);
|
||||
int StoatSpriteEngine_SP_GetDrawMethod(int sp_no);
|
||||
bool StoatSpriteEngine_SP_SetTextSprite(int sp_no, struct string *text);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteType(int type);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteSize(int size);
|
||||
|
||||
@@ -20,6 +20,7 @@ in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float gray = dot(texture(tex, tex_coord).rgb, vec3(0.299, 0.587, 0.114));
|
||||
frag_color = vec4(vec3(gray), 1);
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
float gray = dot(texel.rgb, vec3(0.299, 0.587, 0.114));
|
||||
frag_color = vec4(vec3(gray), texel.a);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright (C) 2026 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* 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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define EFFECT_HEIGHT 210.0
|
||||
|
||||
void main() {
|
||||
// start position of effect
|
||||
float top = ((resolution.y + EFFECT_HEIGHT) * (1.0 - progress)) - EFFECT_HEIGHT;
|
||||
// current pixel y-coordinate
|
||||
float p = tex_coord.y * resolution.y;
|
||||
// calculate alpha value
|
||||
float alpha = 1.0 - clamp((p - top) / EFFECT_HEIGHT, 0.0, 1.0);
|
||||
vec3 col = mix(texture(tex, tex_coord).rgb, texture(old, tex_coord).rgb, alpha);
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -22,47 +22,30 @@ uniform float progress; // effect progress (0..1)
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define MAX_STRIDE 128.0
|
||||
#define MAX_CHUNK 128.0
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).rgb, texture(tex, xy).rgb, progress);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// size of each chunk
|
||||
float stride = MAX_STRIDE * (1.0 - abs(progress * 2.0 - 1.0)); // progress=(0..0.5..1) -> stride=(0..max..0)
|
||||
// offset of frag coordinate relative to chunk
|
||||
vec2 offset = mod(gl_FragCoord.xy, stride);
|
||||
// the chunk coordinate
|
||||
vec2 chunk = tex_coord - offset / resolution;
|
||||
// coordinate of chunk center
|
||||
vec2 center = chunk + vec2(stride/2.0, stride/2.0) / resolution;
|
||||
|
||||
// FIXME: In this implementation, the anchor point is at the top left of the screen.
|
||||
// It should be anchored at the center to match SACT2.dll.
|
||||
frag_color = vec4(get_pixel(center), 1.0);
|
||||
}
|
||||
|
||||
/* NOTE: This is an alternate implementation which computes an average for each chunk.
|
||||
SACT2.dll does not do this.
|
||||
|
||||
#define SAMPLES 8.0
|
||||
#define MAX_STRIDE 16.0
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).xyz, texture(tex, xy).xyz, progress);
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float stride = MAX_STRIDE * (1.0 - abs(progress * 2.0 - 1.0)); // progress=(0..0.5..1) -> stride=(0..max..0)
|
||||
vec3 avg = vec3(0, 0, 0);
|
||||
vec2 xy = floor(gl_FragCoord.xy / (SAMPLES * stride)) * (SAMPLES * stride);
|
||||
for (float j = 0.0; j < SAMPLES; j += 1.0) {
|
||||
for (float i = 0.0; i < SAMPLES; i+= 1.0) {
|
||||
avg += get_pixel((xy + stride * vec2(i, j)) / resolution);
|
||||
}
|
||||
}
|
||||
avg /= SAMPLES * SAMPLES;
|
||||
frag_color = vec4(avg, 1.0);
|
||||
// center of the screen
|
||||
vec2 center = resolution * 0.5;
|
||||
// size of each chunk
|
||||
float chunk_size = floor(triangle(progress) * MAX_CHUNK);
|
||||
// number of chunks in each screen quadrant (including partial chunks at edges)
|
||||
vec2 chunks_per_quadrant = floor(center / chunk_size);
|
||||
// offset from screen to edge of grid (negative or zero)
|
||||
vec2 grid_off = center - (chunks_per_quadrant * chunk_size);
|
||||
// offset of pixel in chunk
|
||||
vec2 chunk_off = mod(gl_FragCoord.xy - grid_off, chunk_size);
|
||||
// the chunk coordinate
|
||||
vec2 chunk = tex_coord - vec2(chunk_off) / resolution;
|
||||
// coordinate of chunk center
|
||||
vec2 chunk_center = chunk + vec2(chunk_size/2.0, chunk_size/2.0) / resolution;
|
||||
frag_color = vec4(get_pixel(chunk_center), 1.0);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -22,8 +22,21 @@ uniform float progress; // effect progress (0..1)
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).rgb, texture(tex, xy).rgb, progress);
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#define OLD_PERIOD ((2.0 * M_PI) / 360.0)
|
||||
#define NEW_PERIOD ((2.0 * M_PI) / 720.0)
|
||||
#define OLD_SHIFT (-M_PI)
|
||||
#define NEW_SHIFT (M_PI / 2.0)
|
||||
|
||||
vec3 get_pixel(vec2 old_xy, vec2 new_xy) {
|
||||
// influence of old color is described by: C_old = (1-t)^2
|
||||
// influence of new color increases linearly: C_new = t
|
||||
// the resulting blend is not normalized
|
||||
vec3 old = texture(old, old_xy).rgb;
|
||||
vec3 new = texture(tex, new_xy).rgb;
|
||||
float old_weight = (1.0 - progress) * (1.0 - progress);
|
||||
float new_weight = progress;
|
||||
return (old * old_weight + new * new_weight);
|
||||
}
|
||||
|
||||
float triangle(float f) {
|
||||
@@ -31,9 +44,14 @@ float triangle(float f) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
// old: x = amp * sin((2pi/360)*y + phase + -pi)
|
||||
// new: x = amp * sin((2pi/720)*y + phase + pi/2)
|
||||
vec2 p = tex_coord * resolution;
|
||||
float scale = triangle(progress) * 80.0; // (0..0.5..1) -> (0..80..0);
|
||||
float wobble = progress * 24.0;
|
||||
float offset = sin((p.y / 100.0) - wobble) * scale;
|
||||
frag_color = vec4(get_pixel(fract(vec2(p.x + offset, p.y) / resolution)), 1.0);
|
||||
float amp = triangle(progress) * 80.0; // (0..0.5..1) -> (0..80..0);
|
||||
float phase = progress * M_PI * 8.0;
|
||||
float old_off = amp * sin(OLD_PERIOD * p.y + OLD_SHIFT + phase);
|
||||
float new_off = amp * sin(NEW_PERIOD * p.y + NEW_SHIFT + phase);
|
||||
vec2 old_xy = fract(vec2(p.x + old_off, p.y) / resolution);
|
||||
vec2 new_xy = fract(vec2(p.x - new_off, p.y) / resolution);
|
||||
frag_color = vec4(get_pixel(old_xy, new_xy), 1.0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* 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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#define OLD_PERIOD ((2.0 * M_PI) / 360.0)
|
||||
#define NEW_PERIOD ((2.0 * M_PI) / 720.0)
|
||||
#define OLD_SHIFT (-M_PI)
|
||||
#define NEW_SHIFT (M_PI / 2.0)
|
||||
|
||||
vec3 get_pixel(vec2 old_xy, vec2 new_xy) {
|
||||
// influence of old color is described by: C_old = (1-t)^2
|
||||
// influence of new color increases linearly: C_new = t
|
||||
// the resulting blend is not normalized
|
||||
vec3 old = texture(old, old_xy).rgb;
|
||||
vec3 new = texture(tex, new_xy).rgb;
|
||||
float old_weight = (1.0 - progress) * (1.0 - progress);
|
||||
float new_weight = progress;
|
||||
return (old * old_weight + new * new_weight);
|
||||
}
|
||||
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// old: x = amp * sin((2pi/360)*y + phase + -pi)
|
||||
// new: x = amp * sin((2pi/720)*y + phase + pi/2)
|
||||
vec2 p = tex_coord * resolution;
|
||||
float amp = triangle(progress) * 160.0; // (0..0.5..1) -> (0..160..0);
|
||||
float phase = progress * M_PI * 8.0;
|
||||
float old_off_x = amp * sin(OLD_PERIOD * p.y + OLD_SHIFT + phase);
|
||||
float new_off_x = amp * sin(NEW_PERIOD * p.y + NEW_SHIFT + phase);
|
||||
float off_y = resolution.y * progress;
|
||||
vec2 old_xy = fract(vec2(p.x + old_off_x, p.y + off_y) / resolution);
|
||||
vec2 new_xy = fract(vec2(p.x - new_off_x, p.y + off_y) / resolution);
|
||||
frag_color = vec4(get_pixel(old_xy, new_xy), 1.0);
|
||||
}
|
||||
+32
-7
@@ -20,21 +20,46 @@ uniform vec2 bot_left;
|
||||
uniform vec2 top_right;
|
||||
uniform vec3 add_color;
|
||||
uniform vec3 multiply_color;
|
||||
uniform int draw_filter;
|
||||
|
||||
uniform int use_clipper;
|
||||
uniform sampler2D clipper_tex;
|
||||
|
||||
in vec2 tex_coord;
|
||||
in vec2 clip_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
float point_in_rect(vec2 p, vec2 bot_left, vec2 top_right) {
|
||||
vec2 s = step(bot_left, p) - step(top_right, p);
|
||||
return s.x * s.y;
|
||||
const int DRAW_FILTER_MULTIPLY = 2;
|
||||
const int DRAW_FILTER_SCREEN = 3;
|
||||
|
||||
bool inside_rect(vec2 p, vec2 bl, vec2 tr) {
|
||||
return all(greaterThanEqual(p, bl)) && all(lessThan(p, tr));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(textureSize(tex, 0));
|
||||
vec2 bl = bot_left / size;
|
||||
vec2 tr = top_right / size;
|
||||
vec2 size = vec2(textureSize(tex, 0));
|
||||
if (!inside_rect(tex_coord, bot_left / size, top_right / size))
|
||||
discard;
|
||||
|
||||
vec4 tex_color = texture(tex, tex_coord);
|
||||
vec3 mod_color = (tex_color.rgb + add_color) * multiply_color;
|
||||
frag_color = vec4(mod_color, tex_color.a * blend_rate) * point_in_rect(tex_coord, bl, tr);
|
||||
float alpha = tex_color.a * blend_rate;
|
||||
|
||||
if (use_clipper != 0) {
|
||||
if (!inside_rect(clip_coord, vec2(0.0), vec2(1.0)))
|
||||
discard;
|
||||
alpha *= texture(clipper_tex, clip_coord).a;
|
||||
}
|
||||
|
||||
if (draw_filter == DRAW_FILTER_MULTIPLY) {
|
||||
// result = mix(dst, mod_color*dst, alpha), used with (GL_DST_COLOR, GL_ZERO)
|
||||
frag_color = vec4(mix(vec3(1.0), mod_color, alpha), 1.0);
|
||||
} else if (draw_filter == DRAW_FILTER_SCREEN) {
|
||||
// result = mod_color*alpha + (1 - mod_color*alpha)*dst,
|
||||
// used with (GL_ONE, GL_ONE_MINUS_SRC_COLOR)
|
||||
frag_color = vec4(mod_color * alpha, 1.0);
|
||||
} else {
|
||||
// normal or additive — blend func handles alpha
|
||||
frag_color = vec4(mod_color, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* 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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 world_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 inv_clipper_transform;
|
||||
|
||||
in vec4 vertex_pos;
|
||||
in vec2 vertex_uv;
|
||||
out vec2 tex_coord;
|
||||
out vec2 clip_coord;
|
||||
|
||||
void main() {
|
||||
vec4 world_pos = world_transform * vertex_pos;
|
||||
gl_Position = view_transform * world_pos;
|
||||
tex_coord = vertex_uv;
|
||||
clip_coord = (inv_clipper_transform * world_pos).xy;
|
||||
}
|
||||
+95
-24
@@ -24,11 +24,15 @@
|
||||
#define ALPHA_MAP_BLEND 2
|
||||
#define ALPHA_MAP_TEST 3
|
||||
|
||||
#define FOG_LINEAR 1
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
#define ENABLE_SPECULAR (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
#define ENABLE_LIGHT_SCATTERING (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define FOG_LINEAR 1
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
@@ -36,25 +40,17 @@ struct dir_light {
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform sampler2D specular_texture;
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform float specular_strength;
|
||||
uniform float specular_shininess;
|
||||
uniform bool use_specular_map;
|
||||
uniform float rim_exponent;
|
||||
uniform vec3 rim_color;
|
||||
uniform int fog_type;
|
||||
uniform float fog_near;
|
||||
uniform float fog_far;
|
||||
uniform vec3 fog_color;
|
||||
in vec3 light_dir[NR_DIR_LIGHTS];
|
||||
in vec3 specular_dir;
|
||||
in vec3 ls_ex;
|
||||
in vec3 ls_in;
|
||||
|
||||
uniform vec3 global_ambient;
|
||||
|
||||
vec3 dir_lights_diffuse(vec3 norm)
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
vec3 diffuse = vec3(0.0);
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
@@ -64,18 +60,75 @@ vec3 dir_lights_diffuse(vec3 norm)
|
||||
return diffuse;
|
||||
}
|
||||
|
||||
#else // ENGINE == REIGN_ENGINE
|
||||
#elif ENGINE == SEAL_ENGINE
|
||||
|
||||
const vec3 global_ambient = vec3(0.0);
|
||||
|
||||
vec3 dir_lights_diffuse(vec3 norm)
|
||||
// Hemisphere lighting (sky/middle/ground).
|
||||
uniform vec3 hemi_light_dir;
|
||||
uniform vec3 hemi_sky_color;
|
||||
uniform vec3 hemi_mid_color;
|
||||
uniform vec3 hemi_ground_color;
|
||||
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
float t = 0.5 * dot(norm, hemi_light_dir) + 0.5;
|
||||
vec3 low_mid = mix(hemi_ground_color, hemi_mid_color, t);
|
||||
vec3 mid_high = mix(hemi_mid_color, hemi_sky_color, t);
|
||||
return mix(low_mid, mid_high, t);
|
||||
}
|
||||
|
||||
#else // TAPIR_ENGINE
|
||||
|
||||
const vec3 global_ambient = vec3(0.0);
|
||||
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
return vec3(1.0);
|
||||
}
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
uniform int fog_type;
|
||||
in vec3 ls_ex;
|
||||
in vec3 ls_in;
|
||||
#endif
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
uniform sampler2D specular_texture;
|
||||
uniform vec3 specular_color;
|
||||
uniform float specular_shininess;
|
||||
uniform bool use_specular_map;
|
||||
in vec3 specular_dir;
|
||||
#endif
|
||||
|
||||
#if ENGINE == SEAL_ENGINE
|
||||
// Uncharted 2 (Hable) filmic tone mapping.
|
||||
uniform vec4 tonemap_param; // (ExposureBias, WhitePoint, A, B)
|
||||
uniform vec4 tonemap_param2; // (C, D, E, F)
|
||||
uniform bool nolighting;
|
||||
uniform float grayscale_rate;
|
||||
|
||||
vec3 hable(vec3 v)
|
||||
{
|
||||
float a = tonemap_param.z, b = tonemap_param.w;
|
||||
float c = tonemap_param2.x, d = tonemap_param2.y;
|
||||
float e = tonemap_param2.z, f = tonemap_param2.w;
|
||||
return ((v * (a * v + c * b) + d * e) / (v * (a * v + b) + d * f)) - e / f;
|
||||
}
|
||||
|
||||
vec3 tone_map(vec3 color)
|
||||
{
|
||||
vec3 curr = hable(color * tonemap_param.x);
|
||||
vec3 white = hable(vec3(tonemap_param.y));
|
||||
return curr / white;
|
||||
}
|
||||
#endif
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D blend_tex;
|
||||
uniform bool use_blend_texture;
|
||||
uniform sampler2D alpha_texture;
|
||||
uniform sampler2D light_texture;
|
||||
uniform sampler2D shadow_texture;
|
||||
@@ -85,6 +138,7 @@ uniform bool use_normal_map;
|
||||
uniform sampler2D normal_texture;
|
||||
|
||||
uniform vec3 instance_ambient;
|
||||
uniform vec3 diffuse_mod;
|
||||
uniform int diffuse_type;
|
||||
uniform float shadow_darkness;
|
||||
uniform float shadow_bias;
|
||||
@@ -98,6 +152,8 @@ in vec4 shadow_frag_pos;
|
||||
in float dist;
|
||||
in vec3 eye;
|
||||
in vec3 normal;
|
||||
in float blend_weight;
|
||||
in vec2 blend_tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
@@ -114,33 +170,36 @@ void main() {
|
||||
|
||||
// Diffuse lighting
|
||||
vec4 texel;
|
||||
vec3 diffuse = vec3(1.0);
|
||||
if (diffuse_type == DIFFUSE_ENV_MAP) {
|
||||
texel = texture(tex, (vec2(normal.x, -normal.y) + 1.0) / 2.0);
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
texel = texture(tex, tex_coord);
|
||||
if (diffuse_type == DIFFUSE_EMISSIVE) {
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
vec3 diffuse = dir_lights_diffuse(norm);
|
||||
if (use_blend_texture) {
|
||||
texel = mix(texel, texture(blend_tex, blend_tex_coord), blend_weight);
|
||||
}
|
||||
if (diffuse_type != DIFFUSE_EMISSIVE) {
|
||||
diffuse = compute_diffuse(norm);
|
||||
if (diffuse_type == DIFFUSE_LIGHT_MAP) {
|
||||
diffuse *= texture(light_texture, light_tex_coord).rgb;
|
||||
}
|
||||
frag_rgb += texel.rgb * diffuse * color_mod.rgb;
|
||||
}
|
||||
}
|
||||
frag_rgb += texel.rgb * diffuse * color_mod.rgb * diffuse_mod;
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
#if ENABLE_SPECULAR
|
||||
// Specular lighting
|
||||
if (specular_strength > 0.0) {
|
||||
if (any(greaterThan(specular_color, vec3(0.0)))) {
|
||||
vec3 reflect_dir = reflect(specular_dir, norm);
|
||||
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), specular_shininess) * specular_strength;
|
||||
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), specular_shininess);
|
||||
if (use_specular_map) {
|
||||
specular *= texture(specular_texture, tex_coord).r;
|
||||
}
|
||||
frag_rgb += vec3(specular);
|
||||
frag_rgb += specular_color * specular;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
// Rim lighting
|
||||
if (rim_exponent > 0.0) {
|
||||
// Normal map is not used here.
|
||||
@@ -152,7 +211,11 @@ void main() {
|
||||
if (fog_type == FOG_LINEAR) {
|
||||
float fog_factor = clamp((dist - fog_near) / (fog_far - fog_near), 0.0, 1.0);
|
||||
frag_rgb = mix(frag_rgb, fog_color, fog_factor);
|
||||
} else if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
frag_rgb = frag_rgb * ls_ex + ls_in;
|
||||
}
|
||||
#endif
|
||||
@@ -174,6 +237,14 @@ void main() {
|
||||
frag_rgb *= brightness;
|
||||
}
|
||||
|
||||
#if ENGINE == SEAL_ENGINE
|
||||
if (!nolighting)
|
||||
frag_rgb = tone_map(frag_rgb);
|
||||
|
||||
if (grayscale_rate != 0.0)
|
||||
frag_rgb = mix(frag_rgb, vec3(dot(frag_rgb, vec3(0.299, 0.587, 0.114))), grayscale_rate);
|
||||
#endif
|
||||
|
||||
// Alpha mapping
|
||||
float alpha = texel.a * color_mod.a * alpha_mod;
|
||||
if (alpha_mode == ALPHA_TEST) {
|
||||
|
||||
+34
-13
@@ -16,10 +16,14 @@
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
#define ENABLE_SPECULAR (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
#define ENABLE_LIGHT_SCATTERING (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
@@ -28,16 +32,7 @@ struct dir_light {
|
||||
};
|
||||
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform vec3 specular_light_dir;
|
||||
uniform int fog_type;
|
||||
uniform vec4 ls_params; // (beta_r, beta_m, g, distance)
|
||||
uniform vec3 ls_light_dir;
|
||||
uniform vec3 ls_light_color;
|
||||
uniform vec3 ls_sun_color;
|
||||
out vec3 light_dir[NR_DIR_LIGHTS];
|
||||
out vec3 specular_dir;
|
||||
out vec3 ls_ex;
|
||||
out vec3 ls_in;
|
||||
|
||||
const vec2 uv_scroll = vec2(0.0);
|
||||
|
||||
@@ -47,6 +42,23 @@ uniform vec2 uv_scroll;
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform vec4 uv_tiling;
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
uniform int fog_type;
|
||||
uniform vec4 ls_params; // (beta_r, beta_m, g, distance)
|
||||
uniform vec3 ls_light_dir;
|
||||
uniform vec3 ls_light_color;
|
||||
uniform vec3 ls_sun_color;
|
||||
out vec3 ls_ex;
|
||||
out vec3 ls_in;
|
||||
#endif
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
uniform vec3 specular_light_dir;
|
||||
out vec3 specular_dir;
|
||||
#endif
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
@@ -72,6 +84,8 @@ in vec4 vertex_color;
|
||||
in vec4 vertex_tangent;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
in float vertex_blend_weight;
|
||||
in vec2 vertex_blend_uv;
|
||||
|
||||
out vec2 tex_coord;
|
||||
out vec2 light_tex_coord;
|
||||
@@ -81,6 +95,8 @@ out vec4 shadow_frag_pos;
|
||||
out float dist;
|
||||
out vec3 eye;
|
||||
out vec3 normal;
|
||||
out float blend_weight;
|
||||
out vec2 blend_tex_coord;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
@@ -114,9 +130,11 @@ void main() {
|
||||
vec4 view_pos = view_transform * world_pos;
|
||||
gl_Position = proj_transform * view_pos;
|
||||
|
||||
tex_coord = vertex_uv + uv_scroll;
|
||||
tex_coord = (vertex_uv + uv_scroll) * uv_tiling.xy;
|
||||
light_tex_coord = vertex_light_uv + uv_scroll;
|
||||
color_mod = vertex_color;
|
||||
blend_weight = vertex_blend_weight;
|
||||
blend_tex_coord = (vertex_blend_uv + uv_scroll) * uv_tiling.zw;
|
||||
dist = -view_pos.z;
|
||||
shadow_frag_pos = shadow_transform * world_pos;
|
||||
|
||||
@@ -125,12 +143,15 @@ void main() {
|
||||
frag_pos = TBN * vec3(world_pos);
|
||||
eye = TBN * camera_pos;
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
specular_dir = TBN * specular_light_dir;
|
||||
#endif
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
light_dir[0] = TBN * dir_lights[0].dir;
|
||||
light_dir[1] = TBN * dir_lights[1].dir;
|
||||
light_dir[2] = TBN * dir_lights[2].dir;
|
||||
specular_dir = TBN * specular_light_dir;
|
||||
|
||||
#endif
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
float beta_r = ls_params.x;
|
||||
float beta_m = ls_params.y;
|
||||
|
||||
+304
-9
@@ -40,9 +40,9 @@ struct model {
|
||||
int nr_materials;
|
||||
struct material *materials;
|
||||
int nr_bones;
|
||||
struct bone *bones;
|
||||
struct hash_table *bone_map; // bone id in POL/MOT -> struct bone *
|
||||
struct hash_table *bone_name_map; // bone name -> (struct bone * | NULL)
|
||||
struct bone *bones; // ordered so that parents precede their children
|
||||
struct bone **bones_by_pol_index; // POL bone array index -> struct bone *
|
||||
struct hash_table *bone_map; // bone id in POL -> struct bone *
|
||||
struct hash_table *mot_cache; // name -> struct mot *
|
||||
struct collider *collider;
|
||||
vec3 aabb[2]; // axis-aligned bounding box
|
||||
@@ -63,17 +63,22 @@ struct mesh {
|
||||
vec3 outline_color;
|
||||
float outline_thickness;
|
||||
vec2 uv_scroll;
|
||||
vec3 specular_color;
|
||||
float specular_power;
|
||||
};
|
||||
|
||||
struct material {
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
vec2 uv_tiling;
|
||||
vec2 blend_uv_tiling;
|
||||
int nr_color_maps;
|
||||
GLuint *color_maps;
|
||||
GLuint specular_map;
|
||||
GLuint alpha_map;
|
||||
GLuint light_map;
|
||||
GLuint normal_map;
|
||||
GLuint blend_texture;
|
||||
float specular_strength;
|
||||
float specular_shininess;
|
||||
float shadow_darkness;
|
||||
@@ -108,6 +113,8 @@ enum RE_attribute_location {
|
||||
VATTR_LIGHT_UV,
|
||||
VATTR_COLOR,
|
||||
VATTR_TANGENT,
|
||||
VATTR_BLEND_WEIGHT,
|
||||
VATTR_BLEND_UV,
|
||||
};
|
||||
|
||||
struct shadow_renderer {
|
||||
@@ -152,6 +159,7 @@ struct RE_renderer {
|
||||
GLint has_bones;
|
||||
GLint global_ambient;
|
||||
GLint instance_ambient;
|
||||
GLint diffuse_mod;
|
||||
struct {
|
||||
GLint dir;
|
||||
GLint diffuse;
|
||||
@@ -159,7 +167,7 @@ struct RE_renderer {
|
||||
GLint ambient;
|
||||
} dir_lights[NR_DIR_LIGHTS];
|
||||
GLint specular_light_dir;
|
||||
GLint specular_strength;
|
||||
GLint specular_color;
|
||||
GLint specular_shininess;
|
||||
GLint use_specular_map;
|
||||
GLint specular_texture;
|
||||
@@ -182,13 +190,27 @@ struct RE_renderer {
|
||||
GLint ls_light_dir;
|
||||
GLint ls_light_color;
|
||||
GLint ls_sun_color;
|
||||
GLint hemi_light_dir;
|
||||
GLint hemi_sky_color;
|
||||
GLint hemi_mid_color;
|
||||
GLint hemi_ground_color;
|
||||
GLint tonemap_param;
|
||||
GLint tonemap_param2;
|
||||
GLint nolighting;
|
||||
GLint grayscale_rate;
|
||||
GLint alpha_mode;
|
||||
GLint alpha_texture;
|
||||
GLint uv_scroll;
|
||||
GLint uv_tiling;
|
||||
GLint blend_tex;
|
||||
GLint use_blend_texture;
|
||||
|
||||
GLuint billboard_vao;
|
||||
GLuint billboard_attr_buffer;
|
||||
struct hash_table *billboard_textures; // cg_no -> struct billboard_texture*
|
||||
GLuint billboard_particle_vao;
|
||||
GLuint billboard_particle_attr_buffer;
|
||||
GLuint billboard_instance_vao;
|
||||
GLuint billboard_instance_attr_buffer;
|
||||
struct hash_table *billboard_textures_by_no; // cg_no -> struct billboard_texture*
|
||||
struct hash_table *billboard_textures_by_path; // path -> struct billboard_texture*
|
||||
|
||||
uint32_t last_frame_timestamp;
|
||||
};
|
||||
@@ -202,6 +224,7 @@ struct billboard_texture {
|
||||
|
||||
extern enum RE_plugin_version re_plugin_version;
|
||||
|
||||
struct RE_instance *RE_create_instance_at(struct RE_plugin *plugin, int index);
|
||||
void RE_instance_update_local_transform(struct RE_instance *inst);
|
||||
|
||||
// model.c
|
||||
@@ -220,7 +243,8 @@ struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, cons
|
||||
struct RE_renderer *RE_renderer_new(void);
|
||||
void RE_renderer_free(struct RE_renderer *r);
|
||||
void RE_renderer_set_viewport_size(struct RE_renderer *r, int width, int height);
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no);
|
||||
bool RE_renderer_load_billboard_texture_by_no(struct RE_renderer *r, int cg_no);
|
||||
struct billboard_texture *RE_renderer_load_billboard_texture_by_path(struct RE_renderer *r, struct archive *aar, const char *path);
|
||||
struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r, struct model *model);
|
||||
void RE_renderer_free_height_detector(struct height_detector *hd);
|
||||
bool RE_renderer_detect_height(struct height_detector *hd, float x, float z, float *y_out);
|
||||
@@ -396,6 +420,218 @@ void particle_effect_free(struct particle_effect *effect);
|
||||
void particle_effect_update(struct RE_instance *inst);
|
||||
void particle_object_calc_local_transform(struct RE_instance *inst, struct particle_object *po, struct particle_instance *pi, float frame, mat4 dest);
|
||||
|
||||
// s3de.c
|
||||
|
||||
// .3de effect system. The 's' prefix is just to avoid an identifier starting
|
||||
// with a digit; it stands for SealEngine.
|
||||
|
||||
enum s3de_object_type {
|
||||
S3DE_OBJ_BILLBOARD,
|
||||
S3DE_OBJ_POLYGON,
|
||||
S3DE_OBJ_CAMERA,
|
||||
};
|
||||
|
||||
enum s3de_move_type {
|
||||
S3DE_MOVE_LINEAR,
|
||||
S3DE_MOVE_BEZIER,
|
||||
};
|
||||
|
||||
enum s3de_spawn_position_type {
|
||||
S3DE_SPAWN_NORMAL,
|
||||
S3DE_SPAWN_SPHERE,
|
||||
S3DE_SPAWN_HORIZONTAL,
|
||||
};
|
||||
|
||||
enum s3de_blend_type {
|
||||
S3DE_BLEND_NORMAL,
|
||||
S3DE_BLEND_ADDITIVE,
|
||||
};
|
||||
|
||||
enum s3de_emitter_link_type {
|
||||
S3DE_LINK_NONE = 0, // frozen at spawn-time world pos
|
||||
S3DE_LINK_FOLLOW = 1, // particle follows current emitter pos
|
||||
S3DE_LINK_ROTATE_AT_SPAWN = 2, // emitter rotation around spawn pos
|
||||
S3DE_LINK_ROTATE_AT_CURRENT = 3, // emitter rotation around current pos
|
||||
S3DE_LINK_MAX = S3DE_LINK_ROTATE_AT_CURRENT
|
||||
};
|
||||
|
||||
enum s3de_direction_type {
|
||||
S3DE_DIR_RANDOM,
|
||||
S3DE_DIR_SPECIFIED,
|
||||
S3DE_DIR_EMITTER,
|
||||
S3DE_DIR_EMITTER_REVERSE,
|
||||
S3DE_DIR_EMITTER_COORD,
|
||||
S3DE_DIR_ARBITRARY_PLANE,
|
||||
S3DE_DIR_SPAWN,
|
||||
};
|
||||
|
||||
enum s3de_posture_type {
|
||||
S3DE_POSE_NONE,
|
||||
S3DE_POSE_CAMERA,
|
||||
S3DE_POSE_MOVE_DIR,
|
||||
S3DE_POSE_MOVE_AND_FLY,
|
||||
S3DE_POSE_FLY_AND_EMITTER_MOVE,
|
||||
};
|
||||
|
||||
enum s3de_interp_type {
|
||||
S3DE_INTERP_NONE,
|
||||
S3DE_INTERP_LINEAR,
|
||||
S3DE_INTERP_SLERP,
|
||||
};
|
||||
|
||||
struct s3de_range {
|
||||
float base;
|
||||
float random;
|
||||
};
|
||||
|
||||
struct s3de_scalar_entry {
|
||||
int frame;
|
||||
float value;
|
||||
enum s3de_interp_type interp;
|
||||
};
|
||||
|
||||
struct s3de_vec3_entry {
|
||||
int frame;
|
||||
vec3 v;
|
||||
enum s3de_interp_type interp;
|
||||
};
|
||||
|
||||
struct s3de_position_entry {
|
||||
int frame;
|
||||
vec3 pos;
|
||||
enum s3de_interp_type interp;
|
||||
float spawn_radius;
|
||||
float spawn_angle_deg;
|
||||
};
|
||||
|
||||
struct s3de_object {
|
||||
char *name;
|
||||
enum s3de_object_type type;
|
||||
enum s3de_move_type movement_type;
|
||||
enum s3de_spawn_position_type spawn_position_type;
|
||||
float spawn_distance;
|
||||
int particle_count;
|
||||
|
||||
char *texture;
|
||||
|
||||
enum s3de_blend_type blend_type;
|
||||
char *polygon_name;
|
||||
enum s3de_emitter_link_type emitter_link_type;
|
||||
bool soft_fog_edge;
|
||||
|
||||
enum s3de_posture_type posture_type;
|
||||
enum s3de_direction_type direction_type;
|
||||
vec3 direction;
|
||||
float direction_angle;
|
||||
|
||||
struct s3de_range start_size, end_size;
|
||||
struct s3de_range start_x_size, end_x_size;
|
||||
struct s3de_range start_y_size, end_y_size;
|
||||
struct s3de_range offset_x, offset_y, offset_z;
|
||||
struct s3de_range speed, acceleration;
|
||||
struct s3de_range movement_distance, movement_curve;
|
||||
|
||||
bool free_fall;
|
||||
float mass, air_resistance;
|
||||
|
||||
struct s3de_range start_x_rotation, end_x_rotation;
|
||||
struct s3de_range start_y_rotation, end_y_rotation;
|
||||
struct s3de_range start_z_rotation, end_z_rotation;
|
||||
|
||||
float x_revolution_angle[2], y_revolution_angle[2], z_revolution_angle[2];
|
||||
float x_revolution_distance[2], y_revolution_distance[2], z_revolution_distance[2];
|
||||
|
||||
int child_frame;
|
||||
float texture_anime_frame;
|
||||
|
||||
struct s3de_range alpha_fade_in_frame, alpha_fade_out_frame;
|
||||
|
||||
int *damages;
|
||||
int nr_damages;
|
||||
|
||||
struct s3de_position_entry *position_list;
|
||||
int nr_position_entries;
|
||||
struct s3de_scalar_entry *size_list;
|
||||
int nr_size_entries;
|
||||
struct s3de_scalar_entry *x_size_list;
|
||||
int nr_x_size_entries;
|
||||
struct s3de_scalar_entry *y_size_list;
|
||||
int nr_y_size_entries;
|
||||
struct s3de_scalar_entry *alpha_list;
|
||||
int nr_alpha_entries;
|
||||
struct s3de_vec3_entry *rotation_list;
|
||||
int nr_rotation_entries;
|
||||
struct s3de_vec3_entry *multiply_color_list;
|
||||
int nr_multiply_color_entries;
|
||||
struct s3de_vec3_entry *additive_color_list;
|
||||
int nr_additive_color_entries;
|
||||
|
||||
struct model *model;
|
||||
};
|
||||
|
||||
struct s3de {
|
||||
char *path;
|
||||
int loop_start_frame;
|
||||
int loop_end_frame;
|
||||
struct s3de_object *objects;
|
||||
int nr_objects;
|
||||
struct hash_table *textures; // name -> struct billboard_texture*
|
||||
};
|
||||
|
||||
struct s3de *s3de_load(struct archive *aar, const char *path);
|
||||
void s3de_free(struct s3de *s);
|
||||
|
||||
struct s3de_particle {
|
||||
float begin_frame, end_frame;
|
||||
vec3 world_pos; // spawn-time world position
|
||||
vec3 spawn_offset; // offset from emitter pos
|
||||
vec3 direction;
|
||||
vec3 spawn_dir; // emitter motion direction at spawn frame
|
||||
float speed, accel;
|
||||
float movement_distance, movement_curve;
|
||||
float start_size_scale, end_size_scale;
|
||||
float start_x_scale, end_x_scale;
|
||||
float start_y_scale, end_y_scale;
|
||||
float offset_x, offset_y; // per-particle 2D quad pivot shift
|
||||
vec3 start_rotation_deg, end_rotation_deg;
|
||||
float fade_in_frames, fade_out_frames;
|
||||
};
|
||||
|
||||
// Emitter-level values interpolated once per frame by s3de_effect_update,
|
||||
// then read (and combined with per-particle values) during rendering.
|
||||
struct s3de_object_state {
|
||||
vec3 emitter_pos;
|
||||
float emitter_size, emitter_x_size, emitter_y_size;
|
||||
float emitter_alpha;
|
||||
vec3 emitter_rotation_deg;
|
||||
vec3 multiply_color;
|
||||
vec3 additive_color;
|
||||
struct s3de_particle *particles; // length = obj->particle_count
|
||||
};
|
||||
|
||||
struct s3de_effect {
|
||||
struct s3de *s3de;
|
||||
struct s3de_object_state *objects; // length = s3de->nr_objects
|
||||
int wav_channel; // -1 if no sound
|
||||
bool sound_started;
|
||||
float last_frame;
|
||||
};
|
||||
|
||||
struct s3de_effect *s3de_effect_create(struct s3de *s, struct archive *aar);
|
||||
void s3de_effect_free(struct s3de_effect *eff);
|
||||
void s3de_effect_update(struct RE_instance *inst);
|
||||
void s3de_calc_frame_range(struct s3de *s, struct motion *motion);
|
||||
bool s3de_particle_alpha(struct s3de_object_state *st, struct s3de_particle *p,
|
||||
float frame, float *alpha_out);
|
||||
bool s3de_billboard_world_transform(struct RE_instance *inst,
|
||||
struct s3de_object *obj, struct s3de_object_state *st,
|
||||
struct s3de_particle *p, float frame, mat3 camera_rot,
|
||||
mat4 out);
|
||||
bool s3de_mesh_world_transform(struct RE_instance *inst,
|
||||
struct s3de_object *obj, struct s3de_object_state *st,
|
||||
struct s3de_particle *p, float frame, mat3 camera_rot, vec3 camera_pos,
|
||||
mat4 out);
|
||||
|
||||
// parser.c
|
||||
|
||||
struct pol {
|
||||
@@ -426,12 +662,13 @@ struct pol_material {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
char *textures[MAX_TEXTURE_TYPE];
|
||||
vec2 uv_tiling;
|
||||
};
|
||||
|
||||
struct pol_material_group {
|
||||
struct pol_material m;
|
||||
uint32_t nr_children;
|
||||
struct pol_material *children;
|
||||
struct pol_material_group *children;
|
||||
};
|
||||
|
||||
enum mesh_flags {
|
||||
@@ -445,6 +682,11 @@ enum mesh_flags {
|
||||
MESH_NO_HEIGHT_DETECTION = 1 << 7,
|
||||
MESH_ALPHA = 1 << 8,
|
||||
MESH_HAS_LIGHT_UV = 1 << 9,
|
||||
MESH_NO_ZWRITE = 1 << 10,
|
||||
MESH_HEIGHT_DETECTION = 1 << 11,
|
||||
MESH_HAS_SPECULAR_COLOR = 1 << 12,
|
||||
MESH_HAS_SPECULAR_POWER = 1 << 13,
|
||||
MESH_NO_DRAWSHADOW = 1 << 14,
|
||||
};
|
||||
|
||||
struct pol_mesh {
|
||||
@@ -461,12 +703,18 @@ struct pol_mesh {
|
||||
vec3 *colors;
|
||||
uint32_t nr_alphas;
|
||||
float *alphas;
|
||||
uint32_t nr_blend_uvs;
|
||||
vec2 *blend_uvs;
|
||||
uint32_t nr_blend_weights;
|
||||
float *blend_weights;
|
||||
uint32_t nr_triangles;
|
||||
struct pol_triangle *triangles;
|
||||
// Parameters in .opr file.
|
||||
Color edge_color;
|
||||
float edge_size;
|
||||
vec2 uv_scroll;
|
||||
vec3 specular_color;
|
||||
float specular_power;
|
||||
};
|
||||
|
||||
struct pol_vertex {
|
||||
@@ -486,6 +734,8 @@ struct pol_triangle {
|
||||
uint32_t light_uv_index[3];
|
||||
uint32_t color_index[3];
|
||||
uint32_t alpha_index[3];
|
||||
uint32_t blend_uv_index[3];
|
||||
uint32_t blend_weight_index[3];
|
||||
vec3 normals[3];
|
||||
uint32_t material_group_index;
|
||||
};
|
||||
@@ -504,6 +754,7 @@ struct mot {
|
||||
uint32_t nr_bones;
|
||||
uint32_t nr_texture_indices;
|
||||
uint32_t *texture_indices; // texture index for each frame
|
||||
struct mpr *mpr; // optional
|
||||
struct mot_bone *motions[];
|
||||
};
|
||||
|
||||
@@ -557,6 +808,49 @@ struct amt_material *amt_find_material(struct amt *amt, const char *name);
|
||||
void opr_load(uint8_t *data, size_t size, struct pol *pol);
|
||||
void txa_load(uint8_t *data, size_t size, struct mot *mot);
|
||||
|
||||
void lit_reset(float *out);
|
||||
bool lit_parse(uint8_t *data, size_t size, float *out);
|
||||
|
||||
// mpr.c
|
||||
|
||||
struct mpr_float_key { int frame; float v; };
|
||||
struct mpr_vec3_key { int frame; vec3 v; };
|
||||
struct mpr_int_key { int frame; int v; };
|
||||
|
||||
struct mpr_track_set {
|
||||
int nr_mul_alpha;
|
||||
struct mpr_float_key *mul_alpha;
|
||||
int nr_mul_diffuse;
|
||||
struct mpr_vec3_key *mul_diffuse;
|
||||
int nr_add_ambient;
|
||||
struct mpr_vec3_key *add_ambient;
|
||||
int nr_texture_anime;
|
||||
struct mpr_int_key *texture_anime; // mesh-scope only
|
||||
};
|
||||
|
||||
struct mpr {
|
||||
int nr_meshes; // == model->nr_meshes
|
||||
struct mpr_track_set **mesh_tracks; // sparse: indexed by mesh index, NULL if absent
|
||||
struct mpr_track_set object;
|
||||
bool has_mesh_alpha; // any mesh_tracks[i]->nr_mul_alpha > 0
|
||||
};
|
||||
|
||||
// Per-draw material modulation.
|
||||
struct mpr_modulation {
|
||||
float alpha;
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
};
|
||||
|
||||
struct mpr *mpr_load(uint8_t *data, size_t size, struct model *model);
|
||||
void mpr_free(struct mpr *mpr);
|
||||
void mpr_evaluate_object(const struct mpr *mpr, float frame,
|
||||
struct RE_instance *inst, struct mpr_modulation *out);
|
||||
void mpr_evaluate_mesh(const struct mpr_track_set *mt, float frame,
|
||||
const struct mpr_modulation *obj, struct mpr_modulation *out);
|
||||
void mpr_build_mat_tex_index(const struct model *model, const struct RE_instance *inst,
|
||||
const struct mpr *mpr, float frame, int *mat_tex_index);
|
||||
|
||||
// collision.c
|
||||
|
||||
struct collider_triangle;
|
||||
@@ -572,6 +866,7 @@ struct collider {
|
||||
};
|
||||
|
||||
struct collider *collider_create(struct pol_mesh *mesh);
|
||||
struct collider *collider_create_raycast(struct pol_mesh **meshes, int nr_meshes);
|
||||
void collider_free(struct collider *collider);
|
||||
bool collider_height(struct collider *collider, vec2 xz, float *h_out);
|
||||
bool check_collision(struct collider *collider, vec2 p0, vec2 p1, float radius, vec2 out);
|
||||
|
||||
+33
-8
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <stdlib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
@@ -36,11 +37,8 @@ struct collider_edge {
|
||||
vec2 aabb[2];
|
||||
};
|
||||
|
||||
static void init_triangles(struct collider *collider, struct pol_mesh *mesh)
|
||||
static struct collider_triangle *fill_triangles(struct collider_triangle *t, struct pol_mesh *mesh)
|
||||
{
|
||||
collider->nr_triangles = mesh->nr_triangles;
|
||||
collider->triangles = xcalloc(mesh->nr_triangles, sizeof(struct collider_triangle));
|
||||
struct collider_triangle *t = collider->triangles;
|
||||
for (int tri_i = 0; tri_i < mesh->nr_triangles; tri_i++, t++) {
|
||||
glm_aabb2d_invalidate(t->aabb);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
@@ -65,6 +63,14 @@ static void init_triangles(struct collider *collider, struct pol_mesh *mesh)
|
||||
t->neighbors[i] = -1;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static void init_triangles(struct collider *collider, struct pol_mesh *mesh)
|
||||
{
|
||||
collider->nr_triangles = mesh->nr_triangles;
|
||||
collider->triangles = xcalloc(mesh->nr_triangles, sizeof(struct collider_triangle));
|
||||
fill_triangles(collider->triangles, mesh);
|
||||
}
|
||||
|
||||
static void link_neighbors(struct collider *collider, int t1_index, int t2_index)
|
||||
@@ -137,6 +143,20 @@ struct collider *collider_create(struct pol_mesh *mesh)
|
||||
return collider;
|
||||
}
|
||||
|
||||
// Creates a collider for raycasting only (no edges / neighbor links), from the
|
||||
// union of the given meshes' triangles. Used for SealEngine HeightDetection meshes.
|
||||
struct collider *collider_create_raycast(struct pol_mesh **meshes, int nr_meshes)
|
||||
{
|
||||
struct collider *collider = xcalloc(1, sizeof(struct collider));
|
||||
for (int i = 0; i < nr_meshes; i++)
|
||||
collider->nr_triangles += meshes[i]->nr_triangles;
|
||||
collider->triangles = xcalloc(collider->nr_triangles, sizeof(struct collider_triangle));
|
||||
struct collider_triangle *t = collider->triangles;
|
||||
for (int i = 0; i < nr_meshes; i++)
|
||||
t = fill_triangles(t, meshes[i]);
|
||||
return collider;
|
||||
}
|
||||
|
||||
void collider_free(struct collider *collider)
|
||||
{
|
||||
free(collider->triangles);
|
||||
@@ -275,16 +295,21 @@ bool check_collision(struct collider *collider, vec2 p0, vec2 p1, float radius,
|
||||
|
||||
bool collider_raycast(struct collider *collider, vec3 origin, vec3 direction, vec3 out)
|
||||
{
|
||||
// Return the intersection nearest to the ray origin (closest to the camera).
|
||||
float nearest = FLT_MAX;
|
||||
struct collider_triangle* t = collider->triangles;
|
||||
for (; t < collider->triangles + collider->nr_triangles; t++) {
|
||||
float d;
|
||||
if (glm_ray_triangle(origin, direction, t->vertices[0], t->vertices[1], t->vertices[2], &d)) {
|
||||
glm_vec3_scale(direction, d, out);
|
||||
glm_vec3_add(origin, out, out);
|
||||
return true;
|
||||
if (d < nearest)
|
||||
nearest = d;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if (nearest == FLT_MAX)
|
||||
return false;
|
||||
glm_vec3_scale(direction, nearest, out);
|
||||
glm_vec3_add(origin, out, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct pathfinder_node {
|
||||
|
||||
+249
-2
@@ -18,8 +18,12 @@
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "cJSON.h"
|
||||
#include "debugger.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "json.h"
|
||||
#include "reign.h"
|
||||
#include "sact.h"
|
||||
#include "scene.h"
|
||||
#include "sprite.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
@@ -115,6 +119,9 @@ static cJSON *instance_to_json(struct RE_instance *inst, int index, bool verbose
|
||||
if (inst->effect) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->effect->pae->path);
|
||||
}
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->s3de_effect->s3de->path);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -143,9 +150,8 @@ static cJSON *back_cg_to_json(struct RE_back_cg *bcg, int index, bool verbose)
|
||||
return obj;
|
||||
}
|
||||
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose)
|
||||
static cJSON *re_plugin_to_json(struct RE_plugin *p, bool verbose)
|
||||
{
|
||||
struct RE_plugin *p = (struct RE_plugin*)sp->plugin;
|
||||
cJSON *obj, *cam, *a;
|
||||
|
||||
obj = cJSON_CreateObject();
|
||||
@@ -198,3 +204,244 @@ cJSON *RE_to_json(struct sact_sprite *sp, bool verbose)
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose)
|
||||
{
|
||||
return re_plugin_to_json((struct RE_plugin*)sp->plugin, verbose);
|
||||
}
|
||||
|
||||
#ifdef DEBUGGER_ENABLED
|
||||
|
||||
static int dbg_current_plugin = -1;
|
||||
|
||||
// Returns the plugin number to use. Falls back to the only existing plugin
|
||||
// if no current is set. Prints an error and returns -1 if neither holds.
|
||||
static int dbg_resolve_plugin(void)
|
||||
{
|
||||
if (dbg_current_plugin >= 0) {
|
||||
if (RE_get_plugin(dbg_current_plugin))
|
||||
return dbg_current_plugin;
|
||||
DBG_ERROR("Current plugin %d no longer exists", dbg_current_plugin);
|
||||
dbg_current_plugin = -1;
|
||||
return -1;
|
||||
}
|
||||
int found = -1;
|
||||
for (int h = 0; h < RE_MAX_PLUGINS; h++) {
|
||||
if (!RE_get_plugin(h))
|
||||
continue;
|
||||
if (found >= 0) {
|
||||
DBG_ERROR("Multiple plugins exist; use '3d plugin <no>' to choose one");
|
||||
return -1;
|
||||
}
|
||||
found = h;
|
||||
}
|
||||
if (found < 0) {
|
||||
DBG_ERROR("No ReignEngine plugins");
|
||||
return -1;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
static struct RE_plugin *dbg_current_re_plugin(void)
|
||||
{
|
||||
int no = dbg_resolve_plugin();
|
||||
return no < 0 ? NULL : RE_get_plugin(no);
|
||||
}
|
||||
|
||||
static bool instance_has_content(struct RE_instance *inst)
|
||||
{
|
||||
if (inst->type == RE_ITYPE_UNINITIALIZED)
|
||||
return false;
|
||||
if (inst->model && inst->model->path)
|
||||
return true;
|
||||
if (inst->motion && inst->motion->mot)
|
||||
return true;
|
||||
if (inst->effect && inst->effect->pae)
|
||||
return true;
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void instance_list_print(struct RE_instance *inst, int index, int indent)
|
||||
{
|
||||
indent_message(indent, inst->draw ? "+ " : "- ");
|
||||
sys_message("instance %d (%s)", index, instance_type_name(inst->type));
|
||||
if (inst->model && inst->model->path)
|
||||
sys_message(" model=%s", display_sjis0(inst->model->path));
|
||||
if (inst->motion && inst->motion->mot)
|
||||
sys_message(" motion=%s/%s frame=%.2f",
|
||||
display_sjis0(inst->motion->mot->name),
|
||||
motion_state_name(inst->motion->state),
|
||||
inst->motion->current_frame);
|
||||
if (inst->effect && inst->effect->pae)
|
||||
sys_message(" effect=%s", display_sjis0(inst->effect->pae->path));
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de)
|
||||
sys_message(" s3de=%s", display_sjis0(inst->s3de_effect->s3de->path));
|
||||
sys_message("\n");
|
||||
}
|
||||
|
||||
static void re_cmd_list(unsigned nr_args, char **args)
|
||||
{
|
||||
bool show_all = nr_args >= 1 && !strcmp(args[0], "all");
|
||||
for (int h = 0; h < RE_MAX_PLUGINS; h++) {
|
||||
struct RE_plugin *p = RE_get_plugin(h);
|
||||
if (!p)
|
||||
continue;
|
||||
int nr_hidden = 0;
|
||||
sys_message("plugin %d (%s sprite=%d nr_instances=%d)\n",
|
||||
h, p->plugin.name, p->sprite, p->nr_instances);
|
||||
for (int i = 0; i < p->nr_instances; i++) {
|
||||
struct RE_instance *inst = p->instances[i];
|
||||
if (!inst)
|
||||
continue;
|
||||
if (!show_all && !instance_has_content(inst)) {
|
||||
nr_hidden++;
|
||||
continue;
|
||||
}
|
||||
instance_list_print(inst, i, 1);
|
||||
}
|
||||
if (nr_hidden)
|
||||
indent_message(1, "(%d empty instance(s) hidden; use 'list all' to show)\n",
|
||||
nr_hidden);
|
||||
}
|
||||
}
|
||||
|
||||
static void re_cmd_plugin(unsigned nr_args, char **args)
|
||||
{
|
||||
if (nr_args == 0) {
|
||||
int no = dbg_resolve_plugin();
|
||||
if (no < 0)
|
||||
return;
|
||||
struct RE_plugin *p = RE_get_plugin(no);
|
||||
sys_message("plugin %d (%s)%s\n", no, p->plugin.name,
|
||||
dbg_current_plugin < 0 ? " (auto-selected)" : "");
|
||||
return;
|
||||
}
|
||||
int no = atoi(args[0]);
|
||||
if (!RE_get_plugin(no)) {
|
||||
DBG_ERROR("No ReignEngine plugin with handle '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
dbg_current_plugin = no;
|
||||
}
|
||||
|
||||
static void re_cmd_show(unsigned nr_args, char **args)
|
||||
{
|
||||
struct RE_plugin *p = dbg_current_re_plugin();
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
cJSON *json;
|
||||
if (nr_args >= 1) {
|
||||
int idx = atoi(args[0]);
|
||||
if (idx < 0 || idx >= p->nr_instances || !p->instances[idx]) {
|
||||
DBG_ERROR("No instance with index '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
json = instance_to_json(p->instances[idx], idx, true);
|
||||
} else {
|
||||
json = re_plugin_to_json(p, true);
|
||||
}
|
||||
char *text = cJSON_Print(json);
|
||||
|
||||
sys_message("%s\n", text);
|
||||
|
||||
free(text);
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
static void re_cmd_set(unsigned nr_args, char **args)
|
||||
{
|
||||
struct RE_plugin *p = dbg_current_re_plugin();
|
||||
if (!p)
|
||||
return;
|
||||
int idx = atoi(args[0]);
|
||||
if (idx < 0 || idx >= p->nr_instances || !p->instances[idx]) {
|
||||
DBG_ERROR("No instance with index '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
struct RE_instance *inst = p->instances[idx];
|
||||
const char *prop = args[1];
|
||||
unsigned nr_values = nr_args - 2;
|
||||
char **values = &args[2];
|
||||
|
||||
if (!strcmp(prop, "draw")) {
|
||||
if (nr_values != 1) {
|
||||
DBG_ERROR("'draw' requires 1 value (0 or 1)");
|
||||
return;
|
||||
}
|
||||
inst->draw = atoi(values[0]) != 0;
|
||||
} else if (!strcmp(prop, "pos")) {
|
||||
if (nr_values != 3) {
|
||||
DBG_ERROR("'pos' requires 3 values (x y z)");
|
||||
return;
|
||||
}
|
||||
inst->pos[0] = strtof(values[0], NULL);
|
||||
inst->pos[1] = strtof(values[1], NULL);
|
||||
inst->pos[2] = strtof(values[2], NULL);
|
||||
inst->local_transform_needs_update = true;
|
||||
} else {
|
||||
DBG_ERROR("Unknown property '%s'", prop);
|
||||
return;
|
||||
}
|
||||
|
||||
sprite_call_plugins();
|
||||
scene_render();
|
||||
gfx_swap();
|
||||
}
|
||||
|
||||
static void re_cmd_help(unsigned nr_args, char **args);
|
||||
|
||||
static struct dbg_cmd re_cmds[] = {
|
||||
{ "list", NULL, "[all]",
|
||||
"Display ReignEngine plugins and their instances (use 'all' to include uninitialized)",
|
||||
0, 1, re_cmd_list },
|
||||
{ "plugin", NULL, "[<plugin-no>]",
|
||||
"Show or set the current plugin used by 'show' and 'set'",
|
||||
0, 1, re_cmd_plugin },
|
||||
{ "show", NULL, "[<instance-no>]",
|
||||
"Display current plugin or instance state as JSON",
|
||||
0, 1, re_cmd_show },
|
||||
{ "set", NULL, "<instance-no> <property> <value...>",
|
||||
"Set an instance property (draw, pos)",
|
||||
3, 5, re_cmd_set },
|
||||
{ "help", "h", NULL, "List 3d commands",
|
||||
0, 0, re_cmd_help },
|
||||
};
|
||||
|
||||
static void re_cmd_help(unsigned nr_args, char **args)
|
||||
{
|
||||
int name_len = 0;
|
||||
for (unsigned i = 0; i < sizeof(re_cmds)/sizeof(*re_cmds); i++) {
|
||||
int n = strlen(re_cmds[i].fullname);
|
||||
if (n > name_len)
|
||||
name_len = n;
|
||||
}
|
||||
sys_message("\n3d commands\n===========\n");
|
||||
for (unsigned i = 0; i < sizeof(re_cmds)/sizeof(*re_cmds); i++) {
|
||||
struct dbg_cmd *c = &re_cmds[i];
|
||||
sys_message("%*s", name_len, c->fullname);
|
||||
if (c->shortname)
|
||||
sys_message(" (%s)", c->shortname);
|
||||
if (c->arg_description)
|
||||
sys_message(" %s", c->arg_description);
|
||||
sys_message(" -- %s\n", c->description);
|
||||
}
|
||||
}
|
||||
|
||||
void re_debug_init(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized)
|
||||
return;
|
||||
initialized = true;
|
||||
|
||||
dbg_cmd_add_module("3d", sizeof(re_cmds)/sizeof(*re_cmds), re_cmds);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void re_debug_init(void) {}
|
||||
|
||||
#endif // DEBUGGER_ENABLED
|
||||
|
||||
+84
-37
@@ -54,6 +54,11 @@ struct vertex_bones {
|
||||
GLfloat bone_weight[NR_WEIGHTS];
|
||||
};
|
||||
|
||||
struct vertex_blend {
|
||||
GLfloat blend_weight;
|
||||
GLfloat blend_uv[2];
|
||||
};
|
||||
|
||||
static bool is_transparent_mesh(const struct pol_mesh *mesh)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
@@ -138,9 +143,11 @@ static GLuint *load_texture_list(struct archive *aar, const char *path, const ch
|
||||
return textures;
|
||||
}
|
||||
|
||||
static bool init_material(struct material *material, const struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
|
||||
static bool init_material(struct material *material, struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
|
||||
{
|
||||
material->flags = m->flags;
|
||||
glm_vec2_copy(m->uv_tiling, material->uv_tiling);
|
||||
glm_vec2_one(material->blend_uv_tiling);
|
||||
if (!m->textures[COLOR_MAP]) {
|
||||
WARNING("No color texture");
|
||||
return false;
|
||||
@@ -202,6 +209,8 @@ static void destroy_material(struct material *material)
|
||||
glDeleteTextures(1, &material->light_map);
|
||||
if (material->normal_map)
|
||||
glDeleteTextures(1, &material->normal_map);
|
||||
if (material->blend_texture)
|
||||
glDeleteTextures(1, &material->blend_texture);
|
||||
}
|
||||
|
||||
static int cmp_by_bone_weight(const void *lhs, const void *rhs)
|
||||
@@ -276,6 +285,7 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
bool has_vertex_colors = m->nr_colors > 0 || m->nr_alphas > 0;
|
||||
bool has_normal_map = model->materials[material].normal_map != 0;
|
||||
bool has_bones = !!model->bone_map;
|
||||
bool has_blend = m->blend_weights && model->materials[material].blend_texture;
|
||||
|
||||
GLsizei stride = sizeof(struct vertex_common);
|
||||
if (has_light_map)
|
||||
@@ -286,6 +296,8 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
stride += sizeof(struct vertex_tangent);
|
||||
if (has_bones)
|
||||
stride += sizeof(struct vertex_bones);
|
||||
if (has_blend)
|
||||
stride += sizeof(struct vertex_blend);
|
||||
|
||||
void *buffer = xmalloc(m->nr_triangles * 3 * stride);
|
||||
uint8_t *ptr = buffer;
|
||||
@@ -336,6 +348,14 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
}
|
||||
}
|
||||
}
|
||||
if (has_blend) {
|
||||
struct vertex_blend *v_blend = buf_alloc(&ptr, sizeof(struct vertex_blend));
|
||||
v_blend->blend_weight = m->blend_weights[t->blend_weight_index[j]];
|
||||
if (m->blend_uvs)
|
||||
glm_vec2_copy(m->blend_uvs[t->blend_uv_index[j]], v_blend->blend_uv);
|
||||
else
|
||||
glm_vec2_copy(m->uvs[t->uv_index[j]], v_blend->blend_uv);
|
||||
}
|
||||
nr_vertices++;
|
||||
}
|
||||
}
|
||||
@@ -361,6 +381,8 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
mesh->outline_color[2] = m->edge_color.b / 255.f;
|
||||
mesh->outline_thickness = m->edge_size ? m->edge_size : DEFAULT_OUTLINE_THICKNESS;
|
||||
glm_vec2_copy(m->uv_scroll, mesh->uv_scroll);
|
||||
glm_vec3_copy(m->specular_color, mesh->specular_color);
|
||||
mesh->specular_power = m->specular_power;
|
||||
mesh->nr_vertices = nr_vertices;
|
||||
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
@@ -412,6 +434,18 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
glDisableVertexAttribArray(VATTR_BONE_WEIGHT);
|
||||
glVertexAttrib4f(VATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
if (has_blend) {
|
||||
glEnableVertexAttribArray(VATTR_BLEND_WEIGHT);
|
||||
glVertexAttribPointer(VATTR_BLEND_WEIGHT, 1, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_blend, blend_weight));
|
||||
glEnableVertexAttribArray(VATTR_BLEND_UV);
|
||||
glVertexAttribPointer(VATTR_BLEND_UV, 2, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_blend, blend_uv));
|
||||
base += sizeof(struct vertex_blend);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_BLEND_WEIGHT);
|
||||
glVertexAttrib1f(VATTR_BLEND_WEIGHT, 0.0);
|
||||
glDisableVertexAttribArray(VATTR_BLEND_UV);
|
||||
glVertexAttrib2f(VATTR_BLEND_UV, 0.0, 0.0);
|
||||
}
|
||||
assert((intptr_t)base == stride);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh->nr_vertices * stride, buffer, GL_STATIC_DRAW);
|
||||
@@ -455,14 +489,6 @@ static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bo
|
||||
glm_quat_mat4(pol_bone->rotq, bone->inverse_bind_matrix);
|
||||
glm_translate(bone->inverse_bind_matrix, pol_bone->pos);
|
||||
|
||||
// Update bone_name_map. If the bone name is not unique in the POL, set the
|
||||
// map value to NULL so that ID matching will be used.
|
||||
struct ht_slot *slot = ht_put(model->bone_name_map, pol_bone->name, bone);
|
||||
if (slot->value != bone) {
|
||||
NOTICE("%s: non-unique bone %s", model->path, pol_bone->name);
|
||||
slot->value = NULL;
|
||||
}
|
||||
|
||||
model->nr_bones++;
|
||||
return bone;
|
||||
}
|
||||
@@ -516,11 +542,11 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
if (pol->nr_bones > MAX_BONES)
|
||||
ERROR("%s: Too many bones (%u)", model->path, pol->nr_bones);
|
||||
model->bone_map = ht_create(pol->nr_bones * 3 / 2);
|
||||
model->bone_name_map = ht_create(pol->nr_bones * 3 / 2);
|
||||
model->mot_cache = ht_create(16);
|
||||
model->bones = xcalloc(pol->nr_bones, sizeof(struct bone));
|
||||
model->bones = xcalloc_aligned(pol->nr_bones, struct bone);
|
||||
model->bones_by_pol_index = xcalloc(pol->nr_bones, sizeof(struct bone *));
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
add_bone(model, pol, &pol->bones[i]);
|
||||
model->bones_by_pol_index[i] = add_bone(model, pol, &pol->bones[i]);
|
||||
}
|
||||
if (model->nr_bones != (int)pol->nr_bones)
|
||||
ERROR("%s: Broken bone data", model->path);
|
||||
@@ -543,12 +569,27 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
continue;
|
||||
}
|
||||
for (uint32_t j = 0; j < pol->materials[i].nr_children; j++) {
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&pol->materials[i].children[j], amt, aar, path);
|
||||
struct pol_material_group *child = &pol->materials[i].children[j];
|
||||
if (child->nr_children >= 2) {
|
||||
// Group node: texture blending (base + blend)
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&child->children[0].m, amt, aar, path);
|
||||
if (child->children[1].m.textures[COLOR_MAP]) {
|
||||
model->materials[material_offsets[i] + j].blend_texture =
|
||||
load_texture(aar, path, child->children[1].m.textures[COLOR_MAP], NULL);
|
||||
glm_vec2_copy(child->children[1].m.uv_tiling,
|
||||
model->materials[material_offsets[i] + j].blend_uv_tiling);
|
||||
}
|
||||
} else {
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&child->m, amt, aar, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Meshes
|
||||
struct pol_mesh **hd_meshes = xmalloc(pol->nr_meshes * sizeof(*hd_meshes));
|
||||
int nr_hd_meshes = 0;
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!pol->meshes[i])
|
||||
continue;
|
||||
@@ -559,6 +600,8 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
model->collider = collider_create(pol->meshes[i]);
|
||||
continue;
|
||||
}
|
||||
if (pol->meshes[i]->flags & MESH_HEIGHT_DETECTION)
|
||||
hd_meshes[nr_hd_meshes++] = pol->meshes[i];
|
||||
struct pol_material_group *mg = &pol->materials[pol->meshes[i]->material];
|
||||
int m_off = material_offsets[pol->meshes[i]->material];
|
||||
if (mg->nr_children == 0) {
|
||||
@@ -570,6 +613,10 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
}
|
||||
}
|
||||
|
||||
if (!model->collider && nr_hd_meshes > 0)
|
||||
model->collider = collider_create_raycast(hd_meshes, nr_hd_meshes);
|
||||
free(hd_meshes);
|
||||
|
||||
pol_compute_aabb(pol, model->aabb);
|
||||
|
||||
free(material_offsets);
|
||||
@@ -594,11 +641,10 @@ void model_free(struct model *model)
|
||||
|
||||
for (int i = 0; i < model->nr_bones; i++)
|
||||
destroy_bone(&model->bones[i]);
|
||||
free(model->bones);
|
||||
xfree_aligned(model->bones);
|
||||
free(model->bones_by_pol_index);
|
||||
if (model->bone_map)
|
||||
ht_free_int(model->bone_map);
|
||||
if (model->bone_name_map)
|
||||
ht_free(model->bone_name_map);
|
||||
if (model->mot_cache) {
|
||||
ht_foreach_value(model->mot_cache, (void(*)(void*))mot_free);
|
||||
ht_free(model->mot_cache);
|
||||
@@ -693,6 +739,8 @@ struct model *model_create_sphere(int r, int g, int b, int a)
|
||||
model->materials = xcalloc(1, sizeof(struct material));
|
||||
struct material *material = &model->materials[0];
|
||||
material->is_transparent = true;
|
||||
glm_vec2_one(material->uv_tiling);
|
||||
glm_vec2_one(material->blend_uv_tiling);
|
||||
material->color_maps = xmalloc(sizeof(GLuint));
|
||||
material->nr_color_maps = 1;
|
||||
glGenTextures(1, material->color_maps);
|
||||
@@ -705,11 +753,6 @@ struct model *model_create_sphere(int r, int g, int b, int a)
|
||||
return model;
|
||||
}
|
||||
|
||||
static int cmp_motions_by_bone_id(const void *lhs, const void *rhs)
|
||||
{
|
||||
return (*(struct mot_bone **)lhs)->id - (*(struct mot_bone **)rhs)->id;
|
||||
}
|
||||
|
||||
static struct mot *mot_load(const char *name, struct model *model, struct archive *aar)
|
||||
{
|
||||
struct archive_data *mot_file = RE_get_aar_entry(aar, model->path, name, ".MOT");
|
||||
@@ -729,24 +772,28 @@ static struct mot *mot_load(const char *name, struct model *model, struct archiv
|
||||
ERROR("%s: wrong number of bones. Expected %d but got %d", name, model->nr_bones, mot->nr_bones);
|
||||
|
||||
// Reorder mot->motions so that motion for model->bones[i] can be
|
||||
// accessed by mot->motions[i].
|
||||
// accessed by mot->motions[i]. MOT bones are applied to POL bones purely
|
||||
// by array index; bone name and bone id are never used for matching.
|
||||
struct mot_bone **reordered = xmalloc(mot->nr_bones * sizeof(struct mot_bone *));
|
||||
for (uint32_t i = 0; i < mot->nr_bones; i++) {
|
||||
// Match by name first, since some MOT have wrong bone IDs (e.g. maidsan_ahoge_*).
|
||||
struct bone *bone = ht_get(model->bone_name_map, mot->motions[i]->name, NULL);
|
||||
// If it is not found or is NULL (non-unique bone name), match by bone ID.
|
||||
if (!bone)
|
||||
bone = ht_get_int(model->bone_map, mot->motions[i]->id, NULL);
|
||||
if (!bone)
|
||||
ERROR("%s: invalid bone \"%s\" (%d)", name, mot->motions[i]->name, mot->motions[i]->id);
|
||||
mot->motions[i]->id = bone->index;
|
||||
reordered[model->bones_by_pol_index[i]->index] = mot->motions[i];
|
||||
}
|
||||
qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
|
||||
memcpy(mot->motions, reordered, mot->nr_bones * sizeof(struct mot_bone *));
|
||||
free(reordered);
|
||||
|
||||
// Load optional .txa file.
|
||||
struct archive_data *txa_file = RE_get_aar_entry(aar, model->path, name, ".txa");
|
||||
if (txa_file) {
|
||||
txa_load(txa_file->data, txa_file->size, mot);
|
||||
archive_free_data(txa_file);
|
||||
// Load optional sidecar file.
|
||||
if (re_plugin_version <= RE_TAPIR_PLUGIN) {
|
||||
struct archive_data *txa_file = RE_get_aar_entry(aar, model->path, name, ".txa");
|
||||
if (txa_file) {
|
||||
txa_load(txa_file->data, txa_file->size, mot);
|
||||
archive_free_data(txa_file);
|
||||
}
|
||||
} else {
|
||||
struct archive_data *mpr_file = RE_get_aar_entry(aar, model->path, name, ".mpr");
|
||||
if (mpr_file) {
|
||||
mot->mpr = mpr_load(mpr_file->data, mpr_file->size, model);
|
||||
archive_free_data(mpr_file);
|
||||
}
|
||||
}
|
||||
|
||||
return mot;
|
||||
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
// .mpr parser.
|
||||
|
||||
static int resolve_mesh_index(struct model *model, const char *name)
|
||||
{
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
if (!strcmp(model->meshes[i].name, name))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void append_float_key(struct mpr_track_set *ts, int frame, float v)
|
||||
{
|
||||
ts->mul_alpha = xrealloc(ts->mul_alpha, (ts->nr_mul_alpha + 1) * sizeof(struct mpr_float_key));
|
||||
ts->mul_alpha[ts->nr_mul_alpha].frame = frame;
|
||||
ts->mul_alpha[ts->nr_mul_alpha].v = v;
|
||||
ts->nr_mul_alpha++;
|
||||
}
|
||||
|
||||
static void append_vec3_key(struct mpr_vec3_key **arr, int *nr, int frame, float r, float g, float b)
|
||||
{
|
||||
*arr = xrealloc(*arr, (*nr + 1) * sizeof(struct mpr_vec3_key));
|
||||
(*arr)[*nr].frame = frame;
|
||||
(*arr)[*nr].v[0] = r;
|
||||
(*arr)[*nr].v[1] = g;
|
||||
(*arr)[*nr].v[2] = b;
|
||||
(*nr)++;
|
||||
}
|
||||
|
||||
static void append_int_key(struct mpr_int_key **arr, int *nr, int frame, int v)
|
||||
{
|
||||
*arr = xrealloc(*arr, (*nr + 1) * sizeof(struct mpr_int_key));
|
||||
(*arr)[*nr].frame = frame;
|
||||
(*arr)[*nr].v = v;
|
||||
(*nr)++;
|
||||
}
|
||||
|
||||
// All mpr_*_key structs have `int frame` as their first field.
|
||||
static int cmp_key_by_frame(const void *a, const void *b)
|
||||
{
|
||||
return *(const int *)a - *(const int *)b;
|
||||
}
|
||||
|
||||
static void sort_track_set(struct mpr_track_set *ts)
|
||||
{
|
||||
if (ts->nr_mul_alpha)
|
||||
qsort(ts->mul_alpha, ts->nr_mul_alpha, sizeof(struct mpr_float_key), cmp_key_by_frame);
|
||||
if (ts->nr_mul_diffuse)
|
||||
qsort(ts->mul_diffuse, ts->nr_mul_diffuse, sizeof(struct mpr_vec3_key), cmp_key_by_frame);
|
||||
if (ts->nr_add_ambient)
|
||||
qsort(ts->add_ambient, ts->nr_add_ambient, sizeof(struct mpr_vec3_key), cmp_key_by_frame);
|
||||
if (ts->nr_texture_anime)
|
||||
qsort(ts->texture_anime, ts->nr_texture_anime, sizeof(struct mpr_int_key), cmp_key_by_frame);
|
||||
}
|
||||
|
||||
struct mpr *mpr_load(uint8_t *data, size_t size, struct model *model)
|
||||
{
|
||||
struct mpr *mpr = xcalloc(1, sizeof(struct mpr));
|
||||
mpr->nr_meshes = model->nr_meshes;
|
||||
mpr->mesh_tracks = xcalloc(model->nr_meshes, sizeof(struct mpr_track_set *));
|
||||
struct mpr_track_set *current = NULL; // current Mesh-scope track set
|
||||
|
||||
while (size > 0) {
|
||||
char line[200];
|
||||
uint8_t *nl = memchr(data, '\n', size);
|
||||
if (nl)
|
||||
nl++;
|
||||
else
|
||||
nl = data + size;
|
||||
int copylen = min(nl - data, sizeof(line) - 1);
|
||||
memcpy(line, (char *)data, copylen);
|
||||
line[copylen] = '\0';
|
||||
size -= nl - data;
|
||||
data = nl;
|
||||
|
||||
char s[200];
|
||||
int frame, ti;
|
||||
float a, r, g, b;
|
||||
if (sscanf(line, "Mesh = \"%[^\"]\"", s) == 1) {
|
||||
int mi = resolve_mesh_index(model, s);
|
||||
if (mi < 0) {
|
||||
WARNING("mpr: unknown mesh \"%s\"", s);
|
||||
current = NULL;
|
||||
} else {
|
||||
if (!mpr->mesh_tracks[mi])
|
||||
mpr->mesh_tracks[mi] = xcalloc(1, sizeof(struct mpr_track_set));
|
||||
current = mpr->mesh_tracks[mi];
|
||||
}
|
||||
} else if (sscanf(line, "MeshMulAlpha = ( %d , %f )", &frame, &a) == 2) {
|
||||
if (current)
|
||||
append_float_key(current, frame, a);
|
||||
else
|
||||
WARNING("mpr: MeshMulAlpha outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshMulDiffuse = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
if (current)
|
||||
append_vec3_key(¤t->mul_diffuse, ¤t->nr_mul_diffuse, frame, r, g, b);
|
||||
else
|
||||
WARNING("mpr: MeshMulDiffuse outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshAddAmbient = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
if (current)
|
||||
append_vec3_key(¤t->add_ambient, ¤t->nr_add_ambient, frame, r, g, b);
|
||||
else
|
||||
WARNING("mpr: MeshAddAmbient outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshTextureAnime = ( %d , %d )", &frame, &ti) == 2) {
|
||||
if (current)
|
||||
append_int_key(¤t->texture_anime, ¤t->nr_texture_anime, frame, ti);
|
||||
else
|
||||
WARNING("mpr: MeshTextureAnime outside Mesh scope");
|
||||
} else if (sscanf(line, "ObjectMulAlpha = ( %d , %f )", &frame, &a) == 2) {
|
||||
append_float_key(&mpr->object, frame, a);
|
||||
} else if (sscanf(line, "ObjectMulDiffuse = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
append_vec3_key(&mpr->object.mul_diffuse, &mpr->object.nr_mul_diffuse, frame, r, g, b);
|
||||
} else if (sscanf(line, "ObjectAddAmbient = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
append_vec3_key(&mpr->object.add_ambient, &mpr->object.nr_add_ambient, frame, r, g, b);
|
||||
} else if (strchr(line, '=')) {
|
||||
WARNING("mpr: unknown field: %s", line);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
if (!mpr->mesh_tracks[i])
|
||||
continue;
|
||||
sort_track_set(mpr->mesh_tracks[i]);
|
||||
if (mpr->mesh_tracks[i]->nr_mul_alpha > 0)
|
||||
mpr->has_mesh_alpha = true;
|
||||
}
|
||||
sort_track_set(&mpr->object);
|
||||
return mpr;
|
||||
}
|
||||
|
||||
static void destroy_track_set(struct mpr_track_set *ts)
|
||||
{
|
||||
free(ts->mul_alpha);
|
||||
free(ts->mul_diffuse);
|
||||
free(ts->add_ambient);
|
||||
free(ts->texture_anime);
|
||||
}
|
||||
|
||||
void mpr_free(struct mpr *mpr)
|
||||
{
|
||||
if (!mpr)
|
||||
return;
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
if (mpr->mesh_tracks[i]) {
|
||||
destroy_track_set(mpr->mesh_tracks[i]);
|
||||
free(mpr->mesh_tracks[i]);
|
||||
}
|
||||
}
|
||||
free(mpr->mesh_tracks);
|
||||
destroy_track_set(&mpr->object);
|
||||
free(mpr);
|
||||
}
|
||||
|
||||
// Keyframe evaluation.
|
||||
|
||||
// Returns the largest index i with keys[i].frame <= frame.
|
||||
// Returns -1 if frame < keys[0].frame.
|
||||
#define DEFINE_FIND_KEY(NAME, KEY_TYPE) \
|
||||
static int NAME(const KEY_TYPE *keys, int nr, float frame) \
|
||||
{ \
|
||||
if (frame < (float)keys[0].frame) \
|
||||
return -1; \
|
||||
int lo = 0, hi = nr - 1; \
|
||||
while (lo < hi) { \
|
||||
int mid = (lo + hi + 1) / 2; \
|
||||
if ((float)keys[mid].frame <= frame) \
|
||||
lo = mid; \
|
||||
else \
|
||||
hi = mid - 1; \
|
||||
} \
|
||||
return lo; \
|
||||
}
|
||||
|
||||
DEFINE_FIND_KEY(find_float_key, struct mpr_float_key)
|
||||
DEFINE_FIND_KEY(find_vec3_key, struct mpr_vec3_key)
|
||||
DEFINE_FIND_KEY(find_int_key, struct mpr_int_key)
|
||||
|
||||
static float eval_float(const struct mpr_float_key *keys, int nr, float frame, float fallback)
|
||||
{
|
||||
if (nr == 0)
|
||||
return fallback;
|
||||
int i = find_float_key(keys, nr, frame);
|
||||
if (i < 0)
|
||||
return keys[0].v;
|
||||
if (i >= nr - 1)
|
||||
return keys[nr - 1].v;
|
||||
float t = (frame - keys[i].frame) / (float)(keys[i + 1].frame - keys[i].frame);
|
||||
return glm_lerp(keys[i].v, keys[i + 1].v, t);
|
||||
}
|
||||
|
||||
static void eval_vec3(const struct mpr_vec3_key *keys, int nr, float frame, const vec3 fallback, vec3 out)
|
||||
{
|
||||
if (nr == 0) {
|
||||
glm_vec3_copy((float *)fallback, out);
|
||||
return;
|
||||
}
|
||||
int i = find_vec3_key(keys, nr, frame);
|
||||
if (i < 0) {
|
||||
glm_vec3_copy((float *)keys[0].v, out);
|
||||
return;
|
||||
}
|
||||
if (i >= nr - 1) {
|
||||
glm_vec3_copy((float *)keys[nr - 1].v, out);
|
||||
return;
|
||||
}
|
||||
float t = (frame - keys[i].frame) / (float)(keys[i + 1].frame - keys[i].frame);
|
||||
glm_vec3_lerp((float *)keys[i].v, (float *)keys[i + 1].v, t, out);
|
||||
}
|
||||
|
||||
static int eval_int(const struct mpr_int_key *keys, int nr, float frame, int fallback)
|
||||
{
|
||||
if (nr == 0)
|
||||
return fallback;
|
||||
int i = find_int_key(keys, nr, frame);
|
||||
if (i < 0)
|
||||
return keys[0].v;
|
||||
return keys[i].v; // step
|
||||
}
|
||||
|
||||
// Combine inst defaults with .mpr Object-scope keyframes.
|
||||
void mpr_evaluate_object(const struct mpr *mpr, float frame,
|
||||
struct RE_instance *inst, struct mpr_modulation *out)
|
||||
{
|
||||
out->alpha = inst->alpha;
|
||||
glm_vec3_copy(inst->ambient, out->ambient);
|
||||
glm_vec3_copy(inst->diffuse, out->diffuse);
|
||||
if (!mpr)
|
||||
return;
|
||||
out->alpha *= eval_float(mpr->object.mul_alpha, mpr->object.nr_mul_alpha, frame, 1.f);
|
||||
vec3 add;
|
||||
eval_vec3(mpr->object.add_ambient, mpr->object.nr_add_ambient, frame, (vec3){0,0,0}, add);
|
||||
glm_vec3_add(out->ambient, add, out->ambient);
|
||||
eval_vec3(mpr->object.mul_diffuse, mpr->object.nr_mul_diffuse, frame, (vec3){1,1,1}, out->diffuse);
|
||||
}
|
||||
|
||||
// Combine the object-scope values with .mpr Mesh-scope keyframes for one mesh.
|
||||
void mpr_evaluate_mesh(const struct mpr_track_set *mt, float frame,
|
||||
const struct mpr_modulation *obj, struct mpr_modulation *out)
|
||||
{
|
||||
*out = *obj;
|
||||
if (!mt)
|
||||
return;
|
||||
out->alpha *= eval_float(mt->mul_alpha, mt->nr_mul_alpha, frame, 1.f);
|
||||
vec3 add;
|
||||
eval_vec3(mt->add_ambient, mt->nr_add_ambient, frame, (vec3){0,0,0}, add);
|
||||
glm_vec3_add(out->ambient, add, out->ambient);
|
||||
vec3 mul;
|
||||
eval_vec3(mt->mul_diffuse, mt->nr_mul_diffuse, frame, (vec3){1,1,1}, mul);
|
||||
glm_vec3_mul(out->diffuse, mul, out->diffuse);
|
||||
}
|
||||
|
||||
// MeshTextureAnime writes to the shared material slot, so all meshes
|
||||
// referencing the same material pick up the animation even if .mpr only names
|
||||
// one of them. Caller supplies the output buffer sized to model->nr_materials.
|
||||
void mpr_build_mat_tex_index(const struct model *model, const struct RE_instance *inst,
|
||||
const struct mpr *mpr, float frame, int *mat_tex_index)
|
||||
{
|
||||
for (int m = 0; m < model->nr_materials; m++)
|
||||
mat_tex_index[m] = inst->texture_animation_index;
|
||||
if (!mpr)
|
||||
return;
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
const struct mpr_track_set *t = mpr->mesh_tracks[i];
|
||||
if (!t || t->nr_texture_anime == 0)
|
||||
continue;
|
||||
int mat = model->meshes[i].material;
|
||||
mat_tex_index[mat] = eval_int(t->texture_anime, t->nr_texture_anime, frame, mat_tex_index[mat]);
|
||||
}
|
||||
}
|
||||
+196
-22
@@ -70,17 +70,22 @@ static uint32_t parse_material_attributes(const char *name)
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void parse_material(struct buffer *r, struct pol_material *m)
|
||||
static void parse_textures(struct buffer *r, int pol_version, struct pol_material *m)
|
||||
{
|
||||
m->name = read_cstring(r);
|
||||
m->flags = parse_material_attributes(m->name);
|
||||
|
||||
glm_vec2_one(m->uv_tiling);
|
||||
int nr_textures = buffer_read_int32(r);
|
||||
for (int i = 0; i < nr_textures; i++) {
|
||||
char *filename = read_cstring(r);
|
||||
int type = buffer_read_int32(r);
|
||||
vec2 uv_tiling = { 1.0f, 1.0f };
|
||||
if (pol_version >= 3) {
|
||||
uv_tiling[0] = buffer_read_float(r);
|
||||
uv_tiling[1] = buffer_read_float(r);
|
||||
}
|
||||
if ((unsigned)type < MAX_TEXTURE_TYPE) {
|
||||
m->textures[type] = filename;
|
||||
if (type == COLOR_MAP)
|
||||
glm_vec2_copy(uv_tiling, m->uv_tiling);
|
||||
} else {
|
||||
WARNING("invalid texture type %d", type);
|
||||
free(filename);
|
||||
@@ -95,29 +100,60 @@ static void destroy_material(struct pol_material *m)
|
||||
free(m->textures[i]);
|
||||
}
|
||||
|
||||
static void parse_material_group(struct buffer *r, struct pol_material_group *mg)
|
||||
{
|
||||
parse_material(r, &mg->m);
|
||||
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
if (mg->nr_children > 0) {
|
||||
mg->children = xcalloc(mg->nr_children, sizeof(struct pol_material));
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++) {
|
||||
parse_material(r, &mg->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_material_group(struct pol_material_group *mg)
|
||||
{
|
||||
destroy_material(&mg->m);
|
||||
if (mg->children) {
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++)
|
||||
destroy_material(&mg->children[i]);
|
||||
destroy_material_group(&mg->children[i]);
|
||||
free(mg->children);
|
||||
}
|
||||
}
|
||||
|
||||
// Material tree layout:
|
||||
// level 0: top-level material per pol->materials[]. Either a textured leaf
|
||||
// or a group containing sub-materials, never both.
|
||||
// level 1: sub-material. Either a textured leaf or, in v4 only, a "blend
|
||||
// group" (is_group=1) with exactly 2 leaf children.
|
||||
// level 2: leaf children of a v4 blend group; cannot themselves be a group.
|
||||
static void parse_material_group(struct buffer *r, int pol_version,
|
||||
struct pol_material_group *mg, int level)
|
||||
{
|
||||
mg->m.name = read_cstring(r);
|
||||
mg->m.flags = parse_material_attributes(mg->m.name);
|
||||
|
||||
bool is_group = false;
|
||||
if (pol_version >= 4)
|
||||
is_group = buffer_read_int32(r);
|
||||
|
||||
if (is_group) {
|
||||
if (level >= 2)
|
||||
ERROR("material group nesting too deep");
|
||||
uint32_t uk = buffer_read_int32(r);
|
||||
if (uk != 0)
|
||||
ERROR("unexpected nonzero value in material group: %u", uk);
|
||||
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
if (level == 1 && mg->nr_children != 2)
|
||||
ERROR("blend group must have 2 children, got %u", mg->nr_children);
|
||||
} else {
|
||||
parse_textures(r, pol_version, &mg->m);
|
||||
|
||||
if (pol_version >= 4) {
|
||||
uint32_t uk = buffer_read_int32(r);
|
||||
if (uk != 0)
|
||||
ERROR("unexpected nonzero value in material leaf: %u", uk);
|
||||
} else if (level == 0) {
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
}
|
||||
}
|
||||
if (mg->nr_children > 0) {
|
||||
mg->children = xcalloc(mg->nr_children, sizeof(struct pol_material_group));
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++)
|
||||
parse_material_group(r, pol_version, &mg->children[i], level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_vertex(struct buffer *r, int pol_version, struct pol_vertex *v)
|
||||
{
|
||||
read_position(r, v->pos);
|
||||
@@ -151,6 +187,11 @@ static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle
|
||||
t->light_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
t->light_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
}
|
||||
if (mesh->blend_uvs) {
|
||||
t->blend_uv_index[0] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
t->blend_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
t->blend_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
}
|
||||
|
||||
t->color_index[0] = buffer_read_int32(r);
|
||||
t->color_index[1] = buffer_read_int32(r);
|
||||
@@ -160,6 +201,11 @@ static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle
|
||||
t->alpha_index[1] = buffer_read_int32(r);
|
||||
t->alpha_index[2] = buffer_read_int32(r);
|
||||
}
|
||||
if (mesh->blend_weights) {
|
||||
t->blend_weight_index[0] = buffer_read_int32(r);
|
||||
t->blend_weight_index[1] = buffer_read_int32(r);
|
||||
t->blend_weight_index[2] = buffer_read_int32(r);
|
||||
}
|
||||
|
||||
read_direction(r, t->normals[0]);
|
||||
read_direction(r, t->normals[1]);
|
||||
@@ -227,6 +273,17 @@ static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol)
|
||||
}
|
||||
}
|
||||
|
||||
if (pol->version >= 4) {
|
||||
mesh->nr_blend_uvs = buffer_read_int32(r);
|
||||
if (mesh->nr_blend_uvs > 0) {
|
||||
mesh->blend_uvs = xcalloc(mesh->nr_blend_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_blend_uvs; i++) {
|
||||
mesh->blend_uvs[i][0] = buffer_read_float(r);
|
||||
mesh->blend_uvs[i][1] = buffer_read_float(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh->nr_colors = buffer_read_int32(r);
|
||||
if (mesh->nr_colors > 0) {
|
||||
mesh->colors = xcalloc(mesh->nr_colors, sizeof(vec3));
|
||||
@@ -254,6 +311,16 @@ static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol)
|
||||
}
|
||||
}
|
||||
|
||||
if (pol->version >= 4) {
|
||||
mesh->nr_blend_weights = buffer_read_int32(r);
|
||||
if (mesh->nr_blend_weights > 0) {
|
||||
mesh->blend_weights = xcalloc(mesh->nr_blend_weights, sizeof(float));
|
||||
for (uint32_t i = 0; i < mesh->nr_blend_weights; i++) {
|
||||
mesh->blend_weights[i] = buffer_read_u8(r) / 255.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh->nr_triangles = buffer_read_int32(r);
|
||||
mesh->triangles = xcalloc(mesh->nr_triangles, sizeof(struct pol_triangle));
|
||||
for (uint32_t i = 0; i < mesh->nr_triangles; i++) {
|
||||
@@ -280,8 +347,10 @@ static void free_mesh(struct pol_mesh *mesh)
|
||||
free(mesh->vertices);
|
||||
free(mesh->uvs);
|
||||
free(mesh->light_uvs);
|
||||
free(mesh->blend_uvs);
|
||||
free(mesh->colors);
|
||||
free(mesh->alphas);
|
||||
free(mesh->blend_weights);
|
||||
free(mesh->triangles);
|
||||
free(mesh);
|
||||
}
|
||||
@@ -310,7 +379,7 @@ struct pol *pol_parse(uint8_t *data, size_t size)
|
||||
|
||||
struct pol *pol = xcalloc(1, sizeof(struct pol));
|
||||
pol->version = buffer_read_int32(&r);
|
||||
if (pol->version != 1 && pol->version != 2) {
|
||||
if (pol->version != 1 && pol->version != 2 && pol->version != 4) {
|
||||
WARNING("unknown POL version: %d", pol->version);
|
||||
free(pol);
|
||||
return NULL;
|
||||
@@ -318,7 +387,7 @@ struct pol *pol_parse(uint8_t *data, size_t size)
|
||||
pol->nr_materials = buffer_read_int32(&r);
|
||||
pol->materials = xcalloc(pol->nr_materials, sizeof(struct pol_material_group));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
parse_material_group(&r, &pol->materials[i]);
|
||||
parse_material_group(&r, pol->version, &pol->materials[i], 0);
|
||||
}
|
||||
|
||||
pol->nr_meshes = buffer_read_int32(&r);
|
||||
@@ -428,6 +497,7 @@ void mot_free(struct mot *mot)
|
||||
if (mot->texture_indices) {
|
||||
free(mot->texture_indices);
|
||||
}
|
||||
mpr_free(mot->mpr);
|
||||
free(mot);
|
||||
}
|
||||
|
||||
@@ -510,7 +580,7 @@ void opr_load(uint8_t *data, size_t size, struct pol *pol)
|
||||
|
||||
char s[200];
|
||||
int i1, i2, i3;
|
||||
float f1, f2;
|
||||
float f1, f2, f3;
|
||||
if (sscanf(line, "Mesh = \"%[^\"]\"", s) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = pol->meshes[i] && !strcmp(pol->meshes[i]->name, s);
|
||||
@@ -548,7 +618,12 @@ void opr_load(uint8_t *data, size_t size, struct pol *pol)
|
||||
pol->meshes[i]->edge_size = f1;
|
||||
}
|
||||
} else if (sscanf(line, "HeightDetection = %s", s) == 1) {
|
||||
if (!strcmp(s, "false")) {
|
||||
if (!strcmp(s, "true")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HEIGHT_DETECTION;
|
||||
}
|
||||
} else if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_HEIGHT_DETECTION;
|
||||
@@ -556,12 +631,51 @@ void opr_load(uint8_t *data, size_t size, struct pol *pol)
|
||||
} else {
|
||||
WARNING("invalid HeightDetection: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "DrawShadow = %s", s) == 1) {
|
||||
if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_DRAWSHADOW;
|
||||
}
|
||||
} else if (strcmp(s, "true")) {
|
||||
WARNING("invalid DrawShadow: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "ZWrite = %s", s) == 1) {
|
||||
if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_ZWRITE;
|
||||
}
|
||||
} else if (strcmp(s, "true")) {
|
||||
WARNING("invalid ZWrite: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "UVScroll = ( %f , %f )", &f1, &f2) == 2) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->uv_scroll[0] = f1;
|
||||
pol->meshes[i]->uv_scroll[1] = f2;
|
||||
}
|
||||
} else if (sscanf(line, "SpecularColor = ( %f , %f , %f )", &f1, &f2, &f3) == 3) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HAS_SPECULAR_COLOR;
|
||||
pol->meshes[i]->specular_color[0] = f1;
|
||||
pol->meshes[i]->specular_color[1] = f2;
|
||||
pol->meshes[i]->specular_color[2] = f3;
|
||||
}
|
||||
} else if (sscanf(line, "SpecularPower = %f", &f1) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HAS_SPECULAR_POWER;
|
||||
pol->meshes[i]->specular_power = f1;
|
||||
}
|
||||
} else if (sscanf(line, "ParallaxScale = %f", &f1) == 1) {
|
||||
// parallax mapping is not implemented
|
||||
} else if (sscanf(line, "ReliefScale = %f", &f1) == 1) {
|
||||
// relief mapping is not implemented
|
||||
} else if (sscanf(line, "MeshCombinable = %s", s) == 1) {
|
||||
if (strcmp(s, "true") && strcmp(s, "false"))
|
||||
WARNING("invalid MeshCombinable: %s", s);
|
||||
} else if (strchr(line, '=')) {
|
||||
WARNING("unknown field: %s", line);
|
||||
}
|
||||
@@ -595,3 +709,63 @@ void txa_load(uint8_t *data, size_t size, struct mot *mot)
|
||||
mot->nr_texture_indices = count;
|
||||
mot->texture_indices = indices;
|
||||
}
|
||||
|
||||
void lit_reset(float *out)
|
||||
{
|
||||
static const float lit_param_defaults[RE_NR_LIGHT_PARAMS] = {
|
||||
[SEAL_LP_TONEMAP_EXPOSURE_BIAS] = 1.0f,
|
||||
[SEAL_LP_TONEMAP_WHITE_POINT] = 1.0f,
|
||||
[SEAL_LP_TONEMAP_A] = 0.22f,
|
||||
[SEAL_LP_TONEMAP_B] = 0.30f,
|
||||
[SEAL_LP_TONEMAP_C] = 0.10f,
|
||||
[SEAL_LP_TONEMAP_D] = 0.20f,
|
||||
[SEAL_LP_TONEMAP_E] = 0.01f,
|
||||
[SEAL_LP_TONEMAP_F] = 0.30f,
|
||||
[SEAL_LP_HEMI_VEC + 0] = -1.0f,
|
||||
[SEAL_LP_HEMI_VEC + 1] = -1.0f,
|
||||
[SEAL_LP_HEMI_VEC + 2] = 1.0f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 0] = 1.2f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 1] = 1.2f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 2] = 1.2f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 0] = 0.5f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 1] = 0.5f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 2] = 0.5f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 0] = 0.3f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 1] = 0.3f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 2] = 0.3f,
|
||||
[SEAL_LP_LS_BETA_R] = 0.25f,
|
||||
[SEAL_LP_LS_BETA_M] = 1.25f,
|
||||
[SEAL_LP_LS_G] = 0.05f,
|
||||
[SEAL_LP_LS_DISTANCE] = 140.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 0] = -1.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 1] = -1.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 2] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 0] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 1] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 2] = 1.0f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 0] = 0.5f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 1] = 0.5f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 2] = 0.5f,
|
||||
};
|
||||
memcpy(out, lit_param_defaults, sizeof(lit_param_defaults));
|
||||
}
|
||||
|
||||
bool lit_parse(uint8_t *data, size_t size, float *out)
|
||||
{
|
||||
if (size != 8 + RE_NR_LIGHT_PARAMS * 4 || memcmp(data, "LITP", 4) != 0) {
|
||||
WARNING("invalid .lit file");
|
||||
return false;
|
||||
}
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
buffer_skip(&r, 4); // "LITP"
|
||||
int32_t version = buffer_read_int32(&r);
|
||||
if (version != 0) {
|
||||
WARNING("unsupported .lit version %d", version);
|
||||
return false;
|
||||
}
|
||||
lit_reset(out);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
out[i] = buffer_read_float(&r);
|
||||
return true;
|
||||
}
|
||||
|
||||
+315
-56
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/aar.h"
|
||||
#include "system4/archive.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/string.h"
|
||||
@@ -38,19 +39,28 @@ enum RE_plugin_version re_plugin_version;
|
||||
|
||||
static struct RE_instance *create_instance(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_instance *instance = xcalloc(1, sizeof(struct RE_instance));
|
||||
struct RE_instance *instance = xcalloc_aligned(1, struct RE_instance);
|
||||
instance->plugin = plugin;
|
||||
instance->draw_edge = plugin->version == RE_TAPIR_PLUGIN;
|
||||
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
|
||||
instance->target[i] = -1;
|
||||
instance->scale[0] = 1.0f;
|
||||
instance->scale[1] = 1.0f;
|
||||
instance->scale[2] = 1.0f;
|
||||
glm_vec3_one(instance->scale);
|
||||
instance->alpha = 1.0f;
|
||||
glm_vec3_one(instance->diffuse);
|
||||
instance->shadow_volume_bone_radius = 0.1f;
|
||||
instance->draw_bump = true;
|
||||
instance->fps = 30.0f;
|
||||
instance->use_mag_speed = true;
|
||||
instance->column_height = 1.0f;
|
||||
instance->column_radius = 1.0f;
|
||||
glm_vec3_copy((vec3){-1.0f, 2.0f, 0.0f}, instance->vertex_pos[0]);
|
||||
glm_vec3_copy((vec3){-1.0f, 0.0f, 0.0f}, instance->vertex_pos[1]);
|
||||
glm_vec3_copy((vec3){ 1.0f, 2.0f, 0.0f}, instance->vertex_pos[2]);
|
||||
glm_vec3_copy((vec3){ 1.0f, 0.0f, 0.0f}, instance->vertex_pos[3]);
|
||||
glm_vec2_copy((vec2){0.0f, 0.0f}, instance->vertex_uv[0]);
|
||||
glm_vec2_copy((vec2){0.0f, 1.0f}, instance->vertex_uv[1]);
|
||||
glm_vec2_copy((vec2){1.0f, 0.0f}, instance->vertex_uv[2]);
|
||||
glm_vec2_copy((vec2){1.0f, 1.0f}, instance->vertex_uv[3]);
|
||||
glm_mat4_identity(instance->local_transform);
|
||||
glm_mat3_identity(instance->normal_transform);
|
||||
return instance;
|
||||
@@ -60,7 +70,13 @@ static void free_instance(struct RE_instance *instance);
|
||||
|
||||
static void unload_instance(struct RE_instance *instance)
|
||||
{
|
||||
if (instance->motion) {
|
||||
if (instance->name) {
|
||||
free_string(instance->name);
|
||||
instance->name = NULL;
|
||||
}
|
||||
// A billboard's motion holds frame animation state that the game can set
|
||||
// before loading the instance data, so it must survive unloads.
|
||||
if (instance->motion && instance->type != RE_ITYPE_BILLBOARD) {
|
||||
motion_free(instance->motion);
|
||||
instance->motion = NULL;
|
||||
}
|
||||
@@ -76,11 +92,21 @@ static void unload_instance(struct RE_instance *instance)
|
||||
particle_effect_free(instance->effect);
|
||||
instance->effect = NULL;
|
||||
}
|
||||
if (instance->s3de_effect) {
|
||||
s3de_effect_free(instance->s3de_effect);
|
||||
instance->s3de_effect = NULL;
|
||||
}
|
||||
if (instance->bone_transforms) {
|
||||
glDeleteBuffers(1, &instance->bone_transforms_ubo);
|
||||
free(instance->bone_transforms);
|
||||
instance->bone_transforms = NULL;
|
||||
}
|
||||
if (instance->billboard_frames) {
|
||||
// No need to free the textures, they are owned by the renderer.
|
||||
free(instance->billboard_frames);
|
||||
instance->billboard_frames = NULL;
|
||||
instance->nr_billboard_frames = 0;
|
||||
}
|
||||
if (instance->height_detector) {
|
||||
RE_renderer_free_height_detector(instance->height_detector);
|
||||
instance->height_detector = NULL;
|
||||
@@ -94,7 +120,10 @@ static void unload_instance(struct RE_instance *instance)
|
||||
static void free_instance(struct RE_instance *instance)
|
||||
{
|
||||
unload_instance(instance);
|
||||
free(instance);
|
||||
if (instance->motion)
|
||||
motion_free(instance->motion);
|
||||
free(instance->light_params);
|
||||
xfree_aligned(instance);
|
||||
}
|
||||
|
||||
static void RE_back_cg_init(struct RE_back_cg *bcg)
|
||||
@@ -215,9 +244,10 @@ static void update_bones(struct RE_instance *inst)
|
||||
|
||||
struct RE_plugin *RE_plugin_new(enum RE_plugin_version version)
|
||||
{
|
||||
re_debug_init();
|
||||
re_plugin_version = version;
|
||||
|
||||
char *aar_path = gamedir_path("Data/ReignData.red");
|
||||
char *aar_path = gamedir_path(version <= RE_TAPIR_PLUGIN ? "Data/ReignData.red" : "Data/3DData.red");
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
struct archive *aar = (struct archive *)aar_open(aar_path, MMAP_IF_64BIT, &error);
|
||||
if (error == ARCHIVE_FILE_ERROR) {
|
||||
@@ -229,7 +259,8 @@ struct RE_plugin *RE_plugin_new(enum RE_plugin_version version)
|
||||
if (!aar)
|
||||
return NULL;
|
||||
|
||||
struct RE_plugin *plugin = xcalloc(1, sizeof(struct RE_plugin));
|
||||
struct RE_plugin *plugin = xcalloc_aligned(1, struct RE_plugin);
|
||||
plugin->version = version;
|
||||
plugin->plugin.name = "ReignEngine";
|
||||
plugin->plugin.update = RE_render;
|
||||
plugin->plugin.to_json = RE_to_json;
|
||||
@@ -237,18 +268,25 @@ struct RE_plugin *RE_plugin_new(enum RE_plugin_version version)
|
||||
plugin->instances = xcalloc(plugin->nr_instances, sizeof(struct RE_instance *));
|
||||
plugin->aar = aar;
|
||||
plugin->model_cache = ht_create(32);
|
||||
plugin->pae_cache = ht_create(32);
|
||||
plugin->effect_cache = ht_create(32);
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
RE_back_cg_init(&plugin->back_cg[i]);
|
||||
plugin->mag_speed = 1;
|
||||
if (version == RE_TAPIR_PLUGIN)
|
||||
plugin->draw_options[RE_DRAW_OPTION_EDGE] = 1;
|
||||
plugin->draw_options[RE_DRAW_OPTION_LIGHTING] = 1;
|
||||
plugin->edge_length = 0.02f;
|
||||
plugin->fog_type = RE_FOG_NONE;
|
||||
plugin->fog_near = 1.0;
|
||||
plugin->fog_far = 10.0;
|
||||
plugin->fog_near = 1.0f;
|
||||
plugin->fog_far = 10.0f;
|
||||
glm_vec3_one(plugin->fog_color);
|
||||
lit_reset(plugin->light_params);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
void RE_plugin_free(struct RE_plugin *plugin)
|
||||
{
|
||||
RE_plugin_unbind(plugin);
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
if (plugin->instances[i])
|
||||
free_instance(plugin->instances[i]);
|
||||
@@ -257,13 +295,12 @@ void RE_plugin_free(struct RE_plugin *plugin)
|
||||
archive_free(plugin->aar);
|
||||
ht_foreach_value(plugin->model_cache, (void(*)(void*))model_free);
|
||||
ht_free(plugin->model_cache);
|
||||
ht_foreach_value(plugin->pae_cache, (void(*)(void*))pae_free);
|
||||
ht_free(plugin->pae_cache);
|
||||
if (plugin->renderer)
|
||||
RE_renderer_free(plugin->renderer);
|
||||
ht_foreach_value(plugin->effect_cache, re_plugin_version < RE_SEAL_PLUGIN ?
|
||||
(void(*)(void*))pae_free : (void(*)(void*))s3de_free);
|
||||
ht_free(plugin->effect_cache);
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
RE_back_cg_destroy(&plugin->back_cg[i]);
|
||||
free(plugin);
|
||||
xfree_aligned(plugin);
|
||||
}
|
||||
|
||||
bool RE_plugin_bind(struct RE_plugin *plugin, int sprite)
|
||||
@@ -320,6 +357,7 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
|
||||
return false;
|
||||
|
||||
plugin->camera.quake_pitch = plugin->camera.quake_yaw = 0.0;
|
||||
plugin->camera.override_active = false;
|
||||
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
@@ -327,6 +365,8 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
|
||||
continue;
|
||||
|
||||
float delta_frame = inst->fps * elapsed_ms / 1000.0;
|
||||
if (inst->use_mag_speed)
|
||||
delta_frame *= plugin->mag_speed;
|
||||
update_motion(inst->motion, delta_frame);
|
||||
update_motion(inst->next_motion, delta_frame);
|
||||
|
||||
@@ -339,7 +379,11 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
|
||||
// update_bones().
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (inst && inst->type == RE_ITYPE_PARTICLE_EFFECT)
|
||||
if (!inst || inst->type != RE_ITYPE_PARTICLE_EFFECT)
|
||||
continue;
|
||||
if (inst->s3de_effect)
|
||||
s3de_effect_update(inst);
|
||||
else
|
||||
particle_effect_update(inst);
|
||||
}
|
||||
return true;
|
||||
@@ -357,6 +401,10 @@ bool RE_set_viewport(struct RE_plugin *plugin, int x, int y, int width, int heig
|
||||
sprite_init_color(sp, width, height, 0, 0, 0, 255);
|
||||
sprite_set_pos(sp, x, y);
|
||||
RE_renderer_set_viewport_size(plugin->renderer, width, height);
|
||||
plugin->viewport_x = x;
|
||||
plugin->viewport_y = y;
|
||||
plugin->viewport_width = width;
|
||||
plugin->viewport_height = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -380,17 +428,29 @@ int RE_create_instance(struct RE_plugin *plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return -1;
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
if (!plugin->instances[i]) {
|
||||
plugin->instances[i] = create_instance(plugin);
|
||||
return i;
|
||||
}
|
||||
int index;
|
||||
for (index = 0; index < plugin->nr_instances; index++) {
|
||||
if (!plugin->instances[index])
|
||||
break;
|
||||
}
|
||||
int old_size = plugin->nr_instances;
|
||||
plugin->nr_instances *= 2;
|
||||
plugin->instances = xrealloc_array(plugin->instances, old_size, plugin->nr_instances, sizeof(struct RE_instance *));
|
||||
plugin->instances[old_size] = create_instance(plugin);
|
||||
return old_size;
|
||||
RE_create_instance_at(plugin, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
struct RE_instance *RE_create_instance_at(struct RE_plugin *plugin, int index)
|
||||
{
|
||||
if (index >= plugin->nr_instances) {
|
||||
int old_size = plugin->nr_instances;
|
||||
int new_size = old_size;
|
||||
while (new_size <= index)
|
||||
new_size *= 2;
|
||||
plugin->instances = xrealloc_array(plugin->instances, old_size, new_size, sizeof(struct RE_instance *));
|
||||
plugin->nr_instances = new_size;
|
||||
}
|
||||
if (plugin->instances[index])
|
||||
free_instance(plugin->instances[index]);
|
||||
plugin->instances[index] = create_instance(plugin);
|
||||
return plugin->instances[index];
|
||||
}
|
||||
|
||||
bool RE_release_instance(struct RE_plugin *plugin, int instance)
|
||||
@@ -419,6 +479,10 @@ bool RE_instance_set_type(struct RE_instance *instance, int type)
|
||||
instance->motion->instance = instance;
|
||||
break;
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT:
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN) {
|
||||
instance->light_params = xmalloc(RE_NR_LIGHT_PARAMS * sizeof(float));
|
||||
lit_reset(instance->light_params);
|
||||
}
|
||||
break;
|
||||
case RE_ITYPE_SPECULAR_LIGHT:
|
||||
break;
|
||||
@@ -433,15 +497,71 @@ bool RE_instance_set_type(struct RE_instance *instance, int type)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_data_exists(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance)
|
||||
return false;
|
||||
const char *ext;
|
||||
switch (instance->type) {
|
||||
case RE_ITYPE_STATIC:
|
||||
case RE_ITYPE_SKINNED:
|
||||
ext = ".POL";
|
||||
break;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
ext = ".3de";
|
||||
break;
|
||||
default:
|
||||
return archive_exists_by_name(instance->plugin->aar, name, NULL);
|
||||
}
|
||||
const char *basename = strrchr(name, '\\');
|
||||
basename = basename ? basename + 1 : name;
|
||||
char *path = xmalloc(strlen(name) + strlen(basename) + strlen(ext) + 2);
|
||||
sprintf(path, "%s\\%s%s", name, basename, ext);
|
||||
bool exists = archive_exists_by_name(instance->plugin->aar, path, NULL);
|
||||
free(path);
|
||||
return exists;
|
||||
}
|
||||
|
||||
// Loads the frame images of a billboard instance. They are stored in the
|
||||
// archive as "<name>\<basename>.png", "<name>\<basename>[1].png", ...
|
||||
static bool load_billboard_frames(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
const char *basename = strrchr(name, '\\');
|
||||
basename = basename ? basename + 1 : name;
|
||||
char *path = xmalloc(strlen(name) + strlen(basename) + 22);
|
||||
|
||||
int capacity = 8;
|
||||
instance->billboard_frames = xcalloc(capacity, sizeof(struct billboard_texture *));
|
||||
for (int i = 0;; i++) {
|
||||
if (i == 0)
|
||||
sprintf(path, "%s\\%s.png", name, basename);
|
||||
else
|
||||
sprintf(path, "%s\\%s[%d].png", name, basename, i);
|
||||
struct billboard_texture *bt = RE_renderer_load_billboard_texture_by_path(
|
||||
instance->plugin->renderer, instance->plugin->aar, path);
|
||||
if (!bt)
|
||||
break;
|
||||
if (i == capacity) {
|
||||
instance->billboard_frames = xrealloc_array(
|
||||
instance->billboard_frames, capacity, capacity * 2, sizeof(struct billboard_texture *));
|
||||
capacity *= 2;
|
||||
}
|
||||
instance->billboard_frames[i] = bt;
|
||||
instance->nr_billboard_frames = i + 1;
|
||||
}
|
||||
free(path);
|
||||
return instance->nr_billboard_frames > 0;
|
||||
}
|
||||
|
||||
bool RE_instance_load(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance)
|
||||
return false;
|
||||
unload_instance(instance);
|
||||
if (name[0] == '\0')
|
||||
return false;
|
||||
return true; // empty name just unloads the instance
|
||||
instance->name = cstr_to_string(name);
|
||||
|
||||
struct pae *pae;
|
||||
switch (instance->type) {
|
||||
case RE_ITYPE_STATIC:
|
||||
case RE_ITYPE_SKINNED:
|
||||
@@ -460,26 +580,68 @@ bool RE_instance_load(struct RE_instance *instance, const char *name)
|
||||
}
|
||||
return !!instance->model;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
pae = ht_get(instance->plugin->pae_cache, name, NULL);
|
||||
if (!pae) {
|
||||
pae = pae_load(instance->plugin->aar, name);
|
||||
if (!pae)
|
||||
return false;
|
||||
ht_put(instance->plugin->pae_cache, name, pae);
|
||||
}
|
||||
instance->effect = particle_effect_create(pae);
|
||||
if (!instance->motion) {
|
||||
instance->motion = xcalloc(1, sizeof(struct motion));
|
||||
instance->motion->instance = instance;
|
||||
pae_calc_frame_range(instance->effect->pae, instance->motion);
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN) {
|
||||
struct s3de *s3de = ht_get(instance->plugin->effect_cache, name, NULL);
|
||||
if (!s3de) {
|
||||
s3de = s3de_load(instance->plugin->aar, name);
|
||||
if (!s3de)
|
||||
return false;
|
||||
ht_put(instance->plugin->effect_cache, name, s3de);
|
||||
}
|
||||
instance->s3de_effect = s3de_effect_create(s3de, instance->plugin->aar);
|
||||
if (!instance->motion) {
|
||||
instance->motion = xcalloc(1, sizeof(struct motion));
|
||||
instance->motion->instance = instance;
|
||||
s3de_calc_frame_range(instance->s3de_effect->s3de, instance->motion);
|
||||
}
|
||||
} else {
|
||||
struct pae *pae = ht_get(instance->plugin->effect_cache, name, NULL);
|
||||
if (!pae) {
|
||||
pae = pae_load(instance->plugin->aar, name);
|
||||
if (!pae)
|
||||
return false;
|
||||
ht_put(instance->plugin->effect_cache, name, pae);
|
||||
}
|
||||
instance->effect = particle_effect_create(pae);
|
||||
if (!instance->motion) {
|
||||
instance->motion = xcalloc(1, sizeof(struct motion));
|
||||
instance->motion->instance = instance;
|
||||
pae_calc_frame_range(instance->effect->pae, instance->motion);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case RE_ITYPE_BILLBOARD:
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN)
|
||||
return load_billboard_frames(instance, name);
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT:
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN) {
|
||||
struct archive_data *dfile = archive_get_by_name(instance->plugin->aar, name);
|
||||
if (!dfile)
|
||||
return false;
|
||||
bool ok = lit_parse(dfile->data, dfile->size, instance->light_params);
|
||||
archive_free_data(dfile);
|
||||
return ok;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
WARNING("Invalid instance type %d", instance->type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RE_instance_motion_exists(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance || !instance->model)
|
||||
return false;
|
||||
if (instance->model->mot_cache && ht_get(instance->model->mot_cache, name, NULL))
|
||||
return true;
|
||||
char *path = xmalloc(strlen(instance->model->path) + strlen(name) + 6);
|
||||
sprintf(path, "%s\\%s.MOT", instance->model->path, name);
|
||||
bool exists = archive_exists_by_name(instance->plugin->aar, path, NULL);
|
||||
free(path);
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool RE_instance_load_motion(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance)
|
||||
@@ -522,19 +684,28 @@ bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_na
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converts from the (top-left, top-right, bottom-left, bottom-right) order used
|
||||
// by the API to the vertex order of the billboard mesh.
|
||||
static const int billboard_vertex_index[4] = {0, 2, 1, 3};
|
||||
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z)
|
||||
{
|
||||
if (!instance)
|
||||
if (!instance || (unsigned)index >= 4)
|
||||
return false;
|
||||
if (instance->type != RE_ITYPE_BILLBOARD)
|
||||
ERROR("not implemented");
|
||||
// Hack: This works because SetInstanceVertexPos is only used to scale
|
||||
// billboards, and SetInstanceScale is not used for billboards.
|
||||
if (index == 1) {
|
||||
instance->scale[0] = x;
|
||||
instance->scale[1] = y / 2.0;
|
||||
}
|
||||
return false;
|
||||
glm_vec3_copy((vec3){x, y, z}, instance->vertex_pos[billboard_vertex_index[index]]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_set_vertex_uv(struct RE_instance *instance, int index, float u, float v)
|
||||
{
|
||||
if (!instance || (unsigned)index >= 4)
|
||||
return false;
|
||||
if (instance->type != RE_ITYPE_BILLBOARD)
|
||||
ERROR("not implemented");
|
||||
glm_vec2_copy((vec2){u, v}, instance->vertex_uv[billboard_vertex_index[index]]);
|
||||
return true;
|
||||
}
|
||||
|
||||
int RE_instance_get_bone_index(struct RE_instance *instance, const char *name)
|
||||
@@ -629,7 +800,7 @@ bool RE_instance_find_path(struct RE_instance *inst, vec3 start, vec3 goal)
|
||||
if (!inst || !inst->model->collider)
|
||||
return false;
|
||||
mat4 vp_transform;
|
||||
RE_calc_view_matrix(&inst->plugin->camera, GLM_YUP, vp_transform);
|
||||
RE_calc_view_matrix(&inst->plugin->camera, NULL, vp_transform);
|
||||
glm_mat4_mul(inst->plugin->proj_transform, vp_transform, vp_transform);
|
||||
return collider_find_path(inst->model->collider, start, goal, vp_transform);
|
||||
}
|
||||
@@ -651,7 +822,7 @@ bool RE_instance_optimize_path_line(struct RE_instance *inst)
|
||||
|
||||
bool RE_instance_calc_path_finder_intersect_eye_vec(struct RE_instance *inst, int mouse_x, int mouse_y, vec3 out)
|
||||
{
|
||||
if (!inst || !inst->model->collider)
|
||||
if (!inst || !inst->model || !inst->model->collider)
|
||||
return false;
|
||||
|
||||
float ndc_x = 2.f * mouse_x / inst->plugin->renderer->viewport_width - 1.f;
|
||||
@@ -660,7 +831,7 @@ bool RE_instance_calc_path_finder_intersect_eye_vec(struct RE_instance *inst, in
|
||||
vec4 far = { ndc_x, ndc_y, 1.f, 1.f };
|
||||
|
||||
mat4 inv_vp;
|
||||
RE_calc_view_matrix(&inst->plugin->camera, GLM_YUP, inv_vp);
|
||||
RE_calc_view_matrix(&inst->plugin->camera, NULL, inv_vp);
|
||||
glm_mat4_mul(inst->plugin->proj_transform, inv_vp, inv_vp);
|
||||
glm_mat4_inv(inv_vp, inv_vp);
|
||||
|
||||
@@ -677,6 +848,94 @@ bool RE_instance_calc_path_finder_intersect_eye_vec(struct RE_instance *inst, in
|
||||
return collider_raycast(inst->model->collider, origin, direction, out);
|
||||
}
|
||||
|
||||
bool RE_plugin_transform_pos_to_view_pos(struct RE_plugin *plugin, float x, float y, float z, int *view_x, int *view_y)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
|
||||
mat4 vp;
|
||||
RE_calc_view_matrix(&plugin->camera, NULL, vp);
|
||||
glm_mat4_mul(plugin->proj_transform, vp, vp);
|
||||
|
||||
vec4 pos = { x, y, z, 1.f };
|
||||
glm_mat4_mulv(vp, pos, pos);
|
||||
|
||||
if (pos[3] == 0.f)
|
||||
return false;
|
||||
|
||||
float ndc_x = pos[0] / pos[3];
|
||||
float ndc_y = pos[1] / pos[3];
|
||||
|
||||
*view_x = (int)((ndc_x + 1.f) * 0.5f * plugin->renderer->viewport_width);
|
||||
*view_y = (int)((1.f - ndc_y) * 0.5f * plugin->renderer->viewport_height);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_plugin_get_camera_z_vector(struct RE_plugin *plugin, vec3 out)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
|
||||
vec3 euler;
|
||||
if (plugin->camera.override_active) {
|
||||
euler[0] = glm_rad(plugin->camera.override_pitch);
|
||||
euler[1] = glm_rad(plugin->camera.override_yaw);
|
||||
euler[2] = glm_rad(plugin->camera.override_roll);
|
||||
} else {
|
||||
euler[0] = glm_rad(plugin->camera.pitch + plugin->camera.quake_pitch);
|
||||
euler[1] = glm_rad(plugin->camera.yaw + plugin->camera.quake_yaw);
|
||||
euler[2] = glm_rad(plugin->camera.roll);
|
||||
}
|
||||
mat4 rot;
|
||||
glm_euler_yxz(euler, rot);
|
||||
glm_mat4_mulv3(rot, GLM_FORWARD, 0.f, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RE_plugin_reset_light_param(struct RE_plugin *plugin)
|
||||
{
|
||||
lit_reset(plugin->light_params);
|
||||
}
|
||||
|
||||
bool RE_plugin_set_fog_type(struct RE_plugin *plugin, int type)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN) {
|
||||
switch (type) {
|
||||
case 0: plugin->fog_type = RE_FOG_NONE; return true;
|
||||
case 1: plugin->fog_type = RE_FOG_LINEAR; return true;
|
||||
case 2: plugin->fog_type = RE_FOG_LIGHT_SCATTERING; return true;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case 0: plugin->fog_type = RE_FOG_NONE; return true;
|
||||
case 1: plugin->fog_type = RE_FOG_LIGHT_SCATTERING; return true;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
WARNING("unknown fog type %d", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
int RE_plugin_get_fog_type(struct RE_plugin *plugin)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN) {
|
||||
switch (plugin->fog_type) {
|
||||
case RE_FOG_NONE: return 0;
|
||||
case RE_FOG_LINEAR: return 1;
|
||||
case RE_FOG_LIGHT_SCATTERING: return 2;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
switch (plugin->fog_type) {
|
||||
case RE_FOG_NONE: return 0;
|
||||
case RE_FOG_LIGHT_SCATTERING: return 1;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
ERROR("[BUG] unexpected fog type %d", plugin->fog_type);
|
||||
}
|
||||
|
||||
void RE_instance_update_local_transform(struct RE_instance *inst)
|
||||
{
|
||||
vec3 euler = {
|
||||
@@ -731,9 +990,9 @@ bool RE_motion_set_frame_range(struct motion *motion, float begin, float end)
|
||||
return false;
|
||||
motion->frame_begin = begin;
|
||||
motion->frame_end = end;
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD && re_plugin_version < RE_SEAL_PLUGIN) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i))
|
||||
if (!RE_renderer_load_billboard_texture_by_no(motion->instance->plugin->renderer, i))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -746,9 +1005,9 @@ bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float en
|
||||
return false;
|
||||
motion->loop_frame_begin = begin;
|
||||
motion->loop_frame_end = end;
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD && re_plugin_version < RE_SEAL_PLUGIN) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i))
|
||||
if (!RE_renderer_load_billboard_texture_by_no(motion->instance->plugin->renderer, i))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+525
-101
@@ -39,6 +39,7 @@ enum {
|
||||
LIGHT_TEXTURE_UNIT,
|
||||
NORMAL_TEXTURE_UNIT,
|
||||
SHADOW_TEXTURE_UNIT,
|
||||
BLEND_TEXTURE_UNIT,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -66,9 +67,11 @@ static GLuint load_shader(const char *vertex_shader_path, const char *fragment_s
|
||||
snprintf(defines, sizeof(defines),
|
||||
"#define REIGN_ENGINE %d\n"
|
||||
"#define TAPIR_ENGINE %d\n"
|
||||
"#define SEAL_ENGINE %d\n"
|
||||
"#define ENGINE %d\n",
|
||||
RE_REIGN_PLUGIN,
|
||||
RE_TAPIR_PLUGIN,
|
||||
RE_SEAL_PLUGIN,
|
||||
re_plugin_version);
|
||||
GLuint program = glCreateProgram();
|
||||
GLuint vertex_shader = gfx_load_shader_file(vertex_shader_path, GL_VERTEX_SHADER, defines);
|
||||
@@ -87,6 +90,8 @@ static GLuint load_shader(const char *vertex_shader_path, const char *fragment_s
|
||||
glBindAttribLocation(program, VATTR_LIGHT_UV, "vertex_light_uv");
|
||||
glBindAttribLocation(program, VATTR_COLOR, "vertex_color");
|
||||
glBindAttribLocation(program, VATTR_TANGENT, "vertex_tangent");
|
||||
glBindAttribLocation(program, VATTR_BLEND_WEIGHT, "vertex_blend_weight");
|
||||
glBindAttribLocation(program, VATTR_BLEND_UV, "vertex_blend_uv");
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
@@ -161,24 +166,8 @@ static void destroy_outline_renderer(struct outline_renderer *or)
|
||||
glDeleteProgram(or->program);
|
||||
}
|
||||
|
||||
static void init_billboard_mesh(struct RE_renderer *r)
|
||||
static void init_billboard_unused_vertex_attrs(void)
|
||||
{
|
||||
static const GLfloat vertices[] = {
|
||||
// x, y, z, u, v
|
||||
-1.0, 1.0, 0.0, 0.0, 0.0,
|
||||
-1.0, -1.0, 0.0, 0.0, 1.0,
|
||||
1.0, 1.0, 0.0, 1.0, 0.0,
|
||||
1.0, -1.0, 0.0, 1.0, 1.0,
|
||||
};
|
||||
glGenVertexArrays(1, &r->billboard_vao);
|
||||
glBindVertexArray(r->billboard_vao);
|
||||
glGenBuffers(1, &r->billboard_attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, r->billboard_attr_buffer);
|
||||
|
||||
glEnableVertexAttribArray(VATTR_POS);
|
||||
glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0);
|
||||
glEnableVertexAttribArray(VATTR_UV);
|
||||
glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12);
|
||||
glDisableVertexAttribArray(VATTR_LIGHT_UV);
|
||||
glVertexAttrib2f(VATTR_LIGHT_UV, 0.0, 0.0);
|
||||
glDisableVertexAttribArray(VATTR_COLOR);
|
||||
@@ -191,8 +180,39 @@ static void init_billboard_mesh(struct RE_renderer *r)
|
||||
glVertexAttribI4i(VATTR_BONE_INDEX, 0, 0, 0, 0);
|
||||
glDisableVertexAttribArray(VATTR_BONE_WEIGHT);
|
||||
glVertexAttrib4f(VATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
static void init_billboard_mesh(struct RE_renderer *r)
|
||||
{
|
||||
static const GLfloat vertices[] = {
|
||||
// x, y, z, u, v
|
||||
-1.0, 1.0, 0.0, 0.0, 0.0,
|
||||
-1.0, -1.0, 0.0, 0.0, 1.0,
|
||||
1.0, 1.0, 0.0, 1.0, 0.0,
|
||||
1.0, -1.0, 0.0, 1.0, 1.0,
|
||||
};
|
||||
glGenBuffers(1, &r->billboard_particle_attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, r->billboard_particle_attr_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glGenBuffers(1, &r->billboard_instance_attr_buffer);
|
||||
|
||||
glGenVertexArrays(1, &r->billboard_particle_vao);
|
||||
glBindVertexArray(r->billboard_particle_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, r->billboard_particle_attr_buffer);
|
||||
glEnableVertexAttribArray(VATTR_POS);
|
||||
glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0);
|
||||
glEnableVertexAttribArray(VATTR_UV);
|
||||
glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12);
|
||||
init_billboard_unused_vertex_attrs();
|
||||
|
||||
glGenVertexArrays(1, &r->billboard_instance_vao);
|
||||
glBindVertexArray(r->billboard_instance_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, r->billboard_instance_attr_buffer);
|
||||
glEnableVertexAttribArray(VATTR_POS);
|
||||
glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0);
|
||||
glEnableVertexAttribArray(VATTR_UV);
|
||||
glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12);
|
||||
init_billboard_unused_vertex_attrs();
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
@@ -200,8 +220,10 @@ static void init_billboard_mesh(struct RE_renderer *r)
|
||||
|
||||
static void destroy_billboard_mesh(struct RE_renderer *r)
|
||||
{
|
||||
glDeleteVertexArrays(1, &r->billboard_vao);
|
||||
glDeleteBuffers(1, &r->billboard_attr_buffer);
|
||||
glDeleteVertexArrays(1, &r->billboard_particle_vao);
|
||||
glDeleteVertexArrays(1, &r->billboard_instance_vao);
|
||||
glDeleteBuffers(1, &r->billboard_particle_attr_buffer);
|
||||
glDeleteBuffers(1, &r->billboard_instance_attr_buffer);
|
||||
}
|
||||
|
||||
struct RE_renderer *RE_renderer_new(void)
|
||||
@@ -220,6 +242,7 @@ struct RE_renderer *RE_renderer_new(void)
|
||||
glUniformBlockBinding(r->program, bone_transforms, BONE_TRANSFORMS_BINDING);
|
||||
r->global_ambient = glGetUniformLocation(r->program, "global_ambient");
|
||||
r->instance_ambient = glGetUniformLocation(r->program, "instance_ambient");
|
||||
r->diffuse_mod = glGetUniformLocation(r->program, "diffuse_mod");
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
char buf[64];
|
||||
sprintf(buf, "dir_lights[%d].dir", i);
|
||||
@@ -230,7 +253,7 @@ struct RE_renderer *RE_renderer_new(void)
|
||||
r->dir_lights[i].globe_diffuse = glGetUniformLocation(r->program, buf);
|
||||
}
|
||||
r->specular_light_dir = glGetUniformLocation(r->program, "specular_light_dir");
|
||||
r->specular_strength = glGetUniformLocation(r->program, "specular_strength");
|
||||
r->specular_color = glGetUniformLocation(r->program, "specular_color");
|
||||
r->specular_shininess = glGetUniformLocation(r->program, "specular_shininess");
|
||||
r->use_specular_map = glGetUniformLocation(r->program, "use_specular_map");
|
||||
r->specular_texture = glGetUniformLocation(r->program, "specular_texture");
|
||||
@@ -253,16 +276,28 @@ struct RE_renderer *RE_renderer_new(void)
|
||||
r->ls_light_dir = glGetUniformLocation(r->program, "ls_light_dir");
|
||||
r->ls_light_color = glGetUniformLocation(r->program, "ls_light_color");
|
||||
r->ls_sun_color = glGetUniformLocation(r->program, "ls_sun_color");
|
||||
r->hemi_light_dir = glGetUniformLocation(r->program, "hemi_light_dir");
|
||||
r->hemi_sky_color = glGetUniformLocation(r->program, "hemi_sky_color");
|
||||
r->hemi_mid_color = glGetUniformLocation(r->program, "hemi_mid_color");
|
||||
r->hemi_ground_color = glGetUniformLocation(r->program, "hemi_ground_color");
|
||||
r->tonemap_param = glGetUniformLocation(r->program, "tonemap_param");
|
||||
r->tonemap_param2 = glGetUniformLocation(r->program, "tonemap_param2");
|
||||
r->nolighting = glGetUniformLocation(r->program, "nolighting");
|
||||
r->grayscale_rate = glGetUniformLocation(r->program, "grayscale_rate");
|
||||
r->alpha_mode = glGetUniformLocation(r->program, "alpha_mode");
|
||||
r->alpha_texture = glGetUniformLocation(r->program, "alpha_texture");
|
||||
r->uv_scroll = glGetUniformLocation(r->program, "uv_scroll");
|
||||
r->uv_tiling = glGetUniformLocation(r->program, "uv_tiling");
|
||||
r->blend_tex = glGetUniformLocation(r->program, "blend_tex");
|
||||
r->use_blend_texture = glGetUniformLocation(r->program, "use_blend_texture");
|
||||
|
||||
glGenRenderbuffers(1, &r->depth_buffer);
|
||||
|
||||
init_shadow_renderer(&r->shadow);
|
||||
init_outline_renderer(&r->outline);
|
||||
init_billboard_mesh(r);
|
||||
r->billboard_textures = ht_create(256);
|
||||
r->billboard_textures_by_no = ht_create(256);
|
||||
r->billboard_textures_by_path = ht_create(256);
|
||||
r->last_frame_timestamp = SDL_GetTicks();
|
||||
return r;
|
||||
}
|
||||
@@ -276,15 +311,8 @@ void RE_renderer_set_viewport_size(struct RE_renderer *r, int width, int height)
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no)
|
||||
static struct billboard_texture *create_billboard_texture(struct cg *cg)
|
||||
{
|
||||
if (ht_get_int(r->billboard_textures, cg_no, NULL))
|
||||
return true;
|
||||
|
||||
struct cg *cg = asset_cg_load(cg_no);
|
||||
if (!cg)
|
||||
return false;
|
||||
|
||||
struct billboard_texture *bt = xcalloc(1, sizeof(struct billboard_texture));
|
||||
glGenTextures(1, &bt->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
@@ -296,12 +324,45 @@ bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no)
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
bt->has_alpha = cg->metrics.has_alpha;
|
||||
return bt;
|
||||
}
|
||||
|
||||
bool RE_renderer_load_billboard_texture_by_no(struct RE_renderer *r, int cg_no)
|
||||
{
|
||||
if (ht_get_int(r->billboard_textures_by_no, cg_no, NULL))
|
||||
return true;
|
||||
|
||||
struct cg *cg = asset_cg_load(cg_no);
|
||||
if (!cg)
|
||||
return false;
|
||||
|
||||
ht_put_int(r->billboard_textures_by_no, cg_no, create_billboard_texture(cg));
|
||||
cg_free(cg);
|
||||
ht_put_int(r->billboard_textures, cg_no, bt);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct billboard_texture *RE_renderer_load_billboard_texture_by_path(struct RE_renderer *r, struct archive *aar, const char *path)
|
||||
{
|
||||
struct billboard_texture *bt = ht_get(r->billboard_textures_by_path, path, NULL);
|
||||
if (bt)
|
||||
return bt;
|
||||
|
||||
struct archive_data *dfile = archive_get_by_name(aar, path);
|
||||
if (!dfile)
|
||||
return NULL;
|
||||
struct cg *cg = cg_load_data(dfile);
|
||||
archive_free_data(dfile);
|
||||
if (!cg) {
|
||||
WARNING("cg_load_data failed: %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bt = create_billboard_texture(cg);
|
||||
cg_free(cg);
|
||||
ht_put(r->billboard_textures_by_path, path, bt);
|
||||
return bt;
|
||||
}
|
||||
|
||||
static void free_billboard_texture(void *value)
|
||||
{
|
||||
struct billboard_texture *bt = value;
|
||||
@@ -313,8 +374,10 @@ void RE_renderer_free(struct RE_renderer *r)
|
||||
{
|
||||
glDeleteProgram(r->program);
|
||||
glDeleteRenderbuffers(1, &r->depth_buffer);
|
||||
ht_foreach_value(r->billboard_textures, free_billboard_texture);
|
||||
ht_free_int(r->billboard_textures);
|
||||
ht_foreach_value(r->billboard_textures_by_no, free_billboard_texture);
|
||||
ht_free_int(r->billboard_textures_by_no);
|
||||
ht_foreach_value(r->billboard_textures_by_path, free_billboard_texture);
|
||||
ht_free(r->billboard_textures_by_path);
|
||||
destroy_billboard_mesh(r);
|
||||
destroy_shadow_renderer(&r->shadow);
|
||||
destroy_outline_renderer(&r->outline);
|
||||
@@ -324,40 +387,74 @@ void RE_renderer_free(struct RE_renderer *r)
|
||||
void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out)
|
||||
{
|
||||
vec3 front = { 0.0, 0.0, -1.0 };
|
||||
vec3 euler = {
|
||||
glm_rad(camera->pitch + camera->quake_pitch),
|
||||
glm_rad(camera->yaw + camera->quake_yaw),
|
||||
glm_rad(camera->roll)
|
||||
};
|
||||
vec3 euler;
|
||||
float *pos;
|
||||
if (camera->override_active) {
|
||||
euler[0] = glm_rad(camera->override_pitch);
|
||||
euler[1] = glm_rad(camera->override_yaw);
|
||||
euler[2] = glm_rad(camera->override_roll);
|
||||
pos = camera->override_pos;
|
||||
} else {
|
||||
euler[0] = glm_rad(camera->pitch + camera->quake_pitch);
|
||||
euler[1] = glm_rad(camera->yaw + camera->quake_yaw);
|
||||
euler[2] = glm_rad(camera->roll);
|
||||
pos = camera->pos;
|
||||
}
|
||||
mat4 rot;
|
||||
glm_euler(euler, rot);
|
||||
glm_euler_yxz(euler, rot);
|
||||
glm_mat4_mulv3(rot, front, 0.0, front);
|
||||
glm_look(camera->pos, front, up, out);
|
||||
vec3 up_v;
|
||||
if (up) {
|
||||
glm_vec3_copy(up, up_v); // Use the provided up vector.
|
||||
} else {
|
||||
// Derive the camera's up by rotating world-Y with the same orientation.
|
||||
glm_mat4_mulv3(rot, GLM_YUP, 0.0, up_v);
|
||||
}
|
||||
glm_look(pos, front, up_v, out);
|
||||
}
|
||||
|
||||
static bool should_draw_shadow(struct mesh *mesh, struct material *material)
|
||||
{
|
||||
return !(mesh->flags & (MESH_ALPHA | MESH_BOTH | MESH_SPRITE))
|
||||
return !(mesh->flags & (MESH_ALPHA | MESH_BOTH | MESH_SPRITE | MESH_NO_DRAWSHADOW))
|
||||
&& !(material->flags & (MATERIAL_ALPHA | MATERIAL_SPRITE));
|
||||
}
|
||||
|
||||
static bool lighting_disabled(struct RE_plugin *plugin)
|
||||
{
|
||||
return plugin->draw_options[RE_DRAW_OPTION_LIGHTING] <= 0;
|
||||
}
|
||||
|
||||
static bool is_nolighting(struct RE_plugin *plugin, struct mesh *mesh)
|
||||
{
|
||||
return lighting_disabled(plugin) || (mesh->flags & MESH_NOLIGHTING);
|
||||
}
|
||||
|
||||
static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase)
|
||||
{
|
||||
struct model *model = inst->model;
|
||||
if (!inst->model)
|
||||
return;
|
||||
bool all_transparent = inst->alpha < 1.0f;
|
||||
bool all_opaque = !model->has_transparent_mesh && inst->alpha >= 1.0f;
|
||||
|
||||
// NOTE: next_motion's .mpr is not sampled.
|
||||
const struct mpr *mpr = (inst->motion && inst->motion->mot) ? inst->motion->mot->mpr : NULL;
|
||||
float frame = inst->motion ? inst->motion->current_frame : 0.f;
|
||||
struct mpr_modulation obj_mod;
|
||||
mpr_evaluate_object(mpr, frame, inst, &obj_mod);
|
||||
|
||||
bool all_transparent = obj_mod.alpha < 1.0f;
|
||||
bool all_opaque = !model->has_transparent_mesh && obj_mod.alpha >= 1.0f
|
||||
&& !(mpr && mpr->has_mesh_alpha);
|
||||
if ((phase == DRAW_OPAQUE && all_transparent) || (phase == DRAW_TRANSPARENT && all_opaque))
|
||||
return;
|
||||
|
||||
int mat_tex_index[model->nr_materials];
|
||||
mpr_build_mat_tex_index(model, inst, mpr, frame, mat_tex_index);
|
||||
|
||||
if (inst->local_transform_needs_update)
|
||||
RE_instance_update_local_transform(inst);
|
||||
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, inst->normal_transform[0]);
|
||||
glUniform1f(r->alpha_mod, inst->alpha);
|
||||
glUniform3fv(r->instance_ambient, 1, inst->ambient);
|
||||
|
||||
bool draw_shadow = inst->draw_shadow && inst->plugin->shadow_mode;
|
||||
if (draw_shadow) {
|
||||
@@ -371,23 +468,44 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
if (mesh->hidden)
|
||||
continue;
|
||||
struct material *material = &model->materials[mesh->material];
|
||||
bool is_transparent = mesh->is_transparent || inst->alpha < 1.0f;
|
||||
|
||||
const struct mpr_track_set *mt = mpr ? mpr->mesh_tracks[i] : NULL;
|
||||
struct mpr_modulation mesh_mod;
|
||||
mpr_evaluate_mesh(mt, frame, &obj_mod, &mesh_mod);
|
||||
if (mesh_mod.alpha <= 0.0f)
|
||||
continue;
|
||||
|
||||
bool is_transparent = mesh->is_transparent || mesh_mod.alpha < 1.0f;
|
||||
if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE))
|
||||
continue;
|
||||
|
||||
glUniform1f(r->alpha_mod, mesh_mod.alpha);
|
||||
glUniform3fv(r->instance_ambient, 1, mesh_mod.ambient);
|
||||
glUniform3fv(r->diffuse_mod, 1, mesh_mod.diffuse);
|
||||
|
||||
if (mesh->flags & MESH_BLEND_ADDITIVE) {
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
} else {
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
}
|
||||
|
||||
glUniform1i(r->fog_type, (inst->plugin->fog_mode && !(mesh->flags & MESH_NOLIGHTING))
|
||||
? inst->plugin->fog_type : 0);
|
||||
bool nolighting = is_nolighting(inst->plugin, mesh);
|
||||
int fog_type = inst->plugin->fog_type;
|
||||
if (!inst->plugin->fog_mode || (re_plugin_version == RE_REIGN_PLUGIN && nolighting)) {
|
||||
fog_type = RE_FOG_NONE;
|
||||
}
|
||||
glUniform1i(r->fog_type, fog_type);
|
||||
glUniform1i(r->nolighting, nolighting);
|
||||
|
||||
GLboolean use_specular_map = GL_FALSE;
|
||||
if (inst->plugin->specular_mode && !(mesh->flags & MESH_NOLIGHTING)) {
|
||||
glUniform1f(r->specular_strength, material->specular_strength);
|
||||
glUniform1f(r->specular_shininess, material->specular_shininess);
|
||||
float shininess = (mesh->flags & MESH_HAS_SPECULAR_POWER) ? mesh->specular_power : material->specular_shininess;
|
||||
if (inst->plugin->specular_mode && !nolighting && shininess > 0.0f) {
|
||||
if (mesh->flags & MESH_HAS_SPECULAR_COLOR) {
|
||||
glUniform3fv(r->specular_color, 1, mesh->specular_color);
|
||||
} else {
|
||||
glUniform3f(r->specular_color, material->specular_strength, material->specular_strength, material->specular_strength);
|
||||
}
|
||||
glUniform1f(r->specular_shininess, shininess);
|
||||
if (material->specular_map) {
|
||||
glActiveTexture(GL_TEXTURE0 + SPECULAR_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->specular_map);
|
||||
@@ -395,8 +513,8 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
use_specular_map = GL_TRUE;
|
||||
}
|
||||
} else {
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
glUniform1f(r->specular_shininess, 0.0);
|
||||
glUniform3f(r->specular_color, 0.0f, 0.0f, 0.0f);
|
||||
glUniform1f(r->specular_shininess, 0.0f);
|
||||
}
|
||||
glUniform1i(r->use_specular_map, use_specular_map);
|
||||
|
||||
@@ -404,8 +522,8 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
glUniform3fv(r->rim_color, 1, material->rim_color);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + COLOR_TEXTURE_UNIT);
|
||||
int animation_index = inst->texture_animation_index < material->nr_color_maps
|
||||
? inst->texture_animation_index : 0;
|
||||
int tex_index = mat_tex_index[mesh->material];
|
||||
int animation_index = tex_index < material->nr_color_maps ? tex_index : 0;
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_maps[animation_index]);
|
||||
glUniform1i(r->texture, COLOR_TEXTURE_UNIT);
|
||||
|
||||
@@ -416,7 +534,7 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
|
||||
if (mesh->flags & MESH_ENVMAP) {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_ENV_MAP);
|
||||
} else if (mesh->flags & MESH_NOLIGHTING) {
|
||||
} else if (nolighting) {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_EMISSIVE);
|
||||
} else if (material->light_map && mesh->flags & MESH_HAS_LIGHT_UV && inst->plugin->light_map_mode) {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_LIGHT_MAP);
|
||||
@@ -437,28 +555,44 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
}
|
||||
|
||||
if (material->alpha_map) {
|
||||
glUniform1i(r->alpha_mode, mesh->is_transparent ? ALPHA_MAP_BLEND : ALPHA_MAP_TEST);
|
||||
glUniform1i(r->alpha_mode, is_transparent ? ALPHA_MAP_BLEND : ALPHA_MAP_TEST);
|
||||
glActiveTexture(GL_TEXTURE0 + ALPHA_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->alpha_map);
|
||||
glUniform1i(r->alpha_texture, ALPHA_TEXTURE_UNIT);
|
||||
} else {
|
||||
glUniform1i(r->alpha_mode, mesh->is_transparent ? ALPHA_BLEND : ALPHA_TEST);
|
||||
glUniform1i(r->alpha_mode, is_transparent ? ALPHA_BLEND : ALPHA_TEST);
|
||||
}
|
||||
|
||||
if (material->blend_texture) {
|
||||
glUniform1i(r->use_blend_texture, GL_TRUE);
|
||||
glActiveTexture(GL_TEXTURE0 + BLEND_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->blend_texture);
|
||||
glUniform1i(r->blend_tex, BLEND_TEXTURE_UNIT);
|
||||
} else {
|
||||
glUniform1i(r->use_blend_texture, GL_FALSE);
|
||||
}
|
||||
|
||||
vec2 uv_scroll;
|
||||
glm_vec2_scale(mesh->uv_scroll, r->last_frame_timestamp / 1000.f, uv_scroll);
|
||||
glUniform2fv(r->uv_scroll, 1, uv_scroll);
|
||||
glUniform4f(r->uv_tiling,
|
||||
material->uv_tiling[0], material->uv_tiling[1],
|
||||
material->blend_uv_tiling[0], material->blend_uv_tiling[1]);
|
||||
|
||||
glBindVertexArray(mesh->vao);
|
||||
|
||||
if (mesh->flags & MESH_BOTH)
|
||||
glDisable(GL_CULL_FACE);
|
||||
if (mesh->flags & MESH_NO_ZWRITE)
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
if (mesh->nr_indices)
|
||||
glDrawElements(GL_TRIANGLES, mesh->nr_indices, GL_UNSIGNED_SHORT, NULL);
|
||||
else
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
|
||||
if (mesh->flags & MESH_NO_ZWRITE)
|
||||
glDepthMask(GL_TRUE);
|
||||
if (mesh->flags & MESH_BOTH)
|
||||
glEnable(GL_CULL_FACE);
|
||||
glBindVertexArray(0);
|
||||
@@ -494,12 +628,41 @@ static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r
|
||||
render_static_model(inst->shadow_volume_instance, r, phase);
|
||||
}
|
||||
|
||||
static void reset_draw_uniforms(struct RE_renderer *r, struct RE_plugin *plugin)
|
||||
{
|
||||
bool nolighting = lighting_disabled(plugin);
|
||||
glUniform1f(r->alpha_mod, 1.0f);
|
||||
glUniform3f(r->diffuse_mod, 1.0f, 1.0f, 1.0f);
|
||||
glUniform2f(r->uv_scroll, 0.0f, 0.0f);
|
||||
glUniform4f(r->uv_tiling, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
glUniform3f(r->specular_color, 0.0f, 0.0f, 0.0f);
|
||||
glUniform1f(r->specular_shininess, 0.0f);
|
||||
glUniform1i(r->use_specular_map, GL_FALSE);
|
||||
glUniform1f(r->rim_exponent, 0.0f);
|
||||
glUniform3f(r->rim_color, 0.0f, 0.0f, 0.0f);
|
||||
glUniform1i(r->use_normal_map, GL_FALSE);
|
||||
glUniform1i(r->use_blend_texture, GL_FALSE);
|
||||
glUniform1f(r->shadow_darkness, 0.0f);
|
||||
glUniform1i(r->alpha_mode, ALPHA_BLEND);
|
||||
glUniform1i(r->fog_type, 0);
|
||||
glUniform1i(r->nolighting, nolighting);
|
||||
glUniform1i(r->diffuse_type, nolighting ? DIFFUSE_EMISSIVE : DIFFUSE_NORMAL);
|
||||
}
|
||||
|
||||
static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, enum draw_phase phase)
|
||||
{
|
||||
if (!inst->draw)
|
||||
return;
|
||||
int cg_no = inst->motion->current_frame;
|
||||
struct billboard_texture *bt = ht_get_int(r->billboard_textures, cg_no, NULL);
|
||||
int frame = inst->motion->current_frame;
|
||||
struct billboard_texture *bt;
|
||||
if (inst->nr_billboard_frames > 0) {
|
||||
if (frame < 0 || frame >= inst->nr_billboard_frames)
|
||||
return;
|
||||
bt = inst->billboard_frames[frame];
|
||||
} else {
|
||||
bt = ht_get_int(r->billboard_textures_by_no, frame, NULL);
|
||||
}
|
||||
if (!bt)
|
||||
return;
|
||||
bool is_transparent = bt->has_alpha || inst->alpha < 1.0f;
|
||||
@@ -512,26 +675,16 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma
|
||||
glm_mat4_pick3t(view_mat, rot);
|
||||
glm_mat4_ins3(rot, local_transform);
|
||||
glm_scale(local_transform, inst->scale);
|
||||
// Billboard instances are bottomed at y=0.
|
||||
glm_translate_y(local_transform, 1.0);
|
||||
mat3 normal_transform;
|
||||
// This should be safe because billboards do not have non-uniform scaling.
|
||||
glm_mat4_pick3(local_transform, normal_transform);
|
||||
|
||||
glUniform3fv(r->instance_ambient, 1, inst->ambient);
|
||||
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, normal_transform[0]);
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
|
||||
reset_draw_uniforms(r, inst->plugin);
|
||||
glUniform1f(r->alpha_mod, inst->alpha);
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
glUniform1f(r->specular_shininess, 0.0);
|
||||
glUniform1i(r->use_specular_map, GL_FALSE);
|
||||
glUniform1f(r->rim_exponent, 0.0);
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_NORMAL);
|
||||
glUniform1i(r->use_normal_map, GL_FALSE);
|
||||
glUniform1f(r->shadow_darkness, 0.0f);
|
||||
glUniform1i(r->alpha_mode, ALPHA_BLEND);
|
||||
glUniform1i(r->fog_type, inst->plugin->fog_mode ? inst->plugin->fog_type : 0);
|
||||
switch (inst->draw_type) {
|
||||
case RE_DRAW_TYPE_NORMAL:
|
||||
@@ -546,7 +699,15 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
glUniform1i(r->texture, 0);
|
||||
glBindVertexArray(r->billboard_vao);
|
||||
GLfloat vertices[4][5];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
glm_vec3_copy(inst->vertex_pos[i], vertices[i]);
|
||||
glm_vec2_copy(inst->vertex_uv[i], vertices[i] + 3);
|
||||
}
|
||||
glBindVertexArray(r->billboard_instance_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, r->billboard_instance_attr_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
@@ -566,7 +727,7 @@ static void render_billboard_particles(struct RE_renderer *r, struct RE_instance
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUniform1i(r->texture, 0);
|
||||
glBindVertexArray(r->billboard_vao);
|
||||
glBindVertexArray(r->billboard_particle_vao);
|
||||
|
||||
for (int index = 0; index < pae_obj->nr_particles; index++) {
|
||||
struct particle_instance *pi = &po->instances[index];
|
||||
@@ -603,7 +764,8 @@ static void render_polygon_particles(struct RE_renderer *r, struct RE_instance *
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_NORMAL);
|
||||
glUniform1i(r->diffuse_type,
|
||||
lighting_disabled(inst->plugin) ? DIFFUSE_EMISSIVE : DIFFUSE_NORMAL);
|
||||
glUniform1i(r->fog_type, inst->plugin->fog_mode ? inst->plugin->fog_type : 0);
|
||||
|
||||
for (int index = 0; index < pae_obj->nr_particles; index++) {
|
||||
@@ -622,6 +784,9 @@ static void render_polygon_particles(struct RE_renderer *r, struct RE_instance *
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
struct mesh *mesh = &model->meshes[i];
|
||||
struct material *material = &model->materials[mesh->material];
|
||||
glUniform4f(r->uv_tiling,
|
||||
material->uv_tiling[0], material->uv_tiling[1],
|
||||
material->blend_uv_tiling[0], material->blend_uv_tiling[1]);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_maps[0]);
|
||||
@@ -636,6 +801,204 @@ static void render_polygon_particles(struct RE_renderer *r, struct RE_instance *
|
||||
}
|
||||
}
|
||||
|
||||
static void render_s3de_billboard_particles(struct RE_renderer *r, struct RE_instance *inst,
|
||||
struct s3de_object *obj, struct s3de_object_state *st,
|
||||
mat3 camera_rot, float frame)
|
||||
{
|
||||
if (!obj->texture || obj->particle_count == 0)
|
||||
return;
|
||||
|
||||
struct billboard_texture *bt = ht_get(
|
||||
inst->s3de_effect->s3de->textures, obj->texture, NULL);
|
||||
if (!bt)
|
||||
return;
|
||||
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_EMISSIVE);
|
||||
switch (obj->blend_type) {
|
||||
case S3DE_BLEND_NORMAL:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case S3DE_BLEND_ADDITIVE:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUniform1i(r->texture, 0);
|
||||
glBindVertexArray(r->billboard_particle_vao);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
|
||||
for (int i = 0; i < obj->particle_count; i++) {
|
||||
struct s3de_particle *p = &st->particles[i];
|
||||
float alpha;
|
||||
if (!s3de_particle_alpha(st, p, frame, &alpha))
|
||||
continue;
|
||||
|
||||
mat4 world;
|
||||
if (!s3de_billboard_world_transform(inst, obj, st, p, frame, camera_rot, world))
|
||||
continue;
|
||||
|
||||
glUniform1f(r->alpha_mod, alpha);
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, world[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, world[0]);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static void render_s3de_polygon_particles(struct RE_renderer *r, struct RE_instance *inst,
|
||||
struct s3de_object *obj, struct s3de_object_state *st,
|
||||
mat3 camera_rot, vec3 camera_pos, float frame,
|
||||
enum draw_phase phase)
|
||||
{
|
||||
struct model *model = obj->model;
|
||||
if (!model || obj->particle_count == 0)
|
||||
return;
|
||||
|
||||
if (phase == DRAW_OPAQUE && st->emitter_alpha < 1.0f)
|
||||
return;
|
||||
|
||||
glUniform1i(r->diffuse_type,
|
||||
lighting_disabled(inst->plugin) ? DIFFUSE_EMISSIVE : DIFFUSE_NORMAL);
|
||||
// The .3de blend_type is ignored for polygon objects. Per-mesh additive
|
||||
// blend modes are not yet handled.
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
|
||||
for (int i = 0; i < obj->particle_count; i++) {
|
||||
struct s3de_particle *p = &st->particles[i];
|
||||
float alpha;
|
||||
if (!s3de_particle_alpha(st, p, frame, &alpha))
|
||||
continue;
|
||||
if (phase == DRAW_OPAQUE && alpha < 1.0f)
|
||||
continue;
|
||||
|
||||
mat4 world;
|
||||
if (!s3de_mesh_world_transform(inst, obj, st, p, frame, camera_rot, camera_pos, world))
|
||||
continue;
|
||||
|
||||
glUniform1f(r->alpha_mod, alpha);
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, world[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, world[0]);
|
||||
|
||||
for (int j = 0; j < model->nr_meshes; j++) {
|
||||
struct mesh *mesh = &model->meshes[j];
|
||||
bool is_transparent = mesh->is_transparent || alpha < 1.0f;
|
||||
if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE))
|
||||
continue;
|
||||
struct material *material = &model->materials[mesh->material];
|
||||
glUniform4f(r->uv_tiling,
|
||||
material->uv_tiling[0], material->uv_tiling[1],
|
||||
material->blend_uv_tiling[0], material->blend_uv_tiling[1]);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_maps[0]);
|
||||
glUniform1i(r->texture, 0);
|
||||
glBindVertexArray(mesh->vao);
|
||||
|
||||
if (mesh->flags & MESH_BOTH)
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
if (mesh->flags & MESH_BOTH)
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort entry for per-object ordering.
|
||||
struct s3de_obj_sort_key {
|
||||
float key;
|
||||
int idx;
|
||||
};
|
||||
|
||||
static int cmp_s3de_obj(const void *lhs, const void *rhs)
|
||||
{
|
||||
const struct s3de_obj_sort_key *l = lhs;
|
||||
const struct s3de_obj_sort_key *r = rhs;
|
||||
if (l->key != r->key)
|
||||
return (l->key > r->key) - (l->key < r->key);
|
||||
// Fall back to declaration order to keep the sort stable.
|
||||
return (l->idx > r->idx) - (l->idx < r->idx);
|
||||
}
|
||||
|
||||
static void render_s3de_effect(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase)
|
||||
{
|
||||
if (!inst->s3de_effect || !inst->draw)
|
||||
return;
|
||||
|
||||
if (inst->local_transform_needs_update)
|
||||
RE_instance_update_local_transform(inst);
|
||||
|
||||
glUniform3fv(r->instance_ambient, 1, inst->ambient);
|
||||
reset_draw_uniforms(r, inst->plugin);
|
||||
|
||||
if (phase == DRAW_TRANSPARENT)
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
mat4 view_mat;
|
||||
RE_calc_view_matrix(&inst->plugin->camera, NULL, view_mat);
|
||||
mat3 camera_rot;
|
||||
glm_mat4_pick3t(view_mat, camera_rot);
|
||||
|
||||
struct s3de *s = inst->s3de_effect->s3de;
|
||||
|
||||
// Sort objects by their emitter's world-space distance to the camera.
|
||||
// The transparent phase draws back-to-front, the opaque phase draws
|
||||
// front-to-back. Particles inside one object are not sorted; they stay
|
||||
// in spawn order.
|
||||
float *camera_pos = inst->plugin->camera.override_active
|
||||
? inst->plugin->camera.override_pos : inst->plugin->camera.pos;
|
||||
float key_sign = phase == DRAW_OPAQUE ? 1.0f : -1.0f;
|
||||
struct s3de_obj_sort_key *keys = xmalloc(s->nr_objects * sizeof(*keys));
|
||||
for (int i = 0; i < s->nr_objects; i++) {
|
||||
keys[i].idx = i;
|
||||
vec3 emitter_world;
|
||||
glm_mat4_mulv3(inst->local_transform,
|
||||
inst->s3de_effect->objects[i].emitter_pos, 1.0f,
|
||||
emitter_world);
|
||||
vec3 d;
|
||||
glm_vec3_sub(emitter_world, camera_pos, d);
|
||||
keys[i].key = key_sign * glm_vec3_norm2(d);
|
||||
}
|
||||
qsort(keys, s->nr_objects, sizeof(*keys), cmp_s3de_obj);
|
||||
|
||||
float frame = inst->motion->current_frame;
|
||||
|
||||
for (int k = 0; k < s->nr_objects; k++) {
|
||||
int i = keys[k].idx;
|
||||
struct s3de_object *obj = &s->objects[i];
|
||||
struct s3de_object_state *st = &inst->s3de_effect->objects[i];
|
||||
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->ambient, st->additive_color, ambient);
|
||||
glUniform3fv(r->instance_ambient, 1, ambient);
|
||||
glUniform3fv(r->diffuse_mod, 1, st->multiply_color);
|
||||
|
||||
switch (obj->type) {
|
||||
case S3DE_OBJ_BILLBOARD:
|
||||
// Billboards are transparent-phase only.
|
||||
if (phase == DRAW_TRANSPARENT)
|
||||
render_s3de_billboard_particles(r, inst, obj, st, camera_rot, frame);
|
||||
break;
|
||||
case S3DE_OBJ_POLYGON:
|
||||
render_s3de_polygon_particles(r, inst, obj, st, camera_rot, camera_pos, frame, phase);
|
||||
break;
|
||||
case S3DE_OBJ_CAMERA:
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(keys);
|
||||
|
||||
if (phase == DRAW_TRANSPARENT)
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
static void render_particle_effect(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase)
|
||||
{
|
||||
// NOTE: inst->draw flag has no effect for particle effects.
|
||||
@@ -643,15 +1006,7 @@ static void render_particle_effect(struct RE_instance *inst, struct RE_renderer
|
||||
return;
|
||||
|
||||
glUniform3fv(r->instance_ambient, 1, inst->ambient);
|
||||
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
glUniform1f(r->specular_shininess, 0.0);
|
||||
glUniform1i(r->use_specular_map, GL_FALSE);
|
||||
glUniform1f(r->rim_exponent, 0.0);
|
||||
glUniform1i(r->use_normal_map, GL_FALSE);
|
||||
glUniform1f(r->shadow_darkness, 0.0f);
|
||||
glUniform1i(r->alpha_mode, ALPHA_BLEND);
|
||||
reset_draw_uniforms(r, inst->plugin);
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
@@ -684,6 +1039,8 @@ static void render_particle_effect(struct RE_instance *inst, struct RE_renderer
|
||||
|
||||
static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, enum draw_phase phase)
|
||||
{
|
||||
glUniform1f(r->grayscale_rate, inst->grayscale_rate);
|
||||
|
||||
switch (inst->type) {
|
||||
case RE_ITYPE_STATIC:
|
||||
render_static_model(inst, r, phase);
|
||||
@@ -695,7 +1052,10 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat
|
||||
render_billboard(inst, r, view_mat, phase);
|
||||
break;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
render_particle_effect(inst, r, phase);
|
||||
if (inst->s3de_effect)
|
||||
render_s3de_effect(inst, r, phase);
|
||||
else
|
||||
render_particle_effect(inst, r, phase);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -800,8 +1160,7 @@ static void render_shadow_map(struct RE_plugin *plugin, mat4 light_space_transfo
|
||||
|
||||
static void render_outlines(struct RE_plugin *plugin, mat4 view_transform)
|
||||
{
|
||||
enum RE_draw_edge_mode mode = plugin->draw_options[RE_DRAW_OPTION_EDGE];
|
||||
if (mode == RE_DRAW_EDGE_NONE)
|
||||
if (plugin->draw_options[RE_DRAW_OPTION_EDGE] == 0)
|
||||
return;
|
||||
struct outline_renderer *or = &plugin->renderer->outline;
|
||||
if (!or->program)
|
||||
@@ -810,12 +1169,21 @@ static void render_outlines(struct RE_plugin *plugin, mat4 view_transform)
|
||||
glUniformMatrix4fv(or->view_transform, 1, GL_FALSE, view_transform[0]);
|
||||
glUniformMatrix4fv(or->proj_transform, 1, GL_FALSE, plugin->proj_transform[0]);
|
||||
|
||||
// SealEngine ignores the per-mesh edge parameters from .opr and uses
|
||||
// a single plugin-wide edge length and color.
|
||||
bool seal_edge = re_plugin_version >= RE_SEAL_PLUGIN;
|
||||
if (seal_edge) {
|
||||
glUniform3fv(or->outline_color, 1, plugin->edge_color);
|
||||
float thickness = plugin->edge_length * (1.f - plugin->edge_reduction_rate);
|
||||
glUniform1f(or->outline_thickness, thickness);
|
||||
}
|
||||
|
||||
glCullFace(GL_FRONT);
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (!inst || !inst->draw)
|
||||
continue;
|
||||
if (mode == RE_DRAW_EDGE_CHARACTERS_ONLY && inst->type != RE_ITYPE_SKINNED)
|
||||
if (!inst->draw_edge)
|
||||
continue;
|
||||
struct model *model = inst->model;
|
||||
if (inst->model && model->nr_bones > 0) {
|
||||
@@ -830,8 +1198,10 @@ static void render_outlines(struct RE_plugin *plugin, mat4 view_transform)
|
||||
struct mesh *mesh = &model->meshes[j];
|
||||
if (mesh->flags & MESH_NO_EDGE)
|
||||
continue;
|
||||
glUniform3fv(or->outline_color, 1, mesh->outline_color);
|
||||
glUniform1f(or->outline_thickness, mesh->outline_thickness);
|
||||
if (!seal_edge) {
|
||||
glUniform3fv(or->outline_color, 1, mesh->outline_color);
|
||||
glUniform1f(or->outline_thickness, mesh->outline_thickness);
|
||||
}
|
||||
glBindVertexArray(mesh->vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
}
|
||||
@@ -850,10 +1220,58 @@ static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct R
|
||||
gfx_copy_stretch_blend_amap_alpha(dst, bcg->x, bcg->y, sw * bcg->mag, sh * bcg->mag, &bcg->texture, 0, 0, sw, sh, bcg->blend_rate * 255);
|
||||
}
|
||||
|
||||
static void setup_light_scattering(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN) {
|
||||
const float *lp = plugin->light_params;
|
||||
glUniform4f(r->ls_params, lp[SEAL_LP_LS_BETA_R], lp[SEAL_LP_LS_BETA_M],
|
||||
lp[SEAL_LP_LS_G], lp[SEAL_LP_LS_DISTANCE]);
|
||||
glUniform3f(r->ls_light_dir, lp[SEAL_LP_LS_LIGHT_VEC],
|
||||
lp[SEAL_LP_LS_LIGHT_VEC + 1], -lp[SEAL_LP_LS_LIGHT_VEC + 2]);
|
||||
glUniform3fv(r->ls_light_color, 1, &lp[SEAL_LP_LS_LIGHT_COLOR]);
|
||||
glUniform3fv(r->ls_sun_color, 1, &lp[SEAL_LP_LS_SUN_COLOR]);
|
||||
} else {
|
||||
glUniform4f(r->ls_params, plugin->ls_beta_r, plugin->ls_beta_m, plugin->ls_g, plugin->ls_distance);
|
||||
glUniform3fv(r->ls_light_dir, 1, plugin->ls_light_dir);
|
||||
glUniform3fv(r->ls_light_color, 1, plugin->ls_light_color);
|
||||
glUniform3fv(r->ls_sun_color, 1, plugin->ls_sun_color);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_hemisphere_light(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
const float *lp = plugin->light_params;
|
||||
vec3 hemi_dir = {
|
||||
-lp[SEAL_LP_HEMI_VEC], -lp[SEAL_LP_HEMI_VEC + 1], lp[SEAL_LP_HEMI_VEC + 2]
|
||||
};
|
||||
glm_vec3_normalize(hemi_dir);
|
||||
glUniform3fv(r->hemi_light_dir, 1, hemi_dir);
|
||||
glUniform3fv(r->hemi_sky_color, 1, &lp[SEAL_LP_HEMI_SKY_COLOR]);
|
||||
glUniform3fv(r->hemi_mid_color, 1, &lp[SEAL_LP_HEMI_MID_COLOR]);
|
||||
glUniform3fv(r->hemi_ground_color, 1, &lp[SEAL_LP_HEMI_GROUND_COLOR]);
|
||||
// SealEngine's specular uses the hemisphere light vector. hemi_dir points
|
||||
// toward the light, but the shader's specular formula expects the light
|
||||
// propagation direction.
|
||||
vec3 spec_dir;
|
||||
glm_vec3_negate_to(hemi_dir, spec_dir);
|
||||
glUniform3fv(r->specular_light_dir, 1, spec_dir);
|
||||
}
|
||||
|
||||
static void setup_tone_mapping(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
const float *lp = plugin->light_params;
|
||||
glUniform4fv(r->tonemap_param, 1, &lp[SEAL_LP_TONEMAP_EXPOSURE_BIAS]);
|
||||
glUniform4fv(r->tonemap_param2, 1, &lp[SEAL_LP_TONEMAP_C]);
|
||||
}
|
||||
|
||||
static void setup_lights(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
glUniform3fv(r->camera_pos, 1, plugin->camera.pos);
|
||||
glUniform3fv(r->camera_pos, 1, plugin->camera.override_active
|
||||
? plugin->camera.override_pos : plugin->camera.pos);
|
||||
glUniform3fv(r->global_ambient, 1, plugin->global_ambient);
|
||||
int light_index = 0;
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
@@ -884,6 +1302,9 @@ static void setup_lights(struct RE_plugin *plugin)
|
||||
glUniform3fv(r->dir_lights[light_index].diffuse, 1, zero);
|
||||
glUniform3fv(r->dir_lights[light_index].globe_diffuse, 1, zero);
|
||||
}
|
||||
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN)
|
||||
setup_hemisphere_light(plugin);
|
||||
}
|
||||
|
||||
static int cmp_instances_by_z(const void *lhs, const void *rhs)
|
||||
@@ -921,6 +1342,16 @@ static struct RE_instance **sort_instances(struct RE_plugin *plugin, mat4 view_t
|
||||
return instances;
|
||||
}
|
||||
|
||||
void RE_update_model(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
if (!r || plugin->suspended)
|
||||
return;
|
||||
uint32_t timestamp = SDL_GetTicks();
|
||||
RE_build_model(plugin, timestamp - r->last_frame_timestamp);
|
||||
r->last_frame_timestamp = timestamp;
|
||||
}
|
||||
|
||||
void RE_render(struct sact_sprite *sp)
|
||||
{
|
||||
struct RE_plugin *plugin = (struct RE_plugin *)sp->plugin;
|
||||
@@ -928,12 +1359,6 @@ void RE_render(struct sact_sprite *sp)
|
||||
if (!r || plugin->suspended)
|
||||
return;
|
||||
|
||||
if (re_plugin_version == RE_TAPIR_PLUGIN) {
|
||||
uint32_t timestamp = SDL_GetTicks();
|
||||
RE_build_model(plugin, timestamp - r->last_frame_timestamp);
|
||||
r->last_frame_timestamp = timestamp;
|
||||
}
|
||||
|
||||
sprite_dirty(sp);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
|
||||
@@ -961,7 +1386,7 @@ void RE_render(struct sact_sprite *sp)
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
mat4 view_transform;
|
||||
RE_calc_view_matrix(&plugin->camera, GLM_YUP, view_transform);
|
||||
RE_calc_view_matrix(&plugin->camera, NULL, view_transform);
|
||||
|
||||
// Tweak the projection transform so that the rendering result is vertically
|
||||
// flipped. If we render the scene normally, the resulting image will be
|
||||
@@ -987,10 +1412,7 @@ void RE_render(struct sact_sprite *sp)
|
||||
glUniform3fv(r->fog_color, 1, plugin->fog_color);
|
||||
break;
|
||||
case RE_FOG_LIGHT_SCATTERING:
|
||||
glUniform4f(r->ls_params, plugin->ls_beta_r, plugin->ls_beta_m, plugin->ls_g, plugin->ls_distance);
|
||||
glUniform3fv(r->ls_light_dir, 1, plugin->ls_light_dir);
|
||||
glUniform3fv(r->ls_light_color, 1, plugin->ls_light_color);
|
||||
glUniform3fv(r->ls_sun_color, 1, plugin->ls_sun_color);
|
||||
setup_light_scattering(plugin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -998,6 +1420,8 @@ void RE_render(struct sact_sprite *sp)
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
setup_lights(plugin);
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN)
|
||||
setup_tone_mapping(plugin);
|
||||
|
||||
// Sort instances by z-order.
|
||||
struct RE_instance **sorted_instances = sort_instances(plugin, view_transform);
|
||||
|
||||
+1599
File diff suppressed because it is too large
Load Diff
+388
@@ -0,0 +1,388 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
#include "../hll/iarray.h"
|
||||
|
||||
static void write_vec3(struct iarray_writer *w, vec3 v)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
iarray_write_float(w, v[i]);
|
||||
}
|
||||
|
||||
static void read_vec3(struct iarray_reader *r, vec3 v)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
v[i] = iarray_read_float(r);
|
||||
}
|
||||
|
||||
static void write_cstring(struct iarray_writer *w, const char *s)
|
||||
{
|
||||
struct string *tmp = cstr_to_string(s);
|
||||
iarray_write_string(w, tmp);
|
||||
free_string(tmp);
|
||||
}
|
||||
|
||||
static void save_motion(struct iarray_writer *w, struct motion *m)
|
||||
{
|
||||
iarray_write(w, !!m);
|
||||
if (!m)
|
||||
return;
|
||||
write_cstring(w, m->mot ? m->mot->name : "");
|
||||
iarray_write(w, m->state);
|
||||
iarray_write_float(w, m->current_frame);
|
||||
iarray_write_float(w, m->frame_begin);
|
||||
iarray_write_float(w, m->frame_end);
|
||||
iarray_write_float(w, m->loop_frame_begin);
|
||||
iarray_write_float(w, m->loop_frame_end);
|
||||
}
|
||||
|
||||
static void load_motion(struct iarray_reader *r, struct RE_instance *inst, bool next)
|
||||
{
|
||||
if (!iarray_read(r))
|
||||
return;
|
||||
struct string *name = iarray_read_string(r);
|
||||
if (name->size > 0) {
|
||||
if (next)
|
||||
RE_instance_load_next_motion(inst, name->text);
|
||||
else
|
||||
RE_instance_load_motion(inst, name->text);
|
||||
}
|
||||
free_string(name);
|
||||
|
||||
int state = iarray_read(r);
|
||||
float current_frame = iarray_read_float(r);
|
||||
float frame_begin = iarray_read_float(r);
|
||||
float frame_end = iarray_read_float(r);
|
||||
float loop_frame_begin = iarray_read_float(r);
|
||||
float loop_frame_end = iarray_read_float(r);
|
||||
|
||||
struct motion *m = next ? inst->next_motion : inst->motion;
|
||||
if (!m)
|
||||
return;
|
||||
m->state = state;
|
||||
m->current_frame = current_frame;
|
||||
m->frame_begin = frame_begin;
|
||||
m->frame_end = frame_end;
|
||||
m->loop_frame_begin = loop_frame_begin;
|
||||
m->loop_frame_end = loop_frame_end;
|
||||
}
|
||||
|
||||
static void save_instance(struct iarray_writer *w, struct RE_instance *inst)
|
||||
{
|
||||
iarray_write(w, inst->type);
|
||||
iarray_write_string_or_null(w, inst->name);
|
||||
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
|
||||
iarray_write(w, inst->target[i]);
|
||||
write_vec3(w, inst->pos);
|
||||
iarray_write_float(w, inst->pitch);
|
||||
iarray_write_float(w, inst->roll);
|
||||
iarray_write_float(w, inst->yaw);
|
||||
write_vec3(w, inst->scale);
|
||||
iarray_write_float(w, inst->alpha);
|
||||
iarray_write_float(w, inst->grayscale_rate);
|
||||
iarray_write(w, inst->draw);
|
||||
iarray_write(w, inst->draw_edge);
|
||||
iarray_write(w, inst->draw_shadow);
|
||||
iarray_write(w, inst->make_shadow);
|
||||
iarray_write(w, inst->draw_bump);
|
||||
iarray_write_float(w, inst->shadow_volume_bone_radius);
|
||||
iarray_write_float(w, inst->fps);
|
||||
iarray_write(w, inst->use_mag_speed);
|
||||
iarray_write(w, inst->motion_blend);
|
||||
iarray_write_float(w, inst->motion_blend_rate);
|
||||
write_vec3(w, inst->diffuse);
|
||||
write_vec3(w, inst->ambient);
|
||||
write_vec3(w, inst->column_pos);
|
||||
iarray_write_float(w, inst->column_height);
|
||||
iarray_write_float(w, inst->column_radius);
|
||||
iarray_write_float(w, inst->column_angle);
|
||||
|
||||
iarray_write(w, inst->draw_type);
|
||||
for (int i = 0; i < 4; i++)
|
||||
write_vec3(w, inst->vertex_pos[i]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
iarray_write_float(w, inst->vertex_uv[i][0]);
|
||||
iarray_write_float(w, inst->vertex_uv[i][1]);
|
||||
}
|
||||
|
||||
write_vec3(w, inst->vec);
|
||||
write_vec3(w, inst->globe_diffuse);
|
||||
iarray_write(w, !!inst->light_params);
|
||||
if (inst->light_params) {
|
||||
iarray_write(w, RE_NR_LIGHT_PARAMS);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
iarray_write_float(w, inst->light_params[i]);
|
||||
}
|
||||
|
||||
save_motion(w, inst->motion);
|
||||
save_motion(w, inst->next_motion);
|
||||
}
|
||||
|
||||
static void load_instance(struct iarray_reader *r, struct RE_instance *inst)
|
||||
{
|
||||
int type = iarray_read(r);
|
||||
if (type != RE_ITYPE_UNINITIALIZED)
|
||||
RE_instance_set_type(inst, type);
|
||||
struct string *name = iarray_read_string_or_null(r);
|
||||
if (name) {
|
||||
RE_instance_load(inst, name->text);
|
||||
free_string(name);
|
||||
}
|
||||
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
|
||||
inst->target[i] = iarray_read(r);
|
||||
read_vec3(r, inst->pos);
|
||||
inst->pitch = iarray_read_float(r);
|
||||
inst->roll = iarray_read_float(r);
|
||||
inst->yaw = iarray_read_float(r);
|
||||
read_vec3(r, inst->scale);
|
||||
inst->alpha = iarray_read_float(r);
|
||||
inst->grayscale_rate = iarray_read_float(r);
|
||||
inst->draw = iarray_read(r);
|
||||
inst->draw_edge = iarray_read(r);
|
||||
inst->draw_shadow = iarray_read(r);
|
||||
inst->make_shadow = iarray_read(r);
|
||||
inst->draw_bump = iarray_read(r);
|
||||
inst->shadow_volume_bone_radius = iarray_read_float(r);
|
||||
inst->fps = iarray_read_float(r);
|
||||
inst->use_mag_speed = iarray_read(r);
|
||||
inst->motion_blend = iarray_read(r);
|
||||
inst->motion_blend_rate = iarray_read_float(r);
|
||||
read_vec3(r, inst->diffuse);
|
||||
read_vec3(r, inst->ambient);
|
||||
read_vec3(r, inst->column_pos);
|
||||
inst->column_height = iarray_read_float(r);
|
||||
inst->column_radius = iarray_read_float(r);
|
||||
inst->column_angle = iarray_read_float(r);
|
||||
inst->local_transform_needs_update = true;
|
||||
|
||||
inst->draw_type = iarray_read(r);
|
||||
for (int i = 0; i < 4; i++)
|
||||
read_vec3(r, inst->vertex_pos[i]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
inst->vertex_uv[i][0] = iarray_read_float(r);
|
||||
inst->vertex_uv[i][1] = iarray_read_float(r);
|
||||
}
|
||||
|
||||
read_vec3(r, inst->vec);
|
||||
read_vec3(r, inst->globe_diffuse);
|
||||
if (iarray_read(r)) {
|
||||
int nr_params = iarray_read(r);
|
||||
for (int i = 0; i < nr_params; i++) {
|
||||
float param = iarray_read_float(r);
|
||||
if (inst->light_params && i < RE_NR_LIGHT_PARAMS)
|
||||
inst->light_params[i] = param;
|
||||
}
|
||||
}
|
||||
|
||||
load_motion(r, inst, false);
|
||||
load_motion(r, inst, true);
|
||||
}
|
||||
|
||||
static void save_back_cg(struct iarray_writer *w, struct RE_back_cg *bcg)
|
||||
{
|
||||
iarray_write_string_or_null(w, bcg->name);
|
||||
iarray_write(w, bcg->no);
|
||||
iarray_write_float(w, bcg->blend_rate);
|
||||
iarray_write_float(w, bcg->x);
|
||||
iarray_write_float(w, bcg->y);
|
||||
iarray_write_float(w, bcg->mag);
|
||||
iarray_write(w, bcg->show);
|
||||
}
|
||||
|
||||
static void load_back_cg(struct iarray_reader *r, struct RE_back_cg *bcg, struct archive *aar)
|
||||
{
|
||||
struct string *name = iarray_read_string_or_null(r);
|
||||
int no = iarray_read(r);
|
||||
float blend_rate = iarray_read_float(r);
|
||||
float x = iarray_read_float(r);
|
||||
float y = iarray_read_float(r);
|
||||
float mag = iarray_read_float(r);
|
||||
bool show = iarray_read(r);
|
||||
if (bcg) {
|
||||
if (name)
|
||||
RE_back_cg_set_name(bcg, name, aar);
|
||||
else if (no)
|
||||
RE_back_cg_set(bcg, no);
|
||||
bcg->blend_rate = blend_rate;
|
||||
bcg->x = x;
|
||||
bcg->y = y;
|
||||
bcg->mag = mag;
|
||||
bcg->show = show;
|
||||
}
|
||||
if (name)
|
||||
free_string(name);
|
||||
}
|
||||
|
||||
void RE_plugin_serialize(struct RE_plugin *plugin, struct iarray_writer *w)
|
||||
{
|
||||
write_vec3(w, plugin->camera.pos);
|
||||
iarray_write_float(w, plugin->camera.pitch);
|
||||
iarray_write_float(w, plugin->camera.roll);
|
||||
iarray_write_float(w, plugin->camera.yaw);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++)
|
||||
iarray_write_float(w, plugin->proj_transform[i][j]);
|
||||
}
|
||||
iarray_write(w, plugin->viewport_x);
|
||||
iarray_write(w, plugin->viewport_y);
|
||||
iarray_write(w, plugin->viewport_width);
|
||||
iarray_write(w, plugin->viewport_height);
|
||||
|
||||
iarray_write(w, RE_NR_BACK_CGS);
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
save_back_cg(w, &plugin->back_cg[i]);
|
||||
|
||||
iarray_write(w, plugin->render_mode);
|
||||
iarray_write(w, plugin->shadow_mode);
|
||||
write_vec3(w, plugin->shadow_map_light_dir);
|
||||
iarray_write_float(w, plugin->shadow_bias);
|
||||
iarray_write(w, plugin->bump_mode);
|
||||
iarray_write(w, plugin->bloom_mode);
|
||||
iarray_write(w, plugin->glare_mode);
|
||||
iarray_write(w, plugin->fog_mode);
|
||||
iarray_write(w, plugin->fog_type);
|
||||
iarray_write_float(w, plugin->fog_near);
|
||||
iarray_write_float(w, plugin->fog_far);
|
||||
write_vec3(w, plugin->fog_color);
|
||||
iarray_write_float(w, plugin->ls_beta_r);
|
||||
iarray_write_float(w, plugin->ls_beta_m);
|
||||
iarray_write_float(w, plugin->ls_g);
|
||||
iarray_write_float(w, plugin->ls_distance);
|
||||
write_vec3(w, plugin->ls_light_dir);
|
||||
write_vec3(w, plugin->ls_light_color);
|
||||
write_vec3(w, plugin->ls_sun_color);
|
||||
iarray_write(w, plugin->specular_mode);
|
||||
iarray_write(w, plugin->light_map_mode);
|
||||
iarray_write(w, plugin->soft_fog_edge_mode);
|
||||
iarray_write(w, plugin->ssao_mode);
|
||||
iarray_write(w, plugin->vertex_blend_mode);
|
||||
iarray_write(w, plugin->shader_precision_mode);
|
||||
iarray_write(w, plugin->texture_filter_mode);
|
||||
iarray_write(w, plugin->use_power2_texture);
|
||||
iarray_write(w, plugin->shadow_map_resolution_level);
|
||||
iarray_write(w, plugin->texture_resolution_level);
|
||||
write_vec3(w, plugin->global_ambient);
|
||||
iarray_write_float(w, plugin->post_effect_filter_y);
|
||||
iarray_write_float(w, plugin->post_effect_filter_cb);
|
||||
iarray_write_float(w, plugin->post_effect_filter_cr);
|
||||
iarray_write(w, RE_DRAW_OPTION_MAX);
|
||||
for (int i = 0; i < RE_DRAW_OPTION_MAX; i++)
|
||||
iarray_write(w, plugin->draw_options[i]);
|
||||
iarray_write(w, plugin->suspended);
|
||||
|
||||
iarray_write(w, plugin->mag_speed);
|
||||
iarray_write(w, RE_NR_LIGHT_PARAMS);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
iarray_write_float(w, plugin->light_params[i]);
|
||||
iarray_write_float(w, plugin->edge_length);
|
||||
iarray_write_float(w, plugin->edge_reduction_rate);
|
||||
write_vec3(w, plugin->edge_color);
|
||||
|
||||
iarray_write(w, plugin->nr_instances);
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
iarray_write(w, !!plugin->instances[i]);
|
||||
if (plugin->instances[i])
|
||||
save_instance(w, plugin->instances[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void RE_plugin_deserialize(struct RE_plugin *plugin, struct iarray_reader *r, int version)
|
||||
{
|
||||
read_vec3(r, plugin->camera.pos);
|
||||
plugin->camera.pitch = iarray_read_float(r);
|
||||
plugin->camera.roll = iarray_read_float(r);
|
||||
plugin->camera.yaw = iarray_read_float(r);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++)
|
||||
plugin->proj_transform[i][j] = iarray_read_float(r);
|
||||
}
|
||||
int viewport_x = iarray_read(r);
|
||||
int viewport_y = iarray_read(r);
|
||||
int viewport_width = iarray_read(r);
|
||||
int viewport_height = iarray_read(r);
|
||||
if (viewport_width > 0 && viewport_height > 0)
|
||||
RE_set_viewport(plugin, viewport_x, viewport_y, viewport_width, viewport_height);
|
||||
|
||||
int nr_back_cgs = iarray_read(r);
|
||||
for (int i = 0; i < nr_back_cgs; i++)
|
||||
load_back_cg(r, i < RE_NR_BACK_CGS ? &plugin->back_cg[i] : NULL, plugin->aar);
|
||||
|
||||
plugin->render_mode = iarray_read(r);
|
||||
plugin->shadow_mode = iarray_read(r);
|
||||
read_vec3(r, plugin->shadow_map_light_dir);
|
||||
plugin->shadow_bias = iarray_read_float(r);
|
||||
plugin->bump_mode = iarray_read(r);
|
||||
plugin->bloom_mode = iarray_read(r);
|
||||
plugin->glare_mode = iarray_read(r);
|
||||
plugin->fog_mode = iarray_read(r);
|
||||
plugin->fog_type = iarray_read(r);
|
||||
plugin->fog_near = iarray_read_float(r);
|
||||
plugin->fog_far = iarray_read_float(r);
|
||||
read_vec3(r, plugin->fog_color);
|
||||
plugin->ls_beta_r = iarray_read_float(r);
|
||||
plugin->ls_beta_m = iarray_read_float(r);
|
||||
plugin->ls_g = iarray_read_float(r);
|
||||
plugin->ls_distance = iarray_read_float(r);
|
||||
read_vec3(r, plugin->ls_light_dir);
|
||||
read_vec3(r, plugin->ls_light_color);
|
||||
read_vec3(r, plugin->ls_sun_color);
|
||||
plugin->specular_mode = iarray_read(r);
|
||||
plugin->light_map_mode = iarray_read(r);
|
||||
plugin->soft_fog_edge_mode = iarray_read(r);
|
||||
plugin->ssao_mode = iarray_read(r);
|
||||
plugin->vertex_blend_mode = iarray_read(r);
|
||||
plugin->shader_precision_mode = iarray_read(r);
|
||||
plugin->texture_filter_mode = iarray_read(r);
|
||||
plugin->use_power2_texture = iarray_read(r);
|
||||
plugin->shadow_map_resolution_level = iarray_read(r);
|
||||
plugin->texture_resolution_level = iarray_read(r);
|
||||
read_vec3(r, plugin->global_ambient);
|
||||
plugin->post_effect_filter_y = iarray_read_float(r);
|
||||
plugin->post_effect_filter_cb = iarray_read_float(r);
|
||||
plugin->post_effect_filter_cr = iarray_read_float(r);
|
||||
int nr_draw_options = iarray_read(r);
|
||||
for (int i = 0; i < nr_draw_options; i++) {
|
||||
int option = iarray_read(r);
|
||||
if (i < RE_DRAW_OPTION_MAX)
|
||||
plugin->draw_options[i] = option;
|
||||
}
|
||||
plugin->suspended = iarray_read(r);
|
||||
|
||||
plugin->mag_speed = iarray_read(r);
|
||||
int nr_light_params = iarray_read(r);
|
||||
for (int i = 0; i < nr_light_params; i++) {
|
||||
float param = iarray_read_float(r);
|
||||
if (i < RE_NR_LIGHT_PARAMS)
|
||||
plugin->light_params[i] = param;
|
||||
}
|
||||
plugin->edge_length = iarray_read_float(r);
|
||||
plugin->edge_reduction_rate = iarray_read_float(r);
|
||||
read_vec3(r, plugin->edge_color);
|
||||
|
||||
int nr_instances = iarray_read(r);
|
||||
for (int i = 0; i < nr_instances; i++) {
|
||||
if (!iarray_read(r))
|
||||
continue;
|
||||
load_instance(r, RE_create_instance_at(plugin, i));
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -85,7 +85,10 @@ bool asset_manager_load_archive(enum asset_type type, const char *archive_name)
|
||||
return false;
|
||||
if (!assets[type]->load_archive)
|
||||
ERROR("load_archive not supported on this archive type");
|
||||
return assets[type]->load_archive(assets[type], archive_name);
|
||||
char *path = gamedir_path(archive_name);
|
||||
bool r = assets[type]->load_archive(assets[type], path);
|
||||
free(path);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool asset_exists(enum asset_type type, int id)
|
||||
|
||||
+27
-5
@@ -103,7 +103,7 @@ bool audio_play_archive_data(struct archive_data *dfile)
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
if (anonymous_channels[i] == -1) {
|
||||
int ch = wav_get_unused_channel();
|
||||
if (!wav_prepare_from_archive_data(ch, dfile))
|
||||
if (!wav_prepare_from_archive_data(ch, AUDIO_NO_METADATA, dfile))
|
||||
return false;
|
||||
if (!wav_play(ch)) {
|
||||
wav_unprepare(ch);
|
||||
@@ -361,6 +361,18 @@ static int audio_get_time_length(struct id_pool *pool, int id)
|
||||
int wav_get_time_length(int id) { return audio_get_time_length(&wav, id); }
|
||||
int bgm_get_time_length(int id) { return audio_get_time_length(&bgm, id); }
|
||||
|
||||
static int audio_get_data_length(enum asset_type type, int no) {
|
||||
struct channel *ch = channel_open(type, no);
|
||||
if (!ch)
|
||||
return 0;
|
||||
int len = channel_get_time_length(ch);
|
||||
channel_close(ch);
|
||||
return len;
|
||||
}
|
||||
|
||||
int wav_get_data_length(int no) { return audio_get_data_length(ASSET_SOUND, no); }
|
||||
int bgm_get_data_length(int no) { return audio_get_data_length(ASSET_BGM, no); }
|
||||
|
||||
static int audio_get_data_no(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
@@ -392,19 +404,29 @@ static int audio_prepare_from_file(struct id_pool *pool, int id, char *filename)
|
||||
int wav_prepare_from_file(int id, char *filename) { return audio_prepare_from_file(&wav, id, filename); }
|
||||
int bgm_prepare_from_file(int id, char *filename) { return audio_prepare_from_file(&bgm, id, filename); }
|
||||
|
||||
static int audio_prepare_from_archive_data(struct id_pool *pool, int id, struct archive_data *dfile)
|
||||
static int audio_prepare_from_archive_data(struct id_pool *pool, int id,
|
||||
enum asset_type type, int metadata_no,
|
||||
struct archive_data *dfile)
|
||||
{
|
||||
if (id < 0)
|
||||
return 0;
|
||||
struct channel *ch = channel_open_archive_data(dfile);
|
||||
struct channel *ch = channel_open_archive_data(dfile, type, metadata_no);
|
||||
struct channel *old = id_pool_set(pool, id, ch);
|
||||
if (old)
|
||||
channel_close(old);
|
||||
return !!ch;
|
||||
}
|
||||
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile) {
|
||||
return audio_prepare_from_archive_data(&wav, id, dfile);
|
||||
int wav_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile)
|
||||
{
|
||||
return audio_prepare_from_archive_data(
|
||||
&wav, id, ASSET_SOUND, metadata_no, dfile);
|
||||
}
|
||||
|
||||
int bgm_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile)
|
||||
{
|
||||
return audio_prepare_from_archive_data(
|
||||
&bgm, id, ASSET_BGM, metadata_no, dfile);
|
||||
}
|
||||
|
||||
int wav_get_group_num_from_data_num(int no)
|
||||
|
||||
+21
-23
@@ -505,33 +505,12 @@ struct channel *channel_open(enum asset_type type, int no)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct channel *ch = channel_open_archive_data(dfile);
|
||||
struct channel *ch = channel_open_archive_data(dfile, type, no);
|
||||
if (!ch) {
|
||||
WARNING("Failed to open %s %d", type == ASSET_SOUND ? "WAV" : "BGM", no);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (type == ASSET_SOUND) {
|
||||
struct wai *wai = wai_get(no);
|
||||
ch->volume = 100;
|
||||
ch->loop_start = 0;
|
||||
ch->loop_end = ch->info.frames;
|
||||
ch->loop_count = 1;
|
||||
ch->mixer_no = wai ? wai->channel : 1;
|
||||
} else {
|
||||
struct bgi *bgi = bgi_get(no);
|
||||
if (bgi) {
|
||||
ch->volume = clamp(0, 100, bgi->volume);
|
||||
ch->loop_start = clamp(0, ch->info.frames, bgi->loop_start);
|
||||
ch->loop_end = clamp(0, ch->info.frames, bgi->loop_end);
|
||||
ch->loop_count = max(0, bgi->loop_count);
|
||||
ch->mixer_no = clamp(0, nr_mixers, bgi->channel);
|
||||
} else {
|
||||
ch->loop_count = 0;
|
||||
}
|
||||
}
|
||||
ch->no = no;
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
@@ -565,7 +544,8 @@ static bool init_channel(struct channel *ch)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile,
|
||||
enum asset_type type, int metadata_no)
|
||||
{
|
||||
struct channel *ch = xcalloc(1, sizeof(struct channel));
|
||||
ch->dfile = dfile;
|
||||
@@ -580,6 +560,24 @@ struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
free(ch);
|
||||
return NULL;
|
||||
}
|
||||
if (metadata_no >= 0) {
|
||||
if (type == ASSET_SOUND) {
|
||||
struct wai *wai = wai_get(metadata_no);
|
||||
ch->mixer_no = wai ? wai->channel : 1;
|
||||
} else if (type == ASSET_BGM) {
|
||||
struct bgi *bgi = bgi_get(metadata_no);
|
||||
if (bgi) {
|
||||
ch->volume = clamp(0, 100, bgi->volume);
|
||||
ch->loop_start = clamp(0, ch->info.frames, bgi->loop_start);
|
||||
ch->loop_end = clamp(0, ch->info.frames, bgi->loop_end);
|
||||
ch->loop_count = max(0, bgi->loop_count);
|
||||
ch->mixer_no = clamp(0, nr_mixers, bgi->channel);
|
||||
} else {
|
||||
ch->loop_count = 0;
|
||||
}
|
||||
}
|
||||
ch->no = metadata_no;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
+20
-2
@@ -56,6 +56,9 @@ static enum {
|
||||
DAP_STOPPED
|
||||
} dap_state = DAP_UNINITIALIZED;
|
||||
|
||||
static bool dap_launch_req_received = false;
|
||||
static bool dap_config_done_req_received = false;
|
||||
|
||||
static enum {
|
||||
NOT_STEPPING = 0,
|
||||
STEPPING_INTO,
|
||||
@@ -178,12 +181,17 @@ static void cmd_launch(cJSON *args, cJSON *resp)
|
||||
|
||||
// TODO: handle noDebug option
|
||||
send_response(resp, true);
|
||||
dap_state = DAP_RUNNING;
|
||||
dap_launch_req_received = true;
|
||||
if (dap_config_done_req_received)
|
||||
dap_state = DAP_RUNNING;
|
||||
}
|
||||
|
||||
static void cmd_configurationDone(cJSON *args, cJSON *resp)
|
||||
{
|
||||
send_response(resp, true);
|
||||
dap_config_done_req_received = true;
|
||||
if (dap_launch_req_received)
|
||||
dap_state = DAP_RUNNING;
|
||||
}
|
||||
|
||||
static void cmd_stackTrace(cJSON *args, cJSON *resp)
|
||||
@@ -403,6 +411,8 @@ static void add_array_elements_to_array(cJSON *array, struct page *page)
|
||||
|
||||
static void add_page_to_array(cJSON *array, struct page *page)
|
||||
{
|
||||
if (!page)
|
||||
return;
|
||||
switch (page->type) {
|
||||
case GLOBAL_PAGE:
|
||||
add_globals_to_array(array, page);
|
||||
@@ -1000,12 +1010,13 @@ static int read_command_thread(void *data)
|
||||
WARNING("Debug Adapter Protocol error: no Content-Length header");
|
||||
continue;
|
||||
}
|
||||
char *buf = malloc(content_length);
|
||||
char *buf = malloc(content_length+1);
|
||||
if (fread(buf, content_length, 1, stdin) != 1) {
|
||||
WARNING("fread(stdin): %s", strerror(errno));
|
||||
free(buf);
|
||||
continue;
|
||||
}
|
||||
buf[content_length] = '\0';
|
||||
msgq_enqueue(queue, buf);
|
||||
content_length = -1;
|
||||
} else {
|
||||
@@ -1087,6 +1098,13 @@ void dbg_dap_log(const char *log, const char *fmt, va_list ap)
|
||||
size_t len = max(1024, strlen(fmt) * 2);
|
||||
char *buf = xmalloc(len);
|
||||
vsnprintf(buf, len, fmt, ap);
|
||||
#ifdef _WIN32
|
||||
// Since the callers use display_sjis*(), buf is in Shift_JIS on Windows.
|
||||
// Convert to UTF-8 for DAP.
|
||||
char *utf8 = sjis2utf(buf, 0);
|
||||
free(buf);
|
||||
buf = utf8;
|
||||
#endif
|
||||
emit_output_event(log, buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
+27
@@ -1094,6 +1094,33 @@ void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_grayscale_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_grayscale_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_grayscale_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
mat4 mw_transform = MAT4(
|
||||
-src->w, 0, 0, sx + w,
|
||||
0, src->h, 0, -sy,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(w, h);
|
||||
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_draw_shader(©_grayscale_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
static void draw_line(Texture *dst, int x0, int y0, int x1, int y1, struct copy_data *data)
|
||||
{
|
||||
GLfloat w = dst->w;
|
||||
|
||||
@@ -60,7 +60,7 @@ struct dungeon_context *dungeon_get_context(int surface)
|
||||
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int width, int height)
|
||||
{
|
||||
struct dungeon_context *ctx = xcalloc(1, sizeof(struct dungeon_context));
|
||||
struct dungeon_context *ctx = xcalloc_aligned(1, struct dungeon_context);
|
||||
ctx->plugin.name = plugin_name;
|
||||
ctx->plugin.free = dungeon_context_free;
|
||||
ctx->plugin.update = dungeon_render;
|
||||
@@ -102,7 +102,7 @@ static void dungeon_context_free(struct draw_plugin *plugin)
|
||||
if (ctx->map)
|
||||
dungeon_map_free(ctx->map);
|
||||
|
||||
free(ctx);
|
||||
xfree_aligned(ctx);
|
||||
}
|
||||
|
||||
static GLuint *load_event_textures(int *nr_textures_out)
|
||||
|
||||
@@ -75,6 +75,7 @@ const char *effect_names[NR_EFFECTS] = {
|
||||
[EFFECT_BLUR_FADEOUT] = "EFFECT_BLUR_FADEOUT",
|
||||
[EFFECT_BLUR_CROSSFADE] = "EFFECT_BLUR_CROSSFADE",
|
||||
[EFFECT_2ROT_ZOOM_BLEND_BLUR] = "EFFECT_2ROT_ZOOM_BLEND_BLUR",
|
||||
[EFFECT_VWAVE_SCROLL_CROSSFADE] = "EFFECT_VWAVE_SCROLL_CROSSFADE",
|
||||
[EFFECT_VWAVE_CROSSFADE] = "EFFECT_VWAVE_CROSSFADE",
|
||||
};
|
||||
|
||||
@@ -145,6 +146,7 @@ static void render_effect_shader(struct effect_shader *shader, Texture *dst, Tex
|
||||
static struct effect_shader crossfade_shader = EFFECT_SHADER("shaders/effects/crossfade.f.glsl");
|
||||
static struct effect_shader crossfade_lr_shader = EFFECT_SHADER("shaders/effects/crossfade_lr.f.glsl");
|
||||
static struct effect_shader crossfade_up_down_shader = EFFECT_SHADER("shaders/effects/crossfade_up_down.f.glsl");
|
||||
static struct effect_shader crossfade_down_up_shader = EFFECT_SHADER("shaders/effects/crossfade_down_up.f.glsl");
|
||||
static struct effect_shader mosaic_shader = EFFECT_SHADER("shaders/effects/mosaic.f.glsl");
|
||||
static struct effect_shader blind_down_shader = EFFECT_SHADER("shaders/effects/blind_down.f.glsl");
|
||||
static struct effect_shader blind_lr_shader = EFFECT_SHADER("shaders/effects/blind_lr.f.glsl");
|
||||
@@ -155,12 +157,14 @@ static struct effect_shader turn_page_shader = EFFECT_SHADER("shaders/effects/tu
|
||||
static struct effect_shader sepia_noise_crossfade_shader = EFFECT_SHADER("shaders/effects/sepia_noise_crossfade.f.glsl");
|
||||
static struct effect_shader blur_fadeout_shader = EFFECT_SHADER("shaders/effects/blur_fadeout.f.glsl");
|
||||
static struct effect_shader blur_crossfade_shader = EFFECT_SHADER("shaders/effects/blur_crossfade.f.glsl");
|
||||
static struct effect_shader vwave_scroll_crossfade_shader = EFFECT_SHADER("shaders/effects/vwave_scroll_crossfade.f.glsl");
|
||||
static struct effect_shader vwave_crossfade_shader = EFFECT_SHADER("shaders/effects/vwave_crossfade.f.glsl");
|
||||
|
||||
static struct effect_shader *effect_shaders[NR_EFFECTS] = {
|
||||
[EFFECT_CROSSFADE] = &crossfade_shader,
|
||||
[EFFECT_CROSSFADE_LR] = &crossfade_lr_shader,
|
||||
[EFFECT_UP_DOWN_CROSSFADE] = &crossfade_up_down_shader,
|
||||
[EFFECT_DOWN_UP_CROSSFADE] = &crossfade_down_up_shader,
|
||||
[EFFECT_CROSSFADE_MOSAIC] = &mosaic_shader,
|
||||
[EFFECT_BLIND_DOWN] = &blind_down_shader,
|
||||
[EFFECT_BLIND_LR] = &blind_lr_shader,
|
||||
@@ -171,6 +175,7 @@ static struct effect_shader *effect_shaders[NR_EFFECTS] = {
|
||||
[EFFECT_SEPIA_NOISE_CROSSFADE] = &sepia_noise_crossfade_shader,
|
||||
[EFFECT_BLUR_FADEOUT] = &blur_fadeout_shader,
|
||||
[EFFECT_BLUR_CROSSFADE] = &blur_crossfade_shader,
|
||||
[EFFECT_VWAVE_SCROLL_CROSSFADE] = &vwave_scroll_crossfade_shader,
|
||||
[EFFECT_VWAVE_CROSSFADE] = &vwave_crossfade_shader,
|
||||
};
|
||||
|
||||
|
||||
@@ -417,6 +417,7 @@ void hll_call(int libno, int fno)
|
||||
extern struct static_library lib_ACXLoader;
|
||||
extern struct static_library lib_ACXLoaderP2;
|
||||
extern struct static_library lib_ADVSYS;
|
||||
extern struct static_library lib_AFAFactory;
|
||||
extern struct static_library lib_AliceLogo;
|
||||
extern struct static_library lib_AliceLogo2;
|
||||
extern struct static_library lib_AliceLogo3;
|
||||
@@ -440,6 +441,7 @@ extern struct static_library lib_DALKDemo;
|
||||
extern struct static_library lib_DALKEDemo;
|
||||
extern struct static_library lib_Data;
|
||||
extern struct static_library lib_DataFile;
|
||||
extern struct static_library lib_Discord;
|
||||
extern struct static_library lib_DrawDungeon;
|
||||
extern struct static_library lib_DrawDungeon2;
|
||||
extern struct static_library lib_DrawDungeon14;
|
||||
@@ -468,6 +470,7 @@ extern struct static_library lib_InputDevice;
|
||||
extern struct static_library lib_InputString;
|
||||
extern struct static_library lib_KiwiSoundEngine;
|
||||
extern struct static_library lib_LoadCG;
|
||||
extern struct static_library lib_MADLoader;
|
||||
extern struct static_library lib_MainEXFile;
|
||||
extern struct static_library lib_MainSurface;
|
||||
extern struct static_library lib_MamanyoDemo;
|
||||
@@ -483,6 +486,8 @@ extern struct static_library lib_MsgSkip;
|
||||
extern struct static_library lib_MusicSystem;
|
||||
extern struct static_library lib_NewFont;
|
||||
extern struct static_library lib_OutputLog;
|
||||
extern struct static_library lib_P3MapSprite;
|
||||
extern struct static_library lib_P3SquareSprite;
|
||||
extern struct static_library lib_PassRegister;
|
||||
extern struct static_library lib_PastelChime2;
|
||||
extern struct static_library lib_PartsEngine;
|
||||
@@ -492,6 +497,7 @@ extern struct static_library lib_PlayMovie;
|
||||
extern struct static_library lib_ReignEngine;
|
||||
extern struct static_library lib_SACT2;
|
||||
extern struct static_library lib_SACTDX;
|
||||
extern struct static_library lib_SealEngine;
|
||||
extern struct static_library lib_SengokuRanceFont;
|
||||
extern struct static_library lib_Sound2ex;
|
||||
extern struct static_library lib_SoundFilePlayer;
|
||||
@@ -527,12 +533,14 @@ extern struct static_library lib_vmString;
|
||||
extern struct static_library lib_vmSurface;
|
||||
extern struct static_library lib_vmSystem;
|
||||
extern struct static_library lib_vmTimer;
|
||||
extern struct static_library lib_ValueEncryption;
|
||||
extern struct static_library lib_VSFile;
|
||||
|
||||
static struct static_library *static_libraries[] = {
|
||||
&lib_ACXLoader,
|
||||
&lib_ACXLoaderP2,
|
||||
&lib_ADVSYS,
|
||||
&lib_AFAFactory,
|
||||
&lib_AliceLogo,
|
||||
&lib_AliceLogo2,
|
||||
&lib_AliceLogo3,
|
||||
@@ -556,6 +564,7 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_DALKEDemo,
|
||||
&lib_Data,
|
||||
&lib_DataFile,
|
||||
&lib_Discord,
|
||||
&lib_DrawDungeon,
|
||||
&lib_DrawDungeon2,
|
||||
&lib_DrawDungeon14,
|
||||
@@ -584,6 +593,7 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_InputString,
|
||||
&lib_KiwiSoundEngine,
|
||||
&lib_LoadCG,
|
||||
&lib_MADLoader,
|
||||
&lib_MainEXFile,
|
||||
&lib_MainSurface,
|
||||
&lib_MamanyoDemo,
|
||||
@@ -599,6 +609,8 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_MusicSystem,
|
||||
&lib_NewFont,
|
||||
&lib_OutputLog,
|
||||
&lib_P3MapSprite,
|
||||
&lib_P3SquareSprite,
|
||||
&lib_PassRegister,
|
||||
&lib_PastelChime2,
|
||||
&lib_PartsEngine,
|
||||
@@ -608,6 +620,7 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_ReignEngine,
|
||||
&lib_SACT2,
|
||||
&lib_SACTDX,
|
||||
&lib_SealEngine,
|
||||
&lib_SengokuRanceFont,
|
||||
&lib_Sound2ex,
|
||||
&lib_SoundFilePlayer,
|
||||
@@ -643,6 +656,7 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_vmSurface,
|
||||
&lib_vmSystem,
|
||||
&lib_vmTimer,
|
||||
&lib_ValueEncryption,
|
||||
&lib_VSFile,
|
||||
NULL
|
||||
};
|
||||
|
||||
+13
-11
@@ -101,24 +101,26 @@ static struct fnl_bitmap_glyph *fnl_get_bitmap_glyph(struct fnl_bitmap_size *bit
|
||||
if (bitmap->glyphs[index])
|
||||
return bitmap->glyphs[index];
|
||||
|
||||
unsigned long data_size;
|
||||
uint8_t *data = fnl_glyph_data(bitmap->face->font->fnl, &bitmap->face->glyphs[index], &data_size);
|
||||
struct fnl_glyph *glyph = &bitmap->face->glyphs[index];
|
||||
uint8_t *data = fnl_glyph_data(bitmap->face->font->fnl, glyph);
|
||||
|
||||
const int glyph_w = (data_size*8) / bitmap->face->height;
|
||||
const int glyph_w = fnl_glyph_stride(glyph) * 8;
|
||||
const int glyph_h = bitmap->face->height;
|
||||
|
||||
struct fnl_bitmap_glyph *out = xmalloc(sizeof(struct fnl_bitmap_glyph));
|
||||
out->width = glyph_w;
|
||||
out->height = glyph_h;
|
||||
out->advance = bitmap->face->glyphs[index].real_width;
|
||||
out->pixels = xmalloc(glyph_w * glyph_h);
|
||||
out->advance = glyph->width;
|
||||
out->pixels = xcalloc(glyph_w, glyph_h);
|
||||
|
||||
// expand 1-bit bitmap to 8-bit
|
||||
for (unsigned i = 0; i < glyph_w * glyph_h; i++) {
|
||||
unsigned row = (glyph_h - 1) - i / glyph_w;
|
||||
unsigned col = i % glyph_w;
|
||||
bool on = data[i/8] & (1 << (7 - i % 8));
|
||||
out->pixels[row*glyph_w + col] = on ? 255 : 0;
|
||||
for (unsigned row = 0; row < glyph_h; row++) {
|
||||
uint8_t *dst = out->pixels + ((glyph_h - 1) - row) * glyph_w;
|
||||
for (unsigned col = 0; col < glyph->width; col++) {
|
||||
unsigned i = row * glyph_w + col;
|
||||
bool on = data[i/8] & (1 << (7 - i % 8));
|
||||
dst[col] = on ? 255 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
@@ -183,7 +185,7 @@ static float fnl_font_size_char(struct font_size *_size, uint32_t code)
|
||||
return 0.0;
|
||||
if (!size->bitmap_size->face->glyphs[index].data_pos)
|
||||
return 0.0;
|
||||
float r = (float)size->bitmap_size->face->glyphs[index].real_width / (float)size->denominator;
|
||||
float r = (float)size->bitmap_size->face->glyphs[index].width / (float)size->denominator;
|
||||
if (code == '\t')
|
||||
r *= 2.88;
|
||||
return r;
|
||||
|
||||
+106
@@ -51,6 +51,15 @@ static void write_instruction1(struct buffer *out, enum opcode op, int32_t arg)
|
||||
buffer_write_int32(out, arg);
|
||||
}
|
||||
|
||||
static void write_instruction3(struct buffer *out, enum opcode op, int32_t arg0, int32_t arg1,
|
||||
int32_t arg2)
|
||||
{
|
||||
buffer_write_int16(out, op);
|
||||
buffer_write_int32(out, arg0);
|
||||
buffer_write_int32(out, arg1);
|
||||
buffer_write_int32(out, arg2);
|
||||
}
|
||||
|
||||
void set_msgskip_delay(struct ain *ain, unsigned ms)
|
||||
{
|
||||
int orig_fno = ain_get_function(ain, "A");
|
||||
@@ -175,6 +184,100 @@ static void apply_rance6_hacks(struct ain *ain)
|
||||
game_rance6_mg = true;
|
||||
}
|
||||
|
||||
// Rance IX
|
||||
// --------
|
||||
// T_PartsNo class is implemented in an extremely inefficient way, which
|
||||
// causes noticable performance issues when using the UI (e.g. when loading
|
||||
// the items screen).
|
||||
// We rewrite this class to improve performance.
|
||||
static void apply_rance9_hacks(struct ain *ain)
|
||||
{
|
||||
int add_fno = ain_get_function(ain, "T_PartsNo@add");
|
||||
int erase_fno = ain_get_function(ain, "T_PartsNo@erase");
|
||||
if (add_fno < 0 || erase_fno < 0) {
|
||||
WARNING("Couldn't location T_PartsNo@add or T_PartsNo@erase");
|
||||
return;
|
||||
}
|
||||
|
||||
struct buffer out;
|
||||
buffer_init(&out, ain->code, ain->code_size);
|
||||
|
||||
// bool T_PartsNo::add(int nNo)
|
||||
// {
|
||||
// if (nNo < 10000 || nNo > 999999)
|
||||
// return false;
|
||||
// int idx = nNo - 10000;
|
||||
// if (this.m_abUsed[idx])
|
||||
// return false;
|
||||
// return (this.m_abUsed[idx] = true);
|
||||
// }
|
||||
int32_t return_false_addr[3];
|
||||
struct ain_function *f = &ain->functions[add_fno];
|
||||
buffer_seek(&out, f->address);
|
||||
// if (nNo < 10000 || nNo > 999999) return false;
|
||||
write_instruction3(&out, SH_IF_LOC_LT_IMM, 0, 10000, 0);
|
||||
return_false_addr[0] = out.index - 4;
|
||||
write_instruction3(&out, SH_IF_LOC_GT_IMM, 0, 999999, 0);
|
||||
return_false_addr[1] = out.index - 4;
|
||||
// int idx = nNo - 10000;
|
||||
write_instruction0(&out, PUSHLOCALPAGE);
|
||||
write_instruction1(&out, PUSH, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 0);
|
||||
write_instruction1(&out, PUSH, 10000);
|
||||
write_instruction0(&out, SUB);
|
||||
write_instruction0(&out, ASSIGN);
|
||||
write_instruction0(&out, POP);
|
||||
// if (this.m_abUsed[idx]) return false;
|
||||
write_instruction1(&out, SH_STRUCTREF, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 1);
|
||||
write_instruction0(&out, REF);
|
||||
write_instruction1(&out, IFNZ, 0);
|
||||
return_false_addr[2] = out.index - 4;
|
||||
// return (this.m_abUsed[idx] = true);
|
||||
write_instruction1(&out, SH_STRUCTREF, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 1);
|
||||
write_instruction1(&out, PUSH, 1);
|
||||
write_instruction0(&out, ASSIGN);
|
||||
write_instruction0(&out, RETURN);
|
||||
// [return false]
|
||||
buffer_write_int32_at(&out, return_false_addr[0], out.index);
|
||||
buffer_write_int32_at(&out, return_false_addr[1], out.index);
|
||||
buffer_write_int32_at(&out, return_false_addr[2], out.index);
|
||||
write_instruction1(&out, PUSH, 0);
|
||||
write_instruction0(&out, RETURN);
|
||||
|
||||
// bool T_PartsNo::erase(int nNo)
|
||||
// {
|
||||
// if (nNo < 10000 || nNo > 999999)
|
||||
// return false;
|
||||
// this.m_abUsed[nNo - 10000] = false;
|
||||
// return true;
|
||||
// }
|
||||
f = &ain->functions[erase_fno];
|
||||
buffer_seek(&out, f->address);
|
||||
// if (nNo < 10000 || nNo > 999999) return false;
|
||||
write_instruction3(&out, SH_IF_LOC_LT_IMM, 0, 10000, 0);
|
||||
return_false_addr[0] = out.index - 4;
|
||||
write_instruction3(&out, SH_IF_LOC_GT_IMM, 0, 999999, 0);
|
||||
return_false_addr[1] = out.index - 4;
|
||||
// this.m_abUsed[nNo - 10000] = false;
|
||||
write_instruction1(&out, SH_STRUCTREF, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 0);
|
||||
write_instruction1(&out, PUSH, 10000);
|
||||
write_instruction0(&out, SUB);
|
||||
write_instruction1(&out, PUSH, 0);
|
||||
write_instruction0(&out, ASSIGN);
|
||||
write_instruction0(&out, POP);
|
||||
// return true;
|
||||
write_instruction1(&out, PUSH, 1);
|
||||
write_instruction0(&out, RETURN);
|
||||
// [return false]
|
||||
buffer_write_int32_at(&out, return_false_addr[0], out.index);
|
||||
buffer_write_int32_at(&out, return_false_addr[1], out.index);
|
||||
write_instruction1(&out, PUSH, 0);
|
||||
write_instruction0(&out, RETURN);
|
||||
}
|
||||
|
||||
// Daibanchou (English fan translation)
|
||||
// ------------------------------------
|
||||
// Gpx2Plus.CopyStretchReduceAMap behaves differently in order to work around
|
||||
@@ -203,6 +306,9 @@ void apply_game_specific_hacks(struct ain *ain)
|
||||
game_rance8_mg = true;
|
||||
} else if (!strcmp(game_name, "DUNGEONS&DOLLS")) {
|
||||
game_dungeons_and_dolls = true;
|
||||
} else if (!strcmp(game_name, "ランス9")
|
||||
|| !strcmp(game_name, "Rance 9 - The Helmanian Revolution")) {
|
||||
apply_rance9_hacks(ain);
|
||||
}
|
||||
free(game_name);
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/acx.h"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "system4/string.h"
|
||||
#include "asset_manager.h"
|
||||
#include "hll.h"
|
||||
|
||||
static enum asset_type get_asset_type(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case 2: return ASSET_CG;
|
||||
case 3: return ASSET_FLAT;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, AFAFactory, IsExistArchive, struct string *ArchiveName);
|
||||
|
||||
bool AFAFactory_LoadArchive(int type, struct string *archive_name)
|
||||
{
|
||||
enum asset_type t = get_asset_type(type);
|
||||
if (t < 0) {
|
||||
WARNING("Unknown asset type: %d", type);
|
||||
return false;
|
||||
}
|
||||
return asset_manager_load_archive(t, archive_name->text);
|
||||
}
|
||||
|
||||
//int AFAFactory_GetCountOfData(int Type);
|
||||
//void AFAFactory_GetTitleByIndex(int Type, int Index, struct string **Name);
|
||||
//int AFAFactory_SearchTitle(int Type, struct string *Name);
|
||||
//int AFAFactory_PrefixSearchTitle(int Type, struct string *Name);
|
||||
//int AFAFactory_SuffixSearchTitle(int Type, struct string *Name);
|
||||
//int AFAFactory_GetCountOfSearchData(int Type);
|
||||
//void AFAFactory_GetSearchTitleByIndex(int Type, int Index, struct string **Name);
|
||||
|
||||
HLL_LIBRARY(AFAFactory,
|
||||
HLL_EXPORT(IsExistArchive, AFAFactory_IsExistArchive),
|
||||
HLL_EXPORT(LoadArchive, AFAFactory_LoadArchive),
|
||||
HLL_TODO_EXPORT(GetCountOfData, AFAFactory_GetCountOfData),
|
||||
HLL_TODO_EXPORT(GetTitleByIndex, AFAFactory_GetTitleByIndex),
|
||||
HLL_TODO_EXPORT(SearchTitle, AFAFactory_SearchTitle),
|
||||
HLL_TODO_EXPORT(PrefixSearchTitle, AFAFactory_PrefixSearchTitle),
|
||||
HLL_TODO_EXPORT(SuffixSearchTitle, AFAFactory_SuffixSearchTitle),
|
||||
HLL_TODO_EXPORT(GetCountOfSearchData, AFAFactory_GetCountOfSearchData),
|
||||
HLL_TODO_EXPORT(GetSearchTitleByIndex, AFAFactory_GetSearchTitleByIndex)
|
||||
);
|
||||
@@ -276,6 +276,7 @@ bool ADVLogList_Load(struct page **data)
|
||||
goto error;
|
||||
}
|
||||
|
||||
free_log(log);
|
||||
log->lines = xcalloc(nr_lines, sizeof(struct string*));
|
||||
for (int i = 0; i < nr_lines; i++) {
|
||||
log->lines[i] = iarray_read_string(&r);
|
||||
@@ -475,6 +476,7 @@ error:
|
||||
|
||||
static void AnteaterADVEngine_ModuleFini(void)
|
||||
{
|
||||
ADVLogList_Clear();
|
||||
ADVSceneKeeper_Clear();
|
||||
}
|
||||
|
||||
|
||||
+8
-9
@@ -17,12 +17,11 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/mt19937int.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "audio.h"
|
||||
@@ -47,14 +46,15 @@ static int BanMisc_SaveStruct(struct page *page, struct string *file_name)
|
||||
iarray_free_writer(&out);
|
||||
|
||||
// compress
|
||||
unsigned long compressed_size = compressBound(raw_size);
|
||||
size_t compressed_size = zlib_compress_bound(raw_size);
|
||||
uint8_t *buf = xmalloc(MD11_HEADER_SIZE + compressed_size);
|
||||
memcpy(buf, md11_signature, sizeof(md11_signature));
|
||||
LittleEndian_putDW(buf, 4, raw_size);
|
||||
uint8_t *compressed = buf + MD11_HEADER_SIZE;
|
||||
int r = compress2(compressed, &compressed_size, raw_buf, raw_size, Z_DEFAULT_COMPRESSION);
|
||||
compressed_size = zlib_compress(compressed, compressed_size, raw_buf,
|
||||
raw_size, ZLIB_DEFAULT_COMPRESSION);
|
||||
free(raw_buf);
|
||||
if (r != Z_OK) {
|
||||
if (!compressed_size) {
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -79,7 +79,7 @@ static struct page *load_md11(int struct_type, uint8_t *buf, size_t len)
|
||||
WARNING("BanMisc.LoadStruct file too short");
|
||||
return NULL;
|
||||
}
|
||||
unsigned long raw_size = LittleEndian_getDW(buf, 4);
|
||||
size_t raw_size = LittleEndian_getDW(buf, 4);
|
||||
if (raw_size % 4 != 0) {
|
||||
WARNING("BanMisc.LoadStruct invalid file format");
|
||||
return NULL;
|
||||
@@ -92,9 +92,8 @@ static struct page *load_md11(int struct_type, uint8_t *buf, size_t len)
|
||||
|
||||
// decompress
|
||||
uint8_t *raw_buf = xmalloc(raw_size);
|
||||
int r = uncompress(raw_buf, &raw_size, buf, len);
|
||||
if (r != Z_OK) {
|
||||
WARNING("BanMisc.LoadStruct uncompress failed: %d", r);
|
||||
if (!zlib_decompress_exact(raw_buf, raw_size, buf, len)) {
|
||||
WARNING("BanMisc.LoadStruct uncompress failed");
|
||||
free(raw_buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+24
-5
@@ -14,9 +14,9 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "system4/cg.h"
|
||||
#include "system4/string.h"
|
||||
#include "asset_manager.h"
|
||||
#include "xsystem4.h"
|
||||
#include "hll.h"
|
||||
|
||||
static bool CGManager_Init(void *imain_system, int cg_cache_size)
|
||||
@@ -26,10 +26,27 @@ static bool CGManager_Init(void *imain_system, int cg_cache_size)
|
||||
|
||||
static bool CGManager_LoadArchive(struct string *archive_name)
|
||||
{
|
||||
char *name = gamedir_path(archive_name->text);
|
||||
bool r = asset_manager_load_archive(ASSET_CG, name);
|
||||
free(name);
|
||||
return r;
|
||||
return asset_manager_load_archive(ASSET_CG, archive_name->text);
|
||||
}
|
||||
|
||||
static bool CGManager_IsExist(struct string *cg_name)
|
||||
{
|
||||
if (!cg_name)
|
||||
return false;
|
||||
return asset_exists_by_name(ASSET_CG, cg_name->text, NULL);
|
||||
}
|
||||
|
||||
static bool CGManager_GetInfo(struct string *cg_name, int *width, int *height, int *bpp, bool *exist_pixel, bool *exist_alpha)
|
||||
{
|
||||
struct cg_metrics metrics;
|
||||
if (!cg_name || !asset_cg_get_metrics_by_name(cg_name->text, &metrics))
|
||||
return false;
|
||||
*width = metrics.w;
|
||||
*height = metrics.h;
|
||||
*bpp = metrics.bpp;
|
||||
*exist_pixel = metrics.has_pixel;
|
||||
*exist_alpha = metrics.has_alpha;
|
||||
return true;
|
||||
}
|
||||
|
||||
//static int CGManager_GetCountOfDataFromArchive(void);
|
||||
@@ -43,6 +60,8 @@ static bool CGManager_LoadArchive(struct string *archive_name)
|
||||
HLL_LIBRARY(CGManager,
|
||||
HLL_EXPORT(Init, CGManager_Init),
|
||||
HLL_EXPORT(LoadArchive, CGManager_LoadArchive),
|
||||
HLL_EXPORT(IsExist, CGManager_IsExist),
|
||||
HLL_EXPORT(GetInfo, CGManager_GetInfo),
|
||||
HLL_TODO_EXPORT(GetCountOfDataFromArchive, CGManager_GetCountOfDataFromArchive),
|
||||
HLL_TODO_EXPORT(GetTitleByIndexFromArchive, CGManager_GetTitleByIndexFromArchive),
|
||||
HLL_TODO_EXPORT(SearchTitleFromArchive, CGManager_SearchTitleFromArchive),
|
||||
|
||||
@@ -176,7 +176,7 @@ static void charsprite_render(struct charsprite *cs)
|
||||
ch[2] = 0;
|
||||
}
|
||||
|
||||
int w = ceilf(text_style_width(&cs->ts, ch));
|
||||
int w = ceilf(gfx_size_char(&cs->ts, ch));
|
||||
int h = cs->ts.size + cs->ts.size/2;
|
||||
sprite_init_color(&cs->sp, w, h, 0, 0, 0, 0);
|
||||
sprite_get_texture(&cs->sp); // XXX: force initialization of texture
|
||||
|
||||
@@ -111,8 +111,8 @@ HLL_WARN_UNIMPLEMENTED(0, int, ChipmunkSpriteEngine, SP_SetBrightness, int sp_n
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, ChipmunkSpriteEngine, SP_GetBrightness, int sp_no);
|
||||
|
||||
static bool keep_previous_view = true;
|
||||
HLL_WARN_UNIMPLEMENTED((keep_previous_view = on, true), bool, ChipmunkSpriteEngine, KeepPreviousView_SetMode, bool on);
|
||||
HLL_WARN_UNIMPLEMENTED(keep_previous_view, bool, ChipmunkSpriteEngine, KeepPreviousView_GetMode);
|
||||
HLL_QUIET_UNIMPLEMENTED((keep_previous_view = on, true), bool, ChipmunkSpriteEngine, KeepPreviousView_SetMode, bool on);
|
||||
HLL_QUIET_UNIMPLEMENTED(keep_previous_view, bool, ChipmunkSpriteEngine, KeepPreviousView_GetMode);
|
||||
HLL_QUIET_UNIMPLEMENTED(false, bool, ChipmunkSpriteEngine, KeepPreviousView);
|
||||
|
||||
static void ChipmunkSpriteEngine_Sleep(void)
|
||||
@@ -250,7 +250,7 @@ static bool ChipmunkSpriteEngine_SP_GetFontWidth(struct string *text, int *width
|
||||
return true;
|
||||
}
|
||||
|
||||
*width = gfx_size_text(&text_sprite_ts, text->text);
|
||||
*width = ceilf(gfx_size_text(&text_sprite_ts, text->text));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_SetMultipleColor, ChipmunkSpriteEngine_SP_SetMultipleColor),
|
||||
HLL_EXPORT(SP_SetAddColor, ChipmunkSpriteEngine_SP_SetAddColor),
|
||||
HLL_EXPORT(SP_SetShow, sact_SP_SetShow),
|
||||
HLL_EXPORT(SP_SetDrawMethod, sact_SP_SetDrawMethod),
|
||||
HLL_EXPORT(SP_SetDrawMethod, StoatSpriteEngine_SP_SetDrawMethod),
|
||||
HLL_EXPORT(SP_SetSurfaceArea, ChipmunkSpriteEngine_SP_SetSurfaceArea),
|
||||
HLL_EXPORT(SP_GetSurfaceArea, ChipmunkSpriteEngine_SP_GetSurfaceArea),
|
||||
HLL_EXPORT(SP_SetSpriteTransformMode, ChipmunkSpriteEngine_SP_SetSpriteTransformMode),
|
||||
@@ -330,7 +330,7 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_GetZ, sact_SP_GetZ),
|
||||
HLL_EXPORT(SP_GetBlendRate, sact_SP_GetBlendRate),
|
||||
HLL_EXPORT(SP_GetShow, sact_SP_GetShow),
|
||||
HLL_EXPORT(SP_GetDrawMethod, sact_SP_GetDrawMethod),
|
||||
HLL_EXPORT(SP_GetDrawMethod, StoatSpriteEngine_SP_GetDrawMethod),
|
||||
HLL_EXPORT(SP_RenderView, ChipmunkSpriteEngine_SP_RenderView),
|
||||
HLL_EXPORT(SP_IsPtIn, sact_SP_IsPtIn),
|
||||
HLL_EXPORT(SP_IsPtInRect, sact_SP_IsPtInRect),
|
||||
|
||||
+1
-1
@@ -288,7 +288,7 @@ static bool ChrLoader_LoadWavFile(int sound_channel, int id)
|
||||
{
|
||||
int blob = chr_loader_get_blob_handle(id, 4);
|
||||
struct archive_data *dfile = chr_loader_get_blob_as_archive_data(blob);
|
||||
if (!dfile || !wav_prepare_from_archive_data(sound_channel, dfile)) {
|
||||
if (!dfile || !wav_prepare_from_archive_data(sound_channel, AUDIO_NO_METADATA, dfile)) {
|
||||
WARNING("ChrLoader.LoadWavFile %d failed", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
+5
-6
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4/archive.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "asset_manager.h"
|
||||
@@ -40,11 +39,11 @@ static int Data_Open(int num)
|
||||
struct archive_data *dfile = asset_get(ASSET_DATA, num);
|
||||
if (!dfile)
|
||||
return 0;
|
||||
unsigned long uncompressed_size = LittleEndian_getDW(dfile->data, 0);
|
||||
size_t uncompressed_size = LittleEndian_getDW(dfile->data, 0);
|
||||
uint8_t *buf = xmalloc(uncompressed_size);
|
||||
int rv = uncompress(buf, &uncompressed_size, (uint8_t *)dfile->data + 4, dfile->size - 4);
|
||||
if (rv != Z_OK) {
|
||||
WARNING("%s: uncompress failed (%d)", dfile->name, rv);
|
||||
if (!zlib_decompress_exact(buf, uncompressed_size,
|
||||
(uint8_t *)dfile->data + 4, dfile->size - 4)) {
|
||||
WARNING("%s: uncompress failed", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
free(buf);
|
||||
return 0;
|
||||
|
||||
+5
-6
@@ -16,12 +16,11 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4/archive.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/mt19937int.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "asset_manager.h"
|
||||
@@ -267,7 +266,7 @@ static int DataFile_Open(int link)
|
||||
}
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
unsigned long uncompressed_size = buffer_read_int32(&r);
|
||||
size_t uncompressed_size = buffer_read_int32(&r);
|
||||
uint32_t compressed_size = buffer_remaining(&r);
|
||||
|
||||
// decode compressed data
|
||||
@@ -277,10 +276,10 @@ static int DataFile_Open(int link)
|
||||
mt19937_xorcode(compressed_data, compressed_size, 4588163);
|
||||
|
||||
uint8_t *raw = xmalloc(uncompressed_size);
|
||||
int rv = uncompress(raw, &uncompressed_size, compressed_data, compressed_size);
|
||||
bool ok = zlib_decompress_exact(raw, uncompressed_size, compressed_data, compressed_size);
|
||||
free(compressed_data);
|
||||
if (rv != Z_OK) {
|
||||
WARNING("Uncompress failed: %d", rv);
|
||||
if (!ok) {
|
||||
WARNING("Uncompress failed");
|
||||
free(raw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (C) 2026 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* 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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
|
||||
static void Discord_Update(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void Discord_UpdateActivity(struct string **pIText, struct string **pIText2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HLL_LIBRARY(Discord,
|
||||
HLL_EXPORT(Update, Discord_Update),
|
||||
HLL_EXPORT(UpdateActivity, Discord_UpdateActivity)
|
||||
);
|
||||
+9
-10
@@ -20,13 +20,12 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "savedata.h"
|
||||
@@ -180,12 +179,13 @@ static int File_Close(void)
|
||||
int r = 1;
|
||||
if (current_mode == FILE_WRITE) {
|
||||
size_t raw_size = contents.index;
|
||||
unsigned long compressed_size = compressBound(raw_size);
|
||||
size_t compressed_size = zlib_compress_bound(raw_size);
|
||||
uint8_t *buf = xmalloc(4 + compressed_size);
|
||||
LittleEndian_putDW(buf, 0, raw_size);
|
||||
int r = compress2(buf + 4, &compressed_size, contents.buf, raw_size, Z_DEFAULT_COMPRESSION);
|
||||
if (r != Z_OK) {
|
||||
WARNING("File.Close: compress2 failed");
|
||||
compressed_size = zlib_compress(buf + 4, compressed_size, contents.buf,
|
||||
raw_size, ZLIB_DEFAULT_COMPRESSION);
|
||||
if (!compressed_size) {
|
||||
WARNING("File.Close: zlib_compress failed");
|
||||
r = 0;
|
||||
} else {
|
||||
if (fwrite(buf, 4 + compressed_size, 1, current_file) != 1) {
|
||||
@@ -238,11 +238,11 @@ static int File_Read(struct page **_page)
|
||||
|
||||
// Check if it's ZLIB compressed
|
||||
if (file_size > 6 && buf[4] == 0x78 && buf[5] == 0x9c) {
|
||||
unsigned long raw_size = LittleEndian_getDW(buf, 0);
|
||||
size_t raw_size = LittleEndian_getDW(buf, 0);
|
||||
uint8_t *raw_buf = xmalloc(raw_size);
|
||||
int r = uncompress(raw_buf, &raw_size, buf + 4, file_size);
|
||||
bool ok = zlib_decompress_exact(raw_buf, raw_size, buf + 4, file_size - 4);
|
||||
free(buf);
|
||||
if (r != Z_OK) {
|
||||
if (!ok) {
|
||||
WARNING("File.Read failed to uncompress");
|
||||
free(raw_buf);
|
||||
return 0;
|
||||
@@ -368,4 +368,3 @@ HLL_LIBRARY(File,
|
||||
//HLL_EXPORT(SetFind, File_SetFind),
|
||||
//HLL_EXPORT(GetFind, File_GetFind),
|
||||
HLL_EXPORT(MakeDirectory, File_MakeDirectory));
|
||||
|
||||
|
||||
+8
-8
@@ -897,12 +897,12 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_EXPORT(Parts_AddFillToPartsConstructionProcess, PE_AddFillToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillAlphaColorToPartsConstructionProcess, PE_AddFillAlphaColorToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillAMapToPartsConstructionProcess, PE_AddFillAMapToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddFillWithAlphaToPartsConstructionProcess, GUIEngine_Parts_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillWithAlphaToPartsConstructionProcess, PE_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddFillGradationHorizonToPartsConstructionProcess, GUIEngine_Parts_AddFillGradationHorizonToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddDrawRectToPartsConstructionProcess, PE_AddDrawRectToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddDrawCutCGToPartsConstructionProcess, PE_AddDrawCutCGToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCopyCutCGToPartsConstructionProcess, PE_AddCopyCutCGToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddGrayFilterToPartsConstructionProcess, GUIEngine_Parts_AddGrayFilterToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddGrayFilterToPartsConstructionProcess, PE_AddGrayFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddAddFilterToPartsConstructionProcess, GUIEngine_Parts_AddAddFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddMulFilterToPartsConstructionProcess, GUIEngine_Parts_AddMulFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddDrawLineToPartsConstructionProcess, GUIEngine_Parts_AddDrawLineToPartsConstructionProcess),
|
||||
@@ -910,15 +910,15 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_EXPORT(Parts_AddDrawTextToPartsConstructionProcess, PE_AddDrawTextToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCopyTextToPartsConstructionProcess, PE_AddCopyTextToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_SetPartsConstructionSurfaceArea, PE_SetPartsConstructionSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_CreateParts3DLayerPluginID, GUIEngine_Parts_CreateParts3DLayerPluginID),
|
||||
HLL_TODO_EXPORT(Parts_GetParts3DLayerPluginID, GUIEngine_Parts_GetParts3DLayerPluginID),
|
||||
HLL_TODO_EXPORT(Parts_ReleaseParts3DLayerPluginID, GUIEngine_Parts_ReleaseParts3DLayerPluginID),
|
||||
HLL_EXPORT(Parts_CreateParts3DLayerPluginID, PE_CreateParts3DLayerPluginID),
|
||||
HLL_EXPORT(Parts_GetParts3DLayerPluginID, PE_GetParts3DLayerPluginID),
|
||||
HLL_EXPORT(Parts_ReleaseParts3DLayerPluginID, PE_ReleaseParts3DLayerPluginID),
|
||||
HLL_EXPORT(Parts_SetPos, PE_SetPos),
|
||||
HLL_EXPORT(Parts_SetZ, PE_SetZ),
|
||||
HLL_EXPORT(Parts_SetShow, PE_SetShow),
|
||||
HLL_EXPORT(Parts_SetAlpha, PE_SetAlpha),
|
||||
HLL_EXPORT(Parts_SetPartsDrawFilter, PE_SetPartsDrawFilter),
|
||||
HLL_TODO_EXPORT(Parts_SetPassCursor, GUIEngine_Parts_SetPassCursor),
|
||||
HLL_EXPORT(Parts_SetPassCursor, PE_SetPassCursor),
|
||||
HLL_EXPORT(Parts_SetClickable, PE_SetClickable),
|
||||
HLL_EXPORT(Parts_SetSpeedupRateByMessageSkip, PE_SetSpeedupRateByMessageSkip),
|
||||
HLL_TODO_EXPORT(Parts_SetResetTimerByChangeInputStatus, GUIEngine_Parts_SetResetTimerByChangeInputStatus),
|
||||
@@ -937,7 +937,7 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_EXPORT(Parts_SetPartsRotateX, PE_SetPartsRotateX),
|
||||
HLL_EXPORT(Parts_SetPartsRotateY, PE_SetPartsRotateY),
|
||||
HLL_EXPORT(Parts_SetPartsRotateZ, PE_SetPartsRotateZ),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsAlphaClipperPartsNumber, GUIEngine_Parts_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_EXPORT(Parts_SetPartsAlphaClipperPartsNumber, PE_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_EXPORT(Parts_SetPartsPixelDecide, PE_SetPartsPixelDecide),
|
||||
HLL_EXPORT(Parts_GetPartsX, PE_GetPartsX),
|
||||
HLL_EXPORT(Parts_GetPartsY, PE_GetPartsY),
|
||||
@@ -945,7 +945,7 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_EXPORT(Parts_GetPartsShow, PE_GetPartsShow),
|
||||
HLL_EXPORT(Parts_GetPartsAlpha, PE_GetPartsAlpha),
|
||||
HLL_EXPORT(Parts_GetPartsDrawFilter, PE_GetPartsDrawFilter),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsPassCursor, GUIEngine_Parts_GetPartsPassCursor),
|
||||
HLL_EXPORT(Parts_GetPartsPassCursor, PE_GetPartsPassCursor),
|
||||
HLL_EXPORT(Parts_GetPartsClickable, PE_GetPartsClickable),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsSpeedupRateByMessageSkip, GUIEngine_Parts_GetPartsSpeedupRateByMessageSkip),
|
||||
HLL_TODO_EXPORT(Parts_GetResetTimerByChangeInputStatus, GUIEngine_Parts_GetResetTimerByChangeInputStatus),
|
||||
|
||||
@@ -171,7 +171,7 @@ HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_EXPORT(SetPartsRotateX, GoatGUIEngine_SetPartsRotateX),
|
||||
HLL_EXPORT(SetPartsRotateY, GoatGUIEngine_SetPartsRotateY),
|
||||
HLL_EXPORT(SetPartsRotateZ, GoatGUIEngine_SetPartsRotateZ),
|
||||
HLL_TODO_EXPORT(SetPartsAlphaClipperPartsNumber, PE_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_EXPORT(SetPartsAlphaClipperPartsNumber, PE_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_EXPORT(SetPartsPixelDecide, PE_SetPartsPixelDecide),
|
||||
HLL_EXPORT(SetThumbnailReductionSize, PE_SetThumbnailReductionSize),
|
||||
HLL_EXPORT(SetThumbnailMode, PE_SetThumbnailMode),
|
||||
@@ -189,7 +189,7 @@ HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_EXPORT(AddFillToPartsConstructionProcess, PE_AddFillToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddFillAlphaColorToPartsConstructionProcess, PE_AddFillAlphaColorToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddFillAMapToPartsConstructionProcess, PE_AddFillAMapToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddFillWithAlphaToPartsConstructionProcess, PE_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddFillWithAlphaToPartsConstructionProcess, PE_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddFillGradationHorizonToPartsConstructionProcess, PE_AddFillGradationHorizonToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddDrawRectToPartsConstructionProcess, PE_AddDrawRectToPartsConstructionProcess),
|
||||
HLL_EXPORT(BuildPartsConstructionProcess, PE_BuildPartsConstructionProcess),
|
||||
|
||||
+35
-2
@@ -17,9 +17,11 @@
|
||||
#include <SDL.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/utfsjis.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "vm.h"
|
||||
#include "gfx/types.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "hll.h"
|
||||
@@ -29,6 +31,7 @@ static int font_size;
|
||||
static int font_weight;
|
||||
static Point pos;
|
||||
static bool has_editing_text;
|
||||
static bool get_result_string_clears;
|
||||
|
||||
static struct string *result;
|
||||
|
||||
@@ -39,13 +42,21 @@ static void InputString_ClearResultString(void)
|
||||
result = string_ref(&EMPTY_STRING);
|
||||
}
|
||||
|
||||
struct string *InputString_GetResultString(void)
|
||||
static struct string *InputString_GetResultString(void)
|
||||
{
|
||||
struct string *s = string_ref(result);
|
||||
InputString_ClearResultString();
|
||||
if (get_result_string_clears)
|
||||
InputString_ClearResultString();
|
||||
return s;
|
||||
}
|
||||
|
||||
static void InputString_SetResultString(struct string *s)
|
||||
{
|
||||
if (result)
|
||||
free_string(result);
|
||||
result = string_ref(s);
|
||||
}
|
||||
|
||||
static void InputString_SetFont(int nSize, possibly_unused struct string *pIName, int nWeight)
|
||||
{
|
||||
font_size = nSize;
|
||||
@@ -84,10 +95,17 @@ static void handle_editing(const char *text, int start, int length)
|
||||
has_editing_text = *text != '\0';
|
||||
}
|
||||
|
||||
static void handle_key(int code)
|
||||
{
|
||||
if (code == VK_BACK && !has_editing_text && result && result->size > 0)
|
||||
string_pop_back(&result);
|
||||
}
|
||||
|
||||
static void InputString_OpenIME(void)
|
||||
{
|
||||
register_input_handler(handle_input);
|
||||
register_editing_handler(handle_editing);
|
||||
register_key_handler(handle_key);
|
||||
has_editing_text = false;
|
||||
SDL_StartTextInput();
|
||||
}
|
||||
@@ -98,6 +116,7 @@ static void InputString_CloseIME(void)
|
||||
has_editing_text = false;
|
||||
clear_input_handler();
|
||||
clear_editing_handler();
|
||||
clear_key_handler();
|
||||
}
|
||||
|
||||
static bool InputString_Inputs(void)
|
||||
@@ -110,7 +129,20 @@ static bool InputString_Converts(void)
|
||||
return has_editing_text;
|
||||
}
|
||||
|
||||
static void InputString_PreLink(void)
|
||||
{
|
||||
get_result_string_clears = true;
|
||||
int libno = ain_get_library(ain, "InputString");
|
||||
if (libno < 0)
|
||||
return;
|
||||
if (ain_get_library_function(ain, libno, "SetResultString") >= 0 ||
|
||||
ain_get_library_function(ain, libno, "Inputs") >= 0) {
|
||||
get_result_string_clears = false;
|
||||
}
|
||||
}
|
||||
|
||||
HLL_LIBRARY(InputString,
|
||||
HLL_EXPORT(_PreLink, InputString_PreLink),
|
||||
HLL_EXPORT(SetFont, InputString_SetFont),
|
||||
HLL_EXPORT(SetPos, InputString_SetPos),
|
||||
HLL_EXPORT(Begin, InputString_Begin),
|
||||
@@ -119,5 +151,6 @@ HLL_LIBRARY(InputString,
|
||||
HLL_EXPORT(CloseIME, InputString_CloseIME),
|
||||
HLL_EXPORT(GetResultString, InputString_GetResultString),
|
||||
HLL_EXPORT(ClearResultString, InputString_ClearResultString),
|
||||
HLL_EXPORT(SetResultString, InputString_SetResultString),
|
||||
HLL_EXPORT(Inputs, InputString_Inputs),
|
||||
HLL_EXPORT(Converts, InputString_Converts));
|
||||
|
||||
@@ -14,16 +14,20 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "system4.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
|
||||
#include "vm.h"
|
||||
#include "hll.h"
|
||||
#include "asset_manager.h"
|
||||
#include "audio.h"
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, KiwiSoundEngine, SetGlobalFocus, possibly_unused int nNum);
|
||||
|
||||
//int KiwiSoundEngine_Sound_GetGroupNum(int nCh);
|
||||
//static bool KiwiSoundEngine_Sound_PrepareFromFile(int ch, struct string *filename);
|
||||
//static int KiwiSoundEngine_Sound_GetDataLength(int data_number);
|
||||
|
||||
static int KiwiSoundEngine_GetMasterGroup(void)
|
||||
{
|
||||
@@ -55,7 +59,44 @@ static int KiwiSoundEngine_GetBackVoiceGroup(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool rance9_bgm_exists(int num)
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "%05d", num);
|
||||
return asset_exists_by_name(ASSET_BGM, name, NULL);
|
||||
}
|
||||
|
||||
static int rance9_bgm_prepare(int ch, int num)
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "%05d", num);
|
||||
struct archive_data *dfile = asset_get_by_name(ASSET_BGM, name, NULL);
|
||||
if (!dfile)
|
||||
return 0;
|
||||
return bgm_prepare_from_archive_data(ch, num, dfile);
|
||||
}
|
||||
|
||||
static bool rance9_wav_exists(int num)
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "%05d", num);
|
||||
return asset_exists_by_name(ASSET_SOUND, name, NULL);
|
||||
}
|
||||
|
||||
static int rance9_wav_prepare(int ch, int num)
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "%05d", num);
|
||||
struct archive_data *dfile = asset_get_by_name(ASSET_SOUND, name, NULL);
|
||||
if (!dfile)
|
||||
return 0;
|
||||
return wav_prepare_from_archive_data(ch, num, dfile);
|
||||
}
|
||||
|
||||
static void KiwiSoundEngine_PreLink(void);
|
||||
|
||||
HLL_LIBRARY(KiwiSoundEngine,
|
||||
HLL_EXPORT(_PreLink, KiwiSoundEngine_PreLink),
|
||||
HLL_EXPORT(SetGlobalFocus, KiwiSoundEngine_SetGlobalFocus),
|
||||
HLL_EXPORT(Music_IsExist, bgm_exists),
|
||||
HLL_EXPORT(Music_Prepare, bgm_prepare),
|
||||
@@ -93,7 +134,7 @@ HLL_LIBRARY(KiwiSoundEngine,
|
||||
HLL_TODO_EXPORT(Sound_GetGroupNum, KiwiSoundEngine_Sound_GetGroupNum),
|
||||
HLL_EXPORT(Sound_GetGroupNumFromDataNum, wav_get_group_num_from_data_num),
|
||||
HLL_TODO_EXPORT(Sound_PrepareFromFile, KiwiSoundEngine_Sound_PrepareFromFile),
|
||||
HLL_TODO_EXPORT(Sound_GetDataLength, KiwiSoundEngine_Sound_GetDataLength),
|
||||
HLL_EXPORT(Sound_GetDataLength, wav_get_data_length),
|
||||
HLL_EXPORT(GetMasterGroup, KiwiSoundEngine_GetMasterGroup),
|
||||
HLL_EXPORT(GetBGMGroup, KiwiSoundEngine_GetBGMGroup),
|
||||
HLL_EXPORT(GetSEGroup, KiwiSoundEngine_GetSEGroup),
|
||||
@@ -101,3 +142,16 @@ HLL_LIBRARY(KiwiSoundEngine,
|
||||
HLL_EXPORT(GetGimicSEGroup, KiwiSoundEngine_GetGimicSEGroup),
|
||||
HLL_EXPORT(GetBackVoiceGroup, KiwiSoundEngine_GetBackVoiceGroup)
|
||||
);
|
||||
|
||||
static void KiwiSoundEngine_PreLink(void)
|
||||
{
|
||||
int libno = ain_get_library(ain, "KiwiSoundEngine");
|
||||
if (libno < 0)
|
||||
return;
|
||||
if (ain_get_library_function(ain, libno, "Sound_Seek") >= 0) {
|
||||
static_library_replace(&lib_KiwiSoundEngine, "Music_IsExist", rance9_bgm_exists);
|
||||
static_library_replace(&lib_KiwiSoundEngine, "Music_Prepare", rance9_bgm_prepare);
|
||||
static_library_replace(&lib_KiwiSoundEngine, "Sound_IsExist", rance9_wav_exists);
|
||||
static_library_replace(&lib_KiwiSoundEngine, "Sound_Prepare", rance9_wav_prepare);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/cg.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "id_pool.h"
|
||||
#include "xsystem4.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "sact.h"
|
||||
|
||||
// A MAD file consists of this header, followed by `nr_images` QNT images, each
|
||||
// prefixed with its size.
|
||||
struct MADHeader {
|
||||
char magic[4]; // "MAD\0"
|
||||
unsigned char unknown1[8];
|
||||
int width;
|
||||
int height;
|
||||
int total_time;
|
||||
int nr_images;
|
||||
int draw_method;
|
||||
int is_loop;
|
||||
unsigned char unknown2[8];
|
||||
int keep_last_frame;
|
||||
};
|
||||
|
||||
struct MAD {
|
||||
struct MADHeader header;
|
||||
Texture textures[];
|
||||
};
|
||||
|
||||
static struct id_pool pool;
|
||||
|
||||
static void MADLoader_ModuleInit(void)
|
||||
{
|
||||
id_pool_init(&pool, 0);
|
||||
}
|
||||
|
||||
static void MADLoader_Close(int handle)
|
||||
{
|
||||
struct MAD *mad = id_pool_release(&pool, handle);
|
||||
if (!mad)
|
||||
return;
|
||||
for (int i = 0; i < mad->header.nr_images; i++) {
|
||||
if (mad->textures[i].handle)
|
||||
gfx_delete_texture(&mad->textures[i]);
|
||||
}
|
||||
free(mad);
|
||||
}
|
||||
|
||||
static void MADLoader_Delete(void)
|
||||
{
|
||||
for (int i = id_pool_get_first(&pool); i >= 0; i = id_pool_get_next(&pool, i))
|
||||
MADLoader_Close(i);
|
||||
}
|
||||
|
||||
static void MADLoader_ModuleFini(void)
|
||||
{
|
||||
MADLoader_Delete();
|
||||
id_pool_delete(&pool);
|
||||
}
|
||||
|
||||
static FILE *mad_open(struct string *filename, struct MADHeader *header)
|
||||
{
|
||||
char *path = gamedir_path(filename->text);
|
||||
FILE *fp = file_open_utf8(path, "rb");
|
||||
free(path);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
uint8_t buf[48];
|
||||
if (fread(buf, sizeof buf, 1, fp) != 1 || memcmp(buf, "MAD", 4)) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(header->magic, buf, 4);
|
||||
memcpy(header->unknown1, buf + 4, 8);
|
||||
header->width = LittleEndian_getDW(buf, 12);
|
||||
header->height = LittleEndian_getDW(buf, 16);
|
||||
header->total_time = LittleEndian_getDW(buf, 20);
|
||||
header->nr_images = LittleEndian_getDW(buf, 24);
|
||||
header->draw_method = LittleEndian_getDW(buf, 28);
|
||||
header->is_loop = LittleEndian_getDW(buf, 32);
|
||||
memcpy(header->unknown2, buf + 36, 8);
|
||||
header->keep_last_frame = LittleEndian_getDW(buf, 44);
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
static bool mad_read_header(struct string *filename, struct MADHeader *header)
|
||||
{
|
||||
FILE *fp = mad_open(filename, header);
|
||||
fclose(fp);
|
||||
return fp != NULL;
|
||||
}
|
||||
|
||||
static int MADLoader_Open(struct string *filename)
|
||||
{
|
||||
struct MADHeader header;
|
||||
FILE *fp = mad_open(filename, &header);
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
struct MAD *mad = xcalloc(1, sizeof(struct MAD) + header.nr_images * sizeof(Texture));
|
||||
mad->header = header;
|
||||
for (int i = 0; i < mad->header.nr_images; i++) {
|
||||
uint8_t buf[4];
|
||||
if (fread(buf, 4, 1, fp) != 1)
|
||||
VM_ERROR("MADLoader: failed to read image size %d of %s", i, display_sjis0(filename->text));
|
||||
uint32_t image_size = LittleEndian_getDW(buf, 0);
|
||||
uint8_t *image_data = xmalloc(image_size);
|
||||
if (fread(image_data, image_size, 1, fp) != 1)
|
||||
VM_ERROR("MADLoader: failed to read image data %d of %s", i, display_sjis0(filename->text));
|
||||
struct cg *cg = cg_load_buffer(image_data, image_size);
|
||||
if (!cg)
|
||||
VM_ERROR("MADLoader: failed to load cg %d of %s", i, display_sjis0(filename->text));
|
||||
gfx_init_texture_with_cg(&mad->textures[i], cg);
|
||||
cg_free(cg);
|
||||
free(image_data);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
int handle = id_pool_get_unused(&pool);
|
||||
id_pool_set(&pool, handle, mad);
|
||||
return handle;
|
||||
}
|
||||
|
||||
static bool MADLoader_GetSize(int handle, int *width, int *height)
|
||||
{
|
||||
struct MAD *mad = id_pool_get(&pool, handle);
|
||||
if (!mad)
|
||||
return false;
|
||||
*width = mad->header.width;
|
||||
*height = mad->header.height;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int MADLoader_GetNumof(int handle)
|
||||
{
|
||||
struct MAD *mad = id_pool_get(&pool, handle);
|
||||
return mad ? mad->header.nr_images : -1;
|
||||
}
|
||||
|
||||
static int MADLoader_GetTotalTime(int handle)
|
||||
{
|
||||
struct MAD *mad = id_pool_get(&pool, handle);
|
||||
return mad ? mad->header.total_time : -1;
|
||||
}
|
||||
|
||||
static int MADLoader_GetDrawMethod(int handle)
|
||||
{
|
||||
struct MAD *mad = id_pool_get(&pool, handle);
|
||||
return mad ? mad->header.draw_method : -1;
|
||||
}
|
||||
|
||||
static bool MADLoader_IsLoop(int handle)
|
||||
{
|
||||
struct MAD *mad = id_pool_get(&pool, handle);
|
||||
return mad && mad->header.is_loop == 1;
|
||||
}
|
||||
|
||||
static bool MADLoader_IsKeepLastFrame(int handle)
|
||||
{
|
||||
struct MAD *mad = id_pool_get(&pool, handle);
|
||||
return mad && mad->header.keep_last_frame == 1;
|
||||
}
|
||||
|
||||
static bool MADLoader_IsLoading(int handle)
|
||||
{
|
||||
// We don't support background loading.
|
||||
return false;
|
||||
}
|
||||
|
||||
static int MADLoader_GetLoadNumof(int handle)
|
||||
{
|
||||
struct MAD *mad = id_pool_get(&pool, handle);
|
||||
return mad ? mad->header.nr_images : 0;
|
||||
}
|
||||
|
||||
static bool MADLoader_GetSizeFromFile(struct string *filename, int *width, int *height)
|
||||
{
|
||||
struct MADHeader header;
|
||||
if (!mad_read_header(filename, &header))
|
||||
return false;
|
||||
*width = header.width;
|
||||
*height = header.height;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int MADLoader_GetNumofFromFile(struct string *filename)
|
||||
{
|
||||
struct MADHeader header;
|
||||
if (!mad_read_header(filename, &header))
|
||||
return -1;
|
||||
return header.nr_images;
|
||||
}
|
||||
|
||||
static int MADLoader_GetTotalTimeFromFile(struct string *filename)
|
||||
{
|
||||
struct MADHeader header;
|
||||
if (!mad_read_header(filename, &header))
|
||||
return -1;
|
||||
return header.total_time;
|
||||
}
|
||||
|
||||
static int MADLoader_GetDrawMethodFromFile(struct string *filename)
|
||||
{
|
||||
struct MADHeader header;
|
||||
if (!mad_read_header(filename, &header))
|
||||
return -1;
|
||||
return header.draw_method;
|
||||
}
|
||||
|
||||
static bool MADLoader_IsLoopFromFile(struct string *filename)
|
||||
{
|
||||
struct MADHeader header;
|
||||
if (!mad_read_header(filename, &header))
|
||||
return false;
|
||||
return header.is_loop == 1;
|
||||
}
|
||||
|
||||
static bool MADLoader_IsKeepLastFrameFromFile(struct string *filename)
|
||||
{
|
||||
struct MADHeader header;
|
||||
if (!mad_read_header(filename, &header))
|
||||
return false;
|
||||
return header.keep_last_frame == 1;
|
||||
}
|
||||
|
||||
static Texture *get_mad_texture(int handle, int index)
|
||||
{
|
||||
struct MAD *mad = id_pool_get(&pool, handle);
|
||||
if (!mad || index < 0 || index >= mad->header.nr_images)
|
||||
return NULL;
|
||||
return &mad->textures[index];
|
||||
}
|
||||
|
||||
static Texture *get_dest_texture(int sp_no)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp)
|
||||
return NULL;
|
||||
sprite_dirty(sp);
|
||||
return sprite_get_texture(sp);
|
||||
}
|
||||
|
||||
static bool MADLoader_Copy(int handle, int sprite, int index)
|
||||
{
|
||||
Texture *src = get_mad_texture(handle, index);
|
||||
Texture *dst = get_dest_texture(sprite);
|
||||
if (!src || !dst)
|
||||
return false;
|
||||
|
||||
gfx_copy_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MADLoader_CopyReverseLR(int handle, int sprite, int index)
|
||||
{
|
||||
Texture *src = get_mad_texture(handle, index);
|
||||
Texture *dst = get_dest_texture(sprite);
|
||||
if (!src || !dst)
|
||||
return false;
|
||||
|
||||
gfx_copy_reverse_LR_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MADLoader_CopyGrayscale(int handle, int sprite, int index)
|
||||
{
|
||||
Texture *src = get_mad_texture(handle, index);
|
||||
Texture *dst = get_dest_texture(sprite);
|
||||
if (!src || !dst)
|
||||
return false;
|
||||
|
||||
gfx_copy_grayscale_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MADLoader_CopyGrayscaleReverseLR(int handle, int sprite, int index)
|
||||
{
|
||||
Texture *src = get_mad_texture(handle, index);
|
||||
Texture *dst = get_dest_texture(sprite);
|
||||
if (!src || !dst)
|
||||
return false;
|
||||
|
||||
gfx_copy_grayscale_reverse_LR_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(MADLoader,
|
||||
HLL_EXPORT(_ModuleInit, MADLoader_ModuleInit),
|
||||
HLL_EXPORT(_ModuleFini, MADLoader_ModuleFini),
|
||||
HLL_EXPORT(Delete, MADLoader_Delete),
|
||||
HLL_EXPORT(Open, MADLoader_Open),
|
||||
HLL_EXPORT(Close, MADLoader_Close),
|
||||
HLL_EXPORT(GetSize, MADLoader_GetSize),
|
||||
HLL_EXPORT(GetNumof, MADLoader_GetNumof),
|
||||
HLL_EXPORT(GetTotalTime, MADLoader_GetTotalTime),
|
||||
HLL_EXPORT(GetDrawMethod, MADLoader_GetDrawMethod),
|
||||
HLL_EXPORT(IsLoop, MADLoader_IsLoop),
|
||||
HLL_EXPORT(IsKeepLastFrame, MADLoader_IsKeepLastFrame),
|
||||
HLL_EXPORT(IsLoading, MADLoader_IsLoading),
|
||||
HLL_EXPORT(GetLoadNumof, MADLoader_GetLoadNumof),
|
||||
HLL_EXPORT(GetSizeFromFile, MADLoader_GetSizeFromFile),
|
||||
HLL_EXPORT(GetNumofFromFile, MADLoader_GetNumofFromFile),
|
||||
HLL_EXPORT(GetTotalTimeFromFile, MADLoader_GetTotalTimeFromFile),
|
||||
HLL_EXPORT(GetDrawMethodFromFile, MADLoader_GetDrawMethodFromFile),
|
||||
HLL_EXPORT(IsLoopFromFile, MADLoader_IsLoopFromFile),
|
||||
HLL_EXPORT(IsKeepLastFrameFromFile, MADLoader_IsKeepLastFrameFromFile),
|
||||
HLL_EXPORT(Copy, MADLoader_Copy),
|
||||
HLL_EXPORT(CopyReverseLR, MADLoader_CopyReverseLR),
|
||||
HLL_EXPORT(CopyGrayscale, MADLoader_CopyGrayscale),
|
||||
HLL_EXPORT(CopyGrayscaleReverseLR, MADLoader_CopyGrayscaleReverseLR)
|
||||
);
|
||||
@@ -51,7 +51,7 @@ static int set_indices(struct ex_value *val, int id)
|
||||
break;
|
||||
case EX_LIST:
|
||||
for (unsigned i = 0; i < val->list->nr_items; i++) {
|
||||
id = set_indices(&val->list->items->value, id);
|
||||
id = set_indices(&val->list->items[i].value, id);
|
||||
}
|
||||
break;
|
||||
case EX_TREE:
|
||||
@@ -86,7 +86,7 @@ static void map_handles(struct ex_value *val)
|
||||
break;
|
||||
case EX_LIST:
|
||||
for (unsigned i = 0; i < val->list->nr_items; i++) {
|
||||
map_handles(&val->list->items->value);
|
||||
map_handles(&val->list->items[i].value);
|
||||
}
|
||||
break;
|
||||
case EX_TREE:
|
||||
@@ -130,7 +130,7 @@ static void MainEXFile_ModuleFini(void)
|
||||
nr_handles = 0;
|
||||
}
|
||||
|
||||
//static bool MainEXFile_ReloadDebugEXFile(void);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, MainEXFile, ReloadDebugEXFile, void);
|
||||
|
||||
/*
|
||||
* Get handle for top-level value.
|
||||
@@ -225,13 +225,16 @@ static int MainEXFile_RA2Handle(int handle, int row, struct string *format_name)
|
||||
static int MainEXFile_Row(int handle)
|
||||
{
|
||||
struct ex_table *t = handle_to_table(handle);
|
||||
return t ? t->nr_rows : 0; // FIXME: should this be nr_columns?
|
||||
return t ? t->nr_rows : 0;
|
||||
}
|
||||
|
||||
static int MainEXFile_Col(int handle)
|
||||
{
|
||||
if (handle > 0 && (unsigned)handle < nr_handles &&
|
||||
handles[handle]->type == EX_LIST)
|
||||
return handles[handle]->list->nr_items;
|
||||
struct ex_table *t = handle_to_table(handle);
|
||||
return t ? t->nr_columns : 0; // FIXME: should this be nr_rows?
|
||||
return t ? t->nr_columns : 0;
|
||||
}
|
||||
|
||||
static int MainEXFile_Type(int handle)
|
||||
@@ -663,7 +666,7 @@ static bool MainEXFile_GetEXName(struct string *tree_path, int index, struct str
|
||||
HLL_LIBRARY(MainEXFile,
|
||||
HLL_EXPORT(_ModuleInit, MainEXFile_ModuleInit),
|
||||
HLL_EXPORT(_ModuleFini, MainEXFile_ModuleFini),
|
||||
HLL_TODO_EXPORT(ReloadDebugEXFile, MainEXFile_ReloadDebugEXFile),
|
||||
HLL_EXPORT(ReloadDebugEXFile, MainEXFile_ReloadDebugEXFile),
|
||||
HLL_EXPORT(Handle, MainEXFile_Handle),
|
||||
HLL_EXPORT(AHandle, MainEXFile_AHandle),
|
||||
HLL_EXPORT(A2Handle, MainEXFile_A2Handle),
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
#include "reign.h"
|
||||
|
||||
static bool MarmotModelEngine_Update(void)
|
||||
{
|
||||
// ???
|
||||
ReignEngine_update_models();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+32
-4
@@ -23,9 +23,23 @@
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
#include "system4/mt19937int.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "vm/page.h"
|
||||
|
||||
static struct mt19937 mt;
|
||||
static bool mt_initialized;
|
||||
|
||||
static uint32_t math_genrand(void)
|
||||
{
|
||||
if (!mt_initialized) {
|
||||
mt19937_init(&mt, 4357);
|
||||
mt_initialized = true;
|
||||
}
|
||||
return mt19937_genrand(&mt);
|
||||
}
|
||||
|
||||
struct shuffle_table {
|
||||
int id;
|
||||
struct shuffle_table *next;
|
||||
@@ -64,9 +78,16 @@ static float Math_Tan(float x)
|
||||
return tanf(deg2rad(x));
|
||||
}
|
||||
|
||||
static void Math_SetSeed(int seed)
|
||||
{
|
||||
mt19937_init(&mt, (uint32_t)seed | 1);
|
||||
mt19937_genrand(&mt); // the original engine discards the first value
|
||||
mt_initialized = true;
|
||||
}
|
||||
|
||||
static void Math_SetSeedByCurrentTime(void)
|
||||
{
|
||||
srand(time(NULL));
|
||||
Math_SetSeed(time(NULL));
|
||||
}
|
||||
|
||||
static int Math_Min(int a, int b)
|
||||
@@ -105,9 +126,14 @@ static void Math_SwapF(float *a, float *b)
|
||||
|
||||
//void Math_SetRandMode(int mode);
|
||||
|
||||
static int Math_Rand(void)
|
||||
{
|
||||
return math_genrand() & 0x7fffffff;
|
||||
}
|
||||
|
||||
static float Math_RandF(void)
|
||||
{
|
||||
return rand() * (1.0 / (RAND_MAX + 1U));
|
||||
return (float)(math_genrand() * (1.0 / 4294967296.0));
|
||||
}
|
||||
|
||||
static void shuffle_array(int *a, int len)
|
||||
@@ -187,15 +213,17 @@ HLL_LIBRARY(Math,
|
||||
HLL_EXPORT(Sin, Math_Sin),
|
||||
HLL_EXPORT(Tan, Math_Tan),
|
||||
HLL_EXPORT(Sqrt, sqrtf),
|
||||
HLL_EXPORT(Asin, asinf),
|
||||
HLL_EXPORT(Acos, acosf),
|
||||
HLL_EXPORT(Atan, atanf),
|
||||
HLL_EXPORT(Atan2, atan2f),
|
||||
HLL_EXPORT(Abs, abs),
|
||||
HLL_EXPORT(AbsF, fabsf),
|
||||
HLL_EXPORT(Pow, powf),
|
||||
HLL_EXPORT(SetSeed, srand),
|
||||
HLL_EXPORT(SetSeed, Math_SetSeed),
|
||||
HLL_EXPORT(SetSeedByCurrentTime, Math_SetSeedByCurrentTime),
|
||||
//HLL_EXPORT(SetRandMode, Math_SetRandMode),
|
||||
HLL_EXPORT(Rand, rand),
|
||||
HLL_EXPORT(Rand, Math_Rand),
|
||||
HLL_EXPORT(RandF, Math_RandF),
|
||||
HLL_EXPORT(RandTableInit, Math_RandTableInit),
|
||||
HLL_EXPORT(RandTable, Math_RandTable),
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -149,7 +150,19 @@ HLL_WARN_UNIMPLEMENTED( , void, MsgSkip, UseFlag, int use);
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, MsgSkip, GetNumofMsg, void);
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, MsgSkip, GetNumofFlag, void);
|
||||
|
||||
static void MsgSkip_PreLink(void)
|
||||
{
|
||||
int libno = ain_get_library(ain, "MsgSkip");
|
||||
assert(libno >= 0);
|
||||
if (ain_get_library_function(ain, libno, "Init") < 0) {
|
||||
struct string *name = cstr_to_string("MsgSkip.asd");
|
||||
MsgSkip_Init(name);
|
||||
free_string(name);
|
||||
}
|
||||
}
|
||||
|
||||
HLL_LIBRARY(MsgSkip,
|
||||
HLL_EXPORT(_PreLink, MsgSkip_PreLink),
|
||||
HLL_EXPORT(Init, MsgSkip_Init),
|
||||
HLL_EXPORT(UseFlag, MsgSkip_UseFlag),
|
||||
HLL_EXPORT(SetEnable, MsgSkip_SetEnable),
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "vm/page.h"
|
||||
#include "hll.h"
|
||||
#include "sact.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
static struct {
|
||||
Texture bg;
|
||||
Texture grid;
|
||||
int blend_rate;
|
||||
} p3map;
|
||||
|
||||
// Initializes the grid texture with isometric tiles based on the provided array.
|
||||
static void init_grid_texture(Texture *tile, int offset_x, int offset_y, int rows, int cols, struct page *array)
|
||||
{
|
||||
if (array->nr_vars != rows * cols)
|
||||
VM_ERROR("Expected %d variables in array, got %d", rows * cols, array->nr_vars);
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
if (array->values[row * cols + col].i != 0)
|
||||
continue;
|
||||
// Isometric Coordinates.
|
||||
int x = ((col - row) - 1) * (tile->w / 2) + offset_x;
|
||||
int y = (col + row) * (tile->h / 2) + offset_y;
|
||||
gfx_copy_with_alpha_map(&p3map.grid, x, y, tile, 0, 0, tile->w, tile->h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the map background and grid textures.
|
||||
static bool P3MapSprite_Create(int src_sprite, int square_sprite, int x, int y, int line, int column, int blend_rate, struct page *array)
|
||||
{
|
||||
struct sact_sprite *src = sact_try_get_sprite(src_sprite);
|
||||
struct sact_sprite *square = sact_try_get_sprite(square_sprite);
|
||||
if (!src || !square)
|
||||
return false;
|
||||
Texture *tx_src = sprite_get_texture(src);
|
||||
Texture *tx_square = sprite_get_texture(square);
|
||||
int width = tx_src->w;
|
||||
int height = tx_src->h;
|
||||
|
||||
if (p3map.bg.handle)
|
||||
gfx_delete_texture(&p3map.bg);
|
||||
gfx_init_texture_blank(&p3map.bg, width, height);
|
||||
gfx_copy(&p3map.bg, 0, 0, tx_src, 0, 0, width, height);
|
||||
|
||||
if (p3map.grid.handle)
|
||||
gfx_delete_texture(&p3map.grid);
|
||||
gfx_init_texture_rgba(&p3map.grid, width, height, COLOR(0, 0, 0, 0));
|
||||
init_grid_texture(tx_square, x, y, line, column, array);
|
||||
|
||||
p3map.blend_rate = blend_rate;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deletes the map background and grid textures.
|
||||
static void P3MapSprite_Delete(void)
|
||||
{
|
||||
if (p3map.grid.handle)
|
||||
gfx_delete_texture(&p3map.grid);
|
||||
if (p3map.bg.handle)
|
||||
gfx_delete_texture(&p3map.bg);
|
||||
}
|
||||
|
||||
// Copies the map background and optionally blends the grid to a destination sprite.
|
||||
static bool P3MapSprite_Copy(int dest_sprite, int src_x, int src_y, bool copy_square)
|
||||
{
|
||||
struct sact_sprite *dst = sact_get_sprite(dest_sprite);
|
||||
if (!dst)
|
||||
return false;
|
||||
Texture *tx_dst = sprite_get_texture(dst);
|
||||
int w = sprite_get_width(dst);
|
||||
int h = sprite_get_height(dst);
|
||||
gfx_copy(tx_dst, 0, 0, &p3map.bg, src_x, src_y, w, h);
|
||||
if (copy_square)
|
||||
gfx_blend_amap_alpha(tx_dst, 0, 0, &p3map.grid, src_x, src_y, w, h, p3map.blend_rate);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sets the blend rate for map rendering.
|
||||
static bool P3MapSprite_CreateMatrixPixel(int blend_rate)
|
||||
{
|
||||
p3map.blend_rate = blend_rate;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blends the grid texture onto a destination sprite.
|
||||
static bool P3MapSprite_CopyMatrix(int dest_sprite, int dest_x, int dest_y, int src_x, int src_y, int width, int height)
|
||||
{
|
||||
struct sact_sprite *dst = sact_get_sprite(dest_sprite);
|
||||
if (!dst)
|
||||
return false;
|
||||
Texture *tx_dst = sprite_get_texture(dst);
|
||||
gfx_blend_amap(tx_dst, dest_x, dest_y, &p3map.grid, src_x, src_y, width, height);
|
||||
gfx_copy_amap_max(tx_dst, dest_x, dest_y, &p3map.grid, src_x, src_y, width, height);
|
||||
return true;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(P3MapSprite,
|
||||
HLL_EXPORT(Create, P3MapSprite_Create),
|
||||
HLL_EXPORT(Delete, P3MapSprite_Delete),
|
||||
HLL_EXPORT(Copy, P3MapSprite_Copy),
|
||||
HLL_EXPORT(CreateMatrixPixel, P3MapSprite_CreateMatrixPixel),
|
||||
HLL_EXPORT(CopyMatrix, P3MapSprite_CopyMatrix)
|
||||
);
|
||||
@@ -0,0 +1,69 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "sact.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "vm/page.h"
|
||||
#include "hll.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
static bool P3SquareSprite_Copy(int dest_handle, int src_handle, int size, struct page *matrix)
|
||||
{
|
||||
struct sact_sprite *dest = sact_try_get_sprite(dest_handle);
|
||||
struct sact_sprite *src = sact_try_get_sprite(src_handle);
|
||||
|
||||
if (!dest || !src)
|
||||
return false;
|
||||
|
||||
Texture *dest_tex = sprite_get_texture(dest);
|
||||
Texture *src_tex = sprite_get_texture(src);
|
||||
|
||||
int tile_w = src_tex->w;
|
||||
int tile_h = src_tex->h;
|
||||
int half_w = tile_w / 2;
|
||||
int half_h = tile_h / 2;
|
||||
|
||||
int canvas_center_x = dest_tex->w / 2;
|
||||
int canvas_center_y = dest_tex->h / 2;
|
||||
|
||||
int side_count = size * 2 + 1;
|
||||
|
||||
if (matrix->nr_vars != side_count * side_count)
|
||||
VM_ERROR("Expected %d variables in array, got %d", side_count * side_count, matrix->nr_vars);
|
||||
|
||||
int index = 0;
|
||||
for (int y = -size; y <= size; ++y) {
|
||||
for (int x = -size; x <= size; ++x) {
|
||||
// Check if we should draw this tile
|
||||
if (matrix->values[index++].i == 0)
|
||||
continue;
|
||||
|
||||
// Isometric coordinate conversion
|
||||
int screen_pos_x = canvas_center_x + (x - y - 1) * half_w;
|
||||
int screen_pos_y = canvas_center_y + (x + y - 1) * half_h;
|
||||
|
||||
// Draw the sprite
|
||||
gfx_copy_use_amap_border(dest_tex, screen_pos_x, screen_pos_y, src_tex, 0, 0, tile_w, tile_h, 1);
|
||||
gfx_copy_amap_max(dest_tex, screen_pos_x, screen_pos_y, src_tex, 0, 0, tile_w, tile_h);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(P3SquareSprite,
|
||||
HLL_EXPORT(Copy, P3SquareSprite_Copy)
|
||||
);
|
||||
+686
-5
@@ -17,7 +17,10 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "system4/ain.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "vm/heap.h"
|
||||
#include "vm/page.h"
|
||||
#include "parts.h"
|
||||
#include "hll.h"
|
||||
|
||||
@@ -36,6 +39,11 @@ static void PartsEngine_Update(int passed_time, bool is_skip, bool message_windo
|
||||
PE_Update(passed_time, message_window_show);
|
||||
}
|
||||
|
||||
static void PartsEngine_Update_Pascha3PC(struct string *xxx1, struct string *xxx2, int passed_time, bool is_skip, bool message_window_show)
|
||||
{
|
||||
PE_Update(passed_time, message_window_show);
|
||||
}
|
||||
|
||||
// Oyako Rankan
|
||||
static bool PartsEngine_AddDrawCutCGToPartsConstructionProcess_old(int parts_no,
|
||||
struct string *cg_name, int dx, int dy, int sx, int sy, int w, int h,
|
||||
@@ -54,6 +62,316 @@ static bool PartsEngine_AddCopyCutCGToPartsConstructionProcess_old(int parts_no,
|
||||
sx, sy, w, h, 0, state);
|
||||
}
|
||||
|
||||
// Rance 9: argument types changed from int to float
|
||||
static void PartsEngine_SetComponentPos(int parts_no, float x, float y)
|
||||
{
|
||||
PE_SetPos(parts_no, x, y);
|
||||
}
|
||||
|
||||
// Rance 9: return type changed from int to float
|
||||
static float PartsEngine_Parts_GetPartsUpperLeftPosX(int parts_no, int state)
|
||||
{
|
||||
return PE_GetPartsUpperLeftPosX(parts_no, state);
|
||||
}
|
||||
|
||||
static float PartsEngine_Parts_GetPartsUpperLeftPosY(int parts_no, int state)
|
||||
{
|
||||
return PE_GetPartsUpperLeftPosY(parts_no, state);
|
||||
}
|
||||
|
||||
// Rance 9: return type changed from int to float
|
||||
static float PartsEngine_Parts_GetComponentPosX(int parts_no)
|
||||
{
|
||||
return PE_GetPartsX(parts_no);
|
||||
}
|
||||
|
||||
static float PartsEngine_GetComponentPosY(int parts_no)
|
||||
{
|
||||
return PE_GetPartsY(parts_no);
|
||||
}
|
||||
|
||||
// Rance 9: X/Y coordinate types changed from int to float
|
||||
static void PartsEngine_AddComponentMotionPos(int parts_no, float begin_x, float begin_y,
|
||||
float end_x, float end_y, int begin_t, int end_t,
|
||||
struct string *curve_name)
|
||||
{
|
||||
PE_AddMotionPos_curve(parts_no, begin_x, begin_y, end_x, end_y,
|
||||
begin_t, end_t, curve_name);
|
||||
}
|
||||
|
||||
static void PartsEngine_add_construction_process(union vm_value *ints,
|
||||
union vm_value *floats, union vm_value *strings)
|
||||
{
|
||||
int parts_no = ints[0].i;
|
||||
int state = ints[1].i;
|
||||
int command = ints[2].i;
|
||||
int interp_type = ints[3].i;
|
||||
int sx = ints[4].i;
|
||||
int sy = ints[5].i;
|
||||
int sw = ints[6].i;
|
||||
int sh = ints[7].i;
|
||||
int dx = ints[8].i;
|
||||
int dy = ints[9].i;
|
||||
int dw = ints[12].i;
|
||||
int dh = ints[13].i;
|
||||
int r = ints[14].i;
|
||||
int g = ints[15].i;
|
||||
int b = ints[16].i;
|
||||
int a = ints[17].i;
|
||||
// int r2 = ints[18].i;
|
||||
// int g2 = ints[19].i;
|
||||
// int b2 = ints[20].i;
|
||||
int char_space = ints[21].i;
|
||||
int line_space = ints[22].i;
|
||||
int font_type = ints[23].i;
|
||||
int font_size = ints[24].i;
|
||||
int font_r = ints[25].i;
|
||||
int font_g = ints[26].i;
|
||||
int font_b = ints[27].i;
|
||||
int edge_r = ints[28].i;
|
||||
int edge_g = ints[29].i;
|
||||
int edge_b = ints[30].i;
|
||||
int full_size = ints[31].i;
|
||||
float bold_weight = floats[0].f;
|
||||
float edge_weight = floats[1].f;
|
||||
struct string *text = heap_get_string(strings[0].i);
|
||||
struct string *cg_name = heap_get_string(strings[1].i);
|
||||
|
||||
switch (command) {
|
||||
case 0: // CASConstructionProcess::SetCreate
|
||||
PE_AddCreateToPartsConstructionProcess(parts_no, dw, dh, state);
|
||||
break;
|
||||
case 1: // CASConstructionProcess::SetCreatePixelOnly
|
||||
PE_AddCreatePixelOnlyToPartsConstructionProcess(parts_no, dw, dh, state);
|
||||
break;
|
||||
case 2: // CASConstructionProcess::SetCreateCG
|
||||
PE_AddCreateCGToProcess(parts_no, cg_name, state);
|
||||
break;
|
||||
case 3: // CASConstructionProcess::SetFill
|
||||
PE_AddFillToPartsConstructionProcess(parts_no,
|
||||
dx, dy, dw, dh, r, g, b, state);
|
||||
break;
|
||||
case 4: // CASConstructionProcess::SetFillAlphaColor
|
||||
PE_AddFillAlphaColorToPartsConstructionProcess(parts_no,
|
||||
dx, dy, dw, dh, r, g, b, a, state);
|
||||
break;
|
||||
case 5: // CASConstructionProcess::SetFillAMap
|
||||
PE_AddFillAMapToPartsConstructionProcess(parts_no,
|
||||
dx, dy, dw, dh, a, state);
|
||||
break;
|
||||
case 6: // CASConstructionProcess::SetFillWithAlpha
|
||||
PE_AddFillWithAlphaToPartsConstructionProcess(parts_no,
|
||||
dx, dy, dw, dh, r, g, b, a, state);
|
||||
break;
|
||||
case 7: // CASConstructionProcess::SetDrawText
|
||||
PE_AddDrawTextToPartsConstructionProcess(parts_no,
|
||||
dx, dy, text, font_type, font_size,
|
||||
font_r, font_g, font_b, bold_weight,
|
||||
edge_r, edge_g, edge_b, edge_weight,
|
||||
char_space, line_space, state);
|
||||
break;
|
||||
case 8: // CASConstructionProcess::SetCopyText
|
||||
PE_AddCopyTextToPartsConstructionProcess(parts_no,
|
||||
dx, dy, text, font_type, font_size,
|
||||
font_r, font_g, font_b, bold_weight,
|
||||
edge_r, edge_g, edge_b, edge_weight,
|
||||
char_space, line_space, state);
|
||||
break;
|
||||
case 9: // CASConstructionProcess::SetFillGradationHorizon
|
||||
WARNING("AddConstructProcess: FillGradationHorizon unimplemented");
|
||||
break;
|
||||
case 10: // CASConstructionProcess::SetDrawRect
|
||||
PE_AddDrawRectToPartsConstructionProcess(parts_no,
|
||||
dx, dy, dw, dh, r, g, b, state);
|
||||
break;
|
||||
case 11: // CASConstructionProcess::SetCutCGBlend (equal scale)
|
||||
PE_AddDrawCutCGToPartsConstructionProcess(parts_no, cg_name,
|
||||
dx, dy, dw, dh, sx, sy, dw, dh,
|
||||
interp_type, state);
|
||||
break;
|
||||
case 12: // CASConstructionProcess::SetCutCGCopy (equal scale)
|
||||
PE_AddCopyCutCGToPartsConstructionProcess(parts_no, cg_name,
|
||||
dx, dy, dw, dh, sx, sy, dw, dh,
|
||||
interp_type, state);
|
||||
break;
|
||||
case 13: // CASConstructionProcess::SetCutCGScaleBlend
|
||||
PE_AddDrawCutCGToPartsConstructionProcess(parts_no, cg_name,
|
||||
dx, dy, dw, dh, sx, sy, sw, sh,
|
||||
interp_type, state);
|
||||
break;
|
||||
case 14: // CASConstructionProcess::SetCutCGScaleCopy
|
||||
PE_AddCopyCutCGToPartsConstructionProcess(parts_no, cg_name,
|
||||
dx, dy, dw, dh, sx, sy, sw, sh,
|
||||
interp_type, state);
|
||||
break;
|
||||
case 15: // CASConstructionProcess::SetGrayFilter
|
||||
PE_AddGrayFilterToPartsConstructionProcess(parts_no,
|
||||
dx, dy, dw, dh, full_size, state);
|
||||
break;
|
||||
case 16: // CASConstructionProcess::SetAddFilter
|
||||
WARNING("AddConstructProcess: AddFilter unimplemented");
|
||||
break;
|
||||
case 17: // CASConstructionProcess::SetMulFilter
|
||||
WARNING("AddConstructProcess: MulFilter unimplemented");
|
||||
break;
|
||||
case 18: // CASConstructionProcess::SetDrawLine
|
||||
WARNING("AddConstructProcess: DrawLine unimplemented");
|
||||
break;
|
||||
default:
|
||||
WARNING("AddConstructProcess: unknown command %d", command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Rance9 predates the layer functions at func_id 3-5. Use the numbering from
|
||||
// later PartsEngine versions as canonical and adjust Rance9's ids to match.
|
||||
static bool parts_func_uses_rance9_ids;
|
||||
|
||||
// Generic dispatch function for PartsEngine operations.
|
||||
// func_id selects the operation; arguments and return values are passed
|
||||
// through three typed arrays (int/bool, float, string).
|
||||
//
|
||||
// Calling convention (from game script):
|
||||
// 1. Push input values into the appropriate arrays.
|
||||
// 2. Push a placeholder (0 or "") for each output slot.
|
||||
// 3. Call PartsFunc; outputs are written back into the placeholder slots.
|
||||
static int PartsEngine_PartsFunc(int func_id, struct page **array_int,
|
||||
struct page **array_float, struct page **array_string)
|
||||
{
|
||||
int nr_ints = (array_int && *array_int) ? (*array_int)->nr_vars : 0;
|
||||
union vm_value *ints = nr_ints ? (*array_int)->values : NULL;
|
||||
int nr_floats = (array_float && *array_float) ? (*array_float)->nr_vars : 0;
|
||||
union vm_value *floats = nr_floats ? (*array_float)->values : NULL;
|
||||
int nr_strings = (array_string && *array_string) ? (*array_string)->nr_vars : 0;
|
||||
union vm_value *strings = nr_strings ? (*array_string)->values : NULL;
|
||||
|
||||
#define REQUIRE_INTS(n) \
|
||||
if (nr_ints != (n)) VM_ERROR("Invalid arguments for PartsFunc %d: expected %d ints, got %d", func_id, (n), nr_ints)
|
||||
#define REQUIRE_FLOATS(n) \
|
||||
if (nr_floats < (n)) VM_ERROR("Invalid arguments for PartsFunc %d: expected %d floats, got %d", func_id, (n), nr_floats)
|
||||
#define REQUIRE_STRINGS(n) \
|
||||
if (nr_strings < (n)) VM_ERROR("Invalid arguments for PartsFunc %d: expected %d strings, got %d", func_id, (n), nr_strings)
|
||||
|
||||
int canonical_func_id = func_id;
|
||||
if (parts_func_uses_rance9_ids && canonical_func_id > 2)
|
||||
canonical_func_id += 3;
|
||||
|
||||
switch (canonical_func_id) {
|
||||
case 0: // void SetActiveLayer(int layer)
|
||||
REQUIRE_INTS(1);
|
||||
PE_set_active_controller(ints[0].i);
|
||||
return 1;
|
||||
case 1: // int GetActiveLayer()
|
||||
REQUIRE_INTS(1);
|
||||
ints[0].i = PE_get_active_controller();
|
||||
return 1;
|
||||
case 2: // int GetSystemOverlayLayer()
|
||||
REQUIRE_INTS(1);
|
||||
ints[0].i = PE_get_system_controller();
|
||||
return 1;
|
||||
// Layers are identified by their index in the controller stack, so the
|
||||
// layer id and index conversions are identity functions.
|
||||
case 3: // int GetLayerIndex(int layer_id)
|
||||
case 4: // int GetLayerID(int layer_index)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = ints[0].i;
|
||||
return 1;
|
||||
case 5: // int NumofLayer()
|
||||
REQUIRE_INTS(1);
|
||||
ints[0].i = PE_get_nr_controllers();
|
||||
return 1;
|
||||
case 6: // void PauseMotion(bool pause)
|
||||
REQUIRE_INTS(1);
|
||||
PE_PauseMotion(!!ints[0].i);
|
||||
return 1;
|
||||
case 7: // void SetWantSave(int parts_no, bool want_save)
|
||||
REQUIRE_INTS(2);
|
||||
PE_parts_set_want_save(ints[0].i, !!ints[1].i);
|
||||
return 1;
|
||||
case 9: // bool SaveThumbnail(string filename, int reduction_factor)
|
||||
REQUIRE_INTS(2);
|
||||
REQUIRE_STRINGS(1);
|
||||
ints[1].i = PE_save_thumbnail(heap_get_string(strings[0].i), ints[0].i);
|
||||
return 1;
|
||||
case 43: // float PARTS_GetAbsoluteX(int number)
|
||||
REQUIRE_INTS(1);
|
||||
REQUIRE_FLOATS(1);
|
||||
floats[0].f = PE_parts_get_absolute_x(ints[0].i);
|
||||
return 1;
|
||||
case 44: // float PARTS_GetAbsoluteY(int number)
|
||||
REQUIRE_INTS(1);
|
||||
REQUIRE_FLOATS(1);
|
||||
floats[0].f = PE_parts_get_absolute_y(ints[0].i);
|
||||
return 1;
|
||||
case 45: // int PARTS_GetAbsoluteZ(int number)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_parts_get_absolute_z(ints[0].i);
|
||||
return 1;
|
||||
case 48: // void PARTS_SetLockInputState(int number, bool lock)
|
||||
REQUIRE_INTS(2);
|
||||
PE_parts_set_lock_input_state(ints[0].i, !!ints[1].i);
|
||||
return 1;
|
||||
case 60: // void AppendChild(int number, int child_number)
|
||||
REQUIRE_INTS(2);
|
||||
PE_SetParentPartsNumber(ints[1].i, ints[0].i);
|
||||
return 1;
|
||||
case 94: // void SetLayoutBoxPadding(int parts_no, int top, int bottom, int left, int right)
|
||||
REQUIRE_INTS(5);
|
||||
PE_set_layoutbox_padding(ints[0].i, ints[1].i, ints[2].i, ints[3].i, ints[4].i);
|
||||
return 1;
|
||||
case 95: // int GetLayoutBoxPaddingTop(int parts_no)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_get_layoutbox_padding_top(ints[0].i);
|
||||
return 1;
|
||||
case 96: // int GetLayoutBoxPaddingBottom(int parts_no)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_get_layoutbox_padding_bottom(ints[0].i);
|
||||
return 1;
|
||||
case 97: // int GetLayoutBoxPaddingLeft(int parts_no)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_get_layoutbox_padding_left(ints[0].i);
|
||||
return 1;
|
||||
case 98: // int GetLayoutBoxPaddingRight(int parts_no)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_get_layoutbox_padding_right(ints[0].i);
|
||||
return 1;
|
||||
case 106: // void GetPartsCGSurfaceArea(int parts_no, int *x, int *y, int *w, int *h, int state)
|
||||
REQUIRE_INTS(6);
|
||||
PE_GetPartsCGSurfaceArea(ints[0].i, &ints[1].i, &ints[2].i, &ints[3].i, &ints[4].i, ints[5].i);
|
||||
return 1;
|
||||
case 112: { // string GetText(int parts_no, int state)
|
||||
REQUIRE_INTS(2);
|
||||
REQUIRE_STRINGS(1);
|
||||
struct string *text = PE_GetText(ints[0].i, ints[1].i);
|
||||
if (text) {
|
||||
heap_string_assign(strings[0].i, text);
|
||||
free_string(text);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
case 162: // AddConstructProcess(ArrayInt[32], ArrayFloat[2], ArrayString[2])
|
||||
REQUIRE_INTS(32);
|
||||
REQUIRE_FLOATS(2);
|
||||
REQUIRE_STRINGS(2);
|
||||
PartsEngine_add_construction_process(ints, floats, strings);
|
||||
return 1;
|
||||
case 165: // bool InitPartsMovie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state)
|
||||
REQUIRE_INTS(8);
|
||||
ints[7].i = PE_init_parts_movie(ints[0].i, ints[1].i, ints[2].i, ints[3].i, ints[4].i, ints[5].i, ints[6].i);
|
||||
return 1;
|
||||
case 166: // int GetMovieSprite(int parts_no, int state)
|
||||
REQUIRE_INTS(3);
|
||||
ints[2].i = PE_get_movie_sprite(ints[0].i, ints[1].i);
|
||||
return 1;
|
||||
default:
|
||||
WARNING("Unknown func_id: %d", func_id);
|
||||
return 0;
|
||||
}
|
||||
#undef REQUIRE_INTS
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(, void, PartsEngine, StopSoundWithoutSystemSound);
|
||||
|
||||
static void PartsEngine_PreLink(void);
|
||||
|
||||
HLL_LIBRARY(PartsEngine,
|
||||
@@ -121,7 +439,7 @@ HLL_LIBRARY(PartsEngine,
|
||||
HLL_EXPORT(StartPartsFlash, PE_StartPartsFlash),
|
||||
HLL_EXPORT(GoFramePartsFlash, PE_GoFramePartsFlash),
|
||||
HLL_EXPORT(GetPartsFlashEndFrame, PE_GetPartsFlashEndFrame),
|
||||
HLL_TODO_EXPORT(ExistsFlashFile, PE_ExistsFlashFile),
|
||||
HLL_EXPORT(ExistsFlashFile, PE_ExistsFlashFile),
|
||||
HLL_EXPORT(ClearPartsConstructionProcess, PE_ClearPartsConstructionProcess),
|
||||
HLL_EXPORT(AddCreateToPartsConstructionProcess, PE_AddCreateToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddCreatePixelOnlyToPartsConstructionProcess, PE_AddCreatePixelOnlyToPartsConstructionProcess),
|
||||
@@ -129,12 +447,12 @@ HLL_LIBRARY(PartsEngine,
|
||||
HLL_EXPORT(AddFillToPartsConstructionProcess, PE_AddFillToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddFillAlphaColorToPartsConstructionProcess, PE_AddFillAlphaColorToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddFillAMapToPartsConstructionProcess, PE_AddFillAMapToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddFillWithAlphaToPartsConstructionProcess, PartsEngine_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddFillWithAlphaToPartsConstructionProcess, PE_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddFillGradationHorizonToPartsConstructionProcess, PartsEngine_AddFillGradationHorizonToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddDrawRectToPartsConstructionProcess, PE_AddDrawRectToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddDrawCutCGToPartsConstructionProcess, PartsEngine_AddDrawCutCGToPartsConstructionProcess_old),
|
||||
HLL_EXPORT(AddCopyCutCGToPartsConstructionProcess, PartsEngine_AddCopyCutCGToPartsConstructionProcess_old),
|
||||
HLL_TODO_EXPORT(AddGrayFilterToPartsConstructionProcess, PartsEngine_AddGrayFilterToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddGrayFilterToPartsConstructionProcess, PE_AddGrayFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddAddFilterToPartsConstructionProcess, PartsEngine_AddAddFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddMulFilterToPartsConstructionProcess, PartsEngine_AddMulFilterToPartsConstructionProcess),
|
||||
HLL_EXPORT(BuildPartsConstructionProcess, PE_BuildPartsConstructionProcess),
|
||||
@@ -151,6 +469,7 @@ HLL_LIBRARY(PartsEngine,
|
||||
HLL_EXPORT(SetPartsDrawFilter, PE_SetPartsDrawFilter),
|
||||
HLL_EXPORT(SetAddColor, PE_SetAddColor),
|
||||
HLL_EXPORT(SetMultiplyColor, PE_SetMultiplyColor),
|
||||
HLL_EXPORT(SetPassCursor, PE_SetPassCursor),
|
||||
HLL_EXPORT(SetClickable, PE_SetClickable),
|
||||
HLL_EXPORT(SetSpeedupRateByMessageSkip, PE_SetSpeedupRateByMessageSkip),
|
||||
HLL_TODO_EXPORT(SetResetTimerByChangeInputStatus, PartsEngine_SetResetTimerByChangeInputStatus),
|
||||
@@ -161,6 +480,7 @@ HLL_LIBRARY(PartsEngine,
|
||||
HLL_EXPORT(GetPartsAlpha, PE_GetPartsAlpha),
|
||||
HLL_TODO_EXPORT(GetAddColor, PartsEngine_GetAddColor),
|
||||
HLL_TODO_EXPORT(GetMultiplyColor, PartsEngine_GetMultiplyColor),
|
||||
HLL_EXPORT(GetPartsPassCursor, PE_GetPartsPassCursor),
|
||||
HLL_EXPORT(GetPartsClickable, PE_GetPartsClickable),
|
||||
HLL_TODO_EXPORT(GetPartsSpeedupRateByMessageSkip, PartsEngine_GetPartsSpeedupRateByMessageSkip),
|
||||
HLL_TODO_EXPORT(GetResetTimerByChangeInputStatus, PartsEngine_GetResetTimerByChangeInputStatus),
|
||||
@@ -214,14 +534,366 @@ HLL_LIBRARY(PartsEngine,
|
||||
HLL_EXPORT(SetPartsRotateX, PE_SetPartsRotateX),
|
||||
HLL_EXPORT(SetPartsRotateY, PE_SetPartsRotateY),
|
||||
HLL_EXPORT(SetPartsRotateZ, PE_SetPartsRotateZ),
|
||||
HLL_TODO_EXPORT(SetPartsAlphaClipperPartsNumber, PartsEngine_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_EXPORT(SetPartsAlphaClipperPartsNumber, PE_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_EXPORT(SetPartsPixelDecide, PE_SetPartsPixelDecide),
|
||||
HLL_EXPORT(IsCursorIn, PE_IsCursorIn),
|
||||
HLL_EXPORT(SetThumbnailReductionSize, PE_SetThumbnailReductionSize),
|
||||
HLL_EXPORT(SetThumbnailMode, PE_SetThumbnailMode),
|
||||
HLL_EXPORT(Save, PE_Save),
|
||||
HLL_EXPORT(SaveWithoutHideParts, PE_SaveWithoutHideParts),
|
||||
HLL_EXPORT(Load, PE_Load)
|
||||
HLL_EXPORT(Load, PE_Load),
|
||||
// Rance 9
|
||||
HLL_EXPORT(PartsFunc, PartsEngine_PartsFunc),
|
||||
HLL_EXPORT(Release, PE_ReleaseParts),
|
||||
HLL_TODO_EXPORT(ReleaseAll, PartsEngine_ReleaseAll),
|
||||
HLL_EXPORT(ReleaseAllWithoutSystem, PE_ReleaseAllWithoutSystem),
|
||||
HLL_EXPORT(GetFreeNumber, PE_GetFreeNumber),
|
||||
HLL_EXPORT(IsExist, PE_IsExist),
|
||||
HLL_EXPORT(AddController, PE_AddController),
|
||||
HLL_EXPORT(RemoveController, PE_RemoveController),
|
||||
HLL_EXPORT(UpdateComponent, PartsEngine_Update),
|
||||
HLL_EXPORT(Parts_SetThumbnailReductionSize, PE_SetThumbnailReductionSize),
|
||||
HLL_EXPORT(Parts_SetThumbnailMode, PE_SetThumbnailMode),
|
||||
HLL_EXPORT(GetClickNumber, PE_GetClickPartsNumber),
|
||||
HLL_EXPORT(StopSoundWithoutSystemSound, PartsEngine_StopSoundWithoutSystemSound),
|
||||
HLL_TODO_EXPORT(ReleaseActivity, PartsEngine_ReleaseActivity),
|
||||
HLL_TODO_EXPORT(CrateActivityBinary, PartsEngine_CrateActivityBinary),
|
||||
HLL_TODO_EXPORT(ReadActivityBinary, PartsEngine_ReadActivityBinary),
|
||||
HLL_EXPORT(ReleaseMessage, PE_ReleaseMessage),
|
||||
HLL_EXPORT(PopMessage, PE_PopMessage),
|
||||
HLL_EXPORT(GetMessagePartsNumber, PE_GetMessagePartsNumber),
|
||||
HLL_EXPORT(GetMessageDelegateIndex, PE_GetMessageDelegateIndex),
|
||||
HLL_EXPORT(GetDelegateIndex, PE_GetDelegateIndex),
|
||||
HLL_EXPORT(GetMessageType, PE_GetMessageType),
|
||||
HLL_EXPORT(GetMessageVariableCount, PE_GetMessageVariableCount),
|
||||
HLL_EXPORT(GetMessageVariableType, PE_GetMessageVariableType),
|
||||
HLL_EXPORT(GetMessageVariableInt, PE_GetMessageVariableInt),
|
||||
HLL_EXPORT(GetMessageVariableFloat, PE_GetMessageVariableFloat),
|
||||
HLL_EXPORT(GetMessageVariableBool, PE_GetMessageVariableBool),
|
||||
HLL_EXPORT(GetMessageVariableString, PE_GetMessageVariableString),
|
||||
HLL_EXPORT(SetDelegateIndex, PE_SetDelegateIndex),
|
||||
HLL_TODO_EXPORT(SetFocus, PartsEngine_SetFocus),
|
||||
HLL_TODO_EXPORT(IsFocus, PartsEngine_IsFocus),
|
||||
HLL_EXPORT(SetComponentType, PE_SetComponentType),
|
||||
HLL_EXPORT(GetComponentType, PE_GetComponentType),
|
||||
HLL_EXPORT(SetComponentPos, PartsEngine_SetComponentPos),
|
||||
HLL_EXPORT(SetComponentPosZ, PE_SetZ),
|
||||
HLL_EXPORT(GetComponentPosX, PartsEngine_Parts_GetComponentPosX),
|
||||
HLL_EXPORT(GetComponentPosY, PartsEngine_GetComponentPosY),
|
||||
HLL_EXPORT(GetComponentPosZ, PE_GetPartsZ),
|
||||
HLL_EXPORT(Parts_GetPartsUpperLeftPosX, PartsEngine_Parts_GetPartsUpperLeftPosX),
|
||||
HLL_EXPORT(Parts_GetPartsUpperLeftPosY, PartsEngine_Parts_GetPartsUpperLeftPosY),
|
||||
HLL_EXPORT(SetComponentOriginPosMode, PE_SetPartsOriginPosMode),
|
||||
HLL_EXPORT(GetComponentOriginPosMode, PE_GetPartsOriginPosMode),
|
||||
HLL_TODO_EXPORT(GetComponentWidth, PartsEngine_GetComponentWidth),
|
||||
HLL_TODO_EXPORT(GetComponentHeight, PartsEngine_GetComponentHeight),
|
||||
HLL_EXPORT(Parts_GetPartsWidth, PE_GetPartsWidth),
|
||||
HLL_EXPORT(Parts_GetPartsHeight, PE_GetPartsHeight),
|
||||
HLL_EXPORT(SetComponentShow, PE_SetShow),
|
||||
HLL_EXPORT(IsComponentShow, PE_GetPartsShow),
|
||||
HLL_EXPORT(SetComponentMessageWindowShowLink, PE_SetPartsMessageWindowShowLink),
|
||||
HLL_EXPORT(IsComponentMessageWindowShowLink, PE_GetPartsMessageWindowShowLink),
|
||||
HLL_EXPORT(SetComponentAlpha, PE_SetAlpha),
|
||||
HLL_EXPORT(GetComponentAlpha, PE_GetPartsAlpha),
|
||||
HLL_EXPORT(SetComponentAddColor, PE_SetAddColor),
|
||||
HLL_TODO_EXPORT(GetComponentAddColorR, PartsEngine_GetComponentAddColorR),
|
||||
HLL_TODO_EXPORT(GetComponentAddColorG, PartsEngine_GetComponentAddColorG),
|
||||
HLL_TODO_EXPORT(GetComponentAddColorB, PartsEngine_GetComponentAddColorB),
|
||||
HLL_EXPORT(SetComponentMulColor, PE_SetMultiplyColor),
|
||||
HLL_TODO_EXPORT(GetComponentMulColorR, PartsEngine_GetComponentMulColorR),
|
||||
HLL_TODO_EXPORT(GetComponentMulColorG, PartsEngine_GetComponentMulColorG),
|
||||
HLL_TODO_EXPORT(GetComponentMulColorB, PartsEngine_GetComponentMulColorB),
|
||||
HLL_EXPORT(SetComponentDrawFilter, PE_SetPartsDrawFilter),
|
||||
HLL_TODO_EXPORT(GetComponentDrawFilter, PartsEngine_GetComponentDrawFilter),
|
||||
HLL_EXPORT(SetComponentMagX, PE_SetPartsMagX),
|
||||
HLL_EXPORT(SetComponentMagY, PE_SetPartsMagY),
|
||||
HLL_TODO_EXPORT(GetComponentMagX, PartsEngine_GetComponentMagX),
|
||||
HLL_TODO_EXPORT(GetComponentMagY, PartsEngine_GetComponentMagY),
|
||||
HLL_EXPORT(SetComponentRotateX, PE_SetPartsRotateX),
|
||||
HLL_EXPORT(SetComponentRotateY, PE_SetPartsRotateY),
|
||||
HLL_EXPORT(SetComponentRotateZ, PE_SetPartsRotateZ),
|
||||
HLL_TODO_EXPORT(GetComponentRotateX, PartsEngine_GetComponentRotateX),
|
||||
HLL_TODO_EXPORT(GetComponentRotateY, PartsEngine_GetComponentRotateY),
|
||||
HLL_EXPORT(GetComponentRotateZ, PE_GetPartsRotateZ),
|
||||
HLL_EXPORT(SetComponentMargin, PE_SetComponentMargin),
|
||||
HLL_EXPORT(GetComponentMarginTop, PE_GetComponentMarginTop),
|
||||
HLL_EXPORT(GetComponentMarginBottom, PE_GetComponentMarginBottom),
|
||||
HLL_EXPORT(GetComponentMarginLeft, PE_GetComponentMarginLeft),
|
||||
HLL_EXPORT(GetComponentMarginRight, PE_GetComponentMarginRight),
|
||||
HLL_EXPORT(SetComponentAlphaClipper, PE_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_TODO_EXPORT(GetComponentAlphaClipper, PartsEngine_GetComponentAlphaClipper),
|
||||
HLL_TODO_EXPORT(SetComponentTextureFilterType, PartsEngine_SetComponentTextureFilterType),
|
||||
HLL_TODO_EXPORT(GetComponentTextureFilterType, PartsEngine_GetComponentTextureFilterType),
|
||||
HLL_TODO_EXPORT(SetComponentMipmap, PartsEngine_SetComponentMipmap),
|
||||
HLL_TODO_EXPORT(IsComponentMipmap, PartsEngine_IsComponentMipmap),
|
||||
HLL_EXPORT(SetComponentSpeedupRateByMessageSkip, PE_SetSpeedupRateByMessageSkip),
|
||||
HLL_EXPORT(GetComponentSpeedupRateByMessageSkip, PE_GetSpeedupRateByMessageSkip),
|
||||
HLL_EXPORT(AddComponentMotionPos, PartsEngine_AddComponentMotionPos),
|
||||
HLL_EXPORT(AddComponentMotionAlpha, PE_AddMotionAlpha_curve),
|
||||
HLL_TODO_EXPORT(AddComponentMotionCG, PartsEngine_AddComponentMotionCG),
|
||||
HLL_TODO_EXPORT(AddComponentMotionCGTermination, PartsEngine_AddComponentMotionCGTermination),
|
||||
HLL_EXPORT(AddComponentMotionHGaugeRate, PE_AddMotionHGaugeRate_curve),
|
||||
HLL_EXPORT(AddComponentMotionVGaugeRate, PE_AddMotionVGaugeRate_curve),
|
||||
HLL_EXPORT(AddComponentMotionNumeralNumber, PE_AddMotionNumeralNumber_curve),
|
||||
HLL_EXPORT(AddComponentMotionMagX, PE_AddMotionMagX_curve),
|
||||
HLL_EXPORT(AddComponentMotionMagY, PE_AddMotionMagY_curve),
|
||||
HLL_EXPORT(AddComponentMotionRotateX, PE_AddMotionRotateX_curve),
|
||||
HLL_EXPORT(AddComponentMotionRotateY, PE_AddMotionRotateY_curve),
|
||||
HLL_EXPORT(AddComponentMotionRotateZ, PE_AddMotionRotateZ_curve),
|
||||
HLL_EXPORT(AddComponentMotionVibrationSize, PE_AddMotionVibrationSize),
|
||||
HLL_TODO_EXPORT(SuspendBuildView, PartsEngine_SuspendBuildView),
|
||||
HLL_TODO_EXPORT(SuspendBuildViewAt, PartsEngine_SuspendBuildViewAt),
|
||||
HLL_TODO_EXPORT(ResumeBuildView, PartsEngine_ResumeBuildView),
|
||||
HLL_TODO_EXPORT(SetButtonSize, PartsEngine_SetButtonSize),
|
||||
HLL_TODO_EXPORT(SetButtonDrag, PartsEngine_SetButtonDrag),
|
||||
HLL_TODO_EXPORT(IsButtonDrag, PartsEngine_IsButtonDrag),
|
||||
HLL_TODO_EXPORT(SetButtonEnable, PartsEngine_SetButtonEnable),
|
||||
HLL_TODO_EXPORT(IsButtonEnable, PartsEngine_IsButtonEnable),
|
||||
HLL_TODO_EXPORT(SetButtonPixelDecide, PartsEngine_SetButtonPixelDecide),
|
||||
HLL_TODO_EXPORT(IsButtonPixelDecide, PartsEngine_IsButtonPixelDecide),
|
||||
HLL_TODO_EXPORT(SetButtonColor, PartsEngine_SetButtonColor),
|
||||
HLL_TODO_EXPORT(GetButtonR, PartsEngine_GetButtonR),
|
||||
HLL_TODO_EXPORT(GetButtonG, PartsEngine_GetButtonG),
|
||||
HLL_TODO_EXPORT(GetButtonB, PartsEngine_GetButtonB),
|
||||
HLL_TODO_EXPORT(SetButtonFontProperty, PartsEngine_SetButtonFontProperty),
|
||||
HLL_TODO_EXPORT(GetButtonFontProperty, PartsEngine_GetButtonFontProperty),
|
||||
HLL_TODO_EXPORT(SetButtonOnCursorSoundNumber, PartsEngine_SetButtonOnCursorSoundNumber),
|
||||
HLL_TODO_EXPORT(SetButtonClickSoundNumber, PartsEngine_SetButtonClickSoundNumber),
|
||||
HLL_TODO_EXPORT(GetButtonOnCursorSoundNumber, PartsEngine_GetButtonOnCursorSoundNumber),
|
||||
HLL_TODO_EXPORT(GetButtonClickSoundNumber, PartsEngine_GetButtonClickSoundNumber),
|
||||
HLL_TODO_EXPORT(SetButtonCGName, PartsEngine_SetButtonCGName),
|
||||
HLL_TODO_EXPORT(GetButtonCGName, PartsEngine_GetButtonCGName),
|
||||
HLL_TODO_EXPORT(SetButtonText, PartsEngine_SetButtonText),
|
||||
HLL_TODO_EXPORT(GetButtonText, PartsEngine_GetButtonText),
|
||||
HLL_TODO_EXPORT(SetCheckBoxSize, PartsEngine_SetCheckBoxSize),
|
||||
HLL_TODO_EXPORT(SetCheckBoxDrag, PartsEngine_SetCheckBoxDrag),
|
||||
HLL_TODO_EXPORT(IsCheckBoxDrag, PartsEngine_IsCheckBoxDrag),
|
||||
HLL_TODO_EXPORT(CheckBoxChecked, PartsEngine_CheckBoxChecked),
|
||||
HLL_TODO_EXPORT(IsCheckBoxChecked, PartsEngine_IsCheckBoxChecked),
|
||||
HLL_TODO_EXPORT(SetCheckBoxColor, PartsEngine_SetCheckBoxColor),
|
||||
HLL_TODO_EXPORT(GetCheckBoxR, PartsEngine_GetCheckBoxR),
|
||||
HLL_TODO_EXPORT(GetCheckBoxG, PartsEngine_GetCheckBoxG),
|
||||
HLL_TODO_EXPORT(GetCheckBoxB, PartsEngine_GetCheckBoxB),
|
||||
HLL_TODO_EXPORT(SetCheckBoxFontProperty, PartsEngine_SetCheckBoxFontProperty),
|
||||
HLL_TODO_EXPORT(GetCheckBoxFontProperty, PartsEngine_GetCheckBoxFontProperty),
|
||||
HLL_TODO_EXPORT(SetCheckBoxOnCursorSoundNumber, PartsEngine_SetCheckBoxOnCursorSoundNumber),
|
||||
HLL_TODO_EXPORT(SetCheckBoxClickSoundNumber, PartsEngine_SetCheckBoxClickSoundNumber),
|
||||
HLL_TODO_EXPORT(GetCheckBoxOnCursorSoundNumber, PartsEngine_GetCheckBoxOnCursorSoundNumber),
|
||||
HLL_TODO_EXPORT(GetCheckBoxClickSoundNumber, PartsEngine_GetCheckBoxClickSoundNumber),
|
||||
HLL_TODO_EXPORT(SetCheckBoxCGName, PartsEngine_SetCheckBoxCGName),
|
||||
HLL_TODO_EXPORT(GetCheckBoxCGName, PartsEngine_GetCheckBoxCGName),
|
||||
HLL_TODO_EXPORT(SetCheckBoxText, PartsEngine_SetCheckBoxText),
|
||||
HLL_TODO_EXPORT(GetCheckBoxText, PartsEngine_GetCheckBoxText),
|
||||
HLL_TODO_EXPORT(SetVScrollbarOnCursorSoundNumber, PartsEngine_SetVScrollbarOnCursorSoundNumber),
|
||||
HLL_TODO_EXPORT(SetVScrollbarClickSoundNumber, PartsEngine_SetVScrollbarClickSoundNumber),
|
||||
HLL_TODO_EXPORT(GetVScrollbarOnCursorSoundNumber, PartsEngine_GetVScrollbarOnCursorSoundNumber),
|
||||
HLL_TODO_EXPORT(GetVScrollbarClickSoundNumber, PartsEngine_GetVScrollbarClickSoundNumber),
|
||||
HLL_TODO_EXPORT(SetVScrollbarSize, PartsEngine_SetVScrollbarSize),
|
||||
HLL_TODO_EXPORT(SetVScrollbarUpHeight, PartsEngine_SetVScrollbarUpHeight),
|
||||
HLL_TODO_EXPORT(SetVScrollbarDownHeight, PartsEngine_SetVScrollbarDownHeight),
|
||||
HLL_TODO_EXPORT(GetVScrollbarUpHeight, PartsEngine_GetVScrollbarUpHeight),
|
||||
HLL_TODO_EXPORT(GetVScrollbarDownHeight, PartsEngine_GetVScrollbarDownHeight),
|
||||
HLL_TODO_EXPORT(SetVScrollbarTotalSize, PartsEngine_SetVScrollbarTotalSize),
|
||||
HLL_TODO_EXPORT(SetVScrollbarViewSize, PartsEngine_SetVScrollbarViewSize),
|
||||
HLL_TODO_EXPORT(SetVScrollbarScrollPos, PartsEngine_SetVScrollbarScrollPos),
|
||||
HLL_TODO_EXPORT(SetVScrollbarScrollRate, PartsEngine_SetVScrollbarScrollRate),
|
||||
HLL_TODO_EXPORT(SetVScrollbarMoveSizeByButton, PartsEngine_SetVScrollbarMoveSizeByButton),
|
||||
HLL_TODO_EXPORT(GetVScrollbarTotalSize, PartsEngine_GetVScrollbarTotalSize),
|
||||
HLL_TODO_EXPORT(GetVScrollbarViewSize, PartsEngine_GetVScrollbarViewSize),
|
||||
HLL_TODO_EXPORT(GetVScrollbarScrollPos, PartsEngine_GetVScrollbarScrollPos),
|
||||
HLL_TODO_EXPORT(GetVScrollbarScrollRate, PartsEngine_GetVScrollbarScrollRate),
|
||||
HLL_TODO_EXPORT(GetVScrollbarMoveSizeByButton, PartsEngine_GetVScrollbarMoveSizeByButton),
|
||||
HLL_TODO_EXPORT(SetVScrollbarCGName, PartsEngine_SetVScrollbarCGName),
|
||||
HLL_TODO_EXPORT(GetVScrollbarCGName, PartsEngine_GetVScrollbarCGName),
|
||||
HLL_TODO_EXPORT(SetHScrollbarOnCursorSoundNumber, PartsEngine_SetHScrollbarOnCursorSoundNumber),
|
||||
HLL_TODO_EXPORT(SetHScrollbarClickSoundNumber, PartsEngine_SetHScrollbarClickSoundNumber),
|
||||
HLL_TODO_EXPORT(GetHScrollbarOnCursorSoundNumber, PartsEngine_GetHScrollbarOnCursorSoundNumber),
|
||||
HLL_TODO_EXPORT(GetHScrollbarClickSoundNumber, PartsEngine_GetHScrollbarClickSoundNumber),
|
||||
HLL_TODO_EXPORT(SetHScrollbarSize, PartsEngine_SetHScrollbarSize),
|
||||
HLL_TODO_EXPORT(SetHScrollbarLeftWidth, PartsEngine_SetHScrollbarLeftWidth),
|
||||
HLL_TODO_EXPORT(SetHScrollbarRightWidth, PartsEngine_SetHScrollbarRightWidth),
|
||||
HLL_TODO_EXPORT(GetHScrollbarLeftWidth, PartsEngine_GetHScrollbarLeftWidth),
|
||||
HLL_TODO_EXPORT(GetHScrollbarRightWidth, PartsEngine_GetHScrollbarRightWidth),
|
||||
HLL_TODO_EXPORT(SetHScrollbarTotalSize, PartsEngine_SetHScrollbarTotalSize),
|
||||
HLL_TODO_EXPORT(SetHScrollbarViewSize, PartsEngine_SetHScrollbarViewSize),
|
||||
HLL_TODO_EXPORT(SetHScrollbarScrollPos, PartsEngine_SetHScrollbarScrollPos),
|
||||
HLL_TODO_EXPORT(SetHScrollbarScrollRate, PartsEngine_SetHScrollbarScrollRate),
|
||||
HLL_TODO_EXPORT(SetHScrollbarMoveSizeByButton, PartsEngine_SetHScrollbarMoveSizeByButton),
|
||||
HLL_TODO_EXPORT(GetHScrollbarTotalSize, PartsEngine_GetHScrollbarTotalSize),
|
||||
HLL_TODO_EXPORT(GetHScrollbarViewSize, PartsEngine_GetHScrollbarViewSize),
|
||||
HLL_TODO_EXPORT(GetHScrollbarScrollPos, PartsEngine_GetHScrollbarScrollPos),
|
||||
HLL_TODO_EXPORT(GetHScrollbarScrollRate, PartsEngine_GetHScrollbarScrollRate),
|
||||
HLL_TODO_EXPORT(GetHScrollbarMoveSizeByButton, PartsEngine_GetHScrollbarMoveSizeByButton),
|
||||
HLL_TODO_EXPORT(SetHScrollbarCGName, PartsEngine_SetHScrollbarCGName),
|
||||
HLL_TODO_EXPORT(GetHScrollbarCGName, PartsEngine_GetHScrollbarCGName),
|
||||
HLL_TODO_EXPORT(SetTextBoxSize, PartsEngine_SetTextBoxSize),
|
||||
HLL_TODO_EXPORT(SetTextBoxFontProperty, PartsEngine_SetTextBoxFontProperty),
|
||||
HLL_TODO_EXPORT(GetTextBoxFontProperty, PartsEngine_GetTextBoxFontProperty),
|
||||
HLL_TODO_EXPORT(SetTextBoxText, PartsEngine_SetTextBoxText),
|
||||
HLL_TODO_EXPORT(GetTextBoxText, PartsEngine_GetTextBoxText),
|
||||
HLL_TODO_EXPORT(SetTextBoxMaxTextLength, PartsEngine_SetTextBoxMaxTextLength),
|
||||
HLL_TODO_EXPORT(GetTextBoxMaxTextLength, PartsEngine_GetTextBoxMaxTextLength),
|
||||
HLL_TODO_EXPORT(SetTextBoxSelectColor, PartsEngine_SetTextBoxSelectColor),
|
||||
HLL_TODO_EXPORT(GetTextBoxSelectR, PartsEngine_GetTextBoxSelectR),
|
||||
HLL_TODO_EXPORT(GetTextBoxSelectG, PartsEngine_GetTextBoxSelectG),
|
||||
HLL_TODO_EXPORT(GetTextBoxSelectB, PartsEngine_GetTextBoxSelectB),
|
||||
HLL_TODO_EXPORT(SetTextBoxCGName, PartsEngine_SetTextBoxCGName),
|
||||
HLL_TODO_EXPORT(GetTextBoxCGName, PartsEngine_GetTextBoxCGName),
|
||||
HLL_TODO_EXPORT(OpenTextBoxIME, PartsEngine_OpenTextBoxIME),
|
||||
HLL_TODO_EXPORT(CloseTextBoxIME, PartsEngine_CloseTextBoxIME),
|
||||
HLL_TODO_EXPORT(SetListBoxSize, PartsEngine_SetListBoxSize),
|
||||
HLL_TODO_EXPORT(SetListBoxLineHeight, PartsEngine_SetListBoxLineHeight),
|
||||
HLL_TODO_EXPORT(GetListBoxLineHeight, PartsEngine_GetListBoxLineHeight),
|
||||
HLL_TODO_EXPORT(SetListBoxMargin, PartsEngine_SetListBoxMargin),
|
||||
HLL_TODO_EXPORT(GetListBoxWidthMargin, PartsEngine_GetListBoxWidthMargin),
|
||||
HLL_TODO_EXPORT(GetListBoxHeightMargin, PartsEngine_GetListBoxHeightMargin),
|
||||
HLL_TODO_EXPORT(SetListBoxCGName, PartsEngine_SetListBoxCGName),
|
||||
HLL_TODO_EXPORT(GetListBoxCGName, PartsEngine_GetListBoxCGName),
|
||||
HLL_TODO_EXPORT(SetListBoxScrollPos, PartsEngine_SetListBoxScrollPos),
|
||||
HLL_TODO_EXPORT(GetListBoxScrollPos, PartsEngine_GetListBoxScrollPos),
|
||||
HLL_TODO_EXPORT(AddListBoxItem, PartsEngine_AddListBoxItem),
|
||||
HLL_TODO_EXPORT(InsertListBoxItem, PartsEngine_InsertListBoxItem),
|
||||
HLL_TODO_EXPORT(SetListBoxItem, PartsEngine_SetListBoxItem),
|
||||
HLL_TODO_EXPORT(GetListBoxItemCount, PartsEngine_GetListBoxItemCount),
|
||||
HLL_TODO_EXPORT(GetListBoxItem, PartsEngine_GetListBoxItem),
|
||||
HLL_TODO_EXPORT(EraseListBoxItem, PartsEngine_EraseListBoxItem),
|
||||
HLL_TODO_EXPORT(ClearListBoxItem, PartsEngine_ClearListBoxItem),
|
||||
HLL_TODO_EXPORT(GetListBoxOnCursorItemIndex, PartsEngine_GetListBoxOnCursorItemIndex),
|
||||
HLL_TODO_EXPORT(GetListBoxOnCursorItem, PartsEngine_GetListBoxOnCursorItem),
|
||||
HLL_TODO_EXPORT(SetListBoxFontProperty, PartsEngine_SetListBoxFontProperty),
|
||||
HLL_TODO_EXPORT(GetListBoxFontProperty, PartsEngine_GetListBoxFontProperty),
|
||||
HLL_TODO_EXPORT(SetListBoxSelectIndex, PartsEngine_SetListBoxSelectIndex),
|
||||
HLL_TODO_EXPORT(GetListBoxSelectIndex, PartsEngine_GetListBoxSelectIndex),
|
||||
HLL_TODO_EXPORT(SetComboBoxSize, PartsEngine_SetComboBoxSize),
|
||||
HLL_TODO_EXPORT(SetComboBoxTextMargin, PartsEngine_SetComboBoxTextMargin),
|
||||
HLL_TODO_EXPORT(GetComboBoxTextWidthMargin, PartsEngine_GetComboBoxTextWidthMargin),
|
||||
HLL_TODO_EXPORT(GetComboBoxTextHeightMargin, PartsEngine_GetComboBoxTextHeightMargin),
|
||||
HLL_TODO_EXPORT(SetComboBoxCGName, PartsEngine_SetComboBoxCGName),
|
||||
HLL_TODO_EXPORT(GetComboBoxCGName, PartsEngine_GetComboBoxCGName),
|
||||
HLL_TODO_EXPORT(SetComboBoxText, PartsEngine_SetComboBoxText),
|
||||
HLL_TODO_EXPORT(GetComboBoxText, PartsEngine_GetComboBoxText),
|
||||
HLL_TODO_EXPORT(SetComboBoxFontProperty, PartsEngine_SetComboBoxFontProperty),
|
||||
HLL_TODO_EXPORT(GetComboBoxFontProperty, PartsEngine_GetComboBoxFontProperty),
|
||||
HLL_TODO_EXPORT(SetMultiTextBoxSize, PartsEngine_SetMultiTextBoxSize),
|
||||
HLL_TODO_EXPORT(SetMultiTextBoxFontProperty, PartsEngine_SetMultiTextBoxFontProperty),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxFontProperty, PartsEngine_GetMultiTextBoxFontProperty),
|
||||
HLL_TODO_EXPORT(SetMultiTextBoxText, PartsEngine_SetMultiTextBoxText),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxText, PartsEngine_GetMultiTextBoxText),
|
||||
HLL_TODO_EXPORT(SetMultiTextBoxMaxTextLength, PartsEngine_SetMultiTextBoxMaxTextLength),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxMaxTextLength, PartsEngine_GetMultiTextBoxMaxTextLength),
|
||||
HLL_TODO_EXPORT(SetMultiTextBoxSelectColor, PartsEngine_SetMultiTextBoxSelectColor),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxSelectR, PartsEngine_GetMultiTextBoxSelectR),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxSelectG, PartsEngine_GetMultiTextBoxSelectG),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxSelectB, PartsEngine_GetMultiTextBoxSelectB),
|
||||
HLL_TODO_EXPORT(SetMultiTextBoxCGName, PartsEngine_SetMultiTextBoxCGName),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxCGName, PartsEngine_GetMultiTextBoxCGName),
|
||||
HLL_EXPORT(SetLayoutBoxLayoutType, PE_SetLayoutBoxLayoutType),
|
||||
HLL_EXPORT(GetLayoutBoxLayoutType, PE_GetLayoutBoxLayoutType),
|
||||
HLL_EXPORT(SetLayoutBoxReturn, PE_SetLayoutBoxReturn),
|
||||
HLL_EXPORT(IsLayoutBoxReturn, PE_IsLayoutBoxReturn),
|
||||
HLL_EXPORT(GetLayoutBoxReturnSize, PE_GetLayoutBoxReturnSize),
|
||||
HLL_EXPORT(SetLayoutBoxAlign, PE_SetLayoutBoxAlign),
|
||||
HLL_EXPORT(GetLayoutBoxAlign, PE_GetLayoutBoxAlign),
|
||||
HLL_EXPORT(Parts_SetPartsCG, PE_SetPartsCG),
|
||||
HLL_EXPORT(Parts_GetPartsCGName, PE_GetPartsCGName),
|
||||
HLL_EXPORT(Parts_SetPartsCGSurfaceArea, PE_SetPartsCGSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetLoopCG, PE_SetLoopCG),
|
||||
HLL_EXPORT(Parts_SetLoopCGSurfaceArea, PE_SetLoopCGSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetText, PE_SetText),
|
||||
HLL_EXPORT(Parts_AddPartsText, PE_AddPartsText),
|
||||
HLL_TODO_EXPORT(Parts_DeletePartsTopTextLine, PartsEngine_Parts_DeletePartsTopTextLine),
|
||||
HLL_EXPORT(Parts_SetPartsTextSurfaceArea, PE_SetPartsTextSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsTextHighlight, PartsEngine_Parts_SetPartsTextHighlight),
|
||||
HLL_TODO_EXPORT(Parts_AddPartsTextHighlight, PartsEngine_Parts_AddPartsTextHighlight),
|
||||
HLL_TODO_EXPORT(Parts_ClearPartsTextHighlight, PartsEngine_Parts_ClearPartsTextHighlight),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsTextCountReturn, PartsEngine_Parts_SetPartsTextCountReturn),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsTextCountReturn, PartsEngine_Parts_GetPartsTextCountReturn),
|
||||
HLL_EXPORT(Parts_SetFont, PE_SetFont),
|
||||
HLL_EXPORT(Parts_SetPartsFontType, PE_SetPartsFontType),
|
||||
HLL_EXPORT(Parts_SetPartsFontSize, PE_SetPartsFontSize),
|
||||
HLL_EXPORT(Parts_SetPartsFontColor, PE_SetPartsFontColor),
|
||||
HLL_EXPORT(Parts_SetPartsFontBoldWeight, PE_SetPartsFontBoldWeight),
|
||||
HLL_EXPORT(Parts_SetPartsFontEdgeColor, PE_SetPartsFontEdgeColor),
|
||||
HLL_EXPORT(Parts_SetPartsFontEdgeWeight, PE_SetPartsFontEdgeWeight),
|
||||
HLL_EXPORT(Parts_SetTextCharSpace, PE_SetTextCharSpace),
|
||||
HLL_EXPORT(Parts_SetTextLineSpace, PE_SetTextLineSpace),
|
||||
HLL_EXPORT(Parts_SetHGaugeCG, PE_SetHGaugeCG),
|
||||
HLL_EXPORT(Parts_SetHGaugeRate, PE_SetHGaugeRate),
|
||||
HLL_EXPORT(Parts_SetVGaugeCG, PE_SetVGaugeCG),
|
||||
HLL_EXPORT(Parts_SetVGaugeRate, PE_SetVGaugeRate),
|
||||
HLL_EXPORT(Parts_SetHGaugeSurfaceArea, PE_SetHGaugeSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetVGaugeSurfaceArea, PE_SetVGaugeSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetNumeralCG, PE_SetNumeralCG),
|
||||
HLL_EXPORT(Parts_SetNumeralLinkedCGNumberWidthWidthList, PE_SetNumeralLinkedCGNumberWidthWidthList),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralFont, PartsEngine_Parts_SetNumeralFont),
|
||||
HLL_EXPORT(Parts_SetNumeralNumber, PE_SetNumeralNumber),
|
||||
HLL_EXPORT(Parts_SetNumeralShowComma, PE_SetNumeralShowComma),
|
||||
HLL_EXPORT(Parts_SetNumeralSpace, PE_SetNumeralSpace),
|
||||
HLL_EXPORT(Parts_SetNumeralLength, PE_SetNumeralLength),
|
||||
HLL_EXPORT(Parts_SetNumeralSurfaceArea, PE_SetNumeralSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetPartsRectangleDetectionSize, PE_SetPartsRectangleDetectionSize),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsRectangleDetectionSurfaceArea, PartsEngine_Parts_SetPartsRectangleDetectionSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetPartsCGDetectionSize, PE_SetPartsCGDetectionSize),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsCGDetectionSurfaceArea, PartsEngine_Parts_SetPartsCGDetectionSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetPartsFlat, PE_SetPartsFlat),
|
||||
HLL_EXPORT(Parts_IsPartsFlatEnd, PE_IsPartsFlatEnd),
|
||||
HLL_EXPORT(Parts_GetPartsFlatCurrentFrameNumber, PE_GetPartsFlatCurrentFrameNumber),
|
||||
HLL_EXPORT(Parts_BackPartsFlatBeginFrame, PE_BackPartsFlatBeginFrame),
|
||||
HLL_EXPORT(Parts_StepPartsFlatFinalFrame, PE_StepPartsFlatFinalFrame),
|
||||
HLL_EXPORT(Parts_SetPartsFlatSurfaceArea, PE_SetPartsFlatSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetPartsFlatAndStop, PE_SetPartsFlatAndStop),
|
||||
HLL_EXPORT(Parts_StopPartsFlat, PE_StopPartsFlat),
|
||||
HLL_EXPORT(Parts_StartPartsFlat, PE_StartPartsFlat),
|
||||
HLL_EXPORT(Parts_GoFramePartsFlat, PE_GoFramePartsFlat),
|
||||
HLL_EXPORT(Parts_GetPartsFlatEndFrame, PE_GetPartsFlatEndFrame),
|
||||
HLL_EXPORT(Parts_ExistsFlatFile, PE_ExistsFlatFile),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsFlatDataInfo, PartsEngine_Parts_GetPartsFlatDataInfo),
|
||||
HLL_TODO_EXPORT(Parts_ChangeFlatCG, PartsEngine_Parts_ChangeFlatCG),
|
||||
HLL_TODO_EXPORT(Parts_ChangeFlatSound, PartsEngine_Parts_ChangeFlatSound),
|
||||
HLL_EXPORT(Parts_ClearPartsConstructionProcess, PE_ClearPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCreateToPartsConstructionProcess, PE_AddCreateToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCreatePixelOnlyToPartsConstructionProcess, PE_AddCreatePixelOnlyToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCreateCGToProcess, PE_AddCreateCGToProcess),
|
||||
HLL_EXPORT(Parts_AddFillToPartsConstructionProcess, PE_AddFillToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillAlphaColorToPartsConstructionProcess, PE_AddFillAlphaColorToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillAMapToPartsConstructionProcess, PE_AddFillAMapToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillWithAlphaToPartsConstructionProcess, PE_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddFillGradationHorizonToPartsConstructionProcess, PartsEngine_Parts_AddFillGradationHorizonToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddDrawRectToPartsConstructionProcess, PE_AddDrawRectToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddDrawCutCGToPartsConstructionProcess, PE_AddDrawCutCGToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCopyCutCGToPartsConstructionProcess, PE_AddCopyCutCGToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddGrayFilterToPartsConstructionProcess, PE_AddGrayFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddAddFilterToPartsConstructionProcess, PartsEngine_Parts_AddAddFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddMulFilterToPartsConstructionProcess, PartsEngine_Parts_AddMulFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddDrawLineToPartsConstructionProcess, PartsEngine_Parts_AddDrawLineToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_BuildPartsConstructionProcess, PE_BuildPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddDrawTextToPartsConstructionProcess, PE_AddDrawTextToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCopyTextToPartsConstructionProcess, PE_AddCopyTextToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_SetPartsConstructionSurfaceArea, PE_SetPartsConstructionSurfaceArea),
|
||||
HLL_EXPORT(Parts_CreateParts3DLayerPluginID, PE_CreateParts3DLayerPluginID),
|
||||
HLL_EXPORT(Parts_GetParts3DLayerPluginID, PE_GetParts3DLayerPluginID),
|
||||
HLL_EXPORT(Parts_ReleaseParts3DLayerPluginID, PE_ReleaseParts3DLayerPluginID),
|
||||
HLL_EXPORT(Parts_SetPassCursor, PE_SetPassCursor),
|
||||
HLL_EXPORT(Parts_SetClickable, PE_SetClickable),
|
||||
HLL_TODO_EXPORT(Parts_SetResetTimerByChangeInputStatus, PartsEngine_Parts_SetResetTimerByChangeInputStatus),
|
||||
HLL_EXPORT(Parts_SetDrag, PE_SetDrag),
|
||||
HLL_EXPORT(Parts_SetParentPartsNumber, PE_SetParentPartsNumber),
|
||||
HLL_EXPORT(Parts_SetInputState, PE_SetInputState),
|
||||
HLL_EXPORT(Parts_SetOnCursorShowLinkPartsNumber, PE_SetOnCursorShowLinkPartsNumber),
|
||||
HLL_TODO_EXPORT(Parts_SetSoundNumber, PartsEngine_Parts_SetSoundNumber),
|
||||
HLL_EXPORT(Parts_SetPartsPixelDecide, PE_SetPartsPixelDecide),
|
||||
HLL_EXPORT(Parts_GetPartsPassCursor, PE_GetPartsPassCursor),
|
||||
HLL_EXPORT(Parts_GetPartsClickable, PE_GetPartsClickable),
|
||||
HLL_TODO_EXPORT(Parts_GetResetTimerByChangeInputStatus, PartsEngine_Parts_GetResetTimerByChangeInputStatus),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsDrag, PartsEngine_Parts_GetPartsDrag),
|
||||
HLL_TODO_EXPORT(Parts_GetParentPartsNumber, PartsEngine_Parts_GetParentPartsNumber),
|
||||
HLL_EXPORT(Parts_GetInputState, PE_GetInputState),
|
||||
HLL_TODO_EXPORT(Parts_GetOnCursorShowLinkPartsNumber, PartsEngine_Parts_GetOnCursorShowLinkPartsNumber),
|
||||
HLL_TODO_EXPORT(Parts_GetSoundNumber, PartsEngine_Parts_GetSoundNumber),
|
||||
HLL_TODO_EXPORT(Parts_IsPartsPixelDecide, PartsEngine_Parts_IsPartsPixelDecide),
|
||||
HLL_EXPORT(Parts_IsCursorIn, PE_IsCursorIn),
|
||||
HLL_TODO_EXPORT(SaveParts, PartsEngine_SaveParts),
|
||||
HLL_TODO_EXPORT(LoadParts, PartsEngine_LoadParts)
|
||||
);
|
||||
|
||||
static struct ain_hll_function *get_fun(int libno, const char *name)
|
||||
@@ -246,4 +918,13 @@ static void PartsEngine_PreLink(void)
|
||||
static_library_replace(&lib_PartsEngine, "AddCopyCutCGToPartsConstructionProcess",
|
||||
PE_AddCopyCutCGToPartsConstructionProcess);
|
||||
}
|
||||
fun = get_fun(libno, "Update");
|
||||
if (fun && fun->nr_arguments == 5) {
|
||||
static_library_replace(&lib_PartsEngine, "Update",
|
||||
PartsEngine_Update_Pascha3PC);
|
||||
}
|
||||
if (get_fun(libno, "AddController")) {
|
||||
PE_enable_multi_controller();
|
||||
}
|
||||
parts_func_uses_rance9_ids = ain_get_function(ain, "PARTS_GetLayerIndex") < 0;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zlib.h>
|
||||
#include "cJSON.h"
|
||||
|
||||
#include "system4.h"
|
||||
@@ -26,6 +25,7 @@
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "xsystem4.h"
|
||||
@@ -97,10 +97,11 @@ static void write_register(unsigned handle)
|
||||
}
|
||||
|
||||
size_t raw_size = buf.index;
|
||||
unsigned long compressed_size = compressBound(raw_size);
|
||||
size_t compressed_size = zlib_compress_bound(raw_size);
|
||||
uint8_t *compressed = xmalloc(compressed_size);
|
||||
int r = compress2(compressed, &compressed_size, buf.buf, raw_size, Z_BEST_COMPRESSION);
|
||||
if (r != Z_OK) {
|
||||
compressed_size = zlib_compress(compressed, compressed_size, buf.buf,
|
||||
raw_size, ZLIB_BEST_COMPRESSION);
|
||||
if (!compressed_size) {
|
||||
WARNING("Failed to compress PassRegister file: %s", display_utf0(reg->filename));
|
||||
goto end;
|
||||
}
|
||||
@@ -136,15 +137,14 @@ static void read_register_psr(struct passregister *reg, uint8_t *data, size_t da
|
||||
WARNING("Unexpected PassRegister version %d", version);
|
||||
return;
|
||||
}
|
||||
unsigned long raw_size = LittleEndian_getDW(data, 8);
|
||||
size_t raw_size = LittleEndian_getDW(data, 8);
|
||||
size_t compressed_size = LittleEndian_getDW(data, 12);
|
||||
if (PSR_HEADER_SIZE + compressed_size != data_len) {
|
||||
WARNING("Broken PassRegister file");
|
||||
return;
|
||||
}
|
||||
uint8_t *raw = xmalloc(raw_size);
|
||||
int r = uncompress(raw, &raw_size, data + PSR_HEADER_SIZE, compressed_size);
|
||||
if (r != Z_OK) {
|
||||
if (!zlib_decompress_exact(raw, raw_size, data + PSR_HEADER_SIZE, compressed_size)) {
|
||||
WARNING("PassRegister: failed to uncompress");
|
||||
free(raw);
|
||||
return;
|
||||
|
||||
+542
-111
File diff suppressed because it is too large
Load Diff
@@ -1031,6 +1031,7 @@ int sact_TRANS_Begin(int type)
|
||||
|
||||
int sact_TRANS_Update(float rate)
|
||||
{
|
||||
sprite_call_plugins();
|
||||
if (scene_is_dirty)
|
||||
scene_render();
|
||||
return effect_update(rate);
|
||||
|
||||
@@ -43,6 +43,105 @@ struct sr_text_properties {
|
||||
struct text_style ts;
|
||||
};
|
||||
|
||||
// A font_size that uses FNL first and Gothic for missing glyphs.
|
||||
struct sr_fallback_font_size {
|
||||
struct font_size super;
|
||||
struct font_size *fnl;
|
||||
struct font_size *gothic;
|
||||
struct sr_fallback_font_size *next;
|
||||
};
|
||||
|
||||
#define SR_FALLBACK_FONT_SCALE 0.75f
|
||||
#define SR_FALLBACK_VERTICAL_SHIFT_RATIO 0.65f
|
||||
|
||||
static struct sr_fallback_font_size *sr_fallback_sizes;
|
||||
|
||||
static uint32_t sjis_code_to_unicode(uint32_t code)
|
||||
{
|
||||
char sjis[] = { code >> 8, code, 0 };
|
||||
const char *p = code > 0xff ? sjis : sjis + 1;
|
||||
int unicode;
|
||||
sjis_char2unicode(p, &unicode);
|
||||
return unicode;
|
||||
}
|
||||
|
||||
static bool sr_fallback_get_glyph(struct font_size *_size, struct glyph *glyph,
|
||||
uint32_t code, enum font_weight weight)
|
||||
{
|
||||
struct sr_fallback_font_size *size = (struct sr_fallback_font_size*)_size;
|
||||
if (size->fnl->font->get_glyph(size->fnl, glyph, code, weight))
|
||||
return true;
|
||||
|
||||
if (!size->gothic->font->get_glyph(size->gothic, glyph,
|
||||
sjis_code_to_unicode(code), weight))
|
||||
return false;
|
||||
// Align the gothic glyph toward the bottom of the FNL line.
|
||||
glyph->rect.y += size->fnl->y_offset - size->gothic->y_offset;
|
||||
glyph->rect.y -= lroundf((size->fnl->size - size->gothic->size)
|
||||
* SR_FALLBACK_VERTICAL_SHIFT_RATIO);
|
||||
return true;
|
||||
}
|
||||
|
||||
static float sr_fallback_size_char(struct font_size *_size, uint32_t code)
|
||||
{
|
||||
struct sr_fallback_font_size *size = (struct sr_fallback_font_size*)_size;
|
||||
float width = size->fnl->font->size_char(size->fnl, code);
|
||||
if (width != 0.0f)
|
||||
return width;
|
||||
return size->gothic->font->size_char(size->gothic,
|
||||
sjis_code_to_unicode(code));
|
||||
}
|
||||
|
||||
static float sr_fallback_size_char_kerning(struct font_size *size, uint32_t code,
|
||||
uint32_t code_next)
|
||||
{
|
||||
struct sr_fallback_font_size *fallback = (struct sr_fallback_font_size*)size;
|
||||
float width = fallback->fnl->font->size_char_kerning(fallback->fnl,
|
||||
code, code_next);
|
||||
if (width != 0.0f)
|
||||
return width;
|
||||
return fallback->gothic->font->size_char_kerning(fallback->gothic,
|
||||
sjis_code_to_unicode(code), sjis_code_to_unicode(code_next));
|
||||
}
|
||||
|
||||
static struct font sr_fallback_font = {
|
||||
.charmap = CHARMAP_SJIS,
|
||||
.get_glyph = sr_fallback_get_glyph,
|
||||
.size_char = sr_fallback_size_char,
|
||||
.size_char_kerning = sr_fallback_size_char_kerning,
|
||||
};
|
||||
|
||||
static struct font_size *sr_get_fallback_size(struct font_size *fnl)
|
||||
{
|
||||
// One wrapper per FNL size.
|
||||
for (struct sr_fallback_font_size *size = sr_fallback_sizes; size;
|
||||
size = size->next) {
|
||||
if (size->fnl == fnl)
|
||||
return &size->super;
|
||||
}
|
||||
|
||||
struct sr_fallback_font_size *size = xcalloc(1, sizeof(*size));
|
||||
size->super.size = fnl->size;
|
||||
size->super.y_offset = fnl->y_offset;
|
||||
size->super.font = &sr_fallback_font;
|
||||
size->fnl = fnl;
|
||||
size->gothic = gfx_font_get_size(FONT_GOTHIC,
|
||||
fnl->size * SR_FALLBACK_FONT_SCALE);
|
||||
size->next = sr_fallback_sizes;
|
||||
sr_fallback_sizes = size;
|
||||
return &size->super;
|
||||
}
|
||||
|
||||
static void sr_prepare_text_style(struct text_style *ts)
|
||||
{
|
||||
if (ts->font_size)
|
||||
return;
|
||||
struct font_size *size = gfx_font_get_size(ts->face, ts->size);
|
||||
// Install the wrapper only on FNL text styles.
|
||||
ts->font_size = size->font->charmap == CHARMAP_SJIS
|
||||
? sr_get_fallback_size(size) : size;
|
||||
}
|
||||
|
||||
static struct sr_text_properties default_text_properties = {
|
||||
.x = 0.0,
|
||||
.y = 0,
|
||||
@@ -134,6 +233,7 @@ static void SengokuRanceFont_SetEdgeWidth(float w)
|
||||
|
||||
static float SengokuRanceFont_GetTextWidth(struct string *str)
|
||||
{
|
||||
sr_prepare_text_style(¤t_text_properties.ts);
|
||||
return gfx_size_text(¤t_text_properties.ts, str->text);
|
||||
}
|
||||
|
||||
@@ -326,6 +426,7 @@ static float sp_text_draw(struct sact_sprite *sp, struct sr_text_properties *tp,
|
||||
if (tp->ts.size < 8) {
|
||||
tp->ts.size = 8;
|
||||
}
|
||||
sr_prepare_text_style(&tp->ts);
|
||||
|
||||
if (!sp->text.texture.handle) {
|
||||
gfx_init_texture_rgba(&sp->text.texture, sp->rect.w, sp->rect.h, COLOR(0, 0, 0, 0));
|
||||
|
||||
@@ -45,13 +45,14 @@ HLL_WARN_UNIMPLEMENTED( , void, StoatSpriteEngine, SetVolumeMixerMasterGroupNum,
|
||||
HLL_WARN_UNIMPLEMENTED( , void, StoatSpriteEngine, SetVolumeMixerSEGroupNum, int n);
|
||||
HLL_WARN_UNIMPLEMENTED( , void, StoatSpriteEngine, SetVolumeMixerBGMGroupNum, int n);
|
||||
|
||||
static int StoatSpriteEngine_SP_SetDrawMethod(int sp_no, int method)
|
||||
int StoatSpriteEngine_SP_SetDrawMethod(int sp_no, int method)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return 0;
|
||||
switch (method) {
|
||||
case 0: return sprite_set_draw_method(sp, DRAW_METHOD_NORMAL);
|
||||
case 1: return sprite_set_draw_method(sp, DRAW_METHOD_ADDITIVE);
|
||||
case 2: return sprite_set_draw_method(sp, DRAW_METHOD_MULTIPLY);
|
||||
case 3: return sprite_set_draw_method(sp, DRAW_METHOD_SCREEN);
|
||||
default:
|
||||
WARNING("unknown draw method: %d", method);
|
||||
@@ -59,7 +60,7 @@ static int StoatSpriteEngine_SP_SetDrawMethod(int sp_no, int method)
|
||||
}
|
||||
}
|
||||
|
||||
static int StoatSpriteEngine_SP_GetDrawMethod(int sp_no)
|
||||
int StoatSpriteEngine_SP_GetDrawMethod(int sp_no)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sp_no);
|
||||
if (!sp) return DRAW_METHOD_NORMAL;
|
||||
@@ -67,6 +68,7 @@ static int StoatSpriteEngine_SP_GetDrawMethod(int sp_no)
|
||||
switch (method) {
|
||||
case DRAW_METHOD_NORMAL: return 0;
|
||||
case DRAW_METHOD_ADDITIVE: return 1;
|
||||
case DRAW_METHOD_MULTIPLY: return 2;
|
||||
case DRAW_METHOD_SCREEN: return 3;
|
||||
default:
|
||||
WARNING("unknown draw method: %d", method);
|
||||
@@ -111,7 +113,7 @@ bool StoatSpriteEngine_SP_SetTextSprite(int sp_no, struct string *text)
|
||||
|
||||
char s[3];
|
||||
extract_sjis_char(text->text, s);
|
||||
int w = ceilf(text_style_width(&text_sprite_ts, s));
|
||||
int w = ceilf(gfx_size_char(&text_sprite_ts, s));
|
||||
int h = text_sprite_ts.size;
|
||||
// XXX: System40.exe lies about the height of half-width characters.
|
||||
// E.g. a size 64 letter "H" is reported as 32 pixels tall.
|
||||
@@ -144,7 +146,7 @@ void StoatSpriteEngine_SP_SetTextSpriteColor(int r, int g, int b)
|
||||
|
||||
void StoatSpriteEngine_SP_SetTextSpriteBoldWeight(float weight)
|
||||
{
|
||||
//NOTICE("StoatSpriteEngine.SP_SetTextSpriteBoldWeight(%f)", weight);
|
||||
text_sprite_ts.bold_width = weight;
|
||||
}
|
||||
|
||||
void StoatSpriteEngine_SP_SetTextSpriteEdgeWeight(float weight)
|
||||
@@ -186,8 +188,8 @@ float StoatSpriteEngine_FPS_Get(void)
|
||||
HLL_WARN_UNIMPLEMENTED( , void, StoatSpriteEngine, VIEW_SetOffsetPos, int x, int y);
|
||||
|
||||
static bool keep_previous_view = true;
|
||||
HLL_WARN_UNIMPLEMENTED((keep_previous_view = on, true), bool, StoatSpriteEngine, KeepPreviousView_SetMode, bool on);
|
||||
HLL_WARN_UNIMPLEMENTED(keep_previous_view, bool, StoatSpriteEngine, KeepPreviousView_GetMode);
|
||||
HLL_QUIET_UNIMPLEMENTED((keep_previous_view = on, true), bool, StoatSpriteEngine, KeepPreviousView_SetMode, bool on);
|
||||
HLL_QUIET_UNIMPLEMENTED(keep_previous_view, bool, StoatSpriteEngine, KeepPreviousView_GetMode);
|
||||
|
||||
/*
|
||||
* NOTE: The multisprite "type" alters the behavior of the multisprite functions.
|
||||
|
||||
+45
-7
@@ -69,11 +69,19 @@ HLL_WARN_UNIMPLEMENTED(false, bool, SystemService, AddURLMenu, struct string *ti
|
||||
|
||||
static bool SystemService_IsFullScreen(void)
|
||||
{
|
||||
return false;
|
||||
return gfx_is_fullscreen();
|
||||
}
|
||||
|
||||
static bool SystemService_ChangeNormalScreen(void)
|
||||
{
|
||||
return gfx_set_fullscreen(false);
|
||||
}
|
||||
|
||||
static bool SystemService_ChangeFullScreen(void)
|
||||
{
|
||||
return gfx_set_fullscreen(true);
|
||||
}
|
||||
|
||||
//bool SystemService_ChangeNormalScreen(void);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, SystemService, ChangeFullScreen);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, SystemService, InitMainWindowPosAndSize);
|
||||
|
||||
//static bool SystemService_UpdateView(void);
|
||||
@@ -103,6 +111,8 @@ static bool SystemService_SetHideMouseCursorByGame(bool hide)
|
||||
//bool SystemService_GetHideMouseCursorByGame(void);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, SystemService, SetUsePower2Texture, bool use);
|
||||
//bool SystemService_GetUsePower2Texture(void);
|
||||
HLL_WARN_UNIMPLEMENTED(true, bool, SystemService, SetAntiAliasingMode, int mode);
|
||||
//int SystemService_GetAntiAliasingMode(void);
|
||||
|
||||
enum window_settings_asect_ratio {
|
||||
ASPECT_RATIO_NORMAL,
|
||||
@@ -325,7 +335,10 @@ static void SystemService_OpenPlayingManual(void) {
|
||||
if (!SystemService_IsExistPlayingManual()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
const int COMMAND_OPEN_PLAYING_MANUAL = 0x8000;
|
||||
SDL_AndroidSendMessage(COMMAND_OPEN_PLAYING_MANUAL, 0);
|
||||
#else
|
||||
char *filename = get_manual_filename();
|
||||
|
||||
char *real_path = realpath_utf8(filename);
|
||||
@@ -352,6 +365,7 @@ static void SystemService_OpenPlayingManual(void) {
|
||||
|
||||
free(url);
|
||||
free(path_component);
|
||||
#endif
|
||||
}
|
||||
|
||||
//static bool SystemService_IsExistSystemMessage(void);
|
||||
@@ -362,26 +376,45 @@ static void SystemService_RestrainScreensaver(void) { }
|
||||
|
||||
//static int SystemService_Debug_GetUseVideoMemorySize(void);
|
||||
|
||||
// Rance 01
|
||||
static void SystemService_Rance0123456789(struct string **text)
|
||||
{
|
||||
*text = cstr_to_string("-RANCE010ECNAR-"); // ???
|
||||
*text = cstr_to_string("-RANCE010ECNAR-");
|
||||
}
|
||||
|
||||
// Rance 01 trial edition
|
||||
static void SystemService_XXXXX01XXXXXXXX(struct string **text)
|
||||
{
|
||||
*text = cstr_to_string("RANCE01RANCEKAKKOII");
|
||||
}
|
||||
|
||||
// Drapeko
|
||||
static void SystemService_Test(struct string **text)
|
||||
{
|
||||
*text = cstr_to_string("DELETE ALL 758490275489207548093");
|
||||
}
|
||||
|
||||
// Drapeko trial edition
|
||||
static void SystemService_DRPKT(struct string **text)
|
||||
{
|
||||
*text = cstr_to_string("DRPKT QWERTY NUFUAUEO 75849027582754829");
|
||||
}
|
||||
|
||||
// Rance 9
|
||||
static void SystemService_Rance96161988(struct string **text) {
|
||||
*text = cstr_to_string("=Rance99/RANCE99=");
|
||||
}
|
||||
|
||||
// Pascha3 Plus Contents
|
||||
static void SystemService_XXX(struct string **text) {
|
||||
*text = cstr_to_string("FORMAT HDD ERASE 578205024758284076520478254092784789752384758204687293");
|
||||
}
|
||||
|
||||
// Busou Shoujotai Blade Briders
|
||||
static void SystemService_GetSystemBufferName(struct string **text) {
|
||||
*text = cstr_to_string("xxx4wxx:xxxx-xxxxxfx5x3");
|
||||
}
|
||||
|
||||
static void SystemService_PreLink(void);
|
||||
|
||||
static void SystemService_ModuleInit(void)
|
||||
@@ -404,7 +437,7 @@ HLL_LIBRARY(SystemService,
|
||||
HLL_EXPORT(GetGameName, SystemService_GetGameName),
|
||||
HLL_EXPORT(AddURLMenu, SystemService_AddURLMenu),
|
||||
HLL_EXPORT(IsFullScreen, SystemService_IsFullScreen),
|
||||
HLL_TODO_EXPORT(ChangeNormalScreen, SystemService_ChangeNormalScreen),
|
||||
HLL_EXPORT(ChangeNormalScreen, SystemService_ChangeNormalScreen),
|
||||
HLL_EXPORT(ChangeFullScreen, SystemService_ChangeFullScreen),
|
||||
HLL_EXPORT(InitMainWindowPosAndSize, SystemService_InitMainWindowPosAndSize),
|
||||
HLL_EXPORT(UpdateView, SystemService_UpdateView),
|
||||
@@ -415,6 +448,8 @@ HLL_LIBRARY(SystemService,
|
||||
HLL_TODO_EXPORT(GetHideMouseCursorByGame, SystemService_GetHideMouseCursorByGame),
|
||||
HLL_EXPORT(SetUsePower2Texture, SystemService_SetUsePower2Texture),
|
||||
HLL_TODO_EXPORT(GetUsePower2Texture, SystemService_GetUsePower2Texture),
|
||||
HLL_EXPORT(SetAntiAliasingMode, SystemService_SetAntiAliasingMode),
|
||||
HLL_TODO_EXPORT(GetAntiAliasingMode, SystemService_GetAntiAliasingMode),
|
||||
HLL_EXPORT(SetWindowSetting, SystemService_SetWindowSetting),
|
||||
HLL_EXPORT(GetWindowSetting, SystemService_GetWindowSetting),
|
||||
HLL_EXPORT(SetMouseCursorConfig, SystemService_SetMouseCursorConfig),
|
||||
@@ -433,8 +468,11 @@ HLL_LIBRARY(SystemService,
|
||||
HLL_TODO_EXPORT(Debug_GetUseVideoMemorySize, SystemService_Debug_GetUseVideoMemorySize),
|
||||
HLL_EXPORT(Rance0123456789, SystemService_Rance0123456789),
|
||||
HLL_EXPORT(XXXXX01XXXXXXXX, SystemService_XXXXX01XXXXXXXX),
|
||||
HLL_EXPORT(GetSystemBufferName, SystemService_GetSystemBufferName),
|
||||
HLL_EXPORT(XXX, SystemService_XXX),
|
||||
HLL_EXPORT(Test, SystemService_Test),
|
||||
HLL_EXPORT(DRPKT, SystemService_DRPKT)
|
||||
HLL_EXPORT(DRPKT, SystemService_DRPKT),
|
||||
HLL_EXPORT(Rance96161988, SystemService_Rance96161988)
|
||||
);
|
||||
|
||||
static struct ain_hll_function *get_fun(int libno, const char *name)
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "vm/heap.h"
|
||||
@@ -83,9 +82,9 @@ static uint8_t *read_compressed_data(FILE *fp, uint32_t *size)
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
unsigned long raw_size = LittleEndian_getDW(buf, 0);
|
||||
size_t raw_size = LittleEndian_getDW(buf, 0);
|
||||
void *raw = malloc(raw_size);
|
||||
if (uncompress(raw, &raw_size, buf + 4, *size - 4) != Z_OK) {
|
||||
if (!zlib_decompress_exact(raw, raw_size, buf + 4, *size - 4)) {
|
||||
WARNING("uncompress failed");
|
||||
free(raw);
|
||||
free(buf);
|
||||
|
||||
+1
-11
@@ -80,19 +80,9 @@ static bool vsf_write_type(enum vsf_valuetype type)
|
||||
return vsf_write(&b, 1);
|
||||
}
|
||||
|
||||
static char *process_filename(struct string *filename)
|
||||
{
|
||||
char *u = sjis2utf(filename->text, filename->size);
|
||||
for (int i = 0; u[i]; i++) {
|
||||
if (u[i] == '\\')
|
||||
u[i] = '/';
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
static bool vsfile_open(struct string *filename, bool read)
|
||||
{
|
||||
char *u = process_filename(filename);
|
||||
char *u = gamedir_path(filename->text);
|
||||
if (vs_file) {
|
||||
WARNING("VSFile is already opened");
|
||||
fclose(vs_file);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
|
||||
static int ValueEncryption_Get(int code, int backup_code, int xor_key)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
static void ValueEncryption_Set(int value, int *code, int *backup_code, int xor_key)
|
||||
{
|
||||
*code = *backup_code = value;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(ValueEncryption,
|
||||
HLL_EXPORT(Get, ValueEncryption_Get),
|
||||
HLL_EXPORT(Set, ValueEncryption_Set)
|
||||
);
|
||||
+1
-1
@@ -39,7 +39,7 @@ static int vmSound_LoadMemory(int channel, int memory)
|
||||
struct archive_data *dfile = chr_loader_get_blob_as_archive_data(memory);
|
||||
if (!dfile)
|
||||
return 0;
|
||||
return wav_prepare_from_archive_data(channel, dfile);
|
||||
return wav_prepare_from_archive_data(channel, AUDIO_NO_METADATA, dfile);
|
||||
}
|
||||
|
||||
static void vmSound_Unload(int channel, int length)
|
||||
|
||||
+48
-11
@@ -24,6 +24,7 @@
|
||||
#include "input.h"
|
||||
#include "scene.h"
|
||||
#include "vm.h"
|
||||
#include "xsystem4.h"
|
||||
#include "debugger.h"
|
||||
|
||||
#ifndef M_PI
|
||||
@@ -112,6 +113,7 @@ static enum sact_keycode sdl_keytable[] = {
|
||||
[SDL_SCANCODE_KP_MINUS] = VK_SUBTRACT,
|
||||
[SDL_SCANCODE_KP_PERIOD] = VK_DECIMAL,
|
||||
[SDL_SCANCODE_KP_DIVIDE] = VK_DIVIDE,
|
||||
[SDL_SCANCODE_KP_ENTER] = VK_RETURN,
|
||||
[SDL_SCANCODE_F1] = VK_F1,
|
||||
[SDL_SCANCODE_F2] = VK_F2,
|
||||
[SDL_SCANCODE_F3] = VK_F3,
|
||||
@@ -140,6 +142,29 @@ static enum sact_keycode sdl_keytable[] = {
|
||||
[SDL_SCANCODE_SCROLLLOCK] = VK_SCROLL,
|
||||
};
|
||||
|
||||
static enum sact_keycode sdl_to_sact_key(SDL_Scancode scancode, SDL_Keymod modifiers)
|
||||
{
|
||||
if (!(modifiers & KMOD_NUM)) {
|
||||
switch (scancode) {
|
||||
case SDL_SCANCODE_KP_0: return VK_INSERT;
|
||||
case SDL_SCANCODE_KP_1: return VK_END;
|
||||
case SDL_SCANCODE_KP_2: return VK_DOWN;
|
||||
case SDL_SCANCODE_KP_3: return VK_NEXT;
|
||||
case SDL_SCANCODE_KP_4: return VK_LEFT;
|
||||
case SDL_SCANCODE_KP_5: return VK_CLEAR;
|
||||
case SDL_SCANCODE_KP_6: return VK_RIGHT;
|
||||
case SDL_SCANCODE_KP_7: return VK_HOME;
|
||||
case SDL_SCANCODE_KP_8: return VK_UP;
|
||||
case SDL_SCANCODE_KP_9: return VK_PRIOR;
|
||||
case SDL_SCANCODE_KP_PERIOD: return VK_DELETE;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (scancode >= sizeof(sdl_keytable) / sizeof(*sdl_keytable))
|
||||
return 0;
|
||||
return sdl_keytable[scancode];
|
||||
}
|
||||
|
||||
bool mouse_focus = true;
|
||||
bool keyboard_focus = true;
|
||||
|
||||
@@ -231,13 +256,18 @@ bool mouse_show_cursor(bool show)
|
||||
return SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE) >= 0;
|
||||
}
|
||||
|
||||
static void(*input_handler)(const char*);
|
||||
static void(*editing_handler)(const char*, int, int);
|
||||
static void(*key_handler)(int);
|
||||
|
||||
static void key_event(SDL_KeyboardEvent *e, bool pressed)
|
||||
{
|
||||
if (e->keysym.scancode >= (sizeof(sdl_keytable)/sizeof(*sdl_keytable)))
|
||||
return;
|
||||
enum sact_keycode code = sdl_keytable[e->keysym.scancode];
|
||||
if (code)
|
||||
enum sact_keycode code = sdl_to_sact_key(e->keysym.scancode, e->keysym.mod);
|
||||
if (code) {
|
||||
key_state[code] = pressed;
|
||||
if (pressed && key_handler)
|
||||
key_handler(code);
|
||||
}
|
||||
}
|
||||
|
||||
static void mouse_event(SDL_MouseButtonEvent *e)
|
||||
@@ -495,9 +525,6 @@ static void controller_button_event(SDL_ControllerButtonEvent *e)
|
||||
joybutton_state[e->button] = e->state == SDL_PRESSED;
|
||||
}
|
||||
|
||||
static void(*input_handler)(const char*);
|
||||
static void(*editing_handler)(const char*, int, int);
|
||||
|
||||
void register_input_handler(void(*handler)(const char*))
|
||||
{
|
||||
input_handler = handler;
|
||||
@@ -518,6 +545,16 @@ void clear_editing_handler(void)
|
||||
editing_handler = NULL;
|
||||
}
|
||||
|
||||
void register_key_handler(void(*handler)(int))
|
||||
{
|
||||
key_handler = handler;
|
||||
}
|
||||
|
||||
void clear_key_handler(void)
|
||||
{
|
||||
key_handler = NULL;
|
||||
}
|
||||
|
||||
static void fire_deferred_events(void)
|
||||
{
|
||||
uint32_t now = SDL_GetTicks();
|
||||
@@ -608,13 +645,14 @@ void handle_events(void)
|
||||
case SDL_WINDOWEVENT:
|
||||
handle_window_event(&e);
|
||||
break;
|
||||
case SDL_APP_DIDENTERFOREGROUND:
|
||||
gfx_swap();
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
if (e.key.keysym.scancode == SDL_SCANCODE_F9) {
|
||||
vm_stack_trace();
|
||||
} else if (e.key.keysym.scancode == SDL_SCANCODE_F11) {
|
||||
uint32_t flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
bool fs = SDL_GetWindowFlags(sdl.window) & flag;
|
||||
SDL_SetWindowFullscreen(sdl.window, fs ? 0 : flag);
|
||||
gfx_toggle_fullscreen();
|
||||
} else if (e.key.keysym.scancode == SDL_SCANCODE_S) {
|
||||
if (e.key.keysym.mod & (KMOD_LALT | KMOD_RALT)) {
|
||||
screenshot_save();
|
||||
@@ -728,4 +766,3 @@ void handle_events(void)
|
||||
if (dbg_dap)
|
||||
dbg_dap_handle_messages();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +40,13 @@ xsystem4 = [version_h,
|
||||
'3d/collision.c',
|
||||
'3d/debug.c',
|
||||
'3d/model.c',
|
||||
'3d/mpr.c',
|
||||
'3d/parser.c',
|
||||
'3d/particle.c',
|
||||
'3d/reign.c',
|
||||
'3d/renderer.c',
|
||||
'3d/s3de.c',
|
||||
'3d/save.c',
|
||||
|
||||
'dungeon/dgn.c',
|
||||
'dungeon/dtx.c',
|
||||
@@ -59,7 +62,10 @@ xsystem4 = [version_h,
|
||||
'parts/construction.c',
|
||||
'parts/debug.c',
|
||||
'parts/flash.c',
|
||||
'parts/flat.c',
|
||||
'parts/input.c',
|
||||
'parts/layoutbox.c',
|
||||
'parts/message.c',
|
||||
'parts/motion.c',
|
||||
'parts/parts.c',
|
||||
'parts/render.c',
|
||||
@@ -68,6 +74,7 @@ xsystem4 = [version_h,
|
||||
|
||||
'hll/ACXLoader.c',
|
||||
'hll/ADVSYS.c',
|
||||
'hll/AFAFactory.c',
|
||||
'hll/AliceLogo.c',
|
||||
'hll/AliceLogo2.c',
|
||||
'hll/AliceLogo3.c',
|
||||
@@ -92,6 +99,7 @@ xsystem4 = [version_h,
|
||||
'hll/DALKEDemo.c',
|
||||
'hll/Data.c',
|
||||
'hll/DataFile.c',
|
||||
'hll/Discord.c',
|
||||
'hll/DrawDungeon.c',
|
||||
'hll/DrawEffect.c',
|
||||
'hll/DrawGraph.c',
|
||||
@@ -116,6 +124,7 @@ xsystem4 = [version_h,
|
||||
'hll/InputString.c',
|
||||
'hll/KiwiSoundEngine.c',
|
||||
'hll/LoadCG.c',
|
||||
'hll/MADLoader.c',
|
||||
'hll/MainEXFile.c',
|
||||
'hll/MainSurface.c',
|
||||
'hll/MamanyoDemo.c',
|
||||
@@ -131,6 +140,8 @@ xsystem4 = [version_h,
|
||||
'hll/MusicSystem.c',
|
||||
'hll/NewFont.c',
|
||||
'hll/OutputLog.c',
|
||||
'hll/P3MapSprite.c',
|
||||
'hll/P3SquareSprite.c',
|
||||
'hll/PassRegister.c',
|
||||
'hll/PastelChime2.c',
|
||||
'hll/PartsEngine.c',
|
||||
@@ -172,6 +183,7 @@ xsystem4 = [version_h,
|
||||
'hll/vmSurface.c',
|
||||
'hll/vmSystem.c',
|
||||
'hll/vmTimer.c',
|
||||
'hll/ValueEncryption.c',
|
||||
'hll/VSFile.c',
|
||||
]
|
||||
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ static int audio_callback(sts_mixer_sample_t *sample, void *data)
|
||||
return STS_STREAM_COMPLETE;
|
||||
}
|
||||
const int nr_channels = 2;
|
||||
unsigned samples = mc->audio.frame->linesize[0] / mc->bytes_per_sample;
|
||||
unsigned samples = mc->audio.frame->nb_samples;
|
||||
if (samples * nr_channels != sample->length) {
|
||||
free(sample->data);
|
||||
sample->length = samples * nr_channels;
|
||||
|
||||
+19
-3
@@ -194,7 +194,7 @@ void variable_set(struct page *page, int varno, enum ain_data_type type, union v
|
||||
|
||||
void delete_page_vars(struct page *page)
|
||||
{
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
for (int i = page->nr_vars - 1; i >= 0; i--) {
|
||||
variable_fini(page->values[i], variable_type(page, i, NULL, NULL), true);
|
||||
}
|
||||
}
|
||||
@@ -292,7 +292,7 @@ struct page *alloc_array(int rank, union vm_value *dimensions, enum ain_data_typ
|
||||
|
||||
data_type = unref_array_type(data_type);
|
||||
enum ain_data_type type = array_type(data_type);
|
||||
struct page *page = alloc_page(ARRAY_PAGE, data_type, dimensions->i);
|
||||
struct page *page = alloc_page(ARRAY_PAGE, data_type, max(0, dimensions->i));
|
||||
page->array.struct_type = struct_type;
|
||||
page->array.rank = rank;
|
||||
|
||||
@@ -557,6 +557,17 @@ static int array_compare_custom(const void *_a, const void *_b)
|
||||
return d ? d : a->index - b->index;
|
||||
}
|
||||
|
||||
static int array_compare_custom_string(const void *_a, const void *_b)
|
||||
{
|
||||
const struct sortable *a = _a;
|
||||
const struct sortable *b = _b;
|
||||
stack_push(vm_string_ref(heap_get_string(a->v.i)));
|
||||
stack_push(vm_string_ref(heap_get_string(b->v.i)));
|
||||
vm_call(current_sort_function, -1);
|
||||
int d = stack_pop().i;
|
||||
return d ? d : a->index - b->index;
|
||||
}
|
||||
|
||||
void array_sort(struct page *page, int compare_fno)
|
||||
{
|
||||
if (!page)
|
||||
@@ -569,7 +580,8 @@ void array_sort(struct page *page, int compare_fno)
|
||||
values[i].index = i;
|
||||
}
|
||||
current_sort_function = compare_fno;
|
||||
qsort(values, page->nr_vars, sizeof(struct sortable), array_compare_custom);
|
||||
qsort(values, page->nr_vars, sizeof(struct sortable),
|
||||
page->a_type == AIN_ARRAY_STRING ? array_compare_custom_string : array_compare_custom);
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
page->values[i] = values[i].v;
|
||||
}
|
||||
@@ -691,6 +703,8 @@ void array_reverse(struct page *page)
|
||||
|
||||
struct page *delegate_new_from_method(int obj, int fun)
|
||||
{
|
||||
if (fun < 1)
|
||||
return alloc_page(DELEGATE_PAGE, 0, 0);
|
||||
struct page *page = alloc_page(DELEGATE_PAGE, 0, 3);
|
||||
page->values[0].i = obj;
|
||||
page->values[1].i = fun;
|
||||
@@ -713,6 +727,8 @@ bool delegate_contains(struct page *dst, int obj, int fun)
|
||||
|
||||
struct page *delegate_append(struct page *dst, int obj, int fun)
|
||||
{
|
||||
if (fun < 1)
|
||||
return dst;
|
||||
if (!dst)
|
||||
return delegate_new_from_method(obj, fun);
|
||||
if (dst->type != DELEGATE_PAGE)
|
||||
|
||||
+149
-38
@@ -34,13 +34,19 @@ void parts_cp_op_free(struct parts_cp_op *op)
|
||||
switch (op->type) {
|
||||
case PARTS_CP_CREATE:
|
||||
case PARTS_CP_CREATE_PIXEL_ONLY:
|
||||
case PARTS_CP_CG:
|
||||
case PARTS_CP_FILL:
|
||||
case PARTS_CP_FILL_ALPHA_COLOR:
|
||||
case PARTS_CP_FILL_AMAP:
|
||||
case PARTS_CP_FILL_WITH_ALPHA:
|
||||
case PARTS_CP_DRAW_RECT:
|
||||
case PARTS_CP_GRAY_FILTER:
|
||||
break;
|
||||
case PARTS_CP_CG:
|
||||
free_string(op->cg.name);
|
||||
break;
|
||||
case PARTS_CP_DRAW_CUT_CG:
|
||||
case PARTS_CP_COPY_CUT_CG:
|
||||
free_string(op->cut_cg.cg_name);
|
||||
break;
|
||||
case PARTS_CP_DRAW_TEXT:
|
||||
case PARTS_CP_COPY_TEXT:
|
||||
@@ -90,8 +96,7 @@ bool PE_AddCreateCGToProcess(int parts_no, struct string *cg_name, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
int no;
|
||||
if (!asset_exists_by_name(ASSET_CG, cg_name->text, &no)) {
|
||||
if (!asset_exists_by_name(ASSET_CG, cg_name->text, NULL)) {
|
||||
WARNING("Invalid CG name: %s", display_sjis0(cg_name->text));
|
||||
return false;
|
||||
}
|
||||
@@ -99,7 +104,7 @@ bool PE_AddCreateCGToProcess(int parts_no, struct string *cg_name, int state)
|
||||
struct parts_construction_process *cproc = get_cproc(parts_no, state);
|
||||
struct parts_cp_op *op = xcalloc(1, sizeof(struct parts_cp_op));
|
||||
op->type = PARTS_CP_CG;
|
||||
op->cg.no = no;
|
||||
op->cg.name = string_ref(cg_name);
|
||||
parts_add_cp_op(cproc, op);
|
||||
return true;
|
||||
}
|
||||
@@ -156,7 +161,23 @@ bool PE_AddFillAMapToPartsConstructionProcess(int parts_no, int x, int y, int w,
|
||||
}
|
||||
|
||||
bool PE_AddFillWithAlphaToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int a, int state);
|
||||
int r, int g, int b, int a, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct parts_construction_process *cproc = get_cproc(parts_no, state);
|
||||
struct parts_cp_op *op = xcalloc(1, sizeof(struct parts_cp_op));
|
||||
op->type = PARTS_CP_FILL_WITH_ALPHA;
|
||||
op->fill = (struct parts_cp_fill) {
|
||||
.x = x, .y = y, .w = w, .h = h,
|
||||
.r = r, .g = g, .b = b, .a = a
|
||||
};
|
||||
|
||||
parts_add_cp_op(cproc, op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PE_AddFillGradationHorizonToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int top_r, int top_g, int top_b, int bot_r, int bot_g, int bot_b, int state);
|
||||
|
||||
@@ -184,8 +205,7 @@ bool PE_AddDrawCutCGToPartsConstructionProcess(int parts_no, struct string *cg_n
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
int cg_no;
|
||||
if (!asset_exists_by_name(ASSET_CG, cg_name->text, &cg_no)) {
|
||||
if (!asset_exists_by_name(ASSET_CG, cg_name->text, NULL)) {
|
||||
WARNING("Invalid CG name: %s", display_sjis0(cg_name->text));
|
||||
return false;
|
||||
}
|
||||
@@ -194,7 +214,7 @@ bool PE_AddDrawCutCGToPartsConstructionProcess(int parts_no, struct string *cg_n
|
||||
struct parts_cp_op *op = xcalloc(1, sizeof(struct parts_cp_op));
|
||||
op->type = PARTS_CP_DRAW_CUT_CG;
|
||||
op->cut_cg = (struct parts_cp_cut_cg) {
|
||||
.cg_no = cg_no,
|
||||
.cg_name = string_ref(cg_name),
|
||||
.dx = dx, .dy = dy, .dw = dw, .dh = dh,
|
||||
.sx = sx, .sy = sy, .sw = sw, .sh = sh,
|
||||
.interp_type = interp_type
|
||||
@@ -210,8 +230,7 @@ bool PE_AddCopyCutCGToPartsConstructionProcess(int parts_no, struct string *cg_n
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
int cg_no;
|
||||
if (!asset_exists_by_name(ASSET_CG, cg_name->text, &cg_no)) {
|
||||
if (!asset_exists_by_name(ASSET_CG, cg_name->text, NULL)) {
|
||||
WARNING("Invalid CG name: %s", display_sjis0(cg_name->text));
|
||||
return false;
|
||||
}
|
||||
@@ -220,7 +239,7 @@ bool PE_AddCopyCutCGToPartsConstructionProcess(int parts_no, struct string *cg_n
|
||||
struct parts_cp_op *op = xcalloc(1, sizeof(struct parts_cp_op));
|
||||
op->type = PARTS_CP_COPY_CUT_CG;
|
||||
op->cut_cg = (struct parts_cp_cut_cg) {
|
||||
.cg_no = cg_no,
|
||||
.cg_name = string_ref(cg_name),
|
||||
.dx = dx, .dy = dy, .dw = dw, .dh = dh,
|
||||
.sx = sx, .sy = sy, .sw = sw, .sh = sh,
|
||||
.interp_type = interp_type
|
||||
@@ -231,7 +250,23 @@ bool PE_AddCopyCutCGToPartsConstructionProcess(int parts_no, struct string *cg_n
|
||||
}
|
||||
|
||||
bool PE_AddGrayFilterToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
bool full_size, int state);
|
||||
bool full_size, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct parts_construction_process *cproc = get_cproc(parts_no, state);
|
||||
struct parts_cp_op *op = xcalloc(1, sizeof(struct parts_cp_op));
|
||||
op->type = PARTS_CP_GRAY_FILTER;
|
||||
op->filter = (struct parts_cp_filter) {
|
||||
.x = x, .y = y, .w = w, .h = h,
|
||||
.full_size = full_size
|
||||
};
|
||||
|
||||
parts_add_cp_op(cproc, op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PE_AddAddFilterToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, bool full_size, int state);
|
||||
bool PE_AddMulFilterToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
@@ -297,60 +332,91 @@ bool PE_AddCopyTextToPartsConstructionProcess(int parts_no, int x, int y, struct
|
||||
PARTS_CP_COPY_TEXT);
|
||||
}
|
||||
|
||||
static void build_create(struct parts *parts, struct parts_construction_process *cproc,
|
||||
static bool build_create(struct parts *parts, struct parts_construction_process *cproc,
|
||||
struct parts_cp_create *op)
|
||||
{
|
||||
gfx_delete_texture(&cproc->common.texture);
|
||||
gfx_init_texture_rgba(&cproc->common.texture, op->w, op->h, (SDL_Color){0,0,0,255});
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
parts_set_dims(parts, &cproc->common, op->w, op->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_create_pixel_only(struct parts *parts, struct parts_construction_process *cproc,
|
||||
static bool build_create_pixel_only(struct parts *parts, struct parts_construction_process *cproc,
|
||||
struct parts_cp_create *op)
|
||||
{
|
||||
gfx_delete_texture(&cproc->common.texture);
|
||||
gfx_init_texture_rgb(&cproc->common.texture, op->w, op->h, (SDL_Color){0,0,0,255});
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
parts_set_dims(parts, &cproc->common, op->w, op->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_cg(struct parts *parts, struct parts_construction_process *cproc, struct parts_cp_cg *op)
|
||||
static bool build_cg(struct parts *parts, struct parts_construction_process *cproc, struct parts_cp_cg *op)
|
||||
{
|
||||
struct cg *cg = asset_cg_load(op->no);
|
||||
assert(cg);
|
||||
struct cg *cg = asset_cg_load_by_name(op->name->text, NULL);
|
||||
if (!cg)
|
||||
return false;
|
||||
gfx_delete_texture(&cproc->common.texture);
|
||||
gfx_init_texture_with_cg(&cproc->common.texture, cg);
|
||||
parts_set_dims(parts, &cproc->common, cg->metrics.w, cg->metrics.h);
|
||||
cg_free(cg);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_fill(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
static bool build_fill(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
{
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
gfx_fill(&cproc->common.texture, op->x, op->y, op->w, op->h, op->r, op->g, op->b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_fill_alpha_color(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
static bool build_fill_alpha_color(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
{
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
gfx_fill_alpha_color(&cproc->common.texture, op->x, op->y, op->w, op->h, op->r, op->g, op->b, op->a);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_fill_amap(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
static bool build_fill_amap(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
{
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
gfx_fill_amap(&cproc->common.texture, op->x, op->y, op->w, op->h, op->a);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_draw_rect(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
static bool build_fill_with_alpha(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
{
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
gfx_fill_with_alpha(&cproc->common.texture, op->x, op->y, op->w, op->h, op->r, op->g, op->b, op->a);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool build_draw_rect(struct parts_construction_process *cproc, struct parts_cp_fill *op)
|
||||
{
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
int x2 = op->x + op->w - 1;
|
||||
int y2 = op->y + op->h - 1;
|
||||
gfx_draw_line(&cproc->common.texture, op->x, op->y, x2, op->y, op->r, op->g, op->b);
|
||||
gfx_draw_line(&cproc->common.texture, op->x, op->y, op->x, y2, op->r, op->g, op->b);
|
||||
gfx_draw_line(&cproc->common.texture, x2, op->y, x2, y2, op->r, op->g, op->b);
|
||||
gfx_draw_line(&cproc->common.texture, op->x, y2, x2, y2, op->r, op->g, op->b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_draw_cut_cg(struct parts_construction_process *cproc, struct parts_cp_cut_cg *op)
|
||||
static bool build_draw_cut_cg(struct parts_construction_process *cproc, struct parts_cp_cut_cg *op)
|
||||
{
|
||||
struct cg *cg = asset_cg_load(op->cg_no);
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
|
||||
struct cg *cg = asset_cg_load_by_name(op->cg_name->text, NULL);
|
||||
assert(cg);
|
||||
|
||||
Texture src;
|
||||
@@ -360,11 +426,15 @@ static void build_draw_cut_cg(struct parts_construction_process *cproc, struct p
|
||||
gfx_copy_stretch_blend_amap(&cproc->common.texture, op->dx, op->dy, op->dw, op->dh,
|
||||
&src, op->sx, op->sy, op->sw, op->sh);
|
||||
gfx_delete_texture(&src);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_copy_cut_cg(struct parts_construction_process *cproc, struct parts_cp_cut_cg *op)
|
||||
static bool build_copy_cut_cg(struct parts_construction_process *cproc, struct parts_cp_cut_cg *op)
|
||||
{
|
||||
struct cg *cg = asset_cg_load(op->cg_no);
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
|
||||
struct cg *cg = asset_cg_load_by_name(op->cg_name->text, NULL);
|
||||
assert(cg);
|
||||
|
||||
Texture src;
|
||||
@@ -374,20 +444,42 @@ static void build_copy_cut_cg(struct parts_construction_process *cproc, struct p
|
||||
gfx_copy_stretch_with_alpha_map(&cproc->common.texture, op->dx, op->dy, op->dw, op->dh,
|
||||
&src, op->sx, op->sy, op->sw, op->sh);
|
||||
gfx_delete_texture(&src);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_draw_text(struct parts_construction_process *cproc, struct parts_cp_text *op)
|
||||
static bool build_draw_text(struct parts_construction_process *cproc, struct parts_cp_text *op)
|
||||
{
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
gfx_render_text(&cproc->common.texture, op->x, op->y, op->text->text, &op->style, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void build_copy_text(struct parts_construction_process *cproc, struct parts_cp_text *op)
|
||||
static bool build_copy_text(struct parts_construction_process *cproc, struct parts_cp_text *op)
|
||||
{
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
int w = ceilf(gfx_size_text (&op->style, op->text->text));
|
||||
int h = ceilf(op->style.size + op->style.edge_up + op->style.edge_down);
|
||||
gfx_fill_with_alpha(&cproc->common.texture, op->x, op->y, w, h,
|
||||
op->style.edge_color.r, op->style.edge_color.g, op->style.edge_color.b, 0);
|
||||
gfx_render_text(&cproc->common.texture, op->x, op->y, op->text->text, &op->style, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool build_gray_filter(struct parts_construction_process *cproc, struct parts_cp_filter *op)
|
||||
{
|
||||
if (!cproc->common.texture.handle)
|
||||
return false;
|
||||
int x = op->x, y = op->y, w = op->w, h = op->h;
|
||||
if (op->full_size) {
|
||||
x = 0; y = 0;
|
||||
w = cproc->common.texture.w;
|
||||
h = cproc->common.texture.h;
|
||||
}
|
||||
|
||||
gfx_copy_grayscale(&cproc->common.texture, x, y, &cproc->common.texture, x, y, w, h);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parts_build_construction_process(struct parts *parts,
|
||||
@@ -397,37 +489,56 @@ bool parts_build_construction_process(struct parts *parts,
|
||||
TAILQ_FOREACH(op, &cproc->ops, entry) {
|
||||
switch (op->type) {
|
||||
case PARTS_CP_CREATE:
|
||||
build_create(parts, cproc, &op->create);
|
||||
if (!build_create(parts, cproc, &op->create))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_CREATE_PIXEL_ONLY:
|
||||
build_create_pixel_only(parts, cproc, &op->create);
|
||||
if (!build_create_pixel_only(parts, cproc, &op->create))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_CG:
|
||||
build_cg(parts, cproc, &op->cg);
|
||||
if (!build_cg(parts, cproc, &op->cg))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_FILL:
|
||||
build_fill(cproc, &op->fill);
|
||||
if (!build_fill(cproc, &op->fill))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_FILL_ALPHA_COLOR:
|
||||
build_fill_alpha_color(cproc, &op->fill);
|
||||
if (!build_fill_alpha_color(cproc, &op->fill))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_FILL_AMAP:
|
||||
build_fill_amap(cproc, &op->fill);
|
||||
if (!build_fill_amap(cproc, &op->fill))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_FILL_WITH_ALPHA:
|
||||
if (!build_fill_with_alpha(cproc, &op->fill))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_DRAW_RECT:
|
||||
build_draw_rect(cproc, &op->fill);
|
||||
if (!build_draw_rect(cproc, &op->fill))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_DRAW_CUT_CG:
|
||||
build_draw_cut_cg(cproc, &op->cut_cg);
|
||||
if (!build_draw_cut_cg(cproc, &op->cut_cg))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_COPY_CUT_CG:
|
||||
build_copy_cut_cg(cproc, &op->cut_cg);
|
||||
if (!build_copy_cut_cg(cproc, &op->cut_cg))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_DRAW_TEXT:
|
||||
build_draw_text(cproc, &op->text);
|
||||
if (!build_draw_text(cproc, &op->text))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_COPY_TEXT:
|
||||
build_copy_text(cproc, &op->text);
|
||||
if (!build_copy_text(cproc, &op->text))
|
||||
return false;
|
||||
break;
|
||||
case PARTS_CP_GRAY_FILTER:
|
||||
if (!build_gray_filter(cproc, &op->filter))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+72
-4
@@ -110,11 +110,13 @@ static void parts_construction_process_to_json(struct parts_construction_process
|
||||
[PARTS_CP_FILL] = "fill",
|
||||
[PARTS_CP_FILL_ALPHA_COLOR] = "fill_alpha_color",
|
||||
[PARTS_CP_FILL_AMAP] = "fill_amap",
|
||||
[PARTS_CP_FILL_WITH_ALPHA] = "fill_with_alpha",
|
||||
[PARTS_CP_DRAW_RECT] = "draw_rect",
|
||||
[PARTS_CP_DRAW_CUT_CG] = "draw_cut_cg",
|
||||
[PARTS_CP_COPY_CUT_CG] = "copy_cut_cg",
|
||||
[PARTS_CP_DRAW_TEXT] = "draw_text",
|
||||
[PARTS_CP_COPY_TEXT] = "copy_text",
|
||||
[PARTS_CP_GRAY_FILTER] = "gray_filter",
|
||||
};
|
||||
|
||||
cJSON *ops, *tmp;
|
||||
@@ -138,11 +140,12 @@ static void parts_construction_process_to_json(struct parts_construction_process
|
||||
cJSON_AddNumberToObject(tmp, "h", op->create.h);
|
||||
break;
|
||||
case PARTS_CP_CG:
|
||||
cJSON_AddNumberToObject(obj, "no", op->cg.no);
|
||||
cJSON_AddSjisToObject(obj, "name", op->cg.name->text);
|
||||
break;
|
||||
case PARTS_CP_FILL:
|
||||
case PARTS_CP_FILL_ALPHA_COLOR:
|
||||
case PARTS_CP_FILL_AMAP:
|
||||
case PARTS_CP_FILL_WITH_ALPHA:
|
||||
case PARTS_CP_DRAW_RECT:
|
||||
cJSON_AddItemToObjectCS(obj, "rect", tmp = cJSON_CreateObject());
|
||||
cJSON_AddNumberToObject(tmp, "x", op->fill.x);
|
||||
@@ -157,7 +160,7 @@ static void parts_construction_process_to_json(struct parts_construction_process
|
||||
break;
|
||||
case PARTS_CP_DRAW_CUT_CG:
|
||||
case PARTS_CP_COPY_CUT_CG:
|
||||
cJSON_AddNumberToObject(obj, "no", op->cut_cg.cg_no);
|
||||
cJSON_AddSjisToObject(obj, "name", op->cut_cg.cg_name->text);
|
||||
cJSON_AddItemToObjectCS(obj, "dst", tmp = cJSON_CreateObject());
|
||||
cJSON_AddNumberToObject(tmp, "x", op->cut_cg.dx);
|
||||
cJSON_AddNumberToObject(tmp, "y", op->cut_cg.dy);
|
||||
@@ -179,6 +182,13 @@ static void parts_construction_process_to_json(struct parts_construction_process
|
||||
cJSON_AddNumberToObject(obj, "line_space", op->text.line_space);
|
||||
cJSON_AddItemToObjectCS(obj, "style", text_style_to_json(&op->text.style, verbose));
|
||||
break;
|
||||
case PARTS_CP_GRAY_FILTER:
|
||||
cJSON_AddNumberToObject(obj, "x", op->filter.x);
|
||||
cJSON_AddNumberToObject(obj, "y", op->filter.y);
|
||||
cJSON_AddNumberToObject(obj, "w", op->filter.w);
|
||||
cJSON_AddNumberToObject(obj, "h", op->filter.h);
|
||||
cJSON_AddBoolToObject(obj, "full_size", op->filter.full_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,6 +200,20 @@ static void parts_flash_to_json(struct parts_flash *flash, cJSON *out, bool verb
|
||||
cJSON_AddNumberToObject(out, "current_frame", flash->current_frame);
|
||||
}
|
||||
|
||||
static void parts_flat_to_json(struct parts_flat *flat, cJSON *out, bool verbose)
|
||||
{
|
||||
if (flat->name)
|
||||
cJSON_AddSjisToObject(out, "name", flat->name->text);
|
||||
if (flat->root_state)
|
||||
cJSON_AddNumberToObject(out, "current_frame", flat->root_state->current_frame);
|
||||
cJSON_AddNumberToObject(out, "end_frame", flat->end_frame);
|
||||
}
|
||||
|
||||
static void parts_movie_to_json(struct parts_movie *movie, cJSON *out, bool verbose)
|
||||
{
|
||||
cJSON_AddNumberToObject(out, "sprite_no", movie->sprite_no);
|
||||
}
|
||||
|
||||
static cJSON *parts_state_to_json(struct parts_state *state, bool verbose)
|
||||
{
|
||||
static const char *state_types[PARTS_NR_TYPES] = {
|
||||
@@ -201,7 +225,8 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose)
|
||||
[PARTS_HGAUGE] = "hgauge",
|
||||
[PARTS_VGAUGE] = "vgauge",
|
||||
[PARTS_CONSTRUCTION_PROCESS] = "construction_process",
|
||||
[PARTS_FLASH] = "flash"
|
||||
[PARTS_FLASH] = "flash",
|
||||
[PARTS_FLAT] = "flat"
|
||||
};
|
||||
const char *type = "invalid";
|
||||
if (state->type >= 0 && state->type < PARTS_NR_TYPES)
|
||||
@@ -239,6 +264,22 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose)
|
||||
case PARTS_FLASH:
|
||||
parts_flash_to_json(&state->flash, obj, verbose);
|
||||
break;
|
||||
case PARTS_FLAT:
|
||||
parts_flat_to_json(&state->flat, obj, verbose);
|
||||
break;
|
||||
case PARTS_MOVIE:
|
||||
parts_movie_to_json(&state->movie, obj, verbose);
|
||||
break;
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
case PARTS_LAYOUT_BOX:
|
||||
cJSON_AddNumberToObject(obj, "layout_type", state->layout_box.layout_type);
|
||||
cJSON_AddNumberToObject(obj, "align", state->layout_box.align);
|
||||
break;
|
||||
case PARTS_3DLAYER:
|
||||
cJSON_AddNumberToObject(obj, "plugin", state->layer3d.plugin);
|
||||
cJSON_AddNumberToObject(obj, "sprite_no", state->layer3d.sprite_no);
|
||||
break;
|
||||
}
|
||||
|
||||
return obj;
|
||||
@@ -262,6 +303,7 @@ static cJSON *parts_params_to_json(struct parts_params *params, bool verbose)
|
||||
cJSON_AddNumberToObject(tmp, "z", params->rotation.z);
|
||||
cJSON_AddItemToObjectCS(obj, "add_color", color_to_json(¶ms->add_color, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "mul_color", color_to_json(¶ms->multiply_color, verbose));
|
||||
cJSON_AddNumberToObject(obj, "alpha_clipper_parts_no", params->alpha_clipper_parts_no);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -349,10 +391,15 @@ cJSON *parts_to_json(struct parts *parts, bool recursive, bool verbose)
|
||||
cJSON_AddItemToObjectCS(obj, "clicked", parts_state_to_json(&parts->states[PARTS_STATE_CLICKED], verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "local", parts_params_to_json(&parts->local, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "global", parts_params_to_json(&parts->global, verbose));
|
||||
if (parts_multi_controller)
|
||||
cJSON_AddNumberToObject(obj, "controller", parts->controller_no);
|
||||
if (parts->delegate_index >= 0)
|
||||
cJSON_AddNumberToObject(obj, "delegate_index", parts->delegate_index);
|
||||
cJSON_AddNumberToObject(obj, "sprite_deform", parts->sprite_deform);
|
||||
cJSON_AddBoolToObject(obj, "clickable", parts->clickable);
|
||||
cJSON_AddBoolToObject(obj, "pass_cursor", parts->pass_cursor);
|
||||
cJSON_AddBoolToObject(obj, "lock_input_state", parts->lock_input_state);
|
||||
cJSON_AddBoolToObject(obj, "want_save", parts->want_save);
|
||||
if (parts->on_cursor_sound >= 0)
|
||||
cJSON_AddNumberToObject(obj, "on_cursor_sound", parts->on_cursor_sound);
|
||||
if (parts->on_click_sound >= 0)
|
||||
@@ -489,6 +536,9 @@ static void parts_list_print(struct parts *parts, int indent)
|
||||
case PARTS_CP_FILL_AMAP:
|
||||
sys_message(" fill-amap");
|
||||
break;
|
||||
case PARTS_CP_FILL_WITH_ALPHA:
|
||||
sys_message(" fill-with-alpha");
|
||||
break;
|
||||
case PARTS_CP_DRAW_RECT:
|
||||
sys_message(" draw-rect");
|
||||
break;
|
||||
@@ -504,6 +554,9 @@ static void parts_list_print(struct parts *parts, int indent)
|
||||
case PARTS_CP_COPY_TEXT:
|
||||
sys_message(" copy-text");
|
||||
break;
|
||||
case PARTS_CP_GRAY_FILTER:
|
||||
sys_message(" gray-filter");
|
||||
break;
|
||||
}
|
||||
}
|
||||
sys_message(")");
|
||||
@@ -512,9 +565,24 @@ static void parts_list_print(struct parts *parts, int indent)
|
||||
case PARTS_FLASH:
|
||||
sys_message("(flash %s)", display_sjis0(state->flash.name->text));
|
||||
break;
|
||||
case PARTS_FLAT:
|
||||
sys_message("(flat %s)", state->flat.name ? display_sjis0(state->flat.name->text) : "(null)");
|
||||
break;
|
||||
case PARTS_MOVIE:
|
||||
sys_message("(movie sp=%d)", state->movie.sprite_no);
|
||||
break;
|
||||
case PARTS_RECT_DETECTION:
|
||||
sys_message("(rect_detection)");
|
||||
break;
|
||||
case PARTS_LAYOUT_BOX:
|
||||
sys_message("(layout_box type=%d)", state->layout_box.layout_type);
|
||||
break;
|
||||
case PARTS_3DLAYER:
|
||||
sys_message("(3dlayer plugin=%d sp=%d)", state->layer3d.plugin, state->layer3d.sprite_no);
|
||||
break;
|
||||
}
|
||||
|
||||
sys_message(" @ z=%d (%d)\n", parts->global.z, parts->local.z);
|
||||
sys_message(" @ controller=%d z=%d (%d)\n", parts->controller_no, parts->global.z, parts->local.z);
|
||||
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
|
||||
+17
-10
@@ -38,7 +38,7 @@ static void clear_display_list(struct parts_flash *f)
|
||||
while (!TAILQ_EMPTY(&f->display_list)) {
|
||||
struct parts_flash_object *obj = TAILQ_FIRST(&f->display_list);
|
||||
TAILQ_REMOVE(&f->display_list, obj, entry);
|
||||
free(obj);
|
||||
xfree_aligned(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ void parts_flash_free(struct parts_flash *f)
|
||||
ht_free_int(f->bitmaps);
|
||||
}
|
||||
if (f->sprites) {
|
||||
ht_foreach_value(f->sprites, free);
|
||||
ht_foreach_value(f->sprites, xfree_aligned);
|
||||
ht_free_int(f->sprites);
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@ bool parts_flash_load(struct parts *parts, struct parts_flash *f, struct string
|
||||
|
||||
f->name = string_dup(filename);
|
||||
f->tag = f->swf->tags;
|
||||
f->has_ended = false;
|
||||
f->stopped = false;
|
||||
f->elapsed = 0;
|
||||
f->current_frame = 0;
|
||||
@@ -128,10 +129,10 @@ static int do_action(struct parts_flash *f, struct swf_tag_do_action *tag)
|
||||
for (struct swf_action *a = tag->actions; a->code != ACTION_END; a = a->next) {
|
||||
switch (a->code) {
|
||||
case ACTION_PLAY:
|
||||
f->stopped = false;
|
||||
f->has_ended = false;
|
||||
break;
|
||||
case ACTION_STOP:
|
||||
f->stopped = true;
|
||||
f->has_ended = true;
|
||||
break;
|
||||
case ACTION_GOTO_FRAME:
|
||||
seek_frame = ((struct swf_action_goto_frame*)a)->frame;
|
||||
@@ -153,7 +154,7 @@ static struct parts_flash_object *update_object(struct parts_flash_object *obj,
|
||||
{
|
||||
if (!obj) {
|
||||
if (tag->flags & PLACE_FLAG_HAS_CHARACTER) {
|
||||
obj = xcalloc(1, sizeof(struct parts_flash_object));
|
||||
obj = xcalloc_aligned(1, struct parts_flash_object);
|
||||
obj->depth = tag->depth;
|
||||
} else {
|
||||
ERROR("no object to update (depth=%d)", tag->depth);
|
||||
@@ -216,7 +217,7 @@ static void remove_object(struct parts_flash *f, struct swf_tag_remove_object2 *
|
||||
TAILQ_FOREACH(p, &f->display_list, entry) {
|
||||
if (p->depth == tag->depth) {
|
||||
TAILQ_REMOVE(&f->display_list, p, entry);
|
||||
free(p);
|
||||
xfree_aligned(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -304,8 +305,6 @@ static void define_sprite(struct parts_flash *f, struct swf_tag_define_sprite *t
|
||||
ERROR("unsupported tag %d in sprite", t->type);
|
||||
}
|
||||
}
|
||||
if (!obj)
|
||||
ERROR("sprite has no PlaceObject2 tag");
|
||||
slot->value = obj;
|
||||
}
|
||||
|
||||
@@ -330,7 +329,7 @@ bool parts_flash_seek(struct parts_flash *f, int frame)
|
||||
// Do nothing.
|
||||
break;
|
||||
case TAG_END:
|
||||
f->stopped = true;
|
||||
f->has_ended = true;
|
||||
break;
|
||||
case TAG_SHOW_FRAME:
|
||||
f->current_frame++;
|
||||
@@ -373,7 +372,7 @@ bool parts_flash_seek(struct parts_flash *f, int frame)
|
||||
|
||||
bool parts_flash_update(struct parts_flash *f, int passed_time)
|
||||
{
|
||||
if (!f->swf || f->stopped)
|
||||
if (!f->swf || f->has_ended || f->stopped)
|
||||
return false;
|
||||
f->elapsed += passed_time;
|
||||
int delta_frame = muldiv(f->elapsed, f->swf->frame_rate, 256000);
|
||||
@@ -383,6 +382,13 @@ bool parts_flash_update(struct parts_flash *f, int passed_time)
|
||||
return parts_flash_seek(f, f->current_frame + delta_frame);
|
||||
}
|
||||
|
||||
bool PE_ExistsFlashFile(struct string *flash_filename)
|
||||
{
|
||||
if (!flash_filename)
|
||||
return false;
|
||||
return asset_exists_by_name(ASSET_FLASH, flash_filename->text, NULL);
|
||||
}
|
||||
|
||||
bool PE_SetPartsFlash(int parts_no, struct string *flash_filename, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
@@ -427,6 +433,7 @@ bool PE_BackPartsFlashBeginFrame(int parts_no, int state)
|
||||
struct parts_flash *f = parts_get_flash(parts, state);
|
||||
if (!f->swf)
|
||||
return false;
|
||||
f->has_ended = false;
|
||||
if (parts_flash_seek(f, 1))
|
||||
parts_dirty(parts);
|
||||
f->elapsed = 0;
|
||||
|
||||
+1044
File diff suppressed because it is too large
Load Diff
+181
-22
@@ -22,11 +22,11 @@
|
||||
#include "xsystem4.h"
|
||||
#include "parts_internal.h"
|
||||
|
||||
// the mouse position at last update
|
||||
Point parts_prev_pos = {0};
|
||||
// true between calls to BeginClick and EndClick
|
||||
bool parts_began_click = false;
|
||||
|
||||
// the mouse position at last update
|
||||
static Point parts_prev_pos = {0};
|
||||
// true when the left mouse button was down last update
|
||||
static bool prev_clicking = false;
|
||||
// the last (fully) clicked parts number
|
||||
@@ -34,57 +34,205 @@ static int clicked_parts = 0;
|
||||
// the current (partially) clicked parts number
|
||||
static int click_down_parts = 0;
|
||||
|
||||
static void parts_update_mouse(struct parts *parts, Point cur_pos, bool cur_clicking)
|
||||
{
|
||||
Rectangle *hitbox = &parts->states[parts->state].common.hitbox;
|
||||
bool prev_in = SDL_PointInRect(&parts_prev_pos, hitbox);
|
||||
bool cur_in = SDL_PointInRect(&cur_pos, hitbox);
|
||||
// drag state
|
||||
static struct parts *drag_parts = NULL;
|
||||
static bool is_dragging = false;
|
||||
static Point drag_initial_pos;
|
||||
static Point drag_start_cursor;
|
||||
static struct parts *drop_target = NULL;
|
||||
|
||||
if (parts->linked_from >= 0 && cur_in != prev_in) {
|
||||
static bool parts_hittest(struct parts *parts, int state, Point pos)
|
||||
{
|
||||
Rectangle hitbox = parts->states[state].common.hitbox;
|
||||
if (parts->parent) {
|
||||
hitbox.x += parts->parent->global.pos.x;
|
||||
hitbox.y += parts->parent->global.pos.y;
|
||||
}
|
||||
return SDL_PointInRect(&pos, &hitbox);
|
||||
}
|
||||
|
||||
static void drag_state_reset(void)
|
||||
{
|
||||
drag_parts = NULL;
|
||||
is_dragging = false;
|
||||
drop_target = NULL;
|
||||
}
|
||||
|
||||
void parts_input_reset_drag(struct parts *parts)
|
||||
{
|
||||
if (parts == drag_parts)
|
||||
drag_state_reset();
|
||||
else if (parts == drop_target)
|
||||
drop_target = NULL;
|
||||
}
|
||||
|
||||
static void parts_update_mouse(struct parts *parts, Point cur_pos, bool cur_clicking,
|
||||
int passed_time, bool *hover_consumed, bool *click_consumed)
|
||||
{
|
||||
// Always use DEFAULT state hitbox regardless of the current display state.
|
||||
bool is_hovered = parts_hittest(parts, PARTS_STATE_DEFAULT, cur_pos)
|
||||
&& !*hover_consumed;
|
||||
|
||||
bool was_hovered = parts->is_hovered;
|
||||
parts->is_hovered = is_hovered;
|
||||
|
||||
// !pass_cursor parts consume the cursor for parts behind them
|
||||
if (is_hovered && !parts->pass_cursor)
|
||||
*hover_consumed = true;
|
||||
|
||||
if (parts->linked_from >= 0 && is_hovered != was_hovered) {
|
||||
parts_dirty(parts_get(parts->linked_from));
|
||||
}
|
||||
|
||||
if (!parts_began_click || !parts->clickable)
|
||||
if (!parts_began_click)
|
||||
return;
|
||||
|
||||
if (!cur_in) {
|
||||
if (is_hovered && !was_hovered) {
|
||||
parts_msg_push(parts, PARTS_MSG_MOUSE_ENTER, "ii", cur_pos.x, cur_pos.y);
|
||||
parts->hover_time = 0;
|
||||
}
|
||||
if (!is_hovered && was_hovered) {
|
||||
parts_msg_push(parts, PARTS_MSG_MOUSE_LEAVE, "ii", cur_pos.x, cur_pos.y);
|
||||
}
|
||||
|
||||
if (is_hovered) {
|
||||
// MOUSE_ON message fires every frame while hovering
|
||||
parts_msg_push(parts, PARTS_MSG_MOUSE_ON,
|
||||
"iii", cur_pos.x, cur_pos.y, parts->hover_time);
|
||||
parts->hover_time += passed_time;
|
||||
|
||||
// MOUSE_MOVE message fires when cursor moves while hovering
|
||||
if (cur_pos.x != parts_prev_pos.x || cur_pos.y != parts_prev_pos.y) {
|
||||
parts_msg_push(parts, PARTS_MSG_MOUSE_MOVE, "ii", cur_pos.x, cur_pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_hovered) {
|
||||
parts_set_state(parts, PARTS_STATE_DEFAULT);
|
||||
return;
|
||||
}
|
||||
|
||||
// click down: just remember the parts number for later
|
||||
bool click_eligible = parts->clickable || !parts->pass_cursor;
|
||||
if (!click_eligible || *click_consumed) {
|
||||
if (!was_hovered)
|
||||
audio_play_sound(parts->on_cursor_sound);
|
||||
parts_set_state(parts, PARTS_STATE_HOVERED);
|
||||
return;
|
||||
}
|
||||
|
||||
// click down: first eligible part captures the click
|
||||
if (cur_clicking && !prev_clicking) {
|
||||
click_down_parts = parts->no;
|
||||
*click_consumed = true;
|
||||
|
||||
drag_parts = parts;
|
||||
drag_initial_pos = parts->local.pos;
|
||||
drag_start_cursor = cur_pos;
|
||||
|
||||
// KEY_TRIGGER message fires on press transition
|
||||
parts_msg_push(parts, PARTS_MSG_KEY_TRIGGER, "i", VK_LBUTTON);
|
||||
}
|
||||
|
||||
// KEY_DOWN message fires every frame while held (not first frame)
|
||||
if (prev_clicking && cur_clicking && click_down_parts == parts->no) {
|
||||
parts_msg_push(parts, PARTS_MSG_KEY_DOWN, "i", VK_LBUTTON);
|
||||
}
|
||||
|
||||
if (cur_clicking && click_down_parts == parts->no) {
|
||||
parts_set_state(parts, PARTS_STATE_CLICKED);
|
||||
} else {
|
||||
if (!prev_in) {
|
||||
if (!was_hovered) {
|
||||
audio_play_sound(parts->on_cursor_sound);
|
||||
}
|
||||
parts_set_state(parts, PARTS_STATE_HOVERED);
|
||||
}
|
||||
|
||||
// click event: only if the click down event had same parts number
|
||||
if (prev_clicking && !cur_clicking && click_down_parts == parts->no) {
|
||||
if (parts->clickable && prev_clicking && !cur_clicking
|
||||
&& click_down_parts == parts->no) {
|
||||
audio_play_sound(parts->on_click_sound);
|
||||
clicked_parts = parts->no;
|
||||
|
||||
parts_msg_push(parts, PARTS_MSG_MOUSE_CLICK,
|
||||
"iii", cur_pos.x, cur_pos.y, VK_LBUTTON);
|
||||
parts_msg_push(parts, PARTS_MSG_KEY_UP, "i", VK_LBUTTON);
|
||||
}
|
||||
}
|
||||
|
||||
void PE_UpdateInputState(possibly_unused int passed_time)
|
||||
void PE_UpdateInputState(int passed_time)
|
||||
{
|
||||
Point cur_pos;
|
||||
bool cur_clicking = key_is_down(VK_LBUTTON);
|
||||
mouse_get_pos(&cur_pos.x, &cur_pos.y);
|
||||
|
||||
bool hover_consumed = false;
|
||||
bool click_consumed = false;
|
||||
struct parts *parts;
|
||||
PARTS_LIST_FOREACH(parts) {
|
||||
parts_update_mouse(parts, cur_pos, cur_clicking);
|
||||
// Iterate front-to-back (highest z first) for proper cursor consumption
|
||||
PARTS_LIST_FOREACH_REVERSE(parts) {
|
||||
parts_update_mouse(parts, cur_pos, cur_clicking, passed_time,
|
||||
&hover_consumed, &click_consumed);
|
||||
}
|
||||
|
||||
// Drag movement processing
|
||||
if (drag_parts && cur_clicking) {
|
||||
bool cursor_moved = (cur_pos.x != parts_prev_pos.x ||
|
||||
cur_pos.y != parts_prev_pos.y);
|
||||
if (cursor_moved && drag_parts->draggable) {
|
||||
Point new_pos = {
|
||||
drag_initial_pos.x + (cur_pos.x - drag_start_cursor.x),
|
||||
drag_initial_pos.y + (cur_pos.y - drag_start_cursor.y)
|
||||
};
|
||||
parts_set_pos(drag_parts, new_pos);
|
||||
if (!is_dragging) {
|
||||
parts_msg_push(drag_parts, PARTS_MSG_DRAG_BEGIN, "");
|
||||
}
|
||||
parts_msg_push(drag_parts, PARTS_MSG_DRAGGING, "iiii",
|
||||
drag_start_cursor.x, drag_start_cursor.y, cur_pos.x, cur_pos.y);
|
||||
is_dragging = true;
|
||||
}
|
||||
|
||||
// Drop target tracking
|
||||
if (cursor_moved && is_dragging) {
|
||||
struct parts *new_drop = NULL;
|
||||
PARTS_LIST_FOREACH_REVERSE(parts) {
|
||||
if (parts == drag_parts)
|
||||
continue;
|
||||
if (parts_hittest(parts, PARTS_STATE_DEFAULT, cur_pos)) {
|
||||
new_drop = parts;
|
||||
if (!parts->pass_cursor)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (new_drop != drop_target) {
|
||||
if (drop_target) {
|
||||
parts_msg_push(drop_target, PARTS_MSG_DROP_LEAVE,
|
||||
"i", drag_parts->no);
|
||||
}
|
||||
if (new_drop) {
|
||||
parts_msg_push(new_drop, PARTS_MSG_DROP_ENTER,
|
||||
"i", drag_parts->no);
|
||||
}
|
||||
drop_target = new_drop;
|
||||
} else if (drop_target) {
|
||||
parts_msg_push(drop_target, PARTS_MSG_DROP_ON,
|
||||
"iii", drag_parts->no, cur_pos.x, cur_pos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Release handling
|
||||
if (prev_clicking && !cur_clicking) {
|
||||
if (is_dragging && drag_parts && drag_parts->draggable) {
|
||||
parts_msg_push(drag_parts, PARTS_MSG_DRAG_END, "");
|
||||
}
|
||||
if (drop_target && drag_parts) {
|
||||
parts_msg_push(drop_target, PARTS_MSG_DROPPED,
|
||||
"iii", drag_parts->no, cur_pos.x, cur_pos.y);
|
||||
parts_msg_push(drop_target, PARTS_MSG_DROP_LEAVE, "i", drag_parts->no);
|
||||
}
|
||||
drag_state_reset();
|
||||
|
||||
if (!click_down_parts) {
|
||||
// TODO: play misclick sound
|
||||
}
|
||||
@@ -95,6 +243,16 @@ void PE_UpdateInputState(possibly_unused int passed_time)
|
||||
parts_prev_pos = cur_pos;
|
||||
}
|
||||
|
||||
void PE_SetPassCursor(int parts_no, bool pass)
|
||||
{
|
||||
parts_get(parts_no)->pass_cursor = !!pass;
|
||||
}
|
||||
|
||||
bool PE_GetPartsPassCursor(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->pass_cursor;
|
||||
}
|
||||
|
||||
void PE_SetClickable(int parts_no, bool clickable)
|
||||
{
|
||||
parts_get(parts_no)->clickable = !!clickable;
|
||||
@@ -167,6 +325,12 @@ void PE_EndInput(void)
|
||||
{
|
||||
parts_began_click = false;
|
||||
clicked_parts = 0;
|
||||
drag_state_reset();
|
||||
}
|
||||
|
||||
void PE_SetDrag(int parts_no, bool enable)
|
||||
{
|
||||
parts_get(parts_no)->draggable = !!enable;
|
||||
}
|
||||
|
||||
int PE_GetClickPartsNumber(void)
|
||||
@@ -183,11 +347,6 @@ bool PE_IsCursorIn(int parts_no, int mouse_x, int mouse_y, int state)
|
||||
if (!parts)
|
||||
return false;
|
||||
|
||||
Rectangle hitbox = parts->states[state].common.hitbox;
|
||||
if (parts->parent) {
|
||||
hitbox.x += parts->parent->global.pos.x;
|
||||
hitbox.y += parts->parent->global.pos.y;
|
||||
}
|
||||
Point mouse_pos = { mouse_x, mouse_y };
|
||||
return SDL_PointInRect(&mouse_pos, &hitbox);
|
||||
return parts_hittest(parts, state, mouse_pos);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "system4/flat.h"
|
||||
|
||||
#include "parts.h"
|
||||
#include "parts_internal.h"
|
||||
|
||||
void PE_SetComponentMargin(int parts_no, int top, int bottom, int left, int right)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
parts->margin_top = top;
|
||||
parts->margin_bottom = bottom;
|
||||
parts->margin_left = left;
|
||||
parts->margin_right = right;
|
||||
}
|
||||
|
||||
int PE_GetComponentMarginTop(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->margin_top;
|
||||
}
|
||||
|
||||
int PE_GetComponentMarginBottom(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->margin_bottom;
|
||||
}
|
||||
|
||||
int PE_GetComponentMarginLeft(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->margin_left;
|
||||
}
|
||||
|
||||
int PE_GetComponentMarginRight(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->margin_right;
|
||||
}
|
||||
|
||||
void PE_SetLayoutBoxLayoutType(int parts_no, int type)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_layout_box *lb = parts_get_layout_box(parts);
|
||||
if (lb->layout_type != type) {
|
||||
lb->layout_type = type;
|
||||
parts_component_dirty(parts);
|
||||
}
|
||||
}
|
||||
|
||||
int PE_GetLayoutBoxLayoutType(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return PARTS_LAYOUT_FREE;
|
||||
return parts->states[0].layout_box.layout_type;
|
||||
}
|
||||
|
||||
void PE_SetLayoutBoxReturn(int parts_no, bool return_flag, int return_size)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_layout_box *lb = parts_get_layout_box(parts);
|
||||
lb->wrap = return_flag;
|
||||
lb->wrap_size = return_size;
|
||||
parts_component_dirty(parts);
|
||||
}
|
||||
|
||||
bool PE_IsLayoutBoxReturn(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return false;
|
||||
return parts->states[0].layout_box.wrap;
|
||||
}
|
||||
|
||||
int PE_GetLayoutBoxReturnSize(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.wrap_size;
|
||||
}
|
||||
|
||||
void PE_SetLayoutBoxAlign(int parts_no, int align)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_layout_box *lb = parts_get_layout_box(parts);
|
||||
if (lb->align != align) {
|
||||
lb->align = align;
|
||||
parts_component_dirty(parts);
|
||||
}
|
||||
}
|
||||
|
||||
int PE_GetLayoutBoxAlign(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.align;
|
||||
}
|
||||
|
||||
void PE_set_layoutbox_padding(int parts_no, int top, int bottom, int left, int right)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_layout_box *lb = parts_get_layout_box(parts);
|
||||
lb->padding_top = top;
|
||||
lb->padding_bottom = bottom;
|
||||
lb->padding_left = left;
|
||||
lb->padding_right = right;
|
||||
parts_component_dirty(parts);
|
||||
}
|
||||
|
||||
int PE_get_layoutbox_padding_top(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.padding_top;
|
||||
}
|
||||
|
||||
int PE_get_layoutbox_padding_bottom(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.padding_bottom;
|
||||
}
|
||||
|
||||
int PE_get_layoutbox_padding_left(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.padding_left;
|
||||
}
|
||||
|
||||
int PE_get_layoutbox_padding_right(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.padding_right;
|
||||
}
|
||||
|
||||
static void parts_get_layout_size(struct parts *parts, int *w, int *h)
|
||||
{
|
||||
struct parts_state *state = &parts->states[parts->state];
|
||||
if (state->type == PARTS_FLAT && state->flat.flat) {
|
||||
// The layout size of Flat is specified in the header, and this may
|
||||
// differ from the hitbox size.
|
||||
*w = state->flat.flat->hdr.width;
|
||||
*h = state->flat.flat->hdr.height;
|
||||
} else {
|
||||
*w = state->common.w;
|
||||
*h = state->common.h;
|
||||
}
|
||||
}
|
||||
|
||||
static int align_offset_x(int align, int total_w)
|
||||
{
|
||||
switch (align) {
|
||||
case 2: case 5: case 8: return -(total_w / 2);
|
||||
case 3: case 6: case 9: return -total_w;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int align_offset_y(int align, int total_h)
|
||||
{
|
||||
switch (align) {
|
||||
case 4: case 5: case 6: return -(total_h / 2);
|
||||
case 7: case 8: case 9: return -total_h;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates the total size of a horizontal layout box by summing child widths per row.
|
||||
static void parts_layout_calc_size_horizontal(struct parts_layout_box *lb, struct parts *parts,
|
||||
int *out_w, int *out_h)
|
||||
{
|
||||
int row_w = 0, max_row_w = 0, max_row_h = 0, total_h = 0;
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
int child_w, child_h;
|
||||
parts_get_layout_size(child, &child_w, &child_h);
|
||||
int cw = child->margin_left + child_w + child->margin_right;
|
||||
int ch = child->margin_top + child_h + child->margin_bottom;
|
||||
if (lb->wrap && row_w + cw > lb->wrap_size && row_w > 0) {
|
||||
if (row_w > max_row_w)
|
||||
max_row_w = row_w;
|
||||
total_h += max_row_h;
|
||||
row_w = 0;
|
||||
max_row_h = 0;
|
||||
}
|
||||
row_w += cw;
|
||||
if (ch > max_row_h)
|
||||
max_row_h = ch;
|
||||
}
|
||||
if (row_w > max_row_w)
|
||||
max_row_w = row_w;
|
||||
total_h += max_row_h;
|
||||
*out_w = max_row_w + lb->padding_left + lb->padding_right;
|
||||
*out_h = total_h + lb->padding_top + lb->padding_bottom;
|
||||
}
|
||||
|
||||
// Calculates the total size of a vertical layout box by summing child heights per column.
|
||||
static void parts_layout_calc_size_vertical(struct parts_layout_box *lb, struct parts *parts,
|
||||
int *out_w, int *out_h)
|
||||
{
|
||||
int col_h = 0, max_col_h = 0, max_col_w = 0, total_w = 0;
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
int child_w, child_h;
|
||||
parts_get_layout_size(child, &child_w, &child_h);
|
||||
int cw = child->margin_left + child_w + child->margin_right;
|
||||
int ch = child->margin_top + child_h + child->margin_bottom;
|
||||
if (lb->wrap && col_h + ch > lb->wrap_size && col_h > 0) {
|
||||
if (col_h > max_col_h)
|
||||
max_col_h = col_h;
|
||||
total_w += max_col_w;
|
||||
col_h = 0;
|
||||
max_col_w = 0;
|
||||
}
|
||||
col_h += ch;
|
||||
if (cw > max_col_w)
|
||||
max_col_w = cw;
|
||||
}
|
||||
if (col_h > max_col_h)
|
||||
max_col_h = col_h;
|
||||
total_w += max_col_w;
|
||||
*out_w = total_w + lb->padding_left + lb->padding_right;
|
||||
*out_h = max_col_h + lb->padding_top + lb->padding_bottom;
|
||||
}
|
||||
|
||||
void parts_do_layout(struct parts *parts)
|
||||
{
|
||||
if (parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return;
|
||||
struct parts_layout_box *lb = &parts->states[0].layout_box;
|
||||
if (lb->layout_type == PARTS_LAYOUT_FREE)
|
||||
return;
|
||||
|
||||
int total_w, total_h;
|
||||
if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL)
|
||||
parts_layout_calc_size_horizontal(lb, parts, &total_w, &total_h);
|
||||
else
|
||||
parts_layout_calc_size_vertical(lb, parts, &total_w, &total_h);
|
||||
|
||||
int align = lb->align;
|
||||
bool is_left = (align == 1 || align == 4 || align == 7);
|
||||
bool is_right = (align == 3 || align == 6 || align == 9);
|
||||
bool is_top = (align == 1 || align == 2 || align == 3);
|
||||
bool is_bottom = (align == 7 || align == 8 || align == 9);
|
||||
|
||||
int origin_x = align_offset_x(parts->origin_mode, total_w);
|
||||
int origin_y = align_offset_y(parts->origin_mode, total_h);
|
||||
int align_x = align_offset_x(align, total_w);
|
||||
int align_y = align_offset_y(align, total_h);
|
||||
|
||||
int start_x = (is_left ? lb->padding_left : -lb->padding_right)
|
||||
+ (origin_x - align_x);
|
||||
int start_y = (is_top ? lb->padding_top : -lb->padding_bottom)
|
||||
+ (origin_y - align_y);
|
||||
|
||||
int x_dir = is_right ? -1 : 1;
|
||||
int y_dir = is_bottom ? -1 : 1;
|
||||
|
||||
int cur_x = start_x;
|
||||
int cur_y = start_y;
|
||||
int max_cross = 0;
|
||||
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
int child_w, child_h;
|
||||
parts_get_layout_size(child, &child_w, &child_h);
|
||||
int cw = child->margin_left + child_w + child->margin_right;
|
||||
int ch = child->margin_top + child_h + child->margin_bottom;
|
||||
|
||||
// The layout box forces the child's origin_mode to match its own align.
|
||||
if (child->origin_mode != align) {
|
||||
child->origin_mode = align;
|
||||
parts_recalculate_hitbox(child);
|
||||
}
|
||||
|
||||
if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL) {
|
||||
if (lb->wrap && abs((cur_x + cw * x_dir) - start_x) > lb->wrap_size
|
||||
&& max_cross > 0) {
|
||||
cur_y += max_cross * y_dir;
|
||||
max_cross = 0;
|
||||
cur_x = start_x;
|
||||
}
|
||||
} else {
|
||||
if (lb->wrap && abs((cur_y + ch * y_dir) - start_y) > lb->wrap_size
|
||||
&& max_cross > 0) {
|
||||
cur_x += max_cross * x_dir;
|
||||
max_cross = 0;
|
||||
cur_y = start_y;
|
||||
}
|
||||
}
|
||||
|
||||
// margin offset depends on alignment direction
|
||||
int mx = is_left ? child->margin_left : is_right ? -child->margin_right : 0;
|
||||
int my = is_top ? child->margin_top : is_bottom ? -child->margin_bottom : 0;
|
||||
|
||||
int pos_x = cur_x + mx;
|
||||
int pos_y = cur_y + my;
|
||||
if (child->local.pos.x != pos_x || child->local.pos.y != pos_y) {
|
||||
child->local.pos.x = pos_x;
|
||||
child->local.pos.y = pos_y;
|
||||
parts_recalculate_hitbox(child);
|
||||
parts_component_dirty(child);
|
||||
}
|
||||
|
||||
if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL) {
|
||||
cur_x += cw * x_dir;
|
||||
if (ch > max_cross)
|
||||
max_cross = ch;
|
||||
} else {
|
||||
cur_y += ch * y_dir;
|
||||
if (cw > max_cross)
|
||||
max_cross = cw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/* Copyright (C) 2026 kichikuou <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, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "vm.h"
|
||||
#include "parts.h"
|
||||
#include "parts_internal.h"
|
||||
|
||||
#define PARTS_MSG_MAX_VARS 4
|
||||
|
||||
struct parts_message {
|
||||
STAILQ_ENTRY(parts_message) entry;
|
||||
int parts_no;
|
||||
int delegate_index;
|
||||
int type;
|
||||
int variables[PARTS_MSG_MAX_VARS];
|
||||
int nr_variables;
|
||||
};
|
||||
|
||||
static STAILQ_HEAD(, parts_message) msg_queue =
|
||||
STAILQ_HEAD_INITIALIZER(msg_queue);
|
||||
|
||||
void parts_msg_push(struct parts* parts, int type, const char *fmt, ...)
|
||||
{
|
||||
// Message system is introduced in Rance 9
|
||||
if (!parts_multi_controller)
|
||||
return;
|
||||
|
||||
struct parts_message *msg = xmalloc(sizeof(*msg));
|
||||
msg->parts_no = parts->no;
|
||||
msg->delegate_index = parts->delegate_index;
|
||||
msg->type = type;
|
||||
msg->nr_variables = 0;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
for (const char *p = fmt; *p; p++) {
|
||||
if (msg->nr_variables >= PARTS_MSG_MAX_VARS) {
|
||||
VM_ERROR("parts_msg_push: too many variables");
|
||||
}
|
||||
switch (*p) {
|
||||
case 'i':
|
||||
msg->variables[msg->nr_variables++] = va_arg(ap, int);
|
||||
break;
|
||||
default:
|
||||
VM_ERROR("parts_msg_push: unsupported format '%c'", *p);
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
STAILQ_INSERT_TAIL(&msg_queue, msg, entry);
|
||||
}
|
||||
|
||||
void PE_ReleaseMessage(void)
|
||||
{
|
||||
while (!STAILQ_EMPTY(&msg_queue)) {
|
||||
struct parts_message *msg = STAILQ_FIRST(&msg_queue);
|
||||
STAILQ_REMOVE_HEAD(&msg_queue, entry);
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void PE_PopMessage(void)
|
||||
{
|
||||
if (STAILQ_EMPTY(&msg_queue))
|
||||
return;
|
||||
struct parts_message *msg = STAILQ_FIRST(&msg_queue);
|
||||
STAILQ_REMOVE_HEAD(&msg_queue, entry);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
int PE_GetMessageType(void)
|
||||
{
|
||||
struct parts_message *msg = STAILQ_FIRST(&msg_queue);
|
||||
return msg ? msg->type : 0;
|
||||
}
|
||||
|
||||
int PE_GetMessagePartsNumber(void)
|
||||
{
|
||||
struct parts_message *msg = STAILQ_FIRST(&msg_queue);
|
||||
return msg ? msg->parts_no : 0;
|
||||
}
|
||||
|
||||
int PE_GetMessageDelegateIndex(void)
|
||||
{
|
||||
struct parts_message *msg = STAILQ_FIRST(&msg_queue);
|
||||
return msg ? msg->delegate_index : -1;
|
||||
}
|
||||
|
||||
int PE_GetMessageVariableCount(void)
|
||||
{
|
||||
struct parts_message *msg = STAILQ_FIRST(&msg_queue);
|
||||
return msg ? msg->nr_variables : 0;
|
||||
}
|
||||
|
||||
int PE_GetMessageVariableType(possibly_unused int index)
|
||||
{
|
||||
// In the current implementation, all variables are integers.
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PE_GetMessageVariableInt(int index)
|
||||
{
|
||||
struct parts_message *msg = STAILQ_FIRST(&msg_queue);
|
||||
if (!msg)
|
||||
return 0;
|
||||
if (index < 0 || index >= msg->nr_variables)
|
||||
return 0;
|
||||
return msg->variables[index];
|
||||
}
|
||||
|
||||
float PE_GetMessageVariableFloat(possibly_unused int index)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool PE_GetMessageVariableBool(possibly_unused int index)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void PE_GetMessageVariableString(possibly_unused int index, struct string **out)
|
||||
{
|
||||
if (*out)
|
||||
free_string(*out);
|
||||
*out = string_ref(&EMPTY_STRING);
|
||||
}
|
||||
+48
-6
@@ -38,6 +38,8 @@ static struct parts_motion_list global_motion_list =
|
||||
|
||||
// true after call to BeginMotion until the motion ends or EndMotion is called
|
||||
static bool is_motion = false;
|
||||
// true while the motion is paused (time does not advance)
|
||||
static bool is_motion_paused = false;
|
||||
// the elapsed time of the current motion
|
||||
static int motion_t = 0;
|
||||
// the end time of the current motion
|
||||
@@ -154,10 +156,15 @@ static void parts_update_with_motion(struct parts *parts, struct parts_motion *m
|
||||
case PARTS_MOTION_ALPHA:
|
||||
parts_set_alpha(parts, motion_calculate_i(motion, motion_t));
|
||||
break;
|
||||
case PARTS_MOTION_CG:
|
||||
parts_cg_set_by_index(parts, parts_get_cg(parts, parts->state),
|
||||
motion_calculate_i(motion, motion_t));
|
||||
case PARTS_MOTION_CG: {
|
||||
int i = motion_calculate_i(motion, motion_t);
|
||||
if (i == 0) {
|
||||
parts_state_reset(&parts->states[parts->state], PARTS_CG);
|
||||
} else {
|
||||
parts_cg_set_by_index(parts, parts_get_cg(parts, parts->state), i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PARTS_MOTION_HGAUGE_RATE:
|
||||
parts_hgauge_set_rate(parts, parts_get_hgauge(parts, parts->state),
|
||||
motion_calculate_f(motion, motion_t));
|
||||
@@ -498,6 +505,7 @@ void PE_BeginMotion(void)
|
||||
// FIXME: starting a motion seems to clear non-default states
|
||||
motion_t = 0;
|
||||
is_motion = true;
|
||||
is_motion_paused = false;
|
||||
parts_init_all_motion();
|
||||
}
|
||||
|
||||
@@ -508,6 +516,7 @@ void PE_EndMotion(void)
|
||||
motion_t = 0;
|
||||
motion_end_t = 0;
|
||||
is_motion = false;
|
||||
is_motion_paused = false;
|
||||
parts_fini_all_motion();
|
||||
}
|
||||
|
||||
@@ -528,10 +537,26 @@ void PE_SeekEndMotion(void)
|
||||
PE_SetMotionTime(motion_end_t - 1);
|
||||
}
|
||||
|
||||
void PE_UpdateMotionTime(int time, possibly_unused bool skip)
|
||||
static int msgskip_speedup_rate = 1;
|
||||
|
||||
void PE_UpdateMotionTime(int time, bool skip)
|
||||
{
|
||||
// TODO: use skip
|
||||
PE_SetMotionTime(motion_t + time);
|
||||
if (is_motion_paused)
|
||||
return;
|
||||
if (skip)
|
||||
PE_SetMotionTime(motion_t + time * msgskip_speedup_rate);
|
||||
else
|
||||
PE_SetMotionTime(motion_t + time);
|
||||
}
|
||||
|
||||
void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate)
|
||||
{
|
||||
msgskip_speedup_rate = max(1, rate);
|
||||
}
|
||||
|
||||
int PE_GetSpeedupRateByMessageSkip(int parts_no)
|
||||
{
|
||||
return msgskip_speedup_rate;
|
||||
}
|
||||
|
||||
bool PE_IsMotion(void)
|
||||
@@ -543,3 +568,20 @@ int PE_GetMotionEndTime(void)
|
||||
{
|
||||
return motion_end_t;
|
||||
}
|
||||
|
||||
void PE_PauseMotion(bool pause)
|
||||
{
|
||||
if (parts_began_click)
|
||||
return;
|
||||
if (!is_motion)
|
||||
return;
|
||||
if (is_motion_paused == pause)
|
||||
return;
|
||||
is_motion_paused = pause;
|
||||
|
||||
// While paused, the per-frame advance stops updating the whole-screen
|
||||
// vibration offset. Reset it to the origin so the screen doesn't stay
|
||||
// stuck at the last shake position.
|
||||
if (pause)
|
||||
parts_set_global_pos((Point){ 0, 0 });
|
||||
}
|
||||
|
||||
+554
-53
@@ -26,25 +26,34 @@
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
#include "sact.h"
|
||||
#include "sprite.h"
|
||||
#include "parts.h"
|
||||
#include "parts_internal.h"
|
||||
#include "reign.h"
|
||||
|
||||
struct parts_list parts_list = TAILQ_HEAD_INITIALIZER(parts_list);
|
||||
static struct parts_list dirty_list = TAILQ_HEAD_INITIALIZER(dirty_list);
|
||||
static struct hash_table *parts_table = NULL;
|
||||
static Point root_pos = { 0, 0 };
|
||||
|
||||
#define PARTS_PARAMS_INITIALIZER (struct parts_params) { \
|
||||
.z = 1, \
|
||||
#define PARTS_PARAMS_INITIALIZER(Z) (struct parts_params) { \
|
||||
.z = Z, \
|
||||
.pos = { 0, 0 }, \
|
||||
.show = true, \
|
||||
.alpha = 255, \
|
||||
.scale = { 1.0f, 1.0f }, \
|
||||
.rotation = { 0.0f, 0.0f, 0.0f }, \
|
||||
.add_color = { 0, 0, 0, 0 }, \
|
||||
.multiply_color = { 255, 255, 255, 255 } \
|
||||
.multiply_color = { 255, 255, 255, 255 }, \
|
||||
.alpha_clipper_parts_no = 0 \
|
||||
}
|
||||
|
||||
static struct parts_params root_params = PARTS_PARAMS_INITIALIZER(0);
|
||||
|
||||
struct parts_controller_stack ctrl_stack;
|
||||
bool parts_multi_controller;
|
||||
|
||||
static void ctrl_stack_init(void);
|
||||
|
||||
static void parts_init(struct parts *parts)
|
||||
{
|
||||
parts->sp.z = 1;
|
||||
@@ -52,9 +61,10 @@ static void parts_init(struct parts *parts)
|
||||
parts->sp.has_alpha = true;
|
||||
parts->sp.render = parts_sprite_render;
|
||||
parts->sp.to_json = parts_sprite_to_json;
|
||||
parts->local = PARTS_PARAMS_INITIALIZER;
|
||||
parts->global = PARTS_PARAMS_INITIALIZER;
|
||||
parts->local = PARTS_PARAMS_INITIALIZER(1);
|
||||
parts->global = PARTS_PARAMS_INITIALIZER(1);
|
||||
parts->delegate_index = -1;
|
||||
parts->want_save = true;
|
||||
parts->on_cursor_sound = -1;
|
||||
parts->on_click_sound = -1;
|
||||
parts->origin_mode = 1;
|
||||
@@ -86,17 +96,38 @@ static void dirty_list_remove(struct parts *parts)
|
||||
TAILQ_REMOVE(&dirty_list, parts, dirty_list_entry);
|
||||
}
|
||||
|
||||
static int parts_get_sprite_z(struct parts *parts)
|
||||
{
|
||||
if (!parts_multi_controller)
|
||||
return parts->global.z;
|
||||
// The system overlay controller sorts above any in-stack controller.
|
||||
return parts->controller_no;
|
||||
}
|
||||
|
||||
static int parts_get_sprite_z2(struct parts *parts)
|
||||
{
|
||||
if (!parts_multi_controller)
|
||||
return 0;
|
||||
return parts->global.z;
|
||||
}
|
||||
|
||||
static void parts_list_insert(struct parts *parts)
|
||||
{
|
||||
int z = parts_get_sprite_z(parts);
|
||||
int z2 = parts_get_sprite_z2(parts);
|
||||
parts->sp.z = z;
|
||||
parts->sp.z2 = z2;
|
||||
struct parts *p;
|
||||
PARTS_LIST_FOREACH(p) {
|
||||
if (p->global.z > parts->global.z) {
|
||||
int pz = parts_get_sprite_z(p);
|
||||
int pz2 = parts_get_sprite_z2(p);
|
||||
if (pz > z || (pz == z && pz2 > z2)) {
|
||||
TAILQ_INSERT_BEFORE(p, parts, parts_list_entry);
|
||||
parts_engine_dirty();
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
TAILQ_INSERT_TAIL(&parts_list, parts, parts_list_entry);
|
||||
done:
|
||||
parts_engine_dirty();
|
||||
scene_register_sprite(&parts->sp);
|
||||
}
|
||||
@@ -112,7 +143,6 @@ void parts_list_resort(struct parts *parts)
|
||||
// TODO: this could be optimized
|
||||
parts_list_remove(parts);
|
||||
parts_list_insert(parts);
|
||||
scene_set_sprite_z(&parts->sp, parts->global.z);
|
||||
}
|
||||
|
||||
struct parts *parts_try_get(int parts_no)
|
||||
@@ -131,6 +161,7 @@ struct parts *parts_get(int parts_no)
|
||||
|
||||
struct parts *parts = parts_alloc();
|
||||
parts->no = parts_no;
|
||||
parts->controller_no = ctrl_stack.active;
|
||||
slot->value = parts;
|
||||
parts_list_insert(parts);
|
||||
return parts;
|
||||
@@ -145,6 +176,8 @@ static void parts_state_free(struct parts_state *state)
|
||||
{
|
||||
switch (state->type) {
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_RECT_DETECTION:
|
||||
case PARTS_LAYOUT_BOX:
|
||||
break;
|
||||
case PARTS_CG:
|
||||
gfx_delete_texture(&state->common.texture);
|
||||
@@ -159,6 +192,8 @@ static void parts_state_free(struct parts_state *state)
|
||||
gfx_delete_texture(&state->anim.frames[i]);
|
||||
}
|
||||
free(state->anim.frames);
|
||||
if (state->anim.cg_name)
|
||||
free_string(state->anim.cg_name);
|
||||
break;
|
||||
case PARTS_NUMERAL:
|
||||
gfx_delete_texture(&state->common.texture);
|
||||
@@ -175,6 +210,22 @@ static void parts_state_free(struct parts_state *state)
|
||||
case PARTS_FLASH:
|
||||
parts_flash_free(&state->flash);
|
||||
break;
|
||||
case PARTS_FLAT:
|
||||
parts_flat_free(&state->flat);
|
||||
break;
|
||||
case PARTS_MOVIE:
|
||||
// The texture is owned by the SACT sprite.
|
||||
state->common.texture.handle = 0;
|
||||
if (state->movie.sprite_no >= 0)
|
||||
sact_SP_Delete(state->movie.sprite_no);
|
||||
break;
|
||||
case PARTS_3DLAYER:
|
||||
state->common.texture.handle = 0;
|
||||
if (state->layer3d.plugin >= 0)
|
||||
ReignEngine_ReleasePlugin(state->layer3d.plugin);
|
||||
if (state->layer3d.sprite_no >= 0)
|
||||
sact_SP_Delete(state->layer3d.sprite_no);
|
||||
break;
|
||||
}
|
||||
memset(state, 0, sizeof(struct parts_state));
|
||||
}
|
||||
@@ -216,10 +267,23 @@ void parts_state_reset(struct parts_state *state, enum parts_type type)
|
||||
state->gauge.cg_no = -1;
|
||||
state->gauge.rate = 0.0f;
|
||||
break;
|
||||
case PARTS_3DLAYER:
|
||||
state->layer3d.plugin = -1;
|
||||
state->layer3d.sprite_no = -1;
|
||||
break;
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_CG:
|
||||
case PARTS_ANIMATION:
|
||||
case PARTS_FLASH:
|
||||
case PARTS_FLAT:
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
case PARTS_MOVIE:
|
||||
state->movie.sprite_no = -1;
|
||||
break;
|
||||
case PARTS_LAYOUT_BOX:
|
||||
state->layout_box.layout_type = PARTS_LAYOUT_VERTICAL;
|
||||
state->layout_box.align = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -288,6 +352,38 @@ struct parts_flash *parts_get_flash(struct parts *parts, int state)
|
||||
return &parts->states[state].flash;
|
||||
}
|
||||
|
||||
struct parts_flat *parts_get_flat(struct parts *parts, int state)
|
||||
{
|
||||
if (parts->states[state].type != PARTS_FLAT) {
|
||||
parts_state_reset(&parts->states[state], PARTS_FLAT);
|
||||
}
|
||||
return &parts->states[state].flat;
|
||||
}
|
||||
|
||||
struct parts_movie *parts_get_movie(struct parts *parts, int state)
|
||||
{
|
||||
if (parts->states[state].type != PARTS_MOVIE) {
|
||||
parts_state_reset(&parts->states[state], PARTS_MOVIE);
|
||||
}
|
||||
return &parts->states[state].movie;
|
||||
}
|
||||
|
||||
struct parts_layout_box *parts_get_layout_box(struct parts *parts)
|
||||
{
|
||||
if (parts->states[0].type != PARTS_LAYOUT_BOX) {
|
||||
parts_state_reset(&parts->states[0], PARTS_LAYOUT_BOX);
|
||||
}
|
||||
return &parts->states[0].layout_box;
|
||||
}
|
||||
|
||||
struct parts_3dlayer *parts_get_3dlayer(struct parts *parts, int state)
|
||||
{
|
||||
if (parts->states[state].type != PARTS_3DLAYER) {
|
||||
parts_state_reset(&parts->states[state], PARTS_3DLAYER);
|
||||
}
|
||||
return &parts->states[state].layer3d;
|
||||
}
|
||||
|
||||
static Point calculate_offset(int mode, int w, int h)
|
||||
{
|
||||
switch (mode) {
|
||||
@@ -358,16 +454,16 @@ void parts_set_pos(struct parts *parts, Point pos)
|
||||
parts->local.pos.x = pos.x;
|
||||
parts->local.pos.y = pos.y;
|
||||
parts_recalculate_hitbox(parts);
|
||||
parts_update_global_pos(parts, parts->parent ? parts->parent->global.pos : root_pos);
|
||||
parts_update_global_pos(parts, parts->parent ? parts->parent->global.pos : root_params.pos);
|
||||
parts_dirty(parts);
|
||||
}
|
||||
|
||||
void parts_set_global_pos(Point pos)
|
||||
{
|
||||
root_pos = pos;
|
||||
root_params.pos = pos;
|
||||
struct parts *parts;
|
||||
PARTS_LIST_FOREACH(parts) {
|
||||
parts_update_global_pos(parts, root_pos);
|
||||
parts_update_global_pos(parts, root_params.pos);
|
||||
}
|
||||
parts_engine_dirty();
|
||||
}
|
||||
@@ -604,7 +700,15 @@ bool parts_cg_set(struct parts *parts, struct parts_cg *parts_cg, struct string
|
||||
{
|
||||
assert(cg_name && *(cg_name->text) != '\0');
|
||||
int no;
|
||||
struct cg *cg = asset_cg_load_by_name(cg_name->text, &no);
|
||||
struct cg *cg;
|
||||
if (!memcmp(cg_name->text, "<save>", 6)) {
|
||||
char *path = savedir_path(cg_name->text + 6);
|
||||
cg = cg_load_file(path);
|
||||
no = 0;
|
||||
free(path);
|
||||
} else {
|
||||
cg = asset_cg_load_by_name(cg_name->text, &no);
|
||||
}
|
||||
return _parts_cg_set(parts, parts_cg, cg, no, string_dup(cg_name));
|
||||
}
|
||||
|
||||
@@ -648,7 +752,7 @@ void parts_numeral_font_init(struct parts_numeral_font *font)
|
||||
x += font->width[i];
|
||||
}
|
||||
gfx_delete_texture(&t);
|
||||
free(cg);
|
||||
cg_free(cg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,7 +818,7 @@ static int parts_load_numeral_font_separate_string(struct string *cg_name)
|
||||
return font_no;
|
||||
}
|
||||
|
||||
static int parts_load_numeral_font_combined(struct cg *cg, int cg_no, int w[12])
|
||||
static int parts_load_numeral_font_combined(int cg_no, int w[12])
|
||||
{
|
||||
// find existing font
|
||||
for (int i = 0; i < parts_nr_numeral_fonts; i++) {
|
||||
@@ -816,7 +920,11 @@ bool parts_numeral_set_number(struct parts *parts, struct parts_numeral *num, in
|
||||
|
||||
void parts_set_state(struct parts *parts, enum parts_state_type state)
|
||||
{
|
||||
if (parts->state != state && parts->states[state].type != PARTS_UNINITIALIZED) {
|
||||
if (parts->lock_input_state)
|
||||
return;
|
||||
while (state > PARTS_STATE_DEFAULT && parts->states[state].type == PARTS_UNINITIALIZED)
|
||||
state--;
|
||||
if (parts->state != state) {
|
||||
parts->state = state;
|
||||
parts_dirty(parts);
|
||||
}
|
||||
@@ -830,20 +938,10 @@ void parts_set_surface_area(struct parts *parts, struct parts_common *common, in
|
||||
parts_common_recalculate_hitbox(parts, common);
|
||||
}
|
||||
|
||||
static void parts_update_loop(struct parts *parts, int passed_time)
|
||||
static bool parts_animation_update(struct parts_animation *anim, int passed_time)
|
||||
{
|
||||
if (parts->states[parts->state].type == PARTS_FLASH) {
|
||||
if (parts_flash_update(&parts->states[parts->state].flash, passed_time))
|
||||
parts_dirty(parts);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parts->states[parts->state].type != PARTS_ANIMATION)
|
||||
return;
|
||||
|
||||
struct parts_animation *anim = &parts->states[parts->state].anim;
|
||||
if (passed_time <= 0 || !anim->nr_frames)
|
||||
return;
|
||||
return false;
|
||||
|
||||
const unsigned elapsed = anim->elapsed + passed_time;
|
||||
const unsigned frame_diff = elapsed / anim->frame_time;
|
||||
@@ -853,12 +951,33 @@ static void parts_update_loop(struct parts *parts, int passed_time)
|
||||
anim->elapsed = remainder;
|
||||
anim->current_frame = (anim->current_frame + frame_diff) % anim->nr_frames;
|
||||
anim->common.texture = anim->frames[anim->current_frame];
|
||||
parts_dirty(parts);
|
||||
return true;
|
||||
} else {
|
||||
anim->elapsed = elapsed;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void parts_update_loop(struct parts *parts, int passed_time)
|
||||
{
|
||||
bool dirty = false;
|
||||
switch (parts->states[parts->state].type) {
|
||||
case PARTS_ANIMATION:
|
||||
dirty = parts_animation_update(&parts->states[parts->state].anim, passed_time);
|
||||
break;
|
||||
case PARTS_FLASH:
|
||||
dirty = parts_flash_update(&parts->states[parts->state].flash, passed_time);
|
||||
break;
|
||||
case PARTS_FLAT:
|
||||
dirty = parts_flat_update(&parts->states[parts->state].flat, passed_time);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (dirty)
|
||||
parts_dirty(parts);
|
||||
}
|
||||
|
||||
static void parts_update_animation(int passed_time)
|
||||
{
|
||||
struct parts *parts;
|
||||
@@ -874,6 +993,7 @@ void parts_release(int parts_no)
|
||||
return;
|
||||
|
||||
struct parts *parts = slot->value;
|
||||
parts_input_reset_drag(parts);
|
||||
parts_clear_motion(parts);
|
||||
for (int i = 0; i < PARTS_NR_STATES; i++) {
|
||||
parts_state_free(&parts->states[i]);
|
||||
@@ -884,6 +1004,7 @@ void parts_release(int parts_no)
|
||||
struct parts *child = TAILQ_FIRST(&parts->children);
|
||||
TAILQ_REMOVE(&parts->children, child, child_list_entry);
|
||||
child->parent = NULL;
|
||||
parts_component_dirty(child);
|
||||
}
|
||||
if (parts->parent) {
|
||||
TAILQ_REMOVE(&parts->parent->children, parts, child_list_entry);
|
||||
@@ -919,6 +1040,14 @@ void parts_release_all(void)
|
||||
|
||||
static bool parts_engine_initialized = false;
|
||||
|
||||
void PE_enable_multi_controller(void)
|
||||
{
|
||||
if (parts_multi_controller)
|
||||
return;
|
||||
assert(!parts_engine_initialized);
|
||||
parts_multi_controller = true;
|
||||
}
|
||||
|
||||
bool PE_Init(void)
|
||||
{
|
||||
if (parts_engine_initialized)
|
||||
@@ -928,6 +1057,7 @@ bool PE_Init(void)
|
||||
parts_table = ht_create(1024);
|
||||
parts_render_init();
|
||||
parts_debug_init();
|
||||
ctrl_stack_init();
|
||||
parts_engine_initialized = true;
|
||||
return true;
|
||||
}
|
||||
@@ -935,6 +1065,8 @@ bool PE_Init(void)
|
||||
void PE_Reset(void)
|
||||
{
|
||||
PE_ReleaseAllParts();
|
||||
PE_ReleaseMessage();
|
||||
ctrl_stack_init();
|
||||
sact_ModuleFini();
|
||||
}
|
||||
|
||||
@@ -965,14 +1097,16 @@ static void parts_combine_params(struct parts_params *parent, struct parts_param
|
||||
out->multiply_color.r = parent->multiply_color.r * (child->multiply_color.r / 255.0f);
|
||||
out->multiply_color.g = parent->multiply_color.g * (child->multiply_color.g / 255.0f);
|
||||
out->multiply_color.b = parent->multiply_color.b * (child->multiply_color.b / 255.0f);
|
||||
out->alpha_clipper_parts_no = child->alpha_clipper_parts_no
|
||||
? child->alpha_clipper_parts_no : parent->alpha_clipper_parts_no;
|
||||
}
|
||||
|
||||
static void parts_update_component(struct parts *parts)
|
||||
{
|
||||
if (parts->parent) {
|
||||
parts_combine_params(&parts->parent->global, &parts->local, &parts->global);
|
||||
}
|
||||
if (parts->global.z != parts->sp.z) {
|
||||
parts_combine_params(parts->parent ? &parts->parent->global : &root_params,
|
||||
&parts->local, &parts->global);
|
||||
if (parts_get_sprite_z(parts) != parts->sp.z
|
||||
|| parts_get_sprite_z2(parts) != parts->sp.z2) {
|
||||
parts_list_resort(parts);
|
||||
}
|
||||
|
||||
@@ -981,6 +1115,8 @@ static void parts_update_component(struct parts *parts)
|
||||
parts->dirty = false;
|
||||
}
|
||||
|
||||
parts_do_layout(parts);
|
||||
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
parts_update_component(child);
|
||||
@@ -989,6 +1125,11 @@ static void parts_update_component(struct parts *parts)
|
||||
|
||||
void PE_UpdateComponent(possibly_unused int passed_time)
|
||||
{
|
||||
// After loading a save, the game script may compute a negative time delta
|
||||
// because it stores an absolute system.GetTime() value in the save data,
|
||||
// which is meaningless across process restarts.
|
||||
if (passed_time < 0)
|
||||
passed_time = 0;
|
||||
while (!TAILQ_EMPTY(&dirty_list)) {
|
||||
// pop parts object from dirty list
|
||||
struct parts *parts = TAILQ_FIRST(&dirty_list);
|
||||
@@ -1003,6 +1144,10 @@ void PE_UpdateComponent(possibly_unused int passed_time)
|
||||
}
|
||||
parts->parent = parent;
|
||||
TAILQ_INSERT_TAIL(&parent->children, parts, child_list_entry);
|
||||
|
||||
// if parent is layout box, mark it dirty so that it can re-layout its children
|
||||
if (parent->states[0].type == PARTS_LAYOUT_BOX)
|
||||
parts_component_dirty(parent);
|
||||
}
|
||||
// TODO: should the child be orphaned if it already has a parent and an invalid
|
||||
// parent no is given?
|
||||
@@ -1026,7 +1171,7 @@ void PE_Update(int passed_time, bool message_window_show)
|
||||
audio_update();
|
||||
parts_update_animation(passed_time);
|
||||
PE_UpdateInputState(passed_time);
|
||||
parts_render_update(passed_time);
|
||||
parts_render_update();
|
||||
}
|
||||
|
||||
void PE_UpdateParts(int passed_time, possibly_unused bool is_skip, bool message_window_show)
|
||||
@@ -1034,7 +1179,7 @@ void PE_UpdateParts(int passed_time, possibly_unused bool is_skip, bool message_
|
||||
parts_message_window_show = message_window_show;
|
||||
audio_update();
|
||||
parts_update_animation(passed_time);
|
||||
parts_render_update(passed_time);
|
||||
parts_render_update();
|
||||
}
|
||||
|
||||
void PE_SetDelegateIndex(int parts_no, int delegate_index)
|
||||
@@ -1048,12 +1193,13 @@ int PE_GetDelegateIndex(int parts_no)
|
||||
return parts ? parts->delegate_index : -1;
|
||||
}
|
||||
|
||||
bool PE_SetPartsCG(int parts_no, struct string *cg_name, possibly_unused int sprite_deform, int state)
|
||||
bool PE_SetPartsCG(int parts_no, struct string *cg_name, int sprite_deform, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
parts->sprite_deform = sprite_deform;
|
||||
if (!cg_name || *(cg_name->text) == '\0') {
|
||||
parts_state_reset(&parts->states[state], PARTS_CG);
|
||||
parts_dirty(parts);
|
||||
@@ -1064,12 +1210,13 @@ bool PE_SetPartsCG(int parts_no, struct string *cg_name, possibly_unused int spr
|
||||
return parts_cg_set(parts, cg, cg_name);
|
||||
}
|
||||
|
||||
bool PE_SetPartsCG_by_index(int parts_no, int cg_no, possibly_unused int sprite_deform, int state)
|
||||
bool PE_SetPartsCG_by_index(int parts_no, int cg_no, int sprite_deform, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
parts->sprite_deform = sprite_deform;
|
||||
if (!cg_no) {
|
||||
parts_state_reset(&parts->states[state], PARTS_CG);
|
||||
parts_dirty(parts);
|
||||
@@ -1082,12 +1229,13 @@ bool PE_SetPartsCG_by_index(int parts_no, int cg_no, possibly_unused int sprite_
|
||||
|
||||
// XXX: Rance Quest
|
||||
bool PE_SetPartsCG_by_string_index(int parts_no, struct string *cg_name,
|
||||
possibly_unused int sprite_deform, int state)
|
||||
int sprite_deform, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
parts->sprite_deform = sprite_deform;
|
||||
if (!cg_name) {
|
||||
parts_state_reset(&parts->states[state], PARTS_CG);
|
||||
parts_dirty(parts);
|
||||
@@ -1134,6 +1282,18 @@ bool PE_SetPartsCGSurfaceArea(int parts_no, int x, int y, int w, int h, int stat
|
||||
return true;
|
||||
}
|
||||
|
||||
void PE_GetPartsCGSurfaceArea(int parts_no, int *x, int *y, int *w, int *h, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return;
|
||||
|
||||
struct parts_cg *cg = parts_get_cg(parts_get(parts_no), state);
|
||||
*x = cg->common.surface_area.x;
|
||||
*y = cg->common.surface_area.y;
|
||||
*w = cg->common.surface_area.w;
|
||||
*h = cg->common.surface_area.h;
|
||||
}
|
||||
|
||||
int PE_GetPartsCGNumber(int parts_no, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state)) {
|
||||
@@ -1216,8 +1376,11 @@ bool parts_animation_set_cg(struct parts *parts, struct parts_animation *anim,
|
||||
{
|
||||
bool r = _parts_animation_set_cg(parts, anim, start_no, nr_frames, frame_time,
|
||||
load_loop_cg_by_name, cg_name);
|
||||
if (r)
|
||||
if (r) {
|
||||
if (anim->cg_name)
|
||||
free_string(anim->cg_name);
|
||||
anim->cg_name = string_dup(cg_name);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1269,6 +1432,7 @@ bool parts_gauge_set_cg(struct parts *parts, struct parts_gauge *g, struct strin
|
||||
if (!cg)
|
||||
return false;
|
||||
_parts_set_gauge_cg(parts, g, cg);
|
||||
cg_free(cg);
|
||||
g->cg_no = cg_no;
|
||||
return true;
|
||||
}
|
||||
@@ -1279,6 +1443,7 @@ bool parts_gauge_set_cg_by_index(struct parts *parts, struct parts_gauge *g, int
|
||||
if (!cg)
|
||||
return false;
|
||||
_parts_set_gauge_cg(parts, g, cg);
|
||||
cg_free(cg);
|
||||
g->cg_no = cg_no;
|
||||
return true;
|
||||
}
|
||||
@@ -1429,13 +1594,12 @@ bool PE_SetNumeralLinkedCGNumberWidthWidthList_by_index(int parts_no, int cg_no,
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct cg *cg = asset_cg_load(cg_no);
|
||||
if (!cg)
|
||||
if (!asset_exists(ASSET_CG, cg_no))
|
||||
return false;
|
||||
|
||||
int w[12] = { w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w_minus, w_comma };
|
||||
struct parts_numeral *n = parts_get_numeral(parts_get(parts_no), state);
|
||||
n->font_no = parts_load_numeral_font_combined(cg, cg_no, w);
|
||||
n->font_no = parts_load_numeral_font_combined(cg_no, w);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1447,13 +1611,12 @@ bool PE_SetNumeralLinkedCGNumberWidthWidthList(int parts_no, struct string *cg_n
|
||||
return false;
|
||||
|
||||
int no;
|
||||
struct cg *cg = asset_cg_load_by_name(cg_name->text, &no);
|
||||
if (!cg)
|
||||
if (!asset_exists_by_name(ASSET_CG, cg_name->text, &no))
|
||||
return false;
|
||||
|
||||
int w[12] = { w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w_minus, w_comma };
|
||||
struct parts_numeral *n = parts_get_numeral(parts_get(parts_no), state);
|
||||
n->font_no = parts_load_numeral_font_combined(cg, no, w);
|
||||
n->font_no = parts_load_numeral_font_combined(no, w);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1540,6 +1703,25 @@ void PE_ReleaseAllPartsWithoutSystem(void)
|
||||
parts_release_all();
|
||||
}
|
||||
|
||||
void PE_ReleaseAllWithoutSystem(struct page **erase_number_list)
|
||||
{
|
||||
// Release all parts not belonging to the system overlay controller
|
||||
struct parts *parts = TAILQ_FIRST(&parts_list);
|
||||
while (parts) {
|
||||
struct parts *next = TAILQ_NEXT(parts, parts_list_entry);
|
||||
if (parts->controller_no != PARTS_CONTROLLER_SYSTEM_OVERLAY) {
|
||||
*erase_number_list = array_pushback(*erase_number_list,
|
||||
(union vm_value){.i = parts->no}, AIN_ARRAY_INT, -1);
|
||||
parts_release(parts->no);
|
||||
}
|
||||
parts = next;
|
||||
}
|
||||
|
||||
// Drop all normal controllers and add a fresh default one.
|
||||
ctrl_stack.nr_controllers = 0;
|
||||
PE_AddController(-1);
|
||||
}
|
||||
|
||||
void PE_SetPos(int parts_no, int x, int y)
|
||||
{
|
||||
parts_set_pos(parts_get(parts_no), (Point){ x, y });
|
||||
@@ -1562,8 +1744,6 @@ void PE_SetAlpha(int parts_no, int alpha)
|
||||
|
||||
void PE_SetPartsDrawFilter(int parts_no, int draw_filter)
|
||||
{
|
||||
if (draw_filter && draw_filter != 1)
|
||||
UNIMPLEMENTED("(%d, %d)", parts_no, draw_filter);
|
||||
parts_get(parts_no)->draw_filter = draw_filter;
|
||||
}
|
||||
|
||||
@@ -1759,6 +1939,14 @@ float PE_GetPartsRotateZ(int parts_no)
|
||||
return parts_get(parts_no)->local.rotation.z;
|
||||
}
|
||||
|
||||
void PE_SetPartsAlphaClipperPartsNumber(int parts_no, int alpha_clipper_parts_no)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
parts->local.alpha_clipper_parts_no = alpha_clipper_parts_no;
|
||||
parts_component_dirty(parts);
|
||||
parts_dirty(parts);
|
||||
}
|
||||
|
||||
void PE_SetPartsPixelDecide(int parts_no, bool pixel_decide)
|
||||
{
|
||||
//UNIMPLEMENTED("(%d, %s)", parts_no, pixel_decide ? "true" : "false");
|
||||
@@ -1776,6 +1964,44 @@ bool PE_SetThumbnailMode(bool mode)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PE_save_thumbnail(struct string *filename, int reduction_factor)
|
||||
{
|
||||
if (reduction_factor < 1)
|
||||
reduction_factor = 1;
|
||||
|
||||
Texture *src = gfx_main_surface();
|
||||
int w = src->w / reduction_factor;
|
||||
int h = src->h / reduction_factor;
|
||||
|
||||
// Downscale by repeatedly halving until we are within a factor of two of
|
||||
// the target size, then do the final stretch. This avoids the aliasing
|
||||
// that a single bilinear minification would otherwise produce.
|
||||
Texture tmp, *cur = src;
|
||||
bool have_tmp = false;
|
||||
while (cur->w / 2 > w && cur->h / 2 > h) {
|
||||
Texture next;
|
||||
gfx_init_texture_blank(&next, cur->w / 2, cur->h / 2);
|
||||
gfx_copy_stretch_with_alpha_map(&next, 0, 0, next.w, next.h, cur, 0, 0, cur->w, cur->h);
|
||||
if (have_tmp)
|
||||
gfx_delete_texture(&tmp);
|
||||
tmp = next;
|
||||
cur = &tmp;
|
||||
have_tmp = true;
|
||||
}
|
||||
|
||||
Texture dst;
|
||||
gfx_init_texture_blank(&dst, w, h);
|
||||
gfx_copy_stretch_with_alpha_map(&dst, 0, 0, w, h, cur, 0, 0, cur->w, cur->h);
|
||||
if (have_tmp)
|
||||
gfx_delete_texture(&tmp);
|
||||
|
||||
char *path = savedir_path(filename->text);
|
||||
int r = gfx_save_texture(&dst, path, ALCG_QNT);
|
||||
free(path);
|
||||
gfx_delete_texture(&dst);
|
||||
return !!r;
|
||||
}
|
||||
|
||||
void PE_SetInputState(int parts_no, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state)) {
|
||||
@@ -1790,10 +2016,72 @@ int PE_GetInputState(int parts_no)
|
||||
return parts_get(parts_no)->state + 1;
|
||||
}
|
||||
|
||||
void PE_SetComponentType(int parts_no, int type, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return;
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
enum parts_type pt = PARTS_UNINITIALIZED;
|
||||
switch (type) {
|
||||
case 8: pt = PARTS_LAYOUT_BOX; break;
|
||||
case 11: pt = PARTS_CG; break;
|
||||
case 12: pt = PARTS_ANIMATION; break;
|
||||
case 13: pt = PARTS_TEXT; break;
|
||||
case 14: pt = PARTS_HGAUGE; break;
|
||||
case 15: pt = PARTS_VGAUGE; break;
|
||||
case 16: pt = PARTS_NUMERAL; break;
|
||||
case 17: pt = PARTS_RECT_DETECTION; break;
|
||||
case 18: pt = PARTS_CONSTRUCTION_PROCESS; break;
|
||||
case 20: pt = PARTS_FLAT; break;
|
||||
case 21: pt = PARTS_3DLAYER; break;
|
||||
case 22: pt = PARTS_MOVIE; break;
|
||||
default:
|
||||
VM_ERROR("unknown component type %d", type);
|
||||
}
|
||||
if (parts->states[state].type != pt)
|
||||
parts_state_reset(&parts->states[state], pt);
|
||||
}
|
||||
|
||||
int PE_GetComponentType(int parts_no, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return -1;
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts)
|
||||
return -1;
|
||||
|
||||
switch (parts->states[state].type) {
|
||||
case PARTS_LAYOUT_BOX: return 8;
|
||||
case PARTS_UNINITIALIZED: // defaluts to CG
|
||||
case PARTS_CG:
|
||||
return 11;
|
||||
case PARTS_ANIMATION: return 12;
|
||||
case PARTS_TEXT: return 13;
|
||||
case PARTS_HGAUGE: return 14;
|
||||
case PARTS_VGAUGE: return 15;
|
||||
case PARTS_NUMERAL: return 16;
|
||||
case PARTS_RECT_DETECTION: return 17;
|
||||
case PARTS_CONSTRUCTION_PROCESS: return 18;
|
||||
case PARTS_FLAT: return 20;
|
||||
case PARTS_3DLAYER: return 21;
|
||||
case PARTS_MOVIE: return 22;
|
||||
case PARTS_FLASH:
|
||||
break;
|
||||
}
|
||||
VM_ERROR("unsupported component type %d", parts->states[state].type);
|
||||
}
|
||||
|
||||
bool PE_SetPartsRectangleDetectionSize(int parts_no, int w, int h, int state)
|
||||
{
|
||||
UNIMPLEMENTED("(%d, %d, %d, %d)", parts_no, w, h, state);
|
||||
return false;
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts)
|
||||
return false;
|
||||
if (parts->states[state].type != PARTS_RECT_DETECTION)
|
||||
parts_state_reset(&parts->states[state], PARTS_RECT_DETECTION);
|
||||
parts_set_dims(parts, &parts->states[state].common, w, h);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PE_SetPartsCGDetectionSize(int parts_no, struct string *cg_name, int state)
|
||||
@@ -1818,8 +2106,221 @@ bool PE_IsExist(int parts_no)
|
||||
return !!ht_get_int(parts_table, parts_no, NULL);
|
||||
}
|
||||
|
||||
void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate)
|
||||
static void ctrl_stack_init(void)
|
||||
{
|
||||
if (rate != 1)
|
||||
UNIMPLEMENTED("(%d, %d)");
|
||||
memset(&ctrl_stack, 0, sizeof(ctrl_stack));
|
||||
// Add initial default controller
|
||||
PE_AddController(-1);
|
||||
}
|
||||
|
||||
// Adds a new controller to the stack and makes it active. The `index`
|
||||
// parameter specifies the position in the stack at which to insert the new
|
||||
// controller; -1 means "insert directly after the currently active
|
||||
// controller". In practice the game only ever passes -1, and the active
|
||||
// controller is always the top of the stack at that point, so this
|
||||
// degenerates to a simple push.
|
||||
int PE_AddController(int index)
|
||||
{
|
||||
if (index != -1)
|
||||
VM_ERROR("index != -1 not supported (got %d)", index);
|
||||
if (ctrl_stack.nr_controllers > 0 &&
|
||||
ctrl_stack.active != ctrl_stack.nr_controllers - 1)
|
||||
VM_ERROR("active controller is not at the top of the stack");
|
||||
if (ctrl_stack.nr_controllers >= PARTS_CONTROLLER_STACK_MAX)
|
||||
VM_ERROR("controller stack overflow");
|
||||
|
||||
int no = ctrl_stack.nr_controllers++;
|
||||
ctrl_stack.active = no;
|
||||
return no;
|
||||
}
|
||||
|
||||
// Removes the controller at position `index` from the stack, releases all
|
||||
// parts belonging to it, and returns their parts numbers in
|
||||
// `erase_number_list`. `index == -1` means "remove the currently active
|
||||
// controller". In practice the game only ever passes -1, and the active
|
||||
// controller is always the top of the stack at that point, so this
|
||||
// degenerates to a simple pop.
|
||||
void PE_RemoveController(struct page **erase_number_list, int index)
|
||||
{
|
||||
if (index != -1)
|
||||
VM_ERROR("index != -1 not supported (got %d)", index);
|
||||
if (ctrl_stack.nr_controllers == 0 ||
|
||||
ctrl_stack.active != ctrl_stack.nr_controllers - 1)
|
||||
VM_ERROR("active controller is not at the top of the stack");
|
||||
|
||||
int ctrl_no = ctrl_stack.active;
|
||||
|
||||
// Collect and release parts belonging to this controller
|
||||
struct parts *p = TAILQ_FIRST(&parts_list);
|
||||
while (p) {
|
||||
struct parts *next = TAILQ_NEXT(p, parts_list_entry);
|
||||
if (p->controller_no == ctrl_no) {
|
||||
*erase_number_list = array_pushback(*erase_number_list,
|
||||
(union vm_value){.i = p->delegate_index}, AIN_ARRAY_INT, -1);
|
||||
parts_release(p->no);
|
||||
}
|
||||
p = next;
|
||||
}
|
||||
|
||||
ctrl_stack.nr_controllers--;
|
||||
if (ctrl_stack.nr_controllers == 0) {
|
||||
PE_AddController(-1);
|
||||
} else {
|
||||
ctrl_stack.active = ctrl_stack.nr_controllers - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void PE_set_active_controller(int controller_no)
|
||||
{
|
||||
if (controller_no == PARTS_CONTROLLER_SYSTEM_OVERLAY ||
|
||||
(controller_no >= 0 && controller_no < ctrl_stack.nr_controllers))
|
||||
ctrl_stack.active = controller_no;
|
||||
else
|
||||
VM_ERROR("Invalid controller number: %d", controller_no);
|
||||
}
|
||||
|
||||
int PE_get_active_controller(void)
|
||||
{
|
||||
return ctrl_stack.active;
|
||||
}
|
||||
|
||||
int PE_get_system_controller(void)
|
||||
{
|
||||
return PARTS_CONTROLLER_SYSTEM_OVERLAY;
|
||||
}
|
||||
|
||||
int PE_get_nr_controllers(void)
|
||||
{
|
||||
return ctrl_stack.nr_controllers;
|
||||
}
|
||||
|
||||
void PE_parts_set_want_save(int parts_no, bool want_save)
|
||||
{
|
||||
parts_get(parts_no)->want_save = want_save;
|
||||
}
|
||||
|
||||
float PE_parts_get_absolute_x(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
return parts ? (float)parts->global.pos.x : 0.0f;
|
||||
}
|
||||
|
||||
float PE_parts_get_absolute_y(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
return parts ? (float)parts->global.pos.y : 0.0f;
|
||||
}
|
||||
|
||||
int PE_parts_get_absolute_z(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
return parts ? parts->global.z : 0;
|
||||
}
|
||||
|
||||
void PE_parts_set_lock_input_state(int parts_no, bool lock)
|
||||
{
|
||||
parts_get(parts_no)->lock_input_state = lock;
|
||||
}
|
||||
|
||||
bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_movie *movie = parts_get_movie(parts, state);
|
||||
|
||||
int sp_no = sact_SP_GetUnuseNum(0);
|
||||
struct sact_sprite *sp = sact_create_sprite(sp_no, width, height, bg_r, bg_g, bg_b, 255);
|
||||
if (!sp)
|
||||
return false;
|
||||
// The movie frames are composited by the parts engine via parts_render(),
|
||||
// so hide the bound sprite from the scene to avoid double-drawing.
|
||||
sprite_set_show(sp, false);
|
||||
|
||||
struct texture *tex = sprite_get_texture(sp);
|
||||
movie->sprite_no = sp_no;
|
||||
movie->common.texture = *tex; // XXX: textures normally shouldn't be copied like this...
|
||||
parts_set_dims(parts, &movie->common, width, height);
|
||||
parts_dirty(parts);
|
||||
return true;
|
||||
}
|
||||
|
||||
int PE_get_movie_sprite(int parts_no, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return -1;
|
||||
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[state].type != PARTS_MOVIE)
|
||||
return -1;
|
||||
|
||||
return parts->states[state].movie.sprite_no;
|
||||
}
|
||||
|
||||
bool parts_3dlayer_create_plugin(struct parts_3dlayer *l)
|
||||
{
|
||||
if (l->plugin >= 0)
|
||||
return false;
|
||||
|
||||
int handle = ReignEngine_create_plugin(RE_SEAL_PLUGIN);
|
||||
if (handle < 0)
|
||||
return false;
|
||||
|
||||
int sp_no = sact_SP_GetUnuseNum(0);
|
||||
struct sact_sprite *sp = sact_create_sprite(sp_no, 1, 1, 0, 0, 0, 255);
|
||||
if (!sp) {
|
||||
ReignEngine_ReleasePlugin(handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReignEngine_BindPlugin(handle, sp_no)) {
|
||||
sact_SP_Delete(sp_no);
|
||||
ReignEngine_ReleasePlugin(handle);
|
||||
return false;
|
||||
}
|
||||
// The 3D content is composited by the parts engine via parts_render(), so
|
||||
// hide the bound sprite from the scene to avoid double-drawing.
|
||||
sprite_set_show(sp, false);
|
||||
|
||||
l->plugin = handle;
|
||||
l->sprite_no = sp_no;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PE_CreateParts3DLayerPluginID(int parts_no, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
return parts_3dlayer_create_plugin(parts_get_3dlayer(parts, state));
|
||||
}
|
||||
|
||||
int PE_GetParts3DLayerPluginID(int parts_no, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return -1;
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[state].type != PARTS_3DLAYER)
|
||||
return -1;
|
||||
return parts->states[state].layer3d.plugin;
|
||||
}
|
||||
|
||||
bool PE_ReleaseParts3DLayerPluginID(int parts_no, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[state].type != PARTS_3DLAYER)
|
||||
return false;
|
||||
struct parts_3dlayer *l = &parts->states[state].layer3d;
|
||||
if (l->plugin < 0)
|
||||
return false;
|
||||
ReignEngine_ReleasePlugin(l->plugin);
|
||||
l->plugin = -1;
|
||||
if (l->sprite_no >= 0) {
|
||||
sact_SP_Delete(l->sprite_no);
|
||||
l->sprite_no = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+218
-18
@@ -82,7 +82,7 @@ struct sound_motion {
|
||||
struct parts_text_char {
|
||||
Texture t;
|
||||
char ch[4];
|
||||
float advance;
|
||||
int advance;
|
||||
Point off;
|
||||
};
|
||||
|
||||
@@ -103,7 +103,12 @@ enum parts_type {
|
||||
PARTS_VGAUGE,
|
||||
PARTS_CONSTRUCTION_PROCESS,
|
||||
PARTS_FLASH,
|
||||
#define PARTS_NR_TYPES (PARTS_FLASH+1)
|
||||
PARTS_FLAT,
|
||||
PARTS_MOVIE,
|
||||
PARTS_RECT_DETECTION,
|
||||
PARTS_LAYOUT_BOX,
|
||||
PARTS_3DLAYER,
|
||||
#define PARTS_NR_TYPES (PARTS_3DLAYER+1)
|
||||
};
|
||||
|
||||
struct parts_common {
|
||||
@@ -124,7 +129,7 @@ struct parts_text {
|
||||
struct parts_common common;
|
||||
unsigned nr_lines;
|
||||
struct parts_text_line *lines;
|
||||
unsigned line_space;
|
||||
int line_space;
|
||||
struct { float x; int y; } cursor;
|
||||
struct text_style ts;
|
||||
};
|
||||
@@ -173,6 +178,7 @@ struct parts_gauge {
|
||||
float rate;
|
||||
};
|
||||
|
||||
// Serialized in save data. Do not reorder.
|
||||
enum parts_cp_op_type {
|
||||
PARTS_CP_CREATE,
|
||||
PARTS_CP_CREATE_PIXEL_ONLY,
|
||||
@@ -185,7 +191,9 @@ enum parts_cp_op_type {
|
||||
PARTS_CP_COPY_CUT_CG,
|
||||
PARTS_CP_DRAW_TEXT,
|
||||
PARTS_CP_COPY_TEXT,
|
||||
#define PARTS_NR_CP_TYPES (PARTS_CP_COPY_TEXT+1)
|
||||
PARTS_CP_GRAY_FILTER,
|
||||
PARTS_CP_FILL_WITH_ALPHA,
|
||||
#define PARTS_NR_CP_TYPES (PARTS_CP_FILL_WITH_ALPHA+1)
|
||||
};
|
||||
|
||||
struct parts_cp_create {
|
||||
@@ -194,7 +202,7 @@ struct parts_cp_create {
|
||||
};
|
||||
|
||||
struct parts_cp_cg {
|
||||
int no;
|
||||
struct string *name;
|
||||
};
|
||||
|
||||
struct parts_cp_fill {
|
||||
@@ -203,7 +211,7 @@ struct parts_cp_fill {
|
||||
};
|
||||
|
||||
struct parts_cp_cut_cg {
|
||||
int cg_no;
|
||||
struct string *cg_name;
|
||||
int dx, dy, dw, dh;
|
||||
int sx, sy, sw, sh;
|
||||
int interp_type;
|
||||
@@ -216,6 +224,11 @@ struct parts_cp_text {
|
||||
struct text_style style;
|
||||
};
|
||||
|
||||
struct parts_cp_filter {
|
||||
int x, y, w, h;
|
||||
bool full_size;
|
||||
};
|
||||
|
||||
struct parts_cp_op {
|
||||
TAILQ_ENTRY(parts_cp_op) entry;
|
||||
enum parts_cp_op_type type;
|
||||
@@ -225,6 +238,7 @@ struct parts_cp_op {
|
||||
struct parts_cp_fill fill;
|
||||
struct parts_cp_cut_cg cut_cg;
|
||||
struct parts_cp_text text;
|
||||
struct parts_cp_filter filter;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -251,6 +265,13 @@ enum parts_flash_blend_mode {
|
||||
PARTS_FLASH_BLEND_HARDLIGHT = 14,
|
||||
};
|
||||
|
||||
enum parts_draw_filter {
|
||||
PARTS_DRAW_FILTER_NORMAL = 0,
|
||||
PARTS_DRAW_FILTER_ADDITIVE = 1,
|
||||
PARTS_DRAW_FILTER_MULTIPLY = 2,
|
||||
PARTS_DRAW_FILTER_SCREEN = 3,
|
||||
};
|
||||
|
||||
struct parts_flash_object {
|
||||
TAILQ_ENTRY(parts_flash_object) entry;
|
||||
uint16_t depth;
|
||||
@@ -266,6 +287,7 @@ struct parts_flash {
|
||||
struct swf *swf;
|
||||
|
||||
struct swf_tag *tag;
|
||||
bool has_ended;
|
||||
bool stopped;
|
||||
unsigned elapsed;
|
||||
int current_frame;
|
||||
@@ -275,6 +297,76 @@ struct parts_flash {
|
||||
TAILQ_HEAD(, parts_flash_object) display_list;
|
||||
};
|
||||
|
||||
struct flat_layer_state {
|
||||
int current_frame;
|
||||
bool stopped;
|
||||
bool suppress_advance;
|
||||
int jump_target; // pending jump frame, -1 = none
|
||||
// Per-timeline: last matched script key index for change detection
|
||||
// (-2 = uninitialized, -1 = no match, >= 0 = key index)
|
||||
int *last_script_keys;
|
||||
// Per-timeline: child layer state (for TIMELINE libs), NULL otherwise
|
||||
struct flat_layer_state **children;
|
||||
size_t nr_timelines;
|
||||
};
|
||||
|
||||
// Per-frame CG list for a STOP_MOTION library. lib_indices[k] is
|
||||
// the library index of the CG to display at frame k.
|
||||
struct flat_stop_motion_frames {
|
||||
int *lib_indices;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct parts_flat {
|
||||
struct parts_common common;
|
||||
struct string *name;
|
||||
struct flat *flat;
|
||||
bool stopped;
|
||||
bool needs_advance;
|
||||
unsigned elapsed;
|
||||
int end_frame;
|
||||
int pending_seek_delta;
|
||||
struct flat_layer_state *root_state;
|
||||
size_t nr_libraries;
|
||||
Texture *textures; // indexed by library index (only CG libs have valid textures)
|
||||
// Indexed by library index. Only entries for STOP_MOTION libraries have lib_indices populated.
|
||||
struct flat_stop_motion_frames *stop_motion_frames;
|
||||
};
|
||||
|
||||
struct parts_movie {
|
||||
struct parts_common common;
|
||||
int sprite_no; // SACT sprite number used as movie render target
|
||||
};
|
||||
|
||||
enum parts_layout_type {
|
||||
PARTS_LAYOUT_FREE = 0, // no automatic layout
|
||||
PARTS_LAYOUT_VERTICAL = 1,
|
||||
PARTS_LAYOUT_HORIZONTAL = 2,
|
||||
};
|
||||
|
||||
// In AliceSoft's PartsEngine implementation, LayoutBox is a component type
|
||||
// that is NOT per-state. We store it per-state for uniformity with other
|
||||
// component types, but parts_get_layout_box() and all code in layoutbox.c
|
||||
// operate on states[0] only. This is safe as long as game scripts never
|
||||
// convert a LayoutBox parts to/from another component type.
|
||||
struct parts_layout_box {
|
||||
struct parts_common common;
|
||||
enum parts_layout_type layout_type;
|
||||
bool wrap;
|
||||
int wrap_size;
|
||||
int align;
|
||||
int padding_top;
|
||||
int padding_bottom;
|
||||
int padding_left;
|
||||
int padding_right;
|
||||
};
|
||||
|
||||
struct parts_3dlayer {
|
||||
struct parts_common common;
|
||||
int plugin; // ReignEngine plugin handle (-1 = none)
|
||||
int sprite_no; // SACT sprite used as render target
|
||||
};
|
||||
|
||||
struct parts_state {
|
||||
enum parts_type type;
|
||||
union {
|
||||
@@ -286,6 +378,10 @@ struct parts_state {
|
||||
struct parts_gauge gauge;
|
||||
struct parts_construction_process cproc;
|
||||
struct parts_flash flash;
|
||||
struct parts_flat flat;
|
||||
struct parts_movie movie;
|
||||
struct parts_layout_box layout_box;
|
||||
struct parts_3dlayer layer3d;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -300,6 +396,7 @@ struct parts_params {
|
||||
struct { float x, y, z; } rotation;
|
||||
SDL_Color add_color;
|
||||
SDL_Color multiply_color;
|
||||
int alpha_clipper_parts_no;
|
||||
};
|
||||
|
||||
struct parts {
|
||||
@@ -318,22 +415,47 @@ struct parts {
|
||||
int delegate_index;
|
||||
int sprite_deform;
|
||||
bool clickable;
|
||||
bool pass_cursor;
|
||||
bool lock_input_state;
|
||||
bool want_save;
|
||||
bool draggable;
|
||||
int on_cursor_sound;
|
||||
int on_click_sound;
|
||||
int origin_mode;
|
||||
int pending_parent;
|
||||
int linked_to;
|
||||
int linked_from;
|
||||
bool is_hovered;
|
||||
int hover_time;
|
||||
int draw_filter;
|
||||
bool message_window;
|
||||
int margin_top;
|
||||
int margin_bottom;
|
||||
int margin_left;
|
||||
int margin_right;
|
||||
struct parts_motion_list motion;
|
||||
int controller_no;
|
||||
};
|
||||
|
||||
#define PARTS_LIST_FOREACH(iter) TAILQ_FOREACH(iter, &parts_list, parts_list_entry)
|
||||
#define PARTS_LIST_FOREACH_REVERSE(iter) TAILQ_FOREACH_REVERSE(iter, &parts_list, parts_list, parts_list_entry)
|
||||
#define PARTS_FOREACH_CHILD(iter, parent) TAILQ_FOREACH(iter, &parent->children, child_list_entry)
|
||||
|
||||
// parts.c
|
||||
extern struct parts_list parts_list;
|
||||
|
||||
// Controllers are identified by their position in the stack (0 = bottom). The
|
||||
// system overlay controller lives outside the stack.
|
||||
#define PARTS_CONTROLLER_STACK_MAX 10000
|
||||
#define PARTS_CONTROLLER_SYSTEM_OVERLAY PARTS_CONTROLLER_STACK_MAX
|
||||
|
||||
struct parts_controller_stack {
|
||||
int nr_controllers;
|
||||
int active; // stack index or PARTS_CONTROLLER_SYSTEM_OVERLAY
|
||||
};
|
||||
extern struct parts_controller_stack ctrl_stack;
|
||||
extern bool parts_multi_controller;
|
||||
|
||||
struct parts *parts_try_get(int parts_no);
|
||||
struct parts *parts_get(int parts_no);
|
||||
struct parts_cg *parts_get_cg(struct parts *parts, int state);
|
||||
@@ -344,6 +466,10 @@ struct parts_gauge *parts_get_hgauge(struct parts *parts, int state);
|
||||
struct parts_gauge *parts_get_vgauge(struct parts *parts, int state);
|
||||
struct parts_construction_process *parts_get_construction_process(struct parts *parts, int state);
|
||||
struct parts_flash *parts_get_flash(struct parts *parts, int state);
|
||||
struct parts_flat *parts_get_flat(struct parts *parts, int state);
|
||||
struct parts_movie *parts_get_movie(struct parts *parts, int state);
|
||||
struct parts_layout_box *parts_get_layout_box(struct parts *parts);
|
||||
struct parts_3dlayer *parts_get_3dlayer(struct parts *parts, int state);
|
||||
void parts_set_pos(struct parts *parts, Point pos);
|
||||
void parts_set_global_pos(Point pos);
|
||||
void parts_set_dims(struct parts *parts, struct parts_common *common, int w, int h);
|
||||
@@ -378,6 +504,7 @@ bool parts_gauge_set_cg(struct parts *parts, struct parts_gauge *g, struct strin
|
||||
bool parts_gauge_set_cg_by_index(struct parts *parts, struct parts_gauge *g, int cg_no);
|
||||
void parts_hgauge_set_rate(struct parts *parts, struct parts_gauge *g, float rate);
|
||||
void parts_vgauge_set_rate(struct parts *parts, struct parts_gauge *g, float rate);
|
||||
bool parts_3dlayer_create_plugin(struct parts_3dlayer *l);
|
||||
|
||||
// text.c
|
||||
void parts_text_free(struct parts_text *t);
|
||||
@@ -386,7 +513,7 @@ struct string *parts_text_get(struct parts_text *t);
|
||||
|
||||
// render.c
|
||||
void parts_render_init(void);
|
||||
void parts_render_update(int passed_time);
|
||||
void parts_render_update(void);
|
||||
void parts_engine_dirty(void);
|
||||
void parts_engine_clean(void);
|
||||
void parts_dirty(struct parts *parts);
|
||||
@@ -399,8 +526,30 @@ void parts_clear_motion(struct parts *parts);
|
||||
void parts_add_motion(struct parts *parts, struct parts_motion *motion);
|
||||
|
||||
// input.c
|
||||
extern Point parts_prev_pos;
|
||||
extern bool parts_began_click;
|
||||
void parts_input_reset_drag(struct parts *parts);
|
||||
|
||||
// message.c
|
||||
enum parts_message_type {
|
||||
PARTS_MSG_MOUSE_ENTER = 1,
|
||||
PARTS_MSG_MOUSE_MOVE = 2,
|
||||
PARTS_MSG_MOUSE_LEAVE = 3,
|
||||
PARTS_MSG_MOUSE_WHEEL = 4,
|
||||
PARTS_MSG_MOUSE_CLICK = 5,
|
||||
PARTS_MSG_MOUSE_ON = 6,
|
||||
PARTS_MSG_DRAG_BEGIN = 7,
|
||||
PARTS_MSG_DRAGGING = 8,
|
||||
PARTS_MSG_DRAG_END = 9,
|
||||
PARTS_MSG_DROP_ENTER = 10,
|
||||
PARTS_MSG_DROP_ON = 11,
|
||||
PARTS_MSG_DROPPED = 12,
|
||||
PARTS_MSG_DROP_LEAVE = 13,
|
||||
PARTS_MSG_KEY_TRIGGER = 14,
|
||||
PARTS_MSG_KEY_DOWN = 15,
|
||||
PARTS_MSG_KEY_UP = 17,
|
||||
};
|
||||
|
||||
void parts_msg_push(struct parts* parts, int type, const char *fmt, ...);
|
||||
|
||||
// construction.c
|
||||
void parts_cp_op_free(struct parts_cp_op *op);
|
||||
@@ -415,6 +564,67 @@ bool parts_flash_load(struct parts *parts, struct parts_flash *f, struct string
|
||||
bool parts_flash_update(struct parts_flash *f, int passed_time);
|
||||
bool parts_flash_seek(struct parts_flash *f, int frame);
|
||||
|
||||
// flat.c
|
||||
void parts_flat_free(struct parts_flat *f);
|
||||
bool parts_flat_load(struct parts *parts, struct parts_flat *f, struct string *filename);
|
||||
bool parts_flat_update(struct parts_flat *f, int passed_time);
|
||||
int parts_flat_find_library(struct flat *fl, const char *name);
|
||||
int parts_flat_stop_motion_get_cg_lib(struct parts_flat *f, int sm_lib_idx, int local);
|
||||
|
||||
struct flat_emitter;
|
||||
struct flat_key_data_graphic;
|
||||
|
||||
struct flat_emitter_particle {
|
||||
vec2 pos; // emitter-space position (pixels)
|
||||
vec2 scale;
|
||||
vec3 rot; // degrees (x, y, z)
|
||||
float fade_alpha; // 0-1.0
|
||||
int cg_lib_idx; // CG library index of the texture for this particle
|
||||
};
|
||||
|
||||
// Per-key emitter properties after applying the emitter's inherit_* flags.
|
||||
struct flat_emitter_layer_effective {
|
||||
vec2 pos;
|
||||
bool reverse_lr, reverse_tb;
|
||||
float alpha;
|
||||
vec3 add_color;
|
||||
vec3 mul_color;
|
||||
int draw_filter;
|
||||
bool use_scale;
|
||||
bool use_rotation;
|
||||
bool use_origin;
|
||||
};
|
||||
|
||||
#define FLAT_MAX_ANCESTOR_DEPTH 32
|
||||
struct flat_key_stack {
|
||||
const struct flat_key_data_graphic *keys[FLAT_MAX_ANCESTOR_DEPTH];
|
||||
int count;
|
||||
};
|
||||
|
||||
typedef void (*flat_emitter_particle_fn)(const struct flat_emitter_particle *p,
|
||||
void *ud);
|
||||
void parts_flat_align_offset(int align, int w, int h, vec2 out);
|
||||
bool parts_flat_emitter_get_align_offset(struct parts_flat *f, int emitter_lib_idx, vec2 out);
|
||||
void parts_flat_foreach_emitter_particle(struct parts_flat *f, int emitter_lib_idx,
|
||||
const struct flat_key_data_graphic *keys,
|
||||
int birth_frame, int age, int frame_count,
|
||||
flat_emitter_particle_fn fn, void *ud);
|
||||
void parts_flat_build_layer_matrix(const struct flat_key_data_graphic *key,
|
||||
vec2 pos,
|
||||
bool use_rotation, bool use_scale, bool use_origin,
|
||||
bool reverse_lr, bool reverse_tb,
|
||||
mat4 out);
|
||||
void parts_flat_build_emitter_base_matrix(const struct flat_emitter *em,
|
||||
const struct flat_key_stack *stack, mat4 out);
|
||||
void parts_flat_emitter_resolve_layer(
|
||||
const struct flat_emitter *em,
|
||||
const struct flat_key_data_graphic *key,
|
||||
float parts_alpha, float layer_alpha,
|
||||
struct flat_emitter_layer_effective *out);
|
||||
|
||||
// layoutbox.c
|
||||
void parts_do_layout(struct parts *parts);
|
||||
|
||||
// debug.c
|
||||
struct sprite;
|
||||
void parts_debug_init(void);
|
||||
@@ -426,14 +636,4 @@ static inline bool parts_state_valid(int state)
|
||||
return state >= 0 && state <= 2;
|
||||
}
|
||||
|
||||
static inline int parts_get_width(struct parts *parts)
|
||||
{
|
||||
return parts->states[parts->state].common.w;
|
||||
}
|
||||
|
||||
static inline int parts_get_height(struct parts *parts)
|
||||
{
|
||||
return parts->states[parts->state].common.h;
|
||||
}
|
||||
|
||||
#endif /* SYSTEM4_PARTS_INTERNAL_H */
|
||||
|
||||
+415
-51
@@ -15,27 +15,23 @@
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/flat.h"
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "sact.h"
|
||||
#include "scene.h"
|
||||
#include "sprite.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#include "parts_internal.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
static inline float deg2rad(float deg)
|
||||
{
|
||||
return deg * (M_PI / 180.0);
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct shader shader;
|
||||
GLint blend_rate;
|
||||
@@ -43,31 +39,30 @@ static struct {
|
||||
GLint top_right;
|
||||
GLint add_color;
|
||||
GLint multiply_color;
|
||||
GLint draw_filter;
|
||||
GLint use_clipper;
|
||||
GLint clipper_tex;
|
||||
GLint inv_clipper_transform;
|
||||
} parts_shader;
|
||||
|
||||
static void parts_render_text(struct parts *parts, struct parts_text *t)
|
||||
static void set_draw_filter_blend_func(int draw_filter)
|
||||
{
|
||||
int x = parts->global.pos.x + t->common.origin_offset.x;
|
||||
int y = parts->global.pos.y + t->common.origin_offset.y;
|
||||
for (int i = 0; i < t->nr_lines; i++) {
|
||||
struct parts_text_line *line = &t->lines[i];
|
||||
for (int j = 0; j < line->nr_chars; j++) {
|
||||
struct parts_text_char *ch = &line->chars[j];
|
||||
Rectangle rect = {
|
||||
.x = x,
|
||||
.y = y,
|
||||
.w = ch->t.w,
|
||||
.h = ch->t.h
|
||||
};
|
||||
gfx_render_texture(&ch->t, &rect);
|
||||
x += ch->advance;
|
||||
}
|
||||
x = parts->global.pos.x + t->common.origin_offset.x;
|
||||
y += line->height;
|
||||
switch (draw_filter) {
|
||||
case PARTS_DRAW_FILTER_ADDITIVE:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case PARTS_DRAW_FILTER_MULTIPLY:
|
||||
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case PARTS_DRAW_FILTER_SCREEN:
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rectangle *rect, float blend_rate, vec3 add_color, vec3 multiply_color)
|
||||
static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rectangle *rect, float blend_rate, vec3 add_color, vec3 multiply_color, int draw_filter, int alpha_clipper)
|
||||
{
|
||||
mat4 wv_transform = WV_TRANSFORM(config.view_width, config.view_height);
|
||||
|
||||
@@ -86,28 +81,98 @@ static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rec
|
||||
glUniform2f(parts_shader.top_right, rect->x + rect->w, rect->y + rect->h);
|
||||
glUniform3fv(parts_shader.add_color, 1, add_color);
|
||||
glUniform3fv(parts_shader.multiply_color, 1, multiply_color);
|
||||
glUniform1i(parts_shader.draw_filter, draw_filter);
|
||||
|
||||
struct parts *clipper = alpha_clipper ? parts_try_get(alpha_clipper) : NULL;
|
||||
if (clipper) {
|
||||
struct parts_common *c_common = &clipper->states[clipper->state].common;
|
||||
|
||||
// Calculate the inverse of the clipper's world matrix.
|
||||
mat4 clip_mw = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate(clip_mw, (vec3) { clipper->global.pos.x, clipper->global.pos.y, 0 });
|
||||
glm_rotate_z(clip_mw, glm_rad(clipper->local.rotation.z), clip_mw);
|
||||
glm_scale(clip_mw, (vec3){ clipper->global.scale.x, clipper->global.scale.y, 1.0 });
|
||||
glm_translate(clip_mw, (vec3){ c_common->origin_offset.x, c_common->origin_offset.y, 0 });
|
||||
glm_scale(clip_mw, (vec3){ c_common->w, c_common->h, 1.0 });
|
||||
mat4 clip_inv;
|
||||
glm_mat4_inv(clip_mw, clip_inv);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, c_common->texture.handle);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glUniform1i(parts_shader.use_clipper, 1);
|
||||
glUniform1i(parts_shader.clipper_tex, 1);
|
||||
glUniformMatrix4fv(parts_shader.inv_clipper_transform, 1, GL_FALSE, clip_inv[0]);
|
||||
} else {
|
||||
glUniform1i(parts_shader.use_clipper, 0);
|
||||
}
|
||||
|
||||
gfx_run_job(&job);
|
||||
}
|
||||
|
||||
static void parts_render_text(struct parts *parts, struct parts_text *t)
|
||||
{
|
||||
vec3 add_color = {
|
||||
parts->global.add_color.r / 255.0f,
|
||||
parts->global.add_color.g / 255.0f,
|
||||
parts->global.add_color.b / 255.0f
|
||||
};
|
||||
vec3 multiply_color = {
|
||||
parts->global.multiply_color.r / 255.0f,
|
||||
parts->global.multiply_color.g / 255.0f,
|
||||
parts->global.multiply_color.b / 255.0f,
|
||||
};
|
||||
float blend_rate = parts->global.alpha / 255.0;
|
||||
|
||||
int x = parts->global.pos.x + t->common.origin_offset.x;
|
||||
int y = parts->global.pos.y + t->common.origin_offset.y;
|
||||
for (int i = 0; i < t->nr_lines; i++) {
|
||||
struct parts_text_line *line = &t->lines[i];
|
||||
for (int j = 0; j < line->nr_chars; j++) {
|
||||
struct parts_text_char *ch = &line->chars[j];
|
||||
mat4 mw_transform = WORLD_TRANSFORM(ch->t.w, ch->t.h, x, y);
|
||||
Rectangle r = { 0, 0, ch->t.w, ch->t.h };
|
||||
parts_render_texture(&ch->t, mw_transform, &r, blend_rate, add_color,
|
||||
multiply_color, 0, parts->global.alpha_clipper_parts_no);
|
||||
x += ch->advance;
|
||||
}
|
||||
x = parts->global.pos.x + t->common.origin_offset.x;
|
||||
y += line->height + t->line_space;
|
||||
}
|
||||
}
|
||||
|
||||
static void parts_render_cg(struct parts *parts, struct parts_common *common)
|
||||
{
|
||||
switch (parts->draw_filter) {
|
||||
case 1:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
set_draw_filter_blend_func(parts->draw_filter);
|
||||
|
||||
mat4 mw_transform = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate(mw_transform, (vec3) { parts->global.pos.x, parts->global.pos.y, 0 });
|
||||
// FIXME: need perspective for 3D rotate
|
||||
//glm_rotate_x(mw_transform, parts->rotation.x, mw_transform);
|
||||
//glm_rotate_y(mw_transform, parts->rotation.y, mw_transform);
|
||||
glm_rotate_z(mw_transform, parts->local.rotation.z * (M_PI/180.0), mw_transform);
|
||||
glm_rotate_z(mw_transform, glm_rad(parts->local.rotation.z), mw_transform);
|
||||
glm_scale(mw_transform, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0 });
|
||||
glm_translate(mw_transform, (vec3){ common->origin_offset.x, common->origin_offset.y, 0 });
|
||||
glm_scale(mw_transform, (vec3){ common->w, common->h, 1.0 });
|
||||
|
||||
switch (parts->sprite_deform) {
|
||||
// Flip horizontally
|
||||
case 1:
|
||||
glm_translate(mw_transform, (vec3){ common->w, 0.0f, 0.0f });
|
||||
glm_scale(mw_transform, (vec3){ -common->w, common->h, 1.0f });
|
||||
break;
|
||||
// Flip vertically
|
||||
case 2:
|
||||
glm_translate(mw_transform, (vec3){ 0.0f, common->h, 0.0f });
|
||||
glm_scale(mw_transform, (vec3){ common->w, -common->h, 1.0f });
|
||||
break;
|
||||
default:
|
||||
WARNING("Invalid sprite_deform: %d", parts->sprite_deform);
|
||||
// No deform
|
||||
case 0:
|
||||
glm_scale(mw_transform, (vec3){ common->w, common->h, 1.0 });
|
||||
break;
|
||||
}
|
||||
|
||||
Rectangle r = common->surface_area;
|
||||
if (!r.w && !r.h) {
|
||||
@@ -124,11 +189,303 @@ static void parts_render_cg(struct parts *parts, struct parts_common *common)
|
||||
parts->global.multiply_color.g / 255.0f,
|
||||
parts->global.multiply_color.b / 255.0f,
|
||||
};
|
||||
parts_render_texture(&common->texture, mw_transform, &r, parts->global.alpha / 255.0, add_color, multiply_color);
|
||||
parts_render_texture(&common->texture, mw_transform, &r, parts->global.alpha / 255.0,
|
||||
add_color, multiply_color, parts->draw_filter,
|
||||
parts->global.alpha_clipper_parts_no);
|
||||
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
|
||||
struct emitter_render_ud {
|
||||
struct parts_flat *f;
|
||||
mat4 transform; // root * base * layer for this emitter+birth_frame
|
||||
float parent_alpha;
|
||||
vec2 align;
|
||||
int alpha_clipper;
|
||||
int draw_filter;
|
||||
vec3 add_color;
|
||||
vec3 mul_color;
|
||||
};
|
||||
|
||||
static void render_emitter_particle_cb(const struct flat_emitter_particle *p,
|
||||
void *ud)
|
||||
{
|
||||
struct emitter_render_ud *d = ud;
|
||||
if (p->cg_lib_idx < 0 || (size_t)p->cg_lib_idx >= d->f->nr_libraries)
|
||||
return;
|
||||
Texture *tex = &d->f->textures[p->cg_lib_idx];
|
||||
if (!tex->handle)
|
||||
return;
|
||||
|
||||
mat4 m = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate(m, (vec3){ p->pos[0], p->pos[1], 0 });
|
||||
if (p->rot[2] != 0)
|
||||
glm_rotate_z(m, glm_rad(p->rot[2]), m);
|
||||
if (p->rot[0] != 0)
|
||||
glm_rotate_x(m, glm_rad(-p->rot[0]), m);
|
||||
if (p->rot[1] != 0)
|
||||
glm_rotate_y(m, glm_rad(p->rot[1]), m);
|
||||
glm_scale(m, (vec3){ p->scale[0], p->scale[1], 1.0f });
|
||||
glm_translate(m, (vec3){ d->align[0], d->align[1], 0 });
|
||||
|
||||
// Bring particle into screen space, then kill the Z-output row so
|
||||
// clip_z stays at the near plane.
|
||||
glm_mat4_mul(d->transform, m, m);
|
||||
m[0][2] = m[1][2] = m[2][2] = m[3][2] = 0.0f;
|
||||
glm_scale(m, (vec3){ tex->w, tex->h, 1.0f });
|
||||
|
||||
if (d->draw_filter != PARTS_DRAW_FILTER_NORMAL)
|
||||
set_draw_filter_blend_func(d->draw_filter);
|
||||
|
||||
float blend_rate = d->parent_alpha * p->fade_alpha;
|
||||
Rectangle rect = { 0, 0, tex->w, tex->h };
|
||||
parts_render_texture(tex, m, &rect, blend_rate,
|
||||
d->add_color, d->mul_color, d->draw_filter, d->alpha_clipper);
|
||||
|
||||
if (d->draw_filter != PARTS_DRAW_FILTER_NORMAL)
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
|
||||
static void render_flat_emitter(struct parts *parts, struct parts_flat *f,
|
||||
int emitter_lib_idx, int local, int frame_count,
|
||||
struct flat_key_data_graphic *keys,
|
||||
mat4 root, float parent_alpha,
|
||||
struct flat_key_stack *key_stack)
|
||||
{
|
||||
vec2 align;
|
||||
if (!parts_flat_emitter_get_align_offset(f, emitter_lib_idx, align))
|
||||
return;
|
||||
|
||||
struct flat_emitter *em = &f->flat->libraries[emitter_lib_idx].emitter;
|
||||
int active_frames = max(1, frame_count - em->particle_lifetime + 1);
|
||||
float parts_alpha = parts->global.alpha / 255.0f;
|
||||
|
||||
// TODO: key_stack reflects the current render frame, but each particle
|
||||
// should see the ancestor keyframes at its birth frame instead.
|
||||
// Currently all particles of this emitter share the same ancestor pose,
|
||||
// which is noticeable only on `猿玉/取得済みマーク.flat` in Rance 9.
|
||||
// Fixing this requires per-emitter state in parts_flat_update to record
|
||||
// ancestor keys at each birth frame; base would then depend on birth_frame.
|
||||
mat4 base;
|
||||
parts_flat_build_emitter_base_matrix(em, key_stack, base);
|
||||
|
||||
int min_birth = max(0, local - em->particle_lifetime + 1);
|
||||
int max_birth = min(active_frames - 1, local);
|
||||
for (int birth_frame = min_birth; birth_frame <= max_birth; birth_frame++) {
|
||||
int age = local - birth_frame;
|
||||
|
||||
// Use the birth frame's key transform so particles stay at their
|
||||
// birth position while the emitter moves.
|
||||
struct flat_key_data_graphic *birth_key = &keys[birth_frame];
|
||||
float layer_alpha = parent_alpha * birth_key->alpha / 255.0f;
|
||||
|
||||
struct flat_emitter_layer_effective eff;
|
||||
parts_flat_emitter_resolve_layer(em, birth_key,
|
||||
parts_alpha, layer_alpha, &eff);
|
||||
|
||||
// Build per-birth-frame layer matrix from birth_key and the emitter's
|
||||
// inherit_* flags.
|
||||
mat4 layer_m;
|
||||
parts_flat_build_layer_matrix(birth_key, eff.pos,
|
||||
eff.use_rotation, eff.use_scale, eff.use_origin,
|
||||
eff.reverse_lr, eff.reverse_tb,
|
||||
layer_m);
|
||||
|
||||
struct emitter_render_ud ud = {
|
||||
.f = f,
|
||||
.parent_alpha = eff.alpha,
|
||||
.alpha_clipper = parts->global.alpha_clipper_parts_no,
|
||||
.draw_filter = eff.draw_filter,
|
||||
};
|
||||
glm_vec2_copy(align, ud.align);
|
||||
glm_vec3_copy(eff.add_color, ud.add_color);
|
||||
glm_vec3_copy(eff.mul_color, ud.mul_color);
|
||||
glm_mat4_mul(base, layer_m, ud.transform);
|
||||
glm_mat4_mul(root, ud.transform, ud.transform);
|
||||
parts_flat_foreach_emitter_particle(f, emitter_lib_idx, keys,
|
||||
birth_frame, age, frame_count,
|
||||
render_emitter_particle_cb, &ud);
|
||||
}
|
||||
}
|
||||
|
||||
struct flat_draw_ctx {
|
||||
mat4 matrix;
|
||||
float alpha;
|
||||
vec3 add_color;
|
||||
vec3 mul_color;
|
||||
int draw_filter;
|
||||
};
|
||||
|
||||
static void render_flat_layer(struct parts *parts, struct parts_flat *f,
|
||||
struct flat_layer_state *state,
|
||||
struct flat_timeline *timelines, size_t nr_timelines,
|
||||
struct flat_draw_ctx *ctx, mat4 root,
|
||||
struct flat_key_stack *key_stack);
|
||||
|
||||
static void render_flat_cg(struct parts *parts, Texture *tex,
|
||||
struct flat_key_data_graphic *key, struct flat_draw_ctx *ctx)
|
||||
{
|
||||
if (!tex->handle)
|
||||
return;
|
||||
|
||||
set_draw_filter_blend_func(ctx->draw_filter);
|
||||
|
||||
Rectangle rect;
|
||||
if (key->area_width && key->area_height) {
|
||||
rect = (Rectangle){ key->area_x, key->area_y, key->area_width, key->area_height };
|
||||
} else {
|
||||
rect = (Rectangle){ 0, 0, tex->w, tex->h };
|
||||
}
|
||||
|
||||
vec2 align;
|
||||
parts_flat_align_offset(key->align, rect.w, rect.h, align);
|
||||
|
||||
mat4 render_m;
|
||||
glm_mat4_copy(ctx->matrix, render_m);
|
||||
// Kill the Z-output row to pin clip_z at the near plane, avoiding
|
||||
// near/far clipping of 3D-rotated sprites.
|
||||
render_m[0][2] = render_m[1][2] = render_m[2][2] = render_m[3][2] = 0.0f;
|
||||
glm_translate(render_m, (vec3){ align[0], align[1], 0.0f });
|
||||
// area_x/area_y select a sub-rectangle of the texture atlas, but
|
||||
// should not shift the on-screen position. This translation cancels
|
||||
// the offset that the sub-rect's top-left would otherwise introduce.
|
||||
glm_translate(render_m, (vec3){ -(float)key->area_x, -(float)key->area_y, 0.0f });
|
||||
glm_scale(render_m, (vec3){ tex->w, tex->h, 1.0f });
|
||||
|
||||
parts_render_texture(tex, render_m, &rect, ctx->alpha, ctx->add_color, ctx->mul_color,
|
||||
ctx->draw_filter, parts->global.alpha_clipper_parts_no);
|
||||
|
||||
if (ctx->draw_filter != PARTS_DRAW_FILTER_NORMAL)
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
|
||||
static void render_flat_item(struct parts *parts, struct parts_flat *f,
|
||||
struct flat_layer_state *state, size_t tl_idx,
|
||||
struct flat_timeline *tl, int local,
|
||||
struct flat_draw_ctx *parent, mat4 root,
|
||||
struct flat_key_stack *key_stack)
|
||||
{
|
||||
int lib_idx = parts_flat_find_library(f->flat, tl->library_name->text);
|
||||
if (lib_idx < 0 || (size_t)lib_idx >= f->flat->nr_libraries)
|
||||
return;
|
||||
|
||||
struct flat_library *lib = &f->flat->libraries[lib_idx];
|
||||
if (lib->type == FLAT_LIB_EMITTER) {
|
||||
render_flat_emitter(parts, f, lib_idx, local, tl->frame_count,
|
||||
tl->graphic.keys,
|
||||
root, parent->alpha, key_stack);
|
||||
return;
|
||||
}
|
||||
|
||||
struct flat_key_data_graphic *key = &tl->graphic.keys[local];
|
||||
mat4 layer_m;
|
||||
vec2 pos = { key->pos_x, key->pos_y };
|
||||
parts_flat_build_layer_matrix(key, pos,
|
||||
true, true, true,
|
||||
key->reverse_lr, key->reverse_tb,
|
||||
layer_m);
|
||||
|
||||
struct flat_draw_ctx ctx;
|
||||
glm_mat4_mul(parent->matrix, layer_m, ctx.matrix);
|
||||
ctx.alpha = parent->alpha * key->alpha / 255.0f;
|
||||
vec3 key_add = { key->add_r / 255.0f, key->add_g / 255.0f, key->add_b / 255.0f };
|
||||
vec3 key_mul = { key->mul_r / 255.0f, key->mul_g / 255.0f, key->mul_b / 255.0f };
|
||||
glm_vec3_add(parent->add_color, key_add, ctx.add_color);
|
||||
glm_vec3_mul(parent->mul_color, key_mul, ctx.mul_color);
|
||||
ctx.draw_filter = key->draw_filter != PARTS_DRAW_FILTER_NORMAL
|
||||
? key->draw_filter : parent->draw_filter;
|
||||
|
||||
switch (lib->type) {
|
||||
case FLAT_LIB_CG:
|
||||
render_flat_cg(parts, &f->textures[lib_idx], key, &ctx);
|
||||
break;
|
||||
case FLAT_LIB_TIMELINE: {
|
||||
struct flat_layer_state *child = state->children[tl_idx];
|
||||
if (child && key_stack->count < FLAT_MAX_ANCESTOR_DEPTH) {
|
||||
key_stack->keys[key_stack->count++] = key;
|
||||
render_flat_layer(parts, f, child,
|
||||
lib->timeline.timelines, lib->timeline.nr_timelines,
|
||||
&ctx, root, key_stack);
|
||||
key_stack->count--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FLAT_LIB_STOP_MOTION: {
|
||||
int cg_idx = parts_flat_stop_motion_get_cg_lib(f, lib_idx, local);
|
||||
if (cg_idx >= 0 && (size_t)cg_idx < f->nr_libraries)
|
||||
render_flat_cg(parts, &f->textures[cg_idx], key, &ctx);
|
||||
break;
|
||||
}
|
||||
case FLAT_LIB_EMITTER:
|
||||
// cannot happen, handled above
|
||||
break;
|
||||
case FLAT_LIB_MEMORY:
|
||||
// not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void render_flat_layer(struct parts *parts, struct parts_flat *f,
|
||||
struct flat_layer_state *state,
|
||||
struct flat_timeline *timelines, size_t nr_timelines,
|
||||
struct flat_draw_ctx *ctx, mat4 root,
|
||||
struct flat_key_stack *key_stack)
|
||||
{
|
||||
// reverse order for correct z-ordering
|
||||
for (size_t i = nr_timelines; i-- > 0;) {
|
||||
struct flat_timeline *tl = &timelines[i];
|
||||
if (tl->type != FLAT_TIMELINE_GRAPHIC)
|
||||
continue;
|
||||
int local = state->current_frame - tl->begin_frame;
|
||||
if (local < 0 || local >= tl->frame_count)
|
||||
continue;
|
||||
|
||||
if (local >= (int)tl->graphic.count)
|
||||
continue;
|
||||
|
||||
render_flat_item(parts, f, state, i, tl, local, ctx, root, key_stack);
|
||||
}
|
||||
}
|
||||
|
||||
static void parts_render_flat(struct parts *parts, struct parts_flat *f)
|
||||
{
|
||||
if (!f->flat || !f->root_state)
|
||||
return;
|
||||
|
||||
struct flat_draw_ctx ctx;
|
||||
glm_mat4_identity(ctx.matrix);
|
||||
glm_translate(ctx.matrix, (vec3){ parts->global.pos.x, parts->global.pos.y, 0 });
|
||||
glm_rotate_z(ctx.matrix, glm_rad(parts->local.rotation.z), ctx.matrix);
|
||||
glm_scale(ctx.matrix, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0f });
|
||||
ctx.alpha = parts->global.alpha / 255.0f;
|
||||
glm_vec3_zero(ctx.add_color);
|
||||
glm_vec3_one(ctx.mul_color);
|
||||
ctx.draw_filter = PARTS_DRAW_FILTER_NORMAL;
|
||||
|
||||
struct flat_key_stack key_stack = { .count = 0 };
|
||||
render_flat_layer(parts, f, f->root_state,
|
||||
f->flat->timelines, f->flat->nr_timelines,
|
||||
&ctx, ctx.matrix, &key_stack);
|
||||
}
|
||||
|
||||
static void parts_render_3dlayer(struct parts *parts, struct parts_3dlayer *l)
|
||||
{
|
||||
if (l->sprite_no < 0)
|
||||
return;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(l->sprite_no);
|
||||
if (!sp)
|
||||
return;
|
||||
struct texture *tex = sprite_get_texture(sp);
|
||||
if (!tex->handle)
|
||||
return;
|
||||
// The bound sprite's texture is recreated by RE_set_viewport, so
|
||||
// refresh the parts_common's view of it each frame.
|
||||
l->common.texture = *tex;
|
||||
l->common.w = tex->w;
|
||||
l->common.h = tex->h;
|
||||
parts_render_cg(parts, &l->common);
|
||||
}
|
||||
|
||||
static void parts_render_flash_shape(struct parts *parts, struct parts_flash *f, struct parts_flash_object *obj, struct swf_tag_define_shape *tag)
|
||||
{
|
||||
struct texture *src = ht_get_int(f->bitmaps, tag->fill_style.bitmap_id, NULL);
|
||||
@@ -165,14 +522,15 @@ static void parts_render_flash_shape(struct parts *parts, struct parts_flash *f,
|
||||
(parts->global.multiply_color.g / 255.0f) * fixed16_to_float(obj->color_transform.mult_terms[1]),
|
||||
(parts->global.multiply_color.b / 255.0f) * fixed16_to_float(obj->color_transform.mult_terms[2])
|
||||
};
|
||||
parts_render_texture(src, mw_transform, &r, blend_rate, add_color, multiply_color);
|
||||
parts_render_texture(src, mw_transform, &r, blend_rate, add_color, multiply_color,
|
||||
0, parts->global.alpha_clipper_parts_no);
|
||||
}
|
||||
|
||||
static void parts_render_flash_sprite(struct parts *parts, struct parts_flash *f, struct parts_flash_object *obj, struct swf_tag_define_sprite *tag)
|
||||
{
|
||||
struct parts_flash_object *obj2 = ht_get_int(f->sprites, tag->sprite_id, NULL);
|
||||
if (!obj)
|
||||
ERROR("undefined sprite id %d", tag->sprite_id);
|
||||
if (!obj2)
|
||||
return; // sprite has no visual elements.
|
||||
|
||||
struct swf_tag *child = ht_get_int(f->dictionary, obj2->character_id, NULL);
|
||||
if (!child)
|
||||
@@ -239,8 +597,7 @@ void parts_render(struct parts *parts)
|
||||
return;
|
||||
if (parts->linked_to >= 0) {
|
||||
struct parts *link_parts = parts_get(parts->linked_to);
|
||||
struct parts_state *link_state = &link_parts->states[link_parts->state];
|
||||
if (!SDL_PointInRect(&parts_prev_pos, &link_state->common.hitbox))
|
||||
if (!link_parts->is_hovered)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -248,6 +605,8 @@ void parts_render(struct parts *parts)
|
||||
struct parts_state *state = &parts->states[parts->state];
|
||||
switch (state->type) {
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_RECT_DETECTION:
|
||||
case PARTS_LAYOUT_BOX:
|
||||
break;
|
||||
case PARTS_CG:
|
||||
case PARTS_ANIMATION:
|
||||
@@ -255,6 +614,7 @@ void parts_render(struct parts *parts)
|
||||
case PARTS_HGAUGE:
|
||||
case PARTS_VGAUGE:
|
||||
case PARTS_CONSTRUCTION_PROCESS:
|
||||
case PARTS_MOVIE:
|
||||
if (state->common.texture.handle)
|
||||
parts_render_cg(parts, &state->common);
|
||||
break;
|
||||
@@ -264,6 +624,12 @@ void parts_render(struct parts *parts)
|
||||
case PARTS_FLASH:
|
||||
parts_render_flash(parts, &state->flash);
|
||||
break;
|
||||
case PARTS_FLAT:
|
||||
parts_render_flat(parts, &state->flat);
|
||||
break;
|
||||
case PARTS_3DLAYER:
|
||||
parts_render_3dlayer(parts, &state->layer3d);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,14 +658,8 @@ void parts_sprite_render(struct sprite *sp)
|
||||
|
||||
static bool pe_dirty = false;
|
||||
|
||||
void parts_render_update(int passed_time)
|
||||
void parts_render_update(void)
|
||||
{
|
||||
// XXX: hack for Rance 01 load issue
|
||||
// There is a bug in Rance 01 where a single bad frame is displayed after
|
||||
// loading a save. When this happens, the game passes a negative time delta
|
||||
// to PE_Update. We fix this issue by ignoring such calls.
|
||||
if (passed_time < 0)
|
||||
return;
|
||||
if (pe_dirty) {
|
||||
struct parts *p;
|
||||
PARTS_LIST_FOREACH(p) {
|
||||
@@ -326,10 +686,14 @@ void parts_dirty(possibly_unused struct parts *parts)
|
||||
|
||||
void parts_render_init(void)
|
||||
{
|
||||
gfx_load_shader(&parts_shader.shader, "shaders/render.v.glsl", "shaders/parts.f.glsl");
|
||||
gfx_load_shader(&parts_shader.shader, "shaders/parts.v.glsl", "shaders/parts.f.glsl");
|
||||
parts_shader.blend_rate = glGetUniformLocation(parts_shader.shader.program, "blend_rate");
|
||||
parts_shader.bot_left = glGetUniformLocation(parts_shader.shader.program, "bot_left");
|
||||
parts_shader.top_right = glGetUniformLocation(parts_shader.shader.program, "top_right");
|
||||
parts_shader.add_color = glGetUniformLocation(parts_shader.shader.program, "add_color");
|
||||
parts_shader.multiply_color = glGetUniformLocation(parts_shader.shader.program, "multiply_color");
|
||||
parts_shader.draw_filter = glGetUniformLocation(parts_shader.shader.program, "draw_filter");
|
||||
parts_shader.use_clipper = glGetUniformLocation(parts_shader.shader.program, "use_clipper");
|
||||
parts_shader.clipper_tex = glGetUniformLocation(parts_shader.shader.program, "clipper_tex");
|
||||
parts_shader.inv_clipper_transform = glGetUniformLocation(parts_shader.shader.program, "inv_clipper_transform");
|
||||
}
|
||||
|
||||
+170
-15
@@ -16,13 +16,16 @@
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/archive.h"
|
||||
|
||||
#include "vm/page.h"
|
||||
#include "asset_manager.h"
|
||||
#include "parts.h"
|
||||
#include "reign.h"
|
||||
#include "parts_internal.h"
|
||||
#include "../hll/iarray.h"
|
||||
|
||||
#define CURRENT_SAVE_VERSION 2
|
||||
#define CURRENT_SAVE_VERSION 7
|
||||
|
||||
static void save_parts_params(struct iarray_writer *w, struct parts_params *params)
|
||||
{
|
||||
@@ -37,9 +40,10 @@ static void save_parts_params(struct iarray_writer *w, struct parts_params *para
|
||||
iarray_write_float(w, params->rotation.z);
|
||||
iarray_write_color(w, ¶ms->add_color);
|
||||
iarray_write_color(w, ¶ms->multiply_color);
|
||||
iarray_write(w, params->alpha_clipper_parts_no);
|
||||
}
|
||||
|
||||
static void load_parts_params(struct iarray_reader *r, struct parts_params *params)
|
||||
static void load_parts_params(struct iarray_reader *r, struct parts_params *params, int version)
|
||||
{
|
||||
params->z = iarray_read(r);
|
||||
iarray_read_point(r, ¶ms->pos);
|
||||
@@ -52,6 +56,8 @@ static void load_parts_params(struct iarray_reader *r, struct parts_params *para
|
||||
params->rotation.z = iarray_read_float(r);
|
||||
iarray_read_color(r, ¶ms->add_color);
|
||||
iarray_read_color(r, ¶ms->multiply_color);
|
||||
if (version >= 7)
|
||||
params->alpha_clipper_parts_no = iarray_read(r);
|
||||
}
|
||||
|
||||
static void save_parts_cg(struct iarray_writer *w, struct parts_cg *cg)
|
||||
@@ -66,6 +72,7 @@ static void load_parts_cg(struct iarray_reader *r, struct parts *parts, struct p
|
||||
struct string *name = iarray_read_string_or_null(r);
|
||||
if (name) {
|
||||
parts_cg_set(parts, cg, name);
|
||||
free_string(name);
|
||||
} else if (no) {
|
||||
parts_cg_set_by_index(parts, cg, no);
|
||||
}
|
||||
@@ -97,9 +104,15 @@ static void load_parts_text(struct iarray_reader *r, struct parts *parts,
|
||||
text->cursor.y = 0;
|
||||
|
||||
int nr_lines = iarray_read(r);
|
||||
for (unsigned i = 0; i < nr_lines; i++) {
|
||||
for (int i = 0; i < nr_lines; i++) {
|
||||
struct string *line = iarray_read_string(r);
|
||||
if (i > 0) {
|
||||
struct string *nl = make_string("\n", 1);
|
||||
parts_text_append(parts, text, nl);
|
||||
free_string(nl);
|
||||
}
|
||||
parts_text_append(parts, text, line);
|
||||
free_string(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,11 +199,12 @@ static void save_parts_cp_op(struct iarray_writer *w, struct parts_cp_op *op)
|
||||
iarray_write(w, op->create.h);
|
||||
break;
|
||||
case PARTS_CP_CG:
|
||||
iarray_write(w, op->cg.no);
|
||||
iarray_write_string(w, op->cg.name);
|
||||
break;
|
||||
case PARTS_CP_FILL:
|
||||
case PARTS_CP_FILL_ALPHA_COLOR:
|
||||
case PARTS_CP_FILL_AMAP:
|
||||
case PARTS_CP_FILL_WITH_ALPHA:
|
||||
case PARTS_CP_DRAW_RECT:
|
||||
iarray_write(w, op->fill.x);
|
||||
iarray_write(w, op->fill.y);
|
||||
@@ -203,7 +217,7 @@ static void save_parts_cp_op(struct iarray_writer *w, struct parts_cp_op *op)
|
||||
break;
|
||||
case PARTS_CP_DRAW_CUT_CG:
|
||||
case PARTS_CP_COPY_CUT_CG:
|
||||
iarray_write(w, op->cut_cg.cg_no);
|
||||
iarray_write_string(w, op->cut_cg.cg_name);
|
||||
iarray_write(w, op->cut_cg.dx);
|
||||
iarray_write(w, op->cut_cg.dy);
|
||||
iarray_write(w, op->cut_cg.dw);
|
||||
@@ -222,10 +236,34 @@ static void save_parts_cp_op(struct iarray_writer *w, struct parts_cp_op *op)
|
||||
iarray_write(w, op->text.line_space);
|
||||
iarray_write_text_style(w, &op->text.style);
|
||||
break;
|
||||
case PARTS_CP_GRAY_FILTER:
|
||||
iarray_write(w, op->filter.x);
|
||||
iarray_write(w, op->filter.y);
|
||||
iarray_write(w, op->filter.w);
|
||||
iarray_write(w, op->filter.h);
|
||||
iarray_write(w, op->filter.full_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct parts_cp_op *load_parts_cp_op(struct iarray_reader *r)
|
||||
static struct string *load_cp_cg_name(struct iarray_reader *r, int version)
|
||||
{
|
||||
if (version >= 5)
|
||||
return iarray_read_string(r);
|
||||
// In version 4 and earlier, the CG name was stored as an integer ID.
|
||||
// Convert it to a string name by looking up the asset.
|
||||
int cg_no = iarray_read(r);
|
||||
if (cg_no <= 0)
|
||||
return string_ref(&EMPTY_STRING);
|
||||
struct archive_data *data = asset_get(ASSET_CG, cg_no);
|
||||
if (!data)
|
||||
return string_ref(&EMPTY_STRING);
|
||||
struct string *name = cstr_to_string(data->name);
|
||||
archive_free_data(data);
|
||||
return name;
|
||||
}
|
||||
|
||||
static struct parts_cp_op *load_parts_cp_op(struct iarray_reader *r, int version)
|
||||
{
|
||||
struct parts_cp_op *op = xcalloc(1, sizeof(struct parts_cp_op));
|
||||
op->type = iarray_read(r);
|
||||
@@ -236,11 +274,12 @@ static struct parts_cp_op *load_parts_cp_op(struct iarray_reader *r)
|
||||
op->create.h = iarray_read(r);
|
||||
break;
|
||||
case PARTS_CP_CG:
|
||||
op->cg.no = iarray_read(r);
|
||||
op->cg.name = load_cp_cg_name(r, version);
|
||||
break;
|
||||
case PARTS_CP_FILL:
|
||||
case PARTS_CP_FILL_ALPHA_COLOR:
|
||||
case PARTS_CP_FILL_AMAP:
|
||||
case PARTS_CP_FILL_WITH_ALPHA:
|
||||
case PARTS_CP_DRAW_RECT:
|
||||
op->fill.x = iarray_read(r);
|
||||
op->fill.y = iarray_read(r);
|
||||
@@ -253,7 +292,7 @@ static struct parts_cp_op *load_parts_cp_op(struct iarray_reader *r)
|
||||
break;
|
||||
case PARTS_CP_DRAW_CUT_CG:
|
||||
case PARTS_CP_COPY_CUT_CG:
|
||||
op->cut_cg.cg_no = iarray_read(r);
|
||||
op->cut_cg.cg_name = load_cp_cg_name(r, version);
|
||||
op->cut_cg.dx = iarray_read(r);
|
||||
op->cut_cg.dy = iarray_read(r);
|
||||
op->cut_cg.dw = iarray_read(r);
|
||||
@@ -272,6 +311,13 @@ static struct parts_cp_op *load_parts_cp_op(struct iarray_reader *r)
|
||||
op->text.line_space = iarray_read(r);
|
||||
iarray_read_text_style(r, &op->text.style);
|
||||
break;
|
||||
case PARTS_CP_GRAY_FILTER:
|
||||
op->filter.x = iarray_read(r);
|
||||
op->filter.y = iarray_read(r);
|
||||
op->filter.w = iarray_read(r);
|
||||
op->filter.h = iarray_read(r);
|
||||
op->filter.full_size = !!iarray_read(r);
|
||||
break;
|
||||
}
|
||||
return op;
|
||||
}
|
||||
@@ -293,11 +339,11 @@ static void save_parts_construction_process(struct iarray_writer *w,
|
||||
}
|
||||
|
||||
static void load_parts_construction_process(struct iarray_reader *r, struct parts *parts,
|
||||
struct parts_construction_process *cproc)
|
||||
struct parts_construction_process *cproc, int version)
|
||||
{
|
||||
int ops_count = iarray_read(r);
|
||||
for (int i = 0; i < ops_count; i++) {
|
||||
struct parts_cp_op *op = load_parts_cp_op(r);
|
||||
struct parts_cp_op *op = load_parts_cp_op(r, version);
|
||||
parts_add_cp_op(cproc, op);
|
||||
}
|
||||
parts_build_construction_process(parts, cproc);
|
||||
@@ -320,6 +366,62 @@ static void load_parts_flash(struct iarray_reader *r, struct parts *parts,
|
||||
parts_flash_seek(flash, iarray_read(r));
|
||||
}
|
||||
|
||||
static void save_parts_flat(struct iarray_writer *w, struct parts_flat *flat)
|
||||
{
|
||||
iarray_write_string_or_null(w, flat->name);
|
||||
}
|
||||
|
||||
static void load_parts_flat(struct iarray_reader *r, struct parts *parts,
|
||||
struct parts_flat *flat)
|
||||
{
|
||||
struct string *name = iarray_read_string_or_null(r);
|
||||
parts_flat_load(parts, flat, name);
|
||||
free_string(name);
|
||||
flat->needs_advance = true;
|
||||
}
|
||||
|
||||
static void save_parts_3dlayer(struct iarray_writer *w, struct parts_3dlayer *l)
|
||||
{
|
||||
struct RE_plugin *plugin = l->plugin >= 0 ? RE_get_plugin(l->plugin) : NULL;
|
||||
iarray_write(w, !!plugin);
|
||||
if (plugin)
|
||||
RE_plugin_serialize(plugin, w);
|
||||
}
|
||||
|
||||
static void load_parts_3dlayer(struct iarray_reader *r, struct parts_3dlayer *l,
|
||||
int version)
|
||||
{
|
||||
if (!iarray_read(r))
|
||||
return;
|
||||
if (!parts_3dlayer_create_plugin(l))
|
||||
VM_ERROR("cannot create 3D layer plugin");
|
||||
RE_plugin_deserialize(RE_get_plugin(l->plugin), r, version);
|
||||
}
|
||||
|
||||
static void save_parts_layout_box(struct iarray_writer *w, struct parts_layout_box *lb)
|
||||
{
|
||||
iarray_write(w, lb->layout_type);
|
||||
iarray_write(w, lb->wrap);
|
||||
iarray_write(w, lb->wrap_size);
|
||||
iarray_write(w, lb->align);
|
||||
iarray_write(w, lb->padding_top);
|
||||
iarray_write(w, lb->padding_bottom);
|
||||
iarray_write(w, lb->padding_left);
|
||||
iarray_write(w, lb->padding_right);
|
||||
}
|
||||
|
||||
static void load_parts_layout_box(struct iarray_reader *r, struct parts_layout_box *lb)
|
||||
{
|
||||
lb->layout_type = iarray_read(r);
|
||||
lb->wrap = iarray_read(r);
|
||||
lb->wrap_size = iarray_read(r);
|
||||
lb->align = iarray_read(r);
|
||||
lb->padding_top = iarray_read(r);
|
||||
lb->padding_bottom = iarray_read(r);
|
||||
lb->padding_left = iarray_read(r);
|
||||
lb->padding_right = iarray_read(r);
|
||||
}
|
||||
|
||||
static void save_parts_state(struct iarray_writer *w, struct parts_state *state)
|
||||
{
|
||||
iarray_write(w, state->type);
|
||||
@@ -330,6 +432,11 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state)
|
||||
iarray_write_rectangle(w, &state->common.surface_area);
|
||||
switch (state->type) {
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_MOVIE:
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
case PARTS_3DLAYER:
|
||||
save_parts_3dlayer(w, &state->layer3d);
|
||||
break;
|
||||
case PARTS_CG:
|
||||
save_parts_cg(w, &state->cg);
|
||||
@@ -353,11 +460,17 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state)
|
||||
case PARTS_FLASH:
|
||||
save_parts_flash(w, &state->flash);
|
||||
break;
|
||||
case PARTS_FLAT:
|
||||
save_parts_flat(w, &state->flat);
|
||||
break;
|
||||
case PARTS_LAYOUT_BOX:
|
||||
save_parts_layout_box(w, &state->layout_box);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_parts_state(struct iarray_reader *r, struct parts *parts,
|
||||
struct parts_state *state)
|
||||
struct parts_state *state, int version)
|
||||
{
|
||||
parts_state_reset(state, iarray_read(r));
|
||||
state->common.w = iarray_read(r);
|
||||
@@ -367,6 +480,12 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts,
|
||||
iarray_read_rectangle(r, &state->common.surface_area);
|
||||
switch (state->type) {
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_MOVIE:
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
case PARTS_3DLAYER:
|
||||
if (version > 5)
|
||||
load_parts_3dlayer(r, &state->layer3d, version);
|
||||
break;
|
||||
case PARTS_CG:
|
||||
load_parts_cg(r, parts, &state->cg);
|
||||
@@ -387,11 +506,17 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts,
|
||||
load_parts_gauge(r, parts, &state->gauge, true);
|
||||
break;
|
||||
case PARTS_CONSTRUCTION_PROCESS:
|
||||
load_parts_construction_process(r, parts, &state->cproc);
|
||||
load_parts_construction_process(r, parts, &state->cproc, version);
|
||||
break;
|
||||
case PARTS_FLASH:
|
||||
load_parts_flash(r, parts, &state->flash);
|
||||
break;
|
||||
case PARTS_FLAT:
|
||||
load_parts_flat(r, parts, &state->flat);
|
||||
break;
|
||||
case PARTS_LAYOUT_BOX:
|
||||
load_parts_layout_box(r, &state->layout_box);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,6 +614,14 @@ static void save_parts(struct iarray_writer *w, struct parts *parts)
|
||||
iarray_write(w, parts->linked_from);
|
||||
iarray_write(w, parts->draw_filter);
|
||||
iarray_write(w, parts->message_window);
|
||||
iarray_write(w, parts->controller_no);
|
||||
iarray_write(w, parts->pass_cursor);
|
||||
iarray_write(w, parts->lock_input_state);
|
||||
iarray_write(w, parts->margin_top);
|
||||
iarray_write(w, parts->margin_bottom);
|
||||
iarray_write(w, parts->margin_left);
|
||||
iarray_write(w, parts->margin_right);
|
||||
iarray_write(w, parts->draggable);
|
||||
|
||||
unsigned motion_count_pos = iarray_writer_pos(w);
|
||||
iarray_write(w, 0); // size of motion list
|
||||
@@ -509,11 +642,11 @@ static void load_parts(struct iarray_reader *r, int version)
|
||||
struct parts *parts = parts_get(no);
|
||||
parts->state = iarray_read(r);
|
||||
for (int i = 0; i < PARTS_NR_STATES; i++) {
|
||||
load_parts_state(r, parts, &parts->states[i]);
|
||||
load_parts_state(r, parts, &parts->states[i], version);
|
||||
}
|
||||
|
||||
load_parts_params(r, &parts->local);
|
||||
load_parts_params(r, &parts->global);
|
||||
load_parts_params(r, &parts->local, version);
|
||||
load_parts_params(r, &parts->global, version);
|
||||
parts->pending_parent = iarray_read(r);
|
||||
parts->delegate_index = iarray_read(r);
|
||||
parts->sprite_deform = iarray_read(r);
|
||||
@@ -526,6 +659,18 @@ static void load_parts(struct iarray_reader *r, int version)
|
||||
parts->draw_filter = iarray_read(r);
|
||||
if (version > 0)
|
||||
parts->message_window = iarray_read(r);
|
||||
if (version > 2 && version < 7)
|
||||
parts->local.alpha_clipper_parts_no = iarray_read(r);
|
||||
if (version > 3 || (version == 3 && parts_multi_controller)) {
|
||||
parts->controller_no = iarray_read(r);
|
||||
parts->pass_cursor = iarray_read(r);
|
||||
parts->lock_input_state = iarray_read(r);
|
||||
parts->margin_top = iarray_read(r);
|
||||
parts->margin_bottom = iarray_read(r);
|
||||
parts->margin_left = iarray_read(r);
|
||||
parts->margin_right = iarray_read(r);
|
||||
parts->draggable = iarray_read(r);
|
||||
}
|
||||
|
||||
int motion_count = iarray_read(r);
|
||||
for (int i = 0; i < motion_count; i++) {
|
||||
@@ -577,6 +722,9 @@ static bool parts_engine_save(struct page **buffer, bool save_hidden)
|
||||
if (CURRENT_SAVE_VERSION > 1)
|
||||
save_numeral_fonts(&w);
|
||||
|
||||
iarray_write(&w, ctrl_stack.active);
|
||||
iarray_write(&w, ctrl_stack.nr_controllers);
|
||||
|
||||
unsigned count_pos = iarray_writer_pos(&w);
|
||||
iarray_write(&w, 0); // size of parts list
|
||||
|
||||
@@ -585,6 +733,8 @@ static bool parts_engine_save(struct page **buffer, bool save_hidden)
|
||||
PARTS_LIST_FOREACH(parts) {
|
||||
if (!save_hidden && !parts->global.show)
|
||||
continue;
|
||||
if (!parts->want_save)
|
||||
continue;
|
||||
save_parts(&w, parts);
|
||||
count++;
|
||||
}
|
||||
@@ -634,6 +784,11 @@ bool PE_Load(struct page **buffer)
|
||||
if (version > 1)
|
||||
load_numeral_fonts(&r);
|
||||
|
||||
if (version > 3 || (version == 3 && parts_multi_controller)) {
|
||||
ctrl_stack.active = iarray_read(&r);
|
||||
ctrl_stack.nr_controllers = iarray_read(&r);
|
||||
}
|
||||
|
||||
int nr_parts = iarray_read(&r);
|
||||
if (nr_parts < 0) {
|
||||
WARNING("invalid parts count");
|
||||
|
||||
+86
-12
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
@@ -71,6 +72,7 @@ static const char *parts_text_append_char(struct parts_text *t, const char *str)
|
||||
if (*str == '\n') {
|
||||
t->lines = xrealloc_array(t->lines, t->nr_lines, t->nr_lines + 1,
|
||||
sizeof(struct parts_text_line));
|
||||
t->lines[t->nr_lines].height = text_style_height(&t->ts);
|
||||
t->nr_lines++;
|
||||
return str + 1;
|
||||
}
|
||||
@@ -82,11 +84,10 @@ static const char *parts_text_append_char(struct parts_text *t, const char *str)
|
||||
|
||||
ch->off = text_style_offset(&t->ts);
|
||||
int len = extract_sjis_char(str, ch->ch);
|
||||
int width = ceilf(text_style_width(&t->ts, ch->ch));
|
||||
int width = gfx_size_char(&t->ts, ch->ch);
|
||||
int height = text_style_height(&t->ts);
|
||||
gfx_init_texture_rgba(&ch->t, width, height, (SDL_Color){0,0,0,0});
|
||||
ch->advance = gfx_render_textf(&ch->t, 0, 0, ch->ch, &t->ts, false);
|
||||
|
||||
ch->advance = ceilf(gfx_render_textf(&ch->t, 0, 0, ch->ch, &t->ts, false));
|
||||
line->width += ch->advance;
|
||||
line->height = max(line->height, height);
|
||||
return str + len;
|
||||
@@ -109,6 +110,8 @@ void parts_text_append(struct parts *parts, struct parts_text *t, struct string
|
||||
int height = 0;
|
||||
for (int i = 0; i < t->nr_lines; i++) {
|
||||
f_width = max(f_width, t->lines[i].width);
|
||||
if (i > 0)
|
||||
height += t->line_space;
|
||||
height += t->lines[i].height;
|
||||
}
|
||||
int width = ceilf(f_width);
|
||||
@@ -131,6 +134,20 @@ void parts_text_free(struct parts_text *t)
|
||||
free(t->lines);
|
||||
}
|
||||
|
||||
static void parts_text_rerender(struct parts *parts, struct parts_text *t)
|
||||
{
|
||||
if (!t->nr_lines)
|
||||
return;
|
||||
struct string *text = parts_text_get(t);
|
||||
parts_text_free(t);
|
||||
t->ts.font_size = NULL; // clear cached font
|
||||
t->lines = NULL;
|
||||
t->nr_lines = 0;
|
||||
parts_text_append(parts, t, text);
|
||||
free_string(text);
|
||||
parts_dirty(parts);
|
||||
}
|
||||
|
||||
static void parts_text_clear(struct parts *parts, int state)
|
||||
{
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
@@ -152,6 +169,14 @@ bool PE_SetText(int parts_no, struct string *text, int state)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct string *PE_GetText(int parts_no, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return NULL;
|
||||
|
||||
return parts_text_get(parts_get_text(parts_get(parts_no), state));
|
||||
}
|
||||
|
||||
bool PE_AddPartsText(int parts_no, struct string *text, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
@@ -180,13 +205,16 @@ bool PE_SetFont(int parts_no, int type, int size, int r, int g, int b, float bol
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct parts_text *text = parts_get_text(parts_get(parts_no), state);
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
text->ts.face = type;
|
||||
text->ts.size = size;
|
||||
text->ts.color = (SDL_Color) { r, g, b, 255 };
|
||||
text->ts.weight = bold_weight * 1000;
|
||||
text->ts.edge_color = (SDL_Color) { edge_r, edge_g, edge_b, 255 };
|
||||
text_style_set_edge_width(&text->ts, edge_weight);
|
||||
text->ts.bold_width = bold_weight;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -195,7 +223,12 @@ bool PE_SetPartsFontType(int parts_no, int type, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
parts_get_text(parts_get(parts_no), state)->ts.face = type;
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
if (text->ts.face == type)
|
||||
return true;
|
||||
text->ts.face = type;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -204,7 +237,12 @@ bool PE_SetPartsFontSize(int parts_no, int size, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
parts_get_text(parts_get(parts_no), state)->ts.size = size;
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
if (text->ts.size == size)
|
||||
return true;
|
||||
text->ts.size = size;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -213,7 +251,13 @@ bool PE_SetPartsFontColor(int parts_no, int r, int g, int b, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
parts_get_text(parts_get(parts_no), state)->ts.color = (SDL_Color) { r, g, b, 255 };
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
SDL_Color color = { r, g, b, 255 };
|
||||
if (!memcmp(&text->ts.color, &color, sizeof(SDL_Color)))
|
||||
return true;
|
||||
text->ts.color = color;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -222,7 +266,12 @@ bool PE_SetPartsFontBoldWeight(int parts_no, float bold_weight, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
parts_get_text(parts_get(parts_no), state)->ts.bold_width = bold_weight;
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
if (text->ts.bold_width == bold_weight)
|
||||
return true;
|
||||
text->ts.bold_width = bold_weight;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -231,7 +280,13 @@ bool PE_SetPartsFontEdgeColor(int parts_no, int r, int g, int b, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
parts_get_text(parts_get(parts_no), state)->ts.edge_color = (SDL_Color) { r, g, b, 255 };
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
SDL_Color color = { r, g, b, 255 };
|
||||
if (!memcmp(&text->ts.edge_color, &color, sizeof(SDL_Color)))
|
||||
return true;
|
||||
text->ts.edge_color = color;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -240,8 +295,17 @@ bool PE_SetPartsFontEdgeWeight(int parts_no, float edge_weight, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
struct parts_text *text = parts_get_text(parts_get(parts_no), state);
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
float old_left = text->ts.edge_left;
|
||||
float old_up = text->ts.edge_up;
|
||||
float old_right = text->ts.edge_right;
|
||||
float old_down = text->ts.edge_down;
|
||||
text_style_set_edge_width(&text->ts, edge_weight);
|
||||
if (text->ts.edge_left == old_left && text->ts.edge_up == old_up &&
|
||||
text->ts.edge_right == old_right && text->ts.edge_down == old_down)
|
||||
return true;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -250,7 +314,12 @@ bool PE_SetTextCharSpace(int parts_no, int char_space, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
parts_get_text(parts_get(parts_no), state)->ts.font_spacing = char_space;
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
if (text->ts.font_spacing == char_space)
|
||||
return true;
|
||||
text->ts.font_spacing = char_space;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -259,7 +328,12 @@ bool PE_SetTextLineSpace(int parts_no, int line_space, int state)
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
|
||||
parts_get_text(parts_get(parts_no), state)->line_space = line_space;
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_text *text = parts_get_text(parts, state);
|
||||
if (text->line_space == line_space)
|
||||
return true;
|
||||
text->line_space = line_space;
|
||||
parts_text_rerender(parts, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+16
-15
@@ -194,16 +194,15 @@ static int get_gsave_version(void)
|
||||
int save_globals(const char *keyname, const char *filename, const char *group_name, int *n_out)
|
||||
{
|
||||
int group = -1;
|
||||
int nr_vars;
|
||||
int nr_vars = 0;
|
||||
if (group_name) {
|
||||
if ((group = get_group_index(group_name)) < 0) {
|
||||
WARNING("Unregistered global group: %s", display_sjis0(group_name));
|
||||
return 0;
|
||||
}
|
||||
nr_vars = 0;
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
if (ain->globals[i].group_index == group)
|
||||
nr_vars++;
|
||||
} else {
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
if (ain->globals[i].group_index == group)
|
||||
nr_vars++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nr_vars = ain->nr_globals;
|
||||
@@ -212,14 +211,16 @@ int save_globals(const char *keyname, const char *filename, const char *group_na
|
||||
struct gsave *save = gsave_create(get_gsave_version(), keyname, ain->nr_globals, group_name);
|
||||
gsave_add_globals_record(save, nr_vars);
|
||||
|
||||
struct gsave_global *global = save->globals;
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
if (group >= 0 && ain->globals[i].group_index != group)
|
||||
continue;
|
||||
global->name = strdup(ain->globals[i].name);
|
||||
global->type = ain->globals[i].type.data;
|
||||
global->value = add_value_to_gsave(global->type, global_get(i), save);
|
||||
global++;
|
||||
if (nr_vars > 0) {
|
||||
struct gsave_global *global = save->globals;
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
if (group >= 0 && ain->globals[i].group_index != group)
|
||||
continue;
|
||||
global->name = strdup(ain->globals[i].name);
|
||||
global->type = ain->globals[i].type.data;
|
||||
global->value = add_value_to_gsave(global->type, global_get(i), save);
|
||||
global++;
|
||||
}
|
||||
}
|
||||
|
||||
char *path = savedir_path(filename);
|
||||
|
||||
+4
-1
@@ -141,13 +141,16 @@ void sprite_init_chipmunk(void)
|
||||
return;
|
||||
}
|
||||
}
|
||||
gfx_load_shader(&sprite_shader.s, "shaders/render.v.glsl", "shaders/parts.f.glsl");
|
||||
gfx_load_shader(&sprite_shader.s, "shaders/parts.v.glsl", "shaders/parts.f.glsl");
|
||||
sprite_shader.blend_rate = glGetUniformLocation(sprite_shader.s.program, "blend_rate");
|
||||
sprite_shader.multiply_color = glGetUniformLocation(sprite_shader.s.program, "multiply_color");
|
||||
sprite_shader.add_color = glGetUniformLocation(sprite_shader.s.program, "add_color");
|
||||
sprite_shader.bot_left = glGetUniformLocation(sprite_shader.s.program, "bot_left");
|
||||
sprite_shader.top_right = glGetUniformLocation(sprite_shader.s.program, "top_right");
|
||||
sprite_shader.s.prepare = prepare_chipmunk_shader;
|
||||
glUseProgram(sprite_shader.s.program);
|
||||
glUniform1i(glGetUniformLocation(sprite_shader.s.program, "use_clipper"), 0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
static void sprite_render(struct sprite *_sp)
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include "system4.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/zlib.h"
|
||||
#include "swf.h"
|
||||
|
||||
static void free_tag_list(struct swf_tag *tags);
|
||||
@@ -277,10 +277,9 @@ static struct swf_tag_define_bits_lossless *parse_define_bits_lossless(struct bu
|
||||
if (tag->format != 5)
|
||||
ERROR("DefineBitsLossless: unsupported format %d", tag->format);
|
||||
|
||||
unsigned long len = 4 * tag->width * tag->height;
|
||||
size_t len = 4 * tag->width * tag->height;
|
||||
uint8_t *pixels = xmalloc(len);
|
||||
int rv = uncompress(pixels, &len, r->buf + r->index, buffer_remaining(r));
|
||||
if (rv != Z_OK || len != 4 * tag->width * tag->height)
|
||||
if (!zlib_decompress_exact(pixels, len, r->buf + r->index, buffer_remaining(r)))
|
||||
ERROR("DefineBitsLossless: broken bitmap data");
|
||||
|
||||
uint8_t alpha_const = type == TAG_DEFINE_BITS_LOSSLESS ? 0xff : 0;
|
||||
@@ -666,10 +665,10 @@ struct swf *aff_load(uint8_t *data, size_t size)
|
||||
uint8_t *raw = xmalloc(raw_size);
|
||||
memcpy(raw, buf, 8);
|
||||
raw[0] = 'F';
|
||||
unsigned long dest_size = raw_size - 8;
|
||||
int rv = uncompress(raw + 8, &dest_size, buf + 8, size - 8);
|
||||
size_t dest_size = raw_size - 8;
|
||||
bool ok = zlib_decompress_exact(raw + 8, dest_size, buf + 8, size - 8);
|
||||
free(buf);
|
||||
if (rv != Z_OK || dest_size != raw_size - 8) {
|
||||
if (!ok) {
|
||||
free(raw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+19
-8
@@ -158,7 +158,16 @@ static bool read_config(const char *path)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void read_user_config_file(const char *path)
|
||||
static char *user_config_path(struct ini_entry *entry, const char *basedir)
|
||||
{
|
||||
const char *path = ini_string(entry)->text;
|
||||
if (basedir && !is_absolute_path(path)) {
|
||||
return path_join(basedir, path);
|
||||
}
|
||||
return xstrdup(path);
|
||||
}
|
||||
|
||||
static void read_user_config_file(const char *path, const char *basedir)
|
||||
{
|
||||
int ini_size;
|
||||
struct ini_entry *ini = ini_parse(path, &ini_size);
|
||||
@@ -167,11 +176,14 @@ static void read_user_config_file(const char *path)
|
||||
|
||||
for (int i = 0; i < ini_size; i++) {
|
||||
if (!strcmp(ini[i].name->text, "font-mincho")) {
|
||||
config.font_paths[FONT_MINCHO] = xstrdup(ini_string(&ini[i])->text);
|
||||
free(config.font_paths[FONT_MINCHO]);
|
||||
config.font_paths[FONT_MINCHO] = user_config_path(&ini[i], basedir);
|
||||
} else if (!strcmp(ini[i].name->text, "font-gothic")) {
|
||||
config.font_paths[FONT_GOTHIC] = xstrdup(ini_string(&ini[i])->text);
|
||||
free(config.font_paths[FONT_GOTHIC]);
|
||||
config.font_paths[FONT_GOTHIC] = user_config_path(&ini[i], basedir);
|
||||
} else if (!strcmp(ini[i].name->text, "font-fnl")) {
|
||||
config.fnl_path = xstrdup(ini_string(&ini[i])->text);
|
||||
free(config.fnl_path);
|
||||
config.fnl_path = user_config_path(&ini[i], basedir);
|
||||
} else if (!strcmp(ini[i].name->text, "font-x-scale")) {
|
||||
float f = strtof(ini_string(&ini[i])->text, NULL);
|
||||
if (fabsf(f) < 0.01) {
|
||||
@@ -190,7 +202,7 @@ static void read_user_config_file(const char *path)
|
||||
}
|
||||
} else if (!strcmp(ini[i].name->text, "save-folder")) {
|
||||
free(config.save_dir);
|
||||
config.save_dir = xstrdup(ini_string(&ini[i])->text);
|
||||
config.save_dir = user_config_path(&ini[i], basedir);
|
||||
} else if (!strcmp(ini[i].name->text, "save-format")) {
|
||||
if (!strcmp(ini_string(&ini[i])->text, "json")) {
|
||||
config.save_format = SAVE_FORMAT_JSON;
|
||||
@@ -213,12 +225,12 @@ static void read_user_config(void)
|
||||
// global config
|
||||
path = xmalloc(PATH_MAX);
|
||||
snprintf(path, PATH_MAX-1, "%s/.xsys4rc", config.home_dir);
|
||||
read_user_config_file(path);
|
||||
read_user_config_file(path, NULL);
|
||||
free(path);
|
||||
|
||||
// game-specific config
|
||||
path = gamedir_path(".xsys4rc");
|
||||
read_user_config_file(path);
|
||||
read_user_config_file(path, config.game_dir);
|
||||
free(path);
|
||||
}
|
||||
|
||||
@@ -419,7 +431,6 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
sys_error_handler = error_handler;
|
||||
|
||||
initialize_instructions();
|
||||
char *ainfile;
|
||||
int err = AIN_SUCCESS;
|
||||
bool audit = false;
|
||||
|
||||
+8
-6
@@ -158,23 +158,25 @@ static uint32_t char_to_code(const char *ch, enum charmap charmap)
|
||||
return c;
|
||||
}
|
||||
|
||||
float gfx_size_char(struct text_style *ts, const char *ch)
|
||||
float gfx_size_char_unstyled(struct text_style *ts, const char *ch)
|
||||
{
|
||||
struct font_size *size = text_style_font_size(ts);
|
||||
return font_size_char(size, char_to_code(ch, size->font->charmap));
|
||||
}
|
||||
|
||||
float gfx_size_char_kerning(struct text_style *ts, uint32_t code, uint32_t code_next)
|
||||
float gfx_size_char(struct text_style *ts, const char *ch)
|
||||
{
|
||||
struct font_size *size = text_style_font_size(ts);
|
||||
return size->font->size_char_kerning(size, code, code_next);
|
||||
float w = gfx_size_char_unstyled(ts, ch);
|
||||
if (gfx_text_advance_edges)
|
||||
w += ts->edge_left + ts->edge_right + ceilf(ts->bold_width)*2;
|
||||
return w * ts->scale_x;
|
||||
}
|
||||
|
||||
float gfx_size_text(struct text_style *ts, const char *text)
|
||||
{
|
||||
struct font_size *size = text_style_font_size(ts);
|
||||
float edge_advance = gfx_text_advance_edges
|
||||
? ts->edge_left + ts->edge_right + ceilf(ts->bold_width)
|
||||
? ts->edge_left + ts->edge_right + ceilf(ts->bold_width)*2
|
||||
: 0.0f;
|
||||
float x = 0.0f;
|
||||
while (*text) {
|
||||
@@ -182,7 +184,7 @@ float gfx_size_text(struct text_style *ts, const char *text)
|
||||
x += edge_advance;
|
||||
text = sjis_skip_char(text);
|
||||
}
|
||||
return x;
|
||||
return x * ts->scale_x;;
|
||||
}
|
||||
|
||||
float gfx_get_actual_font_size(unsigned face, float size)
|
||||
|
||||
+21
-8
@@ -243,19 +243,12 @@ int gfx_init(void)
|
||||
#endif
|
||||
|
||||
sdl.format = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA32);
|
||||
uint32_t window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
||||
#ifdef __ANDROID__
|
||||
window_flags |= SDL_WINDOW_FULLSCREEN;
|
||||
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
|
||||
#else
|
||||
window_flags |= SDL_WINDOW_RESIZABLE;
|
||||
#endif
|
||||
sdl.window = SDL_CreateWindow("XSystem4",
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
config.view_width,
|
||||
config.view_height,
|
||||
window_flags);
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||
if (!sdl.window)
|
||||
ERROR("SDL_CreateWindow failed: %s", SDL_GetError());
|
||||
|
||||
@@ -326,6 +319,26 @@ void gfx_set_window_logical_size(int w, int h)
|
||||
main_surface_init(w, h);
|
||||
}
|
||||
|
||||
bool gfx_set_fullscreen(bool enable)
|
||||
{
|
||||
uint32_t flags = enable ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
|
||||
if (SDL_SetWindowFullscreen(sdl.window, flags) < 0) {
|
||||
WARNING("Failed to set fullscreen mode: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gfx_is_fullscreen(void)
|
||||
{
|
||||
return SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
}
|
||||
|
||||
void gfx_toggle_fullscreen(void)
|
||||
{
|
||||
gfx_set_fullscreen(!gfx_is_fullscreen());
|
||||
}
|
||||
|
||||
void gfx_update_screen_scale(void)
|
||||
{
|
||||
int display_w, display_h;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user