Compare commits
254 Commits
ab3c032313
...
main
Author | SHA1 | Date | |
---|---|---|---|
75890b1a71 | |||
73db3e57dc | |||
2975aa2dfb | |||
6726faf719 | |||
183dfa6be5 | |||
e974194af0 | |||
8607aa48ec | |||
f6600dfbda | |||
bdabd04388 | |||
0e075ec334 | |||
b256fc903a | |||
b52ecaeaa0 | |||
37e46e9a7e | |||
a472e6af52 | |||
66b2f04d9d | |||
90f4097070 | |||
829ff4780c | |||
8e15c9ec3c | |||
474ea84a77 | |||
7b8b9416ba | |||
8ed8158ae6 | |||
48e3a4c233 | |||
56530f9864 | |||
f86f3dd41a | |||
adae6be7e5 | |||
cd3033f9c4 | |||
e11e63f273 | |||
75737b738f | |||
ce2c2513aa | |||
36c0af9953 | |||
826622cd58 | |||
78b6a26de9 | |||
5f7b8bac6d | |||
6d6230c6a1 | |||
c07e16490e | |||
f5e55bb997 | |||
1e6e323fe1 | |||
dbf9599fe5 | |||
923cd81571 | |||
733a1786ab | |||
a03e1d885d | |||
67feb5974a | |||
5be4ed4645 | |||
4a41f47a58 | |||
35bb26705a | |||
13bc71a28d | |||
b97a155de4 | |||
5df80addeb | |||
787977b747 | |||
f90b973d86 | |||
32675c012c | |||
a97515e948 | |||
ed8e826b94 | |||
4e5ff9433c | |||
55829a1bef | |||
119bd52c51 | |||
5abd1ced1c | |||
80db96672d | |||
2f6f7852be | |||
307d5552f6 | |||
5911cbd980 | |||
e47b761a2c | |||
844283c2fb | |||
09eac707c3 | |||
5e89710458 | |||
4bc1feb826 | |||
1c3973c6a2 | |||
da5bdb4fae | |||
ed2afec5a7 | |||
6812c7c13d | |||
8c0f43ec34 | |||
23fbd45564 | |||
a36459397e | |||
5f3920fdba | |||
f57525cea6 | |||
6b2901be28 | |||
9f0d15b9f6 | |||
b46331e08d | |||
d2938da8e2 | |||
9134e51817 | |||
d66eda1894 | |||
a88392b9e9 | |||
05f85062e8 | |||
d5aec5e6e1 | |||
62866d33ae | |||
ce7240d423 | |||
7a38f7bcf3 | |||
affaf7f557 | |||
a223506a5f | |||
98d7d76a42 | |||
814269ab0c | |||
d76ea06470 | |||
dc6b298532 | |||
f25e27b102 | |||
dd4fc45be3 | |||
85e47dd677 | |||
a020b92824 | |||
b6347996f9 | |||
66c525a0d4 | |||
70fab28158 | |||
80a4ae3d0e | |||
bd3b090f6f | |||
6eb0730c52 | |||
a231d650f2 | |||
e15975bfaa | |||
b67bc92857 | |||
991196f7c8 | |||
0b89c90ad7 | |||
3d51c8c48f | |||
21d8e2c5a5 | |||
f805bf3f92 | |||
5228fa7e41 | |||
f044a75ffe | |||
723ccf1126 | |||
6bd3afe9b2 | |||
d90bf4cbe2 | |||
48f34f4623 | |||
9c007f34df | |||
a1f4599efd | |||
4c1a8e087a | |||
a2b1f1820a | |||
85ec8d3366 | |||
7eebc7a2d7 | |||
2b26fad983 | |||
47799deb8b | |||
1cd4bfa638 | |||
9beef7686e | |||
cee344c7c1 | |||
18a76649b9
|
|||
88a4876d91
|
|||
835edd737c | |||
9a486fa912
|
|||
d4ce6ab9ec | |||
24b417c287 | |||
5a83381ae1 | |||
793bd850f6 | |||
29d163216c | |||
ffc3badc50 | |||
bedfe0cdfb | |||
927f284fda | |||
2616549f88 | |||
cc4f7f7417 | |||
f81c583319 | |||
0df0a9226f | |||
af1b9caedc | |||
72d1941091 | |||
8a58336d16 | |||
1818532ec9 | |||
e9f8dbebbf | |||
c81f95e571 | |||
5ba11dc584 | |||
f2aded9046 | |||
d6aaef3f68 | |||
322fbf6bbd | |||
5a7d7433d1 | |||
037548436d | |||
5e27845e55 | |||
3bf8d7bedb | |||
85d7d54eed | |||
8c248cb3fb | |||
145b040a0f | |||
559ff9fedc | |||
990135105a | |||
7040d6f218 | |||
cb88b4bcc5 | |||
8110789b3a | |||
0eadeb7e9d | |||
3d10e1782a | |||
d9df3f9b04 | |||
d9d7072c86 | |||
3733b53cc5 | |||
b6b436e1b7 | |||
02b5ac4cc3 | |||
4efe80bb5a | |||
f3a2dc9063 | |||
53c43a8f34 | |||
507bff6ed8 | |||
00636d65a9 | |||
42253fc58a | |||
c6cbf941a2 | |||
277d1b2e10 | |||
46955a19c1 | |||
9ab3e942cd | |||
c7bb317ead | |||
241e72be1a | |||
f4fccc08c4 | |||
2286cdefeb | |||
00ada15dbc | |||
96b6b7e70b | |||
ccfdfd8a35 | |||
7284bb726a | |||
87b33c2e0c | |||
732a3579b0 | |||
2f629433aa | |||
6d58e964bc | |||
0014458dbb | |||
9112630330 | |||
108810d68a | |||
2c94efb796 | |||
11ec35bc8a | |||
2120f6876c | |||
dfd888a80a | |||
695784301d | |||
98d19495a2 | |||
0929aa9be4 | |||
898c11bbdf | |||
aeabb17f86 | |||
24e8dc052d | |||
3f264ca0ad | |||
a7557fceb4 | |||
bd89c4b938 | |||
7074e7499a | |||
2e29cfcfe2 | |||
74d7190c62 | |||
4b2a22bf3c | |||
630c6fb5d4 | |||
d7c744a6ca | |||
f8d7aa8a07 | |||
ea2bbf5de0 | |||
637343db5b | |||
fedf1b5ef3 | |||
732b61e207 | |||
6cb166522e | |||
916e433753 | |||
458b44d0b0 | |||
8de4a1f09b | |||
ac93d114c9 | |||
37b0f4e3a6 | |||
fad11041bc | |||
bbd654a569 | |||
7c33107585 | |||
f625dde8d1 | |||
166ae43981 | |||
6ef3cf1a3a | |||
d84e5f8610 | |||
6a87119c70 | |||
791ab628ca | |||
20394eed6c | |||
a54657e7be | |||
3bdee30e7b | |||
0e4abaae3c | |||
cf8227d7d3 | |||
f365cff590 | |||
77ff5c7f25 | |||
bf3eb50b55 | |||
6a029b7e79 | |||
fad46137a0 | |||
7a403c766e | |||
b0e718876a | |||
785b507330 | |||
d884a9026f | |||
9ddc5c4a66 | |||
16bd49b42e | |||
dd158dee01 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,13 +2,17 @@
|
|||||||
*
|
*
|
||||||
!*.*
|
!*.*
|
||||||
!*/
|
!*/
|
||||||
|
!bin/*
|
||||||
!Makefile
|
!Makefile
|
||||||
|
!LICENSE
|
||||||
|
!COPYING
|
||||||
|
|
||||||
**/*.exe
|
**/*.exe
|
||||||
**/*.html
|
**/*.html
|
||||||
**/*.js
|
**/*.js
|
||||||
**/*.wasm
|
**/*.wasm
|
||||||
**/*.wasm.map
|
**/*.wasm.map
|
||||||
|
**/*.data
|
||||||
**/*.so
|
**/*.so
|
||||||
**/*.dll
|
**/*.dll
|
||||||
**/*.7z
|
**/*.7z
|
||||||
|
134
CMakeLists.txt
134
CMakeLists.txt
@ -5,6 +5,8 @@ project(townengine LANGUAGES C)
|
|||||||
set(CMAKE_MESSAGE_LOG_LEVEL "WARNING")
|
set(CMAKE_MESSAGE_LOG_LEVEL "WARNING")
|
||||||
set(CMAKE_INSTALL_MESSAGE NEVER)
|
set(CMAKE_INSTALL_MESSAGE NEVER)
|
||||||
|
|
||||||
|
# TODO: test whether webgl 1 is good enough.
|
||||||
|
|
||||||
# SDL dependencies
|
# SDL dependencies
|
||||||
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
|
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
|
||||||
if(NOT EMSCRIPTEN)
|
if(NOT EMSCRIPTEN)
|
||||||
@ -26,19 +28,19 @@ set(TWN_TARGET townengine CACHE INTERNAL "")
|
|||||||
set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
|
set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
|
||||||
|
|
||||||
# feature configuration, set them with -DFEATURE=ON/OFF in cli
|
# feature configuration, set them with -DFEATURE=ON/OFF in cli
|
||||||
option(TWN_FEATURE_DYNLIB_GAME "Enable dynamic library loading support" ON)
|
|
||||||
option(TWN_FEATURE_PUSH_AUDIO "Enable frame based audio push for easy realtime audio" ON)
|
|
||||||
option(TWN_USE_AMALGAM "Enable use of twn_amalgam.c as a single compilation unit" ON)
|
option(TWN_USE_AMALGAM "Enable use of twn_amalgam.c as a single compilation unit" ON)
|
||||||
|
option(TWN_FEATURE_DYNLIB_GAME "Enable dynamic library loading support" ON)
|
||||||
|
set(TWN_OUT_DIR ${CMAKE_SOURCE_DIR} CACHE PATH "Artifact destination")
|
||||||
|
|
||||||
# todo: figure out how to compile for dynamic linking instead
|
# todo: figure out how to compile for dynamic linking instead?
|
||||||
if(EMSCRIPTEN)
|
if(HAIKU OR EMSCRIPTEN)
|
||||||
if(TWN_FEATURE_DYNLIB_GAME)
|
if(TWN_FEATURE_DYNLIB_GAME)
|
||||||
message(WARNING "TWN_FEATURE_DYNLIB_GAME is set, but not supported - it is turned off")
|
message(WARNING "TWN_FEATURE_DYNLIB_GAME is set, but not supported - it is turned off")
|
||||||
set(TWN_FEATURE_DYNLIB_GAME OFF CACHE INTERNAL "")
|
set(TWN_FEATURE_DYNLIB_GAME OFF CACHE INTERNAL "")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HAIKU)
|
if(HAIKU OR EMSCRIPTEN)
|
||||||
if(TWN_SANITIZE)
|
if(TWN_SANITIZE)
|
||||||
message(WARNING "TWN_SANITIZE is set, but not supported - it is turned off")
|
message(WARNING "TWN_SANITIZE is set, but not supported - it is turned off")
|
||||||
set(TWN_SANITIZE OFF CACHE INTERNAL "")
|
set(TWN_SANITIZE OFF CACHE INTERNAL "")
|
||||||
@ -64,23 +66,7 @@ set(PHYSFS_ARCHIVE_7Z OFF CACHE INTERNAL "")
|
|||||||
add_subdirectory(third-party/physfs ${CMAKE_CURRENT_BINARY_DIR}/third-party/physfs SYSTEM)
|
add_subdirectory(third-party/physfs ${CMAKE_CURRENT_BINARY_DIR}/third-party/physfs SYSTEM)
|
||||||
add_subdirectory(third-party/libxm ${CMAKE_CURRENT_BINARY_DIR}/third-party/libxm SYSTEM)
|
add_subdirectory(third-party/libxm ${CMAKE_CURRENT_BINARY_DIR}/third-party/libxm SYSTEM)
|
||||||
|
|
||||||
|
set(TWN_RENDERING_API OPENGL_15)
|
||||||
if(LINUX)
|
|
||||||
set(SYSTEM_SOURCE_FILES
|
|
||||||
src/system/linux/twn_elf.c
|
|
||||||
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_linux_game_object.c>)
|
|
||||||
elseif(WIN32)
|
|
||||||
set(SYSTEM_SOURCE_FILES
|
|
||||||
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_win32_game_object.c>)
|
|
||||||
else()
|
|
||||||
set(SYSTEM_SOURCE_FILES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(EMSCRIPTEN)
|
|
||||||
set(TWN_RENDERING_API WEBGL1)
|
|
||||||
else()
|
|
||||||
set(TWN_RENDERING_API OPENGL_15)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TWN_RENDERING_API MATCHES OPENGL_15)
|
if(TWN_RENDERING_API MATCHES OPENGL_15)
|
||||||
set(SYSTEM_SOURCE_FILES ${SYSTEM_SOURCE_FILES}
|
set(SYSTEM_SOURCE_FILES ${SYSTEM_SOURCE_FILES}
|
||||||
@ -96,14 +82,19 @@ set(TWN_THIRD_PARTY_SOURCE_FILES
|
|||||||
|
|
||||||
set(TWN_NONOPT_SOURCE_FILES
|
set(TWN_NONOPT_SOURCE_FILES
|
||||||
src/twn_stb.c
|
src/twn_stb.c
|
||||||
src/twn_loop.c
|
|
||||||
src/twn_main.c
|
|
||||||
src/twn_context.c include/twn_context.h
|
src/twn_context.c include/twn_context.h
|
||||||
src/twn_audio.c include/twn_audio.h
|
src/twn_audio.c include/twn_audio.h
|
||||||
src/twn_util.c include/twn_util.h
|
src/twn_util.c include/twn_util.h
|
||||||
src/twn_input.c include/twn_input.h
|
src/twn_input.c include/twn_input.h
|
||||||
|
|
||||||
|
src/twn_loop.c src/twn_loop_c.h
|
||||||
src/twn_camera.c src/twn_camera_c.h
|
src/twn_camera.c src/twn_camera_c.h
|
||||||
src/twn_textures.c src/twn_textures_c.h
|
src/twn_textures.c src/twn_textures_c.h
|
||||||
|
src/twn_filewatch.c src/twn_filewatch_c.h
|
||||||
|
src/twn_filewatch.c src/twn_filewatch_c.h
|
||||||
|
src/twn_timer.c src/twn_timer_c.h
|
||||||
|
src/twn_workers.c src/twn_workers_c.h
|
||||||
|
|
||||||
src/rendering/twn_draw.c src/rendering/twn_draw_c.h
|
src/rendering/twn_draw.c src/rendering/twn_draw_c.h
|
||||||
src/rendering/twn_quads.c
|
src/rendering/twn_quads.c
|
||||||
@ -113,12 +104,16 @@ set(TWN_NONOPT_SOURCE_FILES
|
|||||||
src/rendering/twn_triangles.c
|
src/rendering/twn_triangles.c
|
||||||
src/rendering/twn_billboards.c
|
src/rendering/twn_billboards.c
|
||||||
src/rendering/twn_circles.c
|
src/rendering/twn_circles.c
|
||||||
src/rendering/twn_skybox.c)
|
src/rendering/twn_skybox.c
|
||||||
|
src/rendering/twn_models.c
|
||||||
|
src/rendering/twn_lines.c
|
||||||
|
)
|
||||||
|
|
||||||
set(TWN_SOURCE_FILES
|
set(TWN_SOURCE_FILES
|
||||||
$<IF:$<BOOL:${TWN_USE_AMALGAM}>,src/twn_amalgam.c src/twn_stb.c,${TWN_NONOPT_SOURCE_FILES}>
|
$<IF:$<BOOL:${TWN_USE_AMALGAM}>,src/twn_amalgam.c src/twn_stb.c,${TWN_NONOPT_SOURCE_FILES}>
|
||||||
|
|
||||||
# for dynamic load based solution main is compiled in a separate target
|
# for dynamic load based solution main is compiled in a separate target
|
||||||
|
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_dynamic_game_object.c>
|
||||||
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c
|
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c
|
||||||
src/game_object/twn_static_game_object.c>
|
src/game_object/twn_static_game_object.c>
|
||||||
|
|
||||||
@ -141,12 +136,10 @@ set_target_properties(${TWN_TARGET} PROPERTIES
|
|||||||
C_STANDARD_REQUIRED ON
|
C_STANDARD_REQUIRED ON
|
||||||
C_EXTENSIONS ON) # extensions are required by stb_ds.h
|
C_EXTENSIONS ON) # extensions are required by stb_ds.h
|
||||||
|
|
||||||
target_compile_definitions(${TWN_TARGET} PRIVATE $<$<BOOL:${TWN_FEATURE_PUSH_AUDIO}>:TWN_FEATURE_PUSH_AUDIO>)
|
|
||||||
|
|
||||||
# precompile commonly used not-so-small headers
|
# precompile commonly used not-so-small headers
|
||||||
target_precompile_headers(${TWN_TARGET} PRIVATE
|
target_precompile_headers(${TWN_TARGET} PRIVATE
|
||||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include/glad/glad.h>
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include/glad/glad.h>
|
||||||
${SDL2_INCLUDE_DIR}/SDL.h
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:${SDL2_INCLUDE_DIR}/SDL.h>
|
||||||
third-party/physfs/src/physfs.h)
|
third-party/physfs/src/physfs.h)
|
||||||
|
|
||||||
|
|
||||||
@ -158,14 +151,14 @@ function(give_options_without_warnings target)
|
|||||||
-fno-signed-zeros
|
-fno-signed-zeros
|
||||||
-fno-trapping-math
|
-fno-trapping-math
|
||||||
-freciprocal-math
|
-freciprocal-math
|
||||||
|
# TODO: require packaging for web case
|
||||||
$<$<BOOL:${EMSCRIPTEN}>:-sUSE_SDL=2>)
|
$<$<BOOL:${EMSCRIPTEN}>:-sUSE_SDL=2>)
|
||||||
|
|
||||||
set(BUILD_FLAGS_RELEASE
|
set(BUILD_FLAGS_RELEASE
|
||||||
-O3
|
-O3
|
||||||
-flto=$<IF:$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>,thin,auto>
|
-flto=$<IF:$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>,thin,auto>
|
||||||
-mavx -mavx2
|
$<$<STREQUAL:${CMAKE_SYSTEM_PROCESSOR},AMD64>:-sse2 -mavx -mavx2>
|
||||||
-fdata-sections
|
$<$<BOOL:${EMSCRIPTEN}>:-msimd128 -mrelaxed-simd>
|
||||||
-ffunction-sections
|
|
||||||
-funroll-loops
|
-funroll-loops
|
||||||
-fomit-frame-pointer
|
-fomit-frame-pointer
|
||||||
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-s>)
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-s>)
|
||||||
@ -175,8 +168,20 @@ function(give_options_without_warnings target)
|
|||||||
-g3
|
-g3
|
||||||
-gdwarf
|
-gdwarf
|
||||||
-fno-omit-frame-pointer
|
-fno-omit-frame-pointer
|
||||||
$<$<BOOL:${TWN_SANITIZE}>:-fstack-protector-all -fsanitize=undefined -fsanitize=address>
|
$<$<BOOL:${TWN_SANITIZE}>:-fstack-protector-all -fsanitize=undefined -fsanitize=address>)
|
||||||
$<$<BOOL:${EMSCRIPTEN}>:-gsource-map>)
|
|
||||||
|
set(LINK_FLAGS
|
||||||
|
-Bsymbolic-functions
|
||||||
|
|
||||||
|
$<$<BOOL:${EMSCRIPTEN}>:-sLEGACY_GL_EMULATION -sGL_FFP_ONLY -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2
|
||||||
|
-sENVIRONMENT=web -sDEFAULT_TO_CXX=0>
|
||||||
|
$<$<BOOL:${EMSCRIPTEN}>:--preload-file ${TWN_OUT_DIR}/data@data -sALLOW_MEMORY_GROWTH>
|
||||||
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:-Wl,--as-needed>
|
||||||
|
$<$<BOOL:${LINUX}>:-Wl,--hash-style=gnu>)
|
||||||
|
|
||||||
|
set(LINK_FLAGS_RELEASE
|
||||||
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-Wl,--strip-all>
|
||||||
|
${BUILD_FLAGS_RELEASE})
|
||||||
|
|
||||||
if (CMAKE_C_COMPILER_LINKER_ID MATCHES GNU OR CMAKE_C_COMPILER_LINKER_ID MATCHES GNUgold)
|
if (CMAKE_C_COMPILER_LINKER_ID MATCHES GNU OR CMAKE_C_COMPILER_LINKER_ID MATCHES GNUgold)
|
||||||
set(THINLTO_USAGE "-plugin-opt,")
|
set(THINLTO_USAGE "-plugin-opt,")
|
||||||
@ -198,11 +203,9 @@ function(give_options_without_warnings target)
|
|||||||
|
|
||||||
target_link_options(${target} PUBLIC
|
target_link_options(${target} PUBLIC
|
||||||
${BUILD_FLAGS}
|
${BUILD_FLAGS}
|
||||||
# -Wl,--no-undefined # TODO: use later for implementing no-libc
|
${LINK_FLAGS}
|
||||||
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}>
|
$<$<CONFIG:Release>:${LINK_FLAGS_RELEASE}>
|
||||||
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>
|
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>)
|
||||||
-Bsymbolic-functions
|
|
||||||
$<$<BOOL:${LINUX}>:-Wl,--hash-style=gnu>)
|
|
||||||
|
|
||||||
get_target_property(target_type ${target} TYPE)
|
get_target_property(target_type ${target} TYPE)
|
||||||
if (target_type MATCHES SHARED_LIBRARY)
|
if (target_type MATCHES SHARED_LIBRARY)
|
||||||
@ -211,11 +214,14 @@ function(give_options_without_warnings target)
|
|||||||
|
|
||||||
target_link_options(${target} PUBLIC
|
target_link_options(${target} PUBLIC
|
||||||
$<$<CONFIG:Release>:${BUILD_SHARED_LIBRARY_FLAGS_RELEASE}>)
|
$<$<CONFIG:Release>:${BUILD_SHARED_LIBRARY_FLAGS_RELEASE}>)
|
||||||
|
elseif(CMAKE_BUILD_TYPE MATCHES Release)
|
||||||
|
target_compile_options(${target} PUBLIC
|
||||||
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-mllvm=--enable-gvn-hoist>)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(${target} PRIVATE
|
target_compile_definitions(${target} PRIVATE
|
||||||
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>
|
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>
|
||||||
$<$<BOOL:${LINUX}>:_GNU_SOURCE>)
|
_GNU_SOURCE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
@ -228,7 +234,8 @@ function(give_options target)
|
|||||||
-Wno-declaration-after-statement
|
-Wno-declaration-after-statement
|
||||||
-Wno-unsafe-buffer-usage
|
-Wno-unsafe-buffer-usage
|
||||||
-Wno-unused-command-line-argument
|
-Wno-unused-command-line-argument
|
||||||
-Wno-covered-switch-default)
|
-Wno-covered-switch-default
|
||||||
|
-Wno-disabled-macro-expansion)
|
||||||
|
|
||||||
set(WARNING_FLAGS
|
set(WARNING_FLAGS
|
||||||
-Wall
|
-Wall
|
||||||
@ -240,6 +247,7 @@ function(give_options target)
|
|||||||
-Werror=vla
|
-Werror=vla
|
||||||
-Wno-missing-field-initializers
|
-Wno-missing-field-initializers
|
||||||
-Wunused-result
|
-Wunused-result
|
||||||
|
-Wno-pre-c11-compat
|
||||||
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-Wcast-align=strict>)
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-Wcast-align=strict>)
|
||||||
|
|
||||||
target_compile_options(${target} PRIVATE
|
target_compile_options(${target} PRIVATE
|
||||||
@ -256,8 +264,9 @@ function(include_deps target)
|
|||||||
third-party/physfs/extras
|
third-party/physfs/extras
|
||||||
third-party/libxm/include
|
third-party/libxm/include
|
||||||
third-party/stb
|
third-party/stb
|
||||||
third-party/x-watcher
|
third-party/dmon
|
||||||
third-party/tomlc99
|
third-party/tomlc99
|
||||||
|
third-party/fast_obj
|
||||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include>)
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include>)
|
||||||
|
|
||||||
list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TWN_ROOT_DIR}/)
|
list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TWN_ROOT_DIR}/)
|
||||||
@ -271,19 +280,40 @@ endfunction()
|
|||||||
function(link_deps target)
|
function(link_deps target)
|
||||||
target_link_libraries(${target} PUBLIC
|
target_link_libraries(${target} PUBLIC
|
||||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2>
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2>
|
||||||
|
$<$<NOT:$<OR:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>,$<BOOL:${EMSCRIPTEN}>>>:SDL2::SDL2main>
|
||||||
physfs-static
|
physfs-static
|
||||||
xms)
|
xms)
|
||||||
target_include_directories(${target} PUBLIC ${SDL2_INCLUDE_DIRS})
|
target_include_directories(${target} PUBLIC ${SDL2_INCLUDE_DIRS})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
function(use_townengine target sources output_directory)
|
function(put_townengine output_directory)
|
||||||
|
cmake_path(GET TWN_OUT_DIR STEM LAST_ONLY target)
|
||||||
|
|
||||||
|
add_executable(${target} ${TWN_ROOT_DIR}/src/twn_main.c)
|
||||||
|
target_link_options(${target} PRIVATE $<$<BOOL:${LINUX}>:-Wl,-rpath,$ORIGIN/>)
|
||||||
|
set_target_properties(${TWN_TARGET} PROPERTIES
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY ${output_directory})
|
||||||
|
set_target_properties(${target} PROPERTIES
|
||||||
|
OUTPUT_NAME ${target}
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
||||||
|
give_options(${target})
|
||||||
|
include_deps(${target})
|
||||||
|
link_deps(${target})
|
||||||
|
target_link_libraries(${target} PUBLIC ${TWN_TARGET})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
function(use_townengine sources output_directory)
|
||||||
|
cmake_path(GET TWN_OUT_DIR STEM LAST_ONLY target)
|
||||||
|
|
||||||
if(TWN_FEATURE_DYNLIB_GAME)
|
if(TWN_FEATURE_DYNLIB_GAME)
|
||||||
# game shared library, for reloading
|
# game shared library, for reloading
|
||||||
add_library(${target}_game SHARED ${sources})
|
add_library(${target}_game SHARED ${sources})
|
||||||
give_options(${target}_game)
|
give_options(${target}_game)
|
||||||
include_deps(${target}_game)
|
include_deps(${target}_game)
|
||||||
target_link_libraries(${target}_game PUBLIC $<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2> ${TWN_TARGET})
|
target_link_libraries(${target}_game PUBLIC ${TWN_TARGET})
|
||||||
set_target_properties(${target}_game PROPERTIES
|
set_target_properties(${target}_game PROPERTIES
|
||||||
OUTPUT_NAME game
|
OUTPUT_NAME game
|
||||||
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
||||||
@ -304,6 +334,7 @@ function(use_townengine target sources output_directory)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_target_properties(${target} PROPERTIES
|
set_target_properties(${target} PROPERTIES
|
||||||
|
OUTPUT_NAME ${target}
|
||||||
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
||||||
|
|
||||||
@ -312,7 +343,7 @@ function(use_townengine target sources output_directory)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_options(${target} PRIVATE
|
target_compile_options(${target} PRIVATE
|
||||||
$<$<BOOL:${EMSCRIPTEN}>:--shell-file ${TWN_ROOT_DIR}/shell_minimal.html>)
|
$<$<BOOL:${EMSCRIPTEN}>:--shell-file ${TWN_ROOT_DIR}/src/shell_minimal.html>)
|
||||||
|
|
||||||
# system libraries
|
# system libraries
|
||||||
find_library(MATH_LIBRARY m)
|
find_library(MATH_LIBRARY m)
|
||||||
@ -341,5 +372,18 @@ link_deps(twn_third_parties)
|
|||||||
give_options(${TWN_TARGET})
|
give_options(${TWN_TARGET})
|
||||||
include_deps(${TWN_TARGET})
|
include_deps(${TWN_TARGET})
|
||||||
link_deps(${TWN_TARGET})
|
link_deps(${TWN_TARGET})
|
||||||
target_link_libraries(${TWN_TARGET} PUBLIC twn_third_parties)
|
|
||||||
target_include_directories(${TWN_TARGET} PRIVATE ${TWN_ROOT_DIR}/src)
|
target_include_directories(${TWN_TARGET} PRIVATE ${TWN_ROOT_DIR}/src)
|
||||||
|
target_link_libraries(${TWN_TARGET} PUBLIC
|
||||||
|
twn_third_parties
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/font.o)
|
||||||
|
|
||||||
|
# embed resources
|
||||||
|
# TODO: think of a portable way to compress/decompress them
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/font.o
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E env CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} sh bin/prep-embed.sh
|
||||||
|
DEPENDS share/assets/Dernyns256.ttf)
|
||||||
|
|
||||||
|
add_custom_target(asset-compilation ALL DEPENDS
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/font.o)
|
||||||
|
@ -6,11 +6,11 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build)
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
state.h
|
||||||
)
|
)
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[[deps]]
|
[[deps]]
|
||||||
source = "../../../common-data"
|
source = "../../../data"
|
||||||
name = "common-data"
|
name = "common-data"
|
||||||
|
@ -66,11 +66,11 @@ void game_tick(void)
|
|||||||
{ // First tick, initalizing data
|
{ // First tick, initalizing data
|
||||||
// Allocating State struct to store data there
|
// Allocating State struct to store data there
|
||||||
if (!ctx.udata)
|
if (!ctx.udata)
|
||||||
ctx.udata = ccalloc(1, sizeof(State));
|
ctx.udata = calloc(1, sizeof(State));
|
||||||
}
|
}
|
||||||
|
|
||||||
input_action("add_a_bit", CONTROL_LEFT_MOUSE);
|
input_action("add_a_bit", "LCLICK");
|
||||||
input_action("add_a_lot", CONTROL_RIGHT_MOUSE);
|
input_action("add_a_lot", "RCLICK");
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
|
1
apps/demos/crawl/data/assets/LICENSES
Normal file
1
apps/demos/crawl/data/assets/LICENSES
Normal file
@ -0,0 +1 @@
|
|||||||
|
castledoors.png - https://opengameart.org/content/castle-door - CC-BY 3.0
|
BIN
apps/demos/crawl/data/assets/brick.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/brick.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/demos/crawl/data/assets/castledoors.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/castledoors.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/demos/crawl/data/assets/lever.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/lever.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/demos/crawl/data/assets/mossy_rock.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/mossy_rock.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/demos/crawl/data/assets/pebbles.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/pebbles.png
(Stored with Git LFS)
Normal file
Binary file not shown.
37
apps/demos/crawl/data/levels/00.lvl
Normal file
37
apps/demos/crawl/data/levels/00.lvl
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
@map
|
||||||
|
-- Grid of symbols defined by @classes section.
|
||||||
|
#####
|
||||||
|
#.@.#
|
||||||
|
#...#
|
||||||
|
##.######
|
||||||
|
#...X...#
|
||||||
|
#./.#####
|
||||||
|
#####
|
||||||
|
|
||||||
|
|
||||||
|
@classes
|
||||||
|
-- Defines classes under symbols, which could have properties attached.
|
||||||
|
# stone_wall
|
||||||
|
wall_texture : /assets/brick.png
|
||||||
|
solid : 1
|
||||||
|
. stone_floor
|
||||||
|
tile_texture : /assets/mossy_rock.png
|
||||||
|
@ player_spawn
|
||||||
|
unique : 1
|
||||||
|
face : south
|
||||||
|
hold : torch
|
||||||
|
tile_texture : /assets/mossy_rock.png
|
||||||
|
X door
|
||||||
|
open_on_signal : sg_torch0
|
||||||
|
tile_texture : /assets/mossy_rock.png
|
||||||
|
face : horizon
|
||||||
|
face_texture : /assets/castledoors.png
|
||||||
|
/ lever
|
||||||
|
on_interact_emit : sg_torch0
|
||||||
|
tile_texture : /assets/mossy_rock.png
|
||||||
|
face : observer
|
||||||
|
face_texture : /assets/lever.png
|
||||||
|
|
||||||
|
@meta
|
||||||
|
-- Arbitrary sections could be defined with value pairs.
|
||||||
|
description : Test Level! Just two square rooms and a tunnel opened by lever.
|
71
apps/demos/crawl/data/scripts/game.lua
Normal file
71
apps/demos/crawl/data/scripts/game.lua
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
require("level")
|
||||||
|
require("render")
|
||||||
|
|
||||||
|
function lerp(a, b, x)
|
||||||
|
return a + ((b - a) * x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function qlerp(a, b, x)
|
||||||
|
return lerp(a, b, x * x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function game_tick()
|
||||||
|
if ctx.udata == nil then
|
||||||
|
ctx.udata = {
|
||||||
|
level = load_level("levels/00.lvl")
|
||||||
|
}
|
||||||
|
ctx.udata.player = {
|
||||||
|
position = ctx.udata.level.classes.player_spawn.position,
|
||||||
|
position_lerp = ctx.udata.level.classes.player_spawn.position,
|
||||||
|
direction = { x = 1, y = 0, z = 0 },
|
||||||
|
direction_lerp = { x = 1, y = 0, z = 0 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
input_action { control = "A", name = "turn_left" }
|
||||||
|
input_action { control = "D", name = "turn_right" }
|
||||||
|
input_action { control = "W", name = "walk_forward" }
|
||||||
|
input_action { control = "S", name = "walk_backward" }
|
||||||
|
|
||||||
|
if input_action_just_released { name = "turn_left" } then
|
||||||
|
ctx.udata.player.direction = { x = ctx.udata.player.direction.z,
|
||||||
|
y = ctx.udata.player.direction.y,
|
||||||
|
z =-ctx.udata.player.direction.x }
|
||||||
|
end
|
||||||
|
|
||||||
|
if input_action_just_released { name = "turn_right" } then
|
||||||
|
ctx.udata.player.direction = { x =-ctx.udata.player.direction.z,
|
||||||
|
y = ctx.udata.player.direction.y,
|
||||||
|
z = ctx.udata.player.direction.x }
|
||||||
|
end
|
||||||
|
|
||||||
|
local move = { x = 0, y = 0 }
|
||||||
|
if input_action_just_released { name = "walk_forward" } then
|
||||||
|
move = { x = move.x + ctx.udata.player.direction.x, y = move.y + ctx.udata.player.direction.z }
|
||||||
|
end
|
||||||
|
if input_action_just_released { name = "walk_backward" } then
|
||||||
|
move = { x = move.x - ctx.udata.player.direction.x, y = move.y - ctx.udata.player.direction.z }
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctx.udata.level.grid[ctx.udata.player.position.y + move.y][ctx.udata.player.position.x + move.x].solid ~= nil then
|
||||||
|
move = { x = 0, y = 0 }
|
||||||
|
end
|
||||||
|
|
||||||
|
ctx.udata.player.position = { x = ctx.udata.player.position.x + move.x, y = ctx.udata.player.position.y + move.y }
|
||||||
|
|
||||||
|
ctx.udata.player.position_lerp.x = qlerp(ctx.udata.player.position_lerp.x, ctx.udata.player.position.x, ctx.frame_duration * 30)
|
||||||
|
ctx.udata.player.position_lerp.y = qlerp(ctx.udata.player.position_lerp.y, ctx.udata.player.position.y, ctx.frame_duration * 30)
|
||||||
|
ctx.udata.player.direction_lerp.x = qlerp(ctx.udata.player.direction_lerp.x, ctx.udata.player.direction.x, ctx.frame_duration * 40)
|
||||||
|
ctx.udata.player.direction_lerp.z = qlerp(ctx.udata.player.direction_lerp.z, ctx.udata.player.direction.z, ctx.frame_duration * 40)
|
||||||
|
|
||||||
|
draw_camera {
|
||||||
|
position = {
|
||||||
|
x = ctx.udata.player.position_lerp.x + 0.5 - ctx.udata.player.direction.x / 2,
|
||||||
|
y = 0.5,
|
||||||
|
z = ctx.udata.player.position_lerp.y + 0.5 - ctx.udata.player.direction.z / 2,
|
||||||
|
},
|
||||||
|
direction = ctx.udata.player.direction_lerp,
|
||||||
|
}
|
||||||
|
|
||||||
|
render_dungeon(ctx.udata.level)
|
||||||
|
end
|
91
apps/demos/crawl/data/scripts/level.lua
Normal file
91
apps/demos/crawl/data/scripts/level.lua
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
function load_level(file)
|
||||||
|
local f = file_read { file = file }
|
||||||
|
|
||||||
|
local result = {
|
||||||
|
-- templates to fill the grid with
|
||||||
|
classes = {
|
||||||
|
-- predefined empty tile
|
||||||
|
void = { },
|
||||||
|
},
|
||||||
|
-- symbol to class lookup table
|
||||||
|
glossary = {
|
||||||
|
[" "] = "void",
|
||||||
|
},
|
||||||
|
-- grid consists of expanded classes, of size dimensions
|
||||||
|
grid = {},
|
||||||
|
-- map consists of original rows of symbols that the grid is constructed from
|
||||||
|
map = {},
|
||||||
|
-- maximum extends of the map, unspecified tiles are filled with "void"
|
||||||
|
size = { x = 0, y = 0 },
|
||||||
|
}
|
||||||
|
|
||||||
|
-- iterate over lines
|
||||||
|
local section, subsection = "none", "none"
|
||||||
|
local from = 1
|
||||||
|
local start, limit = string.find(f, "\n", from)
|
||||||
|
while start do
|
||||||
|
local line = string.sub(f, from, start - 1)
|
||||||
|
-- skip over
|
||||||
|
if #line == 0 or line:find("^%-%-%s*") then
|
||||||
|
goto skip
|
||||||
|
-- start new section
|
||||||
|
elseif line:find("^@%g+") then
|
||||||
|
section = line:sub(2); subsection = "none"
|
||||||
|
-- decode map one line at a time
|
||||||
|
elseif section == "map" then
|
||||||
|
if result.size.x < #line then
|
||||||
|
result.size.x = #line
|
||||||
|
end
|
||||||
|
result.map[#result.map + 1] = line
|
||||||
|
-- templates to expand
|
||||||
|
elseif section == "classes" then
|
||||||
|
-- properties
|
||||||
|
if line:find("^ %g+") then
|
||||||
|
local _, _, property, value = line:find("^ (%g+)%s?:%s?(.*)")
|
||||||
|
result.classes[subsection][property] = value
|
||||||
|
goto skip
|
||||||
|
end
|
||||||
|
local symbol, classname = line:sub(1,1), line:sub(3)
|
||||||
|
result.classes[classname] = {
|
||||||
|
symbol = symbol,
|
||||||
|
}
|
||||||
|
result.glossary[symbol] = classname
|
||||||
|
subsection = classname
|
||||||
|
elseif section ~= "none" then
|
||||||
|
local _, _, property, value = line:find("^(%g+)%s?:%s?(.*)")
|
||||||
|
if result[section] == nil then
|
||||||
|
result[section] = {}
|
||||||
|
end
|
||||||
|
result[section][property] = value
|
||||||
|
end
|
||||||
|
::skip::
|
||||||
|
from = limit + 1
|
||||||
|
start, limit = string.find(f, "\n", from)
|
||||||
|
end
|
||||||
|
-- post process, expand map to grid
|
||||||
|
for y = 1, #result.map do
|
||||||
|
result.grid[y] = {}
|
||||||
|
for x = 1, result.size.x do
|
||||||
|
-- past defined for line
|
||||||
|
local symbol
|
||||||
|
if x > #result.map[y] then
|
||||||
|
symbol = " "
|
||||||
|
else
|
||||||
|
symbol = result.map[y]:sub(x,x)
|
||||||
|
end
|
||||||
|
local class = result.classes[result.glossary[symbol]]
|
||||||
|
if class["unique"] ~= nil then
|
||||||
|
class.position = { x = x, y = y }
|
||||||
|
end
|
||||||
|
result.grid[y][x] = class
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result.size.y = #result.map
|
||||||
|
|
||||||
|
print(result.meta.description)
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
88
apps/demos/crawl/data/scripts/render.lua
Normal file
88
apps/demos/crawl/data/scripts/render.lua
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
-- if this is too wasteful, one could check nerby tiles to see whether faces could be visible
|
||||||
|
-- more robust solution would be to travel the level from observer point of view
|
||||||
|
function render_dungeon(dungeon)
|
||||||
|
for y = 1, dungeon.size.y do
|
||||||
|
for x = 1, dungeon.size.x do
|
||||||
|
if dungeon.grid[y][x].wall_texture ~= nil then
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].wall_texture,
|
||||||
|
v3 = { x = x, y = 1, z = y },
|
||||||
|
v2 = { x = x, y = 0, z = y },
|
||||||
|
v1 = { x = x + 1, y = 0, z = y },
|
||||||
|
v0 = { x = x + 1, y = 1, z = y },
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].wall_texture,
|
||||||
|
v3 = { x = x + 1, y = 1, z = y },
|
||||||
|
v2 = { x = x + 1, y = 0, z = y },
|
||||||
|
v1 = { x = x + 1, y = 0, z = y + 1 },
|
||||||
|
v0 = { x = x + 1, y = 1, z = y + 1 },
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].wall_texture,
|
||||||
|
v3 = { x = x + 1, y = 1, z = y + 1 },
|
||||||
|
v2 = { x = x + 1, y = 0, z = y + 1 },
|
||||||
|
v1 = { x = x, y = 0, z = y + 1 },
|
||||||
|
v0 = { x = x, y = 1, z = y + 1 },
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].wall_texture,
|
||||||
|
v3 = { x = x, y = 1, z = y + 1 },
|
||||||
|
v2 = { x = x, y = 0, z = y + 1 },
|
||||||
|
v1 = { x = x, y = 0, z = y },
|
||||||
|
v0 = { x = x, y = 1, z = y },
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif dungeon.grid[y][x].tile_texture ~= nil then
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].tile_texture,
|
||||||
|
v0 = { x = x + 1, y = 0, z = y },
|
||||||
|
v1 = { x = x, y = 0, z = y },
|
||||||
|
v2 = { x = x, y = 0, z = y + 1 },
|
||||||
|
v3 = { x = x + 1, y = 0, z = y + 1},
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].tile_texture,
|
||||||
|
v3 = { x = x + 1, y = 1, z = y },
|
||||||
|
v2 = { x = x, y = 1, z = y },
|
||||||
|
v1 = { x = x, y = 1, z = y + 1 },
|
||||||
|
v0 = { x = x + 1, y = 1, z = y + 1},
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
if dungeon.grid[y][x].face_texture ~= nil then
|
||||||
|
if dungeon.grid[y][x].face == "horizon" then
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].face_texture,
|
||||||
|
v3 = { x = x + 1, y = 1, z = y },
|
||||||
|
v2 = { x = x + 1, y = 0, z = y },
|
||||||
|
v1 = { x = x + 1, y = 0, z = y + 1 },
|
||||||
|
v0 = { x = x + 1, y = 1, z = y + 1 },
|
||||||
|
texture_region = { w = 64, h = 64 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].face_texture,
|
||||||
|
v3 = { x = x, y = 1, z = y + 1 },
|
||||||
|
v2 = { x = x, y = 0, z = y + 1 },
|
||||||
|
v1 = { x = x, y = 0, z = y },
|
||||||
|
v0 = { x = x, y = 1, z = y },
|
||||||
|
texture_region = { w = 64, h = 64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif dungeon.grid[y][x].face == "observer" then
|
||||||
|
draw_billboard {
|
||||||
|
texture = dungeon.grid[y][x].face_texture,
|
||||||
|
position = { x = x + 0.5, y = 0.5, z = y + 0.5 },
|
||||||
|
size = { x = 0.5, y = 0.5 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -14,6 +14,7 @@ dev_id = "you"
|
|||||||
# Game runtime details
|
# Game runtime details
|
||||||
[game]
|
[game]
|
||||||
resolution = [ 640, 480 ]
|
resolution = [ 640, 480 ]
|
||||||
|
interpreter = "$TWNROOT/apps/twnlua"
|
||||||
#debug = true
|
#debug = true
|
||||||
|
|
||||||
# Engine tweaks. You probably don't need to change these
|
# Engine tweaks. You probably don't need to change these
|
@ -6,7 +6,7 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build)
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
@ -20,4 +20,4 @@ set(SOURCE_FILES
|
|||||||
scenes/ingame.c scenes/ingame.h
|
scenes/ingame.c scenes/ingame.h
|
||||||
)
|
)
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
use_townengine("${SOURCE_FILES}" ${TWN_OUT_DIR})
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[[deps]]
|
[[deps]]
|
||||||
source = "../../../common-data" # where does it come from, might be an url
|
source = "../../../data" # where does it come from, might be an url
|
||||||
name = "common-data" # should be globally unique
|
name = "common-data" # should be globally unique
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
if (!ctx.udata) {
|
if (!ctx.udata) {
|
||||||
ctx.udata = ccalloc(1, sizeof (State));
|
ctx.udata = calloc(1, sizeof (State));
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
state->ctx = &ctx;
|
state->ctx = &ctx;
|
||||||
@ -22,17 +22,13 @@ void game_tick(void) {
|
|||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
input_action("debug_toggle", CONTROL_BACKSPACE);
|
input_action("debug_toggle", "BACKSPACE");
|
||||||
input_action("debug_dump_atlases", CONTROL_HOME);
|
input_action("debug_dump_atlases", "HOME");
|
||||||
|
|
||||||
if (input_action_just_pressed("debug_toggle")) {
|
if (input_action_just_pressed("debug_toggle")) {
|
||||||
ctx.debug = !ctx.debug;
|
ctx.debug = !ctx.debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_action_just_pressed("debug_dump_atlases")) {
|
|
||||||
textures_dump_atlases();
|
|
||||||
}
|
|
||||||
|
|
||||||
state->scene->tick(state);
|
state->scene->tick(state);
|
||||||
|
|
||||||
/* there's a scene switch pending, we can do it now that the tick is done */
|
/* there's a scene switch pending, we can do it now that the tick is done */
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
|
|
||||||
|
|
||||||
static void update_timers(Player *player) {
|
static void update_timers(Player *player) {
|
||||||
player->jump_air_timer = timer_tick_frames(player->jump_air_timer);
|
player->jump_air_timer = player->jump_air_timer - 1 <= 0 ? 0 : player->jump_air_timer - 1;
|
||||||
player->jump_coyote_timer = timer_tick_frames(player->jump_coyote_timer);
|
player->jump_coyote_timer = player->jump_coyote_timer - 1 <= 0 ? 0 : player->jump_coyote_timer - 1;
|
||||||
player->jump_buffer_timer = timer_tick_frames(player->jump_buffer_timer);
|
player->jump_buffer_timer = player->jump_buffer_timer - 1 <= 0 ? 0 : player->jump_buffer_timer - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ static void calc_collisions_y(Player *player) {
|
|||||||
|
|
||||||
|
|
||||||
Player *player_create(World *world) {
|
Player *player_create(World *world) {
|
||||||
Player *player = cmalloc(sizeof *player);
|
Player *player = malloc(sizeof *player);
|
||||||
|
|
||||||
*player = (Player) {
|
*player = (Player) {
|
||||||
.world = world,
|
.world = world,
|
||||||
|
@ -11,12 +11,17 @@
|
|||||||
static void ingame_tick(State *state) {
|
static void ingame_tick(State *state) {
|
||||||
SceneIngame *scn = (SceneIngame *)state->scene;
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
input_action("player_left", CONTROL_A);
|
input_action("player_left", "A");
|
||||||
input_action("player_right", CONTROL_D);
|
input_action("player_right", "D");
|
||||||
input_action("player_forward", CONTROL_W);
|
input_action("player_forward", "W");
|
||||||
input_action("player_backward", CONTROL_S);
|
input_action("player_backward", "S");
|
||||||
input_action("player_jump", CONTROL_SPACE);
|
input_action("player_jump", "SPACE");
|
||||||
input_action("player_run", CONTROL_LSHIFT);
|
input_action("player_run", "LSHIFT");
|
||||||
|
|
||||||
|
draw_camera_2d((Vec2){ scn->player->rect.x + scn->player->rect.w / 2 - ctx.resolution.x / 2,
|
||||||
|
scn->player->rect.y + scn->player->rect.h / 2 - ctx.resolution.y / 2 },
|
||||||
|
0, 1
|
||||||
|
);
|
||||||
|
|
||||||
world_drawdef(scn->world);
|
world_drawdef(scn->world);
|
||||||
player_calc(scn->player);
|
player_calc(scn->player);
|
||||||
@ -34,7 +39,7 @@ static void ingame_end(State *state) {
|
|||||||
Scene *ingame_scene(State *state) {
|
Scene *ingame_scene(State *state) {
|
||||||
(void)state;
|
(void)state;
|
||||||
|
|
||||||
SceneIngame *new_scene = ccalloc(1, sizeof *new_scene);
|
SceneIngame *new_scene = calloc(1, sizeof *new_scene);
|
||||||
new_scene->base.tick = ingame_tick;
|
new_scene->base.tick = ingame_tick;
|
||||||
new_scene->base.end = ingame_end;
|
new_scene->base.end = ingame_end;
|
||||||
|
|
||||||
|
@ -5,16 +5,15 @@
|
|||||||
|
|
||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
static void title_tick(State *state) {
|
static void title_tick(State *state) {
|
||||||
SceneTitle *scn = (SceneTitle *)state->scene;
|
SceneTitle *scn = (SceneTitle *)state->scene;
|
||||||
(void)scn;
|
(void)scn;
|
||||||
|
|
||||||
input_action("ui_accept", CONTROL_RETURN);
|
input_action("ui_accept", "ENTER");
|
||||||
|
|
||||||
if (input_action_just_pressed("ui_accept")) {
|
if (input_action_just_pressed("ui_accept")) {
|
||||||
switch_to(state, ingame_scene);
|
switch_to(state, ingame_scene);
|
||||||
@ -26,7 +25,7 @@ static void title_tick(State *state) {
|
|||||||
|
|
||||||
/* draw the tick count as an example of dynamic text */
|
/* draw the tick count as an example of dynamic text */
|
||||||
size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1;
|
size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1;
|
||||||
char *text_str = cmalloc(text_str_len);
|
char *text_str = malloc(text_str_len);
|
||||||
snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number);
|
snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number);
|
||||||
|
|
||||||
const char *font = "fonts/kenney-pixel.ttf";
|
const char *font = "fonts/kenney-pixel.ttf";
|
||||||
@ -64,7 +63,7 @@ static void title_end(State *state) {
|
|||||||
Scene *title_scene(State *state) {
|
Scene *title_scene(State *state) {
|
||||||
(void)state;
|
(void)state;
|
||||||
|
|
||||||
SceneTitle *new_scene = ccalloc(1, sizeof *new_scene);
|
SceneTitle *new_scene = calloc(1, sizeof *new_scene);
|
||||||
new_scene->base.tick = title_tick;
|
new_scene->base.tick = title_tick;
|
||||||
new_scene->base.end = title_end;
|
new_scene->base.end = title_end;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ static void drawdef_debug(struct World *world) {
|
|||||||
|
|
||||||
|
|
||||||
struct World *world_create(void) {
|
struct World *world_create(void) {
|
||||||
struct World *world = cmalloc(sizeof *world);
|
struct World *world = malloc(sizeof *world);
|
||||||
|
|
||||||
*world = (struct World) {
|
*world = (struct World) {
|
||||||
.tiles = NULL,
|
.tiles = NULL,
|
||||||
@ -58,9 +58,9 @@ struct World *world_create(void) {
|
|||||||
/* create the tilemap */
|
/* create the tilemap */
|
||||||
/* it simply stores what's in each tile as a 2d array */
|
/* it simply stores what's in each tile as a 2d array */
|
||||||
/* on its own, it's entirely unrelated to drawing or logic */
|
/* on its own, it's entirely unrelated to drawing or logic */
|
||||||
world->tilemap = cmalloc(sizeof *world->tilemap * world->tilemap_height);
|
world->tilemap = malloc(sizeof *world->tilemap * world->tilemap_height);
|
||||||
for (size_t i = 0; i < world->tilemap_height; ++i) {
|
for (size_t i = 0; i < world->tilemap_height; ++i) {
|
||||||
world->tilemap[i] = cmalloc(sizeof **world->tilemap * world->tilemap_width);
|
world->tilemap[i] = malloc(sizeof **world->tilemap * world->tilemap_width);
|
||||||
|
|
||||||
for (size_t j = 0; j < world->tilemap_width; ++j) {
|
for (size_t j = 0; j < world->tilemap_width; ++j) {
|
||||||
world->tilemap[i][j] = TILE_TYPE_VOID;
|
world->tilemap[i][j] = TILE_TYPE_VOID;
|
||||||
@ -81,7 +81,7 @@ struct World *world_create(void) {
|
|||||||
/* the tiles array contains data meant to be used by other logic */
|
/* the tiles array contains data meant to be used by other logic */
|
||||||
/* most importantly, it is used to draw the tiles */
|
/* most importantly, it is used to draw the tiles */
|
||||||
const size_t tile_count = world->tilemap_height * world->tilemap_width;
|
const size_t tile_count = world->tilemap_height * world->tilemap_width;
|
||||||
world->tiles = cmalloc(sizeof *world->tiles * tile_count);
|
world->tiles = malloc(sizeof *world->tiles * tile_count);
|
||||||
update_tiles(world);
|
update_tiles(world);
|
||||||
|
|
||||||
return world;
|
return world;
|
||||||
|
@ -6,15 +6,14 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build)
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
state.h
|
||||||
|
|
||||||
scenes/scene.c scenes/scene.h
|
scenes/scene.c scenes/scene.h
|
||||||
scenes/title.c scenes/title.h
|
|
||||||
scenes/ingame.c scenes/ingame.h
|
scenes/ingame.c scenes/ingame.h
|
||||||
)
|
)
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[[deps]]
|
[[deps]]
|
||||||
source = "../../../common-data" # where does it come from, might be an url
|
source = "../../../data" # where does it come from, might be an url
|
||||||
name = "common-data" # should be globally unique
|
name = "common-data" # should be globally unique
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "scenes/scene.h"
|
#include "scenes/scene.h"
|
||||||
#include "scenes/title.h"
|
|
||||||
#include "scenes/ingame.h"
|
#include "scenes/ingame.h"
|
||||||
|
|
||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
@ -14,7 +13,7 @@
|
|||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
if (!ctx.udata) {
|
if (!ctx.udata) {
|
||||||
ctx.udata = ccalloc(1, sizeof (State));
|
ctx.udata = calloc(1, sizeof (State));
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
state->ctx = &ctx;
|
state->ctx = &ctx;
|
||||||
@ -24,17 +23,13 @@ void game_tick(void) {
|
|||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
input_action("debug_toggle", CONTROL_BACKSPACE);
|
input_action("debug_toggle", "BACKSPACE");
|
||||||
input_action("debug_dump_atlases", CONTROL_HOME);
|
input_action("debug_dump_atlases", "HOME");
|
||||||
|
|
||||||
if (input_action_just_pressed("debug_toggle")) {
|
if (input_action_just_pressed("debug_toggle")) {
|
||||||
ctx.debug = !ctx.debug;
|
ctx.debug = !ctx.debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_action_just_pressed("debug_dump_atlases")) {
|
|
||||||
textures_dump_atlases();
|
|
||||||
}
|
|
||||||
|
|
||||||
state->scene->tick(state);
|
state->scene->tick(state);
|
||||||
|
|
||||||
/* there's a scene switch pending, we can do it now that the tick is done */
|
/* there's a scene switch pending, we can do it now that the tick is done */
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "ingame.h"
|
#include "ingame.h"
|
||||||
#include "title.h"
|
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
|
|
||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
@ -13,21 +12,298 @@
|
|||||||
|
|
||||||
|
|
||||||
#define TERRAIN_FREQUENCY 0.15f
|
#define TERRAIN_FREQUENCY 0.15f
|
||||||
#define TERRAIN_DISTANCE 64
|
#define TERRAIN_RADIUS 128
|
||||||
|
#define GRASS_RADIUS 16
|
||||||
|
#define TERRAIN_DISTANCE (TERRAIN_RADIUS * 2)
|
||||||
#define HALF_TERRAIN_DISTANCE ((float)TERRAIN_DISTANCE / 2)
|
#define HALF_TERRAIN_DISTANCE ((float)TERRAIN_DISTANCE / 2)
|
||||||
#define PLAYER_HEIGHT 0.6f
|
#define PLAYER_HEIGHT 0.6f
|
||||||
|
#define TREE_DENSITY 0.03f
|
||||||
|
|
||||||
|
#define G_CONST 10.0f
|
||||||
|
|
||||||
|
/* TODO: pregenerate grid of levels of detail */
|
||||||
static float heightmap[TERRAIN_DISTANCE][TERRAIN_DISTANCE];
|
static float heightmap[TERRAIN_DISTANCE][TERRAIN_DISTANCE];
|
||||||
|
|
||||||
|
|
||||||
|
/* vehicle sim ! */
|
||||||
|
/* https://www.youtube.com/watch?v=pwbwFdWBkU0 */
|
||||||
|
/* representation is a "jelly" box with spring configuration trying to make it coherent */
|
||||||
|
|
||||||
|
/* == springs == */
|
||||||
|
/* damped spring: F = -kx - cv */
|
||||||
|
/* x = length(p1-p0) - at_rest_length */
|
||||||
|
/* v = dot(v1 - v0, unit(p1-p0)) */
|
||||||
|
/* F = (-kx - cv) * unit(p1-p0) */
|
||||||
|
/* v += (F/m)*t */
|
||||||
|
/* one points gains positive F, other negative F, to come together */
|
||||||
|
|
||||||
|
/* == ground interaction == */
|
||||||
|
/* if point is under terrain, then apply this: */
|
||||||
|
/* x = y difference on point and ground */
|
||||||
|
/* -x(n) = x * normal */
|
||||||
|
/* -v(n) = dot(v, normal) */
|
||||||
|
/* -F = (-kx(n)-cv(n)) * normal */
|
||||||
|
|
||||||
|
/* == friction == */
|
||||||
|
/* v(o)/F(o) are perpendicular to slope (x - x(n)) */
|
||||||
|
/* if v(o) == 0, then */
|
||||||
|
/* -- at rest, static friction overcomes */
|
||||||
|
/* if F(o) <= f(s)*x(n), F(o) = 0 */
|
||||||
|
/* else, F(o) -= f(k) * x(n) */
|
||||||
|
/* else if length(v(o) + (F(o)/m)*t) <= (f(k)*x(n)*t), v(o) = 0 */
|
||||||
|
/* else, F = -unit(v(o)*f(k)*x(n)) */
|
||||||
|
|
||||||
|
#define VEHICLE_MASS 200.0f
|
||||||
|
#define VEHICLE_LENGTH 3.0f
|
||||||
|
#define VEHICLE_WIDTH 1.7f
|
||||||
|
#define VEHICLE_HEIGHT 1.3f
|
||||||
|
/* spring constant */
|
||||||
|
#define VEHICLE_SPRING_K 22000.0f
|
||||||
|
#define VEHICLE_SPRING_K_SHOCK 18000.0f
|
||||||
|
#define VEHICLE_SPRING_GK 70000.0f
|
||||||
|
/* damping constant */
|
||||||
|
#define VEHICLE_SPRING_C 800.0f
|
||||||
|
#define VEHICLE_SPRING_C_SHOCK 500.0f
|
||||||
|
#define VEHICLE_SPRING_GC 100.0f
|
||||||
|
#define VEHICLE_FRICTION_S 1000.0f
|
||||||
|
#define VEHICLE_FRICTION_K 110.0f
|
||||||
|
#define VEHICLE_FRICTION_V 4000.0f
|
||||||
|
|
||||||
|
/* TODO: shock springs, that are more loose, which are used to simulate the wheels */
|
||||||
|
/* initial, ideal corner positions */
|
||||||
|
static const Vec3 vbpi[8] = {
|
||||||
|
[0] = { 0, 0, 0 },
|
||||||
|
[1] = { VEHICLE_LENGTH, 0, 0 },
|
||||||
|
[2] = { VEHICLE_LENGTH, 0, VEHICLE_WIDTH },
|
||||||
|
[3] = { 0, 0, VEHICLE_WIDTH },
|
||||||
|
[4] = { 0, VEHICLE_HEIGHT, 0 },
|
||||||
|
[5] = { VEHICLE_LENGTH, VEHICLE_HEIGHT, 0 },
|
||||||
|
[6] = { VEHICLE_LENGTH, VEHICLE_HEIGHT, VEHICLE_WIDTH },
|
||||||
|
[7] = { 0, VEHICLE_HEIGHT, VEHICLE_WIDTH },
|
||||||
|
};
|
||||||
|
/* corner positions in simulation */
|
||||||
|
static Vec3 vbp[8] = { vbpi[0], vbpi[1], vbpi[2], vbpi[3],
|
||||||
|
vbpi[4], vbpi[5], vbpi[6], vbpi[7], };
|
||||||
|
/* corner velocities */
|
||||||
|
static Vec3 vbv[8];
|
||||||
|
/* springs */
|
||||||
|
static uint8_t vbs[28][2] = {
|
||||||
|
{0, 1}, {4, 5}, {0, 4},
|
||||||
|
{1, 2}, {5, 6}, {1, 5},
|
||||||
|
{2, 3}, {6, 7}, {2, 6},
|
||||||
|
{3, 0}, {7, 4}, {3, 7},
|
||||||
|
|
||||||
|
{0, 2}, {0, 5}, {0, 7},
|
||||||
|
{1, 3}, {1, 6}, {1, 4},
|
||||||
|
{4, 6}, {2, 7}, {2, 5},
|
||||||
|
{5, 7}, {3, 4}, {3, 6},
|
||||||
|
|
||||||
|
{0, 6}, {1, 7}, {2, 4}, {3, 5},
|
||||||
|
};
|
||||||
|
/* ackermann steering geometry */
|
||||||
|
static float vehicle_turning_extend;
|
||||||
|
static float vehicle_turning_speed = 0.12f;
|
||||||
|
static float vehicle_turning_extend_limit = VEHICLE_WIDTH * 1.75f;
|
||||||
|
|
||||||
|
static float height_at(SceneIngame *scn, Vec2 position);
|
||||||
|
static Vec3 normal_at(SceneIngame *scn, Vec2 position);
|
||||||
|
|
||||||
|
|
||||||
|
static inline float clampf(float f, float min, float max) {
|
||||||
|
const float t = f < min ? min : f;
|
||||||
|
return t > max ? max : t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_vehicle(SceneIngame *scn) {
|
||||||
|
for (size_t i = 0; i < 12; ++i)
|
||||||
|
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){255, 255, 255, 255});
|
||||||
|
for (size_t i = 12; i < 24; ++i)
|
||||||
|
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){200, 200, 200, 255});
|
||||||
|
for (size_t i = 24; i < 28; ++i)
|
||||||
|
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){255, 125, 125, 255});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_vehicle(SceneIngame *scn) {
|
||||||
|
/* apply gravity */
|
||||||
|
Vec3 Facc[8] = {0};
|
||||||
|
|
||||||
|
/* steering */
|
||||||
|
bool steered = false;
|
||||||
|
if (input_action_pressed("player_left")) {
|
||||||
|
vehicle_turning_extend -= vehicle_turning_speed;
|
||||||
|
steered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_pressed("player_right")) {
|
||||||
|
vehicle_turning_extend += vehicle_turning_speed;
|
||||||
|
steered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!steered)
|
||||||
|
vehicle_turning_extend -= copysignf(vehicle_turning_speed * 0.9f, vehicle_turning_extend);
|
||||||
|
|
||||||
|
vehicle_turning_extend = clampf(vehicle_turning_extend, -vehicle_turning_extend_limit, vehicle_turning_extend_limit);
|
||||||
|
if (fabsf(vehicle_turning_extend) <= 0.11f)
|
||||||
|
vehicle_turning_extend = 0;
|
||||||
|
|
||||||
|
Vec3 const Fg = { .y = -VEHICLE_MASS * G_CONST };
|
||||||
|
for (size_t i = 0; i < 8; ++i)
|
||||||
|
Facc[i] = vec3_add(Facc[i], Fg);
|
||||||
|
|
||||||
|
/* apply springs */
|
||||||
|
for (size_t i = 0; i < 28; ++i) {
|
||||||
|
Vec3 const p0 = vbp[vbs[i][0]];
|
||||||
|
Vec3 const p1 = vbp[vbs[i][1]];
|
||||||
|
Vec3 const v0 = vbv[vbs[i][0]];
|
||||||
|
Vec3 const v1 = vbv[vbs[i][1]];
|
||||||
|
Vec3 const pd = vec3_sub(p1, p0);
|
||||||
|
Vec3 const pn = vec3_norm(pd);
|
||||||
|
/* TODO: length at rest could be precalculated */
|
||||||
|
float const lar = vec3_length(vec3_sub(vbpi[vbs[i][1]], vbpi[vbs[i][0]]));
|
||||||
|
float const x = vec3_length(pd) - lar;
|
||||||
|
float const v = vec3_dot(vec3_sub(v1, v0), pn);
|
||||||
|
float const spring_k = i == 2 | i == 5 || i == 8 || i == 11 ? VEHICLE_SPRING_K_SHOCK : VEHICLE_SPRING_K;
|
||||||
|
float const spring_c = i == 2 | i == 5 || i == 8 || i == 11 ? VEHICLE_SPRING_C_SHOCK : VEHICLE_SPRING_C;
|
||||||
|
Vec3 const Fs = vec3_scale(pn, -spring_k * x - spring_c * v);
|
||||||
|
Facc[vbs[i][0]] = vec3_sub(Facc[vbs[i][0]], Fs);
|
||||||
|
Facc[vbs[i][1]] = vec3_add(Facc[vbs[i][1]], Fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* spring and friction against the ground */
|
||||||
|
for (size_t i = 0; i < 8; ++i) {
|
||||||
|
Vec3 const p = vbp[i];
|
||||||
|
Vec3 const v = vbv[i];
|
||||||
|
float const h = height_at(scn, (Vec2){ p.x, p.z });
|
||||||
|
Vec3 const fwd = vec3_norm(vec3_sub(vbp[1], vbp[0]));
|
||||||
|
if (h >= p.y) {
|
||||||
|
/* back wheel processing: acceleration */
|
||||||
|
if (i == 0 || i == 3) {
|
||||||
|
float scale = 0;
|
||||||
|
if (scn->camera_mode == 2 && input_action_pressed("player_forward"))
|
||||||
|
scale += 1;
|
||||||
|
if (scn->camera_mode == 2 && input_action_pressed("player_backward"))
|
||||||
|
scale -= 1;
|
||||||
|
Facc[i] = vec3_add(Facc[i], vec3_scale(fwd, 6500 * scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* normal force, for displacement */
|
||||||
|
Vec3 const n = normal_at(scn, (Vec2){ p.x, p.z });
|
||||||
|
float const xn = (h - p.y) * n.y;
|
||||||
|
float const vn = vec3_dot(v, n);
|
||||||
|
Vec3 const Fn = vec3_scale(n, -VEHICLE_SPRING_GK * xn - VEHICLE_SPRING_GC * vn);
|
||||||
|
Facc[i] = vec3_sub(Facc[i], Fn);
|
||||||
|
|
||||||
|
/* friction force, perpendicular to normal force */
|
||||||
|
/* TODO: is it right? aren't vn+vol should be = |v| */
|
||||||
|
Vec3 const von = vec3_norm(vec3_cross(n, vec3_cross(v, n)));
|
||||||
|
Vec3 const vo = vec3_scale(vec3_scale(von, vec3_length(v) - vn), -1);
|
||||||
|
float const vol = vec3_length(vo);
|
||||||
|
Vec3 const Fon = vec3_norm(vec3_cross(n, vec3_cross(Facc[i], n)));
|
||||||
|
Vec3 Fo = vec3_scale(vec3_scale(Fon, vec3_length(Facc[i]) - vec3_dot(Facc[i], n)), -1);
|
||||||
|
/* portion of total force along the surface */
|
||||||
|
float const Fol = vec3_length(Fo);
|
||||||
|
float const fkxn = VEHICLE_FRICTION_K * xn;
|
||||||
|
/* at rest, might want to start moving */
|
||||||
|
if (fabsf(0.0f - vol) <= 0.0001f) {
|
||||||
|
/* cannot overcome static friction, force along the surface is zeroed */
|
||||||
|
if (Fol <= VEHICLE_FRICTION_S * xn) { Fo = vec3_scale(Fo, -1);}
|
||||||
|
/* resist the force by friction, while starting to move */
|
||||||
|
else { Fo = vec3_sub(Fo, vec3_scale(von, fkxn));}
|
||||||
|
/* not at rest, stop accelerating along the surface */
|
||||||
|
} else if (vol + (Fol / VEHICLE_MASS) * ctx.frame_duration <= fkxn * ctx.frame_duration * 2) {
|
||||||
|
/* ugh ... */
|
||||||
|
vbv[i] = vec3_add(v, vo);
|
||||||
|
/* just apply friction */
|
||||||
|
} else {
|
||||||
|
Fo = vec3_scale(von, -fkxn * 400);
|
||||||
|
}
|
||||||
|
Facc[i] = vec3_add(Facc[i], Fo);
|
||||||
|
|
||||||
|
/* rear wheel friction */
|
||||||
|
if (i == 0 || i == 3) {
|
||||||
|
Vec3 const pn = vec3_cross(fwd, n);
|
||||||
|
Vec3 const Fp = vec3_scale(pn, vec3_dot(v, pn) * -VEHICLE_FRICTION_V);
|
||||||
|
Facc[i] = vec3_add(Facc[i], Fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* front wheel processing */
|
||||||
|
if (i == 1 || i == 2) {
|
||||||
|
/* steering influences "center of turning", which is a point */
|
||||||
|
/* laying on line defined by rear axle */
|
||||||
|
/* front arms are rotated to be perpendicular to center of turning, */
|
||||||
|
/* which then are used to dissipate forces, thus providing control */
|
||||||
|
Vec3 const rear_bar = vec3_sub(vbp[0], vbp[3]);
|
||||||
|
Vec3 const rear_center = vec3_scale(vec3_add(vbp[0], vbp[3]), 0.5);
|
||||||
|
Vec3 a, b, r;
|
||||||
|
if (i == 1) {
|
||||||
|
a = vec3_sub(vbp[3], vbp[2]);
|
||||||
|
b = vec3_sub(rear_center, vbp[2]);
|
||||||
|
r = vbp[2];
|
||||||
|
} else {
|
||||||
|
a = vec3_sub(vbp[0], vbp[1]);
|
||||||
|
b = vec3_sub(rear_center, vbp[1]);
|
||||||
|
r = vbp[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
float const arm_angle = vec3_angle(a, b);
|
||||||
|
Vec3 const turn_center = vec3_add(rear_center, vec3_scale(vec3_norm(rear_bar), vehicle_turning_extend));
|
||||||
|
Vec3 const arm = vec3_sub(r, turn_center);
|
||||||
|
Vec3 const n = vec3_norm(vec3_cross(a, b));
|
||||||
|
Vec3 const wheel = vec3_norm(vec3_rotate(arm, -arm_angle, n));
|
||||||
|
Vec3 const p = vec3_norm(vec3_cross(wheel, n));
|
||||||
|
draw_line_3d(r, vec3_add(r, p), 1, (Color){0,255,255,255});
|
||||||
|
|
||||||
|
Vec3 const Fp = vec3_scale(p, vec3_dot(v, p) * -VEHICLE_FRICTION_V);
|
||||||
|
Facc[i] = vec3_add(Facc[i], Fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 vd = vec3_scale(vec3_scale(Facc[i], (1.0f / VEHICLE_MASS)), ctx.frame_duration);
|
||||||
|
vbv[i] = vec3_add(vbv[i], vd);
|
||||||
|
vbp[i] = vec3_add(vbp[i], vec3_scale(vbv[i], ctx.frame_duration));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_vehicle_mode(State *state) {
|
||||||
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
|
Vec3 const top_center = vec3_sub(vbp[4], vec3_scale(vec3_sub(vbp[4], vbp[6]), 1.0f / 2.0f));
|
||||||
|
// Vec3 const front_center = vec3_add(vbp[4], vec3_scale(vec3_sub(vbp[4], vbp[7]), 1.0f / 2.0f));
|
||||||
|
// Vec3 const facing_direction = vec3_sub(top_center, front_center);
|
||||||
|
|
||||||
|
float yawc, yaws, pitchc, pitchs;
|
||||||
|
sincosf(scn->yaw + (float)M_PI_2, &yaws, &yawc);
|
||||||
|
sincosf(scn->pitch, &pitchs, &pitchc);
|
||||||
|
|
||||||
|
Vec3 const looking_direction = vec3_norm(((Vec3){
|
||||||
|
yawc * pitchc,
|
||||||
|
pitchs,
|
||||||
|
yaws * pitchc,
|
||||||
|
}));
|
||||||
|
|
||||||
|
Vec3 const orbit = vec3_sub(top_center, vec3_scale(looking_direction, 7.5));
|
||||||
|
|
||||||
|
draw_camera(orbit, looking_direction, (Vec3){0,1,0}, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
|
||||||
|
|
||||||
|
scn->looking_direction = looking_direction;
|
||||||
|
|
||||||
|
scn->pos = top_center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void process_fly_mode(State *state) {
|
static void process_fly_mode(State *state) {
|
||||||
SceneIngame *scn = (SceneIngame *)state->scene;
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
DrawCameraFromPrincipalAxesResult dir_and_up =
|
DrawCameraFromPrincipalAxesResult dir_and_up =
|
||||||
draw_camera_from_principal_axes(scn->pos, (float)M_PI_2 * 0.8f, scn->roll, scn->pitch, scn->yaw);
|
draw_camera_from_principal_axes(scn->pos, scn->roll, scn->pitch, scn->yaw, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
|
||||||
|
|
||||||
|
scn->looking_direction = dir_and_up.direction;
|
||||||
|
|
||||||
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
|
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
|
||||||
const float speed = 0.04f; /* TODO: put this in a better place */
|
const float speed = 0.1f; /* TODO: put this in a better place */
|
||||||
if (input_action_pressed("player_left"))
|
if (input_action_pressed("player_left"))
|
||||||
scn->pos = vec3_sub(scn->pos, m_vec_scale(right, speed));
|
scn->pos = vec3_sub(scn->pos, m_vec_scale(right, speed));
|
||||||
|
|
||||||
@ -47,33 +323,39 @@ static void process_fly_mode(State *state) {
|
|||||||
scn->pos.y -= speed;
|
scn->pos.y -= speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: could be baked in map format */
|
||||||
|
static Vec3 normal_at(SceneIngame *scn, Vec2 position) {
|
||||||
|
int const x = (int)(floorf(position.x - scn->world_center.x));
|
||||||
|
int const y = (int)(floorf(position.y - scn->world_center.y));
|
||||||
|
|
||||||
|
float const height0 = heightmap[x][y];
|
||||||
|
float const height1 = heightmap[x + 1][y];
|
||||||
|
float const height2 = heightmap[x][y + 1];
|
||||||
|
|
||||||
|
Vec3 const a = { .x = 1, .y = height0 - height1, .z = 0 };
|
||||||
|
Vec3 const b = { .x = 0, .y = height0 - height2, .z = -1 };
|
||||||
|
|
||||||
|
return vec3_norm(vec3_cross(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: don't operate on triangles, instead interpolate on quads */
|
||||||
static float height_at(SceneIngame *scn, Vec2 position) {
|
static float height_at(SceneIngame *scn, Vec2 position) {
|
||||||
float height0, height1, height2, weight0, weight1, weight2;
|
int const x = (int)(floorf(position.x - scn->world_center.x));
|
||||||
|
int const y = (int)(floorf(position.y - scn->world_center.y));
|
||||||
|
|
||||||
int const x = (int)(HALF_TERRAIN_DISTANCE + (position.x - scn->pos.x));
|
float const height0 = heightmap[x][y];
|
||||||
int const y = (int)(HALF_TERRAIN_DISTANCE + (position.y - scn->pos.z));
|
float const height1 = heightmap[x + 1][y];
|
||||||
|
float const height2 = heightmap[x][y + 1];
|
||||||
height0 = heightmap[x][y];
|
float const height3 = heightmap[x + 1][y + 1];
|
||||||
height1 = heightmap[x + 1][y + 1];
|
|
||||||
|
|
||||||
Vec2 incell = { position.x - floorf(position.x), position.y - floorf(position.y) };
|
Vec2 incell = { position.x - floorf(position.x), position.y - floorf(position.y) };
|
||||||
|
|
||||||
/* who needs barycentric coordinates, am i right? */
|
float const weight0 = (1 - incell.x) * (1 - incell.y);
|
||||||
weight0 = 1 / sqrtf(powf(incell.x, 2) + powf(incell.y, 2));
|
float const weight1 = ( incell.x) * (1 - incell.y);
|
||||||
weight1 = 1 / sqrtf(powf(1 - incell.x, 2) + powf(1 - incell.y, 2));
|
float const weight2 = (1 - incell.x) * ( incell.y);
|
||||||
|
float const weight3 = ( incell.x) * ( incell.y);
|
||||||
|
|
||||||
/* find which triangle we're directly under */
|
return (height0 * weight0 + height1 * weight1 + height2 * weight2 + height3 * weight3) / (weight0 + weight1 + weight2 + weight3);
|
||||||
/* for this manhattan distance is sufficient */
|
|
||||||
if (incell.x + (1 - incell.y) < (1 - incell.x) + incell.y) {
|
|
||||||
height2 = heightmap[x][y + 1];
|
|
||||||
weight2 = 1 / sqrtf(powf(incell.x, 2) + powf(1 - incell.y, 2));
|
|
||||||
} else {
|
|
||||||
height2 = heightmap[x + 1][y];
|
|
||||||
weight2 = 1 / sqrtf(powf(1 - incell.x, 2) + powf(incell.y, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (height0 * weight0 + height1 * weight1 + height2 * weight2) / (weight0 + weight1 + weight2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,13 +363,15 @@ static void process_ground_mode(State *state) {
|
|||||||
SceneIngame *scn = (SceneIngame *)state->scene;
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
DrawCameraFromPrincipalAxesResult dir_and_up =
|
DrawCameraFromPrincipalAxesResult dir_and_up =
|
||||||
draw_camera_from_principal_axes(scn->pos, (float)M_PI_2 * 0.8f, scn->roll, scn->pitch, scn->yaw);
|
draw_camera_from_principal_axes(scn->pos, scn->roll, scn->pitch, scn->yaw, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
|
||||||
|
|
||||||
|
scn->looking_direction = dir_and_up.direction;
|
||||||
|
|
||||||
dir_and_up.direction.y = 0;
|
dir_and_up.direction.y = 0;
|
||||||
dir_and_up.direction = vec3_norm(dir_and_up.direction);
|
dir_and_up.direction = vec3_norm(dir_and_up.direction);
|
||||||
|
|
||||||
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
|
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
|
||||||
const float speed = 0.18f; /* TODO: put this in a better place */
|
const float speed = 0.20f; /* TODO: put this in a better place */
|
||||||
|
|
||||||
Vec3 target = scn->pos;
|
Vec3 target = scn->pos;
|
||||||
|
|
||||||
@ -96,7 +380,7 @@ static void process_ground_mode(State *state) {
|
|||||||
float const height = height_at(scn, (Vec2){scn->pos.x, scn->pos.z});
|
float const height = height_at(scn, (Vec2){scn->pos.x, scn->pos.z});
|
||||||
|
|
||||||
if (target.y > height + PLAYER_HEIGHT)
|
if (target.y > height + PLAYER_HEIGHT)
|
||||||
target.y = target.y - 0.4f;
|
target.y = target.y - 0.6f;
|
||||||
|
|
||||||
if (target.y < height + PLAYER_HEIGHT)
|
if (target.y < height + PLAYER_HEIGHT)
|
||||||
target.y = height + PLAYER_HEIGHT;
|
target.y = height + PLAYER_HEIGHT;
|
||||||
@ -135,50 +419,100 @@ static void generate_terrain(SceneIngame *scn) {
|
|||||||
float y = floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly);
|
float y = floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly);
|
||||||
|
|
||||||
float height = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 1;
|
float height = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 1;
|
||||||
height += stb_perlin_noise3((float)x * TERRAIN_FREQUENCY / 10, (float)y * TERRAIN_FREQUENCY / 10, 0, 0, 0, 0) * 10 - 1;
|
height += stb_perlin_noise3((float)x * TERRAIN_FREQUENCY / 10, (float)y * TERRAIN_FREQUENCY / 10, 0, 0, 0, 0) * 20 - 1;
|
||||||
|
|
||||||
heightmap[lx][ly] = height;
|
heightmap[lx][ly] = height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scn->world_center = (Vec2){ floorf(scn->pos.x - HALF_TERRAIN_DISTANCE), floorf(scn->pos.z - HALF_TERRAIN_DISTANCE) };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t ceil_sqrt(int32_t const n) {
|
||||||
|
int32_t res = 1;
|
||||||
|
while(res * res < n)
|
||||||
|
res++;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32_t adler32(const void *buf, size_t buflength) {
|
||||||
|
const uint8_t *buffer = (const uint8_t*)buf;
|
||||||
|
|
||||||
|
uint32_t s1 = 1;
|
||||||
|
uint32_t s2 = 0;
|
||||||
|
|
||||||
|
for (size_t n = 0; n < buflength; n++) {
|
||||||
|
s1 = (s1 + buffer[n]) % 65521;
|
||||||
|
s2 = (s2 + s1) % 65521;
|
||||||
|
}
|
||||||
|
return (s2 << 16) | s1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void draw_terrain(SceneIngame *scn) {
|
static void draw_terrain(SceneIngame *scn) {
|
||||||
for (int ly = TERRAIN_DISTANCE - 1; ly > 0; ly--) {
|
/* used to cull invisible tiles over field of view (to horizon) */
|
||||||
for (int lx = 0; lx < TERRAIN_DISTANCE - 1; lx++) {
|
Vec2 const d = vec2_norm((Vec2){ .x = scn->looking_direction.x, .y = scn->looking_direction.z });
|
||||||
int32_t x = (int32_t)(floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx));
|
float const c = cosf((float)M_PI_2 * 0.8f * 0.8f);
|
||||||
int32_t y = (int32_t)(floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly));
|
|
||||||
|
/* draw terrain in circle */
|
||||||
|
int32_t const rsi = (int32_t)TERRAIN_RADIUS * (int32_t)TERRAIN_RADIUS;
|
||||||
|
for (int32_t iy = -(int32_t)TERRAIN_RADIUS; iy <= (int32_t)TERRAIN_RADIUS - 1; ++iy) {
|
||||||
|
int32_t const dx = ceil_sqrt(rsi - (iy + (iy <= 0)) * (iy + (iy <= 0)));
|
||||||
|
for (int32_t ix = -dx; ix < dx - 1; ++ix) {
|
||||||
|
int32_t lx = ix + TERRAIN_RADIUS;
|
||||||
|
int32_t ly = iy + TERRAIN_RADIUS;
|
||||||
|
|
||||||
|
float x = (float)(floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx));
|
||||||
|
float y = (float)(floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly));
|
||||||
|
|
||||||
|
/* cull tiles outside of vision */
|
||||||
|
if (vec2_dot(vec2_norm((Vec2){x - scn->pos.x + d.x * 2, y - scn->pos.z + d.y * 2}), d) < c)
|
||||||
|
continue;
|
||||||
|
|
||||||
float d0 = heightmap[lx][ly];
|
float d0 = heightmap[lx][ly];
|
||||||
float d1 = heightmap[lx + 1][ly];
|
float d1 = heightmap[lx + 1][ly];
|
||||||
float d2 = heightmap[lx + 1][ly - 1];
|
float d2 = heightmap[lx + 1][ly - 1];
|
||||||
float d3 = heightmap[lx][ly - 1];
|
float d3 = heightmap[lx][ly - 1];
|
||||||
|
|
||||||
draw_triangle("/assets/grass.png",
|
draw_quad("/assets/grass2.png",
|
||||||
(Vec3){ (float)x, d0, (float)y },
|
(Vec3){ (float)x, d0, (float)y },
|
||||||
(Vec3){ (float)x + 1, d1, (float)y },
|
|
||||||
(Vec3){ (float)x, d3, (float)y - 1 },
|
|
||||||
(Vec2){ 128, 128 },
|
|
||||||
(Vec2){ 128, 0 },
|
|
||||||
(Vec2){ 0, 128 },
|
|
||||||
(Color){255, 255, 255, 255},
|
|
||||||
(Color){255, 255, 255, 255},
|
|
||||||
(Color){255, 255, 255, 255});
|
|
||||||
|
|
||||||
draw_triangle("/assets/grass.png",
|
|
||||||
(Vec3){ (float)x + 1, d1, (float)y },
|
(Vec3){ (float)x + 1, d1, (float)y },
|
||||||
(Vec3){ (float)x + 1, d2, (float)y - 1 },
|
(Vec3){ (float)x + 1, d2, (float)y - 1 },
|
||||||
(Vec3){ (float)x, d3, (float)y - 1 },
|
(Vec3){ (float)x, d3, (float)y - 1 },
|
||||||
(Vec2){ 128, 0 },
|
(Rect){ .w = 128, .h = 128 },
|
||||||
(Vec2){ 0, 0 },
|
|
||||||
(Vec2){ 0, 128 },
|
|
||||||
(Color){255, 255, 255, 255},
|
|
||||||
(Color){255, 255, 255, 255},
|
|
||||||
(Color){255, 255, 255, 255});
|
(Color){255, 255, 255, 255});
|
||||||
|
|
||||||
draw_billboard("/assets/grasses/10.png",
|
if (((float)(adler32(&((Vec2){x, y}), sizeof (Vec2)) % 100) / 100) <= TREE_DENSITY)
|
||||||
(Vec3){ (float)x, d0 + 0.15f, (float)y },
|
draw_billboard("/assets/trreez.png",
|
||||||
(Vec2){0.3f, 0.3f},
|
(Vec3){ (float)x, d0 + 1.95f, (float)y },
|
||||||
|
(Vec2){2.f, 2.f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255, 255, 255, 255}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t const rsi_g = (int32_t)GRASS_RADIUS * (int32_t)GRASS_RADIUS;
|
||||||
|
for (int32_t iy = -(int32_t)GRASS_RADIUS; iy <= (int32_t)GRASS_RADIUS - 1; ++iy) {
|
||||||
|
int32_t const dx = ceil_sqrt(rsi_g - (iy + (iy <= 0)) * (iy + (iy <= 0)));
|
||||||
|
for (int32_t ix = -dx; ix < dx; ++ix) {
|
||||||
|
int32_t lx = ix + TERRAIN_RADIUS;
|
||||||
|
int32_t ly = iy + TERRAIN_RADIUS;
|
||||||
|
|
||||||
|
float x = (float)(floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx));
|
||||||
|
float y = (float)(floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly));
|
||||||
|
|
||||||
|
float d = heightmap[lx][ly];
|
||||||
|
|
||||||
|
draw_billboard("/assets/grasses/25.png",
|
||||||
|
(Vec3){
|
||||||
|
(float)x + (float)((adler32(&((Vec2){x, y}), sizeof (Vec2))) % 32) / 64.0f,
|
||||||
|
d + 0.2f,
|
||||||
|
(float)y + (float)((adler32(&((Vec2){y, x}), sizeof (Vec2))) % 32) / 64.0f
|
||||||
|
},
|
||||||
|
(Vec2){0.4f, 0.4f},
|
||||||
|
(Rect){0},
|
||||||
(Color){255, 255, 255, 255}, true);
|
(Color){255, 255, 255, 255}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,29 +522,31 @@ static void draw_terrain(SceneIngame *scn) {
|
|||||||
static void ingame_tick(State *state) {
|
static void ingame_tick(State *state) {
|
||||||
SceneIngame *scn = (SceneIngame *)state->scene;
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
input_action("player_left", CONTROL_A);
|
input_action("player_left", "A");
|
||||||
input_action("player_right", CONTROL_D);
|
input_action("player_right", "D");
|
||||||
input_action("player_forward", CONTROL_W);
|
input_action("player_forward", "W");
|
||||||
input_action("player_backward", CONTROL_S);
|
input_action("player_backward", "S");
|
||||||
input_action("player_jump", CONTROL_SPACE);
|
input_action("player_jump", "SPACE");
|
||||||
input_action("player_run", CONTROL_LSHIFT);
|
input_action("player_run", "LSHIFT");
|
||||||
input_action("mouse_capture_toggle", CONTROL_ESCAPE);
|
input_action("mouse_capture_toggle", "ESCAPE");
|
||||||
input_action("toggle_camera_mode", CONTROL_C);
|
input_action("toggle_camera_mode", "C");
|
||||||
|
|
||||||
if (scn->mouse_captured) {
|
if (scn->mouse_captured) {
|
||||||
const float sensitivity = 0.4f * (float)DEG2RAD; /* TODO: put this in a better place */
|
const float sensitivity = 0.4f * (float)(M_PI / 180); /* TODO: put this in a better place */
|
||||||
scn->yaw += (float)ctx.mouse_movement.x * sensitivity;
|
scn->yaw += (float)ctx.mouse_movement.x * sensitivity;
|
||||||
scn->pitch -= (float)ctx.mouse_movement.y * sensitivity;
|
scn->pitch -= (float)ctx.mouse_movement.y * sensitivity;
|
||||||
scn->pitch = clampf(scn->pitch, (float)-M_PI * 0.49f, (float)M_PI * 0.49f);
|
scn->pitch = clampf(scn->pitch, (float)-M_PI * 0.49f, (float)M_PI * 0.49f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_action_just_pressed("toggle_camera_mode"))
|
if (input_action_just_pressed("toggle_camera_mode"))
|
||||||
scn->flying_camera = !scn->flying_camera;
|
scn->camera_mode = scn->camera_mode == 2 ? 0 : scn->camera_mode + 1;
|
||||||
|
|
||||||
if (scn->flying_camera) {
|
if (scn->camera_mode == 1) {
|
||||||
process_fly_mode(state);
|
process_fly_mode(state);
|
||||||
} else {
|
} else if (scn->camera_mode == 0) {
|
||||||
process_ground_mode(state);
|
process_ground_mode(state);
|
||||||
|
} else if (scn->camera_mode) {
|
||||||
|
process_vehicle_mode(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* toggle mouse capture with end key */
|
/* toggle mouse capture with end key */
|
||||||
@ -220,12 +556,15 @@ static void ingame_tick(State *state) {
|
|||||||
ctx.mouse_capture = scn->mouse_captured;
|
ctx.mouse_capture = scn->mouse_captured;
|
||||||
|
|
||||||
generate_terrain(scn);
|
generate_terrain(scn);
|
||||||
|
process_vehicle(scn);
|
||||||
|
|
||||||
draw_terrain(scn);
|
draw_terrain(scn);
|
||||||
|
draw_vehicle(scn);
|
||||||
|
|
||||||
draw_skybox("/assets/miramar/miramar_*.tga");
|
draw_skybox("/assets/miramar/miramar_*.tga");
|
||||||
|
|
||||||
ctx.fog_color = (Color){ 140, 147, 160, 255 };
|
ctx.fog_color = (Color){ 140, 147, 160, 255 };
|
||||||
ctx.fog_density = 0.03f;
|
ctx.fog_density = 0.015f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -237,13 +576,13 @@ static void ingame_end(State *state) {
|
|||||||
Scene *ingame_scene(State *state) {
|
Scene *ingame_scene(State *state) {
|
||||||
(void)state;
|
(void)state;
|
||||||
|
|
||||||
SceneIngame *new_scene = ccalloc(1, sizeof *new_scene);
|
SceneIngame *new_scene = calloc(1, sizeof *new_scene);
|
||||||
new_scene->base.tick = ingame_tick;
|
new_scene->base.tick = ingame_tick;
|
||||||
new_scene->base.end = ingame_end;
|
new_scene->base.end = ingame_end;
|
||||||
|
|
||||||
new_scene->mouse_captured = true;
|
new_scene->mouse_captured = true;
|
||||||
|
|
||||||
m_audio(m_set(path, "music/mod65.xm"),
|
m_audio(m_set(path, "music/woah.ogg"),
|
||||||
m_opt(channel, "soundtrack"),
|
m_opt(channel, "soundtrack"),
|
||||||
m_opt(repeat, true));
|
m_opt(repeat, true));
|
||||||
|
|
||||||
|
@ -12,13 +12,16 @@
|
|||||||
typedef struct SceneIngame {
|
typedef struct SceneIngame {
|
||||||
Scene base;
|
Scene base;
|
||||||
|
|
||||||
|
Vec3 looking_direction;
|
||||||
|
Vec2 world_center;
|
||||||
|
|
||||||
Vec3 pos;
|
Vec3 pos;
|
||||||
float yaw;
|
float yaw;
|
||||||
float pitch;
|
float pitch;
|
||||||
float roll;
|
float roll;
|
||||||
|
|
||||||
bool mouse_captured;
|
bool mouse_captured;
|
||||||
bool flying_camera;
|
int camera_mode;
|
||||||
} SceneIngame;
|
} SceneIngame;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
#include "title.h"
|
|
||||||
#include "ingame.h"
|
|
||||||
|
|
||||||
#include "twn_game_api.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
|
|
||||||
static void title_tick(State *state) {
|
|
||||||
SceneTitle *scn = (SceneTitle *)state->scene;
|
|
||||||
(void)scn;
|
|
||||||
|
|
||||||
input_action("ui_accept", CONTROL_RETURN);
|
|
||||||
|
|
||||||
if (input_action_just_pressed("ui_accept")) {
|
|
||||||
switch_to(state, ingame_scene);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_sprite("/assets/title.png", ((Rect) {
|
|
||||||
((float)ctx.resolution.x / 2) - ((float)320 / 2), 64, 320, 128 }));
|
|
||||||
|
|
||||||
/* draw the tick count as an example of dynamic text */
|
|
||||||
size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1;
|
|
||||||
char *text_str = cmalloc(text_str_len);
|
|
||||||
snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number);
|
|
||||||
|
|
||||||
const char *font = "/fonts/kenney-pixel.ttf";
|
|
||||||
float text_h = 32;
|
|
||||||
float text_w = draw_text_width(text_str, text_h, font);
|
|
||||||
|
|
||||||
draw_rectangle(
|
|
||||||
(Rect) {
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.w = text_w,
|
|
||||||
.h = text_h,
|
|
||||||
},
|
|
||||||
(Color) { 0, 0, 0, 255 }
|
|
||||||
);
|
|
||||||
|
|
||||||
draw_text(text_str, (Vec2){ 0, 0 }, text_h, (Color) { 255, 255, 255, 255 }, font);
|
|
||||||
free(text_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void title_end(State *state) {
|
|
||||||
free(state->scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Scene *title_scene(State *state) {
|
|
||||||
(void)state;
|
|
||||||
|
|
||||||
SceneTitle *new_scene = ccalloc(1, sizeof *new_scene);
|
|
||||||
new_scene->base.tick = title_tick;
|
|
||||||
new_scene->base.end = title_end;
|
|
||||||
|
|
||||||
return (Scene *)new_scene;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#ifndef TITLE_H
|
|
||||||
#define TITLE_H
|
|
||||||
|
|
||||||
#include "../state.h"
|
|
||||||
#include "scene.h"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct SceneTitle {
|
|
||||||
Scene base;
|
|
||||||
} SceneTitle;
|
|
||||||
|
|
||||||
|
|
||||||
Scene *title_scene(State *state);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -6,11 +6,11 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build)
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
state.h
|
||||||
)
|
)
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
@ -76,6 +76,26 @@ static void benchmark(struct state *state) {
|
|||||||
|
|
||||||
profile_end("int32_t acc");
|
profile_end("int32_t acc");
|
||||||
|
|
||||||
|
profile_start("int32_t acc precalc");
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; ++i) {
|
||||||
|
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
|
||||||
|
int32_t acc = (int32_t)(sqrtf(state->r * state->r - (state->r - 1) * (state->r - 1)));
|
||||||
|
for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) {
|
||||||
|
while (acc * acc < rsi - iy * iy) acc++;
|
||||||
|
for (int32_t ix = -acc; ix < acc; ++ix) {
|
||||||
|
/* lower portion */
|
||||||
|
x = (float)ix;
|
||||||
|
y = (float)iy;
|
||||||
|
/* upper portion */
|
||||||
|
x = (float)ix;
|
||||||
|
y = (float)(int32_t)(~(uint32_t)iy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profile_end("int32_t acc precalc");
|
||||||
|
|
||||||
(void)x; (void)y;
|
(void)x; (void)y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,9 +103,9 @@ static void benchmark(struct state *state) {
|
|||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
if (!ctx.udata) {
|
if (!ctx.udata) {
|
||||||
ctx.udata = ccalloc(1, sizeof (struct state));
|
ctx.udata = calloc(1, sizeof (struct state));
|
||||||
struct state *state = ctx.udata;
|
struct state *state = ctx.udata;
|
||||||
state->r = 10;
|
state->r = 24;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,14 +113,16 @@ void game_tick(void) {
|
|||||||
|
|
||||||
Vec2 const mouse_snap = {floorf(ctx.mouse_position.x / 8) * 8, floorf(ctx.mouse_position.y / 8) * 8};
|
Vec2 const mouse_snap = {floorf(ctx.mouse_position.x / 8) * 8, floorf(ctx.mouse_position.y / 8) * 8};
|
||||||
|
|
||||||
input_action("up", CONTROL_LEFT_MOUSE);
|
input_action("up", "LCLICK");
|
||||||
input_action("down", CONTROL_RIGHT_MOUSE);
|
input_action("down", "RCLICK");
|
||||||
|
|
||||||
if (input_action_just_pressed("up"))
|
if (input_action_pressed("up"))
|
||||||
state->r += 1;
|
state->r += 1;
|
||||||
if (input_action_just_pressed("down"))
|
if (input_action_pressed("down") && state->r > 2)
|
||||||
state->r -= 1;
|
state->r -= 1;
|
||||||
|
|
||||||
|
draw_circle(mouse_snap, state->r * 8, (Color){125, 125, 125, 125});
|
||||||
|
|
||||||
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
|
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
|
||||||
int32_t acc = 1;
|
int32_t acc = 1;
|
||||||
for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) {
|
for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) {
|
||||||
@ -113,9 +135,8 @@ void game_tick(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_circle(mouse_snap, state->r * 8, (Color){125, 125, 125, 125});
|
/* uncomment to see performance difference between variants */
|
||||||
|
// benchmark(state);
|
||||||
benchmark(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
|
||||||
|
|
||||||
project(template LANGUAGES C)
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build)
|
|
||||||
|
|
||||||
set(SOURCE_FILES
|
|
||||||
game.c
|
|
||||||
state.h
|
|
||||||
)
|
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
|
16
apps/templates/c/CMakeLists.txt
Normal file
16
apps/templates/c/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(twngame LANGUAGES C)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
|
set(SOURCE_FILES
|
||||||
|
game.c
|
||||||
|
state.h
|
||||||
|
)
|
||||||
|
|
||||||
|
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
|
||||||
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
27
apps/templates/c/data/twn.toml
Normal file
27
apps/templates/c/data/twn.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# This file contains everything about the engine and your game that can be
|
||||||
|
# configured before it runs.
|
||||||
|
#
|
||||||
|
# Optional settings are commented out, with their default values shown.
|
||||||
|
# Invalid values in these settings will be ignored.
|
||||||
|
|
||||||
|
# Data about your game as an application
|
||||||
|
[about]
|
||||||
|
title = "Template"
|
||||||
|
developer = "You"
|
||||||
|
app_id = "template"
|
||||||
|
dev_id = "you"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
background_color = [ 255, 125, 0, 255 ]
|
||||||
|
#debug = true
|
||||||
|
|
||||||
|
# Engine tweaks. You probably don't need to change these
|
||||||
|
[engine]
|
||||||
|
#ticks_per_second = 60 # minimum of 8
|
||||||
|
#keybind_slots = 3 # minimum of 1
|
||||||
|
#texture_atlas_size = 2048 # minimum of 32
|
||||||
|
#font_texture_size = 2048 # minimum of 1024
|
||||||
|
#font_oversampling = 4 # minimum of 0
|
||||||
|
#font_filtering = "linear" # possible values: "nearest", "linear"
|
@ -1,7 +1,7 @@
|
|||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
#include <malloc.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
@ -10,7 +10,7 @@ void game_tick(void) {
|
|||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
/* application data could be stored in ctx.udata and retrieved anywhere */
|
/* application data could be stored in ctx.udata and retrieved anywhere */
|
||||||
if (!ctx.udata)
|
if (!ctx.udata)
|
||||||
ctx.udata = ccalloc(1, sizeof (struct state));
|
ctx.udata = calloc(1, sizeof (struct state));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a lot of data is accessible from `ctx`, look into `townengine/context.h` for more */
|
/* a lot of data is accessible from `ctx`, look into `townengine/context.h` for more */
|
17
apps/templates/lua/.gitignore
vendored
Normal file
17
apps/templates/lua/.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# ignore executables
|
||||||
|
*
|
||||||
|
!*.*
|
||||||
|
!*/
|
||||||
|
|
||||||
|
**/*.so
|
||||||
|
**/*.dll
|
||||||
|
**/*.exe
|
||||||
|
**/*.trace
|
||||||
|
**/*.js
|
||||||
|
**/*.wasm
|
||||||
|
**/*.wasm.map
|
||||||
|
**/*.data
|
||||||
|
**/*.html
|
||||||
|
|
||||||
|
data/scripts/twnapi.lua
|
||||||
|
build/
|
7
apps/templates/lua/data/scripts/game.lua
Normal file
7
apps/templates/lua/data/scripts/game.lua
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
-- called every frame, with constant delta time
|
||||||
|
function game_tick()
|
||||||
|
-- ctx.udata persists on code reload
|
||||||
|
if ctx.udata == nil then
|
||||||
|
ctx.udata = {}
|
||||||
|
end
|
||||||
|
end
|
27
apps/templates/lua/data/twn.toml
Normal file
27
apps/templates/lua/data/twn.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# This file contains everything about the engine and your game that can be
|
||||||
|
# configured before it runs.
|
||||||
|
#
|
||||||
|
# Optional settings are commented out, with their default values shown.
|
||||||
|
# Invalid values in these settings will be ignored.
|
||||||
|
|
||||||
|
# Data about your game as an application
|
||||||
|
[about]
|
||||||
|
title = "Template"
|
||||||
|
developer = "You"
|
||||||
|
app_id = "template"
|
||||||
|
dev_id = "you"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
interpreter = "$TWNROOT/apps/twnlua"
|
||||||
|
#debug = true
|
||||||
|
|
||||||
|
# Engine tweaks. You probably don't need to change these
|
||||||
|
[engine]
|
||||||
|
#ticks_per_second = 60 # minimum of 8
|
||||||
|
#keybind_slots = 3 # minimum of 1
|
||||||
|
#texture_atlas_size = 2048 # minimum of 32
|
||||||
|
#font_texture_size = 2048 # minimum of 1024
|
||||||
|
#font_oversampling = 4 # minimum of 0
|
||||||
|
#font_filtering = "linear" # possible values: "nearest", "linear"
|
19
apps/templates/zig/.gitignore
vendored
Normal file
19
apps/templates/zig/.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# ignore executables
|
||||||
|
*
|
||||||
|
!*.*
|
||||||
|
!*/
|
||||||
|
|
||||||
|
**/*.so
|
||||||
|
**/*.dll
|
||||||
|
**/*.exe
|
||||||
|
**/*.trace
|
||||||
|
**/*.js
|
||||||
|
**/*.wasm
|
||||||
|
**/*.wasm.map
|
||||||
|
**/*.data
|
||||||
|
**/*.html
|
||||||
|
|
||||||
|
data/scripts/twnapi.lua
|
||||||
|
build/
|
||||||
|
.zig-cache/
|
||||||
|
zig-out/
|
26
apps/templates/zig/CMakeLists.txt
Normal file
26
apps/templates/zig/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(twngame LANGUAGES C)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
|
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
|
||||||
|
put_townengine(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE zig-sources ${CMAKE_CURRENT_SOURCE_DIR}/src/*.zig)
|
||||||
|
|
||||||
|
# TODO: support static build
|
||||||
|
# TODO: propagate release switches
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/libgame.so
|
||||||
|
COMMAND env zig build
|
||||||
|
DEPENDS ${TWN_TARGET} ${zig-sources}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(
|
||||||
|
zig-step ALL
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgame.so
|
||||||
|
)
|
58
apps/templates/zig/build.zig
Normal file
58
apps/templates/zig/build.zig
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
// Although this function looks imperative, note that its job is to
|
||||||
|
// declaratively construct a build graph that will be executed by an external
|
||||||
|
// runner.
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// This creates a "module", which represents a collection of source files alongside
|
||||||
|
// some compilation options, such as optimization mode and linked system libraries.
|
||||||
|
// Every executable or library we compile will be based on one or more modules.
|
||||||
|
const lib_mod = b.createModule(.{
|
||||||
|
// `root_source_file` is the Zig "entry point" of the module. If a module
|
||||||
|
// only contains e.g. external object files, you can make this `null`.
|
||||||
|
// In this case the main source file is merely a path, however, in more
|
||||||
|
// complicated build scripts, this could be a generated file.
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.link_libc = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
lib_mod.addIncludePath(b.path("../../../"));
|
||||||
|
lib_mod.addIncludePath(b.path("../../../include/"));
|
||||||
|
lib_mod.addLibraryPath(b.path("./"));
|
||||||
|
|
||||||
|
// Now, we will create a static library based on the module we created above.
|
||||||
|
// This creates a `std.Build.Step.Compile`, which is the build step responsible
|
||||||
|
// for actually invoking the compiler.
|
||||||
|
const lib = b.addLibrary(.{
|
||||||
|
.linkage = .dynamic,
|
||||||
|
.name = "game",
|
||||||
|
.root_module = lib_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.linkSystemLibrary("townengine");
|
||||||
|
|
||||||
|
// This declares intent for the library to be installed into the standard
|
||||||
|
// location when the user invokes the "install" step (the default step when
|
||||||
|
// running `zig build`).
|
||||||
|
const install_artifact = b.addInstallArtifact(lib, .{
|
||||||
|
.dest_dir = .{
|
||||||
|
.override = .{
|
||||||
|
.custom = "../",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
b.getInstallStep().dependOn(&install_artifact.step);
|
||||||
|
}
|
27
apps/templates/zig/data/twn.toml
Normal file
27
apps/templates/zig/data/twn.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# This file contains everything about the engine and your game that can be
|
||||||
|
# configured before it runs.
|
||||||
|
#
|
||||||
|
# Optional settings are commented out, with their default values shown.
|
||||||
|
# Invalid values in these settings will be ignored.
|
||||||
|
|
||||||
|
# Data about your game as an application
|
||||||
|
[about]
|
||||||
|
title = "Zig Awesomeness"
|
||||||
|
developer = "notwanp"
|
||||||
|
app_id = "yourzigthing"
|
||||||
|
dev_id = "definatelynotwanp"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
background_color = [ 255, 125, 0, 255 ]
|
||||||
|
#debug = true
|
||||||
|
|
||||||
|
# Engine tweaks. You probably don't need to change these
|
||||||
|
[engine]
|
||||||
|
#ticks_per_second = 60 # minimum of 8
|
||||||
|
#keybind_slots = 3 # minimum of 1
|
||||||
|
#texture_atlas_size = 2048 # minimum of 32
|
||||||
|
#font_texture_size = 2048 # minimum of 1024
|
||||||
|
#font_oversampling = 4 # minimum of 0
|
||||||
|
#font_filtering = "linear" # possible values: "nearest", "linear"
|
24
apps/templates/zig/src/root.zig
Normal file
24
apps/templates/zig/src/root.zig
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("twn_game_api.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
export fn game_tick() void {
|
||||||
|
tick() catch |err| {
|
||||||
|
std.log.err(@errorName(err));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn game_end() void {
|
||||||
|
end() catch |err| {
|
||||||
|
std.log.err(@errorName(err));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick() !void {
|
||||||
|
if (c.ctx.initialization_needed) {
|
||||||
|
std.debug.print("lmao\n", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end() !void {}
|
16
apps/tools/twndel/CMakeLists.txt
Normal file
16
apps/tools/twndel/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(twndel LANGUAGES C)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
|
set(SOURCE_FILES
|
||||||
|
tool.c
|
||||||
|
state.h
|
||||||
|
)
|
||||||
|
|
||||||
|
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
|
||||||
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
BIN
apps/tools/twndel/data/bong.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/bong.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/camera.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/camera.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/center.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/center.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/click.wav
Normal file
BIN
apps/tools/twndel/data/click.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/drop.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/drop.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/grab.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/grab.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/point.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/point.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/pop.wav
Normal file
BIN
apps/tools/twndel/data/pop.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/rip.wav
Normal file
BIN
apps/tools/twndel/data/rip.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/selectin.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/selectin.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/selectout.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/selectout.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/snip.wav
Normal file
BIN
apps/tools/twndel/data/snip.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/spooncap.wav
Normal file
BIN
apps/tools/twndel/data/spooncap.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/switch.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/switch.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
12
apps/tools/twndel/data/twn.toml
Normal file
12
apps/tools/twndel/data/twn.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[about]
|
||||||
|
title = "Townengine Modeling Tool"
|
||||||
|
developer = "twnteam"
|
||||||
|
app_id = "twndel"
|
||||||
|
dev_id = "twnteam"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
background_color = [ 255, 125, 0, 255 ]
|
||||||
|
|
||||||
|
[engine]
|
BIN
apps/tools/twndel/data/x.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/x.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/y.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/y.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/z.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/z.png
(Stored with Git LFS)
Normal file
Binary file not shown.
118
apps/tools/twndel/state.h
Normal file
118
apps/tools/twndel/state.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#ifndef STATE_H
|
||||||
|
#define STATE_H
|
||||||
|
|
||||||
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define POINTS_PER_METER 128
|
||||||
|
#define UNDO_STACK_SIZE 32
|
||||||
|
#define CAMERA_FOV ((float)M_PI_2 * 0.75f)
|
||||||
|
|
||||||
|
#define POINT_LIMIT 65534
|
||||||
|
#define FACE_LIMIT 2048
|
||||||
|
#define OBJECT_LIMIT 16
|
||||||
|
#define TEXTURE_LIMIT 32
|
||||||
|
|
||||||
|
#define INVALID_POINT (POINT_LIMIT+1)
|
||||||
|
#define INVALID_FACE (FACE_LIMIT+1)
|
||||||
|
#define INVALID_OBJECT (OBJECT_LIMIT+1)
|
||||||
|
#define INVALID_TEXTURE (TEXTURE_LIMIT+1)
|
||||||
|
|
||||||
|
#define CAMERA_ROTATION_SPEED 0.04f
|
||||||
|
#define CAMERA_TRANSLATION_SPEED 0.04f
|
||||||
|
#define SELECTION_SPHERE_RADIUS 32
|
||||||
|
/* should be an odd number */
|
||||||
|
#define SNAP_LINES_SHOW 7
|
||||||
|
#define SNAP_LINES_WIDTH 1.0f
|
||||||
|
#define SNAP_LINES_COLOR ((Color){200,200,200,150})
|
||||||
|
|
||||||
|
typedef struct Operation {
|
||||||
|
enum {
|
||||||
|
OPERATION_MOVE_POINT,
|
||||||
|
OPERATION_SET_TEXTURE,
|
||||||
|
OPERATION_TRIANGULATE,
|
||||||
|
} kind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint16_t point;
|
||||||
|
int16_t delta_x;
|
||||||
|
int16_t delta_y;
|
||||||
|
int16_t delta_z;
|
||||||
|
uint8_t object;
|
||||||
|
} move_point;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint16_t face;
|
||||||
|
int16_t delta_texture;
|
||||||
|
uint8_t object;
|
||||||
|
} set_texture;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint16_t old_face;
|
||||||
|
uint16_t new_face;
|
||||||
|
uint8_t object;
|
||||||
|
} triangulate;
|
||||||
|
} data;
|
||||||
|
|
||||||
|
bool chained;
|
||||||
|
} Operation;
|
||||||
|
|
||||||
|
typedef struct Point {
|
||||||
|
int16_t x, y, z;
|
||||||
|
} Point;
|
||||||
|
|
||||||
|
/* TODO: store topology in terms on edge connections? might be bad, as it's stateful */
|
||||||
|
/* triangles have p3 = INVALID_POINT */
|
||||||
|
/* lines have p2, p3 = INVALID_POINT */
|
||||||
|
/* billboards have p1, p2, p3 = INVALID_POINT */
|
||||||
|
typedef struct Face {
|
||||||
|
uint16_t p[4];
|
||||||
|
/* texture origin, as point on face plane, in absolute coordinates */
|
||||||
|
int16_t tex_x, tex_y;
|
||||||
|
uint8_t texture;
|
||||||
|
uint8_t tex_scale;
|
||||||
|
} Face;
|
||||||
|
|
||||||
|
typedef struct Object {
|
||||||
|
char *name;
|
||||||
|
bool is_invisible;
|
||||||
|
Point position;
|
||||||
|
char *textures[TEXTURE_LIMIT + 1];
|
||||||
|
uint8_t textures_sz;
|
||||||
|
Point rotation;
|
||||||
|
Face faces[FACE_LIMIT];
|
||||||
|
uint16_t faces_sz;
|
||||||
|
} Object;
|
||||||
|
|
||||||
|
typedef struct State {
|
||||||
|
Operation op_stack[UNDO_STACK_SIZE];
|
||||||
|
uint32_t op_stack_ptr;
|
||||||
|
bool op_active;
|
||||||
|
|
||||||
|
Vec3 active_center;
|
||||||
|
Vec3 camera_position;
|
||||||
|
Vec3 camera_direction;
|
||||||
|
float camera_zoom;
|
||||||
|
bool camera_is_orthographic;
|
||||||
|
/* defaults to wireframe */
|
||||||
|
bool solid_display_mode;
|
||||||
|
/* positions skipped */
|
||||||
|
uint8_t grid_snap_granularity;
|
||||||
|
|
||||||
|
Point points[POINT_LIMIT];
|
||||||
|
uint16_t points_sz;
|
||||||
|
|
||||||
|
Object objects[OBJECT_LIMIT];
|
||||||
|
uint8_t objects_sz;
|
||||||
|
|
||||||
|
/* which axes are blocked and which are not */
|
||||||
|
/* order: x, y, z */
|
||||||
|
bool axis_mask[3];
|
||||||
|
char *current_texture;
|
||||||
|
|
||||||
|
uint8_t current_hovered_obj;
|
||||||
|
uint16_t current_hovered_face;
|
||||||
|
} State;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
972
apps/tools/twndel/tool.c
Normal file
972
apps/tools/twndel/tool.c
Normal file
@ -0,0 +1,972 @@
|
|||||||
|
#include "twn_game_api.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "twn_vec.h"
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
/* planned features: */
|
||||||
|
/* grid-based, bounded space (65536 / POINTS_PER_METER meters), which allows for efficient storage */
|
||||||
|
/* 65534 point limit, 16 object limit, 2048 faces per object, 32 textures per object */
|
||||||
|
/* triangles and quads only */
|
||||||
|
/* support for billboards and flat two sided quads */
|
||||||
|
/* texture painting */
|
||||||
|
/* bones with mesh animations, snapping to grid, with no weights */
|
||||||
|
/* billboard render to specified angles */
|
||||||
|
/* 1 point light primitive lighting */
|
||||||
|
/* live edited textures are capped at 128x128 */
|
||||||
|
|
||||||
|
/* assumptions: */
|
||||||
|
/* up is always (0,1,0) */
|
||||||
|
/* preallocations everywhere */
|
||||||
|
|
||||||
|
static State state;
|
||||||
|
static bool init;
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t new_object(const char *name) {
|
||||||
|
if (state.objects_sz >= OBJECT_LIMIT)
|
||||||
|
return INVALID_OBJECT;
|
||||||
|
state.objects_sz++;
|
||||||
|
state.objects[state.objects_sz-1].name = SDL_strdup(name);
|
||||||
|
return state.objects_sz-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint16_t new_point(int16_t x, int16_t y, int16_t z) {
|
||||||
|
if (state.points_sz >= POINT_LIMIT)
|
||||||
|
return INVALID_POINT;
|
||||||
|
state.points_sz++;
|
||||||
|
state.points[state.points_sz-1] = (Point){x, y, z};
|
||||||
|
return state.points_sz-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint16_t push_face(uint8_t object,
|
||||||
|
uint16_t p0, uint16_t p1, uint16_t p2, uint16_t p3,
|
||||||
|
uint8_t texture, uint8_t tex_scale, int16_t tex_x, int16_t tex_y)
|
||||||
|
{
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
o->faces_sz++;
|
||||||
|
o->faces[o->faces_sz-1] = (Face) {
|
||||||
|
.p = {p0, p1, p2, p3},
|
||||||
|
.texture = texture,
|
||||||
|
.tex_scale = tex_scale,
|
||||||
|
.tex_x = tex_x,
|
||||||
|
.tex_y = tex_y,
|
||||||
|
};
|
||||||
|
return o->faces_sz-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t push_texture(uint8_t object, char *texture) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
|
||||||
|
/* check whether it's already here */
|
||||||
|
for (uint8_t i = 0; i < o->textures_sz; ++i)
|
||||||
|
if (SDL_strcmp(o->textures[i], texture) == 0)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
o->textures_sz++;
|
||||||
|
o->textures[o->textures_sz-1] = SDL_strdup(texture);
|
||||||
|
return o->textures_sz-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: use tombstones instead? it would be easier to maintain, by a lot */
|
||||||
|
/* note: make sure nothing depends on none */
|
||||||
|
static void pop_face(uint8_t object, uint16_t face) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
if (face != o->faces_sz-1)
|
||||||
|
o->faces[face] = o->faces[o->faces_sz-1];
|
||||||
|
o->faces_sz--;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void push_operation(Operation operation, bool active) {
|
||||||
|
state.op_stack_ptr++;
|
||||||
|
uint8_t op = state.op_stack_ptr % UNDO_STACK_SIZE;
|
||||||
|
state.op_stack[op] = operation;
|
||||||
|
state.op_active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void extend_operation(Operation operation) {
|
||||||
|
uint8_t op = state.op_stack_ptr % UNDO_STACK_SIZE;
|
||||||
|
Operation ext = state.op_stack[op];
|
||||||
|
ext.chained = true;
|
||||||
|
state.op_stack[op] = operation;
|
||||||
|
push_operation(ext, state.op_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline Vec3 point_to_vec3(uint8_t object, uint16_t point) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
return (Vec3){ (o->position.x + state.points[point].x) / (float)POINTS_PER_METER,
|
||||||
|
(o->position.y + state.points[point].y) / (float)POINTS_PER_METER,
|
||||||
|
(o->position.z + state.points[point].z) / (float)POINTS_PER_METER };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: something is wrong, figure out how to introduce rotation to this. */
|
||||||
|
static inline Vec2 project_texture_coordinate(Vec2 origin, Vec3 plane, Vec3 point, float scale) {
|
||||||
|
Vec3 right = vec3_norm(vec3_cross(plane, fabsf(0.0f - plane.y) < 1e-9f ? (Vec3){0,1,0} : (Vec3){1,0,0}));
|
||||||
|
Vec3 up = vec3_norm(vec3_cross(right, plane));
|
||||||
|
Vec2 pp = { vec3_dot(point, right), vec3_dot(point, up) };
|
||||||
|
return vec2_scale(vec2_sub(origin, pp), scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void render_object(uint8_t object) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
|
||||||
|
if (o->is_invisible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
|
||||||
|
if (f->p[2] != INVALID_POINT) {
|
||||||
|
Vec3 p0 = point_to_vec3(object, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(object, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(object, f->p[2]);
|
||||||
|
|
||||||
|
if (state.solid_display_mode) {
|
||||||
|
Vec3 n = vec3_norm(vec3_cross(vec3_sub(p1, p0), vec3_sub(p2, p0)));
|
||||||
|
Vec2 to = { f->tex_x / (float)POINTS_PER_METER, f->tex_y / (float)POINTS_PER_METER, };
|
||||||
|
|
||||||
|
Vec2 tul = project_texture_coordinate(to, n, p0, (float)f->tex_scale);
|
||||||
|
Vec2 tdl = project_texture_coordinate(to, n, p1, (float)f->tex_scale);
|
||||||
|
Vec2 tdr = project_texture_coordinate(to, n, p2, (float)f->tex_scale);
|
||||||
|
|
||||||
|
draw_triangle(o->textures[f->texture],
|
||||||
|
p0, p1, p2,
|
||||||
|
tul, tdl, tdr,
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
(Color){255,255,255,255});
|
||||||
|
|
||||||
|
if (f->p[3] != INVALID_POINT) {
|
||||||
|
Vec3 p3 = point_to_vec3(object, f->p[3]);
|
||||||
|
Vec2 tur = project_texture_coordinate(to, n, p3, (float)f->tex_scale);
|
||||||
|
draw_triangle(o->textures[f->texture],
|
||||||
|
p2, p3, p0,
|
||||||
|
tdr, tur, tul,
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
(Color){255,255,255,255});
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
draw_line_3d(p0, p1, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(p1, p2, 1, (Color){255,255,255,255});
|
||||||
|
|
||||||
|
if (f->p[3] == INVALID_POINT)
|
||||||
|
draw_line_3d(p2, p0, 1, (Color){255,255,255,255});
|
||||||
|
else {
|
||||||
|
Vec3 p3 = point_to_vec3(object, f->p[3]);
|
||||||
|
draw_line_3d(p2, p3, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(p3, p0, 1, (Color){255,255,255,255});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
SDL_assert_always(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t new_cube(Point pos, Point size) {
|
||||||
|
uint8_t object = new_object("cube");
|
||||||
|
|
||||||
|
uint16_t p0 = new_point(pos.x - size.x / 2, pos.y - size.y / 2, pos.z - size.z / 2);
|
||||||
|
uint16_t p1 = new_point(pos.x - size.x / 2, pos.y + size.y / 2, pos.z - size.z / 2);
|
||||||
|
uint16_t p2 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z - size.z / 2);
|
||||||
|
uint16_t p3 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z - size.z / 2);
|
||||||
|
uint16_t p4 = new_point(pos.x - size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
|
||||||
|
uint16_t p5 = new_point(pos.x - size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
|
||||||
|
uint16_t p6 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
|
||||||
|
uint16_t p7 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
|
||||||
|
|
||||||
|
uint8_t tex = push_texture(object, "/data/placeholder.png");
|
||||||
|
push_face(object, p2, p3, p0, p1, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p5, p4, p7, p6, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p1, p0, p4, p5, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p6, p7, p3, p2, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p2, p1, p5, p6, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p0, p3, p7, p4, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_camera_rotation(void) {
|
||||||
|
float horizontal_rotation = 0;
|
||||||
|
float vertical_rotation = 0;
|
||||||
|
|
||||||
|
if (input_action_pressed("camera_rotate_left"))
|
||||||
|
horizontal_rotation -= CAMERA_ROTATION_SPEED;
|
||||||
|
if (input_action_pressed("camera_rotate_right"))
|
||||||
|
horizontal_rotation += CAMERA_ROTATION_SPEED;
|
||||||
|
if (input_action_pressed("camera_rotate_up"))
|
||||||
|
vertical_rotation -= CAMERA_ROTATION_SPEED;
|
||||||
|
if (input_action_pressed("camera_rotate_down"))
|
||||||
|
vertical_rotation += CAMERA_ROTATION_SPEED;
|
||||||
|
|
||||||
|
Vec3 front = vec3_cross(state.camera_direction, (Vec3){0,1,0});
|
||||||
|
Vec3 local_position = vec3_sub(state.active_center, state.camera_position);
|
||||||
|
Vec3 new_local_position = vec3_rotate(local_position, horizontal_rotation, (Vec3){0,1,0});
|
||||||
|
|
||||||
|
state.camera_direction = vec3_rotate(state.camera_direction, horizontal_rotation, (Vec3){0,1,0});
|
||||||
|
Vec3 new_rot = vec3_rotate(state.camera_direction, vertical_rotation, front);
|
||||||
|
|
||||||
|
/* only apply if it's in limits */
|
||||||
|
float d = vec3_dot(new_rot, (Vec3){0,-1,0});
|
||||||
|
if (fabsf(d) <= 0.999f) {
|
||||||
|
new_local_position = vec3_rotate(new_local_position, vertical_rotation, front);
|
||||||
|
state.camera_direction = new_rot;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.camera_position = vec3_sub(state.active_center, new_local_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_camera_translation(void) {
|
||||||
|
Vec3 right = vec3_norm(vec3_cross(state.camera_direction, (Vec3){0,1,0}));
|
||||||
|
Vec3 up = vec3_norm(vec3_cross(state.camera_direction, right));
|
||||||
|
Vec3 was = state.camera_position;
|
||||||
|
|
||||||
|
if (input_action_pressed("camera_rotate_left"))
|
||||||
|
state.camera_position = vec3_sub(state.camera_position, vec3_scale(right, CAMERA_TRANSLATION_SPEED));
|
||||||
|
if (input_action_pressed("camera_rotate_right"))
|
||||||
|
state.camera_position = vec3_add(state.camera_position, vec3_scale(right, CAMERA_TRANSLATION_SPEED));
|
||||||
|
if (input_action_pressed("camera_rotate_up"))
|
||||||
|
state.camera_position = vec3_sub(state.camera_position, vec3_scale(up, CAMERA_TRANSLATION_SPEED));
|
||||||
|
if (input_action_pressed("camera_rotate_down"))
|
||||||
|
state.camera_position = vec3_add(state.camera_position, vec3_scale(up, CAMERA_TRANSLATION_SPEED));
|
||||||
|
|
||||||
|
state.active_center = vec3_add(state.active_center, vec3_sub(state.camera_position, was));
|
||||||
|
|
||||||
|
draw_billboard("/data/camera.png",
|
||||||
|
vec3_add(state.camera_position, vec3_scale(state.camera_direction, vec3_length(state.camera_position))),
|
||||||
|
(Vec2){0.2f,0.2f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
/* show relation to origin */
|
||||||
|
draw_billboard("/data/center.png",
|
||||||
|
(Vec3){0},
|
||||||
|
(Vec2){0.1f,0.1f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
draw_line_3d((Vec3){0}, state.active_center, 1, (Color){255,255,255,255});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_camera_movement(void) {
|
||||||
|
input_action("camera_rotate_left", "A");
|
||||||
|
input_action("camera_rotate_right", "D");
|
||||||
|
input_action("camera_rotate_up", "W");
|
||||||
|
input_action("camera_rotate_down", "S");
|
||||||
|
input_action("camera_lock_rotation", "SPACE");
|
||||||
|
|
||||||
|
if (input_action_pressed("camera_lock_rotation")) {
|
||||||
|
process_camera_translation();
|
||||||
|
} else {
|
||||||
|
process_camera_rotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline DrawCameraUnprojectResult unproject_point(Vec2 point) {
|
||||||
|
return draw_camera_unproject(
|
||||||
|
point,
|
||||||
|
state.camera_position,
|
||||||
|
state.camera_direction,
|
||||||
|
(Vec3){0, 1, 0},
|
||||||
|
state.camera_is_orthographic ? 0 : CAMERA_FOV,
|
||||||
|
state.camera_zoom,
|
||||||
|
100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool find_closest_point(uint8_t* object_result, uint16_t *point_result) {
|
||||||
|
DrawCameraUnprojectResult pos_and_ray = unproject_point(ctx.mouse_position);
|
||||||
|
|
||||||
|
/* step over every selectable object and find points closest to the view ray */
|
||||||
|
/* by constructing triangles and finding their height, from perpendicular */
|
||||||
|
uint16_t closest_point = INVALID_POINT;
|
||||||
|
uint8_t closest_obj = INVALID_OBJECT;
|
||||||
|
float closest_distance = INFINITY;
|
||||||
|
|
||||||
|
for (uint8_t obj = 0; obj < state.objects_sz; ++obj) {
|
||||||
|
Object *o = &state.objects[obj];
|
||||||
|
|
||||||
|
if (o->is_invisible)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* TODO: is it possible to skip repeated points? does it matter? */
|
||||||
|
/* as we limit the point could we could actually have bool array preallocated for this */
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
for (uint16_t pi = 0; pi < 4; ++pi) {
|
||||||
|
if (f->p[pi] == INVALID_POINT) break;
|
||||||
|
Vec3 p = point_to_vec3(obj, f->p[pi]);
|
||||||
|
Vec3 d = vec3_sub(pos_and_ray.position, p);
|
||||||
|
Vec3 b = vec3_cross(d, pos_and_ray.direction);
|
||||||
|
float ray_dist = vec3_length(b);
|
||||||
|
if (ray_dist > ((float)SELECTION_SPHERE_RADIUS / POINTS_PER_METER))
|
||||||
|
continue;
|
||||||
|
float dist = vec3_length(vec3_sub(pos_and_ray.position, vec3_add(p, b)));
|
||||||
|
if (dist < closest_distance) {
|
||||||
|
closest_distance = dist;
|
||||||
|
closest_obj = obj;
|
||||||
|
closest_point = f->p[pi];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closest_point == INVALID_POINT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (object_result)
|
||||||
|
*object_result = closest_obj;
|
||||||
|
if (point_result)
|
||||||
|
*point_result = closest_point;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* o = vector origin */
|
||||||
|
/* v = vector direction, normalized */
|
||||||
|
/* p = any point on plane */
|
||||||
|
/* n = normal of a plane */
|
||||||
|
static bool vector_plane_intersection(Vec3 o, Vec3 v, Vec3 p, Vec3 n, Vec3 *out) {
|
||||||
|
float dot = vec3_dot(n, v);
|
||||||
|
if (fabsf(dot) > FLT_EPSILON) {
|
||||||
|
Vec3 w = vec3_sub(o, p);
|
||||||
|
float fac = -vec3_dot(n, w) / dot;
|
||||||
|
*out = vec3_add(o, vec3_scale(v, fac));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* vector and plane are perpendicular, assume that it lies exactly on it */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool find_closest_face(uint8_t* object_result, uint16_t *face_result) {
|
||||||
|
DrawCameraUnprojectResult cam = unproject_point(ctx.mouse_position);
|
||||||
|
|
||||||
|
uint8_t closest_obj = INVALID_OBJECT;
|
||||||
|
uint16_t closest_face = INVALID_FACE;
|
||||||
|
float closest_distance = INFINITY;
|
||||||
|
|
||||||
|
for (uint8_t oi = 0; oi < state.objects_sz; ++oi) {
|
||||||
|
Object *o = &state.objects[oi];
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
|
||||||
|
if (f->p[1] == INVALID_POINT) continue;
|
||||||
|
|
||||||
|
Vec3 p0 = point_to_vec3(oi, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(oi, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(oi, f->p[2]);
|
||||||
|
|
||||||
|
Vec3 n = vec3_norm(vec3_cross(vec3_sub(p1, p0), vec3_sub(p2, p0)));
|
||||||
|
|
||||||
|
/* culling */
|
||||||
|
if (vec3_dot(state.camera_direction, n) >= 0) continue;
|
||||||
|
|
||||||
|
Vec3 i;
|
||||||
|
if (!vector_plane_intersection(cam.position, cam.direction, p0, n, &i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dist = vec3_length(vec3_sub(p0, i));
|
||||||
|
if (dist >= closest_distance) continue;
|
||||||
|
|
||||||
|
/* left normals are used to determine whether point lies to the left for all forming lines */
|
||||||
|
Vec3 ln0 = vec3_norm(vec3_cross(n, vec3_sub(p1, p0)));
|
||||||
|
if (vec3_dot(ln0, vec3_sub(p0, i)) >= 0) continue;
|
||||||
|
|
||||||
|
Vec3 ln1 = vec3_norm(vec3_cross(n, vec3_sub(p2, p1)));
|
||||||
|
if (vec3_dot(ln1, vec3_sub(p1, i)) >= 0) continue;
|
||||||
|
|
||||||
|
if (f->p[3] == INVALID_POINT) {
|
||||||
|
/* triangle */
|
||||||
|
Vec3 ln2 = vec3_norm(vec3_cross(n, vec3_sub(p0, p2)));
|
||||||
|
if (vec3_dot(ln2, vec3_sub(p2, i)) >= 0) continue;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* quad */
|
||||||
|
Vec3 p3 = point_to_vec3(oi, f->p[3]);
|
||||||
|
Vec3 ln2 = vec3_norm(vec3_cross(n, vec3_sub(p3, p2)));
|
||||||
|
if (vec3_dot(ln2, vec3_sub(p2, i)) >= 0) continue;
|
||||||
|
|
||||||
|
Vec3 ln3 = vec3_norm(vec3_cross(n, vec3_sub(p0, p3)));
|
||||||
|
if (vec3_dot(ln3, vec3_sub(p3, i)) >= 0) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
closest_distance = dist;
|
||||||
|
closest_face = fi;
|
||||||
|
closest_obj = oi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closest_face == INVALID_FACE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (object_result)
|
||||||
|
*object_result = closest_obj;
|
||||||
|
if (face_result)
|
||||||
|
*face_result = closest_face;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void show_snap_lines(Vec3 p) {
|
||||||
|
float step = 1.0f / ((float)POINTS_PER_METER / state.grid_snap_granularity);
|
||||||
|
int const lines_per_side = (SNAP_LINES_SHOW - 1) / 2;
|
||||||
|
|
||||||
|
for (int l = -lines_per_side; l <= lines_per_side; ++l) {
|
||||||
|
if (!state.axis_mask[0]) {
|
||||||
|
Vec3 c = vec3_add(p, vec3_scale((Vec3){1,0,0}, step * (float)l));
|
||||||
|
draw_line_3d(
|
||||||
|
vec3_add(c, vec3_scale((Vec3){0,0,1}, SNAP_LINES_WIDTH)),
|
||||||
|
vec3_add(c, vec3_scale((Vec3){0,0,1}, -SNAP_LINES_WIDTH)),
|
||||||
|
1,
|
||||||
|
SNAP_LINES_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.axis_mask[1]) {
|
||||||
|
Vec3 axis = fabsf(vec3_dot(state.camera_direction, (Vec3){0,0,1})) >= 0.5f ? (Vec3){1,0,0} : (Vec3){0,0,1};
|
||||||
|
Vec3 c = vec3_add(p, vec3_scale((Vec3){0,1,0}, step * (float)l));
|
||||||
|
draw_line_3d(
|
||||||
|
vec3_add(c, vec3_scale(axis, SNAP_LINES_WIDTH)),
|
||||||
|
vec3_add(c, vec3_scale(axis, -SNAP_LINES_WIDTH)),
|
||||||
|
1,
|
||||||
|
SNAP_LINES_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.axis_mask[2]) {
|
||||||
|
Vec3 c = vec3_add(p, vec3_scale((Vec3){0,0,1}, step * (float)l));
|
||||||
|
draw_line_3d(
|
||||||
|
vec3_add(c, vec3_scale((Vec3){1,0,0}, SNAP_LINES_WIDTH)),
|
||||||
|
vec3_add(c, vec3_scale((Vec3){1,0,0}, -SNAP_LINES_WIDTH)),
|
||||||
|
1,
|
||||||
|
SNAP_LINES_COLOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool process_operation_move_point(Operation *op) {
|
||||||
|
/* finish dragging around */
|
||||||
|
/* TODO: dont keep empty ops on stack? */
|
||||||
|
if (input_action_just_released("select")) {
|
||||||
|
state.op_active = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.05f;
|
||||||
|
draw_billboard("/data/grab.png",
|
||||||
|
point_to_vec3(op->data.move_point.object, op->data.move_point.point),
|
||||||
|
(Vec2){size, size},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
DrawCameraUnprojectResult cam = unproject_point(ctx.mouse_position);
|
||||||
|
Vec3 p = point_to_vec3(op->data.move_point.object, op->data.move_point.point);
|
||||||
|
bool point_moved = false;
|
||||||
|
|
||||||
|
/* figure out which planes are angled acutely against the viewing direction */
|
||||||
|
bool y_closer_than_horizon = fabsf(vec3_dot(state.camera_direction, (Vec3){0,1,0})) >= 0.5f;
|
||||||
|
|
||||||
|
Vec3 plane_normal_x = y_closer_than_horizon ? (Vec3){0,1,0} : (Vec3){0,0,1};
|
||||||
|
Vec3 plane_normal_z = y_closer_than_horizon ? (Vec3){0,1,0} : (Vec3){1,0,0};
|
||||||
|
|
||||||
|
/* show snapping in lines */
|
||||||
|
show_snap_lines(p);
|
||||||
|
|
||||||
|
Vec3 s;
|
||||||
|
if (!state.axis_mask[0]) {
|
||||||
|
if (vector_plane_intersection(cam.position, cam.direction, p, plane_normal_x, &s)) {
|
||||||
|
int16_t xch = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){1,0,0}) * (float)POINTS_PER_METER));
|
||||||
|
xch -= xch % state.grid_snap_granularity;
|
||||||
|
state.points[op->data.move_point.point].x += xch;
|
||||||
|
op->data.move_point.delta_x += xch;
|
||||||
|
if (xch != 0) point_moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.axis_mask[1]) {
|
||||||
|
if (vector_plane_intersection(cam.position, cam.direction, p, (Vec3){0,0,1}, &s)) {
|
||||||
|
int16_t ych = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){0,1,0}) * (float)POINTS_PER_METER));
|
||||||
|
ych -= ych % state.grid_snap_granularity;
|
||||||
|
state.points[op->data.move_point.point].y += ych;
|
||||||
|
op->data.move_point.delta_y += ych;
|
||||||
|
if (ych != 0) point_moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.axis_mask[2]) {
|
||||||
|
if (vector_plane_intersection(cam.position, cam.direction, p, plane_normal_z, &s)) {
|
||||||
|
int16_t zch = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){0,0,1}) * (float)POINTS_PER_METER));
|
||||||
|
zch -= zch % state.grid_snap_granularity;
|
||||||
|
state.points[op->data.move_point.point].z += zch;
|
||||||
|
op->data.move_point.delta_z += zch;
|
||||||
|
if (zch != 0) point_moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point_moved) {
|
||||||
|
audio_play("/data/bong.ogg", NULL, false, 0.12f, 0.0f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reverse_operation_move_point(Operation *op) {
|
||||||
|
SDL_assert(op->kind == OPERATION_MOVE_POINT);
|
||||||
|
state.points[op->data.move_point.point].x -= op->data.move_point.delta_x;
|
||||||
|
state.points[op->data.move_point.point].y -= op->data.move_point.delta_y;
|
||||||
|
state.points[op->data.move_point.point].z -= op->data.move_point.delta_z;
|
||||||
|
audio_play("/data/drop.ogg", NULL, false, 0.4f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reverse_operation_set_texture(Operation *op) {
|
||||||
|
SDL_assert(op->kind == OPERATION_SET_TEXTURE);
|
||||||
|
Object *o = &state.objects[op->data.set_texture.object];
|
||||||
|
o->faces[op->data.set_texture.face].texture += op->data.set_texture.delta_texture;
|
||||||
|
audio_play("/data/drop.ogg", NULL, false, 0.4f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reverse_triangulation(Operation *op) {
|
||||||
|
SDL_assert(op->kind == OPERATION_TRIANGULATE);
|
||||||
|
Object *o = &state.objects[op->data.set_texture.object];
|
||||||
|
Face *fn = &o->faces[op->data.triangulate.new_face];
|
||||||
|
Face *fo = &o->faces[op->data.triangulate.old_face];
|
||||||
|
fo->p[3] = fo->p[2];
|
||||||
|
fo->p[2] = fn->p[1];
|
||||||
|
pop_face(op->data.set_texture.object, op->data.triangulate.new_face);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: reverse of this */
|
||||||
|
static void try_subdividing_from_moving(uint8_t object, uint16_t point) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
bool not_first = false;
|
||||||
|
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
if (f->p[3] == INVALID_POINT) continue;
|
||||||
|
for (uint16_t pi = 0; pi < 4; ++pi) {
|
||||||
|
if (f->p[pi] == point) {
|
||||||
|
Face new0 = *f;
|
||||||
|
new0.p[0] = f->p[pi];
|
||||||
|
new0.p[1] = f->p[(pi + 1) % 4];
|
||||||
|
new0.p[2] = f->p[(pi + 3) % 4];
|
||||||
|
new0.p[3] = INVALID_POINT;
|
||||||
|
|
||||||
|
uint16_t newf = push_face(
|
||||||
|
object,
|
||||||
|
f->p[(pi + 1) % 4],
|
||||||
|
f->p[(pi + 2) % 4],
|
||||||
|
f->p[(pi + 3) % 4],
|
||||||
|
INVALID_POINT,
|
||||||
|
f->texture,
|
||||||
|
f->tex_scale,
|
||||||
|
f->tex_x,
|
||||||
|
f->tex_y);
|
||||||
|
|
||||||
|
*f = new0;
|
||||||
|
|
||||||
|
extend_operation((Operation){
|
||||||
|
.kind = OPERATION_TRIANGULATE,
|
||||||
|
.data = { .triangulate = { .new_face = newf, .old_face = fi, .object = object} },
|
||||||
|
.chained = not_first,
|
||||||
|
});
|
||||||
|
|
||||||
|
not_first = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_undo(void) {
|
||||||
|
if (state.op_active)
|
||||||
|
state.op_active = false;
|
||||||
|
|
||||||
|
/* TODO: checks and defined limit */
|
||||||
|
Operation *op = &state.op_stack[state.op_stack_ptr % UNDO_STACK_SIZE];
|
||||||
|
state.op_stack_ptr--;
|
||||||
|
|
||||||
|
switch (op->kind) {
|
||||||
|
case OPERATION_MOVE_POINT: {
|
||||||
|
reverse_operation_move_point(op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPERATION_SET_TEXTURE: {
|
||||||
|
reverse_operation_set_texture(op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPERATION_TRIANGULATE: {
|
||||||
|
reverse_triangulation(op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
(void)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pop another if they're chained together */
|
||||||
|
if (op->chained) process_undo();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_operations(void) {
|
||||||
|
if (input_action_just_pressed("undo"))
|
||||||
|
process_undo();
|
||||||
|
|
||||||
|
if (!state.op_active) {
|
||||||
|
/* point dragging */
|
||||||
|
if (!state.current_texture) {
|
||||||
|
uint16_t point_select; uint8_t obj_select;
|
||||||
|
if (find_closest_point(&obj_select, &point_select)) {
|
||||||
|
draw_billboard("/data/point.png",
|
||||||
|
point_to_vec3(obj_select, point_select),
|
||||||
|
(Vec2){0.05f, 0.05f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (input_action_just_pressed("select"))
|
||||||
|
push_operation((Operation){
|
||||||
|
.kind = OPERATION_MOVE_POINT,
|
||||||
|
.data = { .move_point = { .point = point_select, .object = obj_select } },
|
||||||
|
}, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* texture setting */
|
||||||
|
} else {
|
||||||
|
uint8_t obj_select; uint16_t face_select;
|
||||||
|
if (find_closest_face(&obj_select, &face_select)) {
|
||||||
|
state.current_hovered_face = face_select;
|
||||||
|
state.current_hovered_obj = obj_select;
|
||||||
|
|
||||||
|
if (input_action_just_pressed("rotate")) {
|
||||||
|
Face *f = &state.objects[obj_select].faces[face_select];
|
||||||
|
int16_t tex_x = f->tex_x;
|
||||||
|
int16_t tex_y = f->tex_y;
|
||||||
|
f->tex_x = -tex_y;
|
||||||
|
f->tex_y = tex_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_pressed("select") && state.current_texture) {
|
||||||
|
uint8_t new_tex = push_texture(obj_select, state.current_texture);
|
||||||
|
uint8_t cur_tex = state.objects[obj_select].faces[face_select].texture;
|
||||||
|
|
||||||
|
if (new_tex != cur_tex) {
|
||||||
|
state.objects[obj_select].faces[face_select].texture = new_tex;
|
||||||
|
audio_play("/data/bong.ogg", NULL, false, 0.5f, 0.0f);
|
||||||
|
|
||||||
|
push_operation((Operation){
|
||||||
|
.kind = OPERATION_SET_TEXTURE,
|
||||||
|
.data = { .set_texture = {
|
||||||
|
.face = face_select,
|
||||||
|
.object = obj_select,
|
||||||
|
.delta_texture = cur_tex - new_tex,
|
||||||
|
}},
|
||||||
|
}, false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.op_active) {
|
||||||
|
Operation *op = &state.op_stack[state.op_stack_ptr % UNDO_STACK_SIZE];
|
||||||
|
switch (op->kind) {
|
||||||
|
case OPERATION_MOVE_POINT: {
|
||||||
|
bool update = process_operation_move_point(op);
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
try_subdividing_from_moving(op->data.move_point.object, op->data.move_point.point);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPERATION_SET_TEXTURE:
|
||||||
|
case OPERATION_TRIANGULATE:
|
||||||
|
default:
|
||||||
|
(void)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_axes(void) {
|
||||||
|
/* axis helpers */
|
||||||
|
/* idea: double selection of axes for diagonal edits */
|
||||||
|
draw_line_3d(state.active_center, (Vec3){256,0,0}, 1, state.axis_mask[0] ? (Color){0,0,0,75} : (Color){255,0,0,125});
|
||||||
|
draw_billboard("/data/x.png",
|
||||||
|
vec3_add(state.active_center, (Vec3){2,0,0}),
|
||||||
|
(Vec2){0.1f, 0.1f},
|
||||||
|
(Rect){0},
|
||||||
|
state.axis_mask[0] ? (Color){0,0,0,255} : (Color){255,0,0,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
draw_line_3d(state.active_center, (Vec3){0,256,0}, 1, state.axis_mask[1] ? (Color){0,0,0,75} : (Color){75,125,25,125});
|
||||||
|
draw_billboard("/data/y.png",
|
||||||
|
vec3_add(state.active_center, (Vec3){0,1.5f,0}),
|
||||||
|
(Vec2){0.1f, 0.1f},
|
||||||
|
(Rect){0},
|
||||||
|
state.axis_mask[1] ? (Color){0,0,0,255} : (Color){75,125,25,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
draw_line_3d(state.active_center, (Vec3){0,0,256}, 1, state.axis_mask[2] ? (Color){0,0,0,75} : (Color){0,0,255,125});
|
||||||
|
draw_billboard("/data/z.png",
|
||||||
|
vec3_add(state.active_center, (Vec3){0,0,2}),
|
||||||
|
(Vec2){0.1f, 0.1f},
|
||||||
|
(Rect){0},
|
||||||
|
state.axis_mask[2] ? (Color){0,0,0,255} : (Color){0,0,255,255},
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_hovered_face_border(void) {
|
||||||
|
if (state.current_hovered_obj == INVALID_OBJECT || state.current_hovered_face == INVALID_FACE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Object *o = &state.objects[state.current_hovered_obj];
|
||||||
|
Face *f = &o->faces[state.current_hovered_face];
|
||||||
|
|
||||||
|
if (f->p[3] != INVALID_POINT) {
|
||||||
|
Vec3 p0 = point_to_vec3(state.current_hovered_obj, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(state.current_hovered_obj, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(state.current_hovered_obj, f->p[2]);
|
||||||
|
Vec3 p3 = point_to_vec3(state.current_hovered_obj, f->p[3]);
|
||||||
|
Vec3 center = vec3_scale(vec3_add(p2, p0), 0.5);
|
||||||
|
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.1f;
|
||||||
|
Vec3 c0 = vec3_add(p0, vec3_scale(vec3_sub(p0, center), size));
|
||||||
|
Vec3 c1 = vec3_add(p1, vec3_scale(vec3_sub(p1, center), size));
|
||||||
|
Vec3 c2 = vec3_add(p2, vec3_scale(vec3_sub(p2, center), size));
|
||||||
|
Vec3 c3 = vec3_add(p3, vec3_scale(vec3_sub(p3, center), size));
|
||||||
|
draw_line_3d(c0, c1, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c1, c2, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c2, c3, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c3, c0, 1, (Color){255,255,255,255});
|
||||||
|
} else {
|
||||||
|
Vec3 p0 = point_to_vec3(state.current_hovered_obj, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(state.current_hovered_obj, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(state.current_hovered_obj, f->p[2]);
|
||||||
|
Vec3 center = vec3_scale(vec3_add(p0, vec3_add(p1, p2)), 0.33f);
|
||||||
|
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.1f;
|
||||||
|
Vec3 c0 = vec3_add(p0, vec3_scale(vec3_sub(p0, center), size));
|
||||||
|
Vec3 c1 = vec3_add(p1, vec3_scale(vec3_sub(p1, center), size));
|
||||||
|
Vec3 c2 = vec3_add(p2, vec3_scale(vec3_sub(p2, center), size));
|
||||||
|
draw_line_3d(c0, c1, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c1, c2, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c2, c0, 1, (Color){255,255,255,255});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void display_texture_selection(void) {
|
||||||
|
String list = file_read("/data/assets/", ":images");
|
||||||
|
if (!list.data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
draw_rectangle((Rect){0, 480 - 50, 640, 480}, (Color){0,0,0,126});
|
||||||
|
|
||||||
|
char *selected = NULL;
|
||||||
|
char *saveptr = NULL;
|
||||||
|
int count = 0;
|
||||||
|
char const *part = SDL_strtokr(list.data, "\n", &saveptr);
|
||||||
|
do {
|
||||||
|
Rect box = (Rect){(float)count * 50, 480 - 48, 48, 48};
|
||||||
|
draw_sprite(part,
|
||||||
|
box,
|
||||||
|
(Rect){0,0,64,64},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
0, false, false, true);
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (state.current_texture && SDL_strcmp(part, state.current_texture) == 0)
|
||||||
|
draw_box(box, 1, (Color){255,255,255,255});
|
||||||
|
|
||||||
|
if (rect_intersects(box, (Rect){ctx.mouse_position.x, ctx.mouse_position.y, 1, 1}))
|
||||||
|
selected = SDL_strdup(part);
|
||||||
|
|
||||||
|
} while ((part = SDL_strtokr(NULL, "\n", &saveptr)));
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
draw_text(selected, (Vec2){0, 480 - 48 - 24}, 24, (Color){255,255,255,255}, NULL);
|
||||||
|
if (input_action_just_pressed("select")) {
|
||||||
|
if (state.current_texture) SDL_free(state.current_texture);
|
||||||
|
state.current_texture = selected;
|
||||||
|
} else
|
||||||
|
SDL_free(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_camera_inputs(void) {
|
||||||
|
if (input_action_just_pressed("toggle_display_mode")) {
|
||||||
|
audio_play("/data/click.wav", NULL, false, 0.7f, 0.0f);
|
||||||
|
state.solid_display_mode = !state.solid_display_mode;
|
||||||
|
if (state.current_texture) {
|
||||||
|
SDL_free(state.current_texture);
|
||||||
|
state.current_texture = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_projection")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = !state.camera_is_orthographic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_x_axis")) {
|
||||||
|
state.axis_mask[0] = 0; state.axis_mask[1] = 1; state.axis_mask[2] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_y_axis")) {
|
||||||
|
state.axis_mask[0] = 1; state.axis_mask[1] = 0; state.axis_mask[2] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_z_axis")) {
|
||||||
|
state.axis_mask[0] = 1; state.axis_mask[1] = 1; state.axis_mask[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: determine bounding box for this? */
|
||||||
|
/* TODO: no idea whether it's all correct in terms of directions. */
|
||||||
|
if (input_action_just_pressed("camera_front")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){0,0,-1};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){0,0,2});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("camera_back")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){0,0,1};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){0,0,-2});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("camera_left")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){1,0,0};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){-2,0,0});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("camera_right")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){-1,0,0};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){2,0,0});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: broken */
|
||||||
|
if (input_action_just_pressed("camera_above")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){0,-1,0};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){0,2,0});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: broken */
|
||||||
|
if (input_action_just_pressed("camera_below")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){0,1,0};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){0,-2,0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void game_tick(void) {
|
||||||
|
if (!init) {
|
||||||
|
/* default state */
|
||||||
|
new_cube((Point){0,0,0}, (Point){POINTS_PER_METER,POINTS_PER_METER,POINTS_PER_METER});
|
||||||
|
state.camera_position = (Vec3){2,1,2};
|
||||||
|
state.camera_direction = vec3_norm(((Vec3){-2,-1,-2}));
|
||||||
|
state.camera_zoom = 0.5f;
|
||||||
|
state.grid_snap_granularity = 16;
|
||||||
|
state.axis_mask[1] = 1; state.axis_mask[2] = 1;
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.current_hovered_face = INVALID_FACE;
|
||||||
|
state.current_hovered_obj = INVALID_OBJECT;
|
||||||
|
|
||||||
|
input_action("toggle_display_mode", "Q");
|
||||||
|
input_action("toggle_projection", "TAB");
|
||||||
|
|
||||||
|
input_action("toggle_x_axis", "Z");
|
||||||
|
input_action("toggle_y_axis", "X");
|
||||||
|
input_action("toggle_z_axis", "C");
|
||||||
|
|
||||||
|
input_action("select", "LCLICK");
|
||||||
|
input_action("rotate", "R");
|
||||||
|
input_action("undo", "F");
|
||||||
|
|
||||||
|
input_action("camera_front", "KP0");
|
||||||
|
input_action("camera_back", "KP1");
|
||||||
|
input_action("camera_left", "KP2");
|
||||||
|
input_action("camera_right", "KP3");
|
||||||
|
input_action("camera_above", "KP4");
|
||||||
|
input_action("camera_below", "KP5");
|
||||||
|
|
||||||
|
process_camera_inputs();
|
||||||
|
process_camera_movement();
|
||||||
|
process_operations();
|
||||||
|
|
||||||
|
/* helpres */
|
||||||
|
draw_axes();
|
||||||
|
draw_hovered_face_border();
|
||||||
|
|
||||||
|
for (uint8_t obj = 0; obj < state.objects_sz; ++obj)
|
||||||
|
render_object(obj);
|
||||||
|
|
||||||
|
if (state.solid_display_mode)
|
||||||
|
display_texture_selection();
|
||||||
|
|
||||||
|
draw_text("twndel\x03", (Vec2){0, 2}, 32, (Color){255,255,255,200}, NULL);
|
||||||
|
draw_camera(
|
||||||
|
state.camera_position,
|
||||||
|
state.camera_direction,
|
||||||
|
(Vec3){0, 1, 0},
|
||||||
|
state.camera_is_orthographic ? 0 : CAMERA_FOV,
|
||||||
|
state.camera_zoom,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void game_end(void) {
|
||||||
|
for (uint8_t obj = 0; obj < state.objects_sz; ++obj) {
|
||||||
|
Object *o = &state.objects[obj];
|
||||||
|
for (uint8_t t = 0; t < o->textures_sz; ++t)
|
||||||
|
SDL_free(o->textures[t]);
|
||||||
|
SDL_free(o->name);
|
||||||
|
}
|
||||||
|
if (state.current_texture) {
|
||||||
|
SDL_free(state.current_texture);
|
||||||
|
state.current_texture = NULL;
|
||||||
|
}
|
||||||
|
}
|
1
apps/twnlua/.gitignore
vendored
1
apps/twnlua/.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
luabind.c
|
luabind.c
|
||||||
|
data/scripts/twnapi.lua
|
||||||
|
@ -1,86 +1,36 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
cmake_minimum_required(VERSION 3.30)
|
||||||
|
|
||||||
project(twnlua LANGUAGES C)
|
project(twnlua LANGUAGES C)
|
||||||
|
|
||||||
|
find_package(Python3 COMPONENTS Interpreter)
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNROOT}/build)
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/game.c
|
||||||
COMMAND ${PYTHON3} ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json > ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json ${FLAGS} > ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
|
||||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${TWN_OUT_DIR}/data/scripts/twnapi.lua
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/docgen.py $ENV{TWNROOT}/share/twn_api.json > ${TWN_OUT_DIR}/data/scripts/twnapi.lua
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/docgen.py $ENV{TWNROOT}/share/twn_api.json
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(
|
||||||
|
twnlua_docgen ALL
|
||||||
|
DEPENDS ${TWN_OUT_DIR}/data/scripts/twnapi.lua
|
||||||
|
)
|
||||||
|
|
||||||
|
add_compile_definitions(LUA_32BITS=1)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
minilua.c
|
||||||
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
|
|
||||||
|
|
||||||
lua/src/lapi.c
|
|
||||||
lua/src/lapi.h
|
|
||||||
lua/src/lauxlib.c
|
|
||||||
lua/src/lauxlib.h
|
|
||||||
lua/src/lbaselib.c
|
|
||||||
lua/src/lcode.c
|
|
||||||
lua/src/lcode.h
|
|
||||||
lua/src/lcorolib.c
|
|
||||||
lua/src/lctype.c
|
|
||||||
lua/src/lctype.h
|
|
||||||
lua/src/ldblib.c
|
|
||||||
lua/src/ldebug.c
|
|
||||||
lua/src/ldebug.h
|
|
||||||
lua/src/ldo.c
|
|
||||||
lua/src/ldo.h
|
|
||||||
lua/src/ldump.c
|
|
||||||
lua/src/lfunc.c
|
|
||||||
lua/src/lfunc.h
|
|
||||||
lua/src/lgc.c
|
|
||||||
lua/src/lgc.h
|
|
||||||
lua/src/linit.c
|
|
||||||
lua/src/liolib.c
|
|
||||||
lua/src/ljumptab.h
|
|
||||||
lua/src/llex.c
|
|
||||||
lua/src/llex.h
|
|
||||||
lua/src/llimits.h
|
|
||||||
lua/src/lmathlib.c
|
|
||||||
lua/src/lmem.c
|
|
||||||
lua/src/lmem.h
|
|
||||||
lua/src/loadlib.c
|
|
||||||
lua/src/lobject.c
|
|
||||||
lua/src/lobject.h
|
|
||||||
lua/src/lopcodes.c
|
|
||||||
lua/src/lopcodes.h
|
|
||||||
lua/src/lopnames.h
|
|
||||||
lua/src/loslib.c
|
|
||||||
lua/src/lparser.c
|
|
||||||
lua/src/lparser.h
|
|
||||||
lua/src/lprefix.h
|
|
||||||
lua/src/lstate.c
|
|
||||||
lua/src/lstate.h
|
|
||||||
lua/src/lstring.c
|
|
||||||
lua/src/lstring.h
|
|
||||||
lua/src/lstrlib.c
|
|
||||||
lua/src/ltable.c
|
|
||||||
lua/src/ltable.h
|
|
||||||
lua/src/ltablib.c
|
|
||||||
lua/src/ltm.c
|
|
||||||
lua/src/ltm.h
|
|
||||||
lua/src/lua.h
|
|
||||||
lua/src/luaconf.h
|
|
||||||
lua/src/lualib.h
|
|
||||||
lua/src/lundump.c
|
|
||||||
lua/src/lundump.h
|
|
||||||
lua/src/lutf8lib.c
|
|
||||||
lua/src/lvm.c
|
|
||||||
lua/src/lvm.h
|
|
||||||
lua/src/lzio.c
|
|
||||||
lua/src/lzio.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
use_townengine("${SOURCE_FILES}" ${TWN_OUT_DIR})
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_SOURCE_DIR})
|
|
||||||
|
62
apps/twnlua/README.md
Normal file
62
apps/twnlua/README.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# MiniLua
|
||||||
|
|
||||||
|
This is Lua contained in a single header to be bundled in C/C++ applications with ease.
|
||||||
|
[Lua](https://www.lua.org/) is a powerful, efficient, lightweight, embeddable scripting language.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define LUA_IMPL
|
||||||
|
#include "minilua.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
lua_State *L = luaL_newstate();
|
||||||
|
if(L == NULL)
|
||||||
|
return -1;
|
||||||
|
luaL_openlibs(L);
|
||||||
|
luaL_loadstring(L, "print 'hello world'");
|
||||||
|
lua_call(L, 0, 0);
|
||||||
|
lua_close(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Copy `minilua.h` into your C or C++ project, include it anywhere you want to use Lua API.
|
||||||
|
Then do the following in *one* C file to implement Lua:
|
||||||
|
```c
|
||||||
|
#define LUA_IMPL
|
||||||
|
#include "minilua.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
By default it detects the system platform to use, however you can explicitly define one.
|
||||||
|
|
||||||
|
Note that almost no modification was made in the Lua implementation code,
|
||||||
|
thus there are some C variable names that may collide with your code,
|
||||||
|
therefore it is best to declare the Lua implementation in dedicated C file.
|
||||||
|
|
||||||
|
Optionally provide the following defines:
|
||||||
|
- `LUA_MAKE_LUA` - implement the Lua command line REPL
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For documentation on how to use Lua read its [official manual](https://www.lua.org/manual/).
|
||||||
|
|
||||||
|
## Updates
|
||||||
|
|
||||||
|
- **25-Jul-2024**: Updated to Lua 5.4.7.
|
||||||
|
- **13-Nov-2023**: Updated to Lua 5.4.6.
|
||||||
|
- **28-Jan-2022**: Updated to Lua 5.4.4.
|
||||||
|
- **31-Mar-2021**: Updated to Lua 5.4.3.
|
||||||
|
- **03-Dec-2020**: Updated to Lua 5.4.2.
|
||||||
|
- **27-Nov-2020**: Library created, using Lua 5.4.2-rc1.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
This library tries to keep up with latest official Lua release.
|
||||||
|
The header is generated using the bash script `gen.sh` all modifications done is there.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Same license as Lua, the MIT license, see LICENSE.txt for information.
|
@ -11,7 +11,8 @@ api = json.loads(api_source)
|
|||||||
def default(parameter):
|
def default(parameter):
|
||||||
basetype = parameter["type"].rsplit(' *', 1)[0]
|
basetype = parameter["type"].rsplit(' *', 1)[0]
|
||||||
if parameter["type"] == "float":
|
if parameter["type"] == "float":
|
||||||
return parameter["default"]
|
s = str(parameter["default"])
|
||||||
|
return s + 'f' if '.' in s else s + '.f'
|
||||||
elif parameter["type"] == "bool":
|
elif parameter["type"] == "bool":
|
||||||
return "true" if parameter["default"] else "false"
|
return "true" if parameter["default"] else "false"
|
||||||
elif parameter["type"] == "char *":
|
elif parameter["type"] == "char *":
|
||||||
@ -35,7 +36,7 @@ def to_table(typedesc, variable, indent = 0):
|
|||||||
binding = ' ' * indent + "lua_createtable(L, 0, %i);\n" % len(typedesc["fields"])
|
binding = ' ' * indent + "lua_createtable(L, 0, %i);\n" % len(typedesc["fields"])
|
||||||
for field in typedesc["fields"]:
|
for field in typedesc["fields"]:
|
||||||
if field["type"] == "float" or field["type"] == "uint8_t":
|
if field["type"] == "float" or field["type"] == "uint8_t":
|
||||||
binding += ' ' * indent + "lua_pushnumber(L, (double)(%s));\n" % (variable + ".%s" % field["name"])
|
binding += ' ' * indent + "lua_pushnumber(L, (float)(%s));\n" % (variable + ".%s" % field["name"])
|
||||||
elif field["type"] == "bool":
|
elif field["type"] == "bool":
|
||||||
binding += ' ' * indent + "lua_pushboolean(L, (%s));\n" % (variable + ".%s" % field["name"])
|
binding += ' ' * indent + "lua_pushboolean(L, (%s));\n" % (variable + ".%s" % field["name"])
|
||||||
elif field["type"] in api["types"]:
|
elif field["type"] in api["types"]:
|
||||||
@ -51,7 +52,7 @@ def from_table(typedesc, variable, indent = 0):
|
|||||||
for field in typedesc["fields"]:
|
for field in typedesc["fields"]:
|
||||||
binding += ' ' * indent + "lua_getfield(L, -1, \"%s\");\n" % field["name"]
|
binding += ' ' * indent + "lua_getfield(L, -1, \"%s\");\n" % field["name"]
|
||||||
if field["type"] == "float" or field["type"] == "uint8_t":
|
if field["type"] == "float" or field["type"] == "uint8_t":
|
||||||
binding += ' ' * indent + "%s = (%s)lua_tonumber(L, -1);\n" % (variable + ".%s" % field["name"], field["type"])
|
binding += ' ' * indent + "%s = (%s)(lua_tonumber(L, -1));\n" % (variable + ".%s" % field["name"], field["type"])
|
||||||
elif field["type"] == "bool":
|
elif field["type"] == "bool":
|
||||||
binding += ' ' * indent + "%s = lua_toboolean(L, -1);\n" % (variable + ".%s" % field["name"])
|
binding += ' ' * indent + "%s = lua_toboolean(L, -1);\n" % (variable + ".%s" % field["name"])
|
||||||
elif field["type"] in api["types"]:
|
elif field["type"] in api["types"]:
|
||||||
@ -63,12 +64,7 @@ def from_table(typedesc, variable, indent = 0):
|
|||||||
|
|
||||||
|
|
||||||
print('#include "twn_game_api.h"\n')
|
print('#include "twn_game_api.h"\n')
|
||||||
# TODO: reuse implementation from the engine, this also breaks with statically compiled build
|
print('/* assumed to be included from game.c, where minilua.h is already present */\n')
|
||||||
print('#define STB_DS_IMPLEMENTATION')
|
|
||||||
print('#include <stb_ds.h>')
|
|
||||||
print('#include <lua.h>')
|
|
||||||
print('#include <lualib.h>')
|
|
||||||
print('#include <lauxlib.h>\n')
|
|
||||||
|
|
||||||
bindings, used_converters = [], {}
|
bindings, used_converters = [], {}
|
||||||
for procedure, procedure_desc in api["procedures"].items():
|
for procedure, procedure_desc in api["procedures"].items():
|
||||||
@ -84,7 +80,7 @@ for procedure, procedure_desc in api["procedures"].items():
|
|||||||
binding += " %s %s;\n" % (parameter["type"] if not parameter["type"].endswith("*") else 'const ' + parameter["type"], parameter["name"])
|
binding += " %s %s;\n" % (parameter["type"] if not parameter["type"].endswith("*") else 'const ' + parameter["type"], parameter["name"])
|
||||||
binding += " lua_getfield(L, 1, \"%s\");\n" % parameter["name"]
|
binding += " lua_getfield(L, 1, \"%s\");\n" % parameter["name"]
|
||||||
|
|
||||||
if "default" in parameter and parameter["type"] != "float":
|
if "default" in parameter and not parameter["type"] in ("float", "bool"):
|
||||||
binding += " if (lua_isnoneornil(L, -1))\n"
|
binding += " if (lua_isnoneornil(L, -1))\n"
|
||||||
binding += " %s = %s;\n" % (parameter["name"], default(parameter))
|
binding += " %s = %s;\n" % (parameter["name"], default(parameter))
|
||||||
binding += " else\n "
|
binding += " else\n "
|
||||||
@ -92,10 +88,10 @@ for procedure, procedure_desc in api["procedures"].items():
|
|||||||
if parameter["type"] == "float":
|
if parameter["type"] == "float":
|
||||||
if "default" in parameter:
|
if "default" in parameter:
|
||||||
binding += " int is_%s_num;\n" % parameter["name"]
|
binding += " int is_%s_num;\n" % parameter["name"]
|
||||||
binding += " %s = (float)lua_tonumberx(L, -1, &is_%s_num);\n" % (parameter["name"], parameter["name"]);
|
binding += " %s = (float)(lua_tonumberx(L, -1, &is_%s_num));\n" % (parameter["name"], parameter["name"]);
|
||||||
binding += " if (!is_%s_num) %s = %s;\n" % (parameter["name"], parameter["name"], default(parameter))
|
binding += " if (!is_%s_num) %s = %s;\n" % (parameter["name"], parameter["name"], default(parameter))
|
||||||
else:
|
else:
|
||||||
binding += " %s = (float)lua_tonumber(L, -1);\n" % (parameter["name"]);
|
binding += " %s = (float)(lua_tonumber(L, -1));\n" % (parameter["name"]);
|
||||||
elif parameter["type"] == "bool":
|
elif parameter["type"] == "bool":
|
||||||
binding += " %s = lua_toboolean(L, -1);\n" % (parameter["name"]);
|
binding += " %s = lua_toboolean(L, -1);\n" % (parameter["name"]);
|
||||||
elif parameter["type"] == "char *":
|
elif parameter["type"] == "char *":
|
||||||
@ -113,11 +109,13 @@ for procedure, procedure_desc in api["procedures"].items():
|
|||||||
if procedure_desc["return"] == "bool":
|
if procedure_desc["return"] == "bool":
|
||||||
binding += " lua_pushboolean(L, (int)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
binding += " lua_pushboolean(L, (int)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
elif procedure_desc["return"] == "float":
|
elif procedure_desc["return"] == "float":
|
||||||
binding += " lua_pushnumber(L, (double)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
binding += " lua_pushnumber(L, (float)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
elif procedure_desc["return"] == "char *":
|
elif procedure_desc["return"] == "char *":
|
||||||
binding += " lua_pushstring(L, %s(%s));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
binding += " lua_pushstring(L, %s(%s));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
|
elif procedure_desc["return"] == "String":
|
||||||
|
binding += " String result = %s(%s);\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
|
binding += " lua_pushlstring(L, result.data, (int)result.length);\n"
|
||||||
elif type(procedure_desc["return"]) is dict or procedure_desc["return"] in api["types"]:
|
elif type(procedure_desc["return"]) is dict or procedure_desc["return"] in api["types"]:
|
||||||
# TODO: handle enums
|
|
||||||
type_desc = procedure_desc["return"] if type(procedure_desc["return"]) is dict else api["types"][procedure_desc["return"]]
|
type_desc = procedure_desc["return"] if type(procedure_desc["return"]) is dict else api["types"][procedure_desc["return"]]
|
||||||
binding += " %s result = %s(%s);\n" % (type_desc["c_type"], procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
binding += " %s result = %s(%s);\n" % (type_desc["c_type"], procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
binding += to_table(type_desc, "result", 4)
|
binding += to_table(type_desc, "result", 4)
|
||||||
@ -141,45 +139,27 @@ for typename, typedesc in used_converters.items():
|
|||||||
converter += " %s %s;\n" % (typename, typename.lower());
|
converter += " %s %s;\n" % (typename, typename.lower());
|
||||||
|
|
||||||
if "fields" in typedesc:
|
if "fields" in typedesc:
|
||||||
for field in typedesc["fields"]:
|
for idx, field in enumerate(typedesc["fields"]):
|
||||||
converter += " lua_getfield(L, -1, \"%s\");\n" % (field["name"]);
|
converter += " lua_getfield(L, -%i, \"%s\");\n" % (idx + 1, field["name"]);
|
||||||
if field["type"] == "float":
|
if field["type"] == "float":
|
||||||
converter += " %s.%s = (float)lua_tonumber(L, -1);\n" % (typename.lower(), field["name"]);
|
converter += " %s.%s = (float)lua_tonumber(L, -1);\n" % (typename.lower(), field["name"]);
|
||||||
elif field["type"] == "uint8_t":
|
elif field["type"] == "uint8_t":
|
||||||
converter += " %s.%s = (uint8_t)lua_tointeger(L, -1);\n" % (typename.lower(), field["name"]);
|
converter += " %s.%s = (uint8_t)lua_tointeger(L, -1);\n" % (typename.lower(), field["name"]);
|
||||||
else:
|
else:
|
||||||
raise BaseException("Unhandled converter field type '%s'" % (field["type"]))
|
raise BaseException("Unhandled converter field type '%s'" % (field["type"]))
|
||||||
converter += " lua_pop(L, 1);\n";
|
converter += " lua_pop(L, %i);\n" % len(typedesc["fields"]);
|
||||||
|
|
||||||
# TODO: wild idea: use compile time built hash table
|
|
||||||
elif "enums" in typedesc:
|
|
||||||
storages += ["struct %sHashItem { char *key; %s value; };\nstatic struct %sHashItem *%s_map = NULL;\n" % (typename, typename, typename, typename.lower())]
|
|
||||||
|
|
||||||
# TODO: use arena
|
|
||||||
for enum in typedesc["enums"]:
|
|
||||||
initializer = " shput(%s_map, \"%s\", %s);" % (typename.lower(), enum, typedesc["enums"][enum])
|
|
||||||
initializers += [initializer]
|
|
||||||
|
|
||||||
deinitializers += [" shfree(%s_map);" % typename.lower()]
|
|
||||||
|
|
||||||
converter += " char const *value = lua_tostring(L, -1);\n";
|
|
||||||
converter += " %s = shget(%s_map, value);\n" % (typename.lower(), typename.lower())
|
|
||||||
converter += " lua_pop(L, 1);\n";
|
|
||||||
|
|
||||||
converter += " return %s;\n}\n" % (typename.lower())
|
converter += " return %s;\n}\n" % (typename.lower())
|
||||||
converters += [converter]
|
converters += [converter]
|
||||||
|
|
||||||
|
|
||||||
print('\n'.join(storages))
|
print('\n'.join(storages))
|
||||||
print("extern void bindgen_init(void);\n")
|
|
||||||
print("void bindgen_init(void) {\n" + '\n'.join(initializers) + "\n}\n")
|
|
||||||
print('\n'.join(converters))
|
print('\n'.join(converters))
|
||||||
print('\n'.join(bindings))
|
print('\n'.join(bindings))
|
||||||
|
|
||||||
|
|
||||||
loader = "extern void bindgen_load_%s(lua_State *L);\n" % api["name"]
|
loader = "extern void bindgen_load_%s(lua_State *L);\n" % api["name"]
|
||||||
loader += "void bindgen_load_%s(lua_State *L) {\n" % api["name"]
|
loader += "void bindgen_load_%s(lua_State *L) {\n" % api["name"]
|
||||||
loader += " bindgen_init();\n"
|
|
||||||
for procedure, procedure_desc in api["procedures"].items():
|
for procedure, procedure_desc in api["procedures"].items():
|
||||||
loader += " lua_pushcfunction(L, binding_%s);\n" % procedure
|
loader += " lua_pushcfunction(L, binding_%s);\n" % procedure
|
||||||
loader += " lua_setglobal(L, \"%s\");\n" % procedure
|
loader += " lua_setglobal(L, \"%s\");\n" % procedure
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[[deps]]
|
[[deps]]
|
||||||
source = "../../common-data" # where does it come from, might be an url
|
source = "../../data" # where does it come from, might be an url
|
||||||
name = "common-data" # should be globally unique
|
name = "common-data" # should be globally unique
|
||||||
|
@ -4,16 +4,39 @@ offset = { x = 0, y = 0 }
|
|||||||
angle = 0
|
angle = 0
|
||||||
|
|
||||||
function game_tick()
|
function game_tick()
|
||||||
|
if ctx.udata == nil then
|
||||||
|
ctx.udata = {
|
||||||
|
frame_count = 0,
|
||||||
|
nest = {
|
||||||
|
frame_count = 0,
|
||||||
|
},
|
||||||
|
arr = { [0] = 0 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
draw_text {
|
||||||
|
string = tostring(ctx.udata.frame_count),
|
||||||
|
position = { x = 0, y = 0 },
|
||||||
|
font = "/fonts/kenney-pixel.ttf",
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_text {
|
||||||
|
string = tostring(ctx.udata.nest.frame_count),
|
||||||
|
position = { x = 0, y = 14 },
|
||||||
|
font = "/fonts/kenney-pixel.ttf",
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_text {
|
||||||
|
string = tostring(ctx.udata.arr[0]),
|
||||||
|
position = { x = 0, y = 28 },
|
||||||
|
font = "/fonts/kenney-pixel.ttf",
|
||||||
|
}
|
||||||
|
|
||||||
input_action {
|
input_action {
|
||||||
name = "press",
|
name = "press",
|
||||||
control = "A"
|
control = "A"
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_rectangle {
|
|
||||||
rect = { x = 0, y = 0, w = 640, h = 360 },
|
|
||||||
color = { r = 127, g = 0, b = 127, a = 255 },
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_sprite {
|
draw_sprite {
|
||||||
texture = "/assets/title.png",
|
texture = "/assets/title.png",
|
||||||
rect = {
|
rect = {
|
||||||
@ -32,6 +55,10 @@ function game_tick()
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ctx.udata.frame_count = ctx.udata.frame_count + 1
|
||||||
|
ctx.udata.nest.frame_count = ctx.udata.nest.frame_count + 1
|
||||||
|
ctx.udata.arr[0] = ctx.udata.arr[0] + 1
|
||||||
|
|
||||||
offset.x = ORIGIN.x + (math.cos(angle) * RADIUS)
|
offset.x = ORIGIN.x + (math.cos(angle) * RADIUS)
|
||||||
offset.y = ORIGIN.y + (math.sin(angle) * RADIUS)
|
offset.y = ORIGIN.y + (math.sin(angle) * RADIUS)
|
||||||
angle = angle + 0.1
|
angle = angle + 0.1
|
||||||
|
@ -6,5 +6,6 @@ dev_id = "somebody"
|
|||||||
|
|
||||||
[game]
|
[game]
|
||||||
resolution = [ 640, 360 ]
|
resolution = [ 640, 360 ]
|
||||||
|
background_color = [ 127, 0, 127, 255 ]
|
||||||
|
|
||||||
[engine]
|
[engine]
|
||||||
|
55
apps/twnlua/docgen.py
Executable file
55
apps/twnlua/docgen.py
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/env python3
|
||||||
|
|
||||||
|
import sys, json
|
||||||
|
|
||||||
|
with open(sys.argv[1], 'r') if sys.argv[1] != "-" else sys.stdin as f:
|
||||||
|
api_source = f.read()
|
||||||
|
|
||||||
|
api = json.loads(api_source)
|
||||||
|
|
||||||
|
def to_lua_type_annot(typedesc):
|
||||||
|
if type(typedesc) is dict:
|
||||||
|
return r'{ %s }' % ','.join('%s: %s' % (f["name"], to_lua_type_annot(f["type"])) for f in typedesc["fields"])
|
||||||
|
basetype = typedesc.rsplit(' *', 1)[0]
|
||||||
|
if typedesc == "char *":
|
||||||
|
return "string"
|
||||||
|
elif basetype == "float":
|
||||||
|
return "number"
|
||||||
|
elif basetype == "bool":
|
||||||
|
return "boolean"
|
||||||
|
elif basetype == "Vec2":
|
||||||
|
return r"{ x: number, y: number }"
|
||||||
|
elif basetype == "Vec3":
|
||||||
|
return r"{ x: number, y: number, z: number }"
|
||||||
|
elif basetype == "Color":
|
||||||
|
return r"{ r: number, g: number, b: number, a: number }"
|
||||||
|
elif basetype == "Rect":
|
||||||
|
return r"{ x: number, y: number, w: number, h: number }"
|
||||||
|
else:
|
||||||
|
return "unknown"
|
||||||
|
# raise BaseException("Unhandled type for annotation: %s" % typedesc)
|
||||||
|
|
||||||
|
print("---@meta twn")
|
||||||
|
print("---@diagnostic disable")
|
||||||
|
|
||||||
|
type_annotations = {}
|
||||||
|
type_annotations["ctx"] = r"{ %s, udata: table }" % \
|
||||||
|
', '.join("%s: %s" % (f["name"], to_lua_type_annot(f["type"])) for f in api["types"]["Context"]["fields"])
|
||||||
|
|
||||||
|
for annot in type_annotations:
|
||||||
|
print("---@type " + type_annotations[annot])
|
||||||
|
print(r"%s = nil" % annot)
|
||||||
|
|
||||||
|
procedure_annotations = {}
|
||||||
|
for procedure, procedure_desc in api["procedures"].items():
|
||||||
|
procedure_annotations[procedure] = {}
|
||||||
|
procedure_annotations[procedure]["params"] = r"{ %s }" % \
|
||||||
|
', '.join("%s: %s" % (p["name"], to_lua_type_annot(p["type"]) + '?' * ("default" in p)) for p in procedure_desc["params"])
|
||||||
|
if "return" in procedure_desc:
|
||||||
|
procedure_annotations[procedure]["return"] = to_lua_type_annot(procedure_desc["return"])
|
||||||
|
|
||||||
|
for annot in procedure_annotations:
|
||||||
|
print("---@param args " + procedure_annotations[annot]["params"])
|
||||||
|
if "return" in procedure_annotations[annot]:
|
||||||
|
print("---@return " + procedure_annotations[annot]["return"])
|
||||||
|
print("function %s(args) end" % annot)
|
@ -1,13 +1,14 @@
|
|||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
|
/* TODO: actually move it back it its own file, it doesn't give any compilation benefits */
|
||||||
|
#include "minilua.h"
|
||||||
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
#include "luabind.c"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include <lua.h>
|
#define UDATA_NESTING_LIMIT 128
|
||||||
#include <lualib.h>
|
|
||||||
#include <lauxlib.h>
|
|
||||||
|
|
||||||
#include <malloc.h>
|
|
||||||
|
|
||||||
|
|
||||||
/* generated by bindgen.py */
|
/* generated by bindgen.py */
|
||||||
@ -18,29 +19,49 @@ void bindgen_upload_context(lua_State *L);
|
|||||||
|
|
||||||
|
|
||||||
/* require will go through physicsfs exclusively so that scripts can be in the data dir */
|
/* require will go through physicsfs exclusively so that scripts can be in the data dir */
|
||||||
|
/* TODO: allow for bytecode files */
|
||||||
|
/* TODO: support .lua suffixes files? */
|
||||||
static int physfs_loader(lua_State *L) {
|
static int physfs_loader(lua_State *L) {
|
||||||
const char *name = luaL_checkstring(L, 1);
|
const char *name = luaL_checkstring(L, 1);
|
||||||
char *final_path = NULL;
|
|
||||||
SDL_asprintf(&final_path, "%s.lua", name);
|
|
||||||
|
|
||||||
if (!file_exists(final_path)) {
|
static const char *name_breaker = NULL;
|
||||||
|
if (name_breaker && SDL_strcmp(name, name_breaker) == 0) {
|
||||||
|
log_string(name_breaker, "Recursive load on itself from lua module");
|
||||||
|
return 0;
|
||||||
|
} name_breaker = name;
|
||||||
|
|
||||||
|
/* replace dots with path slashes */
|
||||||
|
char *path_copy = SDL_strdup(name);
|
||||||
|
char *ch = NULL;
|
||||||
|
while ((ch = SDL_strchr(path_copy, '.')))
|
||||||
|
*ch = '/';
|
||||||
|
|
||||||
|
char *final_path = NULL;
|
||||||
|
SDL_asprintf(&final_path, "/scripts/%s.lua", path_copy);
|
||||||
|
SDL_free(path_copy);
|
||||||
|
|
||||||
|
if (SDL_strcmp(file_read(final_path, ":exists").data, "yes") == 0) {
|
||||||
char *error_message = NULL;
|
char *error_message = NULL;
|
||||||
SDL_asprintf(&error_message, "could not find module %s in filesystem", name);
|
SDL_asprintf(&error_message, "could not find module %s in filesystem", name);
|
||||||
lua_pushstring(L, error_message);
|
lua_pushstring(L, error_message);
|
||||||
free(error_message);
|
SDL_free(error_message);
|
||||||
|
|
||||||
free(final_path);
|
SDL_free(final_path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *buf = NULL;
|
char *final_path_binary = NULL;
|
||||||
int64_t buf_size = file_to_bytes(final_path, &buf);
|
SDL_asprintf(&final_path_binary, "%s%s", final_path, ":binary");
|
||||||
free(final_path);
|
|
||||||
|
|
||||||
luaL_loadbuffer(L, (char *)buf, buf_size, name);
|
String file = file_read(final_path, ":binary");
|
||||||
free(buf);
|
SDL_free(final_path);
|
||||||
|
|
||||||
return 1;
|
/* TODO: use reader interface for streaming instead */
|
||||||
|
int const result = luaL_loadbuffer(L, file.data, (size_t)file.length, name);
|
||||||
|
if (result != LUA_OK)
|
||||||
|
log_string(lua_tostring(L, -1), NULL);
|
||||||
|
|
||||||
|
return result == LUA_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -87,21 +108,76 @@ static void *custom_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void exchange_lua_states(lua_State *from, lua_State *to, int level, int index) {
|
||||||
|
if (level >= UDATA_NESTING_LIMIT) {
|
||||||
|
log_string("ctx.udata nesting limit is reached", NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: use arrays for optimized paths */
|
||||||
|
/* TODO: preallocate table records */
|
||||||
|
switch (lua_type(from, index)) {
|
||||||
|
case LUA_TTABLE:
|
||||||
|
lua_newtable(to);
|
||||||
|
lua_pushnil(from); /* first key */
|
||||||
|
while (lua_next(from, index - 1) != 0) {
|
||||||
|
/* 'key' at index -2 and 'value' at index -1 */
|
||||||
|
exchange_lua_states(from, to, level + 1, -2);
|
||||||
|
exchange_lua_states(from, to, level + 1, -1);
|
||||||
|
lua_settable(to, index - 2);
|
||||||
|
/* removes 'value'; keeps 'key' for next iteration */
|
||||||
|
lua_pop(from, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LUA_TNUMBER:
|
||||||
|
lua_pushnumber(to, lua_tonumber(from, index));
|
||||||
|
break;
|
||||||
|
case LUA_TBOOLEAN:
|
||||||
|
lua_pushboolean(to, lua_toboolean(from, index));
|
||||||
|
break;
|
||||||
|
case LUA_TSTRING:
|
||||||
|
lua_pushstring(to, lua_tostring(from, index));
|
||||||
|
break;
|
||||||
|
case LUA_TNIL:
|
||||||
|
lua_pushnil(to);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* TODO: provide a path and type of it for better diagnostic */
|
||||||
|
log_string("Unserializable udata found and is ignored", NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
if (!ctx.udata)
|
if (!ctx.udata)
|
||||||
ctx.udata = ccalloc(1, sizeof (State));
|
ctx.udata = SDL_calloc(1, sizeof (State));
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
/* let's init lua */
|
/* let's init lua */
|
||||||
/* state existed already */
|
lua_State *new_state = lua_newstate(custom_alloc, NULL);
|
||||||
|
lua_setallocf(new_state, custom_alloc, NULL);
|
||||||
|
|
||||||
|
/* state existed already, copy its udata over */
|
||||||
if (state->L != NULL) {
|
if (state->L != NULL) {
|
||||||
|
lua_getglobal(state->L, "ctx");
|
||||||
|
lua_getfield(state->L, -1, "udata");
|
||||||
|
SDL_assert(!lua_isnoneornil(state->L, -1));
|
||||||
|
SDL_assert(!lua_isnoneornil(state->L, -2));
|
||||||
|
if (!lua_isnoneornil(state->L, -1)) {
|
||||||
|
lua_newtable(new_state);
|
||||||
|
exchange_lua_states(state->L, new_state, 0, -1);
|
||||||
|
lua_setfield(new_state, -2, "udata");
|
||||||
|
lua_setglobal(new_state, "ctx");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bye :) */
|
||||||
lua_close(state->L);
|
lua_close(state->L);
|
||||||
}
|
}
|
||||||
state->L = luaL_newstate();
|
|
||||||
|
|
||||||
lua_setallocf(state->L, custom_alloc, NULL);
|
state->L = new_state;
|
||||||
|
|
||||||
/* fakey version of luaL_openlibs() that excludes file i/o and os stuff */
|
/* fakey version of luaL_openlibs() that excludes file i/o and os stuff */
|
||||||
{
|
{
|
||||||
@ -136,27 +212,30 @@ void game_tick(void) {
|
|||||||
lua_pop(state->L, 2);
|
lua_pop(state->L, 2);
|
||||||
|
|
||||||
/* binding */
|
/* binding */
|
||||||
// lua_register(state->L, "sprite", b_sprite);
|
|
||||||
// lua_register(state->L, "rectangle", b_rectangle);
|
|
||||||
// lua_register(state->L, "text", b_text);
|
|
||||||
|
|
||||||
bindgen_load_twn(state->L);
|
bindgen_load_twn(state->L);
|
||||||
|
|
||||||
/* now finally get to running the code */
|
/* now finally get to running the code */
|
||||||
unsigned char *game_buf = NULL;
|
String file = file_read("/scripts/game.lua", ":binary");
|
||||||
size_t game_buf_size = file_to_bytes("/scripts/game.lua", &game_buf);
|
/* TODO: use reader interface for streaming instead */
|
||||||
if (luaL_loadbuffer(state->L, (char *)game_buf, game_buf_size, "game.lua") == LUA_OK) {
|
if (luaL_loadbuffer(state->L, file.data, (size_t)file.length, "game.lua") == LUA_OK) {
|
||||||
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
|
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
|
||||||
log_critical("%s", lua_tostring(state->L, -1));
|
log_string(luaL_tolstring(state->L, -1, NULL), "Error executing /scripts/game.lua entry");
|
||||||
|
lua_pop(state->L, 1);
|
||||||
|
} else
|
||||||
|
state->loaded_successfully = true;
|
||||||
|
} else {
|
||||||
|
/* got some sort of error, it should be pushed on top of the stack */
|
||||||
|
SDL_assert(lua_isstring(state->L, -1));
|
||||||
|
log_string(luaL_tolstring(state->L, -1, NULL), "Error loading /scripts/game.lua entry");
|
||||||
lua_pop(state->L, 1);
|
lua_pop(state->L, 1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
free(game_buf);
|
|
||||||
/* from this point we have access to everything defined in lua */
|
/* from this point we have access to everything defined in lua */
|
||||||
}
|
}
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
|
if (state->loaded_successfully) {
|
||||||
bindgen_build_context(state->L);
|
bindgen_build_context(state->L);
|
||||||
lua_getglobal(state->L, "ctx");
|
lua_getglobal(state->L, "ctx");
|
||||||
if (!lua_isnoneornil(state->L, -1)) {
|
if (!lua_isnoneornil(state->L, -1)) {
|
||||||
@ -168,12 +247,13 @@ void game_tick(void) {
|
|||||||
|
|
||||||
lua_getglobal(state->L, "game_tick");
|
lua_getglobal(state->L, "game_tick");
|
||||||
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
|
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
|
||||||
log_critical("%s", lua_tostring(state->L, -1));
|
log_string(luaL_tolstring(state->L, -1, NULL), "Error executing game_tick()");
|
||||||
lua_pop(state->L, 1);
|
lua_pop(state->L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_getglobal(state->L, "ctx");
|
lua_getglobal(state->L, "ctx");
|
||||||
bindgen_upload_context(state->L);
|
bindgen_upload_context(state->L);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -181,5 +261,5 @@ void game_end(void) {
|
|||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
bindgen_unload_twn(state->L);
|
bindgen_unload_twn(state->L);
|
||||||
lua_close(state->L);
|
lua_close(state->L);
|
||||||
free(state);
|
SDL_free(state);
|
||||||
}
|
}
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
# Makefile for installing Lua
|
|
||||||
# See doc/readme.html for installation and customization instructions.
|
|
||||||
|
|
||||||
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
|
|
||||||
|
|
||||||
# Your platform. See PLATS for possible values.
|
|
||||||
PLAT= guess
|
|
||||||
|
|
||||||
# Where to install. The installation starts in the src and doc directories,
|
|
||||||
# so take care if INSTALL_TOP is not an absolute path. See the local target.
|
|
||||||
# You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with
|
|
||||||
# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h.
|
|
||||||
INSTALL_TOP= /usr/local
|
|
||||||
INSTALL_BIN= $(INSTALL_TOP)/bin
|
|
||||||
INSTALL_INC= $(INSTALL_TOP)/include
|
|
||||||
INSTALL_LIB= $(INSTALL_TOP)/lib
|
|
||||||
INSTALL_MAN= $(INSTALL_TOP)/man/man1
|
|
||||||
INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
|
|
||||||
INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V
|
|
||||||
|
|
||||||
# How to install. If your install program does not support "-p", then
|
|
||||||
# you may have to run ranlib on the installed liblua.a.
|
|
||||||
INSTALL= install -p
|
|
||||||
INSTALL_EXEC= $(INSTALL) -m 0755
|
|
||||||
INSTALL_DATA= $(INSTALL) -m 0644
|
|
||||||
#
|
|
||||||
# If you don't have "install" you can use "cp" instead.
|
|
||||||
# INSTALL= cp -p
|
|
||||||
# INSTALL_EXEC= $(INSTALL)
|
|
||||||
# INSTALL_DATA= $(INSTALL)
|
|
||||||
|
|
||||||
# Other utilities.
|
|
||||||
MKDIR= mkdir -p
|
|
||||||
RM= rm -f
|
|
||||||
|
|
||||||
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
|
|
||||||
|
|
||||||
# Convenience platforms targets.
|
|
||||||
PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
|
|
||||||
|
|
||||||
# What to install.
|
|
||||||
TO_BIN= lua luac
|
|
||||||
TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp
|
|
||||||
TO_LIB= liblua.a
|
|
||||||
TO_MAN= lua.1 luac.1
|
|
||||||
|
|
||||||
# Lua version and release.
|
|
||||||
V= 5.4
|
|
||||||
R= $V.7
|
|
||||||
|
|
||||||
# Targets start here.
|
|
||||||
all: $(PLAT)
|
|
||||||
|
|
||||||
$(PLATS) help test clean:
|
|
||||||
@cd src && $(MAKE) $@
|
|
||||||
|
|
||||||
install: dummy
|
|
||||||
cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
|
|
||||||
cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
|
|
||||||
cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
|
|
||||||
cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
|
|
||||||
cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
|
|
||||||
|
|
||||||
uninstall:
|
|
||||||
cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN)
|
|
||||||
cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC)
|
|
||||||
cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB)
|
|
||||||
cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN)
|
|
||||||
|
|
||||||
local:
|
|
||||||
$(MAKE) install INSTALL_TOP=../install
|
|
||||||
|
|
||||||
# make may get confused with install/ if it does not support .PHONY.
|
|
||||||
dummy:
|
|
||||||
|
|
||||||
# Echo config parameters.
|
|
||||||
echo:
|
|
||||||
@cd src && $(MAKE) -s echo
|
|
||||||
@echo "PLAT= $(PLAT)"
|
|
||||||
@echo "V= $V"
|
|
||||||
@echo "R= $R"
|
|
||||||
@echo "TO_BIN= $(TO_BIN)"
|
|
||||||
@echo "TO_INC= $(TO_INC)"
|
|
||||||
@echo "TO_LIB= $(TO_LIB)"
|
|
||||||
@echo "TO_MAN= $(TO_MAN)"
|
|
||||||
@echo "INSTALL_TOP= $(INSTALL_TOP)"
|
|
||||||
@echo "INSTALL_BIN= $(INSTALL_BIN)"
|
|
||||||
@echo "INSTALL_INC= $(INSTALL_INC)"
|
|
||||||
@echo "INSTALL_LIB= $(INSTALL_LIB)"
|
|
||||||
@echo "INSTALL_MAN= $(INSTALL_MAN)"
|
|
||||||
@echo "INSTALL_LMOD= $(INSTALL_LMOD)"
|
|
||||||
@echo "INSTALL_CMOD= $(INSTALL_CMOD)"
|
|
||||||
@echo "INSTALL_EXEC= $(INSTALL_EXEC)"
|
|
||||||
@echo "INSTALL_DATA= $(INSTALL_DATA)"
|
|
||||||
|
|
||||||
# Echo pkg-config data.
|
|
||||||
pc:
|
|
||||||
@echo "version=$R"
|
|
||||||
@echo "prefix=$(INSTALL_TOP)"
|
|
||||||
@echo "libdir=$(INSTALL_LIB)"
|
|
||||||
@echo "includedir=$(INSTALL_INC)"
|
|
||||||
|
|
||||||
# Targets that do not create files (not all makes understand .PHONY).
|
|
||||||
.PHONY: all $(PLATS) help test clean install uninstall local dummy echo pc
|
|
||||||
|
|
||||||
# (end of Makefile)
|
|
@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:0f6cc3aaa68597034efda8bb6bfa214fc2fde99bd1e67d0bb32d7fe75a5dc777
|
|
||||||
size 12127
|
|
@ -1,21 +0,0 @@
|
|||||||
ul {
|
|
||||||
list-style-type: none ;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.contents {
|
|
||||||
padding: 0 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border: none ;
|
|
||||||
border-spacing: 0 ;
|
|
||||||
border-collapse: collapse ;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
vertical-align: top ;
|
|
||||||
padding: 0 ;
|
|
||||||
text-align: left ;
|
|
||||||
line-height: 1.25 ;
|
|
||||||
width: 15% ;
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.7 KiB |
@ -1,155 +0,0 @@
|
|||||||
.\" $Id: lua.man,v 1.14 2024/05/08 18:48:27 lhf Exp $
|
|
||||||
.TH LUA 1 "$Date: 2024/05/08 18:48:27 $"
|
|
||||||
.SH NAME
|
|
||||||
lua \- Lua interpreter
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B lua
|
|
||||||
[
|
|
||||||
.I options
|
|
||||||
]
|
|
||||||
[
|
|
||||||
.I script
|
|
||||||
[
|
|
||||||
.I args
|
|
||||||
]
|
|
||||||
]
|
|
||||||
.SH DESCRIPTION
|
|
||||||
.B lua
|
|
||||||
is the standalone Lua interpreter.
|
|
||||||
It loads and executes Lua programs,
|
|
||||||
either in textual source form or
|
|
||||||
in precompiled binary form.
|
|
||||||
(Precompiled binaries are output by
|
|
||||||
.BR luac ,
|
|
||||||
the Lua compiler.)
|
|
||||||
.B lua
|
|
||||||
can be used as a batch interpreter and also interactively.
|
|
||||||
.LP
|
|
||||||
After handling the
|
|
||||||
.IR options ,
|
|
||||||
the Lua program in file
|
|
||||||
.I script
|
|
||||||
is loaded and executed.
|
|
||||||
The
|
|
||||||
.I args
|
|
||||||
are available to
|
|
||||||
.I script
|
|
||||||
as strings in a global table named
|
|
||||||
.B arg
|
|
||||||
and also as arguments to its main function.
|
|
||||||
When called without arguments,
|
|
||||||
.B lua
|
|
||||||
behaves as
|
|
||||||
.B "lua \-v \-i"
|
|
||||||
if the standard input is a terminal,
|
|
||||||
and as
|
|
||||||
.B "lua \-"
|
|
||||||
otherwise.
|
|
||||||
.LP
|
|
||||||
In interactive mode,
|
|
||||||
.B lua
|
|
||||||
prompts the user,
|
|
||||||
reads lines from the standard input,
|
|
||||||
and executes them as they are read.
|
|
||||||
If the line contains an expression,
|
|
||||||
then the line is evaluated and the result is printed.
|
|
||||||
If a line does not contain a complete statement,
|
|
||||||
then a secondary prompt is displayed and
|
|
||||||
lines are read until a complete statement is formed or
|
|
||||||
a syntax error is found.
|
|
||||||
.LP
|
|
||||||
Before handling command line options and scripts,
|
|
||||||
.B lua
|
|
||||||
checks the contents of the environment variables
|
|
||||||
.B LUA_INIT_5_4
|
|
||||||
and
|
|
||||||
.BR LUA_INIT ,
|
|
||||||
in that order.
|
|
||||||
If the contents are of the form
|
|
||||||
.RI '@ filename ',
|
|
||||||
then
|
|
||||||
.I filename
|
|
||||||
is executed.
|
|
||||||
Otherwise, the contents are assumed to be a Lua statement and is executed.
|
|
||||||
When
|
|
||||||
.B LUA_INIT_5_4
|
|
||||||
is defined,
|
|
||||||
.B LUA_INIT
|
|
||||||
is ignored.
|
|
||||||
.SH OPTIONS
|
|
||||||
.TP
|
|
||||||
.BI \-e " stat"
|
|
||||||
execute statement
|
|
||||||
.IR stat .
|
|
||||||
.TP
|
|
||||||
.B \-i
|
|
||||||
enter interactive mode after executing
|
|
||||||
.IR script .
|
|
||||||
.TP
|
|
||||||
.BI \-l " mod"
|
|
||||||
require library
|
|
||||||
.I mod
|
|
||||||
into global
|
|
||||||
.IR mod .
|
|
||||||
.TP
|
|
||||||
.BI \-l " g=mod"
|
|
||||||
require library
|
|
||||||
.I mod
|
|
||||||
into global
|
|
||||||
.IR g .
|
|
||||||
.TP
|
|
||||||
.B \-v
|
|
||||||
show version information.
|
|
||||||
.TP
|
|
||||||
.B \-E
|
|
||||||
ignore environment variables.
|
|
||||||
.TP
|
|
||||||
.B \-W
|
|
||||||
turn warnings on.
|
|
||||||
.TP
|
|
||||||
.B \-\-
|
|
||||||
stop handling options.
|
|
||||||
.TP
|
|
||||||
.B \-
|
|
||||||
stop handling options and execute the standard input as a file.
|
|
||||||
.SH ENVIRONMENT VARIABLES
|
|
||||||
The following environment variables affect the execution of
|
|
||||||
.BR lua .
|
|
||||||
When defined,
|
|
||||||
the version-specific variants take priority
|
|
||||||
and the version-neutral variants are ignored.
|
|
||||||
.TP
|
|
||||||
.B LUA_INIT, LUA_INIT_5_4
|
|
||||||
Code to be executed before command line options and scripts.
|
|
||||||
.TP
|
|
||||||
.B LUA_PATH, LUA_PATH_5_4
|
|
||||||
Initial value of package.path,
|
|
||||||
the path used by require to search for Lua loaders.
|
|
||||||
.TP
|
|
||||||
.B LUA_CPATH, LUA_CPATH_5_4
|
|
||||||
Initial value of package.cpath,
|
|
||||||
the path used by require to search for C loaders.
|
|
||||||
.SH EXIT STATUS
|
|
||||||
If a script calls os.exit,
|
|
||||||
then
|
|
||||||
.B lua
|
|
||||||
exits with the given exit status.
|
|
||||||
Otherwise,
|
|
||||||
.B lua
|
|
||||||
exits
|
|
||||||
with EXIT_SUCCESS (0 on POSIX systems) if there were no errors
|
|
||||||
and
|
|
||||||
with EXIT_FAILURE (1 on POSIX systems) if there were errors.
|
|
||||||
Errors raised in interactive mode do not cause exits.
|
|
||||||
.SH DIAGNOSTICS
|
|
||||||
Error messages should be self explanatory.
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
.BR luac (1)
|
|
||||||
.br
|
|
||||||
The documentation at lua.org,
|
|
||||||
especially section 7 of the reference manual.
|
|
||||||
.SH AUTHORS
|
|
||||||
R. Ierusalimschy,
|
|
||||||
L. H. de Figueiredo,
|
|
||||||
W. Celes
|
|
||||||
.\" EOF
|
|
@ -1,162 +0,0 @@
|
|||||||
html {
|
|
||||||
background-color: #F8F8F8 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #FFFFFF ;
|
|
||||||
color: #000000 ;
|
|
||||||
font-family: Helvetica, Arial, sans-serif ;
|
|
||||||
text-align: justify ;
|
|
||||||
line-height: 1.25 ;
|
|
||||||
margin: 16px auto ;
|
|
||||||
padding: 32px ;
|
|
||||||
border: solid #ccc 1px ;
|
|
||||||
border-radius: 20px ;
|
|
||||||
max-width: 70em ;
|
|
||||||
width: 90% ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4 {
|
|
||||||
color: #000080 ;
|
|
||||||
font-family: Verdana, Geneva, sans-serif ;
|
|
||||||
font-weight: normal ;
|
|
||||||
font-style: normal ;
|
|
||||||
text-align: left ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 28pt ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 img {
|
|
||||||
vertical-align: text-bottom ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2:before {
|
|
||||||
content: "\2756" ;
|
|
||||||
padding-right: 0.5em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none ;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
color: #000080 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link:hover, a:visited:hover {
|
|
||||||
background-color: #D0D0FF ;
|
|
||||||
color: #000080 ;
|
|
||||||
border-radius: 4px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link:active, a:visited:active {
|
|
||||||
color: #FF0000 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.menubar {
|
|
||||||
padding-bottom: 0.5em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.menubar {
|
|
||||||
margin-left: 2.5em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menubar a:hover {
|
|
||||||
margin: -3px -3px -3px -3px ;
|
|
||||||
padding: 3px 3px 3px 3px ;
|
|
||||||
border-radius: 4px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
:target {
|
|
||||||
background-color: #F0F0F0 ;
|
|
||||||
margin: -8px ;
|
|
||||||
padding: 8px ;
|
|
||||||
border-radius: 8px ;
|
|
||||||
outline: none ;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
display: none ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table hr {
|
|
||||||
background-color: #a0a0a0 ;
|
|
||||||
color: #a0a0a0 ;
|
|
||||||
border: 0 ;
|
|
||||||
height: 1px ;
|
|
||||||
display: block ;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
color: gray ;
|
|
||||||
font-size: x-small ;
|
|
||||||
text-transform: lowercase ;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text] {
|
|
||||||
border: solid #a0a0a0 2px ;
|
|
||||||
border-radius: 2em ;
|
|
||||||
background-image: url('images/search.png') ;
|
|
||||||
background-repeat: no-repeat ;
|
|
||||||
background-position: 4px center ;
|
|
||||||
padding-left: 20px ;
|
|
||||||
height: 2em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.session {
|
|
||||||
background-color: #F8F8F8 ;
|
|
||||||
padding: 1em ;
|
|
||||||
border-radius: 8px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border: none ;
|
|
||||||
border-spacing: 0 ;
|
|
||||||
border-collapse: collapse ;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding: 0 ;
|
|
||||||
margin: 0 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.gutter {
|
|
||||||
width: 4% ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.columns td {
|
|
||||||
vertical-align: top ;
|
|
||||||
padding-bottom: 1em ;
|
|
||||||
text-align: justify ;
|
|
||||||
line-height: 1.25 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.book td {
|
|
||||||
vertical-align: top ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.book td.cover {
|
|
||||||
padding-right: 1em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.book img {
|
|
||||||
border: solid #000080 1px ;
|
|
||||||
border-radius: 2px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.book span {
|
|
||||||
font-size: small ;
|
|
||||||
text-align: left ;
|
|
||||||
display: block ;
|
|
||||||
margin-top: 0.25em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.logos a:link:hover, p.logos a:visited:hover {
|
|
||||||
background-color: inherit ;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
background-color: white ;
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
.\" $Id: luac.man,v 1.29 2011/11/16 13:53:40 lhf Exp $
|
|
||||||
.TH LUAC 1 "$Date: 2011/11/16 13:53:40 $"
|
|
||||||
.SH NAME
|
|
||||||
luac \- Lua compiler
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B luac
|
|
||||||
[
|
|
||||||
.I options
|
|
||||||
] [
|
|
||||||
.I filenames
|
|
||||||
]
|
|
||||||
.SH DESCRIPTION
|
|
||||||
.B luac
|
|
||||||
is the Lua compiler.
|
|
||||||
It translates programs written in the Lua programming language
|
|
||||||
into binary files containing precompiled chunks
|
|
||||||
that can be later loaded and executed.
|
|
||||||
.LP
|
|
||||||
The main advantages of precompiling chunks are:
|
|
||||||
faster loading,
|
|
||||||
protecting source code from accidental user changes,
|
|
||||||
and
|
|
||||||
off-line syntax checking.
|
|
||||||
Precompiling does not imply faster execution
|
|
||||||
because in Lua chunks are always compiled into bytecodes before being executed.
|
|
||||||
.B luac
|
|
||||||
simply allows those bytecodes to be saved in a file for later execution.
|
|
||||||
Precompiled chunks are not necessarily smaller than the corresponding source.
|
|
||||||
The main goal in precompiling is faster loading.
|
|
||||||
.LP
|
|
||||||
In the command line,
|
|
||||||
you can mix
|
|
||||||
text files containing Lua source and
|
|
||||||
binary files containing precompiled chunks.
|
|
||||||
.B luac
|
|
||||||
produces a single output file containing the combined bytecodes
|
|
||||||
for all files given.
|
|
||||||
Executing the combined file is equivalent to executing the given files.
|
|
||||||
By default,
|
|
||||||
the output file is named
|
|
||||||
.BR luac.out ,
|
|
||||||
but you can change this with the
|
|
||||||
.B \-o
|
|
||||||
option.
|
|
||||||
.LP
|
|
||||||
Precompiled chunks are
|
|
||||||
.I not
|
|
||||||
portable across different architectures.
|
|
||||||
Moreover,
|
|
||||||
the internal format of precompiled chunks
|
|
||||||
is likely to change when a new version of Lua is released.
|
|
||||||
Make sure you save the source files of all Lua programs that you precompile.
|
|
||||||
.LP
|
|
||||||
.SH OPTIONS
|
|
||||||
.TP
|
|
||||||
.B \-l
|
|
||||||
produce a listing of the compiled bytecode for Lua's virtual machine.
|
|
||||||
Listing bytecodes is useful to learn about Lua's virtual machine.
|
|
||||||
If no files are given, then
|
|
||||||
.B luac
|
|
||||||
loads
|
|
||||||
.B luac.out
|
|
||||||
and lists its contents.
|
|
||||||
Use
|
|
||||||
.B \-l \-l
|
|
||||||
for a full listing.
|
|
||||||
.TP
|
|
||||||
.BI \-o " file"
|
|
||||||
output to
|
|
||||||
.IR file ,
|
|
||||||
instead of the default
|
|
||||||
.BR luac.out .
|
|
||||||
(You can use
|
|
||||||
.B "'\-'"
|
|
||||||
for standard output,
|
|
||||||
but not on platforms that open standard output in text mode.)
|
|
||||||
The output file may be one of the given files because
|
|
||||||
all files are loaded before the output file is written.
|
|
||||||
Be careful not to overwrite precious files.
|
|
||||||
.TP
|
|
||||||
.B \-p
|
|
||||||
load files but do not generate any output file.
|
|
||||||
Used mainly for syntax checking and for testing precompiled chunks:
|
|
||||||
corrupted files will probably generate errors when loaded.
|
|
||||||
If no files are given, then
|
|
||||||
.B luac
|
|
||||||
loads
|
|
||||||
.B luac.out
|
|
||||||
and tests its contents.
|
|
||||||
No messages are displayed if the file loads without errors.
|
|
||||||
.TP
|
|
||||||
.B \-s
|
|
||||||
strip debug information before writing the output file.
|
|
||||||
This saves some space in very large chunks,
|
|
||||||
but if errors occur when running a stripped chunk,
|
|
||||||
then the error messages may not contain the full information they usually do.
|
|
||||||
In particular,
|
|
||||||
line numbers and names of local variables are lost.
|
|
||||||
.TP
|
|
||||||
.B \-v
|
|
||||||
show version information.
|
|
||||||
.TP
|
|
||||||
.B \-\-
|
|
||||||
stop handling options.
|
|
||||||
.TP
|
|
||||||
.B \-
|
|
||||||
stop handling options and process standard input.
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
.BR lua (1)
|
|
||||||
.br
|
|
||||||
The documentation at lua.org.
|
|
||||||
.SH DIAGNOSTICS
|
|
||||||
Error messages should be self explanatory.
|
|
||||||
.SH AUTHORS
|
|
||||||
R. Ierusalimschy,
|
|
||||||
L. H. de Figueiredo,
|
|
||||||
W. Celes
|
|
||||||
.\" EOF
|
|
@ -1,21 +0,0 @@
|
|||||||
h3 code {
|
|
||||||
font-family: inherit ;
|
|
||||||
font-size: inherit ;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, code {
|
|
||||||
font-size: 12pt ;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.apii {
|
|
||||||
color: gray ;
|
|
||||||
float: right ;
|
|
||||||
font-family: inherit ;
|
|
||||||
font-style: normal ;
|
|
||||||
font-size: small ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2:before {
|
|
||||||
content: "" ;
|
|
||||||
padding-right: 0em ;
|
|
||||||
}
|
|
@ -1,225 +0,0 @@
|
|||||||
# Makefile for building Lua
|
|
||||||
# See ../doc/readme.html for installation and customization instructions.
|
|
||||||
|
|
||||||
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
|
|
||||||
|
|
||||||
# Your platform. See PLATS for possible values.
|
|
||||||
PLAT= guess
|
|
||||||
|
|
||||||
CC= gcc -std=gnu99
|
|
||||||
CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_3 $(SYSCFLAGS) $(MYCFLAGS)
|
|
||||||
LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS)
|
|
||||||
LIBS= -lm $(SYSLIBS) $(MYLIBS)
|
|
||||||
|
|
||||||
AR= ar rcu
|
|
||||||
RANLIB= ranlib
|
|
||||||
RM= rm -f
|
|
||||||
UNAME= uname
|
|
||||||
|
|
||||||
SYSCFLAGS=
|
|
||||||
SYSLDFLAGS=
|
|
||||||
SYSLIBS=
|
|
||||||
|
|
||||||
MYCFLAGS=
|
|
||||||
MYLDFLAGS=
|
|
||||||
MYLIBS=
|
|
||||||
MYOBJS=
|
|
||||||
|
|
||||||
# Special flags for compiler modules; -Os reduces code size.
|
|
||||||
CMCFLAGS=
|
|
||||||
|
|
||||||
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
|
|
||||||
|
|
||||||
PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
|
|
||||||
|
|
||||||
LUA_A= liblua.a
|
|
||||||
CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o
|
|
||||||
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
|
|
||||||
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
|
|
||||||
|
|
||||||
LUA_T= lua
|
|
||||||
LUA_O= lua.o
|
|
||||||
|
|
||||||
LUAC_T= luac
|
|
||||||
LUAC_O= luac.o
|
|
||||||
|
|
||||||
ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O)
|
|
||||||
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
|
|
||||||
ALL_A= $(LUA_A)
|
|
||||||
|
|
||||||
# Targets start here.
|
|
||||||
default: $(PLAT)
|
|
||||||
|
|
||||||
all: $(ALL_T)
|
|
||||||
|
|
||||||
o: $(ALL_O)
|
|
||||||
|
|
||||||
a: $(ALL_A)
|
|
||||||
|
|
||||||
$(LUA_A): $(BASE_O)
|
|
||||||
$(AR) $@ $(BASE_O)
|
|
||||||
$(RANLIB) $@
|
|
||||||
|
|
||||||
$(LUA_T): $(LUA_O) $(LUA_A)
|
|
||||||
$(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)
|
|
||||||
|
|
||||||
$(LUAC_T): $(LUAC_O) $(LUA_A)
|
|
||||||
$(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
|
|
||||||
|
|
||||||
test:
|
|
||||||
./$(LUA_T) -v
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) $(ALL_T) $(ALL_O)
|
|
||||||
|
|
||||||
depend:
|
|
||||||
@$(CC) $(CFLAGS) -MM l*.c
|
|
||||||
|
|
||||||
echo:
|
|
||||||
@echo "PLAT= $(PLAT)"
|
|
||||||
@echo "CC= $(CC)"
|
|
||||||
@echo "CFLAGS= $(CFLAGS)"
|
|
||||||
@echo "LDFLAGS= $(LDFLAGS)"
|
|
||||||
@echo "LIBS= $(LIBS)"
|
|
||||||
@echo "AR= $(AR)"
|
|
||||||
@echo "RANLIB= $(RANLIB)"
|
|
||||||
@echo "RM= $(RM)"
|
|
||||||
@echo "UNAME= $(UNAME)"
|
|
||||||
|
|
||||||
# Convenience targets for popular platforms.
|
|
||||||
ALL= all
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Do 'make PLATFORM' where PLATFORM is one of these:"
|
|
||||||
@echo " $(PLATS)"
|
|
||||||
@echo "See doc/readme.html for complete instructions."
|
|
||||||
|
|
||||||
guess:
|
|
||||||
@echo Guessing `$(UNAME)`
|
|
||||||
@$(MAKE) `$(UNAME)`
|
|
||||||
|
|
||||||
AIX aix:
|
|
||||||
$(MAKE) $(ALL) CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall"
|
|
||||||
|
|
||||||
bsd:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-Wl,-E"
|
|
||||||
|
|
||||||
c89:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_C89" CC="gcc -std=c89"
|
|
||||||
@echo ''
|
|
||||||
@echo '*** C89 does not guarantee 64-bit integers for Lua.'
|
|
||||||
@echo '*** Make sure to compile all external Lua libraries'
|
|
||||||
@echo '*** with LUA_USE_C89 to ensure consistency'
|
|
||||||
@echo ''
|
|
||||||
|
|
||||||
FreeBSD NetBSD OpenBSD freebsd:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"
|
|
||||||
|
|
||||||
generic: $(ALL)
|
|
||||||
|
|
||||||
ios:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_IOS"
|
|
||||||
|
|
||||||
Linux linux: linux-noreadline
|
|
||||||
|
|
||||||
linux-noreadline:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl"
|
|
||||||
|
|
||||||
linux-readline:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE" SYSLIBS="-Wl,-E -ldl -lreadline"
|
|
||||||
|
|
||||||
Darwin macos macosx:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX -DLUA_USE_READLINE" SYSLIBS="-lreadline"
|
|
||||||
|
|
||||||
mingw:
|
|
||||||
$(MAKE) "LUA_A=lua54.dll" "LUA_T=lua.exe" \
|
|
||||||
"AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
|
|
||||||
"SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe
|
|
||||||
$(MAKE) "LUAC_T=luac.exe" luac.exe
|
|
||||||
|
|
||||||
posix:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX"
|
|
||||||
|
|
||||||
SunOS solaris:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" SYSLIBS="-ldl"
|
|
||||||
|
|
||||||
# Targets that do not create files (not all makes understand .PHONY).
|
|
||||||
.PHONY: all $(PLATS) help test clean default o a depend echo
|
|
||||||
|
|
||||||
# Compiler modules may use special flags.
|
|
||||||
llex.o:
|
|
||||||
$(CC) $(CFLAGS) $(CMCFLAGS) -c llex.c
|
|
||||||
|
|
||||||
lparser.o:
|
|
||||||
$(CC) $(CFLAGS) $(CMCFLAGS) -c lparser.c
|
|
||||||
|
|
||||||
lcode.o:
|
|
||||||
$(CC) $(CFLAGS) $(CMCFLAGS) -c lcode.c
|
|
||||||
|
|
||||||
# DO NOT DELETE
|
|
||||||
|
|
||||||
lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \
|
|
||||||
ltable.h lundump.h lvm.h
|
|
||||||
lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h
|
|
||||||
lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
|
||||||
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
|
||||||
ldo.h lgc.h lstring.h ltable.h lvm.h
|
|
||||||
lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h
|
|
||||||
ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \
|
|
||||||
ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h
|
|
||||||
ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \
|
|
||||||
lparser.h lstring.h ltable.h lundump.h lvm.h
|
|
||||||
ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \
|
|
||||||
ltm.h lzio.h lmem.h lundump.h
|
|
||||||
lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h
|
|
||||||
lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
|
|
||||||
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h
|
|
||||||
liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \
|
|
||||||
lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \
|
|
||||||
lstring.h ltable.h
|
|
||||||
lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h
|
|
||||||
loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \
|
|
||||||
ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \
|
|
||||||
lvm.h
|
|
||||||
lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h
|
|
||||||
loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
|
||||||
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
|
||||||
ldo.h lfunc.h lstring.h lgc.h ltable.h
|
|
||||||
lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \
|
|
||||||
lstring.h ltable.h
|
|
||||||
lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
|
||||||
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h
|
|
||||||
lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
|
|
||||||
ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
|
|
||||||
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h ldebug.h lstate.h \
|
|
||||||
lobject.h llimits.h ltm.h lzio.h lmem.h lopcodes.h lopnames.h lundump.h
|
|
||||||
lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
|
||||||
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \
|
|
||||||
lundump.h
|
|
||||||
lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \
|
|
||||||
ltable.h lvm.h ljumptab.h
|
|
||||||
lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h
|
|
||||||
|
|
||||||
# (end of Makefile)
|
|
File diff suppressed because it is too large
Load Diff
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lapi.h $
|
|
||||||
** Auxiliary functions from Lua API
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef lapi_h
|
|
||||||
#define lapi_h
|
|
||||||
|
|
||||||
|
|
||||||
#include "llimits.h"
|
|
||||||
#include "lstate.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Increments 'L->top.p', checking for stack overflows */
|
|
||||||
#define api_incr_top(L) {L->top.p++; \
|
|
||||||
api_check(L, L->top.p <= L->ci->top.p, \
|
|
||||||
"stack overflow");}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** If a call returns too many multiple returns, the callee may not have
|
|
||||||
** stack space to accommodate all results. In this case, this macro
|
|
||||||
** increases its stack space ('L->ci->top.p').
|
|
||||||
*/
|
|
||||||
#define adjustresults(L,nres) \
|
|
||||||
{ if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
|
|
||||||
L->ci->top.p = L->top.p; }
|
|
||||||
|
|
||||||
|
|
||||||
/* Ensure the stack has at least 'n' elements */
|
|
||||||
#define api_checknelems(L,n) \
|
|
||||||
api_check(L, (n) < (L->top.p - L->ci->func.p), \
|
|
||||||
"not enough elements in the stack")
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** To reduce the overhead of returning from C functions, the presence of
|
|
||||||
** to-be-closed variables in these functions is coded in the CallInfo's
|
|
||||||
** field 'nresults', in a way that functions with no to-be-closed variables
|
|
||||||
** with zero, one, or "all" wanted results have no overhead. Functions
|
|
||||||
** with other number of wanted results, as well as functions with
|
|
||||||
** variables to be closed, have an extra check.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
|
||||||
|
|
||||||
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
|
|
||||||
#define codeNresults(n) (-(n) - 3)
|
|
||||||
#define decodeNresults(n) (-(n) - 3)
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -1,301 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lauxlib.h $
|
|
||||||
** Auxiliary functions for building Lua libraries
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef lauxlib_h
|
|
||||||
#define lauxlib_h
|
|
||||||
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "luaconf.h"
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* global table */
|
|
||||||
#define LUA_GNAME "_G"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct luaL_Buffer luaL_Buffer;
|
|
||||||
|
|
||||||
|
|
||||||
/* extra error code for 'luaL_loadfilex' */
|
|
||||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
|
||||||
|
|
||||||
|
|
||||||
/* key, in the registry, for table of loaded modules */
|
|
||||||
#define LUA_LOADED_TABLE "_LOADED"
|
|
||||||
|
|
||||||
|
|
||||||
/* key, in the registry, for table of preloaded loaders */
|
|
||||||
#define LUA_PRELOAD_TABLE "_PRELOAD"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct luaL_Reg {
|
|
||||||
const char *name;
|
|
||||||
lua_CFunction func;
|
|
||||||
} luaL_Reg;
|
|
||||||
|
|
||||||
|
|
||||||
#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
|
|
||||||
#define luaL_checkversion(L) \
|
|
||||||
luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
|
||||||
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
|
|
||||||
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
|
|
||||||
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
|
|
||||||
LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
|
|
||||||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
|
|
||||||
size_t *l);
|
|
||||||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
|
|
||||||
const char *def, size_t *l);
|
|
||||||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
|
|
||||||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
|
|
||||||
|
|
||||||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
|
|
||||||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
|
|
||||||
lua_Integer def);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
|
||||||
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
|
|
||||||
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
|
|
||||||
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
|
|
||||||
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
|
|
||||||
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
|
|
||||||
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
|
|
||||||
const char *const lst[]);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
|
||||||
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
|
|
||||||
|
|
||||||
|
|
||||||
/* predefined references */
|
|
||||||
#define LUA_NOREF (-2)
|
|
||||||
#define LUA_REFNIL (-1)
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_ref) (lua_State *L, int t);
|
|
||||||
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
|
||||||
const char *mode);
|
|
||||||
|
|
||||||
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
|
||||||
const char *name, const char *mode);
|
|
||||||
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
|
||||||
|
|
||||||
LUALIB_API lua_State *(luaL_newstate) (void);
|
|
||||||
|
|
||||||
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
|
|
||||||
const char *p, const char *r);
|
|
||||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
|
|
||||||
const char *p, const char *r);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
|
|
||||||
const char *msg, int level);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
|
||||||
lua_CFunction openf, int glb);
|
|
||||||
|
|
||||||
/*
|
|
||||||
** ===============================================================
|
|
||||||
** some useful macros
|
|
||||||
** ===============================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#define luaL_newlibtable(L,l) \
|
|
||||||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
|
||||||
|
|
||||||
#define luaL_newlib(L,l) \
|
|
||||||
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
|
||||||
|
|
||||||
#define luaL_argcheck(L, cond,arg,extramsg) \
|
|
||||||
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
|
|
||||||
|
|
||||||
#define luaL_argexpected(L,cond,arg,tname) \
|
|
||||||
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
|
|
||||||
|
|
||||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
|
||||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
|
||||||
|
|
||||||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
|
||||||
|
|
||||||
#define luaL_dofile(L, fn) \
|
|
||||||
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
|
||||||
|
|
||||||
#define luaL_dostring(L, s) \
|
|
||||||
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
|
||||||
|
|
||||||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
|
||||||
|
|
||||||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
|
||||||
|
|
||||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Perform arithmetic operations on lua_Integer values with wrap-around
|
|
||||||
** semantics, as the Lua core does.
|
|
||||||
*/
|
|
||||||
#define luaL_intop(op,v1,v2) \
|
|
||||||
((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
|
|
||||||
|
|
||||||
|
|
||||||
/* push the value used to represent failure/error */
|
|
||||||
#define luaL_pushfail(L) lua_pushnil(L)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Internal assertions for in-house debugging
|
|
||||||
*/
|
|
||||||
#if !defined(lua_assert)
|
|
||||||
|
|
||||||
#if defined LUAI_ASSERT
|
|
||||||
#include <assert.h>
|
|
||||||
#define lua_assert(c) assert(c)
|
|
||||||
#else
|
|
||||||
#define lua_assert(c) ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {======================================================
|
|
||||||
** Generic Buffer manipulation
|
|
||||||
** =======================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct luaL_Buffer {
|
|
||||||
char *b; /* buffer address */
|
|
||||||
size_t size; /* buffer size */
|
|
||||||
size_t n; /* number of characters in buffer */
|
|
||||||
lua_State *L;
|
|
||||||
union {
|
|
||||||
LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
|
|
||||||
char b[LUAL_BUFFERSIZE]; /* initial buffer */
|
|
||||||
} init;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#define luaL_bufflen(bf) ((bf)->n)
|
|
||||||
#define luaL_buffaddr(bf) ((bf)->b)
|
|
||||||
|
|
||||||
|
|
||||||
#define luaL_addchar(B,c) \
|
|
||||||
((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
|
|
||||||
((B)->b[(B)->n++] = (c)))
|
|
||||||
|
|
||||||
#define luaL_addsize(B,s) ((B)->n += (s))
|
|
||||||
|
|
||||||
#define luaL_buffsub(B,s) ((B)->n -= (s))
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
|
||||||
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
|
|
||||||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
|
||||||
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
|
|
||||||
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
|
|
||||||
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
|
|
||||||
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
|
||||||
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
|
||||||
|
|
||||||
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
|
||||||
|
|
||||||
/* }====================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {======================================================
|
|
||||||
** File handles for IO library
|
|
||||||
** =======================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
|
|
||||||
** initial structure 'luaL_Stream' (it may contain other fields
|
|
||||||
** after that initial structure).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LUA_FILEHANDLE "FILE*"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct luaL_Stream {
|
|
||||||
FILE *f; /* stream (NULL for incompletely created streams) */
|
|
||||||
lua_CFunction closef; /* to close stream (NULL for closed streams) */
|
|
||||||
} luaL_Stream;
|
|
||||||
|
|
||||||
/* }====================================================== */
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {==================================================================
|
|
||||||
** "Abstraction Layer" for basic report of messages and errors
|
|
||||||
** ===================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* print a string */
|
|
||||||
#if !defined(lua_writestring)
|
|
||||||
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* print a newline and flush the output */
|
|
||||||
#if !defined(lua_writeline)
|
|
||||||
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* print an error message */
|
|
||||||
#if !defined(lua_writestringerror)
|
|
||||||
#define lua_writestringerror(s,p) \
|
|
||||||
(fprintf(stderr, (s), (p)), fflush(stderr))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* }================================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {============================================================
|
|
||||||
** Compatibility with deprecated conversions
|
|
||||||
** =============================================================
|
|
||||||
*/
|
|
||||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
|
||||||
|
|
||||||
#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
|
|
||||||
#define luaL_optunsigned(L,a,d) \
|
|
||||||
((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
|
|
||||||
|
|
||||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
|
||||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
|
||||||
|
|
||||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
|
||||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
|
||||||
|
|
||||||
#endif
|
|
||||||
/* }============================================================ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
@ -1,549 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lbaselib.c $
|
|
||||||
** Basic library
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define lbaselib_c
|
|
||||||
#define LUA_LIB
|
|
||||||
|
|
||||||
#include "lprefix.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
#include "lauxlib.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_print (lua_State *L) {
|
|
||||||
int n = lua_gettop(L); /* number of arguments */
|
|
||||||
int i;
|
|
||||||
for (i = 1; i <= n; i++) { /* for each argument */
|
|
||||||
size_t l;
|
|
||||||
const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
|
|
||||||
if (i > 1) /* not the first element? */
|
|
||||||
lua_writestring("\t", 1); /* add a tab before it */
|
|
||||||
lua_writestring(s, l); /* print it */
|
|
||||||
lua_pop(L, 1); /* pop result */
|
|
||||||
}
|
|
||||||
lua_writeline();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Creates a warning with all given arguments.
|
|
||||||
** Check first for errors; otherwise an error may interrupt
|
|
||||||
** the composition of a warning, leaving it unfinished.
|
|
||||||
*/
|
|
||||||
static int luaB_warn (lua_State *L) {
|
|
||||||
int n = lua_gettop(L); /* number of arguments */
|
|
||||||
int i;
|
|
||||||
luaL_checkstring(L, 1); /* at least one argument */
|
|
||||||
for (i = 2; i <= n; i++)
|
|
||||||
luaL_checkstring(L, i); /* make sure all arguments are strings */
|
|
||||||
for (i = 1; i < n; i++) /* compose warning */
|
|
||||||
lua_warning(L, lua_tostring(L, i), 1);
|
|
||||||
lua_warning(L, lua_tostring(L, n), 0); /* close warning */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define SPACECHARS " \f\n\r\t\v"
|
|
||||||
|
|
||||||
static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
|
|
||||||
lua_Unsigned n = 0;
|
|
||||||
int neg = 0;
|
|
||||||
s += strspn(s, SPACECHARS); /* skip initial spaces */
|
|
||||||
if (*s == '-') { s++; neg = 1; } /* handle sign */
|
|
||||||
else if (*s == '+') s++;
|
|
||||||
if (!isalnum((unsigned char)*s)) /* no digit? */
|
|
||||||
return NULL;
|
|
||||||
do {
|
|
||||||
int digit = (isdigit((unsigned char)*s)) ? *s - '0'
|
|
||||||
: (toupper((unsigned char)*s) - 'A') + 10;
|
|
||||||
if (digit >= base) return NULL; /* invalid numeral */
|
|
||||||
n = n * base + digit;
|
|
||||||
s++;
|
|
||||||
} while (isalnum((unsigned char)*s));
|
|
||||||
s += strspn(s, SPACECHARS); /* skip trailing spaces */
|
|
||||||
*pn = (lua_Integer)((neg) ? (0u - n) : n);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_tonumber (lua_State *L) {
|
|
||||||
if (lua_isnoneornil(L, 2)) { /* standard conversion? */
|
|
||||||
if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
|
|
||||||
lua_settop(L, 1); /* yes; return it */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
size_t l;
|
|
||||||
const char *s = lua_tolstring(L, 1, &l);
|
|
||||||
if (s != NULL && lua_stringtonumber(L, s) == l + 1)
|
|
||||||
return 1; /* successful conversion to number */
|
|
||||||
/* else not a number */
|
|
||||||
luaL_checkany(L, 1); /* (but there must be some parameter) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
size_t l;
|
|
||||||
const char *s;
|
|
||||||
lua_Integer n = 0; /* to avoid warnings */
|
|
||||||
lua_Integer base = luaL_checkinteger(L, 2);
|
|
||||||
luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
|
|
||||||
s = lua_tolstring(L, 1, &l);
|
|
||||||
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
|
|
||||||
if (b_str2int(s, (int)base, &n) == s + l) {
|
|
||||||
lua_pushinteger(L, n);
|
|
||||||
return 1;
|
|
||||||
} /* else not a number */
|
|
||||||
} /* else not a number */
|
|
||||||
luaL_pushfail(L); /* not a number */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_error (lua_State *L) {
|
|
||||||
int level = (int)luaL_optinteger(L, 2, 1);
|
|
||||||
lua_settop(L, 1);
|
|
||||||
if (lua_type(L, 1) == LUA_TSTRING && level > 0) {
|
|
||||||
luaL_where(L, level); /* add extra information */
|
|
||||||
lua_pushvalue(L, 1);
|
|
||||||
lua_concat(L, 2);
|
|
||||||
}
|
|
||||||
return lua_error(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_getmetatable (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
if (!lua_getmetatable(L, 1)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1; /* no metatable */
|
|
||||||
}
|
|
||||||
luaL_getmetafield(L, 1, "__metatable");
|
|
||||||
return 1; /* returns either __metatable field (if present) or metatable */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_setmetatable (lua_State *L) {
|
|
||||||
int t = lua_type(L, 2);
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
|
||||||
if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
|
|
||||||
return luaL_error(L, "cannot change a protected metatable");
|
|
||||||
lua_settop(L, 2);
|
|
||||||
lua_setmetatable(L, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_rawequal (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
luaL_checkany(L, 2);
|
|
||||||
lua_pushboolean(L, lua_rawequal(L, 1, 2));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_rawlen (lua_State *L) {
|
|
||||||
int t = lua_type(L, 1);
|
|
||||||
luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
|
|
||||||
"table or string");
|
|
||||||
lua_pushinteger(L, lua_rawlen(L, 1));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_rawget (lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
luaL_checkany(L, 2);
|
|
||||||
lua_settop(L, 2);
|
|
||||||
lua_rawget(L, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int luaB_rawset (lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
luaL_checkany(L, 2);
|
|
||||||
luaL_checkany(L, 3);
|
|
||||||
lua_settop(L, 3);
|
|
||||||
lua_rawset(L, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int pushmode (lua_State *L, int oldmode) {
|
|
||||||
if (oldmode == -1)
|
|
||||||
luaL_pushfail(L); /* invalid call to 'lua_gc' */
|
|
||||||
else
|
|
||||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
|
|
||||||
: "generational");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** check whether call to 'lua_gc' was valid (not inside a finalizer)
|
|
||||||
*/
|
|
||||||
#define checkvalres(res) { if (res == -1) break; }
|
|
||||||
|
|
||||||
static int luaB_collectgarbage (lua_State *L) {
|
|
||||||
static const char *const opts[] = {"stop", "restart", "collect",
|
|
||||||
"count", "step", "setpause", "setstepmul",
|
|
||||||
"isrunning", "generational", "incremental", NULL};
|
|
||||||
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
|
||||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
|
||||||
LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
|
|
||||||
int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
|
|
||||||
switch (o) {
|
|
||||||
case LUA_GCCOUNT: {
|
|
||||||
int k = lua_gc(L, o);
|
|
||||||
int b = lua_gc(L, LUA_GCCOUNTB);
|
|
||||||
checkvalres(k);
|
|
||||||
lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case LUA_GCSTEP: {
|
|
||||||
int step = (int)luaL_optinteger(L, 2, 0);
|
|
||||||
int res = lua_gc(L, o, step);
|
|
||||||
checkvalres(res);
|
|
||||||
lua_pushboolean(L, res);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case LUA_GCSETPAUSE:
|
|
||||||
case LUA_GCSETSTEPMUL: {
|
|
||||||
int p = (int)luaL_optinteger(L, 2, 0);
|
|
||||||
int previous = lua_gc(L, o, p);
|
|
||||||
checkvalres(previous);
|
|
||||||
lua_pushinteger(L, previous);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case LUA_GCISRUNNING: {
|
|
||||||
int res = lua_gc(L, o);
|
|
||||||
checkvalres(res);
|
|
||||||
lua_pushboolean(L, res);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case LUA_GCGEN: {
|
|
||||||
int minormul = (int)luaL_optinteger(L, 2, 0);
|
|
||||||
int majormul = (int)luaL_optinteger(L, 3, 0);
|
|
||||||
return pushmode(L, lua_gc(L, o, minormul, majormul));
|
|
||||||
}
|
|
||||||
case LUA_GCINC: {
|
|
||||||
int pause = (int)luaL_optinteger(L, 2, 0);
|
|
||||||
int stepmul = (int)luaL_optinteger(L, 3, 0);
|
|
||||||
int stepsize = (int)luaL_optinteger(L, 4, 0);
|
|
||||||
return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
int res = lua_gc(L, o);
|
|
||||||
checkvalres(res);
|
|
||||||
lua_pushinteger(L, res);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
luaL_pushfail(L); /* invalid call (inside a finalizer) */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_type (lua_State *L) {
|
|
||||||
int t = lua_type(L, 1);
|
|
||||||
luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
|
|
||||||
lua_pushstring(L, lua_typename(L, t));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_next (lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
|
|
||||||
if (lua_next(L, 1))
|
|
||||||
return 2;
|
|
||||||
else {
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int pairscont (lua_State *L, int status, lua_KContext k) {
|
|
||||||
(void)L; (void)status; (void)k; /* unused */
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int luaB_pairs (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
|
|
||||||
lua_pushcfunction(L, luaB_next); /* will return generator, */
|
|
||||||
lua_pushvalue(L, 1); /* state, */
|
|
||||||
lua_pushnil(L); /* and initial value */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
|
|
||||||
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
|
|
||||||
}
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Traversal function for 'ipairs'
|
|
||||||
*/
|
|
||||||
static int ipairsaux (lua_State *L) {
|
|
||||||
lua_Integer i = luaL_checkinteger(L, 2);
|
|
||||||
i = luaL_intop(+, i, 1);
|
|
||||||
lua_pushinteger(L, i);
|
|
||||||
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
|
|
||||||
** (The given "table" may not be a table.)
|
|
||||||
*/
|
|
||||||
static int luaB_ipairs (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
lua_pushcfunction(L, ipairsaux); /* iteration function */
|
|
||||||
lua_pushvalue(L, 1); /* state */
|
|
||||||
lua_pushinteger(L, 0); /* initial value */
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int load_aux (lua_State *L, int status, int envidx) {
|
|
||||||
if (l_likely(status == LUA_OK)) {
|
|
||||||
if (envidx != 0) { /* 'env' parameter? */
|
|
||||||
lua_pushvalue(L, envidx); /* environment for loaded function */
|
|
||||||
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
|
||||||
lua_pop(L, 1); /* remove 'env' if not used by previous call */
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else { /* error (message is on top of the stack) */
|
|
||||||
luaL_pushfail(L);
|
|
||||||
lua_insert(L, -2); /* put before error message */
|
|
||||||
return 2; /* return fail plus error message */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_loadfile (lua_State *L) {
|
|
||||||
const char *fname = luaL_optstring(L, 1, NULL);
|
|
||||||
const char *mode = luaL_optstring(L, 2, NULL);
|
|
||||||
int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
|
|
||||||
int status = luaL_loadfilex(L, fname, mode);
|
|
||||||
return load_aux(L, status, env);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {======================================================
|
|
||||||
** Generic Read function
|
|
||||||
** =======================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** reserved slot, above all arguments, to hold a copy of the returned
|
|
||||||
** string to avoid it being collected while parsed. 'load' has four
|
|
||||||
** optional arguments (chunk, source name, mode, and environment).
|
|
||||||
*/
|
|
||||||
#define RESERVEDSLOT 5
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Reader for generic 'load' function: 'lua_load' uses the
|
|
||||||
** stack for internal stuff, so the reader cannot change the
|
|
||||||
** stack top. Instead, it keeps its resulting string in a
|
|
||||||
** reserved slot inside the stack.
|
|
||||||
*/
|
|
||||||
static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
|
|
||||||
(void)(ud); /* not used */
|
|
||||||
luaL_checkstack(L, 2, "too many nested functions");
|
|
||||||
lua_pushvalue(L, 1); /* get function */
|
|
||||||
lua_call(L, 0, 1); /* call it */
|
|
||||||
if (lua_isnil(L, -1)) {
|
|
||||||
lua_pop(L, 1); /* pop result */
|
|
||||||
*size = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else if (l_unlikely(!lua_isstring(L, -1)))
|
|
||||||
luaL_error(L, "reader function must return a string");
|
|
||||||
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
|
||||||
return lua_tolstring(L, RESERVEDSLOT, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_load (lua_State *L) {
|
|
||||||
int status;
|
|
||||||
size_t l;
|
|
||||||
const char *s = lua_tolstring(L, 1, &l);
|
|
||||||
const char *mode = luaL_optstring(L, 3, "bt");
|
|
||||||
int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
|
|
||||||
if (s != NULL) { /* loading a string? */
|
|
||||||
const char *chunkname = luaL_optstring(L, 2, s);
|
|
||||||
status = luaL_loadbufferx(L, s, l, chunkname, mode);
|
|
||||||
}
|
|
||||||
else { /* loading from a reader function */
|
|
||||||
const char *chunkname = luaL_optstring(L, 2, "=(load)");
|
|
||||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
|
||||||
lua_settop(L, RESERVEDSLOT); /* create reserved slot */
|
|
||||||
status = lua_load(L, generic_reader, NULL, chunkname, mode);
|
|
||||||
}
|
|
||||||
return load_aux(L, status, env);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }====================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
|
|
||||||
(void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */
|
|
||||||
return lua_gettop(L) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_dofile (lua_State *L) {
|
|
||||||
const char *fname = luaL_optstring(L, 1, NULL);
|
|
||||||
lua_settop(L, 1);
|
|
||||||
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
|
|
||||||
return lua_error(L);
|
|
||||||
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
|
||||||
return dofilecont(L, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_assert (lua_State *L) {
|
|
||||||
if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
|
|
||||||
return lua_gettop(L); /* return all arguments */
|
|
||||||
else { /* error */
|
|
||||||
luaL_checkany(L, 1); /* there must be a condition */
|
|
||||||
lua_remove(L, 1); /* remove it */
|
|
||||||
lua_pushliteral(L, "assertion failed!"); /* default message */
|
|
||||||
lua_settop(L, 1); /* leave only message (default if no other one) */
|
|
||||||
return luaB_error(L); /* call 'error' */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_select (lua_State *L) {
|
|
||||||
int n = lua_gettop(L);
|
|
||||||
if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
|
|
||||||
lua_pushinteger(L, n-1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lua_Integer i = luaL_checkinteger(L, 1);
|
|
||||||
if (i < 0) i = n + i;
|
|
||||||
else if (i > n) i = n;
|
|
||||||
luaL_argcheck(L, 1 <= i, 1, "index out of range");
|
|
||||||
return n - (int)i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Continuation function for 'pcall' and 'xpcall'. Both functions
|
|
||||||
** already pushed a 'true' before doing the call, so in case of success
|
|
||||||
** 'finishpcall' only has to return everything in the stack minus
|
|
||||||
** 'extra' values (where 'extra' is exactly the number of items to be
|
|
||||||
** ignored).
|
|
||||||
*/
|
|
||||||
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
|
|
||||||
if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
|
|
||||||
lua_pushboolean(L, 0); /* first result (false) */
|
|
||||||
lua_pushvalue(L, -2); /* error message */
|
|
||||||
return 2; /* return false, msg */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return lua_gettop(L) - (int)extra; /* return all results */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_pcall (lua_State *L) {
|
|
||||||
int status;
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
lua_pushboolean(L, 1); /* first result if no errors */
|
|
||||||
lua_insert(L, 1); /* put it in place */
|
|
||||||
status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall);
|
|
||||||
return finishpcall(L, status, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Do a protected call with error handling. After 'lua_rotate', the
|
|
||||||
** stack will have <f, err, true, f, [args...]>; so, the function passes
|
|
||||||
** 2 to 'finishpcall' to skip the 2 first values when returning results.
|
|
||||||
*/
|
|
||||||
static int luaB_xpcall (lua_State *L) {
|
|
||||||
int status;
|
|
||||||
int n = lua_gettop(L);
|
|
||||||
luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */
|
|
||||||
lua_pushboolean(L, 1); /* first result */
|
|
||||||
lua_pushvalue(L, 1); /* function */
|
|
||||||
lua_rotate(L, 3, 2); /* move them below function's arguments */
|
|
||||||
status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
|
|
||||||
return finishpcall(L, status, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_tostring (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
luaL_tolstring(L, 1, NULL);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const luaL_Reg base_funcs[] = {
|
|
||||||
{"assert", luaB_assert},
|
|
||||||
{"collectgarbage", luaB_collectgarbage},
|
|
||||||
{"dofile", luaB_dofile},
|
|
||||||
{"error", luaB_error},
|
|
||||||
{"getmetatable", luaB_getmetatable},
|
|
||||||
{"ipairs", luaB_ipairs},
|
|
||||||
{"loadfile", luaB_loadfile},
|
|
||||||
{"load", luaB_load},
|
|
||||||
{"next", luaB_next},
|
|
||||||
{"pairs", luaB_pairs},
|
|
||||||
{"pcall", luaB_pcall},
|
|
||||||
{"print", luaB_print},
|
|
||||||
{"warn", luaB_warn},
|
|
||||||
{"rawequal", luaB_rawequal},
|
|
||||||
{"rawlen", luaB_rawlen},
|
|
||||||
{"rawget", luaB_rawget},
|
|
||||||
{"rawset", luaB_rawset},
|
|
||||||
{"select", luaB_select},
|
|
||||||
{"setmetatable", luaB_setmetatable},
|
|
||||||
{"tonumber", luaB_tonumber},
|
|
||||||
{"tostring", luaB_tostring},
|
|
||||||
{"type", luaB_type},
|
|
||||||
{"xpcall", luaB_xpcall},
|
|
||||||
/* placeholders */
|
|
||||||
{LUA_GNAME, NULL},
|
|
||||||
{"_VERSION", NULL},
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
LUAMOD_API int luaopen_base (lua_State *L) {
|
|
||||||
/* open lib into global table */
|
|
||||||
lua_pushglobaltable(L);
|
|
||||||
luaL_setfuncs(L, base_funcs, 0);
|
|
||||||
/* set global _G */
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_setfield(L, -2, LUA_GNAME);
|
|
||||||
/* set global _VERSION */
|
|
||||||
lua_pushliteral(L, LUA_VERSION);
|
|
||||||
lua_setfield(L, -2, "_VERSION");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lcode.h $
|
|
||||||
** Code generator for Lua
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef lcode_h
|
|
||||||
#define lcode_h
|
|
||||||
|
|
||||||
#include "llex.h"
|
|
||||||
#include "lobject.h"
|
|
||||||
#include "lopcodes.h"
|
|
||||||
#include "lparser.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Marks the end of a patch list. It is an invalid value both as an absolute
|
|
||||||
** address, and as a list link (would link an element to itself).
|
|
||||||
*/
|
|
||||||
#define NO_JUMP (-1)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** grep "ORDER OPR" if you change these enums (ORDER OP)
|
|
||||||
*/
|
|
||||||
typedef enum BinOpr {
|
|
||||||
/* arithmetic operators */
|
|
||||||
OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
|
|
||||||
OPR_DIV, OPR_IDIV,
|
|
||||||
/* bitwise operators */
|
|
||||||
OPR_BAND, OPR_BOR, OPR_BXOR,
|
|
||||||
OPR_SHL, OPR_SHR,
|
|
||||||
/* string operator */
|
|
||||||
OPR_CONCAT,
|
|
||||||
/* comparison operators */
|
|
||||||
OPR_EQ, OPR_LT, OPR_LE,
|
|
||||||
OPR_NE, OPR_GT, OPR_GE,
|
|
||||||
/* logical operators */
|
|
||||||
OPR_AND, OPR_OR,
|
|
||||||
OPR_NOBINOPR
|
|
||||||
} BinOpr;
|
|
||||||
|
|
||||||
|
|
||||||
/* true if operation is foldable (that is, it is arithmetic or bitwise) */
|
|
||||||
#define foldbinop(op) ((op) <= OPR_SHR)
|
|
||||||
|
|
||||||
|
|
||||||
#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0)
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
|
|
||||||
|
|
||||||
|
|
||||||
/* get (pointer to) instruction of given 'expdesc' */
|
|
||||||
#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info])
|
|
||||||
|
|
||||||
|
|
||||||
#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
|
|
||||||
|
|
||||||
#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
|
|
||||||
|
|
||||||
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
|
|
||||||
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
|
|
||||||
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
|
|
||||||
int B, int C, int k);
|
|
||||||
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
|
|
||||||
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
|
|
||||||
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
|
|
||||||
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
|
|
||||||
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
|
|
||||||
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
|
|
||||||
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
|
|
||||||
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
|
|
||||||
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
|
|
||||||
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
|
|
||||||
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
|
|
||||||
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
|
|
||||||
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
|
|
||||||
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
|
|
||||||
LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
|
|
||||||
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
|
|
||||||
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
|
|
||||||
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
|
|
||||||
LUAI_FUNC int luaK_jump (FuncState *fs);
|
|
||||||
LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
|
|
||||||
LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
|
|
||||||
LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
|
|
||||||
LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
|
|
||||||
LUAI_FUNC int luaK_getlabel (FuncState *fs);
|
|
||||||
LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
|
|
||||||
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
|
|
||||||
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
|
|
||||||
expdesc *v2, int line);
|
|
||||||
LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
|
|
||||||
int ra, int asize, int hsize);
|
|
||||||
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
|
|
||||||
LUAI_FUNC void luaK_finish (FuncState *fs);
|
|
||||||
LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,210 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lcorolib.c $
|
|
||||||
** Coroutine Library
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define lcorolib_c
|
|
||||||
#define LUA_LIB
|
|
||||||
|
|
||||||
#include "lprefix.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
#include "lauxlib.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
|
|
||||||
|
|
||||||
static lua_State *getco (lua_State *L) {
|
|
||||||
lua_State *co = lua_tothread(L, 1);
|
|
||||||
luaL_argexpected(L, co, 1, "thread");
|
|
||||||
return co;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Resumes a coroutine. Returns the number of results for non-error
|
|
||||||
** cases or -1 for errors.
|
|
||||||
*/
|
|
||||||
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
|
||||||
int status, nres;
|
|
||||||
if (l_unlikely(!lua_checkstack(co, narg))) {
|
|
||||||
lua_pushliteral(L, "too many arguments to resume");
|
|
||||||
return -1; /* error flag */
|
|
||||||
}
|
|
||||||
lua_xmove(L, co, narg);
|
|
||||||
status = lua_resume(co, L, narg, &nres);
|
|
||||||
if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
|
|
||||||
if (l_unlikely(!lua_checkstack(L, nres + 1))) {
|
|
||||||
lua_pop(co, nres); /* remove results anyway */
|
|
||||||
lua_pushliteral(L, "too many results to resume");
|
|
||||||
return -1; /* error flag */
|
|
||||||
}
|
|
||||||
lua_xmove(co, L, nres); /* move yielded values */
|
|
||||||
return nres;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lua_xmove(co, L, 1); /* move error message */
|
|
||||||
return -1; /* error flag */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_coresume (lua_State *L) {
|
|
||||||
lua_State *co = getco(L);
|
|
||||||
int r;
|
|
||||||
r = auxresume(L, co, lua_gettop(L) - 1);
|
|
||||||
if (l_unlikely(r < 0)) {
|
|
||||||
lua_pushboolean(L, 0);
|
|
||||||
lua_insert(L, -2);
|
|
||||||
return 2; /* return false + error message */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lua_pushboolean(L, 1);
|
|
||||||
lua_insert(L, -(r + 1));
|
|
||||||
return r + 1; /* return true + 'resume' returns */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_auxwrap (lua_State *L) {
|
|
||||||
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
|
||||||
int r = auxresume(L, co, lua_gettop(L));
|
|
||||||
if (l_unlikely(r < 0)) { /* error? */
|
|
||||||
int stat = lua_status(co);
|
|
||||||
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
|
|
||||||
stat = lua_closethread(co, L); /* close its tbc variables */
|
|
||||||
lua_assert(stat != LUA_OK);
|
|
||||||
lua_xmove(co, L, 1); /* move error message to the caller */
|
|
||||||
}
|
|
||||||
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
|
||||||
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
|
|
||||||
luaL_where(L, 1); /* add extra info, if available */
|
|
||||||
lua_insert(L, -2);
|
|
||||||
lua_concat(L, 2);
|
|
||||||
}
|
|
||||||
return lua_error(L); /* propagate error */
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_cocreate (lua_State *L) {
|
|
||||||
lua_State *NL;
|
|
||||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
|
||||||
NL = lua_newthread(L);
|
|
||||||
lua_pushvalue(L, 1); /* move function to top */
|
|
||||||
lua_xmove(L, NL, 1); /* move function from L to NL */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_cowrap (lua_State *L) {
|
|
||||||
luaB_cocreate(L);
|
|
||||||
lua_pushcclosure(L, luaB_auxwrap, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_yield (lua_State *L) {
|
|
||||||
return lua_yield(L, lua_gettop(L));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define COS_RUN 0
|
|
||||||
#define COS_DEAD 1
|
|
||||||
#define COS_YIELD 2
|
|
||||||
#define COS_NORM 3
|
|
||||||
|
|
||||||
|
|
||||||
static const char *const statname[] =
|
|
||||||
{"running", "dead", "suspended", "normal"};
|
|
||||||
|
|
||||||
|
|
||||||
static int auxstatus (lua_State *L, lua_State *co) {
|
|
||||||
if (L == co) return COS_RUN;
|
|
||||||
else {
|
|
||||||
switch (lua_status(co)) {
|
|
||||||
case LUA_YIELD:
|
|
||||||
return COS_YIELD;
|
|
||||||
case LUA_OK: {
|
|
||||||
lua_Debug ar;
|
|
||||||
if (lua_getstack(co, 0, &ar)) /* does it have frames? */
|
|
||||||
return COS_NORM; /* it is running */
|
|
||||||
else if (lua_gettop(co) == 0)
|
|
||||||
return COS_DEAD;
|
|
||||||
else
|
|
||||||
return COS_YIELD; /* initial state */
|
|
||||||
}
|
|
||||||
default: /* some error occurred */
|
|
||||||
return COS_DEAD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_costatus (lua_State *L) {
|
|
||||||
lua_State *co = getco(L);
|
|
||||||
lua_pushstring(L, statname[auxstatus(L, co)]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_yieldable (lua_State *L) {
|
|
||||||
lua_State *co = lua_isnone(L, 1) ? L : getco(L);
|
|
||||||
lua_pushboolean(L, lua_isyieldable(co));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_corunning (lua_State *L) {
|
|
||||||
int ismain = lua_pushthread(L);
|
|
||||||
lua_pushboolean(L, ismain);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_close (lua_State *L) {
|
|
||||||
lua_State *co = getco(L);
|
|
||||||
int status = auxstatus(L, co);
|
|
||||||
switch (status) {
|
|
||||||
case COS_DEAD: case COS_YIELD: {
|
|
||||||
status = lua_closethread(co, L);
|
|
||||||
if (status == LUA_OK) {
|
|
||||||
lua_pushboolean(L, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lua_pushboolean(L, 0);
|
|
||||||
lua_xmove(co, L, 1); /* move error message */
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: /* normal or running coroutine */
|
|
||||||
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const luaL_Reg co_funcs[] = {
|
|
||||||
{"create", luaB_cocreate},
|
|
||||||
{"resume", luaB_coresume},
|
|
||||||
{"running", luaB_corunning},
|
|
||||||
{"status", luaB_costatus},
|
|
||||||
{"wrap", luaB_cowrap},
|
|
||||||
{"yield", luaB_yield},
|
|
||||||
{"isyieldable", luaB_yieldable},
|
|
||||||
{"close", luaB_close},
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LUAMOD_API int luaopen_coroutine (lua_State *L) {
|
|
||||||
luaL_newlib(L, co_funcs);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lctype.c $
|
|
||||||
** 'ctype' functions for Lua
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define lctype_c
|
|
||||||
#define LUA_CORE
|
|
||||||
|
|
||||||
#include "lprefix.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include "lctype.h"
|
|
||||||
|
|
||||||
#if !LUA_USE_CTYPE /* { */
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
|
|
||||||
#if defined (LUA_UCID) /* accept UniCode IDentifiers? */
|
|
||||||
/* consider all non-ascii codepoints to be alphabetic */
|
|
||||||
#define NONA 0x01
|
|
||||||
#else
|
|
||||||
#define NONA 0x00 /* default */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
|
|
||||||
0x00, /* EOZ */
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
|
|
||||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
|
|
||||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
||||||
0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
|
|
||||||
0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
|
||||||
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
|
|
||||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
||||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
|
|
||||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05,
|
|
||||||
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
|
|
||||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
|
||||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
|
|
||||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
|
||||||
0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */
|
|
||||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
|
||||||
NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* } */
|
|
@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lctype.h $
|
|
||||||
** 'ctype' functions for Lua
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef lctype_h
|
|
||||||
#define lctype_h
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** WARNING: the functions defined here do not necessarily correspond
|
|
||||||
** to the similar functions in the standard C ctype.h. They are
|
|
||||||
** optimized for the specific needs of Lua.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if !defined(LUA_USE_CTYPE)
|
|
||||||
|
|
||||||
#if 'A' == 65 && '0' == 48
|
|
||||||
/* ASCII case: can use its own tables; faster and fixed */
|
|
||||||
#define LUA_USE_CTYPE 0
|
|
||||||
#else
|
|
||||||
/* must use standard C ctype */
|
|
||||||
#define LUA_USE_CTYPE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if !LUA_USE_CTYPE /* { */
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "llimits.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define ALPHABIT 0
|
|
||||||
#define DIGITBIT 1
|
|
||||||
#define PRINTBIT 2
|
|
||||||
#define SPACEBIT 3
|
|
||||||
#define XDIGITBIT 4
|
|
||||||
|
|
||||||
|
|
||||||
#define MASK(B) (1 << (B))
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** add 1 to char to allow index -1 (EOZ)
|
|
||||||
*/
|
|
||||||
#define testprop(c,p) (luai_ctype_[(c)+1] & (p))
|
|
||||||
|
|
||||||
/*
|
|
||||||
** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
|
|
||||||
*/
|
|
||||||
#define lislalpha(c) testprop(c, MASK(ALPHABIT))
|
|
||||||
#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT)))
|
|
||||||
#define lisdigit(c) testprop(c, MASK(DIGITBIT))
|
|
||||||
#define lisspace(c) testprop(c, MASK(SPACEBIT))
|
|
||||||
#define lisprint(c) testprop(c, MASK(PRINTBIT))
|
|
||||||
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** In ASCII, this 'ltolower' is correct for alphabetic characters and
|
|
||||||
** for '.'. That is enough for Lua needs. ('check_exp' ensures that
|
|
||||||
** the character either is an upper-case letter or is unchanged by
|
|
||||||
** the transformation, which holds for lower-case letters and '.'.)
|
|
||||||
*/
|
|
||||||
#define ltolower(c) \
|
|
||||||
check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
|
|
||||||
(c) | ('A' ^ 'a'))
|
|
||||||
|
|
||||||
|
|
||||||
/* one entry for each character and for -1 (EOZ) */
|
|
||||||
LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)
|
|
||||||
|
|
||||||
|
|
||||||
#else /* }{ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
** use standard C ctypes
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define lislalpha(c) (isalpha(c) || (c) == '_')
|
|
||||||
#define lislalnum(c) (isalnum(c) || (c) == '_')
|
|
||||||
#define lisdigit(c) (isdigit(c))
|
|
||||||
#define lisspace(c) (isspace(c))
|
|
||||||
#define lisprint(c) (isprint(c))
|
|
||||||
#define lisxdigit(c) (isxdigit(c))
|
|
||||||
|
|
||||||
#define ltolower(c) (tolower(c))
|
|
||||||
|
|
||||||
#endif /* } */
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,483 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: ldblib.c $
|
|
||||||
** Interface from Lua to its debug API
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define ldblib_c
|
|
||||||
#define LUA_LIB
|
|
||||||
|
|
||||||
#include "lprefix.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
#include "lauxlib.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** The hook table at registry[HOOKKEY] maps threads to their current
|
|
||||||
** hook function.
|
|
||||||
*/
|
|
||||||
static const char *const HOOKKEY = "_HOOKKEY";
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** If L1 != L, L1 can be in any state, and therefore there are no
|
|
||||||
** guarantees about its stack space; any push in L1 must be
|
|
||||||
** checked.
|
|
||||||
*/
|
|
||||||
static void checkstack (lua_State *L, lua_State *L1, int n) {
|
|
||||||
if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
|
|
||||||
luaL_error(L, "stack overflow");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_getregistry (lua_State *L) {
|
|
||||||
lua_pushvalue(L, LUA_REGISTRYINDEX);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_getmetatable (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
if (!lua_getmetatable(L, 1)) {
|
|
||||||
lua_pushnil(L); /* no metatable */
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_setmetatable (lua_State *L) {
|
|
||||||
int t = lua_type(L, 2);
|
|
||||||
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
|
||||||
lua_settop(L, 2);
|
|
||||||
lua_setmetatable(L, 1);
|
|
||||||
return 1; /* return 1st argument */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_getuservalue (lua_State *L) {
|
|
||||||
int n = (int)luaL_optinteger(L, 2, 1);
|
|
||||||
if (lua_type(L, 1) != LUA_TUSERDATA)
|
|
||||||
luaL_pushfail(L);
|
|
||||||
else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
|
|
||||||
lua_pushboolean(L, 1);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_setuservalue (lua_State *L) {
|
|
||||||
int n = (int)luaL_optinteger(L, 3, 1);
|
|
||||||
luaL_checktype(L, 1, LUA_TUSERDATA);
|
|
||||||
luaL_checkany(L, 2);
|
|
||||||
lua_settop(L, 2);
|
|
||||||
if (!lua_setiuservalue(L, 1, n))
|
|
||||||
luaL_pushfail(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Auxiliary function used by several library functions: check for
|
|
||||||
** an optional thread as function's first argument and set 'arg' with
|
|
||||||
** 1 if this argument is present (so that functions can skip it to
|
|
||||||
** access their other arguments)
|
|
||||||
*/
|
|
||||||
static lua_State *getthread (lua_State *L, int *arg) {
|
|
||||||
if (lua_isthread(L, 1)) {
|
|
||||||
*arg = 1;
|
|
||||||
return lua_tothread(L, 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*arg = 0;
|
|
||||||
return L; /* function will operate over current thread */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Variations of 'lua_settable', used by 'db_getinfo' to put results
|
|
||||||
** from 'lua_getinfo' into result table. Key is always a string;
|
|
||||||
** value can be a string, an int, or a boolean.
|
|
||||||
*/
|
|
||||||
static void settabss (lua_State *L, const char *k, const char *v) {
|
|
||||||
lua_pushstring(L, v);
|
|
||||||
lua_setfield(L, -2, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void settabsi (lua_State *L, const char *k, int v) {
|
|
||||||
lua_pushinteger(L, v);
|
|
||||||
lua_setfield(L, -2, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void settabsb (lua_State *L, const char *k, int v) {
|
|
||||||
lua_pushboolean(L, v);
|
|
||||||
lua_setfield(L, -2, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** In function 'db_getinfo', the call to 'lua_getinfo' may push
|
|
||||||
** results on the stack; later it creates the result table to put
|
|
||||||
** these objects. Function 'treatstackoption' puts the result from
|
|
||||||
** 'lua_getinfo' on top of the result table so that it can call
|
|
||||||
** 'lua_setfield'.
|
|
||||||
*/
|
|
||||||
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
|
|
||||||
if (L == L1)
|
|
||||||
lua_rotate(L, -2, 1); /* exchange object and table */
|
|
||||||
else
|
|
||||||
lua_xmove(L1, L, 1); /* move object to the "main" stack */
|
|
||||||
lua_setfield(L, -2, fname); /* put object into table */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Calls 'lua_getinfo' and collects all results in a new table.
|
|
||||||
** L1 needs stack space for an optional input (function) plus
|
|
||||||
** two optional outputs (function and line table) from function
|
|
||||||
** 'lua_getinfo'.
|
|
||||||
*/
|
|
||||||
static int db_getinfo (lua_State *L) {
|
|
||||||
lua_Debug ar;
|
|
||||||
int arg;
|
|
||||||
lua_State *L1 = getthread(L, &arg);
|
|
||||||
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
|
|
||||||
checkstack(L, L1, 3);
|
|
||||||
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
|
|
||||||
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
|
|
||||||
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
|
|
||||||
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
|
|
||||||
lua_xmove(L, L1, 1);
|
|
||||||
}
|
|
||||||
else { /* stack level */
|
|
||||||
if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
|
|
||||||
luaL_pushfail(L); /* level out of range */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!lua_getinfo(L1, options, &ar))
|
|
||||||
return luaL_argerror(L, arg+2, "invalid option");
|
|
||||||
lua_newtable(L); /* table to collect results */
|
|
||||||
if (strchr(options, 'S')) {
|
|
||||||
lua_pushlstring(L, ar.source, ar.srclen);
|
|
||||||
lua_setfield(L, -2, "source");
|
|
||||||
settabss(L, "short_src", ar.short_src);
|
|
||||||
settabsi(L, "linedefined", ar.linedefined);
|
|
||||||
settabsi(L, "lastlinedefined", ar.lastlinedefined);
|
|
||||||
settabss(L, "what", ar.what);
|
|
||||||
}
|
|
||||||
if (strchr(options, 'l'))
|
|
||||||
settabsi(L, "currentline", ar.currentline);
|
|
||||||
if (strchr(options, 'u')) {
|
|
||||||
settabsi(L, "nups", ar.nups);
|
|
||||||
settabsi(L, "nparams", ar.nparams);
|
|
||||||
settabsb(L, "isvararg", ar.isvararg);
|
|
||||||
}
|
|
||||||
if (strchr(options, 'n')) {
|
|
||||||
settabss(L, "name", ar.name);
|
|
||||||
settabss(L, "namewhat", ar.namewhat);
|
|
||||||
}
|
|
||||||
if (strchr(options, 'r')) {
|
|
||||||
settabsi(L, "ftransfer", ar.ftransfer);
|
|
||||||
settabsi(L, "ntransfer", ar.ntransfer);
|
|
||||||
}
|
|
||||||
if (strchr(options, 't'))
|
|
||||||
settabsb(L, "istailcall", ar.istailcall);
|
|
||||||
if (strchr(options, 'L'))
|
|
||||||
treatstackoption(L, L1, "activelines");
|
|
||||||
if (strchr(options, 'f'))
|
|
||||||
treatstackoption(L, L1, "func");
|
|
||||||
return 1; /* return table */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_getlocal (lua_State *L) {
|
|
||||||
int arg;
|
|
||||||
lua_State *L1 = getthread(L, &arg);
|
|
||||||
int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */
|
|
||||||
if (lua_isfunction(L, arg + 1)) { /* function argument? */
|
|
||||||
lua_pushvalue(L, arg + 1); /* push function */
|
|
||||||
lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */
|
|
||||||
return 1; /* return only name (there is no value) */
|
|
||||||
}
|
|
||||||
else { /* stack-level argument */
|
|
||||||
lua_Debug ar;
|
|
||||||
const char *name;
|
|
||||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
|
||||||
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
|
|
||||||
return luaL_argerror(L, arg+1, "level out of range");
|
|
||||||
checkstack(L, L1, 1);
|
|
||||||
name = lua_getlocal(L1, &ar, nvar);
|
|
||||||
if (name) {
|
|
||||||
lua_xmove(L1, L, 1); /* move local value */
|
|
||||||
lua_pushstring(L, name); /* push name */
|
|
||||||
lua_rotate(L, -2, 1); /* re-order */
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
luaL_pushfail(L); /* no name (nor value) */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_setlocal (lua_State *L) {
|
|
||||||
int arg;
|
|
||||||
const char *name;
|
|
||||||
lua_State *L1 = getthread(L, &arg);
|
|
||||||
lua_Debug ar;
|
|
||||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
|
||||||
int nvar = (int)luaL_checkinteger(L, arg + 2);
|
|
||||||
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
|
|
||||||
return luaL_argerror(L, arg+1, "level out of range");
|
|
||||||
luaL_checkany(L, arg+3);
|
|
||||||
lua_settop(L, arg+3);
|
|
||||||
checkstack(L, L1, 1);
|
|
||||||
lua_xmove(L, L1, 1);
|
|
||||||
name = lua_setlocal(L1, &ar, nvar);
|
|
||||||
if (name == NULL)
|
|
||||||
lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */
|
|
||||||
lua_pushstring(L, name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** get (if 'get' is true) or set an upvalue from a closure
|
|
||||||
*/
|
|
||||||
static int auxupvalue (lua_State *L, int get) {
|
|
||||||
const char *name;
|
|
||||||
int n = (int)luaL_checkinteger(L, 2); /* upvalue index */
|
|
||||||
luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */
|
|
||||||
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
|
|
||||||
if (name == NULL) return 0;
|
|
||||||
lua_pushstring(L, name);
|
|
||||||
lua_insert(L, -(get+1)); /* no-op if get is false */
|
|
||||||
return get + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_getupvalue (lua_State *L) {
|
|
||||||
return auxupvalue(L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_setupvalue (lua_State *L) {
|
|
||||||
luaL_checkany(L, 3);
|
|
||||||
return auxupvalue(L, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Check whether a given upvalue from a given closure exists and
|
|
||||||
** returns its index
|
|
||||||
*/
|
|
||||||
static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
|
|
||||||
void *id;
|
|
||||||
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
|
|
||||||
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
|
|
||||||
id = lua_upvalueid(L, argf, nup);
|
|
||||||
if (pnup) {
|
|
||||||
luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
|
|
||||||
*pnup = nup;
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_upvalueid (lua_State *L) {
|
|
||||||
void *id = checkupval(L, 1, 2, NULL);
|
|
||||||
if (id != NULL)
|
|
||||||
lua_pushlightuserdata(L, id);
|
|
||||||
else
|
|
||||||
luaL_pushfail(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_upvaluejoin (lua_State *L) {
|
|
||||||
int n1, n2;
|
|
||||||
checkupval(L, 1, 2, &n1);
|
|
||||||
checkupval(L, 3, 4, &n2);
|
|
||||||
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
|
|
||||||
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
|
|
||||||
lua_upvaluejoin(L, 1, n1, 3, n2);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Call hook function registered at hook table for the current
|
|
||||||
** thread (if there is one)
|
|
||||||
*/
|
|
||||||
static void hookf (lua_State *L, lua_Debug *ar) {
|
|
||||||
static const char *const hooknames[] =
|
|
||||||
{"call", "return", "line", "count", "tail call"};
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
|
|
||||||
lua_pushthread(L);
|
|
||||||
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
|
|
||||||
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
|
|
||||||
if (ar->currentline >= 0)
|
|
||||||
lua_pushinteger(L, ar->currentline); /* push current line */
|
|
||||||
else lua_pushnil(L);
|
|
||||||
lua_assert(lua_getinfo(L, "lS", ar));
|
|
||||||
lua_call(L, 2, 0); /* call hook function */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Convert a string mask (for 'sethook') into a bit mask
|
|
||||||
*/
|
|
||||||
static int makemask (const char *smask, int count) {
|
|
||||||
int mask = 0;
|
|
||||||
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
|
|
||||||
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
|
|
||||||
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
|
|
||||||
if (count > 0) mask |= LUA_MASKCOUNT;
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Convert a bit mask (for 'gethook') into a string mask
|
|
||||||
*/
|
|
||||||
static char *unmakemask (int mask, char *smask) {
|
|
||||||
int i = 0;
|
|
||||||
if (mask & LUA_MASKCALL) smask[i++] = 'c';
|
|
||||||
if (mask & LUA_MASKRET) smask[i++] = 'r';
|
|
||||||
if (mask & LUA_MASKLINE) smask[i++] = 'l';
|
|
||||||
smask[i] = '\0';
|
|
||||||
return smask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_sethook (lua_State *L) {
|
|
||||||
int arg, mask, count;
|
|
||||||
lua_Hook func;
|
|
||||||
lua_State *L1 = getthread(L, &arg);
|
|
||||||
if (lua_isnoneornil(L, arg+1)) { /* no hook? */
|
|
||||||
lua_settop(L, arg+1);
|
|
||||||
func = NULL; mask = 0; count = 0; /* turn off hooks */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const char *smask = luaL_checkstring(L, arg+2);
|
|
||||||
luaL_checktype(L, arg+1, LUA_TFUNCTION);
|
|
||||||
count = (int)luaL_optinteger(L, arg + 3, 0);
|
|
||||||
func = hookf; mask = makemask(smask, count);
|
|
||||||
}
|
|
||||||
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
|
|
||||||
/* table just created; initialize it */
|
|
||||||
lua_pushliteral(L, "k");
|
|
||||||
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
|
|
||||||
}
|
|
||||||
checkstack(L, L1, 1);
|
|
||||||
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
|
|
||||||
lua_pushvalue(L, arg + 1); /* value (hook function) */
|
|
||||||
lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
|
|
||||||
lua_sethook(L1, func, mask, count);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_gethook (lua_State *L) {
|
|
||||||
int arg;
|
|
||||||
lua_State *L1 = getthread(L, &arg);
|
|
||||||
char buff[5];
|
|
||||||
int mask = lua_gethookmask(L1);
|
|
||||||
lua_Hook hook = lua_gethook(L1);
|
|
||||||
if (hook == NULL) { /* no hook? */
|
|
||||||
luaL_pushfail(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if (hook != hookf) /* external hook? */
|
|
||||||
lua_pushliteral(L, "external hook");
|
|
||||||
else { /* hook table must exist */
|
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
|
|
||||||
checkstack(L, L1, 1);
|
|
||||||
lua_pushthread(L1); lua_xmove(L1, L, 1);
|
|
||||||
lua_rawget(L, -2); /* 1st result = hooktable[L1] */
|
|
||||||
lua_remove(L, -2); /* remove hook table */
|
|
||||||
}
|
|
||||||
lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */
|
|
||||||
lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_debug (lua_State *L) {
|
|
||||||
for (;;) {
|
|
||||||
char buffer[250];
|
|
||||||
lua_writestringerror("%s", "lua_debug> ");
|
|
||||||
if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
|
|
||||||
strcmp(buffer, "cont\n") == 0)
|
|
||||||
return 0;
|
|
||||||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
|
||||||
lua_pcall(L, 0, 0, 0))
|
|
||||||
lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL));
|
|
||||||
lua_settop(L, 0); /* remove eventual returns */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_traceback (lua_State *L) {
|
|
||||||
int arg;
|
|
||||||
lua_State *L1 = getthread(L, &arg);
|
|
||||||
const char *msg = lua_tostring(L, arg + 1);
|
|
||||||
if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */
|
|
||||||
lua_pushvalue(L, arg + 1); /* return it untouched */
|
|
||||||
else {
|
|
||||||
int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0);
|
|
||||||
luaL_traceback(L, L1, msg, level);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int db_setcstacklimit (lua_State *L) {
|
|
||||||
int limit = (int)luaL_checkinteger(L, 1);
|
|
||||||
int res = lua_setcstacklimit(L, limit);
|
|
||||||
lua_pushinteger(L, res);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const luaL_Reg dblib[] = {
|
|
||||||
{"debug", db_debug},
|
|
||||||
{"getuservalue", db_getuservalue},
|
|
||||||
{"gethook", db_gethook},
|
|
||||||
{"getinfo", db_getinfo},
|
|
||||||
{"getlocal", db_getlocal},
|
|
||||||
{"getregistry", db_getregistry},
|
|
||||||
{"getmetatable", db_getmetatable},
|
|
||||||
{"getupvalue", db_getupvalue},
|
|
||||||
{"upvaluejoin", db_upvaluejoin},
|
|
||||||
{"upvalueid", db_upvalueid},
|
|
||||||
{"setuservalue", db_setuservalue},
|
|
||||||
{"sethook", db_sethook},
|
|
||||||
{"setlocal", db_setlocal},
|
|
||||||
{"setmetatable", db_setmetatable},
|
|
||||||
{"setupvalue", db_setupvalue},
|
|
||||||
{"traceback", db_traceback},
|
|
||||||
{"setcstacklimit", db_setcstacklimit},
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
LUAMOD_API int luaopen_debug (lua_State *L) {
|
|
||||||
luaL_newlib(L, dblib);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,962 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: ldebug.c $
|
|
||||||
** Debug Interface
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define ldebug_c
|
|
||||||
#define LUA_CORE
|
|
||||||
|
|
||||||
#include "lprefix.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
#include "lapi.h"
|
|
||||||
#include "lcode.h"
|
|
||||||
#include "ldebug.h"
|
|
||||||
#include "ldo.h"
|
|
||||||
#include "lfunc.h"
|
|
||||||
#include "lobject.h"
|
|
||||||
#include "lopcodes.h"
|
|
||||||
#include "lstate.h"
|
|
||||||
#include "lstring.h"
|
|
||||||
#include "ltable.h"
|
|
||||||
#include "ltm.h"
|
|
||||||
#include "lvm.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL)
|
|
||||||
|
|
||||||
|
|
||||||
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
|
||||||
const char **name);
|
|
||||||
|
|
||||||
|
|
||||||
static int currentpc (CallInfo *ci) {
|
|
||||||
lua_assert(isLua(ci));
|
|
||||||
return pcRel(ci->u.l.savedpc, ci_func(ci)->p);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Get a "base line" to find the line corresponding to an instruction.
|
|
||||||
** Base lines are regularly placed at MAXIWTHABS intervals, so usually
|
|
||||||
** an integer division gets the right place. When the source file has
|
|
||||||
** large sequences of empty/comment lines, it may need extra entries,
|
|
||||||
** so the original estimate needs a correction.
|
|
||||||
** If the original estimate is -1, the initial 'if' ensures that the
|
|
||||||
** 'while' will run at least once.
|
|
||||||
** The assertion that the estimate is a lower bound for the correct base
|
|
||||||
** is valid as long as the debug info has been generated with the same
|
|
||||||
** value for MAXIWTHABS or smaller. (Previous releases use a little
|
|
||||||
** smaller value.)
|
|
||||||
*/
|
|
||||||
static int getbaseline (const Proto *f, int pc, int *basepc) {
|
|
||||||
if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
|
|
||||||
*basepc = -1; /* start from the beginning */
|
|
||||||
return f->linedefined;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
|
|
||||||
/* estimate must be a lower bound of the correct base */
|
|
||||||
lua_assert(i < 0 ||
|
|
||||||
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
|
|
||||||
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
|
|
||||||
i++; /* low estimate; adjust it */
|
|
||||||
*basepc = f->abslineinfo[i].pc;
|
|
||||||
return f->abslineinfo[i].line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Get the line corresponding to instruction 'pc' in function 'f';
|
|
||||||
** first gets a base line and from there does the increments until
|
|
||||||
** the desired instruction.
|
|
||||||
*/
|
|
||||||
int luaG_getfuncline (const Proto *f, int pc) {
|
|
||||||
if (f->lineinfo == NULL) /* no debug information? */
|
|
||||||
return -1;
|
|
||||||
else {
|
|
||||||
int basepc;
|
|
||||||
int baseline = getbaseline(f, pc, &basepc);
|
|
||||||
while (basepc++ < pc) { /* walk until given instruction */
|
|
||||||
lua_assert(f->lineinfo[basepc] != ABSLINEINFO);
|
|
||||||
baseline += f->lineinfo[basepc]; /* correct line */
|
|
||||||
}
|
|
||||||
return baseline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int getcurrentline (CallInfo *ci) {
|
|
||||||
return luaG_getfuncline(ci_func(ci)->p, currentpc(ci));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Set 'trap' for all active Lua frames.
|
|
||||||
** This function can be called during a signal, under "reasonable"
|
|
||||||
** assumptions. A new 'ci' is completely linked in the list before it
|
|
||||||
** becomes part of the "active" list, and we assume that pointers are
|
|
||||||
** atomic; see comment in next function.
|
|
||||||
** (A compiler doing interprocedural optimizations could, theoretically,
|
|
||||||
** reorder memory writes in such a way that the list could be
|
|
||||||
** temporarily broken while inserting a new element. We simply assume it
|
|
||||||
** has no good reasons to do that.)
|
|
||||||
*/
|
|
||||||
static void settraps (CallInfo *ci) {
|
|
||||||
for (; ci != NULL; ci = ci->previous)
|
|
||||||
if (isLua(ci))
|
|
||||||
ci->u.l.trap = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** This function can be called during a signal, under "reasonable"
|
|
||||||
** assumptions.
|
|
||||||
** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
|
|
||||||
** are for debug only, and it is no problem if they get arbitrary
|
|
||||||
** values (causes at most one wrong hook call). 'hookmask' is an atomic
|
|
||||||
** value. We assume that pointers are atomic too (e.g., gcc ensures that
|
|
||||||
** for all platforms where it runs). Moreover, 'hook' is always checked
|
|
||||||
** before being called (see 'luaD_hook').
|
|
||||||
*/
|
|
||||||
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
|
|
||||||
if (func == NULL || mask == 0) { /* turn off hooks? */
|
|
||||||
mask = 0;
|
|
||||||
func = NULL;
|
|
||||||
}
|
|
||||||
L->hook = func;
|
|
||||||
L->basehookcount = count;
|
|
||||||
resethookcount(L);
|
|
||||||
L->hookmask = cast_byte(mask);
|
|
||||||
if (mask)
|
|
||||||
settraps(L->ci); /* to trace inside 'luaV_execute' */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_API lua_Hook lua_gethook (lua_State *L) {
|
|
||||||
return L->hook;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_API int lua_gethookmask (lua_State *L) {
|
|
||||||
return L->hookmask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_API int lua_gethookcount (lua_State *L) {
|
|
||||||
return L->basehookcount;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
|
|
||||||
int status;
|
|
||||||
CallInfo *ci;
|
|
||||||
if (level < 0) return 0; /* invalid (negative) level */
|
|
||||||
lua_lock(L);
|
|
||||||
for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous)
|
|
||||||
level--;
|
|
||||||
if (level == 0 && ci != &L->base_ci) { /* level found? */
|
|
||||||
status = 1;
|
|
||||||
ar->i_ci = ci;
|
|
||||||
}
|
|
||||||
else status = 0; /* no such level */
|
|
||||||
lua_unlock(L);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const char *upvalname (const Proto *p, int uv) {
|
|
||||||
TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name);
|
|
||||||
if (s == NULL) return "?";
|
|
||||||
else return getstr(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
|
||||||
if (clLvalue(s2v(ci->func.p))->p->is_vararg) {
|
|
||||||
int nextra = ci->u.l.nextraargs;
|
|
||||||
if (n >= -nextra) { /* 'n' is negative */
|
|
||||||
*pos = ci->func.p - nextra - (n + 1);
|
|
||||||
return "(vararg)"; /* generic name for any vararg */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL; /* no such vararg */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
|
||||||
StkId base = ci->func.p + 1;
|
|
||||||
const char *name = NULL;
|
|
||||||
if (isLua(ci)) {
|
|
||||||
if (n < 0) /* access to vararg values? */
|
|
||||||
return findvararg(ci, n, pos);
|
|
||||||
else
|
|
||||||
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
|
|
||||||
}
|
|
||||||
if (name == NULL) { /* no 'standard' name? */
|
|
||||||
StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p;
|
|
||||||
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
|
|
||||||
/* generic name for any valid slot */
|
|
||||||
name = isLua(ci) ? "(temporary)" : "(C temporary)";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return NULL; /* no name */
|
|
||||||
}
|
|
||||||
if (pos)
|
|
||||||
*pos = base + (n - 1);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|
||||||
const char *name;
|
|
||||||
lua_lock(L);
|
|
||||||
if (ar == NULL) { /* information about non-active function? */
|
|
||||||
if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */
|
|
||||||
name = NULL;
|
|
||||||
else /* consider live variables at function start (parameters) */
|
|
||||||
name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0);
|
|
||||||
}
|
|
||||||
else { /* active function; get information through 'ar' */
|
|
||||||
StkId pos = NULL; /* to avoid warnings */
|
|
||||||
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
|
||||||
if (name) {
|
|
||||||
setobjs2s(L, L->top.p, pos);
|
|
||||||
api_incr_top(L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lua_unlock(L);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|
||||||
StkId pos = NULL; /* to avoid warnings */
|
|
||||||
const char *name;
|
|
||||||
lua_lock(L);
|
|
||||||
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
|
||||||
if (name) {
|
|
||||||
setobjs2s(L, pos, L->top.p - 1);
|
|
||||||
L->top.p--; /* pop value */
|
|
||||||
}
|
|
||||||
lua_unlock(L);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void funcinfo (lua_Debug *ar, Closure *cl) {
|
|
||||||
if (!LuaClosure(cl)) {
|
|
||||||
ar->source = "=[C]";
|
|
||||||
ar->srclen = LL("=[C]");
|
|
||||||
ar->linedefined = -1;
|
|
||||||
ar->lastlinedefined = -1;
|
|
||||||
ar->what = "C";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const Proto *p = cl->l.p;
|
|
||||||
if (p->source) {
|
|
||||||
ar->source = getstr(p->source);
|
|
||||||
ar->srclen = tsslen(p->source);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ar->source = "=?";
|
|
||||||
ar->srclen = LL("=?");
|
|
||||||
}
|
|
||||||
ar->linedefined = p->linedefined;
|
|
||||||
ar->lastlinedefined = p->lastlinedefined;
|
|
||||||
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
|
|
||||||
}
|
|
||||||
luaO_chunkid(ar->short_src, ar->source, ar->srclen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int nextline (const Proto *p, int currentline, int pc) {
|
|
||||||
if (p->lineinfo[pc] != ABSLINEINFO)
|
|
||||||
return currentline + p->lineinfo[pc];
|
|
||||||
else
|
|
||||||
return luaG_getfuncline(p, pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void collectvalidlines (lua_State *L, Closure *f) {
|
|
||||||
if (!LuaClosure(f)) {
|
|
||||||
setnilvalue(s2v(L->top.p));
|
|
||||||
api_incr_top(L);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const Proto *p = f->l.p;
|
|
||||||
int currentline = p->linedefined;
|
|
||||||
Table *t = luaH_new(L); /* new table to store active lines */
|
|
||||||
sethvalue2s(L, L->top.p, t); /* push it on stack */
|
|
||||||
api_incr_top(L);
|
|
||||||
if (p->lineinfo != NULL) { /* proto with debug information? */
|
|
||||||
int i;
|
|
||||||
TValue v;
|
|
||||||
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
|
||||||
if (!p->is_vararg) /* regular function? */
|
|
||||||
i = 0; /* consider all instructions */
|
|
||||||
else { /* vararg function */
|
|
||||||
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
|
|
||||||
currentline = nextline(p, currentline, 0);
|
|
||||||
i = 1; /* skip first instruction (OP_VARARGPREP) */
|
|
||||||
}
|
|
||||||
for (; i < p->sizelineinfo; i++) { /* for each instruction */
|
|
||||||
currentline = nextline(p, currentline, i); /* get its line */
|
|
||||||
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
|
|
||||||
/* calling function is a known function? */
|
|
||||||
if (ci != NULL && !(ci->callstatus & CIST_TAIL))
|
|
||||||
return funcnamefromcall(L, ci->previous, name);
|
|
||||||
else return NULL; /* no way to find a name */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
|
|
||||||
Closure *f, CallInfo *ci) {
|
|
||||||
int status = 1;
|
|
||||||
for (; *what; what++) {
|
|
||||||
switch (*what) {
|
|
||||||
case 'S': {
|
|
||||||
funcinfo(ar, f);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'l': {
|
|
||||||
ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'u': {
|
|
||||||
ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
|
|
||||||
if (!LuaClosure(f)) {
|
|
||||||
ar->isvararg = 1;
|
|
||||||
ar->nparams = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ar->isvararg = f->l.p->is_vararg;
|
|
||||||
ar->nparams = f->l.p->numparams;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 't': {
|
|
||||||
ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'n': {
|
|
||||||
ar->namewhat = getfuncname(L, ci, &ar->name);
|
|
||||||
if (ar->namewhat == NULL) {
|
|
||||||
ar->namewhat = ""; /* not found */
|
|
||||||
ar->name = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'r': {
|
|
||||||
if (ci == NULL || !(ci->callstatus & CIST_TRAN))
|
|
||||||
ar->ftransfer = ar->ntransfer = 0;
|
|
||||||
else {
|
|
||||||
ar->ftransfer = ci->u2.transferinfo.ftransfer;
|
|
||||||
ar->ntransfer = ci->u2.transferinfo.ntransfer;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'L':
|
|
||||||
case 'f': /* handled by lua_getinfo */
|
|
||||||
break;
|
|
||||||
default: status = 0; /* invalid option */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
|
|
||||||
int status;
|
|
||||||
Closure *cl;
|
|
||||||
CallInfo *ci;
|
|
||||||
TValue *func;
|
|
||||||
lua_lock(L);
|
|
||||||
if (*what == '>') {
|
|
||||||
ci = NULL;
|
|
||||||
func = s2v(L->top.p - 1);
|
|
||||||
api_check(L, ttisfunction(func), "function expected");
|
|
||||||
what++; /* skip the '>' */
|
|
||||||
L->top.p--; /* pop function */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ci = ar->i_ci;
|
|
||||||
func = s2v(ci->func.p);
|
|
||||||
lua_assert(ttisfunction(func));
|
|
||||||
}
|
|
||||||
cl = ttisclosure(func) ? clvalue(func) : NULL;
|
|
||||||
status = auxgetinfo(L, what, ar, cl, ci);
|
|
||||||
if (strchr(what, 'f')) {
|
|
||||||
setobj2s(L, L->top.p, func);
|
|
||||||
api_incr_top(L);
|
|
||||||
}
|
|
||||||
if (strchr(what, 'L'))
|
|
||||||
collectvalidlines(L, cl);
|
|
||||||
lua_unlock(L);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {======================================================
|
|
||||||
** Symbolic Execution
|
|
||||||
** =======================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
static int filterpc (int pc, int jmptarget) {
|
|
||||||
if (pc < jmptarget) /* is code conditional (inside a jump)? */
|
|
||||||
return -1; /* cannot know who sets that register */
|
|
||||||
else return pc; /* current position sets that register */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Try to find last instruction before 'lastpc' that modified register 'reg'.
|
|
||||||
*/
|
|
||||||
static int findsetreg (const Proto *p, int lastpc, int reg) {
|
|
||||||
int pc;
|
|
||||||
int setreg = -1; /* keep last instruction that changed 'reg' */
|
|
||||||
int jmptarget = 0; /* any code before this address is conditional */
|
|
||||||
if (testMMMode(GET_OPCODE(p->code[lastpc])))
|
|
||||||
lastpc--; /* previous instruction was not actually executed */
|
|
||||||
for (pc = 0; pc < lastpc; pc++) {
|
|
||||||
Instruction i = p->code[pc];
|
|
||||||
OpCode op = GET_OPCODE(i);
|
|
||||||
int a = GETARG_A(i);
|
|
||||||
int change; /* true if current instruction changed 'reg' */
|
|
||||||
switch (op) {
|
|
||||||
case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */
|
|
||||||
int b = GETARG_B(i);
|
|
||||||
change = (a <= reg && reg <= a + b);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OP_TFORCALL: { /* affect all regs above its base */
|
|
||||||
change = (reg >= a + 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OP_CALL:
|
|
||||||
case OP_TAILCALL: { /* affect all registers above base */
|
|
||||||
change = (reg >= a);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */
|
|
||||||
int b = GETARG_sJ(i);
|
|
||||||
int dest = pc + 1 + b;
|
|
||||||
/* jump does not skip 'lastpc' and is larger than current one? */
|
|
||||||
if (dest <= lastpc && dest > jmptarget)
|
|
||||||
jmptarget = dest; /* update 'jmptarget' */
|
|
||||||
change = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: /* any instruction that sets A */
|
|
||||||
change = (testAMode(op) && reg == a);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (change)
|
|
||||||
setreg = filterpc(pc, jmptarget);
|
|
||||||
}
|
|
||||||
return setreg;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Find a "name" for the constant 'c'.
|
|
||||||
*/
|
|
||||||
static const char *kname (const Proto *p, int index, const char **name) {
|
|
||||||
TValue *kvalue = &p->k[index];
|
|
||||||
if (ttisstring(kvalue)) {
|
|
||||||
*name = getstr(tsvalue(kvalue));
|
|
||||||
return "constant";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*name = "?";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const char *basicgetobjname (const Proto *p, int *ppc, int reg,
|
|
||||||
const char **name) {
|
|
||||||
int pc = *ppc;
|
|
||||||
*name = luaF_getlocalname(p, reg + 1, pc);
|
|
||||||
if (*name) /* is a local? */
|
|
||||||
return "local";
|
|
||||||
/* else try symbolic execution */
|
|
||||||
*ppc = pc = findsetreg(p, pc, reg);
|
|
||||||
if (pc != -1) { /* could find instruction? */
|
|
||||||
Instruction i = p->code[pc];
|
|
||||||
OpCode op = GET_OPCODE(i);
|
|
||||||
switch (op) {
|
|
||||||
case OP_MOVE: {
|
|
||||||
int b = GETARG_B(i); /* move from 'b' to 'a' */
|
|
||||||
if (b < GETARG_A(i))
|
|
||||||
return basicgetobjname(p, ppc, b, name); /* get name for 'b' */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OP_GETUPVAL: {
|
|
||||||
*name = upvalname(p, GETARG_B(i));
|
|
||||||
return "upvalue";
|
|
||||||
}
|
|
||||||
case OP_LOADK: return kname(p, GETARG_Bx(i), name);
|
|
||||||
case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name);
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL; /* could not find reasonable name */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Find a "name" for the register 'c'.
|
|
||||||
*/
|
|
||||||
static void rname (const Proto *p, int pc, int c, const char **name) {
|
|
||||||
const char *what = basicgetobjname(p, &pc, c, name); /* search for 'c' */
|
|
||||||
if (!(what && *what == 'c')) /* did not find a constant name? */
|
|
||||||
*name = "?";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Find a "name" for a 'C' value in an RK instruction.
|
|
||||||
*/
|
|
||||||
static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
|
|
||||||
int c = GETARG_C(i); /* key index */
|
|
||||||
if (GETARG_k(i)) /* is 'c' a constant? */
|
|
||||||
kname(p, c, name);
|
|
||||||
else /* 'c' is a register */
|
|
||||||
rname(p, pc, c, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Check whether table being indexed by instruction 'i' is the
|
|
||||||
** environment '_ENV'
|
|
||||||
*/
|
|
||||||
static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) {
|
|
||||||
int t = GETARG_B(i); /* table index */
|
|
||||||
const char *name; /* name of indexed variable */
|
|
||||||
if (isup) /* is 't' an upvalue? */
|
|
||||||
name = upvalname(p, t);
|
|
||||||
else /* 't' is a register */
|
|
||||||
basicgetobjname(p, &pc, t, &name);
|
|
||||||
return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Extend 'basicgetobjname' to handle table accesses
|
|
||||||
*/
|
|
||||||
static const char *getobjname (const Proto *p, int lastpc, int reg,
|
|
||||||
const char **name) {
|
|
||||||
const char *kind = basicgetobjname(p, &lastpc, reg, name);
|
|
||||||
if (kind != NULL)
|
|
||||||
return kind;
|
|
||||||
else if (lastpc != -1) { /* could find instruction? */
|
|
||||||
Instruction i = p->code[lastpc];
|
|
||||||
OpCode op = GET_OPCODE(i);
|
|
||||||
switch (op) {
|
|
||||||
case OP_GETTABUP: {
|
|
||||||
int k = GETARG_C(i); /* key index */
|
|
||||||
kname(p, k, name);
|
|
||||||
return isEnv(p, lastpc, i, 1);
|
|
||||||
}
|
|
||||||
case OP_GETTABLE: {
|
|
||||||
int k = GETARG_C(i); /* key index */
|
|
||||||
rname(p, lastpc, k, name);
|
|
||||||
return isEnv(p, lastpc, i, 0);
|
|
||||||
}
|
|
||||||
case OP_GETI: {
|
|
||||||
*name = "integer index";
|
|
||||||
return "field";
|
|
||||||
}
|
|
||||||
case OP_GETFIELD: {
|
|
||||||
int k = GETARG_C(i); /* key index */
|
|
||||||
kname(p, k, name);
|
|
||||||
return isEnv(p, lastpc, i, 0);
|
|
||||||
}
|
|
||||||
case OP_SELF: {
|
|
||||||
rkname(p, lastpc, i, name);
|
|
||||||
return "method";
|
|
||||||
}
|
|
||||||
default: break; /* go through to return NULL */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL; /* could not find reasonable name */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Try to find a name for a function based on the code that called it.
|
|
||||||
** (Only works when function was called by a Lua function.)
|
|
||||||
** Returns what the name is (e.g., "for iterator", "method",
|
|
||||||
** "metamethod") and sets '*name' to point to the name.
|
|
||||||
*/
|
|
||||||
static const char *funcnamefromcode (lua_State *L, const Proto *p,
|
|
||||||
int pc, const char **name) {
|
|
||||||
TMS tm = (TMS)0; /* (initial value avoids warnings) */
|
|
||||||
Instruction i = p->code[pc]; /* calling instruction */
|
|
||||||
switch (GET_OPCODE(i)) {
|
|
||||||
case OP_CALL:
|
|
||||||
case OP_TAILCALL:
|
|
||||||
return getobjname(p, pc, GETARG_A(i), name); /* get function name */
|
|
||||||
case OP_TFORCALL: { /* for iterator */
|
|
||||||
*name = "for iterator";
|
|
||||||
return "for iterator";
|
|
||||||
}
|
|
||||||
/* other instructions can do calls through metamethods */
|
|
||||||
case OP_SELF: case OP_GETTABUP: case OP_GETTABLE:
|
|
||||||
case OP_GETI: case OP_GETFIELD:
|
|
||||||
tm = TM_INDEX;
|
|
||||||
break;
|
|
||||||
case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD:
|
|
||||||
tm = TM_NEWINDEX;
|
|
||||||
break;
|
|
||||||
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
|
|
||||||
tm = cast(TMS, GETARG_C(i));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OP_UNM: tm = TM_UNM; break;
|
|
||||||
case OP_BNOT: tm = TM_BNOT; break;
|
|
||||||
case OP_LEN: tm = TM_LEN; break;
|
|
||||||
case OP_CONCAT: tm = TM_CONCAT; break;
|
|
||||||
case OP_EQ: tm = TM_EQ; break;
|
|
||||||
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
|
|
||||||
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
|
|
||||||
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
|
|
||||||
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
|
|
||||||
default:
|
|
||||||
return NULL; /* cannot find a reasonable name */
|
|
||||||
}
|
|
||||||
*name = getshrstr(G(L)->tmname[tm]) + 2;
|
|
||||||
return "metamethod";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Try to find a name for a function based on how it was called.
|
|
||||||
*/
|
|
||||||
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
|
||||||
const char **name) {
|
|
||||||
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
|
||||||
*name = "?";
|
|
||||||
return "hook";
|
|
||||||
}
|
|
||||||
else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */
|
|
||||||
*name = "__gc";
|
|
||||||
return "metamethod"; /* report it as such */
|
|
||||||
}
|
|
||||||
else if (isLua(ci))
|
|
||||||
return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name);
|
|
||||||
else
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }====================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Check whether pointer 'o' points to some value in the stack frame of
|
|
||||||
** the current function and, if so, returns its index. Because 'o' may
|
|
||||||
** not point to a value in this stack, we cannot compare it with the
|
|
||||||
** region boundaries (undefined behavior in ISO C).
|
|
||||||
*/
|
|
||||||
static int instack (CallInfo *ci, const TValue *o) {
|
|
||||||
int pos;
|
|
||||||
StkId base = ci->func.p + 1;
|
|
||||||
for (pos = 0; base + pos < ci->top.p; pos++) {
|
|
||||||
if (o == s2v(base + pos))
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
return -1; /* not found */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Checks whether value 'o' came from an upvalue. (That can only happen
|
|
||||||
** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on
|
|
||||||
** upvalues.)
|
|
||||||
*/
|
|
||||||
static const char *getupvalname (CallInfo *ci, const TValue *o,
|
|
||||||
const char **name) {
|
|
||||||
LClosure *c = ci_func(ci);
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < c->nupvalues; i++) {
|
|
||||||
if (c->upvals[i]->v.p == o) {
|
|
||||||
*name = upvalname(c->p, i);
|
|
||||||
return "upvalue";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const char *formatvarinfo (lua_State *L, const char *kind,
|
|
||||||
const char *name) {
|
|
||||||
if (kind == NULL)
|
|
||||||
return ""; /* no information */
|
|
||||||
else
|
|
||||||
return luaO_pushfstring(L, " (%s '%s')", kind, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Build a string with a "description" for the value 'o', such as
|
|
||||||
** "variable 'x'" or "upvalue 'y'".
|
|
||||||
*/
|
|
||||||
static const char *varinfo (lua_State *L, const TValue *o) {
|
|
||||||
CallInfo *ci = L->ci;
|
|
||||||
const char *name = NULL; /* to avoid warnings */
|
|
||||||
const char *kind = NULL;
|
|
||||||
if (isLua(ci)) {
|
|
||||||
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
|
|
||||||
if (!kind) { /* not an upvalue? */
|
|
||||||
int reg = instack(ci, o); /* try a register */
|
|
||||||
if (reg >= 0) /* is 'o' a register? */
|
|
||||||
kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return formatvarinfo(L, kind, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Raise a type error
|
|
||||||
*/
|
|
||||||
static l_noret typeerror (lua_State *L, const TValue *o, const char *op,
|
|
||||||
const char *extra) {
|
|
||||||
const char *t = luaT_objtypename(L, o);
|
|
||||||
luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Raise a type error with "standard" information about the faulty
|
|
||||||
** object 'o' (using 'varinfo').
|
|
||||||
*/
|
|
||||||
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
|
||||||
typeerror(L, o, op, varinfo(L, o));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Raise an error for calling a non-callable object. Try to find a name
|
|
||||||
** for the object based on how it was called ('funcnamefromcall'); if it
|
|
||||||
** cannot get a name there, try 'varinfo'.
|
|
||||||
*/
|
|
||||||
l_noret luaG_callerror (lua_State *L, const TValue *o) {
|
|
||||||
CallInfo *ci = L->ci;
|
|
||||||
const char *name = NULL; /* to avoid warnings */
|
|
||||||
const char *kind = funcnamefromcall(L, ci, &name);
|
|
||||||
const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o);
|
|
||||||
typeerror(L, o, "call", extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
|
|
||||||
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
|
|
||||||
what, luaT_objtypename(L, o));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) {
|
|
||||||
if (ttisstring(p1) || cvt2str(p1)) p1 = p2;
|
|
||||||
luaG_typeerror(L, p1, "concatenate");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
|
||||||
const TValue *p2, const char *msg) {
|
|
||||||
if (!ttisnumber(p1)) /* first operand is wrong? */
|
|
||||||
p2 = p1; /* now second is wrong */
|
|
||||||
luaG_typeerror(L, p2, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Error when both values are convertible to numbers, but not to integers
|
|
||||||
*/
|
|
||||||
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
|
|
||||||
lua_Integer temp;
|
|
||||||
if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
|
|
||||||
p2 = p1;
|
|
||||||
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
|
|
||||||
const char *t1 = luaT_objtypename(L, p1);
|
|
||||||
const char *t2 = luaT_objtypename(L, p2);
|
|
||||||
if (strcmp(t1, t2) == 0)
|
|
||||||
luaG_runerror(L, "attempt to compare two %s values", t1);
|
|
||||||
else
|
|
||||||
luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* add src:line information to 'msg' */
|
|
||||||
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
|
|
||||||
int line) {
|
|
||||||
char buff[LUA_IDSIZE];
|
|
||||||
if (src)
|
|
||||||
luaO_chunkid(buff, getstr(src), tsslen(src));
|
|
||||||
else { /* no source available; use "?" instead */
|
|
||||||
buff[0] = '?'; buff[1] = '\0';
|
|
||||||
}
|
|
||||||
return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
l_noret luaG_errormsg (lua_State *L) {
|
|
||||||
if (L->errfunc != 0) { /* is there an error handling function? */
|
|
||||||
StkId errfunc = restorestack(L, L->errfunc);
|
|
||||||
lua_assert(ttisfunction(s2v(errfunc)));
|
|
||||||
setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */
|
|
||||||
setobjs2s(L, L->top.p - 1, errfunc); /* push function */
|
|
||||||
L->top.p++; /* assume EXTRA_STACK */
|
|
||||||
luaD_callnoyield(L, L->top.p - 2, 1); /* call it */
|
|
||||||
}
|
|
||||||
luaD_throw(L, LUA_ERRRUN);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
|
||||||
CallInfo *ci = L->ci;
|
|
||||||
const char *msg;
|
|
||||||
va_list argp;
|
|
||||||
luaC_checkGC(L); /* error message uses memory */
|
|
||||||
va_start(argp, fmt);
|
|
||||||
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
|
|
||||||
va_end(argp);
|
|
||||||
if (isLua(ci)) { /* if Lua function, add source:line information */
|
|
||||||
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
|
|
||||||
setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */
|
|
||||||
L->top.p--;
|
|
||||||
}
|
|
||||||
luaG_errormsg(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Check whether new instruction 'newpc' is in a different line from
|
|
||||||
** previous instruction 'oldpc'. More often than not, 'newpc' is only
|
|
||||||
** one or a few instructions after 'oldpc' (it must be after, see
|
|
||||||
** caller), so try to avoid calling 'luaG_getfuncline'. If they are
|
|
||||||
** too far apart, there is a good chance of a ABSLINEINFO in the way,
|
|
||||||
** so it goes directly to 'luaG_getfuncline'.
|
|
||||||
*/
|
|
||||||
static int changedline (const Proto *p, int oldpc, int newpc) {
|
|
||||||
if (p->lineinfo == NULL) /* no debug information? */
|
|
||||||
return 0;
|
|
||||||
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
|
|
||||||
int delta = 0; /* line difference */
|
|
||||||
int pc = oldpc;
|
|
||||||
for (;;) {
|
|
||||||
int lineinfo = p->lineinfo[++pc];
|
|
||||||
if (lineinfo == ABSLINEINFO)
|
|
||||||
break; /* cannot compute delta; fall through */
|
|
||||||
delta += lineinfo;
|
|
||||||
if (pc == newpc)
|
|
||||||
return (delta != 0); /* delta computed successfully */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* either instructions are too far apart or there is an absolute line
|
|
||||||
info in the way; compute line difference explicitly */
|
|
||||||
return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Traces Lua calls. If code is running the first instruction of a function,
|
|
||||||
** and function is not vararg, and it is not coming from an yield,
|
|
||||||
** calls 'luaD_hookcall'. (Vararg functions will call 'luaD_hookcall'
|
|
||||||
** after adjusting its variable arguments; otherwise, they could call
|
|
||||||
** a line/count hook before the call hook. Functions coming from
|
|
||||||
** an yield already called 'luaD_hookcall' before yielding.)
|
|
||||||
*/
|
|
||||||
int luaG_tracecall (lua_State *L) {
|
|
||||||
CallInfo *ci = L->ci;
|
|
||||||
Proto *p = ci_func(ci)->p;
|
|
||||||
ci->u.l.trap = 1; /* ensure hooks will be checked */
|
|
||||||
if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */
|
|
||||||
if (p->is_vararg)
|
|
||||||
return 0; /* hooks will start at VARARGPREP instruction */
|
|
||||||
else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yieded? */
|
|
||||||
luaD_hookcall(L, ci); /* check 'call' hook */
|
|
||||||
}
|
|
||||||
return 1; /* keep 'trap' on */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Traces the execution of a Lua function. Called before the execution
|
|
||||||
** of each opcode, when debug is on. 'L->oldpc' stores the last
|
|
||||||
** instruction traced, to detect line changes. When entering a new
|
|
||||||
** function, 'npci' will be zero and will test as a new line whatever
|
|
||||||
** the value of 'oldpc'. Some exceptional conditions may return to
|
|
||||||
** a function without setting 'oldpc'. In that case, 'oldpc' may be
|
|
||||||
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
|
|
||||||
** at most causes an extra call to a line hook.)
|
|
||||||
** This function is not "Protected" when called, so it should correct
|
|
||||||
** 'L->top.p' before calling anything that can run the GC.
|
|
||||||
*/
|
|
||||||
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
|
||||||
CallInfo *ci = L->ci;
|
|
||||||
lu_byte mask = L->hookmask;
|
|
||||||
const Proto *p = ci_func(ci)->p;
|
|
||||||
int counthook;
|
|
||||||
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
|
|
||||||
ci->u.l.trap = 0; /* don't need to stop again */
|
|
||||||
return 0; /* turn off 'trap' */
|
|
||||||
}
|
|
||||||
pc++; /* reference is always next instruction */
|
|
||||||
ci->u.l.savedpc = pc; /* save 'pc' */
|
|
||||||
counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0);
|
|
||||||
if (counthook)
|
|
||||||
resethookcount(L); /* reset count */
|
|
||||||
else if (!(mask & LUA_MASKLINE))
|
|
||||||
return 1; /* no line hook and count != 0; nothing to be done now */
|
|
||||||
if (ci->callstatus & CIST_HOOKYIELD) { /* hook yielded last time? */
|
|
||||||
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
|
|
||||||
return 1; /* do not call hook again (VM yielded, so it did not move) */
|
|
||||||
}
|
|
||||||
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
|
|
||||||
L->top.p = ci->top.p; /* correct top */
|
|
||||||
if (counthook)
|
|
||||||
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
|
||||||
if (mask & LUA_MASKLINE) {
|
|
||||||
/* 'L->oldpc' may be invalid; use zero in this case */
|
|
||||||
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
|
|
||||||
int npci = pcRel(pc, p);
|
|
||||||
if (npci <= oldpc || /* call hook when jump back (loop), */
|
|
||||||
changedline(p, oldpc, npci)) { /* or when enter new line */
|
|
||||||
int newline = luaG_getfuncline(p, npci);
|
|
||||||
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
|
||||||
}
|
|
||||||
L->oldpc = npci; /* 'pc' of last call to line hook */
|
|
||||||
}
|
|
||||||
if (L->status == LUA_YIELD) { /* did hook yield? */
|
|
||||||
if (counthook)
|
|
||||||
L->hookcount = 1; /* undo decrement to zero */
|
|
||||||
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
|
|
||||||
luaD_throw(L, LUA_YIELD);
|
|
||||||
}
|
|
||||||
return 1; /* keep 'trap' on */
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: ldebug.h $
|
|
||||||
** Auxiliary functions from Debug Interface module
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ldebug_h
|
|
||||||
#define ldebug_h
|
|
||||||
|
|
||||||
|
|
||||||
#include "lstate.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
|
|
||||||
|
|
||||||
|
|
||||||
/* Active Lua function (given call info) */
|
|
||||||
#define ci_func(ci) (clLvalue(s2v((ci)->func.p)))
|
|
||||||
|
|
||||||
|
|
||||||
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
|
||||||
|
|
||||||
/*
|
|
||||||
** mark for entries in 'lineinfo' array that has absolute information in
|
|
||||||
** 'abslineinfo' array
|
|
||||||
*/
|
|
||||||
#define ABSLINEINFO (-0x80)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** MAXimum number of successive Instructions WiTHout ABSolute line
|
|
||||||
** information. (A power of two allows fast divisions.)
|
|
||||||
*/
|
|
||||||
#if !defined(MAXIWTHABS)
|
|
||||||
#define MAXIWTHABS 128
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
|
|
||||||
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
|
||||||
StkId *pos);
|
|
||||||
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
|
||||||
const char *opname);
|
|
||||||
LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
|
|
||||||
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
|
||||||
const char *what);
|
|
||||||
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
|
||||||
const TValue *p2);
|
|
||||||
LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
|
||||||
const TValue *p2,
|
|
||||||
const char *msg);
|
|
||||||
LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
|
|
||||||
const TValue *p2);
|
|
||||||
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
|
|
||||||
const TValue *p2);
|
|
||||||
LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
|
|
||||||
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
|
|
||||||
TString *src, int line);
|
|
||||||
LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
|
|
||||||
LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
|
|
||||||
LUAI_FUNC int luaG_tracecall (lua_State *L);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: ldo.h $
|
|
||||||
** Stack and Call structure of Lua
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ldo_h
|
|
||||||
#define ldo_h
|
|
||||||
|
|
||||||
|
|
||||||
#include "llimits.h"
|
|
||||||
#include "lobject.h"
|
|
||||||
#include "lstate.h"
|
|
||||||
#include "lzio.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Macro to check stack size and grow stack if needed. Parameters
|
|
||||||
** 'pre'/'pos' allow the macro to preserve a pointer into the
|
|
||||||
** stack across reallocations, doing the work only when needed.
|
|
||||||
** It also allows the running of one GC step when the stack is
|
|
||||||
** reallocated.
|
|
||||||
** 'condmovestack' is used in heavy tests to force a stack reallocation
|
|
||||||
** at every check.
|
|
||||||
*/
|
|
||||||
#define luaD_checkstackaux(L,n,pre,pos) \
|
|
||||||
if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \
|
|
||||||
{ pre; luaD_growstack(L, n, 1); pos; } \
|
|
||||||
else { condmovestack(L,pre,pos); }
|
|
||||||
|
|
||||||
/* In general, 'pre'/'pos' are empty (nothing to save) */
|
|
||||||
#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p))
|
|
||||||
#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n))
|
|
||||||
|
|
||||||
|
|
||||||
/* macro to check stack size, preserving 'p' */
|
|
||||||
#define checkstackp(L,n,p) \
|
|
||||||
luaD_checkstackaux(L, n, \
|
|
||||||
ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \
|
|
||||||
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
|
|
||||||
|
|
||||||
|
|
||||||
/* macro to check stack size and GC, preserving 'p' */
|
|
||||||
#define checkstackGCp(L,n,p) \
|
|
||||||
luaD_checkstackaux(L, n, \
|
|
||||||
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
|
|
||||||
luaC_checkGC(L), /* stack grow uses memory */ \
|
|
||||||
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
|
|
||||||
|
|
||||||
|
|
||||||
/* macro to check stack size and GC */
|
|
||||||
#define checkstackGC(L,fsize) \
|
|
||||||
luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
|
|
||||||
|
|
||||||
|
|
||||||
/* type of protected functions, to be ran by 'runprotected' */
|
|
||||||
typedef void (*Pfunc) (lua_State *L, void *ud);
|
|
||||||
|
|
||||||
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
|
|
||||||
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
|
||||||
const char *mode);
|
|
||||||
LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
|
|
||||||
int fTransfer, int nTransfer);
|
|
||||||
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
|
|
||||||
LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
|
|
||||||
int narg1, int delta);
|
|
||||||
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
|
|
||||||
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
|
||||||
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
|
||||||
LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
|
|
||||||
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
|
||||||
ptrdiff_t oldtop, ptrdiff_t ef);
|
|
||||||
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
|
|
||||||
LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror);
|
|
||||||
LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror);
|
|
||||||
LUAI_FUNC void luaD_shrinkstack (lua_State *L);
|
|
||||||
LUAI_FUNC void luaD_inctop (lua_State *L);
|
|
||||||
|
|
||||||
LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode);
|
|
||||||
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,230 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: ldump.c $
|
|
||||||
** save precompiled Lua chunks
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define ldump_c
|
|
||||||
#define LUA_CORE
|
|
||||||
|
|
||||||
#include "lprefix.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
#include "lobject.h"
|
|
||||||
#include "lstate.h"
|
|
||||||
#include "lundump.h"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
lua_State *L;
|
|
||||||
lua_Writer writer;
|
|
||||||
void *data;
|
|
||||||
int strip;
|
|
||||||
int status;
|
|
||||||
} DumpState;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** All high-level dumps go through dumpVector; you can change it to
|
|
||||||
** change the endianness of the result
|
|
||||||
*/
|
|
||||||
#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0]))
|
|
||||||
|
|
||||||
#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char))
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpBlock (DumpState *D, const void *b, size_t size) {
|
|
||||||
if (D->status == 0 && size > 0) {
|
|
||||||
lua_unlock(D->L);
|
|
||||||
D->status = (*D->writer)(D->L, b, size, D->data);
|
|
||||||
lua_lock(D->L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define dumpVar(D,x) dumpVector(D,&x,1)
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpByte (DumpState *D, int y) {
|
|
||||||
lu_byte x = (lu_byte)y;
|
|
||||||
dumpVar(D, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6"
|
|
||||||
** rounds up the division.)
|
|
||||||
*/
|
|
||||||
#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7)
|
|
||||||
|
|
||||||
static void dumpSize (DumpState *D, size_t x) {
|
|
||||||
lu_byte buff[DIBS];
|
|
||||||
int n = 0;
|
|
||||||
do {
|
|
||||||
buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */
|
|
||||||
x >>= 7;
|
|
||||||
} while (x != 0);
|
|
||||||
buff[DIBS - 1] |= 0x80; /* mark last byte */
|
|
||||||
dumpVector(D, buff + DIBS - n, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpInt (DumpState *D, int x) {
|
|
||||||
dumpSize(D, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpNumber (DumpState *D, lua_Number x) {
|
|
||||||
dumpVar(D, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpInteger (DumpState *D, lua_Integer x) {
|
|
||||||
dumpVar(D, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpString (DumpState *D, const TString *s) {
|
|
||||||
if (s == NULL)
|
|
||||||
dumpSize(D, 0);
|
|
||||||
else {
|
|
||||||
size_t size = tsslen(s);
|
|
||||||
const char *str = getstr(s);
|
|
||||||
dumpSize(D, size + 1);
|
|
||||||
dumpVector(D, str, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpCode (DumpState *D, const Proto *f) {
|
|
||||||
dumpInt(D, f->sizecode);
|
|
||||||
dumpVector(D, f->code, f->sizecode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpFunction(DumpState *D, const Proto *f, TString *psource);
|
|
||||||
|
|
||||||
static void dumpConstants (DumpState *D, const Proto *f) {
|
|
||||||
int i;
|
|
||||||
int n = f->sizek;
|
|
||||||
dumpInt(D, n);
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
const TValue *o = &f->k[i];
|
|
||||||
int tt = ttypetag(o);
|
|
||||||
dumpByte(D, tt);
|
|
||||||
switch (tt) {
|
|
||||||
case LUA_VNUMFLT:
|
|
||||||
dumpNumber(D, fltvalue(o));
|
|
||||||
break;
|
|
||||||
case LUA_VNUMINT:
|
|
||||||
dumpInteger(D, ivalue(o));
|
|
||||||
break;
|
|
||||||
case LUA_VSHRSTR:
|
|
||||||
case LUA_VLNGSTR:
|
|
||||||
dumpString(D, tsvalue(o));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpProtos (DumpState *D, const Proto *f) {
|
|
||||||
int i;
|
|
||||||
int n = f->sizep;
|
|
||||||
dumpInt(D, n);
|
|
||||||
for (i = 0; i < n; i++)
|
|
||||||
dumpFunction(D, f->p[i], f->source);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpUpvalues (DumpState *D, const Proto *f) {
|
|
||||||
int i, n = f->sizeupvalues;
|
|
||||||
dumpInt(D, n);
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
dumpByte(D, f->upvalues[i].instack);
|
|
||||||
dumpByte(D, f->upvalues[i].idx);
|
|
||||||
dumpByte(D, f->upvalues[i].kind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpDebug (DumpState *D, const Proto *f) {
|
|
||||||
int i, n;
|
|
||||||
n = (D->strip) ? 0 : f->sizelineinfo;
|
|
||||||
dumpInt(D, n);
|
|
||||||
dumpVector(D, f->lineinfo, n);
|
|
||||||
n = (D->strip) ? 0 : f->sizeabslineinfo;
|
|
||||||
dumpInt(D, n);
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
dumpInt(D, f->abslineinfo[i].pc);
|
|
||||||
dumpInt(D, f->abslineinfo[i].line);
|
|
||||||
}
|
|
||||||
n = (D->strip) ? 0 : f->sizelocvars;
|
|
||||||
dumpInt(D, n);
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
dumpString(D, f->locvars[i].varname);
|
|
||||||
dumpInt(D, f->locvars[i].startpc);
|
|
||||||
dumpInt(D, f->locvars[i].endpc);
|
|
||||||
}
|
|
||||||
n = (D->strip) ? 0 : f->sizeupvalues;
|
|
||||||
dumpInt(D, n);
|
|
||||||
for (i = 0; i < n; i++)
|
|
||||||
dumpString(D, f->upvalues[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpFunction (DumpState *D, const Proto *f, TString *psource) {
|
|
||||||
if (D->strip || f->source == psource)
|
|
||||||
dumpString(D, NULL); /* no debug info or same source as its parent */
|
|
||||||
else
|
|
||||||
dumpString(D, f->source);
|
|
||||||
dumpInt(D, f->linedefined);
|
|
||||||
dumpInt(D, f->lastlinedefined);
|
|
||||||
dumpByte(D, f->numparams);
|
|
||||||
dumpByte(D, f->is_vararg);
|
|
||||||
dumpByte(D, f->maxstacksize);
|
|
||||||
dumpCode(D, f);
|
|
||||||
dumpConstants(D, f);
|
|
||||||
dumpUpvalues(D, f);
|
|
||||||
dumpProtos(D, f);
|
|
||||||
dumpDebug(D, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dumpHeader (DumpState *D) {
|
|
||||||
dumpLiteral(D, LUA_SIGNATURE);
|
|
||||||
dumpByte(D, LUAC_VERSION);
|
|
||||||
dumpByte(D, LUAC_FORMAT);
|
|
||||||
dumpLiteral(D, LUAC_DATA);
|
|
||||||
dumpByte(D, sizeof(Instruction));
|
|
||||||
dumpByte(D, sizeof(lua_Integer));
|
|
||||||
dumpByte(D, sizeof(lua_Number));
|
|
||||||
dumpInteger(D, LUAC_INT);
|
|
||||||
dumpNumber(D, LUAC_NUM);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** dump Lua function as precompiled chunk
|
|
||||||
*/
|
|
||||||
int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
|
|
||||||
int strip) {
|
|
||||||
DumpState D;
|
|
||||||
D.L = L;
|
|
||||||
D.writer = w;
|
|
||||||
D.data = data;
|
|
||||||
D.strip = strip;
|
|
||||||
D.status = 0;
|
|
||||||
dumpHeader(&D);
|
|
||||||
dumpByte(&D, f->sizeupvalues);
|
|
||||||
dumpFunction(&D, f, NULL);
|
|
||||||
return D.status;
|
|
||||||
}
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user