mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3cb6b4001 | ||
|
|
2198bf5970 | ||
|
|
0a152f0e34 | ||
|
|
2a3b636bb0 | ||
|
|
1506d085db | ||
|
|
729957b842 | ||
|
|
6f661fb370 | ||
|
|
57a73cd924 | ||
|
|
6424efa6ab | ||
|
|
dc4a6f1dfa | ||
|
|
b53c511b4a | ||
|
|
0c75f21985 | ||
|
|
b4c3818d6b | ||
|
|
fe61704b90 | ||
|
|
199c91dd6c | ||
|
|
828971ce19 | ||
|
|
e1b1208438 | ||
|
|
b42e59c494 | ||
|
|
0d689ac1c2 | ||
|
|
aba2a53afe | ||
|
|
30d599f161 | ||
|
|
7817592728 | ||
|
|
85244a304a | ||
|
|
f2d26c2a46 | ||
|
|
d48f637744 | ||
|
|
008d43be1b | ||
|
|
05b78e6dc6 | ||
|
|
b28624203a | ||
|
|
e943ef8ea9 | ||
|
|
d547919713 | ||
|
|
ddf6ac5088 | ||
|
|
037616f38d | ||
|
|
af70ee09de | ||
|
|
8d67c87f6f | ||
|
|
94c8329d7a | ||
|
|
2c771acfe6 | ||
|
|
eef99c0910 | ||
|
|
44c73f644f | ||
|
|
6cada80d34 | ||
|
|
96a560f6a2 | ||
|
|
e13583a75e | ||
|
|
f0e25e53d7 | ||
|
|
eb0c642ab1 | ||
|
|
acb4d92705 | ||
|
|
b6c523f4f3 | ||
|
|
6b9edcdd69 | ||
|
|
538480b348 | ||
|
|
d9be9a7ad3 | ||
|
|
554f742a00 | ||
|
|
eda8ad35f3 | ||
|
|
59a66a8a5a | ||
|
|
02eab7149c | ||
|
|
1042b328a1 | ||
|
|
f3484643a6 | ||
|
|
46469e8732 | ||
|
|
62ec0be41a | ||
|
|
0972c60558 | ||
|
|
ba2805b09f | ||
|
|
20711ffa57 | ||
|
|
7adbf9d6e6 | ||
|
|
2dcc9b2aa9 | ||
|
|
441419d0f9 | ||
|
|
a32e3b9401 | ||
|
|
d579374a9c | ||
|
|
f0a54b337d | ||
|
|
80f4246095 | ||
|
|
b71b61c1d6 | ||
|
|
8c791db5d4 | ||
|
|
d404324e7b | ||
|
|
aeb5dbb08c | ||
|
|
9db75ab870 | ||
|
|
67387e88d0 | ||
|
|
d57c8cc9f4 | ||
|
|
677f20628c | ||
|
|
e68c41a0b9 | ||
|
|
fbd105b697 | ||
|
|
3c7634522a | ||
|
|
5ed05ba685 | ||
|
|
11fe6f6dda | ||
|
|
3057c2a96b | ||
|
|
5041d817d1 | ||
|
|
cb4e44a551 | ||
|
|
74ae7223c3 | ||
|
|
7dc6760928 | ||
|
|
e1ca00a27b | ||
|
|
7989ffc542 | ||
|
|
6e14285038 | ||
|
|
55a214553a | ||
|
|
bf9900d8aa | ||
|
|
e00ba69546 | ||
|
|
b88abe375a | ||
|
|
c86977ad1b | ||
|
|
ffb9fbae31 | ||
|
|
3ef7ce926b | ||
|
|
bf6dcef2a8 | ||
|
|
8a62ec6dc5 | ||
|
|
152af8685f | ||
|
|
c5d6afdd08 | ||
|
|
9d6861b10b | ||
|
|
c7ba379173 | ||
|
|
f4d4e8f90a | ||
|
|
379bbfeb07 | ||
|
|
117c72cf09 | ||
|
|
3577079a04 | ||
|
|
8d120b8a4d | ||
|
|
e2440a5a44 | ||
|
|
bba3f96a1b | ||
|
|
f4f7adb618 | ||
|
|
8730475f1c | ||
|
|
a84d78ac5d | ||
|
|
92322c27f0 | ||
|
|
50cb0f8063 | ||
|
|
13e76f67b3 | ||
|
|
37f1bc2723 | ||
|
|
0d7830f24c | ||
|
|
c70e5e028e | ||
|
|
a4b4e7f5bb | ||
|
|
9ccc4ab0c7 | ||
|
|
13a00c131d | ||
|
|
929a2dd3e7 | ||
|
|
b5b92bc367 | ||
|
|
e5fadf2b73 | ||
|
|
2713290ac9 | ||
|
|
3bfb92e1fb | ||
|
|
378450c625 | ||
|
|
919b86916c | ||
|
|
6fbd147c54 | ||
|
|
bcae91003a | ||
|
|
7e584c5fd2 | ||
|
|
b0c7aad6c3 | ||
|
|
5d0819a115 | ||
|
|
724bbcdfdd | ||
|
|
dc0daf3329 | ||
|
|
5d271deec0 | ||
|
|
2ede7e6bb1 | ||
|
|
e6d069b757 | ||
|
|
d6abcc2f92 | ||
|
|
de0646e430 | ||
|
|
c04fd2280d | ||
|
|
71c58e85ae | ||
|
|
61650308b7 | ||
|
|
bb26326929 | ||
|
|
f50c9f6d77 | ||
|
|
ea081e0eb7 | ||
|
|
e8ecad598a | ||
|
|
cb551e8f06 | ||
|
|
002bca4e24 | ||
|
|
0bbd9171b5 | ||
|
|
7dfc9926be | ||
|
|
1dd10ecc45 | ||
|
|
ba11900350 | ||
|
|
508839ed4c | ||
|
|
55f826db36 | ||
|
|
882354c692 | ||
|
|
071d7d2137 | ||
|
|
eddac54fa0 | ||
|
|
2e1059cbac | ||
|
|
f9177b89b1 | ||
|
|
7bae2cdf8a | ||
|
|
769bdca725 | ||
|
|
6517303e2a | ||
|
|
0e388c74c0 | ||
|
|
7ff5ec89e7 | ||
|
|
33403cbd3d | ||
|
|
40fe2068e7 | ||
|
|
40914a7cef | ||
|
|
0946900e02 | ||
|
|
56d9b607b2 | ||
|
|
155c801804 | ||
|
|
4942e41fb8 | ||
|
|
ac8a37f67b | ||
|
|
331563a165 | ||
|
|
c30510e688 | ||
|
|
81c0c494b0 | ||
|
|
e9bec3c2fe | ||
|
|
298e9bddf5 | ||
|
|
bf3c4feb9d | ||
|
|
6a36fc8f53 | ||
|
|
45876a9921 | ||
|
|
2461c1e52d | ||
|
|
41c7830558 | ||
|
|
009bafae40 | ||
|
|
e891e74bcf | ||
|
|
5576f14a0f | ||
|
|
90075f2e25 | ||
|
|
c0cc3f729b | ||
|
|
4d303b291d | ||
|
|
e73abc3cd6 | ||
|
|
579c158477 | ||
|
|
842bac92f3 | ||
|
|
dc8a9379d0 | ||
|
|
f8f02a1c99 | ||
|
|
d7d9bb70f2 | ||
|
|
dcdb82e2a8 | ||
|
|
f1a2904262 | ||
|
|
ecd71896b3 | ||
|
|
d7b7879747 | ||
|
|
1cbf6125ce | ||
|
|
9b03741288 | ||
|
|
4e04db0045 | ||
|
|
abd3a276a3 | ||
|
|
9ca3d3e9c6 | ||
|
|
d8ad22b638 | ||
|
|
3f02a22a8d | ||
|
|
bff491bbe9 | ||
|
|
f566539e4f | ||
|
|
c04aa8a844 | ||
|
|
87b1e2abe2 | ||
|
|
a0a68a022c | ||
|
|
3fb2dc3650 | ||
|
|
c1ce2c88c6 | ||
|
|
222d2700a9 | ||
|
|
dd18d68be5 | ||
|
|
db7c3a608c | ||
|
|
899c6c02a1 | ||
|
|
8ff5ffe30b | ||
|
|
ff9175c1fd | ||
|
|
74666c9b90 | ||
|
|
68bd33e099 | ||
|
|
84ab9a41da | ||
|
|
22a06b1da9 | ||
|
|
128b2fd6d5 | ||
|
|
09dc9470ae | ||
|
|
0f5a39c292 | ||
|
|
73f7acde40 | ||
|
|
3afe4e4453 | ||
|
|
8f69f1911a | ||
|
|
e466b3f06b | ||
|
|
a2ae28c1c1 | ||
|
|
41626b5018 | ||
|
|
9a76d472db | ||
|
|
964bba1823 | ||
|
|
49b85f885d | ||
|
|
93e9357f84 | ||
|
|
2a7f58c187 | ||
|
|
08e47b8d04 | ||
|
|
217c4d5d65 | ||
|
|
ea762de937 | ||
|
|
e6105ca654 | ||
|
|
7a87ffc20a | ||
|
|
f53ad7d557 | ||
|
|
ffe4d0d4ae | ||
|
|
9e2f03d8be | ||
|
|
489cfe5a96 | ||
|
|
71debfbb65 | ||
|
|
34258511b9 | ||
|
|
0f121860fe | ||
|
|
70a6f547cf | ||
|
|
886f15aa9f | ||
|
|
d895301116 | ||
|
|
fdaa2d1dba | ||
|
|
da52b312ca | ||
|
|
8cc2334692 | ||
|
|
486d89669f | ||
|
|
c3614c9280 | ||
|
|
00aa0c2ae6 | ||
|
|
240c2c9395 | ||
|
|
88236b5a0b | ||
|
|
041cbafef1 | ||
|
|
c5b0916279 | ||
|
|
3c61f7cebf | ||
|
|
a13bd155dc | ||
|
|
09bae3d423 | ||
|
|
1f0fc09294 | ||
|
|
acc869086f | ||
|
|
0cdf793b42 | ||
|
|
d1ba60b673 | ||
|
|
ade7ea9235 | ||
|
|
7365e8a7ca | ||
|
|
c4207e4226 | ||
|
|
ddaef07fb5 | ||
|
|
f8174231cb | ||
|
|
0c8e52f18c | ||
|
|
559ccae1ad | ||
|
|
0597bfa513 | ||
|
|
921c0b699c | ||
|
|
3f36bf29b1 | ||
|
|
c6e1f8f155 | ||
|
|
65c178c04c | ||
|
|
69ab413676 | ||
|
|
71a4f5079e | ||
|
|
caeba1536a | ||
|
|
fd16bab763 | ||
|
|
2046ad4d06 | ||
|
|
12ddb585fb | ||
|
|
0a78489993 | ||
|
|
21abe6ff9a | ||
|
|
ab810e8f57 | ||
|
|
ac298baaec | ||
|
|
cf3fe0e852 | ||
|
|
79ebe443f5 | ||
|
|
f886f0a64c | ||
|
|
6b78f53bd7 | ||
|
|
b21e28ce82 | ||
|
|
35bd6292bb | ||
|
|
c4e17c672d | ||
|
|
d06d0ff8b5 | ||
|
|
07290cc8df | ||
|
|
bc65a40eb4 | ||
|
|
bc04af131a | ||
|
|
d48cd2545d | ||
|
|
133ff5421f | ||
|
|
69deb9ee91 | ||
|
|
f2700398dd | ||
|
|
8f7d9886c2 | ||
|
|
a2c225bf64 | ||
|
|
dd77de027b | ||
|
|
bcf81af71d | ||
|
|
c3c241adf5 | ||
|
|
cb4a04ce14 | ||
|
|
877ca1f74e | ||
|
|
d3bd28a0c1 | ||
|
|
4e4f0b8683 | ||
|
|
dfad543f6e | ||
|
|
9a1f9e763a | ||
|
|
1a9021e1ed | ||
|
|
5cca12b0ce | ||
|
|
0aed62f448 | ||
|
|
37a5f977c3 | ||
|
|
d7c550ea62 | ||
|
|
da86fd4875 | ||
|
|
aba32d7fab | ||
|
|
103976b2b0 | ||
|
|
0330006b89 | ||
|
|
12f3b5d241 | ||
|
|
673827cf70 | ||
|
|
5212d5b7f9 | ||
|
|
bfa2deeffb | ||
|
|
635cce877f | ||
|
|
0bab7544cd | ||
|
|
48b6e24135 | ||
|
|
423ceb5abc | ||
|
|
9b6a032d24 | ||
|
|
525006d716 | ||
|
|
083fd23503 | ||
|
|
3f18b181a3 | ||
|
|
50ec61ba37 | ||
|
|
f1ad3ddf25 | ||
|
|
936167ee9c | ||
|
|
52a624009d | ||
|
|
71ead2bf85 | ||
|
|
d56f075ee3 | ||
|
|
04d5237020 | ||
|
|
6379dbad4b | ||
|
|
b155b4b79f | ||
|
|
d7fd6d4e1e | ||
|
|
64fd2a6858 | ||
|
|
1ec06a4b80 | ||
|
|
2840b21946 | ||
|
|
69b1826d63 | ||
|
|
69cd1cb630 | ||
|
|
012f8f8af4 | ||
|
|
b85b426872 | ||
|
|
10169951c0 | ||
|
|
8eefbcec95 | ||
|
|
f73dd38dab | ||
|
|
9e821a754c | ||
|
|
f2caed46a8 | ||
|
|
7ef9719cc7 | ||
|
|
f47e5e5a82 | ||
|
|
f0fc0b6699 | ||
|
|
8cfc20999f | ||
|
|
bb72ad60b2 | ||
|
|
2b3e100cba | ||
|
|
5f432ce672 | ||
|
|
c3e4cd5e9f | ||
|
|
0f4529e1a6 | ||
|
|
58366e199e | ||
|
|
d512275f99 | ||
|
|
7e54e159df | ||
|
|
a92e26c49c | ||
|
|
e65a506dac | ||
|
|
6e78f05d75 | ||
|
|
f0f84a05ce | ||
|
|
18a71960e8 | ||
|
|
5e80c96587 | ||
|
|
9ec354c198 | ||
|
|
b7304f747b | ||
|
|
b7ab1ed476 | ||
|
|
bc522b27e0 | ||
|
|
3e9f975213 | ||
|
|
ba9e42fad6 | ||
|
|
05226453d9 | ||
|
|
4e2c050ffc | ||
|
|
39701c7657 | ||
|
|
6bdd22f875 | ||
|
|
20411f3518 | ||
|
|
27e733758d | ||
|
|
288381d8f6 | ||
|
|
c3f39aeb96 | ||
|
|
05d2ad8600 | ||
|
|
0911103949 | ||
|
|
9c8c290d83 | ||
|
|
bf3700ea8e | ||
|
|
c521f1faf7 | ||
|
|
ef7b789810 | ||
|
|
530f606657 | ||
|
|
120b9888f1 | ||
|
|
87884d4230 | ||
|
|
0b4eb92e95 | ||
|
|
51e55644f5 | ||
|
|
296496e895 | ||
|
|
afae3fc9a6 | ||
|
|
a301ca1311 | ||
|
|
eb97693e3f | ||
|
|
a6f01f7f82 | ||
|
|
a27996def8 | ||
|
|
fa96987eca | ||
|
|
192e65a827 | ||
|
|
a536912b38 | ||
|
|
fcf26a9ca7 | ||
|
|
4cb374dd5d | ||
|
|
9543bf1761 | ||
|
|
1527a1cb85 | ||
|
|
588d4f1024 | ||
|
|
c815d48274 | ||
|
|
1730ddebae | ||
|
|
1694f5cb89 | ||
|
|
1855cd4167 | ||
|
|
a4ff641366 | ||
|
|
2085c28bd1 | ||
|
|
6c2a55c67a | ||
|
|
2ffb4a4ab0 | ||
|
|
0e5d41a728 | ||
|
|
e1d5eba1f2 | ||
|
|
c99e9842ad | ||
|
|
20fabf40fa | ||
|
|
d8bba6e391 | ||
|
|
01429fe2a8 | ||
|
|
3496f45e39 | ||
|
|
88c37a9165 | ||
|
|
6d3abdaa02 | ||
|
|
eaa08b6991 | ||
|
|
1887ceff7f | ||
|
|
ff26a7f646 | ||
|
|
7dd85e445e | ||
|
|
4f22907004 | ||
|
|
ddc9f1ae12 | ||
|
|
7392d74efe | ||
|
|
535a1f1804 | ||
|
|
d387ee2816 | ||
|
|
dc174fa91c | ||
|
|
cf318bb47c | ||
|
|
d0a2c5ec40 | ||
|
|
cff6272c18 | ||
|
|
e808d770a9 | ||
|
|
63067d32d3 | ||
|
|
f9e9f20c05 | ||
|
|
3ee2f703a4 | ||
|
|
7b512485e6 | ||
|
|
d4e06db8e2 | ||
|
|
4ad893c147 | ||
|
|
381e644a42 | ||
|
|
ba81077c2e | ||
|
|
f7cf2264fa | ||
|
|
9bb4665fa9 | ||
|
|
4c9c23f620 | ||
|
|
75d6386e25 | ||
|
|
983944a776 | ||
|
|
373903cdb5 | ||
|
|
4b1257fc13 | ||
|
|
0416d4f1f7 | ||
|
|
911f82cda0 | ||
|
|
e4ed459376 | ||
|
|
46f25755c5 | ||
|
|
249e447920 | ||
|
|
cc199dcf4e | ||
|
|
baae8f5237 | ||
|
|
8437a87b54 | ||
|
|
b11a173b67 | ||
|
|
0e18f69cbf | ||
|
|
c3f9d93917 | ||
|
|
ac411754a0 | ||
|
|
45ebaa2c7f | ||
|
|
a923b0ce23 | ||
|
|
1be2cffd22 | ||
|
|
50af8bd3ff | ||
|
|
32ef29ee20 | ||
|
|
8620be452e | ||
|
|
cc79f9caee | ||
|
|
6a2eae7274 | ||
|
|
66002f7ceb | ||
|
|
d1b18f39dd | ||
|
|
dbd1e54c46 | ||
|
|
3c0ee51714 | ||
|
|
d4f7ab06cf | ||
|
|
66d8def3f8 | ||
|
|
8f29146c28 | ||
|
|
e53dc6ad0e | ||
|
|
90a96eb8bb | ||
|
|
3ff2c33d3c | ||
|
|
2237bb814c | ||
|
|
6b62e1db23 |
+222
@@ -0,0 +1,222 @@
|
||||
# CMakeLists.txt for Android build
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(xsystem4 LANGUAGES C)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
cglm
|
||||
URL https://github.com/recp/cglm/archive/refs/tags/v0.9.2.tar.gz
|
||||
URL_HASH SHA1=cb8472aa8c2ab67b66378dbaf10c2c7368d4e4c3
|
||||
)
|
||||
FetchContent_MakeAvailable(cglm)
|
||||
|
||||
add_library(ffi STATIC IMPORTED)
|
||||
set_target_properties(ffi PROPERTIES
|
||||
IMPORTED_LOCATION ${CMAKE_STAGING_PREFIX}/lib/libffi.a
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_STAGING_PREFIX}/include)
|
||||
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(SndFile REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
add_subdirectory(subprojects/libsys4)
|
||||
|
||||
add_library(xsystem4 SHARED)
|
||||
|
||||
target_compile_definitions(xsystem4 PRIVATE
|
||||
_DEFAULT_SOURCE
|
||||
USE_GLES
|
||||
)
|
||||
|
||||
target_include_directories(xsystem4 PRIVATE include)
|
||||
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE VCS_TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
configure_file(src/version.h.in version.h)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_sources(xsystem4 PRIVATE
|
||||
src/audio.c
|
||||
src/audio_meta.c
|
||||
src/audio_mixer.c
|
||||
src/asset_manager.c
|
||||
src/base64.c
|
||||
src/cJSON.c
|
||||
src/draw.c
|
||||
src/effect.c
|
||||
src/ffi.c
|
||||
src/font_freetype.c
|
||||
src/font_fnl.c
|
||||
src/format.c
|
||||
src/hacks.c
|
||||
src/heap.c
|
||||
src/icon.c
|
||||
src/id_pool.c
|
||||
src/input.c
|
||||
src/json.c
|
||||
src/movie_plmpeg.c
|
||||
src/msgqueue.c
|
||||
src/page.c
|
||||
src/resume.c
|
||||
src/savedata.c
|
||||
src/scene.c
|
||||
src/screenshot.c
|
||||
src/sprite.c
|
||||
src/swf.c
|
||||
src/system4.c
|
||||
src/text.c
|
||||
src/util.c
|
||||
src/video.c
|
||||
src/vm.c
|
||||
|
||||
src/3d/collision.c
|
||||
src/3d/debug.c
|
||||
src/3d/model.c
|
||||
src/3d/parser.c
|
||||
src/3d/particle.c
|
||||
src/3d/reign.c
|
||||
src/3d/renderer.c
|
||||
|
||||
src/dungeon/dgn.c
|
||||
src/dungeon/dtx.c
|
||||
src/dungeon/dungeon.c
|
||||
src/dungeon/generator.c
|
||||
src/dungeon/map.c
|
||||
src/dungeon/mtrand43.c
|
||||
src/dungeon/polyobj.c
|
||||
src/dungeon/renderer.c
|
||||
src/dungeon/skybox.c
|
||||
src/dungeon/tes.c
|
||||
|
||||
src/parts/construction.c
|
||||
src/parts/debug.c
|
||||
src/parts/flash.c
|
||||
src/parts/input.c
|
||||
src/parts/motion.c
|
||||
src/parts/parts.c
|
||||
src/parts/render.c
|
||||
src/parts/text.c
|
||||
src/parts/save.c
|
||||
|
||||
src/hll/ACXLoader.c
|
||||
src/hll/ADVSYS.c
|
||||
src/hll/AliceLogo.c
|
||||
src/hll/AliceLogo2.c
|
||||
src/hll/AliceLogo3.c
|
||||
src/hll/AliceLogo4.c
|
||||
src/hll/AliceLogo5.c
|
||||
src/hll/AnteaterADVEngine.c
|
||||
src/hll/Array.c
|
||||
src/hll/BanMisc.c
|
||||
src/hll/Bitarray.c
|
||||
src/hll/CalcTable.c
|
||||
src/hll/CGManager.c
|
||||
src/hll/CharSpriteManager.c
|
||||
src/hll/ChipmunkSpriteEngine.c
|
||||
src/hll/ChrLoader.c
|
||||
src/hll/CommonSystemData.c
|
||||
src/hll/Confirm.c
|
||||
src/hll/Confirm2.c
|
||||
src/hll/Confirm3.c
|
||||
src/hll/CrayfishLogViewer.c
|
||||
src/hll/Cursor.c
|
||||
src/hll/DALKDemo.c
|
||||
src/hll/DALKEDemo.c
|
||||
src/hll/Data.c
|
||||
src/hll/DataFile.c
|
||||
src/hll/DrawDungeon.c
|
||||
src/hll/DrawEffect.c
|
||||
src/hll/DrawGraph.c
|
||||
src/hll/DrawMovie.c
|
||||
src/hll/DrawNumeral.c
|
||||
src/hll/DrawPluginManager.c
|
||||
src/hll/DrawRain.c
|
||||
src/hll/DrawRipple.c
|
||||
src/hll/DrawSimpleText.c
|
||||
src/hll/DrawSnow.c
|
||||
src/hll/File.c
|
||||
src/hll/File2.c
|
||||
src/hll/FileOperation.c
|
||||
src/hll/FillAngle.c
|
||||
src/hll/GoatGUIEngine.c
|
||||
src/hll/Gpx2Plus.c
|
||||
src/hll/GUIEngine.c
|
||||
src/hll/HTTPDownloader.c
|
||||
src/hll/iarray.c
|
||||
src/hll/IbisInputEngine.c
|
||||
src/hll/InputDevice.c
|
||||
src/hll/InputString.c
|
||||
src/hll/KiwiSoundEngine.c
|
||||
src/hll/LoadCG.c
|
||||
src/hll/MainEXFile.c
|
||||
src/hll/MainSurface.c
|
||||
src/hll/MamanyoDemo.c
|
||||
src/hll/MamanyoSDemo.c
|
||||
src/hll/MarmotModelEngine.c
|
||||
src/hll/Math.c
|
||||
src/hll/MapLoader.c
|
||||
src/hll/MenuMsg.c
|
||||
src/hll/MonsterInfo.c
|
||||
src/hll/MsgLogManager.c
|
||||
src/hll/MsgLogViewer.c
|
||||
src/hll/MsgSkip.c
|
||||
src/hll/MusicSystem.c
|
||||
src/hll/NewFont.c
|
||||
src/hll/OutputLog.c
|
||||
src/hll/PassRegister.c
|
||||
src/hll/PastelChime2.c
|
||||
src/hll/PartsEngine.c
|
||||
src/hll/PixelRestore.c
|
||||
src/hll/PlayDemo.c
|
||||
src/hll/PlayMovie.c
|
||||
src/hll/ReignEngine.c
|
||||
src/hll/SACT2.c
|
||||
src/hll/SengokuRanceFont.c
|
||||
src/hll/SoundFilePlayer.c
|
||||
src/hll/Sound2ex.c
|
||||
src/hll/StoatSpriteEngine.c
|
||||
src/hll/StretchHelper.c
|
||||
src/hll/SystemService.c
|
||||
src/hll/SystemServiceEx.c
|
||||
src/hll/Timer.c
|
||||
src/hll/Toushin3Loader.c
|
||||
src/hll/vmAnime.c
|
||||
src/hll/vmArray.c
|
||||
src/hll/vmCG.c
|
||||
src/hll/vmChrLoader.c
|
||||
src/hll/vmCursor.c
|
||||
src/hll/vmDalkGaiden.c
|
||||
src/hll/vmData.c
|
||||
src/hll/vmDialog.c
|
||||
src/hll/vmDrawGauge.c
|
||||
src/hll/vmDrawMsg.c
|
||||
src/hll/vmDrawNumber.c
|
||||
src/hll/vmFile.c
|
||||
src/hll/vmGraph.c
|
||||
src/hll/vmGraphQuake.c
|
||||
src/hll/vmKey.c
|
||||
src/hll/vmMapLoader.c
|
||||
src/hll/vmMsgLog.c
|
||||
src/hll/vmMusic.c
|
||||
src/hll/vmSound.c
|
||||
src/hll/vmSprite.c
|
||||
src/hll/vmString.c
|
||||
src/hll/vmSurface.c
|
||||
src/hll/vmSystem.c
|
||||
src/hll/vmTimer.c
|
||||
src/hll/VSFile.c
|
||||
)
|
||||
|
||||
target_link_libraries(xsystem4 PRIVATE
|
||||
m ZLIB::ZLIB SDL2::SDL2 Freetype::Freetype ffi GLESv3 cglm SndFile::sndfile sys4)
|
||||
|
||||
target_compile_options(xsystem4 PRIVATE -Wno-unused-parameter)
|
||||
|
||||
install(TARGETS xsystem4)
|
||||
@@ -47,6 +47,72 @@ Finally install it to your system (optional),
|
||||
|
||||
ninja -C build install
|
||||
|
||||
### Windows
|
||||
|
||||
xsystem4 can be built on Windows using MSYS2.
|
||||
|
||||
First install MSYS2, and then open the MINGW64 shell and run the following command,
|
||||
|
||||
pacman -S make flex bison \
|
||||
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-libsndfile \
|
||||
mingw-w64-x86_64-glew \
|
||||
mingw-w64-x86_64-nasm \
|
||||
mingw-w64-x86_64-diffutils
|
||||
|
||||
To build with FFmpeg support, you must compile FFmpeg as a static library:
|
||||
|
||||
git clone https://github.com/FFmpeg/FFmpeg.git
|
||||
cd FFmpeg
|
||||
git checkout n7.1.2
|
||||
./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
|
||||
make
|
||||
make install
|
||||
cd ..
|
||||
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
Then build the xsystem4 executable with meson,
|
||||
|
||||
mkdir build
|
||||
meson build
|
||||
ninja -C build
|
||||
|
||||
To create a portable executable, it is neccessary to copy some DLLs into the same directory as xsystem4.exe.
|
||||
You can determine the required DLLs with the following command,
|
||||
|
||||
ldd build/src/xsystem4.exe | grep mingw64
|
||||
|
||||
The `fonts` and `shaders` directories must also be shipped together with xsystem4.exe.
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
|
||||
Generated
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1757244434,
|
||||
"narHash": "sha256-AeqTqY0Y95K1Fgs6wuT1LafBNcmKxcOkWnm4alD9pqM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "092c565d333be1e17b4779ac22104338941d913f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-25.05",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
description = "Open source implementation of AliceSoft's System 4 game engine for unix-like operating systems";
|
||||
|
||||
# Nixpkgs / NixOS version to use.
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
|
||||
# System types to support.
|
||||
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
|
||||
|
||||
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
|
||||
# Nixpkgs instantiated for supported system types.
|
||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||
|
||||
in {
|
||||
|
||||
# Set up xsystem4 development environment:
|
||||
# nix develop # create shell environment with dependencies available
|
||||
devShell = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in with pkgs; pkgs.mkShell {
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
ffmpeg
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
# Build xsystem4:
|
||||
# nix build .?submodules=1 # build xsystem4 (outputs to ./result/)
|
||||
# nix shell .?submodules=1 # create shell environment with xsystem4 available
|
||||
# Install xsystem4 to user profile:
|
||||
# nix profile install .?submodules=1
|
||||
packages = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in {
|
||||
default = with pkgs; stdenv.mkDerivation rec {
|
||||
name = "xsystem4";
|
||||
src = ./.;
|
||||
mesonAutoFeatures = "auto";
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
ffmpeg
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
+40
-31
@@ -14,52 +14,52 @@ issues, please let me know or open a pull request to update this table.
|
||||
|
||||
| Game | Status | Notes |
|
||||
| ------------------------------------------------------- | ----------- | ----- |
|
||||
| DALK外伝 | Unsupported | |
|
||||
| ままにょにょ | Unsupported | |
|
||||
| DALK外伝 | Supported | |
|
||||
| ままにょにょ | Supported | |
|
||||
| 大番長 -Big Bang Age- | Supported | |
|
||||
| Daibanchou -Big Bang Age- (EN) | Supported | |
|
||||
| 魔女の贖罪 | Unknown | |
|
||||
| Rance VI -ゼス崩壊- | Supported | |
|
||||
| Rance VI -Collapse of Zeth- (EN) | Supported | |
|
||||
| ウルトラ魔法少女まなな Vol.1~Vol.5 | Unknown | |
|
||||
| 鈴宮刑務娼館 | Unknown | From ALICEの館7 |
|
||||
| しまいま | Unknown | From ALICEの館7 |
|
||||
| DUNGEONS & DOLLS | Unsupported | From ALICEの館7 |
|
||||
| こすぷれれもん | Unknown | From ALICEの館7 |
|
||||
| エルメイフラワー | Unknown | From ALICEの館7 |
|
||||
| CGは17年ゼミ | Unknown | From ALICEの館7 |
|
||||
| でーぴーえすVer_N | Unknown | From ALICEの館7 |
|
||||
| 大番長外伝 | Unknown | From ALICEの館7 |
|
||||
| ZERO | Unknown | From ALICEの館7 |
|
||||
| ぷろGRX | Unknown | From ALICEの館7 |
|
||||
| Girls | Unknown | From ALICEの館7 |
|
||||
| 神武 -カムナガラ- | Unknown | From ALICEの館7 |
|
||||
| オニむス | Unknown | From ALICEの館7 |
|
||||
| 透色 | Unknown | From ALICEの館7 |
|
||||
| DEBUT | Unknown | From ALICEの館7 |
|
||||
| ランス6その後 | Unknown | From ALICEの館7 |
|
||||
| ほいっぷハニー | Unknown | From ALICEの館7 |
|
||||
| Text Jam Session | Unknown | From ALICEの館7 |
|
||||
| 没ロゴの哀しみ | Unknown | From ALICEの館7 |
|
||||
| スタッフコーナー | Unknown | From ALICEの館7 |
|
||||
| ぱすてるチャイムContinue | Unsupported | |
|
||||
| ウルトラ魔法少女まなな Vol.1~Vol.5 | Supported | |
|
||||
| 鈴宮刑務娼館 | Supported | From ALICEの館7 |
|
||||
| しまいま | Supported | From ALICEの館7 |
|
||||
| DUNGEONS & DOLLS | Supported | From ALICEの館7 |
|
||||
| こすぷれれもん | Supported | From ALICEの館7 |
|
||||
| エルメイフラワー | Supported | From ALICEの館7 |
|
||||
| CGは17年ゼミ | Supported | From ALICEの館7 |
|
||||
| でーぴーえすVer_N | Supported | From ALICEの館7 |
|
||||
| 大番長外伝 | Supported | From ALICEの館7 |
|
||||
| ZERO | Unsupported | From ALICEの館7 |
|
||||
| ぷろGRX | Supported | From ALICEの館7 |
|
||||
| Girls | Supported | From ALICEの館7 |
|
||||
| 神武 -カムナガラ- | Supported | From ALICEの館7 |
|
||||
| オニむス | Supported | From ALICEの館7 |
|
||||
| 透色 | Supported | From ALICEの館7 |
|
||||
| DEBUT | Supported | From ALICEの館7 |
|
||||
| ランス6その後 | Supported | From ALICEの館7 |
|
||||
| ほいっぷハニー | Supported | From ALICEの館7 |
|
||||
| Text Jam Session | Supported | From ALICEの館7 |
|
||||
| 没ロゴの哀しみ | Supported | From ALICEの館7 |
|
||||
| スタッフコーナー | Supported | From ALICEの館7 |
|
||||
| ぱすてるチャイムContinue | Supported | |
|
||||
| GALZOOアイランド | Supported | |
|
||||
| ぱすちゃC++ | Unknown | |
|
||||
| ぱすちゃC++ | Supported | |
|
||||
| よくばりサボテン | Supported | |
|
||||
| 妻しぼり | Unknown | |
|
||||
| 戦国ランス | Supported | |
|
||||
| Sengoku Rance (EN FanTL) | Supported | |
|
||||
| Sengoku Rance (MangaGamer) | Supported | |
|
||||
| だぶる先生らいふっ | Unknown | |
|
||||
| お嬢様をいいなりにするゲーム | Unknown | |
|
||||
| お嬢様をいいなりにするゲーム | Supported | |
|
||||
| 超昂閃忍ハルカ | Unknown | |
|
||||
| 戦国ランス~三匹が斬ったり突いたり燃やしたり | Unknown | |
|
||||
| Sengoku Rance -Three for the Slash! Thrust! Burn!- (EN) | Unknown | |
|
||||
| 戦国ランス~三匹が斬ったり突いたり燃やしたり | Supported | |
|
||||
| Sengoku Rance -Three for the Slash! Thrust! Burn!- (EN) | Supported | |
|
||||
| AliveZ | Unknown | |
|
||||
| エドランゼ ~Come on!水戸姫様~ | Unknown | |
|
||||
| エドランゼ ~Come on!水戸姫様~ | Supported | |
|
||||
| 闘神都市III | Supported | |
|
||||
| ばにしゅ!~おっぱいの消えた王国~ | Unknown | |
|
||||
| 僕だけの保健室 | Unknown | |
|
||||
| 僕だけの保健室 | Supported | |
|
||||
| ももいろガーディアン | Unknown | |
|
||||
| 超昂閃忍ハルカ ハルカVSエスカレイヤー | Unknown | From Alice 2010 |
|
||||
| 超昂閃忍ハルカ ―疾風!?忍者大作戦― | Unknown | From Alice 2010 |
|
||||
@@ -72,4 +72,13 @@ issues, please let me know or open a pull request to update this table.
|
||||
| ランス02 -反逆の少女たち- | Supported | From Alice 2010 |
|
||||
| Rance 02 -The Rebellious Maidens- (Fan TL) | Supported | |
|
||||
| Rance 02 -The Rebellious Maidens- (MangaGamer) | Supported | |
|
||||
| All games released after Alice 2010 | Unsupported | |
|
||||
| しゃーまんず・さんくちゅあり-巫女の聖域- | Unknown | |
|
||||
| 大帝国 | Supported | |
|
||||
| ランス・クエスト | Supported | |
|
||||
| ランス・クエスト マグナム | Supported | |
|
||||
| 母娘乱館 | Unknown | |
|
||||
| パステルチャイム3 バインドシーカー | Unsupported | |
|
||||
| どらぺこ! ~おねだりドラゴンとおっぱい勇者~ | Unsupported | |
|
||||
| ランス01 光をもとめて | Supported | |
|
||||
| Rance 01 - Quest for Hikari (EN) | Supported | |
|
||||
| All games released after Rance 01 | Unsupported | |
|
||||
|
||||
@@ -30,9 +30,10 @@ enum asset_type {
|
||||
ASSET_CG,
|
||||
ASSET_FLAT,
|
||||
ASSET_PACT,
|
||||
ASSET_DATA
|
||||
ASSET_DATA,
|
||||
ASSET_FLASH
|
||||
};
|
||||
#define ASSET_TYPE_MAX (ASSET_DATA+1)
|
||||
#define ASSET_TYPE_MAX (ASSET_FLASH+1)
|
||||
|
||||
const char *asset_strtype(enum asset_type type);
|
||||
void asset_manager_init(void);
|
||||
|
||||
+8
-6
@@ -19,10 +19,14 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct archive_data;
|
||||
|
||||
void audio_init(void);
|
||||
void audio_fini(void);
|
||||
void audio_reset(void);
|
||||
void audio_update(void);
|
||||
bool audio_play_sound(int sound_no);
|
||||
// Takes ownership of dfile.
|
||||
bool audio_play_archive_data(struct archive_data *dfile);
|
||||
|
||||
bool wav_exists(int no);
|
||||
bool bgm_exists(int no);
|
||||
@@ -74,15 +78,13 @@ 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_group_num(int id);
|
||||
int wav_get_group_num(int id);
|
||||
//int bgm_get_group_num(int id);
|
||||
//int wav_prepare_from_file(int id, char *filename);
|
||||
//int bgm_prepare_from_file(int id, char *filename);
|
||||
int wav_prepare_from_file(int id, char *filename);
|
||||
int bgm_prepare_from_file(int id, char *filename);
|
||||
|
||||
int wav_get_group_num_from_data_num(int no);
|
||||
|
||||
struct archive_data;
|
||||
|
||||
// Takes ownership of dfile.
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile);
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_BASE64_H
|
||||
#define SYSTEM4_BASE64_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
unsigned char *base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len);
|
||||
|
||||
#endif /* SYSTEM4_BASE64_H */
|
||||
+45
-2
@@ -18,6 +18,7 @@
|
||||
#define SYSTEM4_DEBUGGER_H
|
||||
#ifdef DEBUGGER_ENABLED
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "system4/instructions.h"
|
||||
@@ -28,6 +29,18 @@
|
||||
|
||||
struct ain_variable;
|
||||
|
||||
enum dbg_stop_type {
|
||||
DBG_STOP_PAUSE,
|
||||
DBG_STOP_ERROR,
|
||||
DBG_STOP_BREAKPOINT,
|
||||
DBG_STOP_STEP,
|
||||
};
|
||||
|
||||
struct dbg_stop {
|
||||
enum dbg_stop_type type;
|
||||
const char *message;
|
||||
};
|
||||
|
||||
struct breakpoint {
|
||||
enum opcode restore_op;
|
||||
void *data;
|
||||
@@ -46,13 +59,19 @@ struct dbg_cmd {
|
||||
void (*run)(unsigned nr_args, char **args);
|
||||
};
|
||||
|
||||
struct dbg_info;
|
||||
|
||||
extern bool dbg_dap;
|
||||
extern bool dbg_enabled;
|
||||
extern bool dbg_start_in_debugger;
|
||||
extern unsigned dbg_current_frame;
|
||||
extern struct dbg_info *dbg_info;
|
||||
|
||||
void dbg_init(void);
|
||||
void dbg_init(const char *debug_info_path);
|
||||
void dbg_fini(void);
|
||||
void dbg_repl(void);
|
||||
void dbg_repl(enum dbg_stop_type, const char *msg);
|
||||
|
||||
void dbg_log(const char *log, const char *fmt, va_list ap);
|
||||
|
||||
void dbg_continue(void);
|
||||
void dbg_quit(void);
|
||||
@@ -61,6 +80,8 @@ void dbg_cmd_init(void);
|
||||
void dbg_cmd_repl(void);
|
||||
void dbg_cmd_add_module(const char *name, unsigned nr_commands, struct dbg_cmd *commands);
|
||||
void dbg_handle_breakpoint(void);
|
||||
bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*));
|
||||
void dbg_foreach_breakpoint(void (*fun)(int addr, struct breakpoint*, void *data), void *data);
|
||||
bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data);
|
||||
bool dbg_set_address_breakpoint(uint32_t address, void(*cb)(struct breakpoint*), void *data);
|
||||
bool dbg_set_step_over_breakpoint(void);
|
||||
@@ -70,13 +91,35 @@ void dbg_print_frame(unsigned no);
|
||||
void dbg_print_stack_trace(void);
|
||||
void dbg_print_stack(void);
|
||||
void dbg_print_vm_state(void);
|
||||
void dbg_print_current_line(void);
|
||||
union vm_value dbg_eval_string(const char *str, struct ain_type *type_out);
|
||||
struct string *dbg_value_to_string(struct ain_type *type, union vm_value value, int recursive);
|
||||
struct string *dbg_variable_to_string(struct ain_type *type, struct page *page, int slot, int recursive);
|
||||
|
||||
void dbg_dap_init(void);
|
||||
void dbg_dap_quit(void);
|
||||
void dbg_dap_repl(struct dbg_stop *stop);
|
||||
void dbg_dap_handle_messages(void);
|
||||
void dbg_dap_log(const char *log, const char *fmt, va_list ap);
|
||||
|
||||
struct dbg_info *dbg_info_load(const char *path);
|
||||
const char *dbg_info_source_name(const struct dbg_info *info, int file);
|
||||
char *dbg_info_source_path(const struct dbg_info *info, int file);
|
||||
const char *dbg_info_source_line(const struct dbg_info *info, int file, int line);
|
||||
int dbg_info_find_file(const struct dbg_info *info, const char *filename);
|
||||
bool dbg_info_addr2line(const struct dbg_info *info, int addr, int *file, int *line);
|
||||
int dbg_info_line2addr(const struct dbg_info *info, int file, int line);
|
||||
|
||||
#ifdef HAVE_SCHEME
|
||||
void dbg_scm_init(void);
|
||||
void dbg_scm_fini(void);
|
||||
void dbg_scm_repl(void);
|
||||
#endif /* HAVE_SCHEME */
|
||||
#else /* DEBUGGER ENABLED */
|
||||
#define dbg_init(debug_info_path)
|
||||
#define dbg_repl(type, msg)
|
||||
#define dbg_dap 0
|
||||
#define dbg_dap_handle_messages()
|
||||
#define dbg_dap_log(log, fmt, ap)
|
||||
#endif /* DEBUGGER_ENABLED */
|
||||
#endif /* SYSTEM4_DEBUGGER_H */
|
||||
|
||||
+28
-3
@@ -20,6 +20,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
/*
|
||||
* Dungeon data is a collection of cells indexed by three integers (x, y, z).
|
||||
@@ -45,7 +46,13 @@ struct dgn_cell {
|
||||
int32_t west_door;
|
||||
int32_t stairs_texture;
|
||||
int32_t stairs_orientation;
|
||||
// int32_t unknown[13];
|
||||
int32_t lightmap_floor;
|
||||
int32_t lightmap_ceiling;
|
||||
int32_t lightmap_north;
|
||||
int32_t lightmap_south;
|
||||
int32_t lightmap_east;
|
||||
int32_t lightmap_west;
|
||||
// int32_t unknown[7];
|
||||
int32_t enterable;
|
||||
int32_t enterable_north;
|
||||
int32_t enterable_south;
|
||||
@@ -86,11 +93,16 @@ struct dgn_cell {
|
||||
float south_door_angle;
|
||||
float east_door_angle;
|
||||
float west_door_angle;
|
||||
int north_door_lock;
|
||||
int south_door_lock;
|
||||
int east_door_lock;
|
||||
int west_door_lock;
|
||||
int32_t floor_event2;
|
||||
int32_t north_event2;
|
||||
int32_t south_event2;
|
||||
int32_t east_event2;
|
||||
int32_t west_event2;
|
||||
int pathfinding_cost;
|
||||
struct dgn_cell **visible_cells;
|
||||
};
|
||||
|
||||
@@ -106,16 +118,28 @@ struct packed_pvs {
|
||||
};
|
||||
|
||||
struct dgn {
|
||||
uint32_t version; // 10: Rance VI, 13: GALZOO Island
|
||||
uint32_t size_x;
|
||||
uint32_t size_y;
|
||||
uint32_t size_z;
|
||||
// uint32_t unknown[10];
|
||||
struct dgn_cell *cells;
|
||||
struct packed_pvs *pvs;
|
||||
vec3 sphere_theta;
|
||||
float sphere_color_top;
|
||||
float sphere_color_bottom;
|
||||
|
||||
// for generated dungeons
|
||||
int start_x, start_y;
|
||||
int exit_x, exit_y;
|
||||
|
||||
// DrawField
|
||||
int32_t back_color_r;
|
||||
int32_t back_color_g;
|
||||
int32_t back_color_b;
|
||||
};
|
||||
|
||||
struct dgn *dgn_parse(uint8_t *data, size_t size);
|
||||
struct dgn *dgn_new(uint32_t size_x, uint32_t size_y, uint32_t size_z);
|
||||
struct dgn *dgn_parse(uint8_t *data, size_t size, bool for_draw_field);
|
||||
void dgn_free(struct dgn *dgn);
|
||||
|
||||
int dgn_cell_index(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z);
|
||||
@@ -134,5 +158,6 @@ static inline bool dgn_is_in_map(struct dgn *dgn, uint32_t x, uint32_t y, uint32
|
||||
|
||||
// Returns a list of cells visible from (x, y, z), sorted by distance from (x, y, z).
|
||||
struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, int *nr_cells_out);
|
||||
void dgn_calc_lightmap(struct dgn *dgn);
|
||||
|
||||
#endif /* SYSTEM4_DGN_H */
|
||||
|
||||
+13
-7
@@ -23,14 +23,19 @@
|
||||
struct cg;
|
||||
|
||||
enum dtx_texture_type {
|
||||
DTX_WALL = 0,
|
||||
DTX_FLOOR = 1,
|
||||
DTX_CEILING = 2,
|
||||
DTX_STAIRS = 3,
|
||||
DTX_DOOR = 4,
|
||||
DTX_SKYBOX = 5,
|
||||
// cell textures
|
||||
DTX_WALL,
|
||||
DTX_FLOOR,
|
||||
DTX_CEILING,
|
||||
DTX_STAIRS,
|
||||
DTX_DOOR,
|
||||
DTX_LIGHTMAP,
|
||||
// skybox textures
|
||||
DTX_SKYBOX,
|
||||
};
|
||||
|
||||
#define DTX_NR_CELL_TEXTURE_TYPES (DTX_LIGHTMAP + 1)
|
||||
|
||||
struct dtx_entry {
|
||||
uint32_t size;
|
||||
uint8_t *data;
|
||||
@@ -45,6 +50,7 @@ struct dtx {
|
||||
|
||||
struct dtx *dtx_parse(uint8_t *data, size_t size);
|
||||
void dtx_free(struct dtx *dtx);
|
||||
struct cg *dtx_create_cg(struct dtx *dtx, int type, int index);
|
||||
struct cg *dtx_create_cg(struct dtx *dtx, enum dtx_texture_type type, int index);
|
||||
struct dtx *dtx_load_from_dtl(const char *path, uint32_t seed);
|
||||
|
||||
#endif /* SYSTEM4_DTX_H */
|
||||
|
||||
@@ -20,20 +20,25 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "gfx/gl.h"
|
||||
#include <cglm/cglm.h>
|
||||
#include "gfx/gfx.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#define DRAWFIELD_NR_CHARACTERS 256
|
||||
|
||||
struct page;
|
||||
struct dgn;
|
||||
struct dtx;
|
||||
struct tes;
|
||||
struct dungeon_renderer;
|
||||
struct dungeon_map;
|
||||
struct drawfield_character;
|
||||
|
||||
enum draw_dungeon_version {
|
||||
DRAW_DUNGEON_1, // Rance VI
|
||||
// DRAW_DUNGEON_2, // Dungeons & Dolls
|
||||
DRAW_DUNGEON_14 // GALZOO Island
|
||||
DRAW_DUNGEON_2, // Dungeons & Dolls
|
||||
DRAW_DUNGEON_14, // GALZOO Island
|
||||
DRAW_FIELD // Pastel Chime Continue
|
||||
};
|
||||
|
||||
struct camera {
|
||||
@@ -62,19 +67,34 @@ struct dungeon_context {
|
||||
struct dgn *dgn;
|
||||
struct dtx *dtx;
|
||||
struct tes *tes;
|
||||
ivec3 player_pos;
|
||||
struct drawfield_character *characters;
|
||||
mat4 proj_transform;
|
||||
struct dungeon_renderer *renderer;
|
||||
struct texture texture;
|
||||
GLuint depth_buffer;
|
||||
};
|
||||
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int surface);
|
||||
void dungeon_context_free(struct dungeon_context *ctx);
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int width, int height);
|
||||
bool dungeon_load(struct dungeon_context *ctx, int num);
|
||||
bool dungeon_load_dungeon(struct dungeon_context *ctx, const char *filename, int num);
|
||||
bool dungeon_load_texture(struct dungeon_context *ctx, const char *filename);
|
||||
void dungeon_set_camera(int surface, float x, float y, float z, float angle, float angle_p);
|
||||
void dungeon_set_perspective(int surface, int width, int height, float near, float far, float deg);
|
||||
void dungeon_set_player_pos(int surface, int x, int y, int z);
|
||||
void dungeon_set_walked(int surface, int x, int y, int z, int flag);
|
||||
int dungeon_get_walked(int surface, int x, int y, int z);
|
||||
void dungeon_set_walked_all(int surface);
|
||||
int dungeon_calc_conquer(int surface);
|
||||
bool dungeon_load_walk_data(int surface, int map, struct page **page);
|
||||
bool dungeon_save_walk_data(int surface, int map, struct page **page);
|
||||
void dungeon_paint_step(int surface, int x, int y, int z);
|
||||
void dungeon_set_chara_sprite(int surface, int num, int sprite);
|
||||
void dungeon_set_chara_pos(int surface, int num, float x, float y, float z);
|
||||
void dungeon_set_chara_cg(int surface, int num, int cg);
|
||||
void dungeon_set_chara_cg_info(int surface, int num, int num_chara_x, int num_chara_y);
|
||||
void dungeon_set_chara_show(int surface, int num, bool show);
|
||||
bool dungeon_project_world_to_screen(struct dungeon_context *ctx, vec3 world_pos, Point *screen_pos);
|
||||
|
||||
struct dungeon_context *dungeon_get_context(int surface);
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright (C) 2025 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/>.
|
||||
*/
|
||||
#ifndef SYSTEM4_DUNGEON_GENERATOR_H
|
||||
#define SYSTEM4_DUNGEON_GENERATOR_H
|
||||
|
||||
struct dgn;
|
||||
|
||||
enum drawfield_cell_flag {
|
||||
DF_FLAG_ITEM = 1 << 0,
|
||||
DF_FLAG_ENEMY = 1 << 1,
|
||||
DF_FLAG_TRAP = 1 << 2,
|
||||
DF_FLAG_NO_TREASURE = 1 << 3,
|
||||
DF_FLAG_NO_MONSTER = 1 << 4,
|
||||
DF_FLAG_NO_TRAP = 1 << 5,
|
||||
DF_FLAG_TREASURE_ROOM = 1 << 6
|
||||
};
|
||||
|
||||
struct dgn *dgn_generate_drawdungeon2(int level);
|
||||
void dgn_paint_step(struct dgn *dgn, int x, int y);
|
||||
|
||||
struct dgn *dgn_generate_drawfield(
|
||||
int floor, int complexity, int wall_arrange_method, int floor_arrange_method,
|
||||
int field_size_x, int field_size_y, int door_lock_percent, uint32_t seed,
|
||||
uint8_t *cell_flags);
|
||||
|
||||
#endif /* SYSTEM4_DUNGEON_GENERATOR_H */
|
||||
@@ -32,6 +32,7 @@ void dungeon_map_set_all_view(int surface, int flag);
|
||||
void dungeon_map_set_cg(int surface, int index, int sprite);
|
||||
void dungeon_map_set_small_map_floor(int surface, int floor);
|
||||
void dungeon_map_set_large_map_floor(int surface, int floor);
|
||||
void dungeon_map_reveal(struct dungeon_context *ctx, int x, int y, int z, bool transparent);
|
||||
void dungeon_map_reveal(struct dungeon_context *ctx, int x, int y, int z, bool from_walk_data);
|
||||
void dungeon_map_hide(struct dungeon_context *ctx, int x, int y, int z);
|
||||
|
||||
#endif /* SYSTEM4_DUNGEON_MAP_H */
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2021 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/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_MTRAND43_H
|
||||
#define SYSTEM4_MTRAND43_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MTRAND43_STATE_SIZE 4
|
||||
|
||||
// Mersenne Twister engine with N=4, M=3
|
||||
struct mtrand43 {
|
||||
uint32_t st[MTRAND43_STATE_SIZE];
|
||||
int i;
|
||||
};
|
||||
|
||||
void mtrand43_init(struct mtrand43 *mt, uint32_t seed);
|
||||
uint32_t mtrand43_genrand(struct mtrand43 *mt);
|
||||
float mtrand43_genfloat(struct mtrand43 *mt);
|
||||
|
||||
#endif /* SYSTEM4_MTRAND43_H */
|
||||
@@ -20,16 +20,32 @@
|
||||
#include <cglm/cglm.h>
|
||||
#include "gfx/gl.h"
|
||||
|
||||
struct texture;
|
||||
struct dgn_cell;
|
||||
struct dtx;
|
||||
struct polyobj;
|
||||
struct dungeon_renderer;
|
||||
|
||||
struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po);
|
||||
struct drawfield_character {
|
||||
vec3 pos;
|
||||
int sprite;
|
||||
int rows;
|
||||
int cols;
|
||||
int cg_index;
|
||||
bool show;
|
||||
};
|
||||
|
||||
struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, int num, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po);
|
||||
void dungeon_renderer_free(struct dungeon_renderer *r);
|
||||
void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, mat4 view_transform, mat4 proj_transform);
|
||||
void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, struct drawfield_character *characters, mat4 view_transform, mat4 proj_transform);
|
||||
void dungeon_renderer_enable_event_markers(struct dungeon_renderer *r, bool enable);
|
||||
bool dungeon_renderer_event_markers_enabled(struct dungeon_renderer *r);
|
||||
bool dungeon_renderer_is_floor_opaque(struct dungeon_renderer *r, struct dgn_cell *cell);
|
||||
void dungeon_renderer_run_post_processing(struct dungeon_renderer *r, struct texture *src, struct texture *dst);
|
||||
void dungeon_renderer_set_raster_scroll(struct dungeon_renderer *r, int type);
|
||||
void dungeon_renderer_set_raster_amp(struct dungeon_renderer *r, float amp);
|
||||
void dungeon_renderer_enable_lightmap(struct dungeon_renderer *r, bool enable);
|
||||
bool dungeon_renderer_get_draw_obj_flag(struct dungeon_renderer *r, int type);
|
||||
void dungeon_renderer_set_draw_obj_flag(struct dungeon_renderer *r, int type, bool flag);
|
||||
|
||||
#endif /* SYSTEM4_DUNGEON_RENDERER_H */
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
enum draw_dungeon_version;
|
||||
struct dtx;
|
||||
struct skybox;
|
||||
|
||||
struct skybox *skybox_create(struct dtx *dtx);
|
||||
struct skybox *skybox_create(enum draw_dungeon_version version, struct dtx *dtx);
|
||||
void skybox_free(struct skybox *s);
|
||||
void skybox_render(struct skybox *s, const mat4 view_transform, const mat4 proj_transform);
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef SYSTEM4_EFFECT_H
|
||||
#define SYSTEM4_EFFECT_H
|
||||
|
||||
struct texture;
|
||||
|
||||
enum effect {
|
||||
EFFECT_CROSSFADE = 1,
|
||||
EFFECT_FADEOUT = 2,
|
||||
@@ -70,9 +72,15 @@ enum effect {
|
||||
EFFECT_BLUR_FADEOUT = 50,
|
||||
EFFECT_BLUR_CROSSFADE = 51,
|
||||
EFFECT_2ROT_ZOOM_BLEND_BLUR = 52,
|
||||
EFFECT_VWAVE_CROSSFADE = 60,
|
||||
NR_EFFECTS
|
||||
};
|
||||
|
||||
extern const char *effect_names[NR_EFFECTS];
|
||||
|
||||
int effect_init(enum effect type);
|
||||
void effect_update_texture(int type, struct texture *dst, struct texture *old, struct texture *new, float rate);
|
||||
int effect_update(float rate);
|
||||
int effect_fini(void);
|
||||
|
||||
#endif /* SYSTEM4_EFFECT_H */
|
||||
|
||||
+40
-2
@@ -68,6 +68,7 @@ struct font {
|
||||
float (*get_actual_size_round_down)(struct font *font, float size);
|
||||
bool (*get_glyph)(struct font_size *size, struct glyph *dst, uint32_t code, enum font_weight weight);
|
||||
float (*size_char)(struct font_size *size, uint32_t code);
|
||||
float (*size_char_kerning)(struct font_size *size, uint32_t code, uint32_t code_next);
|
||||
};
|
||||
|
||||
struct text_style {
|
||||
@@ -89,6 +90,27 @@ struct text_style {
|
||||
struct font_size *font_size;
|
||||
};
|
||||
|
||||
enum text_render_mode {
|
||||
RENDER_COPY,
|
||||
RENDER_BLENDED,
|
||||
RENDER_PMAP,
|
||||
RENDER_AMAP,
|
||||
};
|
||||
|
||||
struct text_render_metrics {
|
||||
float x;
|
||||
int y;
|
||||
SDL_Color color;
|
||||
enum font_weight weight;
|
||||
float edge_width;
|
||||
float scale_x;
|
||||
float space_scale_x;
|
||||
float font_spacing;
|
||||
float edge_spacing;
|
||||
enum text_render_mode mode;
|
||||
struct font_size *font_size;
|
||||
};
|
||||
|
||||
static inline float text_style_edge_width(struct text_style *ts)
|
||||
{
|
||||
return max(max(ts->edge_up, ts->edge_down), max(ts->edge_left, ts->edge_right));
|
||||
@@ -132,14 +154,30 @@ int gfx_get_font_space(void);
|
||||
SDL_Color gfx_get_font_color(void);
|
||||
void gfx_set_font_name(const char *name);
|
||||
|
||||
int gfx_render_text(Texture *dst, float x, int y, char *msg, struct text_style *ts);
|
||||
struct font_size *gfx_font_get_size(unsigned face, float size);
|
||||
enum font_weight gfx_int_to_font_weight(int weight);
|
||||
float _gfx_render_text(Texture *dst, char *msg, struct text_render_metrics *tm);
|
||||
|
||||
int gfx_render_text(Texture *dst, float x, int y, char *msg, struct text_style *ts, bool blend);
|
||||
float gfx_render_textf(Texture *dst, float x, int y, char *msg, struct text_style *ts, bool blend);
|
||||
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(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);
|
||||
|
||||
void gfx_print_text_style(struct text_style *style, int indent);
|
||||
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;
|
||||
}
|
||||
|
||||
#endif /* SYSTEM4_FONT_H */
|
||||
|
||||
+31
-14
@@ -36,11 +36,7 @@ enum draw_method {
|
||||
|
||||
typedef struct texture {
|
||||
GLuint handle;
|
||||
mat4 world_transform;
|
||||
int w, h;
|
||||
bool has_alpha;
|
||||
int alpha_mod;
|
||||
enum draw_method draw_method;
|
||||
} Texture;
|
||||
|
||||
struct gfx_render_job;
|
||||
@@ -51,19 +47,31 @@ typedef struct shader {
|
||||
GLuint world_transform;
|
||||
GLuint view_transform;
|
||||
GLuint texture;
|
||||
GLuint vertex;
|
||||
GLuint alpha_mod;
|
||||
GLuint vertex_pos;
|
||||
GLuint vertex_uv;
|
||||
void (*prepare)(struct gfx_render_job *, void *);
|
||||
} Shader;
|
||||
|
||||
enum gfx_shape {
|
||||
GFX_RECTANGLE,
|
||||
GFX_LINE,
|
||||
GFX_QUADRILATERAL,
|
||||
};
|
||||
|
||||
struct gfx_render_job {
|
||||
struct shader *shader;
|
||||
enum gfx_shape shape;
|
||||
GLuint texture;
|
||||
GLfloat *world_transform;
|
||||
GLfloat *view_transform;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct gfx_vertex {
|
||||
GLfloat x, y, z, w;
|
||||
GLfloat u, v;
|
||||
};
|
||||
|
||||
int gfx_init(void);
|
||||
void gfx_fini(void);
|
||||
|
||||
@@ -71,18 +79,24 @@ Texture *gfx_main_surface(void);
|
||||
void gfx_set_window_logical_size(int w, int h);
|
||||
void gfx_update_screen_scale(void);
|
||||
void gfx_set_wait_vsync(bool wait);
|
||||
float gfx_get_frame_rate(void);
|
||||
|
||||
void gfx_load_shader(struct shader *dst, const char *vertex_shader_path, const char *fragment_shader_path);
|
||||
GLuint gfx_load_shader_file(const char *path, GLenum type);
|
||||
GLuint gfx_load_shader_file(const char *path, GLenum type, const char *defines);
|
||||
|
||||
// rendering
|
||||
void gfx_set_clear_color(int r, int g, int b, int a);
|
||||
void gfx_set_view_offset(int x, int y);
|
||||
void gfx_clear(void);
|
||||
void gfx_swap(void);
|
||||
void gfx_set_view(struct texture *t);
|
||||
void gfx_reset_view(void);
|
||||
void gfx_prepare_job(struct gfx_render_job *job);
|
||||
void gfx_run_job(struct gfx_render_job *job);
|
||||
void gfx_render(struct gfx_render_job *job);
|
||||
void _gfx_render_texture(struct shader *s, struct texture *t, Rectangle *r, void *data);
|
||||
void gfx_render_texture(struct texture *t, Rectangle *r);
|
||||
void gfx_render_quadrilateral(struct texture *t, struct gfx_vertex vertices[4]);
|
||||
|
||||
// texture management
|
||||
void gfx_init_texture_blank(struct texture *t, int w, int h);
|
||||
@@ -92,6 +106,7 @@ void gfx_init_texture_rgb(struct texture *t, int w, int h, SDL_Color color);
|
||||
void gfx_init_texture_with_pixels(struct texture *t, int w, int h, void *pixels);
|
||||
void gfx_init_texture_amap(struct texture *t, int w, int h, uint8_t *amap, SDL_Color color);
|
||||
void gfx_init_texture_rmap(struct texture *t, int w, int h, uint8_t *rmap);
|
||||
void gfx_update_texture_with_pixels(struct texture *t, void *pixels);
|
||||
void gfx_copy_main_surface(struct texture *dst);
|
||||
void gfx_delete_texture(struct texture *t);
|
||||
GLuint gfx_set_framebuffer(GLenum target, Texture *t, int x, int y, int w, int h);
|
||||
@@ -143,13 +158,17 @@ void gfx_bright_dest_only(Texture *dst, int x, int y, int w, int h, int rate);
|
||||
void gfx_copy_stretch(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_blend(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh, int a);
|
||||
void gfx_copy_stretch_blend_screen(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_blend_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_blend_amap_alpha(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh, int a);
|
||||
void gfx_copy_rot_zoom(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_rot_zoom_amap(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_rot_zoom_use_amap(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_rot_zoom2(Texture *dst, float cx, float cy, Texture *src, float scx, float scy, float rot, float mag);
|
||||
void gfx_copy_reverse_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_reverse_UD(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_rotate_y(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_rotate_y_use_amap(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_rotate_x(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
@@ -161,14 +180,12 @@ void gfx_copy_amap_height_blur(Texture *dst, int dx, int dy, Texture *src, int s
|
||||
void gfx_copy_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
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_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width);
|
||||
void gfx_copy_grayscale(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);
|
||||
void gfx_draw_glyph_to_pmap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, SDL_Color color, float scale_x);
|
||||
void gfx_draw_glyph_to_amap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, float scale_x);
|
||||
|
||||
// debug printing
|
||||
void gfx_print_color(SDL_Color *c);
|
||||
void gfx_print_rectangle(Rectangle *r);
|
||||
void gfx_print_point(Point *p);
|
||||
void gfx_print_texture(struct texture *t, int indent);
|
||||
void gfx_draw_quadrilateral(Texture *dst, Texture *src, struct gfx_vertex vertices[4]);
|
||||
|
||||
#endif /* SYSTEM4_SDL_CORE_H */
|
||||
|
||||
@@ -31,7 +31,9 @@ struct sdl_private {
|
||||
SDL_GLContext context;
|
||||
GLuint vao;
|
||||
GLuint vbo;
|
||||
GLuint ibo;
|
||||
GLuint quad_vbo;
|
||||
GLuint rect_ibo;
|
||||
GLuint line_ibo;
|
||||
} gl;
|
||||
int w, h;
|
||||
SDL_Rect viewport;
|
||||
|
||||
@@ -56,4 +56,10 @@ typedef SDL_Rect Rectangle;
|
||||
0, 0, 1, -1, \
|
||||
0, 0, 0, 1)
|
||||
|
||||
#define WORLD_TRANSFORM(w, h, x, y) \
|
||||
MAT4(w, 0, 0, x, \
|
||||
0, h, 0, y, \
|
||||
0, 0, 1, 0, \
|
||||
0, 0, 0, 1)
|
||||
|
||||
#endif /* SYSTEM4_GRAPHICS_H */
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2025 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/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_ICON_H
|
||||
#define SYSTEM4_ICON_H
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define icon_init()
|
||||
#else
|
||||
void icon_init(void);
|
||||
#endif
|
||||
|
||||
#endif /* SYSTEM4_ICON_H */
|
||||
+10
-2
@@ -17,14 +17,22 @@
|
||||
#ifndef SYSTEM4_ID_POOL_H
|
||||
#define SYSTEM4_ID_POOL_H
|
||||
|
||||
// A reasonable base value for pointer-based handles.
|
||||
#define ID_POOL_HANDLE_BASE 0x100000
|
||||
|
||||
struct id_pool {
|
||||
void **slots;
|
||||
int nr_slots;
|
||||
int base;
|
||||
};
|
||||
|
||||
void id_pool_init(struct id_pool *pool);
|
||||
void id_pool_release(struct id_pool *pool, int id);
|
||||
void id_pool_init(struct id_pool *pool, int base);
|
||||
void id_pool_delete(struct id_pool *pool);
|
||||
void *id_pool_release(struct id_pool *pool, int id);
|
||||
int id_pool_count(struct id_pool *pool);
|
||||
int id_pool_get_unused(struct id_pool *pool);
|
||||
int id_pool_get_first(struct id_pool *pool);
|
||||
int id_pool_get_next(struct id_pool *pool, int id);
|
||||
void *id_pool_get(struct id_pool *pool, int id);
|
||||
void *id_pool_set(struct id_pool *pool, int id, void *data);
|
||||
|
||||
|
||||
+2
-1
@@ -137,9 +137,10 @@ enum sact_keycode {
|
||||
extern bool mouse_focus;
|
||||
extern bool keyboard_focus;
|
||||
|
||||
void handle_window_events(void);
|
||||
void handle_events(void);
|
||||
bool key_is_down(enum sact_keycode code);
|
||||
void key_clear_flag(void);
|
||||
void key_clear_flag(bool no_ctrl);
|
||||
bool joy_key_is_down(uint8_t code);
|
||||
void joy_clear_flag(void);
|
||||
bool joy_get_stick_status(int controller, int type, float *deg, float *pow);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* Copyright (C) 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef XSYSTEM4_JSON_H
|
||||
#define XSYSTEM4_JSON_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "gfx/types.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct texture;
|
||||
struct text_style;
|
||||
struct sact_sprite;
|
||||
struct sprite;
|
||||
|
||||
cJSON *num2_to_json(double a, double b);
|
||||
cJSON *num2_to_json_object(const char *a_name, double a, const char *b_name, double b);
|
||||
cJSON *num3_to_json(double a, double b, double c);
|
||||
cJSON *num3_to_json_object(const char *a_name, double a, const char *b_name, double b,
|
||||
const char *c_name, double c);
|
||||
cJSON *num4_to_json(double a, double b, double c, double d);
|
||||
cJSON *num4_to_json_object(const char *a_name, double a, const char *b_name, double b,
|
||||
const char *c_name, double c, const char *d_name, double d);
|
||||
cJSON *texture_to_json(struct texture *t, bool verbose);
|
||||
cJSON *texture_to_json_with_pixels(struct texture *t);
|
||||
cJSON *text_style_to_json(struct text_style *ts, bool verbose);
|
||||
cJSON *scene_sprite_to_json(struct sprite *sp, bool verbose);
|
||||
cJSON *sprite_to_json(struct sact_sprite *sp, bool verbose);
|
||||
|
||||
static inline cJSON *xy_to_json(double x, double y, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num2_to_json(x, y);
|
||||
return num2_to_json_object("x", x, "y", y);
|
||||
}
|
||||
|
||||
static inline cJSON *wh_to_json(double w, double h, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num2_to_json(w, h);
|
||||
return num2_to_json_object("w", w, "h", h);
|
||||
}
|
||||
|
||||
static inline cJSON *point_to_json(Point *p, bool verbose)
|
||||
{
|
||||
return xy_to_json(p->x, p->y, verbose);
|
||||
}
|
||||
|
||||
static inline cJSON *rectangle_to_json(Rectangle *r, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num4_to_json(r->x, r->y, r->w, r->h);
|
||||
return num4_to_json_object("x", r->x, "y", r->y, "w", r->w, "h", r->h);
|
||||
}
|
||||
|
||||
static inline cJSON *color_to_json(Color *c, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num4_to_json(c->r, c->g, c->b, c->a);
|
||||
return num4_to_json_object("r", c->r, "g", c->g, "b", c->b, "a", c->a);
|
||||
}
|
||||
|
||||
static inline cJSON *vec3_point_to_json(vec3 v, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num3_to_json(v[0], v[1], v[2]);
|
||||
return num3_to_json_object("x", v[0], "y", v[1], "z", v[2]);
|
||||
}
|
||||
|
||||
static inline cJSON *vec3_color_to_json(vec3 v, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num3_to_json(v[0], v[1], v[2]);
|
||||
return num3_to_json_object("r", v[0], "g", v[1], "b", v[2]);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,76 +0,0 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* Based on code from xsystem35
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef LITTLE_ENDIAN_H
|
||||
#define LITTLE_ENDIAN_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// no need to decode on byte-addressable, little-endian arch
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define LittleEndian_getDW(b, i) (*((int32_t*)((uint8_t*)(b)+(i))))
|
||||
#define LittleEndian_getW(b, i) (*((int16_t*)((uint8_t*)(b)+(i))))
|
||||
#else
|
||||
static inline int32_t LittleEndian_getDW(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1, c2, c3;
|
||||
int d0, d1;
|
||||
c0 = *(b + i + 0);
|
||||
c1 = *(b + i + 1);
|
||||
c2 = *(b + i + 2);
|
||||
c3 = *(b + i + 3);
|
||||
d0 = c0 + (c1 << 8);
|
||||
d1 = c2 + (c3 << 8);
|
||||
return d0 + (d1 << 16);
|
||||
}
|
||||
|
||||
static inline int16_t LittleEndian_getW(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1;
|
||||
c0 = *(b + i + 0);
|
||||
c1 = *(b + i + 1);
|
||||
return c0 + (c1 << 8);
|
||||
}
|
||||
#endif /* defined(__i386__) || defined(__x86_64__) */
|
||||
|
||||
static inline int32_t LittleEndian_get3B(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1, c2;
|
||||
c0 = *(b + i + 0);
|
||||
c1 = *(b + i + 1);
|
||||
c2 = *(b + i + 2);
|
||||
return c0 + (c1 << 8) + (c2 << 16);
|
||||
}
|
||||
|
||||
static inline void LittleEndian_putDW(uint8_t *dst, int i, uint32_t dword)
|
||||
{
|
||||
dst[i] = (uint8_t)(dword);
|
||||
dst[i+1] = (uint8_t)(dword >> 8);
|
||||
dst[i+2] = (uint8_t)(dword >> 16);
|
||||
dst[i+3] = (uint8_t)(dword >> 24);
|
||||
}
|
||||
|
||||
static inline void LittleEndian_putW(uint8_t *dst, int i, uint16_t word)
|
||||
{
|
||||
dst[i] = word & 0xFF;
|
||||
dst[i+1] = word >> 8;
|
||||
}
|
||||
|
||||
#endif /* LITTLE_ENDIAN_H */
|
||||
@@ -61,6 +61,7 @@ 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);
|
||||
struct channel *channel_open_file(const char *path);
|
||||
void channel_close(struct channel *ch);
|
||||
int channel_play(struct channel *ch);
|
||||
int channel_stop(struct channel *ch);
|
||||
@@ -83,5 +84,6 @@ int channel_seek(struct channel *ch, int pos);
|
||||
int channel_reverse_LR(struct channel *ch);
|
||||
int channel_get_volume(struct channel *ch);
|
||||
int channel_get_time_length(struct channel *ch);
|
||||
int channel_get_data_no(struct channel *ch);
|
||||
|
||||
#endif /* SYSTEM4_MIXER_H */
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MSGQUEUE_H__
|
||||
#define __MSGQUEUE_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL_mutex.h>
|
||||
|
||||
struct msgq_elem;
|
||||
|
||||
struct msgq {
|
||||
SDL_mutex *mutex;
|
||||
SDL_cond *cond_nonempty;
|
||||
struct msgq_elem *head;
|
||||
struct msgq_elem *last;
|
||||
};
|
||||
|
||||
static inline bool msgq_isempty(struct msgq *q) {
|
||||
return !q->head;
|
||||
}
|
||||
|
||||
struct msgq *msgq_new(void);
|
||||
void msgq_free(struct msgq *q);
|
||||
void msgq_enqueue(struct msgq *q, void *msg);
|
||||
void *msgq_dequeue(struct msgq *q);
|
||||
void *msgq_dequeue_timeout(struct msgq *q, uint32_t timeout_ms);
|
||||
|
||||
#endif // __MSGQUEUE_H__
|
||||
+75
-28
@@ -24,6 +24,7 @@ struct string;
|
||||
|
||||
// parts.c
|
||||
bool PE_Init(void);
|
||||
void PE_Reset(void);
|
||||
void PE_Update(int passed_time, bool message_window_show);
|
||||
void PE_UpdateComponent(int passed_time);
|
||||
void PE_UpdateParts(int passed_time, bool is_skip, bool message_window_show);
|
||||
@@ -31,6 +32,8 @@ void PE_SetDelegateIndex(int parts_no, int delegate_index);
|
||||
int PE_GetDelegateIndex(int parts_no);
|
||||
bool PE_SetPartsCG(int parts_no, struct string *cg_name, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_index(int parts_no, int cg_no, int sprite_deform, int state);
|
||||
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);
|
||||
int PE_GetPartsCGNumber(int parts_no, int state);
|
||||
@@ -40,12 +43,15 @@ bool PE_SetLoopCG(int parts_no, struct string *cg_name, int start_no, int nr_fra
|
||||
bool PE_SetLoopCGSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetHGaugeCG(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_SetHGaugeCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetHGaugeRate(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetHGaugeRate(int parts_no, float numerator, float denominator, int state);
|
||||
bool PE_SetHGaugeRate_int(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetHGaugeSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetVGaugeCG(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_SetVGaugeCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetVGaugeRate(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetVGaugeRate(int parts_no, float numerator, float denominator, int state);
|
||||
bool PE_SetVGaugeRate_int(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetVGaugeSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetNumeralCG(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_SetNumeralCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetNumeralLinkedCGNumberWidthWidthList_by_index(int parts_no, int cg_no,
|
||||
int w0, int w1, int w2, int w3, int w4, int w5, int w6, int w7, int w8,
|
||||
@@ -58,46 +64,51 @@ bool PE_SetNumeralShowComma(int parts_no, bool show_comma, int state);
|
||||
bool PE_SetNumeralSpace(int parts_no, int space, int state);
|
||||
bool PE_SetNumeralLength(int parts_no, int length, int state);
|
||||
bool PE_SetNumeralSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetPartsRectangleDetectionSize(int PartsNumber, int Width, int Height, int State);
|
||||
bool PE_SetPartsFlash(int PartsNumber, struct string *pIFlashFileName, int State);
|
||||
bool PE_IsPartsFlashEnd(int PartsNumber, int State);
|
||||
int PE_GetPartsFlashCurrentFrameNumber(int PartsNumber, int State);
|
||||
bool PE_BackPartsFlashBeginFrame(int PartsNumber, int State);
|
||||
bool PE_StepPartsFlashFinalFrame(int PartsNumber, int State);
|
||||
void PE_ReleaseParts(int parts_no);
|
||||
void PE_ReleaseAllParts(void);
|
||||
void PE_ReleaseAllPartsWithoutSystem(void);
|
||||
void PE_SetPos(int parts_no, int x, int y);
|
||||
void PE_SetZ(int parts_no, int z);
|
||||
void PE_SetShow(int parts_no, bool show);
|
||||
void PE_SetAlpha(int parts_no, int alpha);
|
||||
void PE_SetPartsDrawFilter(int PartsNumber, int DrawFilter);
|
||||
void PE_SetAddColor(int parts_no, int r, int g, int b);
|
||||
void PE_SetMultiplyColor(int parts_no, int r, int g, int b);
|
||||
int PE_GetPartsX(int parts_no);
|
||||
int PE_GetPartsY(int parts_no);
|
||||
void PE_SetZ(int parts_no, int z);
|
||||
int PE_GetPartsZ(int parts_no);
|
||||
void PE_SetShow(int parts_no, bool show);
|
||||
bool PE_GetPartsShow(int parts_no);
|
||||
void PE_SetAlpha(int parts_no, int alpha);
|
||||
int PE_GetPartsAlpha(int parts_no);
|
||||
void PE_SetPartsDrawFilter(int parts_no, int draw_filter);
|
||||
int PE_GetPartsDrawFilter(int parts_no);
|
||||
void PE_SetAddColor(int parts_no, int r, int g, int b);
|
||||
void PE_GetAddColor(int parts_no, int *r, int *g, int *b);
|
||||
void PE_SetMultiplyColor(int parts_no, int r, int g, int b);
|
||||
void PE_GetMultiplyColor(int parts_no, int *r, int *g, int *b);
|
||||
int PE_GetPartsWidth(int parts_no, int state);
|
||||
int PE_GetPartsHeight(int parts_no, int state);
|
||||
int PE_GetPartsUpperLeftPosX(int parts_no, int state);
|
||||
int PE_GetPartsUpperLeftPosY(int parts_no, int state);
|
||||
int PE_GetPartsHeight(int parts_no, int state);
|
||||
bool PE_GetPartsShow(int parts_no);
|
||||
int PE_GetPartsAlpha(int parts_no);
|
||||
void PE_SetPartsOriginPosMode(int parts_no, int origin_pos_mode);
|
||||
int PE_GetPartsOriginPosMode(int parts_no);
|
||||
void PE_SetParentPartsNumber(int parts_no, int parent_parts_no);
|
||||
bool PE_SetPartsGroupNumber(int PartsNumber, int GroupNumber);
|
||||
int PE_GetParentPartsNumber(int parts_no);
|
||||
bool PE_SetPartsGroupNumber(int parts_no, int group_no);
|
||||
void PE_SetPartsMessageWindowShowLink(int parts_no, bool message_window_show_link);
|
||||
bool PE_GetPartsMessageWindowShowLink(int PartsNumber);
|
||||
bool PE_GetPartsMessageWindowShowLink(int parts_no);
|
||||
void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate);
|
||||
void PE_SetPartsMagX(int parts_no, float scale_x);
|
||||
float PE_GetPartsMagX(int parts_no);
|
||||
void PE_SetPartsMagY(int parts_no, float scale_y);
|
||||
float PE_GetPartsMagY(int parts_no);
|
||||
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);
|
||||
void PE_SetPartsAlphaClipperPartsNumber(int PartsNumber, int AlphaClipperPartsNumber);
|
||||
void PE_SetPartsPixelDecide(int PartsNumber, bool PixelDecide);
|
||||
bool PE_SetThumbnailReductionSize(int ReductionSize);
|
||||
float PE_GetPartsRotateZ(int parts_no);
|
||||
void PE_SetPartsPixelDecide(int PartsNumber, bool pixel_decide);
|
||||
bool PE_SetThumbnailReductionSize(int reduction_size);
|
||||
bool PE_SetThumbnailMode(bool Mode);
|
||||
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);
|
||||
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);
|
||||
@@ -114,6 +125,8 @@ 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_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,
|
||||
@@ -127,15 +140,17 @@ bool PE_AddDrawTextToPartsConstructionProcess(int parts_no, int x, int y, struct
|
||||
int edge_r, int edge_g, int edge_b, float edge_weight,
|
||||
int char_space, int line_space, int state);
|
||||
bool PE_BuildPartsConstructionProcess(int parts_no, int state);
|
||||
bool PE_ClearPartsConstructionProcess(int parts_no, int state);
|
||||
bool PE_SetPartsConstructionSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
|
||||
// input.c
|
||||
void PE_UpdateInputState(int passed_time);
|
||||
void PE_SetClickable(int parts_no, bool clickable);
|
||||
bool PE_GetPartsClickable(int parts_no);
|
||||
void PE_SetPartsGroupDecideOnCursor(int GroupNumber, bool DecideOnCursor);
|
||||
void PE_SetPartsGroupDecideClick(int GroupNumber, bool DecideClick);
|
||||
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);
|
||||
int PE_GetOnCursorShowLinkPartsNumber(int parts_no);
|
||||
bool PE_SetPartsOnCursorSoundNumber(int parts_no, int sound_no);
|
||||
bool PE_SetPartsClickSoundNumber(int parts_no, int sound_no);
|
||||
bool PE_SetClickMissSoundNumber(int sound_no);
|
||||
@@ -152,21 +167,41 @@ void PE_AddMotionAlpha(int parts_no, int begin_a, int end_a, int begin_t, int en
|
||||
void PE_AddMotionAlpha_curve(int parts_no, int begin_a, int end_a, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionCG_by_index(int parts_no, int begin_cg_no, int nr_cg, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate(int parts_no, int begin_numerator, int begin_denominator,
|
||||
int end_numerator, int end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionVGaugeRate(int parts_no, int begin_numerator, int begin_denominator,
|
||||
int end_numerator, int end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate_curve(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionVGaugeRate(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionVGaugeRate_curve(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionNumeralNumber(int parts_no, int begin_n, int end_n, int begin_t, int end_t);
|
||||
void PE_AddMotionNumeralNumber_curve(int parts_no, int begin_n, int end_n, int begin_t,
|
||||
int end_t, struct string *curve_name);
|
||||
void PE_AddMotionMagX(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionMagX_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionMagY(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionMagY_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateX(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateX_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateY(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateY_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateZ(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateZ_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionVibrationSize(int parts_no, int begin_w, int begin_h, int begin_t, int end_t);
|
||||
void PE_AddMotionSound(int sound_no, int begin_t);
|
||||
void PE_AddWholeMotionVibrationSize(int begin_w, int begin_h, int begin_t, int end_t);
|
||||
void PE_BeginMotion(void);
|
||||
void PE_EndMotion(void);
|
||||
void PE_SetMotionTime(int t);
|
||||
void PE_SeekEndMotion(void);
|
||||
void PE_UpdateMotionTime(int time, bool skip);
|
||||
bool PE_IsMotion(void);
|
||||
int PE_GetMotionEndTime(void);
|
||||
@@ -185,4 +220,16 @@ bool PE_SetPartsFontEdgeWeight(int parts_no, float edge_weight, int state);
|
||||
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_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);
|
||||
bool PE_BackPartsFlashBeginFrame(int parts_no, int state);
|
||||
bool PE_StepPartsFlashFinalFrame(int parts_no, int state);
|
||||
bool PE_SetPartsFlashAndStop(int parts_no, struct string *flash_filename, int state);
|
||||
bool PE_StopPartsFlash(int parts_no, int state);
|
||||
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);
|
||||
|
||||
#endif /* SYSTEM4_PARTS_H */
|
||||
|
||||
+6
-1
@@ -17,12 +17,17 @@
|
||||
#ifndef SYSTEM4_PLUGIN_H
|
||||
#define SYSTEM4_PLUGIN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct sact_sprite;
|
||||
|
||||
struct draw_plugin {
|
||||
const char *name;
|
||||
void (*free)(struct draw_plugin *);
|
||||
void (*update)(struct sact_sprite *);
|
||||
void (*debug_print)(struct sact_sprite *, int indent);
|
||||
void (*render)(struct sact_sprite *);
|
||||
cJSON *(*to_json)(struct sact_sprite *, bool);
|
||||
};
|
||||
|
||||
#endif /* SYSTEM4_PLUGIN_H */
|
||||
|
||||
+37
-3
@@ -25,8 +25,14 @@
|
||||
#include "gfx/gfx.h"
|
||||
#include "plugin.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct hash_table;
|
||||
|
||||
enum RE_plugin_version {
|
||||
RE_REIGN_PLUGIN, // Toushin Toshi 3
|
||||
RE_TAPIR_PLUGIN, // Rance Quest
|
||||
};
|
||||
|
||||
enum RE_instance_type {
|
||||
RE_ITYPE_UNINITIALIZED = 0,
|
||||
RE_ITYPE_STATIC = 1,
|
||||
@@ -35,6 +41,7 @@ enum RE_instance_type {
|
||||
RE_ITYPE_DIRECTIONAL_LIGHT = 4,
|
||||
RE_ITYPE_SPECULAR_LIGHT = 6,
|
||||
RE_ITYPE_PARTICLE_EFFECT = 8,
|
||||
RE_ITYPE_PATH_LINE = 9,
|
||||
};
|
||||
|
||||
enum RE_motion_state {
|
||||
@@ -55,6 +62,17 @@ enum RE_draw_type {
|
||||
RE_DRAW_TYPE_MAX = RE_DRAW_TYPE_ADDITIVE
|
||||
};
|
||||
|
||||
enum RE_draw_options {
|
||||
RE_DRAW_OPTION_EDGE = 0,
|
||||
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;
|
||||
@@ -126,6 +144,10 @@ struct RE_plugin {
|
||||
float post_effect_filter_y;
|
||||
float post_effect_filter_cb;
|
||||
float post_effect_filter_cr;
|
||||
int draw_options[RE_DRAW_OPTION_MAX];
|
||||
|
||||
// TapirEngine
|
||||
bool suspended;
|
||||
};
|
||||
|
||||
struct RE_instance {
|
||||
@@ -168,17 +190,22 @@ struct RE_instance {
|
||||
bool local_transform_needs_update;
|
||||
mat4 local_transform;
|
||||
mat3 normal_transform;
|
||||
mat4 *bone_transforms; // model->nr_bones elements
|
||||
mat3x4 *bone_transforms; // row-major, model->nr_bones elements
|
||||
GLuint bone_transforms_ubo;
|
||||
vec4 bounding_sphere;
|
||||
struct RE_instance *shadow_volume_instance;
|
||||
float z_from_camera;
|
||||
int texture_animation_index;
|
||||
};
|
||||
|
||||
struct RE_plugin *RE_plugin_new(void);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
@@ -188,11 +215,18 @@ bool RE_instance_load(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);
|
||||
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);
|
||||
float RE_instance_calc_2d_detection_height(struct RE_instance *instance, float x, float z);
|
||||
bool RE_instance_calc_2d_detection(struct RE_instance *instance, float x0, float y0, float z0, float x1, float y1, float z1, float *x2, float *y2, float *z2, float radius);
|
||||
bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *instance, bool draw);
|
||||
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);
|
||||
|
||||
int RE_motion_get_state(struct motion *motion);
|
||||
bool RE_motion_set_state(struct motion *motion, int state);
|
||||
@@ -270,6 +304,6 @@ bool RE_back_cg_set(struct RE_back_cg *bcg, int no);
|
||||
bool RE_back_cg_set_name(struct RE_back_cg *bcg, struct string *name, struct archive *aar);
|
||||
|
||||
void RE_render(struct sact_sprite *sp);
|
||||
void RE_debug_print(struct sact_sprite *sp, int indent);
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose);
|
||||
|
||||
#endif /* SYSTEM4_REIGN_H */
|
||||
|
||||
+21
-2
@@ -20,11 +20,19 @@
|
||||
#include <stdbool.h>
|
||||
#include "sprite.h"
|
||||
|
||||
enum sprite_engine_type {
|
||||
UNINITIALIZED_SPRITE_ENGINE,
|
||||
SACT2_SPRITE_ENGINE,
|
||||
SACTDX_SPRITE_ENGINE,
|
||||
STOAT_SPRITE_ENGINE,
|
||||
CHIPMUNK_SPRITE_ENGINE,
|
||||
};
|
||||
|
||||
struct sact_sprite *sact_get_sprite(int sp);
|
||||
struct sact_sprite *sact_try_get_sprite(int sp);
|
||||
struct sact_sprite *sact_create_sprite(int sp_no, int width, int height, int r, int g, int b, int a);
|
||||
void sact_ModuleFini(void);
|
||||
int sact_Init(void *_, int cg_cache_size);
|
||||
int sact_init(int cg_cache_size, enum sprite_engine_type engine);
|
||||
#define sact_SetWP scene_set_wp
|
||||
#define sact_SetWP_Color scene_set_wp_color
|
||||
int sact_GetScreenWidth(void);
|
||||
@@ -42,6 +50,7 @@ int sact_SP_SetCGFromFile(int sp, struct string *filename);
|
||||
int sact_SP_SaveCG(int sp, struct string *filename);
|
||||
int sact_SP_Create(int sp, int width, int height, int r, int g, int b, int a);
|
||||
int sact_SP_CreatePixelOnly(int sp, int width, int height);
|
||||
int sact_SP_CreateCustom(int sp);
|
||||
int sact_SP_Delete(int sp);
|
||||
int sact_SP_DeleteAll(void);
|
||||
int sact_SP_SetPos(int sp_no, int x, int y);
|
||||
@@ -50,7 +59,7 @@ int sact_SP_SetY(int sp_no, int y);
|
||||
int sact_SP_SetZ(int sp, int z);
|
||||
int sact_SP_GetBlendRate(int sp_no);
|
||||
int sact_SP_SetBlendRate(int sp_no, int rate);
|
||||
int sact_SP_SetShow(int sp_no, bool show);
|
||||
int sact_SP_SetShow(int sp_no, int show);
|
||||
int sact_SP_SetDrawMethod(int sp_no, int method);
|
||||
int sact_SP_GetDrawMethod(int sp_no);
|
||||
int sact_SP_IsUsing(int sp_no);
|
||||
@@ -82,6 +91,8 @@ int sact_SP_IsPtInRect(int sp_no, int x, int y);
|
||||
int sact_CG_GetMetrics(int cg_no, struct page **page);
|
||||
int sact_SP_GetAMapValue(int sp_no, int x, int y);
|
||||
int sact_SP_GetPixelValue(int sp_no, int x, int y, int *r, int *g, int *b);
|
||||
int sact_SP_SetBrightness(int sp_no, int brightness);
|
||||
int sact_SP_GetBrightness(int sp_no);
|
||||
int sact_GAME_MSG_GetNumOf(void);
|
||||
void sact_IntToZenkaku(struct string **s, int value, int figures, int zero_pad);
|
||||
void sact_IntToHankaku(struct string **s, int value, int figures, int zero_pad);
|
||||
@@ -92,6 +103,7 @@ int sact_Joypad_IsKeyDown(int num, int key);
|
||||
bool sact_Joypad_GetAnalogStickStatus(int num, int type, float *degree, float *power);
|
||||
bool sact_Joypad_GetDigitalStickStatus(int num, int type, bool *left, bool *right, bool *up, bool *down);
|
||||
int sact_Key_ClearFlag(void);
|
||||
void sact_Key_ClearFlagNoCtrl(void);
|
||||
int sact_Key_IsDown(int keycode);
|
||||
int sact_CG_IsExist(int cg_no);
|
||||
void sact_System_GetDate(int *year, int *month, int *mday, int *wday);
|
||||
@@ -100,6 +112,10 @@ void sact_CG_BlendAMapBin(int dst, int dx, int dy, int src, int sx, int sy, int
|
||||
int sact_TRANS_Begin(int type);
|
||||
int sact_TRANS_Update(float rate);
|
||||
int sact_TRANS_End(void);
|
||||
bool sact_VIEW_SetMode(int mode);
|
||||
int sact_VIEW_GetMode(void);
|
||||
bool sact_DX_GetUsePower2Texture(void);
|
||||
void sact_DX_SetUsePower2Texture(bool use);
|
||||
|
||||
extern struct text_style text_sprite_ts;
|
||||
bool StoatSpriteEngine_SP_SetTextSprite(int sp_no, struct string *text);
|
||||
@@ -110,6 +126,9 @@ void StoatSpriteEngine_SP_SetTextSpriteBoldWeight(float weight);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteEdgeWeight(float weight);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteEdgeColor(int r, int g, int b);
|
||||
bool StoatSpriteEngine_SP_SetDashTextSprite(int sp_no, int width, int height);
|
||||
void StoatSpriteEngine_FPS_SetShow(bool show);
|
||||
bool StoatSpriteEngine_FPS_GetShow(void);
|
||||
float StoatSpriteEngine_FPS_Get(void);
|
||||
//int StoatSpriteEngine_SP_SetBrightness(int sp_no, int brightness);
|
||||
//int StoatSpriteEngine_SP_GetBrightness(int sp_no);
|
||||
int StoatSpriteEngine_SYSTEM_IsResetOnce(void);
|
||||
|
||||
+2
-5
@@ -20,14 +20,11 @@
|
||||
#include "cJSON.h"
|
||||
#include "vm.h"
|
||||
|
||||
cJSON *page_to_json(struct page *page);
|
||||
cJSON *vm_value_to_json(enum ain_data_type type, union vm_value val);
|
||||
void json_load_page(struct page *page, cJSON *vars, bool call_dtors);
|
||||
union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type struct_type, int array_rank, cJSON *json);
|
||||
|
||||
int save_json(const char *filename, cJSON *json);
|
||||
int save_globals(const char *keyname, const char *filename);
|
||||
int save_group(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
cJSON *load_json(const char *filename);
|
||||
int save_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int load_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int delete_save_file(const char *filename);
|
||||
|
||||
|
||||
+6
-1
@@ -20,6 +20,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "queue.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct texture;
|
||||
|
||||
struct sprite {
|
||||
@@ -37,10 +38,12 @@ struct sprite {
|
||||
// the current scene. A sprite should be in the scene if there is pixel
|
||||
// or text data attached to it and it is not hidden.
|
||||
bool in_scene;
|
||||
// Unique ID
|
||||
int id;
|
||||
// The rendering function.
|
||||
void (*render)(struct sprite*);
|
||||
// Debug printing function
|
||||
void (*debug_print)(struct sprite*);
|
||||
cJSON *(*to_json)(struct sprite*, bool);
|
||||
};
|
||||
|
||||
extern bool scene_is_dirty;
|
||||
@@ -55,6 +58,8 @@ void scene_set_sprite_z(struct sprite *sp, int z);
|
||||
void scene_set_sprite_z2(struct sprite *sp, int z, int z2);
|
||||
|
||||
void scene_print(void);
|
||||
cJSON *scene_to_json(bool verbose);
|
||||
struct sprite *scene_get(int id);
|
||||
|
||||
static inline void scene_sprite_dirty(struct sprite *sp)
|
||||
{
|
||||
|
||||
+29
-4
@@ -35,8 +35,16 @@ struct sact_sprite {
|
||||
struct texture texture;
|
||||
// If no CG is attached to the sprite, the solid color to fill with.
|
||||
SDL_Color color;
|
||||
// Shader params
|
||||
int blend_rate;
|
||||
SDL_Color multiply_color;
|
||||
SDL_Color add_color;
|
||||
// Draw method (blend mode)
|
||||
enum draw_method draw_method;
|
||||
// The position and dimensions of the sprite.
|
||||
Rectangle rect;
|
||||
// The visible area of the sprite
|
||||
Rectangle surface_area;
|
||||
// Arbitrary text can be attached to the sprite. This is rendered on a
|
||||
// separate texture and overlayed.
|
||||
struct {
|
||||
@@ -46,15 +54,29 @@ struct sact_sprite {
|
||||
Point pos;
|
||||
int char_space;
|
||||
int line_space;
|
||||
int current_line_height;
|
||||
} text;
|
||||
// The sprite handle
|
||||
int no;
|
||||
// The CG number attached to the sprite.
|
||||
int cg_no;
|
||||
// The sprite is suspended. The exact meaning of this is unknown.
|
||||
bool suspended;
|
||||
// (optional) Draw plugin bound to this sprite.
|
||||
struct draw_plugin *plugin;
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX: There are two different sprite renderers:
|
||||
* * SACT2 - blend rate and brightness (stored in multiply_color field)
|
||||
* * ChipmunkSpriteEngine - blend rate, add color, multiply color, surface area
|
||||
* (uses parts shader)
|
||||
*
|
||||
* The initialization function determines which renderer is used.
|
||||
*/
|
||||
void sprite_init_sact(void);
|
||||
void sprite_init_chipmunk(void);
|
||||
|
||||
static inline void sprite_dirty(struct sact_sprite *sp)
|
||||
{
|
||||
scene_sprite_dirty(&sp->sp);
|
||||
@@ -69,14 +91,19 @@ int sprite_set_cg_2x_from_asset(struct sact_sprite *sp, int cg_no);
|
||||
int sprite_set_cg_by_name(struct sact_sprite *sp, const char *name);
|
||||
int sprite_set_cg_from_file(struct sact_sprite *sp, const char *path);
|
||||
int sprite_save_cg(struct sact_sprite *sp, const char *path);
|
||||
void sprite_init(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a);
|
||||
void sprite_init(struct sact_sprite *sp);
|
||||
void sprite_init_color(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a);
|
||||
void sprite_init_custom(struct sact_sprite *sp);
|
||||
void sprite_set_pos(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_set_x(struct sact_sprite *sp, int x);
|
||||
void sprite_set_y(struct sact_sprite *sp, int y);
|
||||
static inline void sprite_set_z(struct sact_sprite *sp, int z) { scene_set_sprite_z(&sp->sp, z); }
|
||||
static inline void sprite_set_z2(struct sact_sprite *sp, int z, int z2) { scene_set_sprite_z2(&sp->sp, z, z2); }
|
||||
int sprite_get_blend_rate(struct sact_sprite *sp);
|
||||
static inline int sprite_get_blend_rate(struct sact_sprite *sp) { return sp->blend_rate; }
|
||||
void sprite_set_blend_rate(struct sact_sprite *sp, int rate);
|
||||
void sprite_set_multiply_color(struct sact_sprite *sp, int r, int g, int b);
|
||||
void sprite_set_add_color(struct sact_sprite *sp, int r, int g, int b);
|
||||
void sprite_set_surface_area(struct sact_sprite *sp, int x, int y, int w, int h);
|
||||
static inline void sprite_set_show(struct sact_sprite *sp, bool show) { scene_set_sprite_show(&sp->sp, show); }
|
||||
int sprite_set_draw_method(struct sact_sprite *sp, enum draw_method method);
|
||||
enum draw_method sprite_get_draw_method(struct sact_sprite *sp);
|
||||
@@ -110,6 +137,4 @@ void sprite_get_pixel_value(struct sact_sprite *sp, int x, int y, int *r, int *g
|
||||
void sprite_bind_plugin(struct sact_sprite *sp, struct draw_plugin *plugin);
|
||||
void sprite_call_plugins(void);
|
||||
|
||||
void sprite_print(struct sact_sprite *sp);
|
||||
|
||||
#endif /* SYSTEM4_SPRITE_H */
|
||||
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
/* Copyright (C) 2023 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/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_SWF_H
|
||||
#define SYSTEM4_SWF_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef int32_t fixed32; // 32-bit 16.16 fixed-point number
|
||||
typedef int16_t fixed16; // 16-bit 8.8 fixed-point number
|
||||
typedef uint16_t fixedu16;
|
||||
typedef uint32_t rgb_t; // 0x00BBGGRR
|
||||
typedef uint32_t rgba_t; // 0xAABBGGRR
|
||||
|
||||
static inline float twips_to_float(int32_t twips) {
|
||||
return twips / 20.0f;
|
||||
}
|
||||
|
||||
static inline float fixed32_to_float(fixed32 f32) {
|
||||
return f32 / 65536.0f;
|
||||
}
|
||||
|
||||
static inline float fixed16_to_float(fixed16 f16) {
|
||||
return f16 / 256.0f;
|
||||
}
|
||||
|
||||
static inline fixed16 fixed16_mul(fixed16 a, fixed16 b) {
|
||||
return (int32_t)a * (int32_t)b / 256;
|
||||
}
|
||||
|
||||
struct swf_rect {
|
||||
// in twips (1/20 pixel)
|
||||
int32_t x_min;
|
||||
int32_t x_max;
|
||||
int32_t y_min;
|
||||
int32_t y_max;
|
||||
};
|
||||
|
||||
struct swf {
|
||||
uint8_t version;
|
||||
uint32_t file_length;
|
||||
struct swf_rect frame_size;
|
||||
fixedu16 frame_rate;
|
||||
uint16_t frame_count;
|
||||
struct swf_tag *tags;
|
||||
};
|
||||
|
||||
struct swf_matrix {
|
||||
fixed32 scale_x;
|
||||
fixed32 scale_y;
|
||||
fixed32 rotate_skew_0;
|
||||
fixed32 rotate_skew_1;
|
||||
int32_t translate_x;
|
||||
int32_t translate_y;
|
||||
};
|
||||
|
||||
struct swf_cxform_with_alpha {
|
||||
int16_t add_terms[4]; // red, green, blue, alpha
|
||||
fixed16 mult_terms[4]; // red, green, blue, alpha
|
||||
};
|
||||
|
||||
// We only support clipped bitmap fill.
|
||||
struct swf_fill_style {
|
||||
uint16_t bitmap_id;
|
||||
bool smoothed;
|
||||
struct swf_matrix matrix;
|
||||
};
|
||||
|
||||
struct swf_sound_spec {
|
||||
uint16_t rate;
|
||||
uint8_t sample_size; // 8 or 16 (bits)
|
||||
uint8_t channels;
|
||||
};
|
||||
|
||||
// Tags
|
||||
|
||||
enum swf_tag_type {
|
||||
TAG_END = 0,
|
||||
TAG_SHOW_FRAME = 1,
|
||||
TAG_DEFINE_SHAPE = 2,
|
||||
TAG_SET_BACKGROUND_COLOR = 9,
|
||||
TAG_DO_ACTION = 12,
|
||||
TAG_DEFINE_SOUND = 14,
|
||||
TAG_START_SOUND = 15,
|
||||
TAG_DEFINE_BITS_LOSSLESS = 20,
|
||||
TAG_PLACE_OBJECT2 = 26,
|
||||
TAG_REMOVE_OBJECT2 = 28,
|
||||
TAG_DEFINE_BITS_LOSSLESS2 = 36,
|
||||
TAG_DEFINE_SPRITE = 39,
|
||||
TAG_SOUND_STREAM_HEAD2 = 45,
|
||||
TAG_FILE_ATTRIBUTES = 69,
|
||||
TAG_PLACE_OBJECT3 = 70,
|
||||
};
|
||||
|
||||
struct swf_tag {
|
||||
int type;
|
||||
struct swf_tag *next;
|
||||
};
|
||||
|
||||
struct swf_tag_define_bits_lossless {
|
||||
struct swf_tag t; // TAG_DEFINE_BITS_LOSSLESS or TAG_DEFINE_BITS_LOSSLESS2
|
||||
uint16_t character_id;
|
||||
uint8_t format;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
// We only support simple rectangle shapes with a fill style.
|
||||
struct swf_tag_define_shape {
|
||||
struct swf_tag t;
|
||||
uint16_t shape_id;
|
||||
struct swf_rect bounds;
|
||||
struct swf_fill_style fill_style;
|
||||
};
|
||||
|
||||
enum swf_sound_format {
|
||||
SWF_SOUND_UNCOMPRESSED_LE = 3,
|
||||
};
|
||||
|
||||
struct swf_tag_define_sound {
|
||||
struct swf_tag t;
|
||||
uint16_t sound_id;
|
||||
uint8_t format;
|
||||
struct swf_sound_spec spec;
|
||||
uint32_t sample_count;
|
||||
uint8_t *data;
|
||||
uint32_t data_len;
|
||||
};
|
||||
|
||||
struct swf_tag_define_sprite {
|
||||
struct swf_tag t;
|
||||
uint16_t sprite_id;
|
||||
uint16_t frame_count;
|
||||
struct swf_tag *tags;
|
||||
};
|
||||
|
||||
struct swf_tag_do_action {
|
||||
struct swf_tag t;
|
||||
struct swf_action *actions;
|
||||
};
|
||||
|
||||
enum place_object_flags {
|
||||
PLACE_FLAG_RESERVED_BITS = 0xffff - ((1 << 11) - 1),
|
||||
PLACE_FLAG_HAS_CACHE_AS_BITMAP = 1 << 10,
|
||||
PLACE_FLAG_HAS_BLEND_MODE = 1 << 9,
|
||||
PLACE_FLAG_HAS_FILTER_LIST = 1 << 8,
|
||||
PLACE_FLAG_HAS_CLIP_ACTIONS = 1 << 7,
|
||||
PLACE_FLAG_HAS_CLIP_DEPTH = 1 << 6,
|
||||
PLACE_FLAG_HAS_NAME = 1 << 5,
|
||||
PLACE_FLAG_HAS_RATIO = 1 << 4,
|
||||
PLACE_FLAG_HAS_COLOR_TRANSFORM = 1 << 3,
|
||||
PLACE_FLAG_HAS_MATRIX = 1 << 2,
|
||||
PLACE_FLAG_HAS_CHARACTER = 1 << 1,
|
||||
PLACE_FLAG_MOVE = 1 << 0,
|
||||
};
|
||||
|
||||
struct swf_tag_place_object {
|
||||
struct swf_tag t; // TAG_PLACE_OBJECT2 or TAG_PLACE_OBJECT3
|
||||
uint16_t flags;
|
||||
uint16_t depth;
|
||||
uint16_t character_id;
|
||||
struct swf_matrix matrix;
|
||||
struct swf_cxform_with_alpha color_transform;
|
||||
uint16_t ratio;
|
||||
char *name;
|
||||
uint8_t blend_mode;
|
||||
uint8_t bitmap_cache;
|
||||
};
|
||||
|
||||
struct swf_tag_remove_object2 {
|
||||
struct swf_tag t;
|
||||
uint16_t depth;
|
||||
};
|
||||
|
||||
struct swf_tag_set_background_color {
|
||||
struct swf_tag t;
|
||||
rgb_t background_color;
|
||||
};
|
||||
|
||||
struct swf_tag_sound_stream_head {
|
||||
struct swf_tag t;
|
||||
struct swf_sound_spec playback_spec;
|
||||
uint8_t compression;
|
||||
struct swf_sound_spec stream_spec;
|
||||
uint16_t sample_count;
|
||||
int16_t latency_seek;
|
||||
};
|
||||
|
||||
enum start_sound_flags {
|
||||
SOUND_INFO_RESERVED_BITS = 0xff - ((1 << 6) - 1),
|
||||
SOUND_INFO_SYNC_STOP = 1 << 5,
|
||||
SOUND_INFO_SYNC_NO_MULTIPLE = 1 << 4,
|
||||
SOUND_INFO_HAS_ENVELOPE = 1 << 3,
|
||||
SOUND_INFO_HAS_LOOPS = 1 << 2,
|
||||
SOUND_INFO_HAS_OUT_POINT = 1 << 1,
|
||||
SOUND_INFO_HAS_IN_POINT = 1 << 0,
|
||||
};
|
||||
|
||||
struct swf_tag_start_sound {
|
||||
struct swf_tag t;
|
||||
uint16_t sound_id;
|
||||
uint8_t flags;
|
||||
uint32_t in_point;
|
||||
uint32_t out_point;
|
||||
uint16_t loop_count;
|
||||
};
|
||||
|
||||
// Actions
|
||||
|
||||
enum swf_action_type {
|
||||
// Actions without payload.
|
||||
ACTION_END = 0x00,
|
||||
ACTION_PLAY = 0x06,
|
||||
ACTION_STOP = 0x07,
|
||||
ACTION_POP = 0x17,
|
||||
ACTION_CALL_FUNCTION = 0x3d,
|
||||
|
||||
// Actions with payloads.
|
||||
ACTION_GOTO_FRAME = 0x81,
|
||||
ACTION_CONSTANT_POOL = 0x88,
|
||||
ACTION_PUSH = 0x96,
|
||||
};
|
||||
|
||||
struct swf_action {
|
||||
uint8_t code;
|
||||
struct swf_action *next;
|
||||
};
|
||||
|
||||
struct swf_action_constant_pool {
|
||||
struct swf_action a;
|
||||
uint16_t count;
|
||||
char *constant_pool[];
|
||||
};
|
||||
|
||||
struct swf_action_goto_frame {
|
||||
struct swf_action a;
|
||||
uint16_t frame;
|
||||
};
|
||||
|
||||
enum swf_action_push_type {
|
||||
ACTION_PUSH_INTEGER = 7,
|
||||
ACTION_PUSH_CONSTANT8 = 8,
|
||||
};
|
||||
|
||||
struct swf_action_push {
|
||||
struct swf_action a;
|
||||
uint16_t count;
|
||||
struct {
|
||||
enum swf_action_push_type type;
|
||||
uint32_t value;
|
||||
} values[];
|
||||
};
|
||||
|
||||
struct swf *swf_load(uint8_t *data, size_t size);
|
||||
void swf_free(struct swf *swf);
|
||||
struct swf *aff_load(uint8_t *data, size_t size);
|
||||
|
||||
#endif /* SYSTEM4_SWF_H */
|
||||
+25
-2
@@ -26,6 +26,11 @@ enum ain_data_type;
|
||||
struct page;
|
||||
struct string;
|
||||
|
||||
// xsystem4-specific system calls (used for hacks)
|
||||
enum vm_extra_syscall {
|
||||
VM_XSYS_KEY_IS_DOWN = 0x80000000,
|
||||
};
|
||||
|
||||
// Non-heap values. Stored in pages and on the stack.
|
||||
union vm_value {
|
||||
int32_t i;
|
||||
@@ -106,6 +111,7 @@ union vm_value vm_copy(union vm_value v, enum ain_data_type type);
|
||||
int vm_execute_ain(struct ain *program);
|
||||
void vm_call(int fno, int struct_page);
|
||||
int vm_time(void);
|
||||
void vm_sleep(int ms);
|
||||
|
||||
void hll_call(int libno, int fno);
|
||||
bool library_exists(int libno);
|
||||
@@ -113,17 +119,34 @@ bool library_function_exists(int libno, int fno);
|
||||
void init_libraries(void);
|
||||
void exit_libraries(void);
|
||||
|
||||
struct string *string_format(struct string *fmt, union vm_value arg, enum ain_data_type type);
|
||||
// NOTE: This can probably be merged with ain_data_type, since the values are
|
||||
// disjoint.
|
||||
enum string_format_type {
|
||||
STRFMT_INT = 2,
|
||||
STRFMT_FLOAT = 3,
|
||||
STRFMT_STRING = 4,
|
||||
STRFMT_BOOL = 48,
|
||||
STRFMT_LONG_INT = 56,
|
||||
};
|
||||
struct string *string_format(struct string *fmt, union vm_value arg, enum string_format_type type);
|
||||
|
||||
void vm_stack_trace(void);
|
||||
_Noreturn void _vm_error(const char *fmt, ...);
|
||||
_Noreturn void vm_exit(int code);
|
||||
_Noreturn void vm_reset(void);
|
||||
|
||||
extern bool vm_reset_once;
|
||||
|
||||
#if defined(__ANDROID__) || defined(RELEASE)
|
||||
// Report the error with a message box and exit.
|
||||
#define VM_ERROR ERROR
|
||||
#else
|
||||
#define VM_ERROR(fmt, ...) \
|
||||
_vm_error("*ERROR*(%s:%s:%d): " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef VM_PRIVATE
|
||||
#include "little_endian.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/ain.h"
|
||||
|
||||
struct function_call {
|
||||
|
||||
+4
-9
@@ -33,6 +33,7 @@ enum vm_pointer_type {
|
||||
// Heap-backed objects. Reference counted.
|
||||
struct vm_pointer {
|
||||
int ref;
|
||||
uint32_t seq;
|
||||
enum vm_pointer_type type;
|
||||
union {
|
||||
struct string *s;
|
||||
@@ -60,6 +61,8 @@ void heap_ref(int slot);
|
||||
void heap_unref(int slot);
|
||||
void exit_unref(int slot);
|
||||
|
||||
uint32_t heap_get_seq(int slot);
|
||||
|
||||
bool heap_index_valid(int index);
|
||||
bool page_index_valid(int index);
|
||||
bool string_index_valid(int index);
|
||||
@@ -75,17 +78,9 @@ int32_t heap_alloc_string(struct string *s);
|
||||
|
||||
void heap_describe_slot(int slot);
|
||||
|
||||
/*
|
||||
* Guarantee `headroom` free slots are available on the heap.
|
||||
*
|
||||
* XXX: this is a hack to fix an issue with HLL calls where a pointer into the
|
||||
* heap can be made invalid by a heap reallocation triggered within the
|
||||
* body of the HLL function.
|
||||
*/
|
||||
void heap_guarantee(unsigned headroom);
|
||||
|
||||
#ifdef VM_PRIVATE
|
||||
|
||||
extern uint32_t heap_next_seq;
|
||||
extern int32_t *heap_free_stack;
|
||||
extern size_t heap_free_ptr;
|
||||
|
||||
|
||||
+15
-9
@@ -50,8 +50,8 @@ enum page_type {
|
||||
* Multi-dimensional arrays are implemented as a tree of pages (meaning the
|
||||
* whole array is NOT contiguous in memory).
|
||||
*
|
||||
* Delegates: each delegate object is backed by a page storing object/function
|
||||
* pairs.
|
||||
* Delegates: each delegate object is backed by a page storing (object,
|
||||
* function, seq) triples.
|
||||
*/
|
||||
struct page {
|
||||
enum page_type type;
|
||||
@@ -66,11 +66,17 @@ struct page {
|
||||
int index;
|
||||
enum ain_data_type a_type;
|
||||
};
|
||||
// array-specific metadata
|
||||
struct {
|
||||
int struct_type;
|
||||
int rank;
|
||||
} array;
|
||||
union {
|
||||
// local-specific metadata
|
||||
struct {
|
||||
int struct_ptr;
|
||||
} local;
|
||||
// array-specific metadata
|
||||
struct {
|
||||
int struct_type;
|
||||
int rank;
|
||||
} array;
|
||||
};
|
||||
int nr_vars;
|
||||
union vm_value values[];
|
||||
};
|
||||
@@ -97,7 +103,7 @@ static inline void _page_set_var(struct page *page, int i, union vm_value v)
|
||||
|
||||
// variables
|
||||
union vm_value variable_initval(enum ain_data_type type);
|
||||
void variable_fini(union vm_value v, enum ain_data_type type);
|
||||
void variable_fini(union vm_value v, enum ain_data_type type, bool call_dtor);
|
||||
enum ain_data_type variable_type(struct page *page, int varno, int *struct_type, int *array_rank);
|
||||
void variable_set(struct page *page, int varno, enum ain_data_type type, union vm_value val);
|
||||
|
||||
@@ -139,6 +145,6 @@ struct page *delegate_append(struct page *dst, int obj, int fun);
|
||||
struct page *delegate_plusa(struct page *dst, struct page *add);
|
||||
struct page *delegate_minusa(struct page *dst, struct page *minus);
|
||||
struct page *delegate_clear(struct page *page);
|
||||
void delegate_get(struct page *page, int i, int *obj_out, int *fun_out);
|
||||
bool delegate_get(struct page *page, int i, int *obj_out, int *fun_out);
|
||||
|
||||
#endif /* SYSTEM4_PAGE_H */
|
||||
|
||||
+22
-3
@@ -20,9 +20,16 @@
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum resume_save_format {
|
||||
SAVE_FORMAT_JSON,
|
||||
SAVE_FORMAT_RSM,
|
||||
};
|
||||
|
||||
struct config {
|
||||
char *game_name;
|
||||
char *boot_name;
|
||||
char *ain_filename;
|
||||
char *vm_name;
|
||||
char *game_dir;
|
||||
char *save_dir;
|
||||
char *home_dir;
|
||||
@@ -43,6 +50,8 @@ struct config {
|
||||
bool echo;
|
||||
float text_x_scale;
|
||||
bool manual_text_x_scale;
|
||||
enum resume_save_format save_format;
|
||||
int msgskip_delay;
|
||||
};
|
||||
|
||||
extern struct config config;
|
||||
@@ -56,15 +65,24 @@ const char *display_utf0(const char *utf);
|
||||
const char *display_utf1(const char *utf);
|
||||
const char *display_utf2(const char *utf);
|
||||
|
||||
void indent_printf(int indent, const char *fmt, ...);
|
||||
void indent_message(int indent, const char *fmt, ...);
|
||||
|
||||
void log_message(const char *log, const char *fmt, ...);
|
||||
|
||||
#define UNIMPLEMENTED(fmt, ...) \
|
||||
sys_warning("unimplemented: %s" fmt "\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
bool is_absolute_path(const char *path);
|
||||
char *unix_path(const char *path);
|
||||
char *gamedir_path(const char *path);
|
||||
char *gamedir_path_icase(const char *path);
|
||||
char *savedir_path(const char *path);
|
||||
|
||||
void get_date(int *year, int *month, int *mday, int *wday);
|
||||
void get_time(int *hour, int *min, int *sec, int *ms);
|
||||
|
||||
void screenshot_save(void);
|
||||
|
||||
#ifndef XSYS4_DATA_DIR
|
||||
#define XSYS4_DATA_DIR "/usr/local/share/xsystem4"
|
||||
#endif
|
||||
@@ -75,7 +93,8 @@ extern bool game_daibanchou_en;
|
||||
extern bool game_rance02_mg;
|
||||
extern bool game_rance6_mg;
|
||||
extern bool game_rance7_mg;
|
||||
|
||||
extern bool id_indexed_afa;
|
||||
extern bool game_rance8;
|
||||
extern bool game_rance8_mg;
|
||||
extern bool game_dungeons_and_dolls;
|
||||
|
||||
#endif /* XSYSTEM4_H */
|
||||
|
||||
+26
-11
@@ -1,18 +1,33 @@
|
||||
project('system4', 'c',
|
||||
meson_version: '>= 0.59',
|
||||
default_options : ['c_std=c11'])
|
||||
add_project_arguments('-D_DEFAULT_SOURCE', language : 'c')
|
||||
add_project_arguments('-DXSYS4_DATA_DIR="' + get_option('prefix') / get_option('datadir') / 'xsystem4"', language : 'c')
|
||||
|
||||
zlib = dependency('zlib')
|
||||
if get_option('buildtype').startswith('release')
|
||||
add_project_arguments('-DRELEASE', language : 'c')
|
||||
endif
|
||||
|
||||
static_libs = false
|
||||
if host_machine.system() == 'windows'
|
||||
static_libs = true
|
||||
endif
|
||||
|
||||
zlib = dependency('zlib', static : static_libs)
|
||||
libm = meson.get_compiler('c').find_library('m', required: false)
|
||||
sdl2 = dependency('sdl2')
|
||||
ft2 = dependency('freetype2')
|
||||
ffi = dependency('libffi')
|
||||
tj = dependency('libturbojpeg')
|
||||
webp = dependency('libwebp')
|
||||
png = dependency('libpng')
|
||||
cglm = dependency('cglm', fallback : ['cglm', 'cglm_dep'])
|
||||
sndfile = dependency('sndfile')
|
||||
sdl2 = dependency('sdl2', static : static_libs)
|
||||
ft2 = dependency('freetype2', static : static_libs)
|
||||
ffi = dependency('libffi', static : static_libs)
|
||||
tj = dependency('libturbojpeg', static : static_libs)
|
||||
webp = dependency('libwebp', static : static_libs)
|
||||
png = dependency('libpng', static : static_libs)
|
||||
cglm = dependency('cglm', version : '>=0.9.2', fallback : ['cglm', 'cglm_dep'])
|
||||
sndfile = dependency('sndfile', static : static_libs)
|
||||
|
||||
avcodec = dependency('libavcodec', version : '>=59.37.100', required: false, static : static_libs)
|
||||
avformat = dependency('libavformat', required: false, static : static_libs)
|
||||
avutil = dependency('libavutil', version : '>=57.28.100', required: false, static : static_libs)
|
||||
swscale = dependency('libswscale', required: false, static : static_libs)
|
||||
|
||||
gles = dependency('glesv2', version : '>=3', required: get_option('opengles'))
|
||||
if gles.found()
|
||||
@@ -24,8 +39,8 @@ else
|
||||
gl_deps = [gl, glew]
|
||||
endif
|
||||
|
||||
chibi = dependency('chibi-scheme', required : false)
|
||||
readline = dependency('readline', required : false)
|
||||
chibi = dependency('chibi-scheme', required : false, static : static_libs)
|
||||
readline = dependency('readline', required : false, static : static_libs)
|
||||
|
||||
flex = find_program('flex')
|
||||
bison = find_program('bison')
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
Updates to the System 4 language since Evenicle
|
||||
===============================================
|
||||
|
||||
SLC's System 4 decompiler/compiler was last updated to work with Evenicle. There have been
|
||||
significant changes to the System 4 language since. What follows is a list of features which
|
||||
have been added.
|
||||
|
||||
Namespaces
|
||||
----------
|
||||
|
||||
Added in Tsumamigui 3. This is purely a compile time feature. If you look at the global
|
||||
variable and function names, you will notice lots of `::`, e.g. `time::detail::g_PrevPlayTime`.
|
||||
|
||||
Lambdas
|
||||
-------
|
||||
|
||||
Added in Heartful Maman. I'm not sure how these work, but if you look at the disassembled
|
||||
code there are now functions embedded within functions with names like `<lambda ...>`.
|
||||
|
||||
Generic Arrays
|
||||
--------------
|
||||
|
||||
Added in Ixseal. The old array types have been replaced with a new generic array type, with
|
||||
the syntax `array<type>`. The newly added types 79 and 80 were added to support this feature
|
||||
(type 79 is the regular array type, and type 80 is the reference-to-array type).
|
||||
|
||||
There also appears to be a `foreach` loop construct added which operates on the new arrays.
|
||||
I believe the newly added type 82 is some kind of iterator type which is used in these loops.
|
||||
The syntax is likely something like:
|
||||
|
||||
foreach (var : array) {
|
||||
// do somthing with var
|
||||
}
|
||||
|
||||
where `var` is a type 82.
|
||||
|
||||
Array methods are now implemented via the `Array` library rather than as dedicated bytecode
|
||||
instructions.
|
||||
|
||||
Properties (Getters/Setters)
|
||||
----------------------------
|
||||
|
||||
Added in Ixseal. Some struct/class members now have their names surrounded in angle brackets,
|
||||
e.g. `<PropertyName>`. These members have get/set methods defined for them, with names like
|
||||
`ClassName@PropertyName::get`/`ClassName@PropertyName::set`. Most likely these methods are
|
||||
automatically generated.
|
||||
|
||||
Interfaces & vtables
|
||||
--------------------
|
||||
|
||||
Added in Ixseal. If you look at the struct declarations, you will find empty structs with
|
||||
names like `IParts`. Embedded in the struct data in the ain file there is a list of interfaces
|
||||
for each struct, listing the implemented interfaces, along with indices into the struct's
|
||||
vtable corresponding to the location of the methods for that interface.
|
||||
|
||||
All structures which implement an interface have an `array<int>` as their first member with
|
||||
the name `<vtable>`. This is a table listing the virtual (interface) methods of the struct.
|
||||
|
||||
`CALLMETHOD` now takes the function index as a stack-passed argument, rather than a static
|
||||
instruction argument. The instruction argument to `CALLMETHOD` is changed to be the number of
|
||||
arguments that are passed to the method. The calling convention is as follows:
|
||||
|
||||
PUSH <struct page>
|
||||
PUSH <function index>
|
||||
PUSH <argument 0>
|
||||
...
|
||||
PUSH <argument n>
|
||||
CALLMETHOD <n>
|
||||
|
||||
This new calling convention enables calling compile-time-indeterminate methods through the
|
||||
vtable.
|
||||
|
||||
To support variables which represent an abstract *instance of an interface*, the new type 89
|
||||
was added. This is a 2-valued type in which the first value is a struct page, and the second
|
||||
value is an index into the struct's vtable corresponding to the location of the interface's
|
||||
methods. Thus, methods can be called on interface objects as follows:
|
||||
|
||||
PUSH <page> ; [A] (A is page of type-89 variable)
|
||||
PUSH <var> ; [A,B] (B is index of type-89 variable)
|
||||
REFREF ; [C,D] (C is page of object; D is index into object's vtable)
|
||||
DUP_U2 ; [C,D,C]
|
||||
PUSH 0 ; [C,D,C,0]
|
||||
REF ; [C,D,E] (E is the vtable of object C)
|
||||
SWAP ; [C,E,D]
|
||||
PUSH <n> ; [C,E,D,n]
|
||||
ADD ; [C,E,D+n]
|
||||
REF ; [C,F] (F is global index of method)
|
||||
PUSH <arg 0> ; [C,F,a0] (a0 is the first argument)
|
||||
...
|
||||
PUSH <arg n> ; [C,F,a0,...,an] (an is the last argument)
|
||||
CALLMETHOD <n> ; [stack now contains return value of method]
|
||||
|
||||
Generic Structures
|
||||
------------------
|
||||
|
||||
Added in Rance X. This is purely a compile time feature. If you look at the struct names you
|
||||
will find names like `MapPair<int, QuestMapPosition>`, where the type names inside the angled
|
||||
brackets appear again in the struct members.
|
||||
|
||||
Function Overloading
|
||||
--------------------
|
||||
|
||||
Added in Rance X. This is purely a compile time feature. If you look at the function
|
||||
signatures you will notice duplicate names with differing argument lists. In fact, there are
|
||||
even duplicate function names with *exactly the same argument list*. I'm not sure why.
|
||||
|
||||
Enums
|
||||
-----
|
||||
|
||||
Added in Rance X. Enum types have the following automatically generated static methods:
|
||||
|
||||
* `string EnumType@String(EnumType value)`
|
||||
* `EnumType EnumType::Parse(string value)`
|
||||
* `EnumType EnumType::Parse(int value)`
|
||||
* `array<EnumType> EnumType::GetList(void)`
|
||||
* `bool EnumType::IsExist(int value)`
|
||||
* `int EnumType::NumOf(void)`
|
||||
|
||||
There are 3 new types associated with enums. Types 92 and 93 are the regular enum and
|
||||
reference-to-enum types, respectively. They store an index into the enum table in the struct
|
||||
type field of their type descriptor.
|
||||
|
||||
Type 91 is only used in the return value from the EnumType::Parse methods. It's wrapped in an option
|
||||
type (new type 86).
|
||||
|
||||
Option Types
|
||||
------------
|
||||
|
||||
Added in Rance X. The new type 86 serves this function. Type 86 is a 2-valued type where the first
|
||||
value is the wrapped value, and the second value indicates sucess/failure.
|
||||
|
||||
In Rance X, a value of -1 as the second value indicates failure, and a value of 0 indicates success.
|
||||
In Evenicle 2 and later games, a value of 1 as the second value indicates failure.
|
||||
|
||||
Unclear
|
||||
-------
|
||||
|
||||
Variables in the ain file now have two names. Usually these names are the same string, but
|
||||
occasionally there are minor differences between them, and the differences don't reveal much
|
||||
about what the purpose of the second name is.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,12 +15,14 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color;
|
||||
uniform vec4 color; // .a is the discard threshold
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
frag_color = vec4(color.rgb, texel.r * color.a);
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
if (texel.r < color.a)
|
||||
discard;
|
||||
frag_color = vec4(color.rgb, texel.r);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2023 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/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform vec4 color;
|
||||
uniform vec4 color; // .a is the discard threshold
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
@@ -26,15 +26,16 @@ void main() {
|
||||
// edge pixels
|
||||
int size = int(floor(threshold));
|
||||
int cutoff = size + 1;
|
||||
float edge_weight = fract(threshold);
|
||||
float edge_weight = fract(threshold) / 4.0;
|
||||
vec2 tex_size = vec2(textureSize(tex, 0).xy);
|
||||
float t = ceil(threshold);
|
||||
|
||||
float a_out = 0.0;
|
||||
|
||||
for (int x = -cutoff; x <= cutoff; x++) {
|
||||
for (int y = -cutoff; y <= cutoff; y++) {
|
||||
float d = distance(vec2(x, y), vec2(0, 0));
|
||||
if (d > threshold)
|
||||
if (d > t)
|
||||
continue;
|
||||
float a = texture(tex, tex_coord + vec2(x, y) / tex_size).r;
|
||||
if (d > float(size))
|
||||
@@ -43,5 +44,7 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
if (a_out < color.a)
|
||||
discard;
|
||||
frag_color = vec4(color.rgb, min(a_out, 1.0));
|
||||
}
|
||||
|
||||
+17
-3
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D light_texture;
|
||||
uniform bool use_lightmap;
|
||||
uniform float alpha_mod;
|
||||
uniform bool use_fog;
|
||||
|
||||
in float dist;
|
||||
in vec2 tex_coord;
|
||||
@@ -26,7 +29,18 @@ const vec3 FOG_COLOR = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST;
|
||||
fog_factor = clamp(fog_factor, 0.3, 1.0);
|
||||
frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a * alpha_mod);
|
||||
float light_factor = 1.0;
|
||||
if (use_lightmap) {
|
||||
light_factor = texture(light_texture, tex_coord).a;
|
||||
}
|
||||
if (use_fog) {
|
||||
float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST;
|
||||
fog_factor = clamp(fog_factor, 0.3, 1.0);
|
||||
frag_color = vec4(mix(FOG_COLOR, texel.rgb * light_factor, fog_factor), texel.a * alpha_mod);
|
||||
} else {
|
||||
if (texel.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
frag_color = vec4(texel.rgb * light_factor, texel.a * alpha_mod);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform vec2 uv_offset;
|
||||
uniform vec2 uv_scale;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec2 vertex_uv;
|
||||
@@ -27,5 +29,5 @@ void main() {
|
||||
vec4 pos = view_transform * local_transform * vec4(vertex_pos, 1.0);
|
||||
dist = abs(pos.z);
|
||||
gl_Position = proj_transform * pos;
|
||||
tex_coord = vertex_uv;
|
||||
tex_coord = vertex_uv * uv_scale + uv_offset;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright (C) 2023 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/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
uniform float amp;
|
||||
uniform float t;
|
||||
|
||||
const float PI = 3.14159265;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
vec4 sample_texture(float offset_x) {
|
||||
return texture(tex, vec2(tex_coord.x + offset_x, tex_coord.y));
|
||||
}
|
||||
|
||||
void main() {
|
||||
float a = amp * cos((tex_coord.y + fract(t / 8.0)) * 16.0 * PI);
|
||||
float p1 = 6.0 * tex_coord.y + 1.5;
|
||||
float p2 = 4.0 * tex_coord.y + 2.0;
|
||||
float offset1 = cos(p1 * 2.0 * PI) * a;
|
||||
float offset2 = cos(p2 * 2.0 * PI) * a;
|
||||
|
||||
frag_color = mix(sample_texture(offset1), sample_texture(offset2), 0.5);
|
||||
}
|
||||
@@ -50,7 +50,7 @@ void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
|
||||
vec2 p1 = resolution;
|
||||
vec2 p2 = vec2((1.0 - progress*2.0) * p1.x, (1.0 - progress*2.0) * p1.y);
|
||||
vec2 p2 = vec2((1.0 - max(progress, 0.01)*2.0) * p1.x, (1.0 - max(progress, 0.01)*2.0) * p1.y);
|
||||
vec2 p3 = vec2(p2.x, resolution.y);
|
||||
vec2 p4 = vec2(resolution.x, p2.y);
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* 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;
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).rgb, texture(tex, xy).rgb, progress);
|
||||
}
|
||||
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
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);
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float alpha_mod;
|
||||
uniform float blend_rate;
|
||||
uniform vec2 bot_left;
|
||||
uniform vec2 top_right;
|
||||
uniform vec3 add_color;
|
||||
@@ -36,5 +36,5 @@ void main() {
|
||||
|
||||
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 * alpha_mod) * point_in_rect(tex_coord, bl, tr);
|
||||
frag_color = vec4(mod_color, tex_color.a * blend_rate) * point_in_rect(tex_coord, bl, tr);
|
||||
}
|
||||
|
||||
+58
-30
@@ -14,19 +14,21 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
|
||||
#define DIFFUSE_EMISSIVE 0
|
||||
#define DIFFUSE_NORMAL 1
|
||||
#define DIFFUSE_LIGHT_MAP 2
|
||||
#define DIFFUSE_ENV_MAP 3
|
||||
|
||||
#define FOG_LINEAR 1
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
#define ALPHA_BLEND 0
|
||||
#define ALPHA_TEST 1
|
||||
#define ALPHA_MAP_BLEND 2
|
||||
#define ALPHA_MAP_TEST 3
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define FOG_LINEAR 1
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
@@ -34,8 +36,46 @@ struct dir_light {
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform sampler2D tex;
|
||||
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 diffuse = vec3(0.0);
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
float half_lambert = dot(norm, -light_dir[i]) * 0.5 + 0.5;
|
||||
diffuse += mix(dir_lights[i].globe_diffuse, dir_lights[i].diffuse, half_lambert);
|
||||
}
|
||||
return diffuse;
|
||||
}
|
||||
|
||||
#else // ENGINE == REIGN_ENGINE
|
||||
|
||||
const vec3 global_ambient = vec3(0.0);
|
||||
|
||||
vec3 dir_lights_diffuse(vec3 norm)
|
||||
{
|
||||
return vec3(1.0);
|
||||
}
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D alpha_texture;
|
||||
uniform sampler2D light_texture;
|
||||
uniform sampler2D shadow_texture;
|
||||
@@ -44,33 +84,20 @@ uniform float alpha_mod;
|
||||
uniform bool use_normal_map;
|
||||
uniform sampler2D normal_texture;
|
||||
|
||||
uniform vec3 ambient;
|
||||
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 vec3 instance_ambient;
|
||||
uniform int diffuse_type;
|
||||
uniform float shadow_darkness;
|
||||
uniform float shadow_bias;
|
||||
uniform int fog_type;
|
||||
uniform float fog_near;
|
||||
uniform float fog_far;
|
||||
uniform vec3 fog_color;
|
||||
uniform int alpha_mode;
|
||||
|
||||
in vec2 tex_coord;
|
||||
in vec2 light_tex_coord;
|
||||
in vec4 color_mod;
|
||||
in vec3 frag_pos;
|
||||
in vec4 shadow_frag_pos;
|
||||
in float dist;
|
||||
in vec3 eye;
|
||||
in vec3 normal;
|
||||
in vec3 light_dir[NR_DIR_LIGHTS];
|
||||
in vec3 specular_dir;
|
||||
in vec3 ls_ex;
|
||||
in vec3 ls_in;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
@@ -83,7 +110,7 @@ void main() {
|
||||
}
|
||||
|
||||
vec3 view_dir = normalize(eye - frag_pos);
|
||||
vec3 frag_rgb = ambient;
|
||||
vec3 frag_rgb = global_ambient + instance_ambient;
|
||||
|
||||
// Diffuse lighting
|
||||
vec4 texel;
|
||||
@@ -95,18 +122,15 @@ void main() {
|
||||
if (diffuse_type == DIFFUSE_EMISSIVE) {
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
vec3 diffuse = vec3(0.0);
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
float half_lambert = dot(norm, -light_dir[i]) * 0.5 + 0.5;
|
||||
diffuse += mix(dir_lights[i].globe_diffuse, dir_lights[i].diffuse, half_lambert);
|
||||
}
|
||||
vec3 diffuse = dir_lights_diffuse(norm);
|
||||
if (diffuse_type == DIFFUSE_LIGHT_MAP) {
|
||||
diffuse *= texture(light_texture, light_tex_coord).rgb;
|
||||
}
|
||||
frag_rgb += texel.rgb * diffuse;
|
||||
frag_rgb += texel.rgb * diffuse * color_mod.rgb;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
// Specular lighting
|
||||
if (specular_strength > 0.0) {
|
||||
vec3 reflect_dir = reflect(specular_dir, norm);
|
||||
@@ -131,6 +155,7 @@ void main() {
|
||||
} else if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
frag_rgb = frag_rgb * ls_ex + ls_in;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shadow mapping
|
||||
if (shadow_darkness > 0.0) {
|
||||
@@ -150,12 +175,15 @@ void main() {
|
||||
}
|
||||
|
||||
// Alpha mapping
|
||||
float alpha = texel.a * alpha_mod;
|
||||
float alpha = texel.a * color_mod.a * alpha_mod;
|
||||
if (alpha_mode == ALPHA_TEST) {
|
||||
if (alpha < 0.8)
|
||||
if (alpha < 0.75)
|
||||
discard;
|
||||
} else if (alpha_mode == ALPHA_MAP_BLEND) {
|
||||
alpha *= texture(alpha_texture, tex_coord).r;
|
||||
} else if (alpha_mode == ALPHA_MAP_TEST) {
|
||||
if (texture(alpha_texture, tex_coord).r < 0.1)
|
||||
discard;
|
||||
}
|
||||
|
||||
frag_color = vec4(frag_rgb, alpha);
|
||||
|
||||
+47
-24
@@ -14,70 +14,89 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 211; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
uniform mat4 bone_matrices[MAX_BONES];
|
||||
|
||||
uniform bool use_normal_map;
|
||||
|
||||
uniform vec3 camera_pos;
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform vec3 specular_light_dir;
|
||||
uniform mat4 shadow_transform;
|
||||
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);
|
||||
|
||||
#else // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform vec2 uv_scroll;
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
layout(std140) uniform BoneTransforms {
|
||||
mat3x4 bone_matrices[MAX_BONES]; // transposed
|
||||
};
|
||||
|
||||
uniform bool use_normal_map;
|
||||
|
||||
uniform vec3 camera_pos;
|
||||
uniform mat4 shadow_transform;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec3 vertex_normal;
|
||||
in vec2 vertex_uv;
|
||||
in vec2 vertex_light_uv;
|
||||
in vec4 vertex_color;
|
||||
in vec4 vertex_tangent;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
out vec2 tex_coord;
|
||||
out vec2 light_tex_coord;
|
||||
out vec4 color_mod;
|
||||
out vec3 frag_pos;
|
||||
out vec4 shadow_frag_pos;
|
||||
out float dist;
|
||||
out vec3 eye;
|
||||
out vec3 normal;
|
||||
out vec3 light_dir[NR_DIR_LIGHTS];
|
||||
out vec3 specular_dir;
|
||||
out vec3 ls_ex;
|
||||
out vec3 ls_in;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
mat3 normal_bone_transform = normal_transform;
|
||||
if (has_bones) {
|
||||
mat4 bone_transform = mat4(0.f);
|
||||
mat3x4 bone_transform = mat3x4(0.0);
|
||||
for (int i = 0; i < NR_WEIGHTS; i++) {
|
||||
if (vertex_bone_index[i] >= 0) {
|
||||
bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i];
|
||||
}
|
||||
}
|
||||
local_bone_transform *= bone_transform;
|
||||
local_bone_transform *= mat4(
|
||||
vec4(bone_transform[0].x, bone_transform[1].x, bone_transform[2].x, 0.0),
|
||||
vec4(bone_transform[0].y, bone_transform[1].y, bone_transform[2].y, 0.0),
|
||||
vec4(bone_transform[0].z, bone_transform[1].z, bone_transform[2].z, 0.0),
|
||||
vec4(bone_transform[0].w, bone_transform[1].w, bone_transform[2].w, 1.0));
|
||||
normal_bone_transform *= mat3(bone_transform);
|
||||
}
|
||||
|
||||
@@ -95,8 +114,9 @@ void main() {
|
||||
vec4 view_pos = view_transform * world_pos;
|
||||
gl_Position = proj_transform * view_pos;
|
||||
|
||||
tex_coord = vertex_uv;
|
||||
light_tex_coord = vertex_light_uv;
|
||||
tex_coord = vertex_uv + uv_scroll;
|
||||
light_tex_coord = vertex_light_uv + uv_scroll;
|
||||
color_mod = vertex_color;
|
||||
dist = -view_pos.z;
|
||||
shadow_frag_pos = shadow_transform * world_pos;
|
||||
|
||||
@@ -104,6 +124,8 @@ void main() {
|
||||
// otherwise.
|
||||
frag_pos = TBN * vec3(world_pos);
|
||||
eye = TBN * camera_pos;
|
||||
|
||||
#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;
|
||||
@@ -127,4 +149,5 @@ void main() {
|
||||
ls_in = (phase_r * beta_r + phase_m * beta_m) / (beta_r + beta_m) * (1.0 - f_ex) * ls_sun_color;
|
||||
ls_ex = ls_light_color * f_ex;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Copyright (C) 2024 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/>.
|
||||
*/
|
||||
|
||||
uniform vec3 outline_color;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = vec4(outline_color, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Copyright (C) 2024 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/>.
|
||||
*/
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
layout(std140) uniform BoneTransforms {
|
||||
mat3x4 bone_matrices[MAX_BONES]; // transposed
|
||||
};
|
||||
uniform float outline_thickness;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec3 vertex_normal;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
mat3 normal_bone_transform = normal_transform;
|
||||
if (has_bones) {
|
||||
mat3x4 bone_transform = mat3x4(0.0);
|
||||
for (int i = 0; i < NR_WEIGHTS; i++) {
|
||||
if (vertex_bone_index[i] >= 0) {
|
||||
bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i];
|
||||
}
|
||||
}
|
||||
local_bone_transform *= mat4(
|
||||
vec4(bone_transform[0].x, bone_transform[1].x, bone_transform[2].x, 0.0),
|
||||
vec4(bone_transform[0].y, bone_transform[1].y, bone_transform[2].y, 0.0),
|
||||
vec4(bone_transform[0].z, bone_transform[1].z, bone_transform[2].z, 0.0),
|
||||
vec4(bone_transform[0].w, bone_transform[1].w, bone_transform[2].w, 1.0));
|
||||
normal_bone_transform *= mat3(bone_transform);
|
||||
}
|
||||
|
||||
vec4 pos = local_bone_transform * vec4(vertex_pos, 1.0);
|
||||
vec3 normal = normalize(normal_bone_transform * vertex_normal);
|
||||
|
||||
gl_Position = proj_transform * view_transform * (pos + vec4(normal, 0.0) * outline_thickness);
|
||||
}
|
||||
@@ -14,29 +14,35 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 world_transform;
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
|
||||
const int MAX_BONES = 211; // see 3d_internal.h
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
uniform mat4 bone_matrices[MAX_BONES];
|
||||
layout(std140) uniform BoneTransforms {
|
||||
mat3x4 bone_matrices[MAX_BONES]; // transposed
|
||||
};
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
void main() {
|
||||
mat4 world_bone_transform = world_transform;
|
||||
mat4 local_bone_transform = local_transform;
|
||||
if (has_bones) {
|
||||
mat4 bone_transform = mat4(0.f);
|
||||
mat3x4 bone_transform = mat3x4(0.0);
|
||||
for (int i = 0; i < NR_WEIGHTS; i++) {
|
||||
if (vertex_bone_index[i] >= 0) {
|
||||
bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i];
|
||||
}
|
||||
}
|
||||
world_bone_transform *= bone_transform;
|
||||
local_bone_transform *= mat4(
|
||||
vec4(bone_transform[0].x, bone_transform[1].x, bone_transform[2].x, 0.0),
|
||||
vec4(bone_transform[0].y, bone_transform[1].y, bone_transform[2].y, 0.0),
|
||||
vec4(bone_transform[0].z, bone_transform[1].z, bone_transform[2].z, 0.0),
|
||||
vec4(bone_transform[0].w, bone_transform[1].w, bone_transform[2].w, 1.0));
|
||||
}
|
||||
|
||||
gl_Position = view_transform * world_bone_transform * vec4(vertex_pos, 1.0);
|
||||
gl_Position = view_transform * local_bone_transform * vec4(vertex_pos, 1.0);
|
||||
}
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float alpha_mod;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
frag_color.a *= alpha_mod;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ uniform mat4 world_transform;
|
||||
uniform mat4 view_transform;
|
||||
|
||||
in vec4 vertex_pos;
|
||||
in vec2 vertex_uv;
|
||||
out vec2 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = view_transform * world_transform * vertex_pos;
|
||||
tex_coord = vertex_pos.xy;
|
||||
tex_coord = vertex_uv;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright (C) 2023 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;
|
||||
uniform float blend_rate;
|
||||
uniform vec3 multiply_color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
frag_color.rgb *= multiply_color;
|
||||
frag_color.a *= blend_rate;
|
||||
}
|
||||
+87
-14
@@ -26,7 +26,7 @@
|
||||
#include "reign.h"
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define MAX_BONES 211 // Maximum in TT3
|
||||
#define MAX_BONES 308 // Maximum in Rance Quest
|
||||
|
||||
typedef struct vec3_range {
|
||||
vec3 begin;
|
||||
@@ -44,24 +44,32 @@ struct model {
|
||||
struct hash_table *bone_map; // bone id in POL/MOT -> struct bone *
|
||||
struct hash_table *bone_name_map; // bone name -> (struct bone * | NULL)
|
||||
struct hash_table *mot_cache; // name -> struct mot *
|
||||
struct collider *collider;
|
||||
vec3 aabb[2]; // axis-aligned bounding box
|
||||
bool has_transparent_material;
|
||||
bool has_transparent_mesh;
|
||||
};
|
||||
|
||||
struct mesh {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
bool hidden;
|
||||
GLuint vao;
|
||||
GLuint attr_buffer;
|
||||
GLuint index_buffer;
|
||||
int nr_vertices;
|
||||
int nr_indices;
|
||||
int material;
|
||||
vec3 outline_color;
|
||||
float outline_thickness;
|
||||
vec2 uv_scroll;
|
||||
};
|
||||
|
||||
struct material {
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
GLuint color_map;
|
||||
int nr_color_maps;
|
||||
GLuint *color_maps;
|
||||
GLuint specular_map;
|
||||
GLuint alpha_map;
|
||||
GLuint light_map;
|
||||
@@ -87,6 +95,7 @@ struct motion {
|
||||
float current_frame;
|
||||
float frame_begin, frame_end;
|
||||
float loop_frame_begin, loop_frame_end;
|
||||
int texture_index;
|
||||
};
|
||||
|
||||
// Attribute variable locations
|
||||
@@ -97,6 +106,7 @@ enum RE_attribute_location {
|
||||
VATTR_BONE_INDEX,
|
||||
VATTR_BONE_WEIGHT,
|
||||
VATTR_LIGHT_UV,
|
||||
VATTR_COLOR,
|
||||
VATTR_TANGENT,
|
||||
};
|
||||
|
||||
@@ -106,19 +116,33 @@ struct shadow_renderer {
|
||||
GLuint texture;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint world_transform;
|
||||
GLint local_transform;
|
||||
GLint view_transform;
|
||||
GLint has_bones;
|
||||
GLint bone_matrices;
|
||||
};
|
||||
|
||||
struct outline_renderer {
|
||||
GLuint program;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint local_transform;
|
||||
GLint view_transform;
|
||||
GLint proj_transform;
|
||||
GLint normal_transform;
|
||||
GLint has_bones;
|
||||
GLint outline_color;
|
||||
GLint outline_thickness;
|
||||
};
|
||||
|
||||
struct RE_renderer {
|
||||
int viewport_width;
|
||||
int viewport_height;
|
||||
GLuint program;
|
||||
GLuint depth_buffer;
|
||||
struct shadow_renderer shadow;
|
||||
struct outline_renderer outline;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint world_transform;
|
||||
GLint view_transform;
|
||||
GLint texture;
|
||||
GLint local_transform;
|
||||
@@ -126,8 +150,8 @@ struct RE_renderer {
|
||||
GLint normal_transform;
|
||||
GLint alpha_mod;
|
||||
GLint has_bones;
|
||||
GLint bone_matrices;
|
||||
GLint ambient;
|
||||
GLint global_ambient;
|
||||
GLint instance_ambient;
|
||||
struct {
|
||||
GLint dir;
|
||||
GLint diffuse;
|
||||
@@ -160,10 +184,13 @@ struct RE_renderer {
|
||||
GLint ls_sun_color;
|
||||
GLint alpha_mode;
|
||||
GLint alpha_texture;
|
||||
GLint uv_scroll;
|
||||
|
||||
GLuint billboard_vao;
|
||||
GLuint billboard_attr_buffer;
|
||||
struct hash_table *billboard_textures; // cg_no -> struct billboard_texture*
|
||||
|
||||
uint32_t last_frame_timestamp;
|
||||
};
|
||||
|
||||
struct billboard_texture {
|
||||
@@ -173,6 +200,8 @@ struct billboard_texture {
|
||||
|
||||
// reign.c
|
||||
|
||||
extern enum RE_plugin_version re_plugin_version;
|
||||
|
||||
void RE_instance_update_local_transform(struct RE_instance *inst);
|
||||
|
||||
// model.c
|
||||
@@ -188,8 +217,9 @@ struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, cons
|
||||
|
||||
// renderer.c
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture);
|
||||
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);
|
||||
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);
|
||||
@@ -389,6 +419,7 @@ enum pol_texture_type {
|
||||
|
||||
enum material_flags {
|
||||
MATERIAL_SPRITE = 1 << 0,
|
||||
MATERIAL_ALPHA = 1 << 1,
|
||||
};
|
||||
|
||||
struct pol_material {
|
||||
@@ -404,11 +435,16 @@ struct pol_material_group {
|
||||
};
|
||||
|
||||
enum mesh_flags {
|
||||
MESH_NOLIGHTING = 1 << 0,
|
||||
MESH_NOMAKESHADOW = 1 << 1,
|
||||
MESH_ENVMAP = 1 << 2,
|
||||
MESH_BOTH = 1 << 3,
|
||||
MESH_SPRITE = 1 << 4,
|
||||
MESH_NOLIGHTING = 1 << 0,
|
||||
MESH_NOMAKESHADOW = 1 << 1,
|
||||
MESH_ENVMAP = 1 << 2,
|
||||
MESH_BOTH = 1 << 3,
|
||||
MESH_SPRITE = 1 << 4,
|
||||
MESH_BLEND_ADDITIVE = 1 << 5,
|
||||
MESH_NO_EDGE = 1 << 6,
|
||||
MESH_NO_HEIGHT_DETECTION = 1 << 7,
|
||||
MESH_ALPHA = 1 << 8,
|
||||
MESH_HAS_LIGHT_UV = 1 << 9,
|
||||
};
|
||||
|
||||
struct pol_mesh {
|
||||
@@ -421,8 +457,16 @@ struct pol_mesh {
|
||||
vec2 *uvs;
|
||||
uint32_t nr_light_uvs;
|
||||
vec2 *light_uvs;
|
||||
uint32_t nr_colors;
|
||||
vec3 *colors;
|
||||
uint32_t nr_alphas;
|
||||
float *alphas;
|
||||
uint32_t nr_triangles;
|
||||
struct pol_triangle *triangles;
|
||||
// Parameters in .opr file.
|
||||
Color edge_color;
|
||||
float edge_size;
|
||||
vec2 uv_scroll;
|
||||
};
|
||||
|
||||
struct pol_vertex {
|
||||
@@ -440,6 +484,8 @@ struct pol_triangle {
|
||||
uint32_t vert_index[3];
|
||||
uint32_t uv_index[3];
|
||||
uint32_t light_uv_index[3];
|
||||
uint32_t color_index[3];
|
||||
uint32_t alpha_index[3];
|
||||
vec3 normals[3];
|
||||
uint32_t material_group_index;
|
||||
};
|
||||
@@ -456,6 +502,8 @@ struct mot {
|
||||
char *name;
|
||||
uint32_t nr_frames;
|
||||
uint32_t nr_bones;
|
||||
uint32_t nr_texture_indices;
|
||||
uint32_t *texture_indices; // texture index for each frame
|
||||
struct mot_bone *motions[];
|
||||
};
|
||||
|
||||
@@ -506,4 +554,29 @@ struct amt *amt_parse(uint8_t *data, size_t size);
|
||||
void amt_free(struct amt *amt);
|
||||
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);
|
||||
|
||||
// collision.c
|
||||
|
||||
struct collider_triangle;
|
||||
struct collider_edge;
|
||||
|
||||
struct collider {
|
||||
struct collider_triangle *triangles;
|
||||
uint32_t nr_triangles;
|
||||
struct collider_edge *edges; // boundary edges
|
||||
uint32_t nr_edges;
|
||||
vec3 *path_points;
|
||||
uint32_t nr_path_points;
|
||||
};
|
||||
|
||||
struct collider *collider_create(struct pol_mesh *mesh);
|
||||
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);
|
||||
bool collider_raycast(struct collider *collider, vec3 origin, vec3 direction, vec3 out);
|
||||
bool collider_find_path(struct collider *collider, vec3 start, vec3 goal, mat4 vp_transform);
|
||||
bool collider_optimize_path(struct collider *collider);
|
||||
|
||||
#endif /* SYSTEM4_3D_3D_INTERNAL_H */
|
||||
|
||||
@@ -0,0 +1,509 @@
|
||||
/* Copyright (C) 2024 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 <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
struct collider_triangle {
|
||||
vec3 vertices[3];
|
||||
vec3 center;
|
||||
vec2 aabb[2]; // xz coordinates
|
||||
vec2 slope;
|
||||
float intercept;
|
||||
int neighbors[3];
|
||||
};
|
||||
|
||||
struct collider_edge {
|
||||
vec2 vertices[2]; // xz coordinates
|
||||
vec2 aabb[2];
|
||||
};
|
||||
|
||||
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));
|
||||
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++) {
|
||||
glm_vec3_copy(mesh->vertices[mesh->triangles[tri_i].vert_index[i]].pos, t->vertices[i]);
|
||||
glm_vec3_add(t->center, t->vertices[i], t->center);
|
||||
vec2 xz = { t->vertices[i][0], t->vertices[i][2] };
|
||||
glm_vec2_minv(xz, t->aabb[0], t->aabb[0]);
|
||||
glm_vec2_maxv(xz, t->aabb[1], t->aabb[1]);
|
||||
}
|
||||
glm_vec3_divs(t->center, 3.f, t->center);
|
||||
|
||||
// Do some precomputation so that we can calculate height(x, z) quickly.
|
||||
vec3 v1, v2, normal;
|
||||
glm_vec3_sub(t->vertices[1], t->vertices[0], v1);
|
||||
glm_vec3_sub(t->vertices[2], t->vertices[0], v2);
|
||||
glm_vec3_cross(v1, v2, normal);
|
||||
t->slope[0] = -normal[0] / normal[1];
|
||||
t->slope[1] = -normal[2] / normal[1];
|
||||
t->intercept = glm_vec3_dot(normal, t->vertices[0]) / normal[1];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
t->neighbors[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void link_neighbors(struct collider *collider, int t1_index, int t2_index)
|
||||
{
|
||||
struct collider_triangle *t1 = &collider->triangles[t1_index];
|
||||
struct collider_triangle *t2 = &collider->triangles[t2_index];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (t1->neighbors[i] == -1) {
|
||||
t1->neighbors[i] = t2_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (t2->neighbors[i] == -1) {
|
||||
t2->neighbors[i] = t1_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_edges(struct collider *collider, struct pol_mesh *mesh)
|
||||
{
|
||||
// Link neighboring triangles.
|
||||
if (mesh->nr_triangles >= 65535)
|
||||
ERROR("Too many triangles in collision mesh: %d", mesh->nr_triangles);
|
||||
const int nv = mesh->nr_vertices;
|
||||
const int table_size = nv * nv;
|
||||
uint16_t *table = xcalloc(table_size, sizeof(uint16_t));
|
||||
int nr_edges = 0;
|
||||
for (int i = 0; i < mesh->nr_triangles; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int v1 = mesh->triangles[i].vert_index[j];
|
||||
int v2 = mesh->triangles[i].vert_index[(j + 1) % 3];
|
||||
int k = v2 * nv + v1;
|
||||
if (table[k]) {
|
||||
link_neighbors(collider, table[k] - 1, i);
|
||||
table[k] = 0;
|
||||
nr_edges--;
|
||||
} else {
|
||||
table[v1 * nv + v2] = i + 1;
|
||||
nr_edges++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Collect boundary edges, i.e., edges that belong to only one triangle.
|
||||
collider->nr_edges = nr_edges;
|
||||
collider->edges = xcalloc(nr_edges, sizeof(struct collider_edge));
|
||||
struct collider_edge* edge = collider->edges;
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
if (!table[i]) continue;
|
||||
int v1 = i / nv;
|
||||
int v2 = i % nv;
|
||||
edge->vertices[0][0] = mesh->vertices[v1].pos[0];
|
||||
edge->vertices[0][1] = mesh->vertices[v1].pos[2];
|
||||
edge->vertices[1][0] = mesh->vertices[v2].pos[0];
|
||||
edge->vertices[1][1] = mesh->vertices[v2].pos[2];
|
||||
glm_vec2_minv(edge->vertices[0], edge->vertices[1], edge->aabb[0]);
|
||||
glm_vec2_maxv(edge->vertices[0], edge->vertices[1], edge->aabb[1]);
|
||||
edge++;
|
||||
}
|
||||
assert(edge == collider->edges + nr_edges);
|
||||
free(table);
|
||||
}
|
||||
|
||||
struct collider *collider_create(struct pol_mesh *mesh)
|
||||
{
|
||||
struct collider *collider = xcalloc(1, sizeof(struct collider));
|
||||
init_triangles(collider, mesh);
|
||||
init_edges(collider, mesh);
|
||||
return collider;
|
||||
}
|
||||
|
||||
void collider_free(struct collider *collider)
|
||||
{
|
||||
free(collider->triangles);
|
||||
free(collider->edges);
|
||||
free(collider->path_points);
|
||||
free(collider);
|
||||
}
|
||||
|
||||
static bool in_triangle(struct collider_triangle* t, vec2 xz)
|
||||
{
|
||||
vec2 v[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
v[i][0] = t->vertices[i][0];
|
||||
v[i][1] = t->vertices[i][2];
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vec2 a, b;
|
||||
glm_vec2_sub(v[(i + 1) % 3], v[i], a);
|
||||
glm_vec2_sub(xz, v[i], b);
|
||||
if (glm_vec2_cross(a, b) > 0.f)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct collider_triangle* find_triangle(struct collider *collider, vec2 xz)
|
||||
{
|
||||
struct collider_triangle* t = collider->triangles;
|
||||
for (; t < collider->triangles + collider->nr_triangles; t++) {
|
||||
if (!glm_aabb2d_point(t->aabb, xz))
|
||||
continue;
|
||||
if (in_triangle(t, xz))
|
||||
return t;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool collider_height(struct collider *collider, vec2 xz, float *h_out)
|
||||
{
|
||||
struct collider_triangle* t = find_triangle(collider, xz);
|
||||
if (!t)
|
||||
return false;
|
||||
*h_out = glm_vec2_dot(t->slope, xz) + t->intercept;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculates the squared distance from a point to a line segment in 2D space.
|
||||
static float distance2_point_to_segment(vec2 p, vec2 a, vec2 b, vec2 closest_point_out)
|
||||
{
|
||||
vec2 v, w;
|
||||
glm_vec2_sub(b, a, v);
|
||||
glm_vec2_sub(p, a, w);
|
||||
|
||||
float c1 = glm_vec2_dot(w, v);
|
||||
if (c1 < 0.f) {
|
||||
glm_vec2_copy(a, closest_point_out);
|
||||
return glm_vec2_distance2(p, a);
|
||||
}
|
||||
float c2 = glm_vec2_norm2(v);
|
||||
if (c2 <= c1) {
|
||||
glm_vec2_copy(b, closest_point_out);
|
||||
return glm_vec2_distance2(p, b);
|
||||
}
|
||||
float t = c1 / c2;
|
||||
glm_vec2_copy(a, closest_point_out);
|
||||
glm_vec2_muladds(v, t, closest_point_out);
|
||||
return glm_vec2_distance2(p, closest_point_out);
|
||||
}
|
||||
|
||||
static bool segments_intersect(vec2 p0, vec2 p1, vec2 q0, vec2 q1)
|
||||
{
|
||||
vec2 r, s;
|
||||
glm_vec2_sub(p1, p0, r);
|
||||
glm_vec2_sub(q1, q0, s);
|
||||
float r_cross_s = glm_vec2_cross(r, s);
|
||||
if (r_cross_s == 0.f) {
|
||||
// The segments are collinear.
|
||||
return false;
|
||||
}
|
||||
vec2 q0_p0;
|
||||
glm_vec2_sub(q0, p0, q0_p0);
|
||||
float t = glm_vec2_cross(q0_p0, s) / r_cross_s;
|
||||
float u = glm_vec2_cross(q0_p0, r) / r_cross_s;
|
||||
return (t >= 0.f && t <= 1.f && u >= 0.f && u <= 1.f);
|
||||
}
|
||||
|
||||
bool check_collision(struct collider *collider, vec2 p0, vec2 p1, float radius, vec2 out)
|
||||
{
|
||||
if (find_triangle(collider, p1)) {
|
||||
glm_vec2_copy(p1, out);
|
||||
} else {
|
||||
if (!find_triangle(collider, p0))
|
||||
return false;
|
||||
// Find a point on the "collision" mesh, using binary search.
|
||||
vec2 good, bad;
|
||||
glm_vec2_copy(p0, good);
|
||||
glm_vec2_copy(p1, bad);
|
||||
float w = glm_vec2_distance(good, bad);
|
||||
while (w > radius) {
|
||||
vec2 mid;
|
||||
glm_vec2_center(good, bad, mid);
|
||||
if (find_triangle(collider, mid)) {
|
||||
glm_vec2_copy(mid, good);
|
||||
} else {
|
||||
glm_vec2_copy(mid, bad);
|
||||
}
|
||||
w /= 2.f;
|
||||
}
|
||||
glm_vec2_copy(good, out);
|
||||
}
|
||||
|
||||
// Ensure that `out` is at least `radius` away from any boundary edge.
|
||||
vec2 aabb[2];
|
||||
glm_vec2_subs(out, radius, aabb[0]);
|
||||
glm_vec2_adds(out, radius, aabb[1]);
|
||||
for (int i = 0; i < collider->nr_edges; i++) {
|
||||
struct collider_edge *e = &collider->edges[i];
|
||||
if (!glm_aabb2d_aabb(e->aabb, aabb))
|
||||
continue;
|
||||
vec2 q;
|
||||
float sq_distance = distance2_point_to_segment(out, e->vertices[0], e->vertices[1], q);
|
||||
if (sq_distance < radius * radius) {
|
||||
// out = q + normalize(out - q) * radius
|
||||
vec2 v;
|
||||
glm_vec2_sub(out, q, v);
|
||||
glm_vec2_normalize(v);
|
||||
glm_vec2_copy(q, out);
|
||||
glm_vec2_muladds(v, radius, out);
|
||||
// update AABB
|
||||
glm_vec2_subs(out, radius, aabb[0]);
|
||||
glm_vec2_adds(out, radius, aabb[1]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collider_raycast(struct collider *collider, vec3 origin, vec3 direction, vec3 out)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct pathfinder_node {
|
||||
int pred;
|
||||
float g_score;
|
||||
enum { UNDISCOVERED, IN_FRONTIER, VISITED, NOT_VISIBLE } state;
|
||||
};
|
||||
|
||||
struct frontier {
|
||||
int triangle_index;
|
||||
float f_score;
|
||||
};
|
||||
|
||||
struct pathfinder {
|
||||
struct pathfinder_node *nodes; // indexed by triangle index
|
||||
struct frontier *frontiers; // min-heap
|
||||
int nr_frontiers;
|
||||
};
|
||||
|
||||
static int frontier_pop(struct pathfinder *pf)
|
||||
{
|
||||
if (pf->nr_frontiers == 0) {
|
||||
return -1;
|
||||
}
|
||||
int index = pf->frontiers[0].triangle_index;
|
||||
if (--pf->nr_frontiers > 0) {
|
||||
pf->frontiers[0] = pf->frontiers[pf->nr_frontiers];
|
||||
int i = 0;
|
||||
while (i < pf->nr_frontiers) {
|
||||
int left = 2 * i + 1;
|
||||
int right = 2 * i + 2;
|
||||
int smallest = i;
|
||||
if (left < pf->nr_frontiers && pf->frontiers[left].f_score < pf->frontiers[smallest].f_score) {
|
||||
smallest = left;
|
||||
}
|
||||
if (right < pf->nr_frontiers && pf->frontiers[right].f_score < pf->frontiers[smallest].f_score) {
|
||||
smallest = right;
|
||||
}
|
||||
if (smallest == i) {
|
||||
break;
|
||||
}
|
||||
struct frontier tmp = pf->frontiers[i];
|
||||
pf->frontiers[i] = pf->frontiers[smallest];
|
||||
pf->frontiers[smallest] = tmp;
|
||||
i = smallest;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static void frontier_push(struct pathfinder *pf, int index, float f_score)
|
||||
{
|
||||
pf->frontiers[pf->nr_frontiers++] = (struct frontier){ index, f_score };
|
||||
int i = pf->nr_frontiers - 1;
|
||||
while (i > 0) {
|
||||
int parent = (i - 1) / 2;
|
||||
if (pf->frontiers[i].f_score >= pf->frontiers[parent].f_score) {
|
||||
break;
|
||||
}
|
||||
struct frontier tmp = pf->frontiers[i];
|
||||
pf->frontiers[i] = pf->frontiers[parent];
|
||||
pf->frontiers[parent] = tmp;
|
||||
i = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_point_visible(vec3 point, mat4 vp_transform)
|
||||
{
|
||||
vec4 p = { point[0], point[1], point[2], 1.f };
|
||||
glm_mat4_mulv(vp_transform, p, p);
|
||||
// Check if the point is in the view frustum.
|
||||
return p[0] >= -p[3] && p[0] <= p[3] &&
|
||||
p[1] >= -p[3] && p[1] <= p[3] &&
|
||||
p[2] >= -p[3] && p[2] <= p[3];
|
||||
}
|
||||
|
||||
bool collider_find_path(struct collider *collider, vec3 start, vec3 goal, mat4 vp_transform)
|
||||
{
|
||||
if (collider->path_points) {
|
||||
collider->nr_path_points = 0;
|
||||
free(collider->path_points);
|
||||
collider->path_points = NULL;
|
||||
}
|
||||
|
||||
// A* search for a graph with triangle centers as nodes and neighbors as edges.
|
||||
struct collider_triangle* start_t = find_triangle(collider, (vec2){ start[0], start[2] });
|
||||
if (!start_t || !is_point_visible(start_t->center, vp_transform))
|
||||
return false;
|
||||
struct collider_triangle* goal_t = find_triangle(collider, (vec2){ goal[0], goal[2] });
|
||||
if (!goal_t || !is_point_visible(goal_t->center, vp_transform))
|
||||
return false;
|
||||
|
||||
int start_i = start_t - collider->triangles;
|
||||
int goal_i = goal_t - collider->triangles;
|
||||
struct pathfinder pf;
|
||||
pf.nodes = xcalloc(collider->nr_triangles, sizeof(struct pathfinder_node));
|
||||
pf.nodes[goal_i].pred = -2;
|
||||
pf.nodes[start_i].pred = -1;
|
||||
pf.nodes[start_i].g_score = 0.f;
|
||||
pf.nodes[start_i].state = IN_FRONTIER;
|
||||
pf.frontiers = xcalloc(collider->nr_triangles, sizeof(struct frontier));
|
||||
pf.nr_frontiers = 0;
|
||||
frontier_push(&pf, start_i, glm_vec3_distance(start_t->center, goal_t->center));
|
||||
|
||||
while (pf.nr_frontiers > 0) {
|
||||
int current_i = frontier_pop(&pf);
|
||||
if (pf.nodes[current_i].state == VISITED)
|
||||
continue;
|
||||
struct collider_triangle *current_t = &collider->triangles[current_i];
|
||||
if (current_i == goal_i)
|
||||
break;
|
||||
pf.nodes[current_i].state = VISITED;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int neighbor_i = current_t->neighbors[j];
|
||||
if (neighbor_i == -1)
|
||||
continue;
|
||||
struct collider_triangle *neighbor_t = &collider->triangles[neighbor_i];
|
||||
struct pathfinder_node *neighbor_n = &pf.nodes[neighbor_i];
|
||||
if (neighbor_n->state == NOT_VISIBLE)
|
||||
continue;
|
||||
if (neighbor_n->state == UNDISCOVERED && !is_point_visible(neighbor_t->center, vp_transform)) {
|
||||
neighbor_n->state = NOT_VISIBLE;
|
||||
continue;
|
||||
}
|
||||
float g = pf.nodes[current_i].g_score + glm_vec3_distance(current_t->center, neighbor_t->center);
|
||||
if (neighbor_n->state == UNDISCOVERED || g < neighbor_n->g_score) {
|
||||
neighbor_n->pred = current_i;
|
||||
neighbor_n->g_score = g;
|
||||
neighbor_n->state = IN_FRONTIER;
|
||||
float f_score = g + glm_vec3_distance(neighbor_t->center, goal_t->center);
|
||||
frontier_push(&pf, neighbor_i, f_score);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pf.nodes[goal_i].pred != -2) {
|
||||
// Reconstruct the path.
|
||||
int path_length = 0;
|
||||
int current_i = goal_i;
|
||||
while (current_i != -1) {
|
||||
path_length++;
|
||||
current_i = pf.nodes[current_i].pred;
|
||||
}
|
||||
collider->nr_path_points = path_length + 2;
|
||||
collider->path_points = xcalloc(path_length + 2, sizeof(vec3));
|
||||
glm_vec3_copy(start, collider->path_points[0]);
|
||||
glm_vec3_copy(goal, collider->path_points[path_length + 1]);
|
||||
current_i = goal_i;
|
||||
for (int i = path_length - 1; i >= 0; i--) {
|
||||
glm_vec3_copy(collider->triangles[current_i].center, collider->path_points[i + 1]);
|
||||
current_i = pf.nodes[current_i].pred;
|
||||
}
|
||||
}
|
||||
free(pf.nodes);
|
||||
free(pf.frontiers);
|
||||
return !!collider->path_points;
|
||||
}
|
||||
|
||||
// determines if a segment intersects with any collider edges, or is close enough to them.
|
||||
static bool test_segment(struct collider *collider, vec2 p0, vec2 p1, float threshold)
|
||||
{
|
||||
vec2 aabb[2];
|
||||
glm_vec2_minv(p0, p1, aabb[0]);
|
||||
glm_vec2_maxv(p0, p1, aabb[1]);
|
||||
aabb[0][0] -= threshold;
|
||||
aabb[0][1] -= threshold;
|
||||
aabb[1][0] += threshold;
|
||||
aabb[1][1] += threshold;
|
||||
float threshold2 = threshold * threshold;
|
||||
for (int i = 0; i < collider->nr_edges; i++) {
|
||||
struct collider_edge *e = &collider->edges[i];
|
||||
if (!glm_aabb2d_aabb(e->aabb, aabb))
|
||||
continue;
|
||||
if (segments_intersect(p0, p1, e->vertices[0], e->vertices[1]))
|
||||
return true;
|
||||
vec2 closest_point;
|
||||
float distance2 = distance2_point_to_segment(p0, e->vertices[0], e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p1, e->vertices[0], e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p0, p1, e->vertices[0], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p0, p1, e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void optimize_path_rec(struct collider *collider, vec3 *points, int start, int end)
|
||||
{
|
||||
if (end - start >= 2) {
|
||||
// If the segment (points[start], points[end]) does not intersect any
|
||||
// collider edges, we can skip all points in between.
|
||||
vec2 p0 = { points[start][0], points[start][2] };
|
||||
vec2 p1 = { points[end][0], points[end][2] };
|
||||
if (test_segment(collider, p0, p1, 0.5f)) {
|
||||
int mid = (start + end) / 2;
|
||||
optimize_path_rec(collider, points, start, mid);
|
||||
optimize_path_rec(collider, points, mid, end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
glm_vec3_copy(points[start], collider->path_points[collider->nr_path_points++]);
|
||||
}
|
||||
|
||||
bool collider_optimize_path(struct collider *collider)
|
||||
{
|
||||
if (collider->nr_path_points < 3)
|
||||
return collider->nr_path_points > 0; // nothing to optimize
|
||||
|
||||
int nr_points = collider->nr_path_points;
|
||||
vec3 *points = xmalloc(nr_points * sizeof(vec3));
|
||||
memcpy(points, collider->path_points, nr_points * sizeof(vec3));
|
||||
collider->nr_path_points = 0;
|
||||
optimize_path_rec(collider, points, 0, nr_points - 1);
|
||||
glm_vec3_copy(points[nr_points - 1], collider->path_points[collider->nr_path_points++]);
|
||||
free(points);
|
||||
return true;
|
||||
}
|
||||
+109
-72
@@ -17,6 +17,8 @@
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "cJSON.h"
|
||||
#include "json.h"
|
||||
#include "reign.h"
|
||||
#include "sprite.h"
|
||||
#include "xsystem4.h"
|
||||
@@ -33,6 +35,7 @@ static const char *instance_type_name(enum RE_instance_type type)
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT: return "DIRECTIONAL_LIGHT";
|
||||
case RE_ITYPE_SPECULAR_LIGHT: return "SPECULAR_LIGHT";
|
||||
case RE_ITYPE_PARTICLE_EFFECT: return "PARTICLE_EFFECT";
|
||||
case RE_ITYPE_PATH_LINE: return "PATH_LINE";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -57,107 +60,141 @@ static const char *fog_type_name(enum RE_fog_type type)
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static void print_motion(const char *name, struct motion *m, int indent)
|
||||
static cJSON *motion_to_json(struct motion *m)
|
||||
{
|
||||
if (m->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
indent_printf(indent, "%s = {state=%s, frame=%f, range=(%d,%d), loop_range=(%d,%d)},\n",
|
||||
name, motion_state_name(m->state), m->current_frame,
|
||||
(int)m->frame_begin, (int)m->frame_end, (int)m->loop_frame_begin, (int)m->loop_frame_end);
|
||||
} else if (m->mot) {
|
||||
indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n",
|
||||
name, m->mot->name, motion_state_name(m->state), m->current_frame);
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(obj, "state", motion_state_name(m->state));
|
||||
cJSON_AddNumberToObject(obj, "frame", m->current_frame);
|
||||
cJSON_AddItemToObjectCS(obj, "range", num2_to_json(m->frame_begin, m->frame_end));
|
||||
cJSON_AddItemToObjectCS(obj, "loop_range", num2_to_json(m->loop_frame_begin, m->loop_frame_end));
|
||||
return obj;
|
||||
}
|
||||
if (m->mot) {
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(obj, "name", m->mot->name);
|
||||
cJSON_AddStringToObject(obj, "state", motion_state_name(m->state));
|
||||
cJSON_AddNumberToObject(obj, "frame", m->current_frame);
|
||||
return obj;
|
||||
}
|
||||
return cJSON_CreateNull();
|
||||
}
|
||||
|
||||
static void print_instance(struct RE_instance *inst, int index, int indent)
|
||||
static cJSON *instance_to_json(struct RE_instance *inst, int index, bool verbose)
|
||||
{
|
||||
if (!inst)
|
||||
return;
|
||||
indent_printf(indent, "instance[%d] = {\n", index);
|
||||
indent++;
|
||||
|
||||
indent_printf(indent, "type = %s,\n", instance_type_name(inst->type));
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(obj, "index", index);
|
||||
cJSON_AddStringToObject(obj, "type", instance_type_name(inst->type));
|
||||
if (inst->type == RE_ITYPE_DIRECTIONAL_LIGHT || inst->type == RE_ITYPE_SPECULAR_LIGHT) {
|
||||
indent_printf(indent, "vec = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->vec));
|
||||
indent_printf(indent, "diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->diffuse));
|
||||
indent_printf(indent, "globe_diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->globe_diffuse));
|
||||
cJSON_AddItemToObjectCS(obj, "vec", vec3_point_to_json(inst->vec, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "diffuse", vec3_color_to_json(inst->diffuse, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "globe_diffuse", vec3_color_to_json(inst->globe_diffuse, verbose));
|
||||
} else {
|
||||
indent_printf(indent, "draw = %d,\n", inst->draw);
|
||||
indent_printf(indent, "pos = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->pos));
|
||||
indent_printf(indent, "scale = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->scale));
|
||||
indent_printf(indent, "ambient = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->ambient));
|
||||
cJSON_AddNumberToObject(obj, "draw", inst->draw);
|
||||
cJSON_AddItemToObjectCS(obj, "pos", vec3_point_to_json(inst->pos, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "scale", vec3_point_to_json(inst->scale, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "ambient", vec3_color_to_json(inst->ambient, verbose));
|
||||
}
|
||||
if (inst->model) {
|
||||
indent_printf(indent, "path = \"%s\",\n", inst->model->path);
|
||||
indent_printf(indent, "aabb = {min = {x=%f, y=%f, z=%f}, max = {x=%f, y=%f, z=%f}},\n",
|
||||
SPREAD_VEC3(inst->model->aabb[0]), SPREAD_VEC3(inst->model->aabb[1]));
|
||||
cJSON *tmp;
|
||||
cJSON_AddStringToObject(obj, "path", inst->model->path);
|
||||
cJSON_AddItemToObjectCS(obj, "aabb", tmp = cJSON_CreateObject());
|
||||
cJSON_AddItemToObjectCS(tmp, "min", vec3_point_to_json(inst->model->aabb[0], verbose));
|
||||
cJSON_AddItemToObjectCS(tmp, "max", vec3_point_to_json(inst->model->aabb[1], verbose));
|
||||
}
|
||||
if (inst->motion) {
|
||||
indent_printf(indent, "fps = %f,\n", inst->fps);
|
||||
print_motion("motion", inst->motion, indent);
|
||||
cJSON_AddNumberToObject(obj, "fps", inst->fps);
|
||||
cJSON_AddItemToObjectCS(obj, "motion", motion_to_json(inst->motion));
|
||||
}
|
||||
if (inst->next_motion) {
|
||||
cJSON_AddItemToObjectCS(obj, "next_motion", motion_to_json(inst->next_motion));
|
||||
}
|
||||
if (inst->motion_blend) {
|
||||
cJSON_AddNumberToObject(obj, "motion_blend_rate", inst->motion_blend_rate);
|
||||
}
|
||||
if (inst->effect) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->effect->pae->path);
|
||||
}
|
||||
if (inst->next_motion)
|
||||
print_motion("next_motion", inst->next_motion, indent);
|
||||
if (inst->motion_blend)
|
||||
indent_printf(indent, "motion_blend_rate = %f,\n", inst->motion_blend_rate);
|
||||
if (inst->effect)
|
||||
indent_printf(indent, "path = \"%s\",\n", inst->effect->pae->path);
|
||||
|
||||
indent--;
|
||||
indent_printf(indent, "},\n");
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void print_back_cg(struct RE_back_cg *bcg, int index, int indent)
|
||||
static cJSON *back_cg_to_json(struct RE_back_cg *bcg, int index, bool verbose)
|
||||
{
|
||||
if (!bcg->name && !bcg->no)
|
||||
return;
|
||||
indent_printf(indent, "backCG[%d] = {\n", index);
|
||||
indent++;
|
||||
|
||||
if (bcg->no)
|
||||
indent_printf(indent, "no = %d,\n", bcg->no);
|
||||
if (bcg->name)
|
||||
indent_printf(indent, "name = \"%s\",\n", bcg->name ? bcg->name->text : "");
|
||||
indent_printf(indent, "pos = {x=%f, y=%f},\n", bcg->x, bcg->y);
|
||||
indent_printf(indent, "blend_rate = %f,\n", bcg->blend_rate);
|
||||
indent_printf(indent, "mag = %f,\n", bcg->mag);
|
||||
indent_printf(indent, "show = %d,\n", bcg->show);
|
||||
|
||||
indent--;
|
||||
indent_printf(indent, "},\n");
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(obj, "index", index);
|
||||
if (bcg->no) {
|
||||
cJSON_AddNumberToObject(obj, "no", bcg->no);
|
||||
}
|
||||
if (bcg->name) {
|
||||
cJSON_AddStringToObject(obj, "name", bcg->name->text);
|
||||
}
|
||||
if (!verbose) {
|
||||
cJSON_AddItemToObjectCS(obj, "pos", num2_to_json(bcg->x, bcg->y));
|
||||
} else {
|
||||
cJSON *pos;
|
||||
cJSON_AddItemToObjectCS(obj, "pos", pos = cJSON_CreateObject());
|
||||
cJSON_AddNumberToObject(pos, "x", bcg->x);
|
||||
cJSON_AddNumberToObject(pos, "y", bcg->y);
|
||||
}
|
||||
cJSON_AddNumberToObject(obj, "blend_rate", bcg->blend_rate);
|
||||
cJSON_AddNumberToObject(obj, "mag", bcg->mag);
|
||||
cJSON_AddNumberToObject(obj, "show", bcg->show);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void RE_debug_print(struct sact_sprite *sp, int indent)
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose)
|
||||
{
|
||||
struct RE_plugin *p = (struct RE_plugin *)sp->plugin;
|
||||
struct RE_plugin *p = (struct RE_plugin*)sp->plugin;
|
||||
cJSON *obj, *cam, *a;
|
||||
|
||||
indent_printf(indent, "name = \"%s\",\n", p->plugin.name);
|
||||
indent_printf(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n",
|
||||
SPREAD_VEC3(p->camera.pos), p->camera.pitch, p->camera.roll, p->camera.yaw);
|
||||
indent_printf(indent, "shadow_map_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->shadow_map_light_dir));
|
||||
indent_printf(indent, "shadow_bias = %f,\n", p->shadow_bias);
|
||||
|
||||
indent_printf(indent, "fog_type = %s,\n", fog_type_name(p->fog_type));
|
||||
obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(obj, "name", p->plugin.name);
|
||||
cJSON_AddItemToObjectCS(obj, "camera", cam = cJSON_CreateObject());
|
||||
cJSON_AddItemToObjectCS(cam, "pos", vec3_point_to_json(p->camera.pos, verbose));
|
||||
cJSON_AddNumberToObject(cam, "pitch", p->camera.pitch);
|
||||
cJSON_AddNumberToObject(cam, "roll", p->camera.roll);
|
||||
cJSON_AddNumberToObject(cam, "yaw", p->camera.yaw);
|
||||
cJSON_AddItemToObjectCS(obj, "shadow_map_light_dir",
|
||||
vec3_point_to_json(p->shadow_map_light_dir, verbose));
|
||||
cJSON_AddNumberToObject(obj, "shadow_bias", p->shadow_bias);
|
||||
cJSON_AddStringToObject(obj, "fog_type", fog_type_name(p->fog_type));
|
||||
switch (p->fog_type) {
|
||||
case RE_FOG_NONE:
|
||||
break;
|
||||
case RE_FOG_LINEAR:
|
||||
indent_printf(indent, "fog_near = %f, fog_far = %f,\n", p->fog_near, p->fog_far);
|
||||
indent_printf(indent, "fog_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->fog_color));
|
||||
cJSON_AddNumberToObject(obj, "fog_near", p->fog_near);
|
||||
cJSON_AddNumberToObject(obj, "fog_far", p->fog_far);
|
||||
break;
|
||||
case RE_FOG_LIGHT_SCATTERING:
|
||||
indent_printf(indent, "ls_beta_r = %f, ls_beta_m = %f,\n", p->ls_beta_r, p->ls_beta_m);
|
||||
indent_printf(indent, "ls_g = %f,\n", p->ls_g);
|
||||
indent_printf(indent, "ls_distance = %f,\n", p->ls_distance);
|
||||
indent_printf(indent, "ls_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->ls_light_dir));
|
||||
indent_printf(indent, "ls_light_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_light_color));
|
||||
indent_printf(indent, "ls_sun_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_sun_color));
|
||||
cJSON_AddNumberToObject(obj, "ls_beta_r", p->ls_beta_r);
|
||||
cJSON_AddNumberToObject(obj, "ls_beta_m", p->ls_beta_m);
|
||||
cJSON_AddNumberToObject(obj, "ls_g", p->ls_g);
|
||||
cJSON_AddNumberToObject(obj, "ls_distance", p->ls_distance);
|
||||
cJSON_AddItemToObjectCS(obj, "ls_light_dir",
|
||||
vec3_point_to_json(p->ls_light_dir, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "ls_light_color",
|
||||
vec3_color_to_json(p->ls_light_color, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "ls_sun_color",
|
||||
vec3_color_to_json(p->ls_sun_color, verbose));
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p->nr_instances; i++)
|
||||
print_instance(p->instances[i], i, indent);
|
||||
cJSON_AddItemToObjectCS(obj, "instances", a = cJSON_CreateArray());
|
||||
for (int i = 0; i < p->nr_instances; i++) {
|
||||
if (!p->instances[i])
|
||||
continue;
|
||||
cJSON_AddItemToArray(a, instance_to_json(p->instances[i], i, verbose));
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
print_back_cg(&p->back_cg[i], i, indent);
|
||||
cJSON_AddItemToObjectCS(obj, "back_cgs", a = cJSON_CreateArray());
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++) {
|
||||
struct RE_back_cg *bcg = &p->back_cg[i];
|
||||
if (!bcg->name && !bcg->no)
|
||||
continue;
|
||||
cJSON_AddItemToArray(a, back_cg_to_json(bcg, i, verbose));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
+131
-23
@@ -29,6 +29,7 @@
|
||||
|
||||
#define FP16_MIN 6.103516e-5f
|
||||
#define NR_WEIGHTS 4
|
||||
#define DEFAULT_OUTLINE_THICKNESS 0.003
|
||||
|
||||
struct vertex_common {
|
||||
GLfloat pos[3];
|
||||
@@ -40,6 +41,10 @@ struct vertex_light_uv {
|
||||
GLfloat uv[2];
|
||||
};
|
||||
|
||||
struct vertex_color {
|
||||
GLfloat color[4];
|
||||
};
|
||||
|
||||
struct vertex_tangent {
|
||||
GLfloat tangent[4];
|
||||
};
|
||||
@@ -49,6 +54,22 @@ struct vertex_bones {
|
||||
GLfloat bone_weight[NR_WEIGHTS];
|
||||
};
|
||||
|
||||
static bool is_transparent_mesh(const struct pol_mesh *mesh)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
return !(mesh->flags & MESH_SPRITE);
|
||||
else
|
||||
return mesh->flags & MESH_ALPHA;
|
||||
}
|
||||
|
||||
static bool is_transparent_material(const struct pol_material *material)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
return !(material->flags & MATERIAL_SPRITE);
|
||||
else
|
||||
return material->flags & MATERIAL_ALPHA;
|
||||
}
|
||||
|
||||
struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, const char *name, const char *ext)
|
||||
{
|
||||
char *path = xmalloc(strlen(dir) + strlen(name) + strlen(ext) + 2);
|
||||
@@ -62,7 +83,6 @@ static GLuint load_texture(struct archive *aar, const char *path, const char *na
|
||||
{
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, path, name, "");
|
||||
if (!dfile) {
|
||||
WARNING("cannot load texture %s\\%s", path, name);
|
||||
return 0;
|
||||
}
|
||||
struct cg *cg = cg_load_data(dfile);
|
||||
@@ -89,6 +109,35 @@ static GLuint load_texture(struct archive *aar, const char *path, const char *na
|
||||
return texture;
|
||||
}
|
||||
|
||||
static GLuint *load_texture_list(struct archive *aar, const char *path, const char *name, int *nr_textures_out, bool *has_alpha_out)
|
||||
{
|
||||
// Load the base texture (e.g. face.png)
|
||||
GLuint tex = load_texture(aar, path, name, has_alpha_out);
|
||||
if (!tex)
|
||||
return NULL;
|
||||
GLuint *textures = xmalloc(10 * sizeof(GLuint));
|
||||
int nr_textures = 0;
|
||||
textures[nr_textures++] = tex;
|
||||
|
||||
// Try to load additional textures (e.g. face[1].png, face[2].png, etc.)
|
||||
const char *ext = strrchr(name, '.');
|
||||
if (ext) {
|
||||
for (;;) {
|
||||
char next_name[100];
|
||||
snprintf(next_name, sizeof(next_name), "%.*s[%d]%s", (int)(ext - name), name, nr_textures, ext);
|
||||
tex = load_texture(aar, path, next_name, NULL);
|
||||
if (!tex)
|
||||
break;
|
||||
textures[nr_textures++] = tex;
|
||||
if (nr_textures % 10 == 0) {
|
||||
textures = xrealloc(textures, (nr_textures + 10) * sizeof(GLuint));
|
||||
}
|
||||
}
|
||||
}
|
||||
*nr_textures_out = nr_textures;
|
||||
return textures;
|
||||
}
|
||||
|
||||
static bool init_material(struct material *material, const struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
|
||||
{
|
||||
material->flags = m->flags;
|
||||
@@ -97,20 +146,25 @@ static bool init_material(struct material *material, const struct pol_material *
|
||||
return false;
|
||||
}
|
||||
bool has_alpha;
|
||||
material->color_map = load_texture(aar, path, m->textures[COLOR_MAP], &has_alpha);
|
||||
if (!material->color_map)
|
||||
material->color_maps = load_texture_list(aar, path, m->textures[COLOR_MAP], &material->nr_color_maps, &has_alpha);
|
||||
if (!material->color_maps)
|
||||
return false;
|
||||
|
||||
if (m->textures[SPECULAR_MAP])
|
||||
material->specular_map = load_texture(aar, path, m->textures[SPECULAR_MAP], NULL);
|
||||
if (m->textures[ALPHA_MAP])
|
||||
material->alpha_map = load_texture(aar, path, m->textures[ALPHA_MAP], NULL);
|
||||
if (m->textures[ALPHA_MAP]) {
|
||||
if (has_alpha && !strcmp(m->textures[COLOR_MAP], m->textures[ALPHA_MAP])) {
|
||||
// Do nothing; the alpha channel of the color map is used.
|
||||
} else {
|
||||
material->alpha_map = load_texture(aar, path, m->textures[ALPHA_MAP], NULL);
|
||||
}
|
||||
}
|
||||
if (m->textures[LIGHT_MAP])
|
||||
material->light_map = load_texture(aar, path, m->textures[LIGHT_MAP], NULL);
|
||||
if (m->textures[NORMAL_MAP])
|
||||
material->normal_map = load_texture(aar, path, m->textures[NORMAL_MAP], NULL);
|
||||
|
||||
material->is_transparent = (has_alpha || material->alpha_map) && !(material->flags & MATERIAL_SPRITE);
|
||||
material->is_transparent = (has_alpha || material->alpha_map) && is_transparent_material(m);
|
||||
material->shadow_darkness = 1.0f;
|
||||
|
||||
struct amt_material *amt_m = amt ? amt_find_material(amt, m->name) : NULL;
|
||||
@@ -136,8 +190,10 @@ static bool init_material(struct material *material, const struct pol_material *
|
||||
|
||||
static void destroy_material(struct material *material)
|
||||
{
|
||||
if (material->color_map)
|
||||
glDeleteTextures(1, &material->color_map);
|
||||
if (material->color_maps) {
|
||||
glDeleteTextures(material->nr_color_maps, material->color_maps);
|
||||
free(material->color_maps);
|
||||
}
|
||||
if (material->specular_map)
|
||||
glDeleteTextures(1, &material->specular_map);
|
||||
if (material->alpha_map)
|
||||
@@ -217,12 +273,15 @@ static void *buf_alloc(uint8_t **ptr, int size)
|
||||
static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_group_index, int material)
|
||||
{
|
||||
bool has_light_map = m->light_uvs && model->materials[material].light_map;
|
||||
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;
|
||||
|
||||
GLsizei stride = sizeof(struct vertex_common);
|
||||
if (has_light_map)
|
||||
stride += sizeof(struct vertex_light_uv);
|
||||
if (has_vertex_colors)
|
||||
stride += sizeof(struct vertex_color);
|
||||
if (has_normal_map)
|
||||
stride += sizeof(struct vertex_tangent);
|
||||
if (has_bones)
|
||||
@@ -249,6 +308,14 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
struct vertex_light_uv *v_light_uv = buf_alloc(&ptr, sizeof(struct vertex_light_uv));
|
||||
glm_vec2_copy(m->light_uvs[t->light_uv_index[j]], v_light_uv->uv);
|
||||
}
|
||||
if (has_vertex_colors) {
|
||||
struct vertex_color *v_color = buf_alloc(&ptr, sizeof(struct vertex_color));
|
||||
if (m->nr_colors > 0)
|
||||
glm_vec3_copy(m->colors[t->color_index[j]], v_color->color);
|
||||
else
|
||||
glm_vec3_one(v_color->color);
|
||||
v_color->color[3] = m->nr_alphas > 0 ? m->alphas[t->alpha_index[j]] : 1.f;
|
||||
}
|
||||
if (has_normal_map) {
|
||||
struct vertex_tangent *v_tangent = buf_alloc(&ptr, sizeof(struct vertex_tangent));
|
||||
glm_vec4_ucopy(tangent[j], v_tangent->tangent);
|
||||
@@ -280,8 +347,20 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
}
|
||||
model->meshes = xrealloc_array(model->meshes, model->nr_meshes, model->nr_meshes + 1, sizeof(struct mesh));
|
||||
struct mesh *mesh = &model->meshes[model->nr_meshes++];
|
||||
mesh->name = xstrdup(m->name);
|
||||
mesh->flags = m->flags;
|
||||
mesh->material = material;
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
mesh->is_transparent = model->materials[material].is_transparent && is_transparent_mesh(m);
|
||||
else
|
||||
mesh->is_transparent = model->materials[material].is_transparent || is_transparent_mesh(m);
|
||||
if (mesh->is_transparent)
|
||||
model->has_transparent_mesh = true;
|
||||
mesh->outline_color[0] = m->edge_color.r / 255.f;
|
||||
mesh->outline_color[1] = m->edge_color.g / 255.f;
|
||||
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);
|
||||
mesh->nr_vertices = nr_vertices;
|
||||
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
@@ -305,6 +384,14 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
glDisableVertexAttribArray(VATTR_LIGHT_UV);
|
||||
glVertexAttrib2f(VATTR_LIGHT_UV, 0.0, 0.0);
|
||||
}
|
||||
if (has_vertex_colors) {
|
||||
glEnableVertexAttribArray(VATTR_COLOR);
|
||||
glVertexAttribPointer(VATTR_COLOR, 4, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_color, color));
|
||||
base += sizeof(struct vertex_color);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_COLOR);
|
||||
glVertexAttrib4f(VATTR_COLOR, 1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
if (has_normal_map) {
|
||||
glEnableVertexAttribArray(VATTR_TANGENT);
|
||||
glVertexAttribPointer(VATTR_TANGENT, 4, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_tangent, tangent));
|
||||
@@ -337,6 +424,7 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
|
||||
static void destroy_mesh(struct mesh *mesh)
|
||||
{
|
||||
free(mesh->name);
|
||||
glDeleteVertexArrays(1, &mesh->vao);
|
||||
glDeleteBuffers(1, &mesh->attr_buffer);
|
||||
if (mesh->index_buffer)
|
||||
@@ -416,6 +504,13 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
struct model *model = xcalloc(1, sizeof(struct model));
|
||||
model->path = strdup(path);
|
||||
|
||||
// Load .opr file, if any
|
||||
struct archive_data *opr_file = RE_get_aar_entry(aar, path, basename, ".opr");
|
||||
if (opr_file) {
|
||||
opr_load(opr_file->data, opr_file->size, pol);
|
||||
archive_free_data(opr_file);
|
||||
}
|
||||
|
||||
// Bones
|
||||
if (pol->nr_bones > 0) {
|
||||
if (pol->nr_bones > MAX_BONES)
|
||||
@@ -452,17 +547,18 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
&pol->materials[i].children[j], amt, aar, path);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < model->nr_materials; i++) {
|
||||
if (model->materials[i].is_transparent) {
|
||||
model->has_transparent_material = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Meshes
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!pol->meshes[i])
|
||||
continue;
|
||||
if (!strcmp(pol->meshes[i]->name, "collision")) {
|
||||
if (model->collider)
|
||||
WARNING("multiple collision meshes");
|
||||
else
|
||||
model->collider = collider_create(pol->meshes[i]);
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
@@ -485,6 +581,9 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
|
||||
void model_free(struct model *model)
|
||||
{
|
||||
if (model->collider)
|
||||
collider_free(model->collider);
|
||||
|
||||
for (int i = 0; i < model->nr_meshes; i++)
|
||||
destroy_mesh(&model->meshes[i]);
|
||||
free(model->meshes);
|
||||
@@ -594,12 +693,14 @@ 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;
|
||||
glGenTextures(1, &material->color_map);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
material->color_maps = xmalloc(sizeof(GLuint));
|
||||
material->nr_color_maps = 1;
|
||||
glGenTextures(1, material->color_maps);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_maps[0]);
|
||||
uint8_t pixel[4] = {r, g, b, a};
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
model->has_transparent_material = true;
|
||||
model->has_transparent_mesh = true;
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -611,18 +712,18 @@ static int cmp_motions_by_bone_id(const void *lhs, const void *rhs)
|
||||
|
||||
static struct mot *mot_load(const char *name, struct model *model, struct archive *aar)
|
||||
{
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, model->path, name, ".MOT");
|
||||
if (!dfile) {
|
||||
struct archive_data *mot_file = RE_get_aar_entry(aar, model->path, name, ".MOT");
|
||||
if (!mot_file) {
|
||||
WARNING("%s\\%s.MOT: not found", model->path, name);
|
||||
return NULL;
|
||||
}
|
||||
struct mot *mot = mot_parse(dfile->data, dfile->size, name);
|
||||
struct mot *mot = mot_parse(mot_file->data, mot_file->size, name);
|
||||
if (!mot) {
|
||||
WARNING("%s: parse error", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
WARNING("%s: parse error", mot_file->name);
|
||||
archive_free_data(mot_file);
|
||||
return NULL;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
archive_free_data(mot_file);
|
||||
|
||||
if (model->nr_bones != (int)mot->nr_bones)
|
||||
ERROR("%s: wrong number of bones. Expected %d but got %d", name, model->nr_bones, mot->nr_bones);
|
||||
@@ -641,6 +742,13 @@ static struct mot *mot_load(const char *name, struct model *model, struct archiv
|
||||
}
|
||||
qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
return mot;
|
||||
}
|
||||
|
||||
|
||||
+152
-14
@@ -14,6 +14,7 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
@@ -64,6 +65,8 @@ static uint32_t parse_material_attributes(const char *name)
|
||||
uint32_t flags = 0;
|
||||
if (strstr(name, "(sprite)"))
|
||||
flags |= MATERIAL_SPRITE;
|
||||
if (strstr(name, "(alpha)"))
|
||||
flags |= MATERIAL_ALPHA;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -134,7 +137,7 @@ static void destroy_vertex(struct pol_vertex *v)
|
||||
free(v->weights);
|
||||
}
|
||||
|
||||
static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle_index, int unknowns_length, const struct pol_material_group *mg)
|
||||
static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle_index, const struct pol_material_group *mg)
|
||||
{
|
||||
struct pol_triangle *t = &mesh->triangles[triangle_index];
|
||||
t->vert_index[0] = buffer_read_int32(r);
|
||||
@@ -149,7 +152,14 @@ static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle
|
||||
t->light_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
}
|
||||
|
||||
buffer_skip(r, unknowns_length);
|
||||
t->color_index[0] = buffer_read_int32(r);
|
||||
t->color_index[1] = buffer_read_int32(r);
|
||||
t->color_index[2] = buffer_read_int32(r);
|
||||
if (mesh->alphas) {
|
||||
t->alpha_index[0] = buffer_read_int32(r);
|
||||
t->alpha_index[1] = buffer_read_int32(r);
|
||||
t->alpha_index[2] = buffer_read_int32(r);
|
||||
}
|
||||
|
||||
read_direction(r, t->normals[0]);
|
||||
read_direction(r, t->normals[1]);
|
||||
@@ -172,6 +182,8 @@ static uint32_t parse_mesh_attributes(const char *name)
|
||||
flags |= MESH_BOTH;
|
||||
if (strstr(name, "(sprite)"))
|
||||
flags |= MESH_SPRITE;
|
||||
if (strstr(name, "(alpha)"))
|
||||
flags |= MESH_ALPHA;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -207,6 +219,7 @@ static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol)
|
||||
|
||||
mesh->nr_light_uvs = buffer_read_int32(r);
|
||||
if (mesh->nr_light_uvs > 0) {
|
||||
mesh->flags |= MESH_HAS_LIGHT_UV;
|
||||
mesh->light_uvs = xcalloc(mesh->nr_light_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_light_uvs; i++) {
|
||||
mesh->light_uvs[i][0] = buffer_read_float(r);
|
||||
@@ -214,24 +227,37 @@ static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol)
|
||||
}
|
||||
}
|
||||
|
||||
int triangle_unknowns_length = 12;
|
||||
if (pol->version == 1) {
|
||||
int nr_unknown_vecs = buffer_read_int32(r);
|
||||
buffer_skip(r, nr_unknown_vecs * 12);
|
||||
} else {
|
||||
int nr_unknown_ints = buffer_read_int32(r);
|
||||
buffer_skip(r, nr_unknown_ints * 4);
|
||||
int nr_unknown_bytes = buffer_read_int32(r);
|
||||
if (nr_unknown_bytes) {
|
||||
buffer_skip(r, nr_unknown_bytes);
|
||||
triangle_unknowns_length += 12;
|
||||
mesh->nr_colors = buffer_read_int32(r);
|
||||
if (mesh->nr_colors > 0) {
|
||||
mesh->colors = xcalloc(mesh->nr_colors, sizeof(vec3));
|
||||
for (uint32_t i = 0; i < mesh->nr_colors; i++) {
|
||||
if (pol->version == 1) {
|
||||
mesh->colors[i][0] = buffer_read_float(r);
|
||||
mesh->colors[i][1] = buffer_read_float(r);
|
||||
mesh->colors[i][2] = buffer_read_float(r);
|
||||
} else {
|
||||
mesh->colors[i][0] = buffer_read_u8(r) / 255.f;
|
||||
mesh->colors[i][1] = buffer_read_u8(r) / 255.f;
|
||||
mesh->colors[i][2] = buffer_read_u8(r) / 255.f;
|
||||
buffer_skip(r, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pol->version >= 2) {
|
||||
mesh->nr_alphas = buffer_read_int32(r);
|
||||
if (mesh->nr_alphas > 0) {
|
||||
mesh->alphas = xcalloc(mesh->nr_alphas, sizeof(float));
|
||||
for (uint32_t i = 0; i < mesh->nr_alphas; i++) {
|
||||
mesh->alphas[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++) {
|
||||
parse_triangle(r, mesh, i, triangle_unknowns_length, &pol->materials[mesh->material]);
|
||||
parse_triangle(r, mesh, i, &pol->materials[mesh->material]);
|
||||
}
|
||||
|
||||
if (pol->version == 1) {
|
||||
@@ -254,6 +280,8 @@ static void free_mesh(struct pol_mesh *mesh)
|
||||
free(mesh->vertices);
|
||||
free(mesh->uvs);
|
||||
free(mesh->light_uvs);
|
||||
free(mesh->colors);
|
||||
free(mesh->alphas);
|
||||
free(mesh->triangles);
|
||||
free(mesh);
|
||||
}
|
||||
@@ -397,6 +425,9 @@ void mot_free(struct mot *mot)
|
||||
free(mot->motions[i]->name);
|
||||
free(mot->motions[i]);
|
||||
}
|
||||
if (mot->texture_indices) {
|
||||
free(mot->texture_indices);
|
||||
}
|
||||
free(mot);
|
||||
}
|
||||
|
||||
@@ -457,3 +488,110 @@ struct amt_material *amt_find_material(struct amt *amt, const char *name)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void opr_load(uint8_t *data, size_t size, struct pol *pol)
|
||||
{
|
||||
bool *selected = xmalloc(pol->nr_meshes * sizeof(bool));
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = !!pol->meshes[i];
|
||||
}
|
||||
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 i1, i2, i3;
|
||||
float f1, f2;
|
||||
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);
|
||||
}
|
||||
} else if (sscanf(line, "MeshPart = \"%[^\"]\"", s) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = pol->meshes[i] && strstr(pol->meshes[i]->name, s);
|
||||
}
|
||||
} else if (sscanf(line, "BlendMode = %s", s) == 1) {
|
||||
if (!strcmp(s, "Add")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_BLEND_ADDITIVE;
|
||||
}
|
||||
} else {
|
||||
WARNING("unknown BlendMode: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "Edge = %d", &i1) == 1) {
|
||||
if (i1 == 0) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_EDGE;
|
||||
}
|
||||
} else {
|
||||
WARNING("invalid Edge value: %d", i1);
|
||||
}
|
||||
} else if (sscanf(line, "EdgeColor = ( %d , %d , %d )", &i1, &i2, &i3) == 3) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->edge_color = COLOR(i1, i2, i3, 255);
|
||||
}
|
||||
} else if (sscanf(line, "EdgeSize = %f", &f1) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->edge_size = f1;
|
||||
}
|
||||
} else if (sscanf(line, "HeightDetection = %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_HEIGHT_DETECTION;
|
||||
}
|
||||
} else {
|
||||
WARNING("invalid HeightDetection: %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 (strchr(line, '=')) {
|
||||
WARNING("unknown field: %s", line);
|
||||
}
|
||||
}
|
||||
free(selected);
|
||||
}
|
||||
|
||||
void txa_load(uint8_t *data, size_t size, struct mot *mot)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
uint32_t *indices = xmalloc(100 * sizeof(uint32_t));
|
||||
const char *p = (const char *)data;
|
||||
while (p < (const char *)data + size) {
|
||||
if (isdigit(*p)) {
|
||||
uint32_t n = 0;
|
||||
while (p < (const char *)data + size && isdigit(*p)) {
|
||||
n = n * 10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
indices[count++] = n;
|
||||
if (count % 100 == 0) {
|
||||
indices = xrealloc(indices, (count + 100) * sizeof(uint32_t));
|
||||
}
|
||||
} else if (isspace(*p)) {
|
||||
p++;
|
||||
} else {
|
||||
WARNING("invalid character in txa file: '%c'", *p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
mot->nr_texture_indices = count;
|
||||
mot->texture_indices = indices;
|
||||
}
|
||||
|
||||
+1
-1
@@ -943,7 +943,7 @@ void particle_object_calc_local_transform(struct RE_instance *inst, struct parti
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
float t = glm_percent(pi->begin_frame, pi->end_frame, frame);
|
||||
|
||||
vec3 pos, up_vector;
|
||||
vec3 pos = {0}, up_vector;
|
||||
calc_pos_and_up_vector(po, pi, t, pos, up_vector);
|
||||
|
||||
float rel_frame = frame - pi->begin_frame;
|
||||
|
||||
+167
-10
@@ -34,6 +34,8 @@
|
||||
#include "vm.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
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));
|
||||
@@ -75,6 +77,7 @@ static void unload_instance(struct RE_instance *instance)
|
||||
instance->effect = NULL;
|
||||
}
|
||||
if (instance->bone_transforms) {
|
||||
glDeleteBuffers(1, &instance->bone_transforms_ubo);
|
||||
free(instance->bone_transforms);
|
||||
instance->bone_transforms = NULL;
|
||||
}
|
||||
@@ -128,12 +131,18 @@ static void update_motion(struct motion *m, float delta_frame)
|
||||
}
|
||||
break;
|
||||
case RE_MOTION_STATE_LOOP:
|
||||
if (m->current_frame > m->loop_frame_end)
|
||||
if (m->loop_frame_begin == m->loop_frame_end)
|
||||
m->current_frame = m->loop_frame_begin;
|
||||
else if (m->current_frame > m->loop_frame_end)
|
||||
m->current_frame = m->loop_frame_begin + fmodf(m->current_frame - m->loop_frame_begin, m->loop_frame_end - m->loop_frame_begin);
|
||||
break;
|
||||
default:
|
||||
VM_ERROR("Invalid motion state %d", m->state);
|
||||
}
|
||||
|
||||
uint32_t anim_frame = m->current_frame - m->frame_begin;
|
||||
m->texture_index = (m->mot && anim_frame < m->mot->nr_texture_indices)
|
||||
? m->mot->texture_indices[anim_frame] : 0;
|
||||
}
|
||||
|
||||
static void calc_motion_frame(struct motion *m, int bone, struct mot_frame *out)
|
||||
@@ -177,11 +186,16 @@ static void update_bones(struct RE_instance *inst)
|
||||
}
|
||||
glm_mat4_copy(bone_transform, parent_transforms[i]);
|
||||
|
||||
glm_mat4_mul(bone_transform, inst->model->bones[i].inverse_bind_matrix, inst->bone_transforms[i]);
|
||||
|
||||
glm_vec3_minv(aabb[0], bone_transform[3], aabb[0]);
|
||||
glm_vec3_maxv(aabb[1], bone_transform[3], aabb[1]);
|
||||
|
||||
glm_mat4_mul(bone_transform, inst->model->bones[i].inverse_bind_matrix, bone_transform);
|
||||
glm_mat4_transpose(bone_transform); // column major -> row major
|
||||
glm_mat3x4_copy(bone_transform, inst->bone_transforms[i]);
|
||||
}
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, inst->bone_transforms_ubo);
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, inst->model->nr_bones * sizeof(mat3x4), inst->bone_transforms);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
// Update inst->bounding_sphere.
|
||||
vec3 center;
|
||||
@@ -199,8 +213,10 @@ static void update_bones(struct RE_instance *inst)
|
||||
}
|
||||
}
|
||||
|
||||
struct RE_plugin *RE_plugin_new(void)
|
||||
struct RE_plugin *RE_plugin_new(enum RE_plugin_version version)
|
||||
{
|
||||
re_plugin_version = version;
|
||||
|
||||
char *aar_path = gamedir_path("Data/ReignData.red");
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
struct archive *aar = (struct archive *)aar_open(aar_path, MMAP_IF_64BIT, &error);
|
||||
@@ -216,7 +232,7 @@ struct RE_plugin *RE_plugin_new(void)
|
||||
struct RE_plugin *plugin = xcalloc(1, sizeof(struct RE_plugin));
|
||||
plugin->plugin.name = "ReignEngine";
|
||||
plugin->plugin.update = RE_render;
|
||||
plugin->plugin.debug_print = RE_debug_print;
|
||||
plugin->plugin.to_json = RE_to_json;
|
||||
plugin->nr_instances = 16;
|
||||
plugin->instances = xcalloc(plugin->nr_instances, sizeof(struct RE_instance *));
|
||||
plugin->aar = aar;
|
||||
@@ -255,9 +271,11 @@ bool RE_plugin_bind(struct RE_plugin *plugin, int sprite)
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sprite);
|
||||
if (!sp)
|
||||
return false;
|
||||
plugin->renderer = RE_renderer_new(sprite_get_texture(sp));
|
||||
plugin->renderer = RE_renderer_new();
|
||||
plugin->sprite = sprite;
|
||||
sprite_bind_plugin(sp, &plugin->plugin);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
RE_renderer_set_viewport_size(plugin->renderer, texture->w, texture->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -274,6 +292,28 @@ bool RE_plugin_unbind(struct RE_plugin *plugin)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_plugin_suspend(struct RE_plugin *plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
plugin->suspended = true;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(plugin->sprite);
|
||||
if (sp)
|
||||
sprite_set_show(sp, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_plugin_resume(struct RE_plugin *plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
plugin->suspended = false;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(plugin->sprite);
|
||||
if (sp)
|
||||
sprite_set_show(sp, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
|
||||
{
|
||||
if (!plugin)
|
||||
@@ -292,6 +332,7 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
|
||||
|
||||
if (inst->type == RE_ITYPE_SKINNED)
|
||||
update_bones(inst);
|
||||
inst->texture_animation_index = inst->motion ? inst->motion->texture_index : 0;
|
||||
}
|
||||
|
||||
// Update particle effects in a second pass, because it uses the results of
|
||||
@@ -304,6 +345,21 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_set_viewport(struct RE_plugin *plugin, int x, int y, int width, int height)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(plugin->sprite);
|
||||
if (!sp || sp->plugin != &plugin->plugin) {
|
||||
WARNING("No sprite bound to plugin");
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_set_projection(struct RE_plugin *plugin, float width, float height, float near, float far, float deg)
|
||||
{
|
||||
if (!plugin)
|
||||
@@ -368,8 +424,10 @@ bool RE_instance_set_type(struct RE_instance *instance, int type)
|
||||
break;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
break;
|
||||
case RE_ITYPE_PATH_LINE:
|
||||
break;
|
||||
default:
|
||||
ERROR("unknown instance type %d", type);
|
||||
VM_ERROR("unknown instance type %d", type);
|
||||
}
|
||||
instance->type = type;
|
||||
return true;
|
||||
@@ -393,8 +451,13 @@ bool RE_instance_load(struct RE_instance *instance, const char *name)
|
||||
if (instance->model)
|
||||
ht_put(instance->plugin->model_cache, name, instance->model);
|
||||
}
|
||||
if (instance->model && instance->model->nr_bones > 0)
|
||||
instance->bone_transforms = xcalloc(instance->model->nr_bones, sizeof(mat4));
|
||||
if (instance->model && instance->model->nr_bones > 0) {
|
||||
instance->bone_transforms = xcalloc(instance->model->nr_bones, sizeof(mat3x4));
|
||||
glGenBuffers(1, &instance->bone_transforms_ubo);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, instance->bone_transforms_ubo);
|
||||
glBufferData(GL_UNIFORM_BUFFER, MAX_BONES * sizeof(mat3x4), NULL, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
return !!instance->model;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
pae = ht_get(instance->plugin->pae_cache, name, NULL);
|
||||
@@ -446,6 +509,19 @@ bool RE_instance_free_next_motion(struct RE_instance *instance)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show)
|
||||
{
|
||||
if (!instance || !instance->model)
|
||||
return false;
|
||||
for (int i = 0; i < instance->model->nr_meshes; i++) {
|
||||
if (!strcmp(instance->model->meshes[i].name, mesh_name)) {
|
||||
instance->model->meshes[i].hidden = !show;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z)
|
||||
{
|
||||
if (!instance)
|
||||
@@ -480,7 +556,10 @@ bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instan
|
||||
if (instance->local_transform_needs_update)
|
||||
RE_instance_update_local_transform(instance);
|
||||
|
||||
glm_mat4_mulv3(instance->bone_transforms[bone], offset, 1.0, out);
|
||||
mat4 bone_transform = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_mat3x4_copy(instance->bone_transforms[bone], bone_transform);
|
||||
glm_mat4_transpose(bone_transform); // row major -> column major
|
||||
glm_mat4_mulv3(bone_transform, offset, 1.0, out);
|
||||
glm_mat4_mulv3(instance->local_transform, out, 1.0, out);
|
||||
return true;
|
||||
}
|
||||
@@ -507,6 +586,31 @@ float RE_instance_calc_height(struct RE_instance *instance, float x, float z)
|
||||
return cnt ? total / cnt : 0.0f;
|
||||
}
|
||||
|
||||
float RE_instance_calc_2d_detection_height(struct RE_instance *instance, float x, float z)
|
||||
{
|
||||
if (!instance || !instance->model->collider)
|
||||
return false;
|
||||
vec2 xz = { x, -z };
|
||||
float h;
|
||||
if (collider_height(instance->model->collider, xz, &h))
|
||||
return h;
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
bool RE_instance_calc_2d_detection(struct RE_instance *instance, float x0, float y0, float z0, float x1, float y1, float z1, float *x2, float *y2, float *z2, float radius)
|
||||
{
|
||||
if (!instance || !instance->model->collider)
|
||||
return false;
|
||||
vec2 p0 = { x0, -z0 };
|
||||
vec2 p1 = { x1, -z1 };
|
||||
vec2 p2;
|
||||
if (!check_collision(instance->model->collider, p0, p1, radius, p2))
|
||||
return false;
|
||||
*x2 = p2[0];
|
||||
*z2 = -p2[1];
|
||||
return collider_height(instance->model->collider, p2, y2);
|
||||
}
|
||||
|
||||
bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *inst, bool draw)
|
||||
{
|
||||
if (!inst)
|
||||
@@ -520,6 +624,59 @@ bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *inst, bool dra
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
glm_mat4_mul(inst->plugin->proj_transform, vp_transform, vp_transform);
|
||||
return collider_find_path(inst->model->collider, start, goal, vp_transform);
|
||||
}
|
||||
|
||||
const vec3 *RE_instance_get_path_line(struct RE_instance *inst, int *nr_path_points)
|
||||
{
|
||||
if (!inst || !inst->model->collider)
|
||||
return NULL;
|
||||
*nr_path_points = inst->model->collider->nr_path_points;
|
||||
return inst->model->collider->path_points;
|
||||
}
|
||||
|
||||
bool RE_instance_optimize_path_line(struct RE_instance *inst)
|
||||
{
|
||||
if (!inst || !inst->model->collider)
|
||||
return false;
|
||||
return collider_optimize_path(inst->model->collider);
|
||||
}
|
||||
|
||||
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)
|
||||
return false;
|
||||
|
||||
float ndc_x = 2.f * mouse_x / inst->plugin->renderer->viewport_width - 1.f;
|
||||
float ndc_y = 1.f - 2.f * mouse_y / inst->plugin->renderer->viewport_height;
|
||||
vec4 near = { ndc_x, ndc_y, -1.f, 1.f };
|
||||
vec4 far = { ndc_x, ndc_y, 1.f, 1.f };
|
||||
|
||||
mat4 inv_vp;
|
||||
RE_calc_view_matrix(&inst->plugin->camera, GLM_YUP, inv_vp);
|
||||
glm_mat4_mul(inst->plugin->proj_transform, inv_vp, inv_vp);
|
||||
glm_mat4_inv(inv_vp, inv_vp);
|
||||
|
||||
glm_mat4_mulv(inv_vp, near, near);
|
||||
glm_mat4_mulv(inv_vp, far, far);
|
||||
glm_vec3_divs(near, near[3], near);
|
||||
glm_vec3_divs(far, far[3], far);
|
||||
|
||||
vec3 origin, direction;
|
||||
glm_vec3_copy(near, origin);
|
||||
glm_vec3_sub(far, near, direction);
|
||||
glm_vec3_normalize(direction);
|
||||
|
||||
return collider_raycast(inst->model->collider, origin, direction, out);
|
||||
}
|
||||
|
||||
void RE_instance_update_local_transform(struct RE_instance *inst)
|
||||
{
|
||||
vec3 euler = {
|
||||
|
||||
+189
-39
@@ -26,6 +26,8 @@
|
||||
#include "reign.h"
|
||||
#include "sact.h"
|
||||
|
||||
#define BONE_TRANSFORMS_BINDING 0
|
||||
|
||||
// TODO: Respect RE_plugin.shadow_map_resolution_level
|
||||
#define SHADOW_WIDTH 512
|
||||
#define SHADOW_HEIGHT 512
|
||||
@@ -50,6 +52,7 @@ enum {
|
||||
ALPHA_BLEND = 0,
|
||||
ALPHA_TEST = 1,
|
||||
ALPHA_MAP_BLEND = 2,
|
||||
ALPHA_MAP_TEST = 3,
|
||||
};
|
||||
|
||||
enum draw_phase {
|
||||
@@ -59,9 +62,17 @@ enum draw_phase {
|
||||
|
||||
static GLuint load_shader(const char *vertex_shader_path, const char *fragment_shader_path)
|
||||
{
|
||||
char defines[256];
|
||||
snprintf(defines, sizeof(defines),
|
||||
"#define REIGN_ENGINE %d\n"
|
||||
"#define TAPIR_ENGINE %d\n"
|
||||
"#define ENGINE %d\n",
|
||||
RE_REIGN_PLUGIN,
|
||||
RE_TAPIR_PLUGIN,
|
||||
re_plugin_version);
|
||||
GLuint program = glCreateProgram();
|
||||
GLuint vertex_shader = gfx_load_shader_file(vertex_shader_path, GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader = gfx_load_shader_file(fragment_shader_path, GL_FRAGMENT_SHADER);
|
||||
GLuint vertex_shader = gfx_load_shader_file(vertex_shader_path, GL_VERTEX_SHADER, defines);
|
||||
GLuint fragment_shader = gfx_load_shader_file(fragment_shader_path, GL_FRAGMENT_SHADER, defines);
|
||||
|
||||
glAttachShader(program, vertex_shader);
|
||||
glAttachShader(program, fragment_shader);
|
||||
@@ -74,6 +85,7 @@ static GLuint load_shader(const char *vertex_shader_path, const char *fragment_s
|
||||
glBindAttribLocation(program, VATTR_BONE_INDEX, "vertex_bone_index");
|
||||
glBindAttribLocation(program, VATTR_BONE_WEIGHT, "vertex_bone_weight");
|
||||
glBindAttribLocation(program, VATTR_LIGHT_UV, "vertex_light_uv");
|
||||
glBindAttribLocation(program, VATTR_COLOR, "vertex_color");
|
||||
glBindAttribLocation(program, VATTR_TANGENT, "vertex_tangent");
|
||||
|
||||
glLinkProgram(program);
|
||||
@@ -96,10 +108,11 @@ static void init_shadow_renderer(struct shadow_renderer *sr)
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &orig_fbo);
|
||||
|
||||
sr->program = load_shader("shaders/reign_shadow.v.glsl", "shaders/reign_shadow.f.glsl");
|
||||
sr->world_transform = glGetUniformLocation(sr->program, "world_transform");
|
||||
sr->local_transform = glGetUniformLocation(sr->program, "local_transform");
|
||||
sr->view_transform = glGetUniformLocation(sr->program, "view_transform");
|
||||
sr->has_bones = glGetUniformLocation(sr->program, "has_bones");
|
||||
sr->bone_matrices = glGetUniformLocation(sr->program, "bone_matrices");
|
||||
GLuint bone_transforms = glGetUniformBlockIndex(sr->program, "BoneTransforms");
|
||||
glUniformBlockBinding(sr->program, bone_transforms, BONE_TRANSFORMS_BINDING);
|
||||
|
||||
glGenFramebuffers(1, &sr->fbo);
|
||||
glGenTextures(1, &sr->texture);
|
||||
@@ -124,6 +137,30 @@ static void destroy_shadow_renderer(struct shadow_renderer *sr)
|
||||
glDeleteProgram(sr->program);
|
||||
}
|
||||
|
||||
static void init_outline_renderer(struct outline_renderer *or)
|
||||
{
|
||||
if (re_plugin_version < RE_TAPIR_PLUGIN) {
|
||||
or->program = 0;
|
||||
return;
|
||||
}
|
||||
or->program = load_shader("shaders/reign_outline.v.glsl", "shaders/reign_outline.f.glsl");
|
||||
or->local_transform = glGetUniformLocation(or->program, "local_transform");
|
||||
or->view_transform = glGetUniformLocation(or->program, "view_transform");
|
||||
or->proj_transform = glGetUniformLocation(or->program, "proj_transform");
|
||||
or->normal_transform = glGetUniformLocation(or->program, "normal_transform");
|
||||
or->has_bones = glGetUniformLocation(or->program, "has_bones");
|
||||
GLuint bone_transforms = glGetUniformBlockIndex(or->program, "BoneTransforms");
|
||||
glUniformBlockBinding(or->program, bone_transforms, BONE_TRANSFORMS_BINDING);
|
||||
or->outline_color = glGetUniformLocation(or->program, "outline_color");
|
||||
or->outline_thickness = glGetUniformLocation(or->program, "outline_thickness");
|
||||
}
|
||||
|
||||
static void destroy_outline_renderer(struct outline_renderer *or)
|
||||
{
|
||||
if (or->program)
|
||||
glDeleteProgram(or->program);
|
||||
}
|
||||
|
||||
static void init_billboard_mesh(struct RE_renderer *r)
|
||||
{
|
||||
static const GLfloat vertices[] = {
|
||||
@@ -144,6 +181,8 @@ static void init_billboard_mesh(struct RE_renderer *r)
|
||||
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);
|
||||
glVertexAttrib4f(VATTR_COLOR, 1.0, 1.0, 1.0, 1.0);
|
||||
glDisableVertexAttribArray(VATTR_NORMAL);
|
||||
glVertexAttrib3f(VATTR_NORMAL, 0.0, 0.0, 1.0);
|
||||
glDisableVertexAttribArray(VATTR_TANGENT);
|
||||
@@ -165,12 +204,11 @@ static void destroy_billboard_mesh(struct RE_renderer *r)
|
||||
glDeleteBuffers(1, &r->billboard_attr_buffer);
|
||||
}
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture)
|
||||
struct RE_renderer *RE_renderer_new(void)
|
||||
{
|
||||
struct RE_renderer *r = xcalloc(1, sizeof(struct RE_renderer));
|
||||
|
||||
r->program = load_shader("shaders/reign.v.glsl", "shaders/reign.f.glsl");
|
||||
r->world_transform = glGetUniformLocation(r->program, "world_transform");
|
||||
r->view_transform = glGetUniformLocation(r->program, "view_transform");
|
||||
r->texture = glGetUniformLocation(r->program, "tex");
|
||||
r->local_transform = glGetUniformLocation(r->program, "local_transform");
|
||||
@@ -178,8 +216,10 @@ struct RE_renderer *RE_renderer_new(struct texture *texture)
|
||||
r->normal_transform = glGetUniformLocation(r->program, "normal_transform");
|
||||
r->alpha_mod = glGetUniformLocation(r->program, "alpha_mod");
|
||||
r->has_bones = glGetUniformLocation(r->program, "has_bones");
|
||||
r->bone_matrices = glGetUniformLocation(r->program, "bone_matrices");
|
||||
r->ambient = glGetUniformLocation(r->program, "ambient");
|
||||
GLuint bone_transforms = glGetUniformBlockIndex(r->program, "BoneTransforms");
|
||||
glUniformBlockBinding(r->program, bone_transforms, BONE_TRANSFORMS_BINDING);
|
||||
r->global_ambient = glGetUniformLocation(r->program, "global_ambient");
|
||||
r->instance_ambient = glGetUniformLocation(r->program, "instance_ambient");
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
char buf[64];
|
||||
sprintf(buf, "dir_lights[%d].dir", i);
|
||||
@@ -215,18 +255,27 @@ struct RE_renderer *RE_renderer_new(struct texture *texture)
|
||||
r->ls_sun_color = glGetUniformLocation(r->program, "ls_sun_color");
|
||||
r->alpha_mode = glGetUniformLocation(r->program, "alpha_mode");
|
||||
r->alpha_texture = glGetUniformLocation(r->program, "alpha_texture");
|
||||
r->uv_scroll = glGetUniformLocation(r->program, "uv_scroll");
|
||||
|
||||
glGenRenderbuffers(1, &r->depth_buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, r->depth_buffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
init_shadow_renderer(&r->shadow);
|
||||
init_outline_renderer(&r->outline);
|
||||
init_billboard_mesh(r);
|
||||
r->billboard_textures = ht_create(256);
|
||||
r->last_frame_timestamp = SDL_GetTicks();
|
||||
return r;
|
||||
}
|
||||
|
||||
void RE_renderer_set_viewport_size(struct RE_renderer *r, int width, int height)
|
||||
{
|
||||
r->viewport_width = width;
|
||||
r->viewport_height = height;
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, r->depth_buffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
}
|
||||
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no)
|
||||
{
|
||||
if (ht_get_int(r->billboard_textures, cg_no, NULL))
|
||||
@@ -268,6 +317,7 @@ void RE_renderer_free(struct RE_renderer *r)
|
||||
ht_free_int(r->billboard_textures);
|
||||
destroy_billboard_mesh(r);
|
||||
destroy_shadow_renderer(&r->shadow);
|
||||
destroy_outline_renderer(&r->outline);
|
||||
free(r);
|
||||
}
|
||||
|
||||
@@ -285,13 +335,19 @@ void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out)
|
||||
glm_look(camera->pos, front, up, out);
|
||||
}
|
||||
|
||||
static bool should_draw_shadow(struct mesh *mesh, struct material *material)
|
||||
{
|
||||
return !(mesh->flags & (MESH_ALPHA | MESH_BOTH | MESH_SPRITE))
|
||||
&& !(material->flags & (MATERIAL_ALPHA | MATERIAL_SPRITE));
|
||||
}
|
||||
|
||||
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_material && inst->alpha >= 1.0f;
|
||||
bool all_opaque = !model->has_transparent_mesh && inst->alpha >= 1.0f;
|
||||
if ((phase == DRAW_OPAQUE && all_transparent) || (phase == DRAW_TRANSPARENT && all_opaque))
|
||||
return;
|
||||
|
||||
@@ -301,9 +357,7 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
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);
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
|
||||
glUniform3fv(r->ambient, 1, ambient);
|
||||
glUniform3fv(r->instance_ambient, 1, inst->ambient);
|
||||
|
||||
bool draw_shadow = inst->draw_shadow && inst->plugin->shadow_mode;
|
||||
if (draw_shadow) {
|
||||
@@ -312,15 +366,21 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
glUniform1i(r->shadow_texture, SHADOW_TEXTURE_UNIT);
|
||||
}
|
||||
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
struct mesh *mesh = &model->meshes[i];
|
||||
if (mesh->hidden)
|
||||
continue;
|
||||
struct material *material = &model->materials[mesh->material];
|
||||
bool is_transparent = (material->is_transparent && !(mesh->flags & MESH_SPRITE)) || inst->alpha < 1.0f;
|
||||
bool is_transparent = mesh->is_transparent || inst->alpha < 1.0f;
|
||||
if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE))
|
||||
continue;
|
||||
|
||||
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);
|
||||
|
||||
@@ -344,10 +404,12 @@ 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);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
int animation_index = inst->texture_animation_index < material->nr_color_maps
|
||||
? inst->texture_animation_index : 0;
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_maps[animation_index]);
|
||||
glUniform1i(r->texture, COLOR_TEXTURE_UNIT);
|
||||
|
||||
if (draw_shadow && !(mesh->flags & MESH_BOTH))
|
||||
if (draw_shadow && should_draw_shadow(mesh, material))
|
||||
glUniform1f(r->shadow_darkness, material->shadow_darkness);
|
||||
else
|
||||
glUniform1f(r->shadow_darkness, 0.0f);
|
||||
@@ -356,7 +418,7 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_ENV_MAP);
|
||||
} else if (mesh->flags & MESH_NOLIGHTING) {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_EMISSIVE);
|
||||
} else if (material->light_map && inst->plugin->light_map_mode) {
|
||||
} else if (material->light_map && mesh->flags & MESH_HAS_LIGHT_UV && inst->plugin->light_map_mode) {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_LIGHT_MAP);
|
||||
glActiveTexture(GL_TEXTURE0 + LIGHT_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->light_map);
|
||||
@@ -375,23 +437,30 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
|
||||
}
|
||||
|
||||
if (material->alpha_map) {
|
||||
glUniform1i(r->alpha_mode, ALPHA_MAP_BLEND);
|
||||
glUniform1i(r->alpha_mode, mesh->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 if (material->flags & MATERIAL_SPRITE || mesh->flags & MESH_SPRITE) {
|
||||
glUniform1i(r->alpha_mode, ALPHA_TEST);
|
||||
} else {
|
||||
glUniform1i(r->alpha_mode, ALPHA_BLEND);
|
||||
glUniform1i(r->alpha_mode, mesh->is_transparent ? ALPHA_BLEND : ALPHA_TEST);
|
||||
}
|
||||
|
||||
vec2 uv_scroll;
|
||||
glm_vec2_scale(mesh->uv_scroll, r->last_frame_timestamp / 1000.f, uv_scroll);
|
||||
glUniform2fv(r->uv_scroll, 1, uv_scroll);
|
||||
|
||||
glBindVertexArray(mesh->vao);
|
||||
|
||||
if (mesh->flags & MESH_BOTH)
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
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_BOTH)
|
||||
glEnable(GL_CULL_FACE);
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
@@ -415,7 +484,7 @@ static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r
|
||||
|
||||
if (model->nr_bones > 0 && inst->motion) {
|
||||
glUniform1i(r->has_bones, GL_TRUE);
|
||||
glUniformMatrix4fv(r->bone_matrices, model->nr_bones, GL_FALSE, inst->bone_transforms[0][0]);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, BONE_TRANSFORMS_BINDING, inst->bone_transforms_ubo);
|
||||
} else {
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
}
|
||||
@@ -449,9 +518,7 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma
|
||||
// This should be safe because billboards do not have non-uniform scaling.
|
||||
glm_mat4_pick3(local_transform, normal_transform);
|
||||
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
|
||||
glUniform3fv(r->ambient, 1, ambient);
|
||||
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]);
|
||||
@@ -557,7 +624,7 @@ static void render_polygon_particles(struct RE_renderer *r, struct RE_instance *
|
||||
struct material *material = &model->materials[mesh->material];
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_maps[0]);
|
||||
glUniform1i(r->texture, 0);
|
||||
|
||||
glBindVertexArray(mesh->vao);
|
||||
@@ -575,9 +642,7 @@ static void render_particle_effect(struct RE_instance *inst, struct RE_renderer
|
||||
if (!inst->effect || phase == DRAW_OPAQUE)
|
||||
return;
|
||||
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
|
||||
glUniform3fv(r->ambient, 1, ambient);
|
||||
glUniform3fv(r->instance_ambient, 1, inst->ambient);
|
||||
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
@@ -637,6 +702,26 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat
|
||||
}
|
||||
}
|
||||
|
||||
static void merge_spheres(vec4 s1, vec4 s2, vec4 dest)
|
||||
{
|
||||
// glm_sphere_merge() doesn't make the smallest possible sphere, so do it manually.
|
||||
float distance = glm_vec3_distance(s1, s2);
|
||||
float r1 = s1[3];
|
||||
float r2 = s2[3];
|
||||
if (distance + r2 <= r1) {
|
||||
glm_vec4_copy(s1, dest);
|
||||
} else if (distance + r1 <= r2) {
|
||||
glm_vec4_copy(s2, dest);
|
||||
} else {
|
||||
float radius = (distance + r1 + r2) / 2.f;
|
||||
vec3 v12;
|
||||
glm_vec3_sub(s2, s1, v12);
|
||||
glm_vec3_copy(s1, dest);
|
||||
glm_vec3_muladds(v12, (radius - r1) / distance, dest);
|
||||
dest[3] = radius;
|
||||
}
|
||||
}
|
||||
|
||||
static bool calc_shadow_light_transform(struct RE_plugin *plugin, mat4 dest)
|
||||
{
|
||||
// Compute a bounding sphere of shadow casters.
|
||||
@@ -646,7 +731,7 @@ static bool calc_shadow_light_transform(struct RE_plugin *plugin, mat4 dest)
|
||||
if (!inst || !inst->draw || !inst->make_shadow || !inst->model)
|
||||
continue;
|
||||
if (bounding_sphere[3] > 0.0f)
|
||||
glm_sphere_merge(inst->bounding_sphere, bounding_sphere, inst->bounding_sphere);
|
||||
merge_spheres(inst->bounding_sphere, bounding_sphere, bounding_sphere);
|
||||
else
|
||||
glm_vec4_copy(inst->bounding_sphere, bounding_sphere);
|
||||
}
|
||||
@@ -657,7 +742,7 @@ static bool calc_shadow_light_transform(struct RE_plugin *plugin, mat4 dest)
|
||||
|
||||
// Create a orthographic frustum that contains the bounding sphere.
|
||||
mat4 view_matrix;
|
||||
glm_look(bounding_sphere, plugin->shadow_map_light_dir, GLM_YUP, view_matrix);
|
||||
glm_look_anyup(bounding_sphere, plugin->shadow_map_light_dir, view_matrix);
|
||||
mat4 proj_matrix;
|
||||
glm_ortho(-radius, radius, -radius, radius, -radius, radius, proj_matrix);
|
||||
glm_mat4_mul(proj_matrix, view_matrix, dest);
|
||||
@@ -693,11 +778,11 @@ static void render_shadow_map(struct RE_plugin *plugin, mat4 light_space_transfo
|
||||
struct model *model = inst->model;
|
||||
if (model->nr_bones > 0) {
|
||||
glUniform1i(r->shadow.has_bones, GL_TRUE);
|
||||
glUniformMatrix4fv(r->shadow.bone_matrices, model->nr_bones, GL_FALSE, inst->bone_transforms[0][0]);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, BONE_TRANSFORMS_BINDING, inst->bone_transforms_ubo);
|
||||
} else {
|
||||
glUniform1i(r->shadow.has_bones, GL_FALSE);
|
||||
}
|
||||
glUniformMatrix4fv(r->shadow.world_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
glUniformMatrix4fv(r->shadow.local_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
for (int j = 0; j < model->nr_meshes; j++) {
|
||||
struct mesh *mesh = &model->meshes[j];
|
||||
if (mesh->flags & MESH_NOMAKESHADOW)
|
||||
@@ -713,6 +798,49 @@ static void render_shadow_map(struct RE_plugin *plugin, mat4 light_space_transfo
|
||||
glViewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]);
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
struct outline_renderer *or = &plugin->renderer->outline;
|
||||
if (!or->program)
|
||||
return;
|
||||
glUseProgram(or->program);
|
||||
glUniformMatrix4fv(or->view_transform, 1, GL_FALSE, view_transform[0]);
|
||||
glUniformMatrix4fv(or->proj_transform, 1, GL_FALSE, plugin->proj_transform[0]);
|
||||
|
||||
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)
|
||||
continue;
|
||||
struct model *model = inst->model;
|
||||
if (inst->model && model->nr_bones > 0) {
|
||||
glUniform1i(or->has_bones, GL_TRUE);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, BONE_TRANSFORMS_BINDING, inst->bone_transforms_ubo);
|
||||
} else {
|
||||
glUniform1i(or->has_bones, GL_FALSE);
|
||||
}
|
||||
glUniformMatrix4fv(or->local_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
glUniformMatrix3fv(or->normal_transform, 1, GL_FALSE, inst->normal_transform[0]);
|
||||
for (int j = 0; j < model->nr_meshes; j++) {
|
||||
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);
|
||||
glBindVertexArray(mesh->vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
glCullFace(GL_BACK);
|
||||
glUseProgram(plugin->renderer->program);
|
||||
}
|
||||
|
||||
static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct RE_renderer *r)
|
||||
{
|
||||
if (!bcg->show || !bcg->texture.handle)
|
||||
@@ -726,6 +854,7 @@ static void setup_lights(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
glUniform3fv(r->camera_pos, 1, plugin->camera.pos);
|
||||
glUniform3fv(r->global_ambient, 1, plugin->global_ambient);
|
||||
int light_index = 0;
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
@@ -796,8 +925,15 @@ void RE_render(struct sact_sprite *sp)
|
||||
{
|
||||
struct RE_plugin *plugin = (struct RE_plugin *)sp->plugin;
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
if (!r)
|
||||
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);
|
||||
|
||||
@@ -812,6 +948,7 @@ void RE_render(struct sact_sprite *sp)
|
||||
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
ERROR("Incomplete framebuffer");
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Draw background CGs.
|
||||
@@ -872,6 +1009,7 @@ void RE_render(struct sact_sprite *sp)
|
||||
continue;
|
||||
render_instance(inst, r, view_transform, DRAW_OPAQUE);
|
||||
}
|
||||
|
||||
// Render transparent instances, from farthest to nearest.
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = sorted_instances[i];
|
||||
@@ -880,6 +1018,9 @@ void RE_render(struct sact_sprite *sp)
|
||||
render_instance(inst, r, view_transform, DRAW_TRANSPARENT);
|
||||
}
|
||||
|
||||
// Render outlines.
|
||||
render_outlines(plugin, view_transform);
|
||||
|
||||
free(sorted_instances);
|
||||
|
||||
plugin->proj_transform[1][1] *= -1;
|
||||
@@ -935,10 +1076,18 @@ struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r
|
||||
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glUseProgram(r->shadow.program);
|
||||
glUniformMatrix4fv(r->shadow.world_transform, 1, GL_FALSE, view_matrix[0]);
|
||||
glUniformMatrix4fv(r->shadow.local_transform, 1, GL_FALSE, view_matrix[0]);
|
||||
glUniformMatrix4fv(r->shadow.view_transform, 1, GL_FALSE, proj_matrix[0]);
|
||||
glUniform1i(r->shadow.has_bones, GL_FALSE);
|
||||
|
||||
// Bone transforms are not used, but we still need to bind a UBO.
|
||||
GLuint bone_transforms_ubo;
|
||||
glGenBuffers(1, &bone_transforms_ubo);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, bone_transforms_ubo);
|
||||
glBufferData(GL_UNIFORM_BUFFER, MAX_BONES * sizeof(mat3x4), NULL, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, BONE_TRANSFORMS_BINDING, bone_transforms_ubo);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// Render the model.
|
||||
@@ -960,6 +1109,7 @@ struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, orig_fbo);
|
||||
glViewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]);
|
||||
|
||||
glDeleteBuffers(1, &bone_transforms_ubo);
|
||||
glDeleteTextures(1, &color_texture);
|
||||
return hd;
|
||||
}
|
||||
|
||||
+48
-101
@@ -18,7 +18,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
@@ -52,6 +51,7 @@ const char *asset_strtype(enum asset_type type)
|
||||
case ASSET_FLAT: return "Flat";
|
||||
case ASSET_PACT: return "Pact";
|
||||
case ASSET_DATA: return "Data";
|
||||
case ASSET_FLASH: return "Flash";
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
@@ -75,7 +75,6 @@ struct asset_manager_ald {
|
||||
struct asset_manager_afa {
|
||||
struct asset_manager manager;
|
||||
struct afa_archive *archives[MAX_ARCHIVES];
|
||||
struct hash_table *index;
|
||||
};
|
||||
|
||||
static struct asset_manager *assets[ASSET_TYPE_MAX] = {0};
|
||||
@@ -197,111 +196,59 @@ static bool afa_load_archive(struct asset_manager *_manager, const char *name)
|
||||
manager->archives[i] = manager->archives[i-1];
|
||||
}
|
||||
manager->archives[0] = ar;
|
||||
|
||||
// index needs to be rebuilt
|
||||
if (manager->index) {
|
||||
ht_free(manager->index);
|
||||
manager->index = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _afa_get_by_id(struct asset_manager *_manager, int id, struct afa_archive **ar_out, int *no_out)
|
||||
static bool afa_exists_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
uint32_t no = id - 1;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
if (no < manager->archives[i]->nr_files) {
|
||||
*ar_out = manager->archives[i];
|
||||
*no_out = no;
|
||||
if (archive_exists(&manager->archives[i]->ar, id - 1))
|
||||
return true;
|
||||
}
|
||||
no -= manager->archives[i]->nr_files;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool afa_exists_by_id(struct asset_manager *manager, int id)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
return _afa_get_by_id(manager, id, &ar, &no);
|
||||
}
|
||||
|
||||
static struct archive_data *afa_get_by_id(struct asset_manager *manager, int id)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
if (!_afa_get_by_id(manager, id, &ar, &no))
|
||||
return NULL;
|
||||
// XXX: we don't use archive_get here because it will return the wrong thing
|
||||
// for AFAv1 archives when not using integer indices
|
||||
struct archive_data *data = afa_entry_to_descriptor(ar, &ar->files[no]);
|
||||
if (data)
|
||||
archive_load_file(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void afa_index_archive(struct hash_table *index, struct afa_archive *ar, unsigned base)
|
||||
{
|
||||
for (unsigned i = 0; i < ar->nr_files; i++) {
|
||||
// normalize case and remove file extension from file name
|
||||
char *name = strdup(ar->files[i].name->text);
|
||||
char *dot = strrchr(name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
sjis_toupper(name);
|
||||
// add file to index
|
||||
ht_put(index, name, (void*)((uintptr_t)base + i + 1));
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _afa_get_by_name(struct asset_manager *_manager, const char *_name,
|
||||
struct afa_archive **ar_out, int *no_out, int *id_out)
|
||||
static struct archive_data *afa_get_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
// initialize index lazily
|
||||
if (!manager->index) {
|
||||
manager->index = ht_create(4096);
|
||||
unsigned base = 0;
|
||||
for (unsigned i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
afa_index_archive(manager->index, manager->archives[i], base);
|
||||
base += manager->archives[i]->nr_files;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
struct archive_data *data = archive_get(&manager->archives[i]->ar, id - 1);
|
||||
if (data)
|
||||
return data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct archive_data *afa_get_by_name(struct asset_manager *_manager, const char *name,
|
||||
int *id_out)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
|
||||
for (unsigned i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
struct archive_data *data;
|
||||
data = archive_get_by_basename(&manager->archives[i]->ar, name);
|
||||
if (data) {
|
||||
if (id_out)
|
||||
*id_out = data->no + 1;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
// normalize name and get id from index
|
||||
char *name = strdup(_name);
|
||||
sjis_toupper(name);
|
||||
char *dot = strrchr(name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
uintptr_t id = (uintptr_t)ht_get(manager->index, name, NULL);
|
||||
free(name);
|
||||
|
||||
if (!id)
|
||||
return false;
|
||||
|
||||
if (id_out)
|
||||
*id_out = id;
|
||||
return _afa_get_by_id(_manager, id, ar_out, no_out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool afa_exists_by_name(struct asset_manager *manager, const char *name, int *id_out)
|
||||
static bool afa_exists_by_name(struct asset_manager *_manager, const char *name, int *id_out)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
return _afa_get_by_name(manager, name, &ar, &no, id_out);
|
||||
}
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
|
||||
static struct archive_data *afa_get_by_name(struct asset_manager *manager, const char *name, int *id_out)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
if (!_afa_get_by_name(manager, name, &ar, &no, id_out))
|
||||
return NULL;
|
||||
return archive_get(&ar->ar, no);
|
||||
for (unsigned i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
if (archive_exists_by_basename(&manager->archives[i]->ar, name, id_out)) {
|
||||
if (id_out)
|
||||
*id_out += 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _ald_init(enum asset_type type, struct archive *ar)
|
||||
@@ -347,18 +294,14 @@ static void afa_init(enum asset_type type, char *file)
|
||||
if (!ar)
|
||||
ERROR("Failed to open AFA file: %s", archive_strerror(error));
|
||||
|
||||
if (id_indexed_afa) {
|
||||
_ald_init(type, &ar->ar);
|
||||
} else {
|
||||
struct asset_manager_afa *manager = xcalloc(1, sizeof(struct asset_manager_afa));
|
||||
manager->manager.load_archive = afa_load_archive;
|
||||
manager->manager.exists_by_id = afa_exists_by_id;
|
||||
manager->manager.exists_by_name = afa_exists_by_name;
|
||||
manager->manager.get_by_id = afa_get_by_id;
|
||||
manager->manager.get_by_name = afa_get_by_name;
|
||||
manager->archives[0] = ar;
|
||||
assets[type] = &manager->manager;
|
||||
}
|
||||
struct asset_manager_afa *manager = xcalloc(1, sizeof(struct asset_manager_afa));
|
||||
manager->manager.load_archive = afa_load_archive;
|
||||
manager->manager.exists_by_id = afa_exists_by_id;
|
||||
manager->manager.get_by_id = afa_get_by_id;
|
||||
manager->manager.exists_by_name = afa_exists_by_name;
|
||||
manager->manager.get_by_name = afa_get_by_name;
|
||||
manager->archives[0] = ar;
|
||||
assets[type] = &manager->manager;
|
||||
|
||||
free(file);
|
||||
}
|
||||
@@ -391,7 +334,7 @@ void asset_manager_init(void)
|
||||
// get ALD filenames
|
||||
while ((d_name = readdir_utf8(dir))) {
|
||||
// archive filename must begin with ain filename
|
||||
if (strncmp(base, d_name, base_len))
|
||||
if (strncasecmp(base, d_name, base_len))
|
||||
goto loop_next;
|
||||
const char *ext = file_extension(d_name);
|
||||
if (!ext)
|
||||
@@ -460,6 +403,8 @@ void asset_manager_init(void)
|
||||
afa_filenames[ASSET_FLAT] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "Pact.afa")) {
|
||||
afa_filenames[ASSET_PACT] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "AFF.afa")) {
|
||||
afa_filenames[ASSET_FLASH] = path_join(config.game_dir, d_name);
|
||||
}
|
||||
} else if (!strcasecmp(ext, "fnl")) {
|
||||
if (!config.fnl_path)
|
||||
@@ -479,6 +424,7 @@ void asset_manager_init(void)
|
||||
ald_init(ASSET_FLAT, ald_filenames[ASSET_FLAT], ald_count[ASSET_FLAT]);
|
||||
ald_init(ASSET_PACT, ald_filenames[ASSET_PACT], ald_count[ASSET_PACT]);
|
||||
ald_init(ASSET_DATA, ald_filenames[ASSET_DATA], ald_count[ASSET_DATA]);
|
||||
ald_init(ASSET_FLASH, ald_filenames[ASSET_FLASH], ald_count[ASSET_FLASH]);
|
||||
|
||||
// open AFA archives
|
||||
afa_init(ASSET_BGM, afa_filenames[ASSET_BGM]);
|
||||
@@ -488,4 +434,5 @@ void asset_manager_init(void)
|
||||
afa_init(ASSET_FLAT, afa_filenames[ASSET_FLAT]);
|
||||
afa_init(ASSET_PACT, afa_filenames[ASSET_PACT]);
|
||||
afa_init(ASSET_DATA, afa_filenames[ASSET_DATA]);
|
||||
afa_init(ASSET_FLASH, afa_filenames[ASSET_FLASH]);
|
||||
}
|
||||
|
||||
+64
-7
@@ -43,8 +43,8 @@ void audio_init(void)
|
||||
if (audio_initialized)
|
||||
return;
|
||||
|
||||
id_pool_init(&wav);
|
||||
id_pool_init(&bgm);
|
||||
id_pool_init(&wav, 0);
|
||||
id_pool_init(&bgm, 0);
|
||||
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
anonymous_channels[i] = -1;
|
||||
@@ -54,6 +54,19 @@ void audio_init(void)
|
||||
audio_initialized = true;
|
||||
}
|
||||
|
||||
void audio_reset(void)
|
||||
{
|
||||
for (int i = id_pool_get_first(&wav); i >= 0; i = id_pool_get_next(&wav, i)) {
|
||||
wav_unprepare(i);
|
||||
}
|
||||
for (int i = id_pool_get_first(&bgm); i >= 0; i = id_pool_get_next(&bgm, i)) {
|
||||
bgm_unprepare(i);
|
||||
}
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
anonymous_channels[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void audio_update(void)
|
||||
{
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
@@ -85,6 +98,25 @@ bool audio_play_sound(int sound_no)
|
||||
return false;
|
||||
}
|
||||
|
||||
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))
|
||||
return false;
|
||||
if (!wav_play(ch)) {
|
||||
wav_unprepare(ch);
|
||||
return false;
|
||||
}
|
||||
anonymous_channels[i] = ch;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
WARNING("Failed to allocate anonymous audio channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wav_exists(int no) { return asset_exists(ASSET_SOUND, no); }
|
||||
bool bgm_exists(int no) { return asset_exists(ASSET_BGM, no); }
|
||||
|
||||
@@ -104,11 +136,10 @@ int bgm_prepare(int id, int no) { return audio_prepare(&bgm, id, ASSET_BGM, no);
|
||||
|
||||
static int audio_unprepare(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
struct channel *ch = id_pool_release(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
channel_close(ch);
|
||||
id_pool_release(pool, id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -330,10 +361,36 @@ 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); }
|
||||
|
||||
//int wav_get_group_num(int channel);
|
||||
static int audio_get_data_no(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return -1;
|
||||
return channel_get_data_no(ch);
|
||||
}
|
||||
|
||||
int wav_get_group_num(int id)
|
||||
{
|
||||
int no = audio_get_data_no(&wav, id);
|
||||
if (no < 0)
|
||||
return 0;
|
||||
return wav_get_group_num_from_data_num(no);
|
||||
}
|
||||
//int bgm_get_group_num(int channel);
|
||||
//int wav_prepare_from_file(int channel, char *filename);
|
||||
//int bgm_prepare_from_file(int channel, char *filename);
|
||||
|
||||
static int audio_prepare_from_file(struct id_pool *pool, int id, char *filename)
|
||||
{
|
||||
if (id < 0)
|
||||
return 0;
|
||||
struct channel *ch = channel_open_file(filename);
|
||||
struct channel *old = id_pool_set(pool, id, ch);
|
||||
if (old)
|
||||
channel_close(old);
|
||||
return !!ch;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
+2
-2
@@ -18,9 +18,9 @@
|
||||
#include <stdlib.h>
|
||||
#include "system4.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
|
||||
#include "mixer.h"
|
||||
#include "little_endian.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define BGI_MAX 100
|
||||
@@ -31,7 +31,7 @@ static struct bgi bgi_data[BGI_MAX];
|
||||
static char *bgi_gets(char *buf, int n, FILE *fp)
|
||||
{
|
||||
char *s = buf;
|
||||
int c;
|
||||
int c = EOF;
|
||||
while (--n > 0 && (c = fgetc(fp)) != EOF) {
|
||||
c = c >> 4 | (c & 0xf) << 4; // decrypt
|
||||
*s++ = c;
|
||||
|
||||
+63
-22
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/archive.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "asset_manager.h"
|
||||
#include "audio.h"
|
||||
@@ -238,6 +239,7 @@ static int refill_stream(sts_mixer_sample_t *sample, void *data)
|
||||
ch->fade.elapsed += frames_read;
|
||||
if (ch->fade.elapsed >= ch->fade.frames) {
|
||||
ch->fade.fading = false;
|
||||
ch->volume = ch->fade.end_volume * 100.0;
|
||||
if (ch->fade.stop) {
|
||||
cb_seek(ch, 0);
|
||||
r = STS_STREAM_COMPLETE;
|
||||
@@ -437,6 +439,11 @@ int channel_get_time_length(struct channel *ch)
|
||||
return muldiv(ch->info.frames, 1000, ch->info.samplerate);
|
||||
}
|
||||
|
||||
int channel_get_data_no(struct channel *ch)
|
||||
{
|
||||
return ch->dfile ? ch->dfile->no : -1;
|
||||
}
|
||||
|
||||
static sf_count_t channel_vio_get_filelen(void *data)
|
||||
{
|
||||
return ((struct channel*)data)->dfile->size;
|
||||
@@ -519,6 +526,8 @@ struct channel *channel_open(enum asset_type type, int no)
|
||||
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;
|
||||
@@ -526,20 +535,15 @@ struct channel *channel_open(enum asset_type type, int no)
|
||||
return ch;
|
||||
}
|
||||
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
static bool init_channel(struct channel *ch)
|
||||
{
|
||||
struct channel *ch = xcalloc(1, sizeof(struct channel));
|
||||
ch->dfile = dfile;
|
||||
|
||||
// open file
|
||||
ch->file = sf_open_virtual(&channel_vio, SFM_READ, &ch->info, ch);
|
||||
if (sf_error(ch->file) != SF_ERR_NO_ERROR) {
|
||||
WARNING("sf_open_virtual failed: %s", sf_strerror(ch->file));
|
||||
goto error;
|
||||
return false;
|
||||
}
|
||||
if (ch->info.channels > 2) {
|
||||
WARNING("Audio file has more than 2 channels");
|
||||
goto error;
|
||||
return false;
|
||||
}
|
||||
|
||||
// create stream
|
||||
@@ -554,23 +558,57 @@ struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
ch->volume = 100;
|
||||
ch->loop_start = 0;
|
||||
ch->loop_end = ch->info.frames;
|
||||
ch->loop_count = 0;
|
||||
ch->loop_count = 1;
|
||||
ch->mixer_no = 0;
|
||||
|
||||
ch->no = -1;
|
||||
return ch;
|
||||
return true;
|
||||
}
|
||||
|
||||
error:
|
||||
archive_free_data(dfile);
|
||||
free(ch);
|
||||
return NULL;
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
{
|
||||
struct channel *ch = xcalloc(1, sizeof(struct channel));
|
||||
ch->dfile = dfile;
|
||||
|
||||
// open file
|
||||
ch->file = sf_open_virtual(&channel_vio, SFM_READ, &ch->info, ch);
|
||||
|
||||
if (!init_channel(ch)) {
|
||||
archive_free_data(dfile);
|
||||
if (ch->file)
|
||||
sf_close(ch->file);
|
||||
free(ch);
|
||||
return NULL;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
struct channel *channel_open_file(const char *path)
|
||||
{
|
||||
struct channel *ch = xcalloc(1, sizeof(struct channel));
|
||||
#ifdef _WIN32
|
||||
wchar_t *wpath = utf8_to_wchar(path);
|
||||
ch->file = sf_wchar_open(wpath, SFM_READ, &ch->info);
|
||||
free(wpath);
|
||||
#else
|
||||
ch->file = sf_open(path, SFM_READ, &ch->info);
|
||||
#endif
|
||||
|
||||
if (!init_channel(ch)) {
|
||||
if (ch->file)
|
||||
sf_close(ch->file);
|
||||
free(ch);
|
||||
return NULL;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
void channel_close(struct channel *ch)
|
||||
{
|
||||
channel_stop(ch);
|
||||
sf_close(ch->file);
|
||||
archive_free_data(ch->dfile);
|
||||
if (ch->dfile)
|
||||
archive_free_data(ch->dfile);
|
||||
free(ch);
|
||||
}
|
||||
|
||||
@@ -586,6 +624,7 @@ void mixer_init(void)
|
||||
mixers[0].name = strdup("Music");
|
||||
mixers[1].name = strdup("Sound");
|
||||
mixers[2].name = strdup("Master");
|
||||
master = &mixers[2];
|
||||
} else {
|
||||
// NOTE: Older games don't have an explicit master channel.
|
||||
// We add an extra mixer in this case for consistency.
|
||||
@@ -663,19 +702,21 @@ void mixer_init(void)
|
||||
|
||||
int mixer_get_numof(void)
|
||||
{
|
||||
return nr_mixers;
|
||||
// Return the number of mixers specified in System40.ini, even if
|
||||
// xsystem4 added more mixers.
|
||||
return config.mixer_nr_channels;
|
||||
}
|
||||
|
||||
const char *mixer_get_name(int n)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return NULL;
|
||||
return mixers[n].name;
|
||||
}
|
||||
|
||||
int mixer_set_name(int n, const char *name)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
free(mixers[n].name);
|
||||
mixers[n].name = strdup(name);
|
||||
@@ -684,7 +725,7 @@ int mixer_set_name(int n, const char *name)
|
||||
|
||||
int mixer_get_volume(int n, int *volume)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers) {
|
||||
if (n < 0 || n >= config.mixer_nr_channels) {
|
||||
return 0;
|
||||
}
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
@@ -695,7 +736,7 @@ int mixer_get_volume(int n, int *volume)
|
||||
|
||||
int mixer_set_volume(int n, int volume)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
mixers[n].mixer.gain = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
@@ -704,7 +745,7 @@ int mixer_set_volume(int n, int volume)
|
||||
}
|
||||
int mixer_get_mute(int n, int *mute)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
*mute = mixers[n].muted;
|
||||
return 1;
|
||||
@@ -712,7 +753,7 @@ int mixer_get_mute(int n, int *mute)
|
||||
|
||||
int mixer_set_mute(int n, int mute)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
mixers[n].muted = !!mute;
|
||||
return 1;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Base64 encoding/decoding (RFC1341)
|
||||
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
*/
|
||||
|
||||
#include "system4.h"
|
||||
#include "base64.h"
|
||||
|
||||
static const unsigned char base64_table[65] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/**
|
||||
* base64_encode - Base64 encode
|
||||
* @src: Data to be encoded
|
||||
* @len: Length of the data to be encoded
|
||||
* @out_len: Pointer to output length variable, or %NULL if not used
|
||||
* Returns: Allocated buffer of out_len bytes of encoded data,
|
||||
* or %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer. Returned buffer is
|
||||
* nul terminated to make it easier to use as a C string. The nul terminator is
|
||||
* not included in out_len.
|
||||
*/
|
||||
unsigned char *base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len)
|
||||
{
|
||||
unsigned char *out, *pos;
|
||||
const unsigned char *end, *in;
|
||||
size_t olen;
|
||||
int line_len;
|
||||
|
||||
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||||
olen += olen / 72; /* line feeds */
|
||||
olen++; /* nul termination */
|
||||
if (olen < len)
|
||||
return NULL; /* integer overflow */
|
||||
out = xmalloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
end = src + len;
|
||||
in = src;
|
||||
pos = out;
|
||||
line_len = 0;
|
||||
while (end - in >= 3) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||||
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||||
*pos++ = base64_table[in[2] & 0x3f];
|
||||
in += 3;
|
||||
line_len += 4;
|
||||
if (line_len >= 72) {
|
||||
*pos++ = '\n';
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (end - in) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
if (end - in == 1) {
|
||||
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||||
*pos++ = '=';
|
||||
} else {
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||||
(in[1] >> 4)];
|
||||
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||||
}
|
||||
*pos++ = '=';
|
||||
line_len += 4;
|
||||
}
|
||||
|
||||
if (line_len)
|
||||
*pos++ = '\n';
|
||||
|
||||
*pos = '\0';
|
||||
if (out_len)
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
+142
-15
@@ -24,7 +24,9 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "system4/dasm.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
@@ -33,11 +35,12 @@
|
||||
#include "vm/page.h"
|
||||
|
||||
#include "debugger.h"
|
||||
#include "little_endian.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
bool dbg_dap = false;
|
||||
bool dbg_enabled = true;
|
||||
bool dbg_start_in_debugger = false;
|
||||
struct dbg_info *dbg_info = NULL;
|
||||
unsigned dbg_current_frame = 0;
|
||||
static jmp_buf dbg_continuation;
|
||||
|
||||
@@ -72,24 +75,50 @@ void dbg_start(void(*fun)(void*), void *data)
|
||||
fun(data);
|
||||
}
|
||||
|
||||
static void _dbg_repl(void *_)
|
||||
static void _dbg_repl(void *stop)
|
||||
{
|
||||
dbg_cmd_repl();
|
||||
if (dbg_dap)
|
||||
dbg_dap_repl(stop);
|
||||
else
|
||||
dbg_cmd_repl();
|
||||
}
|
||||
|
||||
void dbg_repl(void)
|
||||
void dbg_repl(enum dbg_stop_type type, const char *msg)
|
||||
{
|
||||
if (!dbg_enabled)
|
||||
return;
|
||||
dbg_start(_dbg_repl, NULL);
|
||||
|
||||
struct dbg_stop stop = {
|
||||
.type = type,
|
||||
.message = msg,
|
||||
};
|
||||
|
||||
dbg_start(_dbg_repl, &stop);
|
||||
}
|
||||
|
||||
void dbg_init(void)
|
||||
void dbg_init(const char *debug_info_path)
|
||||
{
|
||||
dbg_cmd_init();
|
||||
if (debug_info_path) {
|
||||
dbg_info = dbg_info_load(debug_info_path);
|
||||
} else {
|
||||
// Try to load from the default location
|
||||
char *path = gamedir_path("src/debug_info.json");
|
||||
if (file_exists(path)) {
|
||||
dbg_info = dbg_info_load(path);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
if (dbg_dap)
|
||||
dbg_dap_init();
|
||||
else
|
||||
dbg_cmd_init();
|
||||
#ifdef HAVE_SCHEME
|
||||
dbg_scm_init();
|
||||
#endif
|
||||
if (!dbg_dap && dbg_start_in_debugger) {
|
||||
dbg_repl(DBG_STOP_PAUSE, "");
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_fini(void)
|
||||
@@ -135,6 +164,38 @@ static struct breakpoint *get_breakpoint(uint32_t addr)
|
||||
return ht_get_int(bp_table, addr, NULL);
|
||||
}
|
||||
|
||||
bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*))
|
||||
{
|
||||
struct breakpoint *bp = get_breakpoint(addr);
|
||||
if (!bp)
|
||||
return false;
|
||||
if (free_data)
|
||||
free_data(bp->data);
|
||||
delete_breakpoint(addr, bp);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct dbg_foreach_data {
|
||||
void (*fun)(int addr, struct breakpoint*, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
void dbg_foreach_breakpoint_cb(struct ht_slot *slot, void *d_)
|
||||
{
|
||||
if (!slot->value)
|
||||
return;
|
||||
struct dbg_foreach_data *d = d_;
|
||||
d->fun(slot->ikey, slot->value, d->data);
|
||||
}
|
||||
|
||||
void dbg_foreach_breakpoint(void (*fun)(int addr, struct breakpoint*, void *data), void *data)
|
||||
{
|
||||
if (!bp_table)
|
||||
return;
|
||||
struct dbg_foreach_data d = { fun, data };
|
||||
ht_foreach(bp_table, dbg_foreach_breakpoint_cb, &d);
|
||||
}
|
||||
|
||||
bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data)
|
||||
{
|
||||
char *name = utf2sjis(_name, 0);
|
||||
@@ -157,7 +218,7 @@ bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*
|
||||
LittleEndian_putW(ain->code, f->address, BREAKPOINT | bp->restore_op);
|
||||
add_breakpoint(f->address, bp);
|
||||
|
||||
printf("Set breakpoint at function '%s' (0x%08x)\n", display_utf0(_name), f->address);
|
||||
log_message("debug", "Set breakpoint at function '%s' (0x%08x)\n", display_utf0(_name), f->address);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -185,7 +246,7 @@ bool dbg_set_address_breakpoint(uint32_t address, void(*cb)(struct breakpoint*),
|
||||
LittleEndian_putW(ain->code, address, BREAKPOINT | bp->restore_op);
|
||||
add_breakpoint(address, bp);
|
||||
|
||||
printf("Set breakpoint at 0x%08x\n", address);
|
||||
log_message("debug", "Set breakpoint at 0x%08x\n", address);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -196,7 +257,8 @@ static void dbg_step_breakpoint_cb(struct breakpoint *bp)
|
||||
if ((intptr_t)bp->data != call_stack_ptr)
|
||||
return;
|
||||
delete_breakpoint(instr_ptr, bp);
|
||||
dbg_cmd_repl();
|
||||
struct dbg_stop stop = { DBG_STOP_STEP, NULL };
|
||||
_dbg_repl(&stop);
|
||||
}
|
||||
|
||||
static void dbg_set_step_breakpoint(int32_t address, int call_index)
|
||||
@@ -384,8 +446,9 @@ static void _dbg_handle_breakpoint(void *data)
|
||||
if (bp->cb) {
|
||||
bp->cb(bp);
|
||||
} else {
|
||||
printf("%s\n", bp->message);
|
||||
dbg_cmd_repl();
|
||||
log_message("debug", "%s\n", bp->message);
|
||||
struct dbg_stop stop = { DBG_STOP_BREAKPOINT, NULL };
|
||||
_dbg_repl(&stop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,8 +471,14 @@ void dbg_print_frame(unsigned no)
|
||||
unsigned cs_no = call_stack_ptr - (1 + no);
|
||||
struct ain_function *f = &ain->functions[call_stack[cs_no].fno];
|
||||
uint32_t addr = no ? call_stack[cs_no+1].call_address : instr_ptr;
|
||||
printf("%c #%d 0x%08x in %s\n", no == dbg_current_frame ? '*' : ' ',
|
||||
sys_message("%c #%d 0x%08x in %s", no == dbg_current_frame ? '*' : ' ',
|
||||
no, addr, display_sjis0(f->name));
|
||||
int file, line;
|
||||
if (dbg_info && dbg_info_addr2line(dbg_info, addr, &file, &line)) {
|
||||
sys_message(" at %s:%d\n", display_utf0(dbg_info_source_name(dbg_info, file)), line);
|
||||
} else {
|
||||
sys_message("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_print_stack_trace(void)
|
||||
@@ -570,7 +639,7 @@ error:
|
||||
static union vm_value dbg_eval_object(struct parse_object *object, struct ain_type *type_out)
|
||||
{
|
||||
if (!strcmp(object->ident->text, "this")) {
|
||||
int32_t page_i = call_stack[call_stack_ptr-1].struct_page;
|
||||
int32_t page_i = call_stack[call_stack_ptr- (dbg_current_frame + 1)].struct_page;
|
||||
if (page_i < 0) {
|
||||
DBG_ERROR("'this' used outside of method");
|
||||
goto error;
|
||||
@@ -695,6 +764,8 @@ static void dbg_indent(struct string **s, int indent_level)
|
||||
}
|
||||
static struct string *_dbg_value_to_string(struct ain_type *type, union vm_value value,
|
||||
int recursive, int indent);
|
||||
static struct string *_dbg_variable_to_string(
|
||||
struct ain_type *type, struct page *page, int slot, int recursive, int indent);
|
||||
|
||||
static struct string *dbg_string_to_string(struct string *str)
|
||||
{
|
||||
@@ -719,10 +790,12 @@ static struct string *dbg_struct_to_string(struct ain_type *type, union vm_value
|
||||
struct string *out = cstr_to_string("{\n");
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
struct ain_variable *m = &ain->structures[type->struc].members[i];
|
||||
if (m->type.data == AIN_VOID)
|
||||
continue;
|
||||
dbg_indent(&out, indent + 1);
|
||||
string_append_cstr(&out, m->name, strlen(m->name));
|
||||
string_append_cstr(&out, " = ", 3);
|
||||
struct string *tmp = _dbg_value_to_string(&m->type, page->values[i],
|
||||
struct string *tmp = _dbg_variable_to_string(&m->type, page, i,
|
||||
recursive - 1, indent + 1);
|
||||
string_append(&out, tmp);
|
||||
free_string(tmp);
|
||||
@@ -802,6 +875,34 @@ struct string *dbg_value_to_string(struct ain_type *type, union vm_value value,
|
||||
return _dbg_value_to_string(type, value, recursive, 0);
|
||||
}
|
||||
|
||||
static struct string *_dbg_variable_to_string(
|
||||
struct ain_type *type, struct page *page, int slot, int recursive, int indent)
|
||||
{
|
||||
enum ain_data_type value_type;
|
||||
switch (type->data) {
|
||||
case AIN_REF_INT: value_type = AIN_INT; break;
|
||||
case AIN_REF_LONG_INT: value_type = AIN_LONG_INT; break;
|
||||
case AIN_REF_BOOL: value_type = AIN_BOOL; break;
|
||||
case AIN_REF_FLOAT: value_type = AIN_FLOAT; break;
|
||||
default:
|
||||
return _dbg_value_to_string(type, page->values[slot], recursive, indent);
|
||||
}
|
||||
|
||||
// get referenced value
|
||||
int pageno = page->values[slot].i;
|
||||
int varno = page->values[slot + 1].i;
|
||||
if (pageno < 0) {
|
||||
return cstr_to_string("NULL");
|
||||
}
|
||||
struct ain_type t = {.data = value_type};
|
||||
return _dbg_value_to_string(&t, heap[pageno].page->values[varno], recursive, indent);
|
||||
}
|
||||
|
||||
struct string *dbg_variable_to_string(struct ain_type *type, struct page *page, int slot, int recursive)
|
||||
{
|
||||
return _dbg_variable_to_string(type, page, slot, recursive, 0);
|
||||
}
|
||||
|
||||
// the maximum number of instructions preceeding the instruction pointer to be displayed
|
||||
#define DASM_REWIND 16
|
||||
// the number of instructions following the instruction pointer to be displayed
|
||||
@@ -1065,3 +1166,29 @@ void dbg_print_vm_state(void)
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_print_current_line(void)
|
||||
{
|
||||
if (call_stack_ptr < 1) {
|
||||
DBG_ERROR("VM not running");
|
||||
return;
|
||||
}
|
||||
if (!dbg_info) {
|
||||
DBG_ERROR("No debug information available");
|
||||
return;
|
||||
}
|
||||
|
||||
int file, line;
|
||||
if (!dbg_info_addr2line(dbg_info, instr_ptr, &file, &line)) {
|
||||
DBG_ERROR("No line number information for address: 0x%08x", instr_ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *src_line = dbg_info_source_line(dbg_info, file, line);
|
||||
if (!src_line) {
|
||||
DBG_ERROR("No source text for %s:%d", display_utf0(dbg_info_source_name(dbg_info, file)), line);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s:%d\t%s\n", display_utf0(dbg_info_source_name(dbg_info, file)), line, display_utf1(src_line));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
/* Copyright (C) 2024 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 <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "system4/file.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "debugger.h"
|
||||
|
||||
struct mapping {
|
||||
int addr;
|
||||
int file;
|
||||
int line;
|
||||
};
|
||||
|
||||
struct source_content {
|
||||
int nr_lines;
|
||||
char *lines[];
|
||||
};
|
||||
|
||||
struct dbg_info {
|
||||
char *src_root;
|
||||
int nr_sources;
|
||||
char **sources;
|
||||
struct source_content **source_contents;
|
||||
int nr_mappings;
|
||||
struct mapping *mappings;
|
||||
};
|
||||
|
||||
struct dbg_info *dbg_info_load(const char *path)
|
||||
{
|
||||
char *json_text = file_read(path, NULL);
|
||||
if (!json_text) {
|
||||
DBG_ERROR("Cannot load debug information file %s: %s", path, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *root = cJSON_Parse(json_text);
|
||||
free(json_text);
|
||||
|
||||
cJSON *version = cJSON_GetObjectItem(root, "version");
|
||||
if (!cJSON_IsString(version) || strcmp(version->valuestring, "alpha-1")) {
|
||||
DBG_ERROR("%s: Unsupported debug information format", display_utf0(path));
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dbg_info *info = xcalloc(1, sizeof(struct dbg_info));
|
||||
cJSON *sources = cJSON_GetObjectItem(root, "sources");
|
||||
info->nr_sources = cJSON_GetArraySize(sources);
|
||||
info->sources = xcalloc(info->nr_sources, sizeof(char *));
|
||||
cJSON *source;
|
||||
int i = 0;
|
||||
cJSON_ArrayForEach(source, sources) {
|
||||
info->sources[i++] = xstrdup(source->valuestring);
|
||||
}
|
||||
|
||||
cJSON *mappings = cJSON_GetObjectItem(root, "mappings");
|
||||
info->nr_mappings = cJSON_GetArraySize(mappings);
|
||||
info->mappings = xcalloc(info->nr_mappings, sizeof(struct mapping));
|
||||
|
||||
struct mapping *m = info->mappings;
|
||||
cJSON *item;
|
||||
cJSON_ArrayForEach(item, mappings) {
|
||||
m->addr = cJSON_GetArrayItem(item, 0)->valueint;
|
||||
m->file = cJSON_GetArrayItem(item, 1)->valueint;
|
||||
m->line = cJSON_GetArrayItem(item, 2)->valueint;
|
||||
m++;
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
|
||||
info->src_root = xstrdup(path_dirname(path));
|
||||
info->source_contents = xcalloc(info->nr_sources, sizeof(struct source_content *));
|
||||
return info;
|
||||
}
|
||||
|
||||
static struct source_content *load_source_content(const char *path)
|
||||
{
|
||||
char *text = file_read(path, NULL);
|
||||
if (!text) {
|
||||
DBG_ERROR("Cannot load source file %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nr_lines = 1; // count the last line even if it doesn't end with '\n'
|
||||
for (char *p = text; *p; p++) {
|
||||
if (*p == '\n')
|
||||
nr_lines++;
|
||||
}
|
||||
struct source_content *content = xcalloc(1, sizeof(struct source_content) + nr_lines * sizeof(char *));
|
||||
content->nr_lines = nr_lines;
|
||||
for (int i = 0;; i++) {
|
||||
content->lines[i] = text;
|
||||
char *next = strchr(text, '\n');
|
||||
if (!next)
|
||||
break;
|
||||
*next = '\0';
|
||||
text = next + 1;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
const char *dbg_info_source_name(const struct dbg_info *info, int file)
|
||||
{
|
||||
if (file < 0 || file >= info->nr_sources)
|
||||
return NULL;
|
||||
return info->sources[file];
|
||||
}
|
||||
|
||||
char *dbg_info_source_path(const struct dbg_info *info, int file)
|
||||
{
|
||||
if (file < 0 || file >= info->nr_sources)
|
||||
return NULL;
|
||||
return path_join(info->src_root, info->sources[file]);
|
||||
}
|
||||
|
||||
const char *dbg_info_source_line(const struct dbg_info *info, int file, int line)
|
||||
{
|
||||
if (file < 0 || file >= info->nr_sources)
|
||||
return NULL;
|
||||
if (!info->source_contents[file]) {
|
||||
char *path = dbg_info_source_path(info, file);
|
||||
info->source_contents[file] = load_source_content(path);
|
||||
free(path);
|
||||
if (!info->source_contents[file])
|
||||
return NULL;
|
||||
}
|
||||
if (line <= 0 || line > info->source_contents[file]->nr_lines)
|
||||
return NULL;
|
||||
return info->source_contents[file]->lines[line - 1];
|
||||
}
|
||||
|
||||
int dbg_info_find_file(const struct dbg_info *info, const char *filename)
|
||||
{
|
||||
if (is_absolute_path(filename)) {
|
||||
char *target = realpath_utf8(filename); // Convert '\\' to '/' on Windows
|
||||
char *prefix = realpath_utf8(info->src_root);
|
||||
for (int i = 0; i < info->nr_sources; i++) {
|
||||
char *path = path_join(prefix, info->sources[i]);
|
||||
if (!strcasecmp(path, target)) {
|
||||
free(path);
|
||||
free(prefix);
|
||||
free(target);
|
||||
return i;
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
free(prefix);
|
||||
free(target);
|
||||
} else {
|
||||
for (int i = 0; i < info->nr_sources; i++) {
|
||||
if (!strcasecmp(info->sources[i], filename) ||
|
||||
!strcasecmp(path_basename(info->sources[i]), filename)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool dbg_info_addr2line(const struct dbg_info *info, int addr, int *file, int *line)
|
||||
{
|
||||
// Find the last mapping whose address is less than or equal to `addr`.
|
||||
int left = 0, right = info->nr_mappings;
|
||||
while (left < right) {
|
||||
int mid = (left + right) / 2;
|
||||
if (info->mappings[mid].addr <= addr)
|
||||
left = mid + 1;
|
||||
else
|
||||
right = mid;
|
||||
}
|
||||
if (left == 0) {
|
||||
assert(info->nr_mappings == 0 || addr < info->mappings[0].addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(info->mappings[left - 1].addr <= addr);
|
||||
assert(left == info->nr_mappings || addr < info->mappings[left].addr);
|
||||
|
||||
if (file)
|
||||
*file = info->mappings[left - 1].file;
|
||||
if (line)
|
||||
*line = info->mappings[left - 1].line;
|
||||
return true;
|
||||
}
|
||||
|
||||
int dbg_info_line2addr(const struct dbg_info *info, int file, int line)
|
||||
{
|
||||
if (file < 0 || file >= info->nr_sources || line <= 0)
|
||||
return -1;
|
||||
|
||||
// Find the first mapping whose file is `file` and line is greater than or equal to `line`.
|
||||
int left = 0, right = info->nr_mappings;
|
||||
while (left < right) {
|
||||
int mid = (left + right) / 2;
|
||||
if (info->mappings[mid].file < file || (info->mappings[mid].file == file && info->mappings[mid].line < line))
|
||||
left = mid + 1;
|
||||
else
|
||||
right = mid;
|
||||
}
|
||||
if (left == info->nr_mappings) {
|
||||
assert(info->nr_mappings == 0 || info->mappings[info->nr_mappings - 1].file < file ||
|
||||
(info->mappings[info->nr_mappings - 1].file == file && info->mappings[info->nr_mappings - 1].line < line));
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(left == 0 || info->mappings[left - 1].file < file ||
|
||||
(info->mappings[left - 1].file == file && info->mappings[left - 1].line < line));
|
||||
if (file < info->mappings[left].file)
|
||||
return -1;
|
||||
assert(file == info->mappings[left].file && line <= info->mappings[left].line);
|
||||
|
||||
return info->mappings[left].addr;
|
||||
}
|
||||
+80
-16
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
@@ -35,11 +36,18 @@
|
||||
|
||||
#include "scene.h"
|
||||
#include "debugger.h"
|
||||
#include "little_endian.h"
|
||||
#include "input.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
struct dbg_cmd_node;
|
||||
static bool stepping = false;
|
||||
static enum {
|
||||
NOT_STEPPING = 0,
|
||||
STEPPING_INTO,
|
||||
STEPPING_OVER,
|
||||
STEPPING_FINISH
|
||||
} stepping = NOT_STEPPING;
|
||||
static int stepping_file = 0;
|
||||
static int stepping_line = 0;
|
||||
|
||||
struct dbg_cmd_list {
|
||||
unsigned nr_commands;
|
||||
@@ -66,17 +74,33 @@ static void dbg_cmd_backtrace(unsigned nr_args, char **args)
|
||||
|
||||
static void dbg_cmd_breakpoint(unsigned nr_args, char **args)
|
||||
{
|
||||
int addr = strtol(args[0], NULL, 0);
|
||||
if (addr > 0) {
|
||||
if (nr_args == 1) {
|
||||
int addr = strtol(args[0], NULL, 0);
|
||||
if (addr > 0) {
|
||||
dbg_set_address_breakpoint(addr, NULL, NULL);
|
||||
} else {
|
||||
dbg_set_function_breakpoint(args[0], NULL, NULL);
|
||||
}
|
||||
} else if (nr_args == 2 && dbg_info) {
|
||||
int file = dbg_info_find_file(dbg_info, args[0]);
|
||||
if (file < 0) {
|
||||
DBG_ERROR("No such file: %s", args[0]);
|
||||
return;
|
||||
}
|
||||
int line = atoi(args[1]);
|
||||
int addr = dbg_info_line2addr(dbg_info, file, line);
|
||||
if (addr < 0) {
|
||||
DBG_ERROR("No such line: %s:%d", args[0], line);
|
||||
return;
|
||||
}
|
||||
dbg_set_address_breakpoint(addr, NULL, NULL);
|
||||
} else {
|
||||
dbg_set_function_breakpoint(args[0], NULL, NULL);
|
||||
DBG_ERROR("Invalid breakpoint command");
|
||||
}
|
||||
}
|
||||
|
||||
static void dbg_cmd_continue(unsigned nr_args, char **args)
|
||||
{
|
||||
stepping = false;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -102,7 +126,9 @@ static void dbg_cmd_locals(unsigned nr_args, char **args)
|
||||
struct page *page = heap_get_page(call_stack[call_stack_ptr - (frame_no+1)].page_slot);
|
||||
struct ain_function *f = &ain->functions[page->index];
|
||||
for (int i = 0; i < f->nr_vars; i++) {
|
||||
struct string *value = dbg_value_to_string(&f->vars[i].type, page->values[i], 1);
|
||||
if (f->vars[i].type.data == AIN_VOID)
|
||||
continue;
|
||||
struct string *value = dbg_variable_to_string(&f->vars[i].type, page, i, 1);
|
||||
printf("[%d] %s: %s\n", i, display_sjis0(f->vars[i].name), display_sjis1(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
@@ -114,7 +140,9 @@ static void log_handler(struct breakpoint *bp)
|
||||
struct page *locals = local_page();
|
||||
printf("%s(", display_sjis0(f->name));
|
||||
for (int i = 0; i < f->nr_args; i++) {
|
||||
struct string *value = dbg_value_to_string(&f->vars[i].type, locals->values[i], 1);
|
||||
if (f->vars[i].type.data == AIN_VOID)
|
||||
continue;
|
||||
struct string *value = dbg_variable_to_string(&f->vars[i].type, locals, i, 1);
|
||||
printf(i > 0 ? ", %s" : "%s", display_sjis0(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
@@ -151,7 +179,9 @@ static void dbg_cmd_members(unsigned nr_args, char **args)
|
||||
assert(page->nr_vars == s->nr_members);
|
||||
|
||||
for (int i = 0; i < s->nr_members; i++) {
|
||||
struct string *value = dbg_value_to_string(&s->members[i].type, page->values[i], 1);
|
||||
if (s->members[i].type.data == AIN_VOID)
|
||||
continue;
|
||||
struct string *value = dbg_variable_to_string(&s->members[i].type, page, i, 1);
|
||||
printf("[%d] %s: %s\n", i, display_sjis0(s->members[i].name), display_sjis1(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
@@ -183,21 +213,29 @@ static void dbg_cmd_scene(unsigned nr_args, char **args)
|
||||
|
||||
static void dbg_cmd_next(unsigned nr_args, char **args)
|
||||
{
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info)
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &stepping_file, &stepping_line);
|
||||
|
||||
if (!dbg_set_step_over_breakpoint()) {
|
||||
DBG_ERROR("Can't step over this instruction");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
stepping = STEPPING_OVER;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
static void dbg_cmd_step(unsigned nr_args, char **args)
|
||||
{
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info)
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &stepping_file, &stepping_line);
|
||||
|
||||
if (!dbg_set_step_into_breakpoint()) {
|
||||
DBG_ERROR("Can't step into this instruction");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
stepping = STEPPING_INTO;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -207,7 +245,7 @@ static void dbg_cmd_finish(unsigned nr_args, char **args)
|
||||
DBG_ERROR("Can't set finish breakpoint from this location");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
stepping = STEPPING_FINISH;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -225,7 +263,7 @@ static void dbg_cmd_vm_state(unsigned nr_args, char **args)
|
||||
|
||||
static struct dbg_cmd dbg_default_commands[] = {
|
||||
{ "backtrace", "bt", NULL, "Display stack trace", 0, 0, dbg_cmd_backtrace },
|
||||
{ "breakpoint", "bp", "<function-or-address>", "Set breakpoint at a function or address", 1, 1, dbg_cmd_breakpoint },
|
||||
{ "breakpoint", "bp", "<function> | <address> | <file> <line>", "Set breakpoint", 1, 2, dbg_cmd_breakpoint },
|
||||
{ "continue", "c", NULL, "Resume execution", 0, 0, dbg_cmd_continue },
|
||||
{ "finish", "fin", NULL, "Execute until the current function returns", 0, 0, dbg_cmd_finish },
|
||||
{ "frame", "f", "<frame-number>", "Set the current frame", 1, 1, dbg_cmd_frame },
|
||||
@@ -374,11 +412,19 @@ void dbg_cmd_add_module(const char *name, unsigned nr_commands, struct dbg_cmd *
|
||||
#ifdef HAVE_READLINE
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
static int cmd_rl_event_hook(void)
|
||||
{
|
||||
handle_window_events();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a string from the user. The result can be modified but shouldn't be free'd.
|
||||
*/
|
||||
static char *cmd_gets(void)
|
||||
{
|
||||
rl_event_hook = cmd_rl_event_hook;
|
||||
static char *line_read = NULL;
|
||||
free(line_read);
|
||||
line_read = readline("dbg(cmd)> ");
|
||||
@@ -394,6 +440,7 @@ static char *cmd_gets(void)
|
||||
{
|
||||
static char line[1024];
|
||||
|
||||
handle_window_events();
|
||||
printf("dbg(cmd)> ");
|
||||
fflush(stdout);
|
||||
fgets(line, 1024, stdin);
|
||||
@@ -467,10 +514,27 @@ static void execute_line(char *line)
|
||||
|
||||
void dbg_cmd_repl(void)
|
||||
{
|
||||
if (stepping)
|
||||
dbg_print_vm_state();
|
||||
else
|
||||
if (stepping) {
|
||||
if (stepping_line) {
|
||||
int file, line;
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &file, &line);
|
||||
if (file == stepping_file && line == stepping_line) {
|
||||
if (stepping == STEPPING_INTO)
|
||||
dbg_set_step_into_breakpoint();
|
||||
else if (stepping == STEPPING_OVER)
|
||||
dbg_set_step_over_breakpoint();
|
||||
dbg_continue();
|
||||
}
|
||||
}
|
||||
stepping = NOT_STEPPING;
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info)
|
||||
dbg_print_current_line();
|
||||
else
|
||||
dbg_print_vm_state();
|
||||
} else {
|
||||
puts("Entering the debugger REPL. Type 'help' for a list of commands.");
|
||||
}
|
||||
while (1) {
|
||||
char *line = cmd_gets();
|
||||
if (line)
|
||||
|
||||
+1092
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -23,6 +23,7 @@
|
||||
#include "system4/ain.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/instructions.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
#include "vm.h"
|
||||
@@ -30,7 +31,6 @@
|
||||
#include "vm/page.h"
|
||||
|
||||
#include "debugger.h"
|
||||
#include "little_endian.h"
|
||||
|
||||
struct variable {
|
||||
enum ain_data_type data_type;
|
||||
|
||||
+160
-17
@@ -64,6 +64,7 @@ static struct copy_shader copy_key_shader;
|
||||
static struct copy_shader copy_alpha_key_shader;
|
||||
static struct copy_shader copy_use_amap_under_shader;
|
||||
static struct copy_shader copy_use_amap_border_shader;
|
||||
static struct copy_shader copy_grayscale_shader;
|
||||
static struct copy_shader blend_amap_color_shader;
|
||||
static struct copy_shader blend_amap_alpha_bright_shader;
|
||||
static struct copy_shader blend_use_amap_color_shader;
|
||||
@@ -121,6 +122,9 @@ void gfx_draw_init(void)
|
||||
// copy shader that only keeps fragments above a certain alpha threshold
|
||||
load_copy_shader(©_use_amap_border_shader, "shaders/render.v.glsl", "shaders/copy_use_amap_border.f.glsl");
|
||||
|
||||
// copy shader with grayscale conversion
|
||||
load_copy_shader(©_grayscale_shader, "shaders/render.v.glsl", "shaders/copy_grayscale.f.glsl");
|
||||
|
||||
// copy shader that sets source RGB to a constant
|
||||
load_copy_shader(&blend_amap_color_shader, "shaders/render.v.glsl", "shaders/blend_amap_color.f.glsl");
|
||||
|
||||
@@ -163,6 +167,7 @@ static void run_draw_shader(Shader *s, Texture *dst, Texture *src, mat4 mw_trans
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = s,
|
||||
.shape = GFX_RECTANGLE,
|
||||
.texture = src ? src->handle : 0,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
@@ -702,6 +707,16 @@ void gfx_copy_stretch_blend(struct texture *dst, int dx, int dy, int dw, int dh,
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_stretch_blend_screen(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = STRETCH_DATA(dx, dy, dw, dh, sx, sy, sw, sh);
|
||||
run_copy_shader(©_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_stretch_blend_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
@@ -729,6 +744,8 @@ void gfx_copy_stretch_blend_amap_alpha(struct texture *dst, int dx, int dy, int
|
||||
// FIXME: this doesn't work correctly when the src rectangle crosses the edge of the CG.
|
||||
static void copy_rot_zoom(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag, Shader *shader)
|
||||
{
|
||||
w = min(w, src->w - sx);
|
||||
h = min(h, src->h - sy);
|
||||
// 1. scale src vertices to texture size
|
||||
// 2. translate so that center point of copy region is at origin
|
||||
// 3. rotate
|
||||
@@ -768,18 +785,45 @@ void gfx_copy_rot_zoom_use_amap(Texture *dst, Texture *src, int sx, int sy, int
|
||||
copy_rot_zoom(dst, src, sx, sy, w, h, rotate, mag, &hitbox_shader.s);
|
||||
}
|
||||
|
||||
void gfx_copy_rot_zoom2(Texture *dst, float cx, float cy, Texture *src, float scx, float scy, float rot, float mag)
|
||||
{
|
||||
gfx_fill_amap(dst, 0, 0, dst->w, dst->h, 0);
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
|
||||
// 1. scale src vertices to texture size
|
||||
// 2. translate so that center point of copy region is at origin
|
||||
// 3. rotate
|
||||
// 4. scale
|
||||
// 4. tranlate to center point of dst texture
|
||||
// (applied in reverse order)
|
||||
mat4 mw_transform = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate(mw_transform, (vec3){ cx, cy, 0 });
|
||||
glm_scale(mw_transform, (vec3){ mag, mag, 0 });
|
||||
glm_rotate_z(mw_transform, -rot * (M_PI/180.0), mw_transform);
|
||||
glm_translate(mw_transform, (vec3){ -scx, -scy, 0 });
|
||||
glm_scale(mw_transform, (vec3){ src->w, src->h, 1 });
|
||||
|
||||
mat4 wv_transform = WV_TRANSFORM(dst->w, dst->h);
|
||||
|
||||
struct copy_data data = ROTATE_DATA(dst, 0, 0, src->w, src->h);
|
||||
run_draw_shader(&hitbox_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
static void copy_rotate_y(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag, Shader *shader)
|
||||
{
|
||||
Texture *src = front;
|
||||
vec3 scale = { src->w * mag, src->h * mag, 0 };
|
||||
if (rot > 90.0 && rot <= 270.0) {
|
||||
src = back;
|
||||
scale[0] *= -1.0f;
|
||||
}
|
||||
|
||||
gfx_fill_amap(dst, 0, 0, dst->w, dst->h, 0);
|
||||
|
||||
mat4 mw_transform = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_rotate_y(mw_transform, rot * (M_PI/180.0), mw_transform);
|
||||
glm_scale(mw_transform, (vec3){ src->w * mag, src->h * mag, 0 });
|
||||
glm_scale(mw_transform, scale);
|
||||
glm_translate(mw_transform, (vec3){ -0.5, -0.5, 0});
|
||||
|
||||
mat4 proj_transform;
|
||||
@@ -859,6 +903,23 @@ void gfx_copy_reverse_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_reverse_UD(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,
|
||||
0, -src->h, 0, sy + h,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(w, h);
|
||||
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_draw_shader(©_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
mat4 mw_transform = MAT4(
|
||||
@@ -876,6 +937,23 @@ void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_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(©_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_width_blur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int blur)
|
||||
{
|
||||
GLfloat f_rate = 1.0 / (blur * 2 + 1);
|
||||
@@ -1006,30 +1084,95 @@ void gfx_copy_stretch_with_alpha_map(Texture *dst, int dx, int dy, int dw, int d
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
// XXX: Not an actual DrawGraph function; used for rendering text
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width)
|
||||
void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
dx = roundf(dx);
|
||||
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
struct copy_data fill_data = COPY_DATA(dx, dy, dx, dy, glyph->w * scale_x, glyph->h);
|
||||
fill_data.r = color.r / 255.0;
|
||||
fill_data.g = color.g / 255.0;
|
||||
fill_data.b = color.b / 255.0;
|
||||
fill_data.a = 0.0;
|
||||
fill_data.threshold = 0.001;
|
||||
run_copy_shader(&fill_amap_under_border_shader.s, dst, dst, &fill_data);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_grayscale_shader.s, dst, src, &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;
|
||||
GLfloat h = dst->h;
|
||||
|
||||
mat4 mw_transform = MAT4(
|
||||
x1 - x0, 0, 0, x0,
|
||||
0, y1 - y0, 0, y0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(w, h);
|
||||
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0, w, h);
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = &fill_shader.s,
|
||||
.shape = GFX_LINE,
|
||||
.texture = 0,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = data
|
||||
};
|
||||
gfx_render(&job);
|
||||
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
void gfx_draw_line(Texture *dst, int x0, int y0, int x1, int y1, int r, int g, int b)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = {
|
||||
.r = r / 255.0,
|
||||
.g = g / 255.0,
|
||||
.b = b / 255.0,
|
||||
.a = 1
|
||||
};
|
||||
draw_line(dst, x0, y0, x1, y1, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_draw_line_to_amap(Texture *dst, int x0, int y0, int x1, int y1, int a)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
|
||||
|
||||
struct copy_data data = { .a = a / 255.0 };
|
||||
draw_line(dst, x0, y0, x1, y1, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_draw_quadrilateral(Texture *dst, Texture *src, struct gfx_vertex vertices[4])
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0, dst->w, dst->h);
|
||||
gfx_render_quadrilateral(src, vertices);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
// XXX: Not an actual DrawGraph function; used for rendering text
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width, bool blend)
|
||||
{
|
||||
if (blend) {
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
} else {
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
}
|
||||
|
||||
dx = roundf(dx);
|
||||
struct copy_data data = STRETCH_DATA(
|
||||
dx, dy, glyph->w * scale_x, glyph->h,
|
||||
0, 0, glyph->w, glyph->h);
|
||||
data.r = color.r / 255.0;
|
||||
data.g = color.g / 255.0;
|
||||
data.b = color.b / 255.0;
|
||||
data.a = 1.0;
|
||||
data.a = 0.01; // discard threshold
|
||||
|
||||
if (bold_width < 0.01) {
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
@@ -1049,7 +1192,7 @@ void gfx_draw_glyph_to_pmap(Texture *dst, float dx, int dy, Texture *glyph, Rect
|
||||
data.r = color.r / 255.0;
|
||||
data.g = color.g / 255.0;
|
||||
data.b = color.b / 255.0;
|
||||
data.a = 1.0;
|
||||
data.a = 0.01; // discard threshold
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
restore_blend_mode();
|
||||
@@ -1063,7 +1206,7 @@ void gfx_draw_glyph_to_amap(Texture *dst, float dx, int dy, Texture *glyph, Rect
|
||||
data.r = 1.0;
|
||||
data.g = 1.0;
|
||||
data.b = 1.0;
|
||||
data.a = 1.0;
|
||||
data.a = 0.0; // discard threshold
|
||||
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
restore_blend_mode();
|
||||
|
||||
+465
-73
@@ -22,26 +22,12 @@
|
||||
#include "dungeon/dgn.h"
|
||||
#include "vm.h"
|
||||
|
||||
struct dgn *dgn_parse(uint8_t *data, size_t size)
|
||||
struct dgn *dgn_new(uint32_t size_x, uint32_t size_y, uint32_t size_z)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (strncmp(buffer_strdata(&r), "DUGN", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
struct dgn *dgn = xcalloc(1, sizeof(struct dgn));
|
||||
dgn->version = buffer_read_int32(&r);
|
||||
if (dgn->version != DGN_VER_RANCE6 && dgn->version != DGN_VER_GALZOO) {
|
||||
WARNING("unknown DGN version: %d", dgn->version);
|
||||
free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
dgn->size_x = buffer_read_int32(&r);
|
||||
dgn->size_y = buffer_read_int32(&r);
|
||||
dgn->size_z = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 40);
|
||||
|
||||
dgn->size_x = size_x;
|
||||
dgn->size_y = size_y;
|
||||
dgn->size_z = size_z;
|
||||
int nr_cells = dgn_nr_cells(dgn);
|
||||
dgn->cells = xcalloc(nr_cells, sizeof(struct dgn_cell));
|
||||
uint32_t x = 0, y = 0, z = 0;
|
||||
@@ -50,15 +36,62 @@ struct dgn *dgn_parse(uint8_t *data, size_t size)
|
||||
cell->x = x;
|
||||
cell->y = y;
|
||||
cell->z = z;
|
||||
if (++x == dgn->size_x) {
|
||||
if (++x == size_x) {
|
||||
x = 0;
|
||||
if (++y == dgn->size_y) {
|
||||
if (++y == size_y) {
|
||||
y = 0;
|
||||
z++;
|
||||
}
|
||||
}
|
||||
cell->event_blend_rate = 255;
|
||||
|
||||
cell->floor = -1;
|
||||
cell->ceiling = -1;
|
||||
cell->north_wall = -1;
|
||||
cell->south_wall = -1;
|
||||
cell->east_wall = -1;
|
||||
cell->west_wall = -1;
|
||||
cell->north_door = -1;
|
||||
cell->south_door = -1;
|
||||
cell->east_door = -1;
|
||||
cell->west_door = -1;
|
||||
cell->stairs_texture = -1;
|
||||
cell->stairs_orientation = -1;
|
||||
cell->lightmap_floor = -1;
|
||||
cell->lightmap_ceiling = -1;
|
||||
cell->lightmap_north = -1;
|
||||
cell->lightmap_south = -1;
|
||||
cell->lightmap_east = -1;
|
||||
cell->lightmap_west = -1;
|
||||
cell->battle_background = -1;
|
||||
cell->polyobj_index = -1;
|
||||
cell->roof_orientation = -1;
|
||||
cell->roof_texture = -1;
|
||||
cell->roof_underside_texture = -1;
|
||||
cell->pathfinding_cost = -1;
|
||||
}
|
||||
return dgn;
|
||||
}
|
||||
|
||||
struct dgn *dgn_parse(uint8_t *data, size_t size, bool for_draw_field)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (strncmp(buffer_strdata(&r), "DUGN", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
uint32_t version = buffer_read_int32(&r);
|
||||
uint32_t size_x = buffer_read_int32(&r);
|
||||
uint32_t size_y = buffer_read_int32(&r);
|
||||
uint32_t size_z = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 40);
|
||||
|
||||
struct dgn *dgn = dgn_new(size_x, size_y, size_z);
|
||||
int nr_cells = dgn_nr_cells(dgn);
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct dgn_cell *cell = &dgn->cells[i];
|
||||
|
||||
cell->floor = buffer_read_int32(&r);
|
||||
cell->ceiling = buffer_read_int32(&r);
|
||||
cell->north_wall = buffer_read_int32(&r);
|
||||
@@ -71,7 +104,13 @@ struct dgn *dgn_parse(uint8_t *data, size_t size)
|
||||
cell->west_door = buffer_read_int32(&r);
|
||||
cell->stairs_texture = buffer_read_int32(&r);
|
||||
cell->stairs_orientation = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 13 * 4);
|
||||
cell->lightmap_floor = buffer_read_int32(&r);
|
||||
cell->lightmap_ceiling = buffer_read_int32(&r);
|
||||
cell->lightmap_north = buffer_read_int32(&r);
|
||||
cell->lightmap_south = buffer_read_int32(&r);
|
||||
cell->lightmap_east = buffer_read_int32(&r);
|
||||
cell->lightmap_west = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 7 * 4); // lightmap parameters for doors / stairs, 2 unknowns
|
||||
cell->enterable = buffer_read_int32(&r);
|
||||
cell->enterable_north = buffer_read_int32(&r);
|
||||
cell->enterable_south = buffer_read_int32(&r);
|
||||
@@ -87,64 +126,98 @@ struct dgn *dgn_parse(uint8_t *data, size_t size)
|
||||
buffer_skip(&r, 4);
|
||||
buffer_skip(&r, strlen(buffer_strdata(&r)) + 1);
|
||||
}
|
||||
buffer_skip(&r, 4);
|
||||
cell->battle_background = buffer_read_int32(&r);
|
||||
if (dgn->version != DGN_VER_GALZOO) {
|
||||
cell->polyobj_index = -1;
|
||||
cell->roof_orientation = -1;
|
||||
cell->roof_texture = -1;
|
||||
cell->roof_underside_texture = -1;
|
||||
continue;
|
||||
if (for_draw_field) {
|
||||
if (version >= 9) {
|
||||
cell->north_door_lock = buffer_read_int32(&r);
|
||||
cell->west_door_lock = buffer_read_int32(&r);
|
||||
cell->south_door_lock = buffer_read_int32(&r);
|
||||
cell->east_door_lock = buffer_read_int32(&r);
|
||||
cell->north_door_angle = buffer_read_float(&r);
|
||||
cell->west_door_angle = buffer_read_float(&r);
|
||||
cell->south_door_angle = buffer_read_float(&r);
|
||||
cell->east_door_angle = buffer_read_float(&r);
|
||||
cell->walked = buffer_read_int32(&r);
|
||||
}
|
||||
if (version >= 11) {
|
||||
cell->polyobj_index = buffer_read_int32(&r);
|
||||
cell->polyobj_scale = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_y = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_z = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_y = buffer_read_float(&r);
|
||||
cell->polyobj_position_z = buffer_read_float(&r);
|
||||
}
|
||||
} else {
|
||||
buffer_skip(&r, 4); // unknown
|
||||
cell->battle_background = buffer_read_int32(&r);
|
||||
if (version != DGN_VER_GALZOO) {
|
||||
continue;
|
||||
}
|
||||
cell->polyobj_index = buffer_read_int32(&r);
|
||||
cell->polyobj_scale = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_y = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_z = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_y = buffer_read_float(&r);
|
||||
cell->polyobj_position_z = buffer_read_float(&r);
|
||||
cell->roof_orientation = buffer_read_int32(&r);
|
||||
cell->roof_texture = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 4); // unknown
|
||||
cell->roof_underside_texture = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 4); // unknown
|
||||
}
|
||||
cell->polyobj_index = buffer_read_int32(&r);
|
||||
cell->polyobj_scale = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_y = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_z = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_y = buffer_read_float(&r);
|
||||
cell->polyobj_position_z = buffer_read_float(&r);
|
||||
cell->roof_orientation = buffer_read_int32(&r);
|
||||
cell->roof_texture = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 4);
|
||||
cell->roof_underside_texture = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 4);
|
||||
}
|
||||
if (buffer_read_u8(&r) != 0 || buffer_read_int32(&r) != 1) {
|
||||
if (buffer_read_u8(&r) != 0) {
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
dgn->pvs = xcalloc(nr_cells, sizeof(struct packed_pvs));
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct packed_pvs *pvs = &dgn->pvs[i];
|
||||
int32_t len = buffer_read_int32(&r);
|
||||
if (len % 8 != 4) {
|
||||
WARNING("dgn_parse: unexpected PVS length");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
if (buffer_read_int32(&r) != nr_cells) {
|
||||
WARNING("dgn_parse: bad PVS");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
pvs->nr_run_lengths = (len - 4) / 8;
|
||||
pvs->run_lengths = xmalloc(pvs->nr_run_lengths * sizeof(struct pvs_run_lengths));
|
||||
int32_t total = 0;
|
||||
for (int j = 0; j < pvs->nr_run_lengths; j++) {
|
||||
int32_t invisible = buffer_read_int32(&r);
|
||||
int32_t visible = buffer_read_int32(&r);
|
||||
pvs->run_lengths[j].invisible_cells = invisible;
|
||||
pvs->run_lengths[j].visible_cells = visible;
|
||||
pvs->nr_visible_cells += visible;
|
||||
total += invisible + visible;
|
||||
}
|
||||
if (total != nr_cells) {
|
||||
WARNING("dgn_parse: bad PVS");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
int has_pvs = buffer_read_int32(&r);
|
||||
if (has_pvs) {
|
||||
dgn->pvs = xcalloc(nr_cells, sizeof(struct packed_pvs));
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct packed_pvs *pvs = &dgn->pvs[i];
|
||||
int32_t len = buffer_read_int32(&r);
|
||||
if (len % 8 != 4) {
|
||||
WARNING("dgn_parse: unexpected PVS length");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
if (buffer_read_int32(&r) != nr_cells) {
|
||||
WARNING("dgn_parse: bad PVS");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
pvs->nr_run_lengths = (len - 4) / 8;
|
||||
pvs->run_lengths = xmalloc(pvs->nr_run_lengths * sizeof(struct pvs_run_lengths));
|
||||
int32_t total = 0;
|
||||
for (int j = 0; j < pvs->nr_run_lengths; j++) {
|
||||
int32_t invisible = buffer_read_int32(&r);
|
||||
int32_t visible = buffer_read_int32(&r);
|
||||
pvs->run_lengths[j].invisible_cells = invisible;
|
||||
pvs->run_lengths[j].visible_cells = visible;
|
||||
pvs->nr_visible_cells += visible;
|
||||
total += invisible + visible;
|
||||
}
|
||||
if (total != nr_cells) {
|
||||
WARNING("dgn_parse: bad PVS");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
dgn->sphere_theta[0] = buffer_read_float(&r);
|
||||
dgn->sphere_theta[1] = buffer_read_float(&r);
|
||||
dgn->sphere_theta[2] = buffer_read_float(&r);
|
||||
dgn->sphere_color_top = buffer_read_float(&r);
|
||||
dgn->sphere_color_bottom = buffer_read_float(&r);
|
||||
buffer_skip(&r, 4); // unknown
|
||||
if (for_draw_field && version >= 10) {
|
||||
dgn->back_color_r = buffer_read_int32(&r);
|
||||
dgn->back_color_g = buffer_read_int32(&r);
|
||||
dgn->back_color_b = buffer_read_int32(&r);
|
||||
}
|
||||
return dgn;
|
||||
}
|
||||
|
||||
@@ -204,7 +277,7 @@ static int pvs_cell_compare(const void *a, const void *b)
|
||||
return ((struct pvs_cell *)a)->distance - ((struct pvs_cell *)b)->distance;
|
||||
}
|
||||
|
||||
struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, int *nr_cells_out)
|
||||
static struct dgn_cell **dgn_get_visible_cells_pvs(struct dgn *dgn, int x, int y, int z, int *nr_cells_out)
|
||||
{
|
||||
int cell_index = dgn_cell_index(dgn, x, y, z);
|
||||
struct dgn_cell *cell = &dgn->cells[cell_index];
|
||||
@@ -236,3 +309,322 @@ struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, in
|
||||
*nr_cells_out = pvs->nr_visible_cells;
|
||||
return cell->visible_cells;
|
||||
}
|
||||
|
||||
static struct dgn_cell **dgn_get_visible_cells_nopvs(struct dgn *dgn, int x, int y, int z, int *nr_cells_out)
|
||||
{
|
||||
int cell_index = dgn_cell_index(dgn, x, y, z);
|
||||
struct dgn_cell *cell = &dgn->cells[cell_index];
|
||||
int nr_cells = dgn_nr_cells(dgn);
|
||||
|
||||
if (!cell->visible_cells) {
|
||||
struct pvs_cell *pvs_cells = xmalloc(nr_cells * sizeof(struct pvs_cell));
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct dgn_cell *c = &dgn->cells[i];
|
||||
int dx = c->x - x;
|
||||
int dy = c->y - y;
|
||||
int dz = c->z - z;
|
||||
pvs_cells[i].cell = c;
|
||||
pvs_cells[i].distance = dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
qsort(pvs_cells, nr_cells, sizeof(struct pvs_cell), pvs_cell_compare);
|
||||
|
||||
cell->visible_cells = xmalloc(nr_cells * sizeof(struct dgn_cell *));
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
cell->visible_cells[i] = pvs_cells[i].cell;
|
||||
}
|
||||
free(pvs_cells);
|
||||
}
|
||||
*nr_cells_out = nr_cells;
|
||||
return cell->visible_cells;
|
||||
}
|
||||
|
||||
struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, int *nr_cells_out)
|
||||
{
|
||||
if (dgn->pvs)
|
||||
return dgn_get_visible_cells_pvs(dgn, x, y, z, nr_cells_out);
|
||||
else
|
||||
return dgn_get_visible_cells_nopvs(dgn, x, y, z, nr_cells_out);
|
||||
}
|
||||
|
||||
#define LM_N (1 << 0)
|
||||
#define LM_S (1 << 1)
|
||||
#define LM_W (1 << 2)
|
||||
#define LM_E (1 << 3)
|
||||
#define LM_NW (1 << 4)
|
||||
#define LM_NE (1 << 5)
|
||||
#define LM_SW (1 << 6)
|
||||
#define LM_SE (1 << 7)
|
||||
|
||||
static int lightmap_texture_index(uint8_t flags)
|
||||
{
|
||||
if (flags & LM_N || flags & LM_W) flags &= ~LM_NW;
|
||||
if (flags & LM_N || flags & LM_E) flags &= ~LM_NE;
|
||||
if (flags & LM_S || flags & LM_W) flags &= ~LM_SW;
|
||||
if (flags & LM_S || flags & LM_E) flags &= ~LM_SE;
|
||||
|
||||
switch (flags) {
|
||||
case LM_N | LM_W | LM_E: return 0;
|
||||
case LM_W | LM_E: return 1;
|
||||
case LM_S | LM_W | LM_E: return 2;
|
||||
case LM_N | LM_S | LM_W: return 3;
|
||||
case LM_N | LM_S: return 4;
|
||||
case LM_N | LM_S | LM_E: return 5;
|
||||
case LM_N | LM_W: return 6;
|
||||
case LM_N: return 7;
|
||||
case LM_N | LM_E: return 8;
|
||||
case LM_W: return 9;
|
||||
case LM_E: return 10;
|
||||
case LM_S | LM_W: return 11;
|
||||
case LM_S: return 12;
|
||||
case LM_S | LM_E: return 13;
|
||||
case LM_N | LM_S | LM_W | LM_E: return 14;
|
||||
case LM_W | LM_NE | LM_SE: return 15;
|
||||
case LM_N | LM_SW | LM_SE: return 16;
|
||||
case LM_E | LM_NW | LM_SW: return 17;
|
||||
case LM_S | LM_NW | LM_NE: return 18;
|
||||
case LM_NW | LM_NE | LM_SW | LM_SE: return 19;
|
||||
case LM_W | LM_NE: return 20;
|
||||
case LM_N | LM_SE: return 21;
|
||||
case LM_E | LM_SW: return 22;
|
||||
case LM_S | LM_NW: return 23;
|
||||
case LM_NE: return 24;
|
||||
case LM_W | LM_SE: return 25;
|
||||
case LM_N | LM_SW: return 26;
|
||||
case LM_E | LM_NW: return 27;
|
||||
case LM_S | LM_NE: return 28;
|
||||
case LM_SE: return 29;
|
||||
case LM_NE | LM_SE: return 30;
|
||||
case LM_SW | LM_SE: return 31;
|
||||
case LM_NW | LM_SW: return 32;
|
||||
case LM_NW | LM_NE: return 33;
|
||||
case LM_SW: return 34;
|
||||
case LM_NW: return 35;
|
||||
case LM_N | LM_W | LM_SE: return 36;
|
||||
case LM_N | LM_E | LM_SW: return 37;
|
||||
case LM_S | LM_E | LM_NW: return 38;
|
||||
case LM_S | LM_W | LM_NE: return 39;
|
||||
case LM_NW | LM_NE | LM_SW: return 40;
|
||||
case LM_NW | LM_NE | LM_SE: return 41;
|
||||
case LM_NE | LM_SW | LM_SE: return 42;
|
||||
case LM_NW | LM_SW | LM_SE: return 43;
|
||||
case LM_NE | LM_SW: return 44;
|
||||
case LM_NW | LM_SE: return 45;
|
||||
default: return -1;
|
||||
};
|
||||
}
|
||||
|
||||
static int calc_floor_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *north_cell, struct dgn_cell *south_cell,
|
||||
struct dgn_cell *west_cell, struct dgn_cell *east_cell)
|
||||
{
|
||||
if (cell->floor == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->north_wall != -1 || cell->north_door != -1 ||
|
||||
(north_cell && north_cell->stairs_texture != -1 && north_cell->stairs_orientation == 0))
|
||||
flags |= LM_N;
|
||||
if (cell->south_wall != -1 || cell->south_door != -1 ||
|
||||
(south_cell && south_cell->stairs_texture != -1 && south_cell->stairs_orientation == 2))
|
||||
flags |= LM_S;
|
||||
if (cell->west_wall != -1 || cell->west_door != -1 ||
|
||||
(west_cell && west_cell->stairs_texture != -1 && west_cell->stairs_orientation == 1))
|
||||
flags |= LM_W;
|
||||
if (cell->east_wall != -1 || cell->east_door != -1 ||
|
||||
(east_cell && east_cell->stairs_texture != -1 && east_cell->stairs_orientation == 3))
|
||||
flags |= LM_E;
|
||||
if ((north_cell && (north_cell->west_wall != -1 || north_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->north_wall != -1 || west_cell->north_door != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((north_cell && (north_cell->east_wall != -1 || north_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->north_wall != -1 || east_cell->north_door != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((south_cell && (south_cell->west_wall != -1 || south_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->south_wall != -1 || west_cell->south_door != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((south_cell && (south_cell->east_wall != -1 || south_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->south_wall != -1 || east_cell->south_door != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_ceiling_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *north_cell, struct dgn_cell *south_cell,
|
||||
struct dgn_cell *west_cell, struct dgn_cell *east_cell)
|
||||
{
|
||||
if (cell->ceiling == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->north_wall != -1 || cell->north_door != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->south_wall != -1 || cell->south_door != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->west_wall != -1 || cell->west_door != -1)
|
||||
flags |= LM_E;
|
||||
if (cell->east_wall != -1 || cell->east_door != -1)
|
||||
flags |= LM_W;
|
||||
if ((north_cell && (north_cell->east_wall != -1 || north_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->north_wall != -1 || east_cell->north_door != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((north_cell && (north_cell->west_wall != -1 || north_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->north_wall != -1 || west_cell->north_door != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((south_cell && (south_cell->east_wall != -1 || south_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->south_wall != -1 || east_cell->south_door != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((south_cell && (south_cell->west_wall != -1 || south_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->south_wall != -1 || west_cell->south_door != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_north_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *up_cell, struct dgn_cell *down_cell,
|
||||
struct dgn_cell *west_cell, struct dgn_cell *east_cell)
|
||||
{
|
||||
if (cell->north_wall == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->ceiling != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->floor != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->west_wall != -1 || cell->west_door != -1)
|
||||
flags |= LM_W;
|
||||
if (cell->east_wall != -1 || cell->east_door != -1)
|
||||
flags |= LM_E;
|
||||
if ((up_cell && (up_cell->west_wall != -1 || up_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->ceiling != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((up_cell && (up_cell->east_wall != -1 || up_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->ceiling != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((down_cell && (down_cell->west_wall != -1 || down_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->floor != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((down_cell && (down_cell->east_wall != -1 || down_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->floor != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_south_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *up_cell, struct dgn_cell *down_cell,
|
||||
struct dgn_cell *west_cell, struct dgn_cell *east_cell)
|
||||
{
|
||||
if (cell->south_wall == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->ceiling != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->floor != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->east_wall != -1 || cell->east_door != -1)
|
||||
flags |= LM_W;
|
||||
if (cell->west_wall != -1 || cell->west_door != -1)
|
||||
flags |= LM_E;
|
||||
if ((up_cell && (up_cell->east_wall != -1 || up_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->ceiling != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((up_cell && (up_cell->west_wall != -1 || up_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->ceiling != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((down_cell && (down_cell->east_wall != -1 || down_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->floor != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((down_cell && (down_cell->west_wall != -1 || down_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->floor != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_west_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *up_cell, struct dgn_cell *down_cell,
|
||||
struct dgn_cell *north_cell, struct dgn_cell *south_cell)
|
||||
{
|
||||
if (cell->west_wall == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->ceiling != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->floor != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->south_wall != -1 || cell->south_door != -1)
|
||||
flags |= LM_W;
|
||||
if (cell->north_wall != -1 || cell->north_door != -1)
|
||||
flags |= LM_E;
|
||||
if ((up_cell && (up_cell->south_wall != -1 || up_cell->south_door != -1)) ||
|
||||
(south_cell && (south_cell->ceiling != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((up_cell && (up_cell->north_wall != -1 || up_cell->north_door != -1)) ||
|
||||
(north_cell && (north_cell->ceiling != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((down_cell && (down_cell->south_wall != -1 || down_cell->south_door != -1)) ||
|
||||
(south_cell && (south_cell->floor != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((down_cell && (down_cell->north_wall != -1 || down_cell->north_door != -1)) ||
|
||||
(north_cell && (north_cell->floor != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_east_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *up_cell, struct dgn_cell *down_cell,
|
||||
struct dgn_cell *north_cell, struct dgn_cell *south_cell)
|
||||
{
|
||||
if (cell->east_wall == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->ceiling != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->floor != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->north_wall != -1 || cell->north_door != -1)
|
||||
flags |= LM_W;
|
||||
if (cell->south_wall != -1 || cell->south_door != -1)
|
||||
flags |= LM_E;
|
||||
if ((up_cell && (up_cell->north_wall != -1 || up_cell->north_door != -1)) ||
|
||||
(north_cell && (north_cell->ceiling != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((up_cell && (up_cell->south_wall != -1 || up_cell->south_door != -1)) ||
|
||||
(south_cell && (south_cell->ceiling != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((down_cell && (down_cell->north_wall != -1 || down_cell->north_door != -1)) ||
|
||||
(north_cell && (north_cell->floor != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((down_cell && (down_cell->south_wall != -1 || down_cell->south_door != -1)) ||
|
||||
(south_cell && (south_cell->floor != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
void dgn_calc_lightmap(struct dgn *dgn)
|
||||
{
|
||||
int nr_cells = dgn_nr_cells(dgn);
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct dgn_cell *cell = &dgn->cells[i];
|
||||
struct dgn_cell *north = cell->z + 1 < dgn->size_z
|
||||
? dgn_cell_at(dgn, cell->x, cell->y, cell->z + 1) : NULL;
|
||||
struct dgn_cell *south = cell->z > 0
|
||||
? dgn_cell_at(dgn, cell->x, cell->y, cell->z - 1) : NULL;
|
||||
struct dgn_cell *west = cell->x > 0
|
||||
? dgn_cell_at(dgn, cell->x - 1, cell->y, cell->z) : NULL;
|
||||
struct dgn_cell *east = cell->x + 1 < dgn->size_x
|
||||
? dgn_cell_at(dgn, cell->x + 1, cell->y, cell->z) : NULL;
|
||||
struct dgn_cell *up = cell->y + 1 < dgn->size_y
|
||||
? dgn_cell_at(dgn, cell->x, cell->y + 1, cell->z) : NULL;
|
||||
struct dgn_cell *down = cell->y > 0
|
||||
? dgn_cell_at(dgn, cell->x, cell->y - 1, cell->z) : NULL;
|
||||
cell->lightmap_floor = calc_floor_lightmap(dgn, cell, north, south, west, east);
|
||||
cell->lightmap_ceiling = calc_ceiling_lightmap(dgn, cell, north, south, west, east);
|
||||
cell->lightmap_north = calc_north_lightmap(dgn, cell, up, down, west, east);
|
||||
cell->lightmap_south = calc_south_lightmap(dgn, cell, up, down, west, east);
|
||||
cell->lightmap_west = calc_west_lightmap(dgn, cell, up, down, north, south);
|
||||
cell->lightmap_east = calc_east_lightmap(dgn, cell, up, down, north, south);
|
||||
}
|
||||
}
|
||||
|
||||
+120
-3
@@ -14,13 +14,20 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "system4.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
|
||||
#include "xsystem4.h"
|
||||
#include "dungeon/dtx.h"
|
||||
#include "dungeon/mtrand43.h"
|
||||
|
||||
#define DTX_VERSION_DTL 0xffffffff
|
||||
|
||||
struct dtx *dtx_parse(uint8_t *data, size_t size)
|
||||
{
|
||||
@@ -68,12 +75,122 @@ void dtx_free(struct dtx *dtx)
|
||||
free(dtx);
|
||||
}
|
||||
|
||||
struct cg *dtx_create_cg(struct dtx *dtx, int type, int index)
|
||||
struct cg *dtx_create_cg(struct dtx *dtx, enum dtx_texture_type type, int index)
|
||||
{
|
||||
if (type < 0 || type >= dtx->nr_rows || index < 0 || index >= dtx->nr_columns)
|
||||
int row;
|
||||
switch (dtx->version) {
|
||||
case 0:
|
||||
switch (type) {
|
||||
case DTX_WALL: row = 0; break;
|
||||
case DTX_FLOOR: row = 1; break;
|
||||
case DTX_CEILING: row = 2; break;
|
||||
case DTX_STAIRS: row = 3; break;
|
||||
case DTX_DOOR: row = 4; break;
|
||||
case DTX_SKYBOX: row = 5; break;
|
||||
case DTX_LIGHTMAP: row = 6; break;
|
||||
default: return NULL;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (type) {
|
||||
case DTX_WALL: row = 0; break;
|
||||
case DTX_FLOOR: row = 1; break;
|
||||
case DTX_CEILING: row = 2; break;
|
||||
case DTX_STAIRS: row = 3; break;
|
||||
case DTX_DOOR: row = 4; break;
|
||||
case DTX_SKYBOX: row = 5; break;
|
||||
case DTX_LIGHTMAP: row = 7; break;
|
||||
default: return NULL;
|
||||
}
|
||||
break;
|
||||
case DTX_VERSION_DTL:
|
||||
switch (type) {
|
||||
case DTX_WALL: row = 0; break;
|
||||
case DTX_FLOOR: row = 1; break;
|
||||
case DTX_CEILING: row = 2; break;
|
||||
case DTX_LIGHTMAP: row = 3; break;
|
||||
default: return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (row >= dtx->nr_rows || index < 0 || index >= dtx->nr_columns)
|
||||
return NULL;
|
||||
struct dtx_entry *entry = &dtx->entries[type * dtx->nr_columns + index];
|
||||
struct dtx_entry *entry = &dtx->entries[row * dtx->nr_columns + index];
|
||||
if (!entry->data)
|
||||
return NULL;
|
||||
return cg_load_buffer(entry->data, entry->size);
|
||||
}
|
||||
|
||||
struct dtx *dtx_load_from_dtl(const char *path, uint32_t seed)
|
||||
{
|
||||
uint8_t *index_buf = NULL;
|
||||
struct dtx *dtx = NULL;
|
||||
FILE *fp = file_open_utf8(path, "rb");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open DTL file '%s': %s", display_utf0(path), strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
char header[12];
|
||||
if (fread(header, sizeof(header), 1, fp) != 1 || strcmp(header, "DTL"))
|
||||
goto err;
|
||||
uint32_t version = LittleEndian_getDW((uint8_t*)header, 4);
|
||||
uint32_t index_size = LittleEndian_getDW((uint8_t*)header, 8);
|
||||
if (version != 0 || index_size <= 1 || index_size > 1000)
|
||||
goto err;
|
||||
index_buf = xmalloc(index_size * 4);
|
||||
if (fread(index_buf, index_size * 4, 1, fp) != 1)
|
||||
goto err;
|
||||
|
||||
// Randomly select an index based on the seed.
|
||||
struct mtrand43 mt;
|
||||
mtrand43_init(&mt, seed);
|
||||
for (;;) {
|
||||
int index = mtrand43_genrand(&mt) % (index_size - 1) + 1;
|
||||
uint32_t offset = LittleEndian_getDW(index_buf, index * 4);
|
||||
if (offset) {
|
||||
if (fseek(fp, offset, SEEK_SET))
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dtx = xcalloc(1, sizeof(struct dtx));
|
||||
dtx->version = DTX_VERSION_DTL;
|
||||
dtx->nr_rows = 4;
|
||||
dtx->nr_columns = 46; // number of lightmap textures
|
||||
dtx->entries = xcalloc(dtx->nr_rows * dtx->nr_columns, sizeof(struct dtx_entry));
|
||||
uint8_t buf[4];
|
||||
for (int row = 0; row < dtx->nr_rows; row++) {
|
||||
if (fread(buf, sizeof(buf), 1, fp) != 1)
|
||||
goto err;
|
||||
uint32_t nr_items = LittleEndian_getDW(buf, 0);
|
||||
for (int col = 0; col < nr_items; col++) {
|
||||
if (fread(buf, sizeof(buf), 1, fp) != 1)
|
||||
goto err;
|
||||
uint32_t texture_size = LittleEndian_getDW(buf, 0);
|
||||
if (col >= dtx->nr_columns) {
|
||||
// skip the rest of the row
|
||||
if (fseek(fp, texture_size, SEEK_CUR))
|
||||
goto err;
|
||||
continue;
|
||||
}
|
||||
struct dtx_entry *entry = &dtx->entries[row * dtx->nr_columns + col];
|
||||
entry->size = texture_size;
|
||||
entry->data = xmalloc(texture_size);
|
||||
if (fread(entry->data, texture_size, 1, fp) != 1)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
free(index_buf);
|
||||
fclose(fp);
|
||||
return dtx;
|
||||
|
||||
err:
|
||||
if (dtx)
|
||||
dtx_free(dtx);
|
||||
if (index_buf)
|
||||
free(index_buf);
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+415
-75
@@ -30,64 +30,74 @@
|
||||
#include "dungeon/dgn.h"
|
||||
#include "dungeon/dtx.h"
|
||||
#include "dungeon/dungeon.h"
|
||||
#include "dungeon/generator.h"
|
||||
#include "dungeon/map.h"
|
||||
#include "dungeon/polyobj.h"
|
||||
#include "dungeon/renderer.h"
|
||||
#include "dungeon/tes.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "cJSON.h"
|
||||
#include "json.h"
|
||||
#include "sact.h"
|
||||
#include "sprite.h"
|
||||
#include "vm.h"
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
static const char plugin_name[] = "DrawDungeon";
|
||||
|
||||
static void dungeon_render(struct sact_sprite *sp);
|
||||
static cJSON *dungeon_to_json(struct sact_sprite *sp, bool verbose);
|
||||
static void dungeon_context_free(struct draw_plugin *plugin);
|
||||
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int surface)
|
||||
struct dungeon_context *dungeon_get_context(int surface)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(surface);
|
||||
if (!sp || !sp->plugin || sp->plugin->name != plugin_name)
|
||||
return NULL;
|
||||
return (struct dungeon_context *)sp->plugin;
|
||||
}
|
||||
|
||||
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));
|
||||
ctx->plugin.name = "DrawDungeon";
|
||||
ctx->plugin.name = plugin_name;
|
||||
ctx->plugin.free = dungeon_context_free;
|
||||
ctx->plugin.update = dungeon_render;
|
||||
ctx->plugin.debug_print = NULL;
|
||||
ctx->plugin.to_json = dungeon_to_json;
|
||||
ctx->version = version;
|
||||
ctx->surface = surface;
|
||||
struct sact_sprite *sp = sact_get_sprite(ctx->surface);
|
||||
if (!sp)
|
||||
VM_ERROR("DrawDungeon.Init: invalid surface %d", surface);
|
||||
|
||||
// Dungeon scene will be rendered to this texture.
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
sprite_bind_plugin(sp, &ctx->plugin);
|
||||
|
||||
gfx_init_texture_blank(&ctx->texture, width, height);
|
||||
glGenRenderbuffers(1, &ctx->depth_buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, ctx->depth_buffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
glm_perspective(GLM_PIf / 3.f, (float)width / height, 0.5f, 100.f, ctx->proj_transform);
|
||||
|
||||
ctx->map = dungeon_map_create(version);
|
||||
|
||||
if (version == DRAW_FIELD) {
|
||||
ctx->characters = xcalloc(DRAWFIELD_NR_CHARACTERS, sizeof(struct drawfield_character));
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void dungeon_context_free(struct dungeon_context *ctx)
|
||||
static void dungeon_context_free(struct draw_plugin *plugin)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(ctx->surface);
|
||||
if (sp)
|
||||
sprite_bind_plugin(sp, NULL);
|
||||
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)plugin;
|
||||
if (ctx->dgn)
|
||||
dgn_free(ctx->dgn);
|
||||
if (ctx->dtx)
|
||||
dtx_free(ctx->dtx);
|
||||
if (ctx->tes)
|
||||
tes_free(ctx->tes);
|
||||
if (ctx->characters)
|
||||
free(ctx->characters);
|
||||
|
||||
if (ctx->renderer)
|
||||
dungeon_renderer_free(ctx->renderer);
|
||||
|
||||
gfx_delete_texture(&ctx->texture);
|
||||
glDeleteRenderbuffers(1, &ctx->depth_buffer);
|
||||
if (ctx->map)
|
||||
dungeon_map_free(ctx->map);
|
||||
@@ -158,7 +168,7 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
|
||||
struct archive_data *dgn = archive_get(dlf, num * 3);
|
||||
if (dgn) {
|
||||
ctx->dgn = dgn_parse(dgn->data, dgn->size);
|
||||
ctx->dgn = dgn_parse(dgn->data, dgn->size, false);
|
||||
archive_free_data(dgn);
|
||||
}
|
||||
struct archive_data *dtx = archive_get(dlf, num * 3 + 1);
|
||||
@@ -172,6 +182,11 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
archive_free_data(tes);
|
||||
}
|
||||
archive_free(dlf);
|
||||
} else if (ctx->version == DRAW_DUNGEON_2) {
|
||||
ctx->dgn = dgn_generate_drawdungeon2(num);
|
||||
char *dtl_path = gamedir_path("Data/Texture.dtl");
|
||||
ctx->dtx = dtx_load_from_dtl(dtl_path, (num * 25) | 1);
|
||||
free(dtl_path);
|
||||
} else if (ctx->version == DRAW_DUNGEON_14) {
|
||||
char buf[100];
|
||||
sprintf(buf, "Data/map%02d.dgn", num);
|
||||
@@ -180,7 +195,7 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
size_t len;
|
||||
uint8_t *dgn = file_read(path, &len);
|
||||
if (dgn) {
|
||||
ctx->dgn = dgn_parse(dgn, len);
|
||||
ctx->dgn = dgn_parse(dgn, len, false);
|
||||
free(dgn);
|
||||
}
|
||||
|
||||
@@ -220,7 +235,7 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
if (!event_textures)
|
||||
return false;
|
||||
|
||||
ctx->renderer = dungeon_renderer_create(ctx->version, ctx->dtx, event_textures, nr_event_textures, polyobj);
|
||||
ctx->renderer = dungeon_renderer_create(ctx->version, num, ctx->dtx, event_textures, nr_event_textures, polyobj);
|
||||
|
||||
if (polyobj)
|
||||
polyobj_free(polyobj);
|
||||
@@ -231,6 +246,74 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_load_dungeon(struct dungeon_context *ctx, const char *filename, int num)
|
||||
{
|
||||
char *path = gamedir_path(filename);
|
||||
size_t len;
|
||||
uint8_t *dgn = file_read(path, &len);
|
||||
if (!dgn) {
|
||||
WARNING("Cannot load %s", path);
|
||||
free(path);
|
||||
return false;
|
||||
}
|
||||
free(path);
|
||||
if (ctx->dgn)
|
||||
dgn_free(ctx->dgn);
|
||||
ctx->dgn = dgn_parse(dgn, len, ctx->version == DRAW_FIELD);
|
||||
free(dgn);
|
||||
if (!ctx->dgn)
|
||||
return false;
|
||||
|
||||
dungeon_map_init(ctx);
|
||||
|
||||
ctx->loaded = ctx->dgn && ctx->dtx;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_load_texture(struct dungeon_context *ctx, const char *filename)
|
||||
{
|
||||
// load .dtx
|
||||
char *path = gamedir_path(filename);
|
||||
size_t dtx_len;
|
||||
uint8_t *dtx = file_read(path, &dtx_len);
|
||||
if (!dtx) {
|
||||
WARNING("Cannot load %s", path);
|
||||
free(path);
|
||||
return false;
|
||||
}
|
||||
if (ctx->dtx)
|
||||
dtx_free(ctx->dtx);
|
||||
ctx->dtx = dtx_parse(dtx, dtx_len);
|
||||
free(dtx);
|
||||
if (!ctx->dtx) {
|
||||
free(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// load .tes
|
||||
if (ctx->tes)
|
||||
tes_free(ctx->tes);
|
||||
ctx->tes = NULL;
|
||||
size_t path_len = strlen(path);
|
||||
if (path_len > 4 && path[path_len - 4] == '.') {
|
||||
strcpy(path + path_len - 4, ".tes"); // .dtx -> .tes
|
||||
size_t tes_len;
|
||||
uint8_t *tes = file_read(path, &tes_len);
|
||||
if (tes) {
|
||||
ctx->tes = tes_parse(tes, tes_len);
|
||||
free(tes);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
|
||||
if (ctx->renderer)
|
||||
dungeon_renderer_free(ctx->renderer);
|
||||
ctx->renderer = dungeon_renderer_create(ctx->version, 0, ctx->dtx, NULL, 0, NULL);
|
||||
|
||||
ctx->loaded = ctx->dgn && ctx->dtx;
|
||||
return true;
|
||||
}
|
||||
|
||||
void dungeon_set_camera(int surface, float x, float y, float z, float angle, float angle_p)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
@@ -239,23 +322,59 @@ void dungeon_set_camera(int surface, float x, float y, float z, float angle, flo
|
||||
ctx->camera.pos[0] = x;
|
||||
ctx->camera.pos[1] = y;
|
||||
ctx->camera.pos[2] = -z;
|
||||
ctx->camera.angle = -angle * M_PI / 180;
|
||||
ctx->camera.angle_p = angle_p * M_PI / 180;
|
||||
ctx->camera.angle = -glm_rad(angle);
|
||||
ctx->camera.angle_p = glm_rad(angle_p);
|
||||
|
||||
if (ctx->version != DRAW_FIELD) {
|
||||
ctx->player_pos[0] = roundf(x / 2.f);
|
||||
ctx->player_pos[1] = roundf(y / 2.f);
|
||||
ctx->player_pos[2] = roundf(z / 2.f);
|
||||
}
|
||||
}
|
||||
|
||||
static void model_view_matrix(struct camera *camera, mat4 out)
|
||||
void dungeon_set_perspective(int surface, int width, int height, float near, float far, float deg)
|
||||
{
|
||||
// The actual camera position is slightly behind camera->pos.
|
||||
float dist = 0.9;
|
||||
vec3 d = {
|
||||
dist * sin(camera->angle) * cos(camera->angle_p),
|
||||
dist * sin(camera->angle_p),
|
||||
dist * -cos(camera->angle) * cos(camera->angle_p),
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
float aspect = (float)width / height;
|
||||
// XXX: Pastel Chime Continue sets unnecessarily small near (0.1) and large
|
||||
// far (10000), which degrades depth buffer precision.
|
||||
near = max(near, 0.4f);
|
||||
far = min(far, 500.f);
|
||||
glm_perspective(glm_rad(deg), aspect, near, far, ctx->proj_transform);
|
||||
}
|
||||
|
||||
void dungeon_set_player_pos(int surface, int x, int y, int z)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx)
|
||||
return;
|
||||
ctx->player_pos[0] = x;
|
||||
ctx->player_pos[1] = y;
|
||||
ctx->player_pos[2] = z;
|
||||
}
|
||||
|
||||
static void model_view_matrix(struct dungeon_context *ctx, mat4 mv_out)
|
||||
{
|
||||
struct camera *camera = &ctx->camera;
|
||||
float dist = 0.9f;
|
||||
vec3 front = {
|
||||
dist * sinf(camera->angle) * cosf(camera->angle_p),
|
||||
dist * sinf(camera->angle_p),
|
||||
dist * -cosf(camera->angle) * cosf(camera->angle_p),
|
||||
};
|
||||
vec3 eye;
|
||||
glm_vec3_sub(camera->pos, d, eye);
|
||||
vec3 up = {0.0, 1.0, 0.0};
|
||||
glm_lookat(eye, camera->pos, up, out);
|
||||
if (ctx->version != DRAW_FIELD) {
|
||||
// The actual camera position is slightly behind camera->pos.
|
||||
vec3 eye;
|
||||
glm_vec3_sub(camera->pos, front, eye);
|
||||
glm_lookat(eye, camera->pos, GLM_YUP, mv_out);
|
||||
} else {
|
||||
vec3 target;
|
||||
glm_vec3_add(camera->pos, front, target);
|
||||
glm_lookat(camera->pos, target, GLM_YUP, mv_out);
|
||||
}
|
||||
}
|
||||
|
||||
static void dungeon_render(struct sact_sprite *sp)
|
||||
@@ -263,50 +382,64 @@ static void dungeon_render(struct sact_sprite *sp)
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)sp->plugin;
|
||||
if (!ctx->loaded || !ctx->draw_enabled)
|
||||
return;
|
||||
sprite_dirty(sp);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, texture, 0, 0, texture->w, texture->h);
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, &ctx->texture, 0, 0, ctx->texture.w, ctx->texture.h);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ctx->depth_buffer);
|
||||
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
ERROR("Incomplete framebuffer");
|
||||
|
||||
glClearColor(ctx->dgn->back_color_r / 255.f, ctx->dgn->back_color_g / 255.f, ctx->dgn->back_color_b / 255.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
mat4 local_transform, view_transform, proj_transform;
|
||||
glm_mat4_identity(local_transform);
|
||||
model_view_matrix(&ctx->camera, view_transform);
|
||||
glm_perspective(M_PI / 3.0, (float)texture->w / texture->h, 0.5, 100.0, proj_transform);
|
||||
mat4 view_transform;
|
||||
model_view_matrix(ctx, 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
|
||||
// bottom-up (the first pixel is at the bottom-left), but we want a top-down
|
||||
// image (the first pixel is at the top-left).
|
||||
proj_transform[1][1] *= -1;
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
int dgn_x = round(ctx->camera.pos[0] / 2.0);
|
||||
int dgn_y = round(ctx->camera.pos[1] / 2.0);
|
||||
int dgn_z = round(ctx->camera.pos[2] / -2.0);
|
||||
int dgn_x, dgn_y, dgn_z;
|
||||
if (ctx->version == DRAW_FIELD) {
|
||||
dgn_x = 0;
|
||||
dgn_y = ctx->dgn->size_y - 1;
|
||||
dgn_z = 0;
|
||||
} else {
|
||||
dgn_x = ctx->player_pos[0];
|
||||
dgn_y = ctx->player_pos[1];
|
||||
dgn_z = ctx->player_pos[2];
|
||||
}
|
||||
int nr_cells;
|
||||
struct dgn_cell **cells = dgn_get_visible_cells(ctx->dgn, dgn_x, dgn_y, dgn_z, &nr_cells);
|
||||
dungeon_renderer_render(ctx->renderer, cells, nr_cells, view_transform, proj_transform);
|
||||
dungeon_renderer_render(ctx->renderer, cells, nr_cells, ctx->characters, view_transform, ctx->proj_transform);
|
||||
|
||||
glFrontFace(GL_CCW);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
|
||||
dungeon_renderer_run_post_processing(ctx->renderer, &ctx->texture, sprite_get_texture(sp));
|
||||
sprite_dirty(sp);
|
||||
}
|
||||
|
||||
static void neighbor_reveal(struct dungeon_context *ctx, int x, int y, int z)
|
||||
{
|
||||
struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z);
|
||||
if (cell->floor < 0) {
|
||||
cell->walked = 1;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
if (cell->floor < 0) {
|
||||
cell->walked = 1;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
}
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
if (cell->floor >= 0) {
|
||||
cell->walked |= 2;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
}
|
||||
break;
|
||||
case DRAW_DUNGEON_14:
|
||||
case DRAW_FIELD:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,9 +448,11 @@ void dungeon_set_walked(int surface, int x, int y, int z, int flag)
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx)
|
||||
return;
|
||||
dgn_cell_at(ctx->dgn, x, y, z)->walked = 1;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
if (ctx->version == DRAW_DUNGEON_1) {
|
||||
if (!dgn_is_in_map(ctx->dgn, x, y, z))
|
||||
return; // avoid VM_ERROR in Pastel Chime Continue
|
||||
if (flag) {
|
||||
dgn_cell_at(ctx->dgn, x, y, z)->walked = ctx->version == DRAW_DUNGEON_2 ? 3 : 1;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
if (x > 0)
|
||||
neighbor_reveal(ctx, x - 1, y, z);
|
||||
if (x + 1u < ctx->dgn->size_x)
|
||||
@@ -326,6 +461,28 @@ void dungeon_set_walked(int surface, int x, int y, int z, int flag)
|
||||
neighbor_reveal(ctx, x, y, z - 1);
|
||||
if (z + 1u < ctx->dgn->size_z)
|
||||
neighbor_reveal(ctx, x, y, z + 1);
|
||||
} else {
|
||||
dgn_cell_at(ctx->dgn, x, y, z)->walked = 0;
|
||||
dungeon_map_hide(ctx, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_set_walked_all(int surface)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx)
|
||||
return;
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
for (struct dgn_cell *c = ctx->dgn->cells; c < ctx->dgn->cells + nr_cells; c++) {
|
||||
if (ctx->version == DRAW_DUNGEON_2) {
|
||||
if (c->floor >= 0) {
|
||||
c->walked = 3;
|
||||
dungeon_map_reveal(ctx, c->x, c->y, c->z, false);
|
||||
}
|
||||
} else {
|
||||
c->walked = 1;
|
||||
dungeon_map_reveal(ctx, c->x, c->y, c->z, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +505,7 @@ int dungeon_calc_conquer(int surface)
|
||||
/*
|
||||
* WalkData serialization format:
|
||||
*
|
||||
* struct WalkData {
|
||||
* struct DrawDungeon1_WalkData {
|
||||
* int version; // zero
|
||||
* int nr_maps;
|
||||
* struct {
|
||||
@@ -357,17 +514,26 @@ int dungeon_calc_conquer(int surface)
|
||||
* int cells[size_x * size_y * size_z];
|
||||
* } maps[nr_maps];
|
||||
* };
|
||||
*
|
||||
* struct DrawDungeon2_WalkData {
|
||||
* int magic; // 1977
|
||||
* int reserved; // zero
|
||||
* int size_x, size_y, size_z;
|
||||
* int cells[size_x * size_y * size_z];
|
||||
* };
|
||||
*/
|
||||
|
||||
#define DD2_WALKDATA_MAGIC 1977
|
||||
|
||||
enum {
|
||||
WALK_DATA_NOT_FOUND = -1,
|
||||
WALK_DATA_BROKEN = -2,
|
||||
};
|
||||
|
||||
static int find_walk_data(struct page *array, int map_no, struct dgn *dgn)
|
||||
static int dd1_find_walk_data(struct page *array, int map_no, struct dgn *dgn)
|
||||
{
|
||||
if (!array)
|
||||
return WALK_DATA_BROKEN;
|
||||
return WALK_DATA_NOT_FOUND;
|
||||
if (array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->array.rank != 1)
|
||||
VM_ERROR("Not a flat integer array");
|
||||
|
||||
@@ -396,16 +562,13 @@ static int find_walk_data(struct page *array, int map_no, struct dgn *dgn)
|
||||
return WALK_DATA_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool dungeon_load_walk_data(int surface, int map, struct page **page)
|
||||
static bool dd1_load_walk_data(struct dungeon_context *ctx, int map, struct page **page)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->dgn)
|
||||
return false;
|
||||
struct page *array = *page;
|
||||
|
||||
int offset = find_walk_data(array, map, ctx->dgn);
|
||||
int offset = dd1_find_walk_data(array, map, ctx->dgn);
|
||||
if (offset < 0)
|
||||
return false;
|
||||
return offset != WALK_DATA_BROKEN;
|
||||
|
||||
offset += 4;
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
@@ -418,20 +581,62 @@ bool dungeon_load_walk_data(int surface, int map, struct page **page)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_save_walk_data(int surface, int map, struct page **page)
|
||||
static bool dd2_load_walk_data(struct dungeon_context *ctx, struct page **page)
|
||||
{
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
struct page *array = *page;
|
||||
if (!array || array->nr_vars != 5 + nr_cells) {
|
||||
WARNING("DrawDungeon2.LoadWalkData: invalid array size");
|
||||
return false;
|
||||
}
|
||||
|
||||
int ptr = 0;
|
||||
if (array->values[ptr++].i != DD2_WALKDATA_MAGIC)
|
||||
return false;
|
||||
if (array->values[ptr++].i != 0)
|
||||
return false;
|
||||
uint32_t size_x = array->values[ptr++].i;
|
||||
uint32_t size_y = array->values[ptr++].i;
|
||||
uint32_t size_z = array->values[ptr++].i;
|
||||
if (size_x != ctx->dgn->size_x || size_y != ctx->dgn->size_y || size_z != ctx->dgn->size_z)
|
||||
return false;
|
||||
|
||||
for (struct dgn_cell *c = ctx->dgn->cells; c < ctx->dgn->cells + nr_cells; c++) {
|
||||
c->walked = array->values[ptr++].i;
|
||||
if (c->walked)
|
||||
dungeon_map_reveal(ctx, c->x, c->y, c->z, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_load_walk_data(int surface, int map, struct page **page)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->dgn)
|
||||
return false;
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
case DRAW_DUNGEON_14:
|
||||
return dd1_load_walk_data(ctx, map, page);
|
||||
case DRAW_DUNGEON_2:
|
||||
return dd2_load_walk_data(ctx, page);
|
||||
case DRAW_FIELD:
|
||||
break; // DrawField does not export LoadWalkData
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dd1_save_walk_data(struct dungeon_context *ctx, int map, struct page **page)
|
||||
{
|
||||
struct page *array = *page;
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
|
||||
int offset = find_walk_data(array, map, ctx->dgn);
|
||||
int offset = dd1_find_walk_data(array, map, ctx->dgn);
|
||||
if (offset == WALK_DATA_NOT_FOUND) {
|
||||
offset = array->nr_vars;
|
||||
array->values[1].i += 1;
|
||||
union vm_value dim = { .i = array->nr_vars + 4 + nr_cells };
|
||||
offset = array ? array->nr_vars : 0;
|
||||
union vm_value dim = { .i = offset + 4 + nr_cells };
|
||||
*page = array = realloc_array(array, 1, &dim, AIN_ARRAY_INT, 0, false);
|
||||
array->values[1].i += 1;
|
||||
} else if (offset == WALK_DATA_BROKEN) {
|
||||
if (array)
|
||||
WARNING("Discarding broken walk data");
|
||||
@@ -449,3 +654,138 @@ bool dungeon_save_walk_data(int surface, int map, struct page **page)
|
||||
array->values[offset++].i = ctx->dgn->cells[i].walked;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dd2_save_walk_data(struct dungeon_context *ctx, struct page **page)
|
||||
{
|
||||
struct page *array = *page;
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
|
||||
if (!array || array->nr_vars != 5 + nr_cells) {
|
||||
WARNING("DrawDungeon2.SaveWalkData: invalid array size");
|
||||
return false;
|
||||
}
|
||||
|
||||
int ptr = 0;
|
||||
array->values[ptr++].i = DD2_WALKDATA_MAGIC;
|
||||
array->values[ptr++].i = 0;
|
||||
array->values[ptr++].i = ctx->dgn->size_x;
|
||||
array->values[ptr++].i = ctx->dgn->size_y;
|
||||
array->values[ptr++].i = ctx->dgn->size_z;
|
||||
for (int i = 0; i < nr_cells; i++)
|
||||
array->values[ptr++].i = ctx->dgn->cells[i].walked;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_save_walk_data(int surface, int map, struct page **page)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->dgn)
|
||||
return false;
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
case DRAW_DUNGEON_14:
|
||||
return dd1_save_walk_data(ctx, map, page);
|
||||
case DRAW_DUNGEON_2:
|
||||
return dd2_save_walk_data(ctx, page);
|
||||
case DRAW_FIELD:
|
||||
break; // DrawField does not export SaveWalkData
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void dungeon_paint_step(int surface, int x, int y, int z)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->dgn)
|
||||
return;
|
||||
dgn_paint_step(ctx->dgn, x, z);
|
||||
}
|
||||
|
||||
void dungeon_set_chara_sprite(int surface, int num, int sprite)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].sprite = sprite;
|
||||
}
|
||||
|
||||
void dungeon_set_chara_pos(int surface, int num, float x, float y, float z)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].pos[0] = x;
|
||||
ctx->characters[num].pos[1] = y;
|
||||
ctx->characters[num].pos[2] = -z;
|
||||
}
|
||||
|
||||
void dungeon_set_chara_cg(int surface, int num, int cg)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].cg_index = cg;
|
||||
}
|
||||
|
||||
void dungeon_set_chara_cg_info(int surface, int num, int num_chara_x, int num_chara_y)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].rows = num_chara_y;
|
||||
ctx->characters[num].cols = num_chara_x;
|
||||
}
|
||||
|
||||
void dungeon_set_chara_show(int surface, int num, bool show)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].show = show;
|
||||
}
|
||||
|
||||
bool dungeon_project_world_to_screen(struct dungeon_context *ctx, vec3 world_pos, Point *screen_pos)
|
||||
{
|
||||
mat4 view_transform;
|
||||
model_view_matrix(ctx, view_transform);
|
||||
mat4 mvp;
|
||||
glm_mat4_mul(ctx->proj_transform, view_transform, mvp);
|
||||
|
||||
vec4 world_pos_h = {
|
||||
world_pos[0],
|
||||
world_pos[1],
|
||||
-world_pos[2], // Z is negated to match coordinate system
|
||||
1.f
|
||||
};
|
||||
|
||||
vec4 clip_coords;
|
||||
glm_mat4_mulv(mvp, world_pos_h, clip_coords);
|
||||
|
||||
if (clip_coords[3] <= 0.f) {
|
||||
return false; // point is behind the near clipping plane
|
||||
}
|
||||
|
||||
vec3 ndc;
|
||||
ndc[0] = clip_coords[0] / clip_coords[3];
|
||||
ndc[1] = clip_coords[1] / clip_coords[3];
|
||||
ndc[2] = clip_coords[2] / clip_coords[3];
|
||||
|
||||
screen_pos->x = roundf(((ndc[0] + 1.f) / 2.f) * ctx->texture.w);
|
||||
// Y is inverted from NDC to screen space (NDC is bottom-up, screen is top-down)
|
||||
screen_pos->y = roundf(((1.f - ndc[1]) / 2.f) * ctx->texture.h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static cJSON *dungeon_to_json(struct sact_sprite *sp, bool verbose)
|
||||
{
|
||||
cJSON *obj, *cam;
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)sp->plugin;
|
||||
obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(obj, "name", ctx->plugin.name);
|
||||
cJSON_AddItemToObjectCS(obj, "camera", cam = cJSON_CreateObject());
|
||||
cJSON_AddItemToObjectCS(cam, "pos", vec3_point_to_json(ctx->camera.pos, verbose));
|
||||
cJSON_AddNumberToObject(cam, "angle", ctx->camera.angle);
|
||||
cJSON_AddNumberToObject(cam, "angle_p", ctx->camera.angle_p);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+257
-79
@@ -25,38 +25,40 @@
|
||||
#include "sact.h"
|
||||
#include "vm.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
#define CS 12 // pixel size of a map cell
|
||||
#define LMAP_WIDTH 32
|
||||
#define LMAP_HEIGHT 32
|
||||
|
||||
static const SDL_Color unenterable_cell_color = {182, 72, 0, 0};
|
||||
|
||||
enum symbol_type {
|
||||
SYMBOL_PLAYER_NORTH = 0,
|
||||
SYMBOL_PLAYER_SOUTH = 1,
|
||||
SYMBOL_PLAYER_EAST = 2,
|
||||
SYMBOL_PLAYER_WEST = 3,
|
||||
SYMBOL_CROSS = 4,
|
||||
SYMBOL_EXIT = 5,
|
||||
SYMBOL_STAIRS_UP = 6,
|
||||
SYMBOL_STAIRS_DOWN = 7,
|
||||
SYMBOL_EVENT = 8,
|
||||
SYMBOL_HEART = 9,
|
||||
SYMBOL_WARP = 10,
|
||||
SYMBOL_TREASURE = 11,
|
||||
SYMBOL_MONSTER = 12,
|
||||
SYMBOL_YELLOW_STAR = 13,
|
||||
enum dd1_symbol_type {
|
||||
DD1_SYMBOL_PLAYER_NORTH = 0,
|
||||
DD1_SYMBOL_PLAYER_SOUTH = 1,
|
||||
DD1_SYMBOL_PLAYER_EAST = 2,
|
||||
DD1_SYMBOL_PLAYER_WEST = 3,
|
||||
DD1_SYMBOL_CROSS = 4,
|
||||
DD1_SYMBOL_EXIT = 5,
|
||||
DD1_SYMBOL_STAIRS_UP = 6,
|
||||
DD1_SYMBOL_STAIRS_DOWN = 7,
|
||||
DD1_SYMBOL_EVENT = 8,
|
||||
DD1_SYMBOL_HEART = 9,
|
||||
DD1_SYMBOL_WARP = 10,
|
||||
DD1_SYMBOL_TREASURE = 11,
|
||||
DD1_SYMBOL_MONSTER = 12,
|
||||
DD1_SYMBOL_YELLOW_STAR = 13,
|
||||
NR_SYMBOLS = 14
|
||||
};
|
||||
|
||||
enum dd2_symbol_type {
|
||||
DD2_SYMBOL_PLAYER_NORTH = 0,
|
||||
DD2_SYMBOL_PLAYER_EAST = 1,
|
||||
DD2_SYMBOL_PLAYER_WEST = 2,
|
||||
DD2_SYMBOL_PLAYER_SOUTH = 3,
|
||||
DD2_SYMBOL_START = 4,
|
||||
DD2_SYMBOL_EXIT = 5,
|
||||
};
|
||||
|
||||
struct dungeon_map {
|
||||
struct texture *textures;
|
||||
int nr_textures;
|
||||
int symbol_sprites[NR_SYMBOLS];
|
||||
int grid_size;
|
||||
int small_map_floor;
|
||||
int large_map_floor;
|
||||
bool radar;
|
||||
@@ -67,8 +69,8 @@ struct dungeon_map {
|
||||
static Point texture_pos(struct dungeon_context *ctx, int x, int z)
|
||||
{
|
||||
return (Point){
|
||||
.x = (ctx->map->offset_x + x) * CS,
|
||||
.y = (ctx->map->offset_y + ctx->dgn->size_z - z - 1) * CS
|
||||
.x = (ctx->map->offset_x + x) * ctx->map->grid_size,
|
||||
.y = (ctx->map->offset_y + ctx->dgn->size_z - z - 1) * ctx->map->grid_size,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,6 +80,18 @@ struct dungeon_map *dungeon_map_create(enum draw_dungeon_version version)
|
||||
return NULL; // GALZOO does not use DrawDungeon's 2D map functions.
|
||||
|
||||
struct dungeon_map *map = xcalloc(1, sizeof(struct dungeon_map));
|
||||
switch (version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
case DRAW_FIELD:
|
||||
map->grid_size = 12;
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
map->grid_size = 11;
|
||||
break;
|
||||
case DRAW_DUNGEON_14:
|
||||
ERROR("cannot happen");
|
||||
break;
|
||||
}
|
||||
map->small_map_floor = -1;
|
||||
map->large_map_floor = -1;
|
||||
return map;
|
||||
@@ -98,33 +112,65 @@ void dungeon_map_init(struct dungeon_context *ctx)
|
||||
if (!map)
|
||||
return;
|
||||
|
||||
if (map->textures) {
|
||||
for (int i = 0; i < map->nr_textures; i++)
|
||||
gfx_delete_texture(&map->textures[i]);
|
||||
free(map->textures);
|
||||
}
|
||||
map->textures = xcalloc(dgn->size_y, sizeof(struct texture));
|
||||
map->nr_textures = dgn->size_y;
|
||||
// Calculate the bounding box on the XZ plane so that map is drawn in the
|
||||
// center of the texture.
|
||||
uint32_t min_x = dgn->size_x;
|
||||
uint32_t min_y = dgn->size_z;
|
||||
uint32_t max_x = 0;
|
||||
uint32_t max_y = 0;
|
||||
for (uint32_t y = 0; y < dgn->size_y; y++) {
|
||||
for (uint32_t z = 0; z < dgn->size_z; z++) {
|
||||
uint32_t map_y = ctx->dgn->size_z - z - 1;
|
||||
for (uint32_t x = 0; x < dgn->size_x; x++) {
|
||||
if (dgn_cell_at(ctx->dgn, x, y, z)->floor < 0)
|
||||
continue;
|
||||
min_x = min(min_x, x);
|
||||
min_y = min(min_y, map_y);
|
||||
max_x = max(max_x, x);
|
||||
max_y = max(max_y, map_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
map->offset_x = (LMAP_WIDTH - (max_x - min_x + 1)) / 2 - min_x;
|
||||
map->offset_y = (LMAP_HEIGHT - (max_y - min_y)) / 2 - min_y;
|
||||
|
||||
int map_width;
|
||||
int map_height;
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1: {
|
||||
// Calculate the bounding box on the XZ plane so that map is drawn in the
|
||||
// center of the texture.
|
||||
uint32_t min_x = dgn->size_x;
|
||||
uint32_t min_y = dgn->size_z;
|
||||
uint32_t max_x = 0;
|
||||
uint32_t max_y = 0;
|
||||
for (uint32_t y = 0; y < dgn->size_y; y++) {
|
||||
for (uint32_t z = 0; z < dgn->size_z; z++) {
|
||||
uint32_t map_y = ctx->dgn->size_z - z - 1;
|
||||
for (uint32_t x = 0; x < dgn->size_x; x++) {
|
||||
if (dgn_cell_at(ctx->dgn, x, y, z)->floor < 0)
|
||||
continue;
|
||||
min_x = min(min_x, x);
|
||||
min_y = min(min_y, map_y);
|
||||
max_x = max(max_x, x);
|
||||
max_y = max(max_y, map_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
map_width = 32;
|
||||
map_height = 32;
|
||||
map->offset_x = (map_width - (max_x - min_x + 1)) / 2 - min_x;
|
||||
map->offset_y = (map_height - (max_y - min_y)) / 2 - min_y;
|
||||
}
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
map_width = 19;
|
||||
map_height = 19;
|
||||
map->offset_x = 0;
|
||||
map->offset_y = 1;
|
||||
break;
|
||||
case DRAW_DUNGEON_14:
|
||||
ERROR("cannot happen");
|
||||
break;
|
||||
case DRAW_FIELD:
|
||||
map_width = dgn->size_x;
|
||||
map_height = dgn->size_z;
|
||||
map->offset_x = 0;
|
||||
map->offset_y = 0;
|
||||
break;
|
||||
}
|
||||
// Initial draw
|
||||
for (uint32_t y = 0; y < dgn->size_y; y++) {
|
||||
gfx_init_texture_rgba(&map->textures[y], LMAP_WIDTH * CS, LMAP_HEIGHT * CS, unenterable_cell_color);
|
||||
gfx_init_texture_rgba(&map->textures[y],
|
||||
map_width * map->grid_size,
|
||||
map_height * map->grid_size,
|
||||
unenterable_cell_color);
|
||||
for (uint32_t z = 0; z < dgn->size_z; z++) {
|
||||
for (uint32_t x = 0; x < dgn->size_x; x++) {
|
||||
dungeon_map_update_cell(ctx, x, y, z);
|
||||
@@ -133,13 +179,43 @@ void dungeon_map_init(struct dungeon_context *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_map_reveal(struct dungeon_context *ctx, int x, int y, int z, bool transparent)
|
||||
void dungeon_map_reveal(struct dungeon_context *ctx, int x, int y, int z, bool from_walk_data)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
struct texture *dst = &ctx->map->textures[y];
|
||||
struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z);
|
||||
Point pos = texture_pos(ctx, x, z);
|
||||
int alpha;
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
alpha = from_walk_data ? 128 : 255;
|
||||
gfx_fill_amap(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, alpha);
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
if ((x == ctx->dgn->start_x && z == ctx->dgn->start_y) || cell->floor_event == 17) {
|
||||
// Do not overwrite the start/goal symbol.
|
||||
gfx_fill_amap(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, 255);
|
||||
} else {
|
||||
int rg = (cell->walked & 1) ? 0 : 255;
|
||||
gfx_fill_with_alpha(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, rg, rg, 0, 255);
|
||||
}
|
||||
break;
|
||||
case DRAW_FIELD:
|
||||
alpha = cell->floor == -1 ? 160 : 255;
|
||||
gfx_fill_amap(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, alpha);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_map_hide(struct dungeon_context *ctx, int x, int y, int z)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
struct texture *dst = &ctx->map->textures[y];
|
||||
Point pos = texture_pos(ctx, x, z);
|
||||
gfx_fill_amap(dst, pos.x, pos.y, CS, CS, transparent ? 128 : 255);
|
||||
gfx_fill_amap(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, 0);
|
||||
}
|
||||
|
||||
static void draw_hline(struct texture *dst, int x, int y, int w, SDL_Color col)
|
||||
@@ -155,13 +231,13 @@ static void draw_vline(struct texture *dst, int x, int y, int h, SDL_Color col)
|
||||
static void draw_symbol(struct dungeon_map *map, struct texture *dst, int x, int y, int type)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(map->symbol_sprites[type]);
|
||||
if (!sp || sp->rect.w != CS - 2 || sp->rect.h != CS - 2)
|
||||
if (!sp)
|
||||
VM_ERROR("Invalid map cg %d", map->symbol_sprites[type]);
|
||||
struct texture *src = sprite_get_texture(sp);
|
||||
gfx_copy(dst, x, y, src, 0, 0, src->w, src->h);
|
||||
}
|
||||
|
||||
void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
static void dd1_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
@@ -170,6 +246,7 @@ void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y,
|
||||
Point pos = texture_pos(ctx, dgn_x, dgn_z);
|
||||
int x = pos.x;
|
||||
int y = pos.y;
|
||||
const int CS = ctx->map->grid_size;
|
||||
|
||||
if (cell->floor >= 0)
|
||||
gfx_fill(dst, x, y, CS, CS, 0, 0, 0);
|
||||
@@ -217,33 +294,126 @@ void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y,
|
||||
bool draw_all_symbols = ctx->map->radar || ctx->map->all_visible;
|
||||
int symbol = -1;
|
||||
switch (cell->floor_event) {
|
||||
case 11: if (draw_all_symbols) symbol = SYMBOL_TREASURE; break;
|
||||
case 12: if (draw_all_symbols) symbol = SYMBOL_MONSTER; break;
|
||||
case 14: case 140: symbol = SYMBOL_EVENT; break;
|
||||
case 15: symbol = SYMBOL_HEART; break;
|
||||
case 16: if (draw_all_symbols) symbol = SYMBOL_CROSS; break;
|
||||
case 17: symbol = SYMBOL_EXIT; break;
|
||||
case 18: symbol = SYMBOL_WARP; break;
|
||||
case 28: if (draw_all_symbols) symbol = SYMBOL_YELLOW_STAR; break;
|
||||
case 11: if (draw_all_symbols) symbol = DD1_SYMBOL_TREASURE; break;
|
||||
case 12: if (draw_all_symbols) symbol = DD1_SYMBOL_MONSTER; break;
|
||||
case 14: case 140: symbol = DD1_SYMBOL_EVENT; break;
|
||||
case 15: symbol = DD1_SYMBOL_HEART; break;
|
||||
case 16: if (draw_all_symbols) symbol = DD1_SYMBOL_CROSS; break;
|
||||
case 17: symbol = DD1_SYMBOL_EXIT; break;
|
||||
case 18: symbol = DD1_SYMBOL_WARP; break;
|
||||
case 28: if (draw_all_symbols) symbol = DD1_SYMBOL_YELLOW_STAR; break;
|
||||
}
|
||||
if (cell->stairs_texture >= 0)
|
||||
symbol = SYMBOL_STAIRS_UP;
|
||||
symbol = DD1_SYMBOL_STAIRS_UP;
|
||||
else if (dgn_y && dgn_cell_at(ctx->dgn, dgn_x, dgn_y - 1, dgn_z)->stairs_texture >= 0)
|
||||
symbol = SYMBOL_STAIRS_DOWN;
|
||||
symbol = DD1_SYMBOL_STAIRS_DOWN;
|
||||
|
||||
if (symbol >= 0)
|
||||
draw_symbol(ctx->map, dst, x + 1, y + 1, symbol);
|
||||
}
|
||||
|
||||
static void dd2_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
struct dgn_cell *cell = dgn_cell_at(ctx->dgn, dgn_x, dgn_y, dgn_z);
|
||||
struct texture *dst = &ctx->map->textures[0];
|
||||
Point pos = texture_pos(ctx, dgn_x, dgn_z);
|
||||
|
||||
if (dgn_x == ctx->dgn->start_x && dgn_z == ctx->dgn->start_y) {
|
||||
draw_symbol(ctx->map, dst, pos.x, pos.y, DD2_SYMBOL_START);
|
||||
} else if (cell->floor_event == 17) {
|
||||
draw_symbol(ctx->map, dst, pos.x, pos.y, DD2_SYMBOL_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void df_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
struct dgn_cell *cell = dgn_cell_at(ctx->dgn, dgn_x, dgn_y, dgn_z);
|
||||
struct texture *dst = &ctx->map->textures[dgn_y];
|
||||
Point pos = texture_pos(ctx, dgn_x, dgn_z);
|
||||
int x = pos.x;
|
||||
int y = pos.y;
|
||||
const int CS = ctx->map->grid_size;
|
||||
|
||||
if (cell->floor >= 0)
|
||||
gfx_fill(dst, x, y, CS, CS, 128, 64, 32);
|
||||
else
|
||||
gfx_fill(dst, x, y, CS, CS, 0, 0, 0);
|
||||
|
||||
const SDL_Color wall_color = {255, 255, 255, 255};
|
||||
if (cell->north_wall >= 0)
|
||||
draw_hline(dst, x, y, CS, wall_color);
|
||||
if (cell->south_wall >= 0)
|
||||
draw_hline(dst, x, y + CS - 1, CS, wall_color);
|
||||
if (cell->west_wall >= 0)
|
||||
draw_vline(dst, x, y, CS, wall_color);
|
||||
if (cell->east_wall >= 0)
|
||||
draw_vline(dst, x + CS - 1, y, CS, wall_color);
|
||||
|
||||
const SDL_Color door_colors[2] = {{0, 255, 0, 255}, {255, 0, 0, 255}};
|
||||
if (cell->north_door >= 0) {
|
||||
draw_hline(dst, x, y, CS / 2 - 1, door_colors[cell->north_door_lock]);
|
||||
draw_hline(dst, x + CS / 2 - 1, y, 2, COLOR(0, 0, 0, 0));
|
||||
draw_hline(dst, x + CS / 2 + 1, y, CS / 2 - 1, door_colors[cell->north_door_lock]);
|
||||
}
|
||||
if (cell->south_door >= 0) {
|
||||
draw_hline(dst, x, y + CS - 1, CS / 2 - 1, door_colors[cell->south_door_lock]);
|
||||
draw_hline(dst, x + CS / 2 - 1, y + CS - 1, 2, COLOR(0, 0, 0, 0));
|
||||
draw_hline(dst, x + CS / 2 + 1, y + CS - 1, CS / 2 - 1, door_colors[cell->south_door_lock]);
|
||||
}
|
||||
if (cell->west_door >= 0) {
|
||||
draw_vline(dst, x, y, CS / 2 - 1, door_colors[cell->west_door_lock]);
|
||||
draw_vline(dst, x, y + CS / 2 - 1, 2, COLOR(0, 0, 0, 0));
|
||||
draw_vline(dst, x, y + CS / 2 + 1, CS / 2 - 1, door_colors[cell->west_door_lock]);
|
||||
}
|
||||
if (cell->east_door >= 0) {
|
||||
draw_vline(dst, x + CS - 1, y, CS / 2 - 1, door_colors[cell->east_door_lock]);
|
||||
draw_vline(dst, x + CS - 1, y + CS / 2 - 1, 2, COLOR(0, 0, 0, 0));
|
||||
draw_vline(dst, x + CS - 1, y + CS / 2 + 1, CS / 2 - 1, door_colors[cell->east_door_lock]);
|
||||
}
|
||||
|
||||
if (cell->stairs_texture >= 0)
|
||||
gfx_fill(dst, x + 2, y + 2, CS - 4, CS - 4, 0, 255, 255);
|
||||
else if (dgn_y && dgn_cell_at(ctx->dgn, dgn_x, dgn_y - 1, dgn_z)->stairs_texture >= 0)
|
||||
gfx_fill(dst, x + 2, y + 2, CS - 4, CS - 4, 0, 0, 255);
|
||||
}
|
||||
|
||||
void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
{
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
dd1_update_cell(ctx, dgn_x, dgn_y, dgn_z);
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
dd2_update_cell(ctx, dgn_x, dgn_y, dgn_z);
|
||||
break;
|
||||
case DRAW_DUNGEON_14:
|
||||
// unused
|
||||
break;
|
||||
case DRAW_FIELD:
|
||||
df_update_cell(ctx, dgn_x, dgn_y, dgn_z);
|
||||
}
|
||||
}
|
||||
|
||||
static int player_symbol(struct dungeon_context *ctx)
|
||||
{
|
||||
const int symbols[] = {
|
||||
SYMBOL_PLAYER_NORTH,
|
||||
SYMBOL_PLAYER_EAST,
|
||||
SYMBOL_PLAYER_SOUTH,
|
||||
SYMBOL_PLAYER_WEST,
|
||||
const int dd1_symbols[] = {
|
||||
DD1_SYMBOL_PLAYER_NORTH,
|
||||
DD1_SYMBOL_PLAYER_EAST,
|
||||
DD1_SYMBOL_PLAYER_SOUTH,
|
||||
DD1_SYMBOL_PLAYER_WEST,
|
||||
};
|
||||
return symbols[(int)(round(ctx->camera.angle * 2 / M_PI)) & 3];
|
||||
const int dd2_symbols[] = {
|
||||
DD2_SYMBOL_PLAYER_NORTH,
|
||||
DD2_SYMBOL_PLAYER_EAST,
|
||||
DD2_SYMBOL_PLAYER_SOUTH,
|
||||
DD2_SYMBOL_PLAYER_WEST,
|
||||
};
|
||||
const int *symbols = ctx->version == DRAW_DUNGEON_1 ? dd1_symbols : dd2_symbols;
|
||||
return symbols[(int)(roundf(ctx->camera.angle * 2 / GLM_PIf)) & 3];
|
||||
}
|
||||
|
||||
void dungeon_map_draw(int surface, int sprite)
|
||||
@@ -251,22 +421,27 @@ void dungeon_map_draw(int surface, int sprite)
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->map)
|
||||
return;
|
||||
|
||||
if (ctx->version == DRAW_DUNGEON_2) {
|
||||
// Draw the entire map
|
||||
dungeon_map_draw_lmap(surface, sprite);
|
||||
return;
|
||||
}
|
||||
|
||||
struct sact_sprite *sp = sact_get_sprite(sprite);
|
||||
if (!sp)
|
||||
VM_ERROR("DrawDungeon.DrawMap: invalid sprite %d", sprite);
|
||||
sprite_dirty(sp);
|
||||
struct texture *dst = sprite_get_texture(sp);
|
||||
|
||||
int player_x = round(ctx->camera.pos[0] / 2.0);
|
||||
int player_y = round(ctx->camera.pos[1] / 2.0);
|
||||
int player_z = round(ctx->camera.pos[2] / -2.0);
|
||||
int level = ctx->map->small_map_floor;
|
||||
if (level < 0)
|
||||
level = player_y;
|
||||
level = ctx->player_pos[1];
|
||||
|
||||
struct texture *src = &ctx->map->textures[level];
|
||||
|
||||
Point pos = texture_pos(ctx, player_x, player_z);
|
||||
const int CS = ctx->map->grid_size;
|
||||
Point pos = texture_pos(ctx, ctx->player_pos[0], ctx->player_pos[2]);
|
||||
int x = pos.x - (dst->w - CS) / 2;
|
||||
int y = pos.y - (dst->h - CS) / 2;
|
||||
|
||||
@@ -292,12 +467,9 @@ void dungeon_map_draw_lmap(int surface, int sprite)
|
||||
sprite_dirty(sp);
|
||||
struct texture *dst = sprite_get_texture(sp);
|
||||
|
||||
int player_x = round(ctx->camera.pos[0] / 2.0);
|
||||
int player_y = round(ctx->camera.pos[1] / 2.0);
|
||||
int player_z = round(ctx->camera.pos[2] / -2.0);
|
||||
int level = ctx->map->large_map_floor;
|
||||
if (level < 0)
|
||||
level = player_y;
|
||||
level = ctx->player_pos[1];
|
||||
|
||||
struct texture *src = &ctx->map->textures[level];
|
||||
gfx_copy_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
|
||||
@@ -306,9 +478,15 @@ void dungeon_map_draw_lmap(int surface, int sprite)
|
||||
gfx_fill_amap(dst, 0, 0, dst->w, dst->h, 255);
|
||||
|
||||
// Draw the player symbol.
|
||||
Point pos = texture_pos(ctx, player_x, player_z);
|
||||
draw_symbol(ctx->map, dst, pos.x + 1, pos.y + 1, player_symbol(ctx));
|
||||
gfx_fill_amap(dst, pos.x + 1, pos.y + 1, CS - 2, CS - 2, level == player_y ? 255 : 128);
|
||||
Point pos = texture_pos(ctx, ctx->player_pos[0], ctx->player_pos[2]);
|
||||
if (ctx->version == DRAW_FIELD) {
|
||||
int ofs = (ctx->map->grid_size - 4) / 2;
|
||||
gfx_fill(dst, pos.x + ofs, pos.y + ofs, 4, 4, 255, 192, 255);
|
||||
} else {
|
||||
draw_symbol(ctx->map, dst, pos.x + 1, pos.y + 1, player_symbol(ctx));
|
||||
int size = ctx->map->grid_size - 2;
|
||||
gfx_fill_amap(dst, pos.x + 1, pos.y + 1, size, size, level == ctx->player_pos[1] ? 255 : 128);
|
||||
}
|
||||
}
|
||||
|
||||
static void redraw_event_symbols(struct dungeon_context *ctx)
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// Mersenne Twister engine with N=4, M=3
|
||||
// Derived from mt19937int.c in libsys4
|
||||
|
||||
/* A C-program for MT19937: Integer version (1998/4/6) */
|
||||
/* genrand() generates one pseudorandom unsigned integer (32bit) */
|
||||
/* which is uniformly distributed among 0 to 2^32-1 for each */
|
||||
/* call. sgenrand(seed) set initial values to the working area */
|
||||
/* of 624 words. Before genrand(), sgenrand(seed) must be */
|
||||
/* called once. (seed is any 32-bit integer except for 0). */
|
||||
/* Coded by Takuji Nishimura, considering the suggestions by */
|
||||
/* Topher Cooper and Marc Rieffel in July-Aug. 1997. */
|
||||
|
||||
/* This library is free software; you can redistribute it and/or */
|
||||
/* modify it under the terms of the GNU Library General Public */
|
||||
/* License as published by the Free Software Foundation; either */
|
||||
/* version 2 of the License, or (at your option) any later */
|
||||
/* version. */
|
||||
/* This library 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 Library General Public License for more details. */
|
||||
/* You should have received a copy of the GNU Library General */
|
||||
/* Public License along with this library; if not, write to the */
|
||||
/* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */
|
||||
/* 02111-1307 USA */
|
||||
|
||||
/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */
|
||||
/* When you use this, send an email to: matumoto@math.keio.ac.jp */
|
||||
/* with an appropriate reference to your work. */
|
||||
|
||||
/* REFERENCE */
|
||||
/* M. Matsumoto and T. Nishimura, */
|
||||
/* "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform */
|
||||
/* Pseudo-Random Number Generator", */
|
||||
/* ACM Transactions on Modeling and Computer Simulation, */
|
||||
/* Vol. 8, No. 1, January 1998, pp 3--30. */
|
||||
|
||||
#include "dungeon/mtrand43.h"
|
||||
|
||||
/* Period parameters */
|
||||
#define N MTRAND43_STATE_SIZE
|
||||
#define M 3
|
||||
#define MATRIX_A 0x9908b0df /* constant vector a */
|
||||
#define UPPER_MASK 0x80000000 /* most significant w-r bits */
|
||||
#define LOWER_MASK 0x7fffffff /* least significant r bits */
|
||||
|
||||
/* Tempering parameters */
|
||||
#define TEMPERING_MASK_B 0x9d2c5680
|
||||
#define TEMPERING_MASK_C 0xefc60000
|
||||
#define TEMPERING_SHIFT_U(y) (y >> 11)
|
||||
#define TEMPERING_SHIFT_S(y) (y << 7)
|
||||
#define TEMPERING_SHIFT_T(y) (y << 15)
|
||||
#define TEMPERING_SHIFT_L(y) (y >> 18)
|
||||
|
||||
/* initializing the array with a NONZERO seed */
|
||||
void mtrand43_init(struct mtrand43 *mt, uint32_t seed)
|
||||
{
|
||||
/* setting initial seeds to mt[N] using */
|
||||
/* the generator Line 25 of Table 1 in */
|
||||
/* [KNUTH 1981, The Art of Computer Programming */
|
||||
/* Vol. 2 (2nd Ed.), pp102] */
|
||||
mt->st[0]= seed & 0xffffffff;
|
||||
for (int i=1; i<N; i++)
|
||||
mt->st[i] = (69069 * mt->st[i-1]) & 0xffffffff;
|
||||
mt->i = N;
|
||||
}
|
||||
|
||||
uint32_t mtrand43_genrand(struct mtrand43 *mt)
|
||||
{
|
||||
uint32_t y;
|
||||
static const uint32_t mag01[2]={0x0, MATRIX_A};
|
||||
/* mag01[x] = x * MATRIX_A for x=0,1 */
|
||||
|
||||
if (mt->i >= N) { /* generate N words at one time */
|
||||
int kk;
|
||||
|
||||
for (kk=0;kk<N-M;kk++) {
|
||||
y = (mt->st[kk]&UPPER_MASK)|(mt->st[kk+1]&LOWER_MASK);
|
||||
mt->st[kk] = mt->st[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
}
|
||||
for (;kk<N-1;kk++) {
|
||||
y = (mt->st[kk]&UPPER_MASK)|(mt->st[kk+1]&LOWER_MASK);
|
||||
mt->st[kk] = mt->st[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
}
|
||||
y = (mt->st[N-1]&UPPER_MASK)|(mt->st[0]&LOWER_MASK);
|
||||
mt->st[N-1] = mt->st[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
|
||||
mt->i = 0;
|
||||
}
|
||||
|
||||
y = mt->st[mt->i++];
|
||||
y ^= TEMPERING_SHIFT_U(y);
|
||||
y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
|
||||
y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
|
||||
y ^= TEMPERING_SHIFT_L(y);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
// Returns a random float between [0.0, 1.0], inclusive.
|
||||
float mtrand43_genfloat(struct mtrand43 *mt)
|
||||
{
|
||||
return (float)(mtrand43_genrand(mt)) / 4294967296.f;
|
||||
}
|
||||
+329
-43
@@ -30,6 +30,12 @@
|
||||
#include "dungeon/renderer.h"
|
||||
#include "dungeon/skybox.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "sact.h"
|
||||
|
||||
enum {
|
||||
COLOR_TEXTURE_UNIT,
|
||||
LIGHT_TEXTURE_UNIT,
|
||||
};
|
||||
|
||||
struct marker_info {
|
||||
int event_type;
|
||||
@@ -42,10 +48,18 @@ enum floor_marker {
|
||||
// Rance VI
|
||||
R6_MAGIC_CIRCLE = 1,
|
||||
R6_RED_OCTAGRAM = 63,
|
||||
// Dungeons & Dolls
|
||||
DD_MAGIC_CIRCLE = 40,
|
||||
DD_RED_OCTAGRAM = 41,
|
||||
DD_WHITE_OCTAGRAM = 42,
|
||||
// GALZOO Island
|
||||
GZ_MAGIC_CIRCLE = 0,
|
||||
};
|
||||
|
||||
static const struct marker_info empty_marker_info[] = {
|
||||
{0},
|
||||
};
|
||||
|
||||
static const struct marker_info rance6_marker_info[] = {
|
||||
{ 11, 41, 3, R6_MAGIC_CIRCLE}, // treasure chest
|
||||
{ 12, 51, 7, R6_RED_OCTAGRAM}, // enemy
|
||||
@@ -58,6 +72,18 @@ static const struct marker_info rance6_marker_info[] = {
|
||||
{0},
|
||||
};
|
||||
|
||||
static const struct marker_info dolls_marker_info[] = {
|
||||
{11, 43, 3, DD_WHITE_OCTAGRAM}, // treasure chest
|
||||
{17, 0, 20, DD_MAGIC_CIRCLE}, // goal
|
||||
{0},
|
||||
};
|
||||
|
||||
static const struct marker_info dolls_boss_marker_info[] = {
|
||||
{11, 43, 3, DD_WHITE_OCTAGRAM}, // treasure chest
|
||||
{17, 20, 20, DD_RED_OCTAGRAM}, // boss
|
||||
{0},
|
||||
};
|
||||
|
||||
static const struct marker_info galzoo_marker_info[] = {
|
||||
{11, 1, 0, GZ_MAGIC_CIRCLE}, // item
|
||||
{12, 2, 0, GZ_MAGIC_CIRCLE}, // enemy
|
||||
@@ -70,21 +96,33 @@ static const struct marker_info galzoo_marker_info[] = {
|
||||
|
||||
static const struct marker_info *marker_tables[] = {
|
||||
[DRAW_DUNGEON_1] = rance6_marker_info,
|
||||
[DRAW_DUNGEON_14] = galzoo_marker_info
|
||||
[DRAW_DUNGEON_2] = dolls_marker_info,
|
||||
[DRAW_DUNGEON_14] = galzoo_marker_info,
|
||||
[DRAW_FIELD] = empty_marker_info
|
||||
};
|
||||
|
||||
struct geometry;
|
||||
struct material;
|
||||
struct object3d;
|
||||
|
||||
struct raster_shader {
|
||||
struct shader s;
|
||||
GLint amp;
|
||||
GLint t;
|
||||
};
|
||||
|
||||
struct dungeon_renderer {
|
||||
enum draw_dungeon_version version;
|
||||
struct shader shader;
|
||||
// Uniform variable locations
|
||||
GLint local_transform;
|
||||
GLint proj_transform;
|
||||
GLuint alpha_mod;
|
||||
// Attribute variable locations
|
||||
GLint vertex_uv;
|
||||
GLint alpha_mod;
|
||||
GLint use_lightmap;
|
||||
GLint light_texture;
|
||||
GLint use_fog;
|
||||
GLint uv_offset;
|
||||
GLint uv_scale;
|
||||
|
||||
struct geometry *wall_geometry;
|
||||
struct geometry *door_left_geometry;
|
||||
@@ -108,6 +146,23 @@ struct dungeon_renderer {
|
||||
struct material *polyobj_materials;
|
||||
|
||||
struct skybox *skybox;
|
||||
|
||||
struct raster_shader raster_shader;
|
||||
bool raster_scroll;
|
||||
float raster_amp;
|
||||
|
||||
bool enable_lightmap;
|
||||
uint32_t draw_obj_flags;
|
||||
};
|
||||
|
||||
enum draw_obj_flag_index {
|
||||
DRAW_POLYOBJ = 0,
|
||||
DRAW_FLOOR = 1,
|
||||
DRAW_WALL = 2,
|
||||
DRAW_DOOR = 3,
|
||||
DRAW_CEILING = 4,
|
||||
DRAW_STAIRS = 5,
|
||||
NR_DRAW_OBJ_FLAGS
|
||||
};
|
||||
|
||||
static const struct marker_info *get_marker_info(struct dungeon_renderer *r, int event_type)
|
||||
@@ -211,10 +266,10 @@ static struct geometry *geometry_create(struct dungeon_renderer *r, const struct
|
||||
glGenBuffers(1, &g->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g->attr_buffer);
|
||||
|
||||
glEnableVertexAttribArray(r->shader.vertex);
|
||||
glVertexAttribPointer(r->shader.vertex, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)0);
|
||||
glEnableVertexAttribArray(r->vertex_uv);
|
||||
glVertexAttribPointer(r->vertex_uv, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)(3 * sizeof(GLfloat)));
|
||||
glEnableVertexAttribArray(r->shader.vertex_pos);
|
||||
glVertexAttribPointer(r->shader.vertex_pos, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)0);
|
||||
glEnableVertexAttribArray(r->shader.vertex_uv);
|
||||
glVertexAttribPointer(r->shader.vertex_uv, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)(3 * sizeof(GLfloat)));
|
||||
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
@@ -332,15 +387,46 @@ static void init_polyobjs(struct dungeon_renderer *r, struct polyobj *po)
|
||||
}
|
||||
}
|
||||
|
||||
struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po)
|
||||
static bool is_dolls_boss_floor(int num)
|
||||
{
|
||||
switch (num) {
|
||||
case 20:
|
||||
case 40:
|
||||
case 60:
|
||||
case 80:
|
||||
case 100:
|
||||
case 120:
|
||||
case 140:
|
||||
case 150:
|
||||
case 160:
|
||||
case 180:
|
||||
case 200:
|
||||
case 300:
|
||||
case 500:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, int num, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po)
|
||||
{
|
||||
struct dungeon_renderer *r = xcalloc(1, sizeof(struct dungeon_renderer));
|
||||
|
||||
r->version = version;
|
||||
gfx_load_shader(&r->shader, "shaders/dungeon.v.glsl", "shaders/dungeon.f.glsl");
|
||||
r->local_transform = glGetUniformLocation(r->shader.program, "local_transform");
|
||||
r->proj_transform = glGetUniformLocation(r->shader.program, "proj_transform");
|
||||
r->alpha_mod = glGetUniformLocation(r->shader.program, "alpha_mod");
|
||||
r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv");
|
||||
r->use_lightmap = glGetUniformLocation(r->shader.program, "use_lightmap");
|
||||
r->light_texture = glGetUniformLocation(r->shader.program, "light_texture");
|
||||
r->use_fog = glGetUniformLocation(r->shader.program, "use_fog");
|
||||
r->uv_offset = glGetUniformLocation(r->shader.program, "uv_offset");
|
||||
r->uv_scale = glGetUniformLocation(r->shader.program, "uv_scale");
|
||||
glUseProgram(r->shader.program);
|
||||
glUniform1i(r->use_fog, version != DRAW_FIELD);
|
||||
glUniform2f(r->uv_offset, 0.0f, 0.0f);
|
||||
glUniform2f(r->uv_scale, 1.0f, 1.0f);
|
||||
glUseProgram(0);
|
||||
|
||||
r->wall_geometry = geometry_create(r, wall_vertices, sizeof(wall_vertices), GL_TRIANGLE_STRIP);
|
||||
r->door_left_geometry = geometry_create(r, door_left_vertices, sizeof(door_left_vertices), GL_TRIANGLE_STRIP);
|
||||
@@ -349,9 +435,8 @@ struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version versi
|
||||
r->stairs_geometry = geometry_create(r, stairs_vertices, sizeof(stairs_vertices), GL_TRIANGLE_STRIP);
|
||||
r->floating_marker_geometry = geometry_create(r, floating_marker_vertices, sizeof(floating_marker_vertices), GL_TRIANGLE_STRIP);
|
||||
|
||||
const int nr_types = DTX_DOOR + 1;
|
||||
r->materials = xcalloc(nr_types * dtx->nr_columns, sizeof(struct material));
|
||||
for (int type = 0; type < nr_types; type++) {
|
||||
r->materials = xcalloc(DTX_NR_CELL_TEXTURE_TYPES * dtx->nr_columns, sizeof(struct material));
|
||||
for (int type = 0; type < DTX_NR_CELL_TEXTURE_TYPES; type++) {
|
||||
for (int i = 0; i < dtx->nr_columns; i++) {
|
||||
struct cg *cg = dtx_create_cg(dtx, type, i);
|
||||
if (!cg)
|
||||
@@ -361,18 +446,26 @@ struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version versi
|
||||
}
|
||||
}
|
||||
r->nr_dtx_columns = dtx->nr_columns;
|
||||
r->nr_materials = nr_types * dtx->nr_columns;
|
||||
r->nr_materials = DTX_NR_CELL_TEXTURE_TYPES * dtx->nr_columns;
|
||||
|
||||
r->event_textures = event_textures;
|
||||
r->nr_event_textures = nr_event_textures;
|
||||
r->marker_table = marker_tables[version];
|
||||
r->draw_event_markers = true;
|
||||
|
||||
if (version == DRAW_DUNGEON_2 && is_dolls_boss_floor(num)) {
|
||||
// Use special marker table for Dungeons & Dolls boss floor
|
||||
r->marker_table = dolls_boss_marker_info;
|
||||
} else {
|
||||
r->marker_table = marker_tables[version];
|
||||
}
|
||||
|
||||
if (po)
|
||||
init_polyobjs(r, po);
|
||||
|
||||
r->skybox = skybox_create(dtx);
|
||||
r->skybox = skybox_create(version, dtx);
|
||||
|
||||
r->enable_lightmap = true;
|
||||
r->draw_obj_flags = (1 << NR_DRAW_OBJ_FLAGS) - 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -402,18 +495,32 @@ void dungeon_renderer_free(struct dungeon_renderer *r)
|
||||
free(r->polyobj_materials);
|
||||
|
||||
skybox_free(r->skybox);
|
||||
if (r->raster_shader.s.program)
|
||||
glDeleteProgram(r->raster_shader.s.program);
|
||||
|
||||
free(r);
|
||||
}
|
||||
|
||||
static void set_lightmap_texture(struct dungeon_renderer *r, int texture_index)
|
||||
{
|
||||
struct material *material = (r->enable_lightmap && texture_index >= 0)
|
||||
? get_material(r, DTX_LIGHTMAP, texture_index) : NULL;
|
||||
glUniform1i(r->use_lightmap, !!material);
|
||||
if (material) {
|
||||
glActiveTexture(GL_TEXTURE0 + LIGHT_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->texture);
|
||||
glUniform1i(r->light_texture, LIGHT_TEXTURE_UNIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw(struct dungeon_renderer *r, struct geometry *geometry, GLuint texture, mat4 transform)
|
||||
{
|
||||
glUseProgram(r->shader.program);
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, transform[0]);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glActiveTexture(GL_TEXTURE0 + COLOR_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glUniform1i(r->shader.texture, 0);
|
||||
glUniform1i(r->shader.texture, COLOR_TEXTURE_UNIT);
|
||||
|
||||
glBindVertexArray(geometry->vao);
|
||||
glDrawArrays(geometry->mode, 0, geometry->nr_vertices);
|
||||
@@ -431,13 +538,13 @@ static void draw_door(struct dungeon_renderer *r, GLuint texture, float angle, m
|
||||
glm_mat4_copy(transform, m);
|
||||
m[3][0] += dx;
|
||||
m[3][2] += dz;
|
||||
glm_rotate_y(m, angle * (M_PI/180), m);
|
||||
glm_rotate_y(m, glm_rad(angle), m);
|
||||
draw(r, r->door_left_geometry, texture, m);
|
||||
|
||||
glm_mat4_copy(transform, m);
|
||||
m[3][0] -= dx;
|
||||
m[3][2] -= dz;
|
||||
glm_rotate_y(m, angle * -(M_PI/180), m);
|
||||
glm_rotate_y(m, -glm_rad(angle), m);
|
||||
draw(r, r->door_right_geometry, texture, m);
|
||||
}
|
||||
|
||||
@@ -478,7 +585,7 @@ static void draw_floating_marker(struct dungeon_renderer *r, const struct marker
|
||||
glm_mat4_pick3t(view_mat, r);
|
||||
glm_mat4_ins3(r, m);
|
||||
} else {
|
||||
float angle = t / -(M_PI * 100.0);
|
||||
float angle = t / -(GLM_PIf * 100.f);
|
||||
glm_rotate_y(m, angle, m);
|
||||
}
|
||||
|
||||
@@ -521,7 +628,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
float y = 2.0 * cell->y;
|
||||
float z = -2.0 * cell->z;
|
||||
|
||||
if (cell->floor >= 0) {
|
||||
if (cell->floor >= 0 && r->draw_obj_flags & (1 << DRAW_FLOOR)) {
|
||||
struct material *material = get_material(r, DTX_FLOOR, cell->floor);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -529,21 +636,37 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 0, 1, y-1,
|
||||
0, -1, 0, z,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_floor);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->ceiling >= 0) {
|
||||
if (cell->ceiling >= 0 && r->draw_obj_flags & (1 << DRAW_CEILING)) {
|
||||
struct material *material = get_material(r, DTX_CEILING, cell->ceiling);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
-1, 0, 0, x,
|
||||
0, 0, -1, y+1,
|
||||
0, -1, 0, z,
|
||||
0, 0, 0, 1);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
if (r->version == DRAW_FIELD) {
|
||||
// Draw as a billboard.
|
||||
mat4 m;
|
||||
vec3 pos = {x, y, z};
|
||||
glm_translate_make(m, pos);
|
||||
mat3 r_bill;
|
||||
glm_mat4_pick3t(view_transform, r_bill);
|
||||
glm_mat4_ins3(r_bill, m);
|
||||
glm_scale_uni(m, 1.7f);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
} else {
|
||||
mat4 m = MAT4(
|
||||
-1, 0, 0, x,
|
||||
0, 0, -1, y+1,
|
||||
0, -1, 0, z,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_ceiling);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cell->north_wall >= 0) {
|
||||
if (cell->north_wall >= 0 && r->draw_obj_flags & (1 << DRAW_WALL)) {
|
||||
struct material *material = get_material(r, DTX_WALL, cell->north_wall);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -551,10 +674,12 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 1, 0, y,
|
||||
0, 0, 1, z-1,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_north);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->south_wall >= 0) {
|
||||
if (cell->south_wall >= 0 && r->draw_obj_flags & (1 << DRAW_WALL)) {
|
||||
struct material *material = get_material(r, DTX_WALL, cell->south_wall);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -562,10 +687,12 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 1, 0, y,
|
||||
0, 0, -1, z+1,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_south);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->east_wall >= 0) {
|
||||
if (cell->east_wall >= 0 && r->draw_obj_flags & (1 << DRAW_WALL)) {
|
||||
struct material *material = get_material(r, DTX_WALL, cell->east_wall);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -573,10 +700,12 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 1, 0, y,
|
||||
1, 0, 0, z,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_east);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->west_wall >= 0) {
|
||||
if (cell->west_wall >= 0 && r->draw_obj_flags & (1 << DRAW_WALL)) {
|
||||
struct material *material = get_material(r, DTX_WALL, cell->west_wall);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -584,10 +713,12 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 1, 0, y,
|
||||
-1, 0, 0, z,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_west);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->north_door >= 0) {
|
||||
if (cell->north_door >= 0 && r->draw_obj_flags & (1 << DRAW_DOOR)) {
|
||||
struct material *material = get_material(r, DTX_DOOR, cell->north_door);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -598,7 +729,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
draw_door(r, material->texture, cell->north_door_angle, m, -1, 0);
|
||||
}
|
||||
}
|
||||
if (cell->south_door >= 0) {
|
||||
if (cell->south_door >= 0 && r->draw_obj_flags & (1 << DRAW_DOOR)) {
|
||||
struct material *material = get_material(r, DTX_DOOR, cell->south_door);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -609,7 +740,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
draw_door(r, material->texture, cell->south_door_angle, m, 1, 0);
|
||||
}
|
||||
}
|
||||
if (cell->east_door >= 0) {
|
||||
if (cell->east_door >= 0 && r->draw_obj_flags & (1 << DRAW_DOOR)) {
|
||||
struct material *material = get_material(r, DTX_DOOR, cell->east_door);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -620,7 +751,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
draw_door(r, material->texture, cell->east_door_angle, m, 0, -1);
|
||||
}
|
||||
}
|
||||
if (cell->west_door >= 0) {
|
||||
if (cell->west_door >= 0 && r->draw_obj_flags & (1 << DRAW_DOOR)) {
|
||||
struct material *material = get_material(r, DTX_DOOR, cell->west_door);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -631,7 +762,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
draw_door(r, material->texture, cell->west_door_angle, m, 0, 1);
|
||||
}
|
||||
}
|
||||
if (cell->stairs_texture >= 0) {
|
||||
if (cell->stairs_texture >= 0 && r->draw_obj_flags & (1 << DRAW_STAIRS)) {
|
||||
struct material *material = get_material(r, DTX_STAIRS, cell->stairs_texture);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m;
|
||||
@@ -659,7 +790,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
glCullFace(GL_BACK);
|
||||
}
|
||||
}
|
||||
if (cell->polyobj_index >= 0) {
|
||||
if (cell->polyobj_index >= 0 && r->draw_obj_flags & (1 << DRAW_POLYOBJ)) {
|
||||
vec3 pos = {
|
||||
x + cell->polyobj_position_x,
|
||||
y + cell->polyobj_position_y - 1,
|
||||
@@ -677,12 +808,81 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, mat4 view_transform, mat4 proj_transform)
|
||||
static void draw_character(struct dungeon_renderer *r, struct drawfield_character *chara, mat4 view_mat)
|
||||
{
|
||||
if (!chara->show || chara->sprite <= 0)
|
||||
return;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(chara->sprite);
|
||||
if (!sp || !sp->texture.handle)
|
||||
return;
|
||||
|
||||
if (chara->cols == 0) chara->cols = 1;
|
||||
if (chara->rows == 0) chara->rows = 1;
|
||||
glUseProgram(r->shader.program);
|
||||
glUniform2f(r->uv_offset, (float)(chara->cg_index % chara->cols) / chara->cols, (float)(chara->cg_index / chara->cols) / chara->rows);
|
||||
glUniform2f(r->uv_scale, 1.0f / chara->cols, 1.0f / chara->rows);
|
||||
|
||||
mat4 m;
|
||||
glm_translate_make(m, chara->pos);
|
||||
|
||||
// Make it a billboard of 2.0 x 2.0
|
||||
mat3 r_bill;
|
||||
glm_mat4_pick3t(view_mat, r_bill);
|
||||
glm_mat4_ins3(r_bill, m);
|
||||
|
||||
draw(r, r->wall_geometry, sp->texture.handle, m);
|
||||
}
|
||||
|
||||
struct character_z {
|
||||
struct drawfield_character *chara;
|
||||
float z;
|
||||
};
|
||||
|
||||
static int compare_character_z(const void *a, const void *b)
|
||||
{
|
||||
const struct character_z *cha = a;
|
||||
const struct character_z *chb = b;
|
||||
if (cha->z < chb->z)
|
||||
return -1;
|
||||
if (cha->z > chb->z)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void draw_characters(struct dungeon_renderer *r, struct drawfield_character *characters, mat4 view_transform)
|
||||
{
|
||||
if (!characters)
|
||||
return;
|
||||
|
||||
struct character_z sorted_characters[DRAWFIELD_NR_CHARACTERS];
|
||||
int n = 0;
|
||||
for (int i = 0; i < DRAWFIELD_NR_CHARACTERS; i++) {
|
||||
struct drawfield_character *chara = characters + i;
|
||||
if (!chara->show || chara->sprite <= 0)
|
||||
continue;
|
||||
sorted_characters[n].chara = chara;
|
||||
vec4 pos;
|
||||
glm_mat4_mulv(view_transform, (vec4){chara->pos[0], chara->pos[1], chara->pos[2], 1}, pos);
|
||||
sorted_characters[n].z = pos[2];
|
||||
n++;
|
||||
}
|
||||
|
||||
qsort(sorted_characters, n, sizeof(struct character_z), compare_character_z);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
draw_character(r, sorted_characters[i].chara, view_transform);
|
||||
|
||||
glUniform2f(r->uv_offset, 0.0f, 0.0f);
|
||||
glUniform2f(r->uv_scale, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, struct drawfield_character *characters, mat4 view_transform, mat4 proj_transform)
|
||||
{
|
||||
glUseProgram(r->shader.program);
|
||||
glUniformMatrix4fv(r->shader.view_transform, 1, GL_FALSE, view_transform[0]);
|
||||
glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, proj_transform[0]);
|
||||
glUniform1f(r->alpha_mod, 1.0);
|
||||
glUniform1i(r->use_lightmap, 0);
|
||||
|
||||
// Render opaque objects, from near to far.
|
||||
glDisable(GL_BLEND);
|
||||
@@ -691,11 +891,15 @@ void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells
|
||||
|
||||
// Render the skybox.
|
||||
skybox_render(r->skybox, view_transform, proj_transform);
|
||||
|
||||
glUseProgram(r->shader.program);
|
||||
// Render transparent objects, from far to near.
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// DrawField: draw characters.
|
||||
draw_characters(r, characters, view_transform);
|
||||
|
||||
// Render transparent objects, from far to near.
|
||||
for (int i = nr_cells - 1; i >= 0; i--)
|
||||
draw_cell(r, cells[i], false, view_transform);
|
||||
|
||||
@@ -719,3 +923,85 @@ bool dungeon_renderer_is_floor_opaque(struct dungeon_renderer *r, struct dgn_cel
|
||||
struct material *material = get_material(r, DTX_FLOOR, cell->floor);
|
||||
return material && material->opaque;
|
||||
}
|
||||
|
||||
static void prepare_raster_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
struct dungeon_renderer *r = data;
|
||||
float width = job->world_transform[0];
|
||||
glUniform1f(r->raster_shader.amp, r->raster_amp / width);
|
||||
glUniform1f(r->raster_shader.t, SDL_GetTicks() / 1000.f);
|
||||
}
|
||||
|
||||
static void load_raster_shader(struct raster_shader *s)
|
||||
{
|
||||
gfx_load_shader(&s->s, "shaders/render.v.glsl", "shaders/dungeon_raster.f.glsl");
|
||||
s->amp = glGetUniformLocation(s->s.program, "amp");
|
||||
s->t = glGetUniformLocation(s->s.program, "t");
|
||||
s->s.prepare = prepare_raster_shader;
|
||||
}
|
||||
|
||||
void dungeon_renderer_run_post_processing(struct dungeon_renderer *r, struct texture *src, struct texture *dst)
|
||||
{
|
||||
// This function flips the image upside down, because the image in `src`
|
||||
// (rendered by dungeon_renderer_render) is bottom-up (the first pixel is at
|
||||
// the bottom-left), but sprite textures are top-down (the first pixel is at
|
||||
// the top-left).
|
||||
|
||||
if (r->raster_scroll) {
|
||||
mat4 mw_transform = MAT4(
|
||||
src->w, 0, 0, 0,
|
||||
0, -src->h, 0, src->h, // flip vertically
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(src->w, src->h);
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = &r->raster_shader.s,
|
||||
.shape = GFX_RECTANGLE,
|
||||
.texture = src->handle,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = r
|
||||
};
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0, dst->w, dst->h);
|
||||
gfx_render(&job);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
} else {
|
||||
gfx_copy_reverse_UD(dst, 0, 0, src, 0, 0, src->w, src->h);
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_renderer_set_raster_scroll(struct dungeon_renderer *r, int type)
|
||||
{
|
||||
r->raster_scroll = !!type;
|
||||
if (r->raster_scroll && !r->raster_shader.s.program) {
|
||||
load_raster_shader(&r->raster_shader);
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_renderer_set_raster_amp(struct dungeon_renderer *r, float amp)
|
||||
{
|
||||
r->raster_amp = amp;
|
||||
}
|
||||
|
||||
void dungeon_renderer_enable_lightmap(struct dungeon_renderer *r, bool enable)
|
||||
{
|
||||
r->enable_lightmap = enable;
|
||||
}
|
||||
|
||||
bool dungeon_renderer_get_draw_obj_flag(struct dungeon_renderer *r, int type)
|
||||
{
|
||||
if (type < 0 || type >= NR_DRAW_OBJ_FLAGS)
|
||||
return false;
|
||||
return r->draw_obj_flags & (1 << type);
|
||||
}
|
||||
|
||||
void dungeon_renderer_set_draw_obj_flag(struct dungeon_renderer *r, int type, bool flag)
|
||||
{
|
||||
if (type < 0 || type >= NR_DRAW_OBJ_FLAGS)
|
||||
return;
|
||||
if (flag)
|
||||
r->draw_obj_flags |= (1 << type);
|
||||
else
|
||||
r->draw_obj_flags &= ~(1 << type);
|
||||
}
|
||||
|
||||
+13
-3
@@ -20,6 +20,7 @@
|
||||
#include "system4/cg.h"
|
||||
|
||||
#include "dungeon/dtx.h"
|
||||
#include "dungeon/dungeon.h"
|
||||
#include "dungeon/skybox.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
@@ -33,8 +34,13 @@ struct skybox {
|
||||
int nr_indices;
|
||||
};
|
||||
|
||||
struct skybox *skybox_create(struct dtx *dtx)
|
||||
struct skybox *skybox_create(enum draw_dungeon_version version, struct dtx *dtx)
|
||||
{
|
||||
if (version == DRAW_DUNGEON_2 || version == DRAW_FIELD) {
|
||||
// Skybox is not used in these games.
|
||||
return NULL;
|
||||
};
|
||||
|
||||
struct skybox *s = xcalloc(1, sizeof(struct skybox));
|
||||
gfx_load_shader(&s->shader, "shaders/dungeon_skybox.v.glsl", "shaders/dungeon_skybox.f.glsl");
|
||||
s->proj_transform = glGetUniformLocation(s->shader.program, "proj_transform");
|
||||
@@ -63,8 +69,8 @@ struct skybox *skybox_create(struct dtx *dtx)
|
||||
glGenBuffers(1, &s->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, s->attr_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(s->shader.vertex);
|
||||
glVertexAttribPointer(s->shader.vertex, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glEnableVertexAttribArray(s->shader.vertex_pos);
|
||||
glVertexAttribPointer(s->shader.vertex_pos, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glGenBuffers(1, &s->index_buffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
||||
@@ -103,6 +109,8 @@ struct skybox *skybox_create(struct dtx *dtx)
|
||||
|
||||
void skybox_free(struct skybox *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
glDeleteVertexArrays(1, &s->vao);
|
||||
glDeleteBuffers(1, &s->attr_buffer);
|
||||
glDeleteBuffers(1, &s->index_buffer);
|
||||
@@ -113,6 +121,8 @@ void skybox_free(struct skybox *s)
|
||||
|
||||
void skybox_render(struct skybox *s, const mat4 view_transform, const mat4 proj_transform)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glUseProgram(s->shader.program);
|
||||
|
||||
|
||||
+90
-79
@@ -19,7 +19,6 @@
|
||||
#include <SDL.h>
|
||||
#include "effect.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "sact.h"
|
||||
#include "system4.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
@@ -76,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_CROSSFADE] = "EFFECT_VWAVE_CROSSFADE",
|
||||
};
|
||||
|
||||
struct effect_shader {
|
||||
@@ -92,6 +92,13 @@ struct effect_data {
|
||||
float progress;
|
||||
};
|
||||
|
||||
static struct {
|
||||
bool on;
|
||||
int type;
|
||||
Texture old;
|
||||
Texture view;
|
||||
} effect = {0};
|
||||
|
||||
static void prepare_effect_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
struct effect_shader *s = (struct effect_shader*)job->shader;
|
||||
@@ -115,18 +122,23 @@ static void load_effect_shader(struct effect_shader *s)
|
||||
s->s.prepare = prepare_effect_shader;
|
||||
}
|
||||
|
||||
static void render_effect_shader(struct effect_shader *shader, Texture *old, Texture *new, float progress)
|
||||
static void render_effect_shader(struct effect_shader *shader, Texture *dst, Texture *old, Texture *new, float progress)
|
||||
{
|
||||
struct effect_data data = { .old = old, .new = new, .progress = progress };
|
||||
mat4 world_transform = WORLD_TRANSFORM(new->w, new->h, 0, 0);
|
||||
mat4 wv_transform = WV_TRANSFORM(config.view_width, config.view_height);
|
||||
struct gfx_render_job job = {
|
||||
.shader = &shader->s,
|
||||
.shape = GFX_RECTANGLE,
|
||||
.texture = new->handle,
|
||||
.world_transform = new->world_transform[0],
|
||||
.world_transform = world_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = &data
|
||||
};
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0,
|
||||
dst->w, dst->h);
|
||||
gfx_render(&job);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
#define EFFECT_SHADER(path) { .s = { .prepare = prepare_effect_shader }, .f_path = path}
|
||||
@@ -143,8 +155,7 @@ 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");
|
||||
|
||||
extern GLuint main_surface_fb;
|
||||
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,
|
||||
@@ -160,70 +171,63 @@ 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_CROSSFADE] = &vwave_crossfade_shader,
|
||||
};
|
||||
|
||||
static struct {
|
||||
bool on;
|
||||
int type;
|
||||
Texture old;
|
||||
Texture new;
|
||||
} effect = {0};
|
||||
|
||||
static void effect_fadeout(float rate)
|
||||
static void effect_fadeout(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
gfx_copy_bright(gfx_main_surface(), 0, 0, &effect.old, 0, 0,
|
||||
effect.old.w, effect.old.h, (1.0f - rate) * 255);
|
||||
gfx_copy_bright(dst, 0, 0, old, 0, 0, old->w, old->h, (1.0f - rate) * 255);
|
||||
}
|
||||
|
||||
static void effect_fadein(float rate)
|
||||
static void effect_fadein(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
gfx_copy_bright(gfx_main_surface(), 0, 0, &effect.new, 0, 0,
|
||||
effect.new.w, effect.new.h, rate * 255);
|
||||
gfx_copy_bright(dst, 0, 0, new, 0, 0, new->w, new->h, rate * 255);
|
||||
}
|
||||
|
||||
static void effect_whiteout(float rate)
|
||||
static void effect_whiteout(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 255, 255, 255);
|
||||
gfx_blend(dst, 0, 0, &effect.old, 0, 0, dst->w, dst->h, (1.0f - rate) * 255);
|
||||
gfx_blend(dst, 0, 0, old, 0, 0, dst->w, dst->h, (1.0f - rate) * 255);
|
||||
}
|
||||
|
||||
static void effect_whitein(float rate)
|
||||
static void effect_whitein(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 255, 255, 255);
|
||||
gfx_blend(dst, 0, 0, &effect.new, 0, 0, dst->w, dst->h, rate * 255);
|
||||
gfx_blend(dst, 0, 0, new, 0, 0, dst->w, dst->h, rate * 255);
|
||||
}
|
||||
|
||||
static void effect_zoom_lr(float rate)
|
||||
static void effect_rotate_out(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 0, 0, 0);
|
||||
gfx_copy_rot_zoom(dst, old, 0, 0, old->w, old->h, rate * 360, 1.f - rate);
|
||||
}
|
||||
|
||||
static void effect_zoom_lr(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
unsigned x_pos = roundf(dst->w * rate);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, &effect.old, 0, 0, effect.old.w, effect.old.h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, &effect.new, 0, 0, effect.new.w, effect.new.h);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, old, 0, 0, old->w, old->h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, new, 0, 0, new->w, new->h);
|
||||
}
|
||||
|
||||
static void effect_zoom_rl(float rate)
|
||||
static void effect_zoom_rl(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
unsigned x_pos = dst->w - roundf(dst->w * rate);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, &effect.new, 0, 0, effect.new.w, effect.new.h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, &effect.old, 0, 0, effect.old.w, effect.old.h);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, new, 0, 0, new->w, new->h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, old, 0, 0, old->w, old->h);
|
||||
}
|
||||
|
||||
static void effect_oscillate(float rate)
|
||||
static void effect_oscillate(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
int delta_x = (rand() % (dst->w / 10) - (dst->w / 20)) * (1.0f - rate);
|
||||
int delta_y = (rand() % (dst->h / 10) - (dst->h / 20)) * (1.0f - rate);;
|
||||
|
||||
gfx_copy(dst, 0, 0, &effect.old, 0, 0, dst->w, dst->h);
|
||||
gfx_copy(dst, delta_x, delta_y, &effect.new, 0, 0, dst->w, dst->h);
|
||||
gfx_copy(dst, 0, 0, old, 0, 0, dst->w, dst->h);
|
||||
gfx_copy(dst, delta_x, delta_y, new, 0, 0, dst->w, dst->h);
|
||||
}
|
||||
|
||||
static void effect_tv_switch(float rate, Texture *src)
|
||||
static void effect_tv_switch(Texture *dst, Texture *src, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 0, 0, 0);
|
||||
if (rate < 0.5f) {
|
||||
int h = max(1, (int)(dst->h * (1.0f - rate * 2.0f)));
|
||||
gfx_copy_stretch(dst, 0, dst->h * rate, dst->w, h,
|
||||
@@ -236,19 +240,18 @@ static void effect_tv_switch(float rate, Texture *src)
|
||||
}
|
||||
}
|
||||
|
||||
static void effect_tv_switch_off(float rate)
|
||||
static void effect_tv_switch_off(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
effect_tv_switch(rate, &effect.old);
|
||||
effect_tv_switch(dst, old, rate);
|
||||
}
|
||||
|
||||
static void effect_tv_switch_on(float rate)
|
||||
static void effect_tv_switch_on(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
effect_tv_switch(1.0f - rate, &effect.new);
|
||||
effect_tv_switch(dst, new, 1.0f - rate);
|
||||
}
|
||||
|
||||
static void effect_zoom_in_crossfade(float rate)
|
||||
static void effect_zoom_in_crossfade(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
float scale = 1.0f + powf(rate, 4.0f) * 16.0f;
|
||||
int w = roundf(dst->w * scale);
|
||||
int h = roundf(dst->h * scale);
|
||||
@@ -256,16 +259,17 @@ static void effect_zoom_in_crossfade(float rate)
|
||||
int y = -((h - dst->h)/2);
|
||||
int a = (1.0f - rate) * 255;
|
||||
|
||||
gfx_copy(dst, 0, 0, &effect.new, 0, 0, dst->w, dst->h);
|
||||
gfx_copy_stretch_blend(dst, x, y, w, h, &effect.old, 0, 0, dst->w, dst->h, a);
|
||||
gfx_copy(dst, 0, 0, new, 0, 0, dst->w, dst->h);
|
||||
gfx_copy_stretch_blend(dst, x, y, w, h, old, 0, 0, dst->w, dst->h, a);
|
||||
}
|
||||
|
||||
typedef void (*effect_fun)(float rate);
|
||||
typedef void (*effect_fun)(Texture *dst, Texture *old, Texture *new, float rate);
|
||||
static effect_fun effect_functions[NR_EFFECTS] = {
|
||||
[EFFECT_FADEOUT] = effect_fadeout,
|
||||
[EFFECT_FADEIN] = effect_fadein,
|
||||
[EFFECT_WHITEOUT] = effect_whiteout,
|
||||
[EFFECT_WHITEIN] = effect_whitein,
|
||||
[EFFECT_ROTATE_OUT] = effect_rotate_out,
|
||||
[EFFECT_OSCILLATE] = effect_oscillate,
|
||||
[EFFECT_TV_SWITCH_OFF] = effect_tv_switch_off,
|
||||
[EFFECT_TV_SWITCH_ON] = effect_tv_switch_on,
|
||||
@@ -274,7 +278,26 @@ static effect_fun effect_functions[NR_EFFECTS] = {
|
||||
[EFFECT_ZOOM_IN_CROSSFADE] = effect_zoom_in_crossfade,
|
||||
};
|
||||
|
||||
int sact_TRANS_Begin(int type)
|
||||
void effect_update_texture(int type, Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
if (effect_functions[type]) {
|
||||
effect_functions[type](dst, old, new, rate);
|
||||
} else {
|
||||
struct effect_shader *s = effect_shaders[type];
|
||||
if (!s) {
|
||||
if (effect_names[type])
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
else
|
||||
WARNING("Unimplemented effect: %d", type);
|
||||
s = effect_shaders[EFFECT_BLUR_CROSSFADE];
|
||||
}
|
||||
if (!s->s.program)
|
||||
load_effect_shader(s);
|
||||
render_effect_shader(s, dst, old, new, rate);
|
||||
}
|
||||
}
|
||||
|
||||
int effect_init(enum effect type)
|
||||
{
|
||||
if (type <= 0 || type >= NR_EFFECTS) {
|
||||
WARNING("Invalid or unknown effect: %d", type);
|
||||
@@ -287,7 +310,10 @@ int sact_TRANS_Begin(int type)
|
||||
else {
|
||||
struct effect_shader *s = effect_shaders[type];
|
||||
if (!s) {
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
if (effect_names[type])
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
else
|
||||
WARNING("Unimplemented effect: %d", type);
|
||||
type = EFFECT_BLUR_CROSSFADE;
|
||||
s = effect_shaders[type];
|
||||
}
|
||||
@@ -299,53 +325,38 @@ int sact_TRANS_Begin(int type)
|
||||
|
||||
effect.on = true;
|
||||
effect.type = type;
|
||||
gfx_delete_texture(&effect.old);
|
||||
gfx_delete_texture(&effect.view);
|
||||
gfx_copy_main_surface(&effect.old);
|
||||
scene_render();
|
||||
gfx_copy_main_surface(&effect.new);
|
||||
gfx_copy_main_surface(&effect.view);
|
||||
gfx_set_view(&effect.view);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_TRANS_Update(float rate)
|
||||
int effect_update(float rate)
|
||||
{
|
||||
if (!effect.on) {
|
||||
return 0;
|
||||
}
|
||||
gfx_clear();
|
||||
|
||||
if (effect_functions[effect.type]) {
|
||||
effect_functions[effect.type](rate);
|
||||
effect_functions[effect.type](&effect.view, &effect.old, gfx_main_surface(), rate);
|
||||
} else {
|
||||
render_effect_shader(effect_shaders[effect.type], &effect.old, &effect.new, rate);
|
||||
Texture new;
|
||||
gfx_copy_main_surface(&new);
|
||||
render_effect_shader(effect_shaders[effect.type], &effect.view, &effect.old, &new, rate);
|
||||
gfx_delete_texture(&new);
|
||||
}
|
||||
|
||||
gfx_swap();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_TRANS_End(void)
|
||||
int effect_fini(void)
|
||||
{
|
||||
effect.on = false;
|
||||
gfx_reset_view();
|
||||
gfx_delete_texture(&effect.old);
|
||||
gfx_delete_texture(&effect.new);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_Effect(int type, int time, possibly_unused int key)
|
||||
{
|
||||
uint32_t start = SDL_GetTicks();
|
||||
if (!sact_TRANS_Begin(type))
|
||||
return 0;
|
||||
|
||||
uint32_t t = SDL_GetTicks() - start;
|
||||
while (t < time) {
|
||||
sact_TRANS_Update((float)t / (float)time);
|
||||
uint32_t t2 = SDL_GetTicks() - start;
|
||||
if (t2 < t + 16) {
|
||||
SDL_Delay(t + 16 - t2);
|
||||
t += 16;
|
||||
} else {
|
||||
t = t2;
|
||||
}
|
||||
}
|
||||
|
||||
sact_TRANS_End();
|
||||
gfx_delete_texture(&effect.view);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -55,59 +55,148 @@ bool library_function_exists(int libno, int fno)
|
||||
#ifdef TRACE_HLL
|
||||
#include "system4/string.h"
|
||||
|
||||
static void trace_hll_call(struct ain_library *lib, struct ain_hll_function *f,
|
||||
struct traced_library {
|
||||
const char *name;
|
||||
bool (*should_trace)(struct ain_hll_function *f, union vm_value **args);
|
||||
};
|
||||
|
||||
static bool chipmunk_should_trace(struct ain_hll_function *f, union vm_value **args)
|
||||
{
|
||||
const char *no_trace[] = {
|
||||
"SYSTEM_SetReadMessageSkipping",
|
||||
"Update",
|
||||
"KeepPreviousView",
|
||||
"Sleep",
|
||||
};
|
||||
for (unsigned i = 0; i < sizeof(no_trace) / sizeof(*no_trace); i++) {
|
||||
if (!strcmp(f->name, no_trace[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#include "parts.h"
|
||||
#include "parts/parts_internal.h"
|
||||
static bool gui_engine_should_trace(struct ain_hll_function *f, union vm_value **args)
|
||||
{
|
||||
const char *no_trace[] = {
|
||||
"Parts_UpdateMotionTime",
|
||||
"UpdateInputState",
|
||||
"UpdateComponent",
|
||||
"UpdateParts",
|
||||
"GetMessageType",
|
||||
"ReleaseMessage",
|
||||
"Parts_SetPartsPixelDecide",
|
||||
"Parts_GetPartsShow",
|
||||
"Parts_IsCursorIn",
|
||||
"Parts_GetPartsAlpha",
|
||||
};
|
||||
for (unsigned i = 0; i < sizeof(no_trace) / sizeof(*no_trace); i++) {
|
||||
if (!strcmp(f->name, no_trace[i]))
|
||||
return false;
|
||||
}
|
||||
if (!strcmp(f->name, "Parts_SetPartsCG")) {
|
||||
static char *u = NULL;
|
||||
if (!u)
|
||||
u = utf2sjis("システム/ボタン/メニュー/通常", 0);
|
||||
struct string ***strs = (void*)args;
|
||||
struct string *s = *strs[1];
|
||||
return strcmp(s->text, u);
|
||||
}
|
||||
if (!strcmp(f->name, "Parts_SetPos")) {
|
||||
if (args[1]->i < 0 || args[2]->i < 0)
|
||||
return false;
|
||||
return PE_GetPartsX(args[0]->i) != args[1]->i || PE_GetPartsY(args[0]->i) != args[2]->i;
|
||||
}
|
||||
if (!strcmp(f->name, "Parts_SetZ"))
|
||||
return PE_GetPartsZ(args[0]->i) != args[1]->i;
|
||||
if (!strcmp(f->name, "Parts_SetShow"))
|
||||
return PE_GetPartsShow(args[0]->i) != args[1]->i;
|
||||
if (!strcmp(f->name, "Parts_SetAlpha"))
|
||||
return PE_GetPartsAlpha(args[0]->i) != args[1]->i;
|
||||
if (!strcmp(f->name, "Parts_SetPartsCGSurfaceArea")) {
|
||||
return false;
|
||||
struct parts *p = parts_try_get(args[0]->i);
|
||||
if (!p)
|
||||
return true;
|
||||
Rectangle *r = &p->states[args[5]->i].common.surface_area;
|
||||
return args[1]->i != r->x || args[2]->i != r->y
|
||||
|| args[3]->i != r->w || args[4]->i != r->h;
|
||||
}
|
||||
if (!strcmp(f->name, "Parts_SetAddColor")) {
|
||||
struct parts *p = parts_try_get(args[0]->i);
|
||||
if (!p)
|
||||
return true;
|
||||
SDL_Color *c = &p->local.add_color;
|
||||
return args[1]->i != c->r || args[2]->i != c->g || args[3]->i != c->b;
|
||||
}
|
||||
if (!strcmp(f->name, "Parts_GetPartsX"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parts_engine_should_trace(struct ain_hll_function *f, union vm_value **args)
|
||||
{
|
||||
if (!strcmp(f->name, "Update"))
|
||||
return false;
|
||||
if (!strcmp(f->name, "SetAddColor"))
|
||||
return false;
|
||||
if (!strcmp(f->name, "SetAlpha"))
|
||||
return PE_GetPartsAlpha(args[0]->i) != args[1]->i;
|
||||
if (!strcmp(f->name, "SetPartsCGSurfaceArea"))
|
||||
return args[1]->i >= 0 && args[2]->i >= 0;
|
||||
if (!strcmp(f->name, "SetPartsConstructionSurfaceArea"))
|
||||
return args[1]->i >= 0 && args[2]->i >= 0;
|
||||
if (!strcmp(f->name, "SetPartsMagX"))
|
||||
return args[1]->f > 1.001 || args[1]->f < -1.001;
|
||||
if (!strcmp(f->name, "SetPartsMagY"))
|
||||
return args[1]->f > 1.001 || args[1]->f < -1.001;
|
||||
if (!strcmp(f->name, "SetPartsOriginPosMode"))
|
||||
return PE_GetPartsOriginPosMode(args[0]->i) != args[1]->i;
|
||||
if (!strcmp(f->name, "SetPartsRotateZ"))
|
||||
return args[1]->f > 0.001 || args[1]->f < -0.001;
|
||||
if (!strcmp(f->name, "SetPos"))
|
||||
return PE_GetPartsX(args[0]->i) != args[1]->i || PE_GetPartsX(args[0]->i) != args[2]->i;
|
||||
if (!strcmp(f->name, "SetShow")) {
|
||||
if (args[0]->i == 0)
|
||||
return false; // ???
|
||||
return PE_GetPartsShow(args[0]->i) != args[1]->i;
|
||||
}
|
||||
if (!strcmp(f->name, "SetZ"))
|
||||
return PE_GetPartsZ(args[0]->i) != args[1]->i;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sact_should_trace(struct ain_hll_function *f, union vm_value **args)
|
||||
{
|
||||
const char *no_trace[] = {
|
||||
"Key_IsDown",
|
||||
"Mouse_ClearWheel",
|
||||
"Mouse_GetPos",
|
||||
"Mouse_GetWheel",
|
||||
"SP_GetWidth",
|
||||
"SP_GetHeight",
|
||||
"SP_ExistAlpha",
|
||||
"Update",
|
||||
};
|
||||
for (unsigned i = 0; i < sizeof(no_trace) / sizeof(*no_trace); i++) {
|
||||
if (!strcmp(f->name, no_trace[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct traced_library traced_libraries[] = {
|
||||
{ "ChipmunkSpriteEngine", chipmunk_should_trace },
|
||||
{ "GUIEngine", gui_engine_should_trace },
|
||||
{ "PartsEngine", parts_engine_should_trace },
|
||||
{ "SACTDX", sact_should_trace },
|
||||
};
|
||||
|
||||
static void print_hll_trace(struct ain_library *lib, struct ain_hll_function *f,
|
||||
struct hll_function *fun, union vm_value *r, void *_args)
|
||||
{
|
||||
/*
|
||||
// list of traced libraries
|
||||
if (!strcmp(lib->name, "StoatSpriteEngine") || !strcmp(lib->name, "ChipmunkSpriteEngine")) {
|
||||
// list of excluded functions
|
||||
if (!strcmp(f->name, "CharSprite_GetAlphaRate")) goto notrace;
|
||||
if (!strcmp(f->name, "CharSprite_SetAlphaRate")) goto notrace;
|
||||
if (!strcmp(f->name, "KEY_GetState")) goto notrace;
|
||||
if (!strcmp(f->name, "Mouse_ClearWheel")) goto notrace;
|
||||
if (!strcmp(f->name, "Mouse_GetPos")) goto notrace;
|
||||
if (!strcmp(f->name, "Mouse_GetWheel")) goto notrace;
|
||||
if (!strcmp(f->name, "MultiSprite_UpdateView")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_ExistAlpha")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_GetHeight")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_GetWidth")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_IsPtIn")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_IsPtInRect")) goto notrace;
|
||||
if (!strcmp(f->name, "SYSTEM_SetReadMessageSkipping")) goto notrace;
|
||||
if (!strcmp(f->name, "TRANS_Update")) goto notrace;
|
||||
if (!strcmp(f->name, "Update")) goto notrace;
|
||||
else goto notrace;
|
||||
}
|
||||
else if (!strcmp(lib->name, "VSFile"));
|
||||
else if (!strcmp(lib->name, "GoatGUIEngine")) {
|
||||
if (!strcmp(f->name, "Update")) goto notrace;
|
||||
if (!strcmp(f->name, "IsMotion")) goto notrace;
|
||||
if (!strcmp(f->name, "SetMotionTime")) goto notrace;
|
||||
if (!strcmp(f->name, "GetClickPartsNumber")) goto notrace;
|
||||
}
|
||||
//else if (!strcmp(lib->name, "DrawGraph"));
|
||||
else if (!strcmp(lib->name, "SACT2")) {
|
||||
if (strncmp(f->name, "SP_", 3)) goto notrace;
|
||||
if (!strcmp(f->name, "Key_IsDown")) goto notrace;
|
||||
if (!strcmp(f->name, "Mouse_GetPos")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_ExistAlpha")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_IsPtInRect")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_IsPtIn")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_IsUsing")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_SetShow")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_SetPos")) goto notrace;
|
||||
if (!strcmp(f->name, "Timer_Get")) goto notrace;
|
||||
if (!strcmp(f->name, "Update")) goto notrace;
|
||||
}
|
||||
else*/ if (!strcmp(lib->name, "SengokuRanceFont")) {
|
||||
if (!strcmp(f->name, "SP_SetReduceDescender")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_ClearState")) goto notrace;
|
||||
}
|
||||
else goto notrace;
|
||||
|
||||
sys_message("(%s) ", display_sjis0(ain->functions[call_stack[call_stack_ptr-1].fno].name));
|
||||
|
||||
sys_message("%s.%s(", lib->name, f->name);
|
||||
union vm_value **args = _args;
|
||||
for (int i = 0; i < f->nr_arguments; i++) {
|
||||
@@ -183,8 +272,22 @@ static void trace_hll_call(struct ain_library *lib, struct ain_hll_function *f,
|
||||
}
|
||||
}
|
||||
sys_message("\n");
|
||||
return;
|
||||
notrace:
|
||||
}
|
||||
|
||||
static void trace_hll_call(struct ain_library *lib, struct ain_hll_function *f,
|
||||
struct hll_function *fun, union vm_value *r, void *_args)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof(traced_libraries)/sizeof(*traced_libraries); i++) {
|
||||
struct traced_library *l = &traced_libraries[i];
|
||||
if (!strcmp(lib->name, l->name)) {
|
||||
if (!l->should_trace || l->should_trace(f, _args)) {
|
||||
print_hll_trace(lib, f, fun, r, _args);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ffi_call(&fun->cif, (void*)fun->fun, r, _args);
|
||||
}
|
||||
#endif /* TRACE_HLL */
|
||||
@@ -200,13 +303,13 @@ void hll_call(int libno, int fno)
|
||||
if (!fun->fun)
|
||||
VM_ERROR("Unimplemented HLL function: %s.%s", ain->libraries[libno].name, f->name);
|
||||
|
||||
// XXX: Try to prevent the heap from being reallocated mid-call.
|
||||
// This only works to the extent that HLL functions can guarantee
|
||||
// no more than 64 heap allocations occur within the call...
|
||||
heap_guarantee(64);
|
||||
|
||||
void *args[HLL_MAX_ARGS];
|
||||
void *ptrs[HLL_MAX_ARGS];
|
||||
// Copy reference arguments to the stack to protect against heap
|
||||
// reallocation during HLL calls.
|
||||
void *heap_ptrs[HLL_MAX_ARGS];
|
||||
int heap_slots[HLL_MAX_ARGS];
|
||||
|
||||
for (int i = f->nr_arguments - 1; i >= 0; i--) {
|
||||
switch (f->arguments[i].type.data) {
|
||||
case AIN_REF_INT:
|
||||
@@ -227,7 +330,9 @@ void hll_call(int libno, int fno)
|
||||
break;
|
||||
case AIN_REF_STRING:
|
||||
stack_ptr--;
|
||||
ptrs[i] = &heap[stack[stack_ptr].i].s;
|
||||
heap_slots[i] = stack[stack_ptr].i;
|
||||
heap_ptrs[i] = heap[stack[stack_ptr].i].s;
|
||||
ptrs[i] = &heap_ptrs[i];
|
||||
args[i] = &ptrs[i];
|
||||
break;
|
||||
case AIN_STRUCT:
|
||||
@@ -238,7 +343,9 @@ void hll_call(int libno, int fno)
|
||||
case AIN_REF_STRUCT:
|
||||
case AIN_REF_ARRAY_TYPE:
|
||||
stack_ptr--;
|
||||
ptrs[i] = &heap[stack[stack_ptr].i].page;
|
||||
heap_slots[i] = stack[stack_ptr].i;
|
||||
heap_ptrs[i] = heap[stack[stack_ptr].i].page;
|
||||
ptrs[i] = &heap_ptrs[i];
|
||||
args[i] = &ptrs[i];
|
||||
break;
|
||||
default:
|
||||
@@ -267,28 +374,48 @@ void hll_call(int libno, int fno)
|
||||
j++;
|
||||
break;
|
||||
case AIN_REF_STRING:
|
||||
case AIN_REF_STRUCT:
|
||||
case AIN_REF_FUNC_TYPE:
|
||||
case AIN_REF_ARRAY_TYPE:
|
||||
heap[heap_slots[i]].s = heap_ptrs[i];
|
||||
break;
|
||||
case AIN_REF_STRUCT:
|
||||
case AIN_REF_ARRAY_TYPE:
|
||||
heap[heap_slots[i]].page = heap_ptrs[i];
|
||||
break;
|
||||
case AIN_REF_FUNC_TYPE:
|
||||
break;
|
||||
case AIN_ARRAY_TYPE:
|
||||
// Sys41VM doesn't make a copy when passing an array by value.
|
||||
if (ain->version <= 1)
|
||||
break;
|
||||
// fallthrough
|
||||
default:
|
||||
variable_fini(stack[stack_ptr+j], f->arguments[i].type.data);
|
||||
variable_fini(stack[stack_ptr+j], f->arguments[i].type.data, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (f->return_type.data != AIN_VOID) {
|
||||
if (f->return_type.data == AIN_STRING) {
|
||||
int slot = heap_alloc_slot(VM_STRING);
|
||||
heap[slot].s = r.ref;
|
||||
stack_push(slot);
|
||||
} else {
|
||||
stack_push(r);
|
||||
}
|
||||
int slot;
|
||||
switch (f->return_type.data) {
|
||||
case AIN_VOID:
|
||||
break;
|
||||
case AIN_STRING:
|
||||
slot = heap_alloc_slot(VM_STRING);
|
||||
heap[slot].s = r.ref;
|
||||
stack_push(slot);
|
||||
break;
|
||||
case AIN_BOOL:
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
stack_push(*(bool*)&r);
|
||||
#pragma GCC diagnostic pop
|
||||
break;
|
||||
default:
|
||||
stack_push(r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern struct static_library lib_ACXLoader;
|
||||
extern struct static_library lib_ACXLoaderP2;
|
||||
extern struct static_library lib_ADVSYS;
|
||||
extern struct static_library lib_AliceLogo;
|
||||
extern struct static_library lib_AliceLogo2;
|
||||
@@ -296,8 +423,10 @@ extern struct static_library lib_AliceLogo3;
|
||||
extern struct static_library lib_AliceLogo4;
|
||||
extern struct static_library lib_AliceLogo5;
|
||||
extern struct static_library lib_AnteaterADVEngine;
|
||||
extern struct static_library lib_Array;
|
||||
extern struct static_library lib_BanMisc;
|
||||
extern struct static_library lib_Bitarray;
|
||||
extern struct static_library lib_CalcTable;
|
||||
extern struct static_library lib_CGManager;
|
||||
extern struct static_library lib_ChipmunkSpriteEngine;
|
||||
extern struct static_library lib_ChrLoader;
|
||||
@@ -307,15 +436,25 @@ extern struct static_library lib_Confirm2;
|
||||
extern struct static_library lib_Confirm3;
|
||||
extern struct static_library lib_CrayfishLogViewer;
|
||||
extern struct static_library lib_Cursor;
|
||||
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_DrawDungeon;
|
||||
extern struct static_library lib_DrawDungeon2;
|
||||
extern struct static_library lib_DrawDungeon14;
|
||||
extern struct static_library lib_DrawEffect;
|
||||
extern struct static_library lib_DrawField;
|
||||
extern struct static_library lib_DrawGraph;
|
||||
extern struct static_library lib_DrawMovie;
|
||||
extern struct static_library lib_DrawMovie2;
|
||||
extern struct static_library lib_DrawMovie3;
|
||||
extern struct static_library lib_DrawNumeral;
|
||||
extern struct static_library lib_DrawPluginManager;
|
||||
extern struct static_library lib_DrawRain;
|
||||
extern struct static_library lib_DrawRipple;
|
||||
extern struct static_library lib_DrawSimpleText;
|
||||
extern struct static_library lib_DrawSnow;
|
||||
extern struct static_library lib_File;
|
||||
extern struct static_library lib_File2;
|
||||
extern struct static_library lib_FileOperation;
|
||||
@@ -323,6 +462,7 @@ extern struct static_library lib_FillAngle;
|
||||
extern struct static_library lib_GoatGUIEngine;
|
||||
extern struct static_library lib_Gpx2Plus;
|
||||
extern struct static_library lib_GUIEngine;
|
||||
extern struct static_library lib_HTTPDownloader;
|
||||
extern struct static_library lib_IbisInputEngine;
|
||||
extern struct static_library lib_InputDevice;
|
||||
extern struct static_library lib_InputString;
|
||||
@@ -330,6 +470,8 @@ extern struct static_library lib_KiwiSoundEngine;
|
||||
extern struct static_library lib_LoadCG;
|
||||
extern struct static_library lib_MainEXFile;
|
||||
extern struct static_library lib_MainSurface;
|
||||
extern struct static_library lib_MamanyoDemo;
|
||||
extern struct static_library lib_MamanyoSDemo;
|
||||
extern struct static_library lib_MarmotModelEngine;
|
||||
extern struct static_library lib_Math;
|
||||
extern struct static_library lib_MapLoader;
|
||||
@@ -338,25 +480,58 @@ extern struct static_library lib_MonsterInfo;
|
||||
extern struct static_library lib_MsgLogManager;
|
||||
extern struct static_library lib_MsgLogViewer;
|
||||
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_PassRegister;
|
||||
extern struct static_library lib_PastelChime2;
|
||||
extern struct static_library lib_PartsEngine;
|
||||
extern struct static_library lib_PixelRestore;
|
||||
extern struct static_library lib_PlayDemo;
|
||||
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_SengokuRanceFont;
|
||||
extern struct static_library lib_Sound2ex;
|
||||
extern struct static_library lib_SoundFilePlayer;
|
||||
extern struct static_library lib_StoatSpriteEngine;
|
||||
extern struct static_library lib_StretchHelper;
|
||||
extern struct static_library lib_SystemService;
|
||||
extern struct static_library lib_SystemServiceEx;
|
||||
extern struct static_library lib_TapirEngine;
|
||||
extern struct static_library lib_Timer;
|
||||
extern struct static_library lib_Toushin3Loader;
|
||||
extern struct static_library lib_vmAnime;
|
||||
extern struct static_library lib_vmArray;
|
||||
extern struct static_library lib_vmCG;
|
||||
extern struct static_library lib_vmChrLoader;
|
||||
extern struct static_library lib_vmCursor;
|
||||
extern struct static_library lib_vmDalkGaiden;
|
||||
extern struct static_library lib_vmData;
|
||||
extern struct static_library lib_vmDialog;
|
||||
extern struct static_library lib_vmDrawGauge;
|
||||
extern struct static_library lib_vmDrawMsg;
|
||||
extern struct static_library lib_vmDrawNumber;
|
||||
extern struct static_library lib_vmFile;
|
||||
extern struct static_library lib_vmGraph;
|
||||
extern struct static_library lib_vmGraphQuake;
|
||||
extern struct static_library lib_vmKey;
|
||||
extern struct static_library lib_vmMapLoader;
|
||||
extern struct static_library lib_vmMsgLog;
|
||||
extern struct static_library lib_vmMsgSkip;
|
||||
extern struct static_library lib_vmMusic;
|
||||
extern struct static_library lib_vmSound;
|
||||
extern struct static_library lib_vmSprite;
|
||||
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_VSFile;
|
||||
|
||||
static struct static_library *static_libraries[] = {
|
||||
&lib_ACXLoader,
|
||||
&lib_ACXLoaderP2,
|
||||
&lib_ADVSYS,
|
||||
&lib_AliceLogo,
|
||||
&lib_AliceLogo2,
|
||||
@@ -364,8 +539,10 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_AliceLogo4,
|
||||
&lib_AliceLogo5,
|
||||
&lib_AnteaterADVEngine,
|
||||
&lib_Array,
|
||||
&lib_BanMisc,
|
||||
&lib_Bitarray,
|
||||
&lib_CalcTable,
|
||||
&lib_CGManager,
|
||||
&lib_ChipmunkSpriteEngine,
|
||||
&lib_ChrLoader,
|
||||
@@ -375,15 +552,25 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_Confirm3,
|
||||
&lib_CrayfishLogViewer,
|
||||
&lib_Cursor,
|
||||
&lib_DALKDemo,
|
||||
&lib_DALKEDemo,
|
||||
&lib_Data,
|
||||
&lib_DataFile,
|
||||
&lib_DrawDungeon,
|
||||
&lib_DrawDungeon2,
|
||||
&lib_DrawDungeon14,
|
||||
&lib_DrawEffect,
|
||||
&lib_DrawField,
|
||||
&lib_DrawGraph,
|
||||
&lib_DrawMovie,
|
||||
&lib_DrawMovie2,
|
||||
&lib_DrawMovie3,
|
||||
&lib_DrawNumeral,
|
||||
&lib_DrawPluginManager,
|
||||
&lib_DrawRain,
|
||||
&lib_DrawRipple,
|
||||
&lib_DrawSimpleText,
|
||||
&lib_DrawSnow,
|
||||
&lib_File,
|
||||
&lib_File2,
|
||||
&lib_FileOperation,
|
||||
@@ -391,6 +578,7 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_GoatGUIEngine,
|
||||
&lib_Gpx2Plus,
|
||||
&lib_GUIEngine,
|
||||
&lib_HTTPDownloader,
|
||||
&lib_IbisInputEngine,
|
||||
&lib_InputDevice,
|
||||
&lib_InputString,
|
||||
@@ -398,6 +586,8 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_LoadCG,
|
||||
&lib_MainEXFile,
|
||||
&lib_MainSurface,
|
||||
&lib_MamanyoDemo,
|
||||
&lib_MamanyoSDemo,
|
||||
&lib_MarmotModelEngine,
|
||||
&lib_Math,
|
||||
&lib_MapLoader,
|
||||
@@ -406,21 +596,53 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_MsgLogManager,
|
||||
&lib_MsgLogViewer,
|
||||
&lib_MsgSkip,
|
||||
&lib_MusicSystem,
|
||||
&lib_NewFont,
|
||||
&lib_OutputLog,
|
||||
&lib_PassRegister,
|
||||
&lib_PastelChime2,
|
||||
&lib_PartsEngine,
|
||||
&lib_PixelRestore,
|
||||
&lib_PlayDemo,
|
||||
&lib_PlayMovie,
|
||||
&lib_ReignEngine,
|
||||
&lib_SACT2,
|
||||
&lib_SACTDX,
|
||||
&lib_SengokuRanceFont,
|
||||
&lib_Sound2ex,
|
||||
&lib_SoundFilePlayer,
|
||||
&lib_StoatSpriteEngine,
|
||||
&lib_StretchHelper,
|
||||
&lib_SystemService,
|
||||
&lib_SystemServiceEx,
|
||||
&lib_TapirEngine,
|
||||
&lib_Timer,
|
||||
&lib_Toushin3Loader,
|
||||
&lib_vmAnime,
|
||||
&lib_vmArray,
|
||||
&lib_vmCG,
|
||||
&lib_vmChrLoader,
|
||||
&lib_vmCursor,
|
||||
&lib_vmDalkGaiden,
|
||||
&lib_vmData,
|
||||
&lib_vmDialog,
|
||||
&lib_vmDrawGauge,
|
||||
&lib_vmDrawMsg,
|
||||
&lib_vmDrawNumber,
|
||||
&lib_vmFile,
|
||||
&lib_vmGraph,
|
||||
&lib_vmGraphQuake,
|
||||
&lib_vmKey,
|
||||
&lib_vmMapLoader,
|
||||
&lib_vmMsgLog,
|
||||
&lib_vmMsgSkip,
|
||||
&lib_vmMusic,
|
||||
&lib_vmSound,
|
||||
&lib_vmSprite,
|
||||
&lib_vmString,
|
||||
&lib_vmSurface,
|
||||
&lib_vmSystem,
|
||||
&lib_vmTimer,
|
||||
&lib_VSFile,
|
||||
NULL
|
||||
};
|
||||
@@ -529,16 +751,20 @@ static void link_libraries(void)
|
||||
}
|
||||
}
|
||||
|
||||
bool libraries_initialized = false;
|
||||
|
||||
void init_libraries(void)
|
||||
{
|
||||
library_run_all("_PreLink");
|
||||
link_libraries();
|
||||
library_run_all("_ModuleInit");
|
||||
libraries_initialized = true;
|
||||
}
|
||||
|
||||
void exit_libraries(void)
|
||||
{
|
||||
library_run_all("_ModuleFini");
|
||||
if (libraries_initialized)
|
||||
library_run_all("_ModuleFini");
|
||||
}
|
||||
|
||||
void static_library_replace(struct static_library *lib, const char *name, void *fun)
|
||||
|
||||
@@ -189,6 +189,12 @@ static float fnl_font_size_char(struct font_size *_size, uint32_t code)
|
||||
return r;
|
||||
}
|
||||
|
||||
static float fnl_font_size_char_kerning(struct font_size *size, uint32_t code,
|
||||
uint32_t code_next)
|
||||
{
|
||||
return fnl_font_size_char(size, code);
|
||||
}
|
||||
|
||||
struct font *fnl_font_load(struct fnl *lib, unsigned index)
|
||||
{
|
||||
if (index >= lib->nr_fonts)
|
||||
@@ -227,6 +233,7 @@ struct font *fnl_font_load(struct fnl *lib, unsigned index)
|
||||
font->super.get_actual_size_round_down = fnl_font_get_actual_size_round_down;
|
||||
font->super.get_glyph = fnl_font_get_glyph;
|
||||
font->super.size_char = fnl_font_size_char;
|
||||
font->super.size_char_kerning = fnl_font_size_char_kerning;
|
||||
|
||||
if (!game_rance7_mg)
|
||||
gfx_text_advance_edges = true;
|
||||
|
||||
+46
-3
@@ -18,6 +18,8 @@
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_BITMAP_H
|
||||
#include FT_ADVANCES_H
|
||||
#include <SDL.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/hashtable.h"
|
||||
@@ -96,7 +98,7 @@ static float ft_font_get_actual_size(struct font *_, float size)
|
||||
static Rectangle init_glyph_texture(Texture *dst, FT_Bitmap *glyph, int bitmap_left, int bitmap_top, int size, bool half_width)
|
||||
{
|
||||
// calculate block size and offsets
|
||||
int block_width = (half_width ? size/2 : size);
|
||||
int block_width = max(half_width ? size/2 : size, max(0, bitmap_left) + glyph->width);
|
||||
int block_height = size + size/2;
|
||||
int width = block_width + GLYPH_BORDER_SIZE*2;
|
||||
int height = block_height + GLYPH_BORDER_SIZE*2;
|
||||
@@ -185,16 +187,58 @@ static float ft_font_size_char(struct font_size *size, uint32_t code)
|
||||
return is_half_width(code) ? size->size / 2 : size->size;
|
||||
}
|
||||
|
||||
static float ft_font_size_char_kerning(struct font_size *size, uint32_t code,
|
||||
uint32_t code_next)
|
||||
{
|
||||
struct font_ft *font = (struct font_ft*)size->font;
|
||||
ft_font_set_size(font, size->size);
|
||||
|
||||
FT_UInt index = FT_Get_Char_Index(font->font, code);
|
||||
FT_UInt next_index = FT_Get_Char_Index(font->font, code_next);
|
||||
|
||||
FT_Fixed advance;
|
||||
FT_Get_Advance(font->font, index, FT_LOAD_DEFAULT, &advance);
|
||||
|
||||
if (!FT_HAS_KERNING(font->font)) {
|
||||
return advance / 65536.f;
|
||||
}
|
||||
|
||||
FT_Vector delta;
|
||||
FT_Get_Kerning(font->font, index, next_index, FT_KERNING_DEFAULT, &delta);
|
||||
|
||||
return (advance / 65536.f) + (delta.x / 64.f);
|
||||
}
|
||||
|
||||
struct font *ft_font_load(const char *path)
|
||||
{
|
||||
struct font_ft *font = xcalloc(1, sizeof(struct font_ft));
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// On Android, path may be an asset name which FT_New_Face cannot read
|
||||
// directly, so use SDL_LoadFile to load the content into memory.
|
||||
size_t size;
|
||||
void *buf = SDL_LoadFile(path, &size);
|
||||
if (!buf) {
|
||||
free(font);
|
||||
return NULL;
|
||||
}
|
||||
if (FT_New_Memory_Face(ft_lib, buf, size, 0, &font->font)) {
|
||||
free(buf);
|
||||
free(font);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
if (FT_New_Face(ft_lib, path, 0, &font->font)) {
|
||||
free(font);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!font->font->charmap) {
|
||||
WARNING("Font '%s' doesn't contain unicode charmap", path);
|
||||
#ifdef __ANDROID__
|
||||
free(buf);
|
||||
#endif
|
||||
free(font);
|
||||
return NULL;
|
||||
}
|
||||
@@ -204,7 +248,6 @@ struct font *ft_font_load(const char *path)
|
||||
font->super.get_actual_size_round_down = ft_font_get_actual_size;
|
||||
font->super.get_glyph = ft_font_get_glyph;
|
||||
font->super.size_char = ft_font_size_char;
|
||||
font->super.size_char_kerning = ft_font_size_char_kerning;
|
||||
return &font->super;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+30
-15
@@ -37,10 +37,17 @@ struct fmt_spec {
|
||||
enum fmt_type type;
|
||||
int precision;
|
||||
int padding;
|
||||
bool zero_pad;
|
||||
bool zero_pad;
|
||||
bool zenkaku;
|
||||
};
|
||||
|
||||
static inline bool is_integer_type(enum string_format_type t)
|
||||
{
|
||||
return t == STRFMT_INT
|
||||
|| t == STRFMT_BOOL
|
||||
|| t == STRFMT_LONG_INT;
|
||||
}
|
||||
|
||||
static int read_number(const char **_fmt)
|
||||
{
|
||||
int n = 0;
|
||||
@@ -54,7 +61,7 @@ static int read_number(const char **_fmt)
|
||||
return n;
|
||||
}
|
||||
|
||||
static bool parse_fmt_spec(const char **_fmt, struct fmt_spec *spec, enum ain_data_type target)
|
||||
static bool parse_fmt_spec(const char **_fmt, struct fmt_spec *spec, enum string_format_type target)
|
||||
{
|
||||
bool r = false;
|
||||
const char *fmt = (*_fmt) + 1;
|
||||
@@ -64,33 +71,33 @@ static bool parse_fmt_spec(const char **_fmt, struct fmt_spec *spec, enum ain_da
|
||||
switch (*fmt) {
|
||||
case 'd':
|
||||
spec->type = FMT_INT;
|
||||
r = target == AIN_INT;
|
||||
r = is_integer_type(target);
|
||||
goto end;
|
||||
case 'D':
|
||||
spec->type = FMT_INT;
|
||||
spec->zenkaku = true;
|
||||
r = target == AIN_INT;
|
||||
r = is_integer_type(target);
|
||||
goto end;
|
||||
case 'f':
|
||||
spec->type = FMT_FLOAT;
|
||||
r = target == AIN_FLOAT;
|
||||
r = target == STRFMT_FLOAT;
|
||||
goto end;
|
||||
case 'F':
|
||||
spec->type = FMT_FLOAT;
|
||||
spec->zenkaku = true;
|
||||
r = target == AIN_FLOAT;
|
||||
r = target == STRFMT_FLOAT;
|
||||
goto end;
|
||||
case 's':
|
||||
spec->type = FMT_STRING;
|
||||
r = target == AIN_STRING;
|
||||
r = target == STRFMT_STRING;
|
||||
goto end;
|
||||
case 'c':
|
||||
spec->type = FMT_CHAR;
|
||||
r = target == AIN_INT;
|
||||
r = is_integer_type(target);
|
||||
goto end;
|
||||
case 'b':
|
||||
spec->type = FMT_BOOL;
|
||||
r = target == AIN_INT;
|
||||
r = is_integer_type(target);
|
||||
goto end;
|
||||
case '0':
|
||||
spec->zero_pad = true;
|
||||
@@ -138,10 +145,18 @@ static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value
|
||||
len = float_to_cstr(buf, DIGIT_MAX, arg.f, spec->padding, spec->zero_pad, spec->precision, spec->zenkaku);
|
||||
string_append_cstr(s, buf, len);
|
||||
break;
|
||||
case FMT_STRING:
|
||||
string_append(s, heap_get_string(arg.i));
|
||||
case FMT_STRING: {
|
||||
struct string *val = heap_get_string(arg.i);
|
||||
int pad_len = spec->padding - val->size;
|
||||
if (pad_len > 0) {
|
||||
for (int i = 0; i < pad_len; i++) {
|
||||
string_push_back(s, ' ');
|
||||
}
|
||||
}
|
||||
string_append(s, val);
|
||||
heap_unref(arg.i);
|
||||
break;
|
||||
}
|
||||
case FMT_CHAR:
|
||||
string_push_back(s, arg.i);
|
||||
break;
|
||||
@@ -154,9 +169,8 @@ static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value
|
||||
}
|
||||
}
|
||||
|
||||
struct string *string_format(struct string *fmt, union vm_value arg, enum ain_data_type type)
|
||||
struct string *string_format(struct string *fmt, union vm_value arg, enum string_format_type type)
|
||||
{
|
||||
struct string *out = string_ref(&EMPTY_STRING);
|
||||
for (const char *s = fmt->text; *s; s++) {
|
||||
if (*s != '%')
|
||||
continue;
|
||||
@@ -164,11 +178,12 @@ struct string *string_format(struct string *fmt, union vm_value arg, enum ain_da
|
||||
size_t size = s - fmt->text;
|
||||
struct fmt_spec spec;
|
||||
if (parse_fmt_spec(&s, &spec, type)) {
|
||||
struct string *out = string_ref(&EMPTY_STRING);
|
||||
string_append_cstr(&out, fmt->text, size);
|
||||
append_fmt(&out, &spec, arg);
|
||||
string_append_cstr(&out, s, strlen(s));
|
||||
break;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
return string_ref(fmt);
|
||||
}
|
||||
|
||||
+52
-16
@@ -27,6 +27,7 @@
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "input.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#include "hll/hll.h"
|
||||
@@ -35,8 +36,9 @@ bool game_daibanchou_en = false;
|
||||
bool game_rance02_mg = false;
|
||||
bool game_rance6_mg = false;
|
||||
bool game_rance7_mg = false;
|
||||
|
||||
bool id_indexed_afa = false;
|
||||
bool game_rance8 = false;
|
||||
bool game_rance8_mg = false;
|
||||
bool game_dungeons_and_dolls = false;
|
||||
|
||||
static void write_instruction0(struct buffer *out, enum opcode op)
|
||||
{
|
||||
@@ -49,6 +51,42 @@ static void write_instruction1(struct buffer *out, enum opcode op, int32_t arg)
|
||||
buffer_write_int32(out, arg);
|
||||
}
|
||||
|
||||
void set_msgskip_delay(struct ain *ain, unsigned ms)
|
||||
{
|
||||
int orig_fno = ain_get_function(ain, "A");
|
||||
if (orig_fno <= 0) {
|
||||
WARNING("No 'A' function when applying msgskip delay");
|
||||
return;
|
||||
}
|
||||
|
||||
int new_fno = ain_dup_function(ain, orig_fno);
|
||||
struct ain_function *override_f = &ain->functions[orig_fno];
|
||||
|
||||
// override void A(void) {
|
||||
// super();
|
||||
// if (key_is_down(VK_CONTROL))
|
||||
// System.sleep(ms);
|
||||
// }
|
||||
struct buffer out;
|
||||
buffer_init(&out, NULL, 0);
|
||||
write_instruction1(&out, FUNC, orig_fno);
|
||||
write_instruction1(&out, CALLFUNC, new_fno);
|
||||
write_instruction1(&out, PUSH, VK_CONTROL);
|
||||
write_instruction1(&out, CALLSYS, VM_XSYS_KEY_IS_DOWN);
|
||||
uint32_t ifz_addr = out.index;
|
||||
write_instruction1(&out, IFZ, 0);
|
||||
write_instruction1(&out, PUSH, ms);
|
||||
write_instruction1(&out, CALLSYS, SYS_SLEEP);
|
||||
buffer_write_int32_at(&out, ifz_addr + 2, ain->code_size + out.index);
|
||||
write_instruction0(&out, RETURN);
|
||||
write_instruction1(&out, ENDFUNC, orig_fno);
|
||||
|
||||
override_f->address = ain->code_size + 6;
|
||||
ain->code = xrealloc(ain->code, ain->code_size + out.index);
|
||||
memcpy(ain->code + ain->code_size, out.buf, out.index);
|
||||
ain->code_size += out.index;
|
||||
}
|
||||
|
||||
// Rance 02 (MangaGamer version)
|
||||
// -----------------------------
|
||||
// Uses a custom proportional font implementation which makes assumptions
|
||||
@@ -113,7 +151,7 @@ static void apply_rance02_hacks(struct ain *ain)
|
||||
|
||||
// multiply by config.text_x_scale (if not 1.0)
|
||||
buffer_write_int32_at(&out, jump_addr + 2, out.index);
|
||||
if (fabsf(1.0 - config.text_x_scale) > 0.01) {
|
||||
if (fabsf(1.0f - config.text_x_scale) > 0.01) {
|
||||
union { int32_t i; float f; } cast = { .f = config.text_x_scale };
|
||||
write_instruction0(&out, ITOF);
|
||||
write_instruction1(&out, F_PUSH, cast.i);
|
||||
@@ -137,13 +175,6 @@ static void apply_rance6_hacks(struct ain *ain)
|
||||
game_rance6_mg = true;
|
||||
}
|
||||
|
||||
static void apply_rance7_hacks(struct ain *ain)
|
||||
{
|
||||
if (ain_get_library(ain, "SengokuRanceFont") < 0)
|
||||
return;
|
||||
game_rance7_mg = true;
|
||||
}
|
||||
|
||||
// Daibanchou (English fan translation)
|
||||
// ------------------------------------
|
||||
// Gpx2Plus.CopyStretchReduceAMap behaves differently in order to work around
|
||||
@@ -165,12 +196,17 @@ void apply_game_specific_hacks(struct ain *ain)
|
||||
apply_rance02_hacks(ain);
|
||||
} else if (!strcmp(game_name, "Rance6")) {
|
||||
apply_rance6_hacks(ain);
|
||||
} else if (!strcmp(game_name, "Sengoku Rance")) {
|
||||
apply_rance7_hacks(ain);
|
||||
} else if (!strcmp(game_name, "しゃーまんず・さんくちゅあり 巫女の聖域")) {
|
||||
id_indexed_afa = true;
|
||||
} else if (!strcmp(game_name, "大帝国")) {
|
||||
id_indexed_afa = true;
|
||||
} else if (!strcmp(game_name, "ランス・クエスト")) {
|
||||
game_rance8 = true;
|
||||
} else if (!strcmp(game_name, "Rance Quest")) {
|
||||
game_rance8 = true;
|
||||
game_rance8_mg = true;
|
||||
} else if (!strcmp(game_name, "DUNGEONS&DOLLS")) {
|
||||
game_dungeons_and_dolls = true;
|
||||
}
|
||||
free(game_name);
|
||||
|
||||
// XXX: we don't rely on game_name because mods change it
|
||||
if (ain_get_library(ain, "SengokuRanceFont") >= 0)
|
||||
game_rance7_mg = true;
|
||||
}
|
||||
|
||||
+9
-13
@@ -30,6 +30,7 @@
|
||||
|
||||
struct vm_pointer *heap = NULL;
|
||||
size_t heap_size = 0;
|
||||
uint32_t heap_next_seq;
|
||||
|
||||
// Heap free list
|
||||
// This is a list of unused indices into the 'heap' array.
|
||||
@@ -59,19 +60,6 @@ void heap_grow(size_t new_size)
|
||||
heap_size = new_size;
|
||||
}
|
||||
|
||||
void heap_guarantee(unsigned headroom)
|
||||
{
|
||||
if (heap_size - heap_free_ptr >= headroom)
|
||||
return;
|
||||
|
||||
size_t new_size = heap_size;
|
||||
while (new_size - heap_free_ptr < headroom) {
|
||||
new_size += HEAP_ALLOC_STEP;
|
||||
}
|
||||
|
||||
heap_grow(new_size);
|
||||
}
|
||||
|
||||
void heap_init(void)
|
||||
{
|
||||
if (!heap) {
|
||||
@@ -86,6 +74,7 @@ void heap_init(void)
|
||||
heap_free_stack[i] = i;
|
||||
}
|
||||
heap_free_ptr = 1; // global page at index 0
|
||||
heap_next_seq = 1;
|
||||
}
|
||||
|
||||
int32_t heap_alloc_slot(enum vm_pointer_type type)
|
||||
@@ -96,6 +85,7 @@ int32_t heap_alloc_slot(enum vm_pointer_type type)
|
||||
|
||||
int32_t slot = heap_free_stack[heap_free_ptr++];
|
||||
heap[slot].ref = 1;
|
||||
heap[slot].seq = heap_next_seq++;
|
||||
heap[slot].type = type;
|
||||
#ifdef DEBUG_HEAP
|
||||
heap[slot].alloc_addr = instr_ptr;
|
||||
@@ -110,6 +100,7 @@ int32_t heap_alloc_slot(enum vm_pointer_type type)
|
||||
|
||||
static void heap_free_slot(int32_t slot)
|
||||
{
|
||||
heap[slot].seq = 0;
|
||||
heap_free_stack[--heap_free_ptr] = slot;
|
||||
}
|
||||
|
||||
@@ -212,6 +203,11 @@ void exit_unref(int slot)
|
||||
heap_free_slot(slot);
|
||||
}
|
||||
|
||||
uint32_t heap_get_seq(int slot)
|
||||
{
|
||||
return heap_index_valid(slot) ? heap[slot].seq : 0;
|
||||
}
|
||||
|
||||
bool heap_index_valid(int index)
|
||||
{
|
||||
return index >= 0 && (size_t)index < heap_size && heap[index].ref > 0;
|
||||
|
||||
+49
-2
@@ -23,14 +23,14 @@
|
||||
#include "system4/string.h"
|
||||
#include "system4/acx.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "little_endian.h"
|
||||
#include "vm/heap.h"
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
struct acx *acx = NULL;
|
||||
static struct acx *acx = NULL;
|
||||
|
||||
static bool ACXLoader_Load(struct string *filename)
|
||||
{
|
||||
@@ -131,3 +131,50 @@ HLL_LIBRARY(ACXLoader,
|
||||
HLL_EXPORT(GetDataInt, ACXLoader_GetDataInt),
|
||||
HLL_EXPORT(GetDataString, ACXLoader_GetDataString),
|
||||
HLL_EXPORT(GetDataStruct, ACXLoader_GetDataStruct));
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(, void, ACXLoaderP2, GetError, int *nError, struct string **szErrorString);
|
||||
|
||||
static bool ACXLoaderP2_GetDataIntByKey(int key, int col, int *ptr)
|
||||
{
|
||||
if (!acx)
|
||||
return false;
|
||||
if (col < 0 || col >= acx->nr_columns)
|
||||
return false;
|
||||
for (int line = 0; line < acx->nr_lines; line++) {
|
||||
if (key == acx_get_int(acx, line, 0)) {
|
||||
*ptr = acx_get_int(acx, line, col);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ACXLoaderP2_GetDataStringByKey(int key, int col, struct string **ptr)
|
||||
{
|
||||
if (!acx)
|
||||
return false;
|
||||
if (col < 0 || col >= acx->nr_columns)
|
||||
return false;
|
||||
for (int line = 0; line < acx->nr_lines; line++) {
|
||||
if (key == acx_get_int(acx, line, 0)) {
|
||||
if (*ptr)
|
||||
free_string(*ptr);
|
||||
*ptr = string_ref(acx_get_string(acx, line, col));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(ACXLoaderP2,
|
||||
HLL_EXPORT(Load, ACXLoader_Load),
|
||||
HLL_EXPORT(Release, ACXLoader_Release),
|
||||
HLL_EXPORT(GetNumofLine, ACXLoader_GetNumofLine),
|
||||
HLL_EXPORT(GetNumofColumn, ACXLoader_GetNumofColumn),
|
||||
HLL_EXPORT(GetDataInt, ACXLoader_GetDataInt),
|
||||
HLL_EXPORT(GetDataString, ACXLoader_GetDataString),
|
||||
HLL_EXPORT(GetDataStruct, ACXLoader_GetDataStruct),
|
||||
HLL_EXPORT(GetError, ACXLoaderP2_GetError),
|
||||
HLL_EXPORT(GetDataIntByKey, ACXLoaderP2_GetDataIntByKey),
|
||||
HLL_EXPORT(GetDataStringByKey, ACXLoaderP2_GetDataStringByKey)
|
||||
);
|
||||
|
||||
+148
-61
@@ -40,8 +40,14 @@ struct adv_log {
|
||||
};
|
||||
|
||||
static bool enabled = true;
|
||||
static unsigned nr_logs = 0;
|
||||
struct adv_log *logs = NULL;
|
||||
|
||||
// A circular buffer of logs.
|
||||
#define LOG_BUFFER_SIZE 1000
|
||||
static struct adv_log *logs = NULL;
|
||||
static unsigned log_first;
|
||||
static unsigned log_last;
|
||||
|
||||
static void AnteaterADVEngine_PreLink(void);
|
||||
|
||||
static void init_log(struct adv_log *log)
|
||||
{
|
||||
@@ -50,20 +56,33 @@ static void init_log(struct adv_log *log)
|
||||
log->nr_lines = 1;
|
||||
}
|
||||
|
||||
static void free_log(struct adv_log *log)
|
||||
{
|
||||
for (unsigned i = 0; i < log->nr_lines; i++) {
|
||||
free_string(log->lines[i]);
|
||||
}
|
||||
free(log->lines);
|
||||
free(log->voices);
|
||||
log->nr_lines = 0;
|
||||
log->lines = NULL;
|
||||
log->nr_voices = 0;
|
||||
log->voices = NULL;
|
||||
}
|
||||
|
||||
// Ensure there is at least one log allocated
|
||||
static void ensure_log(void)
|
||||
{
|
||||
if (!nr_logs) {
|
||||
logs = xcalloc(1, sizeof(struct adv_log));
|
||||
if (!logs) {
|
||||
logs = xcalloc(LOG_BUFFER_SIZE, sizeof(struct adv_log));
|
||||
init_log(&logs[0]);
|
||||
nr_logs = 1;
|
||||
log_first = log_last = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct adv_log *current_log()
|
||||
{
|
||||
ensure_log();
|
||||
return &logs[nr_logs-1];
|
||||
return &logs[log_last];
|
||||
}
|
||||
|
||||
static struct string **current_line(struct adv_log *log)
|
||||
@@ -75,17 +94,14 @@ static struct string **current_line(struct adv_log *log)
|
||||
void ADVLogList_Clear(void)
|
||||
{
|
||||
// XXX: this function works even if logging is disabled
|
||||
for (unsigned i = 0; i < nr_logs; i++) {
|
||||
struct adv_log *log = &logs[i];
|
||||
for (unsigned i = 0; i < log->nr_lines; i++) {
|
||||
free_string(log->lines[i]);
|
||||
}
|
||||
free(log->lines);
|
||||
free(log->voices);
|
||||
if (!logs) return;
|
||||
for (unsigned i = log_first;; i = (i + 1) % LOG_BUFFER_SIZE) {
|
||||
free_log(&logs[i]);
|
||||
if (i == log_last)
|
||||
break;
|
||||
}
|
||||
free(logs);
|
||||
logs = NULL;
|
||||
nr_logs = 0;
|
||||
}
|
||||
|
||||
void ADVLogList_AddText(struct string **text)
|
||||
@@ -106,8 +122,12 @@ void ADVLogList_AddNewLine(void)
|
||||
void ADVLogList_AddNewPage(void)
|
||||
{
|
||||
if (!enabled) return;
|
||||
logs = xrealloc_array(logs, nr_logs, nr_logs + 1, sizeof(struct adv_log));
|
||||
init_log(&logs[nr_logs++]);
|
||||
log_last = (log_last + 1) % LOG_BUFFER_SIZE;
|
||||
if (log_last == log_first) {
|
||||
free_log(&logs[log_first]);
|
||||
log_first = (log_first + 1) % LOG_BUFFER_SIZE;
|
||||
}
|
||||
init_log(&logs[log_last]);
|
||||
}
|
||||
|
||||
void ADVLogList_AddVoice(int voice_no)
|
||||
@@ -130,55 +150,55 @@ bool ADVLogList_IsEnable(void)
|
||||
|
||||
int ADVLogList_GetNumofADVLog(void)
|
||||
{
|
||||
return nr_logs ? nr_logs : 1;
|
||||
if (!logs) return 1;
|
||||
return (log_last - log_first + LOG_BUFFER_SIZE) % LOG_BUFFER_SIZE + 1;
|
||||
}
|
||||
|
||||
static void check_log_nr(int log_no)
|
||||
static struct adv_log *log_entry(int log_no)
|
||||
{
|
||||
// NOTE: AnteaterADVEngine.dll segfaults on invalid index
|
||||
ensure_log();
|
||||
if (log_no < 0 || (unsigned)log_no >= nr_logs)
|
||||
if (log_no < 0 || (unsigned)log_no >= ADVLogList_GetNumofADVLog())
|
||||
VM_ERROR("Invalid log number: %d", log_no);
|
||||
return &logs[(log_first + log_no) % LOG_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
int ADVLogList_GetNumofADVLogText(int log_no)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
return logs[log_no].nr_lines;
|
||||
return log_entry(log_no)->nr_lines;
|
||||
}
|
||||
|
||||
void ADVLogList_GetADVLogText(int log_no, int line_no, struct string **text)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
struct adv_log *log = log_entry(log_no);
|
||||
// NOTE: AnteaterADVEngine.dll segfaults on invalid index
|
||||
if (line_no < 0 || (unsigned)line_no >= logs[log_no].nr_lines)
|
||||
if (line_no < 0 || (unsigned)line_no >= log->nr_lines)
|
||||
VM_ERROR("Invalid line number: %d", line_no);
|
||||
|
||||
*text = string_ref(logs[log_no].lines[line_no]);
|
||||
*text = string_ref(log->lines[line_no]);
|
||||
}
|
||||
|
||||
int ADVLogList_GetNumofADVLogVoice(int log_no)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
return logs[log_no].nr_voices;
|
||||
return log_entry(log_no)->nr_voices;
|
||||
}
|
||||
|
||||
int ADVLogList_GetADVLogVoice(int log_no, int voice_no)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
if (voice_no < 0 || (unsigned)voice_no >= logs[log_no].nr_voices)
|
||||
struct adv_log *log = log_entry(log_no);
|
||||
if (voice_no < 0 || (unsigned)voice_no >= log->nr_voices)
|
||||
VM_ERROR("Invalid voice number: %d", voice_no);
|
||||
|
||||
return logs[log_no].voices[voice_no];
|
||||
return log->voices[voice_no];
|
||||
}
|
||||
|
||||
int ADVLogList_GetADVLogVoiceLast(int log_no)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
unsigned nr_voices = logs[log_no].nr_voices;
|
||||
struct adv_log *log = log_entry(log_no);
|
||||
unsigned nr_voices = log->nr_voices;
|
||||
if (!nr_voices)
|
||||
return 0;
|
||||
return logs[log_no].voices[nr_voices - 1];
|
||||
return log->voices[nr_voices - 1];
|
||||
}
|
||||
|
||||
bool ADVLogList_Save(struct page **iarray)
|
||||
@@ -200,9 +220,10 @@ bool ADVLogList_Save(struct page **iarray)
|
||||
struct iarray_writer b;
|
||||
iarray_init_writer(&b, "ADL");
|
||||
iarray_write(&b, 0);
|
||||
int nr_logs = ADVLogList_GetNumofADVLog();
|
||||
iarray_write(&b, nr_logs);
|
||||
for (unsigned i = 0; i < nr_logs; i++) {
|
||||
struct adv_log *log = &logs[i];
|
||||
struct adv_log *log = &logs[(log_first + i) % LOG_BUFFER_SIZE];
|
||||
iarray_write(&b, log->nr_lines);
|
||||
for (unsigned i = 0; i < log->nr_lines; i++) {
|
||||
iarray_write_string(&b, log->lines[i]);
|
||||
@@ -241,14 +262,14 @@ bool ADVLogList_Load(struct page **data)
|
||||
goto error;
|
||||
}
|
||||
|
||||
logs = xcalloc(nr_pages + 1, sizeof(struct adv_log));
|
||||
// XXX: For whatever reason this function always creates a blank page 0,
|
||||
// and then loads the input pages after that. So a consecutive save
|
||||
// and load will increase all page numbers by 1.
|
||||
init_log(&logs[0]);
|
||||
ensure_log();
|
||||
|
||||
for (int i = 1; i < nr_pages + 1; i++) {
|
||||
struct adv_log *log = &logs[i];
|
||||
ADVLogList_AddNewPage();
|
||||
struct adv_log *log = current_log();
|
||||
int nr_lines = iarray_read(&r);
|
||||
if (nr_lines <= 0 || nr_lines > 10000) {
|
||||
WARNING("Invalid value for nr_lines[%d]: %d", i, nr_lines);
|
||||
@@ -273,7 +294,6 @@ bool ADVLogList_Load(struct page **data)
|
||||
}
|
||||
log->nr_voices = nr_voices;
|
||||
}
|
||||
nr_logs = nr_pages + 1;
|
||||
|
||||
if (iarray_read(&r) != 1 || r.error)
|
||||
goto error;
|
||||
@@ -283,10 +303,11 @@ error:
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct {
|
||||
unsigned n;
|
||||
struct page **data;
|
||||
} scenes = {0};
|
||||
#define SCENE_BUFFER_SIZE 500 // Maximum in Rance 02
|
||||
static struct page **scenes = NULL;
|
||||
static unsigned scene_first = 0;
|
||||
static unsigned scene_last = 0;
|
||||
|
||||
|
||||
static int scene_struct_no(void)
|
||||
{
|
||||
@@ -309,13 +330,25 @@ bool ADVSceneKeeper_AddADVScene(struct page **page)
|
||||
WARNING("Invalid struct type: %d", (*page)->index);
|
||||
return false;
|
||||
}
|
||||
scenes.data = xrealloc_array(scenes.data, scenes.n, scenes.n+1, sizeof(struct page*));
|
||||
|
||||
if (!scenes) {
|
||||
scenes = xcalloc(SCENE_BUFFER_SIZE, sizeof(struct page*));
|
||||
scene_first = 0;
|
||||
scene_last = 0;
|
||||
} else {
|
||||
scene_last = (scene_last + 1) % SCENE_BUFFER_SIZE;
|
||||
if (scene_last == scene_first) {
|
||||
free_page(scenes[scene_first]);
|
||||
scenes[scene_first] = NULL;
|
||||
scene_first = (scene_first + 1) % SCENE_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// serialize
|
||||
struct iarray_writer out;
|
||||
iarray_init_writer(&out, "SCN");
|
||||
iarray_write_struct(&out, *page);
|
||||
scenes.data[scenes.n++] = iarray_to_page(&out);
|
||||
iarray_write_struct(&out, *page, false);
|
||||
scenes[scene_last] = iarray_to_page(&out);
|
||||
iarray_free_writer(&out);
|
||||
|
||||
return true;
|
||||
@@ -323,17 +356,21 @@ bool ADVSceneKeeper_AddADVScene(struct page **page)
|
||||
|
||||
int ADVSceneKeeper_GetNumofADVScene(void)
|
||||
{
|
||||
return scenes.n;
|
||||
if (!scenes)
|
||||
return 0;
|
||||
return (scene_last - scene_first + SCENE_BUFFER_SIZE) % SCENE_BUFFER_SIZE + 1;
|
||||
}
|
||||
|
||||
bool ADVSceneKeeper_GetADVScene(int index, struct page **page)
|
||||
{
|
||||
if (index < 0 || (unsigned)index >= scenes.n)
|
||||
if (!scenes || index < 0 || (unsigned)index >= ADVSceneKeeper_GetNumofADVScene())
|
||||
return false;
|
||||
|
||||
unsigned i = (scene_first + index) % SCENE_BUFFER_SIZE;
|
||||
|
||||
// deserialize
|
||||
struct iarray_reader in;
|
||||
if (!iarray_init_reader(&in, scenes.data[index], "SCN"))
|
||||
if (!iarray_init_reader(&in, scenes[i], "SCN"))
|
||||
return false;
|
||||
|
||||
if (*page) {
|
||||
@@ -341,18 +378,22 @@ bool ADVSceneKeeper_GetADVScene(int index, struct page **page)
|
||||
free_page(*page);
|
||||
}
|
||||
|
||||
*page = iarray_read_struct(&in, scene_struct_no());
|
||||
*page = iarray_read_struct(&in, scene_struct_no(), false);
|
||||
return !in.error;
|
||||
}
|
||||
|
||||
void ADVSceneKeeper_Clear(void)
|
||||
{
|
||||
for (unsigned i = 0; i < scenes.n; i++) {
|
||||
free_page(scenes.data[i]);
|
||||
if (!scenes)
|
||||
return;
|
||||
for (unsigned i = scene_first;; i = (i + 1) % SCENE_BUFFER_SIZE) {
|
||||
if (scenes[i])
|
||||
free_page(scenes[i]);
|
||||
if (i == scene_last)
|
||||
break;
|
||||
}
|
||||
free(scenes.data);
|
||||
scenes.data = NULL;
|
||||
scenes.n = 0;
|
||||
free(scenes);
|
||||
scenes = NULL;
|
||||
}
|
||||
|
||||
bool ADVSceneKeeper_Save(struct page **iarray)
|
||||
@@ -367,9 +408,11 @@ bool ADVSceneKeeper_Save(struct page **iarray)
|
||||
struct iarray_writer w;
|
||||
iarray_init_writer(&w, "SCS");
|
||||
iarray_write(&w, 0); // version
|
||||
iarray_write(&w, scenes.n);
|
||||
for (unsigned i = 0; i < scenes.n; i++) {
|
||||
iarray_write_array(&w, scenes.data[i]);
|
||||
int nr_scenes = ADVSceneKeeper_GetNumofADVScene();
|
||||
iarray_write(&w, nr_scenes);
|
||||
for (int i = 0; i < nr_scenes; i++) {
|
||||
unsigned idx = (scene_first + i) % SCENE_BUFFER_SIZE;
|
||||
iarray_write_array(&w, scenes[idx]);
|
||||
}
|
||||
*iarray = iarray_to_page(&w);
|
||||
iarray_free_writer(&w);
|
||||
@@ -395,15 +438,35 @@ bool ADVSceneKeeper_Load(struct page **iarray)
|
||||
if (nr_scenes < 0 || nr_scenes > 10000)
|
||||
goto error;
|
||||
|
||||
scenes.n = 0;
|
||||
scenes.data = xcalloc(nr_scenes, sizeof(struct page*));
|
||||
if (nr_scenes == 0)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < nr_scenes; i++) {
|
||||
struct ain_type type = { .data = AIN_ARRAY_INT, .struc = 0, .rank = 0 };
|
||||
scenes.data[scenes.n++] = iarray_read_array(&r, &type);
|
||||
int start_offset = 0;
|
||||
if (nr_scenes > SCENE_BUFFER_SIZE) {
|
||||
start_offset = nr_scenes - SCENE_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
struct ain_type type = { .data = AIN_ARRAY_INT, .struc = 0, .rank = 0 };
|
||||
for (int i = 0; i < start_offset; i++) {
|
||||
struct page *p = iarray_read_array(&r, &type);
|
||||
if (r.error)
|
||||
goto error;
|
||||
free_page(p);
|
||||
}
|
||||
|
||||
int scenes_to_load = nr_scenes - start_offset;
|
||||
if (scenes_to_load > 0) {
|
||||
scenes = xcalloc(SCENE_BUFFER_SIZE, sizeof(struct page*));
|
||||
scene_first = 0;
|
||||
scene_last = scenes_to_load - 1;
|
||||
|
||||
for (int i = 0; i < scenes_to_load; i++) {
|
||||
scenes[i] = iarray_read_array(&r, &type);
|
||||
if (r.error)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
error:
|
||||
ADVSceneKeeper_Clear();
|
||||
@@ -415,7 +478,13 @@ static void AnteaterADVEngine_ModuleFini(void)
|
||||
ADVSceneKeeper_Clear();
|
||||
}
|
||||
|
||||
static void AnteaterADVEngine_ADVLogList_AddText_with_window(struct string **text, possibly_unused int window_no)
|
||||
{
|
||||
ADVLogList_AddText(text);
|
||||
}
|
||||
|
||||
HLL_LIBRARY(AnteaterADVEngine,
|
||||
HLL_EXPORT(_PreLink, AnteaterADVEngine_PreLink),
|
||||
HLL_EXPORT(_ModuleFini, AnteaterADVEngine_ModuleFini),
|
||||
HLL_EXPORT(ADVLogList_Clear, ADVLogList_Clear),
|
||||
HLL_EXPORT(ADVLogList_AddText, ADVLogList_AddText),
|
||||
@@ -438,3 +507,21 @@ HLL_LIBRARY(AnteaterADVEngine,
|
||||
HLL_EXPORT(ADVSceneKeeper_Save, ADVSceneKeeper_Save),
|
||||
HLL_EXPORT(ADVSceneKeeper_Load, ADVSceneKeeper_Load)
|
||||
);
|
||||
|
||||
static struct ain_hll_function *get_fun(int libno, const char *name)
|
||||
{
|
||||
int fno = ain_get_library_function(ain, libno, name);
|
||||
return fno >= 0 ? &ain->libraries[libno].functions[fno] : NULL;
|
||||
}
|
||||
|
||||
static void AnteaterADVEngine_PreLink(void)
|
||||
{
|
||||
struct ain_hll_function *fun;
|
||||
int libno = ain_get_library(ain, "AnteaterADVEngine");
|
||||
assert(libno >= 0);
|
||||
|
||||
fun = get_fun(libno, "ADVLogList_AddText");
|
||||
if (fun && fun->nr_arguments == 2) {
|
||||
static_library_replace(&lib_AnteaterADVEngine, "ADVLogList_AddText", AnteaterADVEngine_ADVLogList_AddText_with_window);
|
||||
}
|
||||
}
|
||||
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
/* Copyright (C) 2025 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"
|
||||
#include "vm/page.h"
|
||||
|
||||
static void check_array(struct page *array)
|
||||
{
|
||||
if (array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->array.rank != 1)
|
||||
VM_ERROR("Not a flat integer array");
|
||||
}
|
||||
|
||||
//void Array_NV_copy(struct page **nArray, int nNum);
|
||||
//void Array_NV_add(struct page **nArray, int nNum);
|
||||
//void Array_NV_sub(struct page **nArray, int nNum);
|
||||
//void Array_NV_mul(struct page **nArray, int nNum);
|
||||
//void Array_NV_div(struct page **nArray, int nNum);
|
||||
//void Array_NV_and(struct page **nArray, int nNum);
|
||||
//void Array_NV_or(struct page **nArray, int nNum);
|
||||
//void Array_NV_xor(struct page **nArray, int nNum);
|
||||
//void Array_NV_min(struct page **nArray, int nNum);
|
||||
//void Array_NV_max(struct page **nArray, int nNum);
|
||||
//void Array_NN_copy(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_add(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_sub(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_mul(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_div(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_and(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_or(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_xor(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_min(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_max(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NS_copy(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_add(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_sub(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_mul(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_div(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_and(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_or(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_xor(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_min(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_max(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_SV_copy(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_add(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_sub(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_mul(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_div(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_and(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_or(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_xor(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_min(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_max(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SN_copy(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_add(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_sub(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_mul(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_div(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_and(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_or(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_xor(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_min(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_max(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SS_copy(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_add(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_sub(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_mul(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_div(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_and(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_or(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_xor(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_min(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_max(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
|
||||
static int Array_NV_eneq(struct page *array, int num)
|
||||
{
|
||||
if (!array)
|
||||
return 0;
|
||||
check_array(array);
|
||||
int count = 0;
|
||||
for (int i = 0; i < array->nr_vars; i++) {
|
||||
if (array->values[i].i == num)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//int Array_NV_enne(struct page *nArray, int nNum);
|
||||
//int Array_NV_enlo(struct page *nArray, int nNum);
|
||||
//int Array_NV_enhi(struct page *nArray, int nNum);
|
||||
//int Array_NV_enra(struct page *nArray, int nMin, int nMax);
|
||||
//int Array_NN_eneq(struct page *nArray, struct page *nArrayS);
|
||||
//int Array_NN_enne(struct page *nArray, struct page *nArrayS);
|
||||
//int Array_NN_enlo(struct page *nArray, struct page *nArrayS);
|
||||
//int Array_NN_enhi(struct page *nArray, struct page *nArrayS);
|
||||
//int Array_NS_eneq(struct page *nArray, struct page *sArray, int nMember);
|
||||
//int Array_NS_enne(struct page *nArray, struct page *sArray, int nMember);
|
||||
//int Array_NS_enlo(struct page *nArray, struct page *sArray, int nMember);
|
||||
//int Array_NS_enhi(struct page *nArray, struct page *sArray, int nMember);
|
||||
//int Array_SV_eneq(struct page *sArray, int nMember, int nNum);
|
||||
//int Array_SV_enne(struct page *sArray, int nMember, int nNum);
|
||||
//int Array_SV_enlo(struct page *sArray, int nMember, int nNum);
|
||||
//int Array_SV_enhi(struct page *sArray, int nMember, int nNum);
|
||||
//int Array_SV_enra(struct page *sArray, int nMember, int nMin, int nMax);
|
||||
//int Array_SN_eneq(struct page *sArray, int nMember, struct page *nArray);
|
||||
//int Array_SN_enne(struct page *sArray, int nMember, struct page *nArray);
|
||||
//int Array_SN_enlo(struct page *sArray, int nMember, struct page *nArray);
|
||||
//int Array_SN_enhi(struct page *sArray, int nMember, struct page *nArray);
|
||||
//int Array_SS_eneq(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//int Array_SS_enne(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//int Array_SS_enlo(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//int Array_SS_enhi(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_NV_cheq(struct page **nArray, int nNum, int nChg);
|
||||
//void Array_NV_chne(struct page **nArray, int nNum, int nChg);
|
||||
//void Array_NV_chlo(struct page **nArray, int nNum, int nChg);
|
||||
//void Array_NV_chhi(struct page **nArray, int nNum, int nChg);
|
||||
//void Array_NV_chra(struct page **nArray, int nMin, int nMax, int nChg);
|
||||
//void Array_NN_cheq(struct page **nArray, struct page *nArrayS, int nChg);
|
||||
//void Array_NN_chne(struct page **nArray, struct page *nArrayS, int nChg);
|
||||
//void Array_NN_chlo(struct page **nArray, struct page *nArrayS, int nChg);
|
||||
//void Array_NN_chhi(struct page **nArray, struct page *nArrayS, int nChg);
|
||||
//void Array_NS_cheq(struct page **nArray, struct page *sArray, int nMember, int nChg);
|
||||
//void Array_NS_chne(struct page **nArray, struct page *sArray, int nMember, int nChg);
|
||||
//void Array_NS_chlo(struct page **nArray, struct page *sArray, int nMember, int nChg);
|
||||
//void Array_NS_chhi(struct page **nArray, struct page *sArray, int nMember, int nChg);
|
||||
//void Array_SV_cheq(struct page **sArray, int nMember, int nNum, int nChg);
|
||||
//void Array_SV_chne(struct page **sArray, int nMember, int nNum, int nChg);
|
||||
//void Array_SV_chlo(struct page **sArray, int nMember, int nNum, int nChg);
|
||||
//void Array_SV_chhi(struct page **sArray, int nMember, int nNum, int nChg);
|
||||
//void Array_SV_chra(struct page **sArray, int nMember, int nMin, int nMax, int nChg);
|
||||
//void Array_SN_cheq(struct page **sArray, int nMember, struct page *nArray, int nChg);
|
||||
//void Array_SN_chne(struct page **sArray, int nMember, struct page *nArray, int nChg);
|
||||
//void Array_SN_chlo(struct page **sArray, int nMember, struct page *nArray, int nChg);
|
||||
//void Array_SN_chhi(struct page **sArray, int nMember, struct page *nArray, int nChg);
|
||||
//void Array_SS_cheq(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS, int nChg);
|
||||
//void Array_SS_chne(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS, int nChg);
|
||||
//void Array_SS_chlo(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS, int nChg);
|
||||
//void Array_SS_chhi(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS, int nChg);
|
||||
//void Array_NV_fweq(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fwne(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fwlo(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fwhi(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fwra(struct page *nArray, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_NV_faeq(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fane(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_falo(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fahi(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fara(struct page *nArray, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_NV_foeq(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fone(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_folo(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fohi(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fora(struct page *nArray, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_NN_fweq(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fwne(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fwlo(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fwhi(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_faeq(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fane(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_falo(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fahi(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_foeq(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fone(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_folo(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fohi(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NS_fweq(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fwne(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fwlo(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fwhi(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_faeq(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fane(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_falo(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fahi(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_foeq(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fone(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_folo(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fohi(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_SV_fweq(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fwne(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fwlo(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fwhi(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fwra(struct page *sArray, int nMember, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_SV_faeq(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fane(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_falo(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fahi(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fara(struct page *sArray, int nMember, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_SV_foeq(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fone(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_folo(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fohi(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fora(struct page *sArray, int nMember, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_SN_fweq(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fwne(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fwlo(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fwhi(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_faeq(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fane(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_falo(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fahi(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_foeq(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fone(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_folo(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fohi(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SS_fweq(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fwne(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fwlo(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fwhi(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_faeq(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fane(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_falo(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fahi(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_foeq(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fone(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_folo(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fohi(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
|
||||
static int Array_NV_sceq(struct page *array, int index, int num, int *out_index)
|
||||
{
|
||||
if (!array)
|
||||
return 0;
|
||||
check_array(array);
|
||||
|
||||
if (index >= 0) {
|
||||
for (int i = index; i < array->nr_vars; i++) {
|
||||
if (array->values[i].i == num) {
|
||||
*out_index = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Negative index means backwards search
|
||||
index &= 0x7fffffff;
|
||||
if (index >= array->nr_vars)
|
||||
return 0;
|
||||
for (int i = index; i >= 0; --i) {
|
||||
if (array->values[i].i == num) {
|
||||
*out_index = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
//int Array_NV_scne(struct page *nArray, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_NV_sclo(struct page *nArray, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_NV_schi(struct page *nArray, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_NV_scra(struct page *nArray, int nIndex, int nMin, int nMax, int *pnIndex);
|
||||
//int Array_SV_sceq(struct page *sArray, int nMember, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_SV_scne(struct page *sArray, int nMember, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_SV_sclo(struct page *sArray, int nMember, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_SV_schi(struct page *sArray, int nMember, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_SV_scra(struct page *sArray, int nMember, int nIndex, int nMin, int nMax, int *pnIndex);
|
||||
//int Array_NN_sclowest(struct page *nArray, struct page **nArrayD, int *pnIndex);
|
||||
//int Array_NN_schighest(struct page *nArray, struct page **nArrayD, int *pnIndex);
|
||||
//int Array_NS_sclowest(struct page *nArray, struct page **sArray, int nMember, int *pnIndex);
|
||||
//int Array_NS_schighest(struct page *nArray, struct page **sArray, int nMember, int *pnIndex);
|
||||
//int Array_SN_sclowest(struct page *sArray, int nMember, struct page **nArray, int *pnIndex);
|
||||
//int Array_SN_schighest(struct page *sArray, int nMember, struct page **nArray, int *pnIndex);
|
||||
//int Array_SS_sclowest(struct page *sArray, int nMember, struct page **sArrayD, int nMemberS, int *pnIndex);
|
||||
//int Array_SS_schighest(struct page *sArray, int nMember, struct page **sArrayD, int nMemberS, int *pnIndex);
|
||||
//int Array_VN_add(struct page *nArray);
|
||||
//int Array_VN_and(struct page *nArray);
|
||||
//int Array_VN_or(struct page *nArray);
|
||||
//int Array_VS_add(struct page *sArray, int nMember);
|
||||
//int Array_VS_and(struct page *sArray, int nMember);
|
||||
//int Array_VS_or(struct page *sArray, int nMember);
|
||||
|
||||
HLL_LIBRARY(Array,
|
||||
HLL_TODO_EXPORT(NV_copy, Array_NV_copy),
|
||||
HLL_TODO_EXPORT(NV_add, Array_NV_add),
|
||||
HLL_TODO_EXPORT(NV_sub, Array_NV_sub),
|
||||
HLL_TODO_EXPORT(NV_mul, Array_NV_mul),
|
||||
HLL_TODO_EXPORT(NV_div, Array_NV_div),
|
||||
HLL_TODO_EXPORT(NV_and, Array_NV_and),
|
||||
HLL_TODO_EXPORT(NV_or, Array_NV_or),
|
||||
HLL_TODO_EXPORT(NV_xor, Array_NV_xor),
|
||||
HLL_TODO_EXPORT(NV_min, Array_NV_min),
|
||||
HLL_TODO_EXPORT(NV_max, Array_NV_max),
|
||||
HLL_TODO_EXPORT(NN_copy, Array_NN_copy),
|
||||
HLL_TODO_EXPORT(NN_add, Array_NN_add),
|
||||
HLL_TODO_EXPORT(NN_sub, Array_NN_sub),
|
||||
HLL_TODO_EXPORT(NN_mul, Array_NN_mul),
|
||||
HLL_TODO_EXPORT(NN_div, Array_NN_div),
|
||||
HLL_TODO_EXPORT(NN_and, Array_NN_and),
|
||||
HLL_TODO_EXPORT(NN_or, Array_NN_or),
|
||||
HLL_TODO_EXPORT(NN_xor, Array_NN_xor),
|
||||
HLL_TODO_EXPORT(NN_min, Array_NN_min),
|
||||
HLL_TODO_EXPORT(NN_max, Array_NN_max),
|
||||
HLL_TODO_EXPORT(NS_copy, Array_NS_copy),
|
||||
HLL_TODO_EXPORT(NS_add, Array_NS_add),
|
||||
HLL_TODO_EXPORT(NS_sub, Array_NS_sub),
|
||||
HLL_TODO_EXPORT(NS_mul, Array_NS_mul),
|
||||
HLL_TODO_EXPORT(NS_div, Array_NS_div),
|
||||
HLL_TODO_EXPORT(NS_and, Array_NS_and),
|
||||
HLL_TODO_EXPORT(NS_or, Array_NS_or),
|
||||
HLL_TODO_EXPORT(NS_xor, Array_NS_xor),
|
||||
HLL_TODO_EXPORT(NS_min, Array_NS_min),
|
||||
HLL_TODO_EXPORT(NS_max, Array_NS_max),
|
||||
HLL_TODO_EXPORT(SV_copy, Array_SV_copy),
|
||||
HLL_TODO_EXPORT(SV_add, Array_SV_add),
|
||||
HLL_TODO_EXPORT(SV_sub, Array_SV_sub),
|
||||
HLL_TODO_EXPORT(SV_mul, Array_SV_mul),
|
||||
HLL_TODO_EXPORT(SV_div, Array_SV_div),
|
||||
HLL_TODO_EXPORT(SV_and, Array_SV_and),
|
||||
HLL_TODO_EXPORT(SV_or, Array_SV_or),
|
||||
HLL_TODO_EXPORT(SV_xor, Array_SV_xor),
|
||||
HLL_TODO_EXPORT(SV_min, Array_SV_min),
|
||||
HLL_TODO_EXPORT(SV_max, Array_SV_max),
|
||||
HLL_TODO_EXPORT(SN_copy, Array_SN_copy),
|
||||
HLL_TODO_EXPORT(SN_add, Array_SN_add),
|
||||
HLL_TODO_EXPORT(SN_sub, Array_SN_sub),
|
||||
HLL_TODO_EXPORT(SN_mul, Array_SN_mul),
|
||||
HLL_TODO_EXPORT(SN_div, Array_SN_div),
|
||||
HLL_TODO_EXPORT(SN_and, Array_SN_and),
|
||||
HLL_TODO_EXPORT(SN_or, Array_SN_or),
|
||||
HLL_TODO_EXPORT(SN_xor, Array_SN_xor),
|
||||
HLL_TODO_EXPORT(SN_min, Array_SN_min),
|
||||
HLL_TODO_EXPORT(SN_max, Array_SN_max),
|
||||
HLL_TODO_EXPORT(SS_copy, Array_SS_copy),
|
||||
HLL_TODO_EXPORT(SS_add, Array_SS_add),
|
||||
HLL_TODO_EXPORT(SS_sub, Array_SS_sub),
|
||||
HLL_TODO_EXPORT(SS_mul, Array_SS_mul),
|
||||
HLL_TODO_EXPORT(SS_div, Array_SS_div),
|
||||
HLL_TODO_EXPORT(SS_and, Array_SS_and),
|
||||
HLL_TODO_EXPORT(SS_or, Array_SS_or),
|
||||
HLL_TODO_EXPORT(SS_xor, Array_SS_xor),
|
||||
HLL_TODO_EXPORT(SS_min, Array_SS_min),
|
||||
HLL_TODO_EXPORT(SS_max, Array_SS_max),
|
||||
HLL_EXPORT(NV_eneq, Array_NV_eneq),
|
||||
HLL_TODO_EXPORT(NV_enne, Array_NV_enne),
|
||||
HLL_TODO_EXPORT(NV_enlo, Array_NV_enlo),
|
||||
HLL_TODO_EXPORT(NV_enhi, Array_NV_enhi),
|
||||
HLL_TODO_EXPORT(NV_enra, Array_NV_enra),
|
||||
HLL_TODO_EXPORT(NN_eneq, Array_NN_eneq),
|
||||
HLL_TODO_EXPORT(NN_enne, Array_NN_enne),
|
||||
HLL_TODO_EXPORT(NN_enlo, Array_NN_enlo),
|
||||
HLL_TODO_EXPORT(NN_enhi, Array_NN_enhi),
|
||||
HLL_TODO_EXPORT(NS_eneq, Array_NS_eneq),
|
||||
HLL_TODO_EXPORT(NS_enne, Array_NS_enne),
|
||||
HLL_TODO_EXPORT(NS_enlo, Array_NS_enlo),
|
||||
HLL_TODO_EXPORT(NS_enhi, Array_NS_enhi),
|
||||
HLL_TODO_EXPORT(SV_eneq, Array_SV_eneq),
|
||||
HLL_TODO_EXPORT(SV_enne, Array_SV_enne),
|
||||
HLL_TODO_EXPORT(SV_enlo, Array_SV_enlo),
|
||||
HLL_TODO_EXPORT(SV_enhi, Array_SV_enhi),
|
||||
HLL_TODO_EXPORT(SV_enra, Array_SV_enra),
|
||||
HLL_TODO_EXPORT(SN_eneq, Array_SN_eneq),
|
||||
HLL_TODO_EXPORT(SN_enne, Array_SN_enne),
|
||||
HLL_TODO_EXPORT(SN_enlo, Array_SN_enlo),
|
||||
HLL_TODO_EXPORT(SN_enhi, Array_SN_enhi),
|
||||
HLL_TODO_EXPORT(SS_eneq, Array_SS_eneq),
|
||||
HLL_TODO_EXPORT(SS_enne, Array_SS_enne),
|
||||
HLL_TODO_EXPORT(SS_enlo, Array_SS_enlo),
|
||||
HLL_TODO_EXPORT(SS_enhi, Array_SS_enhi),
|
||||
HLL_TODO_EXPORT(NV_cheq, Array_NV_cheq),
|
||||
HLL_TODO_EXPORT(NV_chne, Array_NV_chne),
|
||||
HLL_TODO_EXPORT(NV_chlo, Array_NV_chlo),
|
||||
HLL_TODO_EXPORT(NV_chhi, Array_NV_chhi),
|
||||
HLL_TODO_EXPORT(NV_chra, Array_NV_chra),
|
||||
HLL_TODO_EXPORT(NN_cheq, Array_NN_cheq),
|
||||
HLL_TODO_EXPORT(NN_chne, Array_NN_chne),
|
||||
HLL_TODO_EXPORT(NN_chlo, Array_NN_chlo),
|
||||
HLL_TODO_EXPORT(NN_chhi, Array_NN_chhi),
|
||||
HLL_TODO_EXPORT(NS_cheq, Array_NS_cheq),
|
||||
HLL_TODO_EXPORT(NS_chne, Array_NS_chne),
|
||||
HLL_TODO_EXPORT(NS_chlo, Array_NS_chlo),
|
||||
HLL_TODO_EXPORT(NS_chhi, Array_NS_chhi),
|
||||
HLL_TODO_EXPORT(SV_cheq, Array_SV_cheq),
|
||||
HLL_TODO_EXPORT(SV_chne, Array_SV_chne),
|
||||
HLL_TODO_EXPORT(SV_chlo, Array_SV_chlo),
|
||||
HLL_TODO_EXPORT(SV_chhi, Array_SV_chhi),
|
||||
HLL_TODO_EXPORT(SV_chra, Array_SV_chra),
|
||||
HLL_TODO_EXPORT(SN_cheq, Array_SN_cheq),
|
||||
HLL_TODO_EXPORT(SN_chne, Array_SN_chne),
|
||||
HLL_TODO_EXPORT(SN_chlo, Array_SN_chlo),
|
||||
HLL_TODO_EXPORT(SN_chhi, Array_SN_chhi),
|
||||
HLL_TODO_EXPORT(SS_cheq, Array_SS_cheq),
|
||||
HLL_TODO_EXPORT(SS_chne, Array_SS_chne),
|
||||
HLL_TODO_EXPORT(SS_chlo, Array_SS_chlo),
|
||||
HLL_TODO_EXPORT(SS_chhi, Array_SS_chhi),
|
||||
HLL_TODO_EXPORT(NV_fweq, Array_NV_fweq),
|
||||
HLL_TODO_EXPORT(NV_fwne, Array_NV_fwne),
|
||||
HLL_TODO_EXPORT(NV_fwlo, Array_NV_fwlo),
|
||||
HLL_TODO_EXPORT(NV_fwhi, Array_NV_fwhi),
|
||||
HLL_TODO_EXPORT(NV_fwra, Array_NV_fwra),
|
||||
HLL_TODO_EXPORT(NV_faeq, Array_NV_faeq),
|
||||
HLL_TODO_EXPORT(NV_fane, Array_NV_fane),
|
||||
HLL_TODO_EXPORT(NV_falo, Array_NV_falo),
|
||||
HLL_TODO_EXPORT(NV_fahi, Array_NV_fahi),
|
||||
HLL_TODO_EXPORT(NV_fara, Array_NV_fara),
|
||||
HLL_TODO_EXPORT(NV_foeq, Array_NV_foeq),
|
||||
HLL_TODO_EXPORT(NV_fone, Array_NV_fone),
|
||||
HLL_TODO_EXPORT(NV_folo, Array_NV_folo),
|
||||
HLL_TODO_EXPORT(NV_fohi, Array_NV_fohi),
|
||||
HLL_TODO_EXPORT(NV_fora, Array_NV_fora),
|
||||
HLL_TODO_EXPORT(NN_fweq, Array_NN_fweq),
|
||||
HLL_TODO_EXPORT(NN_fwne, Array_NN_fwne),
|
||||
HLL_TODO_EXPORT(NN_fwlo, Array_NN_fwlo),
|
||||
HLL_TODO_EXPORT(NN_fwhi, Array_NN_fwhi),
|
||||
HLL_TODO_EXPORT(NN_faeq, Array_NN_faeq),
|
||||
HLL_TODO_EXPORT(NN_fane, Array_NN_fane),
|
||||
HLL_TODO_EXPORT(NN_falo, Array_NN_falo),
|
||||
HLL_TODO_EXPORT(NN_fahi, Array_NN_fahi),
|
||||
HLL_TODO_EXPORT(NN_foeq, Array_NN_foeq),
|
||||
HLL_TODO_EXPORT(NN_fone, Array_NN_fone),
|
||||
HLL_TODO_EXPORT(NN_folo, Array_NN_folo),
|
||||
HLL_TODO_EXPORT(NN_fohi, Array_NN_fohi),
|
||||
HLL_TODO_EXPORT(NS_fweq, Array_NS_fweq),
|
||||
HLL_TODO_EXPORT(NS_fwne, Array_NS_fwne),
|
||||
HLL_TODO_EXPORT(NS_fwlo, Array_NS_fwlo),
|
||||
HLL_TODO_EXPORT(NS_fwhi, Array_NS_fwhi),
|
||||
HLL_TODO_EXPORT(NS_faeq, Array_NS_faeq),
|
||||
HLL_TODO_EXPORT(NS_fane, Array_NS_fane),
|
||||
HLL_TODO_EXPORT(NS_falo, Array_NS_falo),
|
||||
HLL_TODO_EXPORT(NS_fahi, Array_NS_fahi),
|
||||
HLL_TODO_EXPORT(NS_foeq, Array_NS_foeq),
|
||||
HLL_TODO_EXPORT(NS_fone, Array_NS_fone),
|
||||
HLL_TODO_EXPORT(NS_folo, Array_NS_folo),
|
||||
HLL_TODO_EXPORT(NS_fohi, Array_NS_fohi),
|
||||
HLL_TODO_EXPORT(SV_fweq, Array_SV_fweq),
|
||||
HLL_TODO_EXPORT(SV_fwne, Array_SV_fwne),
|
||||
HLL_TODO_EXPORT(SV_fwlo, Array_SV_fwlo),
|
||||
HLL_TODO_EXPORT(SV_fwhi, Array_SV_fwhi),
|
||||
HLL_TODO_EXPORT(SV_fwra, Array_SV_fwra),
|
||||
HLL_TODO_EXPORT(SV_faeq, Array_SV_faeq),
|
||||
HLL_TODO_EXPORT(SV_fane, Array_SV_fane),
|
||||
HLL_TODO_EXPORT(SV_falo, Array_SV_falo),
|
||||
HLL_TODO_EXPORT(SV_fahi, Array_SV_fahi),
|
||||
HLL_TODO_EXPORT(SV_fara, Array_SV_fara),
|
||||
HLL_TODO_EXPORT(SV_foeq, Array_SV_foeq),
|
||||
HLL_TODO_EXPORT(SV_fone, Array_SV_fone),
|
||||
HLL_TODO_EXPORT(SV_folo, Array_SV_folo),
|
||||
HLL_TODO_EXPORT(SV_fohi, Array_SV_fohi),
|
||||
HLL_TODO_EXPORT(SV_fora, Array_SV_fora),
|
||||
HLL_TODO_EXPORT(SN_fweq, Array_SN_fweq),
|
||||
HLL_TODO_EXPORT(SN_fwne, Array_SN_fwne),
|
||||
HLL_TODO_EXPORT(SN_fwlo, Array_SN_fwlo),
|
||||
HLL_TODO_EXPORT(SN_fwhi, Array_SN_fwhi),
|
||||
HLL_TODO_EXPORT(SN_faeq, Array_SN_faeq),
|
||||
HLL_TODO_EXPORT(SN_fane, Array_SN_fane),
|
||||
HLL_TODO_EXPORT(SN_falo, Array_SN_falo),
|
||||
HLL_TODO_EXPORT(SN_fahi, Array_SN_fahi),
|
||||
HLL_TODO_EXPORT(SN_foeq, Array_SN_foeq),
|
||||
HLL_TODO_EXPORT(SN_fone, Array_SN_fone),
|
||||
HLL_TODO_EXPORT(SN_folo, Array_SN_folo),
|
||||
HLL_TODO_EXPORT(SN_fohi, Array_SN_fohi),
|
||||
HLL_TODO_EXPORT(SS_fweq, Array_SS_fweq),
|
||||
HLL_TODO_EXPORT(SS_fwne, Array_SS_fwne),
|
||||
HLL_TODO_EXPORT(SS_fwlo, Array_SS_fwlo),
|
||||
HLL_TODO_EXPORT(SS_fwhi, Array_SS_fwhi),
|
||||
HLL_TODO_EXPORT(SS_faeq, Array_SS_faeq),
|
||||
HLL_TODO_EXPORT(SS_fane, Array_SS_fane),
|
||||
HLL_TODO_EXPORT(SS_falo, Array_SS_falo),
|
||||
HLL_TODO_EXPORT(SS_fahi, Array_SS_fahi),
|
||||
HLL_TODO_EXPORT(SS_foeq, Array_SS_foeq),
|
||||
HLL_TODO_EXPORT(SS_fone, Array_SS_fone),
|
||||
HLL_TODO_EXPORT(SS_folo, Array_SS_folo),
|
||||
HLL_TODO_EXPORT(SS_fohi, Array_SS_fohi),
|
||||
HLL_EXPORT(NV_sceq, Array_NV_sceq),
|
||||
HLL_TODO_EXPORT(NV_scne, Array_NV_scne),
|
||||
HLL_TODO_EXPORT(NV_sclo, Array_NV_sclo),
|
||||
HLL_TODO_EXPORT(NV_schi, Array_NV_schi),
|
||||
HLL_TODO_EXPORT(NV_scra, Array_NV_scra),
|
||||
HLL_TODO_EXPORT(SV_sceq, Array_SV_sceq),
|
||||
HLL_TODO_EXPORT(SV_scne, Array_SV_scne),
|
||||
HLL_TODO_EXPORT(SV_sclo, Array_SV_sclo),
|
||||
HLL_TODO_EXPORT(SV_schi, Array_SV_schi),
|
||||
HLL_TODO_EXPORT(SV_scra, Array_SV_scra),
|
||||
HLL_TODO_EXPORT(NN_sclowest, Array_NN_sclowest),
|
||||
HLL_TODO_EXPORT(NN_schighest, Array_NN_schighest),
|
||||
HLL_TODO_EXPORT(NS_sclowest, Array_NS_sclowest),
|
||||
HLL_TODO_EXPORT(NS_schighest, Array_NS_schighest),
|
||||
HLL_TODO_EXPORT(SN_sclowest, Array_SN_sclowest),
|
||||
HLL_TODO_EXPORT(SN_schighest, Array_SN_schighest),
|
||||
HLL_TODO_EXPORT(SS_sclowest, Array_SS_sclowest),
|
||||
HLL_TODO_EXPORT(SS_schighest, Array_SS_schighest),
|
||||
HLL_TODO_EXPORT(VN_add, Array_VN_add),
|
||||
HLL_TODO_EXPORT(VN_and, Array_VN_and),
|
||||
HLL_TODO_EXPORT(VN_or, Array_VN_or),
|
||||
HLL_TODO_EXPORT(VS_add, Array_VS_add),
|
||||
HLL_TODO_EXPORT(VS_and, Array_VS_and),
|
||||
HLL_TODO_EXPORT(VS_or, Array_VS_or)
|
||||
);
|
||||
+98
-37
@@ -17,39 +17,101 @@
|
||||
#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 "hll.h"
|
||||
#include "audio.h"
|
||||
#include "iarray.h"
|
||||
#include "savedata.h"
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define MD11_ENCRYPT_KEY 0x18ec03
|
||||
#define MD11_HEADER_SIZE 8
|
||||
|
||||
static const uint8_t md11_signature[] = { 'M', 'D', 0x01, 0x01 };
|
||||
|
||||
static int BanMisc_SaveStruct(struct page *page, struct string *file_name)
|
||||
{
|
||||
// serialize
|
||||
struct iarray_writer out;
|
||||
iarray_init_writer(&out, NULL);
|
||||
iarray_write_struct(&out, page, true);
|
||||
size_t raw_size;
|
||||
uint8_t *raw_buf = iarray_to_buffer(&out, &raw_size);
|
||||
iarray_free_writer(&out);
|
||||
|
||||
// compress
|
||||
unsigned long compressed_size = compressBound(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);
|
||||
free(raw_buf);
|
||||
if (r != Z_OK) {
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// encrypt
|
||||
mt19937_xorcode(compressed, compressed_size, MD11_ENCRYPT_KEY);
|
||||
|
||||
// write
|
||||
char *path = unix_path(file_name->text);
|
||||
FILE *fp = file_open_utf8(path, "w");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open file '%s': %s", display_utf0(path), strerror(errno));
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
free(path);
|
||||
|
||||
cJSON *json = page_to_json(page);
|
||||
char *str = cJSON_Print(json);
|
||||
cJSON_Delete(json);
|
||||
|
||||
bool ok = fputs(str, fp) != EOF;
|
||||
free(str);
|
||||
fclose(fp);
|
||||
bool ok = file_write(path, buf, MD11_HEADER_SIZE + compressed_size);
|
||||
if (!ok) {
|
||||
WARNING("BanMisc.SaveStruct failed (fputs): %s", strerror(errno));
|
||||
return 0;
|
||||
WARNING("Failed to write file '%s': %s", display_utf0(path), strerror(errno));
|
||||
}
|
||||
return 1;
|
||||
free(buf);
|
||||
free(path);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static struct page *load_md11(int struct_type, uint8_t *buf, size_t len)
|
||||
{
|
||||
if (len < MD11_HEADER_SIZE) {
|
||||
WARNING("BanMisc.LoadStruct file too short");
|
||||
return NULL;
|
||||
}
|
||||
unsigned long raw_size = LittleEndian_getDW(buf, 4);
|
||||
if (raw_size % 4 != 0) {
|
||||
WARNING("BanMisc.LoadStruct invalid file format");
|
||||
return NULL;
|
||||
}
|
||||
buf += MD11_HEADER_SIZE;
|
||||
len -= MD11_HEADER_SIZE;
|
||||
|
||||
// decrypt
|
||||
mt19937_xorcode(buf, len, MD11_ENCRYPT_KEY);
|
||||
|
||||
// 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);
|
||||
free(raw_buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// deserialize
|
||||
int nr_vars = raw_size / 4;
|
||||
struct page *tmp_page = alloc_page(ARRAY_PAGE, AIN_ARRAY_INT, nr_vars);
|
||||
tmp_page->array.rank = 1;
|
||||
for (int i = 0; i < nr_vars; i++) {
|
||||
tmp_page->values[i].i = LittleEndian_getDW(raw_buf, i * 4);
|
||||
}
|
||||
struct iarray_reader in;
|
||||
iarray_init_reader(&in, tmp_page, NULL);
|
||||
struct page *page = iarray_read_struct(&in, struct_type, true);
|
||||
free_page(tmp_page);
|
||||
free(raw_buf);
|
||||
return page;
|
||||
}
|
||||
|
||||
static int BanMisc_LoadStruct(struct page **_page, struct string *file_name)
|
||||
@@ -60,31 +122,30 @@ static int BanMisc_LoadStruct(struct page **_page, struct string *file_name)
|
||||
}
|
||||
|
||||
char *path = unix_path(file_name->text);
|
||||
FILE *fp = file_open_utf8(path, "r");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open file '%s': %s", display_utf0(path), strerror(errno));
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
size_t len;
|
||||
uint8_t *buf = file_read(path, &len);
|
||||
free(path);
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long len = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
char *buf = xmalloc(len + 1);
|
||||
buf[len] = '\0';
|
||||
if (fread(buf, len, 1, fp) != 1) {
|
||||
WARNING("BanMisc.LoadStruct failed (fread): %s", strerror(errno));
|
||||
free(buf);
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
cJSON *json = cJSON_Parse(buf);
|
||||
if (!memcmp(buf, md11_signature, sizeof(md11_signature))) {
|
||||
page = load_md11(page->index, buf, len);
|
||||
free(buf);
|
||||
if (!page)
|
||||
return 0;
|
||||
if (*_page) {
|
||||
delete_page_vars(*_page);
|
||||
free_page(*_page);
|
||||
}
|
||||
*_page = page;
|
||||
return 1;
|
||||
}
|
||||
// load from JSON (for backward compatibility)
|
||||
cJSON *json = cJSON_Parse((char *)buf);
|
||||
free(buf);
|
||||
if (!json) {
|
||||
WARNING("BanMisc.LoadStruct failed to parse JSON");
|
||||
WARNING("BanMisc.LoadStruct invalid file format");
|
||||
return 0;
|
||||
}
|
||||
if (!cJSON_IsArray(json)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user