From 83ce81f775f2cc95f3d57b515d52f3e5bf5720d2 Mon Sep 17 00:00:00 2001 From: Luis Michaelis Date: Sat, 9 Sep 2023 17:15:53 +0200 Subject: [PATCH] refactor(v2): step 2: api changes --- CMakeLists.txt | 251 ++-- examples/CMakeLists.txt | 6 +- examples/load_vdf.cc | 22 +- examples/load_zen.cc | 2 +- examples/run_interpreter.cc | 6 +- include/phoenix/Vfs.hh | 17 + include/phoenix/animation.hh | 11 + include/phoenix/archive.hh | 11 + include/phoenix/buffer.hh | 194 ++-- include/phoenix/ext/daedalus_classes.hh | 51 + include/phoenix/ext/dds_convert.hh | 16 + include/phoenix/font.hh | 9 + include/phoenix/material.hh | 13 + include/phoenix/math.hh | 9 + include/phoenix/mesh.hh | 15 + include/phoenix/messages.hh | 10 + include/phoenix/model.hh | 8 + include/phoenix/model_hierarchy.hh | 9 + include/phoenix/model_mesh.hh | 8 + include/phoenix/model_script.hh | 44 + include/phoenix/morph_mesh.hh | 10 + include/phoenix/phoenix.hh | 155 +-- include/phoenix/proto_mesh.hh | 16 + include/phoenix/save_game.hh | 15 + include/phoenix/script.hh | 36 + include/phoenix/softskin_mesh.hh | 12 + include/phoenix/texture.hh | 26 + include/phoenix/vdfs.hh | 239 ---- include/phoenix/vm.hh | 31 + include/phoenix/vobs/camera.hh | 17 + include/phoenix/vobs/light.hh | 14 + include/phoenix/vobs/misc.hh | 24 + include/phoenix/vobs/mob.hh | 16 + include/phoenix/vobs/sound.hh | 14 + include/phoenix/vobs/trigger.hh | 24 + include/phoenix/vobs/vob.hh | 14 + include/phoenix/vobs/zone.hh | 10 + include/phoenix/world.hh | 12 + include/phoenix/world/bsp_tree.hh | 11 + include/phoenix/world/vob_tree.hh | 13 + include/phoenix/world/way_net.hh | 10 + include/zenkit/Archive.hh | 158 ++- include/zenkit/Boxes.hh | 26 +- include/zenkit/CutsceneLibrary.hh | 34 +- include/zenkit/DaedalusScript.hh | 653 ++++++----- include/zenkit/DaedalusVm.hh | 552 ++++----- include/zenkit/Date.hh | 28 + include/zenkit/Error.hh | 37 + include/zenkit/Font.hh | 39 +- include/zenkit/Library.hh | 30 +- include/zenkit/Logger.hh | 50 + include/zenkit/Material.hh | 136 ++- include/zenkit/Mesh.hh | 72 +- include/zenkit/Misc.hh | 36 + include/zenkit/Model.hh | 31 +- include/zenkit/ModelAnimation.hh | 157 ++- include/zenkit/ModelHierarchy.hh | 41 +- include/zenkit/ModelMesh.hh | 33 +- include/zenkit/ModelScript.hh | 509 +++++---- include/zenkit/MorphMesh.hh | 45 +- include/zenkit/MultiResolutionMesh.hh | 110 +- include/zenkit/SaveGame.hh | 96 +- include/zenkit/SoftSkinMesh.hh | 45 +- include/zenkit/Stream.hh | 167 +++ include/zenkit/Texture.hh | 123 +- include/zenkit/Vfs.hh | 120 +- include/zenkit/World.hh | 47 +- include/zenkit/addon/daedalus.hh | 1383 +++++++---------------- include/zenkit/addon/texcvt.hh | 14 +- include/zenkit/vobs/Camera.hh | 123 +- include/zenkit/vobs/Light.hh | 72 +- include/zenkit/vobs/Misc.hh | 182 ++- include/zenkit/vobs/MovableObject.hh | 81 +- include/zenkit/vobs/Sound.hh | 55 +- include/zenkit/vobs/Trigger.hh | 157 ++- include/zenkit/vobs/VirtualObject.hh | 165 ++- include/zenkit/vobs/Zone.hh | 130 ++- include/zenkit/world/BspTree.hh | 63 +- include/zenkit/world/VobTree.hh | 14 +- include/zenkit/world/WayNet.hh | 38 +- source/buffer.cc | 11 +- source/phoenix.cc | 76 -- source/vdfs.cc | 303 ----- src/Archive.cc | 236 ++-- src/Boxes.cc | 61 +- src/CutsceneLibrary.cc | 96 +- src/DaedalusScript.cc | 532 +++++---- src/DaedalusVm.cc | 673 +++++------ src/Date.cc | 27 + src/Error.cc | 23 + src/Font.cc | 82 +- src/Internal.hh | 10 + src/Logger.cc | 94 ++ src/Material.cc | 161 ++- src/Mesh.cc | 461 ++++---- src/Misc.cc | 20 + src/Model.cc | 27 +- src/ModelAnimation.cc | 171 ++- src/ModelHierarchy.cc | 119 +- src/ModelMesh.cc | 135 +-- src/ModelScript.cc | 764 +++++++------ src/ModelScriptDsl.cc | 923 +++++++-------- src/ModelScriptDsl.hh | 105 +- src/MorphMesh.cc | 112 +- src/MultiResolutionMesh.cc | 208 ++-- src/SaveGame.cc | 190 ++-- src/SoftSkinMesh.cc | 114 +- src/Stream.cc | 500 ++++++++ src/Texture.cc | 139 ++- src/Vfs.cc | 375 +++--- src/World.cc | 350 +++--- src/addon/daedalus.cc | 890 ++++++++++++++- src/addon/texcvt.cc | 139 ++- src/archive/ArchiveAscii.cc | 200 ++-- src/archive/ArchiveAscii.hh | 26 +- src/archive/ArchiveBinary.cc | 127 ++- src/archive/ArchiveBinary.hh | 27 +- src/archive/ArchiveBinsafe.cc | 332 +++--- src/archive/ArchiveBinsafe.hh | 53 +- src/vobs/Camera.cc | 116 +- src/vobs/Light.cc | 89 +- src/vobs/Misc.cc | 444 +++++--- src/vobs/MovableObject.cc | 125 +- src/vobs/Sound.cc | 59 +- src/vobs/Trigger.cc | 205 ++-- src/vobs/VirtualObject.cc | 275 +++-- src/vobs/Zone.cc | 73 +- src/world/BspTree.cc | 149 ++- src/world/VobTree.cc | 404 ++++--- src/world/WayNet.cc | 143 ++- support/BuildSupport.cmake | 86 ++ tests/TestArchive.cc | 41 +- tests/TestCutsceneLibrary.cc | 16 +- tests/TestDaedalusScript.cc | 39 +- tests/TestFont.cc | 35 +- tests/TestMaterial.cc | 42 +- tests/TestModel.cc | 2 +- tests/TestModelAnimation.cc | 61 +- tests/TestModelHierarchy.cc | 19 +- tests/TestModelMesh.cc | 25 +- tests/TestModelScript.cc | 70 +- tests/TestMorphMesh.cc | 18 +- tests/TestMultiResolutionMesh.cc | 24 +- tests/TestSaveGame.cc | 44 +- tests/TestStream.cc | 290 +++++ tests/TestTexture.cc | 22 +- tests/TestVfs.cc | 38 +- tests/TestVobsG1.cc | 552 ++++----- tests/TestVobsG2.cc | 746 ++++++------ tests/TestWorld.cc | 64 +- tests/test_buffer.cc | 571 ---------- 151 files changed, 11411 insertions(+), 9651 deletions(-) create mode 100644 include/phoenix/Vfs.hh create mode 100644 include/phoenix/animation.hh create mode 100644 include/phoenix/archive.hh create mode 100644 include/phoenix/ext/daedalus_classes.hh create mode 100644 include/phoenix/ext/dds_convert.hh create mode 100644 include/phoenix/font.hh create mode 100644 include/phoenix/material.hh create mode 100644 include/phoenix/math.hh create mode 100644 include/phoenix/mesh.hh create mode 100644 include/phoenix/messages.hh create mode 100644 include/phoenix/model.hh create mode 100644 include/phoenix/model_hierarchy.hh create mode 100644 include/phoenix/model_mesh.hh create mode 100644 include/phoenix/model_script.hh create mode 100644 include/phoenix/morph_mesh.hh create mode 100644 include/phoenix/proto_mesh.hh create mode 100644 include/phoenix/save_game.hh create mode 100644 include/phoenix/script.hh create mode 100644 include/phoenix/softskin_mesh.hh create mode 100644 include/phoenix/texture.hh delete mode 100644 include/phoenix/vdfs.hh create mode 100644 include/phoenix/vm.hh create mode 100644 include/phoenix/vobs/camera.hh create mode 100644 include/phoenix/vobs/light.hh create mode 100644 include/phoenix/vobs/misc.hh create mode 100644 include/phoenix/vobs/mob.hh create mode 100644 include/phoenix/vobs/sound.hh create mode 100644 include/phoenix/vobs/trigger.hh create mode 100644 include/phoenix/vobs/vob.hh create mode 100644 include/phoenix/vobs/zone.hh create mode 100644 include/phoenix/world.hh create mode 100644 include/phoenix/world/bsp_tree.hh create mode 100644 include/phoenix/world/vob_tree.hh create mode 100644 include/phoenix/world/way_net.hh create mode 100644 include/zenkit/Date.hh create mode 100644 include/zenkit/Error.hh create mode 100644 include/zenkit/Logger.hh create mode 100644 include/zenkit/Misc.hh create mode 100644 include/zenkit/Stream.hh delete mode 100644 source/phoenix.cc delete mode 100644 source/vdfs.cc create mode 100644 src/Date.cc create mode 100644 src/Error.cc create mode 100644 src/Internal.hh create mode 100644 src/Logger.cc create mode 100644 src/Misc.cc create mode 100644 src/Stream.cc create mode 100644 support/BuildSupport.cmake create mode 100644 tests/TestStream.cc delete mode 100644 tests/test_buffer.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 22719237..b85bfea5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,153 +1,123 @@ cmake_minimum_required(VERSION 3.10) -include(CheckIncludeFiles) -project(phoenix VERSION 1.1.1) +project(ZenKit VERSION 2.0.0) +include(support/BuildSupport.cmake) set(CMAKE_CXX_STANDARD 17) -set(PHOENIX_LOG_LEVEL 3 CACHE STRING "The logging level to use for phoenix. Set to 4, 3, 2, or 1 for DEBUG, INFO, WARN or ERROR respectively") -option(PHOENIX_BUILD_EXAMPLES "Build example code" OFF) -option(PHOENIX_BUILD_TESTS "Build tests" ON) -option(PHOENIX_BUILD_SHARED "Build phoenix as a shared library" OFF) -option(PHOENIX_DISABLE_SANITIZERS "Build without sanitizers in debug mode" OFF) -option(PHOENIX_INSTALL "Configure phoenix for cmake install" ON) +option(ZK_BUILD_EXAMPLES "ZenKit: Build the examples." OFF) +option(ZK_BUILD_TESTS "ZenKit: Build the test suite." ON) +option(ZK_BUILD_SHARED "ZenKit: Build a shared library." OFF) -set(PHOENIX_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.") - -if (MSVC) - # enable all warnings - set(PHOENIX_CXX_FLAGS "/W4") - - # in debug mode, enable sanitizers - if (${CMAKE_BUILD_TYPE} MATCHES "Debug" AND NOT ${PHOENIX_DISABLE_SANITIZERS}) - set(PHOENIX_CXX_FLAGS ${PHOENIX_CXX_FLAGS} "/fsanitize=address") - endif () -else () - # enable all warnings - set(PHOENIX_CXX_FLAGS -Wall -Wextra -Werror -Wconversion -Wshadow -Wpedantic) - - # in debug mode, enable sanitizers; note: MinGW does not seem to understand sanitizers on Windows - if (${CMAKE_BUILD_TYPE} MATCHES "Debug" AND NOT ${PHOENIX_DISABLE_SANITIZERS} AND NOT WIN32) - set(PHOENIX_CXX_FLAGS ${PHOENIX_CXX_FLAGS} -fsanitize=address -fsanitize=undefined) - - # when not compiling for MacOS, enable leak sanitizer - if (NOT APPLE) - set(PHOENIX_CXX_FLAGS ${PHOENIX_CXX_FLAGS} -fsanitize=leak) - endif () - endif () - - # in debug mode on Clang to get proper debugging support, add -fstandalone-debug - if (${CMAKE_BUILD_TYPE} MATCHES "Debug" AND ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") - set(PHOENIX_CXX_FLAGS ${PHOENIX_CXX_FLAGS} -fstandalone-debug) - endif () -endif () +option(ZK_ENABLE_ASAN "ZenKit: Enable sanitizers in debug builds." ON) +option(ZK_ENABLE_DEPRECATION "ZenKit: Enable deprecation warnings." ON) +option(ZK_ENABLE_INSTALL "ZenKit: Enable CMake install target creation." ON) add_subdirectory(vendor) -# add log level definition -set(PHOENIX_DEFINES PHOENIX_LOG_LEVEL=${PHOENIX_LOG_LEVEL}) - # find all header files; required for them to show up properly in VisualStudio -file(GLOB_RECURSE PHOENIX_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/**/*.hh") - -set(PHOENIX_SOURCES - source/animation.cc - source/archive.cc - source/archive/archive_ascii.cc - source/archive/archive_binary.cc - source/archive/archive_binsafe.cc +file(GLOB_RECURSE _ZK_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/**/*.hh") + +list(APPEND _ZK_SOURCES source/buffer.cc - source/font.cc - source/material.cc - source/math.cc - source/mesh.cc - source/messages.cc - source/model.cc - source/model_hierarchy.cc - source/model_mesh.cc - source/model_script.cc - source/model_script_dsl.cc - source/morph_mesh.cc - source/phoenix.cc - source/proto_mesh.cc - source/save_game.cc - source/script.cc - source/softskin_mesh.cc - source/texture.cc - source/vdfs.cc - source/Vfs.cc - source/vobs/camera.cc - source/vobs/light.cc - source/vobs/misc.cc - source/vobs/mob.cc - source/vobs/sound.cc - source/vobs/trigger.cc - source/vobs/vob.cc - source/vobs/zone.cc - source/vm.cc - source/world.cc - source/world/bsp_tree.cc - source/world/vob_tree.cc - source/world/way_net.cc) - -set(PHOENIX_EXTENSIONS - source/ext/dds_convert.cc - source/ext/daedalus_classes.cc) - -set(PHOENIX_TESTS - tests/test_animation.cc - tests/test_archive.cc - tests/test_buffer.cc - tests/test_font.cc - tests/test_material.cc - tests/test_messages.cc - tests/test_model.cc - tests/test_model_hierarchy.cc - tests/test_model_mesh.cc - tests/test_model_script.cc - tests/test_morph_mesh.cc - tests/test_proto_mesh.cc - tests/test_save_game.cc - tests/test_script.cc - tests/test_texture.cc + + src/world/BspTree.cc + src/world/VobTree.cc + src/world/WayNet.cc + + src/vobs/Camera.cc + src/vobs/Light.cc + src/vobs/Misc.cc + src/vobs/MovableObject.cc + src/vobs/Sound.cc + src/vobs/Trigger.cc + src/vobs/VirtualObject.cc + src/vobs/Zone.cc + + src/addon/daedalus.cc + src/addon/texcvt.cc + + src/archive/ArchiveAscii.cc + src/archive/ArchiveBinary.cc + src/archive/ArchiveBinsafe.cc + + src/Archive.cc + src/Boxes.cc + src/CutsceneLibrary.cc + src/DaedalusScript.cc + src/Date.cc + src/DaedalusVm.cc + src/Error.cc + src/Font.cc + src/Logger.cc + src/Material.cc + src/Mesh.cc + src/Misc.cc + src/Model.cc + src/ModelAnimation.cc + src/ModelHierarchy.cc + src/ModelMesh.cc + src/ModelScript.cc + src/ModelScriptDsl.cc + src/MorphMesh.cc + src/MultiResolutionMesh.cc + src/SaveGame.cc + src/SoftSkinMesh.cc + src/Stream.cc + src/Texture.cc + src/Vfs.cc + src/World.cc +) + +list(APPEND _ZK_TESTS + tests/TestArchive.cc + tests/TestCutsceneLibrary.cc + tests/TestDaedalusScript.cc + tests/TestFont.cc + tests/TestMaterial.cc + tests/TestModel.cc + tests/TestModelAnimation.cc + tests/TestModelHierarchy.cc + tests/TestModelMesh.cc + tests/TestModelScript.cc + tests/TestMorphMesh.cc + tests/TestMultiResolutionMesh.cc + tests/TestSaveGame.cc + tests/TestStream.cc + tests/TestTexture.cc tests/TestVfs.cc - tests/test_vobs_g1.cc - tests/test_vobs_g2.cc - tests/test_world.cc) - -# add the phoenix library definition -if (BUILD_SHARED_LIBS AND PHOENIX_BUILD_SHARED) - add_library(phoenix SHARED) - set_target_properties(phoenix PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1) + tests/TestVobsG1.cc + tests/TestVobsG2.cc + tests/TestWorld.cc +) + +if (BUILD_SHARED_LIBS AND ZK_BUILD_SHARED) + add_library(zenkit SHARED) + set_target_properties(zenkit PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1) + target_compile_definitions(zenkit PRIVATE ZKDYN=1) else () - add_library(phoenix STATIC) - set(PHOENIX_DEFINES ${PHOENIX_DEFINES} PHOENIX_STATIC=1) - set_target_properties(phoenix PROPERTIES COMPILE_FLAGS "-DPHOENIX_STATIC=1") + add_library(zenkit STATIC) endif () -target_sources(phoenix PRIVATE ${PHOENIX_SOURCES} ${PHOENIX_EXTENSIONS} ${PHOENIX_HEADERS}) -target_include_directories(phoenix PUBLIC include) - -# Apps using phoenix will also need to link to glm, fmt and squish to avoid missing symbols -target_link_libraries(phoenix - PUBLIC glm::glm_static squish - PRIVATE mio) - -target_compile_definitions(phoenix PUBLIC ${PHOENIX_DEFINES} PRIVATE PHOENIX_EXPORTS=1) -target_compile_options(phoenix PRIVATE ${PHOENIX_CXX_FLAGS}) - -if (NOT MSVC) - target_link_options(phoenix PUBLIC ${PHOENIX_CXX_FLAGS}) +if (NOT ZK_ENABLE_DEPRECATION) + target_compile_definitions(zenkit PUBLIC ZKNO_REM=1) endif () -set_target_properties(phoenix PROPERTIES - DEBUG_POSTFIX "${PHOENIX_DEBUG_POSTFIX}" - VERSION ${PROJECT_VERSION}) +bs_select_cflags(${ZK_ENABLE_ASAN} _ZK_COMPILE_FLAGS _ZK_LINK_FLAGS) + +target_sources(zenkit PRIVATE ${_ZK_SOURCES} ${_ZK_HEADERS}) +target_include_directories(zenkit PUBLIC include) +target_compile_definitions(zenkit PRIVATE _ZKEXPORT=1) +target_compile_options(zenkit PRIVATE ${_ZK_COMPILE_FLAGS}) +target_link_options(zenkit PUBLIC ${_ZK_LINK_FLAGS}) +target_link_libraries(zenkit PUBLIC glm::glm_static squish mio) +set_target_properties(zenkit PROPERTIES DEBUG_POSTFIX "d" VERSION ${PROJECT_VERSION}) -if (PHOENIX_INSTALL) - install(TARGETS phoenix ARCHIVE LIBRARY RUNTIME) +if (ZK_ENABLE_INSTALL) + install(TARGETS zenkit ARCHIVE LIBRARY RUNTIME) install(DIRECTORY "include/phoenix" TYPE INCLUDE) + install(DIRECTORY "include/zenkit" TYPE INCLUDE) - if (NOT PHOENIX_BUILD_SHARED) + if (NOT ZK_BUILD_SHARED) # For static linking we'll need to provide the dependency static libraries install(DIRECTORY "${glm_SOURCE_DIR}/glm" TYPE INCLUDE FILES_MATCHING PATTERN "*.hpp" PATTERN "*.inl" PATTERN "*.h") @@ -158,24 +128,19 @@ if (PHOENIX_INSTALL) endif () # when building tests, create a test executable and load it into CTest -if (PHOENIX_BUILD_TESTS AND CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) +if (ZK_BUILD_TESTS AND CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) enable_testing() include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake) - add_executable(phoenix-tests ${PHOENIX_TESTS}) - target_link_libraries(phoenix-tests PRIVATE phoenix doctest_with_main) - target_compile_options(phoenix-tests PRIVATE ${PHOENIX_CXX_FLAGS}) - - if (NOT MSVC) - target_compile_options(phoenix-tests PRIVATE -Wno-overloaded-shift-op-parentheses -Wno-deprecated-declarations) - endif () - - target_link_options(phoenix-tests PRIVATE ${PHOENIX_CXX_FLAGS}) + add_executable(test-zenkit ${_ZK_TESTS}) + target_link_libraries(test-zenkit PRIVATE zenkit doctest_with_main) + target_compile_options(test-zenkit PRIVATE ${_ZK_COMPILE_FLAGS}) + target_link_options(test-zenkit PUBLIC ${_ZK_LINK_FLAGS}) - doctest_discover_tests(phoenix-tests EXTRA_ARGS -tse=messages,script,world WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/tests) + doctest_discover_tests(test-zenkit EXTRA_ARGS -tse=CutsceneLibrary,DaedalusScript,World WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/tests) endif () # when building examples, include the subdirectory -if (PHOENIX_BUILD_EXAMPLES) +if (ZK_BUILD_EXAMPLES) add_subdirectory(examples) endif () diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2a4e7f9b..b336eb97 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,11 +1,11 @@ add_executable(load_vdf load_vdf.cc) -target_link_libraries(load_vdf PRIVATE phoenix) +target_link_libraries(load_vdf PRIVATE zenkit) add_executable(load_zen load_zen.cc) -target_link_libraries(load_zen PRIVATE phoenix) +target_link_libraries(load_zen PRIVATE zenkit) add_executable(run_interpreter run_interpreter.cc) -target_link_libraries(run_interpreter PRIVATE phoenix) +target_link_libraries(run_interpreter PRIVATE zenkit) set_target_properties(load_vdf load_zen run_interpreter PROPERTIES diff --git a/examples/load_vdf.cc b/examples/load_vdf.cc index ee567520..fa86d327 100644 --- a/examples/load_vdf.cc +++ b/examples/load_vdf.cc @@ -1,15 +1,15 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include #include -void print_entries(const std::set& entries) { +void print_entries(const std::set& entries) { for (auto& e : entries) { - if (e.is_directory()) { - print_entries(e.children); + if (e.type() == zenkit::VfsNodeType::DIRECTORY) { + print_entries(e.children()); } else { - std::cout << " " << e.name << ": " << e.size << " bytes\n"; + std::cout << e.name() << "\n"; } } } @@ -20,12 +20,8 @@ int main(int argc, char** argv) { return -1; } - auto vdf = phoenix::vdf_file::open(argv[1]); - auto& header = vdf.header; - - std::cout << "Description: " << header.comment << "\n" - << "Timestamp (Unix): " << header.timestamp << "\nEntries:\n"; - - print_entries(vdf.entries); + zenkit::Vfs vfs {}; + vfs.mount_disk(argv[1]); + print_entries(vfs.root().children()); return 0; } diff --git a/examples/load_zen.cc b/examples/load_zen.cc index 8c909a52..454ca12d 100644 --- a/examples/load_zen.cc +++ b/examples/load_zen.cc @@ -1,4 +1,4 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include diff --git a/examples/run_interpreter.cc b/examples/run_interpreter.cc index 4f8e2073..0d0de550 100644 --- a/examples/run_interpreter.cc +++ b/examples/run_interpreter.cc @@ -1,7 +1,9 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include +#include #include +#include #include @@ -11,6 +13,8 @@ int main(int argc, char** argv) { return -1; } + zenkit::Logger::set_default(zenkit::LogLevel::DEBUG); + phoenix::vm vm {phoenix::script::parse(argv[1])}; phoenix::register_all_script_classes(vm); // needed for Gothic scripts diff --git a/include/phoenix/Vfs.hh b/include/phoenix/Vfs.hh new file mode 100644 index 00000000..67c6c02d --- /dev/null +++ b/include/phoenix/Vfs.hh @@ -0,0 +1,17 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Vfs.hh" + +namespace phoenix { + using VfsBrokenDiskError ZKREM("renamed to zenkit::VfsBrokenDiskError") = zenkit::VfsBrokenDiskError; + using VfsFileExistsError ZKREM("renamed to zenkit::VfsFileExistsError") = zenkit::VfsFileExistsError; + using VfsNotFoundError ZKREM("renamed to zenkit::VfsNotFoundError") = zenkit::VfsNotFoundError; + using VfsNodeType ZKREM("renamed to zenkit::VfsNodeType") = zenkit::VfsNodeType; + using VfsFileDescriptor ZKREM("renamed to zenkit::VfsFileDescriptor") = zenkit::VfsFileDescriptor; + using VfsNode ZKREM("renamed to zenkit::VfsNode") = zenkit::VfsNode; + using VfsNodeComparator ZKREM("renamed to zenkit::VfsNodeComparator") = zenkit::VfsNodeComparator; + using VfsNode ZKREM("renamed to zenkit::VfsNode") = zenkit::VfsNode; + using VfsOverwriteBehavior ZKREM("renamed to zenkit::VfsOverwriteBehavior") = zenkit::VfsOverwriteBehavior; + using Vfs ZKREM("renamed to zenkit::Vfs") = zenkit::Vfs; +} // namespace phoenix diff --git a/include/phoenix/animation.hh b/include/phoenix/animation.hh new file mode 100644 index 00000000..6cf40507 --- /dev/null +++ b/include/phoenix/animation.hh @@ -0,0 +1,11 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/ModelAnimation.hh" + +namespace phoenix { + using animation ZKREM("renamed to zenkit::Animation") = zenkit::ModelAnimation; + using animation_sample ZKREM("renamed to zenkit::AnimationSample") = zenkit::AnimationSample; + using animation_event ZKREM("renamed to zenkit::AnimationEvent") = zenkit::AnimationEvent; + using animation_event_type ZKREM("renamed to zenkit::AnimationEventType") = zenkit::AnimationEventType; +} // namespace phoenix diff --git a/include/phoenix/archive.hh b/include/phoenix/archive.hh new file mode 100644 index 00000000..f921ed0a --- /dev/null +++ b/include/phoenix/archive.hh @@ -0,0 +1,11 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Archive.hh" + +namespace phoenix { + using archive_format ZKREM("renamed to zenkit::ArchiveFormat") = zenkit::ArchiveFormat; + using archive_header ZKREM("renamed to zenkit::ArchiveHeader") = zenkit::ArchiveHeader; + using archive_object ZKREM("renamed to zenkit::ArchiveObject") = zenkit::ArchiveObject; + using archive_reader ZKREM("renamed to zenkit::ArchiveReader") = zenkit::ReadArchive; +} // namespace phoenix diff --git a/include/phoenix/buffer.hh b/include/phoenix/buffer.hh index c0ee69d3..95f80389 100644 --- a/include/phoenix/buffer.hh +++ b/include/phoenix/buffer.hh @@ -1,6 +1,7 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once +#include "zenkit/Library.hh" #include #include @@ -21,9 +22,9 @@ namespace phoenix { /// \brief Base class for exceptions thrown by a phoenix::buffer. - class buffer_error : public error { + class buffer_error : public zenkit::Error { public: - using error::error; + using zenkit::Error::Error; }; /// \brief Exception thrown when reading too many bytes from a buffer. @@ -32,9 +33,9 @@ namespace phoenix { /// is more than the number of bytes remaining. class buffer_underflow : public buffer_error { public: - PHOENIX_API buffer_underflow(std::uint64_t byte, std::uint64_t size); - PHOENIX_API buffer_underflow(std::uint64_t byte, std::uint64_t size, std::string&& context); - PHOENIX_API buffer_underflow(std::uint64_t byte, std::string&& context); + ZKAPI buffer_underflow(std::uint64_t byte, std::uint64_t size); + ZKAPI buffer_underflow(std::uint64_t byte, std::uint64_t size, std::string&& context); + ZKAPI buffer_underflow(std::uint64_t byte, std::string&& context); public: const std::uint64_t byte, size; @@ -47,8 +48,8 @@ namespace phoenix { /// is more than the number of bytes remaining. class buffer_overflow : public buffer_error { public: - PHOENIX_API buffer_overflow(std::uint64_t byte, std::uint64_t size); - PHOENIX_API buffer_overflow(std::uint64_t byte, std::uint64_t size, std::string&& context); + ZKAPI buffer_overflow(std::uint64_t byte, std::uint64_t size); + ZKAPI buffer_overflow(std::uint64_t byte, std::uint64_t size, std::string&& context); public: const std::uint64_t byte, size; @@ -58,7 +59,7 @@ namespace phoenix { /// \brief Exception thrown if a write is attempted on a readonly buffer. class buffer_readonly : public buffer_error { public: - PHOENIX_API explicit buffer_readonly() : buffer_error("buffer is not readonly") {} + ZKAPI explicit buffer_readonly() : buffer_error("buffer is not readonly") {} }; /// \brief Base class for all buffer backings. @@ -67,7 +68,7 @@ namespace phoenix { /// each referencing a subsection of the backing. For this reason, buffer backings should be stateless. class buffer_backing { public: - PHOENIX_API virtual ~buffer_backing() = default; + ZKAPI virtual ~buffer_backing() = default; /// \brief Returns whether this backing considered direct or not. /// @@ -76,22 +77,22 @@ namespace phoenix { /// file. /// /// \return `true` if this backing is direct and `false` if not. - [[nodiscard]] PHOENIX_API virtual bool direct() const noexcept = 0; + [[nodiscard]] ZKAPI virtual bool direct() const noexcept = 0; /// \brief Returns whether or not this backing is readonly or not. /// /// A readonly backing is a backing which can not be written to. /// /// \return `true` if this backing is read-only and `false` if not. - [[nodiscard]] PHOENIX_API virtual bool readonly() const noexcept = 0; + [[nodiscard]] ZKAPI virtual bool readonly() const noexcept = 0; /// \brief Returns the number of bytes available in this backing. /// \return The number of bytes available in this backing. - [[nodiscard]] PHOENIX_API virtual std::uint64_t size() const noexcept = 0; + [[nodiscard]] ZKAPI virtual std::uint64_t size() const noexcept = 0; /// \brief Retrieves a read-only raw byte array of this backing. /// \return A read-only raw byte array into this backing. - [[nodiscard]] PHOENIX_API virtual const std::byte* array() const = 0; + [[nodiscard]] ZKAPI virtual const std::byte* array() const = 0; /// \brief Fills the given \p buf with bytes from this backing starting at \p offset. /// @@ -101,7 +102,7 @@ namespace phoenix { /// \param size The number of bytes to read. /// \param offset The offset at which to start reading bytes into \p buf. /// \throws buffer_underflow if filling \p buf with bytes starting at \p offset fails. - PHOENIX_API virtual void read(std::byte* buf, std::uint64_t size, std::uint64_t offset) const = 0; + ZKAPI virtual void read(std::byte* buf, std::uint64_t size, std::uint64_t offset) const = 0; /// \brief Writes all bytes from \p buf into this backing beginning at \p offset. /// @@ -112,9 +113,9 @@ namespace phoenix { /// \param offset The offset at which to start writing. /// \throws buffer_overflow if writing all bytes of \p buf starting at \p offset fails. /// \throws buffer_readonly if this backing is readonly. - PHOENIX_API virtual void write([[maybe_unused]] const std::byte* buf, - [[maybe_unused]] std::uint64_t size, - [[maybe_unused]] std::uint64_t offset) { + ZKAPI virtual void write([[maybe_unused]] const std::byte* buf, + [[maybe_unused]] std::uint64_t size, + [[maybe_unused]] std::uint64_t offset) { throw buffer_readonly {}; } }; @@ -122,13 +123,15 @@ namespace phoenix { /// \brief A buffer implementation inspired by Java's ByteBuffer class buffer { private: - PHOENIX_INTERNAL buffer(std::shared_ptr backing, std::uint64_t begin, std::uint64_t end); - PHOENIX_INTERNAL buffer(std::shared_ptr backing, - std::uint64_t begin, - std::uint64_t end, - std::uint64_t capacity, - std::uint64_t position, - std::optional mark); + ZKREM("Deprecated. Use zenkit::Read instead.") + ZKINT buffer(std::shared_ptr backing, std::uint64_t begin, std::uint64_t end); + ZKREM("Deprecated. Use zenkit::Read instead.") + ZKINT buffer(std::shared_ptr backing, + std::uint64_t begin, + std::uint64_t end, + std::uint64_t capacity, + std::uint64_t position, + std::optional mark); /// \brief Reads a scalar of type \p T at the current #position. /// @@ -141,7 +144,7 @@ namespace phoenix { template < typename T, typename = typename std::enable_if::value || std::is_floating_point::value>::type> - [[nodiscard]] PHOENIX_INTERNAL T _get_t(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKINT T _get_t(); /// \brief Reads a scalar of type \p T at the given \p pos. /// \tparam T The type of scalar to read. Must be a std::integral or a std::floating_point type. @@ -151,7 +154,7 @@ namespace phoenix { template < typename T, typename = typename std::enable_if::value || std::is_floating_point::value>::type> - [[nodiscard]] PHOENIX_INTERNAL T _get_t(std::uint64_t pos) const; + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKINT T _get_t(std::uint64_t pos) const; /// \brief Writes a scalar of type \p T at the current #position. /// @@ -163,7 +166,8 @@ namespace phoenix { template < typename T, typename = typename std::enable_if::value || std::is_floating_point::value>::type> - PHOENIX_INTERNAL void _put_t(T value); + ZKREM("Deprecated. Use zenkit::Read instead.") + ZKINT void _put_t(T value); public: /// \brief Constructs a new buffer from the given backing. @@ -172,22 +176,23 @@ namespace phoenix { /// To shrink the buffer, see #slice or #extract. /// /// \param backing The buffer backing to use. - PHOENIX_API explicit buffer(std::shared_ptr backing); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI explicit buffer(std::shared_ptr backing); /// \brief Gets the current position of this buffer. /// \return The current position of this buffer. - [[nodiscard]] PHOENIX_API inline std::uint64_t position() const noexcept { + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline std::uint64_t + position() const noexcept { return _m_position; } /// \brief Sets this buffer's position. /// \param pos The new position value. /// \throws buffer_underflow if \p pos is greater than #limit. - PHOENIX_API void position(std::uint64_t pos); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void position(std::uint64_t pos); /// \brief Returns the number of bytes available in this buffer. /// \return The limit of this buffer. - [[nodiscard]] PHOENIX_API inline std::uint64_t limit() const noexcept { + [[nodiscard]] ZKAPI inline std::uint64_t limit() const noexcept { return _m_backing_end - _m_backing_begin; } @@ -197,12 +202,12 @@ namespace phoenix { /// /// \param limit The new limit to set. /// \throws buffer_underflow if \p limit is greater than #capacity. - PHOENIX_API void limit(std::uint64_t limit); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void limit(std::uint64_t limit); /// \brief Rewinds this buffer by setting the position to 0. /// /// This operation discards the #mark if it is set. - PHOENIX_API inline void rewind() { + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline void rewind() { _m_position = 0; _m_mark.reset(); } @@ -211,7 +216,7 @@ namespace phoenix { /// \param count The number of bytes to skip. /// \throws buffer_underflow if #position + \p count > #limit /// \see #position - PHOENIX_API inline void skip(std::uint64_t count) { + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline void skip(std::uint64_t count) { return this->position(this->position() + count); } @@ -220,7 +225,8 @@ namespace phoenix { /// The number of remaining bytes is equal to #limit - #position. /// /// \return The number of bytes remaining in this buffer. - [[nodiscard]] PHOENIX_API inline std::uint64_t remaining() const noexcept { + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline std::uint64_t + remaining() const noexcept { return this->limit() - this->position(); } @@ -230,21 +236,22 @@ namespace phoenix { /// the total number of bytes available in the backing. /// /// \return The capacity of this buffer. - [[nodiscard]] PHOENIX_API inline std::uint64_t capacity() const noexcept { + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline std::uint64_t + capacity() const noexcept { return _m_capacity; } /// \brief Returns whether this buffer is considered to be direct or not. /// \return `true` if the backing of this buffer is considered to be direct. /// \see buffer_backing::direct - [[nodiscard]] PHOENIX_API inline bool direct() const noexcept { + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline bool direct() const noexcept { return _m_backing->direct(); } /// \brief Returns whether this buffer is read-only or not. /// \return `true` if this buffer is read-only. /// \see buffer_backing::readonly - [[nodiscard]] PHOENIX_API inline bool readonly() const noexcept { + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline bool readonly() const noexcept { return _m_backing->readonly(); } @@ -252,22 +259,22 @@ namespace phoenix { /// /// #limit is set to #capacity and #position is set to 0. The mark is discarded if /// it is set. - PHOENIX_API void clear() noexcept; + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void clear() noexcept; /// \brief Flips this buffer. /// /// Its limit is set to the current position and its current position is set to 0. - PHOENIX_API void flip() noexcept; + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void flip() noexcept; /// \brief Sets this buffer's mark at its position. /// \return This buffer. - PHOENIX_API inline void mark() noexcept { + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline void mark() noexcept { _m_mark = position(); } /// \brief Resets this buffer's position to the previously-marked position. /// \return This buffer. - PHOENIX_API inline void reset() { + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline void reset() { if (_m_mark) { position(*_m_mark); } @@ -279,14 +286,14 @@ namespace phoenix { /// capacity and limit. /// /// \return The newly created buffer. - [[nodiscard]] PHOENIX_API buffer duplicate() const noexcept; + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI buffer duplicate() const noexcept; /// \brief Creates a new buffer which shares a subsequence of this buffer. /// /// The shared subsequence starts at the current position and ends at this buffer's limit. /// /// \return The newly created buffer. - [[nodiscard]] PHOENIX_API buffer slice() const noexcept; + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI buffer slice() const noexcept; /// \brief Creates a new buffer which shares a subsequence of this buffer. /// @@ -296,7 +303,8 @@ namespace phoenix { /// \param size The number of bytes the new buffer will encompass. /// \return The newly created buffer. /// \throws buffer_underflow if \p index + \p size > #limit. - [[nodiscard]] PHOENIX_API buffer slice(std::uint64_t index, std::uint64_t size) const; + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI buffer + slice(std::uint64_t index, std::uint64_t size) const; /// \brief Creates a new buffer which shares a subsequence of this buffer. /// @@ -306,14 +314,14 @@ namespace phoenix { /// \param size The number of bytes to extract. /// \return The newly created buffer. /// \throws buffer_underflow if #position + \p size > #limit. - [[nodiscard]] PHOENIX_API buffer extract(std::uint64_t size) { + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI buffer extract(std::uint64_t size) { auto sl = this->slice(position(), size); _m_position += size; return sl; } /// \return A read-only view into the raw contents of this buffer. - [[nodiscard]] PHOENIX_API inline const std::byte* array() const noexcept { + [[nodiscard]] ZKAPI inline const std::byte* array() const noexcept { return _m_backing->array() + _m_backing_begin; } @@ -321,85 +329,87 @@ namespace phoenix { /// \param buf The buffer to write into. /// \param size The number of bytes to get. /// \throws buffer_underflow if the size of \p buf > #remaining. - PHOENIX_API void get(std::byte* buf, std::uint64_t size); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void get(std::byte* buf, std::uint64_t size); /// \brief Get bytes from the buffer, put them into buf and advance the position accordingly. /// \param buf The buffer to write into. /// \param size The number of bytes to get. /// \throws buffer_underflow if the size of \p buf > #remaining. - PHOENIX_API inline void get(std::uint8_t* buf, std::uint64_t size) { + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI inline void get(std::uint8_t* buf, std::uint64_t size) { return this->get((std::byte*) buf, size); } /// \brief Get a value of type std::uint8_t from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API std::uint8_t get(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::uint8_t get(); /// \brief Get a value of type ``char`` from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API char get_char(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI char get_char(); /// \brief Get a value of type std::int16_t from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API std::int16_t get_short(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::int16_t get_short(); /// \brief Get a value of type std::uint16_t from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API std::uint16_t get_ushort(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::uint16_t get_ushort(); /// \brief Get a value of type std::int32_t from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API std::int32_t get_int(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::int32_t get_int(); /// \brief Get a value of type std::uint32_t from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API std::uint32_t get_uint(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::uint32_t get_uint(); /// \brief Get a value of type std::int64_t from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API std::int64_t get_long(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::int64_t get_long(); /// \brief Get a value of type std::uint64_t from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API std::uint64_t get_ulong(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::uint64_t get_ulong(); /// \brief Get a value of type float from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API float get_float(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI float get_float(); /// \brief Get a value of type double from the buffer and advance the position accordingly. /// \return The value just read. /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API double get_double(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI double get_double(); /// \brief Get a string of the given size from the buffer and advance the position accordingly /// \param size The number of characters to read. /// \return The string just read. /// \throws buffer_underflow if the string can't be read. - [[nodiscard]] PHOENIX_API std::string get_string(std::uint64_t size); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::string get_string(std::uint64_t size); /// \brief Get a line from the buffer and advance the position accordingly. /// \param skip_whitespace Set to `true` to skip whitespace characters immediately following the line. /// \return The line just read. /// \throws buffer_underflow if the string can't be read. /// \see isspace - [[nodiscard]] PHOENIX_API std::string get_line(bool skip_whitespace = true); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::string + get_line(bool skip_whitespace = true); /// \brief Get a line from the buffer and advance the position accordingly. /// \param skip_whitespace Set to `true` to skip whitespace characters immediately following the line. /// \return The line just read. /// \throws buffer_underflow if the string can't be read. /// \see isspace - [[nodiscard]] PHOENIX_API std::string get_line_and_ignore(std::string_view whitespace); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::string + get_line_and_ignore(std::string_view whitespace); /// \brief Get a line from the buffer, unescape all relevant escape sequences, and /// advance the position accordingly. @@ -407,32 +417,33 @@ namespace phoenix { /// \return The line just read. /// \throws buffer_underflow if the string can't be read. /// \see isspace - [[nodiscard]] PHOENIX_API std::string get_line_escaped(bool skip_whitespace = true); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI std::string + get_line_escaped(bool skip_whitespace = true); /// \brief Get a 2D-vector from the buffer. /// \return The vector just read /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API glm::vec2 get_vec2(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI glm::vec2 get_vec2(); /// \brief Get a 3D-vector from the buffer. /// \return The vector just read /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API glm::vec3 get_vec3(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI glm::vec3 get_vec3(); /// \brief Get a 3x3 column-major matrix from the buffer. /// \return The vector just read /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API glm::mat3x3 get_mat3x3(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI glm::mat3x3 get_mat3x3(); /// \brief Get a 4x4 column-major matrix from the buffer. /// \return The vector just read /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API glm::mat4x4 get_mat4x4(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI glm::mat4x4 get_mat4x4(); /// \brief Get a 4D-vector from the buffer. /// \return The vector just read /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] PHOENIX_API glm::vec4 get_vec4(); + [[nodiscard]] ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI glm::vec4 get_vec4(); /// \brief Put bytes from buf into the buffer and advance the position accordingly. /// \param buf The data to write. @@ -440,7 +451,7 @@ namespace phoenix { /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put(const std::byte* buf, std::uint64_t size); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put(const std::byte* buf, std::uint64_t size); /// \brief Put bytes from buf into the buffer and advance the position accordingly. /// \param buf The data to write. @@ -448,91 +459,91 @@ namespace phoenix { /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put(const std::uint8_t* buf, std::uint64_t size); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put(const std::uint8_t* buf, std::uint64_t size); /// \brief Put a value of type std::uint8_t into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put(std::uint8_t value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put(std::uint8_t value); /// \brief Put a value of type char into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_char(char value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_char(char value); /// \brief Put a value of type std::int16_t into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_short(std::int16_t value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_short(std::int16_t value); /// \brief Put a value of type std::uint16_t into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_ushort(std::uint16_t value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_ushort(std::uint16_t value); /// \brief Put a value of type std::int32_t into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_int(std::int32_t value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_int(std::int32_t value); /// \brief Put a value of type std::uint32_t into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_uint(std::uint32_t value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_uint(std::uint32_t value); /// \brief Put a value of type std::int64_t into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_long(std::int64_t value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_long(std::int64_t value); /// \brief Put a value of type std::uint64_t into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_ulong(std::uint64_t value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_ulong(std::uint64_t value); /// \brief Put a value of type float into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_float(float value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_float(float value); /// \brief Put a value of type double into the buffer and advance the position accordingly /// \param value The value to put into the buffer /// \return This buffer. /// \throws buffer_overflow if the value can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_double(double value); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_double(double value); /// \brief Put string into the buffer and advance the position accordingly /// \param str The string to put into the buffer. /// \return This buffer. /// \throws buffer_overflow if the string can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_string(std::string_view str); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_string(std::string_view str); /// \brief Put string followed by into the buffer and advance the position accordingly /// \param str The string to put into the buffer. /// \return This buffer. /// \throws buffer_overflow if the string can't be written. /// \throws buffer_readonly if the buffer is read-only. - PHOENIX_API void put_line(std::string_view str); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI void put_line(std::string_view str); /// \brief Allocates a new buffer with the given size. /// @@ -541,7 +552,7 @@ namespace phoenix { /// /// \param size The number of bytes to allocate. /// \return The newly allocated buffer. - PHOENIX_API static buffer allocate(std::uint64_t size); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI static buffer allocate(std::uint64_t size); /// \brief Creates a new buffer from the given vector. /// @@ -551,7 +562,8 @@ namespace phoenix { /// \param buf A vector containing the data to be wrapped into a buffer. /// \param readonly Set to `false` to be able to write to the buffer. /// \return The newly created buffer. - PHOENIX_API static buffer of(std::vector&& buf, bool readonly = true); + ZKREM("Deprecated. Use zenkit::Read instead.") + ZKAPI static buffer of(std::vector&& buf, bool readonly = true); /// \brief Opens the given file as a direct buffer. /// @@ -560,7 +572,8 @@ namespace phoenix { /// \param path The path of the file to mmap. /// \param readonly Set to `false` to be able to write to the buffer. /// \return The newly created buffer. - PHOENIX_API static buffer mmap(const std::filesystem::path& path, bool readonly = true); + ZKREM("Deprecated. Use zenkit::Read instead.") + ZKAPI static buffer mmap(const std::filesystem::path& path, bool readonly = true); /// \brief Opens the given file as an indirect buffer. /// @@ -572,15 +585,16 @@ namespace phoenix { /// gain for small files but it is generally slower and uses more memory. You should probably use #mmap /// instead. /// \return The newly created buffer. - PHOENIX_API static buffer read(const std::filesystem::path& path, bool readonly = true); + ZKREM("Deprecated. Use zenkit::Read instead.") + ZKAPI static buffer read(const std::filesystem::path& path, bool readonly = true); /// \brief Returns a duplicate of the empty buffer. /// \return The empty buffer. - PHOENIX_API static buffer empty(); + ZKREM("Deprecated. Use zenkit::Read instead.") ZKAPI static buffer empty(); private: static std::unique_ptr _m_empty; - PHOENIX_API friend bool operator==(const buffer&, const buffer&); + ZKAPI friend bool operator==(const buffer&, const buffer&); std::shared_ptr _m_backing; std::uint64_t _m_backing_begin, _m_backing_end; @@ -590,5 +604,5 @@ namespace phoenix { std::optional _m_mark; }; - PHOENIX_API bool operator==(const buffer&, const buffer&); + ZKREM("Deprecated") ZKAPI bool operator==(const buffer&, const buffer&); } // namespace phoenix diff --git a/include/phoenix/ext/daedalus_classes.hh b/include/phoenix/ext/daedalus_classes.hh new file mode 100644 index 00000000..0562b016 --- /dev/null +++ b/include/phoenix/ext/daedalus_classes.hh @@ -0,0 +1,51 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/addon/daedalus.hh" + +#include "phoenix/script.hh" + +namespace phoenix { + using c_gil_values ZKREM("renamed to zenkit::IGuildValues") = zenkit::IGuildValues; + using c_npc ZKREM("renamed to zenkit::INpc") = zenkit::INpc; + using c_item ZKREM("renamed to zenkit::IItem") = zenkit::IItem; + using c_item_react ZKREM("renamed to zenkit::IItemReact") = zenkit::IItemReact; + using c_mission ZKREM("renamed to zenkit::IMission") = zenkit::IMission; + using c_focus ZKREM("renamed to zenkit::IFocus") = zenkit::IFocus; + using c_info ZKREM("renamed to zenkit::IInfo") = zenkit::IInfo; + using c_info_choice ZKREM("renamed to zenkit::IInfoChoice") = zenkit::IInfoChoice; + using c_spell ZKREM("renamed to zenkit::ISpell") = zenkit::ISpell; + using c_svm ZKREM("renamed to zenkit::ISvm") = zenkit::ISvm; + using c_menu ZKREM("renamed to zenkit::IMenu") = zenkit::IMenu; + using c_menu_item ZKREM("renamed to zenkit::IMenuItem") = zenkit::IMenuItem; + using c_camera ZKREM("renamed to zenkit::ICamera") = zenkit::ICamera; + using c_music_system ZKREM("renamed to zenkit::IMusicSystem") = zenkit::IMusicSystem; + using c_music_theme ZKREM("renamed to zenkit::IMusicTheme") = zenkit::IMusicTheme; + using c_music_jingle ZKREM("renamed to zenkit::IMusicJingle") = zenkit::IMusicJingle; + using c_particle_fx ZKREM("renamed to zenkit::IParticleEffect") = zenkit::IParticleEffect; + using c_particle_fx_emit_key ZKREM("renamed to zenkit::IParticleEffectEmitKey") = zenkit::IParticleEffectEmitKey; + using c_fx_base ZKREM("renamed to zenkit::IEffectBase") = zenkit::IEffectBase; + using c_fight_ai ZKREM("renamed to zenkit::IFightAi") = zenkit::IFightAi; + using c_sfx ZKREM("renamed to zenkit::ISoundEffect") = zenkit::ISoundEffect; + using c_sound_system ZKREM("renamed to zenkit::ISoundSystem") = zenkit::ISoundSystem; + + namespace damage_type = zenkit::DamageType; + namespace npc_attribute = zenkit::NpcAttribute; + + using npc_type ZKREM("renamed to zenkit::NpcType") = zenkit::NpcType; + using npc_flag ZKREM("renamed to zenkit::NpcFlag") = zenkit::NpcFlag; + using item_flags ZKREM("renamed to zenkit::ItemFlag") = zenkit::ItemFlag; + using c_menu_flags ZKREM("renamed to zenkit::MenuFlag") = zenkit::MenuFlag; + using c_menu_item_flags ZKREM("renamed to zenkit::MenuItemFlag") = zenkit::MenuItemFlag; + using c_menu_item_type ZKREM("renamed to zenkit::MenuItemType") = zenkit::MenuItemType; + using c_menu_item_select_event ZKREM("renamed to zenkit::MenuItemSelectEvent") = zenkit::MenuItemSelectEvent; + using c_menu_item_select_action ZKREM("renamed to zenkit::MenuItemSelectAction") = zenkit::MenuItemSelectAction; + using music_transition_type ZKREM("renamed to zenkit::MusicTransitionEffect") = zenkit::MusicTransitionEffect; + using music_transition_subtype ZKREM("renamed to zenkit::MusicTransitionType") = zenkit::MusicTransitionType; + using c_fight_ai_move ZKREM("renamed to zenkit::FightAiMove") = zenkit::FightAiMove; + + ZKREM("renamed to zenkit::register_all_script_classes") + inline void register_all_script_classes(zenkit::DaedalusScript& s) { + zenkit::register_all_script_classes(s); + } +} // namespace phoenix diff --git a/include/phoenix/ext/dds_convert.hh b/include/phoenix/ext/dds_convert.hh new file mode 100644 index 00000000..4ae5b8aa --- /dev/null +++ b/include/phoenix/ext/dds_convert.hh @@ -0,0 +1,16 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Library.hh" +#include "zenkit/addon/texcvt.hh" + +#include "phoenix/buffer.hh" + +namespace phoenix { + /// \brief Converts a texture to the DDS format. + /// \param tex The texture to convert. + /// \return A buffer containing the DDS file. + [[nodiscard]] ZKREM("use zenkit::to_dds") inline buffer texture_to_dds(zenkit::Texture const& tex) { + return buffer::of(zenkit::to_dds(tex)); + } +} // namespace phoenix diff --git a/include/phoenix/font.hh b/include/phoenix/font.hh new file mode 100644 index 00000000..c58ddeb6 --- /dev/null +++ b/include/phoenix/font.hh @@ -0,0 +1,9 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Font.hh" + +namespace phoenix { + using glyph ZKREM("renamed to FontGlyph") = zenkit::FontGlyph; + using font ZKREM("renamed to Font") = zenkit::Font; +} // namespace phoenix diff --git a/include/phoenix/material.hh b/include/phoenix/material.hh new file mode 100644 index 00000000..835e874f --- /dev/null +++ b/include/phoenix/material.hh @@ -0,0 +1,13 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Material.hh" + +namespace phoenix { + using alpha_function ZKREM("renamed to zenkit::AlphaFunction") = zenkit::AlphaFunction; + using material_group ZKREM("renamed to zenkit::MaterialGroup") = zenkit::MaterialGroup; + using wave_speed_type ZKREM("renamed to zenkit::WaveSpeed") = zenkit::WaveSpeed; + using wave_mode_type ZKREM("renamed to zenkit::WaveMode") = zenkit::WaveMode; + using animation_mapping_mode ZKREM("renamed to zenkit::AnimationMapping") = zenkit::AnimationMapping; + using material ZKREM("renamed to zenkit::Material") = zenkit::Material; +} // namespace phoenix diff --git a/include/phoenix/math.hh b/include/phoenix/math.hh new file mode 100644 index 00000000..70acaa81 --- /dev/null +++ b/include/phoenix/math.hh @@ -0,0 +1,9 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Boxes.hh" + +namespace phoenix { + using bounding_box ZKREM("renamed to zenkit::AxisAlignedBoundingBox") = zenkit::AxisAlignedBoundingBox; + using obb ZKREM("renamed to zenkit::OrientedBoundingBox") = zenkit::OrientedBoundingBox; +} // namespace phoenix diff --git a/include/phoenix/mesh.hh b/include/phoenix/mesh.hh new file mode 100644 index 00000000..eedce6f1 --- /dev/null +++ b/include/phoenix/mesh.hh @@ -0,0 +1,15 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Mesh.hh" + +#include "phoenix/material.hh" +#include "phoenix/math.hh" + +namespace phoenix { + using light_map ZKREM("renamed to zenkit::LightMap") = zenkit::LightMap; + using vertex_feature ZKREM("renamed to zenkit::VertexFeature") = zenkit::VertexFeature; + using polygon_flags ZKREM("renamed to zenkit::PolygonFlags") = zenkit::PolygonFlagSet; + using polygon_list ZKREM("renamed to zenkit::PolygonList") = zenkit::PolygonList; + using mesh ZKREM("renamed to zenkit::Mesh") = zenkit::Mesh; +} // namespace phoenix diff --git a/include/phoenix/messages.hh b/include/phoenix/messages.hh new file mode 100644 index 00000000..9f283cb2 --- /dev/null +++ b/include/phoenix/messages.hh @@ -0,0 +1,10 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/CutsceneLibrary.hh" + +namespace phoenix { + using atomic_message ZKREM("renamed to zenkit::CutsceneMessage") = zenkit::CutsceneMessage; + using message_block ZKREM("renamed to zenkit::CutsceneBlock") = zenkit::CutsceneBlock; + using messages ZKREM("renamed to zenkit::CutsceneLibrary") = zenkit::CutsceneLibrary; +} // namespace phoenix diff --git a/include/phoenix/model.hh b/include/phoenix/model.hh new file mode 100644 index 00000000..3af93aed --- /dev/null +++ b/include/phoenix/model.hh @@ -0,0 +1,8 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Model.hh" + +namespace phoenix { + using model ZKREM("renamed to zenkit::Model") = zenkit::Model; +} diff --git a/include/phoenix/model_hierarchy.hh b/include/phoenix/model_hierarchy.hh new file mode 100644 index 00000000..419e6f46 --- /dev/null +++ b/include/phoenix/model_hierarchy.hh @@ -0,0 +1,9 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/ModelHierarchy.hh" + +namespace phoenix { + using model_hierarchy_node ZKREM("renamed to zenkit::ModelHierarchyNode") = zenkit::ModelHierarchyNode; + using model_hierarchy ZKREM("renamed to zenkit::ModelHierarchy") = zenkit::ModelHierarchy; +} // namespace phoenix diff --git a/include/phoenix/model_mesh.hh b/include/phoenix/model_mesh.hh new file mode 100644 index 00000000..a505d335 --- /dev/null +++ b/include/phoenix/model_mesh.hh @@ -0,0 +1,8 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/ModelMesh.hh" + +namespace phoenix { + using model_mesh ZKREM("renamed to zenkit::ModelMesh") = zenkit::ModelMesh; +} // namespace phoenix diff --git a/include/phoenix/model_script.hh b/include/phoenix/model_script.hh new file mode 100644 index 00000000..882f695f --- /dev/null +++ b/include/phoenix/model_script.hh @@ -0,0 +1,44 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "phoenix.hh" +#include "zenkit/ModelScript.hh" + +namespace phoenix { + namespace mds { + ZKREM("use zenkit::AnimationFlags::NONE") static constexpr auto const af_none = zenkit::AnimationFlags::NONE; + ZKREM("use zenkit::AnimationFlags::MOVE") static constexpr auto const af_move = zenkit::AnimationFlags::MOVE; + ZKREM("use zenkit::AnimationFlags::ROTATE") + static constexpr auto const af_rotate = zenkit::AnimationFlags::ROTATE; + ZKREM("use zenkit::AnimationFlags::QUEUE") static constexpr auto const af_queue = zenkit::AnimationFlags::QUEUE; + ZKREM("use zenkit::AnimationFlags::FLY") static constexpr auto const af_fly = zenkit::AnimationFlags::FLY; + ZKREM("use zenkit::AnimationFlags::IDLE") static constexpr auto const af_idle = zenkit::AnimationFlags::IDLE; + ZKREM("use zenkit::AnimationFlags::INPLACE") + static constexpr auto const af_inplace = zenkit::AnimationFlags::INPLACE; + + using animation_direction ZKREM("renamed to zenkit::AnimationDirection") = zenkit::AnimationDirection; + using animation_flags ZKREM("use zenkit::AnimationFlags instead") = zenkit::AnimationFlags; + using event_fight_mode ZKREM("renamed to zenkit::MdsFightMode") = zenkit::MdsFightMode; + using event_tag_type ZKREM("renamed to zenkit::MdsEventType") = zenkit::MdsEventType; + using skeleton ZKREM("renamed to zenkit::MdsSkeleton") = zenkit::MdsSkeleton; + using model_tag ZKREM("renamed to zenkit::MdsModelTag") = zenkit::MdsModelTag; + using event_tag ZKREM("renamed to zenkit::MdsEventTag") = zenkit::MdsEventTag; + using event_pfx ZKREM("renamed to zenkit::MdsParticleEffect") = zenkit::MdsParticleEffect; + using event_camera_tremor ZKREM("renamed to zenkit::MdsCameraTremor") = zenkit::MdsCameraTremor; + using event_pfx_stop ZKREM("renamed to zenkit::MdsParticleEffectStop") = zenkit::MdsParticleEffectStop; + using event_sfx ZKREM("renamed to zenkit::MdsSoundEffect") = zenkit::MdsSoundEffect; + using event_sfx_ground ZKREM("renamed to zenkit::MdsSoundEffectGround") = zenkit::MdsSoundEffectGround; + using event_morph_animate ZKREM("renamed to zenkit::MdsMorphAnimation") = zenkit::MdsMorphAnimation; + using animation ZKREM("renamed to zenkit::MdsAnimation") = zenkit::MdsAnimation; + using animation_alias ZKREM("renamed to zenkit::MdsAnimationAlias") = zenkit::MdsAnimationAlias; + using animation_blending ZKREM("renamed to zenkit::MdsAnimationBlend") = zenkit::MdsAnimationBlend; + using animation_combination ZKREM("renamed to zenkit::MdsAnimationCombine") = zenkit::MdsAnimationCombine; + } // namespace mds + + struct script_sytax_error : public zenkit::ParserError { + public: + ZKINT inline script_sytax_error(std::string&&, std::string&&) : zenkit::ParserError("") {} + }; + + using model_script ZKREM("renamed to zenkit::ModelScript") = zenkit::ModelScript; +} // namespace phoenix diff --git a/include/phoenix/morph_mesh.hh b/include/phoenix/morph_mesh.hh new file mode 100644 index 00000000..bc537207 --- /dev/null +++ b/include/phoenix/morph_mesh.hh @@ -0,0 +1,10 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/MorphMesh.hh" + +namespace phoenix { + using morph_mesh ZKREM("using zenkit::MorphMesh") = zenkit::MorphMesh; + using morph_source ZKREM("using zenkit::MorphSource") = zenkit::MorphSource; + using morph_animation ZKREM("using zenkit::MorphAnimation") = zenkit::MorphAnimation; +} // namespace phoenix diff --git a/include/phoenix/phoenix.hh b/include/phoenix/phoenix.hh index 7f3c4acf..8cf42e31 100644 --- a/include/phoenix/phoenix.hh +++ b/include/phoenix/phoenix.hh @@ -1,7 +1,10 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" +#include "zenkit/Date.hh" +#include "zenkit/Error.hh" +#include "zenkit/Logger.hh" +#include "zenkit/Misc.hh" #include #include @@ -11,151 +14,21 @@ #include #include -#if PHOENIX_LOG_LEVEL > 0 - #define PX_LOGE(...) phoenix::logging::log(phoenix::logging::level::error, __VA_ARGS__) -#else - #define PX_LOGE(...) -#endif - -#if PHOENIX_LOG_LEVEL > 1 - #define PX_LOGW(...) phoenix::logging::log(phoenix::logging::level::warn, __VA_ARGS__) -#else - #define PX_LOGW(...) -#endif - -#if PHOENIX_LOG_LEVEL > 2 - #define PX_LOGI(...) phoenix::logging::log(phoenix::logging::level::info, __VA_ARGS__) -#else - #define PX_LOGI(...) -#endif - -#if PHOENIX_LOG_LEVEL > 3 - #define PX_LOGD(...) phoenix::logging::log(phoenix::logging::level::debug, __VA_ARGS__) -#else - #define PX_LOGD(...) -#endif - namespace phoenix { class buffer; - class archive_reader; - - /// \brief An enum for providing a game version hint to some functions - enum class game_version { - gothic_1, ///< Represents any patch of Gothic - gothic_2, ///< Represents any patch of Gothic II, including _Night of the Raven_. - }; - - /// \brief Tests whether two strings are equal when ignoring case. - /// - /// Internally, uses std::tolower to compare the strings character by character. - /// - /// \param a A string. - /// \param b Another string. - /// \return ``true`` if both strings are equal when ignoring case. - PHOENIX_API bool iequals(std::string_view a, std::string_view b); - - /// \brief Tests whether \p a is lexicographically less than \p b. - /// - /// Internally, uses std::tolower to compare the strings character by character. - /// - /// \param a A string. - /// \param b Another string. - /// \return ``true`` if \p a is lexicographically less than \p b. - PHOENIX_API bool icompare(std::string_view a, std::string_view b); - - /// \brief A basic datetime structure used by the *ZenGin*. - struct date { - /// \brief Parses a date from a buffer. - /// \param buf The buffer to read from - /// \return The date. - PHOENIX_API static date parse(buffer& buf); - - std::uint32_t year; - std::uint16_t month; - std::uint16_t day; - std::uint16_t hour; - std::uint16_t minute; - std::uint16_t second; - }; - - /// \brief Logging manager for phoenix. - class logging { - public: - enum class level { error, warn, info, debug }; - - /// \brief Supply a custom logger callback to be used for log output from phoenix. - /// \param callback The callback to use. - PHOENIX_API static void use_logger(std::function&& callback); - /// \brief Use the default logger callback for phoenix. - PHOENIX_API static void use_default_logger(); + using game_version ZKREM("renamed to zenkit::GameVersion") = zenkit::GameVersion; + using error ZKREM("renamed to zenkit::Error") = zenkit::Error; + using parser_error ZKREM("renamed to zenkit::ParserError") = zenkit::ParserError; - /// \brief Send a logging event to the underlying log callback. - /// \param lvl The level of the log message. - /// \param message The message to send. - template - static void log(level lvl, const T&... msg) { - if (logging::callback) { - std::stringstream msg_buffer {}; - logging::_build_log_message(msg_buffer, msg...); - (*logging::callback)(lvl, msg_buffer.str()); - } - } - - private: - template - static void _build_log_message(std::stringstream& stream, const T& v, const Ts&... vals) { - stream << v; - - if constexpr (sizeof...(vals) > 0) { - _build_log_message(stream, vals...); - } - } - - static std::optional> callback; - }; - - /// \brief Base class for all exceptions. - class error : public std::exception { - public: - PHOENIX_API explicit error(std::string&& message); - - [[nodiscard]] PHOENIX_API inline const char* what() const noexcept override { - return message.c_str(); - } - - public: - const std::string message; - }; - - /// \brief An error representing a parsing failure of any kind. - class parser_error : public error { - public: - PHOENIX_INTERNAL explicit parser_error(std::string&& resource_type); - PHOENIX_API explicit parser_error(std::string&& resource_type, std::string&& context); - PHOENIX_INTERNAL explicit parser_error(std::string&& resource_type, const std::exception& cause); - PHOENIX_INTERNAL explicit parser_error(std::string&& resource_type, - const std::exception& cause, - std::string&& context); - - public: - const std::string resource_type; - const std::optional context {std::nullopt}; - const std::optional cause {std::nullopt}; - }; - - template - T parse([[maybe_unused]] buffer& buf) { - throw parser_error {"unknown", "parsing routine not implemented"}; + inline bool iequals(std::string_view a, std::string_view b) { + return zenkit::iequals(a, b); } - template - inline T parse(buffer&& buf) { - return parse(buf); + inline bool icompare(std::string_view a, std::string_view b) { + return zenkit::icompare(a, b); } - template - T parse([[maybe_unused]] archive_reader& buf) { - throw parser_error {"unknown", "parsing routine not implemented"}; - } + using date ZKREM("renamed to zenkit::Date") = zenkit::Date; + using logging ZKREM("renamed to zenkit::Logger") = zenkit::Logger; } // namespace phoenix diff --git a/include/phoenix/proto_mesh.hh b/include/phoenix/proto_mesh.hh new file mode 100644 index 00000000..9fb9af0d --- /dev/null +++ b/include/phoenix/proto_mesh.hh @@ -0,0 +1,16 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/MultiResolutionMesh.hh" + +namespace phoenix { + using triangle ZKREM("renamed to zenkit::MeshTriangle") = zenkit::MeshTriangle; + using triangle_edge ZKREM("renamed to zenkit::MeshTriangleEdge") = zenkit::MeshTriangleEdge; + using edge ZKREM("renamed to zenkit::MeshEdge") = zenkit::MeshEdge; + using wedge ZKREM("renamed to zenkit::MeshWedge") = zenkit::MeshWedge; + using plane ZKREM("renamed to zenkit::MeshPlane") = zenkit::MeshPlane; + using mesh_section ZKREM("renamed to zenkit::MeshSection") = zenkit::MeshSection; + using sub_mesh_section ZKREM("renamed to zenkit::SubMeshSection") = zenkit::SubMeshSection; + using sub_mesh ZKREM("renamed to zenkit::SubMesh") = zenkit::SubMesh; + using proto_mesh ZKREM("renamed to zenkit::MultiResolutionMesh") = zenkit::MultiResolutionMesh; +} // namespace phoenix diff --git a/include/phoenix/save_game.hh b/include/phoenix/save_game.hh new file mode 100644 index 00000000..b26a3005 --- /dev/null +++ b/include/phoenix/save_game.hh @@ -0,0 +1,15 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/SaveGame.hh" + +namespace phoenix::unstable { + using save_info ZKREM("renamed to zenkit::unstable::SaveInfo") = zenkit::unstable::SaveInfo; + using topic_section ZKREM("renamed to zenkit::unstable::SaveTopicSection") = zenkit::unstable::SaveTopicSection; + using topic_status ZKREM("renamed to zenkit::unstable::SaveTopicStatus") = zenkit::unstable::SaveTopicStatus; + using log_topic ZKREM("renamed to zenkit::unstable::SaveLogTopic") = zenkit::unstable::SaveLogTopic; + using info_state ZKREM("renamed to zenkit::unstable::SaveInfoState") = zenkit::unstable::SaveInfoState; + using symbol_state ZKREM("renamed to zenkit::unstable::SaveSymbolState") = zenkit::unstable::SaveSymbolState; + using script_state ZKREM("renamed to zenkit::unstable::SaveScriptState") = zenkit::unstable::SaveScriptState; + using save_game ZKREM("renamed to zenkit::unstable::SaveGame") = zenkit::unstable::SaveGame; +} // namespace phoenix::unstable diff --git a/include/phoenix/script.hh b/include/phoenix/script.hh new file mode 100644 index 00000000..a450dc51 --- /dev/null +++ b/include/phoenix/script.hh @@ -0,0 +1,36 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/DaedalusScript.hh" + +namespace phoenix { + namespace symbol_flag = zenkit::DaedalusSymbolFlag; + + using datatype ZKREM("renamed to zenkit::DaedalusDataType") = zenkit::DaedalusDataType; + using opcode ZKREM("renamed to zenkit::DaedalusOpcode") = zenkit::DaedalusOpcode; + using symbol ZKREM("renamed to zenkit::DaedalusSymbol") = zenkit::DaedalusSymbol; + using instance ZKREM("renamed to zenkit::DaedalusInstance") = zenkit::DaedalusInstance; + using script_error ZKREM("renamed to zenkit::DaedalusScriptError") = zenkit::DaedalusScriptError; + using symbol_not_found ZKREM("renamed to zenkit::DaedalusSymbolNotFound") = zenkit::DaedalusSymbolNotFound; + using member_registration_error + ZKREM("renamed to zenkit::DaedalusMemberRegistrationError") = zenkit::DaedalusMemberRegistrationError; + using invalid_registration_datatype + ZKREM("renamed to zenkit::DaedalusInvalidRegistrationDataType") = zenkit::DaedalusInvalidRegistrationDataType; + using illegal_access ZKREM("renamed to zenkit::DaedalusIllegalAccess") = zenkit::DaedalusIllegalAccess; + using illegal_type_access ZKREM("renamed to zenkit::DaedalusIllegalTypeAccess") = zenkit::DaedalusIllegalTypeAccess; + using illegal_index_access + ZKREM("renamed to zenkit::DaedalusIllegalIndexAccess") = zenkit::DaedalusIllegalIndexAccess; + using illegal_const_access + ZKREM("renamed to zenkit::DaedalusIllegalConstAccess") = zenkit::DaedalusIllegalConstAccess; + using illegal_instance_access + ZKREM("renamed to zenkit::DaedalusIllegalInstanceAccess") = zenkit::DaedalusIllegalInstanceAccess; + using unbound_member_access + ZKREM("renamed to zenkit::DaedalusUnboundMemberAccess") = zenkit::DaedalusUnboundMemberAccess; + using no_context ZKREM("renamed to zenkit::DaedalusNoContextError") = zenkit::DaedalusNoContextError; + using illegal_context_type + ZKREM("renamed to zenkit::DaedalusIllegalContextType") = zenkit::DaedalusIllegalContextType; + using instruction ZKREM("renamed to zenkit::DaedalusInstruction") = zenkit::DaedalusInstruction; + using script ZKREM("renamed to zenkit::DaedalusScript") = zenkit::DaedalusScript; + using transient_instance ZKREM("renamed to zenkit::DaedalusTransientInstance") = zenkit::DaedalusTransientInstance; + using opaque_instance ZKREM("renamed to zenkit::DaedalusOpaqueInstance") = zenkit::DaedalusOpaqueInstance; +} // namespace phoenix diff --git a/include/phoenix/softskin_mesh.hh b/include/phoenix/softskin_mesh.hh new file mode 100644 index 00000000..888efff5 --- /dev/null +++ b/include/phoenix/softskin_mesh.hh @@ -0,0 +1,12 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/SoftSkinMesh.hh" + +#include "phoenix/math.hh" + +namespace phoenix { + using softskin_mesh ZKREM("renamed to zenkit::SoftSkinMesh") = zenkit::SoftSkinMesh; + using wedge_normal ZKREM("renamed to zenkit::SoftSkinWedgeNormal") = zenkit::SoftSkinWedgeNormal; + using weight_entry ZKREM("renamed to zenkit::SoftSkinWeightEntry") = zenkit::SoftSkinWeightEntry; +} // namespace phoenix diff --git a/include/phoenix/texture.hh b/include/phoenix/texture.hh new file mode 100644 index 00000000..846cbcd7 --- /dev/null +++ b/include/phoenix/texture.hh @@ -0,0 +1,26 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Texture.hh" + +namespace phoenix { + constexpr zenkit::TextureFormat const tex_B8G8R8A8 ZKREM("use zenkit::TextureFormat") = zenkit::tex_B8G8R8A8; + constexpr zenkit::TextureFormat const tex_R8G8B8A8 ZKREM("use zenkit::TextureFormat") = zenkit::tex_R8G8B8A8; + constexpr zenkit::TextureFormat const tex_A8B8G8R8 ZKREM("use zenkit::TextureFormat") = zenkit::tex_A8B8G8R8; + constexpr zenkit::TextureFormat const tex_A8R8G8B8 ZKREM("use zenkit::TextureFormat") = zenkit::tex_A8R8G8B8; + constexpr zenkit::TextureFormat const tex_B8G8R8 ZKREM("use zenkit::TextureFormat") = zenkit::tex_B8G8R8; + constexpr zenkit::TextureFormat const tex_R8G8B8 ZKREM("use zenkit::TextureFormat") = zenkit::tex_R8G8B8; + constexpr zenkit::TextureFormat const tex_A4R4G4B4 ZKREM("use zenkit::TextureFormat") = zenkit::tex_A4R4G4B4; + constexpr zenkit::TextureFormat const tex_A1R5G5B5 ZKREM("use zenkit::TextureFormat") = zenkit::tex_A1R5G5B5; + constexpr zenkit::TextureFormat const tex_R5G6B5 ZKREM("use zenkit::TextureFormat") = zenkit::tex_R5G6B5; + constexpr zenkit::TextureFormat const tex_p8 ZKREM("use zenkit::TextureFormat") = zenkit::tex_p8; + constexpr zenkit::TextureFormat const tex_dxt1 ZKREM("use zenkit::TextureFormat") = zenkit::tex_dxt1; + constexpr zenkit::TextureFormat const tex_dxt2 ZKREM("use zenkit::TextureFormat") = zenkit::tex_dxt2; + constexpr zenkit::TextureFormat const tex_dxt3 ZKREM("use zenkit::TextureFormat") = zenkit::tex_dxt3; + constexpr zenkit::TextureFormat const tex_dxt4 ZKREM("use zenkit::TextureFormat") = zenkit::tex_dxt4; + constexpr zenkit::TextureFormat const tex_dxt5 ZKREM("use zenkit::TextureFormat") = zenkit::tex_dxt5; + + using texture ZKREM("renamed to zenkit::Texture") = zenkit::Texture; + using texture_format ZKREM("renamed to zenkit::TextureFormat") = zenkit::TextureFormat; + using argb ZKREM("renamed to zenkit::ColorARGB") = zenkit::ColorARGB; +} // namespace phoenix diff --git a/include/phoenix/vdfs.hh b/include/phoenix/vdfs.hh deleted file mode 100644 index 8b83dca6..00000000 --- a/include/phoenix/vdfs.hh +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright © 2022 Luis Michaelis -// SPDX-License-Identifier: MIT -#pragma once -#include "Api.hh" -#include - -#include -#include -#include -#include -#include - -namespace phoenix { - static constexpr std::string_view VDF_SIGNATURE_G1 = "PSVDSC_V2.00\r\n\r\n"; - static constexpr std::string_view VDF_SIGNATURE_G2 = "PSVDSC_V2.00\n\r\n\r"; - static constexpr std::uint32_t VDF_MASK_DIRECTORY = 0x80'00'00'00; - static constexpr std::uint32_t VDF_MASK_LAST = 0x40'00'00'00; - static constexpr std::uint32_t VDF_VERSION = 0x50; - static constexpr std::uint32_t VDF_COMMENT_LENGTH = 256; - static constexpr std::uint32_t VDF_SIGNATURE_LENGTH = 16; - static constexpr std::uint32_t VDF_ENTRY_NAME_LENGTH = 64; - - /// \brief Converts a ``DOS`` timestamp into a unix timestamp. - /// - /// This date format does not seem to be documented anywhere, so this implementation - /// is based on the implementation in PhysicsFS - /// ([here](https://github.com/icculus/physfs/blob/0d4e9aac4575744ddaae56b146f1be19f064f0e5/src/physfs_archiver_vdf.c#l37)). - /// - /// \param dos The timestamp to convert. - /// \return The converted unix timestamp. - /// \note Assumes the ``DOS`` timestamp is in the ``GMT`` timezone. - PHOENIX_API std::time_t dos_to_unix_time(std::uint32_t dos) noexcept; - - /// \brief Converts a unix timestamp (std::time_t) to a ``DOS`` timestamp. - /// \param tm The unix timestamp to convert - /// \return The unix timestamp as a ``DOS`` timestamp. - /// \note This will convert to a ``DOS`` timestamp in the ``GMT`` timezone. - PHOENIX_API std::uint32_t unix_time_to_dos(std::time_t tm) noexcept; - - /// \brief An exception thrown if the signature of a VDF file is not recognized. - /// - /// If the signature is not recognized, this means that the VDF file was created using a 3rd-party tool - /// like Union and is not necessarily compatible with the VDFS spec. Accepted signatures are - /// phoenix::VDF_SIGNATURE_G1 and phoenix::VDF_SIGNATURE_G2. - class vdfs_signature_error : public error { - public: - PHOENIX_INTERNAL explicit vdfs_signature_error(const std::string& signature); - }; - - /// \brief Represents the header of a VDF. - class vdf_header { - public: - /// \brief Constructs a new header with the given comment and timestamp. - /// \param comment The comment to set. Note that comments are trimmed to 256 characters. - /// \param timestamp The timestamp to set.# - PHOENIX_API explicit vdf_header(std::string_view comment, std::time_t timestamp = -1); - - PHOENIX_API vdf_header() = default; - - /// \brief Reads a vdf_header from the given buffer. - /// \param in The reader to read from. - /// \return The header read. - PHOENIX_INTERNAL static vdf_header read(buffer& in); - - static constexpr const auto packed_size = VDF_COMMENT_LENGTH + VDF_SIGNATURE_LENGTH + 6 * sizeof(std::uint32_t); - - public: - /// \brief The comment of the VDF. - std::string comment; - - /// \brief The signature of the VDF. - std::string signature {VDF_SIGNATURE_G2}; - - /// \brief The number of file entries in the VDF. - std::uint32_t entry_count {0}; - - /// \brief The total number of entries in the VDF. - std::uint32_t file_count {0}; - - /// \brief The timestamp of the VDF. - std::time_t timestamp {0}; - - /// \brief The total size of the VDF in bytes. - std::uint32_t size {0}; - - /// \brief The offset of the entry catalog in bytes. - std::uint32_t catalog_offset {0}; - - /// \brief The version of the VDF. - std::uint32_t version {VDF_VERSION}; - }; - - class vdf_entry; - - struct vdf_entry_comparator { - public: - using is_transparent = std::true_type; - - PHOENIX_INTERNAL bool operator()(const vdf_entry& a, const vdf_entry& b) const; - PHOENIX_INTERNAL bool operator()(const vdf_entry& a, std::string_view b) const; - PHOENIX_INTERNAL bool operator()(std::string_view a, const vdf_entry& b) const; - }; - - /// \brief Represents an entry of a VDF. - class vdf_entry { - public: - /// \brief Creates a new directory entry with the given name and attributes. - /// \param name The name of the entry to create. - /// \param attributes Attributes to set on the entry. - PHOENIX_API explicit vdf_entry(std::string_view name, std::uint32_t attributes = 0); - - PHOENIX_API vdf_entry() = default; - - /// \brief Reads a vdf_entry and all it's children from the given buffer. - /// \param in The buffer to read from. - /// \param catalog_offset The offset of the entry catalog. - /// \return The entry read. - PHOENIX_INTERNAL static vdf_entry read(buffer& in, std::uint32_t catalog_offset); - - /// \brief Searches the entry for the first child with the given name. - /// \param name The name of the child to search for. - /// \return The child with the given name or `nullptr` if no entry was found. - [[nodiscard]] PHOENIX_API const vdf_entry* find_child(std::string_view name) const; - - /// \brief Resolves the given path into the entry. - /// \param path The path to resolve. - /// \return The child at the given path or `nullptr` if no entry was found. - [[nodiscard]] PHOENIX_API const vdf_entry* resolve_path(std::string_view path) const; - - /// \brief Searches the entry for the first child with the given name. - /// \param name The name of the child to search for. - /// \return The child with the give name or `nullptr` if no entry was found. - PHOENIX_DEPRECATED("mutating vdf_entry children is broken!") - PHOENIX_API vdf_entry* find_child(std::string_view name); - - /// \brief Merges the given VDF entry into this one. - /// \param itm The entry to merge. - /// \param override_existing Whether to replace existing files with the new ones. - PHOENIX_API void merge(const vdf_entry& itm, bool override_existing = true); - - /// \return A new reader for the contents of the entry. - [[nodiscard]] PHOENIX_API inline buffer open() const noexcept { - return _m_data.duplicate(); - } - - /// \return `true` if the entry is a file entry, `false` if not. - [[nodiscard]] PHOENIX_API inline bool is_file() const noexcept { - return !is_directory(); - } - - /// \return `true` if the entry is a directory entry, `false` if not. - [[nodiscard]] PHOENIX_API inline bool is_directory() const noexcept { - return (type & VDF_MASK_DIRECTORY) != 0; - } - - /// \return `true` if the entry is the last entry in a directory, `false` if not. - [[nodiscard]] PHOENIX_API inline bool is_last() const noexcept { - return (type & VDF_MASK_LAST) != 0; - } - - static constexpr const auto packed_size = VDF_ENTRY_NAME_LENGTH + 4 * sizeof(std::uint32_t); - - public: - /// \brief The name of the entry. - std::string name; - - /// \brief A list of child entries of the entry. - std::set children {}; - - /// \brief The offset of the entry's data in the VDF. - std::uint32_t offset {0}; - - /// \brief The size of the entry in bytes. - std::uint32_t size {0}; - - /// \brief The type of the entry. - std::uint32_t type {0}; - - /// \brief The attributes of the file. - std::uint32_t attributes {0}; - - private: - buffer _m_data = buffer::empty(); - }; - - /// \brief Represents a VDF file (`.VDF` files). - class vdf_file { - public: - /// \brief Opens the file at the given \p path as a VDF file. - /// \param comment The comment on the file. Note that comments are trimmed to 256 characters. - /// \param timestamp The timestamp of the archive. - PHOENIX_DEPRECATED("scheduled for removal; use Vfs instead") - PHOENIX_API explicit vdf_file(std::string_view comment, std::time_t timestamp = -1); - - /// \brief Reads the header and catalog from a file and creates a vdf_file from it. - /// \param path The path of the file to read from. - /// \return The vdf_file. - PHOENIX_DEPRECATED("scheduled for removal; use Vfs instead") - PHOENIX_API static vdf_file open(const std::filesystem::path& path); - - /// \brief Reads the header and catalog from a buffer and creates a vdf_file from it. - /// \param path The buffer to read from. - /// \return The vdf_file. - PHOENIX_DEPRECATED("scheduled for removal; use Vfs instead") - PHOENIX_API static vdf_file open(phoenix::buffer& buf); - - /// \brief Searches the VDF file for the first entry with the given name. - /// \param name The name of the entry to search for. - /// \return The entry with the give name or `nullptr` if no entry was found. - [[nodiscard]] PHOENIX_API const vdf_entry* find_entry(std::string_view name) const; - - /// \brief Resolves the given path into the VDF. - /// \param path The path to resolve. - /// \return The child at the given path or `nullptr` if no entry was found. - [[nodiscard]] PHOENIX_API const vdf_entry* resolve_path(std::string_view path) const; - - /// \brief Searches the VDF file for the first entry with the given name. - /// \param name The name of the entry to search for. - /// \return The entry with the give name or `nullptr` if no entry was found. - PHOENIX_DEPRECATED("mutating vdf_entry children is broken!") - PHOENIX_API vdf_entry* find_entry(std::string_view name); - - /// \brief Merges the given VDF file into this one. - /// \param itm The file to merge. - /// \param override_existing Whether to replace existing files with the new ones. - PHOENIX_API void merge(const vdf_file& file, bool override_existing = true); - - public: - /// \brief A list of root entries in the VDF file. - std::set entries; - - /// \brief The header data of the VDF file. - vdf_header header; - - protected: - vdf_file() = default; - }; - -} // namespace phoenix diff --git a/include/phoenix/vm.hh b/include/phoenix/vm.hh new file mode 100644 index 00000000..3c987827 --- /dev/null +++ b/include/phoenix/vm.hh @@ -0,0 +1,31 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/DaedalusVm.hh" + +#include "phoenix/script.hh" + +namespace phoenix { + namespace execution_flag = zenkit::DaedalusVmExecutionFlag; + + using illegal_external_definition + ZKREM("renamed to zenkit::DaedalusIllegalExternalDefinition") = zenkit::DaedalusIllegalExternalDefinition; + using illegal_external_rtype + ZKREM("renamed to zenkit::DaedalusIllegalExternalReturnType") = zenkit::DaedalusIllegalExternalReturnType; + using illegal_external_param + ZKREM("renamed to zenkit::DaedalusIllegalExternalParameter") = zenkit::DaedalusIllegalExternalParameter; + using vm_exception ZKREM("renamed to zenkit::DaedalusVmException") = zenkit::DaedalusVmException; + using vm_exception_strategy + ZKREM("renamed to zenkit::DaedalusVmExceptionStrategy") = zenkit::DaedalusVmExceptionStrategy; + using daedalus_stack_frame ZKREM("renamed to zenkit::DaedalusStackFrame") = zenkit::DaedalusStackFrame; + using daedalus_call_stack_frame ZKREM("renamed to zenkit::DaedalusCallStackFrame") = zenkit::DaedalusCallStackFrame; + using vm ZKREM("renamed to zenkit::DaedalusVm") = zenkit::DaedalusVm; + using func ZKREM("renamed to zenkit::DaedalusFunction") = zenkit::DaedalusFunction; + using naked_call ZKREM("renamed to zenkit::DaedalusNakedCall") = zenkit::DaedalusNakedCall; + + inline zenkit::DaedalusVmExceptionStrategy lenient_vm_exception_handler(zenkit::DaedalusVm& v, + zenkit::DaedalusScriptError const& exc, + zenkit::DaedalusInstruction const& instr) { + return zenkit::lenient_vm_exception_handler(v, exc, instr); + } +} // namespace phoenix diff --git a/include/phoenix/vobs/camera.hh b/include/phoenix/vobs/camera.hh new file mode 100644 index 00000000..5f3eff72 --- /dev/null +++ b/include/phoenix/vobs/camera.hh @@ -0,0 +1,17 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/vobs/Camera.hh" + +namespace phoenix { + namespace vobs { + using camera_trj_frame + ZKREM("renamed to zenkit::vobs::CameraTrajectoryFrame") = zenkit::vobs::CameraTrajectoryFrame; + using cs_camera ZKREM("renamed to zenkit::vobs::CutsceneCamera") = zenkit::vobs::CutsceneCamera; + } // namespace vobs + + using camera_motion ZKREM("renamed to zenkit::CameraMotion") = zenkit::CameraMotion; + using camera_trajectory ZKREM("renamed to zenkit::CameraTrajectory") = zenkit::CameraTrajectory; + using camera_lerp_mode ZKREM("renamed to zenkit::CameraLerpType") = zenkit::CameraLerpType; + using camera_loop ZKREM("renamed to zenkit::CameraLoop") = zenkit::CameraLoop; +} // namespace phoenix diff --git a/include/phoenix/vobs/light.hh b/include/phoenix/vobs/light.hh new file mode 100644 index 00000000..4b70716a --- /dev/null +++ b/include/phoenix/vobs/light.hh @@ -0,0 +1,14 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/vobs/Light.hh" + +namespace phoenix { + namespace vobs { + using light_preset ZKREM("renamed to zenkit::vobs::LightPreset") = zenkit::vobs::LightPreset; + using light ZKREM("renamed to zenkit::vobs::Light") = zenkit::vobs::Light; + } // namespace vobs + + using light_mode ZKREM("renamed to zenkit::LightType") = zenkit::LightType; + using light_quality ZKREM("renamed to zenkit::LightQuality") = zenkit::LightQuality; +} // namespace phoenix diff --git a/include/phoenix/vobs/misc.hh b/include/phoenix/vobs/misc.hh new file mode 100644 index 00000000..8a8eb531 --- /dev/null +++ b/include/phoenix/vobs/misc.hh @@ -0,0 +1,24 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/vobs/Misc.hh" + +namespace phoenix { + namespace vobs { + using animate ZKREM("renamed to zenkit::vobs::Animate") = zenkit::vobs::Animate; + using item ZKREM("renamed to zenkit::vobs::Item") = zenkit::vobs::Item; + using lens_flare ZKREM("renamed to zenkit::vobs::LensFlare") = zenkit::vobs::LensFlare; + using pfx_controller + ZKREM("renamed to zenkit::vobs::ParticleEffectController") = zenkit::vobs::ParticleEffectController; + using code_master ZKREM("renamed to zenkit::vobs::CodeMaster") = zenkit::vobs::CodeMaster; + using message_filter ZKREM("renamed to zenkit::vobs::MessageFilter") = zenkit::vobs::MessageFilter; + using mover_controller ZKREM("renamed to zenkit::vobs::MoverController") = zenkit::vobs::MoverController; + using touch_damage ZKREM("renamed to zenkit::vobs::TouchDamage") = zenkit::vobs::TouchDamage; + using earthquake ZKREM("renamed to zenkit::vobs::Earthquake") = zenkit::vobs::Earthquake; + using npc ZKREM("renamed to zenkit::vobs::Npc") = zenkit::vobs::Npc; + } // namespace vobs + + using message_filter_action ZKREM("renamed to zenkit::MessageFilterAction") = zenkit::MessageFilterAction; + using mover_message_type ZKREM("renamed to zenkit::MoverMessageType") = zenkit::MoverMessageType; + using collision_type ZKREM("renamed to zenkit::TouchCollisionType") = zenkit::TouchCollisionType; +} // namespace phoenix diff --git a/include/phoenix/vobs/mob.hh b/include/phoenix/vobs/mob.hh new file mode 100644 index 00000000..4e9e1dd5 --- /dev/null +++ b/include/phoenix/vobs/mob.hh @@ -0,0 +1,16 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/vobs/MovableObject.hh" + +namespace phoenix { + namespace vobs { + using mob ZKREM("renamed to zenkit::vobs::MovableObject") = zenkit::vobs::MovableObject; + using mob_inter ZKREM("renamed to zenkit::vobs::InteractiveObject") = zenkit::vobs::InteractiveObject; + using mob_fire ZKREM("renamed to zenkit::vobs::Fire") = zenkit::vobs::Fire; + using mob_container ZKREM("renamed to zenkit::vobs::Container") = zenkit::vobs::Container; + using mob_door ZKREM("renamed to zenkit::vobs::Door") = zenkit::vobs::Door; + } // namespace vobs + + using sound_material ZKREM("renamed to zenkit::SoundMaterialType") = zenkit::SoundMaterialType; +} // namespace phoenix diff --git a/include/phoenix/vobs/sound.hh b/include/phoenix/vobs/sound.hh new file mode 100644 index 00000000..77d8f735 --- /dev/null +++ b/include/phoenix/vobs/sound.hh @@ -0,0 +1,14 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/vobs/Sound.hh" + +namespace phoenix { + namespace vobs { + using sound ZKREM("renamed to zenkit::vobs::Sound") = zenkit::vobs::Sound; + using sound_daytime ZKREM("renamed to zenkit::vobs::SoundDaytime") = zenkit::vobs::SoundDaytime; + } // namespace vobs + + using sound_mode ZKREM("renamed to zenkit::SoundMode") = zenkit::SoundMode; + using sound_trigger_volume ZKREM("renamed to SoundTriggerVolumeType") = zenkit::SoundTriggerVolumeType; +} // namespace phoenix diff --git a/include/phoenix/vobs/trigger.hh b/include/phoenix/vobs/trigger.hh new file mode 100644 index 00000000..4f495397 --- /dev/null +++ b/include/phoenix/vobs/trigger.hh @@ -0,0 +1,24 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/vobs/Trigger.hh" + +#include "phoenix/animation.hh" + +namespace phoenix { + namespace vobs { + using trigger ZKREM("renamed to zenkit::vobs::Trigger") = zenkit::vobs::Trigger; + using trigger_mover ZKREM("renamed to zenkit::vobs::Mover") = zenkit::vobs::Mover; + using trigger_list ZKREM("renamed to zenkit::vobs::TriggerList") = zenkit::vobs::TriggerList; + using trigger_script ZKREM("renamed to zenkit::vobs::TriggerScript") = zenkit::vobs::TriggerScript; + using trigger_change_level + ZKREM("renamed to zenkit::vobs::TriggerChangeLevel") = zenkit::vobs::TriggerChangeLevel; + using trigger_world_start ZKREM("renamed to zenkit::vobs::TriggerWorldStart") = zenkit::vobs::TriggerWorldStart; + using trigger_untouch ZKREM("renamed to zenkit::vobs::TriggerUntouch") = zenkit::vobs::TriggerUntouch; + } // namespace vobs + + using mover_behavior ZKREM("renamed to zenkit::MoverBehavior") = zenkit::MoverBehavior; + using mover_lerp_mode ZKREM("renamed to zenkit::MoverLerpType") = zenkit::MoverLerpType; + using mover_speed_mode ZKREM("renamed to zenkit::MoverSpeedType") = zenkit::MoverSpeedType; + using trigger_batch_mode ZKREM("renamed to zenkit::TriggerBatchMode") = zenkit::TriggerBatchMode; +} // namespace phoenix diff --git a/include/phoenix/vobs/vob.hh b/include/phoenix/vobs/vob.hh new file mode 100644 index 00000000..47b6051c --- /dev/null +++ b/include/phoenix/vobs/vob.hh @@ -0,0 +1,14 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/vobs/VirtualObject.hh" + +namespace phoenix { + using vob_type ZKREM("renamed to zenkit::VobType") = zenkit::VobType; + using shadow_mode ZKREM("renamed to zenkit::ShadowType") = zenkit::ShadowType; + using visual_type ZKREM("renamed to zenkit::VisualType") = zenkit::VisualType; + using sprite_alignment ZKREM("renamed to zenkit::SpriteAlignment") = zenkit::SpriteAlignment; + using animation_mode ZKREM("renamed to zenkit::AnimationType") = zenkit::AnimationType; + using decal ZKREM("renamed to zenkit::Decal") = zenkit::Decal; + using vob ZKREM("renamed to zenkit::VirtualObject") = zenkit::VirtualObject; +} // namespace phoenix diff --git a/include/phoenix/vobs/zone.hh b/include/phoenix/vobs/zone.hh new file mode 100644 index 00000000..ead08c5f --- /dev/null +++ b/include/phoenix/vobs/zone.hh @@ -0,0 +1,10 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/vobs/Zone.hh" + +namespace phoenix::vobs { + using zone_music ZKREM("renamed to zenkit::vobs::ZoneMusic") = zenkit::vobs::ZoneMusic; + using zone_far_plane ZKREM("renamed to zenkit::vobs::ZoneFarPlane") = zenkit::vobs::ZoneFarPlane; + using zone_fog ZKREM("renamed to zenkit::vobs::ZoneFog") = zenkit::vobs::ZoneFog; +} // namespace phoenix::vobs diff --git a/include/phoenix/world.hh b/include/phoenix/world.hh new file mode 100644 index 00000000..020a8121 --- /dev/null +++ b/include/phoenix/world.hh @@ -0,0 +1,12 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/World.hh" + +#include "phoenix/world/bsp_tree.hh" +#include "phoenix/world/vob_tree.hh" +#include "phoenix/world/way_net.hh" + +namespace phoenix { + using world ZKREM("renamed to zenkit::World") = zenkit::World; +} diff --git a/include/phoenix/world/bsp_tree.hh b/include/phoenix/world/bsp_tree.hh new file mode 100644 index 00000000..adb32672 --- /dev/null +++ b/include/phoenix/world/bsp_tree.hh @@ -0,0 +1,11 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/world/BspTree.hh" + +namespace phoenix { + using bsp_tree ZKREM("renamed to zenkit::BspTree") = zenkit::BspTree; + using bsp_sector ZKREM("renamed to zenkit::BspSector") = zenkit::BspSector; + using bsp_node ZKREM("renamed to zenkit::BspNode") = zenkit::BspNode; + using bsp_tree_mode ZKREM("renamed to zenkit::BspTreeType") = zenkit::BspTreeType; +} // namespace phoenix diff --git a/include/phoenix/world/vob_tree.hh b/include/phoenix/world/vob_tree.hh new file mode 100644 index 00000000..543475a3 --- /dev/null +++ b/include/phoenix/world/vob_tree.hh @@ -0,0 +1,13 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/world/VobTree.hh" + +#include "phoenix/vobs/vob.hh" + +namespace phoenix { + ZKAPI inline std::unique_ptr parse_vob_tree(zenkit::ReadArchive& in, + zenkit::GameVersion version) { + return zenkit::parse_vob_tree(in, version); + } +} // namespace phoenix diff --git a/include/phoenix/world/way_net.hh b/include/phoenix/world/way_net.hh new file mode 100644 index 00000000..5ef5bb81 --- /dev/null +++ b/include/phoenix/world/way_net.hh @@ -0,0 +1,10 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/world/WayNet.hh" + +namespace phoenix { + using way_net ZKREM("renamed to zenkit::WayNet") = zenkit::WayNet; + using way_edge ZKREM("renamed to zenkit::WayEdge") = zenkit::WayEdge; + using way_point ZKREM("renamed to zenkit::WayPoint") = zenkit::WayPoint; +} // namespace phoenix diff --git a/include/zenkit/Archive.hh b/include/zenkit/Archive.hh index 144e5a9c..a35a0846 100644 --- a/include/zenkit/Archive.hh +++ b/include/zenkit/Archive.hh @@ -1,10 +1,11 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "phoenix/Api.hh" -#include -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Library.hh" +#include "zenkit/Stream.hh" + +#include "phoenix/buffer.hh" #include #include @@ -17,17 +18,31 @@ #include namespace phoenix { - enum class archive_format { binary, binsafe, ascii }; + class buffer; +} + +namespace zenkit { + class Read; + + enum class ArchiveFormat { + BINARY = 0, + BINSAFE = 1, + ASCII = 2, + + binary ZKREM("renamed to ArchiveFormat::BINARY") = BINARY, + binsafe ZKREM("renamed to ArchiveFormat::BINSAFE") = BINSAFE, + ascii ZKREM("renamed to ArchiveFormat::ASCII") = ASCII, + }; /// \brief Represents the header of a ZenGin archive. - struct archive_header { + struct ArchiveHeader { int32_t version; /// \brief The type of archiver used to create the archive. Either `zCArchiverGeneric` or `zCArchiverBinSafe`. std::string archiver; /// \brief The format of the archive. - archive_format format; + ArchiveFormat format; /// \brief Whether the archive contains a save-game or not. bool save {false}; @@ -38,15 +53,11 @@ namespace phoenix { /// \brief The date this archive was created at. std::string date; - /// \brief Reads in an archive_header from the given reader. - /// \param in The reader to read from. - /// \return The header read. - /// \throws parser_error if parsing fails. - [[nodiscard]] PHOENIX_INTERNAL static archive_header parse(buffer& in); + void load(Read* r); }; /// \brief Represents the header of an object stored in a ZenGin archive. - struct archive_object { + struct ArchiveObject { /// \brief The name of the sub-object used for storing this object in the ZenGin. std::string object_name; @@ -61,49 +72,50 @@ namespace phoenix { std::uint32_t index; }; - enum class archive_entry_type : uint8_t { - string = 0x1, - int_ = 0x2, - float_ = 0x3, - byte = 0x4, - word = 0x5, - bool_ = 0x6, - vec3 = 0x7, - color = 0x8, - raw = 0x9, - raw_float = 0x10, - enum_ = 0x11, - hash = 0x12, - }; - - struct archive_entry { - archive_entry_type type; - std::string name; - std::variant value; + enum class ArchiveEntryType : uint8_t { + STRING = 0x1, + INTEGER = 0x2, + FLOAT = 0x3, + BYTE = 0x4, + WORD = 0x5, + BOOL = 0x6, + VEC3 = 0x7, + COLOR = 0x8, + RAW = 0x9, + RAW_FLOAT = 0x10, + ENUM = 0x11, + HASH = 0x12, + + string ZKREM("renamed to ArchiveEntryType::STRING") = STRING, + int_ ZKREM("renamed to ArchiveEntryType::INTEGER") = INTEGER, + float_ ZKREM("renamed to ArchiveEntryType::FLOAT") = FLOAT, + byte ZKREM("renamed to ArchiveEntryType::BYTE") = BYTE, + word ZKREM("renamed to ArchiveEntryType::WORD") = WORD, + bool_ ZKREM("renamed to ArchiveEntryType::BOOL") = BOOL, + vec3 ZKREM("renamed to ArchiveEntryType::VEC3") = VEC3, + color ZKREM("renamed to ArchiveEntryType::COLOR") = COLOR, + raw ZKREM("renamed to ArchiveEntryType::RAW") = RAW, + raw_float ZKREM("renamed to ArchiveEntryType::RAW_FLOAT") = RAW_FLOAT, + enum_ ZKREM("renamed to ArchiveEntryType::ENUM") = ENUM, + hash ZKREM("renamed to ArchiveEntryType::HASH") = HASH, }; - struct archive_object_end {}; - - using archive_visitor = - std::function&, const std::optional&)>; - /// \brief A reader for ZenGin archives. - class PHOENIX_API archive_reader { + class ZKAPI ReadArchive { public: - /// \brief Constructs a new archive_reader from the given reader and header. - /// \note This constructor should never be called explicitly. Use #open instead! - /// \param in The reader to read from - /// \param header The header of the archive. - inline archive_reader(buffer& in, archive_header&& parent_header) - : header(std::move(parent_header)), input(in) {} + virtual ~ReadArchive() = default; - virtual ~archive_reader() = default; + /// \brief Creates a new archive_reader from the given buffer. + /// \param[in] in The buffer to use. + /// \return The new archive_reader. + /// \throws zenkit::ParserError + ZKREM("use ::from(Read*)") static std::unique_ptr open(phoenix::buffer& in); /// \brief Creates a new archive_reader from the given buffer. - /// \param[in,out] in The buffer to use. + /// \param[in] r The buffer to use. /// \return The new archive_reader. - /// \throws phoenix::parser_error - static std::unique_ptr open(buffer& in); + /// \throws zenkit::ParserError + static std::unique_ptr from(Read* r); /// \brief Tries to read the begin of a new object from the archive. /// @@ -112,7 +124,7 @@ namespace phoenix { /// /// \param[out] obj The object to store the data in. /// \return `true` if the begin of an object was read successfully, `false` if not. - virtual bool read_object_begin(archive_object& obj) = 0; + virtual bool read_object_begin(ArchiveObject& obj) = 0; /// \brief Tries to read the end of an object from the archive. /// @@ -175,51 +187,27 @@ namespace phoenix { /// \brief Reads a bounding box consisting of two consecutive vec3's from the reader. /// \return The value read. /// \throws parser_error if the value actually present is not a bounding box - virtual bounding_box read_bbox() = 0; + virtual AxisAlignedBoundingBox read_bbox() = 0; /// \brief Reads a 3-by-3 matrix from the reader. /// \return The matrix. virtual glm::mat3x3 read_mat3x3() = 0; - /// \brief Reads a raw entry and returns the raw bytes stored within. - /// \return A vector containing the raw bytes of the entry. - /// \throws parser_error if the value actually present is not raw - PHOENIX_DEPRECATED("unsafe api: use read_raw_bytes(uint32_t) instead") virtual buffer read_raw_bytes() = 0; - /// \brief Reads a raw entry and returns the raw bytes stored within. /// \param size The number of bytes to read (checked at runtime for ASCII and BIN_SAFE archives) /// \return A vector containing the raw bytes of the entry. /// \throws parser_error if the value actually present is not raw - virtual buffer read_raw_bytes(uint32_t size) = 0; + ZKREM("use ::read_raw()") virtual phoenix::buffer read_raw_bytes(uint32_t size) = 0; + + virtual std::unique_ptr read_raw(uint32_t size) = 0; /// \brief Skips the next object in the reader and all it's children /// \param skip_current If `false` skips the next object in this buffer, otherwise skip the object /// currently being read. virtual void skip_object(bool skip_current); - /// \brief Dumps the current or next object of this reader as XML to standard out. - /// \param open_object If `false`, dumps out the next object in the reader, otherwise dumps all - /// values until the current object closes. - virtual void print_structure(bool open_object); - - /// \brief Get the next element in the archive. - /// - /// Parses the next element, either an object begin, object end or entry from the archive and returns - /// its associated value. - /// - /// \return The value of the next element in the archive. - /// \warning This API is unstable and may change at any time! - virtual std::variant unstable__next() = 0; - - /// \brief Walks the current or next object of the archive recursively, calling `cb` for each element. - /// \param open_object If `false`, walks out the next object in the reader, otherwise walks all - /// values until the current object closes. - /// \param cb A callback function to handle each element. - /// \warning This API is unstable and may change at any time! - void unstable__visit_objects(bool open_object, const archive_visitor& cb); - /// \return The header of the archive - [[nodiscard]] const archive_header& get_header() const noexcept { + [[nodiscard]] const ArchiveHeader& get_header() const noexcept { return header; } @@ -229,6 +217,9 @@ namespace phoenix { } protected: + ReadArchive(ArchiveHeader head, Read* read); + ReadArchive(ArchiveHeader head, Read* read, std::unique_ptr owned); + /// \brief Read the header of the specific archive format. virtual void read_header() = 0; @@ -236,7 +227,10 @@ namespace phoenix { virtual void skip_entry() = 0; protected: - archive_header header; - buffer& input; + ArchiveHeader header; + Read* read; + + private: + std::unique_ptr _m_owned; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/Boxes.hh b/include/zenkit/Boxes.hh index 4ffc9a24..dd4ec420 100644 --- a/include/zenkit/Boxes.hh +++ b/include/zenkit/Boxes.hh @@ -1,15 +1,21 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once +#include "zenkit/Library.hh" + #include #include namespace phoenix { class buffer; +} + +namespace zenkit { + class Read; /// \brief Represents a axis-aligned bounding box (AABB) - struct bounding_box { + struct AxisAlignedBoundingBox { /// \brief The coordinates of the minimum corner of the bounding box. glm::vec3 min; @@ -19,7 +25,9 @@ namespace phoenix { /// \brief Parses a bounding box from the given buffer. /// \param[in,out] in The buffer to parse from. /// \return The bounding box parsed. - PHOENIX_API static bounding_box parse(buffer& in); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static AxisAlignedBoundingBox parse(phoenix::buffer& in); + + ZKAPI void load(Read* r); }; /// \brief Represents an oriented bounding box. @@ -27,21 +35,23 @@ namespace phoenix { /// In contrast to regular bounding boxes, [oriented bounding /// boxes](https://en.wikipedia.org/wiki/Minimum_bounding_box#Arbitrarily_oriented_minimum_bounding_box) may be /// rotated in the coordinate system and don't have to align with its axes. - struct obb { + struct OrientedBoundingBox { glm::vec3 center; glm::vec3 axes[3]; glm::vec3 half_width; - std::vector children; + std::vector children; + + ZKAPI void load(Read* r); /// \brief Calculates an axis-aligned bounding box from this oriented bounding box. /// \todo Write a test for this. /// \return An AABB which contains this OBB. - [[nodiscard]] PHOENIX_API bounding_box as_bbox() const; + [[nodiscard]] ZKAPI AxisAlignedBoundingBox as_bbox() const; /// \brief Parses an oriented bounding box from a buffer. /// \param[in,out] in The buffer to parse from. /// \return The parsed bounding box. - [[nodiscard]] PHOENIX_API static obb parse(buffer& in); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static OrientedBoundingBox parse(phoenix::buffer& in); }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/CutsceneLibrary.hh b/include/zenkit/CutsceneLibrary.hh index 37d3837e..8c593c77 100644 --- a/include/zenkit/CutsceneLibrary.hh +++ b/include/zenkit/CutsceneLibrary.hh @@ -1,16 +1,22 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include +#include "zenkit/Library.hh" +#include #include #include #include namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + /// \brief A single cutscene message. - struct atomic_message { + struct CutsceneMessage { uint32_t type = 0; /// \brief The text associated with the message. @@ -21,7 +27,7 @@ namespace phoenix { }; /// \brief A block containing a cutscene message and a unique name. - struct message_block { + struct CutsceneBlock { /// \brief The unique name of the message. std::string name; @@ -29,7 +35,7 @@ namespace phoenix { /// \details It seems like it was possible to specify multiple atomic_message object for each message_block. /// This seems to have been abandoned, however, so this implementation only supports one atomic_message /// per message block. - atomic_message message; + CutsceneMessage message; }; /// \brief Represents a cutscene library. @@ -40,7 +46,7 @@ namespace phoenix { /// of Gothic and Gothic II installations. Cutscene libraries are *ZenGin* archives in either binary or ASCII /// format. They have either the `.DAT` or `.BIN` extension for binary files or the `.CSL` or `.LSC` extension for /// text files

- class messages { + class CutsceneLibrary { public: /// \brief Parses a message database from the data in the given buffer. /// @@ -54,24 +60,24 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static messages parse(buffer& path); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static CutsceneLibrary parse(phoenix::buffer& path); /// \brief Parses a message database from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed message database object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static messages parse(buffer&& path) { - return messages::parse(path); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static CutsceneLibrary parse(phoenix::buffer&& path); /// \brief Retrieves a message block by it's name. /// \param name The name of the block to get /// \return A pointer to the block or `nullptr` if the block was not found. - [[nodiscard]] PHOENIX_API const message_block* block_by_name(std::string_view name) const; + [[nodiscard]] ZKAPI const CutsceneBlock* block_by_name(std::string_view name) const; + + ZKAPI void load(Read* r); public: /// \brief A list of all message blocks in the database. - std::vector blocks {}; + std::vector blocks {}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/DaedalusScript.hh b/include/zenkit/DaedalusScript.hh index bee2bbc3..69cf54d7 100644 --- a/include/zenkit/DaedalusScript.hh +++ b/include/zenkit/DaedalusScript.hh @@ -1,30 +1,48 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include +#include "zenkit/Error.hh" +#include "zenkit/Library.hh" +#include "zenkit/Stream.hh" +#include "phoenix/buffer.hh" + +#include #include #include -#include #include +#include #include #include #include -#define unset 0xFF'FF'FF'FFU - namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + /// \brief Daedalus data types. - enum class datatype : std::uint32_t { - void_ = 0U, ///< A datatype similar to C++'s `void`. - float_ = 1U, ///< A 32-bit floating point type similar to C++'s `float`. - integer = 2U, ///< A 32-bit signed integer type like std::int32_t - string = 3U, ///< A [Windows-1252](https://en.wikipedia.org/wiki/Windows-1252) encoded character array. - class_ = 4U, ///< A structure definition. - function = 5U, ///< A function definition or a function pointer represented as a 32-bit signed integer. - prototype = 6U, ///< A prototype definition. - instance = 7U, ///< An instance definition or instance reference represented as a 32-bit unsigned integer. + enum class DaedalusDataType : std::uint32_t { + VOID = 0U, ///< A datatype similar to C++'s `void`. + FLOAT = 1U, ///< A 32-bit floating point type similar to C++'s `float`. + INT = 2U, ///< A 32-bit signed integer type like std::int32_t + STRING = 3U, ///< A [Windows-1252](https://en.wikipedia.org/wiki/Windows-1252) encoded character array. + CLASS = 4U, ///< A structure definition. + FUNCTION = 5U, ///< A function definition or a function pointer represented as a 32-bit signed integer. + PROTOTYPE = 6U, ///< A prototype definition. + INSTANCE = 7U, ///< An instance definition or instance reference represented as a 32-bit unsigned integer. + + // Deprecated entries. + void_ ZKREM("renamed to DaedalusDataType::VOID") = VOID, + float_ ZKREM("renamed to DaedalusDataType::FLOAT") = FLOAT, + integer ZKREM("renamed to DaedalusDataType::INT") = INT, + string ZKREM("renamed to DaedalusDataType::STRING") = STRING, + class_ ZKREM("renamed to DaedalusDataType::CLASS") = CLASS, + function ZKREM("renamed to DaedalusDataType::FUNCTION") = FUNCTION, + prototype ZKREM("renamed to DaedalusDataType::PROTOTYPE") = PROTOTYPE, + instance ZKREM("renamed to DaedalusDataType::INSTANCE") = INSTANCE, }; constexpr const char* const DAEDALUS_DATA_TYPE_NAMES[] = { @@ -39,14 +57,22 @@ namespace phoenix { }; /// \brief Flags of symbols. - namespace symbol_flag { - static constexpr auto const_ = 1U << 0U; ///< The symbol is not mutable. - static constexpr auto return_ = 1U << 1U; ///< The symbol is a function and has a return value. - static constexpr auto member = 1U << 2U; ///< The symbol is a class member. - static constexpr auto external = 1U << 3U; ///< The symbol refers to an external function. - static constexpr auto merged = 1U << 4U; ///< Unused. - static constexpr auto access_trap = 1U << 6U; ///< VM should call trap callback, when symbol accessed. - } // namespace symbol_flag + namespace DaedalusSymbolFlag { + static constexpr auto CONST = 1U << 0U; ///< The symbol is not mutable. + static constexpr auto RETURN = 1U << 1U; ///< The symbol is a function and has a return value. + static constexpr auto MEMBER = 1U << 2U; ///< The symbol is a class member. + static constexpr auto EXTERNAL = 1U << 3U; ///< The symbol refers to an external function. + static constexpr auto MERGED = 1U << 4U; ///< Unused. + static constexpr auto TRAP_ACCESS = 1U << 6U; ///< VM should call trap callback, when symbol accessed. + + // Deprecated entries. + ZKREM("renamed to DaedalusSymbolFlag::CONST") static constexpr auto const_ = CONST; + ZKREM("renamed to DaedalusSymbolFlag::RETURN") static constexpr auto return_ = RETURN; + ZKREM("renamed to DaedalusSymbolFlag::MEMBER") static constexpr auto member = MEMBER; + ZKREM("renamed to DaedalusSymbolFlag::EXTERNAL") static constexpr auto external = EXTERNAL; + ZKREM("renamed to DaedalusSymbolFlag::MERGED") static constexpr auto merged = MERGED; + ZKREM("renamed to DaedalusSymbolFlag::TRAP_ACCESS") static constexpr auto access_trap = TRAP_ACCESS; + } // namespace DaedalusSymbolFlag /// \brief All opcodes supported by the daedalus interpreter. /// @@ -59,158 +85,201 @@ namespace phoenix { ///
  • `x` refers to the first value on the stack and is required to be variable reference.
  • ///
  • `y` refers to the second value on the stack and is required to be variable reference.
  • /// - enum class opcode : std::uint8_t { + enum class DaedalusOpcode : std::uint8_t { /// \brief Add `a` and `b` and put the result back onto the stack. - add = 0, + ADD = 0, /// \brief Subtract `b` from `a` and put the result back onto the stack. - sub = 1, + SUB = 1, /// \brief Multiply `a` and `b` and put the result back onto the stack. - mul = 2, + MUL = 2, /// \brief Divide `a` by `b` and put the result back onto the stack. - div = 3, + DIV = 3, /// \brief Divide `a` by `b` and put the remainder back onto the stack. - mod = 4, + MOD = 4, /// \brief Compute the bitwise or of `a` and `b` and put the result back onto the stack. - or_ = 5, + OR = 5, /// \brief Compute the bitwise and of `a` and `b` and put the result back onto the stack. - andb = 6, ///< a & b + ANDB = 6, ///< a & b /// \brief Test if `a` is less than `b` and put `1` or `0` onto the stack if /// the test is true or false respectively. - lt = 7, + LT = 7, /// \brief Test if `a` is greater than `b` and put `1` or `0` onto the stack /// if the test is true or false respectively. - gt = 8, + GT = 8, /// \brief Write `b` to `x` as an integer. - movi = 9, + MOVI = 9, /// \brief Test if `a == 1` or `b == 1` and put `1` or `0` onto the stack if /// the test is true or false respectively. - orr = 11, + ORR = 11, /// \brief Test if `a == 1` and `b == 1` and put `1` or `0` onto the stack if /// the test is true or false respectively. - and_ = 12, + AND = 12, /// \brief Left shift `a` by `b` bits and put the result back onto the stack. - lsl = 13, + LSL = 13, /// \brief Right shift `a` by `b` bits and put the result back onto the stack. - lsr = 14, + LSR = 14, /// \brief Test if `a` is less than or equal to `b` and put `1` or `0` onto the /// stack if the test is true or false respectively. - lte = 15, + LTE = 15, /// \brief Test if `a` is equal to `b` and put `1` or `0` onto the /// stack if the test is true or false respectively. - eq = 16, + EQ = 16, /// \brief Test if `a` is not equal to `b` and put `1` or `0` onto the /// stack if the test is true or false respectively. - neq = 17, + NEQ = 17, /// \brief Test if `a` is greater than or equal to `b` and put `1` or `0` onto the /// stack if the test is true or false respectively. - gte = 18, + GTE = 18, /// \brief Add `x` and `b` and assign the result back to `x`. /// \note `x` must be a reference to an integer. - addmovi = 19, + ADDMOVI = 19, /// \brief Subtract `b` from `x` and assign the result back to `x`. /// \note `x` must be a reference to an integer. - submovi = 20, + SUBMOVI = 20, /// \brief Multiply `x` from `b` and assign the result back to `x`. /// \note `x` must be a reference to an integer. - mulmovi = 21, + MULMOVI = 21, /// \brief Divide `x` by `b` and assign the result back to `x`. /// \note `x` must be a reference to an integer. - divmovi = 22, + DIVMOVI = 22, /// \brief Compute `+a` and put the result back onto the stack. - plus = 30, + PLUS = 30, /// \brief Compute `-a` and put the result back onto the stack. - negate = 31, + NEGATE = 31, /// \brief Compute `!a` and put the result back onto the stack. - not_ = 32, + NOT = 32, /// \brief Compute the bitwise complement `a` and put the result back onto the stack. - cmpl = 33, + CMPL = 33, /// \brief Do nothing. - nop = 45, + NOP = 45, /// \brief Return from the currently running function - rsr = 60, + RSR = 60, /// \brief Call the function at the address provided in the instruction. - bl = 61, + BL = 61, /// \brief Call the external function at the symbol index provided in the instruction. - be = 62, + BE = 62, /// \brief Push the immediate value provided in the instruction onto the stack as an integer. - pushi = 64, + PUSHI = 64, /// \brief Push the symbol with the index provided in the instruction onto the stack as a reference. - pushv = 65, + PUSHV = 65, /// \brief Push the instance with the symbol index provided in the instruction onto the stack as a reference. - pushvi = 67, + PUSHVI = 67, /// \brief Write `m` to `x` as a string. - movs = 70, + MOVS = 70, /// \brief Write `m` to `x` as a string reference; not implemented. - movss = 71, + MOVSS = 71, /// \brief Write `b` to `x` as a function reference. - movvf = 72, + MOVVF = 72, /// \brief Write `b` to `x` as a floating point number. - movf = 73, + MOVF = 73, /// \brief Write `y` to `x` as an instance reference. - movvi = 74, + MOVVI = 74, /// \brief Immediately jump to the instruction at the address provided in the instruction. - b = 75, + B = 75, /// \brief Jump to the instruction at the address provided in the instruction if `a == 0`. - bz = 76, + BZ = 76, /// \brief Set the global instance reference to the instance with the symbol index provided in the instrucion. - gmovi = 80, + GMOVI = 80, /// \brief Push the element at the given index of the symbol with the index provided in the /// instruction onto the stack as a reference. - pushvv = 245, + PUSHVV = 245, + + add ZKREM("renamed to DaedalusDataType::ADD") = ADD, + sub ZKREM("renamed to DaedalusDataType::SUB") = SUB, + mul ZKREM("renamed to DaedalusDataType::MUL") = MUL, + div ZKREM("renamed to DaedalusDataType::DIV") = DIV, + mod ZKREM("renamed to DaedalusDataType::MOD") = MOD, + or_ ZKREM("renamed to DaedalusDataType::OR") = OR, + andb ZKREM("renamed to DaedalusDataType::ANDB") = ANDB, + lt ZKREM("renamed to DaedalusDataType::LT") = LT, + gt ZKREM("renamed to DaedalusDataType::GT") = GT, + movi ZKREM("renamed to DaedalusDataType::MOVI") = MOVI, + orr ZKREM("renamed to DaedalusDataType::ORR") = ORR, + and_ ZKREM("renamed to DaedalusDataType::ADD") = AND, + lsl ZKREM("renamed to DaedalusDataType::LSL") = LSL, + lsr ZKREM("renamed to DaedalusDataType::LSR") = LSR, + lte ZKREM("renamed to DaedalusDataType::LTE") = LTE, + eq ZKREM("renamed to DaedalusDataType::EQ") = EQ, + neq ZKREM("renamed to DaedalusDataType::NEQ") = NEQ, + gte ZKREM("renamed to DaedalusDataType::GTE") = GTE, + addmovi ZKREM("renamed to DaedalusDataType::ADDMOVI") = ADDMOVI, + submovi ZKREM("renamed to DaedalusDataType::SUBMOVI") = SUBMOVI, + mulmovi ZKREM("renamed to DaedalusDataType::MULMOVI") = MULMOVI, + divmovi ZKREM("renamed to DaedalusDataType::DIVMOVI") = DIVMOVI, + plus ZKREM("renamed to DaedalusDataType::PLUS") = PLUS, + negate ZKREM("renamed to DaedalusDataType::NEGATE") = NEGATE, + not_ ZKREM("renamed to DaedalusDataType::NOT") = NOT, + cmpl ZKREM("renamed to DaedalusDataType::CMPL") = CMPL, + nop ZKREM("renamed to DaedalusDataType::NOP") = NOP, + rsr ZKREM("renamed to DaedalusDataType::RSR") = RSR, + bl ZKREM("renamed to DaedalusDataType::BL") = BL, + be ZKREM("renamed to DaedalusDataType::BE") = BE, + pushi ZKREM("renamed to DaedalusDataType::PUSHI") = PUSHI, + pushv ZKREM("renamed to DaedalusDataType::PUSHV") = PUSHV, + pushvi ZKREM("renamed to DaedalusDataType::PUSHVI") = PUSHVI, + movs ZKREM("renamed to DaedalusDataType::MOVS") = MOVS, + movss ZKREM("renamed to DaedalusDataType::MOVSS") = MOVSS, + movvf ZKREM("renamed to DaedalusDataType::MOVVF") = MOVVF, + movf ZKREM("renamed to DaedalusDataType::MOVF") = MOVF, + movvi ZKREM("renamed to DaedalusDataType::MOVVI") = MOVVI, + b ZKREM("renamed to DaedalusDataType::B") = B, + bz ZKREM("renamed to DaedalusDataType::BZ") = BZ, + gmovi ZKREM("renamed to DaedalusDataType::GMOVI") = GMOVI, + pushvv ZKREM("renamed to DaedalusDataType::PUSHVV") = PUSHVV, }; - class symbol; + class DaedalusSymbol; /// \brief Represents an object associated with an instance in the script. /// /// Every class defined in C++ that can be used as an instance has to inherit from this class. - class instance { + class ZKAPI DaedalusInstance { public: - PHOENIX_API virtual ~instance() = default; + virtual ~DaedalusInstance() = default; /// \return The index of the symbol this instance is bound to. - [[nodiscard]] PHOENIX_API inline uint32_t symbol_index() const { + [[nodiscard]] uint32_t symbol_index() const { return _m_symbol_index; } @@ -218,46 +287,46 @@ namespace phoenix { void* user_ptr = nullptr; protected: - PHOENIX_INTERNAL virtual std::uint8_t* data() { + virtual std::uint8_t* data() { return reinterpret_cast(this); } - PHOENIX_INTERNAL virtual const std::uint8_t* data() const { + [[nodiscard]] virtual const std::uint8_t* data() const { return reinterpret_cast(this); } private: - friend class transient_instance; - friend class symbol; - friend class script; - friend class vm; + friend class DaedalusTransientInstance; + friend class DaedalusSymbol; + friend class DaedalusScript; + friend class DaedalusVm; - uint32_t _m_symbol_index {unset}; + uint32_t _m_symbol_index {static_cast(-1)}; const std::type_info* _m_type {nullptr}; }; /// \brief Represents an object associated with an instance in the script. /// /// Instances allocated with init_opaque will be backed up by this class with plain memory storage - class opaque_instance final : public instance { + class DaedalusOpaqueInstance final : public DaedalusInstance { public: - PHOENIX_INTERNAL opaque_instance(symbol const& sym, std::vector const& members); - PHOENIX_INTERNAL ~opaque_instance(); + ZKAPI DaedalusOpaqueInstance(DaedalusSymbol const& sym, std::vector const& members); + ZKAPI ~DaedalusOpaqueInstance(); protected: friend class symbol; - PHOENIX_INTERNAL std::uint8_t* data() override { + ZKAPI std::uint8_t* data() override { return _m_storage.get(); } - PHOENIX_INTERNAL const std::uint8_t* data() const override { + [[nodiscard]] ZKAPI std::uint8_t const* data() const override { return _m_storage.get(); } private: template - PHOENIX_INTERNAL T* construct_at(size_t offset, Args&&... args); + ZKINT T* construct_at(size_t offset, Args&&... args); std::unique_ptr _m_storage; std::vector _m_strings; @@ -266,192 +335,198 @@ namespace phoenix { /// \brief Represents object instance in the script with no defined backing to memory. /// /// Expected to be used for DMA mods or to emulate variable-like access to engine-functions. - class transient_instance : public instance { + class ZKAPI DaedalusTransientInstance : public DaedalusInstance { public: - transient_instance(); - ~transient_instance(); + DaedalusTransientInstance(); + ~DaedalusTransientInstance(); protected: - friend class symbol; + friend class DaedalusSymbol; - virtual void set_int(symbol const& sym, uint16_t index, std::int32_t value) = 0; - virtual std::int32_t get_int(symbol const& sym, uint16_t index) = 0; + virtual void set_int(DaedalusSymbol const& sym, uint16_t index, std::int32_t value) = 0; + virtual std::int32_t get_int(DaedalusSymbol const& sym, uint16_t index) = 0; - virtual void set_float(symbol const& sym, uint16_t index, float value) = 0; - virtual float get_float(symbol const& sym, uint16_t index) = 0; + virtual void set_float(DaedalusSymbol const& sym, uint16_t index, float value) = 0; + virtual float get_float(DaedalusSymbol const& sym, uint16_t index) = 0; - virtual void set_string(symbol const& sym, uint16_t index, std::string_view value) = 0; - virtual const std::string& get_string(symbol const& sym, uint16_t index) = 0; + virtual void set_string(DaedalusSymbol const& sym, uint16_t index, std::string_view value) = 0; + virtual const std::string& get_string(DaedalusSymbol const& sym, uint16_t index) = 0; }; /// \brief The base class for all exceptions thrown by interacting with a script. - struct script_error : public error { - using error::error; + struct DaedalusScriptError : public Error { + using Error::Error; }; /// \brief An exception thrown if the symbol with a given name could not be found. - struct symbol_not_found : public script_error { + struct DaedalusSymbolNotFound : public DaedalusScriptError { public: - PHOENIX_API explicit symbol_not_found(std::string&& name); + ZKAPI explicit DaedalusSymbolNotFound(std::string&& name); public: std::string name; }; /// \brief An exception thrown if registering a class member was unsuccessful. - struct member_registration_error : public script_error { + struct DaedalusMemberRegistrationError : public DaedalusScriptError { public: - PHOENIX_API explicit member_registration_error(const symbol* sym, std::string&& message); + ZKAPI explicit DaedalusMemberRegistrationError(DaedalusSymbol const* sym, std::string&& message); public: /// \brief The symbol being registered. - const symbol* sym; + DaedalusSymbol const* sym; }; /// \brief An exception thrown if the type of the member being registered does not match the type provided. - struct invalid_registration_datatype final : public member_registration_error { + struct DaedalusInvalidRegistrationDataType final : public DaedalusMemberRegistrationError { public: - PHOENIX_API explicit invalid_registration_datatype(const symbol* sym, std::string&& given); + ZKAPI explicit DaedalusInvalidRegistrationDataType(const DaedalusSymbol* sym, std::string&& given); public: std::string given; }; /// \brief An exception thrown when the value of a symbol accessed in a way which is not permissible. - struct illegal_access : public script_error { - using script_error::script_error; + struct DaedalusIllegalAccess : public DaedalusScriptError { + using DaedalusScriptError::DaedalusScriptError; }; /// \brief An exception thrown when the type of a symbol does not match the type expected. - struct illegal_type_access final : public illegal_access { + struct DaedalusIllegalTypeAccess final : public DaedalusIllegalAccess { public: - PHOENIX_INTERNAL illegal_type_access(const symbol* sym, datatype expected); + ZKINT DaedalusIllegalTypeAccess(DaedalusSymbol const* sym, DaedalusDataType expected); public: /// \brief The symbol being accessed. - const symbol* sym; + DaedalusSymbol const* sym; /// \brief The datatype excepted. - datatype expected; + DaedalusDataType expected; }; /// \brief An exception thrown when an out-of-bounds index is accessed. - struct illegal_index_access final : public illegal_access { + struct DaedalusIllegalIndexAccess final : public DaedalusIllegalAccess { public: - PHOENIX_INTERNAL illegal_index_access(const symbol* sym, std::uint8_t index); + ZKINT DaedalusIllegalIndexAccess(DaedalusSymbol const* sym, std::uint8_t index); public: /// \brief The symbol being accessed. - const symbol* sym; + const DaedalusSymbol* sym; /// \brief The index being accessed std::uint8_t index; }; /// \brief An exception thrown when a constant symbol is accessed as mutable - struct illegal_const_access final : public illegal_access { + struct DaedalusIllegalConstAccess final : public DaedalusIllegalAccess { public: - PHOENIX_INTERNAL explicit illegal_const_access(const symbol* sym); + ZKINT explicit DaedalusIllegalConstAccess(DaedalusSymbol const* sym); public: /// \brief The symbol being accessed. - const symbol* sym; + DaedalusSymbol const* sym; }; /// \brief An exception thrown when the parent class of a member does not match the class of an instance. - struct illegal_instance_access final : public illegal_access { + struct DaedalusIllegalInstanceAccess final : public DaedalusIllegalAccess { public: - PHOENIX_INTERNAL illegal_instance_access(const symbol* sym, std::uint32_t expected_parent); + ZKINT DaedalusIllegalInstanceAccess(DaedalusSymbol const* sym, std::uint32_t expected_parent); public: /// \brief The symbol being accessed. - const symbol* sym; + DaedalusSymbol const* sym; /// \brief The parent which was expected for the member. std::uint32_t expected_parent; }; /// \brief An exception thrown when the parent class of a member does not match the class of an instance. - struct unbound_member_access final : public illegal_access { + struct DaedalusUnboundMemberAccess final : public DaedalusIllegalAccess { public: - PHOENIX_API explicit unbound_member_access(const symbol* sym); + ZKAPI explicit DaedalusUnboundMemberAccess(DaedalusSymbol const* sym); public: /// \brief The symbol being accessed. - const symbol* sym; + DaedalusSymbol const* sym; }; /// \brief An exception thrown if a member symbol is being access without a context set. - struct no_context final : public illegal_access { + struct DaedalusNoContextError final : public DaedalusIllegalAccess { public: - PHOENIX_INTERNAL explicit no_context(const symbol* sym); + ZKINT explicit DaedalusNoContextError(DaedalusSymbol const* sym); public: /// \brief The symbol being accessed. - const symbol* sym; + DaedalusSymbol const* sym; }; - /// \brief An excpetion thrown if a member symbol is being accessed with a context instance it is not bound to. - struct illegal_context_type final : public illegal_access { + /// \brief An exception thrown if a member symbol is being accessed with a context instance it is not bound to. + struct DaedalusIllegalContextType final : public DaedalusIllegalAccess { public: - PHOENIX_API illegal_context_type(const symbol* sym, const std::type_info& context_type); + ZKAPI DaedalusIllegalContextType(DaedalusSymbol const* sym, const std::type_info& context_type); public: /// \brief The symbol being accessed. - const symbol* sym; + DaedalusSymbol const* sym; /// \brief The type of context currently set. const std::type_info& context_type; }; /// \brief Represents a compiled daedalus symbol. - class symbol final { + class DaedalusSymbol final { public: + ZKINT DaedalusSymbol() = default; + /// \brief Parses a symbol from the given reader. /// \param[in,out] in The reader to read the symbol from. /// \return The symbol parsed. - [[nodiscard]] PHOENIX_API static symbol parse(buffer& in); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static DaedalusSymbol parse(phoenix::buffer& in); + ZKAPI void load(Read* in); /// \brief Validates that the symbol is a string and retrieves it's value in the given context. /// \param index The index of the value to get. /// \param context An instance to use as context for getting member variables. /// \return The string associated with the symbol. - /// \throws illegal_type_access if the #type of this symbol is not dt_string. - /// \throws illegal_index_access if \p index >= #count. - /// \throws no_context if this symbol #is_member and \p context is `nullptr`. - /// \throws unbound_member_access if this symbol has not been registered yet - /// \throws illegal_context_type if this symbol #is_registered_to a different type than the type of \p context. - [[nodiscard]] PHOENIX_API const std::string& - get_string(std::size_t index = 0, const std::shared_ptr& context = nullptr) const; + /// \throws DaedalusIllegalTypeAccess if the #type of this symbol is not dt_string. + /// \throws DaedalusIllegalIndexAccess if \p index >= #count. + /// \throws DaedalusNoContextError if this symbol #is_member and \p context is `nullptr`. + /// \throws DaedalusUnboundMemberAccess if this symbol has not been registered yet + /// \throws DaedalusIllegalContextType if this symbol #is_registered_to a different type than the type of \p + /// context. + [[nodiscard]] ZKAPI const std::string& + get_string(std::size_t index = 0, const std::shared_ptr& context = nullptr) const; /// \brief Validates that the symbol is a float and retrieves it's value in the given context. /// \param index The index of the value to get. /// \param context An instance to use as context for getting member variables. /// \return The float value associated with the symbol. - /// \throws illegal_type_access if the #type of this symbol is not dt_float. - /// \throws illegal_index_access if \p index >= #count. - /// \throws no_context if this symbol #is_member and \p context is `nullptr`. - /// \throws unbound_member_access if this symbol has not been registered yet - /// \throws illegal_context_type if this symbol #is_registered_to a different type than the type of \p context. - [[nodiscard]] PHOENIX_API float get_float(std::size_t index = 0, - const std::shared_ptr& context = nullptr) const; + /// \throws DaedalusIllegalTypeAccess if the #type of this symbol is not dt_float. + /// \throws DaedalusIllegalIndexAccess if \p index >= #count. + /// \throws DaedalusNoContextError if this symbol #is_member and \p context is `nullptr`. + /// \throws DaedalusUnboundMemberAccess if this symbol has not been registered yet + /// \throws DaedalusIllegalContextType if this symbol #is_registered_to a different type than the type of \p + /// context. + [[nodiscard]] ZKAPI float get_float(std::size_t index = 0, + const std::shared_ptr& context = nullptr) const; /// \brief Validates that the symbol is an int and retrieves it's value in the given context. /// \param index The index of the value to get. /// \param context An instance to use as context for getting member variables. /// \return The int value associated with the symbol. - /// \throws illegal_type_access if the #type of this symbol is not dt_int or dt_function. - /// \throws illegal_index_access if \p index >= #count. - /// \throws no_context if this symbol #is_member and \p context is `nullptr`. - /// \throws unbound_member_access if this symbol has not been registered yet - /// \throws illegal_context_type if this symbol #is_registered_to a different type than the type of \p context. - [[nodiscard]] PHOENIX_API std::int32_t get_int(std::size_t index = 0, - const std::shared_ptr& context = nullptr) const; + /// \throws DaedalusIllegalTypeAccess if the #type of this symbol is not dt_int or dt_function. + /// \throws DaedalusIllegalIndexAccess if \p index >= #count. + /// \throws DaedalusNoContextError if this symbol #is_member and \p context is `nullptr`. + /// \throws DaedalusUnboundMemberAccess if this symbol has not been registered yet + /// \throws DaedalusIllegalContextType if this symbol #is_registered_to a different type than the type of \p + /// context. + [[nodiscard]] ZKAPI std::int32_t get_int(std::size_t index = 0, + const std::shared_ptr& context = nullptr) const; /// \brief Validates that the symbol is an instance and retrieves it's value /// \return The instance associated with the symbol. - /// \throws illegal_type_access if the #type of this symbol is not dt_instance - [[nodiscard]] PHOENIX_API const std::shared_ptr& get_instance(); + /// \throws DaedalusIllegalTypeAccess if the #type of this symbol is not dt_instance + [[nodiscard]] ZKAPI const std::shared_ptr& get_instance(); // -=-= Value setters =-=- // @@ -459,177 +534,179 @@ namespace phoenix { /// \param value The new value to set. /// \param index The index of the value to set /// \param context An instance to use as context for setting member variables. - /// \throws illegal_type_access if the #type of this symbol is not dt_string. - /// \throws illegal_index_access if \p index >= #count. - /// \throws no_context if this symbol #is_member and \p context is `nullptr`. - /// \throws unbound_member_access if this symbol has not been registered yet - /// \throws illegal_context_type if this symbol #is_registered_to a different type than the type of \p context. - PHOENIX_API void - set_string(std::string_view value, std::size_t index = 0, const std::shared_ptr& context = nullptr); + /// \throws DaedalusIllegalTypeAccess if the #type of this symbol is not dt_string. + /// \throws DaedalusIllegalIndexAccess if \p index >= #count. + /// \throws DaedalusNoContextError if this symbol #is_member and \p context is `nullptr`. + /// \throws DaedalusUnboundMemberAccess if this symbol has not been registered yet + /// \throws DaedalusIllegalContextType if this symbol #is_registered_to a different type than the type of \p + /// context. + ZKAPI void set_string(std::string_view value, + std::size_t index = 0, + const std::shared_ptr& context = nullptr); /// \brief Validates that the symbol is a float and not constant and sets it's value in the given context. /// \param value The new value to set. /// \param index The index of the value to set /// \param context An instance to use as context for setting member variables. - /// \throws illegal_type_access if the #type of this symbol is not dt_float - /// \throws illegal_index_access if \p index >= #count. - /// \throws no_context if this symbol #is_member and \p context is `nullptr`. - /// \throws unbound_member_access if this symbol has not been registered yet - /// \throws illegal_context_type if this symbol #is_registered_to a different type than the type of \p context. - PHOENIX_API void - set_float(float value, std::size_t index = 0, const std::shared_ptr& context = nullptr); + /// \throws DaedalusIllegalTypeAccess if the #type of this symbol is not dt_float + /// \throws DaedalusIllegalIndexAccess if \p index >= #count. + /// \throws DaedalusNoContextError if this symbol #is_member and \p context is `nullptr`. + /// \throws DaedalusUnboundMemberAccess if this symbol has not been registered yet + /// \throws DaedalusIllegalContextType if this symbol #is_registered_to a different type than the type of \p + /// context. + ZKAPI void + set_float(float value, std::size_t index = 0, const std::shared_ptr& context = nullptr); /// \brief Validates that the symbol is an int and not constant and sets it's value in the given context. /// \param value The new value to set. /// \param index The index of the value to set /// \param context An instance to use as context for setting member variables. - /// \throws illegal_type_access if the #type of this symbol is not dt_int or dt_function. - /// \throws illegal_index_access if \p index >= #count. - /// \throws no_context if this symbol #is_member and \p context is `nullptr`. - /// \throws unbound_member_access if this symbol has not been registered yet - /// \throws illegal_context_type if this symbol #is_registered_to a different type than the type of \p context. - PHOENIX_API void - set_int(std::int32_t value, std::size_t index = 0, const std::shared_ptr& context = nullptr); + /// \throws DaedalusIllegalTypeAccess if the #type of this symbol is not dt_int or dt_function. + /// \throws DaedalusIllegalIndexAccess if \p index >= #count. + /// \throws DaedalusNoContextError if this symbol #is_member and \p context is `nullptr`. + /// \throws DaedalusUnboundMemberAccess if this symbol has not been registered yet + /// \throws DaedalusIllegalContextType if this symbol #is_registered_to a different type than the type of \p + /// context. + ZKAPI void + set_int(std::int32_t value, std::size_t index = 0, const std::shared_ptr& context = nullptr); /// \brief Validates that the symbol is an instance and sets it's value /// \param inst The instance value to set - /// \throws illegal_type_access if the #type of this symbol is not dt_instance. - PHOENIX_API void set_instance(const std::shared_ptr& inst); + /// \throws DaedalusIllegalTypeAccess if the #type of this symbol is not dt_instance. + ZKAPI void set_instance(const std::shared_ptr& inst); /// \brief Tests whether this symbol holds an instance of the given type. /// \tparam T The type of instance to check for. /// \return true if the symbol contains an instance of the given type, false if not. template - PHOENIX_API typename std::enable_if, - bool>::type inline is_instance_of() { // clang-format on - return this->type() == datatype::instance && this->get_instance() != nullptr && + ZKAPI typename std::enable_if, + bool>::type inline is_instance_of() { // clang-format on + return this->type() == DaedalusDataType::INSTANCE && this->get_instance() != nullptr && this->get_instance()->_m_type == &typeid(T); } /// \brief Allows VM traps on access to this symbol /// \param enable true to enable and false to disable - PHOENIX_API void set_access_trap_enable(bool enable) noexcept; + ZKAPI void set_access_trap_enable(bool enable) noexcept; /// \brief Tests whether the symbol is a constant. /// \return `true` if the symbol is a constant, `false` if not. - [[nodiscard]] PHOENIX_API inline bool is_const() const noexcept { - return (_m_flags & symbol_flag::const_) != 0; + [[nodiscard]] ZKAPI inline bool is_const() const noexcept { + return (_m_flags & DaedalusSymbolFlag::CONST) != 0; } /// \brief Tests whether the symbol is a member variable. /// \return `true` if the symbol is a member, `false` if not. - [[nodiscard]] PHOENIX_API inline bool is_member() const noexcept { - return (_m_flags & symbol_flag::member) != 0; + [[nodiscard]] ZKAPI inline bool is_member() const noexcept { + return (_m_flags & DaedalusSymbolFlag::MEMBER) != 0; } /// \brief Tests whether the symbol is an extern symbol. /// \return `true` if the symbol is an extern symbol, `false` if not. - [[nodiscard]] PHOENIX_API inline bool is_external() const noexcept { - return (_m_flags & symbol_flag::external) != 0; + [[nodiscard]] ZKAPI inline bool is_external() const noexcept { + return (_m_flags & DaedalusSymbolFlag::EXTERNAL) != 0; } /// \brief Tests whether the symbol is merged. /// \return `true` if the symbol is merged, `false` if not. /// \note It is currently not known what 'merged' means. - [[nodiscard]] PHOENIX_API inline bool is_merged() const noexcept { - return (_m_flags & symbol_flag::merged) != 0; + [[nodiscard]] ZKAPI inline bool is_merged() const noexcept { + return (_m_flags & DaedalusSymbolFlag::MERGED) != 0; } /// \brief Tests whether the symbol has access trap. /// \return `true` if the symbol has trap enabled, `false` if not. - [[nodiscard]] PHOENIX_API inline bool has_access_trap() const noexcept { - return (_m_flags & symbol_flag::access_trap) != 0; + [[nodiscard]] ZKAPI inline bool has_access_trap() const noexcept { + return (_m_flags & DaedalusSymbolFlag::TRAP_ACCESS) != 0; } /// \brief brief Tests whether the symbol is a compiler-generated symbol /// \return return `true` if the symbol is generated, `false` if not. - [[nodiscard]] PHOENIX_API inline bool is_generated() const noexcept { + [[nodiscard]] ZKAPI inline bool is_generated() const noexcept { return _m_generated; } /// \brief brief Tests whether the symbol has a return value. /// \return return `true` if the symbol has a return value, `false` if not. - [[nodiscard]] PHOENIX_API inline bool has_return() const noexcept { - return (_m_flags & symbol_flag::return_) != 0; + [[nodiscard]] ZKAPI inline bool has_return() const noexcept { + return (_m_flags & DaedalusSymbolFlag::RETURN) != 0; } /// \return The name of the symbol. - [[nodiscard]] PHOENIX_API inline const std::string& name() const noexcept { + [[nodiscard]] ZKAPI inline const std::string& name() const noexcept { return _m_name; } /// \return The address of the symbol. - [[nodiscard]] PHOENIX_API inline std::uint32_t address() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t address() const noexcept { return _m_address; } /// \return The index of the parent symbol or unset if the symbol does not have a parent. - [[nodiscard]] PHOENIX_API inline std::uint32_t parent() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t parent() const noexcept { return _m_parent; } /// \return The count of values stored in the symbol. - [[nodiscard]] PHOENIX_API inline std::uint32_t count() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t count() const noexcept { return _m_count; } /// \return The type of the symbol. - [[nodiscard]] PHOENIX_API inline datatype type() const noexcept { + [[nodiscard]] ZKAPI inline DaedalusDataType type() const noexcept { return _m_type; } /// \return The index of the symbol. - [[nodiscard]] PHOENIX_API inline std::uint32_t index() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t index() const noexcept { return _m_index; } /// \return The return type of the symbol. - [[nodiscard]] PHOENIX_API inline datatype rtype() const noexcept { + [[nodiscard]] ZKAPI inline DaedalusDataType rtype() const noexcept { return _m_return_type; } /// \return The index of the file the symbol was in. - [[nodiscard]] PHOENIX_API inline std::uint32_t file_index() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t file_index() const noexcept { return _m_file_index; } /// \return The offset in bytes of a member from the start of the instance. - [[nodiscard]] PHOENIX_API inline std::uint32_t offset_as_member() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t offset_as_member() const noexcept { return _m_member_offset; } - [[nodiscard]] PHOENIX_API inline std::uint32_t line_start() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t line_start() const noexcept { return _m_line_start; } - [[nodiscard]] PHOENIX_API inline std::uint32_t line_count() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t line_count() const noexcept { return _m_line_count; } - [[nodiscard]] PHOENIX_API inline std::uint32_t char_start() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t char_start() const noexcept { return _m_char_start; } - [[nodiscard]] PHOENIX_API inline std::uint32_t char_count() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t char_count() const noexcept { return _m_char_count; } - [[nodiscard]] PHOENIX_API inline std::uint32_t class_size() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t class_size() const noexcept { return _m_class_size; } - [[nodiscard]] PHOENIX_API inline const std::type_info& registered_to() const noexcept { + [[nodiscard]] ZKAPI inline const std::type_info& registered_to() const noexcept { return *_m_registered_to; }; protected: - PHOENIX_INTERNAL symbol() = default; - template - inline const T* get_member_ptr(std::uint8_t index, const std::shared_ptr& context) const { + inline const T* get_member_ptr(std::uint8_t index, const std::shared_ptr& context) const { if (!_m_registered_to) - throw unbound_member_access(this); + throw DaedalusUnboundMemberAccess(this); if (*_m_registered_to != *context->_m_type) - throw illegal_context_type {this, *context->_m_type}; + throw DaedalusIllegalContextType {this, *context->_m_type}; auto data_ptr = context->data(); std::uint32_t target_offset = offset_as_member() + index * sizeof(T); @@ -637,11 +714,11 @@ namespace phoenix { } template - inline T* get_member_ptr(std::uint8_t index, const std::shared_ptr& context) { + inline T* get_member_ptr(std::uint8_t index, const std::shared_ptr& context) { if (!_m_registered_to) - throw unbound_member_access(this); + throw DaedalusUnboundMemberAccess(this); if (*_m_registered_to != *context->_m_type) - throw illegal_context_type {this, *context->_m_type}; + throw DaedalusIllegalContextType {this, *context->_m_type}; auto data_ptr = context->data(); std::uint32_t target_offset = offset_as_member() + index * sizeof(T); @@ -649,19 +726,19 @@ namespace phoenix { } private: - friend class script; + friend class DaedalusScript; std::string _m_name; std::variant, std::unique_ptr, std::unique_ptr, - std::shared_ptr> + std::shared_ptr> _m_value; - std::uint32_t _m_address {unset}; - std::uint32_t _m_parent {unset}; - std::uint32_t _m_class_offset {unset}; + std::uint32_t _m_address {static_cast(-1)}; + std::uint32_t _m_parent {static_cast(-1)}; + std::uint32_t _m_class_offset {static_cast(-1)}; std::uint32_t _m_count {0}; - datatype _m_type {0}; + DaedalusDataType _m_type {0}; std::uint32_t _m_flags {0}; bool _m_generated {false}; @@ -671,16 +748,16 @@ namespace phoenix { std::uint32_t _m_char_start {0}; std::uint32_t _m_char_count {0}; - std::uint32_t _m_member_offset {unset}; - std::uint32_t _m_class_size {unset}; - datatype _m_return_type {datatype::void_}; - std::uint32_t _m_index {unset}; + std::uint32_t _m_member_offset {static_cast(-1)}; + std::uint32_t _m_class_size {static_cast(-1)}; + DaedalusDataType _m_return_type {DaedalusDataType::VOID}; + std::uint32_t _m_index {static_cast(-1)}; const std::type_info* _m_registered_to {nullptr}; }; /// \brief Represents a daedalus VM instruction. - struct instruction { - opcode op {opcode::nop}; + struct DaedalusInstruction { + DaedalusOpcode op {DaedalusOpcode::NOP}; std::uint32_t address {0}; std::uint32_t symbol {0}; std::int32_t immediate {0}; @@ -690,32 +767,36 @@ namespace phoenix { /// \brief Reads an instruction from a reader. /// \param[in,out] in The reader to read from /// \return The instruction read. - PHOENIX_INTERNAL static instruction decode(buffer& in); + ZKINT static DaedalusInstruction decode(Read* r); }; /// \brief Represents a compiled daedalus script - class script { + class DaedalusScript { public: - PHOENIX_API script(const script& copy) = default; - PHOENIX_API script(script&& move) = default; + ZKAPI DaedalusScript() = default; + ZKAPI DaedalusScript(const DaedalusScript& copy) = delete; + ZKAPI DaedalusScript(DaedalusScript&& move) = default; /// \brief Parses in a compiled daedalus script. /// \param path The path of the script file. /// \return The script parsed - [[nodiscard]] PHOENIX_API static script parse(const std::string& path); + [[nodiscard]] ZKREM("use ::load") ZKAPI static DaedalusScript parse(const std::string& path); /// \brief Parses in a compiled daedalus script. /// \param buf A buffer containing the script data. /// \return The script parsed - [[nodiscard]] PHOENIX_API static script parse(phoenix::buffer& buf); + [[nodiscard]] ZKREM("use ::load") ZKAPI static DaedalusScript parse(phoenix::buffer& buf); + + ZKAPI void load(Read* r); /// \brief Registers a member offset /// \param name The name of the member in the script /// \param field The field to register - /// \throws symbol_not_found if there is no symbol with the given name. - /// \throws member_registration_error if the member could not be registered - /// \throws invalid_registration_datatype If the datatype of \p _member is different than that of the symbol. + /// \throws DaedalusSymbolNotFound if there is no symbol with the given name. + /// \throws DaedalusMemberRegistrationError if the member could not be registered + /// \throws DaedalusInvalidRegistrationDataType If the datatype of \p _member is different than that of the + /// symbol. template typename std::enable_if || std::is_same_v<_member, float> || std::is_same_v<_member, std::int32_t> || @@ -734,9 +815,10 @@ namespace phoenix { /// \brief Registers a member offset /// \param name The name of the member in the script /// \param field The field to register - /// \throws symbol_not_found if there is no symbol with the given name. - /// \throws member_registration_error if the member could not be registered - /// \throws invalid_registration_datatype If the datatype of \p _member is different than that of the symbol. + /// \throws DaedalusSymbolNotFound if there is no symbol with the given name. + /// \throws DaedalusMemberRegistrationError if the member could not be registered + /// \throws DaedalusInvalidRegistrationDataType If the datatype of \p _member is different than that of the + /// symbol. template typename std::enable_if || std::is_same_v<_member, float> || std::is_same_v<_member, std::int32_t> || @@ -753,72 +835,71 @@ namespace phoenix { } /// \return All symbols in the script - [[nodiscard]] PHOENIX_API inline const std::vector& symbols() const noexcept { + [[nodiscard]] ZKAPI inline const std::vector& symbols() const noexcept { return _m_symbols; } /// \brief Retrieves the symbol with the given \p index /// \param index The index of the symbol to get /// \return The symbol or `nullptr` if the index was out-of-range. - [[nodiscard]] PHOENIX_API const symbol* find_symbol_by_index(std::uint32_t index) const; + [[nodiscard]] ZKAPI const DaedalusSymbol* find_symbol_by_index(std::uint32_t index) const; /// \brief Looks for parameters of the given function symbol. Only works for external functions. /// \param parent The function symbol to get the parameter symbols for. /// \return A list of function parameter symbols. - [[nodiscard]] PHOENIX_API std::vector find_parameters_for_function(const symbol* parent) const; + [[nodiscard]] ZKAPI std::vector + find_parameters_for_function(const DaedalusSymbol* parent) const; /// \brief Retrieves the symbol with the given \p address set /// \param index The address of the symbol to get /// \return The symbol or `nullptr` if no symbol with that address was found. - [[nodiscard]] PHOENIX_API const symbol* find_symbol_by_address(std::uint32_t address) const; + [[nodiscard]] ZKAPI const DaedalusSymbol* find_symbol_by_address(std::uint32_t address) const; /// \brief Retrieves the symbol with the given \p name. /// \param name The name of the symbol to get. /// \return The symbol or `nullptr` if no symbol with that name was found. - [[nodiscard]] PHOENIX_API const symbol* find_symbol_by_name(std::string_view name) const; + [[nodiscard]] ZKAPI const DaedalusSymbol* find_symbol_by_name(std::string_view name) const; /// \brief Retrieves the symbol with the given \p index /// \param index The index of the symbol to get /// \return The symbol or `nullptr` if the index was out-of-range. - [[nodiscard]] PHOENIX_API symbol* find_symbol_by_index(std::uint32_t index); + [[nodiscard]] ZKAPI DaedalusSymbol* find_symbol_by_index(std::uint32_t index); /// \brief Retrieves the symbol with the given \p address set /// \param index The address of the symbol to get /// \return The symbol or `nullptr` if no symbol with that address was found. - [[nodiscard]] PHOENIX_API symbol* find_symbol_by_address(std::uint32_t address); + [[nodiscard]] ZKAPI DaedalusSymbol* find_symbol_by_address(std::uint32_t address); /// \brief Looks for parameters of the given function symbol. Only works for external functions. /// \param parent The function symbol to get the parameter symbols for. /// \return A list of function parameter symbols. - [[nodiscard]] PHOENIX_API std::vector find_parameters_for_function(const symbol* parent); + [[nodiscard]] ZKAPI std::vector find_parameters_for_function(const DaedalusSymbol* parent); /// \brief Retrieves the symbol with the given \p name. /// \param name The name of the symbol to get. /// \return The symbol or `nullptr` if no symbol with that name was found. - [[nodiscard]] PHOENIX_API symbol* find_symbol_by_name(std::string_view name); + [[nodiscard]] ZKAPI DaedalusSymbol* find_symbol_by_name(std::string_view name); /// \brief Call the given callback function for every instance symbol which is a descendant of the class with /// the given name. /// \param name The name of the parent class. /// \param callback The function to call with each instance symbol. - PHOENIX_API void enumerate_instances_by_class_name(std::string_view name, - const std::function& callback); + ZKAPI void enumerate_instances_by_class_name(std::string_view name, + const std::function& callback); /// \brief Decodes the instruction at \p address and returns it. /// \param address The address of the instruction to decode /// \return The instruction. - [[nodiscard]] PHOENIX_API instruction instruction_at(std::uint32_t address) const; + [[nodiscard]] ZKAPI DaedalusInstruction instruction_at(std::uint32_t address) const; /// \return The total size of the script. - [[nodiscard]] PHOENIX_API std::uint32_t size() const noexcept { - return _m_text.limit() & 0xFFFFFF; - } + [[nodiscard]] ZKAPI std::uint32_t size() const noexcept; /// \brief Finds the symbol the given instance is currently bound to. /// \param inst The instance to get the symbol for. /// \return The symbol associated with that instance or nullptr if the symbol is not associated /// with any instance. - PHOENIX_API inline const symbol* find_symbol_by_instance(const instance& inst) const { + ZKAPI inline const DaedalusSymbol* find_symbol_by_instance(const DaedalusInstance& inst) const { return find_symbol_by_index(inst._m_symbol_index); } @@ -826,7 +907,7 @@ namespace phoenix { /// \param inst The instance to get the symbol for. /// \return The symbol associated with that instance or nullptr if the symbol is not associated /// with any instance. - PHOENIX_API inline symbol* find_symbol_by_instance(const instance& inst) { + ZKAPI inline DaedalusSymbol* find_symbol_by_instance(const DaedalusInstance& inst) { return find_symbol_by_index(inst._m_symbol_index); } @@ -835,9 +916,8 @@ namespace phoenix { /// \return The symbol associated with that instance or nullptr if the symbol is not associated /// with any instance. template - PHOENIX_API - typename std::enable_if, const symbol*>::type inline find_symbol_by_instance( - const std::shared_ptr& inst) const { // clang-format on + ZKAPI typename std::enable_if, const DaedalusSymbol*>:: + type inline find_symbol_by_instance(const std::shared_ptr& inst) const { // clang-format on return find_symbol_by_index(inst->_m_symbol_index); } @@ -846,74 +926,71 @@ namespace phoenix { /// \return The symbol associated with that instance or nullptr if the symbol is not associated /// with any instance. template - PHOENIX_API - typename std::enable_if, symbol*>::type inline find_symbol_by_instance( - const std::shared_ptr& inst) { + ZKAPI typename std::enable_if, DaedalusSymbol*>:: + type inline find_symbol_by_instance(const std::shared_ptr& inst) { return find_symbol_by_index(inst->_m_symbol_index); } - [[nodiscard]] PHOENIX_API std::vector find_class_members(symbol const& cls); + [[nodiscard]] ZKAPI std::vector find_class_members(DaedalusSymbol const& cls); - inline void register_as_opaque(std::string_view class_name) { + ZKAPI void register_as_opaque(std::string_view class_name) { return register_as_opaque(find_symbol_by_name(class_name)); } - void register_as_opaque(symbol* sym); + void register_as_opaque(DaedalusSymbol* sym); protected: - PHOENIX_INTERNAL script() = default; - template - symbol* _check_member(std::string_view name, const std::type_info* type) { + DaedalusSymbol* _check_member(std::string_view name, const std::type_info* type) { auto* sym = find_symbol_by_name(name); if (sym == nullptr) - throw symbol_not_found {std::string {name}}; + throw DaedalusSymbolNotFound {std::string {name}}; if (!sym->is_member()) - throw member_registration_error {sym, "not a member"}; + throw DaedalusMemberRegistrationError {sym, "not a member"}; if (sym->count() > N) - throw member_registration_error {sym, - "incorrect number of elements: given " + std::to_string(N) + - " expected " + std::to_string(sym->count())}; + throw DaedalusMemberRegistrationError {sym, + "incorrect number of elements: given " + std::to_string(N) + + " expected " + std::to_string(sym->count())}; // check class registration auto* parent = find_symbol_by_index(sym->parent()); if (parent == nullptr) - throw member_registration_error {sym, "no parent found"}; + throw DaedalusMemberRegistrationError {sym, "no parent found"}; if (parent->_m_registered_to == nullptr) { parent->_m_registered_to = type; } else if (parent->_m_registered_to != type) { - throw member_registration_error {sym, - "parent class is already registered with a different type (" + - std::string {parent->_m_registered_to->name()} + ")"}; + throw DaedalusMemberRegistrationError {sym, + "parent class is already registered with a different type (" + + std::string {parent->_m_registered_to->name()} + ")"}; } // check type matches if constexpr (std::is_same_v) { - if (sym->type() != datatype::string) - throw invalid_registration_datatype {sym, "string"}; + if (sym->type() != DaedalusDataType::STRING) + throw DaedalusInvalidRegistrationDataType {sym, "string"}; } else if constexpr (std::is_same_v) { - if (sym->type() != datatype::float_) - throw invalid_registration_datatype {sym, "float"}; + if (sym->type() != DaedalusDataType::FLOAT) + throw DaedalusInvalidRegistrationDataType {sym, "float"}; } else if constexpr (std::is_same_v || std::is_enum_v<_member>) { - if (sym->type() != datatype::integer && sym->type() != datatype::function) - throw invalid_registration_datatype {sym, "int"}; + if (sym->type() != DaedalusDataType::INT && sym->type() != DaedalusDataType::FUNCTION) + throw DaedalusInvalidRegistrationDataType {sym, "int"}; } else { - throw invalid_registration_datatype {sym, ""}; + throw DaedalusInvalidRegistrationDataType {sym, ""}; } return sym; } - PHOENIX_API symbol* add_temporary_strings_symbol(); + ZKAPI DaedalusSymbol* add_temporary_strings_symbol(); private: - std::vector _m_symbols; + std::vector _m_symbols; std::unordered_map _m_symbols_by_name; std::unordered_map _m_symbols_by_address; - mutable buffer _m_text = buffer::empty(); + mutable std::unique_ptr _m_text; std::uint8_t _m_version {0}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/DaedalusVm.hh b/include/zenkit/DaedalusVm.hh index ed854268..6adb9cc9 100644 --- a/include/zenkit/DaedalusVm.hh +++ b/include/zenkit/DaedalusVm.hh @@ -1,109 +1,119 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include +#include "zenkit/DaedalusScript.hh" +#include "zenkit/Library.hh" #include +#include #include +#include #include #include -#include -#include -#include +#include +#include -namespace phoenix { - struct _ignore_return_value {}; +namespace zenkit { + struct IgnoreReturnValue {}; - struct naked_call {}; + struct DaedalusNakedCall {}; - struct func { - symbol* value; + struct DaedalusFunction { + DaedalusSymbol* value; }; template static constexpr bool is_instance_ptr_v = false; template - static constexpr bool is_instance_ptr_v> = std::is_base_of_v; + static constexpr bool is_instance_ptr_v> = std::is_base_of_v; template - static constexpr bool is_raw_instance_ptr_v = std::is_base_of_v>; + static constexpr bool is_raw_instance_ptr_v = std::is_base_of_v>; /// \brief An exception thrown if the definition of an external is incorrect. - class illegal_external_definition : public script_error { + class DaedalusIllegalExternalDefinition : public DaedalusScriptError { public: - PHOENIX_API illegal_external_definition(const symbol* sym, std::string&& message); + ZKAPI DaedalusIllegalExternalDefinition(const DaedalusSymbol* sym, std::string&& message); public: /// \brief The symbol the external is being registered for. - const symbol* sym; + const DaedalusSymbol* sym; }; /// \brief An exception thrown if the return type of a new external registration does not match /// the return type defined in the script. - class illegal_external_rtype : public illegal_external_definition { + class DaedalusIllegalExternalReturnType : public DaedalusIllegalExternalDefinition { public: - PHOENIX_API illegal_external_rtype(const symbol* sym, std::string&& provided); + ZKAPI DaedalusIllegalExternalReturnType(const DaedalusSymbol* sym, std::string&& provided); }; /// \brief An exception thrown if one of the parameter types of a new external registration does not match /// the type defined in the script. - class illegal_external_param : public illegal_external_definition { + class DaedalusIllegalExternalParameter : public DaedalusIllegalExternalDefinition { public: - PHOENIX_API illegal_external_param(const symbol* sym, std::string&& provided, std::uint8_t i); + ZKAPI DaedalusIllegalExternalParameter(const DaedalusSymbol* sym, std::string&& provided, std::uint8_t i); }; - class vm_exception : public script_error { - using script_error::script_error; + class DaedalusVmException : public DaedalusScriptError { + using DaedalusScriptError::DaedalusScriptError; }; /// \brief An exception handling strategy given by VM exception handlers. - enum class vm_exception_strategy { - /// \brief Continue with execution of the script. - continue_, - - /// \brief Return from the current subroutine. - return_, - - /// \brief Re-throw the exception and fail. - fail_, + enum class DaedalusVmExceptionStrategy { + CONTINUE = 0, ///< Continue with execution of the script. + RETURN = 1, ///< Return from the current subroutine. + FAIL = 2, ///< Re-throw the exception and fail. + + // Deprecated entries. + continue_ ZKREM("renamed to DaedalusVmExceptionStrategy::CONTINUE") = CONTINUE, + return_ ZKREM("renamed to DaedalusVmExceptionStrategy::RETURN") = RETURN, + fail_ ZKREM("renamed to DaedalusVmExceptionStrategy::FAIL") = FAIL, }; /// \brief A stack frame in the VM. - struct daedalus_stack_frame { - std::shared_ptr context; + struct DaedalusStackFrame { + std::shared_ptr context; bool reference; - std::variant> value; + std::variant> value; uint16_t index {0}; }; /// \brief A call stack frame in the VM. - struct daedalus_call_stack_frame { - const symbol* function; + struct DaedalusCallStackFrame { + const DaedalusSymbol* function; std::uint32_t program_counter; - std::shared_ptr context; + std::shared_ptr context; }; - namespace execution_flag { - static constexpr std::uint8_t none = 0; - static constexpr std::uint8_t vm_allow_null_instance_access = 1 << 1; - static constexpr std::uint8_t vm_ignore_const_specifier = 1 << 2; - } // namespace execution_flag + namespace DaedalusVmExecutionFlag { + static constexpr std::uint8_t NONE = 0; + static constexpr std::uint8_t ALLOW_NULL_INSTANCE_ACCESS = 1 << 1; + static constexpr std::uint8_t IGNORE_CONST_SPECIFIER = 1 << 2; + + // Deprecated entries. + ZKREM("renamed to DaedalusVmExecutionFlag::NONE") static constexpr std::uint8_t none = NONE; + + ZKREM("renamed to DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS") + static constexpr std::uint8_t vm_allow_null_instance_access = ALLOW_NULL_INSTANCE_ACCESS; + + ZKREM("renamed to DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER") + static constexpr std::uint8_t vm_ignore_const_specifier = IGNORE_CONST_SPECIFIER; + } // namespace DaedalusVmExecutionFlag - class vm : public script { + class DaedalusVm : public DaedalusScript { public: static constexpr auto stack_size = 2048; - /// \brief Creates a DaedalusVM instance for the given script. + /// \brief Creates a DaedalusVM DaedalusInstance for the given script. /// \param scr The script to load into the VM. - PHOENIX_API explicit vm(script&& scr, uint8_t flags = execution_flag::none); + ZKAPI explicit DaedalusVm(DaedalusScript&& scr, uint8_t flags = DaedalusVmExecutionFlag::NONE); /// \brief Calls a function by it's name. /// \tparam P The types for the argument values. /// \param sym The name of the function to call. /// \param args The arguments for the function call. - template + template R call_function(std::string_view name, P... args) { return call_function(find_symbol_by_name(name), args...); } @@ -112,28 +122,28 @@ namespace phoenix { /// \tparam P The types for the argument values. /// \param sym The symbol of the function to call. /// \param args The arguments for the function call. - template - R call_function(const symbol* sym, P... args) { + template + R call_function(const DaedalusSymbol* sym, P... args) { if (sym == nullptr) { - throw vm_exception {"Cannot call function: not found"}; + throw DaedalusVmException {"Cannot call function: not found"}; } - if (sym->type() != datatype::function) { - throw vm_exception {"Cannot call " + sym->name() + ": not a function"}; + if (sym->type() != DaedalusDataType::FUNCTION) { + throw DaedalusVmException {"Cannot call " + sym->name() + ": not a function"}; } - std::vector params = find_parameters_for_function(sym); + std::vector params = find_parameters_for_function(sym); if (params.size() < sizeof...(P)) { - throw vm_exception {"too many arguments provided for " + sym->name() + ": given " + - std::to_string(sizeof...(P)) + " expected " + std::to_string(params.size())}; + throw DaedalusVmException {"too many arguments provided for " + sym->name() + ": given " + + std::to_string(sizeof...(P)) + " expected " + std::to_string(params.size())}; } if (params.size() > sizeof...(P)) { - throw vm_exception {"not enough arguments provided for " + sym->name() + ": given " + - std::to_string(sizeof...(P)) + " expected " + std::to_string(params.size())}; + throw DaedalusVmException {"not enough arguments provided for " + sym->name() + ": given " + + std::to_string(sizeof...(P)) + " expected " + std::to_string(params.size())}; } - if constexpr (!std::is_same_v) { + if constexpr (!std::is_same_v) { check_call_return_type(sym); } @@ -141,10 +151,9 @@ namespace phoenix { push_call_parameters<0, P...>(params, args...); } - PX_LOGD("vm: calling function ", sym->name()); unsafe_call(sym); - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { // clear the stack _m_stack_ptr = 0; @@ -172,7 +181,7 @@ namespace phoenix { /// \return The initialized instance. template - typename std::enable_if, std::shared_ptr<_instance_t>>::type + typename std::enable_if, std::shared_ptr<_instance_t>>::type init_instance(std::string_view name) { return init_instance<_instance_t>(find_symbol_by_name(name)); } @@ -182,7 +191,7 @@ namespace phoenix { /// \param instance The instance to initialize. /// \param name The name of the instance to initialize (ie. 'STT_309_WHISTLER') template - typename std::enable_if, void>::type + typename std::enable_if, void>::type init_instance(const std::shared_ptr<_instance_t>& instance, std::string_view name) { init_instance<_instance_t>(instance, find_symbol_by_name(name)); } @@ -197,23 +206,21 @@ namespace phoenix { /// \return The initialized instance. template - typename std::enable_if, std::shared_ptr<_instance_t>>::type - init_instance(symbol* sym) { + typename std::enable_if, std::shared_ptr<_instance_t>>::type + init_instance(DaedalusSymbol* sym) { // create the instance auto inst = std::make_shared<_instance_t>(); init_instance(inst, sym); return inst; } - std::shared_ptr init_opaque_instance(symbol* sym); - /// \brief Initializes an instance with the given type into \p instance /// \tparam _instance_t The type of the instance to initialize (ie. C_NPC). /// \param instance The instance to initialize. /// \param sym The symbol to initialize. template - typename std::enable_if, void>::type - init_instance(const std::shared_ptr<_instance_t>& instance, symbol* sym) { + typename std::enable_if, void>::type + init_instance(const std::shared_ptr<_instance_t>& instance, DaedalusSymbol* sym) { // Perform initial instance setup this->allocate_instance(instance, sym); @@ -233,6 +240,8 @@ namespace phoenix { _m_self_sym->set_instance(old_self_instance); } + std::shared_ptr init_opaque_instance(DaedalusSymbol* sym); + /// \brief Allocates an instance with the given type and name and returns it. /// /// In contrast to #init_instance, this function will only create an instance of _instance_t and assign @@ -242,8 +251,7 @@ namespace phoenix { /// \param name The name of the instance to initialize (ie. 'STT_309_WHISTLER') /// \return The initialized instance. template - - typename std::enable_if, std::shared_ptr<_instance_t>>::type + typename std::enable_if, std::shared_ptr<_instance_t>>::type allocate_instance(std::string_view name) { return allocate_instance<_instance_t>(find_symbol_by_name(name)); } @@ -257,7 +265,7 @@ namespace phoenix { /// \param instance The instance to initialize. /// \param name The name of the instance to initialize (ie. 'STT_309_WHISTLER') template - typename std::enable_if, void>::type + typename std::enable_if, void>::type allocate_instance(const std::shared_ptr<_instance_t>& instance, std::string_view name) { allocate_instance<_instance_t>(instance, find_symbol_by_name(name)); } @@ -272,8 +280,8 @@ namespace phoenix { /// \return The initialized instance. template - typename std::enable_if, std::shared_ptr<_instance_t>>::type - allocate_instance(symbol* sym) { + typename std::enable_if, std::shared_ptr<_instance_t>>::type + allocate_instance(DaedalusSymbol* sym) { // create the instance auto inst = std::make_shared<_instance_t>(); allocate_instance(inst, sym); @@ -289,13 +297,13 @@ namespace phoenix { /// \param instance The instance to initialize. /// \param sym The symbol to initialize. template - typename std::enable_if, void>::type - allocate_instance(const std::shared_ptr<_instance_t>& instance, symbol* sym) { + typename std::enable_if, void>::type + allocate_instance(const std::shared_ptr<_instance_t>& instance, DaedalusSymbol* sym) { if (sym == nullptr) { - throw vm_exception {"Cannot init instance: not found"}; + throw DaedalusVmException {"Cannot init instance: not found"}; } - if (sym->type() != datatype::instance) { - throw vm_exception {"Cannot init " + sym->name() + ": not an instance"}; + if (sym->type() != DaedalusDataType::INSTANCE) { + throw DaedalusVmException {"Cannot init " + sym->name() + ": not an instance"}; } // check that the parent class is registered for the given instance type @@ -303,38 +311,37 @@ namespace phoenix { if (parent == nullptr) { // We're probably trying to initialize $INSTANCE_HELP which is not permitted - throw vm_exception {"Cannot init " + sym->name() + - ": parent class not found (did you try to initialize $INSTANCE_HELP?)"}; + throw DaedalusVmException {"Cannot init " + sym->name() + + ": parent class not found (did you try to initialize $INSTANCE_HELP?)"}; } - while (parent->type() != datatype::class_) { + while (parent->type() != DaedalusDataType::CLASS) { parent = find_symbol_by_index(parent->parent()); } if (parent->registered_to() != typeid(_instance_t)) { - throw vm_exception {"Cannot init " + sym->name() + - ": parent class is not registered or is " - "registered to a different instance class"}; + throw DaedalusVmException {"Cannot init " + sym->name() + + ": parent class is not registered or is " + "registered to a different instance class"}; } - PX_LOGD("vm: initializing instance ", sym->name()); - instance->_m_symbol_index = sym->index(); instance->_m_type = &typeid(_instance_t); sym->set_instance(instance); } - PHOENIX_API void push_int(std::int32_t value); - PHOENIX_API void push_float(float value); - PHOENIX_API void push_instance(std::shared_ptr value); - PHOENIX_API void push_reference(symbol* value, std::uint8_t index = 0); - PHOENIX_API void push_string(std::string_view value); + ZKAPI void push_int(std::int32_t value); + ZKAPI void push_float(float value); + ZKAPI void push_instance(std::shared_ptr value); + ZKAPI void push_reference(DaedalusSymbol* value, std::uint8_t index = 0); + ZKAPI void push_string(std::string_view value); - [[nodiscard]] PHOENIX_API std::int32_t pop_int(); - [[nodiscard]] PHOENIX_API float pop_float(); - [[nodiscard]] PHOENIX_API std::shared_ptr pop_instance(); - [[nodiscard]] PHOENIX_API const std::string& pop_string(); - [[nodiscard]] PHOENIX_API std::tuple> pop_reference(); + [[nodiscard]] ZKAPI std::int32_t pop_int(); + [[nodiscard]] ZKAPI float pop_float(); + [[nodiscard]] ZKAPI std::shared_ptr pop_instance(); + [[nodiscard]] ZKAPI const std::string& pop_string(); + [[nodiscard]] ZKAPI std::tuple> + pop_reference(); /// \brief Registers a Daedalus external function. /// @@ -403,16 +410,16 @@ namespace phoenix { /// /// ///

    - /// [1] instances in the C++-World have to inherit from phoenix::instance. + /// [1] instances in the C++-World have to inherit from phoenix::DaedalusInstance. ///

    /// /// \tparam R The return type of the external. /// \tparam P The parameters types of the external. /// \param name The name of the external to register. /// \param callback The C++ function to register as the external. - /// \throws illegal_external_rtype if the return type of the C++ function does not match it's + /// \throws DaedalusIllegalExternalReturnType if the return type of the C++ function does not match it's /// definition in the script. - /// \throws illegal_external_param if a parameter type of the C++ function does not match it's + /// \throws DaedalusIllegalExternalParameter if a parameter type of the C++ function does not match it's /// definition in the script. /// \throws illegal_external if The number of parameters of the definition and callback is not the same. /// \throws runtime_error if any other error occurs. @@ -423,50 +430,50 @@ namespace phoenix { return; if (!sym->is_external()) - throw vm_exception {"symbol is not external"}; + throw DaedalusVmException {"symbol is not external"}; if constexpr (!std::is_same_v) { if (!sym->has_return()) - throw illegal_external_rtype(sym, ""); + throw DaedalusIllegalExternalReturnType(sym, ""); if constexpr (is_instance_ptr_v) { - if (sym->rtype() != datatype::instance) - throw illegal_external_rtype(sym, "instance"); + if (sym->rtype() != DaedalusDataType::INSTANCE) + throw DaedalusIllegalExternalReturnType(sym, "instance"); } else if constexpr (std::is_floating_point_v) { - if (sym->rtype() != datatype::float_) - throw illegal_external_rtype(sym, "float"); + if (sym->rtype() != DaedalusDataType::FLOAT) + throw DaedalusIllegalExternalReturnType(sym, "float"); } else if constexpr (std::is_convertible_v) { - if (sym->rtype() != datatype::integer) - throw illegal_external_rtype(sym, "int"); + if (sym->rtype() != DaedalusDataType::INT) + throw DaedalusIllegalExternalReturnType(sym, "int"); } else if constexpr (std::is_convertible_v) { - if (sym->rtype() != datatype::string) - throw illegal_external_rtype(sym, "string"); + if (sym->rtype() != DaedalusDataType::STRING) + throw DaedalusIllegalExternalReturnType(sym, "string"); } else { - throw vm_exception {"unsupported return type"}; + throw DaedalusVmException {"unsupported return type"}; } } else { if (sym->has_return()) - throw illegal_external_rtype(sym, "void"); + throw DaedalusIllegalExternalReturnType(sym, "void"); } - std::vector params = find_parameters_for_function(sym); + std::vector params = find_parameters_for_function(sym); if (params.size() < sizeof...(P)) - throw illegal_external_definition {sym, - "too many arguments declared for external " + sym->name() + - ": declared " + std::to_string(sizeof...(P)) + " expected " + - std::to_string(params.size())}; + throw DaedalusIllegalExternalDefinition {sym, + "too many arguments declared for external " + sym->name() + + ": declared " + std::to_string(sizeof...(P)) + + " expected " + std::to_string(params.size())}; if (params.size() > sizeof...(P)) - throw illegal_external_definition {sym, - "not enough arguments declared for external " + sym->name() + - ": declared " + std::to_string(sizeof...(P)) + " expected " + - std::to_string(params.size())}; + throw DaedalusIllegalExternalDefinition {sym, + "not enough arguments declared for external " + sym->name() + + ": declared " + std::to_string(sizeof...(P)) + + " expected " + std::to_string(params.size())}; if constexpr (sizeof...(P) > 0) { check_external_params<0, P...>(params); } // *evil template hacking ensues* - _m_externals[sym] = [callback](vm& machine) { + _m_externals[sym] = [callback](DaedalusVm& machine) { if constexpr (std::is_same_v) { if constexpr (sizeof...(P) > 0) { auto v = machine.pop_values_for_external(); @@ -482,8 +489,6 @@ namespace phoenix { } } }; - - PX_LOGD("vm: registered external for ", sym->name()); } /// \brief Registers an external function. @@ -516,52 +521,52 @@ namespace phoenix { void override_function(std::string_view name, const std::function& callback) { auto* sym = find_symbol_by_name(name); if (sym == nullptr) - throw vm_exception {"symbol not found"}; + throw DaedalusVmException {"symbol not found"}; if (sym->is_external()) - throw vm_exception {"symbol is already an external"}; + throw DaedalusVmException {"symbol is already an external"}; if constexpr (!std::is_same_v) { if (!sym->has_return()) - throw illegal_external_rtype(sym, ""); + throw DaedalusIllegalExternalReturnType(sym, ""); if constexpr (is_instance_ptr_v) { - if (sym->rtype() != datatype::instance) - throw illegal_external_rtype(sym, "instance"); + if (sym->rtype() != DaedalusDataType::INSTANCE) + throw DaedalusIllegalExternalReturnType(sym, "instance"); } else if constexpr (std::is_floating_point_v) { - if (sym->rtype() != datatype::float_) - throw illegal_external_rtype(sym, "float"); + if (sym->rtype() != DaedalusDataType::FLOAT) + throw DaedalusIllegalExternalReturnType(sym, "float"); } else if constexpr (std::is_convertible_v) { - if (sym->rtype() != datatype::integer) - throw illegal_external_rtype(sym, "int"); + if (sym->rtype() != DaedalusDataType::INT) + throw DaedalusIllegalExternalReturnType(sym, "int"); } else if constexpr (std::is_convertible_v) { - if (sym->rtype() != datatype::string) - throw illegal_external_rtype(sym, "string"); + if (sym->rtype() != DaedalusDataType::STRING) + throw DaedalusIllegalExternalReturnType(sym, "string"); } else { - throw vm_exception {"unsupported return type"}; + throw DaedalusVmException {"unsupported return type"}; } } else { if (sym->has_return()) - throw illegal_external_rtype(sym, "void"); + throw DaedalusIllegalExternalReturnType(sym, "void"); } - std::vector params = find_parameters_for_function(sym); + std::vector params = find_parameters_for_function(sym); if (params.size() < sizeof...(P)) - throw illegal_external_definition {sym, - "too many arguments declared for function override " + sym->name() + - ": declared " + std::to_string(sizeof...(P)) + " expected " + - std::to_string(params.size())}; + throw DaedalusIllegalExternalDefinition { + sym, + "too many arguments declared for function override " + sym->name() + ": declared " + + std::to_string(sizeof...(P)) + " expected " + std::to_string(params.size())}; if (params.size() > sizeof...(P)) - throw illegal_external_definition {sym, - "not enough arguments declared for function override " + - sym->name() + ": declared " + std::to_string(sizeof...(P)) + - " expected " + std::to_string(params.size())}; + throw DaedalusIllegalExternalDefinition { + sym, + "not enough arguments declared for function override " + sym->name() + ": declared " + + std::to_string(sizeof...(P)) + " expected " + std::to_string(params.size())}; if constexpr (sizeof...(P) > 0) { check_external_params<0, P...>(params); } // *evil template hacking ensues* - _m_function_overrides[sym->address()] = [callback, sym](vm& machine) { + _m_function_overrides[sym->address()] = [callback, sym](DaedalusVm& machine) { machine.push_call(sym); if constexpr (std::is_same_v) { if constexpr (sizeof...(P) > 0) { @@ -579,8 +584,6 @@ namespace phoenix { } machine.pop_call(); }; - - PX_LOGD("vm: overrode function ", sym->name()); } /// \brief Overrides a function in Daedalus code with an external naked call. @@ -590,18 +593,16 @@ namespace phoenix { /// /// \param name The name of the function to override. /// \param callback The C++ function to register as the external. - void override_function(std::string_view name, const std::function& callback) { + void override_function(std::string_view name, const std::function& callback) { auto* sym = find_symbol_by_name(name); if (sym == nullptr) - throw vm_exception {"symbol not found"}; + throw DaedalusVmException {"symbol not found"}; if (sym->is_external()) - throw vm_exception {"symbol is already an external"}; + throw DaedalusVmException {"symbol is already an external"}; - _m_function_overrides[sym->address()] = [callback](vm& machine) { + _m_function_overrides[sym->address()] = [callback](DaedalusVm& machine) { callback(machine); }; - - PX_LOGD("vm: overrode function ", sym->name()); } /// \brief Overrides a function in Daedalus code with an external definition. @@ -627,11 +628,11 @@ namespace phoenix { /// /// \param callback The function to call. The one parameter of the function is the name of the unresolved /// external. - PHOENIX_API void register_default_external(const std::function& callback); + ZKAPI void register_default_external(const std::function& callback); - PHOENIX_API void register_default_external_custom(const std::function& callback); + ZKAPI void register_default_external_custom(const std::function& callback); - PHOENIX_API void register_access_trap(const std::function& callback); + ZKAPI void register_access_trap(const std::function& callback); /// \brief Registers a function to be called when script execution fails. /// @@ -642,8 +643,10 @@ namespace phoenix { /// If the function returns `true` the error is assumed to have been handled and execution will /// continue as normal. If `false` is returned, the VM will re-raise the exception and thus, /// halt execution. - PHOENIX_API void register_exception_handler( - const std::function& callback); + ZKAPI void register_exception_handler( + const std::function& callback); /// \brief Calls the given symbol as a function. /// @@ -651,51 +654,69 @@ namespace phoenix { /// the caller is required to deal with them appropriately. /// /// \param sym The symbol to unsafe_call. - PHOENIX_API void unsafe_call(const symbol* sym); + ZKAPI void unsafe_call(const DaedalusSymbol* sym); - PHOENIX_API void unsafe_jump(uint32_t address); + ZKAPI void unsafe_jump(uint32_t address); /// \return the symbol referring to the global var C_NPC self. - PHOENIX_API inline symbol* global_self() { + ZKAPI inline DaedalusSymbol* global_self() { return _m_self_sym; } /// \return the symbol referring to the global var C_NPC other. - PHOENIX_API inline symbol* global_other() { + ZKAPI inline DaedalusSymbol* global_other() { return _m_other_sym; } /// \return the symbol referring to the global var C_NPC victim. - PHOENIX_API inline symbol* global_victim() { + ZKAPI inline DaedalusSymbol* global_victim() { return _m_victim_sym; } /// \return the symbol referring to the global var C_NPC hero. - PHOENIX_API inline symbol* global_hero() { + ZKAPI inline DaedalusSymbol* global_hero() { return _m_hero_sym; } /// \return the symbol referring to the global var C_NPC item. - PHOENIX_API inline symbol* global_item() { + ZKAPI inline DaedalusSymbol* global_item() { return _m_item_sym; } /// \brief Prints the contents of the function call stack and the VMs stack to stderr. - PHOENIX_API void print_stack_trace() const; + ZKAPI void print_stack_trace() const; /// \return The current program counter (or instruction index) the VM is at. - [[nodiscard]] PHOENIX_API inline uint32_t pc() const noexcept { + [[nodiscard]] ZKAPI inline uint32_t pc() const noexcept { return _m_pc; } + [[nodiscard]] ZKAPI std::int32_t + get_int(std::shared_ptr& context, + std::variant>& value, + uint16_t index); + [[nodiscard]] ZKAPI float + get_float(std::shared_ptr& context, + std::variant>& value, + uint16_t index); + + ZKAPI void + set_int(std::shared_ptr& context, DaedalusSymbol* ref, uint16_t index, std::int32_t value); + ZKAPI void + set_float(std::shared_ptr& context, DaedalusSymbol* ref, uint16_t index, float value); + ZKAPI void set_string(std::shared_ptr& context, + DaedalusSymbol* ref, + uint16_t index, + std::string_view value); + protected: /// \brief Runs the instruction at the current program counter and advances it properly. /// \return false, the instruction executed was a op_return instruction, otherwise true. - PHOENIX_INTERNAL bool exec(); + ZKINT bool exec(); /// \brief Validates the given address and jumps to it (sets the program counter). /// \param address The address to jump to. - PHOENIX_INTERNAL void jump(std::uint32_t address); + ZKINT void jump(std::uint32_t address); /// \brief Pushes a call stack frame onto the call stack. /// @@ -703,13 +724,13 @@ namespace phoenix { /// as #pop_call is invoked. /// /// \param sym The symbol referring to the function called. - PHOENIX_INTERNAL void push_call(const symbol* sym); + ZKINT void push_call(const DaedalusSymbol* sym); /// \brief Pops a call stack from from the call stack. /// /// This method restores the interpreter's state to before the function which the /// call stack entry refers to was called. - PHOENIX_INTERNAL void pop_call(); + ZKINT void pop_call(); /// \brief Checks that the type of each symbol in the given set of defined symbols matches the given type /// parameters. @@ -722,22 +743,23 @@ namespace phoenix { /// \tparam P A type /// \tparam Px more types. /// \param defined A list of symbols to check the types P and then P... against. - /// \throws illegal_external_param If the types don't match. + /// \throws DaedalusIllegalExternalParameter If the types don't match. /// \note Requires that sizeof...(Px) + 1 == defined.size(). template - void check_external_params(const std::vector& defined) { - if constexpr (is_instance_ptr_v

    || std::is_same_v || is_raw_instance_ptr_v

    ) { - if (defined[i]->type() != datatype::instance) - throw illegal_external_param(defined[i], "instance", i + 1); + void check_external_params(const std::vector& defined) { + if constexpr (is_instance_ptr_v

    || std::is_same_v || is_raw_instance_ptr_v

    ) { + if (defined[i]->type() != DaedalusDataType::INSTANCE) + throw DaedalusIllegalExternalParameter(defined[i], "instance", i + 1); } else if constexpr (std::is_same_v) { - if (defined[i]->type() != datatype::float_) - throw illegal_external_param(defined[i], "float", i + 1); - } else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { - if (defined[i]->type() != datatype::integer && defined[i]->type() != datatype::function) - throw illegal_external_param(defined[i], "int/func", i + 1); + if (defined[i]->type() != DaedalusDataType::FLOAT) + throw DaedalusIllegalExternalParameter(defined[i], "float", i + 1); + } else if constexpr (std::is_same_v || std::is_same_v || + std::is_same_v) { + if (defined[i]->type() != DaedalusDataType::INT && defined[i]->type() != DaedalusDataType::FUNCTION) + throw DaedalusIllegalExternalParameter(defined[i], "int/func", i + 1); } else if constexpr (std::is_same_v) { - if (defined[i]->type() != datatype::string) - throw illegal_external_param(defined[i], "string", i + 1); + if (defined[i]->type() != DaedalusDataType::STRING) + throw DaedalusIllegalExternalParameter(defined[i], "string", i + 1); } if constexpr (sizeof...(Px) > 0) { @@ -750,29 +772,30 @@ namespace phoenix { /// This is only used by #pop_values_for_external for now as it does checks related to external /// functions. It is not recommended to use this function for anything else. /// - /// \tparam T The type of the value to pop (one of std::shared_ptr, float, int32_t, - /// symbol*, std::string_view) + /// \tparam T The type of the value to pop (one of std::shared_ptr, float, int32_t, + /// DaedalusSymbol*, std::string_view) /// \return The value popped. template typename std::enable_if || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || - std::is_same_v || is_raw_instance_ptr_v || std::is_same_v, + std::is_same_v || is_raw_instance_ptr_v || + std::is_same_v, T>::type pop_value_for_external() { if constexpr (is_instance_ptr_v) { auto r = pop_instance(); - if (r != nullptr && !std::is_same_v>) { + if (r != nullptr && !std::is_same_v>) { auto& expected = typeid(typename T::element_type); if (!r->_m_type) { - throw vm_exception {"Popping instance of unregistered type: " + - std::string {r->_m_type->name()} + ", expected " + expected.name()}; + throw DaedalusVmException {"Popping instance of unregistered type: " + + std::string {r->_m_type->name()} + ", expected " + expected.name()}; } if (*r->_m_type != expected) { - throw vm_exception {"Popping instance of wrong type: " + std::string {r->_m_type->name()} + - ", expected " + expected.name()}; + throw DaedalusVmException {"Popping instance of wrong type: " + + std::string {r->_m_type->name()} + ", expected " + expected.name()}; } } @@ -780,17 +803,17 @@ namespace phoenix { } else if constexpr (is_raw_instance_ptr_v) { auto r = pop_instance(); - if (r != nullptr && !std::is_same_v) { + if (r != nullptr && !std::is_same_v) { auto& expected = typeid(typename std::remove_pointer_t); if (!r->_m_type) { - throw vm_exception {"Popping instance of unregistered type: " + - std::string {r->_m_type->name()} + ", expected " + expected.name()}; + throw DaedalusVmException {"Popping instance of unregistered type: " + + std::string {r->_m_type->name()} + ", expected " + expected.name()}; } if (*r->_m_type != expected) { - throw vm_exception {"Popping instance of wrong type: " + std::string {r->_m_type->name()} + - ", expected " + expected.name()}; + throw DaedalusVmException {"Popping instance of wrong type: " + + std::string {r->_m_type->name()} + ", expected " + expected.name()}; } } @@ -803,26 +826,24 @@ namespace phoenix { return pop_int() != 0; } else if constexpr (std::is_same_v) { return pop_string(); - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { return std::get<0>(pop_reference()); - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { auto symbol_id = static_cast(pop_int()); auto* sym = find_symbol_by_index(symbol_id); - while (sym != nullptr && sym->type() == datatype::function && !sym->is_const()) { + while (sym != nullptr && sym->type() == DaedalusDataType::FUNCTION && !sym->is_const()) { symbol_id = static_cast(sym->get_int()); sym = find_symbol_by_index(symbol_id); } - if (sym != nullptr && sym->type() != datatype::function) { - PX_LOGW("Failed to resolve external function parameter (func): Reference chain leads to a " - "non-function symbol!"); - return func {nullptr}; + if (sym != nullptr && sym->type() != DaedalusDataType::FUNCTION) { + return DaedalusFunction {nullptr}; } - return func {sym}; + return DaedalusFunction {sym}; } else { - throw vm_exception {"pop: unsupported stack frame type"}; + throw DaedalusVmException {"pop: unsupported stack frame type"}; } } @@ -836,21 +857,21 @@ namespace phoenix { template typename std::enable_if || std::is_convertible_v || std::is_convertible_v || std::is_same_v || - std::is_same_v || std::is_same_v, + std::is_same_v || std::is_same_v, void>::type push_value_from_external(T v) { // clang-format on if constexpr (is_instance_ptr_v) { - push_instance(std::static_pointer_cast(v)); + push_instance(std::static_pointer_cast(v)); } else if constexpr (std::is_floating_point_v) { push_float(static_cast(v)); } else if constexpr (std::is_convertible_v) { push_int(static_cast(v)); - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { push_int(static_cast(v.value->index())); } else if constexpr (std::is_same_v || std::is_same_v) { push_string(v); } else { - throw vm_exception {"push: unsupported stack frame type"}; + throw DaedalusVmException {"push: unsupported stack frame type"}; } } @@ -861,7 +882,7 @@ namespace phoenix { /// /// \tparam P A value type to pop from the stack (one of std::shared_ptr, float, /// int32_t, - /// symbol*, std::string_view) + /// DaedalusSymbol*, std::string_view) /// \tparam Px more value types to pop. /// \return template @@ -878,25 +899,25 @@ namespace phoenix { template typename std::enable_if || std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v, void>::type - check_call_return_type(const symbol* sym) { + check_call_return_type(const DaedalusSymbol* sym) { if constexpr (is_instance_ptr_v) { - if (sym->rtype() != datatype::instance) - throw vm_exception {"invalid return type for function " + sym->name()}; + if (sym->rtype() != DaedalusDataType::INSTANCE) + throw DaedalusVmException {"invalid return type for function " + sym->name()}; } else if constexpr (std::is_same_v) { - if (sym->rtype() != datatype::float_) - throw vm_exception {"invalid return type for function " + sym->name()}; - } else if constexpr (std::is_same_v || std::is_same_v) { - if (sym->rtype() != datatype::integer && sym->rtype() != datatype::function) - throw vm_exception {"invalid return type for function " + sym->name()}; + if (sym->rtype() != DaedalusDataType::FLOAT) + throw DaedalusVmException {"invalid return type for function " + sym->name()}; + } else if constexpr (std::is_same_v || std::is_same_v) { + if (sym->rtype() != DaedalusDataType::INT && sym->rtype() != DaedalusDataType::FUNCTION) + throw DaedalusVmException {"invalid return type for function " + sym->name()}; } else if constexpr (std::is_same_v) { - if (sym->rtype() != datatype::string) - throw vm_exception {"invalid return type for function " + sym->name()}; + if (sym->rtype() != DaedalusDataType::STRING) + throw DaedalusVmException {"invalid return type for function " + sym->name()}; } else if constexpr (std::is_same_v) { - if (sym->rtype() != datatype::void_) - throw vm_exception {"invalid return type for function " + sym->name()}; + if (sym->rtype() != DaedalusDataType::VOID) + throw DaedalusVmException {"invalid return type for function " + sym->name()}; } } @@ -905,27 +926,27 @@ namespace phoenix { std::is_same_v, std::int32_t> || std::is_same_v, bool> || std::is_same_v, std::string_view> || - std::is_same_v, symbol*>, + std::is_same_v, DaedalusSymbol*>, void>::type - push_call_parameters(const std::vector& defined, P value, Px... more) { // clang-format on - if constexpr (is_instance_ptr_v

    || std::is_same_v) { - if (defined[i]->type() != datatype::instance) - throw illegal_external_param(defined[i], "instance", i + 1); + push_call_parameters(const std::vector& defined, P value, Px... more) { // clang-format on + if constexpr (is_instance_ptr_v

    || std::is_same_v) { + if (defined[i]->type() != DaedalusDataType::INSTANCE) + throw DaedalusIllegalExternalParameter(defined[i], "instance", i + 1); push_instance(value); } else if constexpr (std::is_same_v) { - if (defined[i]->type() != datatype::float_) - throw illegal_external_param(defined[i], "float", i + 1); + if (defined[i]->type() != DaedalusDataType::FLOAT) + throw DaedalusIllegalExternalParameter(defined[i], "float", i + 1); push_float(value); } else if constexpr (std::is_same_v || std::is_same_v) { - if (defined[i]->type() != datatype::integer && defined[i]->type() != datatype::function) - throw illegal_external_param(defined[i], "int", i + 1); + if (defined[i]->type() != DaedalusDataType::INT && defined[i]->type() != DaedalusDataType::FUNCTION) + throw DaedalusIllegalExternalParameter(defined[i], "int", i + 1); push_int(value); } else if constexpr (std::is_same_v) { - if (defined[i]->type() != datatype::string) - throw illegal_external_param(defined[i], "string", i + 1); + if (defined[i]->type() != DaedalusDataType::STRING) + throw DaedalusIllegalExternalParameter(defined[i], "string", i + 1); push_string(value); } @@ -944,13 +965,13 @@ namespace phoenix { auto& expected = typeid(typename R::element_type); if (!r->_m_type) { - throw vm_exception {"Popping instance of unregistered type: " + - std::string {r->_m_type->name()} + ", expected " + expected.name()}; + throw DaedalusVmException {"Popping instance of unregistered type: " + + std::string {r->_m_type->name()} + ", expected " + expected.name()}; } if (*r->_m_type != expected) { - throw vm_exception {"Popping instance of wrong type: " + std::string {r->_m_type->name()} + - ", expected " + expected.name()}; + throw DaedalusVmException {"Popping instance of wrong type: " + + std::string {r->_m_type->name()} + ", expected " + expected.name()}; } } @@ -964,43 +985,30 @@ namespace phoenix { } } - [[nodiscard]] PHOENIX_API std::int32_t - get_int(std::shared_ptr& context, - std::variant>& value, - uint16_t index); - [[nodiscard]] PHOENIX_API float - get_float(std::shared_ptr& context, - std::variant>& value, - uint16_t index); - - PHOENIX_API void set_int(std::shared_ptr& context, symbol* ref, uint16_t index, std::int32_t value); - PHOENIX_API void set_float(std::shared_ptr& context, symbol* ref, uint16_t index, float value); - PHOENIX_API void - set_string(std::shared_ptr& context, symbol* ref, uint16_t index, std::string_view value); - private: - std::array _m_stack; + std::array _m_stack; uint16_t _m_stack_ptr {0}; - std::stack _m_call_stack; - std::unordered_map> _m_externals; - std::unordered_map> _m_function_overrides; - std::optional> _m_default_external {std::nullopt}; - std::function _m_access_trap; - std::optional> + std::stack _m_call_stack; + std::unordered_map> _m_externals; + std::unordered_map> _m_function_overrides; + std::optional> _m_default_external {std::nullopt}; + std::function _m_access_trap; + std::optional> _m_exception_handler {std::nullopt}; - symbol* _m_self_sym; - symbol* _m_other_sym; - symbol* _m_victim_sym; - symbol* _m_hero_sym; - symbol* _m_item_sym; + DaedalusSymbol* _m_self_sym; + DaedalusSymbol* _m_other_sym; + DaedalusSymbol* _m_victim_sym; + DaedalusSymbol* _m_hero_sym; + DaedalusSymbol* _m_item_sym; - symbol* _m_temporary_strings; + DaedalusSymbol* _m_temporary_strings; - std::shared_ptr _m_instance; + std::shared_ptr _m_instance; std::uint32_t _m_pc {0}; - std::uint8_t _m_flags {execution_flag::none}; + std::uint8_t _m_flags {DaedalusVmExecutionFlag::NONE}; }; /// \brief A VM exception handler which handles some and pretends to handle other VM exceptions. @@ -1010,8 +1018,8 @@ namespace phoenix { /// \param v The VM the exception occurred in. /// \param exc The exception being handled. /// \param instr The instruction being executed. - /// \return vm_exception_strategy::continue_ - PHOENIX_API vm_exception_strategy lenient_vm_exception_handler(vm& v, - const script_error& exc, - const instruction& instr); -} // namespace phoenix + /// \return DaedalusVmExceptionStrategy::continue_ + ZKAPI DaedalusVmExceptionStrategy lenient_vm_exception_handler(DaedalusVm& v, + const DaedalusScriptError& exc, + const DaedalusInstruction& instr); +} // namespace zenkit diff --git a/include/zenkit/Date.hh b/include/zenkit/Date.hh new file mode 100644 index 00000000..8198e714 --- /dev/null +++ b/include/zenkit/Date.hh @@ -0,0 +1,28 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Library.hh" + +#include + +namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + + /// \brief A basic date and time structure used by the *ZenGin*. + struct Date { + [[nodiscard]] ZKREM("use ::load()") ZKAPI static Date parse(phoenix::buffer& buf); + + ZKAPI void load(Read* r); + + std::uint32_t year; + std::uint16_t month; + std::uint16_t day; + std::uint16_t hour; + std::uint16_t minute; + std::uint16_t second; + }; +} // namespace zenkit diff --git a/include/zenkit/Error.hh b/include/zenkit/Error.hh new file mode 100644 index 00000000..6a773e97 --- /dev/null +++ b/include/zenkit/Error.hh @@ -0,0 +1,37 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Library.hh" + +#include +#include +#include + +namespace zenkit { + /// \brief Base class for all exceptions. + class Error : public std::exception { + public: + ZKAPI explicit Error(std::string&& message); + + [[nodiscard]] ZKAPI const char* what() const noexcept override { + return message.c_str(); + } + + public: + const std::string message; + }; + + /// \brief An error representing a parsing failure of any kind. + class ParserError : public Error { + public: + ZKINT explicit ParserError(std::string&& resource_type); + ZKAPI explicit ParserError(std::string&& resource_type, std::string&& context); + ZKINT explicit ParserError(std::string&& resource_type, const std::exception& cause); + ZKINT explicit ParserError(std::string&& resource_type, const std::exception& cause, std::string&& context); + + public: + const std::string resource_type; + const std::optional context {std::nullopt}; + const std::optional cause {std::nullopt}; + }; +} // namespace zenkit diff --git a/include/zenkit/Font.hh b/include/zenkit/Font.hh index 941ca5e0..4b540e61 100644 --- a/include/zenkit/Font.hh +++ b/include/zenkit/Font.hh @@ -1,16 +1,23 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include +#include "zenkit/Library.hh" #include +#include +#include #include namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + /// \brief A single font glyph. - struct glyph { + struct FontGlyph { /// \brief The width of the glyph in pixels. std::uint8_t width; @@ -20,9 +27,7 @@ namespace phoenix { /// one multiplies `uv[0].x` by the width of the font texture and `uv[0].y` by its height. glm::vec2 uv[2]; - [[nodiscard]] PHOENIX_API inline bool operator==(const glyph& g) const noexcept { - return this->width == g.width && this->uv[0] == g.uv[0] && this->uv[1] == g.uv[1]; - } + [[nodiscard]] ZKAPI bool operator==(FontGlyph const& g) const noexcept; }; /// \brief Represents a *ZenGin* font file. @@ -32,8 +37,10 @@ namespace phoenix { /// [Windows-1252](https://en.wikipedia.org/wiki/Windows-1252) encoded character within the font texture file. Font /// files can be identified most easily by their `.FNT` extension or alternatively through the `"1\n"` string at the /// beginning of the file.

    - class font { + class Font { public: + ZKAPI Font() = default; + /// \brief Creates a new font from the given values. /// \param name The name of the font. /// \param height The height of each glyph in pixels. @@ -41,7 +48,7 @@ namespace phoenix { /// \warning While *phoenix* supports an arbitrary number of glyphs for fonts, Gothic and Gothic II always /// expect 256 glyphs for all fonts. Should you create a font a number of glyphs not equal to 256 and /// try to load it into *ZenGin*, it will fail. - PHOENIX_API font(std::string name, std::uint32_t height, std::vector glyphs); + ZKAPI Font(std::string name, std::uint32_t height, std::vector glyphs); /// \brief Parses a font from the data in the given buffer. /// @@ -57,23 +64,23 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static font parse(buffer& buf); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static Font parse(phoenix::buffer& buf); /// \brief Parses a font from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed font object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static font parse(buffer&& in) { - return font::parse(in); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static Font parse(phoenix::buffer&& in); + + ZKAPI void load(Read* r); public: /// \brief The name of this font. std::string name; /// \brief The height of glyphs of this font in pixels. - std::uint32_t height; + std::uint32_t height {}; /// \brief All glyphs of this font. /// @@ -86,6 +93,6 @@ namespace phoenix { /// /// \note The glyphs UV-coordinates are not straightforward. Refer to glyph::uv for an explanation about to /// how to use them - std::vector glyphs; + std::vector glyphs {}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/Library.hh b/include/zenkit/Library.hh index efc4dc0b..16bafd74 100644 --- a/include/zenkit/Library.hh +++ b/include/zenkit/Library.hh @@ -1,30 +1,34 @@ -// Copyright © 2023 Luis Michaelis +// Copyright © 2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#ifndef PHOENIX_STATIC +#ifdef ZKDYN #if defined(_WIN32) || defined(__CYGWIN__) - #ifdef PHOENIX_EXPORTS + #ifdef _ZKEXPORT #ifdef __GNUC__ - #define PHOENIX_API __attribute__((dllexport)) + #define ZKAPI __attribute__((dllexport)) #else - #define PHOENIX_API __declspec(dllexport) + #define ZKAPI __declspec(dllexport) #endif #else #ifdef __GNUC__ - #define PHOENIX_API __attribute__((dllimport)) + #define ZKAPI __attribute__((dllimport)) #else - #define PHOENIX_API __declspec(dllimport) + #define ZKAPI __declspec(dllimport) #endif #endif - #define PHOENIX_INTERNAL + #define ZKINT #else - #define PHOENIX_API __attribute__((visibility("default"))) - #define PHOENIX_INTERNAL __attribute__((visibility("hidden"))) + #define ZKAPI __attribute__((visibility("default"))) + #define ZKINT __attribute__((visibility("hidden"))) #endif #else - #define PHOENIX_API - #define PHOENIX_INTERNAL + #define ZKAPI + #define ZKINT #endif -#define PHOENIX_DEPRECATED(reason) [[deprecated(reason)]] +#ifndef ZKNO_REM + #define ZKREM(reason) [[deprecated(reason)]] +#else + #define ZKREM(reason) +#endif diff --git a/include/zenkit/Logger.hh b/include/zenkit/Logger.hh new file mode 100644 index 00000000..54d1de20 --- /dev/null +++ b/include/zenkit/Logger.hh @@ -0,0 +1,50 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Library.hh" + +#include +#include +#include + +#if defined(_WIN32) || defined(__CYGWIN__) + #define ZK_PRINTF_LIKE(a, b) +#else + #define ZK_PRINTF_LIKE(a, b) [[gnu::format(printf, a, b)]] +#endif + +namespace zenkit { + enum class LogLevel : std::uint8_t { + ERROR = 0, + WARNING = 1, + INFO = 2, + DEBUG = 3, + TRACE = 4, + + error ZKREM("renamed to LogLevel::ERROR") = ERROR, + warn ZKREM("renamed to LogLevel::WARNING") = WARNING, + info ZKREM("renamed to LogLevel::INFO") = INFO, + debug ZKREM("renamed to LogLevel::DEBUG") = DEBUG + }; + + class Logger { + public: + using level ZKREM("renamed to zenkit::LogLevel") = LogLevel; + + /// \brief Supply a custom logger callback to be used for log output from phoenix. + /// \param callback The callback to use. + ZKREM("renamed to ::set") + ZKAPI static void use_logger(std::function&& callback); + + /// \brief Use the default logger callback for phoenix. + ZKREM("renamed to ::set_default") ZKAPI static void use_default_logger(); + + ZK_PRINTF_LIKE(3, 4) ZKAPI static void log(LogLevel lvl, char const* name, char const* fmt, ...); + ZKAPI static void set(LogLevel lvl, std::function const& cb); + ZKAPI static void set_default(LogLevel lvl); + + private: + static std::function _s_callback; + static LogLevel _s_level; + }; +} // namespace zenkit diff --git a/include/zenkit/Material.hh b/include/zenkit/Material.hh index 7c91b319..be77c855 100644 --- a/include/zenkit/Material.hh +++ b/include/zenkit/Material.hh @@ -1,62 +1,106 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once +#include "zenkit/Library.hh" + #include -#include +#include + #include -namespace phoenix { +namespace zenkit { + class ReadArchive; + /// \brief Alpha compositing modes used by the *ZenGin*. - enum class alpha_function { - default_ = 0, - none = 1, - blend = 2, - add = 3, - sub = 4, - mul = 5, - mul2 = 6, + enum class AlphaFunction { + DEFAULT = 0, + NONE = 1, + BLEND = 2, + ADD = 3, + SUBTRACT = 4, + MULTIPLY = 5, + MULTIPLY_ALT = 6, + + // Deprecated entries. + default_ ZKREM("renamed to AlphaFunction::DEFAULT") = DEFAULT, + none ZKREM("renamed to AlphaFunction::NONE") = NONE, + blend ZKREM("renamed to AlphaFunction::BLEND") = BLEND, + add ZKREM("renamed to AlphaFunction::ADD") = ADD, + sub ZKREM("renamed to AlphaFunction::SUBTRACT") = SUBTRACT, + mul ZKREM("renamed to AlphaFunction::MULTIPLY") = MULTIPLY, + mul2 ZKREM("renamed to AlphaFunction::MULTIPLY_ALT") = MULTIPLY_ALT, }; /// \brief The general type of a material. - enum class material_group : std::uint8_t { + enum class MaterialGroup : std::uint8_t { /// \brief A material group has not been assigned to the material. - undefined = 0, - metal = 1, - stone = 2, - wood = 3, - earth = 4, - water = 5, - snow = 6, + UNDEFINED = 0, + METAL = 1, + STONE = 2, + WOOD = 3, + EARTH = 4, + WATER = 5, + SNOW = 6, /// \brief The material group is explicitly not set. Added for [OpenGothic](https://github.com/Try/OpenGothic) /// compatibility. It does not exist in real Gothic or Gothic 2 materials. - none = 0xFF, + NONE = 0xFF, + + // Deprecated entries. + undefined ZKREM("renamed to MaterialGroup::UNDEFINED") = UNDEFINED, + metal ZKREM("renamed to MaterialGroup::METAL") = METAL, + stone ZKREM("renamed to MaterialGroup::STONE") = STONE, + wood ZKREM("renamed to MaterialGroup::WOOD") = WOOD, + earth ZKREM("renamed to MaterialGroup::EARTH") = EARTH, + water ZKREM("renamed to MaterialGroup::WATER") = WATER, + snow ZKREM("renamed to MaterialGroup::SNOW") = SNOW, + none ZKREM("renamed to MaterialGroup::NONE") = NONE, }; /// \brief Wave animation speed modes. - enum class wave_speed_type : std::uint8_t { - none = 0, - slow = 1, - normal = 2, - fast = 3, + enum class WaveSpeed : std::uint8_t { + NONE = 0, + SLOW = 1, + NORMAL = 2, + FAST = 3, + + // Deprecated entries. + none ZKREM("renamed to WaveSpeed::NONE") = NONE, + slow ZKREM("renamed to WaveSpeed::SLOW") = SLOW, + normal ZKREM("renamed to WaveSpeed::NORMAL") = NORMAL, + fast ZKREM("renamed to WaveSpeed::FAST") = FAST, }; /// \brief Wave animation modes. - enum class wave_mode_type : std::uint8_t { - none = 0, - ambient_ground = 1, - ground = 2, - ambient_wall = 3, - wall = 4, - env = 5, - ambient_wind = 6, - wind = 7 + enum class WaveMode : std::uint8_t { + NONE = 0, + GROUND_AMBIENT = 1, + GROUND = 2, + WALL_AMBIENT = 3, + WALL = 4, + ENVIRONMENT = 5, + WIND_AMBIENT = 6, + WIND = 7, + + // Deprecated entries. + none ZKREM("renamed to WaveType::NONE") = NONE, + ambient_ground ZKREM("renamed to WaveType::GROUND_AMBIENT") = GROUND_AMBIENT, + ground ZKREM("renamed to WaveType::GROUND") = GROUND, + ambient_wall ZKREM("renamed to WaveType::WALL_AMBIENT") = WALL_AMBIENT, + wall ZKREM("renamed to WaveType::WALL") = WALL, + env ZKREM("renamed to WaveType::ENVIRONMENT") = ENVIRONMENT, + ambient_wind ZKREM("renamed to WaveType::WIND_AMBIENT") = WIND_AMBIENT, + wind ZKREM("renamed to WaveType::WIND") = WIND, }; /// \brief Texture animation mapping modes. - enum class animation_mapping_mode { - none = 0, - linear = 1, + enum class AnimationMapping { + NONE = 0, + LINEAR = 1, + + // Deprecated entries. + none ZKREM("renamed to AnimationMapping::NONE") = NONE, + linear ZKREM("renamed to AnimationMapping::LINEAR") = LINEAR, }; /// \brief Represents a *ZenGin* material. @@ -64,7 +108,7 @@ namespace phoenix { ///

    Materials describe the way things look and sound in the *ZenGin*. Among other things, materials control which /// texture is used for a model, if the player can collide with it and even some animations. Materials are normally /// embedded into meshes.

    - class material { + class Material { public: /// \brief Parses a material from the given *ZenGin* archive. /// @@ -77,17 +121,19 @@ namespace phoenix { /// \return The parsed material object. /// \throws parser_error if parsing fails. /// \see #parse(archive_reader&&) for an owning version this function. - [[nodiscard]] PHOENIX_API static material parse(archive_reader& ctx); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static Material parse(ReadArchive& ctx); + + ZKAPI void load(ReadArchive& r); public: std::string name; - material_group group {material_group::undefined}; + MaterialGroup group {MaterialGroup::UNDEFINED}; glm::u8vec4 color {0, 0, 0, 0}; float smooth_angle {0.0f}; std::string texture {}; glm::vec2 texture_scale {}; float texture_anim_fps {0.0f}; - animation_mapping_mode texture_anim_map_mode {animation_mapping_mode::none}; + AnimationMapping texture_anim_map_mode {AnimationMapping::NONE}; glm::vec2 texture_anim_map_dir {}; bool disable_collision {false}; bool disable_lightmap {false}; @@ -97,12 +143,12 @@ namespace phoenix { bool force_occluder {false}; bool environment_mapping {false}; float environment_mapping_strength {0.0f}; - wave_mode_type wave_mode {wave_mode_type::none}; - wave_speed_type wave_speed {wave_speed_type::none}; + WaveMode wave_mode {WaveMode::NONE}; + WaveSpeed wave_speed {WaveSpeed::NONE}; float wave_max_amplitude {0.0f}; float wave_grid_size {0.0f}; bool ignore_sun {false}; - alpha_function alpha_func {alpha_function::none}; + AlphaFunction alpha_func {AlphaFunction::NONE}; glm::vec2 default_mapping {}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/Mesh.hh b/include/zenkit/Mesh.hh index bf27b02d..d06837e7 100644 --- a/include/zenkit/Mesh.hh +++ b/include/zenkit/Mesh.hh @@ -1,26 +1,37 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include -#include -#include -#include -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Date.hh" +#include "zenkit/Library.hh" +#include "zenkit/Material.hh" +#include "zenkit/Texture.hh" +#include +#include + +#include #include +#include #include +#include namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + /// \brief Represents a light map. - struct light_map { - std::shared_ptr image; + struct LightMap { + std::shared_ptr image; glm::vec3 normals[2]; glm::vec3 origin; }; /// \brief Represents a vertex feature. - struct vertex_feature { + struct VertexFeature { /// \brief The texture coordinates of the polygon. glm::vec2 texture; @@ -32,7 +43,7 @@ namespace phoenix { }; /// \brief Flags set for a polygon of a mesh. - struct polygon_flags { + struct PolygonFlagSet { std::uint8_t is_portal : 2; std::uint8_t is_occluder : 1; std::uint8_t is_sector : 1; @@ -45,11 +56,11 @@ namespace phoenix { uint8_t is_lod : 1; uint8_t normal_axis : 2; - PHOENIX_API bool operator==(const polygon_flags& b) const; + ZKAPI bool operator==(PolygonFlagSet const& b) const; }; /// \brief List of data indices for polygons of meshes. - struct polygon_list { + struct PolygonList { /// \brief The index of the material for each polygon in mesh::materials std::vector material_indices {}; @@ -63,14 +74,14 @@ namespace phoenix { std::vector vertex_indices {}; /// \brief The flags for each polygon. - std::vector flags {}; + std::vector flags {}; }; /// \brief Represents a *ZenGin* basic mesh. /// ///

    Found in files with the `MSH` extension and as the world meshes in world archives, these meshes contain a /// set of polygons and accompanying data describing a static mesh in three dimensions.

    - class mesh { + class Mesh { public: /// \brief Parses a mesh from the data in the given buffer. /// @@ -90,8 +101,9 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&, const std::vector&) - [[nodiscard]] PHOENIX_API static mesh - parse(buffer& buf, std::vector const& include_polygons = {}, bool force_wide_indices = false); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static Mesh parse(phoenix::buffer& buf, + std::vector const& include_polygons = {}, + bool force_wide_indices = false); /// \brief Parses a mesh from the data in the given buffer. /// @@ -105,37 +117,37 @@ namespace phoenix { /// \return The parsed mesh object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&, const std::vector&) - [[nodiscard]] PHOENIX_API inline static mesh parse(buffer&& buf, - std::vector const& include_polygons = {}) { - return mesh::parse(buf, include_polygons); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static Mesh + parse(phoenix::buffer&& buf, std::vector const& include_polygons = {}); + + ZKAPI void load(Read* r, std::vector const& leaf_polygons, bool force_wide_indices); public: /// \brief The creation date of this mesh. - phoenix::date date {}; + Date date {}; /// \brief The name of this mesh std::string name {}; /// \brief The bounding box of this mesh. - bounding_box bbox {}; + AxisAlignedBoundingBox bbox {}; /// \brief The oriented bbox tree of this mesh. - phoenix::obb obb {}; + OrientedBoundingBox obb {}; /// \brief A list of materials used by this mesh. - std::vector materials {}; + std::vector materials {}; /// \brief A list of vertices of this mesh. std::vector vertices {}; /// \brief A list of vertex features of this mesh. - std::vector features {}; + std::vector features {}; - /// \brief All shared lightmaps associated with this mesh - std::vector lightmaps {}; + /// \brief All shared light-maps associated with this mesh + std::vector lightmaps {}; /// \brief A list of polygons of this mesh. - polygon_list polygons {}; + PolygonList polygons {}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/Misc.hh b/include/zenkit/Misc.hh new file mode 100644 index 00000000..68fcc72b --- /dev/null +++ b/include/zenkit/Misc.hh @@ -0,0 +1,36 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Library.hh" + +#include + +namespace zenkit { + /// \brief An enum for providing a game version hint to some functions + enum class GameVersion { + GOTHIC_1, ///< Represents any patch of Gothic + GOTHIC_2, ///< Represents any patch of Gothic II, including _Night of the Raven_. + + gothic_1 ZKREM("renamed to GOTHIC_1") = GOTHIC_1, ///< Represents any patch of Gothic + gothic_2 ZKREM("renamed to GOTHIC_2") = + GOTHIC_2, ///< Represents any patch of Gothic II, including _Night of the Raven_. + }; + + /// \brief Tests whether two strings are equal when ignoring case. + /// + /// Internally, uses std::tolower to compare the strings character by character. + /// + /// \param a A string. + /// \param b Another string. + /// \return ``true`` if both strings are equal when ignoring case. + ZKAPI bool iequals(std::string_view a, std::string_view b); + + /// \brief Tests whether \p a is lexicographically less than \p b. + /// + /// Internally, uses std::tolower to compare the strings character by character. + /// + /// \param a A string. + /// \param b Another string. + /// \return ``true`` if \p a is lexicographically less than \p b. + ZKAPI bool icompare(std::string_view a, std::string_view b); +} // namespace zenkit diff --git a/include/zenkit/Model.hh b/include/zenkit/Model.hh index 3e4a8e68..ac38671b 100644 --- a/include/zenkit/Model.hh +++ b/include/zenkit/Model.hh @@ -1,17 +1,22 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include - -#include -#include +#include "zenkit/Library.hh" +#include "zenkit/ModelHierarchy.hh" +#include "zenkit/ModelMesh.hh" namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + /// \brief Represents a *ZenGin* model. /// ///

    *ZenGin* models contain a phoenix::model_mesh and a phoenix::model_hierarchy bundled into one file. Try are /// typically found in files with the `MDL` extension.

    - class model { + class Model { public: /// \brief Parses a model from the data in the given buffer. /// \param[in,out] buf The buffer to read from. @@ -21,22 +26,22 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static model parse(buffer& buf); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static Model parse(phoenix::buffer& buf); /// \brief Parses a model from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed model object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static model parse(buffer&& buf) { - return model::parse(buf); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static Model parse(phoenix::buffer&& buf); + + ZKAPI void load(Read* r); public: /// \brief The phoenix::model_hierarchy associated with this model. - model_hierarchy hierarchy {}; + ModelHierarchy hierarchy {}; /// \brief The phoenix::model_mesh associated with this model. - model_mesh mesh {}; + ModelMesh mesh {}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/ModelAnimation.hh b/include/zenkit/ModelAnimation.hh index 1caf3b78..962899d4 100644 --- a/include/zenkit/ModelAnimation.hh +++ b/include/zenkit/ModelAnimation.hh @@ -1,59 +1,114 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include -#include -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Date.hh" +#include "zenkit/Library.hh" #include #include -#include -#include +#include #include namespace phoenix { - /// \brief Represents an animation sample. - struct animation_sample { + class buffer; +} + +namespace zenkit { + class Read; + + /// \brief A single sample of an Animation. + /// + /// Each sample contains a position and a rotation for a given skeletal node. These nodes are defined + /// in a skeleton, also called a 'model hierarchy' which must be loaded in addition to the Animation. + /// + /// \see zenkit::ModelAnimation + /// \see zenkit::ModelHierarchy + struct AnimationSample { glm::vec3 position; glm::quat rotation; - [[nodiscard]] inline bool operator==(const animation_sample& other) const noexcept { - return this->position == other.position && this->rotation == other.rotation; - } + [[nodiscard]] ZKAPI bool operator==(const AnimationSample& other) const noexcept; }; /// \brief Types of animation events. - enum class animation_event_type : std::uint32_t { - tag = 0, - sound = 1, - sound_ground = 2, - animation_batch = 3, - swap_mesh = 4, - heading = 5, - pfx = 6, - pfx_ground = 7, - pfx_stop = 8, - set_mesh = 9, - start_animation = 10, - tremor = 11, + enum class AnimationEventType : std::uint32_t { + /// \brief Also known as "*eventTag" + TAG = 0, + + /// \brief Also known as "*eventSfx" + SOUND_EFFECT = 1, + + /// \brief Also known as "*eventSfxGrnd" + SOUND_EFFECT_GROUND = 2, + + /// \brief Also known as "*aniBatch". + /// \note The Gothic ModKit states that this feature is "unused, untested". + BATCH = 3, + + /// \brief Also known as "*eventSwapMesh" + SWAP_MESH = 4, + + /// \brief Also known as "*eventHeading" + /// \note The Gothic ModKit state that "this ani-event is currently disabled because the + /// implementation of the R-flag makes it obsolete". + HEADING = 5, + + /// \brief Also known as "*eventPFX" + PARTICLE_EFFECT = 6, + + /// \brief Also known as "*eventPFXGrnd" + /// \note I can't find any relevant references to this event type. Possibly unused. + PARTICLE_EFFECT_GROUND = 7, + + /// \brief Also known as "*eventPFXStop" + PARTICLE_EFFECT_STOP = 8, + + /// \brief Also known as "*eventSetMesh" + /// \note I can't find any relevant references to this event type. Possibly unused. + SET_MESH = 9, + + /// \brief Also known as "*eventMMStartAni" + MORPH_MESH_ANIMATION = 10, + + /// \brief Also known as "*eventCamTremor" + CAMERA_TREMOR = 11, + + // Deprecated entries. + tag ZKREM("renamed to AnimationEventType::TAG") = TAG, + sound ZKREM("renamed to AnimationEventType::SOUND_EFFECT") = SOUND_EFFECT, + sound_ground ZKREM("renamed to AnimationEventType::SOUND_EFFECT_GROUND") = SOUND_EFFECT_GROUND, + animation_batch ZKREM("renamed to AnimationEventType::BATCH") = BATCH, + swap_mesh ZKREM("renamed to AnimationEventType::SWAP_MESH") = SWAP_MESH, + heading ZKREM("renamed to AnimationEventType::HEADING") = HEADING, + pfx ZKREM("renamed to AnimationEventType::PARTICLE_EFFECT") = PARTICLE_EFFECT, + pfx_ground ZKREM("renamed to AnimationEventType::PARTICLE_EFFECT_GROUND") = PARTICLE_EFFECT_GROUND, + pfx_stop ZKREM("renamed to AnimationEventType::PARTICLE_FX_STOP") = PARTICLE_EFFECT_STOP, + set_mesh ZKREM("renamed to AnimationEventType::SET_MESH") = SET_MESH, + start_animation ZKREM("renamed to AnimationEventType::MORPH_MESH_ANIMATION") = MORPH_MESH_ANIMATION, + tremor ZKREM("renamed to AnimationEventType::CAMERA_TREMOR") = CAMERA_TREMOR, }; /// \brief Represents an animation event. - struct animation_event { - static constexpr const auto vmax = 4; + struct AnimationEvent { + static constexpr const auto VMAX = 4; + + AnimationEventType type; + + union { + std::uint32_t frame; + ZKREM("renamed to AnimationEvent::frame") std::uint32_t no; + }; - animation_event_type type; - std::uint32_t no; std::string tag; - std::string content[vmax]; - float values[vmax]; - float probability; // ? + std::string content[VMAX]; + float values[VMAX]; + float probability; }; /// \brief Represents a model animation. - class animation { + class ModelAnimation { public: /// \brief Parses an animation from the data in the given buffer. /// @@ -67,16 +122,16 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static animation parse(buffer& in); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static ModelAnimation parse(phoenix::buffer& in); /// \brief Parses an animation from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed animation. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static animation parse(buffer&& in) { - return animation::parse(in); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static ModelAnimation parse(phoenix::buffer&& in); + + ZKAPI void load(Read* r); public: /// \brief The name of the animation @@ -90,19 +145,28 @@ namespace phoenix { /// \brief The number of frames of this animation. std::uint32_t frame_count {}; + + /// \brief The number of skeleton nodes this animation requires. std::uint32_t node_count {}; /// \brief The number of frames of this animation to play per second. float fps {}; - /// \brief Unknown. + /// \brief The number of frames per second the original model was animated with before being converted. float fps_source {}; - float sample_position_range_min {}; - float sample_position_scalar {}; + union { + ZKREM("renamed to Animation::sample_position_min") float sample_position_range_min {}; + float sample_position_min; + }; + + union { + ZKREM("renamed to Animation::sample_position_scale") float sample_position_scalar {}; + float sample_position_scale; + }; /// \brief The bounding box of the animation. - bounding_box bbox {}; + AxisAlignedBoundingBox bbox {}; /// \brief The checksum of the model hierarchy this animation was made for. std::uint32_t checksum {}; @@ -110,16 +174,21 @@ namespace phoenix { /// \brief The original path of the animation script this animation was generated from. std::string source_path {}; + Date source_date {}; + /// \brief The original model script snippet this animation was generated from. std::string source_script {}; /// \brief The list of animation samples of this animation. - std::vector samples {}; + std::vector samples {}; /// \brief The list of animation events of this animation. - std::vector events {}; + /// \warning Though I could not find any source specifically mentioning this, all animation file I have seen + /// **do not contain any events**, meaning **this vector will always be empty**. You should retrieve + /// the relevant events from the model script file instead. + std::vector events {}; /// \brief A list of model hierarchy node indices. std::vector node_indices; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/ModelHierarchy.hh b/include/zenkit/ModelHierarchy.hh index ea216746..9fbda923 100644 --- a/include/zenkit/ModelHierarchy.hh +++ b/include/zenkit/ModelHierarchy.hh @@ -1,18 +1,24 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Date.hh" +#include "zenkit/Library.hh" -#include +#include "glm/mat4x4.hpp" -#include +#include #include namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + /// \brief A node in the hierarchy tree. - struct model_hierarchy_node { + struct ModelHierarchyNode { /// \brief The index of this node's parent node. std::int16_t parent_index; @@ -27,7 +33,7 @@ namespace phoenix { /// ///

    Model hierarchy files represent the skeletal structure of a mesh. These skeletons are used to animate mostly /// animals and humans in the game which is commonly referred to as rigging.

    - class model_hierarchy { + class ModelHierarchy { public: /// \brief Parses a model hierarchy from the data in the given buffer. /// @@ -41,7 +47,7 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static model_hierarchy parse(buffer& in); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static ModelHierarchy parse(phoenix::buffer& in); /// \brief Parses a model hierarchy from the data in the given buffer. /// @@ -52,24 +58,27 @@ namespace phoenix { /// \return The parsed model hierarchy object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static model_hierarchy parse(buffer&& in) { - return model_hierarchy::parse(in); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static ModelHierarchy parse(phoenix::buffer&& in); + + ZKAPI void load(Read* r); public: /// \brief The list of nodes this hierarchy consists of. - std::vector nodes {}; + std::vector nodes {}; /// \brief The bounding box of this hierarchy. - bounding_box bbox {}; + AxisAlignedBoundingBox bbox {}; /// \brief The collision bounding box of this hierarchy. - bounding_box collision_bbox {}; + AxisAlignedBoundingBox collision_bbox {}; /// \brief The translation of the root node of this hierarchy. glm::vec3 root_translation {}; /// \brief The checksum of this hierarchy. std::uint32_t checksum; + + Date source_date; + std::string source_path; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/ModelMesh.hh b/include/zenkit/ModelMesh.hh index 12cf26b1..37785b29 100644 --- a/include/zenkit/ModelMesh.hh +++ b/include/zenkit/ModelMesh.hh @@ -1,18 +1,27 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include -#include +#include "zenkit/Library.hh" +#include "zenkit/MultiResolutionMesh.hh" +#include "zenkit/SoftSkinMesh.hh" +#include +#include #include +#include namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + /// \brief Represents a *ZenGin* model mesh. /// ///

    Model meshes contain multiple phoenix::softskin_mesh instances as well as a set of phoenix::proto_mesh /// attachments. They can be found within `MDM` files and are embedded within phoenix::model objects.

    - class model_mesh { + class ModelMesh { public: /// \brief Parses a model mesh from the data in the given buffer. /// @@ -26,25 +35,25 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static model_mesh parse(buffer& buf); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static ModelMesh parse(phoenix::buffer& buf); /// \brief Parses a model mesh from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed model mesh object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API inline static model_mesh parse(buffer&& buf) { - return model_mesh::parse(buf); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static ModelMesh parse(phoenix::buffer&& buf); + + ZKAPI void load(Read* r); public: /// \brief A list of soft-skin meshes associated with this model mesh. - std::vector meshes {}; + std::vector meshes {}; /// \brief A map of attachment names to attachment meshes of this model mesh. - std::unordered_map attachments {}; + std::unordered_map attachments {}; /// \brief The checksum of the model hierarchy this model was made for. std::uint32_t checksum; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/ModelScript.hh b/include/zenkit/ModelScript.hh index 9c7d3453..b483c308 100644 --- a/include/zenkit/ModelScript.hh +++ b/include/zenkit/ModelScript.hh @@ -1,218 +1,270 @@ -// Copyright © 2023 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include +#include "zenkit/Library.hh" + +#include +#include +#include namespace phoenix { - struct syntax_error : public phoenix::parser_error { - public: - syntax_error(std::string&& location, std::string&& msg); + class buffer; +} + +namespace zenkit { + class Read; + + enum class MdsEventType : uint8_t { + UNKNOWN = 0, + ITEM_CREATE = 1, + ITEM_INSERT = 2, + ITEM_REMOVE = 3, + ITEM_DESTROY = 4, + ITEM_PLACE = 5, + ITEM_EXCHANGE = 6, + SET_FIGHT_MODE = 7, + MUNITION_PLACE = 8, + MUNITION_REMOVE = 9, + SOUND_DRAW = 10, + SOUND_UNDRAW = 11, + MESH_SWAP = 12, + TORCH_DRAW = 13, + TORCH_INVENTORY = 14, + TORCH_DROP = 15, + HIT_LIMB = 16, + HIT_DIRECTION = 17, + DAMAGE_MULTIPLIER = 18, + PARRY_FRAME = 19, + OPTIMAL_FRAME = 20, + HIT_END = 21, + COMBO_WINDOW = 22, + + // Deprecated entries. + unknown ZKREM("renamed to MdsEventType::UNKNOWN") = UNKNOWN, + create_item ZKREM("renamed to MdsEventType::ITEM_CREATE") = ITEM_CREATE, + insert_item ZKREM("renamed to MdsEventType::ITEM_INSERT") = ITEM_INSERT, + remove_item ZKREM("renamed to MdsEventType::ITEM_REMOVE") = ITEM_REMOVE, + destroy_item ZKREM("renamed to MdsEventType::ITEM_DESTROY") = ITEM_DESTROY, + place_item ZKREM("renamed to MdsEventType::ITEM_PLACE") = ITEM_PLACE, + exchange_item ZKREM("renamed to MdsEventType::ITEM_EXCHANGE") = ITEM_EXCHANGE, + fight_mode ZKREM("renamed to MdsEventType::SET_FIGHT_MODE") = SET_FIGHT_MODE, + place_munition ZKREM("renamed to MdsEventType::MUNITION_PLACE") = MUNITION_PLACE, + remove_munition ZKREM("renamed to MdsEventType::MUNITION_REMOVE") = MUNITION_REMOVE, + draw_sound ZKREM("renamed to MdsEventType::SOUND_DRAW") = SOUND_DRAW, + undraw_sound ZKREM("renamed to MdsEventType::SOUND_UNDRAW") = SOUND_UNDRAW, + swap_mesh ZKREM("renamed to MdsEventType::MESH_SWAP") = MESH_SWAP, + draw_torch ZKREM("renamed to MdsEventType::TORCH_DRAW") = TORCH_DRAW, + inventory_torch ZKREM("renamed to MdsEventType::TORCH_INVENTORY") = TORCH_INVENTORY, + drop_torch ZKREM("renamed to MdsEventType::TORCH_DROP") = TORCH_DROP, + hit_limb ZKREM("renamed to MdsEventType::HIT_LIMB") = HIT_LIMB, + hit_direction ZKREM("renamed to MdsEventType::HIT_DIRECTION") = HIT_DIRECTION, + dam_multiply ZKREM("renamed to MdsEventType::DAMAGE_MULTIPLIER") = DAMAGE_MULTIPLIER, + par_frame ZKREM("renamed to MdsEventType::PARRY_FRAME") = PARRY_FRAME, + opt_frame ZKREM("renamed to MdsEventType::OPTIMAL_FRAME") = OPTIMAL_FRAME, + hit_end ZKREM("renamed to MdsEventType::HIT_END") = HIT_END, + window ZKREM("renamed to MdsEventType::COMBO_WINDOW") = COMBO_WINDOW, + }; + + /// \brief A set of fight stances the player can take. + enum class MdsFightMode : uint8_t { + FIST = 0, ///< The player fights with his fists. + SINGLE_HANDED = 1, ///< The player wields a one-handed weapon. + DUAL_HANDED = 2, ///< The player wields a two-handed weapon. + BOW = 3, ///< The player wields a bow. + CROSSBOW = 4, ///< The player wields a crossbow. + MAGIC = 5, ///< The player casts a magic spell. + NONE = 6, ///< The player is not in a fighting stance. + INVALID = 0xFF, ///< A fight mode which acts as an `unset` marker. Added for OpenGothic compatibility. + + // Deprecated entries. + fist ZKREM("renamed to MdsFightMode::FIST") = FIST, + one_handed ZKREM("renamed to MdsFightMode::SINGLE_HANDED") = SINGLE_HANDED, + two_handed ZKREM("renamed to MdsFightMode::DUAL_HANDED") = DUAL_HANDED, + bow ZKREM("renamed to MdsFightMode::BOW") = BOW, + crossbow ZKREM("renamed to MdsFightMode::CROSSBOW") = CROSSBOW, + magic ZKREM("renamed to MdsFightMode::MAGIC") = MAGIC, + none ZKREM("renamed to MdsFightMode::NONE") = NONE, + invalid ZKREM("renamed to MdsFightMode::INVALID") = INVALID, + }; + + enum class AnimationFlags : uint8_t { + NONE = 0, + MOVE = 1, + ROTATE = 2, + QUEUE = 4, + FLY = 8, + IDLE = 16, + INPLACE = 32, + + af_none ZKREM("renamed to AnimationFlags::NONE") = NONE, + af_move ZKREM("renamed to AnimationFlags::MOVE") = MOVE, + af_rotate ZKREM("renamed to AnimationFlags::ROTATE") = ROTATE, + af_queue ZKREM("renamed to AnimationFlags::QUEUE") = QUEUE, + af_fly ZKREM("renamed to AnimationFlags::FLY") = FLY, + af_idle ZKREM("renamed to AnimationFlags::IDLE") = IDLE, + af_inplace ZKREM("renamed to AnimationFlags::INPLACE") = INPLACE, + }; + + [[nodiscard]] ZKAPI bool operator&(AnimationFlags a, AnimationFlags b); + [[nodiscard]] ZKAPI AnimationFlags operator|(AnimationFlags a, AnimationFlags b); + ZKAPI AnimationFlags& operator|=(AnimationFlags& a, AnimationFlags b); + + /// \brief The way the animation is to be played. + enum class AnimationDirection : uint8_t { + FORWARD = 0, ///< The animation samples are played from first to last. + BACKWARD = 1, ///< The animation samples are played from last to first. + + // Deprecated entries. + forward ZKREM("renamed to AnimationDirection::FORWARD") = FORWARD, + backward ZKREM("renamed to AnimationDirection::BACKWARD") = BACKWARD, + }; + + /// \brief The `meshAndTree` tag + /// \remark MDS syntax: `meshAndTree( [DONT_USE_MESH])` + struct MdsSkeleton { + std::string name; + bool disable_mesh {false}; + }; + + /// \brief The `modelTag` tag + /// \remark MDS syntax: `modelTag( )` + struct MdsModelTag { + std::string bone; + }; + + /// \brief The `*eventTag` tag + /// \remark MDS syntax: `*eventTag( [] [] [ATTACH])` + struct MdsEventTag { + std::int32_t frame; + MdsEventType type; + std::string slot {}; + std::string slot2 {}; + std::string item {}; + std::vector frames {}; + MdsFightMode fight_mode {MdsFightMode::NONE}; + bool attached {false}; + }; + + /// \brief The `*eventPFX` tag + /// \remark MDS syntax: `*eventPFX( [] [ATTACH])` + struct MdsParticleEffect { + std::int32_t frame; + std::int32_t index {0}; + std::string name; + std::string position; + bool attached {false}; + }; + + /// \brief The `*eventCamTremor` tag + /// \remark MDS syntax: `*eventCamTremor( )` + struct MdsCameraTremor { + std::int32_t frame {0}; + std::int32_t field1 {0}; + std::int32_t field2 {0}; + std::int32_t field3 {0}; + std::int32_t field4 {0}; + }; + + /// \brief The `*eventPFXStop` tag + /// \remark MDS syntax: `*eventPFXStop( )` + struct MdsParticleEffectStop { + std::int32_t frame; + std::int32_t index; + }; + + /// \brief The `*eventSFX` tag + /// \remark MDS syntax: `*eventSFX( [R:] [EMPTY_SLOT])` + struct MdsSoundEffect { + std::int32_t frame; + std::string name; + float range {1000.0f}; + bool empty_slot {false}; + }; + + /// \brief The `*eventSFXGrnd` tag + /// \remark MDS syntax: `*eventSFXGrnd( )` + struct MdsSoundEffectGround { + std::int32_t frame; + std::string name; + float range {1000.0f}; + bool empty_slot {false}; + }; + + /// \brief The `*eventMMStartAni` tag + /// \remark MDS syntax: `*eventMMStartAni( [])` + struct MdsMorphAnimation { + std::int32_t frame; + std::string animation; + std::string node {}; + }; + + /// \brief The `aniAlias` tag + /// \remark MDS syntax: `ani( + /// [FPS:] [CVS:])` + struct MdsAnimation { + std::string name; + std::uint32_t layer; + std::string next; + float blend_in; + float blend_out; + AnimationFlags flags {AnimationFlags::NONE}; + std::string model; + AnimationDirection direction; + std::int32_t first_frame; + std::int32_t last_frame; + float fps; + float speed; + float collision_volume_scale; + + std::vector events {}; + std::vector pfx {}; + std::vector pfx_stop {}; + std::vector sfx {}; + std::vector sfx_ground {}; + std::vector morph {}; + std::vector tremors {}; }; - namespace mds { - enum class event_tag_type { - unknown, - create_item, - insert_item, - remove_item, - destroy_item, - place_item, - exchange_item, - fight_mode, - place_munition, - remove_munition, - draw_sound, - undraw_sound, - swap_mesh, - draw_torch, - inventory_torch, - drop_torch, - hit_limb, - hit_direction, - dam_multiply, - par_frame, - opt_frame, - hit_end, - window, - }; - - /// \brief A set of fight stances the player can take. - enum class event_fight_mode { - fist, ///< The player fights with his fists. - one_handed, ///< The player wields a one-handed weapon. - two_handed, ///< The player wields a two-handed weapon. - bow, ///< The player wields a bow. - crossbow, ///< The player wields a crossbow. - magic, ///< The player casts a magic spell. - none, ///< The player is not in a fighting stance. - invalid, ///< A fight mode which acts as an `unset` marker. Added for OpenGothic compatibility. - }; - - enum animation_flags : uint8_t { - af_none = 0, - af_move = 1, - af_rotate = 2, - af_queue = 4, - af_fly = 8, - af_idle = 16, - }; - - /// \brief The way the animation is to be played. - enum class animation_direction : uint8_t { - forward = 0, ///< The animation samples are played from first to last. - backward = 1, ///< The animation samples are played from last to first. - }; - - /// \brief The `meshAndTree` tag - /// \remark MDS syntax: `meshAndTree( [DONT_USE_MESH])` - struct skeleton { - std::string name; - bool disable_mesh {false}; - }; - - /// \brief The `modelTag` tag - /// \remark MDS syntax: `modelTag( )` - struct model_tag { - std::string bone; - }; - - /// \brief The `*eventTag` tag - /// \remark MDS syntax: `*eventTag( [] [] [ATTACH])` - struct event_tag { - std::int32_t frame; - event_tag_type type; - std::string slot {}; - std::string slot2 {}; - std::string item {}; - std::vector frames {}; - event_fight_mode fight_mode {event_fight_mode::none}; - bool attached {false}; - }; - - /// \brief The `*eventPFX` tag - /// \remark MDS syntax: `*eventPFX( [] [ATTACH])` - struct event_pfx { - std::int32_t frame; - std::int32_t index {0}; - std::string name; - std::string position; - bool attached {false}; - }; - - /// \brief The `*eventCamTremor` tag - /// \remark MDS syntax: `*eventCamTremor( )` - struct event_camera_tremor { - std::int32_t frame {0}; - std::int32_t field1 {0}; - std::int32_t field2 {0}; - std::int32_t field3 {0}; - std::int32_t field4 {0}; - }; - - /// \brief The `*eventPFXStop` tag - /// \remark MDS syntax: `*eventPFXStop( )` - struct event_pfx_stop { - std::int32_t frame; - std::int32_t index; - }; - - /// \brief The `*eventSFX` tag - /// \remark MDS syntax: `*eventSFX( [R:] [EMPTY_SLOT])` - struct event_sfx { - std::int32_t frame; - std::string name; - float range {1000.0f}; - bool empty_slot {false}; - }; - - /// \brief The `*eventSFXGrnd` tag - /// \remark MDS syntax: `*eventSFXGrnd( )` - struct event_sfx_ground { - std::int32_t frame; - std::string name; - float range {1000.0f}; - bool empty_slot {false}; - }; - - /// \brief The `*eventMMStartAni` tag - /// \remark MDS syntax: `*eventMMStartAni( [])` - struct event_morph_animate { - std::int32_t frame; - std::string animation; - std::string node {}; - }; - - /// \brief The `aniAlias` tag - /// \remark MDS syntax: `ani( - /// [FPS:] [CVS:])` - struct animation { - std::string name; - std::uint32_t layer; - std::string next; - float blend_in; - float blend_out; - animation_flags flags; - std::string model; - animation_direction direction; - std::int32_t first_frame; - std::int32_t last_frame; - float fps; - float speed; - float collision_volume_scale; - - std::vector events {}; - std::vector pfx {}; - std::vector pfx_stop {}; - std::vector sfx {}; - std::vector sfx_ground {}; - std::vector morph {}; - std::vector tremors {}; - }; - - /// \brief The `aniAlias` tag - /// \remark MDS syntax: `aniAlias( [])` - struct animation_alias { - std::string name; - std::uint32_t layer; - std::string next; - float blend_in; - float blend_out; - animation_flags flags; - std::string alias; - animation_direction direction; - }; - - /// \brief The `aniBlend` tag - /// \remark MDS syntax: `aniBlend( [] [ ])` - struct animation_blending { - std::string name; - std::string next; - float blend_in {0}; - float blend_out {0}; - }; - - /// \brief The `aniComb` tag - /// \remark MDS syntax: `aniComb( )` - struct animation_combination { - std::string name; - std::uint32_t layer; - std::string next; - float blend_in; - float blend_out; - animation_flags flags; - std::string model; - std::int32_t last_frame; - }; - - PHOENIX_INTERNAL animation_flags animation_flags_from_string(std::string_view str); - } // namespace mds + /// \brief The `aniAlias` tag + /// \remark MDS syntax: `aniAlias( [])` + struct MdsAnimationAlias { + std::string name; + std::uint32_t layer; + std::string next; + float blend_in; + float blend_out; + AnimationFlags flags {AnimationFlags::NONE}; + std::string alias; + AnimationDirection direction; + }; + + /// \brief The `aniBlend` tag + /// \remark MDS syntax: `aniBlend( [] [ ])` + struct MdsAnimationBlend { + std::string name; + std::string next; + float blend_in {0}; + float blend_out {0}; + }; + + /// \brief The `aniComb` tag + /// \remark MDS syntax: `aniComb( )` + struct MdsAnimationCombine { + std::string name; + std::uint32_t layer; + std::string next; + float blend_in; + float blend_out; + AnimationFlags flags {AnimationFlags::NONE}; + std::string model; + std::int32_t last_frame; + }; /// \brief Represents a *ZenGin* model script. /// ///

    Model scripts contain animations related to a model and actions the animation controller should take during /// or after an animation plays (such as playing a sound).

    - class model_script { + class ModelScript { public: /// \brief Parses a model script from the data in the given buffer. /// \param[in,out] buf The buffer to read from. @@ -220,45 +272,26 @@ namespace phoenix { /// \note After this function returns the position of \p buf will be at the end of the parsed object. /// If you would like to keep your buffer immutable, consider passing a copy of it to #parse(buffer&&) /// using buffer::duplicate. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static model_script parse(buffer& buf); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static ModelScript parse(phoenix::buffer& buf); /// \brief Parses a model script from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed model script. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static model_script parse(buffer&& buf) { - return model_script::parse(buf); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static ModelScript parse(phoenix::buffer&& buf); - /// \brief Parses a compiled model script from the data in the given buffer. - /// \param[in,out] buf The buffer to read from. - /// \return The parsed model script. - /// \note After this function returns the position of \p buf will be at the end of the parsed object. - /// If you would like to keep your buffer immutable, consider passing a copy of it to #parse(buffer&&) - /// using buffer::duplicate. - /// \throws parser_error if parsing fails. - /// \deprecated model_script::parse can now handle both binary and text file types. - /// \see #parse_binary(buffer&&) - [[nodiscard]] PHOENIX_DEPRECATED("use model_script::parse()") PHOENIX_API static model_script - parse_binary(buffer& buf); + ZKAPI void load(Read* r); - // \brief Parses a compiled model script from the data in the given buffer. - /// \param[in] buf The buffer to read from (by rvalue-reference). - /// \return The parsed model script. - /// \throws parser_error if parsing fails. - /// \deprecated model_script::parse can now handle both binary and text file types. - /// \see #parse_binary(buffer&) - [[nodiscard]] PHOENIX_DEPRECATED("use model_script::parse()") PHOENIX_API inline static model_script - parse_binary(buffer&& buf) { - return model_script::parse(buf); - } + private: + ZKINT void load_binary(Read* r); + ZKINT void load_source(Read* r); public: /// \brief The model skeleton this model script was made for. - mds::skeleton skeleton {}; + MdsSkeleton skeleton {}; /// \brief A list of meshes which can be used with this model script. std::vector meshes {}; @@ -266,10 +299,10 @@ namespace phoenix { /// \brief A list of animation names which are disabled. std::vector disabled_animations {}; - std::vector combinations {}; - std::vector blends {}; - std::vector aliases {}; - std::vector model_tags {}; - std::vector animations {}; + std::vector combinations {}; + std::vector blends {}; + std::vector aliases {}; + std::vector model_tags {}; + std::vector animations {}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/MorphMesh.hh b/include/zenkit/MorphMesh.hh index 4cf9f2d7..43f46e54 100644 --- a/include/zenkit/MorphMesh.hh +++ b/include/zenkit/MorphMesh.hh @@ -1,14 +1,25 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include -#include -#include +#include "zenkit/Date.hh" +#include "zenkit/Library.hh" +#include "zenkit/MultiResolutionMesh.hh" + +#include + +#include +#include +#include namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; + /// \brief An animation used by morph meshes - struct morph_animation { + struct MorphAnimation { /// \brief The name of the animation. std::string name; @@ -33,9 +44,9 @@ namespace phoenix { }; /// \brief A reference to a morph mesh source file. - struct morph_source { + struct MorphSource { /// \brief The date of file creation. - date file_date; + Date file_date; /// \brief The name of the source file. std::string file_name; @@ -45,7 +56,7 @@ namespace phoenix { /// ///

    Morph meshes represents meshes which can deform using a set of animations. With these meshes, the positions /// of the vertices of the underlying phoenix::proto_mesh are actually changed while an animation plays.

    - class morph_mesh { + class MorphMesh { public: /// \brief Parses a morph mesh from the data in the given buffer. /// \param[in,out] buf The buffer to read from. @@ -55,31 +66,31 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static morph_mesh parse(buffer& buf); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static MorphMesh parse(phoenix::buffer& buf); /// \brief Parses a morph mesh from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed morph mesh. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static morph_mesh parse(buffer&& buf) { - return morph_mesh::parse(buf); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static MorphMesh parse(phoenix::buffer&& buf); + + ZKAPI void load(Read* r); public: /// \brief The name of the mesh. std::string name {}; /// \brief The underlying mesh. - proto_mesh mesh {}; + MultiResolutionMesh mesh {}; /// \brief All morph positions associated with the mesh. std::vector morph_positions {}; /// \brief All animations associated with the mesh. - std::vector animations {}; + std::vector animations {}; /// \brief A list of source files this morph mesh was compiled from. - std::vector sources {}; + std::vector sources {}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/MultiResolutionMesh.hh b/include/zenkit/MultiResolutionMesh.hh index 12703825..52af2fc0 100644 --- a/include/zenkit/MultiResolutionMesh.hh +++ b/include/zenkit/MultiResolutionMesh.hh @@ -1,83 +1,89 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Library.hh" +#include "zenkit/Material.hh" + +#include + +#include +#include namespace phoenix { - struct triangle { + class buffer; +} + +namespace zenkit { + class Read; + + struct MeshTriangle { std::uint16_t wedges[3]; }; - struct triangle_edge { + struct MeshTriangleEdge { std::uint16_t edges[3]; }; - struct edge { + struct MeshEdge { std::uint16_t edges[2]; }; - struct wedge { + struct MeshWedge { glm::vec3 normal; glm::vec2 texture; std::uint16_t index; }; - struct plane { + struct MeshPlane { float distance; glm::vec3 normal; }; /// \brief An offset and size tuple for mesh sections. - struct mesh_section { - std::uint32_t offset; + struct MeshSection { + std::size_t offset; std::uint32_t size; }; /// \brief Offsets and sizes of binary data sections containing sub-mesh data. /// \note This is only of use phoenix-internally. - struct sub_mesh_section { - mesh_section triangles; - mesh_section wedges; - mesh_section colors; - mesh_section triangle_plane_indices; - mesh_section triangle_planes; - mesh_section wedge_map; - mesh_section vertex_updates; - mesh_section triangle_edges; - mesh_section edges; - mesh_section edge_scores; + struct SubMeshSection { + MeshSection triangles; + MeshSection wedges; + MeshSection colors; + MeshSection triangle_plane_indices; + MeshSection triangle_planes; + MeshSection wedge_map; + MeshSection vertex_updates; + MeshSection triangle_edges; + MeshSection edges; + MeshSection edge_scores; }; /// \brief Represents a sub-mesh. - struct sub_mesh { + struct SubMesh { /// \brief The material of this sub mesh. - material mat; + Material mat; - std::vector triangles; - std::vector wedges; + std::vector triangles; + std::vector wedges; std::vector colors; std::vector triangle_plane_indices; - std::vector triangle_planes; - std::vector triangle_edges; - std::vector edges; + std::vector triangle_planes; + std::vector triangle_edges; + std::vector edges; std::vector edge_scores; std::vector wedge_map; - /// \brief Reads sub-mesh data from a reader. - /// \param in The reader to read from - /// \param map A a section map for the sub-mesh. - /// \return The sub-mesh read. - [[nodiscard]] PHOENIX_INTERNAL static sub_mesh parse(buffer& in, const sub_mesh_section& map); + ZKINT void load(Read* r, const SubMeshSection& map); }; /// \brief Represents a *ZenGin* proto mesh. /// ///

    A proto mesh is a mesh which is made up of multiple sub-meshes. Each sub-mesh has its own material and /// related values.

    - class proto_mesh { + class MultiResolutionMesh { public: /// \brief Parses a proto mesh from the data in the given buffer. /// \param[in,out] buf The buffer to read from. @@ -87,29 +93,17 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static proto_mesh parse(buffer& in); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static MultiResolutionMesh parse(phoenix::buffer& in); /// \brief Parses a proto mesh from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed proto mesh. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API static proto_mesh parse(buffer&& in) { - return proto_mesh::parse(in); - } - - /// \brief Parses a proto mesh directly from the given buffer. - /// - /// This function assumes that the caller has already parsed part of the file and should only be used if you - /// know what you're doing. If you just want to parse a basic proto mesh, please use #parse. - /// - /// \param[in,out] buf The buffer to read from. - /// \return The parsed proto mesh. - /// \note After this function returns the position of \p buf will be at the end of the parsed object. - /// If you would like to keep your buffer immutable, consider passing a copy of it to #parse(buffer&&) - /// using buffer::duplicate. - /// \throws parser_error if parsing fails. - [[nodiscard]] PHOENIX_INTERNAL static proto_mesh parse_from_section(buffer& in); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static MultiResolutionMesh parse(phoenix::buffer&& in); + + ZKAPI void load(Read* r); + ZKINT void load_from_section(Read* r); public: /// \brief The vertex positions associated with the mesh. @@ -119,17 +113,17 @@ namespace phoenix { std::vector normals; /// \brief A list of sub-meshes of the mesh. - std::vector sub_meshes; + std::vector sub_meshes; /// \brief A list of all materials used by the mesh. - std::vector materials; + std::vector materials; /// \brief If alpha testing should be enabled. std::uint8_t alpha_test {true}; /// \brief The bounding box of the mesh. - bounding_box bbox; + AxisAlignedBoundingBox bbox; - obb obbox; + OrientedBoundingBox obbox; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/SaveGame.hh b/include/zenkit/SaveGame.hh index 4ac98dcb..50a0423c 100644 --- a/include/zenkit/SaveGame.hh +++ b/include/zenkit/SaveGame.hh @@ -1,21 +1,31 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include -#include +#include "zenkit/Library.hh" +#include "zenkit/Stream.hh" +#include "zenkit/Texture.hh" +#include #include #include #include +#include -namespace phoenix::unstable { +namespace phoenix { + class buffer; +} + +namespace zenkit { + class Read; +} + +namespace zenkit::unstable { /// \brief Contains general information about a save-game. /// /// The information contained within this struct is found in the `SAVEINFO.SAV` file within /// the save-game folder. It contains metadata about the save-game, like a title and the game /// version it was created with. - struct save_info { + struct SaveInfo { std::string title; std::string world; std::int32_t time_day; @@ -32,70 +42,65 @@ namespace phoenix::unstable { /// \brief Parses a save-game info structure from the data in the given buffer. /// \param buf The buffer to read from. /// \return The parsed save-game info structure. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \note Instead of calling this directly, you probably want to parse a whole save-game. /// Use save_game::parse for that instead. - PHOENIX_API static save_info parse(buffer&& buf); + ZKREM("use ::load()") ZKAPI static SaveInfo parse(phoenix::buffer&& buf); + + ZKAPI void load(Read* r); }; /// \brief The section log entries are in. - enum class topic_section : std::uint32_t { - quests = 0x00, - infos = 0x01, + enum class SaveTopicSection : std::uint32_t { + QUESTS = 0x00, + INFOS = 0x01, }; /// \brief The status of a single log entry. - enum class topic_status : std::uint32_t { - active = 0x01, - completed = 0x02, - failed = 0x03, - obsolete = 0x04, + enum class SaveTopicStatus : std::uint32_t { + ACTIVE = 0x01, + COMPLETED = 0x02, + FAILED = 0x03, + OBSOLETE = 0x04, }; - struct log_topic { + struct SaveLogTopic { std::string description; - topic_section section; - topic_status status; + SaveTopicSection section; + SaveTopicStatus status; std::vector entries; }; - struct info_state { + struct SaveInfoState { std::string name; bool told; }; - struct symbol_state { + struct SaveSymbolState { std::string name; std::vector values; }; - struct script_state { + struct SaveScriptState { std::int32_t day; std::int32_t hour; std::int32_t minute; - std::vector infos; - std::vector symbols; - std::vector log; + std::vector infos; + std::vector symbols; + std::vector log; std::uint8_t guild_attitudes[42][42]; - /// \brief Parses a save-game script state structure from the data in the given buffer. - /// \param buf The buffer to read from. - /// \param g2 Set to true if a Gothic II save-game is being parsed. - /// \return The parsed save-game script state structure. - /// \throws parser_error if parsing fails. - /// \note Instead of calling this directly, you probably want to parse a whole save-game. - /// Use save_game::parse for that instead. - PHOENIX_INTERNAL static script_state parse(buffer&& buf, bool g2); + ZKINT void load(Read* r, bool g2); }; - struct save_game { + struct SaveGame { public: /// \brief Parses a save-game from the data in the given directory. /// \param path The path of the save-game folder. /// \return The parsed save-game. - /// \throws parser_error if parsing fails. - PHOENIX_API static save_game parse(const std::filesystem::path& path); + /// \throws ParserError if parsing fails. + [[nodiscard]] ZKREM("use ::load()") ZKAPI static SaveGame parse(const std::filesystem::path& path); /// \brief Opens the saved world file with the given world name as a buffer and returns it. /// @@ -104,11 +109,20 @@ namespace phoenix::unstable { /// /// \param world_name The name of the world to open, including the `.ZEN` extension. /// \return A buffer containing the world's data or std::nullopt if the world is not present in this save. - PHOENIX_API std::optional open_world_save(std::string_view world_name) const; + [[nodiscard]] ZKREM("use ::load_world()") ZKAPI std::optional open_world_save( + std::string_view world_name) const; + + [[nodiscard]] ZKAPI std::optional> open_world(std::string_view world_name) const; + + /// \brief Parses a save-game from the data in the given directory. + /// \param path The path of the save-game folder. + /// \return The parsed save-game. + /// \throws ParserError if parsing fails. + ZKAPI void load(const std::filesystem::path& path); public: /// \brief Contains metadata about the save-game, like its name and version numbers. - save_info metadata; + SaveInfo metadata; /// \brief The name of the world file the player is currently in. std::string current_world; @@ -118,12 +132,12 @@ namespace phoenix::unstable { /// Contains the states of relevant script symbols, the state of the quest log, /// as well as the state of each piece of in-game information and the attitudes /// of guilds towards each other. - script_state script; + SaveScriptState script; /// \brief The thumbnail image of the save-game. Not present if `THUMB.SAV` is missing from the save-game. - std::optional thumbnail; + std::optional thumbnail; private: std::filesystem::path _m_root_path; }; -} // namespace phoenix::unstable +} // namespace zenkit::unstable diff --git a/include/zenkit/SoftSkinMesh.hh b/include/zenkit/SoftSkinMesh.hh index 926fe5f9..a60fd2e4 100644 --- a/include/zenkit/SoftSkinMesh.hh +++ b/include/zenkit/SoftSkinMesh.hh @@ -1,25 +1,35 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Library.hh" +#include "zenkit/MultiResolutionMesh.hh" + +#include + +#include +#include namespace phoenix { - struct wedge_normal { + class buffer; +} + +namespace zenkit { + class Read; + + struct SoftSkinWedgeNormal { glm::vec3 normal; std::uint32_t index; }; - struct weight_entry { + struct SoftSkinWeightEntry { float weight; glm::vec3 position; std::uint8_t node_index; }; /// \brief Represents a soft-skin mesh. - class softskin_mesh { + class SoftSkinMesh { public: /// \brief Parses a soft-skin mesh from the data in the given buffer. /// \param[in,out] buf The buffer to read from. @@ -29,32 +39,31 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static softskin_mesh parse(buffer& in); + [[nodiscard]] ZKREM(":: load()") ZKAPI static SoftSkinMesh parse(phoenix::buffer& in); /// \brief Parses a soft-skin mesh from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). /// \return The parsed soft-skin mesh. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static softskin_mesh parse(buffer&& in) { - return softskin_mesh::parse(in); - } + [[nodiscard]] ZKREM(":: load()") ZKAPI static SoftSkinMesh parse(phoenix::buffer&& in); + + ZKAPI void load(Read* r); public: /// \brief The embedded proto-mesh. - proto_mesh mesh; + MultiResolutionMesh mesh; /// \brief The meshes bounding boxes (there is one for each node). - std::vector bboxes; + std::vector bboxes; /// \brief A list of wedge normals. - std::vector wedge_normals; + std::vector wedge_normals; /// \brief Node weights. - std::vector> weights; + std::vector> weights; /// \brief Nodes. std::vector nodes; }; - -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/Stream.hh b/include/zenkit/Stream.hh new file mode 100644 index 00000000..577a0a20 --- /dev/null +++ b/include/zenkit/Stream.hh @@ -0,0 +1,167 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Library.hh" +#include "zenkit/Logger.hh" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace phoenix { + class buffer; +} + +namespace zenkit { + + // Windows does not define ssize_t +#ifdef _WIN32 + typedef std::ptrdiff_t ssize_t; +#endif + + enum class Whence { BEG = 0x00, CUR = 0x01, END = 0x02 }; + + class Read ZKAPI { + public: + virtual ~Read() noexcept = default; + + [[nodiscard]] char read_char() noexcept; + [[nodiscard]] int8_t read_byte() noexcept; + [[nodiscard]] uint8_t read_ubyte() noexcept; + [[nodiscard]] int16_t read_short() noexcept; + [[nodiscard]] uint16_t read_ushort() noexcept; + [[nodiscard]] int32_t read_int() noexcept; + [[nodiscard]] uint32_t read_uint() noexcept; + [[nodiscard]] float read_float() noexcept; + [[nodiscard]] std::string read_string(size_t len) noexcept; + [[nodiscard]] std::string read_line(bool skipws) noexcept; + [[nodiscard]] glm::vec2 read_vec2() noexcept; + [[nodiscard]] glm::vec3 read_vec3() noexcept; + [[nodiscard]] glm::mat3 read_mat3() noexcept; + [[nodiscard]] glm::mat4 read_mat4() noexcept; + + [[nodiscard]] virtual std::string read_line_then_ignore(std::string_view chars) noexcept; + + virtual size_t read(void* buf, size_t len) noexcept = 0; + virtual void seek(ssize_t off, Whence whence) noexcept = 0; + [[nodiscard]] virtual size_t tell() const noexcept = 0; + [[nodiscard]] virtual bool eof() const noexcept = 0; + + [[nodiscard]] static std::unique_ptr from(FILE* stream); + [[nodiscard]] static std::unique_ptr from(std::istream* stream); + [[nodiscard]] static std::unique_ptr from(std::byte const* bytes, size_t len); + [[nodiscard]] static std::unique_ptr from(std::vector const* vector); + [[nodiscard]] static std::unique_ptr from(std::vector vector); + [[nodiscard]] static std::unique_ptr from(std::filesystem::path const& path); + [[nodiscard]] ZKREM("deprecated") static std::unique_ptr from(phoenix::buffer* buf); + }; + + class Write ZKAPI { + public: + virtual ~Write() noexcept = default; + + void write_char(char v) noexcept; + void write_byte(int8_t v) noexcept; + void write_ubyte(uint8_t v) noexcept; + void write_short(int16_t v) noexcept; + void write_ushort(uint16_t v) noexcept; + void write_int(int32_t v) noexcept; + void write_uint(uint32_t v) noexcept; + void write_float(float v) noexcept; + void write_string(std::string_view v) noexcept; + void write_string0(std::string_view v) noexcept; + void write_line(std::string_view v) noexcept; + void write_vec2(glm::vec2 const& v) noexcept; + void write_vec3(glm::vec3 const& v) noexcept; + void write_mat3(glm::mat3 const& v) noexcept; + void write_mat4(glm::mat4 const& v) noexcept; + + virtual size_t write(void const* buf, size_t len) noexcept = 0; + virtual void seek(ssize_t off, Whence whence) noexcept = 0; + [[nodiscard]] virtual size_t tell() const noexcept = 0; + + [[nodiscard]] static std::unique_ptr to(::FILE* stream); + [[nodiscard]] static std::unique_ptr to(std::ostream* stream); + [[nodiscard]] static std::unique_ptr to(std::byte* bytes, size_t len); + [[nodiscard]] static std::unique_ptr to(std::vector* vector); + }; + + namespace proto { + template + inline std::enable_if_t, void> + read_chunked(Read* r, char const* name, std::function cb) { + do { + auto type = static_cast(r->read_ushort()); + auto size = r->read_uint(); + auto target = r->tell() + size; + + auto done = cb(r, type); + + auto pos = r->tell(); + if (pos < target) { + Logger::log(LogLevel::WARNING, name, "%zu bytes remaining in section %hx", target - pos, type); + } else if (r->tell() > target) { + Logger::log(LogLevel::ERROR, name, "%zu bytes overflowed in section %hx", target - pos, type); + } + + // Make sure we always place the cursor at the position of the next chunk. + r->seek(static_cast(target), Whence::BEG); + + if (done) { + break; + } + } while (!r->eof()); + } + + template + inline std::enable_if_t, void> + read_chunked(Read* r, char const* name, std::function cb) { + do { + auto type = static_cast(r->read_ushort()); + auto size = r->read_uint(); + auto target = r->tell() + size; + + auto done = cb(r, type, target); + + auto pos = r->tell(); + if (pos < target) { + Logger::log(LogLevel::WARNING, name, "%zu bytes remaining in section %hx", target - pos, type); + } else if (r->tell() > target) { + Logger::log(LogLevel::ERROR, name, "%zu bytes overflowed in section %hx", target - pos, type); + } + + // Make sure we always place the cursor at the position of the next chunk. + r->seek(static_cast(target), Whence::BEG); + + if (done) { + break; + } + } while (!r->eof()); + } + + template + inline std::enable_if_t, void> write_chunk(Write* w, T v, std::function cb) { + w->write_ushort(static_cast(v)); + + auto size_off = w->tell(); + w->write_uint(0); + + cb(w); + + auto len = w->tell() - size_off; + w->seek(size_off, Whence::BEG); + w->write_uint(len - sizeof(uint32_t)); + w->seek(len, Whence::CUR); + } + + } // namespace proto +} // namespace zenkit diff --git a/include/zenkit/Texture.hh b/include/zenkit/Texture.hh index 0fe5affa..0a73829d 100644 --- a/include/zenkit/Texture.hh +++ b/include/zenkit/Texture.hh @@ -1,49 +1,73 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include +#include "zenkit/Library.hh" +#include +#include +#include #include namespace phoenix { - constexpr const std::string_view ZTEX_SIGNATURE = "ZTEX"; + class buffer; +} + +namespace zenkit { + class Read; + constexpr const std::uint16_t ZTEX_PALETTE_ENTRIES = 0x100; /// \brief Texture formats used by the ZenGin. - enum texture_format { - tex_B8G8R8A8 = 0x0, ///< \brief 32-bit ARGB pixel format with alpha, using 8 bits per channel - tex_R8G8B8A8 = 0x1, ///< \brief 32-bit ARGB pixel format with alpha, using 8 bits per channel - tex_A8B8G8R8 = 0x2, ///< \brief 32-bit ARGB pixel format with alpha, using 8 bits per channel - tex_A8R8G8B8 = 0x3, ///< \brief 32-bit ARGB pixel format with alpha, using 8 bits per channel - tex_B8G8R8 = 0x4, ///< \brief 24-bit RGB pixel format with 8 bits per channel - tex_R8G8B8 = 0x5, ///< \brief 24-bit RGB pixel format with 8 bits per channel - tex_A4R4G4B4 = 0x6, ///< \brief 16-bit ARGB pixel format with 4 bits for each channel - tex_A1R5G5B5 = 0x7, ///< \brief 16-bit pixel format where 5 bits are reserved for each color - ///< and 1 bit is reserved for alpha - tex_R5G6B5 = 0x8, ///< \brief 16-bit RGB pixel format with 5 bits for red, 6 bits for green, - ///< and 5 bits for blue - tex_p8 = 0x9, ///< \brief 8-bit color indexed - tex_dxt1 = 0xA, ///< \brief DXT1 compression texture format - tex_dxt2 = 0xB, ///< \brief DXT2 compression texture format - tex_dxt3 = 0xC, ///< \brief DXT3 compression texture format - tex_dxt4 = 0xD, ///< \brief DXT4 compression texture format - tex_dxt5 = 0xE, ///< \brief DXT5 compression texture format + enum TextureFormat { + B8G8R8A8 = 0x0, ///< \brief 32-bit ARGB pixel format with alpha, using 8 bits per channel + R8G8B8A8 = 0x1, ///< \brief 32-bit ARGB pixel format with alpha, using 8 bits per channel + A8B8G8R8 = 0x2, ///< \brief 32-bit ARGB pixel format with alpha, using 8 bits per channel + A8R8G8B8 = 0x3, ///< \brief 32-bit ARGB pixel format with alpha, using 8 bits per channel + B8G8R8 = 0x4, ///< \brief 24-bit RGB pixel format with 8 bits per channel + R8G8B8 = 0x5, ///< \brief 24-bit RGB pixel format with 8 bits per channel + A4R4G4B4 = 0x6, ///< \brief 16-bit ARGB pixel format with 4 bits for each channel + A1R5G5B5 = 0x7, ///< \brief 16-bit pixel format where 5 bits are reserved for each color and 1 bit is reserved + ///< for alpha + R5G6B5 = 0x8, ///< \brief 16-bit RGB pixel format with 5 bits for red, 6 bits for green, and 5 bits for blue + P8 = 0x9, ///< \brief 8-bit color indexed + DXT1 = 0xA, ///< \brief DXT1 compression texture format + DXT2 = 0xB, ///< \brief DXT2 compression texture format + DXT3 = 0xC, ///< \brief DXT3 compression texture format + DXT4 = 0xD, ///< \brief DXT4 compression texture format + DXT5 = 0xE, ///< \brief DXT5 compression texture format + + // Deprecated entries. + tex_B8G8R8A8 ZKREM("renamed to TextureFormat::B8G8R8A8") = B8G8R8A8, + tex_R8G8B8A8 ZKREM("renamed to TextureFormat::R8G8B8A8") = R8G8B8A8, + tex_A8B8G8R8 ZKREM("renamed to TextureFormat::A8B8G8R8") = A8B8G8R8, + tex_A8R8G8B8 ZKREM("renamed to TextureFormat::A8R8G8B8") = A8R8G8B8, + tex_B8G8R8 ZKREM("renamed to TextureFormat::B8G8R8") = B8G8R8, + tex_R8G8B8 ZKREM("renamed to TextureFormat::R8G8B8") = R8G8B8, + tex_A4R4G4B4 ZKREM("renamed to TextureFormat::A4R4G4B4") = A4R4G4B4, + tex_A1R5G5B5 ZKREM("renamed to TextureFormat::A1R5G5B5") = A1R5G5B5, + tex_R5G6B5 ZKREM("renamed to TextureFormat::R5G6B5") = R5G6B5, + tex_p8 ZKREM("renamed to TextureFormat::P8") = P8, + tex_dxt1 ZKREM("renamed to TextureFormat::DXT1") = DXT1, + tex_dxt2 ZKREM("renamed to TextureFormat::DXT2") = DXT2, + tex_dxt3 ZKREM("renamed to TextureFormat::DXT3") = DXT3, + tex_dxt4 ZKREM("renamed to TextureFormat::DXT4") = DXT4, + tex_dxt5 ZKREM("renamed to TextureFormat::DXT5") = DXT5, }; /// \brief Simple ARGB quad. - struct argb { + struct ColorARGB { std::uint8_t a, r, g, b; }; /// \brief Represents a ZenGin texture. - class texture { + class Texture { public: - texture(texture&&) = default; - texture(texture const&) = default; + Texture() = default; + Texture(Texture&&) = default; + Texture(Texture const&) = default; - texture& operator=(texture&&) = default; - texture& operator=(texture const&) = default; + Texture& operator=(Texture&&) = default; + Texture& operator=(Texture const&) = default; /// \brief Parses a texture from the data in the given buffer. /// @@ -57,72 +81,72 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static texture parse(buffer& in); + [[nodiscard]] ZKREM("use ::load") ZKAPI static Texture parse(phoenix::buffer& in); /// \brief Parses a texture from the data in the given buffer. /// \param[in,out] buf The buffer to read from (by rvalue-reference). /// \return The parsed texture. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static texture parse(buffer&& in) { - return parse(in); - } + [[nodiscard]] ZKREM("use ::load") ZKAPI static Texture parse(phoenix::buffer&& in); + + ZKAPI void load(Read* r); /// \return The format of the texture. - [[nodiscard]] PHOENIX_API inline texture_format format() const noexcept { + [[nodiscard]] ZKAPI inline TextureFormat format() const noexcept { return _m_format; } /// \return The width in pixels of the first mipmap level. - [[nodiscard]] PHOENIX_API inline std::uint32_t width() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t width() const noexcept { return _m_width; } /// \return The height in pixels of the first mipmap level. - [[nodiscard]] PHOENIX_API inline std::uint32_t height() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t height() const noexcept { return _m_height; } /// \param level The mipmap level to use (beginning from 0). /// \return The width in pixels of the given mipmap level. - [[nodiscard]] PHOENIX_API inline std::uint32_t mipmap_width(std::uint32_t level) const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t mipmap_width(std::uint32_t level) const noexcept { return _m_width >> level; } /// \param level The mipmap level to use (beginning from 0). /// \return The height in pixels of the given mipmap level. - [[nodiscard]] PHOENIX_API inline std::uint32_t mipmap_height(std::uint32_t level) const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t mipmap_height(std::uint32_t level) const noexcept { return _m_height >> level; } /// \return The width of the texture in-engine. - [[nodiscard]] PHOENIX_API inline std::uint32_t ref_width() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t ref_width() const noexcept { return _m_reference_width; } /// \return The height of the texture in-engine. - [[nodiscard]] PHOENIX_API inline std::uint32_t ref_height() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t ref_height() const noexcept { return _m_reference_height; } /// \return The number of mipmaps of the texture. - [[nodiscard]] PHOENIX_API inline std::uint32_t mipmaps() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t mipmaps() const noexcept { return _m_mipmap_count; } /// \return The average color of the texture. - [[nodiscard]] PHOENIX_API inline std::uint32_t average_color() const noexcept { + [[nodiscard]] ZKAPI inline std::uint32_t average_color() const noexcept { return _m_average_color; } /// \return The palette of the texture. - [[nodiscard]] PHOENIX_API inline argb* palette() const noexcept { - return (argb*) _m_palette; + [[nodiscard]] ZKAPI inline ColorARGB* palette() const noexcept { + return (ColorARGB*) _m_palette; } /// \param mipmap_level The mipmap level to get. /// \return The texture data at the given mipmap level. - [[nodiscard]] PHOENIX_API inline const std::vector& + [[nodiscard]] ZKAPI inline const std::vector& data(std::uint32_t mipmap_level = 0) const noexcept { return _m_textures.at(_m_mipmap_count - 1 - mipmap_level); } @@ -131,14 +155,11 @@ namespace phoenix { /// \param mipmap_level The mipmap level of the texture to convert /// \return The converted texture data. /// \attention This method is very expensive as it allocates a new buffer and copies the internal data into it. - [[nodiscard]] PHOENIX_API std::vector as_rgba8(std::uint32_t mipmap_level = 0) const; - - private: - texture() = default; + [[nodiscard]] ZKAPI std::vector as_rgba8(std::uint32_t mipmap_level = 0) const; private: - texture_format _m_format {}; - argb _m_palette[ZTEX_PALETTE_ENTRIES] {}; + TextureFormat _m_format {}; + ColorARGB _m_palette[ZTEX_PALETTE_ENTRIES] {}; std::uint32_t _m_width {}; std::uint32_t _m_height {}; std::uint32_t _m_reference_width {}; @@ -149,4 +170,4 @@ namespace phoenix { // Quirk: largest mipmap (level 0) stored at the end of the vector std::vector> _m_textures; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/Vfs.hh b/include/zenkit/Vfs.hh index 243eeaec..63657806 100644 --- a/include/zenkit/Vfs.hh +++ b/include/zenkit/Vfs.hh @@ -1,30 +1,35 @@ -// Copyright © 2023 Luis Michaelis +// Copyright © 2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include "buffer.hh" -#include "math.hh" +#include "Library.hh" + +#include "phoenix/buffer.hh" +#include "phoenix/phoenix.hh" + +#include #include +#include #include #include #include #include +#include -namespace phoenix { - class VfsBrokenDiskError : public error { +namespace zenkit { + class VfsBrokenDiskError : public zenkit::Error { public: - PHOENIX_INTERNAL explicit VfsBrokenDiskError(std::string const& signature); + ZKINT explicit VfsBrokenDiskError(std::string const& signature); }; - class VfsFileExistsError : public error { + class VfsFileExistsError : public zenkit::Error { public: - PHOENIX_INTERNAL explicit VfsFileExistsError(std::string const& name); + ZKINT explicit VfsFileExistsError(std::string const& name); }; - class VfsNotFoundError : public error { + class VfsNotFoundError : public zenkit::Error { public: - PHOENIX_INTERNAL explicit VfsNotFoundError(std::string const& name); + ZKINT explicit VfsNotFoundError(std::string const& name); }; enum class VfsNodeType { @@ -32,45 +37,57 @@ namespace phoenix { FILE = 2, }; + struct VfsFileDescriptor { + std::byte const* memory; + std::size_t size; + }; + class VfsNode; struct VfsNodeComparator { using is_transparent = std::true_type; - PHOENIX_API [[nodiscard]] bool operator()(const VfsNode& a, const VfsNode& b) const noexcept; - PHOENIX_API [[nodiscard]] bool operator()(const VfsNode& a, std::string_view b) const noexcept; - PHOENIX_API [[nodiscard]] bool operator()(std::string_view a, const VfsNode& b) const noexcept; + ZKAPI [[nodiscard]] bool operator()(const VfsNode& a, const VfsNode& b) const noexcept; + ZKAPI [[nodiscard]] bool operator()(const VfsNode& a, std::string_view b) const noexcept; + ZKAPI [[nodiscard]] bool operator()(std::string_view a, const VfsNode& b) const noexcept; }; class VfsNode { + using ChildContainer = std::set; + public: - PHOENIX_API [[nodiscard]] VfsNodeType type() const noexcept; - PHOENIX_API [[nodiscard]] std::time_t time() const noexcept; - PHOENIX_API [[nodiscard]] std::string const& name() const noexcept; + [[nodiscard]] ZKAPI VfsNodeType type() const noexcept; + [[nodiscard]] ZKAPI std::time_t time() const noexcept; + [[nodiscard]] ZKAPI std::string const& name() const noexcept; - PHOENIX_API [[nodiscard]] std::vector const& children() const; - PHOENIX_API [[nodiscard]] VfsNode const* child(std::string_view name) const; - PHOENIX_API [[nodiscard]] VfsNode* child(std::string_view name); + [[nodiscard]] ZKAPI ChildContainer const& children() const; + [[nodiscard]] ZKAPI VfsNode const* child(std::string_view name) const; + [[nodiscard]] ZKAPI VfsNode* child(std::string_view name); - PHOENIX_API VfsNode* create(VfsNode node); - PHOENIX_API bool remove(std::string_view name); + ZKAPI VfsNode* create(VfsNode node); + ZKAPI bool remove(std::string_view name); - PHOENIX_API [[nodiscard]] buffer open() const; + [[nodiscard]] ZKREM("use ::open_read()") ZKAPI phoenix::buffer open() const; + [[nodiscard]] ZKAPI std::unique_ptr open_read() const; - PHOENIX_API [[nodiscard]] static VfsNode directory(std::string_view name); - PHOENIX_API [[nodiscard]] static VfsNode file(std::string_view name, buffer dev); + [[nodiscard]] ZKAPI static VfsNode directory(std::string_view name); + [[nodiscard]] ZKREM("Deprecated") ZKAPI static VfsNode file(std::string_view name, phoenix::buffer dev); + [[nodiscard]] ZKAPI static VfsNode file(std::string_view name, VfsFileDescriptor dev); - PHOENIX_API [[nodiscard]] static VfsNode directory(std::string_view name, std::time_t ts); - PHOENIX_API [[nodiscard]] static VfsNode file(std::string_view name, buffer dev, std::time_t ts); + [[nodiscard]] ZKAPI static VfsNode directory(std::string_view name, std::time_t ts); + [[nodiscard]] ZKREM("Deprecated") ZKAPI static VfsNode + file(std::string_view name, phoenix::buffer dev, std::time_t ts); + [[nodiscard]] ZKAPI static VfsNode file(std::string_view name, VfsFileDescriptor dev, std::time_t ts); protected: - PHOENIX_API explicit VfsNode(std::string_view name, std::time_t ts); - PHOENIX_API explicit VfsNode(std::string_view name, buffer dev, std::time_t ts); + ZKAPI explicit VfsNode(std::string_view name, std::time_t ts); + ZKREM("Deprecated") ZKAPI explicit VfsNode(std::string_view name, phoenix::buffer dev, std::time_t ts); + ZKAPI explicit VfsNode(std::string_view name, VfsFileDescriptor dev, std::time_t ts); private: std::string _m_name; std::time_t _m_time; - std::variant, buffer> _m_data; + std::variant _m_data; }; enum class VfsOverwriteBehavior { @@ -83,22 +100,22 @@ namespace phoenix { /// \brief An implementation of the virtual file system. class Vfs { public: - PHOENIX_API Vfs(); + ZKAPI Vfs(); /// \brief Get the root node of the file system structure. /// \return The root node of the file system structure. - PHOENIX_API [[nodiscard]] VfsNode const& root() const noexcept; + [[nodiscard]] ZKAPI VfsNode const& root() const noexcept; /// \brief Create all missing directories in the given path. /// \param path The path of the directory to create. /// \return The newly created directory. /// \throws VfsFileExistsError if a part of the given path already exists and is a file. - PHOENIX_API VfsNode& mkdir(std::string_view path); + ZKAPI VfsNode& mkdir(std::string_view path); /// \brief Delete the file or directory at the given path /// \param path The path of the node to delete. /// \return `true` if removal was successful and `false` if not (ie. the file could not be found). - PHOENIX_API bool remove(std::string_view path); + ZKAPI bool remove(std::string_view path); /// \brief Mount the given file system node into the given directory. /// @@ -110,9 +127,9 @@ namespace phoenix { /// \param overwrite The behavior of the system when conflicting files are found. /// \throws VfsNotFoundError if the given \p parent node could not be found. /// \throws VfsFileExistsError if the given parent node is not a directory. - PHOENIX_API void mount(VfsNode node, // - std::string_view parent, - VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::ALL); + ZKAPI void mount(VfsNode node, // + std::string_view parent, + VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::ALL); /// \brief Mount the disk file at the given host path into the file system. /// @@ -123,8 +140,8 @@ namespace phoenix { /// \param overwrite The behavior of the system when conflicting files are found. /// \throws VfsBrokenDiskError if the disk file is corrupted or invalid and thus, can't be loaded. /// \see #mount_disk(buffer, VfsOverwriteBehavior) - PHOENIX_API void mount_disk(std::filesystem::path const& host, - VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::OLDER); + ZKAPI void mount_disk(std::filesystem::path const& host, + VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::OLDER); /// \brief Mount the disk file in the given buffer into the file system. /// @@ -134,7 +151,9 @@ namespace phoenix { /// \param buf A buffer containing the disk file contents. /// \param overwrite The behavior of the system when conflicting files are found. /// \throws VfsBrokenDiskError if the disk file is corrupted or invalid and thus, can't be loaded. - PHOENIX_API void mount_disk(buffer buf, VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::OLDER); + ZKREM("") + ZKAPI void mount_disk(phoenix::buffer buf, VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::OLDER); + ZKAPI void mount_disk(Read* buf, VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::OLDER); /// \brief Mount a file or directory from the host file system into the Vfs. /// \note If a path to a directory is provided, only its children are mounted, not the directory itself. @@ -143,31 +162,36 @@ namespace phoenix { /// \param overwrite The behavior of the system when conflicting files are found. /// \throws VfsNotFoundError if the given \p parent node could not be found. /// \throws VfsFileExistsError if the given parent node is not a directory. - PHOENIX_API void mount_host(std::filesystem::path const& host, - std::string_view parent, - VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::ALL); + ZKAPI void mount_host(std::filesystem::path const& host, + std::string_view parent, + VfsOverwriteBehavior overwrite = VfsOverwriteBehavior::ALL); /// \brief Resolve the given path in the Vfs to a file system node. /// \param path The path to the node to resolve. /// \return The node at the given path or `nullptr` if the path could not be resolved. - PHOENIX_API [[nodiscard]] VfsNode const* resolve(std::string_view path) const noexcept; + [[nodiscard]] ZKAPI VfsNode const* resolve(std::string_view path) const noexcept; /// \brief Resolve the given path in the Vfs to a file system node. /// \param path The path to the node to resolve. /// \return The node at the given path or `nullptr` if the path could not be resolved. - PHOENIX_API [[nodiscard]] VfsNode* resolve(std::string_view path) noexcept; + [[nodiscard]] ZKAPI VfsNode* resolve(std::string_view path) noexcept; /// \brief Find the first node with the given name in the Vfs. /// \param name The name of the node to find. /// \return The node with the given name or `nullptr` if no node with the given name was found. - PHOENIX_API [[nodiscard]] VfsNode const* find(std::string_view name) const noexcept; + [[nodiscard]] ZKAPI VfsNode const* find(std::string_view name) const noexcept; /// \brief Find the first node with the given name in the Vfs. /// \param name The name of the node to find. /// \return The node with the given name or `nullptr` if no node with the given name was found. - PHOENIX_API [[nodiscard]] VfsNode* find(std::string_view name) noexcept; + [[nodiscard]] ZKAPI VfsNode* find(std::string_view name) noexcept; + + private: + ZKINT void mount_disk(std::byte const* buf, std::size_t size, VfsOverwriteBehavior overwrite); private: VfsNode _m_root; + std::vector> _m_data; + std::vector> _m_data_mapped; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/World.hh b/include/zenkit/World.hh index ad8de62f..65f08911 100644 --- a/include/zenkit/World.hh +++ b/include/zenkit/World.hh @@ -1,19 +1,24 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "Api.hh" -#include -#include -#include -#include -#include +#include "zenkit/Library.hh" +#include "zenkit/Mesh.hh" +#include "zenkit/Misc.hh" + +#include "zenkit/world/BspTree.hh" +#include "zenkit/world/VobTree.hh" +#include "zenkit/world/WayNet.hh" #include #include namespace phoenix { + class buffer; +} + +namespace zenkit { /// \brief Represents a ZenGin world. - class world { + class World { public: /// \brief Parses a world from the data in the given buffer. /// @@ -28,7 +33,7 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API static world parse(buffer& buf, game_version version); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static World parse(phoenix::buffer& buf, GameVersion version); /// \brief Parses a world from the data in the given buffer. /// @@ -49,7 +54,7 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_API static world parse(buffer& buf); + [[nodiscard]] ZKREM("use ::load()") ZKAPI static World parse(phoenix::buffer& buf); /// \brief Parses a world from the data in the given buffer. /// \param[in,out] buf The buffer to read from (by rvalue-reference). @@ -57,29 +62,29 @@ namespace phoenix { /// \return The parsed world object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static world parse(buffer&& buf, game_version version) { - return world::parse(buf, version); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static World parse(phoenix::buffer&& buf, GameVersion version); /// \brief Parses a world from the data in the given buffer. /// \param[in,out] buf The buffer to read from (by rvalue-reference). /// \return The parsed world object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_API inline static world parse(buffer&& buf) { - return world::parse(buf); - } + [[nodiscard]] ZKREM("use ::load()") ZKAPI static World parse(phoenix::buffer&& buf); + ZKAPI void load(Read* r); + ZKAPI void load(Read* r, GameVersion version); + + public: /// \brief The list of VObs defined in this world. - std::vector> world_vobs; + std::vector> world_vobs; /// \brief The mesh of the world. - mesh world_mesh; + Mesh world_mesh; /// \brief The BSP-tree of this world. - bsp_tree world_bsp_tree; + BspTree world_bsp_tree; /// \brief The way-net of this world. - way_net world_way_net; + WayNet world_way_net; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/addon/daedalus.hh b/include/zenkit/addon/daedalus.hh index 0f48a41c..1a622638 100644 --- a/include/zenkit/addon/daedalus.hh +++ b/include/zenkit/addon/daedalus.hh @@ -1,8 +1,10 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "../Api.hh" -#include +#include "zenkit/DaedalusScript.hh" +#include "zenkit/Library.hh" + +#include #define var #define string std::string @@ -16,8 +18,8 @@ inline bool operator&(E a, E b) { return (static_cast(a) & static_cast(b)) != 0; } // clang-format on -namespace phoenix { - struct c_gil_values : public instance { +namespace zenkit { + struct IGuildValues : public DaedalusInstance { static constexpr std::uint32_t count = 66; var int32_t water_depth_knee[count]; @@ -52,113 +54,128 @@ namespace phoenix { var string blood_texture[count]; var int32_t turn_speed[count]; - PHOENIX_API static void register_(script& s) { - s.register_member("C_GILVALUES.WATER_DEPTH_KNEE", &c_gil_values::water_depth_knee); - s.register_member("C_GILVALUES.WATER_DEPTH_CHEST", &c_gil_values::water_depth_chest); - s.register_member("C_GILVALUES.JUMPUP_HEIGHT", &c_gil_values::jumpup_height); - s.register_member("C_GILVALUES.SWIM_TIME", &c_gil_values::swim_time); - s.register_member("C_GILVALUES.DIVE_TIME", &c_gil_values::dive_time); - s.register_member("C_GILVALUES.STEP_HEIGHT", &c_gil_values::step_height); - s.register_member("C_GILVALUES.JUMPLOW_HEIGHT", &c_gil_values::jumplow_height); - s.register_member("C_GILVALUES.JUMPMID_HEIGHT", &c_gil_values::jumpmid_height); - s.register_member("C_GILVALUES.SLIDE_ANGLE", &c_gil_values::slide_angle); - s.register_member("C_GILVALUES.SLIDE_ANGLE2", &c_gil_values::slide_angle2); - s.register_member("C_GILVALUES.DISABLE_AUTOROLL", &c_gil_values::disable_autoroll); - s.register_member("C_GILVALUES.SURFACE_ALIGN", &c_gil_values::surface_align); - s.register_member("C_GILVALUES.CLIMB_HEADING_ANGLE", &c_gil_values::climb_heading_angle); - s.register_member("C_GILVALUES.CLIMB_HORIZ_ANGLE", &c_gil_values::climb_horiz_angle); - s.register_member("C_GILVALUES.CLIMB_GROUND_ANGLE", &c_gil_values::climb_ground_angle); - s.register_member("C_GILVALUES.FIGHT_RANGE_BASE", &c_gil_values::fight_range_base); - s.register_member("C_GILVALUES.FIGHT_RANGE_FIST", &c_gil_values::fight_range_fist); - s.register_member("C_GILVALUES.FIGHT_RANGE_1HS", &c_gil_values::fight_range_1hs); - s.register_member("C_GILVALUES.FIGHT_RANGE_1HA", &c_gil_values::fight_range_1ha); - s.register_member("C_GILVALUES.FIGHT_RANGE_2HS", &c_gil_values::fight_range_2hs); - s.register_member("C_GILVALUES.FIGHT_RANGE_2HA", &c_gil_values::fight_range_2ha); - s.register_member("C_GILVALUES.FALLDOWN_HEIGHT", &c_gil_values::falldown_height); - s.register_member("C_GILVALUES.FALLDOWN_DAMAGE", &c_gil_values::falldown_damage); - s.register_member("C_GILVALUES.BLOOD_DISABLED", &c_gil_values::blood_disabled); - s.register_member("C_GILVALUES.BLOOD_MAX_DISTANCE", &c_gil_values::blood_max_distance); - s.register_member("C_GILVALUES.BLOOD_AMOUNT", &c_gil_values::blood_amount); - s.register_member("C_GILVALUES.BLOOD_FLOW", &c_gil_values::blood_flow); - s.register_member("C_GILVALUES.BLOOD_EMITTER", &c_gil_values::blood_emitter); - s.register_member("C_GILVALUES.BLOOD_TEXTURE", &c_gil_values::blood_texture); - s.register_member("C_GILVALUES.TURN_SPEED", &c_gil_values::turn_speed); - - // Gothic 2 only - if (s.find_symbol_by_name("C_GILVALUES.FIGHT_RANGE_G") != nullptr) { - s.register_member("C_GILVALUES.FIGHT_RANGE_G", &c_gil_values::fight_range_g); - } - } + ZKAPI static void register_(DaedalusScript& s); }; - namespace damage_type { - static constexpr std::uint32_t barrier = 0U; - static constexpr std::uint32_t blunt = 1U; - static constexpr std::uint32_t edge = 2U; - static constexpr std::uint32_t fire = 3U; - static constexpr std::uint32_t fly = 4U; - static constexpr std::uint32_t magic = 5U; - static constexpr std::uint32_t point = 6U; - static constexpr std::uint32_t fall = 7U; - static constexpr std::uint32_t count = 8U; - } // namespace damage_type - - namespace npc_attribute { - static constexpr std::uint32_t hitpoints = 0U; - static constexpr std::uint32_t hitpoints_max = 1U; - static constexpr std::uint32_t mana = 2U; - static constexpr std::uint32_t mana_max = 3U; - static constexpr std::uint32_t strength = 4U; - static constexpr std::uint32_t dexterity = 5U; - static constexpr std::uint32_t regenerate_hp = 6U; - static constexpr std::uint32_t regenerate_mana = 7U; - static constexpr std::uint32_t count = 8U; - } // namespace npc_attribute - - enum class npc_type : std::uint32_t { - g1_ambient = 0U, - g1_main = 1U, - g1_guard = 2U, - g1_friend = 3U, - g1_mine_ambient = 4U, - g1_mine_guard = 5U, - g1_ow_ambient = 6U, - g1_ow_guard = 7U, - g1_rogue = 8U, - - g2_ambient = 0U, - g2_main = 1U, - g2_friend = 2U, - g2_oc_ambient = 3U, - g2_oc_main = 4U, - g2_bl_ambient = 5U, - g2_tal_ambient = 6U, - g2_bl_main = 7U, - - ambient = g2_ambient, - main = g2_main, - - friend_ PHOENIX_DEPRECATED("use npc_type::g2_friend") = g2_friend, - oc_ambient PHOENIX_DEPRECATED("use npc_type::g2_oc_ambient") = g2_oc_ambient, - oc_main PHOENIX_DEPRECATED("use npc_type::g2_oc_main") = g2_oc_main, - bl_ambient PHOENIX_DEPRECATED("use npc_type::g2_bl_ambient") = g2_bl_ambient, - tal_ambient PHOENIX_DEPRECATED("use npc_type::g2_tal_ambient") = g2_tal_ambient, - bl_main PHOENIX_DEPRECATED("use npc_type::g2_bl_main") = g2_bl_main, + namespace DamageType { + static constexpr std::uint32_t BARRIER = 0U; + static constexpr std::uint32_t BLUNT = 1U; + static constexpr std::uint32_t EDGE = 2U; + static constexpr std::uint32_t FIRE = 3U; + static constexpr std::uint32_t FLY = 4U; + static constexpr std::uint32_t MAGIC = 5U; + static constexpr std::uint32_t POINT = 6U; + static constexpr std::uint32_t FALL = 7U; + + static constexpr std::uint32_t _NUM = 8U; + + static constexpr std::uint32_t barrier ZKREM("renamed to DamageType::BARRIER") = BARRIER; + static constexpr std::uint32_t blunt ZKREM("renamed to DamageType::BLUNT") = BLUNT; + static constexpr std::uint32_t edge ZKREM("renamed to DamageType::EDGE") = EDGE; + static constexpr std::uint32_t fire ZKREM("renamed to DamageType::FIRE") = FIRE; + static constexpr std::uint32_t fly ZKREM("renamed to DamageType::FLY") = FLY; + static constexpr std::uint32_t magic ZKREM("renamed to DamageType::MAGIC") = MAGIC; + static constexpr std::uint32_t point ZKREM("renamed to DamageType::POINT") = POINT; + static constexpr std::uint32_t fall ZKREM("renamed to DamageType::FALL") = FALL; + static constexpr std::uint32_t count ZKREM("renamed to DamageType::_NUM") = _NUM; + } // namespace DamageType + + namespace NpcAttribute { + static constexpr std::uint32_t HITPOINTS = 0U; + static constexpr std::uint32_t HITPOINTS_MAX = 1U; + static constexpr std::uint32_t MANA = 2U; + static constexpr std::uint32_t MANA_MAX = 3U; + static constexpr std::uint32_t STRENGTH = 4U; + static constexpr std::uint32_t DEXTERITY = 5U; + static constexpr std::uint32_t REGENERATE_HP = 6U; + static constexpr std::uint32_t REGENERATE_MANA = 7U; + + static constexpr std::uint32_t _NUM = 8U; + + static constexpr std::uint32_t hitpoints ZKREM("renamed to NpcAttribute::HITPOINTS") = HITPOINTS; + static constexpr std::uint32_t hitpoints_max ZKREM("renamed to NpcAttribute::HITPOINTS_MAX") = HITPOINTS_MAX; + static constexpr std::uint32_t mana ZKREM("renamed to NpcAttribute::MANA") = MANA; + static constexpr std::uint32_t mana_max ZKREM("renamed to NpcAttribute::MANA_MAX") = MANA_MAX; + static constexpr std::uint32_t strength ZKREM("renamed to NpcAttribute::STRENGTH") = STRENGTH; + static constexpr std::uint32_t dexterity ZKREM("renamed to NpcAttribute::DEXTERITY") = DEXTERITY; + static constexpr std::uint32_t regenerate_hp ZKREM("renamed to NpcAttribute::REGENERATE_HP") = REGENERATE_HP; + static constexpr std::uint32_t + regenerate_mana ZKREM("renamed to NpcAttribute::REGENERATE_MANA") = REGENERATE_MANA; + static constexpr std::uint32_t count ZKREM("renamed to NpcAttribute::_NUM") = _NUM; + } // namespace NpcAttribute + + enum class NpcType : std::uint32_t { + G1_AMBIENT = 0U, + G1_MAIN = 1U, + G1_GUARD = 2U, + G1_FRIEND = 3U, + G1_MINE_AMBIENT = 4U, + G1_MINE_GUARD = 5U, + G1_OW_AMBIENT = 6U, + G1_OW_GUARD = 7U, + G1_ROGUE = 8U, + + G2_AMBIENT = 0U, + G2_MAIN = 1U, + G2_FRIEND = 2U, + G2_OC_AMBIENT = 3U, + G2_OC_MAIN = 4U, + G2_BL_AMBIENT = 5U, + G2_TAL_AMBIENT = 6U, + G2_BL_MAIN = 7U, + + AMBIENT = G2_AMBIENT, + MAIN = G2_MAIN, + + g1_ambient ZKREM("renamed to NpcType::G1_AMBIENT") = G1_AMBIENT, + g1_main ZKREM("renamed to NpcType::G1_MAIN") = G1_MAIN, + g1_guard ZKREM("renamed to NpcType::G1_GUARD") = G1_GUARD, + g1_friend ZKREM("renamed to NpcType::G1_FRIEND") = G1_FRIEND, + g1_mine_ambient ZKREM("renamed to NpcType::G1_MINE_AMBIENT") = G1_MINE_AMBIENT, + g1_mine_guard ZKREM("renamed to NpcType::G1_MINE_GUARD") = G1_MINE_GUARD, + g1_ow_ambient ZKREM("renamed to NpcType::G1_OW_AMBIENT") = G1_OW_AMBIENT, + g1_ow_guard ZKREM("renamed to NpcType::G1_OW_GUARD") = G1_OW_GUARD, + g1_rogue ZKREM("renamed to NpcType::G1_ROGUE") = G1_ROGUE, + + g2_ambient ZKREM("renamed to NpcType::G2_AMBIENT") = G2_AMBIENT, + g2_main ZKREM("renamed to NpcType::G2_MAIN") = G2_MAIN, + g2_friend ZKREM("renamed to NpcType::G2_FRIEND") = G2_FRIEND, + g2_oc_ambient ZKREM("renamed to NpcType::G2_OC_AMBIENT") = G2_OC_AMBIENT, + g2_oc_main ZKREM("renamed to NpcType::G2_OC_MAIN") = G2_OC_MAIN, + g2_bl_ambient ZKREM("renamed to NpcType::G2_BL_AMBIENT") = G2_BL_AMBIENT, + g2_tal_ambient ZKREM("renamed to NpcType::G2_TAL_AMBIENT") = G2_TAL_AMBIENT, + g2_bl_main ZKREM("renamed to NpcType::G2_BL_MAIN") = G2_BL_MAIN, + + ambient ZKREM("renamed to NpcType::AMBIENT") = AMBIENT, + main ZKREM("renamed to NpcType::MAIN") = MAIN, + + friend_ ZKREM("use NpcType::G2_FRIEND") = G2_FRIEND, + oc_ambient ZKREM("use NpcType::G2_OC_AMBIENT") = G2_OC_AMBIENT, + oc_main ZKREM("use NpcType::G2_OC_MAIN") = G2_OC_MAIN, + bl_ambient ZKREM("use NpcType::G2_BL_AMBIENT") = G2_BL_AMBIENT, + tal_ambient ZKREM("use NpcType::G2_TAL_AMBIENT") = G2_TAL_AMBIENT, + bl_main ZKREM("use NpcType::G2_BL_MAIN") = G2_BL_MAIN, }; - static_assert(npc_type::g1_ambient == npc_type::g2_ambient); - static_assert(npc_type::g1_main == npc_type::g2_main); - - enum class npc_flag : std::uint32_t { - none = 0U, - friends = 1U << 0U, - immortal = 1U << 1U, - ghost = 1U << 2U, - protected_ = 1U << 3U, + static_assert(NpcType::G1_AMBIENT == NpcType::G2_AMBIENT); + static_assert(NpcType::G1_MAIN == NpcType::G2_MAIN); + + enum class NpcFlag : std::uint32_t { + NONE = 0U, + FRIENDS = 1U << 0U, + IMMORTAL = 1U << 1U, + GHOST = 1U << 2U, + PROTECTED = 1U << 3U, + + none ZKREM("renamed to zenkit::NONE") = NONE, + friends ZKREM("renamed to zenkit::FRIENDS") = FRIENDS, + immortal ZKREM("renamed to zenkit::IMMORTAL") = IMMORTAL, + ghost ZKREM("renamed to zenkit::GHOST") = GHOST, + protected_ ZKREM("renamed to zenkit::PROTECTED") = PROTECTED, }; - FLAG(npc_flag) + FLAG(NpcFlag) - struct c_npc : public instance { + struct INpc : public DaedalusInstance { static constexpr std::uint32_t hitchance_count = 5; // -> TODO: one of "unknown", "one-handed", "two-handed", "bow", "crossbow" static constexpr std::uint32_t name_count = 5; @@ -169,12 +186,12 @@ namespace phoenix { var string name[name_count]; var string slot; var string effect; - var npc_type type; - var npc_flag flags; - var int32_t attribute[npc_attribute::count]; + var NpcType type; + var NpcFlag flags; + var int32_t attribute[NpcAttribute::_NUM]; var int32_t hitchance[hitchance_count]; - var int32_t protection[damage_type::count]; - var int32_t damage[damage_type::count]; + var int32_t protection[DamageType::_NUM]; + var int32_t damage[DamageType::_NUM]; var int32_t damage_type; var int32_t guild; var int32_t level; @@ -198,47 +215,10 @@ namespace phoenix { var int32_t bodystate_interruptable_override; var int32_t no_focus; - PHOENIX_API static void register_(script& s) { - s.register_member("C_NPC.ID", &c_npc::id); - s.register_member("C_NPC.NAME", &c_npc::name); - s.register_member("C_NPC.SLOT", &c_npc::slot); - s.register_member("C_NPC.NPCTYPE", &c_npc::type); - s.register_member("C_NPC.FLAGS", &c_npc::flags); - s.register_member("C_NPC.ATTRIBUTE", &c_npc::attribute); - s.register_member("C_NPC.PROTECTION", &c_npc::protection); - s.register_member("C_NPC.DAMAGE", &c_npc::damage); - s.register_member("C_NPC.DAMAGETYPE", &c_npc::damage_type); - s.register_member("C_NPC.GUILD", &c_npc::guild); - s.register_member("C_NPC.LEVEL", &c_npc::level); - s.register_member("C_NPC.MISSION", &c_npc::mission); - s.register_member("C_NPC.FIGHT_TACTIC", &c_npc::fight_tactic); - s.register_member("C_NPC.WEAPON", &c_npc::weapon); - s.register_member("C_NPC.VOICE", &c_npc::voice); - s.register_member("C_NPC.VOICEPITCH", &c_npc::voice_pitch); - s.register_member("C_NPC.BODYMASS", &c_npc::body_mass); - s.register_member("C_NPC.DAILY_ROUTINE", &c_npc::daily_routine); - s.register_member("C_NPC.START_AISTATE", &c_npc::start_aistate); - s.register_member("C_NPC.SPAWNPOINT", &c_npc::spawnpoint); - s.register_member("C_NPC.SPAWNDELAY", &c_npc::spawn_delay); - s.register_member("C_NPC.SENSES", &c_npc::senses); - s.register_member("C_NPC.SENSES_RANGE", &c_npc::senses_range); - s.register_member("C_NPC.AIVAR", &c_npc::aivar); - s.register_member("C_NPC.WP", &c_npc::wp); - s.register_member("C_NPC.EXP", &c_npc::exp); - s.register_member("C_NPC.EXP_NEXT", &c_npc::exp_next); - s.register_member("C_NPC.LP", &c_npc::lp); - - // Gothic 2 only - if (s.find_symbol_by_name("C_NPC.EFFECT") != nullptr) { - s.register_member("C_NPC.EFFECT", &c_npc::effect); - s.register_member("C_NPC.HITCHANCE", &c_npc::hitchance); - s.register_member("C_NPC.BODYSTATEINTERRUPTABLEOVERRIDE", &c_npc::bodystate_interruptable_override); - s.register_member("C_NPC.NOFOCUS", &c_npc::no_focus); - } - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_mission : public instance { + struct IMission : public DaedalusInstance { var string name; var string description; var int32_t duration; @@ -253,39 +233,37 @@ namespace phoenix { var func obsolete; var func running; - PHOENIX_API static void register_(script& s) { - s.register_member("C_MISSION.NAME", &c_mission::name); - s.register_member("C_MISSION.DESCRIPTION", &c_mission::description); - s.register_member("C_MISSION.DURATION", &c_mission::duration); - s.register_member("C_MISSION.IMPORTANT", &c_mission::important); - s.register_member("C_MISSION.OFFERCONDITIONS", &c_mission::offer_conditions); - s.register_member("C_MISSION.OFFER", &c_mission::offer); - s.register_member("C_MISSION.SUCCESSCONDITIONS", &c_mission::success_conditions); - s.register_member("C_MISSION.SUCCESS", &c_mission::success); - s.register_member("C_MISSION.FAILURECONDITIONS", &c_mission::failure_conditions); - s.register_member("C_MISSION.FAILURE", &c_mission::failure); - s.register_member("C_MISSION.OBSOLETECONDITIONS", &c_mission::obsolete_conditions); - s.register_member("C_MISSION.OBSOLETE", &c_mission::obsolete); - s.register_member("C_MISSION.RUNNING", &c_mission::running); - } + ZKAPI static void register_(DaedalusScript& s); }; - enum class item_flags : std::uint32_t { - dagger = 1U << 13U, - sword = 1U << 14U, - axe = 1U << 15U, - two_handed_sword = 1U << 16U, - two_handed_axe = 1U << 17U, - bow = 1U << 19U, - crossbow = 1U << 20U, - amulet = 1U << 22U, - ring = 1U << 11U, - belt = 1U << 24U, - mission = 1U << 12U, + enum class ItemFlag : std::uint32_t { + DAGGER = 1U << 13U, + SWORD = 1U << 14U, + AXE = 1U << 15U, + TWO_HANDED_SWORD = 1U << 16U, + TWO_HANDED_AXE = 1U << 17U, + BOW = 1U << 19U, + CROSSBOW = 1U << 20U, + AMULET = 1U << 22U, + RING = 1U << 11U, + BELT = 1U << 24U, + MISSION = 1U << 12U, + + dagger ZKREM("renamed to zenkit::DAGGER") = DAGGER, + sword ZKREM("renamed to zenkit::SWORD") = SWORD, + axe ZKREM("renamed to zenkit::AXE") = AXE, + two_handed_sword ZKREM("renamed to zenkit::TWO_HANDED_SWORD") = TWO_HANDED_SWORD, + two_handed_axe ZKREM("renamed to zenkit::TWO_HANDED_AXE") = TWO_HANDED_AXE, + bow ZKREM("renamed to zenkit::BOW") = BOW, + crossbow ZKREM("renamed to zenkit::CROSSBOW") = CROSSBOW, + amulet ZKREM("renamed to zenkit::AMULET") = AMULET, + ring ZKREM("renamed to zenkit::RING") = RING, + belt ZKREM("renamed to zenkit::BELT") = BELT, + mission ZKREM("renamed to zenkit::MISSION") = MISSION, }; - FLAG(item_flags) + FLAG(ItemFlag) - struct c_item : public instance { + struct IItem : public DaedalusInstance { static constexpr std::uint32_t condition_count = 3; static constexpr std::uint32_t state_count = 4; static constexpr std::uint32_t text_count = 6; @@ -296,14 +274,14 @@ namespace phoenix { var int32_t hp; var int32_t hp_max; var int32_t main_flag; - var item_flags flags; + var ItemFlag flags; var int32_t weight; var int32_t value; var int32_t damage_type; var int32_t damage_total; - var int32_t damage[damage_type::count]; + var int32_t damage[DamageType::_NUM]; var int32_t wear; - var int32_t protection[damage_type::count]; + var int32_t protection[DamageType::_NUM]; var int32_t nutrition; var int32_t cond_atr[condition_count]; var int32_t cond_value[condition_count]; @@ -335,59 +313,10 @@ namespace phoenix { var int32_t inv_rot_z; var int32_t inv_animate; - PHOENIX_API static void register_(script& s) { - s.register_member("C_ITEM.ID", &c_item::id); - s.register_member("C_ITEM.NAME", &c_item::name); - s.register_member("C_ITEM.NAMEID", &c_item::name_id); - s.register_member("C_ITEM.HP", &c_item::hp); - s.register_member("C_ITEM.HP_MAX", &c_item::hp_max); - s.register_member("C_ITEM.MAINFLAG", &c_item::main_flag); - s.register_member("C_ITEM.FLAGS", &c_item::flags); - s.register_member("C_ITEM.WEIGHT", &c_item::weight); - s.register_member("C_ITEM.VALUE", &c_item::value); - s.register_member("C_ITEM.DAMAGETYPE", &c_item::damage_type); - s.register_member("C_ITEM.DAMAGETOTAL", &c_item::damage_total); - s.register_member("C_ITEM.DAMAGE", &c_item::damage); - s.register_member("C_ITEM.WEAR", &c_item::wear); - s.register_member("C_ITEM.PROTECTION", &c_item::protection); - s.register_member("C_ITEM.NUTRITION", &c_item::nutrition); - s.register_member("C_ITEM.COND_ATR", &c_item::cond_atr); - s.register_member("C_ITEM.COND_VALUE", &c_item::cond_value); - s.register_member("C_ITEM.CHANGE_ATR", &c_item::change_atr); - s.register_member("C_ITEM.CHANGE_VALUE", &c_item::change_value); - s.register_member("C_ITEM.MAGIC", &c_item::magic); - s.register_member("C_ITEM.ON_EQUIP", &c_item::on_equip); - s.register_member("C_ITEM.ON_UNEQUIP", &c_item::on_unequip); - s.register_member("C_ITEM.ON_STATE", &c_item::on_state); - s.register_member("C_ITEM.OWNER", &c_item::owner); - s.register_member("C_ITEM.OWNERGUILD", &c_item::owner_guild); - s.register_member("C_ITEM.DISGUISEGUILD", &c_item::disguise_guild); - s.register_member("C_ITEM.VISUAL", &c_item::visual); - s.register_member("C_ITEM.VISUAL_CHANGE", &c_item::visual_change); - s.register_member("C_ITEM.VISUAL_SKIN", &c_item::visual_skin); - s.register_member("C_ITEM.SCEMENAME", &c_item::scheme_name); - s.register_member("C_ITEM.MATERIAL", &c_item::material); - s.register_member("C_ITEM.MUNITION", &c_item::munition); - s.register_member("C_ITEM.SPELL", &c_item::spell); - s.register_member("C_ITEM.RANGE", &c_item::range); - s.register_member("C_ITEM.MAG_CIRCLE", &c_item::mag_circle); - s.register_member("C_ITEM.DESCRIPTION", &c_item::description); - s.register_member("C_ITEM.TEXT", &c_item::text); - s.register_member("C_ITEM.COUNT", &c_item::count); - - // Gothic 2 only - if (s.find_symbol_by_name("C_ITEM.EFFECT") != nullptr) { - s.register_member("C_ITEM.EFFECT", &c_item::effect); - s.register_member("C_ITEM.INV_ZBIAS", &c_item::inv_zbias); - s.register_member("C_ITEM.INV_ROTX", &c_item::inv_rot_x); - s.register_member("C_ITEM.INV_ROTY", &c_item::inv_rot_y); - s.register_member("C_ITEM.INV_ROTZ", &c_item::inv_rot_z); - s.register_member("C_ITEM.INV_ANIMATE", &c_item::inv_animate); - } - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_focus : public instance { + struct IFocus : public DaedalusInstance { var float npc_longrange; var float npc_range1; var float npc_range2; @@ -408,35 +337,15 @@ namespace phoenix { var float mob_elevup; var int32_t mob_prio; - PHOENIX_API static void register_(script& s) { - s.register_member("C_FOCUS.NPC_LONGRANGE", &c_focus::npc_longrange); - s.register_member("C_FOCUS.NPC_RANGE1", &c_focus::npc_range1); - s.register_member("C_FOCUS.NPC_RANGE2", &c_focus::npc_range2); - s.register_member("C_FOCUS.NPC_AZI", &c_focus::npc_azi); - s.register_member("C_FOCUS.NPC_ELEVDO", &c_focus::npc_elevdo); - s.register_member("C_FOCUS.NPC_ELEVUP", &c_focus::npc_elevup); - s.register_member("C_FOCUS.NPC_PRIO", &c_focus::npc_prio); - s.register_member("C_FOCUS.ITEM_RANGE1", &c_focus::item_range1); - s.register_member("C_FOCUS.ITEM_RANGE2", &c_focus::item_range2); - s.register_member("C_FOCUS.ITEM_AZI", &c_focus::item_azi); - s.register_member("C_FOCUS.ITEM_ELEVDO", &c_focus::item_elevdo); - s.register_member("C_FOCUS.ITEM_ELEVUP", &c_focus::item_elevup); - s.register_member("C_FOCUS.ITEM_PRIO", &c_focus::item_prio); - s.register_member("C_FOCUS.MOB_RANGE1", &c_focus::mob_range1); - s.register_member("C_FOCUS.MOB_RANGE2", &c_focus::mob_range2); - s.register_member("C_FOCUS.MOB_AZI", &c_focus::mob_azi); - s.register_member("C_FOCUS.MOB_ELEVDO", &c_focus::mob_elevdo); - s.register_member("C_FOCUS.MOB_ELEVUP", &c_focus::mob_elevup); - s.register_member("C_FOCUS.MOB_PRIO", &c_focus::mob_prio); - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_info_choice { + struct IInfoChoice { var string text; var int32_t function = 0; }; - struct c_info : public instance { + struct IInfo : public DaedalusInstance { var int32_t npc; var int32_t nr; var int32_t important; @@ -446,29 +355,15 @@ namespace phoenix { var int32_t trade; var int32_t permanent; - std::vector choices {}; - - PHOENIX_API void add_choice(const c_info_choice& ch) { - choices.insert(choices.begin(), ch); - } - - PHOENIX_API void remove_choice(std::size_t index) { - choices.erase(choices.begin() + static_cast(index)); - } - - PHOENIX_API static void register_(script& s) { - s.register_member("C_INFO.NPC", &c_info::npc); - s.register_member("C_INFO.NR", &c_info::nr); - s.register_member("C_INFO.IMPORTANT", &c_info::important); - s.register_member("C_INFO.CONDITION", &c_info::condition); - s.register_member("C_INFO.INFORMATION", &c_info::information); - s.register_member("C_INFO.DESCRIPTION", &c_info::description); - s.register_member("C_INFO.TRADE", &c_info::trade); - s.register_member("C_INFO.PERMANENT", &c_info::permanent); - } + std::vector choices {}; + + ZKAPI void add_choice(const IInfoChoice& ch); + ZKAPI void remove_choice(std::size_t index); + + ZKAPI static void register_(DaedalusScript& s); }; - struct c_item_react : public instance { + struct IItemReact : public DaedalusInstance { var int32_t npc; var int32_t trade_item; var int32_t trade_amount; @@ -477,18 +372,10 @@ namespace phoenix { var int32_t requested_amount; var func reaction; - PHOENIX_API static void register_(script& s) { - s.register_member("C_ITEMREACT.NPC", &c_item_react::npc); - s.register_member("C_ITEMREACT.TRADE_ITEM", &c_item_react::trade_item); - s.register_member("C_ITEMREACT.TRADE_AMOUNT", &c_item_react::trade_amount); - s.register_member("C_ITEMREACT.REQUESTED_CAT", &c_item_react::requested_cat); - s.register_member("C_ITEMREACT.REQUESTED_ITEM", &c_item_react::requested_item); - s.register_member("C_ITEMREACT.REQUESTED_AMOUNT", &c_item_react::requested_amount); - s.register_member("C_ITEMREACT.REACTION", &c_item_react::reaction); - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_spell : public instance { + struct ISpell : public DaedalusInstance { var float time_per_mana; var int32_t damage_per_level; var int32_t damage_type; @@ -502,23 +389,10 @@ namespace phoenix { var int32_t target_collect_azi; var int32_t target_collect_elev; - PHOENIX_API static void register_(script& s) { - s.register_member("C_SPELL.TIME_PER_MANA", &c_spell::time_per_mana); - s.register_member("C_SPELL.DAMAGE_PER_LEVEL", &c_spell::damage_per_level); - s.register_member("C_SPELL.DAMAGETYPE", &c_spell::damage_type); - s.register_member("C_SPELL.SPELLTYPE", &c_spell::spell_type); - s.register_member("C_SPELL.CANTURNDURINGINVEST", &c_spell::can_turn_during_invest); - s.register_member("C_SPELL.CANCHANGETARGETDURINGINVEST", &c_spell::can_change_target_during_invest); - s.register_member("C_SPELL.ISMULTIEFFECT", &c_spell::is_multi_effect); - s.register_member("C_SPELL.TARGETCOLLECTALGO", &c_spell::target_collect_algo); - s.register_member("C_SPELL.TARGETCOLLECTTYPE", &c_spell::target_collect_type); - s.register_member("C_SPELL.TARGETCOLLECTRANGE", &c_spell::target_collect_range); - s.register_member("C_SPELL.TARGETCOLLECTAZI", &c_spell::target_collect_azi); - s.register_member("C_SPELL.TARGETCOLLECTELEV", &c_spell::target_collect_elev); - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_svm : public instance { + struct ISvm : public DaedalusInstance { var string MILGREETINGS; var string PALGREETINGS; var string WEATHER; @@ -833,345 +707,29 @@ namespace phoenix { var string youdefeatedmewell; var string om; -#define REG_IF_SYM_EXIST(sym, ref) \ - do { \ - if (s.find_symbol_by_name(sym)) { \ - s.register_member(sym, ref); \ - } \ - } while (false) - - PHOENIX_API static void register_(script& s) { - REG_IF_SYM_EXIST("C_SVM.MILGREETINGS", &c_svm::MILGREETINGS); - REG_IF_SYM_EXIST("C_SVM.PALGREETINGS", &c_svm::PALGREETINGS); - REG_IF_SYM_EXIST("C_SVM.WEATHER", &c_svm::WEATHER); - REG_IF_SYM_EXIST("C_SVM.IGETYOUSTILL", &c_svm::IGETYOUSTILL); - REG_IF_SYM_EXIST("C_SVM.DIEENEMY", &c_svm::DIEENEMY); - REG_IF_SYM_EXIST("C_SVM.DIEMONSTER", &c_svm::DIEMONSTER); - REG_IF_SYM_EXIST("C_SVM.ADDON_DIEMONSTER", &c_svm::ADDON_DIEMONSTER); - REG_IF_SYM_EXIST("C_SVM.ADDON_DIEMONSTER2", &c_svm::ADDON_DIEMONSTER2); - REG_IF_SYM_EXIST("C_SVM.DIRTYTHIEF", &c_svm::DIRTYTHIEF); - REG_IF_SYM_EXIST("C_SVM.HANDSOFF", &c_svm::HANDSOFF); - REG_IF_SYM_EXIST("C_SVM.SHEEPKILLER", &c_svm::SHEEPKILLER); - REG_IF_SYM_EXIST("C_SVM.SHEEPKILLERMONSTER", &c_svm::SHEEPKILLERMONSTER); - REG_IF_SYM_EXIST("C_SVM.YOUMURDERER", &c_svm::YOUMURDERER); - REG_IF_SYM_EXIST("C_SVM.DIESTUPIDBEAST", &c_svm::DIESTUPIDBEAST); - REG_IF_SYM_EXIST("C_SVM.YOUDAREHITME", &c_svm::YOUDAREHITME); - REG_IF_SYM_EXIST("C_SVM.YOUASKEDFORIT", &c_svm::YOUASKEDFORIT); - REG_IF_SYM_EXIST("C_SVM.THENIBEATYOUOUTOFHERE", &c_svm::THENIBEATYOUOUTOFHERE); - REG_IF_SYM_EXIST("C_SVM.WHATDIDYOUDOINTHERE", &c_svm::WHATDIDYOUDOINTHERE); - REG_IF_SYM_EXIST("C_SVM.WILLYOUSTOPFIGHTING", &c_svm::WILLYOUSTOPFIGHTING); - REG_IF_SYM_EXIST("C_SVM.KILLENEMY", &c_svm::KILLENEMY); - REG_IF_SYM_EXIST("C_SVM.ENEMYKILLED", &c_svm::ENEMYKILLED); - REG_IF_SYM_EXIST("C_SVM.MONSTERKILLED", &c_svm::MONSTERKILLED); - REG_IF_SYM_EXIST("C_SVM.ADDON_MONSTERKILLED", &c_svm::ADDON_MONSTERKILLED); - REG_IF_SYM_EXIST("C_SVM.ADDON_MONSTERKILLED2", &c_svm::ADDON_MONSTERKILLED2); - REG_IF_SYM_EXIST("C_SVM.THIEFDOWN", &c_svm::THIEFDOWN); - REG_IF_SYM_EXIST("C_SVM.RUMFUMMLERDOWN", &c_svm::RUMFUMMLERDOWN); - REG_IF_SYM_EXIST("C_SVM.SHEEPATTACKERDOWN", &c_svm::SHEEPATTACKERDOWN); - REG_IF_SYM_EXIST("C_SVM.KILLMURDERER", &c_svm::KILLMURDERER); - REG_IF_SYM_EXIST("C_SVM.STUPIDBEASTKILLED", &c_svm::STUPIDBEASTKILLED); - REG_IF_SYM_EXIST("C_SVM.NEVERHITMEAGAIN", &c_svm::NEVERHITMEAGAIN); - REG_IF_SYM_EXIST("C_SVM.YOUBETTERSHOULDHAVELISTENED", &c_svm::YOUBETTERSHOULDHAVELISTENED); - REG_IF_SYM_EXIST("C_SVM.GETUPANDBEGONE", &c_svm::GETUPANDBEGONE); - REG_IF_SYM_EXIST("C_SVM.NEVERENTERROOMAGAIN", &c_svm::NEVERENTERROOMAGAIN); - REG_IF_SYM_EXIST("C_SVM.THEREISNOFIGHTINGHERE", &c_svm::THEREISNOFIGHTINGHERE); - REG_IF_SYM_EXIST("C_SVM.SPAREME", &c_svm::SPAREME); - REG_IF_SYM_EXIST("C_SVM.RUNAWAY", &c_svm::RUNAWAY); - REG_IF_SYM_EXIST("C_SVM.ALARM", &c_svm::ALARM); - REG_IF_SYM_EXIST("C_SVM.GUARDS", &c_svm::GUARDS); - REG_IF_SYM_EXIST("C_SVM.HELP", &c_svm::HELP); - REG_IF_SYM_EXIST("C_SVM.GOODMONSTERKILL", &c_svm::GOODMONSTERKILL); - REG_IF_SYM_EXIST("C_SVM.GOODKILL", &c_svm::GOODKILL); - REG_IF_SYM_EXIST("C_SVM.NOTNOW", &c_svm::NOTNOW); - REG_IF_SYM_EXIST("C_SVM.RUNCOWARD", &c_svm::RUNCOWARD); - REG_IF_SYM_EXIST("C_SVM.GETOUTOFHERE", &c_svm::GETOUTOFHERE); - REG_IF_SYM_EXIST("C_SVM.WHYAREYOUINHERE", &c_svm::WHYAREYOUINHERE); - REG_IF_SYM_EXIST("C_SVM.YESGOOUTOFHERE", &c_svm::YESGOOUTOFHERE); - REG_IF_SYM_EXIST("C_SVM.WHATSTHISSUPPOSEDTOBE", &c_svm::WHATSTHISSUPPOSEDTOBE); - REG_IF_SYM_EXIST("C_SVM.YOUDISTURBEDMYSLUMBER", &c_svm::YOUDISTURBEDMYSLUMBER); - REG_IF_SYM_EXIST("C_SVM.ITOOKYOURGOLD", &c_svm::ITOOKYOURGOLD); - REG_IF_SYM_EXIST("C_SVM.SHITNOGOLD", &c_svm::SHITNOGOLD); - REG_IF_SYM_EXIST("C_SVM.ITAKEYOURWEAPON", &c_svm::ITAKEYOURWEAPON); - REG_IF_SYM_EXIST("C_SVM.WHATAREYOUDOING", &c_svm::WHATAREYOUDOING); - REG_IF_SYM_EXIST("C_SVM.LOOKINGFORTROUBLEAGAIN", &c_svm::LOOKINGFORTROUBLEAGAIN); - REG_IF_SYM_EXIST("C_SVM.STOPMAGIC", &c_svm::STOPMAGIC); - REG_IF_SYM_EXIST("C_SVM.ISAIDSTOPMAGIC", &c_svm::ISAIDSTOPMAGIC); - REG_IF_SYM_EXIST("C_SVM.WEAPONDOWN", &c_svm::WEAPONDOWN); - REG_IF_SYM_EXIST("C_SVM.ISAIDWEAPONDOWN", &c_svm::ISAIDWEAPONDOWN); - REG_IF_SYM_EXIST("C_SVM.WISEMOVE", &c_svm::WISEMOVE); - REG_IF_SYM_EXIST("C_SVM.NEXTTIMEYOUREINFORIT", &c_svm::NEXTTIMEYOUREINFORIT); - REG_IF_SYM_EXIST("C_SVM.OHMYHEAD", &c_svm::OHMYHEAD); - REG_IF_SYM_EXIST("C_SVM.THERESAFIGHT", &c_svm::THERESAFIGHT); - REG_IF_SYM_EXIST("C_SVM.OHMYGODITSAFIGHT", &c_svm::OHMYGODITSAFIGHT); - REG_IF_SYM_EXIST("C_SVM.GOODVICTORY", &c_svm::GOODVICTORY); - REG_IF_SYM_EXIST("C_SVM.NOTBAD", &c_svm::NOTBAD); - REG_IF_SYM_EXIST("C_SVM.OHMYGODHESDOWN", &c_svm::OHMYGODHESDOWN); - REG_IF_SYM_EXIST("C_SVM.CHEERFRIEND01", &c_svm::CHEERFRIEND01); - REG_IF_SYM_EXIST("C_SVM.CHEERFRIEND02", &c_svm::CHEERFRIEND02); - REG_IF_SYM_EXIST("C_SVM.CHEERFRIEND03", &c_svm::CHEERFRIEND03); - REG_IF_SYM_EXIST("C_SVM.OOH01", &c_svm::OOH01); - REG_IF_SYM_EXIST("C_SVM.OOH02", &c_svm::OOH02); - REG_IF_SYM_EXIST("C_SVM.OOH03", &c_svm::OOH03); - REG_IF_SYM_EXIST("C_SVM.WHATWASTHAT", &c_svm::WHATWASTHAT); - REG_IF_SYM_EXIST("C_SVM.GETOUTOFMYBED", &c_svm::GETOUTOFMYBED); - REG_IF_SYM_EXIST("C_SVM.AWAKE", &c_svm::AWAKE); - REG_IF_SYM_EXIST("C_SVM.ABS_COMMANDER", &c_svm::ABS_COMMANDER); - REG_IF_SYM_EXIST("C_SVM.ABS_MONASTERY", &c_svm::ABS_MONASTERY); - REG_IF_SYM_EXIST("C_SVM.ABS_FARM", &c_svm::ABS_FARM); - REG_IF_SYM_EXIST("C_SVM.ABS_GOOD", &c_svm::ABS_GOOD); - REG_IF_SYM_EXIST("C_SVM.SHEEPKILLER_CRIME", &c_svm::SHEEPKILLER_CRIME); - REG_IF_SYM_EXIST("C_SVM.ATTACK_CRIME", &c_svm::ATTACK_CRIME); - REG_IF_SYM_EXIST("C_SVM.THEFT_CRIME", &c_svm::THEFT_CRIME); - REG_IF_SYM_EXIST("C_SVM.MURDER_CRIME", &c_svm::MURDER_CRIME); - REG_IF_SYM_EXIST("C_SVM.PAL_CITY_CRIME", &c_svm::PAL_CITY_CRIME); - REG_IF_SYM_EXIST("C_SVM.MIL_CITY_CRIME", &c_svm::MIL_CITY_CRIME); - REG_IF_SYM_EXIST("C_SVM.CITY_CRIME", &c_svm::CITY_CRIME); - REG_IF_SYM_EXIST("C_SVM.MONA_CRIME", &c_svm::MONA_CRIME); - REG_IF_SYM_EXIST("C_SVM.FARM_CRIME", &c_svm::FARM_CRIME); - REG_IF_SYM_EXIST("C_SVM.OC_CRIME", &c_svm::OC_CRIME); - REG_IF_SYM_EXIST("C_SVM.TOUGHGUY_ATTACKLOST", &c_svm::TOUGHGUY_ATTACKLOST); - REG_IF_SYM_EXIST("C_SVM.TOUGHGUY_ATTACKWON", &c_svm::TOUGHGUY_ATTACKWON); - REG_IF_SYM_EXIST("C_SVM.TOUGHGUY_PLAYERATTACK", &c_svm::TOUGHGUY_PLAYERATTACK); - REG_IF_SYM_EXIST("C_SVM.GOLD_1000", &c_svm::GOLD_1000); - REG_IF_SYM_EXIST("C_SVM.GOLD_950", &c_svm::GOLD_950); - REG_IF_SYM_EXIST("C_SVM.GOLD_900", &c_svm::GOLD_900); - REG_IF_SYM_EXIST("C_SVM.GOLD_850", &c_svm::GOLD_850); - REG_IF_SYM_EXIST("C_SVM.GOLD_800", &c_svm::GOLD_800); - REG_IF_SYM_EXIST("C_SVM.GOLD_750", &c_svm::GOLD_750); - REG_IF_SYM_EXIST("C_SVM.GOLD_700", &c_svm::GOLD_700); - REG_IF_SYM_EXIST("C_SVM.GOLD_650", &c_svm::GOLD_650); - REG_IF_SYM_EXIST("C_SVM.GOLD_600", &c_svm::GOLD_600); - REG_IF_SYM_EXIST("C_SVM.GOLD_550", &c_svm::GOLD_550); - REG_IF_SYM_EXIST("C_SVM.GOLD_500", &c_svm::GOLD_500); - REG_IF_SYM_EXIST("C_SVM.GOLD_450", &c_svm::GOLD_450); - REG_IF_SYM_EXIST("C_SVM.GOLD_400", &c_svm::GOLD_400); - REG_IF_SYM_EXIST("C_SVM.GOLD_350", &c_svm::GOLD_350); - REG_IF_SYM_EXIST("C_SVM.GOLD_300", &c_svm::GOLD_300); - REG_IF_SYM_EXIST("C_SVM.GOLD_250", &c_svm::GOLD_250); - REG_IF_SYM_EXIST("C_SVM.GOLD_200", &c_svm::GOLD_200); - REG_IF_SYM_EXIST("C_SVM.GOLD_150", &c_svm::GOLD_150); - REG_IF_SYM_EXIST("C_SVM.GOLD_100", &c_svm::GOLD_100); - REG_IF_SYM_EXIST("C_SVM.GOLD_90", &c_svm::GOLD_90); - REG_IF_SYM_EXIST("C_SVM.GOLD_80", &c_svm::GOLD_80); - REG_IF_SYM_EXIST("C_SVM.GOLD_70", &c_svm::GOLD_70); - REG_IF_SYM_EXIST("C_SVM.GOLD_60", &c_svm::GOLD_60); - REG_IF_SYM_EXIST("C_SVM.GOLD_50", &c_svm::GOLD_50); - REG_IF_SYM_EXIST("C_SVM.GOLD_40", &c_svm::GOLD_40); - REG_IF_SYM_EXIST("C_SVM.GOLD_30", &c_svm::GOLD_30); - REG_IF_SYM_EXIST("C_SVM.GOLD_20", &c_svm::GOLD_20); - REG_IF_SYM_EXIST("C_SVM.GOLD_10", &c_svm::GOLD_10); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK01", &c_svm::SMALLTALK01); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK02", &c_svm::SMALLTALK02); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK03", &c_svm::SMALLTALK03); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK04", &c_svm::SMALLTALK04); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK05", &c_svm::SMALLTALK05); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK06", &c_svm::SMALLTALK06); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK07", &c_svm::SMALLTALK07); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK08", &c_svm::SMALLTALK08); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK09", &c_svm::SMALLTALK09); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK10", &c_svm::SMALLTALK10); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK11", &c_svm::SMALLTALK11); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK12", &c_svm::SMALLTALK12); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK13", &c_svm::SMALLTALK13); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK14", &c_svm::SMALLTALK14); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK15", &c_svm::SMALLTALK15); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK16", &c_svm::SMALLTALK16); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK17", &c_svm::SMALLTALK17); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK18", &c_svm::SMALLTALK18); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK19", &c_svm::SMALLTALK19); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK20", &c_svm::SMALLTALK20); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK21", &c_svm::SMALLTALK21); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK22", &c_svm::SMALLTALK22); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK23", &c_svm::SMALLTALK23); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK24", &c_svm::SMALLTALK24); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK25", &c_svm::SMALLTALK25); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK26", &c_svm::SMALLTALK26); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK27", &c_svm::SMALLTALK27); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK28", &c_svm::SMALLTALK28); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK29", &c_svm::SMALLTALK29); - REG_IF_SYM_EXIST("C_SVM.SMALLTALK30", &c_svm::SMALLTALK30); - REG_IF_SYM_EXIST("C_SVM.NOLEARNNOPOINTS", &c_svm::NOLEARNNOPOINTS); - REG_IF_SYM_EXIST("C_SVM.NOLEARNOVERPERSONALMAX", &c_svm::NOLEARNOVERPERSONALMAX); - REG_IF_SYM_EXIST("C_SVM.NOLEARNYOUREBETTER", &c_svm::NOLEARNYOUREBETTER); - REG_IF_SYM_EXIST("C_SVM.YOULEARNEDSOMETHING", &c_svm::YOULEARNEDSOMETHING); - REG_IF_SYM_EXIST("C_SVM.UNTERSTADT", &c_svm::UNTERSTADT); - REG_IF_SYM_EXIST("C_SVM.OBERSTADT", &c_svm::OBERSTADT); - REG_IF_SYM_EXIST("C_SVM.TEMPEL", &c_svm::TEMPEL); - REG_IF_SYM_EXIST("C_SVM.MARKT", &c_svm::MARKT); - REG_IF_SYM_EXIST("C_SVM.GALGEN", &c_svm::GALGEN); - REG_IF_SYM_EXIST("C_SVM.KASERNE", &c_svm::KASERNE); - REG_IF_SYM_EXIST("C_SVM.HAFEN", &c_svm::HAFEN); - REG_IF_SYM_EXIST("C_SVM.WHERETO", &c_svm::WHERETO); - REG_IF_SYM_EXIST("C_SVM.OBERSTADT_2_UNTERSTADT", &c_svm::OBERSTADT_2_UNTERSTADT); - REG_IF_SYM_EXIST("C_SVM.UNTERSTADT_2_OBERSTADT", &c_svm::UNTERSTADT_2_OBERSTADT); - REG_IF_SYM_EXIST("C_SVM.UNTERSTADT_2_TEMPEL", &c_svm::UNTERSTADT_2_TEMPEL); - REG_IF_SYM_EXIST("C_SVM.UNTERSTADT_2_HAFEN", &c_svm::UNTERSTADT_2_HAFEN); - REG_IF_SYM_EXIST("C_SVM.TEMPEL_2_UNTERSTADT", &c_svm::TEMPEL_2_UNTERSTADT); - REG_IF_SYM_EXIST("C_SVM.TEMPEL_2_MARKT", &c_svm::TEMPEL_2_MARKT); - REG_IF_SYM_EXIST("C_SVM.TEMPEL_2_GALGEN", &c_svm::TEMPEL_2_GALGEN); - REG_IF_SYM_EXIST("C_SVM.MARKT_2_TEMPEL", &c_svm::MARKT_2_TEMPEL); - REG_IF_SYM_EXIST("C_SVM.MARKT_2_KASERNE", &c_svm::MARKT_2_KASERNE); - REG_IF_SYM_EXIST("C_SVM.MARKT_2_GALGEN", &c_svm::MARKT_2_GALGEN); - REG_IF_SYM_EXIST("C_SVM.GALGEN_2_TEMPEL", &c_svm::GALGEN_2_TEMPEL); - REG_IF_SYM_EXIST("C_SVM.GALGEN_2_MARKT", &c_svm::GALGEN_2_MARKT); - REG_IF_SYM_EXIST("C_SVM.GALGEN_2_KASERNE", &c_svm::GALGEN_2_KASERNE); - REG_IF_SYM_EXIST("C_SVM.KASERNE_2_MARKT", &c_svm::KASERNE_2_MARKT); - REG_IF_SYM_EXIST("C_SVM.KASERNE_2_GALGEN", &c_svm::KASERNE_2_GALGEN); - REG_IF_SYM_EXIST("C_SVM.HAFEN_2_UNTERSTADT", &c_svm::HAFEN_2_UNTERSTADT); - REG_IF_SYM_EXIST("C_SVM.DEAD", &c_svm::DEAD); - REG_IF_SYM_EXIST("C_SVM.AARGH_1", &c_svm::AARGH_1); - REG_IF_SYM_EXIST("C_SVM.AARGH_2", &c_svm::AARGH_2); - REG_IF_SYM_EXIST("C_SVM.AARGH_3", &c_svm::AARGH_3); - REG_IF_SYM_EXIST("C_SVM.ADDON_WRONGARMOR", &c_svm::ADDON_WRONGARMOR); - REG_IF_SYM_EXIST("C_SVM.ADDON_WRONGARMOR_SLD", &c_svm::ADDON_WRONGARMOR_SLD); - REG_IF_SYM_EXIST("C_SVM.ADDON_WRONGARMOR_MIL", &c_svm::ADDON_WRONGARMOR_MIL); - REG_IF_SYM_EXIST("C_SVM.ADDON_WRONGARMOR_KDF", &c_svm::ADDON_WRONGARMOR_KDF); - REG_IF_SYM_EXIST("C_SVM.ADDON_NOARMOR_BDT", &c_svm::ADDON_NOARMOR_BDT); - REG_IF_SYM_EXIST("C_SVM.ADDON_DIEBANDIT", &c_svm::ADDON_DIEBANDIT); - REG_IF_SYM_EXIST("C_SVM.ADDON_DIRTYPIRATE", &c_svm::ADDON_DIRTYPIRATE); - REG_IF_SYM_EXIST("C_SVM.SC_HEYTURNAROUND", &c_svm::SC_HEYTURNAROUND); - REG_IF_SYM_EXIST("C_SVM.SC_HEYTURNAROUND02", &c_svm::SC_HEYTURNAROUND02); - REG_IF_SYM_EXIST("C_SVM.SC_HEYTURNAROUND03", &c_svm::SC_HEYTURNAROUND03); - REG_IF_SYM_EXIST("C_SVM.SC_HEYTURNAROUND04", &c_svm::SC_HEYTURNAROUND04); - REG_IF_SYM_EXIST("C_SVM.SC_HEYWAITASECOND", &c_svm::SC_HEYWAITASECOND); - REG_IF_SYM_EXIST("C_SVM.DOESNTWORK", &c_svm::DOESNTWORK); - REG_IF_SYM_EXIST("C_SVM.PICKBROKE", &c_svm::PICKBROKE); - REG_IF_SYM_EXIST("C_SVM.NEEDKEY", &c_svm::NEEDKEY); - REG_IF_SYM_EXIST("C_SVM.NOMOREPICKS", &c_svm::NOMOREPICKS); - REG_IF_SYM_EXIST("C_SVM.NOPICKLOCKTALENT", &c_svm::NOPICKLOCKTALENT); - REG_IF_SYM_EXIST("C_SVM.NOSWEEPING", &c_svm::NOSWEEPING); - REG_IF_SYM_EXIST("C_SVM.PICKLOCKORKEYMISSING", &c_svm::PICKLOCKORKEYMISSING); - REG_IF_SYM_EXIST("C_SVM.KEYMISSING", &c_svm::KEYMISSING); - REG_IF_SYM_EXIST("C_SVM.PICKLOCKMISSING", &c_svm::PICKLOCKMISSING); - REG_IF_SYM_EXIST("C_SVM.NEVEROPEN", &c_svm::NEVEROPEN); - REG_IF_SYM_EXIST("C_SVM.MISSINGITEM", &c_svm::MISSINGITEM); - REG_IF_SYM_EXIST("C_SVM.DONTKNOW", &c_svm::DONTKNOW); - REG_IF_SYM_EXIST("C_SVM.NOTHINGTOGET", &c_svm::NOTHINGTOGET); - REG_IF_SYM_EXIST("C_SVM.NOTHINGTOGET02", &c_svm::NOTHINGTOGET02); - REG_IF_SYM_EXIST("C_SVM.NOTHINGTOGET03", &c_svm::NOTHINGTOGET03); - REG_IF_SYM_EXIST("C_SVM.HEALSHRINE", &c_svm::HEALSHRINE); - REG_IF_SYM_EXIST("C_SVM.HEALLASTSHRINE", &c_svm::HEALLASTSHRINE); - REG_IF_SYM_EXIST("C_SVM.IRDORATHTHEREYOUARE", &c_svm::IRDORATHTHEREYOUARE); - REG_IF_SYM_EXIST("C_SVM.SCOPENSIRDORATHBOOK", &c_svm::SCOPENSIRDORATHBOOK); - REG_IF_SYM_EXIST("C_SVM.SCOPENSLASTDOOR", &c_svm::SCOPENSLASTDOOR); - REG_IF_SYM_EXIST("C_SVM.TRADE_1", &c_svm::TRADE_1); - REG_IF_SYM_EXIST("C_SVM.TRADE_2", &c_svm::TRADE_2); - REG_IF_SYM_EXIST("C_SVM.TRADE_3", &c_svm::TRADE_3); - REG_IF_SYM_EXIST("C_SVM.VERSTEHE", &c_svm::VERSTEHE); - REG_IF_SYM_EXIST("C_SVM.FOUNDTREASURE", &c_svm::FOUNDTREASURE); - REG_IF_SYM_EXIST("C_SVM.CANTUNDERSTANDTHIS", &c_svm::CANTUNDERSTANDTHIS); - REG_IF_SYM_EXIST("C_SVM.CANTREADTHIS", &c_svm::CANTREADTHIS); - REG_IF_SYM_EXIST("C_SVM.STONEPLATE_1", &c_svm::STONEPLATE_1); - REG_IF_SYM_EXIST("C_SVM.STONEPLATE_2", &c_svm::STONEPLATE_2); - REG_IF_SYM_EXIST("C_SVM.STONEPLATE_3", &c_svm::STONEPLATE_3); - REG_IF_SYM_EXIST("C_SVM.COUGH", &c_svm::COUGH); - REG_IF_SYM_EXIST("C_SVM.HUI", &c_svm::HUI); - REG_IF_SYM_EXIST("C_SVM.ADDON_THISLITTLEBASTARD", &c_svm::ADDON_THISLITTLEBASTARD); - REG_IF_SYM_EXIST("C_SVM.ADDON_OPENADANOSTEMPLE", &c_svm::ADDON_OPENADANOSTEMPLE); - REG_IF_SYM_EXIST("C_SVM.ATTENTAT_ADDON_DESCRIPTION", &c_svm::ATTENTAT_ADDON_DESCRIPTION); - REG_IF_SYM_EXIST("C_SVM.ATTENTAT_ADDON_DESCRIPTION2", &c_svm::ATTENTAT_ADDON_DESCRIPTION2); - REG_IF_SYM_EXIST("C_SVM.ATTENTAT_ADDON_PRO", &c_svm::ATTENTAT_ADDON_PRO); - REG_IF_SYM_EXIST("C_SVM.ATTENTAT_ADDON_CONTRA", &c_svm::ATTENTAT_ADDON_CONTRA); - REG_IF_SYM_EXIST("C_SVM.MINE_ADDON_DESCRIPTION", &c_svm::MINE_ADDON_DESCRIPTION); - REG_IF_SYM_EXIST("C_SVM.ADDON_SUMMONANCIENTGHOST", &c_svm::ADDON_SUMMONANCIENTGHOST); - REG_IF_SYM_EXIST("C_SVM.ADDON_ANCIENTGHOST_NOTNEAR", &c_svm::ADDON_ANCIENTGHOST_NOTNEAR); - REG_IF_SYM_EXIST("C_SVM.ADDON_GOLD_DESCRIPTION", &c_svm::ADDON_GOLD_DESCRIPTION); - - REG_IF_SYM_EXIST("C_SVM.WATCHYOURAIM", &c_svm::WATCHYOURAIM); - REG_IF_SYM_EXIST("C_SVM.watchyouraimangry", &c_svm::watchyouraimangry); - REG_IF_SYM_EXIST("C_SVM.letsforgetourlittlefight", &c_svm::letsforgetourlittlefight); - REG_IF_SYM_EXIST("C_SVM.strange", &c_svm::strange); - REG_IF_SYM_EXIST("C_SVM.diemortalenemy", &c_svm::diemortalenemy); - REG_IF_SYM_EXIST("C_SVM.nowwait", &c_svm::nowwait); - REG_IF_SYM_EXIST("C_SVM.nowwaitintruder", &c_svm::nowwaitintruder); - REG_IF_SYM_EXIST("C_SVM.youstillnothaveenough", &c_svm::youstillnothaveenough); - REG_IF_SYM_EXIST("C_SVM.youattackedmycharge", &c_svm::youattackedmycharge); - REG_IF_SYM_EXIST("C_SVM.iwillteachyourespectforforeignproperty", - &c_svm::iwillteachyourespectforforeignproperty); - REG_IF_SYM_EXIST("C_SVM.youkilledoneofus", &c_svm::youkilledoneofus); - REG_IF_SYM_EXIST("C_SVM.berzerk", &c_svm::berzerk); - REG_IF_SYM_EXIST("C_SVM.youllbesorryforthis", &c_svm::youllbesorryforthis); - REG_IF_SYM_EXIST("C_SVM.yesyes", &c_svm::yesyes); - REG_IF_SYM_EXIST("C_SVM.shitwhatamonster", &c_svm::shitwhatamonster); - REG_IF_SYM_EXIST("C_SVM.wewillmeetagain", &c_svm::wewillmeetagain); - REG_IF_SYM_EXIST("C_SVM.nevertrythatagain", &c_svm::nevertrythatagain); - REG_IF_SYM_EXIST("C_SVM.itookyourore", &c_svm::itookyourore); - REG_IF_SYM_EXIST("C_SVM.shitnoore", &c_svm::shitnoore); - REG_IF_SYM_EXIST("C_SVM.youviolatedforbiddenterritory", &c_svm::youviolatedforbiddenterritory); - REG_IF_SYM_EXIST("C_SVM.youwannafoolme", &c_svm::youwannafoolme); - REG_IF_SYM_EXIST("C_SVM.whatdidyouinthere", &c_svm::whatdidyouinthere); - REG_IF_SYM_EXIST("C_SVM.intruderalert", &c_svm::intruderalert); - REG_IF_SYM_EXIST("C_SVM.behindyou", &c_svm::behindyou); - REG_IF_SYM_EXIST("C_SVM.heyheyhey", &c_svm::heyheyhey); - REG_IF_SYM_EXIST("C_SVM.cheerfight", &c_svm::cheerfight); - REG_IF_SYM_EXIST("C_SVM.cheerfriend", &c_svm::cheerfriend); - REG_IF_SYM_EXIST("C_SVM.ooh", &c_svm::ooh); - REG_IF_SYM_EXIST("C_SVM.yeahwelldone", &c_svm::yeahwelldone); - REG_IF_SYM_EXIST("C_SVM.hedefeatedhim", &c_svm::hedefeatedhim); - REG_IF_SYM_EXIST("C_SVM.hedeservedit", &c_svm::hedeservedit); - REG_IF_SYM_EXIST("C_SVM.hekilledhim", &c_svm::hekilledhim); - REG_IF_SYM_EXIST("C_SVM.itwasagoodfight", &c_svm::itwasagoodfight); - REG_IF_SYM_EXIST("C_SVM.friendlygreetings", &c_svm::friendlygreetings); - REG_IF_SYM_EXIST("C_SVM.algreetings", &c_svm::algreetings); - REG_IF_SYM_EXIST("C_SVM.magegreetings", &c_svm::magegreetings); - REG_IF_SYM_EXIST("C_SVM.sectgreetings", &c_svm::sectgreetings); - REG_IF_SYM_EXIST("C_SVM.thereheis", &c_svm::thereheis); - REG_IF_SYM_EXIST("C_SVM.nolearnovermax", &c_svm::nolearnovermax); - REG_IF_SYM_EXIST("C_SVM.nolearnyoualreadyknow", &c_svm::nolearnyoualreadyknow); - REG_IF_SYM_EXIST("C_SVM.heyyou", &c_svm::heyyou); - REG_IF_SYM_EXIST("C_SVM.whatdoyouwant", &c_svm::whatdoyouwant); - REG_IF_SYM_EXIST("C_SVM.isaidwhatdoyouwant", &c_svm::isaidwhatdoyouwant); - REG_IF_SYM_EXIST("C_SVM.makeway", &c_svm::makeway); - REG_IF_SYM_EXIST("C_SVM.outofmyway", &c_svm::outofmyway); - REG_IF_SYM_EXIST("C_SVM.youdeaforwhat", &c_svm::youdeaforwhat); - REG_IF_SYM_EXIST("C_SVM.lookaway", &c_svm::lookaway); - REG_IF_SYM_EXIST("C_SVM.okaykeepit", &c_svm::okaykeepit); - REG_IF_SYM_EXIST("C_SVM.whatsthat", &c_svm::whatsthat); - REG_IF_SYM_EXIST("C_SVM.thatsmyweapon", &c_svm::thatsmyweapon); - REG_IF_SYM_EXIST("C_SVM.giveittome", &c_svm::giveittome); - REG_IF_SYM_EXIST("C_SVM.youcankeepthecrap", &c_svm::youcankeepthecrap); - REG_IF_SYM_EXIST("C_SVM.theykilledmyfriend", &c_svm::theykilledmyfriend); - REG_IF_SYM_EXIST("C_SVM.suckergotsome", &c_svm::suckergotsome); - REG_IF_SYM_EXIST("C_SVM.suckerdefeatedebr", &c_svm::suckerdefeatedebr); - REG_IF_SYM_EXIST("C_SVM.suckerdefeatedgur", &c_svm::suckerdefeatedgur); - REG_IF_SYM_EXIST("C_SVM.suckerdefeatedmage", &c_svm::suckerdefeatedmage); - REG_IF_SYM_EXIST("C_SVM.suckerdefeatednov_guard", &c_svm::suckerdefeatednov_guard); - REG_IF_SYM_EXIST("C_SVM.suckerdefeatedvlk_guard", &c_svm::suckerdefeatedvlk_guard); - REG_IF_SYM_EXIST("C_SVM.youdefeatedmycomrade", &c_svm::youdefeatedmycomrade); - REG_IF_SYM_EXIST("C_SVM.youdefeatednov_guard", &c_svm::youdefeatednov_guard); - REG_IF_SYM_EXIST("C_SVM.youdefeatedvlk_guard", &c_svm::youdefeatedvlk_guard); - REG_IF_SYM_EXIST("C_SVM.youstolefromme", &c_svm::youstolefromme); - REG_IF_SYM_EXIST("C_SVM.youstolefromus", &c_svm::youstolefromus); - REG_IF_SYM_EXIST("C_SVM.youstolefromebr", &c_svm::youstolefromebr); - REG_IF_SYM_EXIST("C_SVM.youstolefromgur", &c_svm::youstolefromgur); - REG_IF_SYM_EXIST("C_SVM.stolefrommage", &c_svm::stolefrommage); - REG_IF_SYM_EXIST("C_SVM.youkilledmyfriend", &c_svm::youkilledmyfriend); - REG_IF_SYM_EXIST("C_SVM.youkilledebr", &c_svm::youkilledebr); - REG_IF_SYM_EXIST("C_SVM.youkilledgur", &c_svm::youkilledgur); - REG_IF_SYM_EXIST("C_SVM.youkilledmage", &c_svm::youkilledmage); - REG_IF_SYM_EXIST("C_SVM.youkilledocfolk", &c_svm::youkilledocfolk); - REG_IF_SYM_EXIST("C_SVM.youkilledncfolk", &c_svm::youkilledncfolk); - REG_IF_SYM_EXIST("C_SVM.youkilledpsifolk", &c_svm::youkilledpsifolk); - REG_IF_SYM_EXIST("C_SVM.getthingsright", &c_svm::getthingsright); - REG_IF_SYM_EXIST("C_SVM.youdefeatedmewell", &c_svm::youdefeatedmewell); - REG_IF_SYM_EXIST("C_SVM.om", &c_svm::om); - } + ZKAPI static void register_(DaedalusScript& s); }; -#undef REG_IF_SYM_EXIST - - enum class c_menu_flags : std::uint32_t { - overtop = 1 << 0, - exclusive = 1 << 1, - no_animation = 1 << 2, - dont_scale_dimension = 1 << 3, - dont_scale_position = 1 << 4, - align_center = 1 << 5, - show_info = 1 << 6, + enum class MenuFlag : std::uint32_t { + OVERTOP = 1 << 0, + EXCLUSIVE = 1 << 1, + NO_ANIMATION = 1 << 2, + DONT_SCALE_DIMENSION = 1 << 3, + DONT_SCALE_POSITION = 1 << 4, + ALIGN_CENTER = 1 << 5, + SHOW_INFO = 1 << 6, + + overtop ZKREM("renamed to MenuFlag::OVERTOP") = OVERTOP, + exclusive ZKREM("renamed to MenuFlag::EXCLUSIVE") = EXCLUSIVE, + no_animation ZKREM("renamed to MenuFlag::NO_ANIMATION") = NO_ANIMATION, + dont_scale_dimension ZKREM("renamed to MenuFlag::DONT_SCALE_DIMENSION") = DONT_SCALE_DIMENSION, + dont_scale_position ZKREM("renamed to MenuFlag::DONT_SCALE_POSITION") = DONT_SCALE_POSITION, + align_center ZKREM("renamed to MenuFlag::ALIGN_CENTER") = ALIGN_CENTER, + show_info ZKREM("renamed to MenuFlag::SHOW_INFO") = SHOW_INFO, }; - FLAG(c_menu_flags) + FLAG(MenuFlag) - struct c_menu : public instance { + struct IMenu : public DaedalusInstance { static constexpr std::uint8_t item_count = 150; var string back_pic; @@ -1184,80 +742,109 @@ namespace phoenix { var string music_theme; var int32_t event_timer_msec; var string items[item_count]; - var c_menu_flags flags; + var MenuFlag flags; var int32_t default_outgame; var int32_t default_ingame; - PHOENIX_API static void register_(script& s) { - s.register_member("C_MENU.BACKPIC", &c_menu::back_pic); - s.register_member("C_MENU.BACKWORLD", &c_menu::back_world); - s.register_member("C_MENU.POSX", &c_menu::pos_x); - s.register_member("C_MENU.POSY", &c_menu::pos_y); - s.register_member("C_MENU.DIMX", &c_menu::dim_x); - s.register_member("C_MENU.DIMY", &c_menu::dim_y); - s.register_member("C_MENU.ALPHA", &c_menu::alpha); - s.register_member("C_MENU.MUSICTHEME", &c_menu::music_theme); - s.register_member("C_MENU.EVENTTIMERMSEC", &c_menu::event_timer_msec); - s.register_member("C_MENU.ITEMS", &c_menu::items); - s.register_member("C_MENU.FLAGS", &c_menu::flags); - s.register_member("C_MENU.DEFAULTOUTGAME", &c_menu::default_outgame); - s.register_member("C_MENU.DEFAULTINGAME", &c_menu::default_ingame); - } + ZKAPI static void register_(DaedalusScript& s); }; - enum class c_menu_item_flags : std::uint32_t { - chromakeyed = 1 << 0, - transparent = 1 << 1, - selectable = 1 << 2, - movable = 1 << 3, - centered = 1 << 4, - disabled = 1 << 5, - fade = 1 << 6, - effects = 1 << 7, - only_outgame = 1 << 8, - only_ingame = 1 << 9, - perf_option = 1 << 10, - multiline = 1 << 11, - needs_apply = 1 << 12, - needs_restart = 1 << 13, - extended_menu = 1 << 14, + enum class MenuItemFlag : std::uint32_t { + CHROMAKEYED = 1 << 0, + TRANSPARENT = 1 << 1, + SELECTABLE = 1 << 2, + MOVABLE = 1 << 3, + CENTERED = 1 << 4, + DISABLED = 1 << 5, + FADE = 1 << 6, + EFFECTS = 1 << 7, + ONLY_OUTGAME = 1 << 8, + ONLY_INGAME = 1 << 9, + PERF_OPTION = 1 << 10, + MULTILINE = 1 << 11, + NEEDS_APPLY = 1 << 12, + NEEDS_RESTART = 1 << 13, + EXTENDED_MENU = 1 << 14, + + chromakeyed ZKREM("renamed to MenuItemFlag::CHROMAKEYED") = CHROMAKEYED, + transparent ZKREM("renamed to MenuItemFlag::TRANSPARENT") = TRANSPARENT, + selectable ZKREM("renamed to MenuItemFlag::SELECTABLE") = SELECTABLE, + movable ZKREM("renamed to MenuItemFlag::MOVABLE") = MOVABLE, + centered ZKREM("renamed to MenuItemFlag::CENTERED") = CENTERED, + disabled ZKREM("renamed to MenuItemFlag::DISABLED") = DISABLED, + fade ZKREM("renamed to MenuItemFlag::FADE") = FADE, + effects ZKREM("renamed to MenuItemFlag::EFFECTS") = EFFECTS, + only_outgame ZKREM("renamed to MenuItemFlag::ONLY_OUTGAME") = ONLY_OUTGAME, + only_ingame ZKREM("renamed to MenuItemFlag::ONLY_INGAME") = ONLY_INGAME, + perf_option ZKREM("renamed to MenuItemFlag::PERF_OPTION") = PERF_OPTION, + multiline ZKREM("renamed to MenuItemFlag::MULTILINE") = MULTILINE, + needs_apply ZKREM("renamed to MenuItemFlag::NEEDS_APPLY") = NEEDS_APPLY, + needs_restart ZKREM("renamed to MenuItemFlag::NEEDS_RESTART") = NEEDS_RESTART, + extended_menu ZKREM("renamed to MenuItemFlag::EXTENDED_MENU") = EXTENDED_MENU, }; - FLAG(c_menu_item_flags) - - enum class c_menu_item_type : std::uint32_t { - unknown = 0, - text = 1, - slider = 2, - input = 3, - cursor = 4, - choicebox = 5, - button = 6, - listbox = 7, + FLAG(MenuItemFlag) + + enum class MenuItemType : std::uint32_t { + UNKNOWN = 0, + TEXT = 1, + SLIDER = 2, + INPUT = 3, + CURSOR = 4, + CHOICEBOX = 5, + BUTTON = 6, + LISTBOX = 7, + + unknown ZKREM("renamed to MenuItemType::UNKNOWN") = UNKNOWN, + text ZKREM("renamed to MenuItemType::TEXT") = TEXT, + slider ZKREM("renamed to MenuItemType::SLIDER") = SLIDER, + input ZKREM("renamed to MenuItemType::INPUT") = INPUT, + cursor ZKREM("renamed to MenuItemType::CURSOR") = CURSOR, + choicebox ZKREM("renamed to MenuItemType::CHOICEBOX") = CHOICEBOX, + button ZKREM("renamed to MenuItemType::BUTTON") = BUTTON, + listbox ZKREM("renamed to MenuItemType::LISTBOX") = LISTBOX, }; - enum class c_menu_item_select_event : uint8_t { - execute = 1, - changed = 2, - leave = 3, - timer = 4, - close = 5, - init = 6, - select_previous = 7, - select_next = 8, + enum class MenuItemSelectEvent : uint8_t { + EXECUTE = 1, + CHANGED = 2, + LEAVE = 3, + TIMER = 4, + CLOSE = 5, + INIT = 6, + SELECT_PREVIOUS = 7, + SELECT_NEXT = 8, + + execute ZKREM("renamed to MenuItemSelectEvent::EXECUTE") = EXECUTE, + changed ZKREM("renamed to MenuItemSelectEvent::CHANGED") = CHANGED, + leave ZKREM("renamed to MenuItemSelectEvent::LEAVE") = LEAVE, + timer ZKREM("renamed to MenuItemSelectEvent::TIMER") = TIMER, + close ZKREM("renamed to MenuItemSelectEvent::CLOSE") = CLOSE, + init ZKREM("renamed to MenuItemSelectEvent::INIT") = INIT, + select_previous ZKREM("renamed to MenuItemSelectEvent::SELECT_PREVIOUS") = SELECT_PREVIOUS, + select_next ZKREM("renamed to MenuItemSelectEvent::SELECT_NEXT") = SELECT_NEXT, }; - enum class c_menu_item_select_action : uint8_t { - unknown = 0, - back = 1, - start_menu = 2, - start_item = 3, - close = 4, - con_commands = 5, - play_sound = 6, - execute_commands = 7, + enum class MenuItemSelectAction : uint8_t { + UNKNOWN = 0, + BACK = 1, + START_MENU = 2, + START_ITEM = 3, + CLOSE = 4, + CON_COMMANDS = 5, + PLAY_SOUND = 6, + EXECUTE_COMMANDS = 7, + + unknown ZKREM("renamed to MenuItemSelectAction::UNKNOWN") = UNKNOWN, + back ZKREM("renamed to MenuItemSelectAction::BACK") = BACK, + start_menu ZKREM("renamed to MenuItemSelectAction::START_MENU") = START_MENU, + start_item ZKREM("renamed to MenuItemSelectAction::START_ITEM") = START_ITEM, + close ZKREM("renamed to MenuItemSelectAction::CLOSE") = CLOSE, + con_commands ZKREM("renamed to MenuItemSelectAction::CON_COMMANDS") = CON_COMMANDS, + play_sound ZKREM("renamed to MenuItemSelectAction::PLAY_SOUND") = PLAY_SOUND, + execute_commands ZKREM("renamed to MenuItemSelectAction::EXECUTE_COMMANDS") = EXECUTE_COMMANDS, }; - struct c_menu_item : public instance { + struct IMenuItem : public DaedalusInstance { static constexpr std::uint32_t text_count = 10; static constexpr std::uint32_t select_action_count = 5; static constexpr std::uint32_t event_action_count = 10; @@ -1268,7 +855,7 @@ namespace phoenix { var string backpic; var string alphamode; var int32_t alpha; - var c_menu_item_type type; + var MenuItemType type; var int32_t on_sel_action[select_action_count]; var string on_sel_action_s[select_action_count]; var string on_chg_set_option; @@ -1279,7 +866,7 @@ namespace phoenix { var int32_t dim_x; var int32_t dim_y; var float size_start_scale; - var c_menu_item_flags flags; + var MenuItemFlag flags; var float open_delay_time; var float open_duration; var float user_float[user_item_count]; @@ -1290,41 +877,10 @@ namespace phoenix { var string hide_if_option_set; var int32_t hide_on_value; - PHOENIX_API static void register_(script& s) { - s.register_member("C_MENU_ITEM.FONTNAME", &c_menu_item::fontname); - s.register_member("C_MENU_ITEM.TEXT", &c_menu_item::text); - s.register_member("C_MENU_ITEM.BACKPIC", &c_menu_item::backpic); - s.register_member("C_MENU_ITEM.ALPHAMODE", &c_menu_item::alphamode); - s.register_member("C_MENU_ITEM.ALPHA", &c_menu_item::alpha); - s.register_member("C_MENU_ITEM.TYPE", &c_menu_item::type); - s.register_member("C_MENU_ITEM.ONSELACTION", &c_menu_item::on_sel_action); - s.register_member("C_MENU_ITEM.ONSELACTION_S", &c_menu_item::on_sel_action_s); - s.register_member("C_MENU_ITEM.ONCHGSETOPTION", &c_menu_item::on_chg_set_option); - s.register_member("C_MENU_ITEM.ONCHGSETOPTIONSECTION", &c_menu_item::on_chg_set_option_section); - s.register_member("C_MENU_ITEM.ONEVENTACTION", &c_menu_item::on_event_action); - s.register_member("C_MENU_ITEM.POSX", &c_menu_item::pos_x); - s.register_member("C_MENU_ITEM.POSY", &c_menu_item::pos_y); - s.register_member("C_MENU_ITEM.DIMX", &c_menu_item::dim_x); - s.register_member("C_MENU_ITEM.DIMY", &c_menu_item::dim_y); - s.register_member("C_MENU_ITEM.SIZESTARTSCALE", &c_menu_item::size_start_scale); - s.register_member("C_MENU_ITEM.FLAGS", &c_menu_item::flags); - s.register_member("C_MENU_ITEM.OPENDELAYTIME", &c_menu_item::open_delay_time); - s.register_member("C_MENU_ITEM.OPENDURATION", &c_menu_item::open_duration); - s.register_member("C_MENU_ITEM.USERFLOAT", &c_menu_item::user_float); - s.register_member("C_MENU_ITEM.USERSTRING", &c_menu_item::user_string); - s.register_member("C_MENU_ITEM.FRAMESIZEX", &c_menu_item::frame_sizex); - s.register_member("C_MENU_ITEM.FRAMESIZEY", &c_menu_item::frame_sizey); - - // Gothic 2 only - if (s.find_symbol_by_name("C_MENU_ITEM.HIDEIFOPTIONSECTIONSET") != nullptr) { - s.register_member("C_MENU_ITEM.HIDEIFOPTIONSECTIONSET", &c_menu_item::hide_if_option_section_set); - s.register_member("C_MENU_ITEM.HIDEIFOPTIONSET", &c_menu_item::hide_if_option_set); - s.register_member("C_MENU_ITEM.HIDEONVALUE", &c_menu_item::hide_on_value); - } - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_camera : public instance { + struct ICamera : public DaedalusInstance { var float best_range; var float min_range; var float max_range; @@ -1349,47 +905,42 @@ namespace phoenix { var int32_t rotate; var int32_t collision; - PHOENIX_API static void register_(script& s) { - s.register_member("CCAMSYS.BESTRANGE", &c_camera::best_range); - s.register_member("CCAMSYS.MINRANGE", &c_camera::min_range); - s.register_member("CCAMSYS.MAXRANGE", &c_camera::max_range); - s.register_member("CCAMSYS.BESTELEVATION", &c_camera::best_elevation); - s.register_member("CCAMSYS.MINELEVATION", &c_camera::min_elevation); - s.register_member("CCAMSYS.MAXELEVATION", &c_camera::max_elevation); - s.register_member("CCAMSYS.BESTAZIMUTH", &c_camera::best_azimuth); - s.register_member("CCAMSYS.MINAZIMUTH", &c_camera::min_azimuth); - s.register_member("CCAMSYS.MAXAZIMUTH", &c_camera::max_azimuth); - s.register_member("CCAMSYS.BESTROTZ", &c_camera::best_rot_z); - s.register_member("CCAMSYS.MINROTZ", &c_camera::min_rot_z); - s.register_member("CCAMSYS.MAXROTZ", &c_camera::max_rot_z); - s.register_member("CCAMSYS.ROTOFFSETX", &c_camera::rot_offset_x); - s.register_member("CCAMSYS.ROTOFFSETY", &c_camera::rot_offset_y); - s.register_member("CCAMSYS.ROTOFFSETZ", &c_camera::rot_offset_z); - s.register_member("CCAMSYS.TARGETOFFSETX", &c_camera::target_offset_x); - s.register_member("CCAMSYS.TARGETOFFSETY", &c_camera::target_offset_y); - s.register_member("CCAMSYS.TARGETOFFSETZ", &c_camera::target_offset_z); - s.register_member("CCAMSYS.VELOTRANS", &c_camera::velo_trans); - s.register_member("CCAMSYS.VELOROT", &c_camera::velo_rot); - s.register_member("CCAMSYS.TRANSLATE", &c_camera::translate); - s.register_member("CCAMSYS.ROTATE", &c_camera::rotate); - s.register_member("CCAMSYS.COLLISION", &c_camera::collision); - } + ZKAPI static void register_(DaedalusScript& s); }; - enum class music_transition_type : std::uint32_t { - unknown = 0, - none = 1, - groove = 2, - fill = 3, - break_ = 4, - intro = 5, - end = 6, - end_and_into = 7 + enum class MusicTransitionEffect : std::uint32_t { + UNKNOWN = 0, + NONE = 1, + GROOVE = 2, + FILL = 3, + BREAK = 4, + INTRO = 5, + END = 6, + END_AND_INTO = 7, + + unknown ZKREM("renamed to MusicTransitionEffect::UNKNOWN") = UNKNOWN, + none ZKREM("renamed to MusicTransitionEffect::NONE") = NONE, + groove ZKREM("renamed to MusicTransitionEffect::GROOVE") = GROOVE, + fill ZKREM("renamed to MusicTransitionEffect::FILL") = FILL, + break_ ZKREM("renamed to MusicTransitionEffect::BREAK") = BREAK, + intro ZKREM("renamed to MusicTransitionEffect::INTRO") = INTRO, + end ZKREM("renamed to MusicTransitionEffect::END") = END, + end_and_into ZKREM("renamed to MusicTransitionEffect::END_AND_INTO") = END_AND_INTO, }; - enum class music_transition_subtype : std::uint32_t { unknown = 0, immediate = 1, beat = 2, measure = 3 }; + enum class MusicTransitionType : std::uint32_t { + UNKNOWN = 0, + IMMEDIATE = 1, + BEAT = 2, + MEASURE = 3, + + unknown ZKREM("renamed to MusicTransitionType::UNKNOWN") = UNKNOWN, + immediate ZKREM("renamed to MusicTransitionType::IMMEDIATE") = IMMEDIATE, + beat ZKREM("renamed to MusicTransitionType::BEAT") = BEAT, + measure ZKREM("renamed to MusicTransitionType::MEASURE") = MEASURE, + }; - struct c_music_system : public instance { + struct IMusicSystem : public DaedalusInstance { var float volume; var int32_t bit_resolution; var int32_t global_reverb_enabled; @@ -1397,51 +948,31 @@ namespace phoenix { var int32_t num_channels; var int32_t reverb_buffer_size; - PHOENIX_API static void register_(script& s) { - s.register_member("C_MUSICSYS_CFG.VOLUME", &c_music_system::volume); - s.register_member("C_MUSICSYS_CFG.BITRESOLUTION", &c_music_system::bit_resolution); - s.register_member("C_MUSICSYS_CFG.GLOBALREVERBENABLED", &c_music_system::global_reverb_enabled); - s.register_member("C_MUSICSYS_CFG.SAMPLERATE", &c_music_system::sample_rate); - s.register_member("C_MUSICSYS_CFG.NUMCHANNELS", &c_music_system::num_channels); - s.register_member("C_MUSICSYS_CFG.REVERBBUFFERSIZE", &c_music_system::reverb_buffer_size); - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_music_theme : public instance { + struct IMusicTheme : public DaedalusInstance { var string file; var float vol; var int32_t loop; var float reverbmix; var float reverbtime; - var music_transition_type transtype; - var music_transition_subtype transsubtype; - - PHOENIX_API static void register_(script& s) { - s.register_member("C_MUSICTHEME.FILE", &c_music_theme::file); - s.register_member("C_MUSICTHEME.VOL", &c_music_theme::vol); - s.register_member("C_MUSICTHEME.LOOP", &c_music_theme::loop); - s.register_member("C_MUSICTHEME.REVERBMIX", &c_music_theme::reverbmix); - s.register_member("C_MUSICTHEME.REVERBTIME", &c_music_theme::reverbtime); - s.register_member("C_MUSICTHEME.TRANSTYPE", &c_music_theme::transtype); - s.register_member("C_MUSICTHEME.TRANSSUBTYPE", &c_music_theme::transsubtype); - } + var MusicTransitionEffect transtype; + var MusicTransitionType transsubtype; + + ZKAPI static void register_(DaedalusScript& s); }; - struct c_music_jingle : public instance { + struct IMusicJingle : public DaedalusInstance { var string name; var int32_t loop; var float vol; var int32_t transsubtype; - PHOENIX_API static void register_(script& s) { - s.register_member("C_MUSICJINGLE.NAME", &c_music_jingle::name); - s.register_member("C_MUSICJINGLE.LOOP", &c_music_jingle::loop); - s.register_member("C_MUSICJINGLE.VOL", &c_music_jingle::vol); - s.register_member("C_MUSICJINGLE.TRANSSUBTYPE", &c_music_jingle::transsubtype); - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_particle_fx : public instance { + struct IParticleEffect : public DaedalusInstance { var float pps_value; var string pps_scale_keys_s; var int32_t pps_is_looping; @@ -1500,72 +1031,10 @@ namespace phoenix { var string time_start_end_s; var int32_t m_bis_ambient_pfx; - PHOENIX_API static void register_(script& s) { - s.register_member("C_PARTICLEFX.PPSVALUE", &c_particle_fx::pps_value); - s.register_member("C_PARTICLEFX.PPSSCALEKEYS_S", &c_particle_fx::pps_scale_keys_s); - s.register_member("C_PARTICLEFX.PPSISLOOPING", &c_particle_fx::pps_is_looping); - s.register_member("C_PARTICLEFX.PPSISSMOOTH", &c_particle_fx::pps_is_smooth); - s.register_member("C_PARTICLEFX.PPSFPS", &c_particle_fx::pps_fps); - s.register_member("C_PARTICLEFX.PPSCREATEEM_S", &c_particle_fx::pps_create_em_s); - s.register_member("C_PARTICLEFX.PPSCREATEEMDELAY", &c_particle_fx::pps_create_em_delay); - s.register_member("C_PARTICLEFX.SHPTYPE_S", &c_particle_fx::shp_type_s); - s.register_member("C_PARTICLEFX.SHPFOR_S", &c_particle_fx::shp_for_s); - s.register_member("C_PARTICLEFX.SHPOFFSETVEC_S", &c_particle_fx::shp_offset_vec_s); - s.register_member("C_PARTICLEFX.SHPDISTRIBTYPE_S", &c_particle_fx::shp_distrib_type_s); - s.register_member("C_PARTICLEFX.SHPDISTRIBWALKSPEED", &c_particle_fx::shp_distrib_walk_speed); - s.register_member("C_PARTICLEFX.SHPISVOLUME", &c_particle_fx::shp_is_volume); - s.register_member("C_PARTICLEFX.SHPDIM_S", &c_particle_fx::shp_dim_s); - s.register_member("C_PARTICLEFX.SHPMESH_S", &c_particle_fx::shp_mesh_s); - s.register_member("C_PARTICLEFX.SHPMESHRENDER_B", &c_particle_fx::shp_mesh_render_b); - s.register_member("C_PARTICLEFX.SHPSCALEKEYS_S", &c_particle_fx::shp_scale_keys_s); - s.register_member("C_PARTICLEFX.SHPSCALEISLOOPING", &c_particle_fx::shp_scale_is_looping); - s.register_member("C_PARTICLEFX.SHPSCALEISSMOOTH", &c_particle_fx::shp_scale_is_smooth); - s.register_member("C_PARTICLEFX.SHPSCALEFPS", &c_particle_fx::shp_scale_fps); - s.register_member("C_PARTICLEFX.DIRMODE_S", &c_particle_fx::dir_mode_s); - s.register_member("C_PARTICLEFX.DIRFOR_S", &c_particle_fx::dir_for_s); - s.register_member("C_PARTICLEFX.DIRMODETARGETFOR_S", &c_particle_fx::dir_mode_target_for_s); - s.register_member("C_PARTICLEFX.DIRMODETARGETPOS_S", &c_particle_fx::dir_mode_target_pos_s); - s.register_member("C_PARTICLEFX.DIRANGLEHEAD", &c_particle_fx::dir_angle_head); - s.register_member("C_PARTICLEFX.DIRANGLEHEADVAR", &c_particle_fx::dir_angle_head_var); - s.register_member("C_PARTICLEFX.DIRANGLEELEV", &c_particle_fx::dir_angle_elev); - s.register_member("C_PARTICLEFX.DIRANGLEELEVVAR", &c_particle_fx::dir_angle_elev_var); - s.register_member("C_PARTICLEFX.VELAVG", &c_particle_fx::vel_avg); - s.register_member("C_PARTICLEFX.VELVAR", &c_particle_fx::vel_var); - s.register_member("C_PARTICLEFX.LSPPARTAVG", &c_particle_fx::lsp_part_avg); - s.register_member("C_PARTICLEFX.LSPPARTVAR", &c_particle_fx::lsp_part_var); - s.register_member("C_PARTICLEFX.FLYGRAVITY_S", &c_particle_fx::fly_gravity_s); - s.register_member("C_PARTICLEFX.FLYCOLLDET_B", &c_particle_fx::fly_colldet_b); - s.register_member("C_PARTICLEFX.VISNAME_S", &c_particle_fx::vis_name_s); - s.register_member("C_PARTICLEFX.VISORIENTATION_S", &c_particle_fx::vis_orientation_s); - s.register_member("C_PARTICLEFX.VISTEXISQUADPOLY", &c_particle_fx::vis_tex_is_quadpoly); - s.register_member("C_PARTICLEFX.VISTEXANIFPS", &c_particle_fx::vis_tex_ani_fps); - s.register_member("C_PARTICLEFX.VISTEXANIISLOOPING", &c_particle_fx::vis_tex_ani_is_looping); - s.register_member("C_PARTICLEFX.VISTEXCOLORSTART_S", &c_particle_fx::vis_tex_color_start_s); - s.register_member("C_PARTICLEFX.VISTEXCOLOREND_S", &c_particle_fx::vis_tex_color_end_s); - s.register_member("C_PARTICLEFX.VISSIZESTART_S", &c_particle_fx::vis_size_start_s); - s.register_member("C_PARTICLEFX.VISSIZEENDSCALE", &c_particle_fx::vis_size_end_scale); - s.register_member("C_PARTICLEFX.VISALPHAFUNC_S", &c_particle_fx::vis_alpha_func_s); - s.register_member("C_PARTICLEFX.VISALPHASTART", &c_particle_fx::vis_alpha_start); - s.register_member("C_PARTICLEFX.VISALPHAEND", &c_particle_fx::vis_alpha_end); - s.register_member("C_PARTICLEFX.TRLFADESPEED", &c_particle_fx::trl_fade_speed); - s.register_member("C_PARTICLEFX.TRLTEXTURE_S", &c_particle_fx::trl_texture_s); - s.register_member("C_PARTICLEFX.TRLWIDTH", &c_particle_fx::trl_width); - s.register_member("C_PARTICLEFX.MRKFADESPEED", &c_particle_fx::mrk_fades_peed); - s.register_member("C_PARTICLEFX.MRKTEXTURE_S", &c_particle_fx::mrkt_exture_s); - s.register_member("C_PARTICLEFX.MRKSIZE", &c_particle_fx::mrk_size); - - // Gothic 2 only - if (s.find_symbol_by_name("C_PARTICLEFX.FLOCKMODE") != nullptr) { - s.register_member("C_PARTICLEFX.FLOCKMODE", &c_particle_fx::flock_mode); - s.register_member("C_PARTICLEFX.FLOCKSTRENGTH", &c_particle_fx::flock_strength); - s.register_member("C_PARTICLEFX.USEEMITTERSFOR", &c_particle_fx::use_emitters_for); - s.register_member("C_PARTICLEFX.TIMESTARTEND_S", &c_particle_fx::time_start_end_s); - s.register_member("C_PARTICLEFX.M_BISAMBIENTPFX", &c_particle_fx::m_bis_ambient_pfx); - } - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_fx_base : public instance { + struct IEffectBase : public DaedalusInstance { static constexpr std::uint8_t user_string_count = 5; var string vis_name_s; @@ -1615,60 +1084,10 @@ namespace phoenix { var float secs_per_damage; var string em_fx_coll_dyn_perc_s; - PHOENIX_API static void register_(script& s) { - s.register_member("CFX_BASE.VISNAME_S", &c_fx_base::vis_name_s); - s.register_member("CFX_BASE.VISSIZE_S", &c_fx_base::vis_size_s); - s.register_member("CFX_BASE.VISALPHA", &c_fx_base::vis_alpha); - s.register_member("CFX_BASE.VISALPHABLENDFUNC_S", &c_fx_base::vis_alpha_blend_func_s); - s.register_member("CFX_BASE.VISTEXANIFPS", &c_fx_base::vis_tex_ani_fps); - s.register_member("CFX_BASE.VISTEXANIISLOOPING", &c_fx_base::vis_tex_ani_is_looping); - s.register_member("CFX_BASE.EMTRJMODE_S", &c_fx_base::em_trj_mode_s); - s.register_member("CFX_BASE.EMTRJORIGINNODE", &c_fx_base::em_trj_origin_node); - s.register_member("CFX_BASE.EMTRJTARGETNODE", &c_fx_base::em_trj_target_node); - s.register_member("CFX_BASE.EMTRJTARGETRANGE", &c_fx_base::em_trj_target_range); - s.register_member("CFX_BASE.EMTRJTARGETAZI", &c_fx_base::em_trj_target_azi); - s.register_member("CFX_BASE.EMTRJTARGETELEV", &c_fx_base::em_trj_target_elev); - s.register_member("CFX_BASE.EMTRJNUMKEYS", &c_fx_base::em_trj_num_keys); - s.register_member("CFX_BASE.EMTRJNUMKEYSVAR", &c_fx_base::em_trj_num_keys_var); - s.register_member("CFX_BASE.EMTRJANGLEELEVVAR", &c_fx_base::em_trj_angle_elev_var); - s.register_member("CFX_BASE.EMTRJANGLEHEADVAR", &c_fx_base::em_trj_angle_head_var); - s.register_member("CFX_BASE.EMTRJKEYDISTVAR", &c_fx_base::em_trj_key_dist_var); - s.register_member("CFX_BASE.EMTRJLOOPMODE_S", &c_fx_base::em_trj_loop_mode_s); - s.register_member("CFX_BASE.EMTRJEASEFUNC_S", &c_fx_base::em_trj_ease_func_s); - s.register_member("CFX_BASE.EMTRJEASEVEL", &c_fx_base::em_trj_ease_vel); - s.register_member("CFX_BASE.EMTRJDYNUPDATEDELAY", &c_fx_base::em_trj_dyn_update_delay); - s.register_member("CFX_BASE.EMTRJDYNUPDATETARGETONLY", &c_fx_base::em_trj_dyn_update_target_only); - s.register_member("CFX_BASE.EMFXCREATE_S", &c_fx_base::em_fx_create_s); - s.register_member("CFX_BASE.EMFXINVESTORIGIN_S", &c_fx_base::em_fx_invest_origin_s); - s.register_member("CFX_BASE.EMFXINVESTTARGET_S", &c_fx_base::em_fx_invest_target_s); - s.register_member("CFX_BASE.EMFXTRIGGERDELAY", &c_fx_base::em_fx_trigger_delay); - s.register_member("CFX_BASE.EMFXCREATEDOWNTRJ", &c_fx_base::em_fx_create_down_trj); - s.register_member("CFX_BASE.EMACTIONCOLLDYN_S", &c_fx_base::em_action_coll_dyn_s); - s.register_member("CFX_BASE.EMACTIONCOLLSTAT_S", &c_fx_base::em_action_coll_stat_s); - s.register_member("CFX_BASE.EMFXCOLLSTAT_S", &c_fx_base::em_fx_coll_stat_s); - s.register_member("CFX_BASE.EMFXCOLLDYN_S", &c_fx_base::em_fx_coll_dyn_s); - s.register_member("CFX_BASE.EMFXCOLLSTATALIGN_S", &c_fx_base::em_fx_coll_stat_align_s); - s.register_member("CFX_BASE.EMFXCOLLDYNALIGN_S", &c_fx_base::em_fx_coll_dyn_align_s); - s.register_member("CFX_BASE.EMFXLIFESPAN", &c_fx_base::em_fx_lifespan); - s.register_member("CFX_BASE.EMCHECKCOLLISION", &c_fx_base::em_check_collision); - s.register_member("CFX_BASE.EMADJUSTSHPTOORIGIN", &c_fx_base::em_adjust_shp_to_origin); - s.register_member("CFX_BASE.EMINVESTNEXTKEYDURATION", &c_fx_base::em_invest_next_key_duration); - s.register_member("CFX_BASE.EMFLYGRAVITY", &c_fx_base::em_fly_gravity); - s.register_member("CFX_BASE.EMSELFROTVEL_S", &c_fx_base::em_self_rot_vel_s); - s.register_member("CFX_BASE.USERSTRING", &c_fx_base::user_string); - s.register_member("CFX_BASE.LIGHTPRESETNAME", &c_fx_base::light_preset_name); - s.register_member("CFX_BASE.SFXID", &c_fx_base::sfx_id); - s.register_member("CFX_BASE.SFXISAMBIENT", &c_fx_base::sfx_is_ambient); - s.register_member("CFX_BASE.SENDASSESSMAGIC", &c_fx_base::send_assess_magic); - s.register_member("CFX_BASE.SECSPERDAMAGE", &c_fx_base::secs_per_damage); - - // Gothic 2 only - if (s.find_symbol_by_name("CFX_BASE.EMFXCOLLDYNPERC_S") != nullptr) - s.register_member("CFX_BASE.EMFXCOLLDYNPERC_S", &c_fx_base::em_fx_coll_dyn_perc_s); - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_particle_fx_emit_key : public instance { + struct IParticleEffectEmitKey : public DaedalusInstance { var string vis_name_s; var float vis_size_scale; var float scale_duration; @@ -1702,79 +1121,58 @@ namespace phoenix { var int32_t em_check_collision; var float em_fx_lifespan; - PHOENIX_API static void register_(script& s) { - s.register_member("C_PARTICLEFXEMITKEY.VISNAME_S", &c_particle_fx_emit_key::vis_name_s); - s.register_member("C_PARTICLEFXEMITKEY.VISSIZESCALE", &c_particle_fx_emit_key::vis_size_scale); - s.register_member("C_PARTICLEFXEMITKEY.SCALEDURATION", &c_particle_fx_emit_key::scale_duration); - s.register_member("C_PARTICLEFXEMITKEY.PFX_PPSVALUE", &c_particle_fx_emit_key::pfx_pps_value); - s.register_member("C_PARTICLEFXEMITKEY.PFX_PPSISSMOOTHCHG", &c_particle_fx_emit_key::pfx_pps_is_smooth_chg); - s.register_member("C_PARTICLEFXEMITKEY.PFX_PPSISLOOPINGCHG", - &c_particle_fx_emit_key::pfx_pps_is_looping_chg); - s.register_member("C_PARTICLEFXEMITKEY.PFX_SCTIME", &c_particle_fx_emit_key::pfx_sc_time); - s.register_member("C_PARTICLEFXEMITKEY.PFX_FLYGRAVITY_S", &c_particle_fx_emit_key::pfx_fly_gravity_s); - s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPDIM_S", &c_particle_fx_emit_key::pfx_shp_dim_s); - s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPISVOLUMECHG", &c_particle_fx_emit_key::pfx_shp_is_volume_chg); - s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPSCALEFPS", &c_particle_fx_emit_key::pfx_shp_scale_fps); - s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPDISTRIBWALKSPEED", - &c_particle_fx_emit_key::pfx_shp_distrib_walks_peed); - s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPOFFSETVEC_S", &c_particle_fx_emit_key::pfx_shp_offset_vec_s); - s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPDISTRIBTYPE_S", - &c_particle_fx_emit_key::pfx_shp_distrib_type_s); - s.register_member("C_PARTICLEFXEMITKEY.PFX_DIRMODE_S", &c_particle_fx_emit_key::pfx_dir_mode_s); - s.register_member("C_PARTICLEFXEMITKEY.PFX_DIRFOR_S", &c_particle_fx_emit_key::pfx_dir_for_s); - s.register_member("C_PARTICLEFXEMITKEY.PFX_DIRMODETARGETFOR_S", - &c_particle_fx_emit_key::pfx_dir_mode_target_for_s); - s.register_member("C_PARTICLEFXEMITKEY.PFX_DIRMODETARGETPOS_S", - &c_particle_fx_emit_key::pfx_dir_mode_target_pos_s); - s.register_member("C_PARTICLEFXEMITKEY.PFX_VELAVG", &c_particle_fx_emit_key::pfx_vel_avg); - s.register_member("C_PARTICLEFXEMITKEY.PFX_LSPPARTAVG", &c_particle_fx_emit_key::pfx_lsp_part_avg); - s.register_member("C_PARTICLEFXEMITKEY.PFX_VISALPHASTART", &c_particle_fx_emit_key::pfx_vis_alpha_start); - s.register_member("C_PARTICLEFXEMITKEY.LIGHTPRESETNAME", &c_particle_fx_emit_key::light_preset_name); - s.register_member("C_PARTICLEFXEMITKEY.LIGHTRANGE", &c_particle_fx_emit_key::light_range); - s.register_member("C_PARTICLEFXEMITKEY.SFXID", &c_particle_fx_emit_key::sfx_id); - s.register_member("C_PARTICLEFXEMITKEY.SFXISAMBIENT", &c_particle_fx_emit_key::sfx_is_ambient); - s.register_member("C_PARTICLEFXEMITKEY.EMCREATEFXID", &c_particle_fx_emit_key::em_create_fx_id); - s.register_member("C_PARTICLEFXEMITKEY.EMFLYGRAVITY", &c_particle_fx_emit_key::em_fly_gravity); - s.register_member("C_PARTICLEFXEMITKEY.EMSELFROTVEL_S", &c_particle_fx_emit_key::em_self_rot_vel_s); - s.register_member("C_PARTICLEFXEMITKEY.EMTRJMODE_S", &c_particle_fx_emit_key::em_trj_mode_s); - s.register_member("C_PARTICLEFXEMITKEY.EMTRJEASEVEL", &c_particle_fx_emit_key::em_trj_ease_vel); - s.register_member("C_PARTICLEFXEMITKEY.EMCHECKCOLLISION", &c_particle_fx_emit_key::em_check_collision); - s.register_member("C_PARTICLEFXEMITKEY.EMFXLIFESPAN", &c_particle_fx_emit_key::em_fx_lifespan); - } + ZKAPI static void register_(DaedalusScript& s); }; - enum class c_fight_ai_move : std::uint32_t { - nop = 0, - run = 1, - run_back = 2, - jump_back = 3, - turn = 4, - strafe = 5, - attack = 6, - attack_side = 7, - attack_front = 8, - attack_triple = 9, - attack_whirl = 10, - attack_master = 11, - turn_to_hit = 15, - parry = 17, - stand_up = 18, - wait = 19, - wait_longer = 23, - wait_ext = 24, + enum class FightAiMove : std::uint32_t { + NOP = 0, + RUN = 1, + RUN_BACK = 2, + JUMP_BACK = 3, + TURN = 4, + STRAFE = 5, + ATTACK = 6, + ATTACK_SIDE = 7, + ATTACK_FRONT = 8, + ATTACK_TRIPLE = 9, + ATTACK_WHIRL = 10, + ATTACK_MASTER = 11, + TURN_TO_HIT = 15, + PARRY = 17, + STAND_UP = 18, + WAIT = 19, + WAIT_LONGER = 23, + WAIT_EXT = 24, + + nop ZKREM("renamed to zenkit::NOP") = NOP, + run ZKREM("renamed to zenkit::RUN") = RUN, + run_back ZKREM("renamed to zenkit::RUN_BACK") = RUN_BACK, + jump_back ZKREM("renamed to zenkit::JUMP_BACK") = JUMP_BACK, + turn ZKREM("renamed to zenkit::TURN") = TURN, + strafe ZKREM("renamed to zenkit::STRAFE") = STRAFE, + attack ZKREM("renamed to zenkit::ATTACK") = ATTACK, + attack_side ZKREM("renamed to zenkit::ATTACK_SIDE") = ATTACK_SIDE, + attack_front ZKREM("renamed to zenkit::ATTACK_FRONT") = ATTACK_FRONT, + attack_triple ZKREM("renamed to zenkit::ATTACK_TRIPLE") = ATTACK_TRIPLE, + attack_whirl ZKREM("renamed to zenkit::ATTACK_WHIRL") = ATTACK_WHIRL, + attack_master ZKREM("renamed to zenkit::ATTACK_MASTER") = ATTACK_MASTER, + turn_to_hit ZKREM("renamed to zenkit::TURN_TO_HIT") = TURN_TO_HIT, + parry ZKREM("renamed to zenkit::PARRY") = PARRY, + stand_up ZKREM("renamed to zenkit::STAND_UP") = STAND_UP, + wait ZKREM("renamed to zenkit::WAIT") = WAIT, + wait_longer ZKREM("renamed to zenkit::WAIT_LONGER") = WAIT_LONGER, + wait_ext ZKREM("renamed to zenkit::WAIT_EXT") = WAIT_EXT, }; - struct c_fight_ai : public instance { + struct IFightAi : public DaedalusInstance { static constexpr std::uint32_t move_count = 6; - var c_fight_ai_move move[move_count]; + var FightAiMove move[move_count]; - PHOENIX_API static void register_(script& s) { - s.register_member("C_FIGHTAI.MOVE", &c_fight_ai::move); - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_sfx : public instance { + struct ISoundEffect : public DaedalusInstance { var string file; var int32_t pitch_off; var int32_t pitch_var; @@ -1785,20 +1183,10 @@ namespace phoenix { var float reverb_level; var string pfx_name; - PHOENIX_API static void register_(script& s) { - s.register_member("C_SFX.FILE", &c_sfx::file); - s.register_member("C_SFX.PITCHOFF", &c_sfx::pitch_off); - s.register_member("C_SFX.PITCHVAR", &c_sfx::pitch_var); - s.register_member("C_SFX.VOL", &c_sfx::vol); - s.register_member("C_SFX.LOOP", &c_sfx::loop); - s.register_member("C_SFX.LOOPSTARTOFFSET", &c_sfx::loop_start_offset); - s.register_member("C_SFX.LOOPENDOFFSET", &c_sfx::loop_end_offset); - s.register_member("C_SFX.REVERBLEVEL", &c_sfx::reverb_level); - s.register_member("C_SFX.PFXNAME", &c_sfx::pfx_name); - } + ZKAPI static void register_(DaedalusScript& s); }; - struct c_sound_system : public instance { + struct ISoundSystem : public DaedalusInstance { var float volume; var int32_t bit_resolution; var int32_t sample_rate; @@ -1806,18 +1194,11 @@ namespace phoenix { var int32_t num_sfx_channels; var string used_3d_provider_name; - PHOENIX_API static void register_(script& s) { - s.register_member("C_SNDSYS_CFG.VOLUME", &c_sound_system::volume); - s.register_member("C_SNDSYS_CFG.BITRESOLUTION", &c_sound_system::bit_resolution); - s.register_member("C_SNDSYS_CFG.SAMPLERATE", &c_sound_system::sample_rate); - s.register_member("C_SNDSYS_CFG.USESTEREO", &c_sound_system::use_stereo); - s.register_member("C_SNDSYS_CFG.NUMSFXCHANNELS", &c_sound_system::num_sfx_channels); - s.register_member("C_SNDSYS_CFG.USED3DPROVIDERNAME", &c_sound_system::used_3d_provider_name); - } + ZKAPI static void register_(DaedalusScript& s); }; - PHOENIX_API void register_all_script_classes(script& s); -} // namespace phoenix + ZKAPI void register_all_script_classes(DaedalusScript& s); +} // namespace zenkit #undef var #undef string diff --git a/include/zenkit/addon/texcvt.hh b/include/zenkit/addon/texcvt.hh index 820327a4..0f56c4da 100644 --- a/include/zenkit/addon/texcvt.hh +++ b/include/zenkit/addon/texcvt.hh @@ -1,12 +1,14 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "../Api.hh" -#include +#include "zenkit/Library.hh" +#include "zenkit/Texture.hh" -namespace phoenix { +#include + +namespace zenkit { /// \brief Converts a texture to the DDS format. /// \param tex The texture to convert. /// \return A buffer containing the DDS file. - [[nodiscard]] PHOENIX_API buffer texture_to_dds(const texture& tex); -} // namespace phoenix + [[nodiscard]] ZKAPI std::vector to_dds(const zenkit::Texture& tex); +} // namespace zenkit diff --git a/include/zenkit/vobs/Camera.hh b/include/zenkit/vobs/Camera.hh index c98d15d0..811d48d9 100644 --- a/include/zenkit/vobs/Camera.hh +++ b/include/zenkit/vobs/Camera.hh @@ -1,70 +1,107 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "../Api.hh" -#include - -namespace phoenix { - enum class camera_motion : std::uint32_t { - undefined = 0, - smooth = 1, - linear = 2, - step = 3, - slow = 4, - fast = 5, - custom = 6 +#include "zenkit/Library.hh" +#include "zenkit/Misc.hh" +#include "zenkit/vobs/VirtualObject.hh" + +#include + +#include +#include +#include +#include + +namespace zenkit { + class ReadArchive; + + enum class CameraMotion : std::uint32_t { + UNDEFINED = 0, + SMOOTH = 1, + LINEAR = 2, + STEP = 3, + SLOW = 4, + FAST = 5, + CUSTOM = 6, + + // Deprecated entries. + undefined ZKREM("renamed to CameraMotion::UNDEFINED") = UNDEFINED, + smooth ZKREM("renamed to CameraMotion::SMOOTH") = SMOOTH, + linear ZKREM("renamed to CameraMotion::LINEAR") = LINEAR, + step ZKREM("renamed to CameraMotion::STEP") = STEP, + slow ZKREM("renamed to CameraMotion::SLOW") = SLOW, + fast ZKREM("renamed to CameraMotion::FAST") = FAST, + custom ZKREM("renamed to CameraMotion::CUSTOM") = CUSTOM, }; - enum class camera_trajectory : std::uint32_t { - world = 0, - object = 1, + enum class CameraTrajectory : std::uint32_t { + WORLD = 0, + OBJECT = 1, + + // Deprecated entries. + world ZKREM("renamed to CameraTrajectory::WORLD") = WORLD, + object ZKREM("renamed to CameraTrajectory::OBJECT") = OBJECT, }; - enum class camera_lerp_mode : std::uint32_t { - undefined = 0, - path = 1, - path_ignore_roll = 2, - path_rotation_samples = 3, + enum class CameraLerpType : std::uint32_t { + UNDEFINED = 0, + PATH = 1, + PATH_IGNORE_ROLL = 2, + PATH_ROTATION_SAMPLES = 3, + + // Deprecated entries. + undefined ZKREM("renamed to CameraLerpType::UNDEFINED") = UNDEFINED, + path ZKREM("renamed to CameraLerpType::PATH") = PATH, + path_ignore_roll ZKREM("renamed to CameraLerpType::PATH_IGNORE_ROLL") = PATH_IGNORE_ROLL, + path_rotation_samples ZKREM("renamed to CameraLerpType::PATH_ROTATION_SAMPLES") = PATH_ROTATION_SAMPLES, }; - enum class camera_loop : std::uint32_t { - none = 0, - restart = 1, - pingpong = 2, + enum class CameraLoop : std::uint32_t { + NONE = 0, + RESTART = 1, + PINGPONG = 2, + + // Deprecated entries. + none ZKREM("renamed to CameraLoop::NONE") = NONE, + restart ZKREM("renamed to CameraLoop::RESTART") = RESTART, + pingpong ZKREM("renamed to CameraLoop::PINGPONG") = PINGPONG, }; namespace vobs { /// \brief A VOb which describes the trajectory of a camera during a cutscene. - struct camera_trj_frame : public vob { + struct CameraTrajectoryFrame : public VirtualObject { float time; float roll_angle; float fov_scale; - camera_motion motion_type; - camera_motion motion_type_fov; - camera_motion motion_type_roll; - camera_motion motion_type_time_scale; + CameraMotion motion_type; + CameraMotion motion_type_fov; + CameraMotion motion_type_roll; + CameraMotion motion_type_time_scale; float tension; float cam_bias; float continuity; float time_scale; bool time_fixed; - glm::mat4x4 original_pose; + glm::mat4 original_pose; /// \brief Parses a camera trajectory VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static std::unique_ptr parse(archive_reader& ctx, game_version version); + ZKREM("use ::load()") + ZKAPI static std::unique_ptr parse(ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which defined the movement of the camera during a cutscene. - struct cs_camera : public vob { - camera_trajectory trajectory_for; - camera_trajectory target_trajectory_for; - camera_loop loop_mode; - camera_lerp_mode lerp_mode; + struct CutsceneCamera : public VirtualObject { + CameraTrajectory trajectory_for; + CameraTrajectory target_trajectory_for; + CameraLoop loop_mode; + CameraLerpType lerp_mode; bool ignore_for_vob_rotation; bool ignore_for_vob_rotation_target; bool adapt; @@ -78,7 +115,7 @@ namespace phoenix { std::int32_t position_count; std::int32_t target_count; - std::vector> frames; + std::vector> frames; // Save-game only variables bool s_paused {false}; @@ -90,9 +127,11 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(cs_camera& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(CutsceneCamera& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; } // namespace vobs -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/vobs/Light.hh b/include/zenkit/vobs/Light.hh index 22396785..2d0dda4f 100644 --- a/include/zenkit/vobs/Light.hh +++ b/include/zenkit/vobs/Light.hh @@ -1,35 +1,55 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "../Api.hh" -#include +#include "zenkit/Library.hh" +#include "zenkit/Misc.hh" +#include "zenkit/vobs/VirtualObject.hh" + +#include + +#include +#include +#include + +namespace zenkit { + class ReadArchive; -namespace phoenix { /// \brief The type of a light source. - enum class light_mode : std::uint32_t { - point = 0, ///< The light source emits a cone-shaped light beam. - spot = 1, ///< The light source emits light in a sphere. - reserved0 = 2, ///< Unused. - reserved1 = 3, ///< Unused. + enum class LightType : std::uint32_t { + POINT = 0, ///< The light source emits a cone-shaped light beam. + SPOT = 1, ///< The light source emits light in a sphere. + RESERVED0 = 2, ///< Unused. + RESERVED1 = 3, ///< Unused. + + // Deprecated entries. + point ZKREM("renamed to LightType::POINT") = POINT, + spot ZKREM("renamed to LightType::SPOT") = SPOT, + reserved0 ZKREM("renamed to LightType::RESERVED0") = RESERVED0, + reserved1 ZKREM("renamed to LightType::RESERVED1") = RESERVED1, }; /// \brief The quality of a light source. - enum class light_quality : std::uint32_t { - high = 0, ///< The light is a high quality light. - medium = 1, ///< The light is a medium quality light. - low = 2, ///< The light is a low quality light, which should be the fastest option. + enum class LightQuality : std::uint32_t { + HIGH = 0, ///< The light is a high quality light. + MEDIUM = 1, ///< The light is a medium quality light. + LOW = 2, ///< The light is a low quality light, which should be the fastest option. + + // Deprecated entries. + high ZKREM("renamed to LightQuality::HIGH") = HIGH, + medium ZKREM("renamed to LightQuality::MEDIUM") = MEDIUM, + low ZKREM("renamed to LightQuality::LOW") = LOW, }; namespace vobs { /// \brief A preset for light sources. - struct light_preset { + struct LightPreset { std::string preset {}; - light_mode light_type {light_mode::spot}; + LightType light_type {LightType::SPOT}; float range {}; glm::u8vec4 color {}; float cone_angle {0}; bool is_static {false}; - light_quality quality {light_quality::medium}; + LightQuality quality {LightQuality::MEDIUM}; std::string lensflare_fx {}; bool on {false}; @@ -45,28 +65,32 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. - PHOENIX_API static void parse(light_preset& obj, archive_reader& ctx, game_version version); + /// \throws ParserError if parsing fails. + ZKREM("use ::load()") ZKAPI static void parse(LightPreset& obj, ReadArchive& ctx, GameVersion version); /// \brief Parses a light preset the given *ZenGin* archive. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. /// \return The parsed light preset. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static light_preset parse(archive_reader& in, game_version version); + ZKREM("use ::load()") ZKAPI static LightPreset parse(ReadArchive& in, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version); }; /// \brief A VOb which acts as a light source. - struct light : public vob, public light_preset { + struct Light : public VirtualObject, public LightPreset { /// \brief Parses a light VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see light_preset::parse - PHOENIX_API static void parse(light& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Light& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; } // namespace vobs -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/vobs/Misc.hh b/include/zenkit/vobs/Misc.hh index 24d26b84..e5bc0ffb 100644 --- a/include/zenkit/vobs/Misc.hh +++ b/include/zenkit/vobs/Misc.hh @@ -1,35 +1,64 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "../Api.hh" -#include - -namespace phoenix { - enum class message_filter_action : uint32_t { - none = 0, - trigger = 1, - untrigger = 2, - enable = 3, - disable = 4, - toggle = 5, +#include "zenkit/Library.hh" +#include "zenkit/Misc.hh" +#include "zenkit/vobs/VirtualObject.hh" + +#include + +#include +#include +#include +#include + +namespace zenkit { + class ReadArchive; + + enum class MessageFilterAction : uint32_t { + NONE = 0, + TRIGGER = 1, + UNTRIGGER = 2, + ENABLE = 3, + DISABLE = 4, + TOGGLE = 5, + + // Deprecated entries. + none ZKREM("renamed to MessageFilterAction::NONE") = NONE, + trigger ZKREM("renamed to MessageFilterAction::TRIGGER") = TRIGGER, + untrigger ZKREM("renamed to MessageFilterAction::UNTRIGGER") = UNTRIGGER, + enable ZKREM("renamed to MessageFilterAction::ENABLE") = ENABLE, + disable ZKREM("renamed to MessageFilterAction::DISABLE") = DISABLE, + toggle ZKREM("renamed to MessageFilterAction::TOGGLE") = TOGGLE, }; - enum class mover_message_type : uint32_t { - fixed_direct = 0, - fixed_order = 1, - next = 2, - previous = 3, + enum class MoverMessageType : uint32_t { + FIXED_DIRECT = 0, + FIXED_ORDER = 1, + NEXT = 2, + PREVIOUS = 3, + + // Deprecated entries. + fixed_direct ZKREM("renamed to MoverMessageType::FIXED_DIRECT") = FIXED_DIRECT, + fixed_order ZKREM("renamed to MoverMessageType::FIXED_ORDER") = FIXED_ORDER, + next ZKREM("renamed to MoverMessageType::NEXT") = NEXT, + previous ZKREM("renamed to MoverMessageType::PREVIOUS") = PREVIOUS, }; - enum class collision_type : std::uint32_t { - none = 0, - box = 1, - point = 2, + enum class TouchCollisionType : std::uint32_t { + NONE = 0, + BOX = 1, + POINT = 2, + + // Deprecated entries. + none ZKREM("renamed to TouchCollisionType::NONE") = NONE, + box ZKREM("renamed to TouchCollisionType::BOX") = BOX, + point ZKREM("renamed to TouchCollisionType::POINT") = POINT, }; namespace vobs { /// \brief An animated VOb. - struct animate : public vob { + struct Animate : public VirtualObject { bool start_on {false}; // Save-game only variables @@ -39,13 +68,15 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(animate& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Animate& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb representing an in-game item. - struct item : public vob { + struct Item : public VirtualObject { std::string instance; // Save-game only variables @@ -56,26 +87,30 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(item& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Item& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb representing a [lens flare](https://en.wikipedia.org/wiki/Lens_flare). - struct lens_flare : public vob { + struct LensFlare : public VirtualObject { std::string fx; /// \brief Parses a lens flare VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(lens_flare& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(LensFlare& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb representing a particle system controller. - struct pfx_controller : public vob { + struct ParticleEffectController : public VirtualObject { std::string pfx_name; bool kill_when_done; bool initially_running; @@ -84,26 +119,31 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(pfx_controller& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") + ZKAPI static void parse(ParticleEffectController& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; - struct message_filter : public vob { + struct MessageFilter : public VirtualObject { std::string target; - message_filter_action on_trigger; - message_filter_action on_untrigger; + MessageFilterAction on_trigger; + MessageFilterAction on_untrigger; /// \brief Parses a message filter VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(message_filter& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(MessageFilter& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; - struct code_master : public vob { + struct CodeMaster : public VirtualObject { std::string target; bool ordered; bool first_false_is_failure; @@ -118,27 +158,31 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(code_master& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(CodeMaster& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; - struct mover_controller : public vob { + struct MoverController : public VirtualObject { std::string target; - mover_message_type message; + MoverMessageType message; std::int32_t key; /// \brief Parses a mover controller VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(mover_controller& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(MoverController& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which represents a damage source. - struct touch_damage : public vob { + struct TouchDamage : public VirtualObject { float damage; bool barrier; @@ -152,19 +196,21 @@ namespace phoenix { float repeat_delay_sec; float volume_scale; - collision_type collision; + TouchCollisionType collision; /// \brief Parses a touch damage VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(touch_damage& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(TouchDamage& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which represents an earthquake-like effect. - struct earthquake : public vob { + struct Earthquake : public VirtualObject { float radius; float duration; glm::vec3 amplitude; @@ -173,25 +219,31 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(earthquake& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Earthquake& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; - struct npc : public vob { - struct talent { + struct Npc : public VirtualObject { + struct Talent { int talent; int value; int skill; }; - struct slot { + using talent ZKREM("renamed to Talent") = Talent; + + struct Slot { bool used; std::string name; - int item_index; + Item* item {}; bool in_inventory; }; + using slot ZKREM("renamed to Slot") = Slot; + std::string npc_instance; glm::vec3 model_scale; float model_fatness; @@ -206,7 +258,7 @@ namespace phoenix { int xp_next_level; int lp; - std::vector talents; + std::vector talents; int fight_tactic; int fight_mode; @@ -228,8 +280,8 @@ namespace phoenix { bool move_lock; std::string packed[9]; - std::vector> items; - std::vector slots; + std::vector> items; + std::vector slots; bool current_state_valid; std::string current_state_name; @@ -265,9 +317,15 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(npc& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Npc& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; + }; + + struct ScreenEffect : public VirtualObject { + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; } // namespace vobs -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/vobs/MovableObject.hh b/include/zenkit/vobs/MovableObject.hh index f24459af..7e6a10ed 100644 --- a/include/zenkit/vobs/MovableObject.hh +++ b/include/zenkit/vobs/MovableObject.hh @@ -1,29 +1,44 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include -#include +#include "zenkit/Library.hh" +#include "zenkit/Misc.hh" +#include "zenkit/vobs/Misc.hh" +#include "zenkit/vobs/VirtualObject.hh" + +#include +#include + +namespace zenkit { + class ReadArchive; -namespace phoenix { /// \brief The different sounds a material can make. - enum class sound_material : std::uint32_t { - wood = 0, - stone = 1, - metal = 2, - leather = 3, - clay = 4, - glass = 5, + enum class SoundMaterialType : std::uint32_t { + WOOD = 0, + STONE = 1, + METAL = 2, + LEATHER = 3, + CLAY = 4, + GLASS = 5, + + // Deprecated entries. + wood ZKREM("renamed to SoundMaterialType::WOOD") = WOOD, + stone ZKREM("renamed to SoundMaterialType::STONE") = STONE, + metal ZKREM("renamed to SoundMaterialType::METAL") = METAL, + leather ZKREM("renamed to SoundMaterialType::LEATHER") = LEATHER, + clay ZKREM("renamed to SoundMaterialType::CLAY") = CLAY, + glass ZKREM("renamed to SoundMaterialType::GLASS") = GLASS, }; namespace vobs { - struct mob : public vob { + struct MovableObject : public VirtualObject { std::string name; std::int32_t hp; std::int32_t damage; bool movable; bool takable; bool focus_override; - sound_material material; + SoundMaterialType material; std::string visual_destroyed; std::string owner; std::string owner_guild; @@ -33,12 +48,14 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(mob& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(MovableObject& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; - struct mob_inter : public mob { + struct InteractiveObject : public MovableObject { std::int32_t state; std::string target; std::string item; @@ -50,13 +67,16 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(mob_inter& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") + ZKAPI static void parse(InteractiveObject& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb representing a campfire. - struct mob_fire : public mob_inter { + struct Fire : public InteractiveObject { std::string slot; std::string vob_tree; @@ -64,35 +84,37 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see mob::parse - PHOENIX_API static void parse(mob_fire& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Fire& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb representing a container. - struct mob_container : public mob_inter { + struct Container : public InteractiveObject { bool locked; std::string key; std::string pick_string; std::string contents; // Save-game only variables - std::vector> s_items; + std::vector> s_items; /// \brief Parses a container VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see mob::parse /// \see mob_container::parse - PHOENIX_API static void parse(mob_container& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Container& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb representing a door. - struct mob_door : public mob_inter { + struct Door : public InteractiveObject { bool locked; std::string key; std::string pick_string; @@ -101,11 +123,12 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see mob::parse /// \see mob_container::parse - PHOENIX_API static void parse(mob_door& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load") ZKAPI static void parse(Door& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; } // namespace vobs -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/vobs/Sound.hh b/include/zenkit/vobs/Sound.hh index e9e5382f..2951f95c 100644 --- a/include/zenkit/vobs/Sound.hh +++ b/include/zenkit/vobs/Sound.hh @@ -1,39 +1,53 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include +#include "zenkit/Library.hh" +#include "zenkit/Misc.hh" +#include "zenkit/vobs/VirtualObject.hh" -namespace phoenix { +#include +#include + +namespace zenkit { /// \brief Describes how a sound should be played when the player enters its trigger volume. - enum class sound_mode : uint32_t { - loop = 0, ///< The sound should be player forever until the player exits the trigger volume. - once = 1, ///< The sound should be played once when the player enters the trigger volume. - random = 2, ///< While the player is in the trigger volume, the should should play randomly. + enum class SoundMode : uint32_t { + LOOP = 0, ///< The sound should be player forever until the player exits the trigger volume. + ONCE = 1, ///< The sound should be played once when the player enters the trigger volume. + RANDOM = 2, ///< While the player is in the trigger volume, the should should play randomly. + + // Deprecated entries. + loop ZKREM("renamed to SoundMode::LOOP") = LOOP, + once ZKREM("renamed to SoundMode::ONCE") = ONCE, + random ZKREM("renamed to SoundMode::RANDOM") = RANDOM, }; /// \brief The trigger volume type for sounds. - enum class sound_trigger_volume : uint32_t { + enum class SoundTriggerVolumeType : uint32_t { /// \brief The sound is triggered when the player enters a spherical area around the VOb /// indicated by its radius setting. - spherical = 0, + SPHERICAL = 0, /// \brief The sound is triggered when the player enters a ellipsoidal area around the VOb /// indicated by its radius setting. - ellipsoidal = 1, + ELLIPSOIDAL = 1, + + // Deprecated entries. + spherical ZKREM("renamed to SoundTriggerVolume::SPHERICAL") = SPHERICAL, + ellipsoidal ZKREM("renamed to SoundTriggerVolume::ELLIPSOIDAL") = ELLIPSOIDAL, }; namespace vobs { /// \brief A VOb which emits a sound. - struct sound : public vob { + struct Sound : public VirtualObject { float volume {0}; - sound_mode mode {sound_mode::once}; + SoundMode mode {SoundMode::ONCE}; float random_delay {0}; float random_delay_var {0}; bool initially_playing {false}; bool ambient3d {false}; bool obstruction {true}; float cone_angle {0}; - sound_trigger_volume volume_type {sound_trigger_volume::spherical}; + SoundTriggerVolumeType volume_type {SoundTriggerVolumeType::SPHERICAL}; float radius {0}; std::string sound_name {}; @@ -45,13 +59,15 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(sound& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Sound& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which emits a sound only during certain times of the day. - struct sound_daytime : public sound { + struct SoundDaytime : public Sound { float start_time {0}; float end_time {0}; std::string sound_name2 {}; @@ -60,9 +76,10 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(sound_daytime& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(SoundDaytime& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; } // namespace vobs -} // namespace phoenix \ No newline at end of file +} // namespace zenkit diff --git a/include/zenkit/vobs/Trigger.hh b/include/zenkit/vobs/Trigger.hh index f3303e78..8025cdd1 100644 --- a/include/zenkit/vobs/Trigger.hh +++ b/include/zenkit/vobs/Trigger.hh @@ -1,42 +1,75 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include -#include - -namespace phoenix { - enum class mover_behavior : uint32_t { - toggle = 0, - trigger_control = 1, - open_timed = 2, - loop = 3, - single_keys = 4, +#include "zenkit/Library.hh" +#include "zenkit/Misc.hh" +#include "zenkit/ModelAnimation.hh" +#include "zenkit/vobs/VirtualObject.hh" + +#include +#include +#include + +namespace zenkit { + class ReadArchive; + + enum class MoverBehavior : uint32_t { + TOGGLE = 0, + TRIGGER_CONTROL = 1, + OPEN_TIME = 2, + LOOP = 3, + SINGLE_KEYS = 4, + + // Deprecated entries. + toggle ZKREM("renamed to MoverBehavior::TOGGLE") = TOGGLE, + trigger_control ZKREM("renamed to MoverBehavior::TRIGGER_CONTROL") = TRIGGER_CONTROL, + open_timed ZKREM("renamed to MoverBehavior::OPEN_TIME") = OPEN_TIME, + loop ZKREM("renamed to MoverBehavior::LOOP") = LOOP, + single_keys ZKREM("renamed to MoverBehavior::SINGLE_KEYS") = SINGLE_KEYS, }; - enum class mover_lerp_mode : uint32_t { - curve = 0, - linear = 1, + enum class MoverLerpType : uint32_t { + CURVE = 0, + LINEAR = 1, + + // Deprecated entries. + curve ZKREM("renamed to MoverLerpType::CURVE") = CURVE, + linear ZKREM("renamed to MoverLerpType::LINEAR") = LINEAR, }; - enum class mover_speed_mode : uint32_t { - seg_constant = 0, - slow_start_end = 1, - slow_start = 2, - slow_end = 3, - seg_slow_start_end = 4, - seg_slow_start = 5, - seg_slow_end = 6, + enum class MoverSpeedType : uint32_t { + CONSTANT = 0, + SLOW_START_END = 1, + SLOW_START = 2, + SLOW_END = 3, + SEGMENT_SLOW_START_END = 4, + SEGMENT_SLOW_START = 5, + SEGMENT_SLOW_END = 6, + + // Deprecated entries. + seg_constant ZKREM("renamed to MoverSpeedType::CONSTANT") = CONSTANT, + slow_start_end ZKREM("renamed to MoverSpeedType::SLOW_START_END") = SLOW_START_END, + slow_start ZKREM("renamed to MoverSpeedType::SLOW_START") = SLOW_START, + slow_end ZKREM("renamed to MoverSpeedType::SLOW_END") = SLOW_END, + seg_slow_start_end ZKREM("renamed to MoverSpeedType::SEGMENT_SLOW_START_END") = SEGMENT_SLOW_START_END, + seg_slow_start ZKREM("renamed to MoverSpeedType::SEGMENT_SLOW_START") = SEGMENT_SLOW_START, + seg_slow_end ZKREM("renamed to MoverSpeedType::SEGMENT_SLOW_END") = SEGMENT_SLOW_END, }; - enum class trigger_batch_mode { - all = 0, - next = 1, - random = 2, + enum class TriggerBatchMode { + ALL = 0, + NEXT = 1, + RANDOM = 2, + + // Deprecated entries. + all ZKREM("renamed to TriggerBatchMode::ALL") = ALL, + next ZKREM("renamed to TriggerBatchMode::NEXT") = NEXT, + random ZKREM("renamed to TriggerBatchMode::RANDOM") = RANDOM, }; namespace vobs { /// \brief A basic trigger VOb which does something upon the player interacting with it. - struct trigger : public vob { + struct Trigger : public VirtualObject { std::string target; std::uint8_t flags; std::uint8_t filter_flags; @@ -55,15 +88,17 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see trigger::parse - PHOENIX_API static void parse(trigger& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Trigger& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which can move upon player interaction. - struct trigger_mover : public trigger { - mover_behavior behavior {mover_behavior::toggle}; + struct Mover : public Trigger { + MoverBehavior behavior {MoverBehavior::TOGGLE}; float touch_blocker_damage {0}; float stay_open_time_sec {0}; bool locked {true}; @@ -71,10 +106,10 @@ namespace phoenix { bool auto_rotate {false}; float speed {0}; - mover_lerp_mode lerp_mode {mover_lerp_mode::curve}; - mover_speed_mode speed_mode {mover_speed_mode::seg_constant}; + MoverLerpType lerp_mode {MoverLerpType::CURVE}; + MoverSpeedType speed_mode {MoverSpeedType::CONSTANT}; - std::vector keyframes {}; + std::vector keyframes {}; std::string sfx_open_start {}; std::string sfx_open_end {}; @@ -100,25 +135,28 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see trigger::parse - PHOENIX_API static void parse(trigger_mover& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(Mover& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which can call multiple script function upon being triggered. - struct trigger_list : public trigger { - struct target { + struct TriggerList : public Trigger { + struct Target { std::string name {}; float delay {}; - [[nodiscard]] inline bool operator==(const target& tgt) const noexcept { + [[nodiscard]] inline bool operator==(const Target& tgt) const noexcept { return this->name == tgt.name && this->delay == tgt.delay; } }; - trigger_batch_mode mode {}; - std::vector targets {}; + using target ZKREM("renamed to Target") = Target; + + TriggerBatchMode mode {}; + std::vector targets {}; // Save-game only variables uint8_t s_act_target {0}; @@ -128,28 +166,30 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see trigger::parse - PHOENIX_API static void parse(trigger_list& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(TriggerList& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which calls a script function upon being triggered. - struct trigger_script : public trigger { + struct TriggerScript : public Trigger { std::string function {}; /// \brief Parses a script trigger VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see trigger::parse - PHOENIX_API static void parse(trigger_script& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(TriggerScript& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which triggers a level change if the player moves close to it. - struct trigger_change_level : public trigger { + struct TriggerChangeLevel : public Trigger { std::string level_name {}; std::string start_vob {}; @@ -157,14 +197,16 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse /// \see trigger::parse - PHOENIX_API static void parse(trigger_change_level& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") + ZKAPI static void parse(TriggerChangeLevel& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; /// \brief A VOb which triggers a world start event. - struct trigger_world_start : public vob { + struct TriggerWorldStart : public VirtualObject { std::string target; bool fire_once; @@ -175,21 +217,24 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(trigger_world_start& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") + ZKAPI static void parse(TriggerWorldStart& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; - struct trigger_untouch : public vob { + struct TriggerUntouch : public VirtualObject { std::string target; /// \brief Parses an untouch trigger VOb the given *ZenGin* archive. /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. + /// \throws ParserError if parsing fails. /// \see vob::parse - PHOENIX_API static void parse(trigger_untouch& obj, archive_reader& ctx, game_version version); + ZKREM("use ::load()") ZKAPI static void parse(TriggerUntouch& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; }; } // namespace vobs -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/vobs/VirtualObject.hh b/include/zenkit/vobs/VirtualObject.hh index 05d16d5a..e348aa8f 100644 --- a/include/zenkit/vobs/VirtualObject.hh +++ b/include/zenkit/vobs/VirtualObject.hh @@ -1,15 +1,27 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "../Api.hh" -#include -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Library.hh" +#include "zenkit/Material.hh" +#include "zenkit/Misc.hh" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace zenkit { + class ReadArchive; -namespace phoenix { /// \brief All possible VOb types. /// \summary Mostly copied from [ZenLib](https://github.com/Try/ZenLib). - enum class vob_type : std::uint8_t { + enum class VobType : std::uint8_t { zCVob = 0, ///< The base type for all VObs. zCVobLevelCompo = 1, ///< A basic VOb used for grouping other VObs. oCItem = 2, ///< A VOb representing an item @@ -59,48 +71,68 @@ namespace phoenix { }; /// \brief Ways a VOb can cast shadows. - enum class shadow_mode : std::uint8_t { - none = 0, ///< The VOb does not cast any shadow. - blob = 1, ///< The VOb casts a basic dark circle at its base. + enum class ShadowType : std::uint8_t { + NONE = 0, ///< The VOb does not cast any shadow. + BLOB = 1, ///< The VOb casts a basic dark circle at its base. + + // Deprecated entries. + none ZKREM("renamed to ShadowType::NONE") = NONE, + blob ZKREM("renamed to ShadowType::BLOB") = BLOB, }; /// \brief Ways a VOb is seen in the game world. - enum class visual_type : std::uint8_t { - decal = 0, ///< The VOb presents as a decal. - mesh = 1, ///< The VOb presents a phoenix::mesh. - proto_mesh = 2, ///< The VOb presents a phoenix::proto_mesh. - particle_system = 3, ///< The VOb presents as a particle system. - ai_camera = 4, ///< The VOb is a game-controlled camera. - model = 5, ///< The VOb presents a phoenix::model. - morph_mesh = 6, ///< The VOb presents a phoenix::morph_mesh. - unknown = 7, ///< The VOb presents an unknown visual or no visual at all. + enum class VisualType : std::uint8_t { + DECAL = 0, ///< The VOb presents as a decal. + MESH = 1, ///< The VOb presents a phoenix::Mesh. + MULTI_RESOLUTION_MESH = 2, ///< The VOb presents a phoenix::MultiResolutionMesh. + PARTICLE_EFFECT = 3, ///< The VOb presents as a particle system. + AI_CAMERA = 4, ///< The VOb is a game-controlled camera. + MODEL = 5, ///< The VOb presents a phoenix::Model. + MORPH_MESH = 6, ///< The VOb presents a phoenix::MorphMesh. + UNKNOWN = 7, ///< The VOb presents an unknown visual or no visual at all. + + // Deprecated entries. + decal ZKREM("renamed to VisualType::DECAL") = DECAL, + mesh ZKREM("renamed to VisualType::MESH") = MESH, + proto_mesh ZKREM("renamed to VisualType::MULTI_RESOLUTION_MESH") = MULTI_RESOLUTION_MESH, + particle_system ZKREM("renamed to VisualType::PARTICLE_EFFECT") = PARTICLE_EFFECT, + ai_camera ZKREM("renamed to VisualType::AI_CAMERA") = AI_CAMERA, + model ZKREM("renamed to VisualType::MODEL") = MODEL, + morph_mesh ZKREM("renamed to VisualType::MORPH_MESH") = MORPH_MESH, + unknown ZKREM("renamed to VisualType::UNKNOWN") = UNKNOWN, }; /// \brief Ways the camera may behave with a VOb. - enum class sprite_alignment : std::uint8_t { - none = 0, ///< The sprite is not affected by the camera's position. - yaw = 1, ///< The sprite rotates with the camera's yaw axis. - full = 2, ///< The sprite rotates with camera fully. + enum class SpriteAlignment : std::uint8_t { + NONE = 0, ///< The sprite is not affected by the camera's position. + YAW = 1, ///< The sprite rotates with the camera's yaw axis. + FULL = 2, ///< The sprite rotates with camera fully. + + // Deprecated entries. + none ZKREM("renamed to SpriteAlignment::NONE") = NONE, + yaw ZKREM("renamed to SpriteAlignment::YAW") = YAW, + full ZKREM("renamed to SpriteAlignment::FULL") = FULL, }; - /// \brief Ways the camera may behave with a VOb. Same as sprite_alignment - /// \deprecated Use sprite_alignment instead. - using camera_lock_mode [[deprecated("use phoenix::sprite_alignment")]] = sprite_alignment; - /// \brief Types of wavy animation. - enum class animation_mode : std::uint8_t { - none = 0, ///< No wave animation. - wind = 1, - wind2 = 2, + enum class AnimationType : std::uint8_t { + NONE = 0, ///< No wave animation. + WIND = 1, + WIND_ALT = 2, + + // Deprecated entries. + none ZKREM("renamed to AnimationType::NONE") = NONE, + wind ZKREM("renamed to AnimationType::WIND") = WIND, + wind2 ZKREM("renamed to AnimationType::WIND_ALT") = WIND_ALT, }; /// \brief Decal visual configuration for VObs. - struct decal { + struct Decal { std::string name {}; glm::vec2 dimension {}; glm::vec2 offset {}; bool two_sided {}; - alpha_function alpha_func {}; + AlphaFunction alpha_func {}; float texture_anim_fps {}; std::uint8_t alpha_weight {}; bool ignore_daylight {}; @@ -109,38 +141,55 @@ namespace phoenix { /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. /// \return The parsed decal. - /// \throws parser_error if parsing fails. - PHOENIX_API static decal parse(archive_reader& ctx, game_version version); + /// \throws ParserError if parsing fails. + ZKREM("use ::load()") ZKAPI static Decal parse(ReadArchive& ctx, GameVersion version); + + ZKAPI void load(ReadArchive& r, GameVersion version); + }; + + struct RigidBody { + glm::vec3 vel; + std::uint8_t mode; + bool gravity_enabled; + float gravity_scale; + glm::vec3 slide_direction; + + ZKAPI void load(ReadArchive& r, GameVersion version); + }; + + struct EventManager { + bool cleared; + bool active; + + ZKAPI void load(ReadArchive& r, GameVersion version); }; /// \brief The base class for all VObs. /// ///

    Contains parameters all VObs have, like their position, bounding box and model.

    - struct vob { - struct save_state { + struct VirtualObject { + struct SaveState { uint8_t sleep_mode; float next_on_timer; + std::optional rigid_body {}; }; - vob_type type; ///< The type of this VOb. - uint32_t id; ///< The index of this VOb in the archive it was read from. + using save_state ZKREM("renamed to SaveState") = SaveState; + + VobType type; ///< The type of this VOb. + uint32_t id; ///< The index of this VOb in the archive it was read from. - bounding_box bbox {}; + AxisAlignedBoundingBox bbox {}; glm::vec3 position {}; glm::mat3x3 rotation {}; bool show_visual {}; - - union { - [[deprecated("use phoenix::vob::sprite_camera_facing_mode")]] sprite_alignment camera_alignment; - sprite_alignment sprite_camera_facing_mode; - }; - + SpriteAlignment sprite_camera_facing_mode {SpriteAlignment::NONE}; bool cd_static {}; bool cd_dynamic {}; bool vob_static {}; - shadow_mode dynamic_shadows {}; + ShadowType dynamic_shadows {}; bool physics_enabled {}; - animation_mode anim_mode {}; + AnimationType anim_mode {}; std::int32_t bias {}; bool ambient {}; float anim_strength {}; @@ -150,20 +199,20 @@ namespace phoenix { std::string vob_name {}; std::string visual_name {}; - visual_type associated_visual_type {}; - std::optional visual_decal {}; + VisualType associated_visual_type {}; + std::optional visual_decal {}; /// \brief Contains extra data about the item in the context of a saved game. - std::optional saved; + std::optional saved {}; /// \brief The children of this VOb. - std::vector> children {}; + std::vector> children {}; /// \brief Default virtual destructor. - virtual ~vob() = default; + virtual ~VirtualObject() = default; /// \return `true` if this VOb is from a save-game and `false` if not. - [[nodiscard]] PHOENIX_API inline bool is_save_game() const noexcept { + [[nodiscard]] ZKAPI inline bool is_save_game() const noexcept { return saved.has_value(); } @@ -175,7 +224,9 @@ namespace phoenix { /// \param[out] obj The object to read. /// \param[in,out] ctx The archive reader to read from. /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. - PHOENIX_API static void parse(vob& obj, archive_reader& ctx, game_version version); + /// \throws ParserError if parsing fails. + ZKREM("use ::load()") ZKAPI static void parse(VirtualObject& obj, ReadArchive& ctx, GameVersion version); + + ZKAPI virtual void load(ReadArchive& r, GameVersion version); }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/vobs/Zone.hh b/include/zenkit/vobs/Zone.hh index 64b43401..c6b83bf5 100644 --- a/include/zenkit/vobs/Zone.hh +++ b/include/zenkit/vobs/Zone.hh @@ -1,61 +1,73 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "../Api.hh" -#include - -namespace phoenix::vobs { - /// \brief A VOb which defines the background music in a certain zone. - struct zone_music : public vob { - bool enabled {false}; - std::int32_t priority {0}; - bool ellipsoid {false}; - float reverb {0}; - float volume {0}; - bool loop {false}; - - // Save-game only variables - bool s_local_enabled {true}; - bool s_day_entrance_done {false}; - bool s_night_entrance_done {false}; - - /// \brief Parses a zone music VOb the given *ZenGin* archive. - /// \param[out] obj The object to read. - /// \param[in,out] ctx The archive reader to read from. - /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. - /// \see vob::parse - PHOENIX_API static void parse(zone_music& obj, archive_reader& ctx, game_version version); - }; - - /// \brief A VOb which defines the far plane settings in a certain zone. - struct zone_far_plane : public vob { - float vob_far_plane_z; - float inner_range_percentage; - - /// \brief Parses a zone far plane VOb the given *ZenGin* archive. - /// \param[out] obj The object to read. - /// \param[in,out] ctx The archive reader to read from. - /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. - /// \see vob::parse - PHOENIX_API static void parse(zone_far_plane& obj, archive_reader& ctx, game_version version); - }; - - /// \brief A VOb which defines the fog in a certain zone. - struct zone_fog : public vob { - float range_center {0}; - float inner_range_percentage {0}; - glm::u8vec4 color {}; - bool fade_out_sky {false}; - bool override_color {false}; - - /// \brief Parses a zone fog VOb the given *ZenGin* archive. - /// \param[out] obj The object to read. - /// \param[in,out] ctx The archive reader to read from. - /// \note After this function returns the position of \p ctx will be at the end of the parsed object. - /// \throws parser_error if parsing fails. - /// \see vob::parse - PHOENIX_API static void parse(zone_fog& obj, archive_reader& ctx, game_version version); - }; -} // namespace phoenix::vobs +#include "zenkit/Library.hh" +#include "zenkit/Misc.hh" +#include "zenkit/vobs/VirtualObject.hh" + +#include + +#include + +namespace zenkit { + class ReadArchive; + + namespace vobs { + /// \brief A VOb which defines the background music in a certain zone. + struct ZoneMusic : public VirtualObject { + bool enabled {false}; + std::int32_t priority {0}; + bool ellipsoid {false}; + float reverb {0}; + float volume {0}; + bool loop {false}; + + // Save-game only variables + bool s_local_enabled {true}; + bool s_day_entrance_done {false}; + bool s_night_entrance_done {false}; + + /// \brief Parses a zone music VOb the given *ZenGin* archive. + /// \param[out] obj The object to read. + /// \param[in,out] ctx The archive reader to read from. + /// \note After this function returns the position of \p ctx will be at the end of the parsed object. + /// \throws ParserError if parsing fails. + /// \see vob::parse + ZKREM("use ::load()") ZKAPI static void parse(ZoneMusic& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; + }; + + /// \brief A VOb which defines the far plane settings in a certain zone. + struct ZoneFarPlane : public VirtualObject { + float vob_far_plane_z; + float inner_range_percentage; + + /// \brief Parses a zone far plane VOb the given *ZenGin* archive. + /// \param[out] obj The object to read. + /// \param[in,out] ctx The archive reader to read from. + /// \note After this function returns the position of \p ctx will be at the end of the parsed object. + /// \throws ParserError if parsing fails. + /// \see vob::parse + ZKREM("use ::load()") ZKAPI static void parse(ZoneFarPlane& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; + }; + + /// \brief A VOb which defines the fog in a certain zone. + struct ZoneFog : public VirtualObject { + float range_center {0}; + float inner_range_percentage {0}; + glm::u8vec4 color {}; + bool fade_out_sky {false}; + bool override_color {false}; + + /// \brief Parses a zone fog VOb the given *ZenGin* archive. + /// \param[out] obj The object to read. + /// \param[in,out] ctx The archive reader to read from. + /// \note After this function returns the position of \p ctx will be at the end of the parsed object. + /// \throws ParserError if parsing fails. + /// \see vob::parse + ZKREM("use ::load()") ZKAPI static void parse(ZoneFog& obj, ReadArchive& ctx, GameVersion version); + ZKAPI void load(ReadArchive& r, GameVersion version) override; + }; + } // namespace vobs +} // namespace zenkit diff --git a/include/zenkit/world/BspTree.hh b/include/zenkit/world/BspTree.hh index 969a2525..8196776f 100644 --- a/include/zenkit/world/BspTree.hh +++ b/include/zenkit/world/BspTree.hh @@ -1,26 +1,30 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "../Api.hh" -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Library.hh" #include #include +#include #include #include -namespace phoenix { - enum class bsp_tree_mode : std::uint32_t { - indoor = 0, - outdoor = 1, +namespace zenkit { + enum class BspTreeType : std::uint32_t { + INDOOR = 0, + OUTDOOR = 1, + + // Deprecated entries. + indoor ZKREM("renamed to BspTreeType::INDOOR") = INDOOR, + outdoor ZKREM("renamed to BspTreeType::OUTDOOR") = OUTDOOR, }; /// \brief Represents a BSP tree node. - struct bsp_node { + struct BspNode { glm::vec4 plane; - bounding_box bbox; + AxisAlignedBoundingBox bbox; std::uint32_t polygon_index; std::uint32_t polygon_count; @@ -28,13 +32,13 @@ namespace phoenix { std::int32_t back_index {-1}; std::int32_t parent_index {-1}; - PHOENIX_API inline bool is_leaf() const noexcept { + [[nodiscard]] ZKAPI bool is_leaf() const noexcept { return front_index == -1 && back_index == -1; } }; /// \brief Represents a BSP sector. - struct bsp_sector { + struct BspSector { std::string name; std::vector node_indices; std::vector portal_polygon_indices; @@ -44,36 +48,13 @@ namespace phoenix { /// /// [Binary space partitioning](https://en.wikipedia.org/wiki/Binary_space_partitioning) is used for rapidly /// drawing three-dimensional scenes and performing ray-casts. - class bsp_tree { + class BspTree { public: - /// \brief Parses a BSP tree from the data in the given buffer. - /// - ///

    This implementation is heavily based on the implementation found in - /// [ZenLib](https://github.com/Try/ZenLib). - /// - /// \param[in,out] buf The buffer to read from. - /// \param version The version identifier of the tree in the buffer. - /// \return The parsed BSP tree. - /// \note After this function returns the position of \p buf will be at the end of the parsed object. - /// If you would like to keep your buffer immutable, consider passing a copy of it to #parse(buffer&&) - /// using buffer::duplicate. - /// \throws parser_error if parsing fails. - /// \see #parse(buffer&&) - [[nodiscard]] PHOENIX_INTERNAL static bsp_tree parse(buffer& in, std::uint32_t version); - - /// \brief Parses a BSP tree from the data in the given buffer. - /// \param[in] buf The buffer to read from (by rvalue-reference). - /// \param version The version identifier of the tree in the buffer. - /// \return The parsed BSP tree. - /// \throws parser_error if parsing fails. - /// \see #parse(buffer&) - [[nodiscard]] PHOENIX_INTERNAL inline static bsp_tree parse(buffer&& in, std::uint32_t version) { - return parse(in, version); - } + ZKINT void load(Read* r, std::uint32_t version); public: /// \brief The mode of the tree (either indoor or outdoor). - bsp_tree_mode mode; + BspTreeType mode; /// \brief A list of polygon indices. std::vector polygon_indices; @@ -85,15 +66,15 @@ namespace phoenix { std::vector light_points; /// \brief All BSP sectors. - std::vector sectors; + std::vector sectors; /// \brief Polygon indices of portals. std::vector portal_polygon_indices; /// \brief All BSP nodes associated with the tree. - std::vector nodes; + std::vector nodes; /// \brief All BSP leaf node indices. std::vector leaf_node_indices; }; -} // namespace phoenix +} // namespace zenkit diff --git a/include/zenkit/world/VobTree.hh b/include/zenkit/world/VobTree.hh index d30cc4d8..fa1b2ca4 100644 --- a/include/zenkit/world/VobTree.hh +++ b/include/zenkit/world/VobTree.hh @@ -1,19 +1,19 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include -#include -#include +#include "zenkit/Archive.hh" +#include "zenkit/Misc.hh" +#include "zenkit/vobs/VirtualObject.hh" #include #include #include -namespace phoenix { +namespace zenkit { /// \brief Parses a VOB tree from the given reader. /// \param in The reader to read from. /// \param version The version of Gothic being used. /// \return The tree parsed. - PHOENIX_API std::unique_ptr parse_vob_tree(archive_reader& in, game_version version); -} // namespace phoenix + ZKAPI std::unique_ptr parse_vob_tree(ReadArchive& in, GameVersion version); +} // namespace zenkit diff --git a/include/zenkit/world/WayNet.hh b/include/zenkit/world/WayNet.hh index 6c7fa7ef..a23a10e4 100644 --- a/include/zenkit/world/WayNet.hh +++ b/include/zenkit/world/WayNet.hh @@ -1,26 +1,30 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include "phoenix/Api.hh" -#include +#include "zenkit/Library.hh" +#include + +#include +#include #include #include -namespace phoenix { +namespace zenkit { + class ReadArchive; + /// \brief Represents a single waypoint. - struct way_point { + struct WayPoint { std::string name; std::int32_t water_depth; bool under_water; glm::vec3 position; glm::vec3 direction; - bool free_point {false}; }; /// \brief Represents a connection between two waypoints. - struct way_edge { + struct WayEdge { /// \brief The index of the first waypoint of the connection. std::uint32_t a; @@ -31,28 +35,18 @@ namespace phoenix { /// \brief Represents a way-net. /// /// Way-nets are used for NPC navigation and path finding. - class way_net { + class WayNet { public: /// \brief PParses a way-net from the given reader. /// \param in The reader to read from. /// \return The way-net parsed. - PHOENIX_INTERNAL static way_net parse(archive_reader& in); - - /// \brief Get the waypoint with the given name. - /// \param name The name of the waypoint to get. - /// \return A pointer to the waypoint or `nullptr` if the waypoint was not fount. - [[nodiscard]] PHOENIX_DEPRECATED("unsupported API") - PHOENIX_API const way_point* waypoint(const std::string& name) const; + ZKINT void load(ReadArchive& in); public: /// \brief All waypoints of this way-net. - std::vector waypoints; + std::vector waypoints; /// \brief All edges of this way-net. - std::vector edges; - - private: - std::unordered_map _m_name_to_waypoint; + std::vector edges; }; - -} // namespace phoenix +} // namespace zenkit diff --git a/source/buffer.cc b/source/buffer.cc index dd5998a5..115940be 100644 --- a/source/buffer.cc +++ b/source/buffer.cc @@ -1,11 +1,10 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include -#include #include #include +#include #include namespace phoenix { @@ -491,7 +490,7 @@ namespace phoenix { } template - PHOENIX_INTERNAL void buffer::_put_t(T value) { + void buffer::_put_t(T value) { if (this->remaining() < sizeof(T)) { throw buffer_overflow {this->position(), sizeof(T)}; } @@ -501,7 +500,7 @@ namespace phoenix { } template - PHOENIX_INTERNAL T buffer::_get_t(std::uint64_t pos) const { + T buffer::_get_t(std::uint64_t pos) const { if (pos + sizeof(T) > limit()) { throw buffer_underflow {pos, sizeof(T)}; } @@ -512,7 +511,7 @@ namespace phoenix { } template - PHOENIX_INTERNAL T buffer::_get_t() { + T buffer::_get_t() { auto tmp = this->_get_t(this->position()); _m_position += sizeof(T); return tmp; diff --git a/source/phoenix.cc b/source/phoenix.cc deleted file mode 100644 index f8692b29..00000000 --- a/source/phoenix.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright © 2022 Luis Michaelis -// SPDX-License-Identifier: MIT -#include -#include - -#include -#include - -namespace phoenix { - error::error(std::string&& msg) : std::exception(), message(std::move(msg)) {} - - parser_error::parser_error(std::string&& type) - : error("failed parsing resource of type " + type), resource_type(type) {} - - parser_error::parser_error(std::string&& type, std::string&& ctx) - : error("failed parsing resource of type " + type + " [context: " + ctx + "]"), resource_type(std::move(type)), - context(std::move(ctx)) {} - - parser_error::parser_error(std::string&& type, const std::exception& other_exc) - : error("failed parsing resource of type " + type + " due to [" + other_exc.what() + "]"), - resource_type(std::move(type)), cause(other_exc) {} - - parser_error::parser_error(std::string&& type, const std::exception& other_exc, std::string&& ctx) - : error("failed parsing resource of type " + type + " due to [" + other_exc.what() + "] [context: " + ctx + - "]"), - resource_type(std::move(type)), context(std::move(ctx)), cause(other_exc) {} - - std::optional> logging::callback {}; - - void logging::use_logger(std::function&& cb) { - logging::callback = std::forward(cb); - } - - void logging::use_default_logger() { - logging::callback = [](level lvl, const std::string& message) { - switch (lvl) { - case level::error: - std::cerr << "[phoenix] [error] " << message << "\n"; - break; - case level::warn: - std::cerr << "[phoenix] [warn ] " << message << "\n"; - break; - case level::info: - std::cerr << "[phoenix] [info ] " << message << "\n"; - break; - case level::debug: - std::cerr << "[phoenix] [debug] " << message << "\n"; - break; - } - }; - } - - bool iequals(std::string_view a, std::string_view b) { - return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char c1, char c2) { - return std::tolower(c1) == std::tolower(c2); - }); - } - - bool icompare(std::string_view a, std::string_view b) { - return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), [](char c1, char c2) { - return std::tolower(c1) < std::tolower(c2); - }); - } - - date date::parse(buffer& buf) { - date dt {}; - dt.year = buf.get_uint(); - dt.month = buf.get_ushort(); - dt.day = buf.get_ushort(); - dt.hour = buf.get_ushort(); - dt.minute = buf.get_ushort(); - dt.second = buf.get_ushort(); - (void) buf.get_ushort(); // padding - return dt; - } -} // namespace phoenix diff --git a/source/vdfs.cc b/source/vdfs.cc deleted file mode 100644 index b49f6953..00000000 --- a/source/vdfs.cc +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright © 2022 Luis Michaelis -// SPDX-License-Identifier: MIT -#include - -namespace phoenix { - std::time_t dos_to_unix_time(std::uint32_t dos) noexcept { - struct tm t {}; - - t.tm_year = ((int32_t) ((dos >> 25) & 0x7F)) + 80; - t.tm_mon = ((int32_t) ((dos >> 21) & 0xF)) - 1; - t.tm_mday = (int32_t) ((dos >> 16) & 0x1F); - t.tm_hour = (int32_t) ((dos >> 11) & 0x1F); - t.tm_min = (int32_t) ((dos >> 5) & 0x3F); - t.tm_sec = ((int32_t) ((dos >> 0) & 0x1F)) * 2; - - return mktime(&t); - } - - std::uint32_t unix_time_to_dos(std::time_t nix) noexcept { - struct std::tm* t {std::gmtime(&nix)}; - std::uint32_t dos {0}; - - dos |= static_cast((t->tm_year - 80) << 25); - dos |= static_cast((t->tm_mon + 1) << 21); - dos |= static_cast(t->tm_mday << 16); - dos |= static_cast(t->tm_hour << 11); - dos |= static_cast(t->tm_min << 5); - dos |= static_cast((t->tm_sec / 2) << 0); - - return dos; - } - - vdfs_signature_error::vdfs_signature_error(const std::string& signature) - : error("VDF signature not recognized: \"" + signature + "\"") {} - - bool vdf_entry_comparator::operator()(const vdf_entry& a, const vdf_entry& b) const { - return icompare(a.name, b.name); - } - - bool vdf_entry_comparator::operator()(const vdf_entry& a, std::string_view b) const { - return icompare(a.name, b); - } - - bool vdf_entry_comparator::operator()(std::string_view a, const vdf_entry& b) const { - return icompare(a, b.name); - } - - vdf_header::vdf_header(std::string_view comment_text, std::time_t ts) : comment(comment_text), timestamp(ts) {} - - vdf_header vdf_header::read(buffer& in) { - vdf_header header {}; - - header.comment = in.get_string(VDF_COMMENT_LENGTH); - header.signature = in.get_string(VDF_SIGNATURE_LENGTH); - header.entry_count = in.get_uint(); - header.file_count = in.get_uint(); - header.timestamp = dos_to_unix_time(in.get_uint()); - header.size = in.get_uint(); - header.catalog_offset = in.get_uint(); - header.version = in.get_uint(); - - if (auto it = header.comment.find('\x1A'); it != std::string::npos) { - header.comment.resize(it); - } - - return header; - } - - vdf_entry::vdf_entry(std::string_view entry_name, std::uint32_t attrs) - : name(entry_name), type(VDF_MASK_DIRECTORY), attributes(attrs) {} - - const vdf_entry* vdf_entry::resolve_path(std::string_view path) const { - auto it = path.find('/'); - auto current = path.substr(0, it); - - auto result = this->children.find(current); - if (result == this->children.end()) { - return nullptr; - } - - if (it != std::string_view::npos) { - return (*result).resolve_path(path.substr(it + 1)); - } - - return &*result; - } - - const vdf_entry* vdf_entry::find_child(std::string_view child_name) const { - auto result = this->children.find(child_name); - if (result == this->children.end()) { - // recurse the search - const vdf_entry* child; - - for (const auto& entry : children) { - if ((child = entry.find_child(child_name), child != nullptr)) { - return child; - } - } - - return nullptr; - } - - return &*result; - } - - vdf_entry* vdf_entry::find_child(std::string_view child_name) { - auto result = this->children.find(child_name); - if (result == this->children.end()) { - // recurse the search - vdf_entry* child; - - for (const auto& entry : children) { - if ((child = const_cast(entry.find_child(child_name)), child != nullptr)) { - return child; - } - } - - return nullptr; - } - - return const_cast(&*result); - } - - vdf_entry vdf_entry::read(buffer& in, std::uint32_t catalog_offset) { - vdf_entry entry {}; - - entry.name = in.get_string(VDF_ENTRY_NAME_LENGTH); - entry.offset = in.get_uint(); - entry.size = in.get_uint(); - entry.type = in.get_uint(); - entry.attributes = in.get_uint(); - - if (auto it = entry.name.find('\x20'); it != std::string::npos) { - entry.name.resize(it); - } - - if (entry.is_directory()) { - auto self_offset = in.position(); - in.position(catalog_offset + entry.offset * vdf_entry::packed_size); - - const vdf_entry* child = nullptr; - do { - child = &*std::get<0>(entry.children.insert(read(in, catalog_offset))); - } while (!child->is_last()); - - in.position(self_offset); - } else { - if (entry.offset + entry.size > in.limit()) { - entry._m_data = in.slice(entry.offset, 0); - PX_LOGE("failed to parse VDF entry '{}': too big", entry.name); - } else { - entry._m_data = in.slice(entry.offset, entry.size); - } - } - - return entry; - } - - void vdf_entry::merge(const vdf_entry& itm, bool override_existing) { - auto result = this->children.find(itm.name); - if (result == this->children.end()) { - // If no matching entry was found, this is a new one. - // Just add it to the children of this entry. - children.insert(itm); - } else { - if (itm.is_file() || (*result).is_file()) { - if (!override_existing) { - return; - } - - // If an entry with the same name is found and either is a file, - // replace the entry with the new one. - children.erase(result); - children.insert(itm); - } else { - // Otherwise, the entry is a directory, so we just continue the merge. - for (const auto& child : itm.children) { - const_cast(*result).merge(child, override_existing); - } - } - } - } - - vdf_file::vdf_file(std::string_view comment, std::time_t timestamp) : header(comment, timestamp) {} - - const vdf_entry* vdf_file::find_entry(std::string_view name) const { - auto result = this->entries.find(name); - if (result == this->entries.end()) { - // recurse the search - vdf_entry* child; - - for (const auto& entry : entries) { - if ((child = const_cast(entry.find_child(name)), child != nullptr)) { - return child; - } - } - - return nullptr; - } - - return &*result; - } - - const vdf_entry* vdf_file::resolve_path(std::string_view path) const { - auto it = path.find('/'); - auto name = path.substr(0, it); - - auto result = this->entries.find(name); - if (result == this->entries.end()) { - return nullptr; - } - - if (it != std::string_view::npos) { - return (*result).resolve_path(path.substr(it + 1)); - } - - return &*result; - } - - vdf_entry* vdf_file::find_entry(std::string_view name) { - auto result = this->entries.find(name); - if (result == this->entries.end()) { - // recurse the search - vdf_entry* child; - - for (const auto& entry : entries) { - if ((child = const_cast(entry.find_child(name)), child != nullptr)) { - return child; - } - } - - return nullptr; - } - - return const_cast(&*result); - } - - vdf_file vdf_file::open(const std::filesystem::path& path) { - auto buf = buffer::mmap(path); - - vdf_file vdf {}; - vdf.header = vdf_header::read(buf); - - // TODO: Reverse-engineer Union VDF format - if (vdf.header.signature != VDF_SIGNATURE_G1 && vdf.header.signature != VDF_SIGNATURE_G2) { - throw vdfs_signature_error {vdf.header.signature}; - } - - buf.position(vdf.header.catalog_offset); - - const vdf_entry* entry = nullptr; - do { - entry = &*std::get<0>(vdf.entries.insert(vdf_entry::read(buf, vdf.header.catalog_offset))); - } while (!entry->is_last()); - - return vdf; - } - - vdf_file vdf_file::open(phoenix::buffer& buf) { - vdf_file vdf {}; - - vdf.header = vdf_header::read(buf); - - // TODO: Reverse-engineer Union VDF format - if (vdf.header.signature != VDF_SIGNATURE_G1 && vdf.header.signature != VDF_SIGNATURE_G2) { - throw vdfs_signature_error {vdf.header.signature}; - } - - buf.position(vdf.header.catalog_offset); - - const vdf_entry* entry = nullptr; - do { - entry = &*std::get<0>(vdf.entries.insert(vdf_entry::read(buf, vdf.header.catalog_offset))); - } while (!entry->is_last()); - - return vdf; - } - - void vdf_file::merge(const vdf_file& file, bool override_existing) { - for (const auto& child : file.entries) { - auto result = this->entries.find(child.name); - if (result == this->entries.end()) { - // If no matching entry was found, this is a new one. - // Just add it to the children of this entry. - entries.insert(child); - } else { - if (child.is_file() || (*result).is_file()) { - // If an entry with the same name is found and either is a file, - // replace the entry with the new one. - if (override_existing) { - entries.erase(result); - entries.insert(child); - } - } else { - // Otherwise, the entry is a directory, so we just continue the merge. - for (const auto& sub_child : child.children) { - const_cast(*result).merge(sub_child, override_existing); - } - } - } - } - } -} // namespace phoenix diff --git a/src/Archive.cc b/src/Archive.cc index 1ad4349a..9f25a952 100644 --- a/src/Archive.cc +++ b/src/Archive.cc @@ -1,112 +1,118 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" -#include "archive/archive_ascii.hh" -#include "archive/archive_binary.hh" -#include "archive/archive_binsafe.hh" +#include "Internal.hh" + +#include "archive/ArchiveAscii.hh" +#include "archive/ArchiveBinary.hh" +#include "archive/ArchiveBinsafe.hh" + +#include "phoenix/buffer.hh" #include -namespace phoenix { - constexpr std::string_view type_names[] = { - "unknown", // ? = 0x00 - "string", // bs_string = 0x01, - "int", // bs_int = 0x02, - "float", // bs_float = 0x03, - "byte", // bs_byte = 0x04, - "word", // bs_word = 0x05, - "bool", // bs_bool = 0x06, - "vec3", // bs_vec3 = 0x07, - "color", // bs_color = 0x08, - "raw", // bs_raw = 0x09, - "unknown", // ? = 0x0A - "unknown", // ? = 0x0B - "unknown", // ? = 0x0C - "unknown", // ? = 0x0D - "unknown", // ? = 0x0E - "unknown", // ? = 0x0F - "unknown", // bs_raw_float = 0x10, - "enum", // bs_enum = 0x11, - "hash", // bs_hash = 0x12, - }; - - archive_header archive_header::parse(buffer& in) { - try { - archive_header header {}; +namespace zenkit { + ReadArchive::ReadArchive(ArchiveHeader head, Read* r) : header(std::move(head)), read(r) {} - if (in.get_line() != "ZenGin Archive") { - throw parser_error {"archive_header", "magic missing"}; - } + ReadArchive::ReadArchive(ArchiveHeader head, Read* r, std::unique_ptr owned) + : header(std::move(head)), read(r), _m_owned(std::move(owned)) {} - std::string version = in.get_line(); - if (version.find("ver ") != 0) { - throw parser_error {"archive_header", "ver field missing"}; + void ArchiveHeader::load(Read* r) { + try { + if (r->read_line(true) != "ZenGin Archive") { + ZKLOGE("ReadArchive", "Invalid Header"); + throw zenkit::ParserError {"ReadArchive", "magic missing"}; } - header.version = std::stoi(version.substr(version.find(' ') + 1)); - - header.archiver = in.get_line(); - auto format = in.get_line(); - if (format == "ASCII") { - header.format = archive_format::ascii; - } else if (format == "BINARY") { - header.format = archive_format::binary; - } else if (format == "BIN_SAFE") { - header.format = archive_format::binsafe; + std::string ver = r->read_line(true); + if (ver.find("ver ") != 0) { + throw zenkit::ParserError {"ReadArchive", "ver field missing"}; + } + this->version = std::stoi(ver.substr(ver.find(' ') + 1)); + this->archiver = r->read_line(true); + + auto fmt = r->read_line(true); + if (fmt == "ASCII") { + this->format = ArchiveFormat::ASCII; + } else if (fmt == "BINARY") { + this->format = ArchiveFormat::BINARY; + } else if (fmt == "BIN_SAFE") { + this->format = ArchiveFormat::BINSAFE; } - std::string save_game = in.get_line(); + std::string save_game = r->read_line(true); if (save_game.find("saveGame ") != 0) { - throw parser_error {"archive_header", "saveGame field missing"}; + throw zenkit::ParserError {"ReadArchive", "saveGame field missing"}; } - header.save = std::stoi(save_game.substr(save_game.find(' ') + 1)) != 0; + this->save = std::stoi(save_game.substr(save_game.find(' ') + 1)) != 0; - std::string optional = in.get_line(); + std::string optional = r->read_line(true); if (optional.find("date ") == 0) { - header.date = optional.substr(optional.find(' ') + 1); - optional = in.get_line(); + this->date = optional.substr(optional.find(' ') + 1); + optional = r->read_line(true); } if (optional.find("user ") == 0) { - header.user = optional.substr(optional.find(' ') + 1); - optional = in.get_line(); + this->user = optional.substr(optional.find(' ') + 1); + optional = r->read_line(true); } if (optional != "END") { - throw parser_error {"archive_header", "first END missing"}; + throw zenkit::ParserError {"ReadArchive", "first END missing"}; } - - return header; - } catch (const buffer_error& exc) { - throw parser_error {"archive_header", exc, "eof reached"}; } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_binary", e, "reading int"}; + throw zenkit::ParserError {"ReadArchive", e, "reading int"}; } } - std::unique_ptr archive_reader::open(buffer& in) { - auto header = archive_header::parse(in); - std::unique_ptr reader; + std::unique_ptr ReadArchive::open(phoenix::buffer& in) { + auto read = zenkit::Read::from(&in); + + ArchiveHeader header {}; + header.load(read.get()); + + std::unique_ptr reader; + if (header.format == ArchiveFormat::ASCII) { + reader = std::make_unique(std::move(header), read.get(), std::move(read)); + } else if (header.format == ArchiveFormat::BINARY) { + reader = std::make_unique(std::move(header), read.get(), std::move(read)); + } else if (header.format == ArchiveFormat::BINSAFE) { + reader = std::make_unique(std::move(header), read.get(), std::move(read)); + } else { + throw zenkit::ParserError {"ReadArchive", + "format '" + std::to_string(static_cast(header.format)) + + "' is not supported"}; + } + + reader->read_header(); + return reader; + } - if (header.format == archive_format::ascii) { - reader = std::make_unique(in, std::move(header)); - } else if (header.format == archive_format::binary) { - reader = std::make_unique(in, std::move(header)); - } else if (header.format == archive_format::binsafe) { - reader = std::make_unique(in, std::move(header)); + std::unique_ptr ReadArchive::from(Read* r) { + ArchiveHeader header {}; + header.load(r); + + std::unique_ptr reader; + if (header.format == ArchiveFormat::ASCII) { + reader = std::make_unique(std::move(header), r); + } else if (header.format == ArchiveFormat::BINARY) { + reader = std::make_unique(std::move(header), r); + } else if (header.format == ArchiveFormat::BINSAFE) { + reader = std::make_unique(std::move(header), r); } else { - throw parser_error {"archiver_reader", - "format '" + std::to_string(static_cast(header.format)) + - "' is not supported"}; + throw zenkit::ParserError {"ReadArchive", + "format '" + std::to_string(static_cast(header.format)) + + "' is not supported"}; } reader->read_header(); return reader; } - void archive_reader::skip_object(bool skip_current) { - archive_object tmp; + void ReadArchive::skip_object(bool skip_current) { + ArchiveObject tmp; int32_t level = skip_current ? 1 : 0; do { @@ -119,82 +125,4 @@ namespace phoenix { } } while (level > 0); } - - void archive_reader::print_structure(bool open_object) { - this->unstable__visit_objects( - open_object, - [](const std::optional& obj, const std::optional& ent) { - if (obj) { - std::cout << "class_name << "\" name=\"" << obj->object_name - << "\" version=\"" << obj->version << "\" index=\"" << obj->index << "\">\n"; - } else if (ent) { - std::cout << "name << "\" type=\"" - << type_names[static_cast(ent->type)] << "\" "; - - switch (ent->type) { - case archive_entry_type::string: - std::cout << "value=\"" << std::get(ent->value) << "\" "; - break; - case archive_entry_type::int_: - std::cout << "value=\"" << std::get(ent->value) << "\" "; - break; - case archive_entry_type::float_: - std::cout << "value=\"" << std::get(ent->value) << "\" "; - break; - case archive_entry_type::byte: - std::cout << "value=\"" << static_cast(std::get(ent->value)) << "\" "; - break; - case archive_entry_type::word: - std::cout << "value=\"" << std::get(ent->value) << "\" "; - break; - case archive_entry_type::bool_: - std::cout << "value=\"" << std::get(ent->value) << "\" "; - break; - case archive_entry_type::vec3: { - auto v = std::get(ent->value); - std::cout << "value=\"(" << v.x << ", " << v.y << ", " << v.z << ")\" "; - break; - } - case archive_entry_type::color: { - auto v = std::get(ent->value); - std::cout << "value=\"(" << v.r << ", " << v.g << ", " << v.b << ", " << v.a << ")\" "; - break; - } - case archive_entry_type::raw: - case archive_entry_type::raw_float: - std::cout << "length=\"" << std::get(ent->value).remaining() << "\" "; - break; - case archive_entry_type::enum_: - std::cout << "value=\"" << std::get(ent->value) << "\" "; - break; - case archive_entry_type::hash: - std::cout << "value=\"" << std::get(ent->value) << "\" "; - break; - } - - std::cout << "/>\n"; - } else { - std::cout << "\n"; - } - }); - } - - void archive_reader::unstable__visit_objects(bool open_object, const archive_visitor& cb) { - std::variant ent; - int32_t level = open_object ? 1 : 0; - - do { - ent = unstable__next(); - - if (std::holds_alternative(ent)) { - cb(std::get(ent), std::nullopt); - ++level; - } else if (std::holds_alternative(ent)) { - cb(std::nullopt, std::nullopt); - --level; - } else { - cb(std::nullopt, std::get(ent)); - } - } while (level > 0); - } -} // namespace phoenix +} // namespace zenkit diff --git a/src/Boxes.cc b/src/Boxes.cc index cfb9f559..977f96f6 100644 --- a/src/Boxes.cc +++ b/src/Boxes.cc @@ -1,35 +1,50 @@ -// Copyright © 2023 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include -#include +#include "zenkit/Boxes.hh" +#include "zenkit/Stream.hh" + +#include "phoenix/buffer.hh" #include -namespace phoenix { - bounding_box bounding_box::parse(buffer& in) { - bounding_box bbox {}; - bbox.min = in.get_vec3(); - bbox.max = in.get_vec3(); +namespace zenkit { + AxisAlignedBoundingBox AxisAlignedBoundingBox::parse(phoenix::buffer& in) { + AxisAlignedBoundingBox bbox {}; + + auto r = Read::from(&in); + bbox.load(r.get()); + return bbox; } - obb obb::parse(buffer& in) { - obb bbox {}; - bbox.center = in.get_vec3(); - bbox.axes[0] = in.get_vec3(); - bbox.axes[1] = in.get_vec3(); - bbox.axes[2] = in.get_vec3(); - bbox.half_width = in.get_vec3(); - - auto child_count = in.get_ushort(); - for (int i = 0; i < child_count; ++i) { - bbox.children.push_back(parse(in)); - } + void AxisAlignedBoundingBox::load(Read* r) { + this->min = r->read_vec3(); + this->max = r->read_vec3(); + } + + OrientedBoundingBox OrientedBoundingBox::parse(phoenix::buffer& in) { + OrientedBoundingBox bbox {}; + + auto r = Read::from(&in); + bbox.load(r.get()); return bbox; } - bounding_box obb::as_bbox() const { + void OrientedBoundingBox::load(zenkit::Read* r) { + center = r->read_vec3(); + axes[0] = r->read_vec3(); + axes[1] = r->read_vec3(); + axes[2] = r->read_vec3(); + half_width = r->read_vec3(); + + children.resize(r->read_ushort()); + for (auto& child : children) { + child.load(r); + } + } + + AxisAlignedBoundingBox OrientedBoundingBox::as_bbox() const { const float sign[8][3] = {{-1, -1, -1}, {-1, -1, +1}, {-1, +1, -1}, @@ -39,7 +54,7 @@ namespace phoenix { {+1, +1, -1}, {+1, +1, +1}}; - bounding_box box {}; + AxisAlignedBoundingBox box {}; box.min = {std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()}; @@ -69,4 +84,4 @@ namespace phoenix { return box; } -} // namespace phoenix \ No newline at end of file +} // namespace zenkit diff --git a/src/CutsceneLibrary.cc b/src/CutsceneLibrary.cc index ddaa0bff..74bb5efc 100644 --- a/src/CutsceneLibrary.cc +++ b/src/CutsceneLibrary.cc @@ -1,87 +1,105 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/CutsceneLibrary.hh" +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" -namespace phoenix { - messages messages::parse(buffer& buf) { - auto archive = archive_reader::open(buf); - messages msgs {}; +#include "Internal.hh" - archive_object obj; +#include "phoenix/buffer.hh" + +namespace zenkit { + CutsceneLibrary CutsceneLibrary::parse(phoenix::buffer& buf) { + CutsceneLibrary msgs {}; + + auto r = Read::from(&buf); + msgs.load(r.get()); + + return msgs; + } + + CutsceneLibrary CutsceneLibrary::parse(phoenix::buffer&& path) { + return CutsceneLibrary::parse(path); + } + + const CutsceneBlock* CutsceneLibrary::block_by_name(std::string_view name) const { + auto result = std::lower_bound(this->blocks.begin(), this->blocks.end(), name, [](const auto& it, auto n) { + return it.name < n; + }); + + if (result == this->blocks.end()) { + return nullptr; + } + + return &*result; + } + + void CutsceneLibrary::load(Read* r) { + auto archive = ReadArchive::from(r); + + ArchiveObject obj; if (!archive->read_object_begin(obj)) { - throw parser_error {"messages", "root object missing"}; + throw zenkit::ParserError {"CutsceneLibrary", "root object missing"}; } if (obj.class_name != "zCCSLib") { - throw parser_error {"messages", "root object is not 'zCCSLib'"}; + throw zenkit::ParserError {"CutsceneLibrary", "root object is not 'zCCSLib'"}; } auto item_count = archive->read_int(); // NumOfItems - msgs.blocks.reserve(static_cast(item_count)); + this->blocks.resize(static_cast(item_count)); - for (int32_t i = 0; i < item_count; ++i) { + for (auto& itm : this->blocks) { if (!archive->read_object_begin(obj) || obj.class_name != "zCCSBlock") { - throw parser_error {"messages", "expected 'zCCSBlock' but didn't find it"}; + throw zenkit::ParserError {"CutsceneLibrary", "expected 'zCCSBlock' but didn't find it"}; } - auto& itm = msgs.blocks.emplace_back(); itm.name = archive->read_string(); // blockName auto block_count = archive->read_int(); // numOfBlocks (void) archive->read_float(); // subBlock0 if (block_count != 1) { - throw parser_error {"messages", - "expected only one block but got " + std::to_string(block_count) + " for " + - itm.name}; + throw zenkit::ParserError {"CutsceneLibrary", + "expected only one block but got " + std::to_string(block_count) + " for " + + itm.name}; } if (!archive->read_object_begin(obj) || obj.class_name != "zCCSAtomicBlock") { - throw parser_error {"messages", "expected atomic block not found for " + itm.name}; + throw zenkit::ParserError {"CutsceneLibrary", "Expected atomic block not found for " + itm.name}; } if (!archive->read_object_begin(obj) || obj.class_name != "oCMsgConversation:oCNpcMessage:zCEventMessage") { - throw parser_error {"messages", "expected oCMsgConversation not found for " + itm.name}; + throw zenkit::ParserError {"CutsceneLibrary", "Expected oCMsgConversation not found for " + itm.name}; } - itm.message.type = archive->read_enum(); - itm.message.text = archive->read_string(); - itm.message.name = archive->read_string(); + itm.message.type = archive->read_enum(); // subType + itm.message.text = archive->read_string(); // text + itm.message.name = archive->read_string(); // name if (!archive->read_object_end()) { archive->skip_object(true); - PX_LOGW("messages: oCMsgConversation(\"", itm.name, "\") not fully parsed"); + ZKLOGW("CutsceneLibrary", "oCMsgConversation(\"%s\") not fully parsed", itm.name.c_str()); } if (!archive->read_object_end()) { // FIXME: in Gothic I cutscene libraries, there is a `synchronized` attribute here archive->skip_object(true); - PX_LOGW("messages: zCCSAtomicBlock(\"", itm.name, "\") not fully parsed"); + ZKLOGW("CutsceneLibrary", "zCCSAtomicBlock(\"%s\") not fully parsed", itm.name.c_str()); } if (!archive->read_object_end()) { archive->skip_object(true); - PX_LOGW("messages: zCCSBlock(\"", itm.name, "\") not fully parsed"); + ZKLOGW("CutsceneLibrary", "zCCSBlock(\"%s\") not fully parsed", itm.name.c_str()); } } if (!archive->read_object_end()) { - PX_LOGW("messages: not fully parsed"); + ZKLOGW("CutsceneLibrary", "Not fully parsed"); } // Prepare blocks for binary search in block_by_name - std::sort(msgs.blocks.begin(), msgs.blocks.end(), [](const auto& a, const auto& b) { return a.name < b.name; }); - return msgs; - } - - const message_block* messages::block_by_name(std::string_view name) const { - auto result = std::lower_bound(this->blocks.begin(), this->blocks.end(), name, [](const auto& it, auto n) { - return it.name < n; + std::sort(this->blocks.begin(), this->blocks.end(), [](const auto& a, const auto& b) { + return a.name < b.name; }); - - if (result == this->blocks.end()) { - return nullptr; - } - - return &*result; } -} // namespace phoenix +} // namespace zenkit diff --git a/src/DaedalusScript.cc b/src/DaedalusScript.cc index 18a2a476..8a4e94cd 100644 --- a/src/DaedalusScript.cc +++ b/src/DaedalusScript.cc @@ -1,79 +1,83 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/DaedalusScript.hh" +#include "zenkit/Stream.hh" -#include +#include "Internal.hh" -namespace phoenix { - symbol_not_found::symbol_not_found(std::string&& sym_name) - : script_error("symbol not found: " + sym_name), name(sym_name) {} +#include "phoenix/buffer.hh" - member_registration_error::member_registration_error(const symbol* s, std::string&& msg) - : script_error("cannot register member " + s->name() + ": " + msg), sym(s) {} +namespace zenkit { + DaedalusSymbolNotFound::DaedalusSymbolNotFound(std::string&& sym_name) + : DaedalusScriptError("symbol not found: " + sym_name), name(sym_name) {} - invalid_registration_datatype::invalid_registration_datatype(const symbol* s, std::string&& provided) - : member_registration_error(s, - "wrong datatype: provided '" + provided + "' expected " + - DAEDALUS_DATA_TYPE_NAMES[(std::uint32_t) s->type()]) {} + DaedalusMemberRegistrationError::DaedalusMemberRegistrationError(const DaedalusSymbol* s, std::string&& msg) + : DaedalusScriptError("cannot register member " + s->name() + ": " + msg), sym(s) {} - illegal_type_access::illegal_type_access(const symbol* s, datatype expected_dt) - : illegal_access("illegal access of type " + std::to_string(static_cast(expected_dt)) + " on symbol " + - s->name() + " which is another type (" + std::to_string(static_cast(s->type())) + - ")"), + DaedalusInvalidRegistrationDataType::DaedalusInvalidRegistrationDataType(const DaedalusSymbol* s, + std::string&& provided) + : DaedalusMemberRegistrationError(s, + "wrong datatype: provided '" + provided + "' expected " + + DAEDALUS_DATA_TYPE_NAMES[(std::uint32_t) s->type()]) {} + + DaedalusIllegalTypeAccess::DaedalusIllegalTypeAccess(const DaedalusSymbol* s, DaedalusDataType expected_dt) + : DaedalusIllegalAccess("illegal access of type " + std::to_string(static_cast(expected_dt)) + + " on DaedalusSymbol " + s->name() + " which is another type (" + + std::to_string(static_cast(s->type())) + ")"), sym(s), expected(expected_dt) {} - illegal_index_access::illegal_index_access(const symbol* s, std::uint8_t idx) - : illegal_access("illegal access of out-of-bounds index " + std::to_string(idx) + " while reading " + - s->name()), + DaedalusIllegalIndexAccess::DaedalusIllegalIndexAccess(const DaedalusSymbol* s, std::uint8_t idx) + : DaedalusIllegalAccess("illegal access of out-of-bounds index " + std::to_string(idx) + " while reading " + + s->name()), sym(s), index(idx) {} - illegal_const_access::illegal_const_access(const symbol* s) - : illegal_access("illegal mutable access of const symbol " + s->name()), sym(s) {} + DaedalusIllegalConstAccess::DaedalusIllegalConstAccess(const DaedalusSymbol* s) + : DaedalusIllegalAccess("illegal mutable access of const symbol " + s->name()), sym(s) {} - illegal_instance_access::illegal_instance_access(const symbol* s, std::uint32_t parent) - : illegal_access("illegal access of member " + s->name() + - " which does not have the same parent " - "class as the context instance (" + - std::to_string(s->parent()) + " != " + std::to_string(parent) + ")"), + DaedalusIllegalInstanceAccess::DaedalusIllegalInstanceAccess(const DaedalusSymbol* s, std::uint32_t parent) + : DaedalusIllegalAccess("illegal access of member " + s->name() + + " which does not have the same parent " + "class as the context instance (" + + std::to_string(s->parent()) + " != " + std::to_string(parent) + ")"), sym(s), expected_parent(parent) {} - unbound_member_access::unbound_member_access(const symbol* s) - : illegal_access("illegal access of unbound member " + s->name()), sym(s) {} + DaedalusUnboundMemberAccess::DaedalusUnboundMemberAccess(const DaedalusSymbol* s) + : DaedalusIllegalAccess("illegal access of unbound member " + s->name()), sym(s) {} - no_context::no_context(const symbol* s) - : illegal_access("illegal access of member " + s->name() + " without a context set."), sym(s) {} + DaedalusNoContextError::DaedalusNoContextError(const DaedalusSymbol* s) + : DaedalusIllegalAccess("illegal access of member " + s->name() + " without a context set."), sym(s) {} - illegal_context_type::illegal_context_type(const symbol* s, const std::type_info& ctx) - : illegal_access("cannot access member " + s->name() + " on context instance of type " + ctx.name() + - " because this symbol is registered to instances of type " + s->registered_to().name()), + DaedalusIllegalContextType::DaedalusIllegalContextType(const DaedalusSymbol* s, const std::type_info& ctx) + : DaedalusIllegalAccess("cannot access member " + s->name() + " on context instance of type " + ctx.name() + + " because this symbol is registered to instances of type " + s->registered_to().name()), sym(s), context_type(ctx) {} - instruction instruction::decode(buffer& in) { - instruction s {}; - s.op = static_cast(in.get()); + DaedalusInstruction DaedalusInstruction::decode(Read* in) { + DaedalusInstruction s {}; + s.op = static_cast(in->read_ubyte()); s.size = 1; switch (s.op) { - case opcode::bl: - case opcode::bz: - case opcode::b: - s.address = in.get_uint(); + case DaedalusOpcode::BL: + case DaedalusOpcode::BZ: + case DaedalusOpcode::B: + s.address = in->read_uint(); s.size += sizeof(std::uint32_t); break; - case opcode::pushi: - s.immediate = in.get_int(); + case DaedalusOpcode::PUSHI: + s.immediate = in->read_int(); s.size += sizeof(std::uint32_t); break; - case opcode::be: - case opcode::pushv: - case opcode::pushvi: - case opcode::gmovi: - s.symbol = in.get_uint(); + case DaedalusOpcode::BE: + case DaedalusOpcode::PUSHV: + case DaedalusOpcode::PUSHVI: + case DaedalusOpcode::GMOVI: + s.symbol = in->read_uint(); s.size += sizeof(std::uint32_t); break; - case opcode::pushvv: - s.symbol = in.get_uint(); - s.index = in.get(); + case DaedalusOpcode::PUSHVV: + s.symbol = in->read_uint(); + s.index = in->read_ubyte(); s.size += sizeof(std::uint32_t) + sizeof(std::uint8_t); break; default: @@ -83,52 +87,68 @@ namespace phoenix { return s; } - script script::parse(const std::string& file) { - auto in = buffer::mmap(file); - return parse(in); + DaedalusScript DaedalusScript::parse(const std::string& file) { + DaedalusScript scr {}; + + auto r = Read::from(file); + scr.load(r.get()); + + return scr; } - script script::parse(phoenix::buffer& in) { - script scr {}; + DaedalusScript DaedalusScript::parse(phoenix::buffer& in) { + DaedalusScript scr {}; + + auto r = Read::from(&in); + scr.load(r.get()); + + return scr; + } - scr._m_version = in.get(); - auto symbol_count = in.get_uint(); + void DaedalusScript::load(Read* r) { + this->_m_version = r->read_ubyte(); + auto symbol_count = r->read_uint(); - scr._m_symbols.reserve(symbol_count + 1 /* account for temporary strings */); - scr._m_symbols_by_name.reserve(symbol_count + 1); - scr._m_symbols_by_address.reserve(symbol_count); - in.skip(symbol_count * sizeof(std::uint32_t)); // Sort table + this->_m_symbols.resize(symbol_count); + this->_m_symbols_by_name.reserve(symbol_count + 1); + this->_m_symbols_by_address.reserve(symbol_count); + + r->seek(static_cast(symbol_count * sizeof(std::uint32_t)), Whence::CUR); // Sort table // The sort table is a list of indexes into the symbol table sorted lexicographically by symbol name! for (std::uint32_t i = 0; i < symbol_count; ++i) { - auto* sym = &scr._m_symbols.emplace_back(symbol::parse(in)); - scr._m_symbols_by_name[sym->name()] = i; - sym->_m_index = i; + auto& sym = this->_m_symbols[i]; + sym.load(r); + + this->_m_symbols_by_name[sym.name()] = i; + sym._m_index = i; - if (sym->type() == datatype::prototype || sym->type() == datatype::instance || - (sym->type() == datatype::function && sym->is_const() && !sym->is_member())) { - scr._m_symbols_by_address[sym->address()] = i; + if (sym.type() == DaedalusDataType::PROTOTYPE || sym.type() == DaedalusDataType::INSTANCE || + (sym.type() == DaedalusDataType::FUNCTION && sym.is_const() && !sym.is_member())) { + this->_m_symbols_by_address[sym.address()] = i; } } - std::uint32_t text_size = in.get_uint(); - scr._m_text = in.extract(text_size); - return scr; + std::uint32_t text_size = r->read_uint(); + std::vector code(text_size, std::byte {}); + r->read(code.data(), text_size); + + this->_m_text = Read::from(std::move(code)); } - instruction script::instruction_at(std::uint32_t address) const { - _m_text.position(address); - return instruction::decode(_m_text); + DaedalusInstruction DaedalusScript::instruction_at(std::uint32_t address) const { + _m_text->seek(address, Whence::BEG); + return DaedalusInstruction::decode(_m_text.get()); } - const symbol* script::find_symbol_by_index(std::uint32_t index) const { + const DaedalusSymbol* DaedalusScript::find_symbol_by_index(std::uint32_t index) const { if (index > _m_symbols.size()) { return nullptr; } return &_m_symbols[index]; } - const symbol* script::find_symbol_by_name(std::string_view name) const { + const DaedalusSymbol* DaedalusScript::find_symbol_by_name(std::string_view name) const { std::string up {name}; std::transform(up.begin(), up.end(), up.begin(), ::toupper); @@ -139,7 +159,7 @@ namespace phoenix { return nullptr; } - const symbol* script::find_symbol_by_address(std::uint32_t address) const { + const DaedalusSymbol* DaedalusScript::find_symbol_by_address(std::uint32_t address) const { if (auto it = _m_symbols_by_address.find(address); it != _m_symbols_by_address.end()) { return find_symbol_by_index(it->second); } @@ -147,14 +167,14 @@ namespace phoenix { return nullptr; } - symbol* script::find_symbol_by_index(std::uint32_t index) { + DaedalusSymbol* DaedalusScript::find_symbol_by_index(std::uint32_t index) { if (index > _m_symbols.size()) { return nullptr; } return &_m_symbols[index]; } - symbol* script::find_symbol_by_name(std::string_view name) { + DaedalusSymbol* DaedalusScript::find_symbol_by_name(std::string_view name) { std::string up {name}; std::transform(up.begin(), up.end(), up.begin(), ::toupper); @@ -165,7 +185,7 @@ namespace phoenix { return nullptr; } - symbol* script::find_symbol_by_address(std::uint32_t address) { + DaedalusSymbol* DaedalusScript::find_symbol_by_address(std::uint32_t address) { if (auto it = _m_symbols_by_address.find(address); it != _m_symbols_by_address.end()) { return find_symbol_by_index(it->second); } @@ -173,17 +193,17 @@ namespace phoenix { return nullptr; } - void script::enumerate_instances_by_class_name(std::string_view name, - const std::function& callback) { + void DaedalusScript::enumerate_instances_by_class_name(std::string_view name, + const std::function& callback) { auto* cls = find_symbol_by_name(name); if (cls == nullptr) return; std::vector prototypes {}; for (auto& sym : _m_symbols) { - if (sym.type() == datatype::prototype && sym.parent() == cls->index()) { + if (sym.type() == DaedalusDataType::PROTOTYPE && sym.parent() == cls->index()) { prototypes.push_back(sym.index()); - } else if (sym.type() == datatype::instance && + } else if (sym.type() == DaedalusDataType::INSTANCE && (std::find(prototypes.begin(), prototypes.end(), sym.parent()) != prototypes.end() || sym.parent() == cls->index())) { callback(sym); @@ -191,8 +211,8 @@ namespace phoenix { } } - std::vector script::find_parameters_for_function(const symbol* parent) { - std::vector syms {}; + std::vector DaedalusScript::find_parameters_for_function(const DaedalusSymbol* parent) { + std::vector syms {}; for (uint32_t i = 0; i < parent->count(); ++i) { syms.push_back(find_symbol_by_index(parent->index() + i + 1)); @@ -201,8 +221,9 @@ namespace phoenix { return syms; } - std::vector script::find_parameters_for_function(const symbol* parent) const { - std::vector syms {}; + std::vector + DaedalusScript::find_parameters_for_function(const DaedalusSymbol* parent) const { + std::vector syms {}; for (uint32_t i = 0; i < parent->count(); ++i) { syms.push_back(find_symbol_by_index(parent->index() + i + 1)); @@ -211,8 +232,8 @@ namespace phoenix { return syms; } - std::vector script::find_class_members(const symbol& cls) { - std::vector members {}; + std::vector DaedalusScript::find_class_members(const DaedalusSymbol& cls) { + std::vector members {}; for (auto& sym : _m_symbols) { if (!sym.is_member() || sym.parent() != cls.index()) @@ -223,27 +244,27 @@ namespace phoenix { return members; } - void script::register_as_opaque(symbol* sym) { + void DaedalusScript::register_as_opaque(DaedalusSymbol* sym) { auto members = find_class_members(*sym); - auto registered_to = &typeid(opaque_instance); + auto registered_to = &typeid(DaedalusOpaqueInstance); size_t class_size = 0; for (auto* member : members) { member->_m_registered_to = registered_to; switch (member->type()) { - case datatype::void_: - case datatype::float_: - case datatype::integer: - case datatype::class_: - case datatype::function: - case datatype::prototype: - case datatype::instance: + case DaedalusDataType::VOID: + case DaedalusDataType::FLOAT: + case DaedalusDataType::INT: + case DaedalusDataType::CLASS: + case DaedalusDataType::FUNCTION: + case DaedalusDataType::PROTOTYPE: + case DaedalusDataType::INSTANCE: member->_m_member_offset = class_size; class_size += 4 * member->count(); break; - case datatype::string: { + case DaedalusDataType::STRING: { auto align = alignof(std::string); auto offset = class_size; @@ -263,11 +284,11 @@ namespace phoenix { sym->_m_class_size = class_size; } - symbol* script::add_temporary_strings_symbol() { - symbol sym {}; + DaedalusSymbol* DaedalusScript::add_temporary_strings_symbol() { + DaedalusSymbol sym {}; sym._m_name = "$PHOENIX_FAKE_STRINGS"; sym._m_generated = true; - sym._m_type = datatype::string; + sym._m_type = DaedalusDataType::STRING; sym._m_count = 1; sym._m_value = std::unique_ptr {new std::string[sym._m_count]}; sym._m_index = static_cast(_m_symbols.size()); @@ -275,131 +296,162 @@ namespace phoenix { return &_m_symbols.emplace_back(std::move(sym)); } - symbol symbol::parse(buffer& in) { - symbol sym {}; + std::uint32_t DaedalusScript::size() const noexcept { + auto off = _m_text->tell(); + _m_text->seek(0, Whence::END); + auto size = _m_text->tell(); + _m_text->seek(static_cast(off), Whence::BEG); + return size; + } + + DaedalusSymbol DaedalusSymbol::parse(phoenix::buffer& in) { + DaedalusSymbol sym {}; - if (in.get_uint() != 0) { - sym._m_name = in.get_line(false); + auto r = Read::from(&in); + sym.load(r.get()); - // If the name starts with \xFF, this symbol was automatically generated by the compiler - if (sym._m_name[0] == '\xFF') { - sym._m_name[0] = '$'; - sym._m_generated = true; + return sym; + } + + void zk_internal_escape(std::string& s) { + for (auto i = 0u; i < s.size(); ++i) { + if (s[i] == '\\') { + switch (s[i + 1]) { + case 'n': + s[i] = '\n'; + s.erase(i + 1, 1); + break; + case 't': + s[i] = '\t'; + s.erase(i + 1, 1); + break; + } + } + } + } + + void DaedalusSymbol::load(Read* r) { + if (r->read_uint() != 0) { + this->_m_name = r->read_line(false); + + // If the name starts with \xFF, this DaedalusSymbol was automatically generated by the compiler + if (this->_m_name[0] == '\xFF') { + this->_m_name[0] = '$'; + this->_m_generated = true; } } - auto vary = in.get_uint(); - auto properties = in.get_uint(); + auto vary = r->read_uint(); + auto properties = r->read_uint(); - sym._m_count = (properties >> 0U) & 0xFFFU; // 12 bits - sym._m_type = static_cast((properties >> 12U) & 0xFU); // 4 bits - sym._m_flags = static_cast((properties >> 16U) & 0x3FU); // 6 bits + this->_m_count = (properties >> 0U) & 0xFFFU; // 12 bits + this->_m_type = static_cast((properties >> 12U) & 0xFU); // 4 bits + this->_m_flags = static_cast((properties >> 16U) & 0x3FU); // 6 bits - if (sym.is_member()) { - sym._m_member_offset = vary; - } else if (sym.type() == datatype::class_) { - sym._m_class_size = vary; - } else if (sym.type() == datatype::function) { - sym._m_return_type = static_cast(vary); + if (this->is_member()) { + this->_m_member_offset = vary; + } else if (this->type() == DaedalusDataType::CLASS) { + this->_m_class_size = vary; + } else if (this->type() == DaedalusDataType::FUNCTION) { + this->_m_return_type = static_cast(vary); } - sym._m_file_index = in.get_uint() & 0x7FFFFU; // 19 bits - sym._m_line_start = in.get_uint() & 0x7FFFFU; // 19 bits - sym._m_line_count = in.get_uint() & 0x7FFFFU; // 19 bits - sym._m_char_start = in.get_uint() & 0xFFFFFFU; // 24 bits - sym._m_char_count = in.get_uint() & 0xFFFFFFU; // 24 bits + this->_m_file_index = r->read_uint() & 0x7FFFFU; // 19 bits + this->_m_line_start = r->read_uint() & 0x7FFFFU; // 19 bits + this->_m_line_count = r->read_uint() & 0x7FFFFU; // 19 bits + this->_m_char_start = r->read_uint() & 0xFFFFFFU; // 24 bits + this->_m_char_count = r->read_uint() & 0xFFFFFFU; // 24 bits - if (!sym.is_member()) { - switch (sym._m_type) { - case datatype::float_: { - std::unique_ptr value {new float[sym._m_count]}; - in.get((std::uint8_t*) value.get(), sym._m_count * sizeof(float)); - sym._m_value = std::move(value); + if (!this->is_member()) { + switch (this->_m_type) { + case DaedalusDataType::FLOAT: { + std::unique_ptr value {new float[this->_m_count]}; + r->read((std::uint8_t*) value.get(), this->_m_count * sizeof(float)); + this->_m_value = std::move(value); break; } - case datatype::integer: { - std::unique_ptr value {new std::int32_t[sym._m_count]}; - in.get((std::uint8_t*) value.get(), sym._m_count * sizeof(std::uint32_t)); - sym._m_value = std::move(value); + case DaedalusDataType::INT: { + std::unique_ptr value {new std::int32_t[this->_m_count]}; + r->read((std::uint8_t*) value.get(), this->_m_count * sizeof(std::uint32_t)); + this->_m_value = std::move(value); break; } - case datatype::string: { - std::unique_ptr value {new std::string[sym._m_count]}; - for (std::uint32_t i = 0; i < sym._m_count; ++i) { - value[i] = in.get_line_escaped(false); + case DaedalusDataType::STRING: { + std::unique_ptr value {new std::string[this->_m_count]}; + for (std::uint32_t i = 0; i < this->_m_count; ++i) { + value[i] = r->read_line(false); + zk_internal_escape(value[i]); } - sym._m_value = std::move(value); + this->_m_value = std::move(value); break; } - case datatype::class_: - sym._m_class_offset = in.get_int(); + case DaedalusDataType::CLASS: + this->_m_class_offset = r->read_int(); break; - case datatype::instance: - sym._m_value = std::shared_ptr {nullptr}; - sym._m_address = in.get_int(); + case DaedalusDataType::INSTANCE: + this->_m_value = std::shared_ptr {nullptr}; + this->_m_address = r->read_int(); break; - case datatype::function: - if (!sym.is_const()) { - sym._m_value = std::unique_ptr(new int32_t[1]); + case DaedalusDataType::FUNCTION: + if (!this->is_const()) { + this->_m_value = std::unique_ptr(new int32_t[1]); } - sym._m_address = in.get_int(); + this->_m_address = r->read_int(); break; - case datatype::prototype: - sym._m_address = in.get_int(); + case DaedalusDataType::PROTOTYPE: + this->_m_address = r->read_int(); break; default: break; } } - sym._m_parent = in.get_int(); - if (sym.type() == datatype::string && !sym.is_member() && sym.is_const() && - std::isspace(sym._m_parent & 0xFF)) { + this->_m_parent = r->read_int(); + if (this->type() == DaedalusDataType::STRING && !this->is_member() && this->is_const() && + std::isspace(this->_m_parent & 0xFF)) { // Possible string newline issues here. - auto savepoint = in.position(); + auto savepoint = r->tell(); // Only lookahead four bytes max. unsigned byte = 4; for (; byte > 0; --byte) { - in.position(in.position() - 3); - auto parent_index = in.get_int(); + r->seek(-3, Whence::CUR); + auto parent_index = r->read_int(); if (parent_index == -1) { // This parent index is valid. - sym._m_parent = parent_index; + this->_m_parent = parent_index; break; } } if (byte == 0) { // We didn't find any match. - in.position(savepoint); - sym._m_parent = in.get_uint(); - PX_LOGW("symbol::parse: heuristic: No valid endpoint found. Aborting search. Issues might arise."); + r->seek(savepoint, Whence::BEG); + this->_m_parent = r->read_uint(); + ZKLOGW("DaedalusSymbol", "Heuristic: No valid endpoint found. Aborting search. Issues might arise."); } - - return sym; } - - return sym; } - const std::string& symbol::get_string(std::size_t index, const std::shared_ptr& context) const { - if (type() != datatype::string) { - throw illegal_type_access(this, datatype::string); + const std::string& DaedalusSymbol::get_string(std::size_t index, + const std::shared_ptr& context) const { + if (type() != DaedalusDataType::STRING) { + throw DaedalusIllegalTypeAccess(this, DaedalusDataType::STRING); } if (count() <= index) { - throw illegal_index_access(this, index); + throw DaedalusIllegalIndexAccess(this, index); } if (is_member()) { if (context == nullptr) { - throw no_context(this); + throw DaedalusNoContextError(this); } - if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) { - return reinterpret_cast(*context).get_string(*this, index); + if (context->symbol_index() == static_cast(-1) && + context->_m_type == &typeid(DaedalusTransientInstance)) { + return reinterpret_cast(*context).get_string(*this, index); } return *get_member_ptr(index, context); @@ -408,21 +460,22 @@ namespace phoenix { } } - float symbol::get_float(std::size_t index, const std::shared_ptr& context) const { - if (type() != datatype::float_) { - throw illegal_type_access(this, datatype::float_); + float DaedalusSymbol::get_float(std::size_t index, const std::shared_ptr& context) const { + if (type() != DaedalusDataType::FLOAT) { + throw DaedalusIllegalTypeAccess(this, DaedalusDataType::FLOAT); } if (count() <= index) { - throw illegal_index_access(this, index); + throw DaedalusIllegalIndexAccess(this, index); } if (is_member()) { if (context == nullptr) { - throw no_context(this); + throw DaedalusNoContextError(this); } - if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) { - return reinterpret_cast(*context).get_float(*this, index); + if (context->symbol_index() == static_cast(-1) && + context->_m_type == &typeid(DaedalusTransientInstance)) { + return reinterpret_cast(*context).get_float(*this, index); } return *get_member_ptr(index, context); @@ -431,21 +484,22 @@ namespace phoenix { } } - std::int32_t symbol::get_int(std::size_t index, const std::shared_ptr& context) const { - if (type() != datatype::integer && type() != datatype::function) { - throw illegal_type_access(this, datatype::integer); + std::int32_t DaedalusSymbol::get_int(std::size_t index, const std::shared_ptr& context) const { + if (type() != DaedalusDataType::INT && type() != DaedalusDataType::FUNCTION) { + throw DaedalusIllegalTypeAccess(this, DaedalusDataType::INT); } if (count() <= index) { - throw illegal_index_access(this, index); + throw DaedalusIllegalIndexAccess(this, index); } if (is_member()) { if (context == nullptr) { - throw no_context(this); + throw DaedalusNoContextError(this); } - if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) { - return reinterpret_cast(*context).get_int(*this, index); + if (context->symbol_index() == static_cast(-1) && + context->_m_type == &typeid(DaedalusTransientInstance)) { + return reinterpret_cast(*context).get_int(*this, index); } return *get_member_ptr(index, context); @@ -454,21 +508,24 @@ namespace phoenix { } } - void symbol::set_string(std::string_view value, std::size_t index, const std::shared_ptr& context) { - if (type() != datatype::string) { - throw illegal_type_access(this, datatype::string); + void DaedalusSymbol::set_string(std::string_view value, + std::size_t index, + const std::shared_ptr& context) { + if (type() != DaedalusDataType::STRING) { + throw DaedalusIllegalTypeAccess(this, DaedalusDataType::STRING); } if (count() <= index) { - throw illegal_index_access(this, index); + throw DaedalusIllegalIndexAccess(this, index); } if (is_member()) { if (context == nullptr) { - throw no_context(this); + throw DaedalusNoContextError(this); } - if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) { - reinterpret_cast(*context).set_string(*this, index, value); + if (context->symbol_index() == static_cast(-1) && + context->_m_type == &typeid(DaedalusTransientInstance)) { + reinterpret_cast(*context).set_string(*this, index, value); return; } @@ -478,21 +535,22 @@ namespace phoenix { } } - void symbol::set_float(float value, std::size_t index, const std::shared_ptr& context) { - if (type() != datatype::float_) { - throw illegal_type_access(this, datatype::float_); + void DaedalusSymbol::set_float(float value, std::size_t index, const std::shared_ptr& context) { + if (type() != DaedalusDataType::FLOAT) { + throw DaedalusIllegalTypeAccess(this, DaedalusDataType::FLOAT); } if (count() <= index) { - throw illegal_index_access(this, index); + throw DaedalusIllegalIndexAccess(this, index); } if (is_member()) { if (context == nullptr) { - throw no_context(this); + throw DaedalusNoContextError(this); } - if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) { - reinterpret_cast(*context).set_float(*this, index, value); + if (context->symbol_index() == static_cast(-1) && + context->_m_type == &typeid(DaedalusTransientInstance)) { + reinterpret_cast(*context).set_float(*this, index, value); return; } @@ -502,21 +560,23 @@ namespace phoenix { } } - void symbol::set_int(std::int32_t value, std::size_t index, const std::shared_ptr& context) { - if (type() != datatype::integer && type() != datatype::function) { - throw illegal_type_access(this, datatype::integer); + void + DaedalusSymbol::set_int(std::int32_t value, std::size_t index, const std::shared_ptr& context) { + if (type() != DaedalusDataType::INT && type() != DaedalusDataType::FUNCTION) { + throw DaedalusIllegalTypeAccess(this, DaedalusDataType::INT); } if (count() <= index) { - throw illegal_index_access(this, index); + throw DaedalusIllegalIndexAccess(this, index); } if (is_member()) { if (context == nullptr) { - throw no_context(this); + throw DaedalusNoContextError(this); } - if (context->symbol_index() == unset && context->_m_type == &typeid(transient_instance)) { - reinterpret_cast(*context).set_int(*this, index, value); + if (context->symbol_index() == static_cast(-1) && + context->_m_type == &typeid(DaedalusTransientInstance)) { + reinterpret_cast(*context).set_int(*this, index, value); return; } @@ -526,33 +586,34 @@ namespace phoenix { } } - const std::shared_ptr& symbol::get_instance() { - if (type() != datatype::instance) { - throw illegal_type_access(this, datatype::instance); + const std::shared_ptr& DaedalusSymbol::get_instance() { + if (type() != DaedalusDataType::INSTANCE) { + throw DaedalusIllegalTypeAccess(this, DaedalusDataType::INSTANCE); } - return std::get>(_m_value); + return std::get>(_m_value); } - void symbol::set_instance(const std::shared_ptr& inst) { - if (type() != datatype::instance) { - throw illegal_type_access(this, datatype::instance); + void DaedalusSymbol::set_instance(const std::shared_ptr& inst) { + if (type() != DaedalusDataType::INSTANCE) { + throw DaedalusIllegalTypeAccess(this, DaedalusDataType::INSTANCE); } - std::get>(_m_value) = inst; + std::get>(_m_value) = inst; } - void symbol::set_access_trap_enable(bool enable) noexcept { + void DaedalusSymbol::set_access_trap_enable(bool enable) noexcept { if (enable) - _m_flags |= symbol_flag::access_trap; + _m_flags |= DaedalusSymbolFlag::TRAP_ACCESS; else - _m_flags &= ~symbol_flag::access_trap; + _m_flags &= ~DaedalusSymbolFlag::TRAP_ACCESS; } - opaque_instance::opaque_instance(const symbol& sym, const std::vector& members) { + DaedalusOpaqueInstance::DaedalusOpaqueInstance(const DaedalusSymbol& sym, + const std::vector& members) { size_t str_count = 0; for (auto* member : members) { - if (member->type() != datatype::string) + if (member->type() != DaedalusDataType::STRING) continue; str_count += member->count(); } @@ -566,27 +627,27 @@ namespace phoenix { for (auto i = 0U; i < member->count(); ++i) { switch (member->type()) { - case datatype::float_: + case DaedalusDataType::FLOAT: this->construct_at(offset, 0); offset += 4; break; - case datatype::integer: + case DaedalusDataType::INT: this->construct_at(offset, 0); offset += 4; break; - case datatype::string: + case DaedalusDataType::STRING: _m_strings[str_count] = this->construct_at(offset, ""); str_count++; offset += sizeof(std::string); break; - case datatype::function: + case DaedalusDataType::FUNCTION: this->construct_at(offset, 0); offset += 4; break; - case datatype::class_: - case datatype::prototype: - case datatype::instance: - case datatype::void_: + case DaedalusDataType::CLASS: + case DaedalusDataType::PROTOTYPE: + case DaedalusDataType::INSTANCE: + case DaedalusDataType::VOID: this->construct_at(offset); offset += 4; break; @@ -595,23 +656,22 @@ namespace phoenix { } } - opaque_instance::~opaque_instance() { + DaedalusOpaqueInstance::~DaedalusOpaqueInstance() { for (auto& i : _m_strings) i->std::string::~string(); } template - T* opaque_instance::construct_at(size_t offset, Args&&... args) { + T* DaedalusOpaqueInstance::construct_at(size_t offset, Args&&... args) { auto align = alignof(T); auto remain = offset % align; auto real_offset = remain == 0 ? offset : offset + (align - remain); return new (static_cast(&_m_storage[real_offset])) T(std::forward(args)...); } - transient_instance::transient_instance() { - _m_type = &typeid(transient_instance); + DaedalusTransientInstance::DaedalusTransientInstance() { + _m_type = &typeid(DaedalusTransientInstance); } - transient_instance::~transient_instance() {} - -} // namespace phoenix + DaedalusTransientInstance::~DaedalusTransientInstance() {} +} // namespace zenkit diff --git a/src/DaedalusVm.cc b/src/DaedalusVm.cc index b31d7e5a..2c4b5349 100644 --- a/src/DaedalusVm.cc +++ b/src/DaedalusVm.cc @@ -1,10 +1,12 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/DaedalusVm.hh" + +#include "Internal.hh" #include -namespace phoenix { +namespace zenkit { /// \brief A helper class for preventing stack corruption. /// /// This class can be used to guard against stack corruption when a value is expected to be @@ -14,47 +16,47 @@ namespace phoenix { /// /// \code /// // construct the stack guard - /// stack_guard guard {this, datatype::int_}; + /// stack_guard guard {this, DaedalusDataType::int_}; /// /// // ... do something that might fail to push an int to the stack ... /// /// // make sure to inhibit the guard if pushing the value succeeded /// guard.inhibit(); /// \endcode - struct stack_guard { + struct StackGuard { public: /// \brief Creates a new stack guard. /// \param machine The VM this instance is guarding. /// \param type The type of value to push if the guard is triggered. - stack_guard(vm* machine, datatype type) : _m_type(type), _m_machine(machine) {} + StackGuard(DaedalusVm* machine, DaedalusDataType type) : _m_type(type), _m_machine(machine) {} /// \brief Triggers this guard. /// /// Unless #inhibit was called, this destructor will push a value of the datatype passed in the /// constructor onto the stack of the VM passed in the constructor. - ~stack_guard() { + StackGuard() { if (_m_inhibited) return; switch (_m_type) { - case datatype::void_: + case DaedalusDataType::VOID: break; - case datatype::float_: + case DaedalusDataType::FLOAT: _m_machine->push_float(0); break; - case datatype::integer: - case datatype::function: + case DaedalusDataType::INT: + case DaedalusDataType::FUNCTION: _m_machine->push_int(0); break; - case datatype::string: + case DaedalusDataType::STRING: _m_machine->push_string(""); break; - case datatype::instance: + case DaedalusDataType::INSTANCE: _m_machine->push_instance(nullptr); break; - case datatype::class_: + case DaedalusDataType::CLASS: break; - case datatype::prototype: + case DaedalusDataType::PROTOTYPE: break; } } @@ -68,37 +70,38 @@ namespace phoenix { } private: - datatype _m_type; - vm* _m_machine; + DaedalusDataType _m_type; + DaedalusVm* _m_machine; bool _m_inhibited {false}; }; - vm::vm(script&& scr, std::uint8_t flags) : script(std::move(scr)), _m_flags(flags) { + DaedalusVm::DaedalusVm(DaedalusScript&& scr, std::uint8_t flags) : DaedalusScript(std::move(scr)), _m_flags(flags) { + _m_temporary_strings = add_temporary_strings_symbol(); _m_self_sym = find_symbol_by_name("SELF"); _m_other_sym = find_symbol_by_name("OTHER"); _m_victim_sym = find_symbol_by_name("VICTIM"); _m_hero_sym = find_symbol_by_name("HERO"); _m_item_sym = find_symbol_by_name("ITEM"); - _m_temporary_strings = add_temporary_strings_symbol(); } - std::shared_ptr vm::init_opaque_instance(symbol* sym) { + std::shared_ptr DaedalusVm::init_opaque_instance(DaedalusSymbol* sym) { auto cls = sym; - while (cls != nullptr && cls->type() != datatype::class_) { + while (cls != nullptr && cls->type() != DaedalusDataType::CLASS) { cls = find_symbol_by_index(cls->parent()); } if (cls == nullptr) { // We're probably trying to initialize $INSTANCE_HELP which is not permitted - throw vm_exception {"Cannot init " + sym->name() + - ": parent class not found (did you try to initialize $INSTANCE_HELP?)"}; + throw DaedalusVmException {"Cannot init " + sym->name() + + ": parent class not found (did you try to initialize $INSTANCE_HELP?)"}; } + // create the instance - auto inst = std::make_shared(*cls, find_class_members(*cls)); + auto inst = std::make_shared(*cls, find_class_members(*cls)); init_instance(inst, sym); return inst; } - void vm::unsafe_call(const symbol* sym) { + void DaedalusVm::unsafe_call(const DaedalusSymbol* sym) { push_call(sym); jump(sym->address()); @@ -109,144 +112,145 @@ namespace phoenix { pop_call(); } - void vm::unsafe_jump(uint32_t address) { + void DaedalusVm::unsafe_jump(uint32_t address) { this->jump(address); } - bool vm::exec() { + bool DaedalusVm::exec() { auto instr = instruction_at(_m_pc); std::int32_t a {}, b {}; - symbol* sym {}; + DaedalusSymbol* sym {}; try { switch (instr.op) { - case opcode::add: + case DaedalusOpcode::ADD: push_int(pop_int() + pop_int()); break; - case opcode::sub: + case DaedalusOpcode::SUB: a = pop_int(); b = pop_int(); push_int(a - b); break; - case opcode::mul: + case DaedalusOpcode::MUL: push_int(pop_int() * pop_int()); break; - case opcode::div: + case DaedalusOpcode::DIV: a = pop_int(); b = pop_int(); if (b == 0) - throw vm_exception {"vm: division by zero"}; + throw DaedalusVmException {"vm: division by zero"}; push_int(a / b); break; - case opcode::mod: + case DaedalusOpcode::MOD: a = pop_int(); b = pop_int(); if (b == 0) - throw vm_exception {"vm: division by zero"}; + throw DaedalusVmException {"vm: division by zero"}; push_int(a % b); break; - case opcode::or_: + case DaedalusOpcode::OR: push_int(pop_int() | pop_int()); break; - case opcode::andb: + case DaedalusOpcode::ANDB: push_int(pop_int() & pop_int()); break; - case opcode::lt: + case DaedalusOpcode::LT: a = pop_int(); b = pop_int(); push_int(a < b); break; - case opcode::gt: + case DaedalusOpcode::GT: a = pop_int(); b = pop_int(); push_int(a > b); break; - case opcode::lsl: + case DaedalusOpcode::LSL: a = pop_int(); b = pop_int(); push_int(a << b); break; - case opcode::lsr: + case DaedalusOpcode::LSR: a = pop_int(); b = pop_int(); push_int(a >> b); break; - case opcode::lte: + case DaedalusOpcode::LTE: a = pop_int(); b = pop_int(); push_int(a <= b); break; - case opcode::eq: + case DaedalusOpcode::EQ: push_int(pop_int() == pop_int()); break; - case opcode::neq: + case DaedalusOpcode::NEQ: push_int(pop_int() != pop_int()); break; - case opcode::gte: + case DaedalusOpcode::GTE: a = pop_int(); b = pop_int(); push_int(a >= b); break; - case opcode::plus: + case DaedalusOpcode::PLUS: push_int(+pop_int()); break; - case opcode::negate: + case DaedalusOpcode::NEGATE: push_int(-pop_int()); break; - case opcode::not_: + case DaedalusOpcode::NOT: push_int(!pop_int()); break; - case opcode::cmpl: + case DaedalusOpcode::CMPL: push_int(~pop_int()); break; - case opcode::orr: + case DaedalusOpcode::ORR: a = pop_int(); b = pop_int(); push_int(a || b); break; - case opcode::and_: + case DaedalusOpcode::AND: a = pop_int(); b = pop_int(); push_int(a && b); break; - case opcode::nop: + case DaedalusOpcode::NOP: // Do nothing break; - case opcode::rsr: + case DaedalusOpcode::RSR: return false; - case opcode::bl: { + case DaedalusOpcode::BL: { // Check if the function is overridden and if it is, call the resulting external. sym = find_symbol_by_address(instr.address); auto cb = _m_function_overrides.find(instr.address); if (cb != _m_function_overrides.end()) { // Guard against exceptions during external invocation. - stack_guard guard {this, sym->rtype()}; + StackGuard guard {this, sym->rtype()}; // Call maybe naked. cb->second(*this); // The stack is left intact. guard.inhibit(); } else { if (sym == nullptr) { - throw vm_exception {"bl: no symbol found for address " + std::to_string(instr.address)}; + throw DaedalusVmException {"bl: no symbol found for address " + std::to_string(instr.address)}; } + unsafe_call(sym); } break; } - case opcode::be: { + case DaedalusOpcode::BE: { sym = find_symbol_by_index(instr.symbol); if (sym == nullptr) { - throw vm_exception {"be: no external found for index"}; + throw DaedalusVmException {"be: no external found for index"}; } // Guard against exceptions during external invocation. - stack_guard guard {this, sym->rtype()}; + StackGuard guard {this, sym->rtype()}; auto cb = _m_externals.find(sym); if (cb == _m_externals.end()) { @@ -255,7 +259,7 @@ namespace phoenix { guard.inhibit(); break; } else { - throw vm_exception {"be: no external registered for " + sym->name()}; + throw DaedalusVmException {"be: no external registered for " + sym->name()}; } } @@ -267,14 +271,14 @@ namespace phoenix { guard.inhibit(); break; } - case opcode::pushi: + case DaedalusOpcode::PUSHI: push_int(instr.immediate); break; - case opcode::pushvi: - case opcode::pushv: + case DaedalusOpcode::PUSHVI: + case DaedalusOpcode::PUSHV: sym = find_symbol_by_index(instr.symbol); if (sym == nullptr) { - throw vm_exception {"pushv: no symbol found for index"}; + throw DaedalusVmException {"pushv: no symbol found for index"}; } if (sym->has_access_trap() && _m_access_trap) { _m_access_trap(*sym); @@ -282,128 +286,131 @@ namespace phoenix { push_reference(sym, 0); } break; - case opcode::movi: - case opcode::movvf: { + case DaedalusOpcode::MOVI: + case DaedalusOpcode::MOVVF: { auto [ref, idx, context] = pop_reference(); auto value = pop_int(); - set_int(context, ref, idx, value); + + this->set_int(context, ref, idx, value); break; } - case opcode::movf: { + case DaedalusOpcode::MOVF: { auto [ref, idx, context] = pop_reference(); auto value = pop_float(); - set_float(context, ref, idx, value); + + this->set_float(context, ref, idx, value); break; } - case opcode::movs: { + case DaedalusOpcode::MOVS: { auto [target, target_idx, context] = pop_reference(); auto source = pop_string(); - set_string(context, target, target_idx, source); + + this->set_string(context, target, target_idx, source); break; } - case opcode::movss: - throw vm_exception {"not implemented: movss"}; - case opcode::addmovi: { + case DaedalusOpcode::MOVSS: + throw DaedalusVmException {"not implemented: movss"}; + case DaedalusOpcode::ADDMOVI: { auto [ref, idx, context] = pop_reference(); auto value = pop_int(); - if (ref->is_const() && !(_m_flags & execution_flag::vm_ignore_const_specifier)) { - throw illegal_const_access(ref); + if (ref->is_const() && !(_m_flags & DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER)) { + throw DaedalusIllegalConstAccess(ref); } if (!ref->is_member() || context != nullptr || - !(_m_flags & execution_flag::vm_allow_null_instance_access)) { + !(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { auto result = ref->get_int(idx, context) + value; ref->set_int(result, idx, context); } else if (ref->is_member()) { - PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", ref->name().c_str()); } break; } - case opcode::submovi: { + case DaedalusOpcode::SUBMOVI: { auto [ref, idx, context] = pop_reference(); auto value = pop_int(); - if (ref->is_const() && !(_m_flags & execution_flag::vm_ignore_const_specifier)) { - throw illegal_const_access(ref); + if (ref->is_const() && !(_m_flags & DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER)) { + throw DaedalusIllegalConstAccess(ref); } if (!ref->is_member() || context != nullptr || - !(_m_flags & execution_flag::vm_allow_null_instance_access)) { + !(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { auto result = ref->get_int(idx, context) - value; ref->set_int(result, idx, context); } else if (ref->is_member()) { - PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", ref->name().c_str()); } break; } - case opcode::mulmovi: { + case DaedalusOpcode::MULMOVI: { auto [ref, idx, context] = pop_reference(); auto value = pop_int(); - if (ref->is_const() && !(_m_flags & execution_flag::vm_ignore_const_specifier)) { - throw illegal_const_access(ref); + if (ref->is_const() && !(_m_flags & DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER)) { + throw DaedalusIllegalConstAccess(ref); } if (!ref->is_member() || context != nullptr || - !(_m_flags & execution_flag::vm_allow_null_instance_access)) { + !(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { auto result = ref->get_int(idx, context) * value; ref->set_int(result, idx, context); } else if (ref->is_member()) { - PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", ref->name().c_str()); } break; } - case opcode::divmovi: { + case DaedalusOpcode::DIVMOVI: { auto [ref, idx, context] = pop_reference(); auto value = pop_int(); if (value == 0) { - throw vm_exception {"vm: division by zero"}; + throw DaedalusVmException {"vm: division by zero"}; } - if (ref->is_const() && !(_m_flags & execution_flag::vm_ignore_const_specifier)) { - throw illegal_const_access(ref); + if (ref->is_const() && !(_m_flags & DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER)) { + throw DaedalusIllegalConstAccess(ref); } if (!ref->is_member() || context != nullptr || - !(_m_flags & execution_flag::vm_allow_null_instance_access)) { + !(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { auto result = ref->get_int(idx, context) / value; ref->set_int(result, idx, context); } else if (ref->is_member()) { - PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", ref->name().c_str()); } break; } - case opcode::movvi: { + case DaedalusOpcode::MOVVI: { auto [target, target_idx, _] = pop_reference(); target->set_instance(pop_instance()); break; } - case opcode::b: + case DaedalusOpcode::B: jump(instr.address); return true; - case opcode::bz: + case DaedalusOpcode::BZ: if (pop_int() == 0) { jump(instr.address); return true; } break; - case opcode::gmovi: { + case DaedalusOpcode::GMOVI: { sym = find_symbol_by_index(instr.symbol); if (sym == nullptr) { - throw vm_exception {"gmovi: no symbol found for index"}; + throw DaedalusVmException {"gmovi: no symbol found for index"}; } _m_instance = sym->get_instance(); break; } - case opcode::pushvv: + case DaedalusOpcode::PUSHVV: sym = find_symbol_by_index(instr.symbol); if (sym == nullptr) { - throw vm_exception {"pushvv: no symbol found for index"}; + throw DaedalusVmException {"pushvv: no symbol found for index"}; } push_reference(sym, instr.index); @@ -411,27 +418,21 @@ namespace phoenix { } _m_pc += instr.size; - } catch (phoenix::script_error& err) { + } catch (zenkit::DaedalusScriptError& err) { uint32_t prev_pc = _m_pc; if (_m_exception_handler) { auto strategy = (*_m_exception_handler)(*this, err, instr); - if (strategy == vm_exception_strategy::fail_) { - phoenix::logging::log(phoenix::logging::level::error, - "+++ Error while executing script: ", - err.what(), - "+++"); + if (strategy == DaedalusVmExceptionStrategy::FAIL) { + ZKLOGE("DaedalusVm", "+++ Error while executing script: %s +++", err.what()); print_stack_trace(); throw err; - } else if (strategy == vm_exception_strategy::return_) { + } else if (strategy == DaedalusVmExceptionStrategy::RETURN) { return false; } } else { - phoenix::logging::log(phoenix::logging::level::error, - "+++ Error while executing script: ", - err.what(), - "+++"); + ZKLOGE("DaedalusVm", "+++ Error while executing script: %s +++", err.what()); print_stack_trace(); throw err; } @@ -444,79 +445,79 @@ namespace phoenix { return true; } - void vm::push_call(const symbol* sym) { + void DaedalusVm::push_call(const DaedalusSymbol* sym) { _m_call_stack.push({sym, _m_pc, _m_instance}); } - void vm::pop_call() { + void DaedalusVm::pop_call() { const auto& call = _m_call_stack.top(); _m_pc = call.program_counter; _m_instance = call.context; _m_call_stack.pop(); } - void vm::push_int(std::int32_t value) { - if (_m_stack_ptr == vm::stack_size) { - throw vm_exception {"stack overflow"}; + void DaedalusVm::push_int(std::int32_t value) { + if (_m_stack_ptr == DaedalusVm::stack_size) { + throw DaedalusVmException {"stack overflow"}; } _m_stack[_m_stack_ptr++] = {nullptr, false, value}; } - void vm::push_reference(symbol* value, std::uint8_t index) { - if (_m_stack_ptr == vm::stack_size) { - throw vm_exception {"stack overflow"}; + void DaedalusVm::push_reference(DaedalusSymbol* value, std::uint8_t index) { + if (_m_stack_ptr == DaedalusVm::stack_size) { + throw DaedalusVmException {"stack overflow"}; } _m_stack[_m_stack_ptr++] = {_m_instance, true, value, index}; } - void vm::push_string(std::string_view value) { + void DaedalusVm::push_string(std::string_view value) { _m_temporary_strings->set_string(value); push_reference(_m_temporary_strings); } - void vm::push_float(float value) { - if (_m_stack_ptr == vm::stack_size) { - throw vm_exception {"stack overflow"}; + void DaedalusVm::push_float(float value) { + if (_m_stack_ptr == DaedalusVm::stack_size) { + throw DaedalusVmException {"stack overflow"}; } _m_stack[_m_stack_ptr++] = {nullptr, false, value}; } - void vm::push_instance(std::shared_ptr value) { - if (_m_stack_ptr == vm::stack_size) { - throw vm_exception {"stack overflow"}; + void DaedalusVm::push_instance(std::shared_ptr value) { + if (_m_stack_ptr == DaedalusVm::stack_size) { + throw DaedalusVmException {"stack overflow"}; } _m_stack[_m_stack_ptr++] = {nullptr, false, value}; } - std::int32_t vm::pop_int() { + std::int32_t DaedalusVm::pop_int() { if (_m_stack_ptr == 0) { return 0; } - daedalus_stack_frame v = std::move(_m_stack[--_m_stack_ptr]); + DaedalusStackFrame v = std::move(_m_stack[--_m_stack_ptr]); if (v.reference) { - return get_int(v.context, v.value, v.index); + return this->get_int(v.context, v.value, v.index); } else if (std::holds_alternative(v.value)) { return std::get(v.value); } else { - throw vm_exception {"tried to pop_int but frame does not contain a int."}; + throw DaedalusVmException {"tried to pop_int but frame does not contain a int."}; } } - float vm::pop_float() { + float DaedalusVm::pop_float() { if (_m_stack_ptr == 0) { return 0.0f; } - daedalus_stack_frame v = std::move(_m_stack[--_m_stack_ptr]); + DaedalusStackFrame v = std::move(_m_stack[--_m_stack_ptr]); if (v.reference) { - return get_float(v.context, v.value, v.index); + return this->get_float(v.context, v.value, v.index); } else if (std::holds_alternative(v.value)) { return std::get(v.value); } else if (std::holds_alternative(v.value)) { @@ -527,163 +528,89 @@ namespace phoenix { return *(float*) &k; #pragma GCC diagnostic pop } else { - throw vm_exception {"tried to pop_float but frame does not contain a float."}; + throw DaedalusVmException {"tried to pop_float but frame does not contain a float."}; } } - std::tuple> vm::pop_reference() { + std::tuple> DaedalusVm::pop_reference() { if (_m_stack_ptr == 0) { - throw vm_exception {"popping reference from empty stack"}; + throw DaedalusVmException {"popping reference from empty stack"}; } - daedalus_stack_frame v = std::move(_m_stack[--_m_stack_ptr]); + DaedalusStackFrame v = std::move(_m_stack[--_m_stack_ptr]); if (!v.reference) { - throw vm_exception {"tried to pop_reference but frame does not contain a reference."}; + throw DaedalusVmException {"tried to pop_reference but frame does not contain a reference."}; } - return {std::get(v.value), v.index, v.context}; + return {std::get(v.value), v.index, v.context}; } - std::shared_ptr vm::pop_instance() { + std::shared_ptr DaedalusVm::pop_instance() { if (_m_stack_ptr == 0) { - throw vm_exception {"popping instance from empty stack"}; + throw DaedalusVmException {"popping instance from empty stack"}; } - daedalus_stack_frame v = std::move(_m_stack[--_m_stack_ptr]); + DaedalusStackFrame v = std::move(_m_stack[--_m_stack_ptr]); if (v.reference) { - return std::get(v.value)->get_instance(); - } else if (std::holds_alternative>(v.value)) { - return std::get>(v.value); + return std::get(v.value)->get_instance(); + } else if (std::holds_alternative>(v.value)) { + return std::get>(v.value); } else { - throw vm_exception {"tried to pop_instance but frame does not contain am instance."}; + throw DaedalusVmException {"tried to pop_instance but frame does not contain am instance."}; } } - const std::string& vm::pop_string() { + const std::string& DaedalusVm::pop_string() { static std::string empty {}; auto [s, i, context] = pop_reference(); // compatibility: sometimes the context might be zero, but we can't fail so when // the compatibility flag is set, we just return 0 if (s->is_member() && context == nullptr) { - if (!(_m_flags & execution_flag::vm_allow_null_instance_access)) { - throw no_context {s}; + if (!(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { + throw DaedalusNoContextError {s}; } - PX_LOGE("vm: accessing member \"", s->name(), "\" without an instance set"); + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", s->name().c_str()); return empty; } return s->get_string(i, context); } - std::int32_t vm::get_int(std::shared_ptr& context, - std::variant>& value, - uint16_t index) { - auto* sym = std::get(value); - - // compatibility: sometimes the context might be zero, but we can't fail so when - // the compatibility flag is set, we just return 0 - if (sym->is_member() && context == nullptr) { - if (!(_m_flags & execution_flag::vm_allow_null_instance_access)) { - throw no_context {sym}; - } - - PX_LOGE("vm: accessing member \"", sym->name(), "\" without an instance set"); - return 0; - } - - return sym->get_int(index, context); - } - - float vm::get_float(std::shared_ptr& context, - std::variant>& value, - uint16_t index) { - auto* sym = std::get(value); - - // compatibility: sometimes the context might be zero, but we can't fail so when - // the compatibility flag is set, we just return 0 - if (sym->is_member() && context == nullptr) { - if (!(_m_flags & execution_flag::vm_allow_null_instance_access)) { - throw no_context {sym}; - } - - PX_LOGE("vm: accessing member \"", sym->name(), "\" without an instance set"); - return 0; - } - - return sym->get_float(index, context); - } - - void vm::set_int(std::shared_ptr& context, symbol* ref, uint16_t index, std::int32_t value) { - if (ref->is_const() && !(_m_flags & execution_flag::vm_ignore_const_specifier)) { - throw illegal_const_access(ref); - } - - if (!ref->is_member() || context != nullptr || !(_m_flags & execution_flag::vm_allow_null_instance_access)) { - ref->set_int(value, index, context); - } else if (ref->is_member()) { - PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); - } - } - - void vm::set_float(std::shared_ptr& context, symbol* ref, uint16_t index, float value) { - if (ref->is_const() && !(_m_flags & execution_flag::vm_ignore_const_specifier)) { - throw illegal_const_access(ref); - } - - if (!ref->is_member() || context != nullptr || !(_m_flags & execution_flag::vm_allow_null_instance_access)) { - ref->set_float(value, index, context); - } else if (ref->is_member()) { - PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); - } - } - - void vm::set_string(std::shared_ptr& context, symbol* ref, uint16_t index, std::string_view value) { - if (ref->is_const() && !(_m_flags & execution_flag::vm_ignore_const_specifier)) { - throw illegal_const_access(ref); - } - - if (!ref->is_member() || context != nullptr || !(_m_flags & execution_flag::vm_allow_null_instance_access)) { - ref->set_string(value, index, context); - } else if (ref->is_member()) { - PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); - } - } - - void vm::jump(std::uint32_t address) { + void DaedalusVm::jump(std::uint32_t address) { if (address > size()) { - throw vm_exception {"Cannot jump to " + std::to_string(address) + ": illegal address"}; + throw DaedalusVmException {"Cannot jump to " + std::to_string(address) + ": illegal address"}; } _m_pc = address; } - void vm::register_default_external(const std::function& callback) { - _m_default_external = [this, callback](vm& v, symbol& sym) { + void DaedalusVm::register_default_external(const std::function& callback) { + _m_default_external = [this, callback](DaedalusVm& v, DaedalusSymbol& sym) { // pop all parameters from the stack auto params = find_parameters_for_function(&sym); for (int32_t i = params.size() - 1; i >= 0; --i) { auto par = params[i]; - if (par->type() == datatype::integer) + if (par->type() == DaedalusDataType::INT) (void) v.pop_int(); - else if (par->type() == datatype::float_) + else if (par->type() == DaedalusDataType::FLOAT) (void) v.pop_float(); - else if (par->type() == datatype::instance || par->type() == datatype::string) + else if (par->type() == DaedalusDataType::INSTANCE || par->type() == DaedalusDataType::STRING) (void) v.pop_reference(); } if (sym.has_return()) { - if (sym.rtype() == datatype::float_) + if (sym.rtype() == DaedalusDataType::FLOAT) v.push_float(0.0f); - else if (sym.rtype() == datatype::integer) + else if (sym.rtype() == DaedalusDataType::INT) v.push_int(0); - else if (sym.rtype() == datatype::string) + else if (sym.rtype() == DaedalusDataType::STRING) v.push_string(""); - else if (sym.rtype() == datatype::instance) + else if (sym.rtype() == DaedalusDataType::INSTANCE) v.push_instance(nullptr); } @@ -691,62 +618,64 @@ namespace phoenix { }; } - void vm::register_default_external_custom(const std::function& callback) { + void + DaedalusVm::register_default_external_custom(const std::function& callback) { _m_default_external = callback; } - void vm::register_access_trap(const std::function& callback) { + void DaedalusVm::register_access_trap(const std::function& callback) { _m_access_trap = callback; } - void vm::register_exception_handler( - const std::function& callback) { + void DaedalusVm::register_exception_handler( + const std::function& callback) { _m_exception_handler = callback; } - void vm::print_stack_trace() const { + void DaedalusVm::print_stack_trace() const { auto last_pc = _m_pc; auto tmp_stack_ptr = _m_stack_ptr; - std::stack callstack {_m_call_stack}; + std::stack callstack {_m_call_stack}; - using log = phoenix::logging; - log::log(log::level::error, "------- CALL STACK (MOST RECENT CALL FIRST) -------"); + ZKLOGE("DaedalusVm", "------- CALL STACK (MOST RECENT CALL FIRST) -------"); while (!callstack.empty()) { auto v = callstack.top(); - log::log(log::level::error, "in ", v.function->name(), " at 0x", std::hex, last_pc, std::dec); + ZKLOGE("DaedalusVm", "in %s at %x", v.function->name().c_str(), last_pc); last_pc = v.program_counter; callstack.pop(); } - log::log(log::level::error, "------- STACK (MOST RECENT PUSH FIRST) -------"); + ZKLOGE("DaedalusVm", "------- STACK (MOST RECENT PUSH FIRST) -------"); while (tmp_stack_ptr > 0) { auto& v = _m_stack[--tmp_stack_ptr]; if (v.reference) { - auto ref = std::get(v.value); + auto ref = std::get(v.value); std::string value; switch (ref->type()) { - case datatype::float_: + case DaedalusDataType::FLOAT: value = std::to_string(ref->get_float(v.index, _m_instance)); break; - case datatype::integer: + case DaedalusDataType::INT: value = std::to_string(ref->get_int(v.index, _m_instance)); break; - case datatype::string: + case DaedalusDataType::STRING: value = "'" + ref->get_string(v.index, _m_instance) + "'"; break; - case datatype::function: { + case DaedalusDataType::FUNCTION: { auto index = ref->get_int(v.index, _m_instance); auto sym = find_symbol_by_index(index); value = "&" + sym->name(); break; } - case datatype::instance: { + case DaedalusDataType::INSTANCE: { auto& inst = ref->get_instance(); if (inst != nullptr) { value = "_m_type->name()) + "'>"; @@ -759,118 +688,206 @@ namespace phoenix { value = ""; } - log::log(log::level::error, - tmp_stack_ptr, - ": [REFERENCE] ", - ref->name(), - "[", - (int32_t) v.index, - "] = ", - value); + ZKLOGE("DaedalusVm", + "%u: [REFERENCE] %s[%hu] = %s", + tmp_stack_ptr, + ref->name().c_str(), + v.index, + value.c_str()); } else { if (std::holds_alternative(v.value)) { - log::log(log::level::error, tmp_stack_ptr, ": [IMMEDIATE FLOAT] = ", std::get(v.value)); + ZKLOGE("DaedalusVm", "%d: [IMMEDIATE FLOAT] = %f", tmp_stack_ptr, std::get(v.value)); } else if (std::holds_alternative(v.value)) { - log::log(log::level::error, tmp_stack_ptr, ": [IMMEDIATE INT] = ", std::get(v.value)); - } else if (std::holds_alternative>(v.value)) { - auto& inst = std::get>(v.value); + ZKLOGE("DaedalusVm", "%d: [IMMEDIATE INT] = %d", tmp_stack_ptr, std::get(v.value)); + } else if (std::holds_alternative>(v.value)) { + auto& inst = std::get>(v.value); if (inst == nullptr) { - log::log(log::level::error, tmp_stack_ptr, ": [IMMEDIATE INSTANCE] = NULL "); + ZKLOGE("DaedalusVm", "%d: [IMMEDIATE INSTANCE] = NULL", tmp_stack_ptr); } else { - log::log(log::level::error, - tmp_stack_ptr, - ": [IMMEDIATE INSTANCE] = _m_type->name()), - "'>"); + ZKLOGE("DaedalusVm", + "%d: [IMMEDIATE INSTANCE] = ", + tmp_stack_ptr, + inst->_m_type->name()); } } } } } - illegal_external_definition::illegal_external_definition(const symbol* s, std::string&& msg) - : script_error(std::move(msg)), sym(s) {} + DaedalusIllegalExternalDefinition::DaedalusIllegalExternalDefinition(const DaedalusSymbol* s, std::string&& msg) + : DaedalusScriptError(std::move(msg)), sym(s) {} - illegal_external_rtype::illegal_external_rtype(const symbol* s, std::string&& provided) - : illegal_external_definition(s, - "external " + s->name() + " has illegal return type '" + provided + - "', expected '" + DAEDALUS_DATA_TYPE_NAMES[(std::uint32_t) s->rtype()] + - "'") {} + DaedalusIllegalExternalReturnType::DaedalusIllegalExternalReturnType(const DaedalusSymbol* s, + std::string&& provided) + : DaedalusIllegalExternalDefinition(s, + "external " + s->name() + " has illegal return type '" + provided + + "', expected '" + DAEDALUS_DATA_TYPE_NAMES[(std::uint32_t) s->rtype()] + + "'") {} - illegal_external_param::illegal_external_param(const symbol* s, std::string&& provided, std::uint8_t i) - : illegal_external_definition(s, - "external " + s->name() + " has illegal parameter type '" + provided + "' (no. " + - std::to_string(i) + "), expected '" + - DAEDALUS_DATA_TYPE_NAMES[(std::uint32_t) s->type()] + "'") {} + DaedalusIllegalExternalParameter::DaedalusIllegalExternalParameter(const DaedalusSymbol* s, + std::string&& provided, + std::uint8_t i) + : DaedalusIllegalExternalDefinition(s, + "external " + s->name() + " has illegal parameter type '" + provided + + "' (no. " + std::to_string(i) + "), expected '" + + DAEDALUS_DATA_TYPE_NAMES[(std::uint32_t) s->type()] + "'") {} - vm_exception_strategy lenient_vm_exception_handler(vm& v, const script_error& exc, const instruction& instr) { - PX_LOGE("vm: internal exception: ", exc.what()); + DaedalusVmExceptionStrategy + lenient_vm_exception_handler(DaedalusVm& v, const DaedalusScriptError& exc, const DaedalusInstruction& instr) { + ZKLOGE("DaedalusVm", "Internal Exception: %s", exc.what()); switch (instr.op) { - case opcode::add: - case opcode::sub: - case opcode::mul: - case opcode::div: - case opcode::mod: - case opcode::or_: - case opcode::andb: - case opcode::lt: - case opcode::gt: - case opcode::orr: - case opcode::and_: - case opcode::lsl: - case opcode::lsr: - case opcode::lte: - case opcode::eq: - case opcode::neq: - case opcode::gte: - case opcode::plus: - case opcode::negate: - case opcode::not_: - case opcode::cmpl: + case DaedalusOpcode::ADD: + case DaedalusOpcode::SUB: + case DaedalusOpcode::MUL: + case DaedalusOpcode::DIV: + case DaedalusOpcode::MOD: + case DaedalusOpcode::OR: + case DaedalusOpcode::ANDB: + case DaedalusOpcode::LT: + case DaedalusOpcode::GT: + case DaedalusOpcode::ORR: + case DaedalusOpcode::AND: + case DaedalusOpcode::LSL: + case DaedalusOpcode::LSR: + case DaedalusOpcode::LTE: + case DaedalusOpcode::EQ: + case DaedalusOpcode::NEQ: + case DaedalusOpcode::GTE: + case DaedalusOpcode::PLUS: + case DaedalusOpcode::NEGATE: + case DaedalusOpcode::NOT: + case DaedalusOpcode::CMPL: v.push_int(0); break; - case opcode::addmovi: - case opcode::submovi: - case opcode::mulmovi: - case opcode::divmovi: - case opcode::movi: - case opcode::movs: - case opcode::movss: - case opcode::movvf: - case opcode::movf: - case opcode::movvi: + case DaedalusOpcode::ADDMOVI: + case DaedalusOpcode::SUBMOVI: + case DaedalusOpcode::MULMOVI: + case DaedalusOpcode::DIVMOVI: + case DaedalusOpcode::MOVI: + case DaedalusOpcode::MOVS: + case DaedalusOpcode::MOVSS: + case DaedalusOpcode::MOVVF: + case DaedalusOpcode::MOVF: + case DaedalusOpcode::MOVVI: // do nothing for now but ideally, this would check the exception and/or remove the offending values // from the stack. break; - case opcode::nop: - case opcode::rsr: - case opcode::b: - case opcode::bz: + case DaedalusOpcode::NOP: + case DaedalusOpcode::RSR: + case DaedalusOpcode::B: + case DaedalusOpcode::BZ: // if these fail, something has gone horribly wrong. still ignore the error though. break; - case opcode::bl: - case opcode::be: + case DaedalusOpcode::BL: + case DaedalusOpcode::BE: // ignore the branch break; - case opcode::pushi: + case DaedalusOpcode::PUSHI: v.push_int(0); break; - case opcode::pushv: - case opcode::pushvi: - case opcode::pushvv: + case DaedalusOpcode::PUSHV: + case DaedalusOpcode::PUSHVI: + case DaedalusOpcode::PUSHVV: // push an int and hope it's the right type! v.push_int(0); break; - case opcode::gmovi: + case DaedalusOpcode::GMOVI: // do nothing for now but ideally, this would set the `null` instance break; } - return vm_exception_strategy::continue_; + return DaedalusVmExceptionStrategy::CONTINUE; + } + + std::int32_t + DaedalusVm::get_int(std::shared_ptr& context, + std::variant>& value, + uint16_t index) { + auto* sym = std::get(value); + + // compatibility: sometimes the context might be zero, but we can't fail so when + // the compatibility flag is set, we just return 0 + if (sym->is_member() && context == nullptr) { + if (!(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { + throw DaedalusNoContextError {sym}; + } + + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", sym->name().c_str()); + return 0; + } + + return sym->get_int(index, context); + } + + float DaedalusVm::get_float(std::shared_ptr& context, + std::variant>& value, + uint16_t index) { + auto* sym = std::get(value); + + // compatibility: sometimes the context might be zero, but we can't fail so when + // the compatibility flag is set, we just return 0 + if (sym->is_member() && context == nullptr) { + if (!(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { + throw DaedalusNoContextError {sym}; + } + + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", sym->name().c_str()); + return 0; + } + + return sym->get_float(index, context); + } + + void DaedalusVm::set_int(std::shared_ptr& context, + DaedalusSymbol* ref, + uint16_t index, + std::int32_t value) { + if (ref->is_const() && !(_m_flags & DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER)) { + throw DaedalusIllegalConstAccess {ref}; + } + + if (!ref->is_member() || context != nullptr || + !(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { + ref->set_int(value, index, context); + } else if (ref->is_member()) { + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", ref->name().c_str()); + } + } + + void DaedalusVm::set_float(std::shared_ptr& context, + DaedalusSymbol* ref, + uint16_t index, + float value) { + if (ref->is_const() && !(_m_flags & DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER)) { + throw DaedalusIllegalConstAccess {ref}; + } + + if (!ref->is_member() || context != nullptr || + !(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { + ref->set_float(value, index, context); + } else if (ref->is_member()) { + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", ref->name().c_str()); + } + } + + void DaedalusVm::set_string(std::shared_ptr& context, + DaedalusSymbol* ref, + uint16_t index, + std::string_view value) { + if (ref->is_const() && !(_m_flags & DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER)) { + throw DaedalusIllegalConstAccess {ref}; + } + + if (!ref->is_member() || context != nullptr || + !(_m_flags & DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS)) { + ref->set_string(value, index, context); + } else if (ref->is_member()) { + ZKLOGE("DaedalusVm", "Accessing member \"%s\" without an instance set", ref->name().c_str()); + } } -} // namespace phoenix +} // namespace zenkit diff --git a/src/Date.cc b/src/Date.cc new file mode 100644 index 00000000..24c838c7 --- /dev/null +++ b/src/Date.cc @@ -0,0 +1,27 @@ +// Copyright © 2022-2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#include "zenkit/Date.hh" +#include "zenkit/Stream.hh" + +#include "phoenix/buffer.hh" + +namespace zenkit { + Date Date::parse(phoenix::buffer& buf) { + Date dt {}; + + auto r = Read::from(&buf); + dt.load(r.get()); + + return dt; + } + + void Date::load(Read* r) { + this->year = r->read_uint(); + this->month = r->read_ushort(); + this->day = r->read_ushort(); + this->hour = r->read_ushort(); + this->minute = r->read_ushort(); + this->second = r->read_ushort(); + (void) r->read_ushort(); // padding + } +} // namespace zenkit diff --git a/src/Error.cc b/src/Error.cc new file mode 100644 index 00000000..cf821939 --- /dev/null +++ b/src/Error.cc @@ -0,0 +1,23 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#include "zenkit/Error.hh" + +namespace zenkit { + Error::Error(std::string&& msg) : std::exception(), message(std::move(msg)) {} + + ParserError::ParserError(std::string&& type) + : Error("failed parsing resource of type " + type), resource_type(type) {} + + ParserError::ParserError(std::string&& type, std::string&& ctx) + : Error("failed parsing resource of type " + type + " [context: " + ctx + "]"), resource_type(std::move(type)), + context(std::move(ctx)) {} + + ParserError::ParserError(std::string&& type, const std::exception& other_exc) + : Error("failed parsing resource of type " + type + " due to [" + other_exc.what() + "]"), + resource_type(std::move(type)), cause(other_exc) {} + + ParserError::ParserError(std::string&& type, const std::exception& other_exc, std::string&& ctx) + : Error("failed parsing resource of type " + type + " due to [" + other_exc.what() + "] [context: " + ctx + + "]"), + resource_type(std::move(type)), context(std::move(ctx)), cause(other_exc) {} +} // namespace zenkit diff --git a/src/Font.cc b/src/Font.cc index a21de3f1..f972ba7f 100644 --- a/src/Font.cc +++ b/src/Font.cc @@ -1,43 +1,51 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/Font.hh" +#include "zenkit/Stream.hh" -namespace phoenix { - font::font(std::string font_name, std::uint32_t font_height, std::vector font_glyphs) +#include "phoenix/phoenix.hh" + +namespace zenkit { + bool FontGlyph::operator==(FontGlyph const& g) const noexcept { + return this->width == g.width && this->uv[0] == g.uv[0] && this->uv[1] == g.uv[1]; + } + + Font::Font(std::string font_name, std::uint32_t font_height, std::vector font_glyphs) : name(std::move(font_name)), height(font_height), glyphs(std::move(font_glyphs)) {} - font font::parse(buffer& in) { - try { - auto version = in.get_line(); - if (version != "1") { - throw parser_error {"font", "version mismatch: expected version 1, got " + version}; - } - - auto name = in.get_line(false); - auto height = in.get_uint(); - - std::vector glyphs {}; - glyphs.resize(in.get_uint()); - - for (auto& glyph : glyphs) { - glyph.width = in.get(); - } - - for (auto& glyph : glyphs) { - glyph.uv[0] = in.get_vec2(); - } - - for (auto& glyph : glyphs) { - glyph.uv[1] = in.get_vec2(); - } - - return font { - name, - height, - std::move(glyphs), - }; - } catch (const buffer_error& exc) { - throw parser_error {"font", exc, "eof reached"}; + Font Font::parse(phoenix::buffer& in) { + Font fnt {}; + + auto r = Read::from(&in); + fnt.load(r.get()); + + return fnt; + } + + Font Font::parse(phoenix::buffer&& in) { + return Font::parse(in); + } + + void Font::load(Read* r) { + auto version = r->read_line(true); + if (version != "1") { + throw zenkit::ParserError {"Font", "version mismatch: expected version 1, got " + version}; + } + + this->name = r->read_line(false); + this->height = r->read_uint(); + + this->glyphs.resize(r->read_uint()); + for (auto& glyph : glyphs) { + glyph.width = r->read_ubyte(); + } + + for (auto& glyph : glyphs) { + glyph.uv[0] = r->read_vec2(); + } + + for (auto& glyph : glyphs) { + glyph.uv[1] = r->read_vec2(); } } -} // namespace phoenix +} // namespace zenkit diff --git a/src/Internal.hh b/src/Internal.hh new file mode 100644 index 00000000..c6d26c4e --- /dev/null +++ b/src/Internal.hh @@ -0,0 +1,10 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#pragma once +#include "zenkit/Logger.hh" + +#define ZKLOGT(name, ...) zenkit::Logger::log(zenkit::LogLevel::TRACE, name, __VA_ARGS__) +#define ZKLOGD(name, ...) zenkit::Logger::log(zenkit::LogLevel::DEBUG, name, __VA_ARGS__) +#define ZKLOGI(name, ...) zenkit::Logger::log(zenkit::LogLevel::INFO, name, __VA_ARGS__) +#define ZKLOGW(name, ...) zenkit::Logger::log(zenkit::LogLevel::WARNING, name, __VA_ARGS__) +#define ZKLOGE(name, ...) zenkit::Logger::log(zenkit::LogLevel::ERROR, name, __VA_ARGS__) diff --git a/src/Logger.cc b/src/Logger.cc new file mode 100644 index 00000000..730f39fb --- /dev/null +++ b/src/Logger.cc @@ -0,0 +1,94 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#include "zenkit/Logger.hh" + +#include +#include +#include + +#define ANSI_RESET "\x1B[0m" +#define ANSI_GRAY "\x1B[90m" +#define ANSI_RED "\x1B[31m" +#define ANSI_GREEN "\x1B[32m" +#define ANSI_YELLOW "\x1B[33m" +#define ANSI_BLUE "\x1B[34m" +#define ANSI_MAGENTA "\x1B[35m" +#define ANSI_BOLD "\x1B[1m" + +#define PREFIX "[" ANSI_MAGENTA ANSI_BOLD "ZenKit" ANSI_RESET "]" + +namespace zenkit { + static char zk_global_logger_buffer[4096]; + + std::function Logger::_s_callback {}; + LogLevel Logger::_s_level {LogLevel::INFO}; + + ZKINT static void zk_internal_logger_default(LogLevel level, char const* name, char const* message) { + time_t now_t = time(nullptr); + struct tm* now = gmtime(&now_t); + + fprintf(stderr, + ANSI_GRAY "%04d-%02d-%02d %02d:%02d:%02d " ANSI_RESET, + now->tm_year + 1900, + now->tm_mon + 1, + now->tm_mday, + now->tm_hour, + now->tm_min, + now->tm_sec); + + switch (level) { + case LogLevel::ERROR: + fprintf(stderr, PREFIX " (" ANSI_RED "ERROR " ANSI_RESET ") › %s: %s\n", name, message); + break; + case LogLevel::WARNING: + fprintf(stderr, PREFIX " (" ANSI_YELLOW "WARNING" ANSI_RESET ") › %s: %s\n", name, message); + break; + case LogLevel::INFO: + fprintf(stderr, PREFIX " (" ANSI_BLUE "INFO " ANSI_RESET ") › %s: %s\n", name, message); + break; + case LogLevel::DEBUG: + fprintf(stderr, PREFIX " (" ANSI_GREEN "DEBUG " ANSI_RESET ") › %s: %s\n", name, message); + break; + case LogLevel::TRACE: + fprintf(stderr, PREFIX " (TRACE ) › %s: %s\n", name, message); + break; + } + } + + /// \brief Supply a custom logger callback to be used for log output from phoenix. + /// \param callback The callback to use. + void Logger::use_logger(std::function&& callback) { + Logger::set(LogLevel::INFO, [callback](LogLevel lvl, std::string_view name, std::string_view sv) { + callback(lvl, std::string {name} + ":" + std::string {sv}); + }); + } + + /// \brief Use the default logger callback for phoenix. + void Logger::use_default_logger() { + Logger::set_default(LogLevel::INFO); + } + + void Logger::log(LogLevel lvl, char const* name, char const* fmt, ...) { + if (!_s_callback) + return; + if (lvl > _s_level) + return; + + va_list ap; + va_start(ap, fmt); + vsnprintf(zk_global_logger_buffer, sizeof zk_global_logger_buffer - 1, fmt, ap); + va_end(ap); + + _s_callback(lvl, name, zk_global_logger_buffer); + } + + void Logger::set(LogLevel lvl, std::function const& cb) { + _s_level = lvl; + _s_callback = cb; + } + + void Logger::set_default(LogLevel lvl) { + _s_level = lvl; + _s_callback = zk_internal_logger_default; + } +} // namespace zenkit diff --git a/src/Material.cc b/src/Material.cc index b0c572db..e7f9fa22 100644 --- a/src/Material.cc +++ b/src/Material.cc @@ -1,95 +1,78 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/Material.hh" +#include "zenkit/Archive.hh" + +#include "phoenix/phoenix.hh" + +#include "Internal.hh" #include -namespace phoenix { - static constexpr auto MATERIAL_VERSION_G1_V108k = 17408; - - material material::parse(archive_reader& in) { - try { - material mat {}; - - // name of the material - ignored - (void) in.read_string(); - - archive_object obj {}; - if (!in.read_object_begin(obj)) { - throw parser_error {"material", "expected archive object begin which was not found"}; - } - - if (obj.class_name != "zCMaterial") { - throw parser_error {"material", "expected archive class zCMaterial; got " + obj.class_name}; - } - - if (obj.version == MATERIAL_VERSION_G1_V108k) { - mat.name = in.read_string(); - mat.group = static_cast(in.read_enum()); - mat.color = in.read_color(); - mat.smooth_angle = in.read_float(); - mat.texture = in.read_string(); - - std::istringstream texture_scale {in.read_string()}; - texture_scale >> mat.texture_scale.x >> mat.texture_scale.y; - - mat.texture_anim_fps = in.read_float(); - mat.texture_anim_map_mode = static_cast(in.read_enum()); - - std::istringstream anim_map_dir {in.read_string()}; - anim_map_dir >> mat.texture_anim_map_dir.x >> mat.texture_anim_map_dir.y; - - mat.disable_collision = in.read_bool(); - mat.disable_lightmap = in.read_bool(); - mat.dont_collapse = in.read_bool(); - mat.detail_object = in.read_string(); - mat.default_mapping = in.read_vec2(); - mat.alpha_func = alpha_function::default_; - } else { - mat.name = in.read_string(); - mat.group = static_cast(in.read_enum()); - mat.color = in.read_color(); - mat.smooth_angle = in.read_float(); - mat.texture = in.read_string(); - - std::istringstream texture_scale {in.read_string()}; - texture_scale >> mat.texture_scale.x >> mat.texture_scale.y; - - mat.texture_anim_fps = in.read_float() / 1000.0f; - mat.texture_anim_map_mode = static_cast(in.read_enum()); - - std::istringstream anim_map_dir {in.read_string()}; - anim_map_dir >> mat.texture_anim_map_dir.x >> mat.texture_anim_map_dir.y; - - mat.disable_collision = in.read_bool(); - mat.disable_lightmap = in.read_bool(); - mat.dont_collapse = in.read_bool(); - mat.detail_object = in.read_string(); - - // This section is specific to G2 - mat.detail_texture_scale = in.read_float(); - mat.force_occluder = in.read_bool(); - mat.environment_mapping = in.read_bool(); - mat.environment_mapping_strength = in.read_float(); - mat.wave_mode = static_cast(in.read_enum()); - mat.wave_speed = static_cast(in.read_enum()); - mat.wave_max_amplitude = in.read_float(); - mat.wave_grid_size = in.read_float(); - mat.ignore_sun = in.read_bool(); - mat.alpha_func = static_cast(in.read_enum()); - - // The mapping comes last :) - mat.default_mapping = in.read_vec2(); - } - - if (!in.read_object_end()) { - PX_LOGW("material(\"", mat.name, "\"): not fully parsed"); - in.skip_object(true); - } - - return mat; - } catch (const buffer_error& exc) { - throw parser_error {"material", exc, "eof reached"}; +namespace zenkit { + static constexpr auto MATERIAL_VERSION_G1 = 17408; + [[maybe_unused]] static constexpr auto MATERIAL_VERSION_G2 = 0x9c03; + + Material Material::parse(ReadArchive& in) { + Material mat {}; + mat.load(in); + return mat; + } + + void Material::load(ReadArchive& r) { + // name of the material - ignored + (void) r.read_string(); + + ArchiveObject obj {}; + if (!r.read_object_begin(obj)) { + throw zenkit::ParserError {"Material", "expected archive object begin which was not found"}; + } + + if (obj.class_name != "zCMaterial") { + throw zenkit::ParserError {"Material", "expected archive class zCMaterial; got " + obj.class_name}; + } + + this->name = r.read_string(); // name + this->group = static_cast(r.read_enum()); // matGroup + this->color = r.read_color(); // color + this->smooth_angle = r.read_float(); // smoothAngle + this->texture = r.read_string(); // texture + + std::istringstream s_texture_scale {r.read_string()}; // texScale + s_texture_scale >> this->texture_scale.x >> this->texture_scale.y; + + this->texture_anim_fps = r.read_float(); // texAniFPS + this->texture_anim_map_mode = static_cast(r.read_enum()); + + std::istringstream anim_map_dir {r.read_string()}; // texAniMapDir + anim_map_dir >> this->texture_anim_map_dir.x >> this->texture_anim_map_dir.y; + + this->disable_collision = r.read_bool(); // noCollDet + this->disable_lightmap = r.read_bool(); // noLightmap + this->dont_collapse = r.read_bool(); // lodDontCollapse + this->detail_object = r.read_string(); + + if (obj.version == MATERIAL_VERSION_G1) { + this->alpha_func = AlphaFunction::DEFAULT; + } else { + // This section is specific to G2 + this->detail_object = r.read_float(); // detailObjectScale + this->force_occluder = r.read_bool(); // forceOccluder + this->environment_mapping = r.read_bool(); // environmentalMapping + this->environment_mapping_strength = r.read_float(); // environmentalMappingStrength + this->wave_mode = static_cast(r.read_enum()); // waveMode + this->wave_speed = static_cast(r.read_enum()); // waveSpeed + this->wave_max_amplitude = r.read_float(); // waveMaxAmplitude + this->wave_grid_size = r.read_float(); // waveGridSize + this->ignore_sun = r.read_bool(); // ignoreSunLight + this->alpha_func = static_cast(r.read_enum()); // alphaFunc + } + + this->default_mapping = r.read_vec2(); + + if (!r.read_object_end()) { + ZKLOGW("Material", "\"%s\" not fully parsed", this->name.c_str()); + r.skip_object(true); } } -} // namespace phoenix +} // namespace zenkit diff --git a/src/Mesh.cc b/src/Mesh.cc index eb264211..3ba2305b 100644 --- a/src/Mesh.cc +++ b/src/Mesh.cc @@ -1,240 +1,247 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include - -namespace phoenix { - [[maybe_unused]] static constexpr auto mesh_version_g1 = 9; - static constexpr auto mesh_version_g2 = 265; - - enum class world_mesh_chunk : std::uint16_t { - unknown, - mesh = 0xB000, - bbox = 0xB010, - material = 0xB020, - lightmaps = 0xB025, - shared_lightmaps = 0xB026, - vertices = 0xB030, - features = 0xB040, - polygons = 0xB050, - end = 0xB060 +#include "zenkit/Mesh.hh" +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" + +namespace zenkit { + [[maybe_unused]] static constexpr auto MESH_VERSION_G1 = 9; + static constexpr auto MESH_VERSION_G2 = 265; + + enum class MeshChunkType : std::uint16_t { + MARKER = 0xB000, + BBOX = 0xB010, + MATERIAL = 0xB020, + LIGHTMAPS = 0xB025, + LIGHTMAPS_SHARED = 0xB026, + VERTICES = 0xB030, + FEATURES = 0xB040, + POLYGONS = 0xB050, + END = 0xB060 }; - mesh mesh::parse(buffer& buf, std::vector const& leaf_polygons, bool force_wide_indices) { - mesh msh {}; + Mesh Mesh::parse(phoenix::buffer& buf, std::vector const& leaf_polygons, bool force_wide_indices) { + Mesh msh {}; - std::uint16_t version {}; - bool finished = false; - world_mesh_chunk type = world_mesh_chunk::unknown; - - do { - type = static_cast(buf.get_ushort()); - - auto length = buf.get_uint(); - auto chunk = buf.extract(length); - - switch (type) { - case world_mesh_chunk::mesh: - version = chunk.get_ushort(); - msh.date = date::parse(chunk); - msh.name = chunk.get_line(false); - break; - case world_mesh_chunk::bbox: - // first, we find a basic AABB bounding box - msh.bbox = bounding_box::parse(chunk); - - // but second, we find a list of OOBBs with one acting as a parent - msh.obb = obb::parse(chunk); - break; - case world_mesh_chunk::material: { - auto matreader = archive_reader::open(chunk); - - std::uint32_t material_count = chunk.get_uint(); - msh.materials.reserve(material_count); - - for (std::uint32_t i = 0; i < material_count; ++i) { - msh.materials.emplace_back(material::parse(*matreader)); - } - - break; - } - case world_mesh_chunk::vertices: - msh.vertices.resize(chunk.get_uint()); - - for (auto& vertex : msh.vertices) { - vertex = chunk.get_vec3(); - } - - break; - case world_mesh_chunk::features: - msh.features.resize(chunk.get_uint()); - - for (auto& feature : msh.features) { - feature.texture = chunk.get_vec2(); - feature.light = chunk.get_uint(); - feature.normal = chunk.get_vec3(); - } - - break; - case world_mesh_chunk::polygons: { - auto poly_count = chunk.get_uint(); - - msh.polygons.material_indices.reserve(poly_count); - msh.polygons.lightmap_indices.reserve(poly_count); - msh.polygons.feature_indices.reserve(poly_count * 3); - msh.polygons.vertex_indices.reserve(poly_count * 3); - msh.polygons.flags.reserve(poly_count); - - for (std::uint32_t i = 0; i < poly_count; ++i) { - - auto material_index = chunk.get_short(); - auto lightmap_index = chunk.get_short(); - - [[maybe_unused]] plane polygon_plane = {chunk.get_float(), chunk.get_vec3()}; - polygon_flags pflags {}; - - if (version == mesh_version_g2) { - std::uint8_t flags = chunk.get(); - pflags.is_portal = (flags & 0b00000011) >> 0; - pflags.is_occluder = (flags & 0b00000100) >> 2; - pflags.is_sector = (flags & 0b00001000) >> 3; - pflags.should_relight = (flags & 0b00010000) >> 4; - pflags.is_outdoor = (flags & 0b00100000) >> 5; - pflags.is_ghost_occluder = (flags & 0b01000000) >> 6; - pflags.is_dynamically_lit = (flags & 0b10000000) >> 7; - pflags.sector_index = chunk.get_ushort(); - } else { - std::uint8_t flags1 = chunk.get(); - std::uint8_t flags2 = chunk.get(); - - pflags.is_portal = (flags1 & 0b00000011) >> 0; - pflags.is_occluder = (flags1 & 0b00000100) >> 2; - pflags.is_sector = (flags1 & 0b00001000) >> 3; - pflags.is_lod = (flags1 & 0b00010000) >> 4; - pflags.is_outdoor = (flags1 & 0b00100000) >> 5; - pflags.is_ghost_occluder = (flags1 & 0b01000000) >> 6; - pflags.normal_axis = ((flags1 & 0b10000000) >> 7) | (flags2 & 0b00000001); - pflags.sector_index = chunk.get_ushort(); - } - - auto vertex_count = chunk.get(); - auto has_wide_indices = (version == mesh_version_g2) || force_wide_indices; - - // TODO: For meshes built for Gothic II, the `is_lod` flag can be used to determine whether a - // polygon is a leaf-polygon or not. Gothic I does not have this luxury, so the leaf polygons - // have to be taken from the BSP tree. - // - // This presents a problem: Taking the leaf polygons as a parameter makes creating a unified - // parsing function for world meshes impossible. Instead, there should be a function to remove - // this extra data which would grant the user more freedom in how they use _phoenix_. - if (!leaf_polygons.empty() && !std::binary_search(leaf_polygons.begin(), leaf_polygons.end(), i)) { - // If the current polygon is not a leaf polygon, skip it. - chunk.skip((has_wide_indices ? 8 : 6) * vertex_count); - continue; - } else if (vertex_count == 0 || pflags.is_portal || pflags.is_ghost_occluder || pflags.is_outdoor) { - // There is no actual geometry associated with this vertex; ignore it. - chunk.skip((has_wide_indices ? 8 : 6) * vertex_count); - } else if (vertex_count == 3) { - // If we have 3 vertices, we are sure that this is already a triangle, - // so we can just read it in - for (int32_t j = 0; j < vertex_count; ++j) { - msh.polygons.vertex_indices.push_back(has_wide_indices ? chunk.get_uint() - : chunk.get_ushort()); - msh.polygons.feature_indices.push_back(chunk.get_uint()); - } - - msh.polygons.material_indices.push_back(material_index); - msh.polygons.lightmap_indices.push_back(lightmap_index); - msh.polygons.flags.push_back(pflags); - } else { - // If we don't have 3 vertices, we need to calculate a triangle fan. - - auto vertex_index_root = has_wide_indices ? chunk.get_uint() : chunk.get_ushort(); - auto feature_index_root = chunk.get_uint(); - - auto vertex_index_a = has_wide_indices ? chunk.get_uint() : chunk.get_ushort(); - auto feature_index_a = chunk.get_uint(); - - for (int32_t j = 0; j < vertex_count - 2; ++j) { - auto vertex_index_b = has_wide_indices ? chunk.get_uint() : chunk.get_ushort(); - auto feature_index_b = chunk.get_uint(); - - msh.polygons.vertex_indices.push_back(vertex_index_root); - msh.polygons.vertex_indices.push_back(vertex_index_a); - msh.polygons.vertex_indices.push_back(vertex_index_b); - msh.polygons.feature_indices.push_back(feature_index_root); - msh.polygons.feature_indices.push_back(feature_index_a); - msh.polygons.feature_indices.push_back(feature_index_b); - - msh.polygons.material_indices.push_back(material_index); - msh.polygons.lightmap_indices.push_back(lightmap_index); - msh.polygons.flags.push_back(pflags); - - vertex_index_a = vertex_index_b; - feature_index_a = feature_index_b; - } - } - } - - break; - } - case world_mesh_chunk::shared_lightmaps: { - auto texture_count = chunk.get_uint(); - - std::vector> lightmap_textures {}; - lightmap_textures.resize(texture_count); - - for (std::uint32_t i = 0; i < texture_count; ++i) { - lightmap_textures[i] = std::make_shared(texture::parse(chunk)); - } - - auto lightmap_count = chunk.get_uint(); - for (std::uint32_t i = 0; i < lightmap_count; ++i) { - auto origin = chunk.get_vec3(); - auto normal_a = chunk.get_vec3(); - auto normal_b = chunk.get_vec3(); - std::uint32_t texture_index = chunk.get_uint(); - - msh.lightmaps.emplace_back( - light_map {lightmap_textures[texture_index], {normal_a, normal_b}, origin}); - } - - break; - } - case world_mesh_chunk::lightmaps: { - auto lightmap_count = chunk.get_uint(); - - for (std::uint32_t i = 0; i < lightmap_count; ++i) { - auto origin = chunk.get_vec3(); - auto normal_a = chunk.get_vec3(); - auto normal_b = chunk.get_vec3(); - auto lightmap_texture = texture::parse(chunk); - - msh.lightmaps.emplace_back(light_map {std::make_shared(std::move(lightmap_texture)), - {normal_a, normal_b}, - origin}); - } - - break; - } - case world_mesh_chunk::end: - finished = true; - break; - default: - break; - } - - if (chunk.remaining() != 0) { - PX_LOGW("mesh: ", chunk.remaining(), " bytes remaining in section ", std::hex, std::uint16_t(type)); - } - } while (!finished); + auto r = Read::from(&buf); + msh.load(r.get(), leaf_polygons, force_wide_indices); return msh; } - bool polygon_flags::operator==(const polygon_flags& b) const { + bool PolygonFlagSet::operator==(const PolygonFlagSet& b) const { return is_portal == b.is_portal && is_occluder == b.is_occluder && is_sector == b.is_sector && should_relight == b.should_relight && is_outdoor == b.is_outdoor && is_ghost_occluder == b.is_ghost_occluder && is_dynamically_lit == b.is_dynamically_lit && sector_index == b.sector_index && is_lod == b.is_lod && normal_axis == b.normal_axis; } -} // namespace phoenix + + Mesh Mesh::parse(phoenix::buffer&& buf, std::vector const& include_polygons) { + return Mesh::parse(buf, include_polygons); + } + + void Mesh::load(Read* r, std::vector const& leaf_polygons, bool force_wide_indices) { + std::uint16_t version {}; + + proto::read_chunked( + r, + "Mesh", + [this, &version, &leaf_polygons, force_wide_indices](Read* c, MeshChunkType type) { + switch (type) { + case MeshChunkType::MARKER: + version = c->read_ushort(); + this->date.load(c); + this->name = c->read_line(false); + break; + case MeshChunkType::BBOX: + // first, we find a basic AABB bounding box + this->bbox.load(c); + + // but second, we find a list of OOBBs with one acting as a parent + this->obb.load(c); + break; + case MeshChunkType::MATERIAL: { + auto matreader = ReadArchive::from(c); + + this->materials.resize(c->read_uint()); + for (auto& material : this->materials) { + material.load(*matreader); + } + + break; + } + case MeshChunkType::VERTICES: + this->vertices.resize(c->read_uint()); + + for (auto& vertex : this->vertices) { + vertex = c->read_vec3(); + } + + break; + case MeshChunkType::FEATURES: + this->features.resize(c->read_uint()); + + for (auto& feature : this->features) { + feature.texture = c->read_vec2(); + feature.light = c->read_uint(); + feature.normal = c->read_vec3(); + } + + break; + case MeshChunkType::POLYGONS: { + auto poly_count = c->read_uint(); + + this->polygons.material_indices.reserve(poly_count); + this->polygons.lightmap_indices.reserve(poly_count); + this->polygons.feature_indices.reserve(poly_count * 3); + this->polygons.vertex_indices.reserve(poly_count * 3); + this->polygons.flags.reserve(poly_count); + + for (std::uint32_t i = 0; i < poly_count; ++i) { + auto material_index = c->read_short(); + auto lightmap_index = c->read_short(); + + (void) c->read_float(); + (void) c->read_vec3(); + + PolygonFlagSet pflags {}; + if (version == MESH_VERSION_G2) { + std::uint8_t flags = c->read_ubyte(); + pflags.is_portal = (flags & 0b00000011) >> 0; + pflags.is_occluder = (flags & 0b00000100) >> 2; + pflags.is_sector = (flags & 0b00001000) >> 3; + pflags.should_relight = (flags & 0b00010000) >> 4; + pflags.is_outdoor = (flags & 0b00100000) >> 5; + pflags.is_ghost_occluder = (flags & 0b01000000) >> 6; + pflags.is_dynamically_lit = (flags & 0b10000000) >> 7; + pflags.sector_index = c->read_short(); + } else { + std::uint8_t flags1 = c->read_ubyte(); + std::uint8_t flags2 = c->read_ubyte(); + + pflags.is_portal = (flags1 & 0b00000011) >> 0; + pflags.is_occluder = (flags1 & 0b00000100) >> 2; + pflags.is_sector = (flags1 & 0b00001000) >> 3; + pflags.is_lod = (flags1 & 0b00010000) >> 4; + pflags.is_outdoor = (flags1 & 0b00100000) >> 5; + pflags.is_ghost_occluder = (flags1 & 0b01000000) >> 6; + pflags.normal_axis = ((flags1 & 0b10000000) >> 7) | (flags2 & 0b00000001); + pflags.sector_index = c->read_short(); + } + + auto vertex_count = c->read_ubyte(); + auto has_wide_indices = (version == MESH_VERSION_G2) || force_wide_indices; + + // TODO: For meshes built for Gothic II, the `is_lod` flag can be used to determine whether a + // polygon is a leaf-polygon or not. Gothic I does not have this luxury, so the leaf + // polygons have to be taken from the BSP tree. + // + // This presents a problem: Taking the leaf polygons as a parameter makes creating a + // unified parsing function for world meshes impossible. Instead, there should be a + // function to remove this extra data which would grant the user more freedom in how they + // use _phoenix_. + if (!leaf_polygons.empty() && + !std::binary_search(leaf_polygons.begin(), leaf_polygons.end(), i)) { + // If the current polygon is not a leaf polygon, skip it. + c->seek((has_wide_indices ? 8 : 6) * vertex_count, Whence::CUR); + continue; + } else if (vertex_count == 0 || pflags.is_portal || pflags.is_ghost_occluder || + pflags.is_outdoor) { + // There is no actual geometry associated with this vertex; ignore it. + c->seek((has_wide_indices ? 8 : 6) * vertex_count, Whence::CUR); + } else if (vertex_count == 3) { + // If we have 3 vertices, we are sure that this is already a triangle, + // so we can just read it in + for (int32_t j = 0; j < vertex_count; ++j) { + this->polygons.vertex_indices.push_back(has_wide_indices ? c->read_uint() + : c->read_ushort()); + this->polygons.feature_indices.push_back(c->read_uint()); + } + + this->polygons.material_indices.push_back(material_index); + this->polygons.lightmap_indices.push_back(lightmap_index); + this->polygons.flags.push_back(pflags); + } else { + // If we don't have 3 vertices, we need to calculate a triangle fan. + + auto vertex_index_root = has_wide_indices ? c->read_uint() : c->read_ushort(); + auto feature_index_root = c->read_uint(); + + auto vertex_index_a = has_wide_indices ? c->read_uint() : c->read_ushort(); + auto feature_index_a = c->read_uint(); + + for (int32_t j = 0; j < vertex_count - 2; ++j) { + auto vertex_index_b = has_wide_indices ? c->read_uint() : c->read_ushort(); + auto feature_index_b = c->read_uint(); + + this->polygons.vertex_indices.push_back(vertex_index_root); + this->polygons.vertex_indices.push_back(vertex_index_a); + this->polygons.vertex_indices.push_back(vertex_index_b); + this->polygons.feature_indices.push_back(feature_index_root); + this->polygons.feature_indices.push_back(feature_index_a); + this->polygons.feature_indices.push_back(feature_index_b); + + this->polygons.material_indices.push_back(material_index); + this->polygons.lightmap_indices.push_back(lightmap_index); + this->polygons.flags.push_back(pflags); + + vertex_index_a = vertex_index_b; + feature_index_a = feature_index_b; + } + } + } + + break; + } + case MeshChunkType::LIGHTMAPS_SHARED: { + auto texture_count = c->read_uint(); + + std::vector> lightmap_textures {}; + lightmap_textures.resize(texture_count); + + for (std::uint32_t i = 0; i < texture_count; ++i) { + lightmap_textures[i] = std::make_shared(); + lightmap_textures[i]->load(c); + } + + auto lightmap_count = c->read_uint(); + for (std::uint32_t i = 0; i < lightmap_count; ++i) { + auto origin = c->read_vec3(); + auto normal_a = c->read_vec3(); + auto normal_b = c->read_vec3(); + std::uint32_t texture_index = c->read_uint(); + + this->lightmaps.emplace_back( + LightMap {lightmap_textures[texture_index], {normal_a, normal_b}, origin}); + } + + break; + } + case MeshChunkType::LIGHTMAPS: { + auto lightmap_count = c->read_uint(); + + for (std::uint32_t i = 0; i < lightmap_count; ++i) { + auto origin = c->read_vec3(); + auto normal_a = c->read_vec3(); + auto normal_b = c->read_vec3(); + + Texture lightmap_texture {}; + lightmap_texture.load(c); + + this->lightmaps.emplace_back(LightMap {std::make_shared(std::move(lightmap_texture)), + {normal_a, normal_b}, + origin}); + } + + break; + } + case MeshChunkType::END: + return true; + default: + break; + } + + return false; + }); + } +} // namespace zenkit diff --git a/src/Misc.cc b/src/Misc.cc new file mode 100644 index 00000000..48b352d8 --- /dev/null +++ b/src/Misc.cc @@ -0,0 +1,20 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#include "zenkit/Misc.hh" + +#include +#include + +namespace zenkit { + bool iequals(std::string_view a, std::string_view b) { + return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char c1, char c2) { + return std::tolower(c1) == std::tolower(c2); + }); + } + + bool icompare(std::string_view a, std::string_view b) { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), [](char c1, char c2) { + return std::tolower(c1) < std::tolower(c2); + }); + } +} // namespace zenkit diff --git a/src/Model.cc b/src/Model.cc index d4d4a867..14ac47ec 100644 --- a/src/Model.cc +++ b/src/Model.cc @@ -1,13 +1,24 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/Model.hh" +#include "zenkit/Stream.hh" -namespace phoenix { - model model::parse(buffer& buf) { - model tmp {}; +namespace zenkit { + Model Model::parse(phoenix::buffer& buf) { + Model tmp {}; + + auto r = Read::from(&buf); + tmp.load(r.get()); - tmp.hierarchy = model_hierarchy::parse(buf); - tmp.mesh = model_mesh::parse(buf); return tmp; } -} // namespace phoenix + + void Model::load(Read* r) { + this->hierarchy.load(r); + this->mesh.load(r); + } + + Model Model::parse(phoenix::buffer&& buf) { + return Model::parse(buf); + } +} // namespace zenkit diff --git a/src/ModelAnimation.cc b/src/ModelAnimation.cc index 9cc6388c..2f04f092 100644 --- a/src/ModelAnimation.cc +++ b/src/ModelAnimation.cc @@ -1,10 +1,13 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/ModelAnimation.hh" +#include "zenkit/Stream.hh" + +#include "phoenix/buffer.hh" #include -namespace phoenix { +namespace zenkit { /// \brief The highest number representable by a single rotation component. static const float SAMPLE_ROTATION_RANGE = float(1 << 16) - 1.0f; @@ -14,139 +17,135 @@ namespace phoenix { /// \brief The number half way to `SAMPLE_ROTATION_RANGE`. static const std::uint16_t SAMPLE_ROTATION_MID = (1 << 15) - 1; - enum class animation_chunk { - animation = 0xa000u, - source = 0xa010u, - header = 0xa020u, - events = 0xa030u, - data = 0xa090u, - unknown + enum class AnimationChunkType { + MARKER = 0xa000u, + SOURCE = 0xa010u, + HEADER = 0xa020u, + EVENTS = 0xa030u, + SAMPLES = 0xa090u, }; /// \brief Reads the position of a single animation sample from the given buffer. - /// \param in The buffer to read from. + /// \param r The stream to read from. /// \param scale The scaling factor to apply (taken from the animation's header). /// \param minimum The value of the smallest position component in the animation (part of its header). /// \return A vector containing the parsed position. - /// \see http://phoenix.lmichaelis.de/engine/formats/animation/#sample-positions - static glm::vec3 read_sample_position(buffer& in, float scale, float minimum) { + /// \see http://phoenix.gothickit.dev/engine/formats/animation/#sample-positions + static glm::vec3 read_sample_position(Read* r, float scale, float minimum) { glm::vec3 v {}; - v.x = (float) in.get_ushort() * scale + minimum; - v.y = (float) in.get_ushort() * scale + minimum; - v.z = (float) in.get_ushort() * scale + minimum; + v.x = (float) r->read_ushort() * scale + minimum; + v.y = (float) r->read_ushort() * scale + minimum; + v.z = (float) r->read_ushort() * scale + minimum; return v; } /// \brief Reads the rotation of a single animation sample from the given buffer. - /// \param in The buffer to read from. + /// \param r The stream to read from. /// \return A quaternion containing the parsed rotation. - /// \see http://phoenix.lmichaelis.de/engine/formats/animation/#sample-rotations - static glm::quat read_sample_quaternion(buffer& in) { + /// \see http://phoenix.gothickit.dev/engine/formats/animation/#sample-rotations + static glm::quat read_sample_quaternion(Read* r) { glm::quat v {}; - v.x = ((float) in.get_ushort() - SAMPLE_ROTATION_MID) * SAMPLE_ROTATION_SCALE; - v.y = ((float) in.get_ushort() - SAMPLE_ROTATION_MID) * SAMPLE_ROTATION_SCALE; - v.z = ((float) in.get_ushort() - SAMPLE_ROTATION_MID) * SAMPLE_ROTATION_SCALE; + v.x = ((float) r->read_ushort() - SAMPLE_ROTATION_MID) * SAMPLE_ROTATION_SCALE; + v.y = ((float) r->read_ushort() - SAMPLE_ROTATION_MID) * SAMPLE_ROTATION_SCALE; + v.z = ((float) r->read_ushort() - SAMPLE_ROTATION_MID) * SAMPLE_ROTATION_SCALE; float len_q = v.x * v.x + v.y * v.y + v.z * v.z; if (len_q > 1.0f) { - float l = 1.0f / sqrtf(len_q); + float l = 1.0f / ::sqrtf(len_q); v.x *= l; v.y *= l; v.z *= l; v.w = 0; } else { // We know the quaternion has to be a unit quaternion, so we can calculate the missing value. - v.w = sqrtf(1.0f - len_q); + v.w = ::sqrtf(1.0f - len_q); } return v; } - animation animation::parse(buffer& buf) { - animation anim {}; - animation_chunk type = animation_chunk::unknown; + bool AnimationSample::operator==(AnimationSample const& other) const noexcept { + return this->position == other.position && this->rotation == other.rotation; + } + + ModelAnimation ModelAnimation::parse(phoenix::buffer& buf) { + ModelAnimation anim {}; - do { - type = static_cast(buf.get_ushort()); - auto chunk = buf.extract(buf.get_uint()); + auto r = Read::from(&buf); + anim.load(r.get()); + + return anim; + } + ModelAnimation ModelAnimation::parse(phoenix::buffer&& in) { + return ModelAnimation::parse(in); + } + + void ModelAnimation::load(Read* r) { + proto::read_chunked(r, "ModelAnimation", [this](Read* c, AnimationChunkType type) { switch (type) { - case animation_chunk::header: - (void) /* version = */ chunk.get_ushort(); - anim.name = chunk.get_line(false); - anim.layer = chunk.get_uint(); - anim.frame_count = chunk.get_uint(); - anim.node_count = chunk.get_uint(); - anim.fps = chunk.get_float(); - anim.fps_source = chunk.get_float(); - anim.sample_position_range_min = chunk.get_float(); - anim.sample_position_scalar = chunk.get_float(); - anim.bbox = bounding_box::parse(chunk); - anim.next = chunk.get_line(false); + case AnimationChunkType::MARKER: break; - case animation_chunk::events: - anim.events.reserve(chunk.get_uint()); - - for (std::uint32_t i = 0; i < anim.events.size(); ++i) { - auto& event = anim.events.emplace_back(); - event.type = static_cast(chunk.get_uint()); - event.no = chunk.get_uint(); - event.tag = chunk.get_line(); + case AnimationChunkType::SOURCE: + // Quirk: This was intended to be a date but the original code uses an uninitialized variable here + // so the actual data stored does not make any sense. + this->source_date.load(c); + this->source_path = c->read_line(false); + this->source_script = c->read_line(false); + break; + case AnimationChunkType::HEADER: + (void) /* version = */ c->read_ushort(); // Should be 0xc for G2 + this->name = c->read_line(false); + this->layer = c->read_uint(); + this->frame_count = c->read_uint(); + this->node_count = c->read_uint(); + this->fps = c->read_float(); + this->fps_source = c->read_float(); + this->sample_position_min = c->read_float(); + this->sample_position_scale = c->read_float(); + this->bbox.load(c); + this->next = c->read_line(false); + break; + case AnimationChunkType::EVENTS: + this->events.resize(c->read_uint()); + for (auto& event : this->events) { + event.type = static_cast(c->read_uint()); + event.frame = c->read_uint(); + event.tag = c->read_line(true); for (auto& j : event.content) { - j = chunk.get_line(); + j = c->read_line(true); } for (float& value : event.values) { - value = chunk.get_float(); + value = c->read_float(); } - event.probability = chunk.get_float(); + event.probability = c->read_float(); } break; - case animation_chunk::data: - anim.checksum = chunk.get_uint(); - anim.node_indices.resize(anim.node_count); + case AnimationChunkType::SAMPLES: + this->checksum = c->read_uint(); - for (std::uint32_t i = 0; i < anim.node_count; ++i) { - anim.node_indices[i] = chunk.get_uint(); + this->node_indices.resize(this->node_count); + for (auto& i : this->node_indices) { + i = c->read_uint(); } - anim.samples.resize(anim.node_count * anim.frame_count); - - for (std::size_t i = 0; i < anim.samples.size(); ++i) { - anim.samples[i].rotation = read_sample_quaternion(chunk); - anim.samples[i].position = - read_sample_position(chunk, anim.sample_position_scalar, anim.sample_position_range_min); + this->samples.resize(this->node_count * this->frame_count); + for (auto& i : this->samples) { + i.rotation = read_sample_quaternion(c); + i.position = read_sample_position(c, this->sample_position_scale, this->sample_position_min); } break; - case animation_chunk::source: - // Quirk: This was intended to be a date but the original code uses an uninitialized variable here - // so the actual data stored does not make any sense. - (void) date::parse(chunk); - - anim.source_path = chunk.get_line(false); - anim.source_script = chunk.get_line(false); - break; - case animation_chunk::animation: // the "animation" chunk is always empty default: break; } - if (chunk.remaining() > 0) { - PX_LOGW("animation(\"", - anim.name, - "\"): ", - chunk.remaining(), - " bytes remaining in section ", - std::hex, - std::uint16_t(type)); - } - } while (buf.remaining() != 0); - - return anim; + return false; + }); } -} // namespace phoenix +} // namespace zenkit diff --git a/src/ModelHierarchy.cc b/src/ModelHierarchy.cc index 6cf4f537..d7d90203 100644 --- a/src/ModelHierarchy.cc +++ b/src/ModelHierarchy.cc @@ -1,61 +1,66 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include - -namespace phoenix { - enum class hierarchy_chunk { unknown, hierarchy = 0xD100, stats = 0xD110, end = 0xD120 }; - - model_hierarchy model_hierarchy::parse(buffer& in) { - model_hierarchy hierarchy {}; - - hierarchy_chunk type = hierarchy_chunk::unknown; - bool end_hierarchy = false; - - do { - type = static_cast(in.get_ushort()); - - auto length = in.get_uint(); - auto chunk = in.extract(length); - - switch (type) { - case hierarchy_chunk::hierarchy: { - (void) /* version = */ chunk.get_uint(); - auto node_count = chunk.get_ushort(); - - for (int32_t i = 0; i < node_count; ++i) { - auto& node = hierarchy.nodes.emplace_back(); - node.name = chunk.get_line(false); - node.parent_index = chunk.get_short(); - node.transform = chunk.get_mat4x4(); - } - - hierarchy.bbox = bounding_box::parse(chunk); - hierarchy.collision_bbox = bounding_box::parse(chunk); - hierarchy.root_translation = chunk.get_vec3(); - hierarchy.checksum = chunk.get_uint(); - break; - } - case hierarchy_chunk::stats: - // maybe a date? - chunk.skip(16); - (void) /* path? = */ chunk.get_line(false); - break; - case hierarchy_chunk::end: - end_hierarchy = true; - break; - default: - break; - } - - if (chunk.remaining() != 0) { - PX_LOGW("model_hierarchy: ", - chunk.remaining(), - " bytes remaining in section ", - std::hex, - std::uint16_t(type)); - } - } while (!end_hierarchy); +#include "zenkit/ModelHierarchy.hh" +#include "zenkit/Stream.hh" + +#include "Internal.hh" + +namespace zenkit { + enum class ModelHierarchyChunkType { + HIERARCHY = 0xD100, + SOURCE = 0xD110, + END = 0xD120, + }; + + ModelHierarchy ModelHierarchy::parse(phoenix::buffer& in) { + ModelHierarchy hierarchy {}; + + auto r = Read::from(&in); + hierarchy.load(r.get()); return hierarchy; } -} // namespace phoenix + + zenkit::ModelHierarchy zenkit::ModelHierarchy::parse(phoenix::buffer&& in) { + return ModelHierarchy::parse(in); + } + + void ModelHierarchy::load(Read* r) { + proto::read_chunked( // + r, + "ModelHierarchy", + [this](Read* c, ModelHierarchyChunkType type) { + switch (type) { + case ModelHierarchyChunkType::HIERARCHY: { + auto version = c->read_uint(); + if (version != 0x03) { + ZKLOGW("ModelHierarchy", "Trying to parse ModelHierarchy with unsupported version %d", version); + } + + this->nodes.resize(c->read_ushort()); + for (auto& node : this->nodes) { + node.name = c->read_line(false); + node.parent_index = c->read_short(); + node.transform = c->read_mat4(); + } + + this->bbox.load(c); + this->collision_bbox.load(c); + this->root_translation = c->read_vec3(); + this->checksum = c->read_uint(); + break; + } + case ModelHierarchyChunkType::SOURCE: + this->source_date.load(c); + this->source_path = c->read_line(false); + break; + case ModelHierarchyChunkType::END: + return true; + default: + break; + } + + return false; + }); + } +} // namespace zenkit diff --git a/src/ModelMesh.cc b/src/ModelMesh.cc index 1b389a94..7e98ede6 100644 --- a/src/ModelMesh.cc +++ b/src/ModelMesh.cc @@ -1,80 +1,81 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/ModelMesh.hh" +#include "zenkit/Date.hh" +#include "zenkit/Stream.hh" -namespace phoenix { - enum class model_mesh_chunk { - unknown, - header = 0xD000, - source = 0xD010, - nodes = 0xD020, - softskins = 0xD030, - end = 0xD040, - proto = 0xB100, +namespace zenkit { + enum class ModelMeshChunkType { + HEADER = 0xD000, + SOURCE = 0xD010, + NODES = 0xD020, + SOFTSKINS = 0xD030, + END = 0xD040, + PROTO = 0xB100, }; - model_mesh model_mesh::parse(buffer& in) { - model_mesh msh {}; - model_mesh_chunk type = model_mesh_chunk::unknown; - bool end_mesh = false; + ModelMesh ModelMesh::parse(phoenix::buffer& in) { + ModelMesh msh {}; - std::vector attachment_names {}; + auto r = Read::from(&in); + msh.load(r.get()); - do { - type = static_cast(in.get_ushort()); + return msh; + } - auto length = in.get_uint(); - auto chunk = in.extract(length); + void ModelMesh::load(Read* r) { + std::vector attachment_names {}; + proto::read_chunked( + r, + "ModelMesh", + [this, &attachment_names](Read* c, ModelMeshChunkType type, size_t& end) { + switch (type) { + case ModelMeshChunkType::HEADER: + (void) /* version = */ c->read_uint(); + break; + case ModelMeshChunkType::SOURCE: { + // supposedly a date? weird values + Date date {}; + date.load(c); - switch (type) { - case model_mesh_chunk::header: - (void) /* version = */ chunk.get_uint(); - break; - case model_mesh_chunk::source: { - // supposedly a date? weird values - (void) /* date = */ date::parse(chunk); - (void) /* source file = */ chunk.get_line(false); - break; - } - case model_mesh_chunk::nodes: { - auto node_count = chunk.get_ushort(); + (void) /* source file = */ c->read_line(false); + break; + } + case ModelMeshChunkType::NODES: { + auto node_count = c->read_ushort(); - for (std::uint32_t i = 0; i < node_count; ++i) { - attachment_names.push_back(chunk.get_line()); - } - break; - } - case model_mesh_chunk::proto: - msh.attachments[attachment_names[msh.attachments.size()]] = proto_mesh::parse_from_section(chunk); - break; - case model_mesh_chunk::softskins: { - msh.checksum = chunk.get_uint(); - auto count = chunk.get_ushort(); - msh.meshes.reserve(count); + for (std::uint32_t i = 0; i < node_count; ++i) { + attachment_names.push_back(c->read_line(true)); + } + break; + } + case ModelMeshChunkType::PROTO: + this->attachments[attachment_names[this->attachments.size()]].load_from_section(c); + break; + case ModelMeshChunkType::SOFTSKINS: { + this->checksum = c->read_uint(); - // Quirk: the meshes are not embedded within the chunk (as in: the stored length does not contain - // the size of these meshes) so they have to be read directly from `in`. - for (int32_t i = 0; i < count; ++i) { - msh.meshes.push_back(softskin_mesh::parse(in)); - } - break; - } - case model_mesh_chunk::end: - end_mesh = true; - break; - default: - break; - } + // Quirk: the meshes are not embedded within the chunk (as in: the stored length does not contain + // the size of these meshes) so they have to be read directly from `in`. + this->meshes.resize(c->read_ushort()); + for (auto& mesh : this->meshes) { + mesh.load(c); + } - if (chunk.remaining() != 0) { - PX_LOGW("model_mesh: ", - chunk.remaining(), - " bytes remaining in section ", - std::hex, - std::uint16_t(type)); - } - } while (!end_mesh); + end = c->tell(); + break; + } + case ModelMeshChunkType::END: + return true; + default: + break; + } - return msh; + return false; + }); + } + + ModelMesh ModelMesh::parse(phoenix::buffer&& buf) { + return ModelMesh::parse(buf); } -} // namespace phoenix +} // namespace zenkit diff --git a/src/ModelScript.cc b/src/ModelScript.cc index af3d4397..7d85c8bf 100644 --- a/src/ModelScript.cc +++ b/src/ModelScript.cc @@ -1,136 +1,136 @@ -// Copyright © 2023 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include -#include +#include "zenkit/ModelScript.hh" +#include "zenkit/Date.hh" +#include "zenkit/Stream.hh" -#include "model_script_dsl.hh" +#include "Internal.hh" +#include "ModelScriptDsl.hh" -#include #include -namespace phoenix { +namespace zenkit { + enum class ModelScriptBinaryChunkType : uint16_t { + // model_mesh = 0xD000, + ROOT = 0xF000, + END = 0xFFFF, + SOURCE = 0xF100, + // model = 0xF200, + // model_end = 0xF2FF, + MESH_AND_TREE = 0xF300, + REGISTER_MESH = 0xF400, + // animation_enum = 0xF500, + // animation_enum_end = 0xF5FF, + // CHUNK_ANI_MAX_FPS = 0xF510, + ANIMATION = 0xF520, + ANIMATION_ALIAS = 0xF530, + ANIMATION_BLEND = 0xF540, + // CHUNK_ANI_SYNC = 0xF550, + // CHUNK_ANI_BATCH = 0xF560, + ANIMATION_COMBINE = 0xF570, + ANIMATION_DISABLE = 0xF580, + MODEL_TAG = 0xF590, + ANIMATION_EVENTS = 0xF5A0, + // animation_events_end = 0xF5AF, + EVENT_SFX = 0xF5A1, + EVENT_SFX_GROUND = 0xF5A2, + EVENT_TAG = 0xF5A3, + EVENT_PFX = 0xF5A4, + EVENT_PFX_STOP = 0xF5A5, + // CHUNK_EVENT_PFX_GRND = 0xF5A6, + // CHUNK_EVENT_SET_MESH = 0xF5A7, + // CHUNK_EVENT_SWAP_MESH = 0xF5A8, + EVENT_MM_ANI = 0xF5A9, + EVENT_CAMERA_TREMOR = 0xF5AA, + UNKNOWN, + }; + + static const std::unordered_map event_types { + {"DEF_CREATE_ITEM", MdsEventType::ITEM_CREATE}, + {"DEF_INSERT_ITEM", MdsEventType::ITEM_INSERT}, + {"DEF_REMOVE_ITEM", MdsEventType::ITEM_REMOVE}, + {"DEF_DESTROY_ITEM", MdsEventType::ITEM_DESTROY}, + {"DEF_PLACE_ITEM", MdsEventType::ITEM_PLACE}, + {"DEF_EXCHANGE_ITEM", MdsEventType::ITEM_EXCHANGE}, + {"DEF_FIGHTMODE", MdsEventType::SET_FIGHT_MODE}, + {"DEF_PLACE_MUNITION", MdsEventType::MUNITION_PLACE}, + {"DEF_REMOVE_MUNITION", MdsEventType::MUNITION_REMOVE}, + {"DEF_DRAWSOUND", MdsEventType::SOUND_DRAW}, + {"DEF_UNDRAWSOUND", MdsEventType::SOUND_UNDRAW}, + {"DEF_SWAPMESH", MdsEventType::MESH_SWAP}, + {"DEF_DRAWTORCH", MdsEventType::TORCH_DRAW}, + {"DEF_INV_TORCH", MdsEventType::TORCH_INVENTORY}, + {"DEF_DROP_TORCH", MdsEventType::TORCH_DROP}, + {"DEF_HIT_LIMB", MdsEventType::HIT_LIMB}, + {"HIT_LIMB", MdsEventType::HIT_LIMB}, + {"DEF_HIT_DIR", MdsEventType::HIT_DIRECTION}, + {"DEF_DIR", MdsEventType::HIT_DIRECTION}, // TODO: Validate this! + {"DEF_DAM_MULTIPLY", MdsEventType::DAMAGE_MULTIPLIER}, + {"DEF_PAR_FRAME", MdsEventType::PARRY_FRAME}, + {"DEF_OPT_FRAME", MdsEventType::OPTIMAL_FRAME}, + {"DEF_HIT_END", MdsEventType::HIT_END}, + {"DEF_WINDOW", MdsEventType::COMBO_WINDOW}, + }; + namespace mds { - enum class parser_chunk : uint16_t { - model_mesh = 0xD000, - model_script = 0xF000, - model_script_end = 0xFFFF, - source = 0xF100, - model = 0xF200, - model_end = 0xF2FF, - mesh_and_tree = 0xF300, - register_mesh = 0xF400, - animation_enum = 0xF500, - animation_enum_end = 0xF5FF, - // CHUNK_ANI_MAX_FPS = 0xF510, - animation = 0xF520, - animation_alias = 0xF530, - animation_blend = 0xF540, - // CHUNK_ANI_SYNC = 0xF550, - // CHUNK_ANI_BATCH = 0xF560, - animation_combination = 0xF570, - animation_disable = 0xF580, - model_tag = 0xF590, - animation_events = 0xF5A0, - animation_events_end = 0xF5AF, - event_sfx = 0xF5A1, - event_sfx_ground = 0xF5A2, - event_tag = 0xF5A3, - event_pfx = 0xF5A4, - event_pfx_stop = 0xF5A5, - // CHUNK_EVENT_PFX_GRND = 0xF5A6, - // CHUNK_EVENT_SET_MESH = 0xF5A7, - // CHUNK_EVENT_SWAP_MESH = 0xF5A8, - event_morph_mesh_animation = 0xF5A9, - event_camera_tremor = 0xF5AA, - error, - unknown, - }; - - static const std::unordered_map event_types { - {"DEF_CREATE_ITEM", event_tag_type::create_item}, - {"DEF_INSERT_ITEM", event_tag_type::insert_item}, - {"DEF_REMOVE_ITEM", event_tag_type::remove_item}, - {"DEF_DESTROY_ITEM", event_tag_type::destroy_item}, - {"DEF_PLACE_ITEM", event_tag_type::place_item}, - {"DEF_EXCHANGE_ITEM", event_tag_type::exchange_item}, - {"DEF_FIGHTMODE", event_tag_type::fight_mode}, - {"DEF_PLACE_MUNITION", event_tag_type::place_munition}, - {"DEF_REMOVE_MUNITION", event_tag_type::remove_munition}, - {"DEF_DRAWSOUND", event_tag_type::draw_sound}, - {"DEF_UNDRAWSOUND", event_tag_type::undraw_sound}, - {"DEF_SWAPMESH", event_tag_type::swap_mesh}, - {"DEF_DRAWTORCH", event_tag_type::draw_torch}, - {"DEF_INV_TORCH", event_tag_type::inventory_torch}, - {"DEF_DROP_TORCH", event_tag_type::drop_torch}, - {"DEF_HIT_LIMB", event_tag_type::hit_limb}, - {"HIT_LIMB", event_tag_type::hit_limb}, - {"DEF_HIT_DIR", event_tag_type::hit_direction}, - {"DEF_DIR", event_tag_type::hit_direction}, // TODO: Validate this! - {"DEF_DAM_MULTIPLY", event_tag_type::dam_multiply}, - {"DEF_PAR_FRAME", event_tag_type::par_frame}, - {"DEF_OPT_FRAME", event_tag_type::opt_frame}, - {"DEF_HIT_END", event_tag_type::hit_end}, - {"DEF_WINDOW", event_tag_type::window}, - }; - - event_tag make_event_tag(int32_t frame, - std::string&& type, - std::optional&& a, - std::optional&& b, - bool attached) { - event_tag evt {}; + MdsEventTag make_event_tag(int32_t frame, + std::string&& type, + std::optional&& a, + std::optional&& b, + bool attached) { + MdsEventTag evt {}; evt.frame = frame; auto tp = event_types.find(type); if (tp == event_types.end()) { - evt.type = mds::event_tag_type::unknown; - PX_LOGW("model_script: unexpected value for event_tag_type: \"", type, "\""); + evt.type = MdsEventType::UNKNOWN; + ZKLOGW("ModelScript", "Unexpected value for MdsEventType: \"%s\"", type.c_str()); } else { evt.type = tp->second; } switch (evt.type) { - case mds::event_tag_type::create_item: - case mds::event_tag_type::exchange_item: + case MdsEventType::ITEM_CREATE: + case MdsEventType::ITEM_EXCHANGE: evt.slot = a.value_or(""); evt.item = b.value_or(""); break; - case mds::event_tag_type::insert_item: - case mds::event_tag_type::place_munition: + case MdsEventType::ITEM_INSERT: + case MdsEventType::MUNITION_PLACE: evt.slot = a.value_or(""); break; - case mds::event_tag_type::fight_mode: { + case MdsEventType::SET_FIGHT_MODE: { auto mode = a.value_or(""); if (mode == "FIST") { - evt.fight_mode = mds::event_fight_mode::fist; + evt.fight_mode = MdsFightMode::FIST; } else if (mode == "1H" || mode == "1h") { - evt.fight_mode = mds::event_fight_mode::one_handed; + evt.fight_mode = MdsFightMode::SINGLE_HANDED; } else if (mode == "2H" || mode == "2h") { - evt.fight_mode = mds::event_fight_mode::two_handed; + evt.fight_mode = MdsFightMode::DUAL_HANDED; } else if (mode == "BOW") { - evt.fight_mode = mds::event_fight_mode::bow; + evt.fight_mode = MdsFightMode::BOW; } else if (mode == "CBOW") { - evt.fight_mode = mds::event_fight_mode::crossbow; + evt.fight_mode = MdsFightMode::CROSSBOW; } else if (mode == "MAG") { - evt.fight_mode = mds::event_fight_mode::magic; + evt.fight_mode = MdsFightMode::MAGIC; } else { - evt.fight_mode = mds::event_fight_mode::none; + evt.fight_mode = MdsFightMode::NONE; } break; } - case mds::event_tag_type::swap_mesh: + case MdsEventType::MESH_SWAP: evt.slot = a.value_or(""); evt.slot2 = b.value_or(""); break; - case mds::event_tag_type::hit_limb: + case MdsEventType::HIT_LIMB: (void) a.value_or(""); break; - case mds::event_tag_type::dam_multiply: - case mds::event_tag_type::par_frame: - case mds::event_tag_type::opt_frame: - case mds::event_tag_type::hit_end: - case mds::event_tag_type::window: { + case MdsEventType::DAMAGE_MULTIPLIER: + case MdsEventType::PARRY_FRAME: + case MdsEventType::OPTIMAL_FRAME: + case MdsEventType::HIT_END: + case MdsEventType::COMBO_WINDOW: { auto frames = a.value_or(""); std::istringstream stream {frames}; @@ -149,323 +149,339 @@ namespace phoenix { return evt; } - animation_flags animation_flags_from_string(std::string_view str) { - uint8_t flags = animation_flags::af_none; + AnimationFlags animation_flags_from_string(std::string_view str) { + AnimationFlags flags = AnimationFlags::NONE; for (char c : str) { switch (c) { case 'M': - flags |= animation_flags::af_move; + flags |= AnimationFlags::MOVE; break; case 'R': - flags |= animation_flags::af_rotate; + flags |= AnimationFlags::ROTATE; break; case 'E': - flags |= animation_flags::af_queue; + flags |= AnimationFlags::QUEUE; break; case 'F': - flags |= animation_flags::af_fly; + flags |= AnimationFlags::FLY; break; case 'I': - flags |= animation_flags::af_idle; + flags |= AnimationFlags::IDLE; + break; + case 'P': + flags |= AnimationFlags::INPLACE; break; default: break; } } - return static_cast(flags); + return flags; } } // namespace mds - static model_script parse_binary_script(buffer& buf) { - model_script script {}; - mds::parser_chunk type = mds::parser_chunk::unknown; + void parse_binary_script(ModelScript& script, Read* r) { int32_t ani_index = -1; + proto::read_chunked( + r, + "ModelScript.Binary", + [&script, &ani_index](Read* c, ModelScriptBinaryChunkType type) { + switch (type) { + case ModelScriptBinaryChunkType::MESH_AND_TREE: { + MdsSkeleton mat {}; + mat.disable_mesh = c->read_uint() != 0; + mat.name = c->read_line(false); + script.skeleton = mat; + break; + } + case ModelScriptBinaryChunkType::REGISTER_MESH: + script.meshes.push_back(c->read_line(true)); + break; + case ModelScriptBinaryChunkType::ANIMATION: { + MdsAnimation anim {}; + anim.name = c->read_line(false); + anim.layer = c->read_uint(); + anim.next = c->read_line(false); + anim.blend_in = c->read_float(); + anim.blend_out = c->read_float(); + anim.flags = mds::animation_flags_from_string(c->read_line(false)); + anim.model = c->read_line(false); + anim.direction = + c->read_line(false).find('R') == 0 ? AnimationDirection::BACKWARD : AnimationDirection::FORWARD; + anim.first_frame = c->read_int(); + anim.last_frame = c->read_int(); + anim.fps = c->read_float(); + anim.speed = c->read_float(); + anim.collision_volume_scale = c->read_float(); + script.animations.push_back(std::move(anim)); + ++ani_index; + break; + } + case ModelScriptBinaryChunkType::ANIMATION_ALIAS: { + MdsAnimationAlias alias {}; + alias.name = c->read_line(false); + alias.layer = c->read_uint(); + alias.next = c->read_line(false); + alias.blend_in = c->read_float(); + alias.blend_out = c->read_float(); + alias.flags = mds::animation_flags_from_string(c->read_line(false)); + alias.alias = c->read_line(false); + alias.direction = + c->read_line(false).find('R') == 0 ? AnimationDirection::BACKWARD : AnimationDirection::FORWARD; + script.aliases.push_back(std::move(alias)); + break; + } + case ModelScriptBinaryChunkType::ANIMATION_BLEND: { + MdsAnimationBlend blend {}; + blend.name = c->read_line(false); + blend.next = c->read_line(false); + blend.blend_in = c->read_float(); + blend.blend_out = c->read_float(); + script.blends.push_back(std::move(blend)); + break; + } + case ModelScriptBinaryChunkType::ANIMATION_COMBINE: { + MdsAnimationCombine combo {}; + combo.name = c->read_line(false); + combo.layer = c->read_uint(); + combo.next = c->read_line(false); + combo.blend_in = c->read_float(); + combo.blend_out = c->read_float(); + combo.flags = mds::animation_flags_from_string(c->read_line(false)); + combo.model = c->read_line(false); + combo.last_frame = c->read_int(); + script.combinations.push_back(std::move(combo)); + break; + } + case ModelScriptBinaryChunkType::ANIMATION_DISABLE: + script.disabled_animations.push_back(c->read_line(false)); + break; + case ModelScriptBinaryChunkType::EVENT_CAMERA_TREMOR: { + MdsCameraTremor trem {}; + trem.frame = c->read_int(); + trem.field1 = c->read_int(); + trem.field2 = c->read_int(); + trem.field3 = c->read_int(); + trem.field4 = c->read_int(); + script.animations[ani_index].tremors.push_back(trem); + break; + } + case ModelScriptBinaryChunkType::EVENT_SFX: { + MdsSoundEffect effect {}; + effect.frame = c->read_int(); + effect.name = c->read_line(false); + effect.range = c->read_float(); + effect.empty_slot = c->read_uint() != 0; + script.animations[ani_index].sfx.push_back(std::move(effect)); + break; + } + case ModelScriptBinaryChunkType::EVENT_SFX_GROUND: { + MdsSoundEffectGround effect {}; + effect.frame = c->read_int(); + effect.name = c->read_line(false); + effect.range = c->read_float(); + effect.empty_slot = c->read_uint() != 0; + script.animations[ani_index].sfx_ground.push_back(std::move(effect)); + break; + } + case ModelScriptBinaryChunkType::MODEL_TAG: { + MdsModelTag tag {}; + (void) c->read_int(); - do { - type = static_cast(buf.get_ushort()); + auto event_type = c->read_line(false); + if (event_type != "DEF_HIT_LIMB" && event_type != "HIT_LIMB") { + ZKLOGW("ModelScript", "Unexpected type for modelTag: \"%s\"", event_type.c_str()); + } - auto length = buf.get_uint(); - auto chunk = buf.extract(length); + tag.bone = c->read_line(true); + script.model_tags.push_back(std::move(tag)); + break; + } + case ModelScriptBinaryChunkType::EVENT_TAG: { + MdsEventTag event {}; + event.frame = c->read_int(); - switch (type) { - case mds::parser_chunk::mesh_and_tree: { - mds::skeleton mat {}; - mat.disable_mesh = chunk.get_uint() != 0; - mat.name = chunk.get_line(false); - script.skeleton = mat; - break; - } - case mds::parser_chunk::register_mesh: - script.meshes.push_back(chunk.get_line()); - break; - case mds::parser_chunk::animation: { - mds::animation anim {}; - anim.name = chunk.get_line(false); - anim.layer = chunk.get_uint(); - anim.next = chunk.get_line(false); - anim.blend_in = chunk.get_float(); - anim.blend_out = chunk.get_float(); - anim.flags = mds::animation_flags_from_string(chunk.get_line(false)); - anim.model = chunk.get_line(false); - anim.direction = chunk.get_line(false).find('R') == 0 ? mds::animation_direction::backward - : mds::animation_direction::forward; - anim.first_frame = chunk.get_int(); - anim.last_frame = chunk.get_int(); - anim.fps = chunk.get_float(); - anim.speed = chunk.get_float(); - anim.collision_volume_scale = chunk.get_float(); - script.animations.push_back(std::move(anim)); - ++ani_index; - break; - } - case mds::parser_chunk::animation_alias: { - mds::animation_alias alias {}; - alias.name = chunk.get_line(false); - alias.layer = chunk.get_uint(); - alias.next = chunk.get_line(false); - alias.blend_in = chunk.get_float(); - alias.blend_out = chunk.get_float(); - alias.flags = mds::animation_flags_from_string(chunk.get_line(false)); - alias.alias = chunk.get_line(false); - alias.direction = chunk.get_line(false).find('R') == 0 ? mds::animation_direction::backward - : mds::animation_direction::forward; - script.aliases.push_back(std::move(alias)); - break; - } - case mds::parser_chunk::animation_blend: { - mds::animation_blending blend {}; - blend.name = chunk.get_line(false); - blend.next = chunk.get_line(false); - blend.blend_in = chunk.get_float(); - blend.blend_out = chunk.get_float(); - script.blends.push_back(std::move(blend)); - break; - } - case mds::parser_chunk::animation_combination: { - mds::animation_combination combo {}; - combo.name = chunk.get_line(false); - combo.layer = chunk.get_uint(); - combo.next = chunk.get_line(false); - combo.blend_in = chunk.get_float(); - combo.blend_out = chunk.get_float(); - combo.flags = mds::animation_flags_from_string(chunk.get_line(false)); - combo.model = chunk.get_line(false); - combo.last_frame = chunk.get_int(); - script.combinations.push_back(std::move(combo)); - break; - } - case mds::parser_chunk::animation_disable: - script.disabled_animations.push_back(chunk.get_line(false)); - break; - case mds::parser_chunk::event_camera_tremor: { - mds::event_camera_tremor trem {}; - trem.frame = chunk.get_int(); - trem.field1 = chunk.get_int(); - trem.field2 = chunk.get_int(); - trem.field3 = chunk.get_int(); - trem.field4 = chunk.get_int(); - script.animations[ani_index].tremors.push_back(std::move(trem)); - break; - } - case mds::parser_chunk::event_sfx: { - mds::event_sfx effect {}; - effect.frame = chunk.get_int(); - effect.name = chunk.get_line(false); - effect.range = chunk.get_float(); - effect.empty_slot = chunk.get_uint() != 0; - script.animations[ani_index].sfx.push_back(std::move(effect)); - break; - } - case mds::parser_chunk::event_sfx_ground: { - mds::event_sfx_ground effect {}; - effect.frame = chunk.get_int(); - effect.name = chunk.get_line(false); - effect.range = chunk.get_float(); - effect.empty_slot = chunk.get_uint() != 0; - script.animations[ani_index].sfx_ground.push_back(std::move(effect)); - break; - } - case mds::parser_chunk::model_tag: { - mds::model_tag tag {}; - (void) chunk.get_int(); + auto event_type = c->read_line(false); + auto tp = event_types.find(event_type); + if (tp == event_types.end()) { + event.type = MdsEventType::UNKNOWN; + ZKLOGW("ModelScript", "Unexpected value for MdsEventType: \"%s\"", event_type.c_str()); + } else { + event.type = tp->second; + } - auto event_type = chunk.get_line(false); - if (event_type != "DEF_HIT_LIMB" && event_type != "HIT_LIMB") { - PX_LOGW("model_script: unexpected type for modelTag: \"", event_type, "\""); - } + switch (event.type) { + case MdsEventType::ITEM_CREATE: + case MdsEventType::ITEM_EXCHANGE: + event.slot = c->read_line(true); + event.item = c->read_line(true); + break; + case MdsEventType::ITEM_INSERT: + case MdsEventType::MUNITION_PLACE: + event.slot = c->read_line(true); + break; + case MdsEventType::SET_FIGHT_MODE: { + auto mode = c->read_line(true); - tag.bone = chunk.get_line(true); - script.model_tags.push_back(std::move(tag)); - break; - } - case mds::parser_chunk::event_tag: { - mds::event_tag event {}; - event.frame = chunk.get_int(); - - auto event_type = chunk.get_line(false); - auto tp = mds::event_types.find(event_type); - if (tp == mds::event_types.end()) { - event.type = mds::event_tag_type::unknown; - PX_LOGW("model_script: unexpected value for event_tag_type: \"", event_type, "\""); - } else { - event.type = tp->second; - } + if (mode == "FIST") { + event.fight_mode = MdsFightMode::FIST; + } else if (mode == "1H" || mode == "1h") { + event.fight_mode = MdsFightMode::SINGLE_HANDED; + } else if (mode == "2H" || mode == "2h") { + event.fight_mode = MdsFightMode::DUAL_HANDED; + } else if (mode == "BOW") { + event.fight_mode = MdsFightMode::BOW; + } else if (mode == "CBOW") { + event.fight_mode = MdsFightMode::CROSSBOW; + } else if (mode == "MAG") { + event.fight_mode = MdsFightMode::MAGIC; + } else { + event.fight_mode = MdsFightMode::NONE; + } + break; + } + case MdsEventType::MESH_SWAP: + event.slot = c->read_line(true); + event.slot2 = c->read_line(true); + break; + case MdsEventType::HIT_LIMB: + (void) c->read_line(true); // TODO + break; + case MdsEventType::HIT_DIRECTION: + (void) c->read_line(true); // TODO + break; + case MdsEventType::SOUND_DRAW: + case MdsEventType::SOUND_UNDRAW: + case MdsEventType::MUNITION_REMOVE: + case MdsEventType::ITEM_DESTROY: + case MdsEventType::TORCH_INVENTORY: + case MdsEventType::ITEM_REMOVE: + (void) c->read_line(true); // TODO + break; + case MdsEventType::DAMAGE_MULTIPLIER: + case MdsEventType::PARRY_FRAME: + case MdsEventType::OPTIMAL_FRAME: + case MdsEventType::HIT_END: + case MdsEventType::COMBO_WINDOW: { + auto frames = c->read_line(true); + std::istringstream stream {frames}; - switch (event.type) { - case mds::event_tag_type::create_item: - case mds::event_tag_type::exchange_item: - event.slot = chunk.get_line(true); - event.item = chunk.get_line(true); - break; - case mds::event_tag_type::insert_item: - case mds::event_tag_type::place_munition: - event.slot = chunk.get_line(true); - break; - case mds::event_tag_type::fight_mode: { - auto mode = chunk.get_line(true); - - if (mode == "FIST") { - event.fight_mode = mds::event_fight_mode::fist; - } else if (mode == "1H" || mode == "1h") { - event.fight_mode = mds::event_fight_mode::one_handed; - } else if (mode == "2H" || mode == "2h") { - event.fight_mode = mds::event_fight_mode::two_handed; - } else if (mode == "BOW") { - event.fight_mode = mds::event_fight_mode::bow; - } else if (mode == "CBOW") { - event.fight_mode = mds::event_fight_mode::crossbow; - } else if (mode == "MAG") { - event.fight_mode = mds::event_fight_mode::magic; - } else { - event.fight_mode = mds::event_fight_mode::none; - } - break; - } - case mds::event_tag_type::swap_mesh: - event.slot = chunk.get_line(true); - event.slot2 = chunk.get_line(true); - break; - case mds::event_tag_type::hit_limb: - (void) chunk.get_line(true); // TODO - break; - case mds::event_tag_type::hit_direction: - (void) chunk.get_line(true); // TODO - break; - case mds::event_tag_type::draw_sound: - case mds::event_tag_type::undraw_sound: - case mds::event_tag_type::remove_munition: - case mds::event_tag_type::destroy_item: - case mds::event_tag_type::inventory_torch: - case mds::event_tag_type::remove_item: - (void) chunk.get_line(true); // TODO - break; - case mds::event_tag_type::dam_multiply: - case mds::event_tag_type::par_frame: - case mds::event_tag_type::opt_frame: - case mds::event_tag_type::hit_end: - case mds::event_tag_type::window: { - auto frames = chunk.get_line(true); - std::istringstream stream {frames}; - - int32_t frame = 0; - while (!stream.eof()) { - stream >> frame; - event.frames.push_back(frame); - } - break; - } - default: - break; - } + int32_t frame = 0; + while (!stream.eof()) { + stream >> frame; + event.frames.push_back(frame); + } + break; + } + default: + break; + } - script.animations[ani_index].events.push_back(std::move(event)); - break; - } - case mds::parser_chunk::event_pfx: { - mds::event_pfx effect {}; - effect.frame = chunk.get_int(); - effect.index = chunk.get_int(); - effect.name = chunk.get_line(false); - effect.position = chunk.get_line(false); - effect.attached = chunk.get_uint() != 0; - script.animations[ani_index].pfx.push_back(std::move(effect)); - break; - } - case mds::parser_chunk::event_pfx_stop: { - mds::event_pfx_stop effect {}; - effect.frame = chunk.get_int(); - effect.index = chunk.get_int(); - script.animations[ani_index].pfx_stop.push_back(effect); - break; - } - case mds::parser_chunk::event_morph_mesh_animation: { - mds::event_morph_animate anim {}; - anim.frame = chunk.get_int(); - anim.animation = chunk.get_line(false); - anim.node = chunk.get_line(false); - (void) chunk.get_float(); - (void) chunk.get_float(); - script.animations[ani_index].morph.push_back(std::move(anim)); - break; - } - case mds::parser_chunk::model_script: - (void) chunk.get_uint(); // bool - (void) chunk.get_line(false); // path - break; - case mds::parser_chunk::source: { - (void) date::parse(chunk); // date - (void) chunk.get_line(false); // path - break; - } - case mds::parser_chunk::model: - case mds::parser_chunk::animation_enum: - case mds::parser_chunk::animation_events_end: - case mds::parser_chunk::animation_enum_end: - case mds::parser_chunk::model_end: - case mds::parser_chunk::model_script_end: - // empty - break; - case mds::parser_chunk::animation_events: - (void) chunk.get_uint(); // bool - break; - case mds::parser_chunk::model_mesh: - case mds::parser_chunk::unknown: - default: - break; - } + script.animations[ani_index].events.push_back(std::move(event)); + break; + } + case ModelScriptBinaryChunkType::EVENT_PFX: { + MdsParticleEffect effect {}; + effect.frame = c->read_int(); + effect.index = c->read_int(); + effect.name = c->read_line(false); + effect.position = c->read_line(false); + effect.attached = c->read_uint() != 0; + script.animations[ani_index].pfx.push_back(std::move(effect)); + break; + } + case ModelScriptBinaryChunkType::EVENT_PFX_STOP: { + MdsParticleEffectStop effect {}; + effect.frame = c->read_int(); + effect.index = c->read_int(); + script.animations[ani_index].pfx_stop.push_back(effect); + break; + } + case ModelScriptBinaryChunkType::EVENT_MM_ANI: { + MdsMorphAnimation anim {}; + anim.frame = c->read_int(); + anim.animation = c->read_line(false); + anim.node = c->read_line(false); + (void) c->read_float(); + (void) c->read_float(); + script.animations[ani_index].morph.push_back(std::move(anim)); + break; + } + case ModelScriptBinaryChunkType::ROOT: + (void) c->read_uint(); // bool + (void) c->read_line(false); // path + break; + case ModelScriptBinaryChunkType::SOURCE: { + Date d {}; + d.load(c); - if (chunk.remaining() != 0) { - PX_LOGW("model_script: ", - chunk.remaining(), - " bytes remaining in section ", - std::hex, - std::uint16_t(type)); - } - } while (buf.remaining() > 0); + (void) c->read_line(false); // path + break; + } + // case ModelScriptBinaryChunkType::model: + // case ModelScriptBinaryChunkType::animation_enum: + // case ModelScriptBinaryChunkType::animation_events_end: + // case ModelScriptBinaryChunkType::animation_enum_end: + // case ModelScriptBinaryChunkType::model_end: + case ModelScriptBinaryChunkType::END: + // empty + break; + case ModelScriptBinaryChunkType::ANIMATION_EVENTS: + (void) c->read_uint(); // bool + break; + case ModelScriptBinaryChunkType::UNKNOWN: + default: + break; + } + return false; + }); + } + + ModelScript ModelScript::parse(phoenix::buffer& buf) { + ModelScript mds {}; + + auto r = Read::from(&buf); + mds.load(r.get()); - return script; + return mds; } - static model_script parse_source_script(buffer& buf) { - parser::parser p {buf.duplicate()}; - return p.parse_script(); + ModelScript ModelScript::parse(phoenix::buffer&& buf) { + return ModelScript::parse(buf); } - model_script model_script::parse(buffer& buf) { - auto peek = buf.position(); - auto potential_chunk_type = buf.get_ushort(); - buf.position(peek); + void ModelScript::load(Read* r) { + auto potential_chunk_type = r->read_ushort(); + r->seek(-2, Whence::CUR); if (potential_chunk_type >= 0xF000 || potential_chunk_type == 0xD000) { - return parse_binary_script(buf); + this->load_binary(r); + return; } - return parse_source_script(buf); + this->load_source(r); + } + + void ModelScript::load_binary(Read* r) { + parse_binary_script(*this, r); + } + + void ModelScript::load_source(Read* r) { + MdsParser p {r}; + p.parse_script(*this); + } + + bool operator&(AnimationFlags a, AnimationFlags b) { + return static_cast(static_cast(a) & static_cast(b)); + } + + AnimationFlags operator|(AnimationFlags a, AnimationFlags b) { + return static_cast(static_cast(a) | static_cast(b)); } - model_script model_script::parse_binary(buffer& buf) { - return model_script::parse(buf); + AnimationFlags& operator|=(AnimationFlags& a, AnimationFlags b) { + a = a | b; + return a; } -} // namespace phoenix +} // namespace zenkit diff --git a/src/ModelScriptDsl.cc b/src/ModelScriptDsl.cc index 172eb5bf..f0afb95a 100644 --- a/src/ModelScriptDsl.cc +++ b/src/ModelScriptDsl.cc @@ -1,553 +1,570 @@ -// Copyright © 2023 Luis Michaelis +// Copyright © 2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include "model_script_dsl.hh" +#include "ModelScriptDsl.hh" +#include "Internal.hh" -#include +#include "zenkit/Stream.hh" -#define WARN_SYNTAX(...) \ - PX_LOGW("model script: syntax error (line ", _m_line, ", column ", _m_column, "): ", __VA_ARGS__) +#define WARN_SYNTAX(msg) ZKLOGW("ModelScript", "Syntax error (line %d, column %d): %s", _m_line, _m_column, msg) -namespace phoenix { - syntax_error::syntax_error(std::string&& location, std::string&& msg) - : phoenix::parser_error("model_script (source)", "MDS syntax error at " + location + ": " + msg) {} +namespace zenkit { + namespace mds { + MdsEventTag + make_event_tag(int32_t, std::string&&, std::optional&&, std::optional&&, bool); + AnimationFlags animation_flags_from_string(std::string_view str); + } // namespace mds - namespace parser { - constexpr std::string_view token_names[] = - {"keyword", "integer", "float", "string", "rparen", "lparen", "rbrace", "lbrace", "colon", "eof", "null"}; + struct ScriptSyntaxError : public zenkit::ParserError { + public: + ScriptSyntaxError(std::string&& location, std::string&& msg); + }; - tokenizer::tokenizer(buffer buf) : _m_buffer(std::move(buf)) {} + ScriptSyntaxError::ScriptSyntaxError(std::string&& location, std::string&& msg) + : zenkit::ParserError("ModelScript (source)", "MDS syntax error at " + location + ": " + msg) {} - token tokenizer::next() { - _m_value.clear(); - while (_m_buffer.remaining() > 0) { - _m_buffer.mark(); - auto chr = static_cast(_m_buffer.get_char()); - _m_column += 1; - - // ignore spaces, quotation marks, semicolons and parentheses - // NOTE: Quirk of original implementation: parens are not significant. - // NOTE: Semicolons are not used in model scripts but can cause parsing failures if not ignored - if (std::isspace(chr) || chr == '(' || chr == ')' || chr == ';') { - if (chr == '\n') { - _m_line += 1; - _m_column = 1; - // TODO? return token::line_feed; - } - - continue; - } + constexpr std::string_view token_names[] = + {"KEYWORD", "integer", "float", "string", "rparen", "lparen", "rbrace", "lbrace", "colon", "eof", "null"}; - // ignore comments - if (chr == '/') { - if (_m_buffer.get_char() != '/') { - WARN_SYNTAX("comments must start with two slashes"); - } + MdsTokenizer::MdsTokenizer(Read* buf) : _m_buffer(buf) {} - // skip everything until the end of the line - while (_m_buffer.get_char() != '\n') - _m_column += 1; + MdsToken MdsTokenizer::next() { + _m_value.clear(); + while (!_m_buffer->eof()) { + _m_mark = _m_buffer->tell(); + auto chr = static_cast(_m_buffer->read_char()); + _m_column += 1; + // ignore spaces, quotation marks, semicolons and parentheses + // NOTE: Quirk of original implementation: parens are not significant. + // NOTE: Semicolons are not used in model scripts but can cause parsing failures if not ignored + if (std::isspace(chr) || chr == '(' || chr == ')' || chr == ';') { + if (chr == '\n') { _m_line += 1; _m_column = 1; - continue; } - // parse keywords - if (std::isalpha(chr) || chr == '*' || chr == '_' || chr == '.') { - do { - _m_value.push_back(static_cast(chr)); - chr = static_cast(_m_buffer.get_char()); - _m_column += 1; - } while (std::isalnum(chr) || chr == '_' || chr == '-' || chr == '.'); - - // (backtrack one) - _m_buffer.position(_m_buffer.position() - 1); - _m_column -= 1; + continue; + } - return token::keyword; + // ignore comments + if (chr == '/') { + if (_m_buffer->read_char() != '/') { + WARN_SYNTAX("comments must start with two slashes"); } - // parse strings - if (chr == '"') { - chr = static_cast(_m_buffer.get_char()); + // skip everything until the end of the line + while (_m_buffer->read_char() != '\n') _m_column += 1; - while (chr != '"' && chr != '\n' && chr != ')') { - _m_value.push_back(static_cast(chr)); - chr = static_cast(_m_buffer.get_char()); - _m_column += 1; - } - - if (chr != '"') { - WARN_SYNTAX("string not terminated"); - _m_buffer.position(_m_buffer.position() - 1); - _m_column -= 1; - } - - return token::string; - } - - // parse numbers - if (std::isdigit(chr) || chr == '-') { - bool floating_point = false; + _m_line += 1; + _m_column = 1; + continue; + } - do { - _m_value.push_back(static_cast(chr)); - chr = static_cast(_m_buffer.get_char()); - _m_column += 1; + // parse keywords + if (std::isalpha(chr) || chr == '*' || chr == '_' || chr == '.') { + do { + _m_value.push_back(static_cast(chr)); + chr = static_cast(_m_buffer->read_char()); + _m_column += 1; + } while (std::isalnum(chr) || chr == '_' || chr == '-' || chr == '.'); - // (allow floating point numbers) - if (chr == '.') { - floating_point = true; + // (backtrack one) + _m_buffer->seek(-1, Whence::CUR); + _m_column -= 1; - _m_value.push_back(static_cast(chr)); - chr = static_cast(_m_buffer.get_char()); - _m_column += 1; - } - } while (std::isdigit(chr)); + return MdsToken::KEYWORD; + } - // (backtrack one) - _m_buffer.position(_m_buffer.position() - 1); - _m_column -= 1; + // parse strings + if (chr == '"') { + chr = static_cast(_m_buffer->read_char()); + _m_column += 1; - return floating_point ? token::float_ : token::integer; + while (chr != '"' && chr != '\n' && chr != ')') { + _m_value.push_back(static_cast(chr)); + chr = static_cast(_m_buffer->read_char()); + _m_column += 1; } - switch (chr) { - case '{': - return token::lbrace; - case '}': - return token::rbrace; - case ':': - return token::colon; - default: - break; + if (chr != '"') { + WARN_SYNTAX("String not terminated"); + _m_buffer->seek(-1, Whence::CUR); + _m_column -= 1; } - WARN_SYNTAX("unknown token \"", chr, "\""); - return token::null; + return MdsToken::STRING; } - return token::eof; - } - - std::string tokenizer::format_location() const { - return "line " + std::to_string(_m_line) + " column " + std::to_string(_m_column); - } - - // ========================== PARSER COMMON FUNCTIONALITY ========================== // + // parse numbers + if (std::isdigit(chr) || chr == '-') { + bool floating_point = false; - parser::parser(buffer buf) : _m_stream(std::move(buf)) {} + do { + _m_value.push_back(static_cast(chr)); + chr = static_cast(_m_buffer->read_char()); + _m_column += 1; - template - void parser::expect() { - if (!this->maybe()) { - throw syntax_error {_m_stream.format_location(), "expected " + std::string {token_names[int(kind)]}}; - } - } + // (allow floating point numbers) + if (chr == '.') { + floating_point = true; - std::string parser::expect_string() { - this->expect(); - return _m_stream.token_value(); - } + _m_value.push_back(static_cast(chr)); + chr = static_cast(_m_buffer->read_char()); + _m_column += 1; + } + } while (std::isdigit(chr)); - std::string parser::expect_keyword() { - this->expect(); - return _m_stream.token_value(); - } + // (backtrack one) + _m_buffer->seek(-1, Whence::CUR); + _m_column -= 1; - std::optional parser::maybe_keyword() { - if (this->maybe()) - return _m_stream.token_value(); - return std::nullopt; - } - - void parser::expect_keyword(std::string_view value) { - this->expect(); - if (!phoenix::iequals(_m_stream.token_value(), value)) { - throw syntax_error {_m_stream.format_location(), - "expected the keyword \"" + std::string {value} + "\""}; + return floating_point ? MdsToken::FLOAT : MdsToken::INTEGER; } - } - float parser::expect_number() { - auto mb = this->maybe_number(); - if (!mb) { - throw syntax_error {_m_stream.format_location(), "expected a number"}; + switch (chr) { + case '{': + return MdsToken::LBRACE; + case '}': + return MdsToken::RBRACE; + case ':': + return MdsToken::COLON; + default: + break; } - return *mb; - } - int parser::expect_int() { - this->expect(); - return std::stoi(_m_stream.token_value()); + WARN_SYNTAX("Illegal token"); + return MdsToken::NOTHING; } - mds::animation_flags parser::expect_flags() { - auto kw = this->expect_keyword(); - // NOTE: Quirk of original implementation: Normally, "." is used in flags to indicate - // _not set_ but sometimes ":" appears instead. - (void) this->maybe(); - return mds::animation_flags_from_string(kw); - } + return MdsToken::END_OF_FILE; + } - std::optional parser::maybe_flags() { - // NOTE: Workaround for mod-specific issues - auto kw = this->maybe_keyword(); - if (!kw) { - return std::nullopt; - } + std::string MdsTokenizer::format_location() const { + return "line " + std::to_string(_m_line) + " column " + std::to_string(_m_column); + } - if (kw->find("ani") != std::string::npos || kw->find("model") != std::string::npos) { - this->_m_stream.backtrack(); - return std::nullopt; - } + void MdsTokenizer::backtrack() { + this->_m_buffer->seek(static_cast(_m_mark), Whence::BEG); + } - // NOTE: Quirk of original implementation: Normally, "." is used in flags to indicate - // _not set_ but sometimes ":" appears instead. - (void) this->maybe(); - return mds::animation_flags_from_string(*kw); - } + bool MdsTokenizer::eof() const { + return _m_buffer->eof(); + } - template - bool parser::maybe() { - if (_m_stream.next() != kind) { - _m_stream.backtrack(); - return false; - } + std::string const& MdsTokenizer::token_value() const { + return _m_value; + } - return true; - } + // ========================== PARSER COMMON FUNCTIONALITY ========================== // - std::optional parser::maybe_int() { - if (!this->maybe()) { - return std::nullopt; - } + MdsParser::MdsParser(Read* buf) : _m_stream(buf) {} - return std::stoi(_m_stream.token_value()); + template + void MdsParser::expect() { + if (!this->maybe()) { + throw ScriptSyntaxError {_m_stream.format_location(), "expected " + std::string {token_names[int(kind)]}}; } + } - std::optional parser::maybe_number() { - auto n = this->_m_stream.next(); - if (n != token::integer && n != token::float_) { - this->_m_stream.backtrack(); - return std::nullopt; - } - - return std::stof(_m_stream.token_value()); - } + std::string MdsParser::expect_string() { + this->expect(); + return _m_stream.token_value(); + } - std::optional parser::maybe_string() { - if (!this->maybe()) { - return std::nullopt; - } + std::string MdsParser::expect_keyword() { + this->expect(); + return _m_stream.token_value(); + } + std::optional MdsParser::maybe_keyword() { + if (this->maybe()) return _m_stream.token_value(); + return std::nullopt; + } + + void MdsParser::expect_keyword(std::string_view value) { + this->expect(); + if (!phoenix::iequals(_m_stream.token_value(), value)) { + throw ScriptSyntaxError {_m_stream.format_location(), + "expected the KEYWORD \"" + std::string {value} + "\""}; + } + } + + float MdsParser::expect_number() { + auto mb = this->maybe_number(); + if (!mb) { + throw ScriptSyntaxError {_m_stream.format_location(), "expected a number"}; + } + return *mb; + } + + int MdsParser::expect_int() { + this->expect(); + return std::stoi(_m_stream.token_value()); + } + + AnimationFlags MdsParser::expect_flags() { + auto kw = this->expect_keyword(); + // NOTE: Quirk of original implementation: Normally, "." is used in flags to indicate + // _not set_ but sometimes ":" appears instead. + (void) this->maybe(); + return mds::animation_flags_from_string(kw); + } + + std::optional MdsParser::maybe_flags() { + // NOTE: Workaround for mod-specific issues + auto kw = this->maybe_keyword(); + if (!kw) { + return std::nullopt; } - bool parser::maybe_keyword(std::string_view value) { - if (!this->maybe()) { - return false; - } - - if (!phoenix::iequals(this->_m_stream.token_value(), value)) { - this->_m_stream.backtrack(); - return false; - } - - return true; + if (kw->find("ani") != std::string::npos || kw->find("model") != std::string::npos) { + this->_m_stream.backtrack(); + return std::nullopt; } - std::optional parser::maybe_named(std::string_view name) { - if (!this->maybe()) { - return std::nullopt; - } - - if (!phoenix::iequals(this->_m_stream.token_value(), name)) { - this->_m_stream.backtrack(); - return std::nullopt; - } + // NOTE: Quirk of original implementation: Normally, "." is used in flags to indicate + // _not set_ but sometimes ":" appears instead. + (void) this->maybe(); + return mds::animation_flags_from_string(*kw); + } - this->expect(); - return this->expect_number(); + template + bool MdsParser::maybe() { + if (_m_stream.next() != kind) { + _m_stream.backtrack(); + return false; } - // ===================== PARSER IMPLEMENTATION ======================= // - - model_script parser::parse_script() { - model_script script {}; - this->expect_keyword("Model"); - - auto name = this->expect_string(); - this->expect(); - - while (!this->eof()) { - if (this->maybe() || this->maybe()) { - break; - } + return true; + } - auto kw = this->expect_keyword(); - if (phoenix::iequals(kw, "meshAndTree")) { - script.skeleton = this->parse_meshAndTree(); - } else if (phoenix::iequals(kw, "registerMesh")) { - script.meshes.push_back(this->parse_registerMesh()); - } else if (phoenix::iequals(kw, "aniEnum")) { - this->parse_aniEnum(script); - } else { - PX_LOGW("model_script: detected invalid use of keyword " + kw + - "in \"Model\" block. Ignoring rest of script."); - break; - } - } - - return script; + std::optional MdsParser::maybe_int() { + if (!this->maybe()) { + return std::nullopt; } - mds::skeleton parser::parse_meshAndTree() { - mds::skeleton skel {}; - skel.name = this->expect_string(); - skel.disable_mesh = this->maybe_keyword("DONT_USE_MESH"); - return skel; - } + return std::stoi(_m_stream.token_value()); + } - std::string parser::parse_registerMesh() { - return this->expect_string(); + std::optional MdsParser::maybe_number() { + auto n = this->_m_stream.next(); + if (n != MdsToken::INTEGER && n != MdsToken::FLOAT) { + this->_m_stream.backtrack(); + return std::nullopt; } - void parser::parse_aniEnum(model_script& into) { - this->expect(); - - while (!this->eof()) { - if (this->maybe() || this->maybe()) { - break; - } + return std::stof(_m_stream.token_value()); + } - auto kw = this->expect_keyword(); - if (phoenix::iequals(kw, "ani")) { - into.animations.push_back(this->parse_ani()); - } else if (phoenix::iequals(kw, "aniBlend")) { - into.blends.push_back(this->parse_aniBlend()); - } else if (phoenix::iequals(kw, "aniAlias")) { - into.aliases.push_back(this->parse_aniAlias()); - } else if (phoenix::iequals(kw, "aniComb")) { - into.combinations.push_back(this->parse_aniComb()); - } else if (phoenix::iequals(kw, "aniDisable")) { - into.disabled_animations.push_back(this->parse_aniDisable()); - } else if (phoenix::iequals(kw, "modelTag")) { - into.model_tags.push_back(this->parse_modelTag()); - } else { - throw syntax_error {_m_stream.format_location(), "invalid keyword in \"aniEnum\" block: " + kw}; - } - } + std::optional MdsParser::maybe_string() { + if (!this->maybe()) { + return std::nullopt; } - void parser::parse_events(mds::animation& ani) { - while (!this->eof()) { - if (this->maybe() || this->maybe()) { - break; - } - - auto kw = this->expect_keyword(); - if (phoenix::iequals(kw, "*eventTag")) { - ani.events.push_back(this->parse_eventTag()); - } else if (phoenix::iequals(kw, "*eventSFX")) { - ani.sfx.push_back(this->parse_eventSFX()); - } else if (phoenix::iequals(kw, "*eventSFXGrnd")) { - ani.sfx_ground.push_back(this->parse_eventSFXGrnd()); - } else if (phoenix::iequals(kw, "*eventPFX")) { - ani.pfx.push_back(this->parse_eventPFX()); - } else if (phoenix::iequals(kw, "*eventPFXStop")) { - ani.pfx_stop.push_back(this->parse_eventPFXStop()); - } else if (phoenix::iequals(kw, "*eventMMStartAni")) { - ani.morph.push_back(this->parse_eventMMStartAni()); - } else if (phoenix::iequals(kw, "*eventCamTremor")) { - ani.tremors.push_back(this->parse_eventCamTremor()); - } else { - throw syntax_error {_m_stream.format_location(), "invalid keyword in \"ani\" block: " + kw}; - } - } - } + return _m_stream.token_value(); + } - void parser::ignore_block() { - while (this->_m_stream.next() != token::rbrace) {} + bool MdsParser::maybe_keyword(std::string_view value) { + if (!this->maybe()) { + return false; } - mds::event_tag parser::parse_eventTag() { - auto frame = this->maybe_int().value_or(0); - auto type = this->expect_string(); - auto a = this->maybe_string(); - auto b = this->maybe_string(); - auto attach = this->maybe_keyword("ATTACH"); - return mds::make_event_tag(frame, std::move(type), std::move(a), std::move(b), attach); + if (!phoenix::iequals(this->_m_stream.token_value(), value)) { + this->_m_stream.backtrack(); + return false; } - mds::event_sfx parser::parse_eventSFX() { - mds::event_sfx sfx {}; - sfx.frame = this->expect_int(); - sfx.name = this->expect_string(); - sfx.range = this->maybe_named("R").value_or(1000.0f); + return true; + } - // NOTE: Quirk of original implementation: Sometimes "EMTPY_SLOT" is used instead of "EMPTY_SLOT". - sfx.empty_slot = this->maybe_keyword("EMPTY_SLOT") || this->maybe_keyword("EMTPY_SLOT"); - return sfx; + std::optional MdsParser::maybe_named(std::string_view name) { + if (!this->maybe()) { + return std::nullopt; } - mds::event_pfx parser::parse_eventPFX() { - mds::event_pfx pfx {}; - pfx.frame = this->expect_int(); - pfx.index = this->maybe_int().value_or(0); - pfx.name = this->expect_string(); - pfx.position = this->expect_string(); - pfx.attached = false; - - // NOTE: Quirk of original implementation: Sometimes, "ATTACH" does not appear as a keyword. - if (this->maybe_keyword("ATTACH") || this->maybe()) { - pfx.attached = true; - } - - return pfx; + if (!phoenix::iequals(this->_m_stream.token_value(), name)) { + this->_m_stream.backtrack(); + return std::nullopt; } - mds::event_sfx_ground parser::parse_eventSFXGrnd() { - mds::event_sfx_ground sfx {}; - sfx.frame = this->expect_int(); - sfx.name = this->expect_string(); + this->expect(); + return this->expect_number(); + } - // NOTE: Fix for mod-specific issues - (void) this->maybe_keyword("EMPTY_SLOT"); - return sfx; - } + // ===================== PARSER IMPLEMENTATION ======================= // - mds::event_pfx_stop parser::parse_eventPFXStop() { - mds::event_pfx_stop pfx {}; - pfx.frame = this->expect_int(); - pfx.index = this->expect_int(); - return pfx; - } + ModelScript MdsParser::parse_script(ModelScript& script) { + this->expect_keyword("Model"); - mds::event_morph_animate parser::parse_eventMMStartAni() { - mds::event_morph_animate morph {}; - morph.frame = this->expect_int(); - morph.animation = this->expect_string(); - morph.node = this->maybe_string().value_or(""); - return morph; - } - - mds::event_camera_tremor parser::parse_eventCamTremor() { - mds::event_camera_tremor tremor {}; - tremor.frame = this->expect_int(); - tremor.field1 = this->expect_int(); - tremor.field2 = this->expect_int(); - tremor.field3 = this->expect_int(); - tremor.field4 = this->expect_int(); - return tremor; - } + auto name = this->expect_string(); + this->expect(); - mds::animation parser::parse_ani() { - mds::animation ani {}; - ani.name = this->expect_string(); - ani.layer = this->expect_int(); - ani.next = this->expect_string(); - ani.blend_in = this->expect_number(); - ani.blend_out = this->expect_number(); - ani.flags = this->expect_flags(); - ani.model = this->expect_string(); + while (!this->eof()) { + if (this->maybe() || this->maybe()) { + break; + } auto kw = this->expect_keyword(); - if (!phoenix::iequals(kw, "F") && !phoenix::iequals(kw, "R")) { - throw syntax_error {_m_stream.format_location(), "expected \"F\" or \"R\""}; + if (phoenix::iequals(kw, "meshAndTree")) { + script.skeleton = this->parse_meshAndTree(); + } else if (phoenix::iequals(kw, "registerMesh")) { + script.meshes.push_back(this->parse_registerMesh()); + } else if (phoenix::iequals(kw, "aniEnum")) { + this->parse_aniEnum(script); + } else { + ZKLOGW("ModelScript", + "detected invalid use of KEYWORD \"%s\" in \"Model\" block. Ignoring rest of script.", + kw.c_str()); + break; } + } - ani.direction = - phoenix::iequals(kw, "R") ? mds::animation_direction::backward : mds::animation_direction::forward; + return script; + } - ani.first_frame = this->expect_int(); - ani.last_frame = this->expect_int(); - ani.speed = 0; // TODO + MdsSkeleton MdsParser::parse_meshAndTree() { + MdsSkeleton skel {}; + skel.name = this->expect_string(); + skel.disable_mesh = this->maybe_keyword("DONT_USE_MESH"); + return skel; + } - ani.fps = this->maybe_named("FPS").value_or(25); - ani.collision_volume_scale = this->maybe_named("CVS").value_or(1); + std::string MdsParser::parse_registerMesh() { + return this->expect_string(); + } - // Optional events block. - if (this->maybe()) { - this->parse_events(ani); - } + void MdsParser::parse_aniEnum(ModelScript& into) { + this->expect(); - return ani; - } - - mds::animation_combination parser::parse_aniComb() { - mds::animation_combination comb {}; - comb.name = this->expect_string(); - comb.layer = this->expect_int(); - comb.next = this->expect_string(); - comb.blend_in = this->expect_number(); - comb.blend_out = this->expect_number(); - comb.flags = this->expect_flags(); - comb.model = this->expect_string(); - comb.last_frame = this->expect_int(); - - // Optional events block. - if (this->maybe()) { - this->ignore_block(); + while (!this->eof()) { + if (this->maybe() || this->maybe()) { + break; } - return comb; - } - mds::animation_alias parser::parse_aniAlias() { - mds::animation_alias alias {}; - alias.direction = mds::animation_direction::forward; - alias.name = this->expect_string(); - alias.layer = this->expect_int(); - alias.next = this->expect_string(); - alias.blend_in = this->expect_number(); - alias.blend_out = this->expect_number(); - alias.flags = this->expect_flags(); - alias.alias = this->expect_string(); - alias.direction = mds::animation_direction::forward; - - if (this->maybe_keyword("F")) { - alias.direction = mds::animation_direction::forward; - } else if (this->maybe_keyword("R")) { - alias.direction = mds::animation_direction::backward; + auto kw = this->expect_keyword(); + if (phoenix::iequals(kw, "ani")) { + into.animations.push_back(this->parse_ani()); + } else if (phoenix::iequals(kw, "aniBlend")) { + into.blends.push_back(this->parse_aniBlend()); + } else if (phoenix::iequals(kw, "aniAlias")) { + into.aliases.push_back(this->parse_aniAlias()); + } else if (phoenix::iequals(kw, "aniComb")) { + into.combinations.push_back(this->parse_aniComb()); + } else if (phoenix::iequals(kw, "aniDisable")) { + into.disabled_animations.push_back(this->parse_aniDisable()); + } else if (phoenix::iequals(kw, "modelTag")) { + into.model_tags.push_back(this->parse_modelTag()); + } else { + throw ScriptSyntaxError {_m_stream.format_location(), "invalid KEYWORD in \"aniEnum\" block: " + kw}; } - - return alias; } + } - mds::animation_blending parser::parse_aniBlend() { - mds::animation_blending blend {}; - blend.name = this->expect_string(); - (void) this->maybe_int(); - blend.next = this->expect_string(); - blend.blend_in = this->maybe_number().value_or(0); - blend.blend_out = this->maybe_number().value_or(0); - - // NOTE: Fix for mod-specific issues - (void) this->maybe_flags(); - - // Optional events block. - if (this->maybe()) { - this->ignore_block(); + void MdsParser::parse_events(MdsAnimation& ani) { + while (!this->eof()) { + if (this->maybe() || this->maybe()) { + break; } - return blend; - } - - std::string parser::parse_aniDisable() { - return this->expect_string(); - } - - mds::model_tag parser::parse_modelTag() { - mds::model_tag tag {}; - - auto type = this->expect_string(); - if (!phoenix::iequals(type, "DEF_HIT_LIMB")) { - throw syntax_error {_m_stream.format_location(), "expected a \"DEF_HIT_LIMB\""}; + auto kw = this->expect_keyword(); + if (phoenix::iequals(kw, "*eventTag")) { + ani.events.push_back(this->parse_eventTag()); + } else if (phoenix::iequals(kw, "*eventSFX")) { + ani.sfx.push_back(this->parse_eventSFX()); + } else if (phoenix::iequals(kw, "*eventSFXGrnd")) { + ani.sfx_ground.push_back(this->parse_eventSFXGrnd()); + } else if (phoenix::iequals(kw, "*eventPFX")) { + ani.pfx.push_back(this->parse_eventPFX()); + } else if (phoenix::iequals(kw, "*eventPFXStop")) { + ani.pfx_stop.push_back(this->parse_eventPFXStop()); + } else if (phoenix::iequals(kw, "*eventMMStartAni")) { + ani.morph.push_back(this->parse_eventMMStartAni()); + } else if (phoenix::iequals(kw, "*eventCamTremor")) { + ani.tremors.push_back(this->parse_eventCamTremor()); + } else { + throw ScriptSyntaxError {_m_stream.format_location(), "invalid KEYWORD in \"ani\" block: " + kw}; } - - tag.bone = this->expect_string(); - return tag; } - } // namespace parser -} // namespace phoenix + } + + void MdsParser::ignore_block() { + while (this->_m_stream.next() != MdsToken::RBRACE) {} + } + + MdsEventTag MdsParser::parse_eventTag() { + auto frame = this->maybe_int().value_or(0); + auto type = this->expect_string(); + auto a = this->maybe_string(); + auto b = this->maybe_string(); + auto attach = this->maybe_keyword("ATTACH"); + return mds::make_event_tag(frame, std::move(type), std::move(a), std::move(b), attach); + } + + MdsSoundEffect MdsParser::parse_eventSFX() { + MdsSoundEffect sfx {}; + sfx.frame = this->expect_int(); + sfx.name = this->expect_string(); + sfx.range = this->maybe_named("R").value_or(1000.0f); + + // NOTE: Quirk of original implementation: Sometimes "EMTPY_SLOT" is used instead of "EMPTY_SLOT". + sfx.empty_slot = this->maybe_keyword("EMPTY_SLOT") || this->maybe_keyword("EMTPY_SLOT"); + return sfx; + } + + MdsParticleEffect MdsParser::parse_eventPFX() { + MdsParticleEffect pfx {}; + pfx.frame = this->expect_int(); + pfx.index = this->maybe_int().value_or(0); + pfx.name = this->expect_string(); + pfx.position = this->expect_string(); + pfx.attached = false; + + // NOTE: Quirk of original implementation: Sometimes, "ATTACH" does not appear as a keyword. + if (this->maybe_keyword("ATTACH") || this->maybe()) { + pfx.attached = true; + } + + return pfx; + } + + MdsSoundEffectGround MdsParser::parse_eventSFXGrnd() { + MdsSoundEffectGround sfx {}; + sfx.frame = this->expect_int(); + sfx.name = this->expect_string(); + // NOTE: Fix for mod-specific issues + (void) this->maybe_keyword("EMPTY_SLOT"); + return sfx; + } + + MdsParticleEffectStop MdsParser::parse_eventPFXStop() { + MdsParticleEffectStop pfx {}; + pfx.frame = this->expect_int(); + pfx.index = this->expect_int(); + return pfx; + } + + MdsMorphAnimation MdsParser::parse_eventMMStartAni() { + MdsMorphAnimation morph {}; + morph.frame = this->expect_int(); + morph.animation = this->expect_string(); + morph.node = this->maybe_string().value_or(""); + return morph; + } + + MdsCameraTremor MdsParser::parse_eventCamTremor() { + MdsCameraTremor tremor {}; + tremor.frame = this->expect_int(); + tremor.field1 = this->expect_int(); + tremor.field2 = this->expect_int(); + tremor.field3 = this->expect_int(); + tremor.field4 = this->expect_int(); + return tremor; + } + + MdsAnimation MdsParser::parse_ani() { + MdsAnimation ani {}; + ani.name = this->expect_string(); + ani.layer = this->expect_int(); + ani.next = this->expect_string(); + ani.blend_in = this->expect_number(); + ani.blend_out = this->expect_number(); + ani.flags = this->expect_flags(); + ani.model = this->expect_string(); + + auto kw = this->expect_keyword(); + if (!phoenix::iequals(kw, "F") && !phoenix::iequals(kw, "R")) { + throw ScriptSyntaxError {_m_stream.format_location(), R"(expected "F" or "R")"}; + } + + ani.direction = phoenix::iequals(kw, "R") ? AnimationDirection::BACKWARD : AnimationDirection::FORWARD; + ani.first_frame = this->expect_int(); + ani.last_frame = this->expect_int(); + ani.speed = 0; // TODO + + ani.fps = this->maybe_named("FPS").value_or(25); + ani.collision_volume_scale = this->maybe_named("CVS").value_or(1); + + // Optional events block. + if (this->maybe()) { + this->parse_events(ani); + } + + return ani; + } + + MdsAnimationCombine MdsParser::parse_aniComb() { + MdsAnimationCombine comb {}; + comb.name = this->expect_string(); + comb.layer = this->expect_int(); + comb.next = this->expect_string(); + comb.blend_in = this->expect_number(); + comb.blend_out = this->expect_number(); + comb.flags = this->expect_flags(); + comb.model = this->expect_string(); + comb.last_frame = this->expect_int(); + + // Optional events block. + if (this->maybe()) { + this->ignore_block(); + } + + return comb; + } + + MdsAnimationAlias MdsParser::parse_aniAlias() { + MdsAnimationAlias alias {}; + alias.direction = AnimationDirection::FORWARD; + alias.name = this->expect_string(); + alias.layer = this->expect_int(); + alias.next = this->expect_string(); + alias.blend_in = this->expect_number(); + alias.blend_out = this->expect_number(); + alias.flags = this->expect_flags(); + alias.alias = this->expect_string(); + + if (this->maybe_keyword("F")) { + alias.direction = AnimationDirection::FORWARD; + } else if (this->maybe_keyword("R")) { + alias.direction = AnimationDirection::BACKWARD; + } + + return alias; + } + + MdsAnimationBlend MdsParser::parse_aniBlend() { + MdsAnimationBlend blend {}; + blend.name = this->expect_string(); + (void) this->maybe_int(); + blend.next = this->expect_string(); + blend.blend_in = this->maybe_number().value_or(0); + blend.blend_out = this->maybe_number().value_or(0); + + // NOTE: Fix for mod-specific issues + (void) this->maybe_flags(); + + // Optional events block. + if (this->maybe()) { + this->ignore_block(); + } + + return blend; + } + + std::string MdsParser::parse_aniDisable() { + return this->expect_string(); + } + + MdsModelTag MdsParser::parse_modelTag() { + MdsModelTag tag {}; + + auto type = this->expect_string(); + if (!phoenix::iequals(type, "DEF_HIT_LIMB")) { + throw ScriptSyntaxError {_m_stream.format_location(), "expected a \"DEF_HIT_LIMB\""}; + } + + tag.bone = this->expect_string(); + return tag; + } +} // namespace zenkit diff --git a/src/ModelScriptDsl.hh b/src/ModelScriptDsl.hh index b670a3cd..1c440609 100644 --- a/src/ModelScriptDsl.hh +++ b/src/ModelScriptDsl.hh @@ -1,89 +1,80 @@ -// Copyright © 2023 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once +#include "phoenix/buffer.hh" +#include "phoenix/model_script.hh" +#include "phoenix/phoenix.hh" + #include #include -#include -#include -#include - #include #include #include -namespace phoenix::mds { - event_tag make_event_tag(int32_t, std::string&&, std::optional&&, std::optional&&, bool); -} - -namespace phoenix::parser { - enum class token { - keyword = 0, - integer = 1, - float_ = 2, - string = 3, - rbrace = 6, - lbrace = 7, - colon = 8, - eof = 9, - null = 10, +namespace zenkit { + enum class MdsToken { + KEYWORD = 0, + INTEGER = 1, + FLOAT = 2, + STRING = 3, + RBRACE = 6, + LBRACE = 7, + COLON = 8, + END_OF_FILE = 9, + NOTHING = 10, }; - class tokenizer { + class MdsTokenizer { public: - explicit tokenizer(buffer buf); + explicit MdsTokenizer(Read* buf); - token next(); + MdsToken next(); - void backtrack() { - this->_m_buffer.reset(); - } + void backtrack(); - [[nodiscard]] std::string const& token_value() const { - return _m_value; - } + [[nodiscard]] std::string const& token_value() const; - [[nodiscard]] bool eof() const { - return _m_buffer.remaining() == 0; - } + [[nodiscard]] bool eof() const; [[nodiscard]] std::string format_location() const; private: - buffer _m_buffer; + Read* _m_buffer; uint32_t _m_line {1}, _m_column {1}; std::string _m_value; + std::size_t _m_mark; }; - class parser { + class MdsParser { public: - explicit parser(buffer buf); + explicit MdsParser(Read* buf); - model_script parse_script(); - mds::skeleton parse_meshAndTree(); + ModelScript parse_script(ModelScript& script); + MdsSkeleton parse_meshAndTree(); std::string parse_registerMesh(); - void parse_aniEnum(model_script& into); - void parse_events(mds::animation& ani); + void parse_aniEnum(ModelScript& into); + void parse_events(MdsAnimation& ani); void ignore_block(); - mds::event_tag parse_eventTag(); - mds::event_sfx parse_eventSFX(); - mds::event_pfx parse_eventPFX(); - mds::event_sfx_ground parse_eventSFXGrnd(); - mds::event_pfx_stop parse_eventPFXStop(); - mds::event_morph_animate parse_eventMMStartAni(); - mds::event_camera_tremor parse_eventCamTremor(); - mds::animation parse_ani(); - mds::animation_combination parse_aniComb(); - mds::animation_alias parse_aniAlias(); - mds::animation_blending parse_aniBlend(); + MdsEventTag parse_eventTag(); + MdsSoundEffect parse_eventSFX(); + MdsParticleEffect parse_eventPFX(); + MdsSoundEffectGround parse_eventSFXGrnd(); + MdsParticleEffectStop parse_eventPFXStop(); + MdsMorphAnimation parse_eventMMStartAni(); + MdsCameraTremor parse_eventCamTremor(); + MdsAnimation parse_ani(); + MdsAnimationCombine parse_aniComb(); + MdsAnimationAlias parse_aniAlias(); + MdsAnimationBlend parse_aniBlend(); std::string parse_aniDisable(); - mds::model_tag parse_modelTag(); + MdsModelTag parse_modelTag(); private: [[nodiscard]] bool eof() const { return this->_m_stream.eof(); } - template + template void expect(); [[nodiscard]] std::string expect_string(); @@ -92,10 +83,10 @@ namespace phoenix::parser { void expect_keyword(std::string_view value); [[nodiscard]] float expect_number(); [[nodiscard]] int expect_int(); - [[nodiscard]] mds::animation_flags expect_flags(); - [[nodiscard]] std::optional maybe_flags(); + [[nodiscard]] AnimationFlags expect_flags(); + [[nodiscard]] std::optional maybe_flags(); - template + template bool maybe(); [[nodiscard]] std::optional maybe_int(); @@ -105,6 +96,6 @@ namespace phoenix::parser { [[nodiscard]] std::optional maybe_named(std::string_view name); private: - tokenizer _m_stream; + MdsTokenizer _m_stream; }; -} // namespace phoenix::parser +} // namespace phoenix diff --git a/src/MorphMesh.cc b/src/MorphMesh.cc index 447fc14a..12a05a71 100644 --- a/src/MorphMesh.cc +++ b/src/MorphMesh.cc @@ -1,78 +1,84 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/MorphMesh.hh" +#include "zenkit/Stream.hh" -namespace phoenix { - enum class morph_mesh_chunk { - unknown, - sources = 0xE010, - header = 0xE020, - animations = 0xE030, - proto = 0xB100, - morph = 0xB1FF +namespace zenkit { + enum class MorphMeshChunkType { + SOURCES = 0xE010, + HEADER = 0xE020, + ANIMATIONS = 0xE030, + PROTO = 0xB100, + MORPH = 0xB1FF }; - morph_mesh morph_mesh::parse(buffer& in) { - morph_mesh msh {}; - morph_mesh_chunk type = morph_mesh_chunk::unknown; + MorphMesh MorphMesh::parse(phoenix::buffer& in) { + MorphMesh msh {}; - do { - type = static_cast(in.get_ushort()); + auto r = Read::from(&in); + msh.load(r.get()); - auto length = in.get_uint(); - auto chunk = in.extract(length); + return msh; + } + + MorphMesh MorphMesh::parse(phoenix::buffer&& buf) { + return MorphMesh::parse(buf); + } + void MorphMesh::load(Read* r) { + proto::read_chunked(r, "MorphMesh", [this](Read* c, MorphMeshChunkType type) { switch (type) { - case morph_mesh_chunk::sources: { - auto count = chunk.get_ushort(); - msh.sources.resize(count); + case MorphMeshChunkType::SOURCES: { + auto count = c->read_ushort(); + this->sources.resize(count); for (int32_t i = 0; i < count; ++i) { - msh.sources[i].file_date = date::parse(chunk); - msh.sources[i].file_name = chunk.get_line(); + this->sources[i].file_date.load(c); + this->sources[i].file_name = c->read_line(true); } break; } - case morph_mesh_chunk::header: - /* version = */ (void) chunk.get_uint(); - msh.name = chunk.get_line(); + case MorphMeshChunkType::HEADER: + /* version = */ (void) c->read_uint(); + this->name = c->read_line(true); break; - case morph_mesh_chunk::proto: - msh.mesh = proto_mesh::parse_from_section(chunk); - msh.morph_positions.resize(msh.mesh.positions.size()); + case MorphMeshChunkType::PROTO: { + this->mesh.load_from_section(c); + this->morph_positions.resize(this->mesh.positions.size()); break; - case morph_mesh_chunk::morph: - for (std::uint32_t i = 0; i < msh.morph_positions.size(); ++i) { - msh.morph_positions[i] = chunk.get_vec3(); + } + case MorphMeshChunkType::MORPH: + for (std::uint32_t i = 0; i < this->morph_positions.size(); ++i) { + this->morph_positions[i] = c->read_vec3(); } break; - case morph_mesh_chunk::animations: { - auto animation_count = chunk.get_ushort(); - msh.animations.reserve(animation_count); + case MorphMeshChunkType::ANIMATIONS: { + auto animation_count = c->read_ushort(); + this->animations.reserve(animation_count); for (int32_t i = 0; i < animation_count; ++i) { - auto& anim = msh.animations.emplace_back(); - anim.name = chunk.get_line(false); - anim.blend_in = chunk.get_float(); - anim.blend_out = chunk.get_float(); - anim.duration = chunk.get_float(); - anim.layer = chunk.get_int(); - anim.speed = chunk.get_float(); - anim.flags = chunk.get(); + auto& anim = this->animations.emplace_back(); + anim.name = c->read_line(false); + anim.blend_in = c->read_float(); + anim.blend_out = c->read_float(); + anim.duration = c->read_float(); + anim.layer = c->read_int(); + anim.speed = c->read_float(); + anim.flags = c->read_ubyte(); - auto vertex_count = chunk.get_uint(); - anim.frame_count = chunk.get_uint(); + auto vertex_count = c->read_uint(); + anim.frame_count = c->read_uint(); anim.vertices.resize(vertex_count); anim.samples.resize(anim.frame_count * vertex_count); for (std::uint32_t j = 0; j < vertex_count; ++j) { - anim.vertices[j] = chunk.get_uint(); + anim.vertices[j] = c->read_uint(); } for (std::uint32_t j = 0; j < vertex_count * anim.frame_count; ++j) { - anim.samples[j] = chunk.get_vec3(); + anim.samples[j] = c->read_vec3(); } } break; @@ -81,15 +87,7 @@ namespace phoenix { break; } - if (chunk.remaining() != 0) { - PX_LOGW("morph_mesh: ", - chunk.remaining(), - " bytes remaining in section ", - std::hex, - std::uint16_t(type)); - } - } while (in.remaining() != 0); - - return msh; + return false; + }); } -} // namespace phoenix +} // namespace zenkit diff --git a/src/MultiResolutionMesh.cc b/src/MultiResolutionMesh.cc index 30597f06..504556e5 100644 --- a/src/MultiResolutionMesh.cc +++ b/src/MultiResolutionMesh.cc @@ -1,198 +1,190 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/MultiResolutionMesh.hh" +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" -namespace phoenix { +namespace zenkit { [[maybe_unused]] static constexpr auto version_g1 = 0x305; static constexpr auto version_g2 = 0x905; - enum class proto_chunk { unknown, mesh = 0xB100, end = 0xB1FF }; + enum class MrmChunkType { MESH = 0xB100, END = 0xB1FF }; - proto_mesh proto_mesh::parse(buffer& in) { - proto_mesh msh {}; - proto_chunk type = proto_chunk::unknown; - bool end_mesh = false; + MultiResolutionMesh MultiResolutionMesh::parse(phoenix::buffer& in) { + MultiResolutionMesh msh {}; - do { - type = static_cast(in.get_ushort()); + auto r = Read::from(&in); + msh.load(r.get()); - auto length = in.get_uint(); - auto chunk = in.extract(length); + return msh; + } + MultiResolutionMesh MultiResolutionMesh::parse(phoenix::buffer&& in) { + return MultiResolutionMesh::parse(in); + } + + void MultiResolutionMesh::load(zenkit::Read* r) { + proto::read_chunked(r, "MultiResolutionMesh", [this](Read* c, MrmChunkType type) { switch (type) { - case proto_chunk::mesh: - msh = parse_from_section(chunk); - break; - case proto_chunk::end: - end_mesh = true; + case MrmChunkType::MESH: + this->load_from_section(c); break; + case MrmChunkType::END: + return true; default: break; } - if (chunk.remaining() != 0) { - PX_LOGW("proto_mesh: ", - chunk.remaining(), - " bytes remaining in section ", - std::hex, - std::uint16_t(type)); - } - } while (!end_mesh); - - return msh; + return false; + }); } - proto_mesh proto_mesh::parse_from_section(buffer& chunk) { - proto_mesh msh {}; + void MultiResolutionMesh::load_from_section(zenkit::Read* r) { + auto version = r->read_ushort(); + auto content_size = r->read_uint(); + auto content_offset = r->tell(); - auto version = chunk.get_ushort(); - auto content_size = chunk.get_uint(); - auto content = chunk.extract(content_size); + r->seek(content_size, Whence::CUR); - auto submesh_count = chunk.get(); - auto vertices_index = chunk.get_uint(); - auto vertices_size = chunk.get_uint(); - auto normals_index = chunk.get_uint(); - auto normals_size = chunk.get_uint(); + auto submesh_count = r->read_byte(); + auto vertices_offset = r->read_uint() + content_offset; + auto vertices_size = r->read_uint(); + auto normals_offset = r->read_uint() + content_offset; + auto normals_size = r->read_uint(); - std::vector submesh_sections; + std::vector submesh_sections; submesh_sections.resize(submesh_count); for (int32_t i = 0; i < submesh_count; ++i) { submesh_sections[i] = { - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, - {chunk.get_uint(), chunk.get_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, + {r->read_uint() + content_offset, r->read_uint()}, }; } // read all materials - auto mats = archive_reader::open(chunk); - for (int32_t i = 0; i < submesh_count; ++i) { - msh.materials.emplace_back(material::parse(*mats)); + auto mats = ReadArchive::from(r); + this->materials.resize(submesh_count); + for (auto& material : this->materials) { + material.load(*mats); } if (version == version_g2) { - msh.alpha_test = chunk.get() != 0; + this->alpha_test = r->read_byte() != 0; } - msh.bbox = bounding_box::parse(chunk); - - // read positions and normals - msh.positions.resize(vertices_size); - auto vertices = content.slice(vertices_index, vertices_size * sizeof(float) * 3); + this->bbox.load(r); + this->obbox.load(r); - for (std::uint32_t i = 0; i < vertices_size; ++i) { - msh.positions[i] = vertices.get_vec3(); + // TODO: this might be a vec4 though the values don't make any sense. + r->seek(0x10, Whence::CUR); + auto end = r->tell(); + + // read positions + this->positions.resize(vertices_size); + r->seek((ssize_t) vertices_offset, Whence::BEG); + for (auto& pos : this->positions) { + pos = r->read_vec3(); } - msh.normals.resize(normals_size); - auto normals = content.slice(normals_index, normals_size * sizeof(float) * 3); - - for (std::uint32_t i = 0; i < normals_size; ++i) { - msh.normals[i] = normals.get_vec3(); + // read normals + this->normals.resize(normals_size); + r->seek((ssize_t) normals_offset, Whence::BEG); + for (auto& normal : this->normals) { + normal = r->read_vec3(); } // read submeshes - msh.sub_meshes.reserve(submesh_count); - + this->sub_meshes.resize(submesh_count); for (int32_t i = 0; i < submesh_count; ++i) { - auto& mesh = msh.sub_meshes.emplace_back(sub_mesh::parse(content, submesh_sections[i])); - mesh.mat = msh.materials[i]; + this->sub_meshes[i].load(r, submesh_sections[i]); + this->sub_meshes[i].mat = this->materials[i]; } - msh.obbox = obb::parse(chunk); - - // TODO: this might be a vec4 though the values don't make any sense. - chunk.skip(0x10); - return msh; + r->seek(static_cast(end), Whence::BEG); } - sub_mesh sub_mesh::parse(buffer& in, const sub_mesh_section& map) { - sub_mesh subm {}; - + void SubMesh::load(Read* r, SubMeshSection const& map) { // triangles - in.position(map.triangles.offset); - subm.triangles.resize(map.triangles.size); - + r->seek((ssize_t) map.triangles.offset, Whence::BEG); + this->triangles.resize(map.triangles.size); for (std::uint32_t i = 0; i < map.triangles.size; ++i) { - subm.triangles[i] = {{in.get_ushort(), in.get_ushort(), in.get_ushort()}}; + this->triangles[i] = {{r->read_ushort(), r->read_ushort(), r->read_ushort()}}; } // wedges - in.position(map.wedges.offset); - subm.wedges.resize(map.wedges.size); + r->seek((ssize_t) map.wedges.offset, Whence::BEG); + this->wedges.resize(map.wedges.size); for (std::uint32_t i = 0; i < map.wedges.size; ++i) { - subm.wedges[i] = {in.get_vec3(), in.get_vec2(), in.get_ushort()}; + this->wedges[i] = {r->read_vec3(), r->read_vec2(), r->read_ushort()}; // and this is why you don't just dump raw binary data - (void) in.get_ushort(); + (void) r->read_ushort(); } // colors - in.position(map.colors.offset); - subm.colors.resize(map.colors.size); + r->seek((ssize_t) map.colors.offset, Whence::BEG); + this->colors.resize(map.colors.size); for (std::uint32_t i = 0; i < map.colors.size; ++i) { - subm.colors[i] = in.get_float(); + this->colors[i] = r->read_float(); } // triangle_plane_indices - in.position(map.triangle_plane_indices.offset); - subm.triangle_plane_indices.resize(map.triangle_plane_indices.size); + r->seek((ssize_t) map.triangle_plane_indices.offset, Whence::BEG); + this->triangle_plane_indices.resize(map.triangle_plane_indices.size); for (std::uint32_t i = 0; i < map.triangle_plane_indices.size; ++i) { - subm.triangle_plane_indices[i] = in.get_ushort(); + this->triangle_plane_indices[i] = r->read_ushort(); } // triangle_planes - in.position(map.triangle_planes.offset); - subm.triangle_planes.resize(map.triangle_planes.size); + r->seek((ssize_t) map.triangle_planes.offset, Whence::BEG); + this->triangle_planes.resize(map.triangle_planes.size); for (std::uint32_t i = 0; i < map.triangle_planes.size; ++i) { - subm.triangle_planes[i] = {in.get_float(), in.get_vec3()}; + this->triangle_planes[i] = {r->read_float(), r->read_vec3()}; } // triangle_edges - in.position(map.triangle_edges.offset); - subm.triangle_edges.resize(map.triangle_edges.size); + r->seek((ssize_t) map.triangle_edges.offset, Whence::BEG); + this->triangle_edges.resize(map.triangle_edges.size); for (std::uint32_t i = 0; i < map.triangle_edges.size; ++i) { - subm.triangle_edges[i] = {{in.get_ushort(), in.get_ushort(), in.get_ushort()}}; + this->triangle_edges[i] = {{r->read_ushort(), r->read_ushort(), r->read_ushort()}}; } // edges - in.position(map.edges.offset); - subm.edges.resize(map.edges.size); + r->seek((ssize_t) map.edges.offset, Whence::BEG); + this->edges.resize(map.edges.size); for (std::uint32_t i = 0; i < map.edges.size; ++i) { - subm.edges[i] = {{in.get_ushort(), in.get_ushort()}}; + this->edges[i] = {{r->read_ushort(), r->read_ushort()}}; } // edge_scores - in.position(map.edge_scores.offset); - subm.edge_scores.resize(map.edge_scores.size); + r->seek((ssize_t) map.edge_scores.offset, Whence::BEG); + this->edge_scores.resize(map.edge_scores.size); for (std::uint32_t i = 0; i < map.edge_scores.size; ++i) { - subm.edge_scores[i] = in.get_float(); + this->edge_scores[i] = r->read_float(); } // wedge_map - in.position(map.wedge_map.offset); - subm.wedge_map.resize(map.wedge_map.size); + r->seek((ssize_t) map.wedge_map.offset, Whence::BEG); + this->wedge_map.resize(map.wedge_map.size); for (std::uint32_t i = 0; i < map.wedge_map.size; ++i) { - subm.wedge_map[i] = in.get_ushort(); + this->wedge_map[i] = r->read_ushort(); } - - return subm; } -} // namespace phoenix +} // namespace zenkit diff --git a/src/SaveGame.cc b/src/SaveGame.cc index 22ff3f1e..a2f50cf2 100644 --- a/src/SaveGame.cc +++ b/src/SaveGame.cc @@ -1,96 +1,103 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include -#include +#include "zenkit/SaveGame.hh" +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" + +#include "Internal.hh" -#include #include -namespace phoenix::unstable { - save_info save_info::parse(buffer&& buf) { - save_info info; - auto archive = archive_reader::open(buf); +namespace zenkit::unstable { + SaveInfo SaveInfo::parse(phoenix::buffer&& buf) { + SaveInfo info; + + auto read = Read::from(&buf); + info.load(read.get()); + + return info; + } + + void SaveInfo::load(Read* r) { + auto archive = ReadArchive::from(r); - archive_object hdr; + ArchiveObject hdr; if (!archive->read_object_begin(hdr) || hdr.class_name != "oCSavegameInfo") { - throw parser_error {"save_info", "expected oCSavegameInfo object not found"}; + throw zenkit::ParserError {"SaveInfo", "expected oCSavegameInfo object not found"}; } - info.title = archive->read_string(); // Title - info.world = archive->read_string(); // WorldName - info.time_day = archive->read_int(); // TimeDay - info.time_hour = archive->read_int(); // TimeHour - info.time_minute = archive->read_int(); // TimeMin - info.save_date = archive->read_string(); // SaveDate - info.version_major = archive->read_int(); // VersionMajor - info.version_minor = archive->read_int(); // VersionMinor - info.play_time_seconds = archive->read_int(); // PlayTimeSeconds + this->title = archive->read_string(); // Title + this->world = archive->read_string(); // WorldName + this->time_day = archive->read_int(); // TimeDay + this->time_hour = archive->read_int(); // TimeHour + this->time_minute = archive->read_int(); // TimeMin + this->save_date = archive->read_string(); // SaveDate + this->version_major = archive->read_int(); // VersionMajor + this->version_minor = archive->read_int(); // VersionMinor + this->play_time_seconds = archive->read_int(); // PlayTimeSeconds if (!archive->read_object_end()) { // Gothic II stores more information. - info.version_point = archive->read_int(); // VersionPoint - info.version_int = archive->read_int(); // VersionInt - info.version_app_name = archive->read_string(); // VersionAppName - } + this->version_point = archive->read_int(); // VersionPoint + this->version_int = archive->read_int(); // VersionInt + this->version_app_name = archive->read_string(); // VersionAppName - if (!archive->read_object_end()) { - PX_LOGW("save_info: ", hdr.class_name, " not fully parsed"); + if (!archive->read_object_end()) { + ZKLOGW("SaveInfo", "\"%s\" not fully parsed", hdr.class_name.c_str()); + } } - - return info; } - script_state script_state::parse(buffer&& buf, bool g2) { - script_state sav; - auto ar = archive_reader::open(buf); + void SaveScriptState::load(zenkit::Read* r, bool g2) { + auto ar = ReadArchive::from(r); - sav.day = ar->read_int(); // day - sav.hour = ar->read_int(); // hour - sav.minute = ar->read_int(); // min + this->day = ar->read_int(); // day + this->hour = ar->read_int(); // hour + this->minute = ar->read_int(); // min auto entry_count = ar->read_int(); // NumOfEntries - sav.infos.resize(entry_count); + this->infos.resize(entry_count); for (auto i = 0; i < entry_count; ++i) { - sav.infos[i].told = ar->read_bool(); // Told - sav.infos[i].name = ar->read_string(); // InstName + this->infos[i].told = ar->read_bool(); // Told + this->infos[i].name = ar->read_string(); // InstName } ar->read_int(); // NumOfEntries auto topic_count = ar->read_int(); // LOGMANAGERTOPICCOUNT - sav.log.resize(topic_count); + this->log.resize(topic_count); for (auto i = 0; i < topic_count; ++i) { - auto& topic = sav.log[i]; - topic.description = ar->read_string(); // TOPICDESCRIPTION - topic.section = static_cast(ar->read_enum()); // TOPICSECTION - topic.status = static_cast(ar->read_enum()); // TOPICSTATUS - topic.entries.resize(ar->read_int()); // LOGTOPICENTRYCOUNT + auto& topic = this->log[i]; + topic.description = ar->read_string(); // TOPICDESCRIPTION + topic.section = static_cast(ar->read_enum()); // TOPICSECTION + topic.status = static_cast(ar->read_enum()); // TOPICSTATUS + topic.entries.resize(ar->read_int()); // LOGTOPICENTRYCOUNT (void) ar->read_int(); // LOGMANAGERENTRYCOUNT - for (auto j = 0u; j < topic.entries.size(); ++j) { - topic.entries[j] = ar->read_string(); // ENTRYDESCRIPTION + for (auto& entrie : topic.entries) { + entrie = ar->read_string(); // ENTRYDESCRIPTION } } - archive_object obj; + ArchiveObject obj; if (!ar->read_object_begin(obj) || obj.class_name != "oCCSManager:zCCSManager") { - throw parser_error {"save_info", "expected oCCSManager:zCCSManager object not found"}; + throw zenkit::ParserError {"SaveScriptState", "expected oCCSManager:zCCSManager object not found"}; } ar->read_int(); // poolCount if (!ar->read_object_end()) { - PX_LOGE("save_game: ", obj.class_name, " not fully parsed"); + ZKLOGW("SaveScriptState", "\"%s\" not fully parsed", obj.class_name.c_str()); ar->skip_object(true); } auto symbol_count = ar->read_int(); // numSymbols - sav.symbols.resize(symbol_count); + this->symbols.resize(symbol_count); for (auto i = 0; i < symbol_count; ++i) { - symbol_state sym; + SaveSymbolState sym; sym.name = ar->read_string(); // symName0 // For Gothic II saves, there is additional data stored @@ -106,19 +113,13 @@ namespace phoenix::unstable { } // A two-dimensional array of the form int[42][42] containing guild attitudes - auto raw = ar->read_raw_bytes(42 * 42); // guildTable + auto raw = ar->read_raw(42 * 42); // guildTable - for (int i = 0; i < 42; ++i) { - for (int j = 0; j < 42; ++j) { - sav.guild_attitudes[i][j] = raw.get(); + for (auto& guild_attitude : this->guild_attitudes) { + for (unsigned char& j : guild_attitude) { + j = raw->read_ubyte(); } } - - if (buf.remaining() > 0) { - PX_LOGE("save_game: not fully parsed"); - } - - return sav; } std::optional find_file_matching(const std::set& choices, @@ -134,12 +135,35 @@ namespace phoenix::unstable { return *result; } - save_game save_game::parse(const std::filesystem::path& path) { - save_game sav; - sav._m_root_path = path; + SaveGame SaveGame::parse(const std::filesystem::path& path) { + SaveGame sav; + sav.load(path); + return sav; + } + + std::optional SaveGame::open_world_save(std::string_view world_name) const { + auto path = _m_root_path / world_name; + path.replace_extension("SAV"); + + if (!std::filesystem::exists(path)) + return std::nullopt; + return phoenix::buffer::mmap(path); + } + + std::optional> SaveGame::open_world(std::string_view world_name) const { + auto path = _m_root_path / world_name; + path.replace_extension("SAV"); + + if (!std::filesystem::exists(path)) + return std::nullopt; + return Read::from(path); + } + + void SaveGame::load(std::filesystem::path const& path) { + this->_m_root_path = path; if (!std::filesystem::is_directory(path)) { - throw parser_error {"save_game", "save game path does not exist or is not a directory"}; + throw zenkit::ParserError {"SaveGame", "save game path does not exist or is not a directory"}; } std::set entries {}; @@ -149,47 +173,41 @@ namespace phoenix::unstable { // Load SAVEINFO.SAV { - PX_LOGI("save_game: loading SAVEINFO.SAV"); + ZKLOGI("SaveGame", "Loading SAVEINFO.SAV"); auto file_save_info = find_file_matching(entries, "SAVEINFO.SAV"); if (!file_save_info) { - throw parser_error {"save_game", - "expected SAVEINFO.SAV not found. this is probably not a Gothic savegame"}; + throw zenkit::ParserError {"SaveGame", + "expected SAVEINFO.SAV not found. this is probably not a Gothic savegame"}; } - sav.metadata = save_info::parse(buffer::mmap(*file_save_info)); - sav.current_world = sav.metadata.world + ".ZEN"; + auto r = Read::from(*file_save_info); + this->metadata.load(r.get()); + this->current_world = this->metadata.world + ".ZEN"; } // Load THUMB.SAV { - PX_LOGI("save_game: loading THUMB.SAV"); + ZKLOGI("SaveGame", "Loading THUMB.SAV"); auto file_thumb = find_file_matching(entries, "THUMB.SAV"); if (file_thumb) { - sav.thumbnail = texture::parse(buffer::mmap(*file_thumb)); + auto r = Read::from(*file_thumb); + + this->thumbnail.emplace(); + this->thumbnail->load(r.get()); } } // Load SAVEDAT.SAV { - PX_LOGI("save_game: loading SAVEDAT.SAV"); + ZKLOGI("SaveGame", "Loading SAVEDAT.SAV"); auto file_save_dat = find_file_matching(entries, "SAVEDAT.SAV"); if (!file_save_dat) { - throw parser_error {"save_game", - "expected SAVEDAT.SAV not found. this is probably not a Gothic savegame"}; + throw zenkit::ParserError {"SaveGame", + "expected SAVEDAT.SAV not found. this is probably not a Gothic savegame"}; } - sav.script = script_state::parse(buffer::mmap(*file_save_dat), !sav.metadata.version_app_name.empty()); + auto r = Read::from(*file_save_dat); + this->script.load(r.get(), !this->metadata.version_app_name.empty()); } - - return sav; - } - - std::optional save_game::open_world_save(std::string_view world_name) const { - auto path = _m_root_path / world_name; - path.replace_extension("SAV"); - - if (!std::filesystem::exists(path)) - return std::nullopt; - return buffer::mmap(path); } -} // namespace phoenix::unstable +} // namespace zenkit::unstable diff --git a/src/SoftSkinMesh.cc b/src/SoftSkinMesh.cc index c9f11a72..085078a1 100644 --- a/src/SoftSkinMesh.cc +++ b/src/SoftSkinMesh.cc @@ -1,91 +1,91 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/SoftSkinMesh.hh" +#include "zenkit/Stream.hh" -namespace phoenix { - enum class softmesh_chunk { unknown, header = 0xE100, end = 0xE110, proto = 0xB100, nodes = 0xB1FF }; +#include "Internal.hh" - softskin_mesh softskin_mesh::parse(buffer& in) { - softskin_mesh msh {}; - softmesh_chunk type = softmesh_chunk::unknown; - bool end_mesh = false; +namespace zenkit { + enum class SoftSkinMeshChunkType { + HEADER = 0xE100, + END = 0xE110, + PROTO = 0xB100, + NODES = 0xB1FF, + }; - do { - type = static_cast(in.get_ushort()); + SoftSkinMesh SoftSkinMesh::parse(phoenix::buffer& in) { + SoftSkinMesh msh {}; - auto length = in.get_uint(); - auto chunk = in.extract(length); + auto r = Read::from(&in); + msh.load(r.get()); + return msh; + } + + void SoftSkinMesh::load(Read* r) { + proto::read_chunked(r, "SoftSkinMesh", [this](Read* c, SoftSkinMeshChunkType type) { switch (type) { - case softmesh_chunk::header: - (void) /* version = */ chunk.get_uint(); + case SoftSkinMeshChunkType::HEADER: + (void) /* version = */ c->read_uint(); break; - case softmesh_chunk::proto: - msh.mesh = proto_mesh::parse_from_section(chunk); + case SoftSkinMeshChunkType::PROTO: + mesh.load_from_section(c); break; - case softmesh_chunk::nodes: { + case SoftSkinMeshChunkType::NODES: { // weights - auto weight_buffer_size = chunk.get_uint(); - auto weight_buffer_end = chunk.position() + weight_buffer_size; + auto weight_buffer_size = c->read_uint(); + auto weight_buffer_end = c->tell() + weight_buffer_size; - msh.weights.resize(msh.mesh.positions.size()); - for (uint32_t i = 0; i < msh.mesh.positions.size(); ++i) { - auto count = chunk.get_uint(); - msh.weights[i].resize(count); + this->weights.resize(this->mesh.positions.size()); + for (uint32_t i = 0; i < this->mesh.positions.size(); ++i) { + auto count = c->read_uint(); + this->weights[i].resize(count); for (std::uint32_t j = 0; j < count; ++j) { - auto& weight = msh.weights[i][j]; - weight.weight = chunk.get_float(); - weight.position = chunk.get_vec3(); - weight.node_index = chunk.get(); + auto& weight = this->weights[i][j]; + weight.weight = c->read_float(); + weight.position = c->read_vec3(); + weight.node_index = c->read_ubyte(); } } - if (chunk.position() != weight_buffer_end) { - PX_LOGW("softskin_mesh: ", - weight_buffer_end - chunk.position(), - " bytes remaining in weight section"); - chunk.position(weight_buffer_end); + if (c->tell() != weight_buffer_end) { + ZKLOGW("SoftSkinMesh", "%zu bytes remaining in weight section", weight_buffer_end - c->tell()); + c->seek(static_cast(weight_buffer_end), Whence::BEG); } // wedge normals - msh.wedge_normals.resize(chunk.get_uint()); - - for (std::uint32_t i = 0; i < msh.wedge_normals.size(); ++i) { - auto& normal = msh.wedge_normals[i]; - normal.normal = chunk.get_vec3(); - normal.index = chunk.get_uint(); + this->wedge_normals.resize(c->read_uint()); + for (auto& normal : this->wedge_normals) { + normal.normal = c->read_vec3(); + normal.index = c->read_uint(); } // nodes - msh.nodes.resize(chunk.get_ushort()); - - for (std::uint32_t i = 0; i < msh.nodes.size(); ++i) { - msh.nodes[i] = chunk.get_int(); + this->nodes.resize(c->read_ushort()); + for (auto& node : this->nodes) { + node = c->read_int(); } - for (std::uint32_t i = 0; i < msh.nodes.size(); ++i) { - msh.bboxes.push_back(obb::parse(chunk)); + // bounding boxes + this->bboxes.resize(this->nodes.size()); + for (auto& bbox : this->bboxes) { + bbox.load(c); } break; } - case softmesh_chunk::end: - end_mesh = true; - break; + case SoftSkinMeshChunkType::END: + return true; default: break; } - if (chunk.remaining() != 0) { - PX_LOGW("softskin_mesh: ", - chunk.remaining(), - " bytes remaining in section ", - std::hex, - std::uint16_t(type)); - } - } while (!end_mesh); + return false; + }); + } - return msh; + SoftSkinMesh SoftSkinMesh::parse(phoenix::buffer&& in) { + return SoftSkinMesh::parse(in); } -} // namespace phoenix +} // namespace zenkit diff --git a/src/Stream.cc b/src/Stream.cc new file mode 100644 index 00000000..1f361b68 --- /dev/null +++ b/src/Stream.cc @@ -0,0 +1,500 @@ +// Copyright © 2022-2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#include "zenkit/Stream.hh" +#include "phoenix/buffer.hh" + +#include +#include + +#include + +namespace zenkit { + template + ZKINT inline T read_any(Read* r) noexcept { + T v {}; + r->read(&v, sizeof v); + return v; + } + + template + ZKINT inline void write_any(Write* r, T const& v) noexcept { + r->write(&v, sizeof v); + } + + char Read::read_char() noexcept { + return read_any(this); + } + + int8_t Read::read_byte() noexcept { + return read_any(this); + } + + uint8_t Read::read_ubyte() noexcept { + return read_any(this); + } + + int16_t Read::read_short() noexcept { + return read_any(this); + } + + uint16_t Read::read_ushort() noexcept { + return read_any(this); + } + + int32_t Read::read_int() noexcept { + return read_any(this); + } + + uint32_t Read::read_uint() noexcept { + return read_any(this); + } + + float Read::read_float() noexcept { + return read_any(this); + } + + glm::vec2 Read::read_vec2() noexcept { + glm::vec2 v {}; + this->read(glm::value_ptr(v), sizeof(float) * 2); + return v; + } + + glm::vec3 Read::read_vec3() noexcept { + glm::vec3 v {}; + this->read(glm::value_ptr(v), sizeof(float) * 3); + return v; + } + + glm::mat3 Read::read_mat3() noexcept { + glm::mat3 v {}; + this->read(glm::value_ptr(v), sizeof(float) * 9); + return glm::transpose(v); + } + + glm::mat4 Read::read_mat4() noexcept { + glm::mat4 v {}; + this->read(glm::value_ptr(v), sizeof(float) * 16); + return glm::transpose(v); + } + + std::string Read::read_string(size_t len) noexcept { + std::string str(len, '\0'); + this->read(str.data(), len); + return str; + } + + std::string Read::read_line(bool skipws) noexcept { + return read_line_then_ignore(skipws ? " \t\r\n\v\f" : ""); + } + + std::string Read::read_line_then_ignore(std::string_view chars) noexcept { + std::string str {}; + + char c; + while (c = this->read_char(), c != '\0' && c != '\r' && c != '\n') { + str.push_back(c); + } + + // Don't ignore the given chars if we're at the end of a C-style string. + if (chars.empty() || c == '\0' || this->eof()) + return str; + + do { + c = this->read_char(); + } while (c != '\0' && chars.find(c) != std::string_view::npos); + + // If we're at EOF and read a null-byte, don't backtrack. + if (this->eof() && c == '\0') + return str; + + // Backtrack by one byte since we have definitely consumed one too many. + this->seek(-1, Whence::CUR); + return str; + } + + void Write::write_char(char v) noexcept { + write_any(this, v); + } + + void Write::write_byte(int8_t v) noexcept { + write_any(this, v); + } + + void Write::write_ubyte(uint8_t v) noexcept { + write_any(this, v); + } + + void Write::write_short(int16_t v) noexcept { + write_any(this, v); + } + + void Write::write_ushort(uint16_t v) noexcept { + write_any(this, v); + } + + void Write::write_int(int32_t v) noexcept { + write_any(this, v); + } + + void Write::write_uint(uint32_t v) noexcept { + write_any(this, v); + } + + void Write::write_float(float v) noexcept { + write_any(this, v); + } + + void Write::write_string(std::string_view v) noexcept { + this->write(v.data(), v.length()); + } + + void Write::write_string0(std::string_view v) noexcept { + this->write(v.data(), v.length()); + this->write_char('\0'); + } + + void Write::write_line(std::string_view v) noexcept { + this->write_string(v); + this->write_char('\n'); + } + + void Write::write_vec2(glm::vec2 const& v) noexcept { + this->write(glm::value_ptr(v), 2 * sizeof(float)); + } + + void Write::write_vec3(glm::vec3 const& v) noexcept { + this->write(glm::value_ptr(v), 3 * sizeof(float)); + } + + void Write::write_mat3(glm::mat3 const& v) noexcept { + auto vT = glm::transpose(v); + this->write(glm::value_ptr(vT), 9 * sizeof(float)); + } + + void Write::write_mat4(glm::mat4 const& v) noexcept { + auto vT = glm::transpose(v); + this->write(glm::value_ptr(vT), 16 * sizeof(float)); + } + + namespace detail { + static int INTO_C_WHENCE[] = { + SEEK_SET, + SEEK_CUR, + SEEK_END, + }; + + static std::ios::seekdir INTO_CXX_WHENCE[] = { + std::ios::beg, + std::ios::cur, + std::ios::end, + }; + + ZKINT inline size_t seek_internal(size_t pos, size_t end, ssize_t off, Whence whence) { + auto new_position = static_cast(pos); + + switch (whence) { + case Whence::BEG: + new_position = off; + break; + case Whence::CUR: + new_position = new_position + off; + break; + case Whence::END: + new_position = static_cast(end) + off; + break; + } + + return static_cast(new_position); + } + + class ReadFile ZKINT : public Read { + public: + explicit ReadFile(::FILE* stream) : _m_stream(stream) {} + + size_t read(void* buf, size_t len) noexcept override { + return ::fread(buf, 1, len, _m_stream); + } + + void seek(ssize_t off, Whence whence) noexcept override { + ::fseek(_m_stream, off, INTO_C_WHENCE[static_cast(whence)]); + } + + [[nodiscard]] size_t tell() const noexcept override { + return static_cast(::ftell(_m_stream)); + } + + [[nodiscard]] bool eof() const noexcept override { + return ::feof(_m_stream); + } + + private: + ::FILE* _m_stream; + }; + + class ReadStream ZKINT : public Read { + public: + explicit ReadStream(std::istream* stream) : _m_stream(stream) {} + + size_t read(void* buf, size_t len) noexcept override { + _m_stream->read(static_cast(buf), static_cast(len)); + return _m_stream->gcount(); + } + + void seek(ssize_t off, Whence whence) noexcept override { + _m_stream->seekg(off, INTO_CXX_WHENCE[static_cast(whence)]); + } + + [[nodiscard]] size_t tell() const noexcept override { + return static_cast(_m_stream->tellg()); + } + + [[nodiscard]] bool eof() const noexcept override { + return _m_stream->eof(); + } + + private: + std::istream* _m_stream; + }; + + class ReadMemory ZKINT : public Read { + public: + ReadMemory(std::byte const* byte, size_t len) : _m_bytes(byte), _m_length(len) {} + + size_t read(void* buf, size_t len) noexcept override { + len = _m_position + len > _m_length ? _m_length - _m_position : len; + ::memcpy(buf, _m_bytes + _m_position, len); + _m_position += len; + return len; + } + + void seek(ssize_t off, Whence whence) noexcept override { + auto new_position = seek_internal(_m_position, _m_length, off, whence); + + if (new_position > _m_length) + return; + + _m_position = static_cast(new_position); + } + + [[nodiscard]] size_t tell() const noexcept override { + return _m_position; + } + + [[nodiscard]] bool eof() const noexcept override { + return _m_position >= _m_length; + } + + private: + std::byte const* _m_bytes; + size_t _m_length, _m_position {0}; + }; + + class ZKREM("Deprecated") ReadBuffer ZKINT : public Read { + public: + explicit ReadBuffer(phoenix::buffer* buf) : _m_buffer(buf) {} + + size_t read(void* buf, size_t len) noexcept override { + try { + _m_buffer->get(static_cast(buf), len); + return len; + } catch (phoenix::buffer_error const&) { + return 0; + } + } + + void seek(ssize_t off, Whence whence) noexcept override { + try { + switch (whence) { + case Whence::BEG: + _m_buffer->position(off); + break; + case Whence::CUR: + _m_buffer->skip(off); + break; + case Whence::END: + _m_buffer->position(_m_buffer->limit() + off); + break; + } + } catch (phoenix::buffer_error const&) {} + } + + [[nodiscard]] size_t tell() const noexcept override { + return _m_buffer->position(); + } + + [[nodiscard]] bool eof() const noexcept override { + return _m_buffer->position() >= _m_buffer->limit(); + } + + private: + phoenix::buffer* _m_buffer; + }; + + class ReadVector ZKINT : public ReadMemory { + public: + explicit ReadVector(std::vector vec) + : ReadMemory(vec.data(), vec.size()), _m_vector(std::move(vec)) {} + + private: + std::vector _m_vector; + }; + + class ReadMmap ZKINT : public ReadMemory { + public: + explicit ReadMmap(std::filesystem::path const& path) : ReadMmap(decltype(_m_mmap)(path.c_str())) {} + + explicit ReadMmap(mio::basic_mmap mmap) + : ReadMemory(mmap.data(), mmap.size()), _m_mmap(std::move(mmap)) {} + + private: + mio::basic_mmap _m_mmap; + }; + + class WriteFile ZKINT : public Write { + public: + explicit WriteFile(::FILE* stream) : _m_stream(stream) {} + + size_t write(void const* buf, size_t len) noexcept override { + return ::fwrite(buf, 1, len, _m_stream); + } + + void seek(ssize_t off, Whence whence) noexcept override { + ::fseek(_m_stream, off, INTO_C_WHENCE[static_cast(whence)]); + } + + [[nodiscard]] size_t tell() const noexcept override { + return static_cast(::ftell(_m_stream)); + } + + private: + ::FILE* _m_stream; + }; + + class WriteStream ZKINT : public Write { + public: + explicit WriteStream(std::ostream* stream) : _m_stream(stream) {} + + size_t write(void const* buf, size_t len) noexcept override { + _m_stream->write(static_cast(buf), static_cast(len)); + return len; + } + + void seek(ssize_t off, Whence whence) noexcept override { + _m_stream->seekp(off, INTO_CXX_WHENCE[static_cast(whence)]); + } + + [[nodiscard]] size_t tell() const noexcept override { + return static_cast(_m_stream->tellp()); + } + + private: + std::ostream* _m_stream; + }; + + class WriteStatic ZKINT : public Write { + public: + explicit WriteStatic(std::byte* buf, size_t len) : _m_bytes(buf), _m_length(len) {} + + size_t write(void const* buf, size_t len) noexcept override { + len = _m_position + len > _m_length ? _m_length - _m_position : len; + ::memcpy(_m_bytes + _m_position, buf, len); + _m_position += len; + return len; + } + + void seek(ssize_t off, Whence whence) noexcept override { + auto new_position = seek_internal(_m_position, _m_length, off, whence); + + if (new_position > _m_length) + return; + _m_position = static_cast(new_position); + } + + [[nodiscard]] size_t tell() const noexcept override { + return _m_position; + } + + private: + std::byte* _m_bytes; + size_t _m_length, _m_position {0}; + }; + + class WriteDynamic ZKINT : public Write { + public: + explicit WriteDynamic(std::vector* vec) : _m_vector(vec) {} + + size_t write(void const* buf, size_t len) noexcept override { + auto const* cbuf = static_cast(buf); + + if (_m_position + len > _m_vector->capacity()) { + this->_m_vector->reserve( + len + this->_m_vector->capacity() < 1024 ? 1024 : this->_m_vector->capacity() * 2); + } + + if (_m_position + len > _m_vector->size()) { + _m_vector->resize(_m_position + len); + } + + std::copy_n(cbuf, len, _m_vector->data() + _m_position); + _m_position += len; + return len; + } + + void seek(ssize_t off, Whence whence) noexcept override { + _m_position = seek_internal(_m_position, _m_vector->size(), off, whence); + } + + [[nodiscard]] size_t tell() const noexcept override { + return _m_position; + } + + private: + std::vector* _m_vector; + size_t _m_position {0}; + }; + } // namespace detail + + std::unique_ptr Read::from(::FILE* stream) { + return std::make_unique(stream); + } + + std::unique_ptr Read::from(std::istream* stream) { + return std::make_unique(stream); + } + + std::unique_ptr Read::from(std::byte const* bytes, size_t len) { + return std::make_unique(bytes, len); + } + + std::unique_ptr Read::from(std::vector const* vector) { + return std::make_unique(vector->data(), vector->size()); + } + + std::unique_ptr Read::from(std::vector vector) { + return std::make_unique(std::move(vector)); + } + + std::unique_ptr Read::from(std::filesystem::path const& path) { + return std::make_unique(path); + } + + std::unique_ptr Read::from(phoenix::buffer* buf) { + return std::make_unique(buf); + } + + std::unique_ptr Write::to(::FILE* stream) { + return std::make_unique(stream); + } + + std::unique_ptr Write::to(std::ostream* stream) { + return std::make_unique(stream); + } + + std::unique_ptr Write::to(std::byte* bytes, size_t len) { + return std::make_unique(bytes, len); + } + + std::unique_ptr Write::to(std::vector* vector) { + return std::make_unique(vector); + } +} // namespace zenkit diff --git a/src/Texture.cc b/src/Texture.cc index eba8c740..7f163b1b 100644 --- a/src/Texture.cc +++ b/src/Texture.cc @@ -1,13 +1,18 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/Texture.hh" +#include "zenkit/Stream.hh" -#include +#include "phoenix/phoenix.hh" + +#include "squish.h" + +namespace zenkit { + constexpr const std::string_view ZTEX_SIGNATURE = "ZTEX"; -namespace phoenix { /// \brief Calculates the size in bytes of a texture at the given mipmap level. /// \return The size in bytes of a texture at the given mipmap level. - std::uint32_t _ztex_mipmap_size(texture_format format, std::uint32_t width, std::uint32_t height, uint32_t level) { + std::uint32_t _ztex_mipmap_size(TextureFormat format, std::uint32_t width, std::uint32_t height, uint32_t level) { std::uint32_t x = std::max(1u, width); std::uint32_t y = std::max(1u, height); @@ -19,73 +24,82 @@ namespace phoenix { } switch (format) { - case tex_B8G8R8A8: - case tex_R8G8B8A8: - case tex_A8B8G8R8: - case tex_A8R8G8B8: + case B8G8R8A8: + case R8G8B8A8: + case A8B8G8R8: + case A8R8G8B8: return x * y * 4; - case tex_B8G8R8: - case tex_R8G8B8: + case B8G8R8: + case R8G8B8: return x * y * 3; - case tex_A4R4G4B4: - case tex_A1R5G5B5: - case tex_R5G6B5: + case A4R4G4B4: + case A1R5G5B5: + case R5G6B5: return x * y * 2; - case tex_p8: + case P8: return x * y; - case tex_dxt1: + case DXT1: return std::max(1u, x / 4) * std::max(1u, y / 4) * 8; - case tex_dxt2: - case tex_dxt3: - case tex_dxt4: - case tex_dxt5: + case DXT2: + case DXT3: + case DXT4: + case DXT5: return std::max(1u, x / 4) * std::max(1u, y / 4) * 16; default: return 0; } } - texture texture::parse(buffer& in) { - texture tex; + Texture Texture::parse(phoenix::buffer& in) { + Texture tex; + + auto r = Read::from(&in); + tex.load(r.get()); + + return tex; + } + + Texture Texture::parse(phoenix::buffer&& in) { + return Texture::parse(in); + } - if (in.get_string(4) != ZTEX_SIGNATURE) { - throw parser_error {"texture", "invalid signature"}; + void Texture::load(Read* r) { + if (r->read_string(4) != ZTEX_SIGNATURE) { + throw zenkit::ParserError {"texture", "invalid signature"}; } - auto version = in.get_uint(); + auto version = r->read_uint(); if (version != 0) { - throw parser_error {"texture", "invalid version"}; + throw zenkit::ParserError {"texture", "invalid version"}; } - tex._m_format = static_cast(in.get_uint()); - tex._m_width = in.get_uint(); - tex._m_height = in.get_uint(); - tex._m_mipmap_count = std::max(1u, in.get_uint()); - tex._m_reference_width = in.get_uint(); - tex._m_reference_height = in.get_uint(); - tex._m_average_color = in.get_uint(); - - if (tex._m_format == tex_p8) { - for (auto& i : tex._m_palette) { - i.b = in.get(); - i.g = in.get(); - i.r = in.get(); - i.a = in.get(); + this->_m_format = static_cast(r->read_uint()); + this->_m_width = r->read_uint(); + this->_m_height = r->read_uint(); + this->_m_mipmap_count = std::max(1u, r->read_uint()); + this->_m_reference_width = r->read_uint(); + this->_m_reference_height = r->read_uint(); + this->_m_average_color = r->read_uint(); + + if (this->_m_format == P8) { + for (auto& i : this->_m_palette) { + i.b = r->read_ubyte(); + i.g = r->read_ubyte(); + i.r = r->read_ubyte(); + i.a = r->read_ubyte(); } } // Lowest mipmap-level first - for (std::int64_t level = tex._m_mipmap_count - 1; level >= 0; --level) { - auto size = _ztex_mipmap_size(tex._m_format, tex._m_width, tex._m_height, level); + for (std::int64_t level = this->_m_mipmap_count - 1; level >= 0; --level) { + auto size = _ztex_mipmap_size(this->_m_format, this->_m_width, this->_m_height, level); std::vector mipmap; mipmap.resize(size); - in.get(mipmap.data(), size); + r->read(mipmap.data(), size); - tex._m_textures.emplace_back(std::move(mipmap)); + this->_m_textures.emplace_back(std::move(mipmap)); } - - return tex; } #pragma pack(push, 1) @@ -104,24 +118,24 @@ namespace phoenix { static_assert(sizeof(r5g5b5) == 2); - std::vector texture::as_rgba8(std::uint32_t mipmap_level) const { + std::vector Texture::as_rgba8(std::uint32_t mipmap_level) const { std::vector conv; switch (_m_format) { - case tex_dxt1: - case tex_dxt3: - case tex_dxt5: { + case DXT1: + case DXT3: + case DXT5: { const auto& map = data(mipmap_level); auto w = mipmap_width(mipmap_level); auto h = mipmap_height(mipmap_level); conv.resize(w * h * 4); - auto flag = _m_format == tex_dxt1 ? squish::kDxt1 : (_m_format == tex_dxt3 ? squish::kDxt3 : squish::kDxt5); + auto flag = _m_format == DXT1 ? squish::kDxt1 : (_m_format == DXT3 ? squish::kDxt3 : squish::kDxt5); squish::DecompressImage(conv.data(), static_cast(w), static_cast(h), map.data(), flag); return conv; } - case tex_B8G8R8A8: { + case B8G8R8A8: { const auto& map = data(mipmap_level); conv.resize(map.size()); @@ -134,9 +148,9 @@ namespace phoenix { return conv; } - case tex_R8G8B8A8: + case R8G8B8A8: return data(mipmap_level); - case tex_A8B8G8R8: { + case A8B8G8R8: { const auto& map = data(mipmap_level); conv.resize(map.size()); @@ -149,7 +163,7 @@ namespace phoenix { return conv; } - case tex_A8R8G8B8: { + case A8R8G8B8: { const auto& map = data(mipmap_level); conv.resize(map.size()); @@ -162,7 +176,7 @@ namespace phoenix { return conv; } - case tex_B8G8R8: { + case B8G8R8: { const auto& map = data(mipmap_level); conv.resize(map.size()); @@ -175,7 +189,7 @@ namespace phoenix { return conv; } - case tex_R8G8B8: { + case R8G8B8: { const auto& map = data(mipmap_level); conv.resize(map.size()); @@ -188,7 +202,7 @@ namespace phoenix { return conv; } - case tex_R5G6B5: { + case R5G6B5: { const auto& map = data(mipmap_level); conv.resize(map.size() * 2); @@ -203,7 +217,7 @@ namespace phoenix { return conv; } - case tex_p8: { + case P8: { const auto& map = data(mipmap_level); conv.resize(map.size() * sizeof(std::uint8_t) * 4); @@ -218,8 +232,9 @@ namespace phoenix { return conv; } default: - throw parser_error {"texture", - "cannot convert format to rgba: " + std::to_string(static_cast(_m_format))}; + throw zenkit::ParserError {"texture", + "cannot convert format to rgba: " + + std::to_string(static_cast(_m_format))}; } } -} // namespace phoenix +} // namespace zenkit diff --git a/src/Vfs.cc b/src/Vfs.cc index de7177aa..5bb8e578 100644 --- a/src/Vfs.cc +++ b/src/Vfs.cc @@ -1,46 +1,89 @@ -// Copyright © 2023 Luis Michaelis +// Copyright © 2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include "phoenix/Vfs.hh" -#include "model_script_dsl.hh" -#include "phoenix/ext/daedalus_classes.hh" -#include "phoenix/phoenix.hh" +#include "zenkit/Vfs.hh" +#include "zenkit/Error.hh" +#include "zenkit/Stream.hh" + +#include "phoenix/buffer.hh" + +#include "Internal.hh" #include #include +#include #include -#include #include -namespace phoenix { +namespace zenkit { static constexpr std::string_view VFS_DISK_SIGNATURE_G1 = "PSVDSC_V2.00\r\n\r\n"; static constexpr std::string_view VFS_DISK_SIGNATURE_G2 = "PSVDSC_V2.00\n\r\n\r"; + class ZKINT RawBufferBacking : public phoenix::buffer_backing { + public: + RawBufferBacking(std::byte const* bytes, uint64_t length) : _m_buffer(bytes), _m_size(length) {} + + [[nodiscard]] bool direct() const noexcept override { + return false; + } + + [[nodiscard]] bool readonly() const noexcept override { + return false; + } + + [[nodiscard]] uint64_t size() const noexcept override { + return _m_size; + } + + [[nodiscard]] const std::byte* array() const override { + return _m_buffer; + } + + void read(std::byte* buf, std::uint64_t size, std::uint64_t offset) const override { + if (this->readonly()) { + throw phoenix::buffer_readonly {}; + } + + if (offset + size > this->size()) { + throw phoenix::buffer_overflow {offset, size, "in backing"}; + } + + std::copy_n(_m_buffer + static_cast(offset), size, buf); + } + + private: + std::byte const* _m_buffer; + uint64_t _m_size; + }; + VfsBrokenDiskError::VfsBrokenDiskError(const std::string& signature) - : error("VFS disk signature not recognized: \"" + signature + "\"") {} + : Error("VFS disk signature not recognized: \"" + signature + "\"") {} - VfsFileExistsError::VfsFileExistsError(const std::string& name) : error("file exists: \"" + name + "\"") {} + VfsFileExistsError::VfsFileExistsError(const std::string& name) : Error("file exists: \"" + name + "\"") {} - VfsNotFoundError::VfsNotFoundError(const std::string& name) : error("not found: \"" + name + "\"") {} + VfsNotFoundError::VfsNotFoundError(const std::string& name) : Error("not found: \"" + name + "\"") {} bool VfsNodeComparator::operator()(const VfsNode& a, const VfsNode& b) const noexcept { - return icompare(a.name(), b.name()); + return phoenix::icompare(a.name(), b.name()); } bool VfsNodeComparator::operator()(const VfsNode& a, std::string_view b) const noexcept { - return icompare(a.name(), b); + return phoenix::icompare(a.name(), b); } bool VfsNodeComparator::operator()(std::string_view a, const VfsNode& b) const noexcept { - return icompare(a, b.name()); + return phoenix::icompare(a, b.name()); } - VfsNode::VfsNode(std::string_view name, time_t ts) : _m_name(name), _m_time(ts), _m_data(std::vector {}) {} + VfsNode::VfsNode(std::string_view name, time_t ts) : _m_name(name), _m_time(ts), _m_data(ChildContainer {}) {} - VfsNode::VfsNode(std::string_view name, buffer dev, time_t ts) + VfsNode::VfsNode(std::string_view name, phoenix::buffer dev, time_t ts) : _m_name(name), _m_time(ts), _m_data(std::move(dev)) {} - std::vector const& VfsNode::children() const { - return std::get>(_m_data); + VfsNode::VfsNode(std::string_view name, VfsFileDescriptor dev, time_t ts) + : _m_name(name), _m_time(ts), _m_data(dev) {} + + VfsNode::ChildContainer const& VfsNode::children() const { + return std::get(_m_data); } std::string_view trim_trailing_whitespace(std::string_view s) { @@ -52,66 +95,90 @@ namespace phoenix { } VfsNode const* VfsNode::child(std::string_view name) const { - auto& children = std::get>(_m_data); + auto& children = std::get(_m_data); name = trim_trailing_whitespace(name); - auto it = std::lower_bound(children.begin(), children.end(), name, VfsNodeComparator {}); - if (it == children.end() || !iequals(it->name(), name)) + auto it = children.find(name); + if (it == children.end() || !phoenix::iequals(it->name(), name)) return nullptr; return &*it; } VfsNode* VfsNode::child(std::string_view name) { - auto& children = std::get>(_m_data); + auto& children = std::get(_m_data); name = trim_trailing_whitespace(name); - auto it = std::lower_bound(children.begin(), children.end(), name, VfsNodeComparator {}); - if (it == children.end() || !iequals(it->name(), name)) + auto it = children.find(name); + if (it == children.end() || !phoenix::iequals(it->name(), name)) return nullptr; - return &*it; + return const_cast(&*it); } VfsNode* VfsNode::create(VfsNode node) { - auto& children = std::get>(_m_data); - auto it = children.insert(std::lower_bound(children.begin(), children.end(), node, VfsNodeComparator {}), - std::move(node)); - return &*it; + auto& children = std::get(_m_data); + auto it = children.insert(node); + return const_cast(&*it.first); } bool VfsNode::remove(std::string_view name) { - auto& children = std::get>(_m_data); + auto& children = std::get(_m_data); name = trim_trailing_whitespace(name); - auto it = std::lower_bound(children.begin(), children.end(), name, VfsNodeComparator {}); - if (it == children.end() || !iequals(it->name(), name)) + auto it = children.find(name); + if (it == children.end() || !phoenix::iequals(it->name(), name)) return false; children.erase(it); return true; } - buffer VfsNode::open() const { - return std::get(_m_data).duplicate(); + phoenix::buffer VfsNode::open() const { + if (std::holds_alternative(_m_data)) { + return std::get(_m_data).duplicate(); + } + + auto fd = std::get(_m_data); + return phoenix::buffer {std::make_shared(fd.memory, fd.size)}; + } + + std::unique_ptr VfsNode::open_read() const { + if (std::holds_alternative(_m_data)) { + auto buf = std::get(_m_data); + return Read::from(buf.array(), buf.limit()); + } + + auto fd = std::get(_m_data); + return Read::from(fd.memory, fd.size); } VfsNode VfsNode::directory(std::string_view name) { return directory(name, -1); } - VfsNode VfsNode::file(std::string_view name, buffer dev) { + VfsNode VfsNode::file(std::string_view name, phoenix::buffer dev) { return file(name, std::move(dev), -1); } + VfsNode VfsNode::file(std::string_view name, VfsFileDescriptor dev) { + return file(name, dev, -1); + } + VfsNode VfsNode::directory(std::string_view name, std::time_t ts) { return VfsNode(name, ts); } - VfsNode VfsNode::file(std::string_view name, buffer dev, time_t ts) { + VfsNode VfsNode::file(std::string_view name, phoenix::buffer dev, time_t ts) { return VfsNode(name, std::move(dev), ts); } + VfsNode VfsNode::file(std::string_view name, VfsFileDescriptor dev, std::time_t ts) { + return VfsNode(name, dev, ts); + } + VfsNodeType VfsNode::type() const noexcept { - return std::holds_alternative(_m_data) ? VfsNodeType::FILE : VfsNodeType::DIRECTORY; + return (std::holds_alternative(_m_data), std::holds_alternative(_m_data)) + ? VfsNodeType::FILE + : VfsNodeType::DIRECTORY; } std::time_t VfsNode::time() const noexcept { @@ -178,7 +245,8 @@ namespace phoenix { } void Vfs::mount_disk(const std::filesystem::path& host, VfsOverwriteBehavior overwrite) { - this->mount_disk(buffer::mmap(host), overwrite); + auto& mem = _m_data_mapped.emplace_back(host.c_str()); + this->mount_disk(mem.data(), mem.size(), overwrite); } static std::time_t vfs_dos_to_unix_time(std::uint32_t dos) noexcept { @@ -194,111 +262,23 @@ namespace phoenix { return mktime(&t); } - void Vfs::mount_disk(buffer buf, VfsOverwriteBehavior overwrite) { - auto comment = buf.get_string(256); - auto signature = buf.get_string(16); - [[maybe_unused]] auto entry_count = buf.get_uint(); - [[maybe_unused]] auto file_count = buf.get_uint(); - auto timestamp = vfs_dos_to_unix_time(buf.get_uint()); - [[maybe_unused]] auto _size = buf.get_uint(); - auto catalog_offset = buf.get_uint(); - [[maybe_unused]] auto version = buf.get_uint(); - - if (signature != VFS_DISK_SIGNATURE_G1 && signature != VFS_DISK_SIGNATURE_G2) { - throw VfsBrokenDiskError {signature}; - } - - if (auto it = comment.find('\x1A'); it != std::string::npos) { - comment.resize(it); - } - - std::function load_entry = - [&load_entry, overwrite, catalog_offset, timestamp, &buf](VfsNode* parent) { - auto name = buf.get_string(64); - auto offset = buf.get_uint(); - auto size = buf.get_uint(); - auto type = buf.get_uint(); - [[maybe_unused]] auto attributes = buf.get_uint(); - - // Find the first non-space char from the end (refer #77) - auto it = std::find_if(name.rbegin(), name.rend(), [](char c) { - return !std::isspace(static_cast(c)); - }); - - if (it != name.rend()) { - auto n = name.rend() - it; - name.resize(n); - } - - VfsNode* existing = parent->child(name); - bool dir = (type & 0x80000000) != 0; - bool last = (type & 0x40000000) != 0; - - if (dir) { - if (existing == nullptr) { - existing = parent->create(VfsNode::directory(name, timestamp)); - } else if (existing->type() != VfsNodeType::DIRECTORY) { - switch (overwrite) { - case VfsOverwriteBehavior::NONE: - return last; - case VfsOverwriteBehavior::NEWER: - if (existing->time() <= timestamp) { - return last; - } - break; - case VfsOverwriteBehavior::OLDER: - if (existing->time() >= timestamp) { - return last; - } - break; - case VfsOverwriteBehavior::ALL: - break; - } - - parent->remove(name); - existing = parent->create(VfsNode::directory(name, timestamp)); - } - - auto self_offset = buf.position(); - buf.position(catalog_offset + offset * 80); - while (!load_entry(existing)) - ; - buf.position(self_offset); - } else { - if (offset + size > buf.limit()) { - PX_LOGE("unable to mount file ", name, ": invalid offset and size"); - return last; - } - - if (existing != nullptr) { - switch (overwrite) { - case VfsOverwriteBehavior::NONE: - return last; - case VfsOverwriteBehavior::NEWER: - if (existing->time() <= timestamp) { - return last; - } - break; - case VfsOverwriteBehavior::OLDER: - if (existing->time() >= timestamp) { - return last; - } - break; - case VfsOverwriteBehavior::ALL: - break; - } - - parent->remove(name); - } + void Vfs::mount_disk(phoenix::buffer buf, VfsOverwriteBehavior overwrite) { + auto mem = std::make_unique(buf.limit()); + ::memcpy(mem.get(), buf.array(), buf.limit()); + _m_data.push_back(std::move(mem)); + this->mount_disk(mem.get(), buf.limit(), overwrite); + } - (void) parent->create(VfsNode::file(name, buf.slice(offset, size), timestamp)); - } + void Vfs::mount_disk(Read* buf, VfsOverwriteBehavior overwrite) { + buf->seek(0, Whence::END); + auto size = buf->tell(); + buf->seek(0, Whence::BEG); - return last; - }; + auto mem = std::make_unique(size); + buf->read(mem.get(), size); - while (!load_entry(&_m_root)) - ; + _m_data.push_back(std::move(mem)); + this->mount_disk(mem.get(), size, overwrite); } VfsNode const& Vfs::root() const noexcept { @@ -406,7 +386,7 @@ namespace phoenix { auto root = VfsNode::directory(sourcePath.filename().string()); std::function load_directory = - [&load_directory](VfsNode* parent, std::filesystem::path const& host) { + [this, &load_directory](VfsNode* parent, std::filesystem::path const& host) { for (auto const& ref : std::filesystem::directory_iterator(host)) { auto const& path = ref.path(); auto time = @@ -416,7 +396,10 @@ namespace phoenix { VfsNode* newP = parent->create(VfsNode::directory(path.filename().string(), time.count())); load_directory(newP, path); } else if (ref.file_size() > 0) { - parent->create(VfsNode::file(path.filename().string(), buffer::mmap(path), time.count())); + auto& mem = this->_m_data_mapped.emplace_back(path.c_str()); + parent->create(VfsNode::file(path.filename().string(), + VfsFileDescriptor {mem.data(), mem.size()}, + time.count())); } } }; @@ -428,4 +411,112 @@ namespace phoenix { } } -} // namespace phoenix + void Vfs::mount_disk(std::byte const* buf, std::size_t size, VfsOverwriteBehavior overwrite) { + auto r = Read::from(buf, size); + + auto comment = r->read_string(256); + auto signature = r->read_string(16); + [[maybe_unused]] auto entry_count = r->read_uint(); + [[maybe_unused]] auto file_count = r->read_uint(); + auto timestamp = vfs_dos_to_unix_time(r->read_uint()); + [[maybe_unused]] auto _size = r->read_uint(); + auto catalog_offset = r->read_uint(); + [[maybe_unused]] auto version = r->read_uint(); + + if (signature != VFS_DISK_SIGNATURE_G1 && signature != VFS_DISK_SIGNATURE_G2) { + throw VfsBrokenDiskError {signature}; + } + + if (auto it = comment.find('\x1A'); it != std::string::npos) { + comment.resize(it); + } + + std::function load_entry = + [&load_entry, overwrite, catalog_offset, timestamp, &r, buf, size](VfsNode* parent) { + auto e_name = r->read_string(64); + auto e_offset = r->read_uint(); + auto e_size = r->read_uint(); + auto e_type = r->read_uint(); + [[maybe_unused]] auto attributes = r->read_uint(); + + // Find the first non-space char from the end (refer #77) + auto it = std::find_if(e_name.rbegin(), e_name.rend(), [](char c) { + return !std::isspace(static_cast(c)); + }); + + if (it != e_name.rend()) { + auto n = e_name.rend() - it; + e_name.resize(n); + } + + VfsNode* existing = parent->child(e_name); + bool dir = (e_type & 0x80000000) != 0; + bool last = (e_type & 0x40000000) != 0; + + if (dir) { + if (existing == nullptr) { + existing = parent->create(VfsNode::directory(e_name, timestamp)); + } else if (existing->type() != VfsNodeType::DIRECTORY) { + switch (overwrite) { + case VfsOverwriteBehavior::NONE: + return last; + case VfsOverwriteBehavior::NEWER: + if (existing->time() <= timestamp) { + return last; + } + break; + case VfsOverwriteBehavior::OLDER: + if (existing->time() >= timestamp) { + return last; + } + break; + case VfsOverwriteBehavior::ALL: + break; + } + + parent->remove(e_name); + existing = parent->create(VfsNode::directory(e_name, timestamp)); + } + + auto self_offset = r->tell(); + r->seek(catalog_offset + e_offset * 80, Whence::BEG); + while (!load_entry(existing)) + ; + r->seek(static_cast(self_offset), Whence::BEG); + } else { + if (e_offset + e_size > size) { + ZKLOGE("Vfs", "Unable to mount file \"%s\": invalid offset and size", e_name.c_str()); + return last; + } + + if (existing != nullptr) { + switch (overwrite) { + case VfsOverwriteBehavior::NONE: + return last; + case VfsOverwriteBehavior::NEWER: + if (existing->time() <= timestamp) { + return last; + } + break; + case VfsOverwriteBehavior::OLDER: + if (existing->time() >= timestamp) { + return last; + } + break; + case VfsOverwriteBehavior::ALL: + break; + } + + parent->remove(e_name); + } + + (void) parent->create(VfsNode::file(e_name, VfsFileDescriptor {buf + e_offset, e_size}, timestamp)); + } + + return last; + }; + + while (!load_entry(&_m_root)) + ; + } +} // namespace zenkit diff --git a/src/World.cc b/src/World.cc index 91fc629c..1649511e 100644 --- a/src/World.cc +++ b/src/World.cc @@ -1,10 +1,13 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include -#include -#include +#include "zenkit/World.hh" +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" +#include "zenkit/vobs/Misc.hh" -namespace phoenix { +#include "Internal.hh" + +namespace zenkit { [[maybe_unused]] static constexpr uint32_t BSP_VERSION_G1 = 0x2090000; static constexpr uint32_t BSP_VERSION_G2 = 0x4090000; @@ -16,204 +19,227 @@ namespace phoenix { /// /// \param buf A buffer containing the world's data. /// \return The game version associated with that world. - game_version determine_world_version(buffer&& buf) { - auto archive = archive_reader::open(buf); + static GameVersion determine_world_version(Read* buf) { + auto archive = ReadArchive::from(buf); if (archive->is_save_game()) { - throw phoenix::parser_error {"world", "cannot automatically detect world version for save-games!s"}; + throw zenkit::ParserError {"World", "cannot automatically detect world version for save-games!s"}; } - archive_object chnk {}; + ArchiveObject chnk {}; archive->read_object_begin(chnk); while (!archive->read_object_end()) { archive->read_object_begin(chnk); if (chnk.object_name == "MeshAndBsp") { - auto bsp_version = buf.get_uint(); - - if (bsp_version == BSP_VERSION_G2) { - return game_version::gothic_2; - } else { - return game_version::gothic_1; - } + auto bsp_version = buf->read_uint(); + return bsp_version == BSP_VERSION_G2 ? GameVersion::GOTHIC_2 : GameVersion::GOTHIC_1; } archive->skip_object(true); } - PX_LOGE("world: failed to determine world version. Assuming Gothic 1."); - return game_version::gothic_1; + ZKLOGE("World", "Failed to determine world version. Assuming Gothic 1."); + return GameVersion::GOTHIC_1; + } + + World World::parse(phoenix::buffer& buf, GameVersion version) { + World wld {}; + + auto r = Read::from(&buf); + wld.load(r.get(), version); + + return wld; + } + + World World::parse(phoenix::buffer&& buf, GameVersion version) { + return World::parse(buf, version); + } + + World World::parse(phoenix::buffer& buf) { + World wld {}; + + auto r = Read::from(&buf); + wld.load(r.get()); + + return wld; } - world world::parse(buffer& in, game_version version) { - try { - world wld; + World World::parse(phoenix::buffer&& buf) { + return World::parse(buf); + } + + void World::load(Read* r) { + auto begin = r->tell(); + auto version = determine_world_version(r); + r->seek(static_cast(begin), Whence::BEG); + this->load(r, version); + } + + void World::load(zenkit::Read* r, zenkit::GameVersion version) { + auto archive = ReadArchive::from(r); - auto archive = archive_reader::open(in); + ArchiveObject chnk {}; + archive->read_object_begin(chnk); + + if (chnk.class_name != "oCWorld:zCWorld") { + throw zenkit::ParserError {"World", "'oCWorld:zCWorld' chunk expected, got '" + chnk.class_name + "'"}; + } - archive_object chnk {}; + while (!archive->read_object_end()) { archive->read_object_begin(chnk); + ZKLOGI("World", + "Parsing object [%s %s %u %u]", + chnk.object_name.c_str(), + chnk.class_name.c_str(), + chnk.version, + chnk.index); - if (chnk.class_name != "oCWorld:zCWorld") { - throw parser_error {"world", "'oCWorld:zCWorld' chunk expected, got '" + chnk.class_name + "'"}; - } + if (chnk.object_name == "MeshAndBsp") { + auto bsp_version = r->read_uint(); + (void) /* size = */ r->read_uint(); - while (!archive->read_object_end()) { - archive->read_object_begin(chnk); - PX_LOGI("world: parsing object [", - chnk.object_name, - " ", - chnk.class_name, - " ", - chnk.version, - " ", - chnk.index, - "]"); - - if (chnk.object_name == "MeshAndBsp") { - auto bsp_version = in.get_uint(); - (void) /* size = */ in.get_uint(); - - std::uint16_t chunk_type = 0; - auto mesh_data = in.slice(); - - do { - chunk_type = in.get_ushort(); - in.skip(in.get_uint()); - } while (chunk_type != 0xB060); - - auto is_xzen = archive->get_header().user == "XZEN"; - if (is_xzen) { - PX_LOGI("world: XZEN world detected, forcing wide vertex indices"); - } + std::uint16_t chunk_type = 0; + auto mesh_offset = r->tell(); - wld.world_bsp_tree = bsp_tree::parse(in, bsp_version); - wld.world_mesh = mesh::parse(mesh_data, wld.world_bsp_tree.leaf_polygons, is_xzen); - } else if (chnk.object_name == "VobTree") { - auto count = archive->read_int(); - wld.world_vobs.reserve(count); - - for (int32_t i = 0; i < count; ++i) { - auto child = parse_vob_tree(*archive, version); - if (child == nullptr) - continue; - wld.world_vobs.push_back(std::move(child)); - } - } else if (chnk.object_name == "WayNet") { - wld.world_way_net = way_net::parse(*archive); - } else if (chnk.object_name == "CutscenePlayer") { - // TODO: only present in save-games - - if (!archive->read_object_begin(chnk)) { - PX_LOGW("world: object [", - chnk.object_name, - " ", - chnk.class_name, - " ", - chnk.version, - " ", - chnk.index, - "] encountered but unable to parse"); - archive->skip_object(true); - continue; - } + do { + chunk_type = r->read_ushort(); + r->seek(r->read_uint(), Whence::CUR); + } while (chunk_type != 0xB060); - (void) archive->read_int(); // lastProcessDay - (void) archive->read_int(); // lastProcessHour - (void) archive->read_int(); // playListCount - - archive->read_object_end(); - } else if (chnk.object_name == "SkyCtrl") { - // TODO: only present in save-games - - if (!archive->read_object_begin(chnk)) { - PX_LOGW("world: object [", - chnk.object_name, - " ", - chnk.class_name, - " ", - chnk.version, - " ", - chnk.index, - "] encountered but unable to parse"); - archive->skip_object(true); - continue; - } + auto is_xzen = archive->get_header().user == "XZEN"; + if (is_xzen) { + ZKLOGI("World", "XZEN world detected, forcing wide vertex indices"); + } - (void) archive->read_float(); // masterTime - (void) archive->read_float(); // rainWeight - (void) archive->read_float(); // rainStart - (void) archive->read_float(); // rainStop - (void) archive->read_float(); // rainSctTimer - (void) archive->read_float(); // rainSndVol - (void) archive->read_float(); // dayCtr - - if (version == game_version::gothic_2) { - (void) archive->read_float(); // fadeScale - (void) archive->read_bool(); // renderLightning - (void) archive->read_bool(); // isRaining - (void) archive->read_int(); // rainCtr - } + this->world_bsp_tree.load(r, bsp_version); + auto end = r->tell(); - archive->read_object_end(); - } else if (chnk.object_name == "EndMarker" && archive->get_header().save) { - // TODO: save games contain a list of NPCs after the end marker - // First, Consume the end-maker fully - archive->read_object_end(); - - // Then, read all the NPCs - auto npc_count = archive->read_int(); // npcCount - for (auto i = 0; i < npc_count; ++i) { - // FIXME: npc::parse(npcs[i], *archive, version) - archive->skip_object(false); - } + r->seek(static_cast(mesh_offset), Whence::BEG); + this->world_mesh.load(r, this->world_bsp_tree.leaf_polygons, is_xzen); - // After that, read all NPC spawn locations - auto npc_spawn_count = archive->read_int(); // NoOfEntries - for (auto i = 0; i < npc_spawn_count; ++i) { - archive->skip_object(false); // npc zReference - (void) archive->read_vec3(); // spawnPos - (void) archive->read_float(); // timer - } + r->seek(static_cast(end), Whence::BEG); + } else if (chnk.object_name == "VobTree") { + auto count = archive->read_int(); + this->world_vobs.reserve(count); + + for (int32_t i = 0; i < count; ++i) { + auto child = parse_vob_tree(*archive, version); + if (child == nullptr) + continue; + this->world_vobs.push_back(std::move(child)); + } + } else if (chnk.object_name == "WayNet") { + this->world_way_net.load(*archive); + } else if (chnk.object_name == "CutscenePlayer") { + // TODO: only present in save-games + + if (!archive->read_object_begin(chnk)) { + ZKLOGW("World", + "Object [%s %s %u %u] encountered but unable to parse", + chnk.object_name.c_str(), + chnk.class_name.c_str(), + chnk.version, + chnk.index); + archive->skip_object(true); + continue; + } - (void) archive->read_bool(); // spawningEnabled + (void) archive->read_int(); // lastProcessDay + (void) archive->read_int(); // lastProcessHour + (void) archive->read_int(); // playListCount + + archive->read_object_end(); + } else if (chnk.object_name == "SkyCtrl") { + // TODO: only present in save-games + + if (!archive->read_object_begin(chnk)) { + ZKLOGW("World", + "Object [%s %s %u %u] encountered but unable to parse", + chnk.object_name.c_str(), + chnk.class_name.c_str(), + chnk.version, + chnk.index); + archive->skip_object(true); + continue; + } - if (version == game_version::gothic_2) { - (void) archive->read_int(); // spawnFlags + (void) archive->read_float(); // masterTime + (void) archive->read_float(); // rainWeight + (void) archive->read_float(); // rainStart + (void) archive->read_float(); // rainStop + (void) archive->read_float(); // rainSctTimer + (void) archive->read_float(); // rainSndVol + (void) archive->read_float(); // dayCtr + + if (version == GameVersion::GOTHIC_2) { + (void) archive->read_float(); // fadeScale + (void) archive->read_bool(); // renderLightning + (void) archive->read_bool(); // isRaining + (void) archive->read_int(); // rainCtr + } + + archive->read_object_end(); + } else if (chnk.object_name == "EndMarker" && archive->get_header().save) { + // TODO: save games contain a list of NPCs after the end marker + // First, Consume the end-maker fully + archive->read_object_end(); + + // Then, read all the NPCs + auto npc_count = archive->read_int(); // npcCount + for (auto i = 0; i < npc_count; ++i) { + archive->read_object_begin(chnk); + + if (chnk.class_name != "\xA7") { + vobs::Npc npc {}; + npc.load(*archive, version); + } else { + ZKLOGE("World", + "Cannot load NPC reference [%s %s %d %d]", + chnk.object_name.c_str(), + chnk.class_name.c_str(), + chnk.version, + chnk.index); } if (!archive->read_object_end()) { - PX_LOGW("world: npc list not fully parsed"); archive->skip_object(true); } + } - // We have fully consumed the world block. From here we should just die. - break; + // After that, read all NPC spawn locations + auto npc_spawn_count = archive->read_int(); // NoOfEntries + for (auto i = 0; i < npc_spawn_count; ++i) { + archive->skip_object(false); // npc zReference + (void) archive->read_vec3(); // spawnPos + (void) archive->read_float(); // timer + } + + (void) archive->read_bool(); // spawningEnabled + + if (version == GameVersion::GOTHIC_2) { + (void) archive->read_int(); // spawnFlags } if (!archive->read_object_end()) { - PX_LOGW("world: object [", - chnk.object_name, - " ", - chnk.class_name, - " ", - chnk.version, - " ", - chnk.index, - "] not fully parsed"); + ZKLOGW("World", "Npc-list not fully parsed"); archive->skip_object(true); } + + // We have fully consumed the world block. From here we should just die. + break; } - return wld; - } catch (const buffer_error& exc) { - throw parser_error {"world", exc, "eof reached"}; + if (!archive->read_object_end()) { + ZKLOGW("World", + "Object [%s %s %u %u] not fully parsed", + chnk.object_name.c_str(), + chnk.class_name.c_str(), + chnk.version, + chnk.index); + archive->skip_object(true); + } } } - - world world::parse(buffer& buf) { - auto version = determine_world_version(buf.duplicate()); - return world::parse(buf, version); - } -} // namespace phoenix +} // namespace zenkit diff --git a/src/addon/daedalus.cc b/src/addon/daedalus.cc index 261f889e..96e9cfd6 100644 --- a/src/addon/daedalus.cc +++ b/src/addon/daedalus.cc @@ -1,6 +1,8 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/addon/daedalus.hh" + +#include "../Internal.hh" #define IF_SYM_EXIST(sym, d) \ do { \ @@ -9,30 +11,866 @@ } \ } while (false) -namespace phoenix { - void register_all_script_classes(script& s) { - IF_SYM_EXIST("C_GILVALUES", c_gil_values::register_(s)); - IF_SYM_EXIST("C_NPC", c_npc::register_(s)); - IF_SYM_EXIST("C_MISSION", c_mission::register_(s)); - IF_SYM_EXIST("C_ITEM", c_item::register_(s)); - IF_SYM_EXIST("C_FOCUS", c_focus::register_(s)); - IF_SYM_EXIST("C_INFO", c_info::register_(s)); - IF_SYM_EXIST("C_ITEMREACT", c_item_react::register_(s)); - IF_SYM_EXIST("C_SPELL", c_spell::register_(s)); - IF_SYM_EXIST("C_SVM", c_svm::register_(s)); - IF_SYM_EXIST("C_MENU", c_menu::register_(s)); - IF_SYM_EXIST("C_MENU_ITEM", c_menu_item::register_(s)); - IF_SYM_EXIST("CCAMSYS", c_camera::register_(s)); - IF_SYM_EXIST("C_MUSICSYS_CFG", c_music_system::register_(s)); - IF_SYM_EXIST("C_MUSICTHEME", c_music_theme::register_(s)); - IF_SYM_EXIST("C_MUSICJINGLE", c_music_jingle::register_(s)); - IF_SYM_EXIST("C_PARTICLEFX", c_particle_fx::register_(s)); - IF_SYM_EXIST("CFX_BASE", c_fx_base::register_(s)); - IF_SYM_EXIST("C_PARTICLEFXEMITKEY", c_particle_fx_emit_key::register_(s)); - IF_SYM_EXIST("C_FIGHTAI", c_fight_ai::register_(s)); - IF_SYM_EXIST("C_SFX", c_sfx::register_(s)); - IF_SYM_EXIST("C_SNDSYS_CFG", c_sound_system::register_(s)); +namespace zenkit { + void register_all_script_classes(DaedalusScript& s) { + IF_SYM_EXIST("C_GILVALUES", IGuildValues::register_(s)); + IF_SYM_EXIST("C_NPC", INpc::register_(s)); + IF_SYM_EXIST("C_MISSION", IMission::register_(s)); + IF_SYM_EXIST("C_ITEM", IItem::register_(s)); + IF_SYM_EXIST("C_FOCUS", IFocus::register_(s)); + IF_SYM_EXIST("C_INFO", IInfo::register_(s)); + IF_SYM_EXIST("C_ITEMREACT", IItemReact::register_(s)); + IF_SYM_EXIST("C_SPELL", ISpell::register_(s)); + IF_SYM_EXIST("C_SVM", ISvm::register_(s)); + IF_SYM_EXIST("C_MENU", IMenu::register_(s)); + IF_SYM_EXIST("C_MENU_ITEM", IMenuItem::register_(s)); + IF_SYM_EXIST("CCAMSYS", ICamera::register_(s)); + IF_SYM_EXIST("C_MUSICSYS_CFG", IMusicSystem::register_(s)); + IF_SYM_EXIST("C_MUSICTHEME", IMusicTheme::register_(s)); + IF_SYM_EXIST("C_MUSICJINGLE", IMusicJingle::register_(s)); + IF_SYM_EXIST("C_PARTICLEFX", IParticleEffect::register_(s)); + IF_SYM_EXIST("CFX_BASE", IEffectBase::register_(s)); + IF_SYM_EXIST("C_PARTICLEFXEMITKEY", IParticleEffectEmitKey::register_(s)); + IF_SYM_EXIST("C_FIGHTAI", IFightAi::register_(s)); + IF_SYM_EXIST("C_SFX", ISoundEffect::register_(s)); + IF_SYM_EXIST("C_SNDSYS_CFG", ISoundSystem::register_(s)); } -} // namespace phoenix +} // namespace zenkit #undef IF_SYM_EXIST + +#define ZKLOG_CLASS(a, b) ZKLOGD("Daedalus", "Registering script class \"%s\" as zenkit::%s", a, b) + +void zenkit::IGuildValues::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_GILVALUES", "IGuildValues"); + s.register_member("C_GILVALUES.WATER_DEPTH_KNEE", &IGuildValues::water_depth_knee); + s.register_member("C_GILVALUES.WATER_DEPTH_CHEST", &IGuildValues::water_depth_chest); + s.register_member("C_GILVALUES.JUMPUP_HEIGHT", &IGuildValues::jumpup_height); + s.register_member("C_GILVALUES.SWIM_TIME", &IGuildValues::swim_time); + s.register_member("C_GILVALUES.DIVE_TIME", &IGuildValues::dive_time); + s.register_member("C_GILVALUES.STEP_HEIGHT", &IGuildValues::step_height); + s.register_member("C_GILVALUES.JUMPLOW_HEIGHT", &IGuildValues::jumplow_height); + s.register_member("C_GILVALUES.JUMPMID_HEIGHT", &IGuildValues::jumpmid_height); + s.register_member("C_GILVALUES.SLIDE_ANGLE", &IGuildValues::slide_angle); + s.register_member("C_GILVALUES.SLIDE_ANGLE2", &IGuildValues::slide_angle2); + s.register_member("C_GILVALUES.DISABLE_AUTOROLL", &IGuildValues::disable_autoroll); + s.register_member("C_GILVALUES.SURFACE_ALIGN", &IGuildValues::surface_align); + s.register_member("C_GILVALUES.CLIMB_HEADING_ANGLE", &IGuildValues::climb_heading_angle); + s.register_member("C_GILVALUES.CLIMB_HORIZ_ANGLE", &IGuildValues::climb_horiz_angle); + s.register_member("C_GILVALUES.CLIMB_GROUND_ANGLE", &IGuildValues::climb_ground_angle); + s.register_member("C_GILVALUES.FIGHT_RANGE_BASE", &IGuildValues::fight_range_base); + s.register_member("C_GILVALUES.FIGHT_RANGE_FIST", &IGuildValues::fight_range_fist); + s.register_member("C_GILVALUES.FIGHT_RANGE_1HS", &IGuildValues::fight_range_1hs); + s.register_member("C_GILVALUES.FIGHT_RANGE_1HA", &IGuildValues::fight_range_1ha); + s.register_member("C_GILVALUES.FIGHT_RANGE_2HS", &IGuildValues::fight_range_2hs); + s.register_member("C_GILVALUES.FIGHT_RANGE_2HA", &IGuildValues::fight_range_2ha); + s.register_member("C_GILVALUES.FALLDOWN_HEIGHT", &IGuildValues::falldown_height); + s.register_member("C_GILVALUES.FALLDOWN_DAMAGE", &IGuildValues::falldown_damage); + s.register_member("C_GILVALUES.BLOOD_DISABLED", &IGuildValues::blood_disabled); + s.register_member("C_GILVALUES.BLOOD_MAX_DISTANCE", &IGuildValues::blood_max_distance); + s.register_member("C_GILVALUES.BLOOD_AMOUNT", &IGuildValues::blood_amount); + s.register_member("C_GILVALUES.BLOOD_FLOW", &IGuildValues::blood_flow); + s.register_member("C_GILVALUES.BLOOD_EMITTER", &IGuildValues::blood_emitter); + s.register_member("C_GILVALUES.BLOOD_TEXTURE", &IGuildValues::blood_texture); + s.register_member("C_GILVALUES.TURN_SPEED", &IGuildValues::turn_speed); + + // Gothic 2 only + if (s.find_symbol_by_name("C_GILVALUES.FIGHT_RANGE_G") != nullptr) { + s.register_member("C_GILVALUES.FIGHT_RANGE_G", &IGuildValues::fight_range_g); + } +} + +void zenkit::IFightAi::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_FIGHTAI", "IFightAi"); + s.register_member("C_FIGHTAI.MOVE", &IFightAi::move); +} + +void zenkit::ISoundEffect::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_SFX", "ISoundEffect"); + s.register_member("C_SFX.FILE", &ISoundEffect::file); + s.register_member("C_SFX.PITCHOFF", &ISoundEffect::pitch_off); + s.register_member("C_SFX.PITCHVAR", &ISoundEffect::pitch_var); + s.register_member("C_SFX.VOL", &ISoundEffect::vol); + s.register_member("C_SFX.LOOP", &ISoundEffect::loop); + s.register_member("C_SFX.LOOPSTARTOFFSET", &ISoundEffect::loop_start_offset); + s.register_member("C_SFX.LOOPENDOFFSET", &ISoundEffect::loop_end_offset); + s.register_member("C_SFX.REVERBLEVEL", &ISoundEffect::reverb_level); + s.register_member("C_SFX.PFXNAME", &ISoundEffect::pfx_name); +} + +void zenkit::ISoundSystem::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_SNDSYS_CFG", "ISoundSystem"); + s.register_member("C_SNDSYS_CFG.VOLUME", &ISoundSystem::volume); + s.register_member("C_SNDSYS_CFG.BITRESOLUTION", &ISoundSystem::bit_resolution); + s.register_member("C_SNDSYS_CFG.SAMPLERATE", &ISoundSystem::sample_rate); + s.register_member("C_SNDSYS_CFG.USESTEREO", &ISoundSystem::use_stereo); + s.register_member("C_SNDSYS_CFG.NUMSFXCHANNELS", &ISoundSystem::num_sfx_channels); + s.register_member("C_SNDSYS_CFG.USED3DPROVIDERNAME", &ISoundSystem::used_3d_provider_name); +} + +void zenkit::IParticleEffectEmitKey::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_PARTICLEFXEMITKEY", "IParticleEffectEmitKey"); + s.register_member("C_PARTICLEFXEMITKEY.VISNAME_S", &IParticleEffectEmitKey::vis_name_s); + s.register_member("C_PARTICLEFXEMITKEY.VISSIZESCALE", &IParticleEffectEmitKey::vis_size_scale); + s.register_member("C_PARTICLEFXEMITKEY.SCALEDURATION", &IParticleEffectEmitKey::scale_duration); + s.register_member("C_PARTICLEFXEMITKEY.PFX_PPSVALUE", &IParticleEffectEmitKey::pfx_pps_value); + s.register_member("C_PARTICLEFXEMITKEY.PFX_PPSISSMOOTHCHG", &IParticleEffectEmitKey::pfx_pps_is_smooth_chg); + s.register_member("C_PARTICLEFXEMITKEY.PFX_PPSISLOOPINGCHG", &IParticleEffectEmitKey::pfx_pps_is_looping_chg); + s.register_member("C_PARTICLEFXEMITKEY.PFX_SCTIME", &IParticleEffectEmitKey::pfx_sc_time); + s.register_member("C_PARTICLEFXEMITKEY.PFX_FLYGRAVITY_S", &IParticleEffectEmitKey::pfx_fly_gravity_s); + s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPDIM_S", &IParticleEffectEmitKey::pfx_shp_dim_s); + s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPISVOLUMECHG", &IParticleEffectEmitKey::pfx_shp_is_volume_chg); + s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPSCALEFPS", &IParticleEffectEmitKey::pfx_shp_scale_fps); + s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPDISTRIBWALKSPEED", + &IParticleEffectEmitKey::pfx_shp_distrib_walks_peed); + s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPOFFSETVEC_S", &IParticleEffectEmitKey::pfx_shp_offset_vec_s); + s.register_member("C_PARTICLEFXEMITKEY.PFX_SHPDISTRIBTYPE_S", &IParticleEffectEmitKey::pfx_shp_distrib_type_s); + s.register_member("C_PARTICLEFXEMITKEY.PFX_DIRMODE_S", &IParticleEffectEmitKey::pfx_dir_mode_s); + s.register_member("C_PARTICLEFXEMITKEY.PFX_DIRFOR_S", &IParticleEffectEmitKey::pfx_dir_for_s); + s.register_member("C_PARTICLEFXEMITKEY.PFX_DIRMODETARGETFOR_S", &IParticleEffectEmitKey::pfx_dir_mode_target_for_s); + s.register_member("C_PARTICLEFXEMITKEY.PFX_DIRMODETARGETPOS_S", &IParticleEffectEmitKey::pfx_dir_mode_target_pos_s); + s.register_member("C_PARTICLEFXEMITKEY.PFX_VELAVG", &IParticleEffectEmitKey::pfx_vel_avg); + s.register_member("C_PARTICLEFXEMITKEY.PFX_LSPPARTAVG", &IParticleEffectEmitKey::pfx_lsp_part_avg); + s.register_member("C_PARTICLEFXEMITKEY.PFX_VISALPHASTART", &IParticleEffectEmitKey::pfx_vis_alpha_start); + s.register_member("C_PARTICLEFXEMITKEY.LIGHTPRESETNAME", &IParticleEffectEmitKey::light_preset_name); + s.register_member("C_PARTICLEFXEMITKEY.LIGHTRANGE", &IParticleEffectEmitKey::light_range); + s.register_member("C_PARTICLEFXEMITKEY.SFXID", &IParticleEffectEmitKey::sfx_id); + s.register_member("C_PARTICLEFXEMITKEY.SFXISAMBIENT", &IParticleEffectEmitKey::sfx_is_ambient); + s.register_member("C_PARTICLEFXEMITKEY.EMCREATEFXID", &IParticleEffectEmitKey::em_create_fx_id); + s.register_member("C_PARTICLEFXEMITKEY.EMFLYGRAVITY", &IParticleEffectEmitKey::em_fly_gravity); + s.register_member("C_PARTICLEFXEMITKEY.EMSELFROTVEL_S", &IParticleEffectEmitKey::em_self_rot_vel_s); + s.register_member("C_PARTICLEFXEMITKEY.EMTRJMODE_S", &IParticleEffectEmitKey::em_trj_mode_s); + s.register_member("C_PARTICLEFXEMITKEY.EMTRJEASEVEL", &IParticleEffectEmitKey::em_trj_ease_vel); + s.register_member("C_PARTICLEFXEMITKEY.EMCHECKCOLLISION", &IParticleEffectEmitKey::em_check_collision); + s.register_member("C_PARTICLEFXEMITKEY.EMFXLIFESPAN", &IParticleEffectEmitKey::em_fx_lifespan); +} + +void zenkit::INpc::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_NPC", "INpc"); + s.register_member("C_NPC.ID", &INpc::id); + s.register_member("C_NPC.NAME", &INpc::name); + s.register_member("C_NPC.SLOT", &INpc::slot); + s.register_member("C_NPC.NPCTYPE", &INpc::type); + s.register_member("C_NPC.FLAGS", &INpc::flags); + s.register_member("C_NPC.ATTRIBUTE", &INpc::attribute); + s.register_member("C_NPC.PROTECTION", &INpc::protection); + s.register_member("C_NPC.DAMAGE", &INpc::damage); + s.register_member("C_NPC.DAMAGETYPE", &INpc::damage_type); + s.register_member("C_NPC.GUILD", &INpc::guild); + s.register_member("C_NPC.LEVEL", &INpc::level); + s.register_member("C_NPC.MISSION", &INpc::mission); + s.register_member("C_NPC.FIGHT_TACTIC", &INpc::fight_tactic); + s.register_member("C_NPC.WEAPON", &INpc::weapon); + s.register_member("C_NPC.VOICE", &INpc::voice); + s.register_member("C_NPC.VOICEPITCH", &INpc::voice_pitch); + s.register_member("C_NPC.BODYMASS", &INpc::body_mass); + s.register_member("C_NPC.DAILY_ROUTINE", &INpc::daily_routine); + s.register_member("C_NPC.START_AISTATE", &INpc::start_aistate); + s.register_member("C_NPC.SPAWNPOINT", &INpc::spawnpoint); + s.register_member("C_NPC.SPAWNDELAY", &INpc::spawn_delay); + s.register_member("C_NPC.SENSES", &INpc::senses); + s.register_member("C_NPC.SENSES_RANGE", &INpc::senses_range); + s.register_member("C_NPC.AIVAR", &INpc::aivar); + s.register_member("C_NPC.WP", &INpc::wp); + s.register_member("C_NPC.EXP", &INpc::exp); + s.register_member("C_NPC.EXP_NEXT", &INpc::exp_next); + s.register_member("C_NPC.LP", &INpc::lp); + + // Gothic 2 only + if (s.find_symbol_by_name("C_NPC.EFFECT") != nullptr) { + s.register_member("C_NPC.EFFECT", &INpc::effect); + s.register_member("C_NPC.HITCHANCE", &INpc::hitchance); + s.register_member("C_NPC.BODYSTATEINTERRUPTABLEOVERRIDE", &INpc::bodystate_interruptable_override); + s.register_member("C_NPC.NOFOCUS", &INpc::no_focus); + } +} + +void zenkit::IMission::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_MISSION", "IMission"); + s.register_member("C_MISSION.NAME", &IMission::name); + s.register_member("C_MISSION.DESCRIPTION", &IMission::description); + s.register_member("C_MISSION.DURATION", &IMission::duration); + s.register_member("C_MISSION.IMPORTANT", &IMission::important); + s.register_member("C_MISSION.OFFERCONDITIONS", &IMission::offer_conditions); + s.register_member("C_MISSION.OFFER", &IMission::offer); + s.register_member("C_MISSION.SUCCESSCONDITIONS", &IMission::success_conditions); + s.register_member("C_MISSION.SUCCESS", &IMission::success); + s.register_member("C_MISSION.FAILURECONDITIONS", &IMission::failure_conditions); + s.register_member("C_MISSION.FAILURE", &IMission::failure); + s.register_member("C_MISSION.OBSOLETECONDITIONS", &IMission::obsolete_conditions); + s.register_member("C_MISSION.OBSOLETE", &IMission::obsolete); + s.register_member("C_MISSION.RUNNING", &IMission::running); +} + +void zenkit::IItem::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_ITEM", "IItem"); + s.register_member("C_ITEM.ID", &IItem::id); + s.register_member("C_ITEM.NAME", &IItem::name); + s.register_member("C_ITEM.NAMEID", &IItem::name_id); + s.register_member("C_ITEM.HP", &IItem::hp); + s.register_member("C_ITEM.HP_MAX", &IItem::hp_max); + s.register_member("C_ITEM.MAINFLAG", &IItem::main_flag); + s.register_member("C_ITEM.FLAGS", &IItem::flags); + s.register_member("C_ITEM.WEIGHT", &IItem::weight); + s.register_member("C_ITEM.VALUE", &IItem::value); + s.register_member("C_ITEM.DAMAGETYPE", &IItem::damage_type); + s.register_member("C_ITEM.DAMAGETOTAL", &IItem::damage_total); + s.register_member("C_ITEM.DAMAGE", &IItem::damage); + s.register_member("C_ITEM.WEAR", &IItem::wear); + s.register_member("C_ITEM.PROTECTION", &IItem::protection); + s.register_member("C_ITEM.NUTRITION", &IItem::nutrition); + s.register_member("C_ITEM.COND_ATR", &IItem::cond_atr); + s.register_member("C_ITEM.COND_VALUE", &IItem::cond_value); + s.register_member("C_ITEM.CHANGE_ATR", &IItem::change_atr); + s.register_member("C_ITEM.CHANGE_VALUE", &IItem::change_value); + s.register_member("C_ITEM.MAGIC", &IItem::magic); + s.register_member("C_ITEM.ON_EQUIP", &IItem::on_equip); + s.register_member("C_ITEM.ON_UNEQUIP", &IItem::on_unequip); + s.register_member("C_ITEM.ON_STATE", &IItem::on_state); + s.register_member("C_ITEM.OWNER", &IItem::owner); + s.register_member("C_ITEM.OWNERGUILD", &IItem::owner_guild); + s.register_member("C_ITEM.DISGUISEGUILD", &IItem::disguise_guild); + s.register_member("C_ITEM.VISUAL", &IItem::visual); + s.register_member("C_ITEM.VISUAL_CHANGE", &IItem::visual_change); + s.register_member("C_ITEM.VISUAL_SKIN", &IItem::visual_skin); + s.register_member("C_ITEM.SCEMENAME", &IItem::scheme_name); + s.register_member("C_ITEM.MATERIAL", &IItem::material); + s.register_member("C_ITEM.MUNITION", &IItem::munition); + s.register_member("C_ITEM.SPELL", &IItem::spell); + s.register_member("C_ITEM.RANGE", &IItem::range); + s.register_member("C_ITEM.MAG_CIRCLE", &IItem::mag_circle); + s.register_member("C_ITEM.DESCRIPTION", &IItem::description); + s.register_member("C_ITEM.TEXT", &IItem::text); + s.register_member("C_ITEM.COUNT", &IItem::count); + + // Gothic 2 only + if (s.find_symbol_by_name("C_ITEM.EFFECT") != nullptr) { + s.register_member("C_ITEM.EFFECT", &IItem::effect); + s.register_member("C_ITEM.INV_ZBIAS", &IItem::inv_zbias); + s.register_member("C_ITEM.INV_ROTX", &IItem::inv_rot_x); + s.register_member("C_ITEM.INV_ROTY", &IItem::inv_rot_y); + s.register_member("C_ITEM.INV_ROTZ", &IItem::inv_rot_z); + s.register_member("C_ITEM.INV_ANIMATE", &IItem::inv_animate); + } +} + +void zenkit::IFocus::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_FOCUS", "IFocus"); + s.register_member("C_FOCUS.NPC_LONGRANGE", &IFocus::npc_longrange); + s.register_member("C_FOCUS.NPC_RANGE1", &IFocus::npc_range1); + s.register_member("C_FOCUS.NPC_RANGE2", &IFocus::npc_range2); + s.register_member("C_FOCUS.NPC_AZI", &IFocus::npc_azi); + s.register_member("C_FOCUS.NPC_ELEVDO", &IFocus::npc_elevdo); + s.register_member("C_FOCUS.NPC_ELEVUP", &IFocus::npc_elevup); + s.register_member("C_FOCUS.NPC_PRIO", &IFocus::npc_prio); + s.register_member("C_FOCUS.ITEM_RANGE1", &IFocus::item_range1); + s.register_member("C_FOCUS.ITEM_RANGE2", &IFocus::item_range2); + s.register_member("C_FOCUS.ITEM_AZI", &IFocus::item_azi); + s.register_member("C_FOCUS.ITEM_ELEVDO", &IFocus::item_elevdo); + s.register_member("C_FOCUS.ITEM_ELEVUP", &IFocus::item_elevup); + s.register_member("C_FOCUS.ITEM_PRIO", &IFocus::item_prio); + s.register_member("C_FOCUS.MOB_RANGE1", &IFocus::mob_range1); + s.register_member("C_FOCUS.MOB_RANGE2", &IFocus::mob_range2); + s.register_member("C_FOCUS.MOB_AZI", &IFocus::mob_azi); + s.register_member("C_FOCUS.MOB_ELEVDO", &IFocus::mob_elevdo); + s.register_member("C_FOCUS.MOB_ELEVUP", &IFocus::mob_elevup); + s.register_member("C_FOCUS.MOB_PRIO", &IFocus::mob_prio); +} + +void zenkit::IInfo::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_INFO", "IInfo"); + s.register_member("C_INFO.NPC", &IInfo::npc); + s.register_member("C_INFO.NR", &IInfo::nr); + s.register_member("C_INFO.IMPORTANT", &IInfo::important); + s.register_member("C_INFO.CONDITION", &IInfo::condition); + s.register_member("C_INFO.INFORMATION", &IInfo::information); + s.register_member("C_INFO.DESCRIPTION", &IInfo::description); + s.register_member("C_INFO.TRADE", &IInfo::trade); + s.register_member("C_INFO.PERMANENT", &IInfo::permanent); +} + +void zenkit::IInfo::add_choice(zenkit::IInfoChoice const& ch) { + choices.insert(choices.begin(), ch); +} + +void zenkit::IInfo::remove_choice(std::size_t index) { + choices.erase(choices.begin() + static_cast(index)); +} + +void zenkit::IItemReact::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_ITEMREACT", "IItemReact"); + s.register_member("C_ITEMREACT.NPC", &IItemReact::npc); + s.register_member("C_ITEMREACT.TRADE_ITEM", &IItemReact::trade_item); + s.register_member("C_ITEMREACT.TRADE_AMOUNT", &IItemReact::trade_amount); + s.register_member("C_ITEMREACT.REQUESTED_CAT", &IItemReact::requested_cat); + s.register_member("C_ITEMREACT.REQUESTED_ITEM", &IItemReact::requested_item); + s.register_member("C_ITEMREACT.REQUESTED_AMOUNT", &IItemReact::requested_amount); + s.register_member("C_ITEMREACT.REACTION", &IItemReact::reaction); +} + +void zenkit::ISpell::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_SPELL", "ISpell"); + s.register_member("C_SPELL.TIME_PER_MANA", &ISpell::time_per_mana); + s.register_member("C_SPELL.DAMAGE_PER_LEVEL", &ISpell::damage_per_level); + s.register_member("C_SPELL.DAMAGETYPE", &ISpell::damage_type); + s.register_member("C_SPELL.SPELLTYPE", &ISpell::spell_type); + s.register_member("C_SPELL.CANTURNDURINGINVEST", &ISpell::can_turn_during_invest); + s.register_member("C_SPELL.CANCHANGETARGETDURINGINVEST", &ISpell::can_change_target_during_invest); + s.register_member("C_SPELL.ISMULTIEFFECT", &ISpell::is_multi_effect); + s.register_member("C_SPELL.TARGETCOLLECTALGO", &ISpell::target_collect_algo); + s.register_member("C_SPELL.TARGETCOLLECTTYPE", &ISpell::target_collect_type); + s.register_member("C_SPELL.TARGETCOLLECTRANGE", &ISpell::target_collect_range); + s.register_member("C_SPELL.TARGETCOLLECTAZI", &ISpell::target_collect_azi); + s.register_member("C_SPELL.TARGETCOLLECTELEV", &ISpell::target_collect_elev); +} + +#define REG_IF_SYM_EXIST(sym, ref) \ + do { \ + if (s.find_symbol_by_name(sym)) { \ + s.register_member(sym, ref); \ + } \ + } while (false) + +void zenkit::ISvm::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_SVM", "ISvm"); + REG_IF_SYM_EXIST("C_SVM.MILGREETINGS", &ISvm::MILGREETINGS); + REG_IF_SYM_EXIST("C_SVM.PALGREETINGS", &ISvm::PALGREETINGS); + REG_IF_SYM_EXIST("C_SVM.WEATHER", &ISvm::WEATHER); + REG_IF_SYM_EXIST("C_SVM.IGETYOUSTILL", &ISvm::IGETYOUSTILL); + REG_IF_SYM_EXIST("C_SVM.DIEENEMY", &ISvm::DIEENEMY); + REG_IF_SYM_EXIST("C_SVM.DIEMONSTER", &ISvm::DIEMONSTER); + REG_IF_SYM_EXIST("C_SVM.ADDON_DIEMONSTER", &ISvm::ADDON_DIEMONSTER); + REG_IF_SYM_EXIST("C_SVM.ADDON_DIEMONSTER2", &ISvm::ADDON_DIEMONSTER2); + REG_IF_SYM_EXIST("C_SVM.DIRTYTHIEF", &ISvm::DIRTYTHIEF); + REG_IF_SYM_EXIST("C_SVM.HANDSOFF", &ISvm::HANDSOFF); + REG_IF_SYM_EXIST("C_SVM.SHEEPKILLER", &ISvm::SHEEPKILLER); + REG_IF_SYM_EXIST("C_SVM.SHEEPKILLERMONSTER", &ISvm::SHEEPKILLERMONSTER); + REG_IF_SYM_EXIST("C_SVM.YOUMURDERER", &ISvm::YOUMURDERER); + REG_IF_SYM_EXIST("C_SVM.DIESTUPIDBEAST", &ISvm::DIESTUPIDBEAST); + REG_IF_SYM_EXIST("C_SVM.YOUDAREHITME", &ISvm::YOUDAREHITME); + REG_IF_SYM_EXIST("C_SVM.YOUASKEDFORIT", &ISvm::YOUASKEDFORIT); + REG_IF_SYM_EXIST("C_SVM.THENIBEATYOUOUTOFHERE", &ISvm::THENIBEATYOUOUTOFHERE); + REG_IF_SYM_EXIST("C_SVM.WHATDIDYOUDOINTHERE", &ISvm::WHATDIDYOUDOINTHERE); + REG_IF_SYM_EXIST("C_SVM.WILLYOUSTOPFIGHTING", &ISvm::WILLYOUSTOPFIGHTING); + REG_IF_SYM_EXIST("C_SVM.KILLENEMY", &ISvm::KILLENEMY); + REG_IF_SYM_EXIST("C_SVM.ENEMYKILLED", &ISvm::ENEMYKILLED); + REG_IF_SYM_EXIST("C_SVM.MONSTERKILLED", &ISvm::MONSTERKILLED); + REG_IF_SYM_EXIST("C_SVM.ADDON_MONSTERKILLED", &ISvm::ADDON_MONSTERKILLED); + REG_IF_SYM_EXIST("C_SVM.ADDON_MONSTERKILLED2", &ISvm::ADDON_MONSTERKILLED2); + REG_IF_SYM_EXIST("C_SVM.THIEFDOWN", &ISvm::THIEFDOWN); + REG_IF_SYM_EXIST("C_SVM.RUMFUMMLERDOWN", &ISvm::RUMFUMMLERDOWN); + REG_IF_SYM_EXIST("C_SVM.SHEEPATTACKERDOWN", &ISvm::SHEEPATTACKERDOWN); + REG_IF_SYM_EXIST("C_SVM.KILLMURDERER", &ISvm::KILLMURDERER); + REG_IF_SYM_EXIST("C_SVM.STUPIDBEASTKILLED", &ISvm::STUPIDBEASTKILLED); + REG_IF_SYM_EXIST("C_SVM.NEVERHITMEAGAIN", &ISvm::NEVERHITMEAGAIN); + REG_IF_SYM_EXIST("C_SVM.YOUBETTERSHOULDHAVELISTENED", &ISvm::YOUBETTERSHOULDHAVELISTENED); + REG_IF_SYM_EXIST("C_SVM.GETUPANDBEGONE", &ISvm::GETUPANDBEGONE); + REG_IF_SYM_EXIST("C_SVM.NEVERENTERROOMAGAIN", &ISvm::NEVERENTERROOMAGAIN); + REG_IF_SYM_EXIST("C_SVM.THEREISNOFIGHTINGHERE", &ISvm::THEREISNOFIGHTINGHERE); + REG_IF_SYM_EXIST("C_SVM.SPAREME", &ISvm::SPAREME); + REG_IF_SYM_EXIST("C_SVM.RUNAWAY", &ISvm::RUNAWAY); + REG_IF_SYM_EXIST("C_SVM.ALARM", &ISvm::ALARM); + REG_IF_SYM_EXIST("C_SVM.GUARDS", &ISvm::GUARDS); + REG_IF_SYM_EXIST("C_SVM.HELP", &ISvm::HELP); + REG_IF_SYM_EXIST("C_SVM.GOODMONSTERKILL", &ISvm::GOODMONSTERKILL); + REG_IF_SYM_EXIST("C_SVM.GOODKILL", &ISvm::GOODKILL); + REG_IF_SYM_EXIST("C_SVM.NOTNOW", &ISvm::NOTNOW); + REG_IF_SYM_EXIST("C_SVM.RUNCOWARD", &ISvm::RUNCOWARD); + REG_IF_SYM_EXIST("C_SVM.GETOUTOFHERE", &ISvm::GETOUTOFHERE); + REG_IF_SYM_EXIST("C_SVM.WHYAREYOUINHERE", &ISvm::WHYAREYOUINHERE); + REG_IF_SYM_EXIST("C_SVM.YESGOOUTOFHERE", &ISvm::YESGOOUTOFHERE); + REG_IF_SYM_EXIST("C_SVM.WHATSTHISSUPPOSEDTOBE", &ISvm::WHATSTHISSUPPOSEDTOBE); + REG_IF_SYM_EXIST("C_SVM.YOUDISTURBEDMYSLUMBER", &ISvm::YOUDISTURBEDMYSLUMBER); + REG_IF_SYM_EXIST("C_SVM.ITOOKYOURGOLD", &ISvm::ITOOKYOURGOLD); + REG_IF_SYM_EXIST("C_SVM.SHITNOGOLD", &ISvm::SHITNOGOLD); + REG_IF_SYM_EXIST("C_SVM.ITAKEYOURWEAPON", &ISvm::ITAKEYOURWEAPON); + REG_IF_SYM_EXIST("C_SVM.WHATAREYOUDOING", &ISvm::WHATAREYOUDOING); + REG_IF_SYM_EXIST("C_SVM.LOOKINGFORTROUBLEAGAIN", &ISvm::LOOKINGFORTROUBLEAGAIN); + REG_IF_SYM_EXIST("C_SVM.STOPMAGIC", &ISvm::STOPMAGIC); + REG_IF_SYM_EXIST("C_SVM.ISAIDSTOPMAGIC", &ISvm::ISAIDSTOPMAGIC); + REG_IF_SYM_EXIST("C_SVM.WEAPONDOWN", &ISvm::WEAPONDOWN); + REG_IF_SYM_EXIST("C_SVM.ISAIDWEAPONDOWN", &ISvm::ISAIDWEAPONDOWN); + REG_IF_SYM_EXIST("C_SVM.WISEMOVE", &ISvm::WISEMOVE); + REG_IF_SYM_EXIST("C_SVM.NEXTTIMEYOUREINFORIT", &ISvm::NEXTTIMEYOUREINFORIT); + REG_IF_SYM_EXIST("C_SVM.OHMYHEAD", &ISvm::OHMYHEAD); + REG_IF_SYM_EXIST("C_SVM.THERESAFIGHT", &ISvm::THERESAFIGHT); + REG_IF_SYM_EXIST("C_SVM.OHMYGODITSAFIGHT", &ISvm::OHMYGODITSAFIGHT); + REG_IF_SYM_EXIST("C_SVM.GOODVICTORY", &ISvm::GOODVICTORY); + REG_IF_SYM_EXIST("C_SVM.NOTBAD", &ISvm::NOTBAD); + REG_IF_SYM_EXIST("C_SVM.OHMYGODHESDOWN", &ISvm::OHMYGODHESDOWN); + REG_IF_SYM_EXIST("C_SVM.CHEERFRIEND01", &ISvm::CHEERFRIEND01); + REG_IF_SYM_EXIST("C_SVM.CHEERFRIEND02", &ISvm::CHEERFRIEND02); + REG_IF_SYM_EXIST("C_SVM.CHEERFRIEND03", &ISvm::CHEERFRIEND03); + REG_IF_SYM_EXIST("C_SVM.OOH01", &ISvm::OOH01); + REG_IF_SYM_EXIST("C_SVM.OOH02", &ISvm::OOH02); + REG_IF_SYM_EXIST("C_SVM.OOH03", &ISvm::OOH03); + REG_IF_SYM_EXIST("C_SVM.WHATWASTHAT", &ISvm::WHATWASTHAT); + REG_IF_SYM_EXIST("C_SVM.GETOUTOFMYBED", &ISvm::GETOUTOFMYBED); + REG_IF_SYM_EXIST("C_SVM.AWAKE", &ISvm::AWAKE); + REG_IF_SYM_EXIST("C_SVM.ABS_COMMANDER", &ISvm::ABS_COMMANDER); + REG_IF_SYM_EXIST("C_SVM.ABS_MONASTERY", &ISvm::ABS_MONASTERY); + REG_IF_SYM_EXIST("C_SVM.ABS_FARM", &ISvm::ABS_FARM); + REG_IF_SYM_EXIST("C_SVM.ABS_GOOD", &ISvm::ABS_GOOD); + REG_IF_SYM_EXIST("C_SVM.SHEEPKILLER_CRIME", &ISvm::SHEEPKILLER_CRIME); + REG_IF_SYM_EXIST("C_SVM.ATTACK_CRIME", &ISvm::ATTACK_CRIME); + REG_IF_SYM_EXIST("C_SVM.THEFT_CRIME", &ISvm::THEFT_CRIME); + REG_IF_SYM_EXIST("C_SVM.MURDER_CRIME", &ISvm::MURDER_CRIME); + REG_IF_SYM_EXIST("C_SVM.PAL_CITY_CRIME", &ISvm::PAL_CITY_CRIME); + REG_IF_SYM_EXIST("C_SVM.MIL_CITY_CRIME", &ISvm::MIL_CITY_CRIME); + REG_IF_SYM_EXIST("C_SVM.CITY_CRIME", &ISvm::CITY_CRIME); + REG_IF_SYM_EXIST("C_SVM.MONA_CRIME", &ISvm::MONA_CRIME); + REG_IF_SYM_EXIST("C_SVM.FARM_CRIME", &ISvm::FARM_CRIME); + REG_IF_SYM_EXIST("C_SVM.OC_CRIME", &ISvm::OC_CRIME); + REG_IF_SYM_EXIST("C_SVM.TOUGHGUY_ATTACKLOST", &ISvm::TOUGHGUY_ATTACKLOST); + REG_IF_SYM_EXIST("C_SVM.TOUGHGUY_ATTACKWON", &ISvm::TOUGHGUY_ATTACKWON); + REG_IF_SYM_EXIST("C_SVM.TOUGHGUY_PLAYERATTACK", &ISvm::TOUGHGUY_PLAYERATTACK); + REG_IF_SYM_EXIST("C_SVM.GOLD_1000", &ISvm::GOLD_1000); + REG_IF_SYM_EXIST("C_SVM.GOLD_950", &ISvm::GOLD_950); + REG_IF_SYM_EXIST("C_SVM.GOLD_900", &ISvm::GOLD_900); + REG_IF_SYM_EXIST("C_SVM.GOLD_850", &ISvm::GOLD_850); + REG_IF_SYM_EXIST("C_SVM.GOLD_800", &ISvm::GOLD_800); + REG_IF_SYM_EXIST("C_SVM.GOLD_750", &ISvm::GOLD_750); + REG_IF_SYM_EXIST("C_SVM.GOLD_700", &ISvm::GOLD_700); + REG_IF_SYM_EXIST("C_SVM.GOLD_650", &ISvm::GOLD_650); + REG_IF_SYM_EXIST("C_SVM.GOLD_600", &ISvm::GOLD_600); + REG_IF_SYM_EXIST("C_SVM.GOLD_550", &ISvm::GOLD_550); + REG_IF_SYM_EXIST("C_SVM.GOLD_500", &ISvm::GOLD_500); + REG_IF_SYM_EXIST("C_SVM.GOLD_450", &ISvm::GOLD_450); + REG_IF_SYM_EXIST("C_SVM.GOLD_400", &ISvm::GOLD_400); + REG_IF_SYM_EXIST("C_SVM.GOLD_350", &ISvm::GOLD_350); + REG_IF_SYM_EXIST("C_SVM.GOLD_300", &ISvm::GOLD_300); + REG_IF_SYM_EXIST("C_SVM.GOLD_250", &ISvm::GOLD_250); + REG_IF_SYM_EXIST("C_SVM.GOLD_200", &ISvm::GOLD_200); + REG_IF_SYM_EXIST("C_SVM.GOLD_150", &ISvm::GOLD_150); + REG_IF_SYM_EXIST("C_SVM.GOLD_100", &ISvm::GOLD_100); + REG_IF_SYM_EXIST("C_SVM.GOLD_90", &ISvm::GOLD_90); + REG_IF_SYM_EXIST("C_SVM.GOLD_80", &ISvm::GOLD_80); + REG_IF_SYM_EXIST("C_SVM.GOLD_70", &ISvm::GOLD_70); + REG_IF_SYM_EXIST("C_SVM.GOLD_60", &ISvm::GOLD_60); + REG_IF_SYM_EXIST("C_SVM.GOLD_50", &ISvm::GOLD_50); + REG_IF_SYM_EXIST("C_SVM.GOLD_40", &ISvm::GOLD_40); + REG_IF_SYM_EXIST("C_SVM.GOLD_30", &ISvm::GOLD_30); + REG_IF_SYM_EXIST("C_SVM.GOLD_20", &ISvm::GOLD_20); + REG_IF_SYM_EXIST("C_SVM.GOLD_10", &ISvm::GOLD_10); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK01", &ISvm::SMALLTALK01); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK02", &ISvm::SMALLTALK02); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK03", &ISvm::SMALLTALK03); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK04", &ISvm::SMALLTALK04); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK05", &ISvm::SMALLTALK05); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK06", &ISvm::SMALLTALK06); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK07", &ISvm::SMALLTALK07); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK08", &ISvm::SMALLTALK08); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK09", &ISvm::SMALLTALK09); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK10", &ISvm::SMALLTALK10); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK11", &ISvm::SMALLTALK11); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK12", &ISvm::SMALLTALK12); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK13", &ISvm::SMALLTALK13); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK14", &ISvm::SMALLTALK14); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK15", &ISvm::SMALLTALK15); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK16", &ISvm::SMALLTALK16); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK17", &ISvm::SMALLTALK17); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK18", &ISvm::SMALLTALK18); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK19", &ISvm::SMALLTALK19); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK20", &ISvm::SMALLTALK20); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK21", &ISvm::SMALLTALK21); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK22", &ISvm::SMALLTALK22); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK23", &ISvm::SMALLTALK23); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK24", &ISvm::SMALLTALK24); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK25", &ISvm::SMALLTALK25); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK26", &ISvm::SMALLTALK26); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK27", &ISvm::SMALLTALK27); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK28", &ISvm::SMALLTALK28); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK29", &ISvm::SMALLTALK29); + REG_IF_SYM_EXIST("C_SVM.SMALLTALK30", &ISvm::SMALLTALK30); + REG_IF_SYM_EXIST("C_SVM.NOLEARNNOPOINTS", &ISvm::NOLEARNNOPOINTS); + REG_IF_SYM_EXIST("C_SVM.NOLEARNOVERPERSONALMAX", &ISvm::NOLEARNOVERPERSONALMAX); + REG_IF_SYM_EXIST("C_SVM.NOLEARNYOUREBETTER", &ISvm::NOLEARNYOUREBETTER); + REG_IF_SYM_EXIST("C_SVM.YOULEARNEDSOMETHING", &ISvm::YOULEARNEDSOMETHING); + REG_IF_SYM_EXIST("C_SVM.UNTERSTADT", &ISvm::UNTERSTADT); + REG_IF_SYM_EXIST("C_SVM.OBERSTADT", &ISvm::OBERSTADT); + REG_IF_SYM_EXIST("C_SVM.TEMPEL", &ISvm::TEMPEL); + REG_IF_SYM_EXIST("C_SVM.MARKT", &ISvm::MARKT); + REG_IF_SYM_EXIST("C_SVM.GALGEN", &ISvm::GALGEN); + REG_IF_SYM_EXIST("C_SVM.KASERNE", &ISvm::KASERNE); + REG_IF_SYM_EXIST("C_SVM.HAFEN", &ISvm::HAFEN); + REG_IF_SYM_EXIST("C_SVM.WHERETO", &ISvm::WHERETO); + REG_IF_SYM_EXIST("C_SVM.OBERSTADT_2_UNTERSTADT", &ISvm::OBERSTADT_2_UNTERSTADT); + REG_IF_SYM_EXIST("C_SVM.UNTERSTADT_2_OBERSTADT", &ISvm::UNTERSTADT_2_OBERSTADT); + REG_IF_SYM_EXIST("C_SVM.UNTERSTADT_2_TEMPEL", &ISvm::UNTERSTADT_2_TEMPEL); + REG_IF_SYM_EXIST("C_SVM.UNTERSTADT_2_HAFEN", &ISvm::UNTERSTADT_2_HAFEN); + REG_IF_SYM_EXIST("C_SVM.TEMPEL_2_UNTERSTADT", &ISvm::TEMPEL_2_UNTERSTADT); + REG_IF_SYM_EXIST("C_SVM.TEMPEL_2_MARKT", &ISvm::TEMPEL_2_MARKT); + REG_IF_SYM_EXIST("C_SVM.TEMPEL_2_GALGEN", &ISvm::TEMPEL_2_GALGEN); + REG_IF_SYM_EXIST("C_SVM.MARKT_2_TEMPEL", &ISvm::MARKT_2_TEMPEL); + REG_IF_SYM_EXIST("C_SVM.MARKT_2_KASERNE", &ISvm::MARKT_2_KASERNE); + REG_IF_SYM_EXIST("C_SVM.MARKT_2_GALGEN", &ISvm::MARKT_2_GALGEN); + REG_IF_SYM_EXIST("C_SVM.GALGEN_2_TEMPEL", &ISvm::GALGEN_2_TEMPEL); + REG_IF_SYM_EXIST("C_SVM.GALGEN_2_MARKT", &ISvm::GALGEN_2_MARKT); + REG_IF_SYM_EXIST("C_SVM.GALGEN_2_KASERNE", &ISvm::GALGEN_2_KASERNE); + REG_IF_SYM_EXIST("C_SVM.KASERNE_2_MARKT", &ISvm::KASERNE_2_MARKT); + REG_IF_SYM_EXIST("C_SVM.KASERNE_2_GALGEN", &ISvm::KASERNE_2_GALGEN); + REG_IF_SYM_EXIST("C_SVM.HAFEN_2_UNTERSTADT", &ISvm::HAFEN_2_UNTERSTADT); + REG_IF_SYM_EXIST("C_SVM.DEAD", &ISvm::DEAD); + REG_IF_SYM_EXIST("C_SVM.AARGH_1", &ISvm::AARGH_1); + REG_IF_SYM_EXIST("C_SVM.AARGH_2", &ISvm::AARGH_2); + REG_IF_SYM_EXIST("C_SVM.AARGH_3", &ISvm::AARGH_3); + REG_IF_SYM_EXIST("C_SVM.ADDON_WRONGARMOR", &ISvm::ADDON_WRONGARMOR); + REG_IF_SYM_EXIST("C_SVM.ADDON_WRONGARMOR_SLD", &ISvm::ADDON_WRONGARMOR_SLD); + REG_IF_SYM_EXIST("C_SVM.ADDON_WRONGARMOR_MIL", &ISvm::ADDON_WRONGARMOR_MIL); + REG_IF_SYM_EXIST("C_SVM.ADDON_WRONGARMOR_KDF", &ISvm::ADDON_WRONGARMOR_KDF); + REG_IF_SYM_EXIST("C_SVM.ADDON_NOARMOR_BDT", &ISvm::ADDON_NOARMOR_BDT); + REG_IF_SYM_EXIST("C_SVM.ADDON_DIEBANDIT", &ISvm::ADDON_DIEBANDIT); + REG_IF_SYM_EXIST("C_SVM.ADDON_DIRTYPIRATE", &ISvm::ADDON_DIRTYPIRATE); + REG_IF_SYM_EXIST("C_SVM.SC_HEYTURNAROUND", &ISvm::SC_HEYTURNAROUND); + REG_IF_SYM_EXIST("C_SVM.SC_HEYTURNAROUND02", &ISvm::SC_HEYTURNAROUND02); + REG_IF_SYM_EXIST("C_SVM.SC_HEYTURNAROUND03", &ISvm::SC_HEYTURNAROUND03); + REG_IF_SYM_EXIST("C_SVM.SC_HEYTURNAROUND04", &ISvm::SC_HEYTURNAROUND04); + REG_IF_SYM_EXIST("C_SVM.SC_HEYWAITASECOND", &ISvm::SC_HEYWAITASECOND); + REG_IF_SYM_EXIST("C_SVM.DOESNTWORK", &ISvm::DOESNTWORK); + REG_IF_SYM_EXIST("C_SVM.PICKBROKE", &ISvm::PICKBROKE); + REG_IF_SYM_EXIST("C_SVM.NEEDKEY", &ISvm::NEEDKEY); + REG_IF_SYM_EXIST("C_SVM.NOMOREPICKS", &ISvm::NOMOREPICKS); + REG_IF_SYM_EXIST("C_SVM.NOPICKLOCKTALENT", &ISvm::NOPICKLOCKTALENT); + REG_IF_SYM_EXIST("C_SVM.NOSWEEPING", &ISvm::NOSWEEPING); + REG_IF_SYM_EXIST("C_SVM.PICKLOCKORKEYMISSING", &ISvm::PICKLOCKORKEYMISSING); + REG_IF_SYM_EXIST("C_SVM.KEYMISSING", &ISvm::KEYMISSING); + REG_IF_SYM_EXIST("C_SVM.PICKLOCKMISSING", &ISvm::PICKLOCKMISSING); + REG_IF_SYM_EXIST("C_SVM.NEVEROPEN", &ISvm::NEVEROPEN); + REG_IF_SYM_EXIST("C_SVM.MISSINGITEM", &ISvm::MISSINGITEM); + REG_IF_SYM_EXIST("C_SVM.DONTKNOW", &ISvm::DONTKNOW); + REG_IF_SYM_EXIST("C_SVM.NOTHINGTOGET", &ISvm::NOTHINGTOGET); + REG_IF_SYM_EXIST("C_SVM.NOTHINGTOGET02", &ISvm::NOTHINGTOGET02); + REG_IF_SYM_EXIST("C_SVM.NOTHINGTOGET03", &ISvm::NOTHINGTOGET03); + REG_IF_SYM_EXIST("C_SVM.HEALSHRINE", &ISvm::HEALSHRINE); + REG_IF_SYM_EXIST("C_SVM.HEALLASTSHRINE", &ISvm::HEALLASTSHRINE); + REG_IF_SYM_EXIST("C_SVM.IRDORATHTHEREYOUARE", &ISvm::IRDORATHTHEREYOUARE); + REG_IF_SYM_EXIST("C_SVM.SCOPENSIRDORATHBOOK", &ISvm::SCOPENSIRDORATHBOOK); + REG_IF_SYM_EXIST("C_SVM.SCOPENSLASTDOOR", &ISvm::SCOPENSLASTDOOR); + REG_IF_SYM_EXIST("C_SVM.TRADE_1", &ISvm::TRADE_1); + REG_IF_SYM_EXIST("C_SVM.TRADE_2", &ISvm::TRADE_2); + REG_IF_SYM_EXIST("C_SVM.TRADE_3", &ISvm::TRADE_3); + REG_IF_SYM_EXIST("C_SVM.VERSTEHE", &ISvm::VERSTEHE); + REG_IF_SYM_EXIST("C_SVM.FOUNDTREASURE", &ISvm::FOUNDTREASURE); + REG_IF_SYM_EXIST("C_SVM.CANTUNDERSTANDTHIS", &ISvm::CANTUNDERSTANDTHIS); + REG_IF_SYM_EXIST("C_SVM.CANTREADTHIS", &ISvm::CANTREADTHIS); + REG_IF_SYM_EXIST("C_SVM.STONEPLATE_1", &ISvm::STONEPLATE_1); + REG_IF_SYM_EXIST("C_SVM.STONEPLATE_2", &ISvm::STONEPLATE_2); + REG_IF_SYM_EXIST("C_SVM.STONEPLATE_3", &ISvm::STONEPLATE_3); + REG_IF_SYM_EXIST("C_SVM.COUGH", &ISvm::COUGH); + REG_IF_SYM_EXIST("C_SVM.HUI", &ISvm::HUI); + REG_IF_SYM_EXIST("C_SVM.ADDON_THISLITTLEBASTARD", &ISvm::ADDON_THISLITTLEBASTARD); + REG_IF_SYM_EXIST("C_SVM.ADDON_OPENADANOSTEMPLE", &ISvm::ADDON_OPENADANOSTEMPLE); + REG_IF_SYM_EXIST("C_SVM.ATTENTAT_ADDON_DESCRIPTION", &ISvm::ATTENTAT_ADDON_DESCRIPTION); + REG_IF_SYM_EXIST("C_SVM.ATTENTAT_ADDON_DESCRIPTION2", &ISvm::ATTENTAT_ADDON_DESCRIPTION2); + REG_IF_SYM_EXIST("C_SVM.ATTENTAT_ADDON_PRO", &ISvm::ATTENTAT_ADDON_PRO); + REG_IF_SYM_EXIST("C_SVM.ATTENTAT_ADDON_CONTRA", &ISvm::ATTENTAT_ADDON_CONTRA); + REG_IF_SYM_EXIST("C_SVM.MINE_ADDON_DESCRIPTION", &ISvm::MINE_ADDON_DESCRIPTION); + REG_IF_SYM_EXIST("C_SVM.ADDON_SUMMONANCIENTGHOST", &ISvm::ADDON_SUMMONANCIENTGHOST); + REG_IF_SYM_EXIST("C_SVM.ADDON_ANCIENTGHOST_NOTNEAR", &ISvm::ADDON_ANCIENTGHOST_NOTNEAR); + REG_IF_SYM_EXIST("C_SVM.ADDON_GOLD_DESCRIPTION", &ISvm::ADDON_GOLD_DESCRIPTION); + + REG_IF_SYM_EXIST("C_SVM.WATCHYOURAIM", &ISvm::WATCHYOURAIM); + REG_IF_SYM_EXIST("C_SVM.watchyouraimangry", &ISvm::watchyouraimangry); + REG_IF_SYM_EXIST("C_SVM.letsforgetourlittlefight", &ISvm::letsforgetourlittlefight); + REG_IF_SYM_EXIST("C_SVM.strange", &ISvm::strange); + REG_IF_SYM_EXIST("C_SVM.diemortalenemy", &ISvm::diemortalenemy); + REG_IF_SYM_EXIST("C_SVM.nowwait", &ISvm::nowwait); + REG_IF_SYM_EXIST("C_SVM.nowwaitintruder", &ISvm::nowwaitintruder); + REG_IF_SYM_EXIST("C_SVM.youstillnothaveenough", &ISvm::youstillnothaveenough); + REG_IF_SYM_EXIST("C_SVM.youattackedmycharge", &ISvm::youattackedmycharge); + REG_IF_SYM_EXIST("C_SVM.iwillteachyourespectforforeignproperty", &ISvm::iwillteachyourespectforforeignproperty); + REG_IF_SYM_EXIST("C_SVM.youkilledoneofus", &ISvm::youkilledoneofus); + REG_IF_SYM_EXIST("C_SVM.berzerk", &ISvm::berzerk); + REG_IF_SYM_EXIST("C_SVM.youllbesorryforthis", &ISvm::youllbesorryforthis); + REG_IF_SYM_EXIST("C_SVM.yesyes", &ISvm::yesyes); + REG_IF_SYM_EXIST("C_SVM.shitwhatamonster", &ISvm::shitwhatamonster); + REG_IF_SYM_EXIST("C_SVM.wewillmeetagain", &ISvm::wewillmeetagain); + REG_IF_SYM_EXIST("C_SVM.nevertrythatagain", &ISvm::nevertrythatagain); + REG_IF_SYM_EXIST("C_SVM.itookyourore", &ISvm::itookyourore); + REG_IF_SYM_EXIST("C_SVM.shitnoore", &ISvm::shitnoore); + REG_IF_SYM_EXIST("C_SVM.youviolatedforbiddenterritory", &ISvm::youviolatedforbiddenterritory); + REG_IF_SYM_EXIST("C_SVM.youwannafoolme", &ISvm::youwannafoolme); + REG_IF_SYM_EXIST("C_SVM.whatdidyouinthere", &ISvm::whatdidyouinthere); + REG_IF_SYM_EXIST("C_SVM.intruderalert", &ISvm::intruderalert); + REG_IF_SYM_EXIST("C_SVM.behindyou", &ISvm::behindyou); + REG_IF_SYM_EXIST("C_SVM.heyheyhey", &ISvm::heyheyhey); + REG_IF_SYM_EXIST("C_SVM.cheerfight", &ISvm::cheerfight); + REG_IF_SYM_EXIST("C_SVM.cheerfriend", &ISvm::cheerfriend); + REG_IF_SYM_EXIST("C_SVM.ooh", &ISvm::ooh); + REG_IF_SYM_EXIST("C_SVM.yeahwelldone", &ISvm::yeahwelldone); + REG_IF_SYM_EXIST("C_SVM.hedefeatedhim", &ISvm::hedefeatedhim); + REG_IF_SYM_EXIST("C_SVM.hedeservedit", &ISvm::hedeservedit); + REG_IF_SYM_EXIST("C_SVM.hekilledhim", &ISvm::hekilledhim); + REG_IF_SYM_EXIST("C_SVM.itwasagoodfight", &ISvm::itwasagoodfight); + REG_IF_SYM_EXIST("C_SVM.friendlygreetings", &ISvm::friendlygreetings); + REG_IF_SYM_EXIST("C_SVM.algreetings", &ISvm::algreetings); + REG_IF_SYM_EXIST("C_SVM.magegreetings", &ISvm::magegreetings); + REG_IF_SYM_EXIST("C_SVM.sectgreetings", &ISvm::sectgreetings); + REG_IF_SYM_EXIST("C_SVM.thereheis", &ISvm::thereheis); + REG_IF_SYM_EXIST("C_SVM.nolearnovermax", &ISvm::nolearnovermax); + REG_IF_SYM_EXIST("C_SVM.nolearnyoualreadyknow", &ISvm::nolearnyoualreadyknow); + REG_IF_SYM_EXIST("C_SVM.heyyou", &ISvm::heyyou); + REG_IF_SYM_EXIST("C_SVM.whatdoyouwant", &ISvm::whatdoyouwant); + REG_IF_SYM_EXIST("C_SVM.isaidwhatdoyouwant", &ISvm::isaidwhatdoyouwant); + REG_IF_SYM_EXIST("C_SVM.makeway", &ISvm::makeway); + REG_IF_SYM_EXIST("C_SVM.outofmyway", &ISvm::outofmyway); + REG_IF_SYM_EXIST("C_SVM.youdeaforwhat", &ISvm::youdeaforwhat); + REG_IF_SYM_EXIST("C_SVM.lookaway", &ISvm::lookaway); + REG_IF_SYM_EXIST("C_SVM.okaykeepit", &ISvm::okaykeepit); + REG_IF_SYM_EXIST("C_SVM.whatsthat", &ISvm::whatsthat); + REG_IF_SYM_EXIST("C_SVM.thatsmyweapon", &ISvm::thatsmyweapon); + REG_IF_SYM_EXIST("C_SVM.giveittome", &ISvm::giveittome); + REG_IF_SYM_EXIST("C_SVM.youcankeepthecrap", &ISvm::youcankeepthecrap); + REG_IF_SYM_EXIST("C_SVM.theykilledmyfriend", &ISvm::theykilledmyfriend); + REG_IF_SYM_EXIST("C_SVM.suckergotsome", &ISvm::suckergotsome); + REG_IF_SYM_EXIST("C_SVM.suckerdefeatedebr", &ISvm::suckerdefeatedebr); + REG_IF_SYM_EXIST("C_SVM.suckerdefeatedgur", &ISvm::suckerdefeatedgur); + REG_IF_SYM_EXIST("C_SVM.suckerdefeatedmage", &ISvm::suckerdefeatedmage); + REG_IF_SYM_EXIST("C_SVM.suckerdefeatednov_guard", &ISvm::suckerdefeatednov_guard); + REG_IF_SYM_EXIST("C_SVM.suckerdefeatedvlk_guard", &ISvm::suckerdefeatedvlk_guard); + REG_IF_SYM_EXIST("C_SVM.youdefeatedmycomrade", &ISvm::youdefeatedmycomrade); + REG_IF_SYM_EXIST("C_SVM.youdefeatednov_guard", &ISvm::youdefeatednov_guard); + REG_IF_SYM_EXIST("C_SVM.youdefeatedvlk_guard", &ISvm::youdefeatedvlk_guard); + REG_IF_SYM_EXIST("C_SVM.youstolefromme", &ISvm::youstolefromme); + REG_IF_SYM_EXIST("C_SVM.youstolefromus", &ISvm::youstolefromus); + REG_IF_SYM_EXIST("C_SVM.youstolefromebr", &ISvm::youstolefromebr); + REG_IF_SYM_EXIST("C_SVM.youstolefromgur", &ISvm::youstolefromgur); + REG_IF_SYM_EXIST("C_SVM.stolefrommage", &ISvm::stolefrommage); + REG_IF_SYM_EXIST("C_SVM.youkilledmyfriend", &ISvm::youkilledmyfriend); + REG_IF_SYM_EXIST("C_SVM.youkilledebr", &ISvm::youkilledebr); + REG_IF_SYM_EXIST("C_SVM.youkilledgur", &ISvm::youkilledgur); + REG_IF_SYM_EXIST("C_SVM.youkilledmage", &ISvm::youkilledmage); + REG_IF_SYM_EXIST("C_SVM.youkilledocfolk", &ISvm::youkilledocfolk); + REG_IF_SYM_EXIST("C_SVM.youkilledncfolk", &ISvm::youkilledncfolk); + REG_IF_SYM_EXIST("C_SVM.youkilledpsifolk", &ISvm::youkilledpsifolk); + REG_IF_SYM_EXIST("C_SVM.getthingsright", &ISvm::getthingsright); + REG_IF_SYM_EXIST("C_SVM.youdefeatedmewell", &ISvm::youdefeatedmewell); + REG_IF_SYM_EXIST("C_SVM.om", &ISvm::om); +} + +#undef REG_IF_SYM_EXIST + +void zenkit::IMenu::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_MENU", "IMenu"); + s.register_member("C_MENU.BACKPIC", &IMenu::back_pic); + s.register_member("C_MENU.BACKWORLD", &IMenu::back_world); + s.register_member("C_MENU.POSX", &IMenu::pos_x); + s.register_member("C_MENU.POSY", &IMenu::pos_y); + s.register_member("C_MENU.DIMX", &IMenu::dim_x); + s.register_member("C_MENU.DIMY", &IMenu::dim_y); + s.register_member("C_MENU.ALPHA", &IMenu::alpha); + s.register_member("C_MENU.MUSICTHEME", &IMenu::music_theme); + s.register_member("C_MENU.EVENTTIMERMSEC", &IMenu::event_timer_msec); + s.register_member("C_MENU.ITEMS", &IMenu::items); + s.register_member("C_MENU.FLAGS", &IMenu::flags); + s.register_member("C_MENU.DEFAULTOUTGAME", &IMenu::default_outgame); + s.register_member("C_MENU.DEFAULTINGAME", &IMenu::default_ingame); +} + +void zenkit::IMenuItem::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_MENU_ITEM", "IMenuItem"); + s.register_member("C_MENU_ITEM.FONTNAME", &IMenuItem::fontname); + s.register_member("C_MENU_ITEM.TEXT", &IMenuItem::text); + s.register_member("C_MENU_ITEM.BACKPIC", &IMenuItem::backpic); + s.register_member("C_MENU_ITEM.ALPHAMODE", &IMenuItem::alphamode); + s.register_member("C_MENU_ITEM.ALPHA", &IMenuItem::alpha); + s.register_member("C_MENU_ITEM.TYPE", &IMenuItem::type); + s.register_member("C_MENU_ITEM.ONSELACTION", &IMenuItem::on_sel_action); + s.register_member("C_MENU_ITEM.ONSELACTION_S", &IMenuItem::on_sel_action_s); + s.register_member("C_MENU_ITEM.ONCHGSETOPTION", &IMenuItem::on_chg_set_option); + s.register_member("C_MENU_ITEM.ONCHGSETOPTIONSECTION", &IMenuItem::on_chg_set_option_section); + s.register_member("C_MENU_ITEM.ONEVENTACTION", &IMenuItem::on_event_action); + s.register_member("C_MENU_ITEM.POSX", &IMenuItem::pos_x); + s.register_member("C_MENU_ITEM.POSY", &IMenuItem::pos_y); + s.register_member("C_MENU_ITEM.DIMX", &IMenuItem::dim_x); + s.register_member("C_MENU_ITEM.DIMY", &IMenuItem::dim_y); + s.register_member("C_MENU_ITEM.SIZESTARTSCALE", &IMenuItem::size_start_scale); + s.register_member("C_MENU_ITEM.FLAGS", &IMenuItem::flags); + s.register_member("C_MENU_ITEM.OPENDELAYTIME", &IMenuItem::open_delay_time); + s.register_member("C_MENU_ITEM.OPENDURATION", &IMenuItem::open_duration); + s.register_member("C_MENU_ITEM.USERFLOAT", &IMenuItem::user_float); + s.register_member("C_MENU_ITEM.USERSTRING", &IMenuItem::user_string); + s.register_member("C_MENU_ITEM.FRAMESIZEX", &IMenuItem::frame_sizex); + s.register_member("C_MENU_ITEM.FRAMESIZEY", &IMenuItem::frame_sizey); + + // Gothic 2 only + if (s.find_symbol_by_name("C_MENU_ITEM.HIDEIFOPTIONSECTIONSET") != nullptr) { + s.register_member("C_MENU_ITEM.HIDEIFOPTIONSECTIONSET", &IMenuItem::hide_if_option_section_set); + s.register_member("C_MENU_ITEM.HIDEIFOPTIONSET", &IMenuItem::hide_if_option_set); + s.register_member("C_MENU_ITEM.HIDEONVALUE", &IMenuItem::hide_on_value); + } +} + +void zenkit::ICamera::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("CCAMSYS", "ICamera"); + s.register_member("CCAMSYS.BESTRANGE", &ICamera::best_range); + s.register_member("CCAMSYS.MINRANGE", &ICamera::min_range); + s.register_member("CCAMSYS.MAXRANGE", &ICamera::max_range); + s.register_member("CCAMSYS.BESTELEVATION", &ICamera::best_elevation); + s.register_member("CCAMSYS.MINELEVATION", &ICamera::min_elevation); + s.register_member("CCAMSYS.MAXELEVATION", &ICamera::max_elevation); + s.register_member("CCAMSYS.BESTAZIMUTH", &ICamera::best_azimuth); + s.register_member("CCAMSYS.MINAZIMUTH", &ICamera::min_azimuth); + s.register_member("CCAMSYS.MAXAZIMUTH", &ICamera::max_azimuth); + s.register_member("CCAMSYS.BESTROTZ", &ICamera::best_rot_z); + s.register_member("CCAMSYS.MINROTZ", &ICamera::min_rot_z); + s.register_member("CCAMSYS.MAXROTZ", &ICamera::max_rot_z); + s.register_member("CCAMSYS.ROTOFFSETX", &ICamera::rot_offset_x); + s.register_member("CCAMSYS.ROTOFFSETY", &ICamera::rot_offset_y); + s.register_member("CCAMSYS.ROTOFFSETZ", &ICamera::rot_offset_z); + s.register_member("CCAMSYS.TARGETOFFSETX", &ICamera::target_offset_x); + s.register_member("CCAMSYS.TARGETOFFSETY", &ICamera::target_offset_y); + s.register_member("CCAMSYS.TARGETOFFSETZ", &ICamera::target_offset_z); + s.register_member("CCAMSYS.VELOTRANS", &ICamera::velo_trans); + s.register_member("CCAMSYS.VELOROT", &ICamera::velo_rot); + s.register_member("CCAMSYS.TRANSLATE", &ICamera::translate); + s.register_member("CCAMSYS.ROTATE", &ICamera::rotate); + s.register_member("CCAMSYS.COLLISION", &ICamera::collision); +} + +void zenkit::IMusicSystem::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_MUSICSYS_CFG", "IMusicSystem"); + s.register_member("C_MUSICSYS_CFG.VOLUME", &IMusicSystem::volume); + s.register_member("C_MUSICSYS_CFG.BITRESOLUTION", &IMusicSystem::bit_resolution); + s.register_member("C_MUSICSYS_CFG.GLOBALREVERBENABLED", &IMusicSystem::global_reverb_enabled); + s.register_member("C_MUSICSYS_CFG.SAMPLERATE", &IMusicSystem::sample_rate); + s.register_member("C_MUSICSYS_CFG.NUMCHANNELS", &IMusicSystem::num_channels); + s.register_member("C_MUSICSYS_CFG.REVERBBUFFERSIZE", &IMusicSystem::reverb_buffer_size); +} + +void zenkit::IMusicTheme::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_MUSICTHEME", "IMusicTheme"); + s.register_member("C_MUSICTHEME.FILE", &IMusicTheme::file); + s.register_member("C_MUSICTHEME.VOL", &IMusicTheme::vol); + s.register_member("C_MUSICTHEME.LOOP", &IMusicTheme::loop); + s.register_member("C_MUSICTHEME.REVERBMIX", &IMusicTheme::reverbmix); + s.register_member("C_MUSICTHEME.REVERBTIME", &IMusicTheme::reverbtime); + s.register_member("C_MUSICTHEME.TRANSTYPE", &IMusicTheme::transtype); + s.register_member("C_MUSICTHEME.TRANSSUBTYPE", &IMusicTheme::transsubtype); +} + +void zenkit::IMusicJingle::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_MUSICJINGLE", "IMusicJingle"); + s.register_member("C_MUSICJINGLE.NAME", &IMusicJingle::name); + s.register_member("C_MUSICJINGLE.LOOP", &IMusicJingle::loop); + s.register_member("C_MUSICJINGLE.VOL", &IMusicJingle::vol); + s.register_member("C_MUSICJINGLE.TRANSSUBTYPE", &IMusicJingle::transsubtype); +} + +void zenkit::IParticleEffect::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("C_PARTICLEFX", "IParticleEffect"); + s.register_member("C_PARTICLEFX.PPSVALUE", &IParticleEffect::pps_value); + s.register_member("C_PARTICLEFX.PPSSCALEKEYS_S", &IParticleEffect::pps_scale_keys_s); + s.register_member("C_PARTICLEFX.PPSISLOOPING", &IParticleEffect::pps_is_looping); + s.register_member("C_PARTICLEFX.PPSISSMOOTH", &IParticleEffect::pps_is_smooth); + s.register_member("C_PARTICLEFX.PPSFPS", &IParticleEffect::pps_fps); + s.register_member("C_PARTICLEFX.PPSCREATEEM_S", &IParticleEffect::pps_create_em_s); + s.register_member("C_PARTICLEFX.PPSCREATEEMDELAY", &IParticleEffect::pps_create_em_delay); + s.register_member("C_PARTICLEFX.SHPTYPE_S", &IParticleEffect::shp_type_s); + s.register_member("C_PARTICLEFX.SHPFOR_S", &IParticleEffect::shp_for_s); + s.register_member("C_PARTICLEFX.SHPOFFSETVEC_S", &IParticleEffect::shp_offset_vec_s); + s.register_member("C_PARTICLEFX.SHPDISTRIBTYPE_S", &IParticleEffect::shp_distrib_type_s); + s.register_member("C_PARTICLEFX.SHPDISTRIBWALKSPEED", &IParticleEffect::shp_distrib_walk_speed); + s.register_member("C_PARTICLEFX.SHPISVOLUME", &IParticleEffect::shp_is_volume); + s.register_member("C_PARTICLEFX.SHPDIM_S", &IParticleEffect::shp_dim_s); + s.register_member("C_PARTICLEFX.SHPMESH_S", &IParticleEffect::shp_mesh_s); + s.register_member("C_PARTICLEFX.SHPMESHRENDER_B", &IParticleEffect::shp_mesh_render_b); + s.register_member("C_PARTICLEFX.SHPSCALEKEYS_S", &IParticleEffect::shp_scale_keys_s); + s.register_member("C_PARTICLEFX.SHPSCALEISLOOPING", &IParticleEffect::shp_scale_is_looping); + s.register_member("C_PARTICLEFX.SHPSCALEISSMOOTH", &IParticleEffect::shp_scale_is_smooth); + s.register_member("C_PARTICLEFX.SHPSCALEFPS", &IParticleEffect::shp_scale_fps); + s.register_member("C_PARTICLEFX.DIRMODE_S", &IParticleEffect::dir_mode_s); + s.register_member("C_PARTICLEFX.DIRFOR_S", &IParticleEffect::dir_for_s); + s.register_member("C_PARTICLEFX.DIRMODETARGETFOR_S", &IParticleEffect::dir_mode_target_for_s); + s.register_member("C_PARTICLEFX.DIRMODETARGETPOS_S", &IParticleEffect::dir_mode_target_pos_s); + s.register_member("C_PARTICLEFX.DIRANGLEHEAD", &IParticleEffect::dir_angle_head); + s.register_member("C_PARTICLEFX.DIRANGLEHEADVAR", &IParticleEffect::dir_angle_head_var); + s.register_member("C_PARTICLEFX.DIRANGLEELEV", &IParticleEffect::dir_angle_elev); + s.register_member("C_PARTICLEFX.DIRANGLEELEVVAR", &IParticleEffect::dir_angle_elev_var); + s.register_member("C_PARTICLEFX.VELAVG", &IParticleEffect::vel_avg); + s.register_member("C_PARTICLEFX.VELVAR", &IParticleEffect::vel_var); + s.register_member("C_PARTICLEFX.LSPPARTAVG", &IParticleEffect::lsp_part_avg); + s.register_member("C_PARTICLEFX.LSPPARTVAR", &IParticleEffect::lsp_part_var); + s.register_member("C_PARTICLEFX.FLYGRAVITY_S", &IParticleEffect::fly_gravity_s); + s.register_member("C_PARTICLEFX.FLYCOLLDET_B", &IParticleEffect::fly_colldet_b); + s.register_member("C_PARTICLEFX.VISNAME_S", &IParticleEffect::vis_name_s); + s.register_member("C_PARTICLEFX.VISORIENTATION_S", &IParticleEffect::vis_orientation_s); + s.register_member("C_PARTICLEFX.VISTEXISQUADPOLY", &IParticleEffect::vis_tex_is_quadpoly); + s.register_member("C_PARTICLEFX.VISTEXANIFPS", &IParticleEffect::vis_tex_ani_fps); + s.register_member("C_PARTICLEFX.VISTEXANIISLOOPING", &IParticleEffect::vis_tex_ani_is_looping); + s.register_member("C_PARTICLEFX.VISTEXCOLORSTART_S", &IParticleEffect::vis_tex_color_start_s); + s.register_member("C_PARTICLEFX.VISTEXCOLOREND_S", &IParticleEffect::vis_tex_color_end_s); + s.register_member("C_PARTICLEFX.VISSIZESTART_S", &IParticleEffect::vis_size_start_s); + s.register_member("C_PARTICLEFX.VISSIZEENDSCALE", &IParticleEffect::vis_size_end_scale); + s.register_member("C_PARTICLEFX.VISALPHAFUNC_S", &IParticleEffect::vis_alpha_func_s); + s.register_member("C_PARTICLEFX.VISALPHASTART", &IParticleEffect::vis_alpha_start); + s.register_member("C_PARTICLEFX.VISALPHAEND", &IParticleEffect::vis_alpha_end); + s.register_member("C_PARTICLEFX.TRLFADESPEED", &IParticleEffect::trl_fade_speed); + s.register_member("C_PARTICLEFX.TRLTEXTURE_S", &IParticleEffect::trl_texture_s); + s.register_member("C_PARTICLEFX.TRLWIDTH", &IParticleEffect::trl_width); + s.register_member("C_PARTICLEFX.MRKFADESPEED", &IParticleEffect::mrk_fades_peed); + s.register_member("C_PARTICLEFX.MRKTEXTURE_S", &IParticleEffect::mrkt_exture_s); + s.register_member("C_PARTICLEFX.MRKSIZE", &IParticleEffect::mrk_size); + + // Gothic 2 only + if (s.find_symbol_by_name("C_PARTICLEFX.FLOCKMODE") != nullptr) { + s.register_member("C_PARTICLEFX.FLOCKMODE", &IParticleEffect::flock_mode); + s.register_member("C_PARTICLEFX.FLOCKSTRENGTH", &IParticleEffect::flock_strength); + s.register_member("C_PARTICLEFX.USEEMITTERSFOR", &IParticleEffect::use_emitters_for); + s.register_member("C_PARTICLEFX.TIMESTARTEND_S", &IParticleEffect::time_start_end_s); + s.register_member("C_PARTICLEFX.M_BISAMBIENTPFX", &IParticleEffect::m_bis_ambient_pfx); + } +} + +void zenkit::IEffectBase::register_(zenkit::DaedalusScript& s) { + ZKLOG_CLASS("CFX_BASE", "IEffectBase"); + s.register_member("CFX_BASE.VISNAME_S", &IEffectBase::vis_name_s); + s.register_member("CFX_BASE.VISSIZE_S", &IEffectBase::vis_size_s); + s.register_member("CFX_BASE.VISALPHA", &IEffectBase::vis_alpha); + s.register_member("CFX_BASE.VISALPHABLENDFUNC_S", &IEffectBase::vis_alpha_blend_func_s); + s.register_member("CFX_BASE.VISTEXANIFPS", &IEffectBase::vis_tex_ani_fps); + s.register_member("CFX_BASE.VISTEXANIISLOOPING", &IEffectBase::vis_tex_ani_is_looping); + s.register_member("CFX_BASE.EMTRJMODE_S", &IEffectBase::em_trj_mode_s); + s.register_member("CFX_BASE.EMTRJORIGINNODE", &IEffectBase::em_trj_origin_node); + s.register_member("CFX_BASE.EMTRJTARGETNODE", &IEffectBase::em_trj_target_node); + s.register_member("CFX_BASE.EMTRJTARGETRANGE", &IEffectBase::em_trj_target_range); + s.register_member("CFX_BASE.EMTRJTARGETAZI", &IEffectBase::em_trj_target_azi); + s.register_member("CFX_BASE.EMTRJTARGETELEV", &IEffectBase::em_trj_target_elev); + s.register_member("CFX_BASE.EMTRJNUMKEYS", &IEffectBase::em_trj_num_keys); + s.register_member("CFX_BASE.EMTRJNUMKEYSVAR", &IEffectBase::em_trj_num_keys_var); + s.register_member("CFX_BASE.EMTRJANGLEELEVVAR", &IEffectBase::em_trj_angle_elev_var); + s.register_member("CFX_BASE.EMTRJANGLEHEADVAR", &IEffectBase::em_trj_angle_head_var); + s.register_member("CFX_BASE.EMTRJKEYDISTVAR", &IEffectBase::em_trj_key_dist_var); + s.register_member("CFX_BASE.EMTRJLOOPMODE_S", &IEffectBase::em_trj_loop_mode_s); + s.register_member("CFX_BASE.EMTRJEASEFUNC_S", &IEffectBase::em_trj_ease_func_s); + s.register_member("CFX_BASE.EMTRJEASEVEL", &IEffectBase::em_trj_ease_vel); + s.register_member("CFX_BASE.EMTRJDYNUPDATEDELAY", &IEffectBase::em_trj_dyn_update_delay); + s.register_member("CFX_BASE.EMTRJDYNUPDATETARGETONLY", &IEffectBase::em_trj_dyn_update_target_only); + s.register_member("CFX_BASE.EMFXCREATE_S", &IEffectBase::em_fx_create_s); + s.register_member("CFX_BASE.EMFXINVESTORIGIN_S", &IEffectBase::em_fx_invest_origin_s); + s.register_member("CFX_BASE.EMFXINVESTTARGET_S", &IEffectBase::em_fx_invest_target_s); + s.register_member("CFX_BASE.EMFXTRIGGERDELAY", &IEffectBase::em_fx_trigger_delay); + s.register_member("CFX_BASE.EMFXCREATEDOWNTRJ", &IEffectBase::em_fx_create_down_trj); + s.register_member("CFX_BASE.EMACTIONCOLLDYN_S", &IEffectBase::em_action_coll_dyn_s); + s.register_member("CFX_BASE.EMACTIONCOLLSTAT_S", &IEffectBase::em_action_coll_stat_s); + s.register_member("CFX_BASE.EMFXCOLLSTAT_S", &IEffectBase::em_fx_coll_stat_s); + s.register_member("CFX_BASE.EMFXCOLLDYN_S", &IEffectBase::em_fx_coll_dyn_s); + s.register_member("CFX_BASE.EMFXCOLLSTATALIGN_S", &IEffectBase::em_fx_coll_stat_align_s); + s.register_member("CFX_BASE.EMFXCOLLDYNALIGN_S", &IEffectBase::em_fx_coll_dyn_align_s); + s.register_member("CFX_BASE.EMFXLIFESPAN", &IEffectBase::em_fx_lifespan); + s.register_member("CFX_BASE.EMCHECKCOLLISION", &IEffectBase::em_check_collision); + s.register_member("CFX_BASE.EMADJUSTSHPTOORIGIN", &IEffectBase::em_adjust_shp_to_origin); + s.register_member("CFX_BASE.EMINVESTNEXTKEYDURATION", &IEffectBase::em_invest_next_key_duration); + s.register_member("CFX_BASE.EMFLYGRAVITY", &IEffectBase::em_fly_gravity); + s.register_member("CFX_BASE.EMSELFROTVEL_S", &IEffectBase::em_self_rot_vel_s); + s.register_member("CFX_BASE.USERSTRING", &IEffectBase::user_string); + s.register_member("CFX_BASE.LIGHTPRESETNAME", &IEffectBase::light_preset_name); + s.register_member("CFX_BASE.SFXID", &IEffectBase::sfx_id); + s.register_member("CFX_BASE.SFXISAMBIENT", &IEffectBase::sfx_is_ambient); + s.register_member("CFX_BASE.SENDASSESSMAGIC", &IEffectBase::send_assess_magic); + s.register_member("CFX_BASE.SECSPERDAMAGE", &IEffectBase::secs_per_damage); + + // Gothic 2 only + if (s.find_symbol_by_name("CFX_BASE.EMFXCOLLDYNPERC_S") != nullptr) + s.register_member("CFX_BASE.EMFXCOLLDYNPERC_S", &IEffectBase::em_fx_coll_dyn_perc_s); +} diff --git a/src/addon/texcvt.cc b/src/addon/texcvt.cc index 170f6d19..f1e1a4b0 100644 --- a/src/addon/texcvt.cc +++ b/src/addon/texcvt.cc @@ -1,14 +1,16 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/addon/texcvt.hh" +#include "zenkit/Stream.hh" + +#include "phoenix/buffer.hh" #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ ((uint32_t) (uint8_t) (ch0) | ((uint32_t) (uint8_t) (ch1) << 8) | ((uint32_t) (uint8_t) (ch2) << 16) | \ ((uint32_t) (uint8_t) (ch3) << 24)) -namespace phoenix { - extern std::uint32_t - _ztex_mipmap_size(texture_format format, std::uint32_t width, std::uint32_t height, uint32_t level); +namespace zenkit { + extern std::uint32_t _ztex_mipmap_size(TextureFormat, std::uint32_t, std::uint32_t, uint32_t); enum { DDSD_CAPS = 0x00000001l, @@ -68,7 +70,7 @@ namespace phoenix { uint32_t dwReserved2; } DDSURFACEDESC2; - static void write_dds_header(buffer& out, const texture& tex) { + static void write_dds_header(Write* w, zenkit::Texture const& tex) { DDSURFACEDESC2 header {}; header.dwSize = sizeof(DDSURFACEDESC2); header.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; @@ -76,8 +78,9 @@ namespace phoenix { header.dwHeight = tex.height(); header.dwWidth = tex.width(); - if (tex.format() != tex_dxt1 && tex.format() != tex_dxt2 && tex.format() != tex_dxt3 && - tex.format() != tex_dxt4 && tex.format() != tex_dxt5) { + if (tex.format() != TextureFormat::DXT1 && tex.format() != TextureFormat::DXT2 && + tex.format() != TextureFormat::DXT3 && tex.format() != TextureFormat::DXT4 && + tex.format() != TextureFormat::DXT5) { header.dwFlags |= DDSD_PITCH; header.dwPitchOrLinearSize = _ztex_mipmap_size(tex.format(), header.dwWidth, 1, 0); } else { @@ -93,7 +96,7 @@ namespace phoenix { header.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); switch (tex.format()) { - case tex_B8G8R8A8: + case TextureFormat::B8G8R8A8: header.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; header.ddpfPixelFormat.dwRGBBitCount = 32; header.ddpfPixelFormat.dwRBitMask = 0x0000FF00; @@ -101,7 +104,7 @@ namespace phoenix { header.ddpfPixelFormat.dwBBitMask = 0xFF000000; header.ddpfPixelFormat.dwRGBAlphaBitMask = 0x000000FF; break; - case tex_R8G8B8A8: + case TextureFormat::R8G8B8A8: header.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; header.ddpfPixelFormat.dwRGBBitCount = 32; header.ddpfPixelFormat.dwRBitMask = 0xFF000000; @@ -109,7 +112,7 @@ namespace phoenix { header.ddpfPixelFormat.dwBBitMask = 0x0000FF00; header.ddpfPixelFormat.dwRGBAlphaBitMask = 0x000000FF; break; - case tex_A8B8G8R8: + case TextureFormat::A8B8G8R8: header.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; header.ddpfPixelFormat.dwRGBBitCount = 32; header.ddpfPixelFormat.dwRBitMask = 0x000000FF; @@ -117,7 +120,7 @@ namespace phoenix { header.ddpfPixelFormat.dwBBitMask = 0x00FF0000; header.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000; break; - case tex_A8R8G8B8: + case TextureFormat::A8R8G8B8: header.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; header.ddpfPixelFormat.dwRGBBitCount = 32; header.ddpfPixelFormat.dwRBitMask = 0x00FF0000; @@ -125,21 +128,21 @@ namespace phoenix { header.ddpfPixelFormat.dwBBitMask = 0x000000FF; header.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000; break; - case tex_B8G8R8: + case TextureFormat::B8G8R8: header.ddpfPixelFormat.dwFlags = DDPF_RGB; header.ddpfPixelFormat.dwRGBBitCount = 24; header.ddpfPixelFormat.dwRBitMask = 0x000000FF; header.ddpfPixelFormat.dwGBitMask = 0x0000FF00; header.ddpfPixelFormat.dwBBitMask = 0x00FF0000; break; - case tex_R8G8B8: + case TextureFormat::R8G8B8: header.ddpfPixelFormat.dwFlags = DDPF_RGB; header.ddpfPixelFormat.dwRGBBitCount = 24; header.ddpfPixelFormat.dwRBitMask = 0x00FF0000; header.ddpfPixelFormat.dwGBitMask = 0x0000FF00; header.ddpfPixelFormat.dwBBitMask = 0x000000FF; break; - case tex_A4R4G4B4: + case TextureFormat::A4R4G4B4: header.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; header.ddpfPixelFormat.dwRGBBitCount = 16; header.ddpfPixelFormat.dwRBitMask = 0x00000F00; @@ -147,7 +150,7 @@ namespace phoenix { header.ddpfPixelFormat.dwBBitMask = 0x0000000F; header.ddpfPixelFormat.dwRGBAlphaBitMask = 0x0000F000; break; - case tex_A1R5G5B5: + case TextureFormat::A1R5G5B5: header.ddpfPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; header.ddpfPixelFormat.dwRGBBitCount = 16; header.ddpfPixelFormat.dwRBitMask = 0x00007C00; @@ -155,101 +158,93 @@ namespace phoenix { header.ddpfPixelFormat.dwBBitMask = 0x0000001F; header.ddpfPixelFormat.dwRGBAlphaBitMask = 0x00008000; break; - case tex_R5G6B5: + case TextureFormat::R5G6B5: header.ddpfPixelFormat.dwFlags = DDPF_RGB; header.ddpfPixelFormat.dwRGBBitCount = 16; header.ddpfPixelFormat.dwRBitMask = 0x0000F800; header.ddpfPixelFormat.dwGBitMask = 0x000007E0; header.ddpfPixelFormat.dwBBitMask = 0x0000001F; break; - case tex_p8: + case TextureFormat::P8: header.ddpfPixelFormat.dwFlags = DDPF_PALETTEINDEXED8; break; - case tex_dxt1: + case TextureFormat::DXT1: header.ddpfPixelFormat.dwFlags = DDPF_FOURCC; header.ddpfPixelFormat.dwFourCC = MAKEFOURCC('D', 'X', 'T', '1'); break; - case tex_dxt2: + case TextureFormat::DXT2: header.ddpfPixelFormat.dwFlags = DDPF_FOURCC; header.ddpfPixelFormat.dwFourCC = MAKEFOURCC('D', 'X', 'T', '2'); break; - case tex_dxt3: + case TextureFormat::DXT3: header.ddpfPixelFormat.dwFlags = DDPF_FOURCC; header.ddpfPixelFormat.dwFourCC = MAKEFOURCC('D', 'X', 'T', '3'); break; - case tex_dxt4: + case TextureFormat::DXT4: header.ddpfPixelFormat.dwFlags = DDPF_FOURCC; header.ddpfPixelFormat.dwFourCC = MAKEFOURCC('D', 'X', 'T', '4'); break; - case tex_dxt5: + case TextureFormat::DXT5: header.ddpfPixelFormat.dwFlags = DDPF_FOURCC; header.ddpfPixelFormat.dwFourCC = MAKEFOURCC('D', 'X', 'T', '5'); break; } - out.put_uint(header.dwSize); - out.put_uint(header.dwFlags); - out.put_uint(header.dwHeight); - out.put_uint(header.dwWidth); - out.put_uint(header.dwPitchOrLinearSize); - out.put_uint(header.dwDepth); - out.put_uint(header.dwMipMapCount); - - for (auto i : header.dwReserved1) - out.put_uint(i); - - out.put_uint(header.ddpfPixelFormat.dwSize); - out.put_uint(header.ddpfPixelFormat.dwFlags); - out.put_uint(header.ddpfPixelFormat.dwFourCC); - out.put_uint(header.ddpfPixelFormat.dwRGBBitCount); - out.put_uint(header.ddpfPixelFormat.dwRBitMask); - out.put_uint(header.ddpfPixelFormat.dwGBitMask); - out.put_uint(header.ddpfPixelFormat.dwBBitMask); - out.put_uint(header.ddpfPixelFormat.dwRGBAlphaBitMask); - out.put_uint(header.ddsCaps.dwCaps1); - out.put_uint(header.ddsCaps.dwCaps2); - out.put_uint(header.ddsCaps.dwReserved[0]); - out.put_uint(header.ddsCaps.dwReserved[1]); - out.put_uint(header.dwReserved2); + w->write_uint(header.dwSize); + w->write_uint(header.dwFlags); + w->write_uint(header.dwHeight); + w->write_uint(header.dwWidth); + w->write_uint(header.dwPitchOrLinearSize); + w->write_uint(header.dwDepth); + w->write_uint(header.dwMipMapCount); + + for (auto i : header.dwReserved1) { + w->write_uint(i); + } + + w->write_uint(header.ddpfPixelFormat.dwSize); + w->write_uint(header.ddpfPixelFormat.dwFlags); + w->write_uint(header.ddpfPixelFormat.dwFourCC); + w->write_uint(header.ddpfPixelFormat.dwRGBBitCount); + w->write_uint(header.ddpfPixelFormat.dwRBitMask); + w->write_uint(header.ddpfPixelFormat.dwGBitMask); + w->write_uint(header.ddpfPixelFormat.dwBBitMask); + w->write_uint(header.ddpfPixelFormat.dwRGBAlphaBitMask); + w->write_uint(header.ddsCaps.dwCaps1); + w->write_uint(header.ddsCaps.dwCaps2); + w->write_uint(header.ddsCaps.dwReserved[0]); + w->write_uint(header.ddsCaps.dwReserved[1]); + w->write_uint(header.dwReserved2); } - void write_dds_palette(buffer& out, const texture& tex) { + static void write_dds_palette(Write* w, zenkit::Texture const& tex) { for (auto i = 0; i < ZTEX_PALETTE_ENTRIES; ++i) { - out.put(tex.palette()[i].b); - out.put(tex.palette()[i].g); - out.put(tex.palette()[i].r); - out.put(tex.palette()[i].a); + w->write_ubyte(tex.palette()[i].b); + w->write_ubyte(tex.palette()[i].g); + w->write_ubyte(tex.palette()[i].r); + w->write_ubyte(tex.palette()[i].a); } } - void write_dds_data(buffer& out, const texture& tex) { + static void write_dds_data(Write* w, zenkit::Texture const& tex) { for (uint32_t level = 0; level < tex.mipmaps(); ++level) { auto& data = tex.data(level); - out.put(data.data(), data.size()); + w->write(data.data(), data.size()); } } - uint32_t calculate_total_buffer_size(const texture& tex) { - uint32_t size = 0; - - for (int32_t level = static_cast(tex.mipmaps()) - 1; level >= 0; --level) - size += _ztex_mipmap_size(tex.format(), tex.width(), tex.height(), level); - - return size; - } + std::vector to_dds(zenkit::Texture const& tex) { + std::vector buf {}; + auto w = Write::to(&buf); - buffer texture_to_dds(const texture& tex) { - auto buf = buffer::allocate(4 + // FOURCC - sizeof(DDSURFACEDESC2) + (tex.format() == tex_p8 ? ZTEX_PALETTE_ENTRIES * 4 : 0) + - calculate_total_buffer_size(tex)); - buf.put_string("DDS "); - write_dds_header(buf, tex); + w->write_string("DDS "); + write_dds_header(w.get(), tex); - if (tex.format() == tex_p8) { - write_dds_palette(buf, tex); + if (tex.format() == TextureFormat::P8) { + write_dds_palette(w.get(), tex); } - write_dds_data(buf, tex); + write_dds_data(w.get(), tex); return buf; } -} // namespace phoenix +} // namespace zenkit diff --git a/src/archive/ArchiveAscii.cc b/src/archive/ArchiveAscii.cc index a9c23e87..9a139a38 100644 --- a/src/archive/ArchiveAscii.cc +++ b/src/archive/ArchiveAscii.cc @@ -1,7 +1,10 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include "archive_ascii.hh" -#include "phoenix/phoenix.hh" +#include "ArchiveAscii.hh" + +#include "phoenix/buffer.hh" + +#include "../Internal.hh" #include #include @@ -9,52 +12,36 @@ #include #include -namespace phoenix { - static const std::unordered_map type_name_to_enum { - {"string", archive_entry_type::string}, - {"int", archive_entry_type::int_}, - {"float", archive_entry_type::float_}, - {"byte", archive_entry_type::byte}, - {"word", archive_entry_type::word}, - {"bool_", archive_entry_type::bool_}, - {"vec3", archive_entry_type::vec3}, - {"color", archive_entry_type::color}, - {"raw", archive_entry_type::raw}, - {"rawFloat", archive_entry_type::raw_float}, - {"enum", archive_entry_type::enum_}, - {"hash", archive_entry_type::hash}, - }; - - void archive_reader_ascii::read_header() { +namespace zenkit { + void ReadArchiveAscii::read_header() { { - std::string objects = input.get_line(); + std::string objects = read->read_line(true); if (objects.find("objects ") != 0) { - throw parser_error {"archive_reader_ascii", "objects field missing"}; + throw zenkit::ParserError {"ReadArchive.Ascii", "objects field missing"}; } try { _m_objects = std::stoi(objects.substr(objects.find(' ') + 1)); } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_ascii", e, "reading int"}; + throw zenkit::ParserError {"ReadArchive.Ascii", e, "reading int"}; } } - if (input.get_line() != "END") { - throw parser_error {"archive_reader_ascii", "second END missing"}; + if (read->read_line(true) != "END") { + throw zenkit::ParserError {"ReadArchive.Ascii", "second END missing"}; } } - bool archive_reader_ascii::read_object_begin(archive_object& obj) { - if (input.remaining() < 3) + bool ReadArchiveAscii::read_object_begin(ArchiveObject& obj) { + if (read->eof()) return false; - input.mark(); - - auto line = input.get_line(); + auto mark = read->tell(); + auto line = read->read_line(true); // Fail quickly if we know this can't be an object begin if (line.length() <= 2) { - input.reset(); + read->seek(static_cast(mark), Whence::BEG); return false; } @@ -65,7 +52,7 @@ namespace phoenix { std::sscanf(line.c_str(), "[%127s %127s %hu %u]", object_name, class_name, &obj.version, &obj.index); if (parsed_elements != 4) { - input.reset(); + read->seek(static_cast(mark), Whence::BEG); return false; } @@ -74,13 +61,13 @@ namespace phoenix { return true; } - bool archive_reader_ascii::read_object_end() { + bool ReadArchiveAscii::read_object_end() { // When there are less than 3 bytes left in the input, this must be the end of the archive. - if (input.remaining() < 3) - return true; + if (read->eof()) + return false; - input.mark(); - auto line = input.get_line(); + auto mark = read->tell(); + auto line = read->read_line(true); // Compatibility fix for binary data in ASCII archives. // TODO: Optimize using `find_if`! @@ -89,80 +76,80 @@ namespace phoenix { } if (line != "[]") { - input.reset(); + read->seek(static_cast(mark), Whence::BEG); return false; } return true; } - std::string archive_reader_ascii::read_entry(std::string_view type) { - auto line = input.get_line(); + std::string ReadArchiveAscii::read_entry(std::string_view type) { + auto line = read->read_line(true); line = line.substr(line.find('=') + 1); auto colon = line.find(':'); if (line.substr(0, colon) != type) { - throw parser_error {"archive_reader_ascii", - "type mismatch: expected " + std::string {type} + ", got: " + line.substr(0, colon)}; + throw zenkit::ParserError {"ReadArchive.Ascii", + "type mismatch: expected " + std::string {type} + + ", got: " + line.substr(0, colon)}; } - auto rv = line.substr(colon + 1); - return rv; + return line.substr(colon + 1); } - std::string archive_reader_ascii::read_string() { + std::string ReadArchiveAscii::read_string() { return read_entry("string"); } - std::int32_t archive_reader_ascii::read_int() { + std::int32_t ReadArchiveAscii::read_int() { try { return std::stoi(read_entry("int")); } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_ascii", e, "reading int"}; + throw zenkit::ParserError {"ReadArchive.Ascii", e, "reading int"}; } } - float archive_reader_ascii::read_float() { + float ReadArchiveAscii::read_float() { try { return std::stof(read_entry("float")); } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_ascii", e, "reading int"}; + throw zenkit::ParserError {"ReadArchive.Ascii", e, "reading int"}; } } - std::uint8_t archive_reader_ascii::read_byte() { + std::uint8_t ReadArchiveAscii::read_byte() { try { return std::stoul(read_entry("int")) & 0xFF; } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_ascii", e, "reading int"}; + throw zenkit::ParserError {"ReadArchive.Ascii", e, "reading int"}; } } - std::uint16_t archive_reader_ascii::read_word() { + std::uint16_t ReadArchiveAscii::read_word() { try { return std::stoul(read_entry("int")) & 0xFF'FF; } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_ascii", e, "reading int"}; + throw zenkit::ParserError {"ReadArchive.Ascii", e, "reading int"}; } } - std::uint32_t archive_reader_ascii::read_enum() { + std::uint32_t ReadArchiveAscii::read_enum() { try { return std::stoul(read_entry("enum")) & 0xFFFF'FFFF; } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_ascii", e, "reading int"}; + throw zenkit::ParserError {"ReadArchive.Ascii", e, "reading int"}; } } - bool archive_reader_ascii::read_bool() { + bool ReadArchiveAscii::read_bool() { try { return std::stoul(read_entry("bool")) != 0; } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_ascii", e, "reading int"}; + throw zenkit::ParserError {"ReadArchive.Ascii", e, "reading int"}; } } - glm::u8vec4 archive_reader_ascii::read_color() { + glm::u8vec4 ReadArchiveAscii::read_color() { std::stringstream in {read_entry("color")}; std::uint16_t r, g, b, a; @@ -170,7 +157,7 @@ namespace phoenix { return glm::u8vec4 {(std::uint8_t) r, (std::uint8_t) g, (std::uint8_t) b, (std::uint8_t) a}; } - glm::vec3 archive_reader_ascii::read_vec3() { + glm::vec3 ReadArchiveAscii::read_vec3() { std::stringstream in {read_entry("vec3")}; glm::vec3 v {}; @@ -178,7 +165,7 @@ namespace phoenix { return v; } - glm::vec2 archive_reader_ascii::read_vec2() { + glm::vec2 ReadArchiveAscii::read_vec2() { std::stringstream in {read_entry("rawFloat")}; glm::vec2 v {}; @@ -186,23 +173,24 @@ namespace phoenix { return v; } - void archive_reader_ascii::skip_entry() { - (void) input.get_line(); + void ReadArchiveAscii::skip_entry() { + (void) read->read_line(true); } - bounding_box archive_reader_ascii::read_bbox() { + AxisAlignedBoundingBox ReadArchiveAscii::read_bbox() { std::stringstream in {read_entry("rawFloat")}; - bounding_box box {}; + AxisAlignedBoundingBox box {}; in >> box.min.x >> box.min.y >> box.min.z >> box.max.x >> box.max.y >> box.max.z; return box; } - glm::mat3x3 archive_reader_ascii::read_mat3x3() { + glm::mat3x3 ReadArchiveAscii::read_mat3x3() { auto in = read_entry("raw"); if (in.length() < 2 /* 2 chars a byte */ * sizeof(float) * 9) { - throw parser_error {"archive_reader_ascii", "raw entry does not contain enough bytes to be a 3x3 matrix"}; + throw zenkit::ParserError {"ReadArchive.Ascii", + "raw entry does not contain enough bytes to be a 3x3 matrix"}; } auto beg_it = in.data(); @@ -223,10 +211,16 @@ namespace phoenix { return glm::transpose(v); } - buffer archive_reader_ascii::read_raw_bytes() { + phoenix::buffer ReadArchiveAscii::read_raw_bytes(uint32_t size) { auto in = read_entry("raw"); auto length = in.length() / 2; + if (length < size) { + throw zenkit::ParserError {"ReadArchive.Ascii", "not enough raw bytes to read!"}; + } else if (length > size) { + ZKLOGW("ReadArchive.Ascii", "Reading %d bytes although %zu are actually available", size, length); + } + std::vector out {}; out.resize(length); @@ -237,17 +231,17 @@ namespace phoenix { beg_it += 2; } - return buffer::of(std::move(out)); + return phoenix::buffer::of(std::move(out)); } - buffer archive_reader_ascii::read_raw_bytes(uint32_t size) { + std::unique_ptr ReadArchiveAscii::read_raw(uint32_t size) { auto in = read_entry("raw"); auto length = in.length() / 2; if (length < size) { - throw parser_error {"archive_reader_ascii", "not enough raw bytes to read!"}; + throw zenkit::ParserError {"ReadArchive.Ascii", "not enough raw bytes to read!"}; } else if (length > size) { - PX_LOGW("read_raw_bytes: reading ", size, " bytes although ", length, " are actually available"); + ZKLOGW("ReadArchive.Ascii", "Reading %d bytes although %zu are actually available", size, length); } std::vector out {}; @@ -260,66 +254,6 @@ namespace phoenix { beg_it += 2; } - return buffer::of(std::move(out)); - } - - std::variant archive_reader_ascii::unstable__next() { - static archive_object tmp {}; - if (read_object_begin(tmp)) { - return tmp; - } else if (read_object_end()) { - return archive_object_end {}; - } else { - input.mark(); - - archive_entry entry {}; - - auto line = input.get_line(); - entry.name = line.substr(line.find('=')); - - line = line.substr(line.find('=') + 1); - entry.type = type_name_to_enum.at(line.substr(0, line.find(':'))); - - input.reset(); - - switch (entry.type) { - case archive_entry_type::string: - entry.value = read_string(); - break; - case archive_entry_type::int_: - entry.value = read_int(); - break; - case archive_entry_type::float_: - entry.value = read_float(); - break; - case archive_entry_type::byte: - entry.value = read_byte(); - break; - case archive_entry_type::word: - entry.value = read_word(); - break; - case archive_entry_type::bool_: - entry.value = read_bool(); - break; - case archive_entry_type::vec3: - entry.value = read_vec3(); - break; - case archive_entry_type::color: - entry.value = read_color(); - break; - case archive_entry_type::raw: - case archive_entry_type::raw_float: - entry.value = read_raw_bytes(); - break; - case archive_entry_type::enum_: - entry.value = read_enum(); - break; - case archive_entry_type::hash: - entry.value = 0u; - break; - } - - return entry; - } + return Read::from(std::move(out)); } -} // namespace phoenix +} // namespace zenkit diff --git a/src/archive/ArchiveAscii.hh b/src/archive/ArchiveAscii.hh index 5dde44e8..8b0ff32c 100644 --- a/src/archive/ArchiveAscii.hh +++ b/src/archive/ArchiveAscii.hh @@ -1,15 +1,19 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" -namespace phoenix { - class archive_reader_ascii final : public archive_reader { +namespace zenkit { + class ReadArchiveAscii final : public ReadArchive { public: - inline archive_reader_ascii(buffer& in, archive_header&& parent_header) - : archive_reader(in, std::move(parent_header)) {} + inline ReadArchiveAscii(ArchiveHeader&& parent_header, Read* r, std::unique_ptr owned) + : ReadArchive(std::forward(parent_header), r, std::move(owned)) {} - bool read_object_begin(archive_object& obj) override; + inline ReadArchiveAscii(ArchiveHeader&& parent_header, Read* r) + : ReadArchive(std::forward(parent_header), r) {} + + bool read_object_begin(ArchiveObject& obj) override; bool read_object_end() override; std::string read_string() override; std::int32_t read_int() override; @@ -21,12 +25,10 @@ namespace phoenix { glm::u8vec4 read_color() override; glm::vec3 read_vec3() override; glm::vec2 read_vec2() override; - bounding_box read_bbox() override; + AxisAlignedBoundingBox read_bbox() override; glm::mat3x3 read_mat3x3() override; - buffer read_raw_bytes() override; - buffer read_raw_bytes(uint32_t size) override; - - std::variant unstable__next() override; + ZKREM("Deprecated") phoenix::buffer read_raw_bytes(uint32_t size) override; + std::unique_ptr read_raw(uint32_t size) override; protected: void read_header() override; diff --git a/src/archive/ArchiveBinary.cc b/src/archive/ArchiveBinary.cc index 16c1e80d..0b153fa7 100644 --- a/src/archive/ArchiveBinary.cc +++ b/src/archive/ArchiveBinary.cc @@ -1,127 +1,132 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include "archive_binary.hh" +#include "ArchiveBinary.hh" + +#include "phoenix/buffer.hh" +#include "phoenix/phoenix.hh" #include -namespace phoenix { - void archive_reader_binary::read_header() { +namespace zenkit { + void ReadArchiveBinary::read_header() { { - std::string objects = input.get_line(); + std::string objects = read->read_line(true); if (objects.find("objects ") != 0) { - throw parser_error {"archive_reader_binary", "objects header field missing"}; + throw zenkit::ParserError {"ReadArchiveBinary", "objects header field missing"}; } try { _m_objects = std::stoi(objects.substr(objects.find(' ') + 1)); } catch (std::invalid_argument const& e) { - throw parser_error {"archive_reader_binary", e, "reading int"}; + throw zenkit::ParserError {"ReadArchiveBinary", e, "reading int"}; } } - if (input.get_line_and_ignore("\n") != "END") { - throw parser_error {"archive_reader_binary", "second END missing"}; + if (read->read_line_then_ignore("\n") != "END") { + throw zenkit::ParserError {"ReadArchiveBinary", "second END missing"}; } } - bool archive_reader_binary::read_object_begin(archive_object& obj) { - if (input.remaining() < 12) + bool ReadArchiveBinary::read_object_begin(ArchiveObject& obj) { + if (read->eof()) return false; - auto pos = input.position(); - _m_object_end.push(pos + input.get_uint()); + auto pos = read->tell(); + _m_object_end.push(pos + read->read_uint()); - obj.version = input.get_ushort(); - obj.index = input.get_uint(); - obj.object_name = input.get_line(false); - obj.class_name = input.get_line(false); + obj.version = read->read_ushort(); + obj.index = read->read_uint(); + obj.object_name = read->read_line(false); + obj.class_name = read->read_line(false); return true; } - bool archive_reader_binary::read_object_end() { - if (input.position() == _m_object_end.top()) { + bool ReadArchiveBinary::read_object_end() { + if (read->tell() == _m_object_end.top()) { _m_object_end.pop(); return true; } - return input.remaining() == 0; + return read->eof(); } - std::string archive_reader_binary::read_string() { - return input.get_line(false); + std::string ReadArchiveBinary::read_string() { + return read->read_line(false); } - std::int32_t archive_reader_binary::read_int() { - return input.get_int(); + std::int32_t ReadArchiveBinary::read_int() { + return read->read_int(); } - float archive_reader_binary::read_float() { - return input.get_float(); + float ReadArchiveBinary::read_float() { + return read->read_float(); } - std::uint8_t archive_reader_binary::read_byte() { - return input.get(); + std::uint8_t ReadArchiveBinary::read_byte() { + return read->read_ubyte(); } - std::uint16_t archive_reader_binary::read_word() { - return input.get_ushort(); + std::uint16_t ReadArchiveBinary::read_word() { + return read->read_ushort(); } - std::uint32_t archive_reader_binary::read_enum() { - return input.get(); + std::uint32_t ReadArchiveBinary::read_enum() { + return read->read_ubyte(); } - bool archive_reader_binary::read_bool() { - return input.get() != 0; + bool ReadArchiveBinary::read_bool() { + return read->read_ubyte() != 0; } - glm::u8vec4 archive_reader_binary::read_color() { - auto b = input.get(); - auto g = input.get(); - auto r = input.get(); - auto a = input.get(); + glm::u8vec4 ReadArchiveBinary::read_color() { + auto b = read->read_ubyte(); + auto g = read->read_ubyte(); + auto r = read->read_ubyte(); + auto a = read->read_ubyte(); return {r, g, b, a}; } - glm::vec3 archive_reader_binary::read_vec3() { - return input.get_vec3(); + glm::vec3 ReadArchiveBinary::read_vec3() { + return read->read_vec3(); } - glm::vec2 archive_reader_binary::read_vec2() { - return input.get_vec2(); + glm::vec2 ReadArchiveBinary::read_vec2() { + return read->read_vec2(); } - bounding_box archive_reader_binary::read_bbox() { - return bounding_box::parse(input); + AxisAlignedBoundingBox ReadArchiveBinary::read_bbox() { + AxisAlignedBoundingBox aabb {}; + aabb.load(read); + return aabb; } - glm::mat3x3 archive_reader_binary::read_mat3x3() { - return input.get_mat3x3(); + glm::mat3x3 ReadArchiveBinary::read_mat3x3() { + return read->read_mat3(); } - buffer archive_reader_binary::read_raw_bytes() { - return input.slice(); + phoenix::buffer ReadArchiveBinary::read_raw_bytes(uint32_t size) { + std::vector bytes(size, std::byte {}); + read->read(bytes.data(), bytes.size()); + return phoenix::buffer::of(std::move(bytes)); } - buffer archive_reader_binary::read_raw_bytes(uint32_t size) { - return input.extract(size); + std::unique_ptr ReadArchiveBinary::read_raw(uint32_t size) { + std::vector bytes(size, std::byte {}); + read->read(bytes.data(), bytes.size()); + return Read::from(std::move(bytes)); } - void archive_reader_binary::skip_entry() { - throw parser_error {"archive_reader", "cannot skip entry in binary archive"}; + void ReadArchiveBinary::skip_entry() { + throw zenkit::ParserError {"archive_reader", "cannot skip entry in binary archive"}; } - void archive_reader_binary::skip_object(bool skip_current) { + void ReadArchiveBinary::skip_object(bool skip_current) { if (skip_current) { - input.position(_m_object_end.top()); + read->seek(static_cast(_m_object_end.top()), Whence::BEG); _m_object_end.pop(); } else { - input.skip(input.get_uint() - 4); + read->seek(read->read_uint() - 4, Whence::CUR); } } - - std::variant archive_reader_binary::unstable__next() { - throw parser_error {"archive_reader", "next() doesn't work for binary archives"}; - } -} // namespace phoenix +} // namespace zenkit diff --git a/src/archive/ArchiveBinary.hh b/src/archive/ArchiveBinary.hh index 5e13d08e..ea6def3b 100644 --- a/src/archive/ArchiveBinary.hh +++ b/src/archive/ArchiveBinary.hh @@ -1,17 +1,21 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" #include -namespace phoenix { - class archive_reader_binary final : public archive_reader { +namespace zenkit { + class ReadArchiveBinary final : public ReadArchive { public: - inline archive_reader_binary(buffer& in, archive_header&& parent_header) - : archive_reader(in, std::move(parent_header)) {} + inline ReadArchiveBinary(ArchiveHeader&& parent_header, Read* r, std::unique_ptr owned) + : ReadArchive(std::forward(parent_header), r, std::move(owned)) {} - bool read_object_begin(archive_object& obj) override; + inline ReadArchiveBinary(ArchiveHeader&& parent_header, Read* r) + : ReadArchive(std::forward(parent_header), r) {} + + bool read_object_begin(ArchiveObject& obj) override; bool read_object_end() override; std::string read_string() override; std::int32_t read_int() override; @@ -23,13 +27,12 @@ namespace phoenix { glm::u8vec4 read_color() override; glm::vec3 read_vec3() override; glm::vec2 read_vec2() override; - bounding_box read_bbox() override; + AxisAlignedBoundingBox read_bbox() override; glm::mat3x3 read_mat3x3() override; - buffer read_raw_bytes() override; - buffer read_raw_bytes(uint32_t size) override; + ZKREM("use ::read_raw") phoenix::buffer read_raw_bytes(uint32_t size) override; + std::unique_ptr read_raw(uint32_t size) override; void skip_object(bool skip_current) override; - std::variant unstable__next() override; protected: void read_header() override; @@ -39,4 +42,4 @@ namespace phoenix { std::stack _m_object_end {}; int32_t _m_objects {0}; }; -} // namespace phoenix +} // namespace zenkit diff --git a/src/archive/ArchiveBinsafe.cc b/src/archive/ArchiveBinsafe.cc index 27cadccc..11c8bc0e 100644 --- a/src/archive/ArchiveBinsafe.cc +++ b/src/archive/ArchiveBinsafe.cc @@ -1,56 +1,60 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include "archive_binsafe.hh" +#include "ArchiveBinsafe.hh" + +#include "phoenix/buffer.hh" + +#include "../Internal.hh" #include -#include -namespace phoenix { - void archive_reader_binsafe::read_header() { - _m_bs_version = input.get_uint(); - _m_object_count = input.get_uint(); - auto hash_table_offset = input.get_uint(); +namespace zenkit { + void ReadArchiveBinsafe::read_header() { + _m_bs_version = read->read_uint(); + _m_object_count = read->read_uint(); + auto hash_table_offset = read->read_uint(); { - input.mark(); - input.position(hash_table_offset); - auto hash_table_size = input.get_uint(); + auto mark = read->tell(); + read->seek(hash_table_offset, Whence::BEG); + + auto hash_table_size = read->read_uint(); _m_hash_table_entries.resize(hash_table_size); for (std::uint32_t i = 0; i < hash_table_size; ++i) { - auto key_length = input.get_ushort(); - auto insertion_index = input.get_ushort(); - auto hash_value = input.get_uint(); - auto key = input.get_string(key_length); + auto key_length = read->read_ushort(); + auto insertion_index = read->read_ushort(); + auto hash_value = read->read_uint(); + auto key = read->read_string(key_length); _m_hash_table_entries[insertion_index] = hash_table_entry {key, hash_value}; } - input.reset(); + read->seek(static_cast(mark), Whence::BEG); } } - bool archive_reader_binsafe::read_object_begin(archive_object& obj) { - if (input.remaining() < 6) + bool ReadArchiveBinsafe::read_object_begin(ArchiveObject& obj) { + if (read->eof()) return false; - input.mark(); - if (static_cast(input.get()) != archive_entry_type::string) { - input.reset(); + auto mark = read->tell(); + if (static_cast(read->read_ubyte()) != ArchiveEntryType::STRING) { + read->seek(static_cast(mark), Whence::BEG); return false; } - auto line = input.get_string(input.get_ushort()); + auto line = read->read_string(read->read_ushort()); // Fail quickly if we know this can't be an object begin if (line.length() <= 2) { - input.reset(); + read->seek(static_cast(mark), Whence::BEG); return false; } char* it = line.data(); if (*it++ != '[') { - input.reset(); + read->seek(static_cast(mark), Whence::BEG); return false; } @@ -60,7 +64,7 @@ namespace phoenix { auto* index = strtok(nullptr, " ]"); if (object_name == nullptr || class_name == nullptr || version == nullptr || index == nullptr) { - input.reset(); + read->seek(static_cast(mark), Whence::BEG); return false; } @@ -71,242 +75,216 @@ namespace phoenix { return true; } - bool archive_reader_binsafe::read_object_end() { - if (input.remaining() == 0) + bool ReadArchiveBinsafe::read_object_end() { + if (read->eof()) return true; - if (input.remaining() < 6) - return false; - input.mark(); - if (static_cast(input.get()) != archive_entry_type::string) { - input.reset(); + auto mark = read->tell(); + if (static_cast(read->read_ubyte()) != ArchiveEntryType::STRING) { + read->seek(static_cast(mark), Whence::BEG); return false; } - if (input.get_ushort() != 2) { - input.reset(); + if (read->read_ushort() != 2) { + read->seek(static_cast(mark), Whence::BEG); return false; } - if (input.get_string(2) != "[]") { - input.reset(); + if (read->read_string(2) != "[]") { + read->seek(static_cast(mark), Whence::BEG); return false; } return true; } - const std::string& archive_reader_binsafe::get_entry_key() { - if (static_cast(input.get()) != archive_entry_type::hash) { - throw parser_error {"archive_reader_binsafe", "invalid format"}; + const std::string& ReadArchiveBinsafe::get_entry_key() { + if (static_cast(read->read_ubyte()) != ArchiveEntryType::HASH) { + throw zenkit::ParserError {"ReadArchive.Binsafe", "invalid format"}; } - auto hash = input.get_uint(); + auto hash = read->read_uint(); return _m_hash_table_entries[hash].key; } - std::string archive_reader_binsafe::read_string() { - return input.get_string(ensure_entry_meta()); + std::string ReadArchiveBinsafe::read_string() { + return read->read_string(ensure_entry_meta()); } - std::int32_t archive_reader_binsafe::read_int() { - ensure_entry_meta(); - return input.get_int(); + std::int32_t ReadArchiveBinsafe::read_int() { + ensure_entry_meta(); + return read->read_int(); } - float archive_reader_binsafe::read_float() { - ensure_entry_meta(); - return input.get_float(); + float ReadArchiveBinsafe::read_float() { + ensure_entry_meta(); + return read->read_float(); } - std::uint8_t archive_reader_binsafe::read_byte() { - ensure_entry_meta(); - return input.get(); + std::uint8_t ReadArchiveBinsafe::read_byte() { + ensure_entry_meta(); + return read->read_ubyte(); } - std::uint16_t archive_reader_binsafe::read_word() { - ensure_entry_meta(); - return input.get_ushort(); + std::uint16_t ReadArchiveBinsafe::read_word() { + ensure_entry_meta(); + return read->read_ushort(); } - std::uint32_t archive_reader_binsafe::read_enum() { - ensure_entry_meta(); - return input.get_uint(); + std::uint32_t ReadArchiveBinsafe::read_enum() { + ensure_entry_meta(); + return read->read_uint(); } - bool archive_reader_binsafe::read_bool() { - ensure_entry_meta(); - return input.get_uint() != 0; + bool ReadArchiveBinsafe::read_bool() { + ensure_entry_meta(); + return read->read_uint() != 0; } - glm::u8vec4 archive_reader_binsafe::read_color() { - ensure_entry_meta(); + glm::u8vec4 ReadArchiveBinsafe::read_color() { + ensure_entry_meta(); - auto b = input.get(); - auto g = input.get(); - auto r = input.get(); - auto a = input.get(); + auto b = read->read_ubyte(); + auto g = read->read_ubyte(); + auto r = read->read_ubyte(); + auto a = read->read_ubyte(); return {r, g, b, a}; } - glm::vec3 archive_reader_binsafe::read_vec3() { - ensure_entry_meta(); - return input.get_vec3(); + glm::vec3 ReadArchiveBinsafe::read_vec3() { + ensure_entry_meta(); + return read->read_vec3(); } - glm::vec2 archive_reader_binsafe::read_vec2() { - auto unused = static_cast(ensure_entry_meta() - 2 * sizeof(float)); + glm::vec2 ReadArchiveBinsafe::read_vec2() { + auto unused = static_cast(ensure_entry_meta() - 2 * sizeof(float)); if (unused < 0) { - throw parser_error {"archive_reader_binsafe" - "cannot read vec2 (2 * float): not enough space in rawFloat entry."}; + throw zenkit::ParserError {"ReadArchive.Binsafe" + "cannot read vec2 (2 * float): not enough space in rawFloat entry."}; } - auto c = input.get_vec2(); + auto c = read->read_vec2(); // There might be more bytes in this. We'll ignore them. - input.skip(unused); + read->seek(unused, Whence::CUR); return c; } - bounding_box archive_reader_binsafe::read_bbox() { + AxisAlignedBoundingBox ReadArchiveBinsafe::read_bbox() { auto unused = - static_cast(ensure_entry_meta() - 3 * 2 * sizeof(float)); + static_cast(ensure_entry_meta() - 3 * 2 * sizeof(float)); if (unused < 0) { - throw parser_error {"archive:reader_binsafe", - "cannot read bbox (6 * float): not enough space in rawFloat entry."}; + throw zenkit::ParserError {"ReadArchive.Binsafe", + "cannot read bbox (6 * float): not enough space in rawFloat entry."}; } - auto c = bounding_box::parse(input); + AxisAlignedBoundingBox aabb {}; + aabb.load(read); // There might be more bytes in this. We'll ignore them. - input.skip(unused); - return c; + read->seek(unused, Whence::CUR); + return aabb; } - glm::mat3x3 archive_reader_binsafe::read_mat3x3() { - auto unused = static_cast(ensure_entry_meta() - 3 * 3 * sizeof(float)); + glm::mat3x3 ReadArchiveBinsafe::read_mat3x3() { + auto unused = static_cast(ensure_entry_meta() - 3 * 3 * sizeof(float)); if (unused < 0) { - throw parser_error( - "archive_reader_binsafe: cannot read mat3x3 (9 * float): not enough space in raw entry."); + throw zenkit::ParserError( + "ReadArchive.Binsafe: cannot read mat3x3 (9 * float): not enough space in raw entry."); } - auto v = input.get_mat3x3(); - input.skip(unused); + auto v = read->read_mat3(); + + // There might be more bytes in this. We'll ignore them. + read->seek(unused, Whence::CUR); return v; } - buffer archive_reader_binsafe::read_raw_bytes() { - auto length = ensure_entry_meta(); - return input.extract(length); + phoenix::buffer ReadArchiveBinsafe::read_raw_bytes(uint32_t size) { + auto length = ensure_entry_meta(); + + if (length < size) { + throw zenkit::ParserError {"ReadArchive.Binsafe", "not enough raw bytes to read!"}; + } else if (length > size) { + ZKLOGW("ReadArchive.Binsafe", "Reading %d bytes although %d are actually available", size, length); + } + + std::vector bytes(length, std::byte {}); + read->read(bytes.data(), length); + return phoenix::buffer::of(std::move(bytes)); } - buffer archive_reader_binsafe::read_raw_bytes(uint32_t size) { - auto length = ensure_entry_meta(); + std::unique_ptr ReadArchiveBinsafe::read_raw(uint32_t size) { + auto length = ensure_entry_meta(); if (length < size) { - throw parser_error {"archive_reader_binsafe", "not enough raw bytes to read!"}; + throw zenkit::ParserError {"ReadArchive.Binsafe", "not enough raw bytes to read!"}; } else if (length > size) { - PX_LOGW("read_raw_bytes: reading ", size, " bytes although ", length, " are actually available"); + ZKLOGW("ReadArchive.Binsafe", "Reading %d bytes although %d are actually available", size, length); } - return input.extract(length); + std::vector bytes(length, std::byte {}); + read->read(bytes.data(), length); + return Read::from(std::move(bytes)); } - void archive_reader_binsafe::skip_entry() { - auto type = static_cast(input.get()); + void ReadArchiveBinsafe::skip_entry() { + auto type = static_cast(read->read_ubyte()); switch (type) { - case archive_entry_type::string: - case archive_entry_type::raw: - case archive_entry_type::raw_float: - input.skip(input.get_ushort()); + case ArchiveEntryType::STRING: + case ArchiveEntryType::RAW: + case ArchiveEntryType::RAW_FLOAT: + read->seek(read->read_ushort(), Whence::CUR); break; - case archive_entry_type::enum_: - case archive_entry_type::hash: - case archive_entry_type::int_: - case archive_entry_type::float_: - case archive_entry_type::bool_: - case archive_entry_type::color: - (void) input.get_uint(); + case ArchiveEntryType::ENUM: + case ArchiveEntryType::HASH: + case ArchiveEntryType::INTEGER: + case ArchiveEntryType::FLOAT: + case ArchiveEntryType::BOOL: + case ArchiveEntryType::COLOR: + (void) read->read_uint(); break; - case archive_entry_type::byte: - (void) input.get(); + case ArchiveEntryType::BYTE: + (void) read->read_ubyte(); break; - case archive_entry_type::word: - (void) input.get_ushort(); + case ArchiveEntryType::WORD: + (void) read->read_ushort(); break; - case archive_entry_type::vec3: - (void) input.get_float(); - (void) input.get_float(); - (void) input.get_float(); + case ArchiveEntryType::VEC3: + (void) read->read_float(); + (void) read->read_float(); + (void) read->read_float(); break; } } - std::variant archive_reader_binsafe::unstable__next() { - static archive_object obj {}; - if (read_object_begin(obj)) { - return obj; - } else if (read_object_end()) { - return archive_object_end {}; - } else { - archive_entry entry {}; - entry.name = get_entry_key(); - entry.type = static_cast(input.get()); - - switch (entry.type) { - case archive_entry_type::string: - entry.value = input.get_string(input.get_ushort()); - break; - case archive_entry_type::raw: - case archive_entry_type::raw_float: - entry.value = input.extract(input.get_ushort()); - break; - case archive_entry_type::enum_: - entry.value = input.get_uint(); - break; - case archive_entry_type::hash: - entry.value = input.get_uint(); - break; - case archive_entry_type::int_: - entry.value = input.get_int(); - break; - case archive_entry_type::float_: - entry.value = input.get_float(); - break; - case archive_entry_type::bool_: - entry.value = input.get_uint() != 0; - break; - case archive_entry_type::color: { - auto b = input.get(); - auto g = input.get(); - auto r = input.get(); - auto a = input.get(); - - entry.value = glm::u8vec4 {r, g, b, a}; - break; - } - case archive_entry_type::byte: - entry.value = input.get(); - break; - case archive_entry_type::word: - entry.value = input.get_ushort(); - break; - case archive_entry_type::vec3: { - auto x = input.get_float(); - auto y = input.get_float(); - auto z = input.get_float(); - - entry.value = glm::vec3 {x, y, z}; - break; - } - } + template + std::uint16_t ReadArchiveBinsafe::ensure_entry_meta() { + auto type = static_cast(read->read_ubyte()); - return entry; + if (type != ArchiveEntryType::HASH) { + throw zenkit::ParserError {"ReadArchive.Binsafe", "invalid format"}; + } + + read->seek(sizeof(uint32_t), Whence::CUR); + type = static_cast(read->read_ubyte()); + + if (type != tp) { + throw zenkit::ParserError {"ReadArchive.Binsafe: type mismatch: expected " + + std::to_string(static_cast(tp)) + + ", got: " + std::to_string(static_cast(type))}; + } + + if constexpr (tp == ArchiveEntryType::STRING || tp == ArchiveEntryType::RAW || + tp == ArchiveEntryType::RAW_FLOAT) { + return read->read_ushort(); + } else { + return type_sizes[static_cast(type)]; } } -} // namespace phoenix +} // namespace zenkit diff --git a/src/archive/ArchiveBinsafe.hh b/src/archive/ArchiveBinsafe.hh index e9a93523..8ba2204b 100644 --- a/src/archive/ArchiveBinsafe.hh +++ b/src/archive/ArchiveBinsafe.hh @@ -1,11 +1,13 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #pragma once -#include +#include "zenkit/Archive.hh" +#include "zenkit/Stream.hh" + #include #include -namespace phoenix { +namespace zenkit { static constexpr const std::uint8_t type_sizes[] = { 0, // ? = 0x00 0, // bs_string = 0x01, @@ -33,12 +35,15 @@ namespace phoenix { std::uint32_t hash; // TODO: I don't know what this is. }; - class archive_reader_binsafe final : public archive_reader { + class ReadArchiveBinsafe final : public ReadArchive { public: - inline archive_reader_binsafe(buffer& in, archive_header&& parent_header) - : archive_reader(in, std::move(parent_header)) {} + inline ReadArchiveBinsafe(ArchiveHeader&& parent_header, Read* r, std::unique_ptr owned) + : ReadArchive(std::forward(parent_header), r, std::move(owned)) {} + + inline ReadArchiveBinsafe(ArchiveHeader&& parent_header, Read* r) + : ReadArchive(std::forward(parent_header), r) {} - bool read_object_begin(archive_object& obj) override; + bool read_object_begin(ArchiveObject& obj) override; bool read_object_end() override; std::string read_string() override; std::int32_t read_int() override; @@ -50,12 +55,10 @@ namespace phoenix { glm::u8vec4 read_color() override; glm::vec3 read_vec3() override; glm::vec2 read_vec2() override; - bounding_box read_bbox() override; + AxisAlignedBoundingBox read_bbox() override; glm::mat3x3 read_mat3x3() override; - buffer read_raw_bytes() override; - buffer read_raw_bytes(uint32_t size) override; - - std::variant unstable__next() override; + ZKREM("Deprecated") phoenix::buffer read_raw_bytes(uint32_t size) override; + std::unique_ptr read_raw(uint32_t size) override; protected: void read_header() override; @@ -63,30 +66,8 @@ namespace phoenix { const std::string& get_entry_key(); - template - std::uint16_t ensure_entry_meta() { - auto type = static_cast(input.get()); - - if (type != archive_entry_type::hash) { - throw parser_error {"archive_reader_binsafe", "invalid format"}; - } - - input.skip(sizeof(uint32_t)); - type = static_cast(input.get()); - - if (type != tp) { - throw parser_error {"archive_reader_binsafe: type mismatch: expected " + - std::to_string(static_cast(tp)) + - ", got: " + std::to_string(static_cast(type))}; - } - - if constexpr (tp == archive_entry_type::string || tp == archive_entry_type::raw || - tp == archive_entry_type::raw_float) { - return input.get_ushort(); - } else { - return type_sizes[static_cast(type)]; - } - } + template + std::uint16_t ensure_entry_meta(); private: std::uint32_t _m_object_count {0}; diff --git a/src/vobs/Camera.cc b/src/vobs/Camera.cc index 1af99354..3e5dc2fc 100644 --- a/src/vobs/Camera.cc +++ b/src/vobs/Camera.cc @@ -1,71 +1,81 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/vobs/Camera.hh" +#include "zenkit/Archive.hh" -namespace phoenix::vobs { - std::unique_ptr camera_trj_frame::parse(archive_reader& ctx, game_version version) { - auto obj = std::make_unique(); +#include "../Internal.hh" - vob::parse(*obj, ctx, version); - obj->time = ctx.read_float(); // time - obj->roll_angle = ctx.read_float(); // angleRollDeg - obj->fov_scale = ctx.read_float(); // camFOVScale - obj->motion_type = static_cast(ctx.read_enum()); // motionType - obj->motion_type_fov = static_cast(ctx.read_enum()); // motionTypeFOV - obj->motion_type_roll = static_cast(ctx.read_enum()); // motionTypeRoll - obj->motion_type_time_scale = static_cast(ctx.read_enum()); // motionTypeTimeScale - obj->tension = ctx.read_float(); // tension - obj->cam_bias = ctx.read_float(); // bias - obj->continuity = ctx.read_float(); // continuity - obj->time_scale = ctx.read_float(); // timeScale - obj->time_fixed = ctx.read_bool(); // timeIsFixed - - auto buf = ctx.read_raw_bytes(sizeof(float) * 4 * 4); // originalPose - obj->original_pose = buf.get_mat4x4(); +namespace zenkit::vobs { + std::unique_ptr CameraTrajectoryFrame::parse(ReadArchive& r, GameVersion version) { + auto obj = std::make_unique(); + obj->load(r, version); return obj; } - void cs_camera::parse(cs_camera& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.trajectory_for = static_cast(ctx.read_enum()); // camTrjFOR - obj.target_trajectory_for = static_cast(ctx.read_enum()); // targetTrjFOR - obj.loop_mode = static_cast(ctx.read_enum()); // loopMode - obj.lerp_mode = static_cast(ctx.read_enum()); // splLerpMode - obj.ignore_for_vob_rotation = ctx.read_bool(); // ignoreFORVobRotCam - obj.ignore_for_vob_rotation_target = ctx.read_bool(); // ignoreFORVobRotTarget - obj.adapt = ctx.read_bool(); // adaptToSurroundings - obj.ease_first = ctx.read_bool(); // easeToFirstKey - obj.ease_last = ctx.read_bool(); // easeFromLastKey - obj.total_duration = ctx.read_float(); // totalTime - obj.auto_focus_vob = ctx.read_string(); // autoCamFocusVobName - obj.auto_player_movable = ctx.read_bool(); // autoCamPlayerMovable - obj.auto_untrigger_last = ctx.read_bool(); // autoCamUntriggerOnLastKey - obj.auto_untrigger_last_delay = ctx.read_float(); // autoCamUntriggerOnLastKeyDelay - obj.position_count = ctx.read_int(); // numPos - obj.target_count = ctx.read_int(); // numTargets + void CameraTrajectoryFrame::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->time = r.read_float(); // time + this->roll_angle = r.read_float(); // angleRollDeg + this->fov_scale = r.read_float(); // camFOVScale + this->motion_type = static_cast(r.read_enum()); // motionType + this->motion_type_fov = static_cast(r.read_enum()); // motionTypeFOV + this->motion_type_roll = static_cast(r.read_enum()); // motionTypeRoll + this->motion_type_time_scale = static_cast(r.read_enum()); // motionTypeTimeScale + this->tension = r.read_float(); // tension + this->cam_bias = r.read_float(); // bias + this->continuity = r.read_float(); // continuity + this->time_scale = r.read_float(); // timeScale + this->time_fixed = r.read_bool(); // timeIsFixed + + auto buf = r.read_raw(sizeof(float) * 4 * 4); // originalPose + this->original_pose = buf->read_mat4(); + } + + void CutsceneCamera::parse(CutsceneCamera& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void CutsceneCamera::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->trajectory_for = static_cast(r.read_enum()); // camTrjFOR + this->target_trajectory_for = static_cast(r.read_enum()); // targetTrjFOR + this->loop_mode = static_cast(r.read_enum()); // loopMode + this->lerp_mode = static_cast(r.read_enum()); // splLerpMode + this->ignore_for_vob_rotation = r.read_bool(); // ignoreFORVobRotCam + this->ignore_for_vob_rotation_target = r.read_bool(); // ignoreFORVobRotTarget + this->adapt = r.read_bool(); // adaptToSurroundings + this->ease_first = r.read_bool(); // easeToFirstKey + this->ease_last = r.read_bool(); // easeFromLastKey + this->total_duration = r.read_float(); // totalTime + this->auto_focus_vob = r.read_string(); // autoCamFocusVobName + this->auto_player_movable = r.read_bool(); // autoCamPlayerMovable + this->auto_untrigger_last = r.read_bool(); // autoCamUntriggerOnLastKey + this->auto_untrigger_last_delay = r.read_float(); // autoCamUntriggerOnLastKeyDelay + this->position_count = r.read_int(); // numPos + this->target_count = r.read_int(); // numTargets - archive_object frame_obj {}; - while (ctx.read_object_begin(frame_obj)) { + ArchiveObject frame_obj {}; + while (r.read_object_begin(frame_obj)) { if (frame_obj.class_name != "zCCamTrj_KeyFrame:zCVob") { - PX_LOGW("cs_camera: unexpected \"", frame_obj.class_name, "\" in \"zCCSCamera:zCVob\""); - ctx.skip_object(true); + ZKLOGW("VOb.CutsceneCamera", "Unexpected \"%s\" in \"zCCSCamera:zCVob\"", frame_obj.class_name.c_str()); + r.skip_object(true); continue; } - obj.frames.emplace_back(camera_trj_frame::parse(ctx, version)); + this->frames.emplace_back(std::make_unique())->load(r, version); - if (!ctx.read_object_end()) { - PX_LOGW("cs_camera: \"zCCamTrj_KeyFrame\" not fully parsed"); - ctx.skip_object(true); + if (!r.read_object_end()) { + ZKLOGW("VOb.CutsceneCamera", "\"zCCamTrj_KeyFrame\" not fully parsed"); + r.skip_object(true); } } - if (ctx.is_save_game() && version == game_version::gothic_2) { + if (r.is_save_game() && version == GameVersion::GOTHIC_2) { // In save-games, cutscene cameras contain extra variables - obj.s_paused = ctx.read_bool(); // paused - obj.s_started = ctx.read_bool(); // started - obj.s_goto_time_mode = ctx.read_bool(); // gotoTimeMode - obj.s_cs_time = ctx.read_float(); // csTime + this->s_paused = r.read_bool(); // paused + this->s_started = r.read_bool(); // started + this->s_goto_time_mode = r.read_bool(); // gotoTimeMode + this->s_cs_time = r.read_float(); // csTime } } -} // namespace phoenix::vobs +} // namespace zenkit::vobs diff --git a/src/vobs/Light.cc b/src/vobs/Light.cc index 2b1fb33d..4a3abf8e 100644 --- a/src/vobs/Light.cc +++ b/src/vobs/Light.cc @@ -1,36 +1,43 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/vobs/Light.hh" +#include "zenkit/Archive.hh" + +#include "../Internal.hh" #include -namespace phoenix::vobs { - void light_preset::parse(light_preset& obj, archive_reader& in, game_version version) { - obj.preset = in.read_string(); // lightPresetInUse - obj.light_type = static_cast(in.read_enum()); // lightType - obj.range = in.read_float(); // range - obj.color = in.read_color(); // color - obj.cone_angle = in.read_float(); // spotConeAngle - obj.is_static = in.read_bool(); // lightStatic - obj.quality = static_cast(in.read_enum()); // lightQuality - obj.lensflare_fx = in.read_string(); // lensflareFX - - if (!obj.is_static) { - obj.on = in.read_bool(); // turnedOn - auto range_ani_scale = in.read_string(); // rangeAniScale - obj.range_animation_fps = in.read_float(); // rangeAniFPS - obj.range_animation_smooth = in.read_bool(); // rangeAniSmooth - auto color_animation_list = in.read_string(); // colorAniList - obj.color_animation_fps = in.read_float(); // colorAniFPS - obj.color_animation_smooth = in.read_bool(); // colorAniSmooth +namespace zenkit::vobs { + void LightPreset::parse(LightPreset& obj, ReadArchive& in, GameVersion version) { + obj.load(in, version); + } + + void LightPreset::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + this->preset = r.read_string(); // lightPresetInUse + this->light_type = static_cast(r.read_enum()); // lightType + this->range = r.read_float(); // range + this->color = r.read_color(); // color + this->cone_angle = r.read_float(); // spotConeAngle + this->is_static = r.read_bool(); // lightStatic + this->quality = static_cast(r.read_enum()); // lightQuality + this->lensflare_fx = r.read_string(); // lensflareFX + + if (!this->is_static) { + this->on = r.read_bool(); // turnedOn + auto range_ani_scale = r.read_string(); // rangeAniScale + this->range_animation_fps = r.read_float(); // rangeAniFPS + this->range_animation_smooth = r.read_bool(); // rangeAniSmooth + auto cal = r.read_string(); // colorAniList + this->color_animation_fps = r.read_float(); // colorAniFPS + this->color_animation_smooth = r.read_bool(); // colorAniSmooth std::istringstream ranges {range_ani_scale}; float value; while (ranges >> value) { - obj.range_animation_scale.push_back(value); + this->range_animation_scale.push_back(value); } - std::istringstream colors {color_animation_list}; + std::istringstream colors {cal}; colors.setf(std::ios::skipws); // # Original Format in ABNF: @@ -44,42 +51,46 @@ namespace phoenix::vobs { // color-scalar = 1*3DIGIT char c; - uint32_t r, g, b; + uint32_t cr, cg, cb; while (colors >> c) { if (::isdigit(c)) { colors.unget(); - colors >> r; - obj.color_animation_list.emplace_back(r, r, r, 255); + colors >> cr; + this->color_animation_list.emplace_back(cr, cr, cr, 255); continue; } if (c != '(') { - PX_LOGW("light_preset: failed parsing `colorAniList`: invalid char '", c, "'"); + ZKLOGW("LightPreset", "Failed parsing `colorAniList`: invalid char '%c'", c); } - colors >> r >> g >> b >> c; + colors >> cr >> cg >> cb >> c; if (c != ')') { - PX_LOGW("light_preset: failed parsing `colorAniList`: expected ')', got '", c, "'"); + ZKLOGW("LightPreset", "Failed parsing `colorAniList`: expected ')', got '%c'", c); } - obj.color_animation_list.emplace_back(r, g, b, 255); + this->color_animation_list.emplace_back(cr, cg, cb, 255); } - if (version == game_version::gothic_2) { - obj.can_move = in.read_bool(); // canMove + if (version == GameVersion::GOTHIC_2) { + this->can_move = r.read_bool(); // canMove } } } - light_preset light_preset::parse(archive_reader& ctx, game_version version) { - light_preset preset {}; - light_preset::parse(preset, ctx, version); + LightPreset LightPreset::parse(ReadArchive& ctx, GameVersion version) { + LightPreset preset {}; + preset.load(ctx, version); return preset; } - void light::parse(phoenix::vobs::light& obj, archive_reader& ctx, phoenix::game_version version) { - vob::parse(obj, ctx, version); - light_preset::parse(obj, ctx, version); + void Light::parse(Light& obj, ReadArchive& ctx, GameVersion version) { + obj.load(ctx, version); + } + + void Light::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + LightPreset::load(r, version); } -} // namespace phoenix::vobs +} // namespace zenkit::vobs diff --git a/src/vobs/Misc.cc b/src/vobs/Misc.cc index e7ebcc4a..685f4e4c 100644 --- a/src/vobs/Misc.cc +++ b/src/vobs/Misc.cc @@ -1,289 +1,369 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/vobs/Misc.hh" +#include "zenkit/Archive.hh" -namespace phoenix::vobs { - void animate::parse(animate& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.start_on = ctx.read_bool(); // startOn +#include "../Internal.hh" - if (ctx.is_save_game()) { +namespace zenkit::vobs { + void Animate::parse(Animate& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void Animate::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->start_on = r.read_bool(); // startOn + + if (r.is_save_game()) { // In save-games, animated VObs contain extra variables - obj.s_is_running = ctx.read_bool(); // isRunning + this->s_is_running = r.read_bool(); // isRunning } } - void item::parse(item& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.instance = ctx.read_string(); // itemInstance + void Item::parse(Item& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void Item::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->instance = r.read_string(); // itemInstance - if (ctx.is_save_game()) { + if (r.is_save_game()) { // In save-games, items contain extra variables - obj.s_amount = ctx.read_int(); // amount - obj.s_flags = ctx.read_int(); // flags + this->s_amount = r.read_int(); // amount + this->s_flags = r.read_int(); // flags } } - void lens_flare::parse(lens_flare& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.fx = ctx.read_string(); // lensflareFX + void LensFlare::parse(LensFlare& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); } - void pfx_controller::parse(pfx_controller& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.pfx_name = ctx.read_string(); // pfxName - obj.kill_when_done = ctx.read_bool(); // killVobWhenDone - obj.initially_running = ctx.read_bool(); // pfxStartOn + void LensFlare::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->fx = r.read_string(); // lensflareFX } - void message_filter::parse(message_filter& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.target = ctx.read_string(); // triggerTarget - obj.on_trigger = static_cast(ctx.read_enum()); // onTrigger - obj.on_untrigger = static_cast(ctx.read_enum()); // onUntrigger + void ParticleEffectController::parse(ParticleEffectController& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); } - void code_master::parse(code_master& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.target = ctx.read_string(); // triggerTarget - obj.ordered = ctx.read_bool(); // orderRelevant - obj.first_false_is_failure = ctx.read_bool(); // firstFalseIsFailure - obj.failure_target = ctx.read_string(); // triggerTargetFailure - obj.untriggered_cancels = ctx.read_bool(); // untriggerCancels + void ParticleEffectController::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->pfx_name = r.read_string(); // pfxName + this->kill_when_done = r.read_bool(); // killVobWhenDone + this->initially_running = r.read_bool(); // pfxStartOn + } + + void MessageFilter::parse(MessageFilter& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } - auto slave_count = ctx.read_byte(); // numSlaves + void MessageFilter::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->target = r.read_string(); // triggerTarget + this->on_trigger = static_cast(r.read_enum()); // onTrigger + this->on_untrigger = static_cast(r.read_enum()); // onUntrigger + } + + void CodeMaster::parse(CodeMaster& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void CodeMaster::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->target = r.read_string(); // triggerTarget + this->ordered = r.read_bool(); // orderRelevant + this->first_false_is_failure = r.read_bool(); // firstFalseIsFailure + this->failure_target = r.read_string(); // triggerTargetFailure + this->untriggered_cancels = r.read_bool(); // untriggerCancels + + auto slave_count = r.read_byte(); // numSlaves for (int32_t i = 0; i < slave_count; ++i) { - obj.slaves.emplace_back(ctx.read_string()); // slaveVobName[i] + this->slaves.emplace_back(r.read_string()); // slaveVobName[i] } - if (obj.saved && version == game_version::gothic_2) { + if (this->saved && version == GameVersion::GOTHIC_2) { // In Gothic II save-games, code masters contain extra variables - obj.s_num_triggered_slaves = ctx.read_byte(); // numSlavesTriggered + this->s_num_triggered_slaves = r.read_byte(); // numSlavesTriggered for (auto i = 0; i < slave_count; ++i) { // TODO: Figure out how to parse these correctly. - ctx.skip_object(false); // [slaveTriggered1 % 0 0] + r.skip_object(false); // [slaveTriggered1 % 0 0] } } } - void mover_controller::parse(mover_controller& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.target = ctx.read_string(); // triggerTarget + void MoverController::parse(MoverController& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void MoverController::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->target = r.read_string(); // triggerTarget - if (version == game_version::gothic_1) { - obj.message = static_cast(ctx.read_enum()); // moverMessage + if (version == GameVersion::GOTHIC_1) { + this->message = static_cast(r.read_enum()); // moverMessage } else { - obj.message = static_cast(ctx.read_byte()); // moverMessage + this->message = static_cast(r.read_byte()); // moverMessage } - obj.key = ctx.read_int(); // gotoFixedKey + this->key = r.read_int(); // gotoFixedKey } - void touch_damage::parse(touch_damage& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.damage = ctx.read_float(); // damage - obj.barrier = ctx.read_bool(); // Barrier - obj.blunt = ctx.read_bool(); // Blunt - obj.edge = ctx.read_bool(); // Edge - obj.fire = ctx.read_bool(); // Fire - obj.fly = ctx.read_bool(); // Fly - obj.magic = ctx.read_bool(); // Magic - obj.point = ctx.read_bool(); // Point - obj.fall = ctx.read_bool(); // Fall - obj.repeat_delay_sec = ctx.read_float(); // damageRepeatDelaySec - obj.volume_scale = ctx.read_float(); // damageVolDownScale - obj.collision = static_cast(ctx.read_enum()); // damageCollType + void TouchDamage::parse(TouchDamage& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); } - void earthquake::parse(earthquake& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.radius = ctx.read_float(); // radius - obj.duration = ctx.read_float(); // timeSec - obj.amplitude = ctx.read_vec3(); // amplitudeCM + void TouchDamage::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->damage = r.read_float(); // damage + this->barrier = r.read_bool(); // Barrier + this->blunt = r.read_bool(); // Blunt + this->edge = r.read_bool(); // Edge + this->fire = r.read_bool(); // Fire + this->fly = r.read_bool(); // Fly + this->magic = r.read_bool(); // Magic + this->point = r.read_bool(); // Point + this->fall = r.read_bool(); // Fall + this->repeat_delay_sec = r.read_float(); // damageRepeatDelaySec + this->volume_scale = r.read_float(); // damageVolDownScale + this->collision = static_cast(r.read_enum()); // damageCollType } - void vobs::npc::parse(vobs::npc& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); + void Earthquake::parse(Earthquake& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } - obj.npc_instance = ctx.read_string(); // npcInstance - obj.model_scale = ctx.read_vec3(); // modelScale - obj.model_fatness = ctx.read_float(); // modelFatness + void Earthquake::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->radius = r.read_float(); // radius + this->duration = r.read_float(); // timeSec + this->amplitude = r.read_vec3(); // amplitudeCM + } - auto overlay_count = ctx.read_int(); // numOverlays + void Npc::parse(vobs::Npc& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void Npc::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + + this->npc_instance = r.read_string(); // npcInstance + this->model_scale = r.read_vec3(); // modelScale + this->model_fatness = r.read_float(); // modelFatness + + auto overlay_count = r.read_int(); // numOverlays for (auto i = 0; i < overlay_count; ++i) { - obj.overlays.push_back(ctx.read_string()); // overlay + this->overlays.push_back(r.read_string()); // overlay } - obj.flags = ctx.read_int(); // flags - obj.guild = ctx.read_int(); // guild - obj.guild_true = ctx.read_int(); // guildTrue - obj.level = ctx.read_int(); // level - obj.xp = ctx.read_int(); // xp - obj.xp_next_level = ctx.read_int(); // xpnl - obj.lp = ctx.read_int(); // lp + this->flags = r.read_int(); // flags + this->guild = r.read_int(); // guild + this->guild_true = r.read_int(); // guildTrue + this->level = r.read_int(); // level + this->xp = r.read_int(); // xp + this->xp_next_level = r.read_int(); // xpnl + this->lp = r.read_int(); // lp - auto talent_count = ctx.read_int(); // numTalents - obj.talents.resize(talent_count); + auto talent_count = r.read_int(); // numTalents + this->talents.resize(talent_count); - archive_object hdr; + ArchiveObject hdr; for (auto i = 0; i < talent_count; ++i) { - if (!ctx.read_object_begin(hdr)) // [% oCNpcTalent 0 0] - throw parser_error {"vobs::npc"}; + if (!r.read_object_begin(hdr)) // [% oCNpcTalent 0 0] + throw zenkit::ParserError {"vobs::Npc"}; // empty object if (hdr.class_name == "%") { - ctx.skip_object(true); + r.skip_object(true); continue; } - obj.talents[i].talent = ctx.read_int(); // talent - obj.talents[i].value = ctx.read_int(); // value - obj.talents[i].skill = ctx.read_int(); // skill + this->talents[i].talent = r.read_int(); // talent + this->talents[i].value = r.read_int(); // value + this->talents[i].skill = r.read_int(); // skill - if (!ctx.read_object_end()) { - PX_LOGW("vob_tree: oCNpcTalent object not fully parsed"); - ctx.skip_object(true); + if (!r.read_object_end()) { + ZKLOGW("VOb.Npc", "oCNpcTalent object not fully parsed"); + r.skip_object(true); } } - obj.fight_tactic = ctx.read_int(); // fightTactic - obj.fight_mode = ctx.read_int(); // fightMode - obj.wounded = ctx.read_bool(); // wounded - obj.mad = ctx.read_bool(); // mad - obj.mad_time = ctx.read_int(); // madTime - obj.player = ctx.read_bool(); // player + this->fight_tactic = r.read_int(); // fightTactic + this->fight_mode = r.read_int(); // fightMode + this->wounded = r.read_bool(); // wounded + this->mad = r.read_bool(); // mad + this->mad_time = r.read_int(); // madTime + this->player = r.read_bool(); // player - for (auto i = 0; i < 8; ++i) { - obj.attributes[i] = ctx.read_int(); // atr0 + for (int& attribute : this->attributes) { + attribute = r.read_int(); // atr0 } - if (version == game_version::gothic_2) { + if (version == GameVersion::GOTHIC_2) { // TODO: what are these (hc)? - for (auto i = 0; i < 4; ++i) { - obj.hcs[i] = ctx.read_int(); // hc1 + for (int& hc : this->hcs) { + hc = r.read_int(); // hc1 } } - for (auto i = 0; i < 5; ++i) { - obj.missions[i] = ctx.read_int(); // mission0 + for (int& mission : this->missions) { + mission = r.read_int(); // mission0 } - obj.start_ai_state = ctx.read_string(); // startAIState + this->start_ai_state = r.read_string(); // startAIState - auto vars = ctx.read_raw_bytes((version == game_version::gothic_1 ? 50 : 100) * 4); // scriptVars - for (auto i = 0u; i < vars.limit() / 4; ++i) { - obj.aivar[i] = vars.get_int(); + auto var_count = version == GameVersion::GOTHIC_1 ? 50 : 100; + auto vars = r.read_raw(var_count * 4); // scriptVars + for (auto i = 0; i < var_count / 4; ++i) { + this->aivar[i] = vars->read_int(); } - obj.script_waypoint = ctx.read_string(); // scriptWp - obj.attitude = ctx.read_int(); // attitude - obj.attitude_temp = ctx.read_int(); // tmpAttitude - obj.name_nr = ctx.read_int(); // nameNr + this->script_waypoint = r.read_string(); // scriptWp + this->attitude = r.read_int(); // attitude + this->attitude_temp = r.read_int(); // tmpAttitude + this->name_nr = r.read_int(); // nameNr // unknown. - [[maybe_unused]] auto spells = ctx.read_raw_bytes(4); // spells + [[maybe_unused]] auto spells = r.read_raw(4); // spells // TODO: News (I don't have a sample.). - auto news_count = ctx.read_int(); // NumOfEntries + auto news_count = r.read_int(); // NumOfEntries if (news_count != 0) { - PX_LOGE("!!! IMPORTANT !!! This save-game contains news entries and cannot be loaded currently. Please " - "open an issue at https://github.com/lmichaelis/phoenix providing your save-game as a ZIP file."); - throw parser_error {"vobs::npc"}; + ZKLOGE("VOb.Npc", + "!!! IMPORTANT !!! This save-game contains news entries and cannot be loaded currently. Please " + "open an issue at https://github.com/GothicKit/phoenix providing your save-game as a ZIP file."); + throw zenkit::ParserError {"vobs::Npc"}; } - ctx.skip_object(false); // [carryVob % 0 0] - ctx.skip_object(false); // [enemy % 0 0] + r.skip_object(false); // [carryVob % 0 0] + r.skip_object(false); // [enemy % 0 0] - obj.move_lock = ctx.read_bool(); // moveLock + this->move_lock = r.read_bool(); // moveLock - if (version == game_version::gothic_1) { - for (auto i = 0; i < 9; ++i) { - obj.packed[i] = ctx.read_string(); // packed + if (version == GameVersion::GOTHIC_1) { + for (auto& i : this->packed) { + i = r.read_string(); // packed } } else { - auto packed = ctx.read_string(); // packed - auto it = packed.begin(); + auto pack = r.read_string(); // packed + auto it = pack.begin(); auto idx = 0; do { - auto next = std::find(it, packed.end(), ';'); - obj.packed[idx++].assign(it, next); + auto next = std::find(it, pack.end(), ';'); + this->packed[idx++].assign(it, next); it = next; - } while (it != packed.end() && idx < 9); + } while (it != pack.end() && idx < 9); } - auto item_count = ctx.read_int(); // itemCount - obj.items.resize(item_count); + auto item_count = r.read_int(); // itemCount + this->items.resize(item_count); for (auto i = 0; i < item_count; ++i) { - if (!ctx.read_object_begin(hdr)) - throw parser_error {"vobs::npc"}; + if (!r.read_object_begin(hdr)) + throw zenkit::ParserError {"vobs::Npc"}; - obj.items[i] = std::make_unique(); - item::parse(*obj.items[i], ctx, version); - obj.items[i]->id = hdr.index; + this->items[i] = std::make_unique(); + this->items[i]->load(r, version); + this->items[i]->id = hdr.index; - if (!ctx.read_object_end()) { - PX_LOGW("vob_tree: oCItem:zCVob object not fully parsed"); - ctx.skip_object(true); + if (!r.read_object_end()) { + ZKLOGW("VOb.Npc", "oCItem:zCVob object not fully parsed"); + r.skip_object(true); + } + + if ((this->items[i]->s_flags & 0x200) != 0) { + (void) r.read_int(); // shortKey // TODO } } - auto inv_slot_count = ctx.read_int(); // numInvSlots - obj.slots.resize(inv_slot_count); + auto inv_slot_count = r.read_int(); // numInvSlots + this->slots.resize(inv_slot_count); for (auto i = 0; i < inv_slot_count; ++i) { - obj.slots[i].used = ctx.read_bool(); // used - obj.slots[i].name = ctx.read_string(); // name + this->slots[i].used = r.read_bool(); // used + this->slots[i].name = r.read_string(); // name - if (obj.slots[i].used) { + if (this->slots[i].used) { // [vob § 0 0] - if (!ctx.read_object_begin(hdr) || !ctx.read_object_end()) - throw parser_error {"vobs::npc"}; - - // TODO: Warn if not found. - for (auto j = 0u; j < obj.items.size(); ++j) { - if (obj.items[j]->id == hdr.index) { - obj.slots[i].item_index = j; - break; + if (!r.read_object_begin(hdr)) { + throw zenkit::ParserError {"VOb.Npc"}; + } + + if (hdr.class_name == "\xA7") { + // This item is a reference. + // TODO: Warn if not found. + for (const auto& item : this->items) { + if (item->id == hdr.index) { + this->slots[i].item = item.get(); + break; + } } + } else { + // This item is embedded. + this->items.push_back(std::make_unique()); + + auto& item = this->items.back(); + item->load(r, version); + item->id = hdr.index; + + this->slots[i].item = item.get(); + } + + if (!r.read_object_end()) { + ZKLOGW("VOb.Npc", "Inventory slot not fully parsed."); + r.skip_object(true); } - obj.slots[i].in_inventory = ctx.read_bool(); // inInv + this->slots[i].in_inventory = r.read_bool(); // inInv } } - obj.current_state_valid = ctx.read_bool(); // curState.valid - obj.current_state_name = ctx.read_string(); // curState.name - obj.current_state_index = ctx.read_int(); // curState.prgIndex - obj.current_state_is_routine = ctx.read_bool(); // curState.isRtnState - obj.next_state_valid = ctx.read_bool(); // nextState.valid - obj.next_state_name = ctx.read_string(); // nextState.name - obj.next_state_index = ctx.read_int(); // nextState.prgIndex - obj.next_state_is_routine = ctx.read_bool(); // nextState.isRtnState - obj.last_ai_state = ctx.read_int(); // lastAIState - obj.has_routine = ctx.read_bool(); // hasRoutine - obj.routine_changed = ctx.read_bool(); // rtnChanged - obj.routine_overlay = ctx.read_bool(); // rtnOverlay - obj.routine_overlay_count = ctx.read_int(); // rtnOverlayCount - obj.walkmode_routine = ctx.read_int(); // walkmode_routine - obj.weaponmode_routine = ctx.read_bool(); // weaponmode_routine - obj.start_new_routine = ctx.read_bool(); // startNewRoutine - obj.ai_state_driven = ctx.read_int(); // aiStateDriven - obj.ai_state_pos = ctx.read_vec3(); // aiStatePos - obj.current_routine = ctx.read_string(); // curRoutine - obj.respawn = ctx.read_bool(); // respawn - obj.respawn_time = ctx.read_int(); // respawnTime - - auto protection = ctx.read_raw_bytes(sizeof(int32_t) * 8); // protection - for (auto i = 0; i < 8; ++i) { - obj.protection[i] = protection.get_int(); + this->current_state_valid = r.read_bool(); // curState.valid + this->current_state_name = r.read_string(); // curState.name + this->current_state_index = r.read_int(); // curState.prgIndex + this->current_state_is_routine = r.read_bool(); // curState.isRtnState + this->next_state_valid = r.read_bool(); // nextState.valid + this->next_state_name = r.read_string(); // nextState.name + this->next_state_index = r.read_int(); // nextState.prgIndex + this->next_state_is_routine = r.read_bool(); // nextState.isRtnState + this->last_ai_state = r.read_int(); // lastAIState + this->has_routine = r.read_bool(); // hasRoutine + this->routine_changed = r.read_bool(); // rtnChanged + this->routine_overlay = r.read_bool(); // rtnOverlay + this->routine_overlay_count = r.read_int(); // rtnOverlayCount + this->walkmode_routine = r.read_int(); // walkmode_routine + this->weaponmode_routine = r.read_bool(); // weaponmode_routine + this->start_new_routine = r.read_bool(); // startNewRoutine + this->ai_state_driven = r.read_int(); // aiStateDriven + this->ai_state_pos = r.read_vec3(); // aiStatePos + this->current_routine = r.read_string(); // curRoutine + this->respawn = r.read_bool(); // respawn + this->respawn_time = r.read_int(); // respawnTime + + auto prot = r.read_raw(sizeof(int32_t) * 8); // protection + for (int& i : this->protection) { + i = prot->read_int(); } - if (version == game_version::gothic_2) { - obj.bs_interruptable_override = ctx.read_int(); // bsInterruptableOverride - obj.npc_type = ctx.read_int(); // npcType - obj.spell_mana = ctx.read_int(); // spellMana + if (version == GameVersion::GOTHIC_2) { + this->bs_interruptable_override = r.read_int(); // bsInterruptableOverride + this->npc_type = r.read_int(); // npcType + this->spell_mana = r.read_int(); // spellMana + } + } + + void ScreenEffect::load(ReadArchive& r, GameVersion version) { + VirtualObject::load(r, version); + + if (r.is_save_game()) { + auto count = version == GameVersion::GOTHIC_1 ? 5 : 12; + (void) r.read_raw(count * 4); // blend + (void) r.read_raw(count * 4); // cinema + (void) r.read_raw(count * 4); // fovMorph + (void) r.read_vec2(); // fovSaved + (void) r.read_vec2(); // fovSaved1st } } -} // namespace phoenix::vobs \ No newline at end of file +} // namespace zenkit::vobs diff --git a/src/vobs/MovableObject.cc b/src/vobs/MovableObject.cc index f178749a..9b56fd90 100644 --- a/src/vobs/MovableObject.cc +++ b/src/vobs/MovableObject.cc @@ -1,72 +1,95 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/vobs/MovableObject.hh" +#include "zenkit/Archive.hh" -namespace phoenix::vobs { - void mob::parse(mob& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.name = ctx.read_string(); // focusName - obj.hp = ctx.read_int(); // hitpoints - obj.damage = ctx.read_int(); // damage - obj.movable = ctx.read_bool(); // moveable - obj.takable = ctx.read_bool(); // takeable - obj.focus_override = ctx.read_bool(); // focusOverride - obj.material = static_cast(ctx.read_enum()); // soundMaterial - obj.visual_destroyed = ctx.read_string(); // visualDestroyed - obj.owner = ctx.read_string(); // owner - obj.owner_guild = ctx.read_string(); // ownerGuild - obj.destroyed = ctx.read_bool(); // isDestroyed +#include "../Internal.hh" + +namespace zenkit::vobs { + void MovableObject::parse(MovableObject& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void MovableObject::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->name = r.read_string(); // focusName + this->hp = r.read_int(); // hitpoints + this->damage = r.read_int(); // damage + this->movable = r.read_bool(); // moveable + this->takable = r.read_bool(); // takeable + this->focus_override = r.read_bool(); // focusOverride + this->material = static_cast(r.read_enum()); // soundMaterial + this->visual_destroyed = r.read_string(); // visualDestroyed + this->owner = r.read_string(); // owner + this->owner_guild = r.read_string(); // ownerGuild + this->destroyed = r.read_bool(); // isDestroyed } - void mob_inter::parse(mob_inter& obj, archive_reader& ctx, game_version version) { - mob::parse(obj, ctx, version); - obj.state = ctx.read_int(); // stateNum - obj.target = ctx.read_string(); // triggerTarget - obj.item = ctx.read_string(); // useWithItem - obj.condition_function = ctx.read_string(); // conditionFunc - obj.on_state_change_function = ctx.read_string(); // onStateFunc - obj.rewind = ctx.read_bool(); // rewind + void InteractiveObject::parse(InteractiveObject& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); } - void mob_container::parse(mob_container& obj, archive_reader& ctx, game_version version) { - mob_inter::parse(obj, ctx, version); - obj.locked = ctx.read_bool(); // locked - obj.key = ctx.read_string(); // keyInstance - obj.pick_string = ctx.read_string(); // pickLockStr - obj.contents = ctx.read_string(); // contains + void InteractiveObject::load(ReadArchive& r, zenkit::GameVersion version) { + MovableObject::load(r, version); + this->state = r.read_int(); // stateNum + this->target = r.read_string(); // triggerTarget + this->item = r.read_string(); // useWithItem + this->condition_function = r.read_string(); // conditionFunc + this->on_state_change_function = r.read_string(); // onStateFunc + this->rewind = r.read_bool(); // rewind + } + + void Container::parse(Container& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } - if (ctx.is_save_game()) { + void Container::load(ReadArchive& r, zenkit::GameVersion version) { + InteractiveObject::load(r, version); + this->locked = r.read_bool(); // locked + this->key = r.read_string(); // keyInstance + this->pick_string = r.read_string(); // pickLockStr + this->contents = r.read_string(); // contains + + if (r.is_save_game()) { // In save-games, containers contain extra variables - auto item_count = ctx.read_int(); // NumOfEntries - obj.s_items.resize(item_count); + auto item_count = r.read_int(); // NumOfEntries + this->s_items.resize(item_count); - archive_object itm; + ArchiveObject itm; for (auto i = 0; i < item_count; ++i) { - if (!ctx.read_object_begin(itm) || itm.class_name != "oCItem:zCVob") { - throw parser_error {"vobs::mob_container"}; + if (!r.read_object_begin(itm) || itm.class_name != "oCItem:zCVob") { + throw zenkit::ParserError {"VOb.Container"}; } - obj.s_items[i] = std::make_unique(); - item::parse(*obj.s_items[i], ctx, version); + this->s_items[i] = std::make_unique(); + this->s_items[i]->load(r, version); - if (!ctx.read_object_end()) { - PX_LOGW("vob_tree: oCItem:zCVob object not fully parsed"); - ctx.skip_object(true); + if (!r.read_object_end()) { + ZKLOGW("VOb.Container", "oCItem:zCVob object not fully parsed"); + r.skip_object(true); } } } } - void mob_door::parse(mob_door& obj, archive_reader& ctx, game_version version) { - mob_inter::parse(obj, ctx, version); - obj.locked = ctx.read_bool(); // locked - obj.key = ctx.read_string(); // keyInstance - obj.pick_string = ctx.read_string(); // pickLockStr + void Door::parse(Door& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void Door::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + InteractiveObject::load(r, version); + this->locked = r.read_bool(); // locked + this->key = r.read_string(); // keyInstance + this->pick_string = r.read_string(); // pickLockStr + } + + void Fire::parse(Fire& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); } - void mob_fire::parse(mob_fire& obj, archive_reader& ctx, game_version version) { - mob_inter::parse(obj, ctx, version); - obj.slot = ctx.read_string(); // fireSlot - obj.vob_tree = ctx.read_string(); // fireVobtreeName + void Fire::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + InteractiveObject::load(r, version); + this->slot = r.read_string(); // fireSlot + this->vob_tree = r.read_string(); // fireVobtreeName } -} // namespace phoenix::vobs \ No newline at end of file +} // namespace zenkit::vobs diff --git a/src/vobs/Sound.cc b/src/vobs/Sound.cc index 40d63a7f..f9e004ea 100644 --- a/src/vobs/Sound.cc +++ b/src/vobs/Sound.cc @@ -1,33 +1,42 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/vobs/Sound.hh" +#include "zenkit/Archive.hh" -namespace phoenix::vobs { - void sound::parse(sound& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.volume = ctx.read_float(); // sndVolume - obj.mode = static_cast(ctx.read_enum()); // sndMode - obj.random_delay = ctx.read_float(); // sndRandDelay - obj.random_delay_var = ctx.read_float(); // sndRandDelayVar - obj.initially_playing = ctx.read_bool(); // sndStartOn - obj.ambient3d = ctx.read_bool(); // sndAmbient3D - obj.obstruction = ctx.read_bool(); // sndObstruction - obj.cone_angle = ctx.read_float(); // sndConeAngle - obj.volume_type = static_cast(ctx.read_enum()); // sndVolType - obj.radius = ctx.read_float(); // sndRadius - obj.sound_name = ctx.read_string(); // sndName +namespace zenkit::vobs { + void Sound::parse(Sound& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void Sound::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->volume = r.read_float(); // sndVolume + this->mode = static_cast(r.read_enum()); // sndMode + this->random_delay = r.read_float(); // sndRandDelay + this->random_delay_var = r.read_float(); // sndRandDelayVar + this->initially_playing = r.read_bool(); // sndStartOn + this->ambient3d = r.read_bool(); // sndAmbient3D + this->obstruction = r.read_bool(); // sndObstruction + this->cone_angle = r.read_float(); // sndConeAngle + this->volume_type = static_cast(r.read_enum()); // sndVolType + this->radius = r.read_float(); // sndRadius + this->sound_name = r.read_string(); // sndName - if (ctx.is_save_game()) { + if (r.is_save_game()) { // In save-games, sounds contain extra variables - obj.s_is_running = ctx.read_bool(); // soundIsRunning - obj.s_is_allowed_to_run = ctx.read_bool(); // soundAllowedToRun + this->s_is_running = r.read_bool(); // soundIsRunning + this->s_is_allowed_to_run = r.read_bool(); // soundAllowedToRun } } - void sound_daytime::parse(sound_daytime& obj, archive_reader& ctx, game_version version) { - sound::parse(obj, ctx, version); - obj.start_time = ctx.read_float(); // sndStartTime - obj.end_time = ctx.read_float(); // sndEndTime - obj.sound_name2 = ctx.read_string(); // sndName2 + void SoundDaytime::parse(SoundDaytime& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void SoundDaytime::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + Sound::load(r, version); + this->start_time = r.read_float(); // sndStartTime + this->end_time = r.read_float(); // sndEndTime + this->sound_name2 = r.read_string(); // sndName2 } -} // namespace phoenix::vobs +} // namespace zenkit::vobs diff --git a/src/vobs/Trigger.cc b/src/vobs/Trigger.cc index 7dca82bf..0d25b67e 100644 --- a/src/vobs/Trigger.cc +++ b/src/vobs/Trigger.cc @@ -1,131 +1,160 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include - -namespace phoenix::vobs { - void trigger::parse(trigger& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.target = ctx.read_string(); // triggerTarget - obj.flags = ctx.read_raw_bytes(1).get(); // flags - obj.filter_flags = ctx.read_raw_bytes(1).get(); // filterFlags - obj.vob_target = ctx.read_string(); // respondToVobName - obj.max_activation_count = ctx.read_int(); // numCanBeActivated - obj.retrigger_delay_sec = ctx.read_float(); // retriggerWaitSec - obj.damage_threshold = ctx.read_float(); // damageThreshold - obj.fire_delay_sec = ctx.read_float(); // fireDelaySec - - obj.s_count_can_be_activated = obj.max_activation_count; - - if (ctx.is_save_game()) { +#include "zenkit/vobs/Trigger.hh" +#include "zenkit/Archive.hh" + +namespace zenkit::vobs { + void Trigger::parse(Trigger& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void Trigger::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->target = r.read_string(); // triggerTarget + this->flags = r.read_raw(1)->read_ubyte(); // flags + this->filter_flags = r.read_raw(1)->read_ubyte(); // filterFlags + this->vob_target = r.read_string(); // respondToVobName + this->max_activation_count = r.read_int(); // numCanBeActivated + this->retrigger_delay_sec = r.read_float(); // retriggerWaitSec + this->damage_threshold = r.read_float(); // damageThreshold + this->fire_delay_sec = r.read_float(); // fireDelaySec + + this->s_count_can_be_activated = this->max_activation_count; + + if (r.is_save_game()) { // In save-games, triggers contain extra variables - obj.s_next_time_triggerable = ctx.read_float(); // nextTimeTriggerable - ctx.skip_object(false); // [savedOtherVob % 0 0] - obj.s_count_can_be_activated = ctx.read_int(); // countCanBeActivated + this->s_next_time_triggerable = r.read_float(); // nextTimeTriggerable + r.skip_object(false); // [savedOtherVob % 0 0] + this->s_count_can_be_activated = r.read_int(); // countCanBeActivated - if (version == game_version::gothic_2) { - obj.s_is_enabled = ctx.read_bool(); // isEnabled + if (version == GameVersion::GOTHIC_2) { + this->s_is_enabled = r.read_bool(); // isEnabled } } } - void trigger_mover::parse(trigger_mover& obj, archive_reader& ctx, game_version version) { - trigger::parse(obj, ctx, version); - obj.behavior = static_cast(ctx.read_enum()); // moverBehavior - obj.touch_blocker_damage = ctx.read_float(); // touchBlockerDamage - obj.stay_open_time_sec = ctx.read_float(); // stayOpenTimeSec - obj.locked = ctx.read_bool(); // moverLocked - obj.auto_link = ctx.read_bool(); // autoLinkEnabled + void Mover::parse(Mover& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void Mover::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + Trigger::load(r, version); + this->behavior = static_cast(r.read_enum()); // moverBehavior + this->touch_blocker_damage = r.read_float(); // touchBlockerDamage + this->stay_open_time_sec = r.read_float(); // stayOpenTimeSec + this->locked = r.read_bool(); // moverLocked + this->auto_link = r.read_bool(); // autoLinkEnabled - if (version == game_version::gothic_2) { - obj.auto_rotate = ctx.read_bool(); // autoRotate + if (version == GameVersion::GOTHIC_2) { + this->auto_rotate = r.read_bool(); // autoRotate } - auto keyframe_count = ctx.read_word(); // numKeyframes + auto keyframe_count = r.read_word(); // numKeyframes if (keyframe_count > 0) { - obj.speed = ctx.read_float(); // moveSpeed - obj.lerp_mode = static_cast(ctx.read_enum()); // posLerpType - obj.speed_mode = static_cast(ctx.read_enum()); // speedType + this->speed = r.read_float(); // moveSpeed + this->lerp_mode = static_cast(r.read_enum()); // posLerpType + this->speed_mode = static_cast(r.read_enum()); // speedType - auto sample_reader = ctx.read_raw_bytes(keyframe_count * sizeof(float) * 7); // keyframes + auto sample_reader = r.read_raw(keyframe_count * sizeof(float) * 7); // keyframes for (int32_t i = 0; i < keyframe_count; ++i) { - auto position = sample_reader.get_vec3(); + auto pos = sample_reader->read_vec3(); - auto x = sample_reader.get_float(); - auto y = sample_reader.get_float(); - auto z = sample_reader.get_float(); - auto w = sample_reader.get_float(); + auto x = sample_reader->read_float(); + auto y = sample_reader->read_float(); + auto z = sample_reader->read_float(); + auto w = sample_reader->read_float(); - obj.keyframes.push_back(animation_sample {position, glm::quat {w, x, y, z}}); + this->keyframes.push_back(AnimationSample {pos, glm::quat {w, x, y, z}}); } } - if (ctx.is_save_game()) { + if (r.is_save_game()) { // In save-games, movers contain extra variables - obj.s_act_key_pos_delta = ctx.read_vec3(); // actKeyPosDelta - obj.s_act_keyframe_f = ctx.read_float(); // actKeyframeF - obj.s_act_keyframe = ctx.read_int(); // actKeyframe - obj.s_next_keyframe = ctx.read_int(); // nextKeyframe - obj.s_move_speed_unit = ctx.read_float(); // moveSpeedUnit - obj.s_advance_dir = ctx.read_float(); // advanceDir - obj.s_mover_state = ctx.read_enum(); // moverState - obj.s_trigger_event_count = ctx.read_int(); // numTriggerEvents - obj.s_stay_open_time_dest = ctx.read_float(); // stayOpenTimeDest + this->s_act_key_pos_delta = r.read_vec3(); // actKeyPosDelta + this->s_act_keyframe_f = r.read_float(); // actKeyframeF + this->s_act_keyframe = r.read_int(); // actKeyframe + this->s_next_keyframe = r.read_int(); // nextKeyframe + this->s_move_speed_unit = r.read_float(); // moveSpeedUnit + this->s_advance_dir = r.read_float(); // advanceDir + this->s_mover_state = r.read_enum(); // moverState + this->s_trigger_event_count = r.read_int(); // numTriggerEvents + this->s_stay_open_time_dest = r.read_float(); // stayOpenTimeDest } - obj.sfx_open_start = ctx.read_string(); // sfxOpenStart - obj.sfx_open_end = ctx.read_string(); // sfxOpenEnd - obj.sfx_transitioning = ctx.read_string(); // sfxMoving - obj.sfx_close_start = ctx.read_string(); // sfxCloseStart - obj.sfx_close_end = ctx.read_string(); // sfxCloseEnd - obj.sfx_lock = ctx.read_string(); // sfxLock - obj.sfx_unlock = ctx.read_string(); // sfxUnlock - obj.sfx_use_locked = ctx.read_string(); // sfxUseLocked + this->sfx_open_start = r.read_string(); // sfxOpenStart + this->sfx_open_end = r.read_string(); // sfxOpenEnd + this->sfx_transitioning = r.read_string(); // sfxMoving + this->sfx_close_start = r.read_string(); // sfxCloseStart + this->sfx_close_end = r.read_string(); // sfxCloseEnd + this->sfx_lock = r.read_string(); // sfxLock + this->sfx_unlock = r.read_string(); // sfxUnlock + this->sfx_use_locked = r.read_string(); // sfxUseLocked + } + + void TriggerList::parse(TriggerList& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); } - void trigger_list::parse(trigger_list& obj, archive_reader& ctx, game_version version) { - trigger::parse(obj, ctx, version); - obj.mode = static_cast(ctx.read_enum()); // listProcess + void TriggerList::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + Trigger::load(r, version); + this->mode = static_cast(r.read_enum()); // listProcess - auto target_count = ctx.read_byte(); // numTarget + auto target_count = r.read_byte(); // numTarget for (int32_t i = 0; i < target_count; ++i) { - obj.targets.emplace_back(target { - ctx.read_string(), // triggerTarget[i] - ctx.read_float() // fireDelay[i] + this->targets.emplace_back(Target { + r.read_string(), // triggerTarget[i] + r.read_float() // fireDelay[i] }); } - if (ctx.is_save_game()) { + if (r.is_save_game()) { // In save-games, trigger lists contain extra variables - obj.s_act_target = ctx.read_byte(); // actTarget - obj.s_send_on_trigger = ctx.read_bool(); // sendOnTrigger + this->s_act_target = r.read_byte(); // actTarget + this->s_send_on_trigger = r.read_bool(); // sendOnTrigger } } - void trigger_script::parse(trigger_script& obj, archive_reader& ctx, game_version version) { - trigger::parse(obj, ctx, version); - obj.function = ctx.read_string(); // scriptFunc + void TriggerScript::parse(TriggerScript& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); } - void trigger_change_level::parse(trigger_change_level& obj, archive_reader& ctx, game_version version) { - trigger::parse(obj, ctx, version); - obj.level_name = ctx.read_string(); // levelName - obj.start_vob = ctx.read_string(); // startVobName + void TriggerScript::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + Trigger::load(r, version); + this->function = r.read_string(); // scriptFunc } - void trigger_world_start::parse(trigger_world_start& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.target = ctx.read_string(); // triggerTarget - obj.fire_once = ctx.read_bool(); // fireOnlyFirstTime + void TriggerChangeLevel::parse(TriggerChangeLevel& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void TriggerChangeLevel::load(zenkit::ReadArchive& r, GameVersion version) { + Trigger::load(r, version); + this->level_name = r.read_string(); // levelName + this->start_vob = r.read_string(); // startVobName + } + + void TriggerWorldStart::parse(TriggerWorldStart& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void TriggerWorldStart::load(ReadArchive& r, GameVersion version) { + VirtualObject::load(r, version); + this->target = r.read_string(); // triggerTarget + this->fire_once = r.read_bool(); // fireOnlyFirstTime - if (ctx.is_save_game() && version == game_version::gothic_2) { + if (r.is_save_game() && version == GameVersion::GOTHIC_2) { // In Gothic 2 save-games, world start triggers contain extra variables - obj.s_has_fired = ctx.read_bool(); // hasFired + this->s_has_fired = r.read_bool(); // hasFired } } - void trigger_untouch::parse(trigger_untouch& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.target = ctx.read_string(); // triggerTarget + void TriggerUntouch::parse(TriggerUntouch& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void TriggerUntouch::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + VirtualObject::load(r, version); + this->target = r.read_string(); // triggerTarget } -} // namespace phoenix::vobs +} // namespace zenkit::vobs diff --git a/src/vobs/VirtualObject.cc b/src/vobs/VirtualObject.cc index 480c259e..4372924e 100644 --- a/src/vobs/VirtualObject.cc +++ b/src/vobs/VirtualObject.cc @@ -1,69 +1,78 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include -#include +#include "zenkit/vobs/VirtualObject.hh" +#include "zenkit/Archive.hh" + +#include "../Internal.hh" #include -namespace phoenix { +namespace zenkit { /// \brief A mapping of archive class names to visual_type values. - static std::unordered_map visual_type_map = { - {"zCDecal", visual_type::decal}, - {"zCMesh", visual_type::mesh}, - {"zCProgMeshProto", visual_type::proto_mesh}, - {"zCParticleFX", visual_type::particle_system}, - {"zCModel", visual_type::model}, - {"zCAICamera", visual_type::ai_camera}, - {"zCMorphMesh", visual_type::morph_mesh}, - {"\xA7", visual_type::unknown}, - {"%", visual_type::unknown}, + static std::unordered_map visual_type_map = { + {"zCDecal", VisualType::DECAL}, + {"zCMesh", VisualType::MESH}, + {"zCProgMeshProto", VisualType::MULTI_RESOLUTION_MESH}, + {"zCParticleFX", VisualType::PARTICLE_EFFECT}, + {"zCModel", VisualType::MODEL}, + {"zCAICamera", VisualType::AI_CAMERA}, + {"zCMorphMesh", VisualType::MORPH_MESH}, + {"\xA7", VisualType::UNKNOWN}, + {"%", VisualType::UNKNOWN}, }; - decal decal::parse(archive_reader& in, game_version version) { - decal dc {}; - dc.name = in.read_string(); // name - dc.dimension = in.read_vec2(); // decalDim - dc.offset = in.read_vec2(); // decalOffset - dc.two_sided = in.read_bool(); // decal2Sided - dc.alpha_func = static_cast(in.read_enum()); // decalAlphaFunc - dc.texture_anim_fps = in.read_float(); // decalTexAniFPS - - if (version == game_version::gothic_2) { - dc.alpha_weight = in.read_byte(); // decalAlphaWeight - dc.ignore_daylight = in.read_bool(); // ignoreDayLight + Decal Decal::parse(ReadArchive& in, GameVersion version) { + Decal dc {}; + dc.load(in, version); + return dc; + } + + void Decal::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + this->name = r.read_string(); // name + this->dimension = r.read_vec2(); // decalDim + this->offset = r.read_vec2(); // decalOffset + this->two_sided = r.read_bool(); // decal2Sided + this->alpha_func = static_cast(r.read_enum()); // decalAlphaFunc + this->texture_anim_fps = r.read_float(); // decalTexAniFPS + + if (version == GameVersion::GOTHIC_2) { + this->alpha_weight = r.read_byte(); // decalAlphaWeight + this->ignore_daylight = r.read_bool(); // ignoreDayLight } + } - return dc; + void VirtualObject::parse(VirtualObject& obj, ReadArchive& in, GameVersion version) { + obj.load(in, version); } - void vob::parse(vob& obj, archive_reader& in, game_version version) { - auto packed = in.read_int() != 0; // pack + void VirtualObject::load(zenkit::ReadArchive& r, zenkit::GameVersion version) { + auto packed = r.read_int() != 0; // pack bool has_visual_object = true; bool has_ai_object = true; bool has_event_manager_object = false; if (packed) { - auto bin = in.read_raw_bytes(version == game_version::gothic_1 ? 74 : 83); // dataRaw + auto bin = r.read_raw(version == GameVersion::GOTHIC_1 ? 74 : 83); // dataRaw - obj.bbox = bounding_box::parse(bin); - obj.position = bin.get_vec3(); - obj.rotation = bin.get_mat3x3(); + this->bbox.load(bin.get()); + this->position = bin->read_vec3(); + this->rotation = bin->read_mat3(); - std::uint8_t bit0 = bin.get(); + std::uint8_t bit0 = bin->read_ubyte(); std::uint16_t bit1; - if (version == game_version::gothic_1) { - bit1 = bin.get(); + if (version == GameVersion::GOTHIC_1) { + bit1 = bin->read_ubyte(); } else { - bit1 = bin.get_ushort(); + bit1 = bin->read_ushort(); } - obj.show_visual = static_cast((bit0 & 0b00000001) >> 0); - obj.sprite_camera_facing_mode = static_cast((bit0 & 0b00000110) >> 1); - obj.cd_static = static_cast((bit0 & 0b00001000) >> 3); - obj.cd_dynamic = static_cast((bit0 & 0b00010000) >> 4); - obj.vob_static = static_cast((bit0 & 0b00100000) >> 5); - obj.dynamic_shadows = static_cast((bit0 & 0b11000000) >> 6); + this->show_visual = static_cast((bit0 & 0b00000001) >> 0); + this->sprite_camera_facing_mode = static_cast((bit0 & 0b00000110) >> 1); + this->cd_static = static_cast((bit0 & 0b00001000) >> 3); + this->cd_dynamic = static_cast((bit0 & 0b00010000) >> 4); + this->vob_static = static_cast((bit0 & 0b00100000) >> 5); + this->dynamic_shadows = static_cast((bit0 & 0b11000000) >> 6); bool has_preset_name = static_cast((bit1 & 0b000000000000001u) >> 0u); bool has_vob_name = static_cast((bit1 & 0b000000000000010u) >> 1u); @@ -73,90 +82,162 @@ namespace phoenix { // Quirk: bit 5 of this bitfield specifies whether an event manger object is // present but this is only relevant in save-games. - has_event_manager_object = static_cast((bit1 & 0b000000000100000u) >> 5u) && in.get_header().save; + has_event_manager_object = static_cast((bit1 & 0b000000000100000u) >> 5u) && r.get_header().save; - obj.physics_enabled = static_cast((bit1 & 0b000000001000000u) >> 6u); + this->physics_enabled = static_cast((bit1 & 0b000000001000000u) >> 6u); - if (version == game_version::gothic_2) { - obj.anim_mode = static_cast(bit1 & 0b000000110000000u >> 7u); - obj.bias = static_cast((bit1 & 0b011111000000000u) >> 9u); - obj.ambient = static_cast((bit1 & 0b100000000000000u) >> 14u); + if (version == GameVersion::GOTHIC_2) { + this->anim_mode = static_cast(bit1 & 0b000000110000000u >> 7u); + this->bias = static_cast((bit1 & 0b011111000000000u) >> 9u); + this->ambient = static_cast((bit1 & 0b100000000000000u) >> 14u); - obj.anim_strength = bin.get_float(); - obj.far_clip_scale = bin.get_float(); + this->anim_strength = bin->read_float(); + this->far_clip_scale = bin->read_float(); } if (has_preset_name) { - obj.preset_name = in.read_string(); // presetName + this->preset_name = r.read_string(); // presetName } if (has_vob_name) { - obj.vob_name = in.read_string(); // vobName + this->vob_name = r.read_string(); // vobName } if (has_visual_name) { - obj.visual_name = in.read_string(); // visual + this->visual_name = r.read_string(); // visual } } else { - obj.preset_name = in.read_string(); - obj.bbox = in.read_bbox(); // bbox3DWS - - obj.rotation = in.read_mat3x3(); // trafoOSToWSRot - obj.position = in.read_vec3(); // trafoOSToWSPos - - obj.vob_name = in.read_string(); // vobName - obj.visual_name = in.read_string(); // visual - obj.show_visual = in.read_bool(); // showVisual - obj.sprite_camera_facing_mode = static_cast(in.read_enum()); // visualCamAlign - - if (version == game_version::gothic_1) { - obj.cd_static = in.read_bool(); // cdStatic - obj.cd_dynamic = in.read_bool(); // cdDyn - obj.vob_static = in.read_bool(); // staticVob - obj.dynamic_shadows = static_cast(in.read_enum()); // dynShadow + this->preset_name = r.read_string(); + this->bbox = r.read_bbox(); // bbox3DWS + + this->rotation = r.read_mat3x3(); // trafoOSToWSRot + this->position = r.read_vec3(); // trafoOSToWSPos + + this->vob_name = r.read_string(); // vobName + this->visual_name = r.read_string(); // visual + this->show_visual = r.read_bool(); // showVisual + this->sprite_camera_facing_mode = static_cast(r.read_enum()); // visualCamAlign + + if (version == GameVersion::GOTHIC_1) { + this->cd_static = r.read_bool(); // cdStatic + this->cd_dynamic = r.read_bool(); // cdDyn + this->vob_static = r.read_bool(); // staticVob + this->dynamic_shadows = static_cast(r.read_enum()); // dynShadow } else { - obj.anim_mode = static_cast(in.read_enum()); // visualAniMode - obj.anim_strength = in.read_float(); // visualAniModeStrength - obj.far_clip_scale = in.read_float(); // vobFarClipZScale - obj.cd_static = in.read_bool(); // cdStatic - obj.cd_dynamic = in.read_bool(); // cdDyn - obj.vob_static = in.read_bool(); // staticVob - obj.dynamic_shadows = static_cast(in.read_enum()); // dynShadow - obj.bias = in.read_int(); // zbias - obj.ambient = in.read_bool(); // isAmbient + this->anim_mode = static_cast(r.read_enum()); // visualAniMode + this->anim_strength = r.read_float(); // visualAniModeStrength + this->far_clip_scale = r.read_float(); // vobFarClipZScale + this->cd_static = r.read_bool(); // cdStatic + this->cd_dynamic = r.read_bool(); // cdDyn + this->vob_static = r.read_bool(); // staticVob + this->dynamic_shadows = static_cast(r.read_enum()); // dynShadow + this->bias = r.read_int(); // zbias + this->ambient = r.read_bool(); // isAmbient } } + ArchiveObject hdr {}; if (has_visual_object) { - archive_object visual {}; - in.read_object_begin(visual); - obj.associated_visual_type = visual_type_map[visual.class_name]; + ArchiveObject visual {}; + r.read_object_begin(visual); + this->associated_visual_type = visual_type_map[visual.class_name]; - if (obj.associated_visual_type == visual_type::decal) { - obj.visual_decal = decal::parse(in, version); + if (this->associated_visual_type == VisualType::DECAL) { + this->visual_decal = Decal {}; + this->visual_decal->load(r, version); } - if (!in.read_object_end()) { - PX_LOGW("vob_tree: visual \"", visual.class_name, "\" not fully parsed"); - in.skip_object(true); + if (!r.read_object_end()) { + ZKLOGW("VirtualObject", "visual \"%s\" not fully parsed", visual.class_name.c_str()); + r.skip_object(true); } } // TODO - if (has_ai_object) { - in.skip_object(false); + if (has_ai_object && r.read_object_begin(hdr)) { + if (hdr.class_name == "oCAIHuman:oCAniCtrl_Human:zCAIPlayer") { + (void) r.read_int(); // waterLevel + (void) r.read_float(); // floorY + (void) r.read_float(); // waterY + (void) r.read_float(); // ceilY + (void) r.read_float(); // feetY + (void) r.read_float(); // headY + (void) r.read_float(); // fallDistY + (void) r.read_float(); // fallStartY + + // TODO: aiNpc + if (r.read_object_begin(hdr)) { + r.skip_object(true); + } else { + ZKLOGW("VirtualObject.Ai", "aiNpc not found"); + } + + (void) r.read_int(); // walkMode + (void) r.read_int(); // weaponMode + (void) r.read_int(); // wmodeLast + (void) r.read_int(); // wmodeSelect + (void) r.read_bool(); // changeWeapon + (void) r.read_int(); // actionMode + } else if (hdr.class_name == "oCAIVobMove") { + // TODO: vob + if (r.read_object_begin(hdr)) { + r.skip_object(true); + } + + // TODO: owner + if (r.read_object_begin(hdr)) { + r.skip_object(true); + } + } + + if (!r.read_object_end()) { + ZKLOGW("VirtualObject.Ai", "\"%s\" not fully parsed.", hdr.class_name.c_str()); + r.skip_object(true); + } } - // TODO - if (has_event_manager_object) { - in.skip_object(false); + if (has_event_manager_object && r.read_object_begin(hdr)) { + if (hdr.class_name != "zCEventManager") { + throw ParserError {"VirtualObject"}; + } + + EventManager em {}; + em.load(r, version); + + r.read_object_end(); } - if (in.get_header().save) { + if (r.get_header().save) { // save-games contain two extra values for each VOb - obj.saved = save_state {}; - obj.saved->sleep_mode = in.read_byte(); // sleepMode - obj.saved->next_on_timer = in.read_float(); // nextOnTimer + // TODO: These are technically from oCVob! + this->saved = SaveState {}; + this->saved->sleep_mode = r.read_byte(); // sleepMode + this->saved->next_on_timer = r.read_float(); // nextOnTimer + + if (this->physics_enabled) { + this->saved->rigid_body.emplace().load(r, version); + } } } -} // namespace phoenix + + void RigidBody::load(ReadArchive& r, GameVersion) { + this->vel = r.read_vec3(); + this->mode = r.read_byte(); + this->gravity_enabled = r.read_bool(); + this->gravity_scale = r.read_float(); + this->slide_direction = r.read_vec3(); + } + + void EventManager::load(ReadArchive& r, GameVersion) { + this->cleared = r.read_bool(); + this->active = r.read_bool(); + + ArchiveObject obj; + if (r.read_object_begin(obj) && !r.read_object_end()) { + ZKLOGW("VirtualObject.EventManager", "emCutscene not fully parsed!"); + r.skip_object(true); + } + + // TODO: this->cutscene.load(r, version); + } +} // namespace zenkit diff --git a/src/vobs/Zone.cc b/src/vobs/Zone.cc index ac294ce4..4a30c2e0 100644 --- a/src/vobs/Zone.cc +++ b/src/vobs/Zone.cc @@ -1,40 +1,53 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include - -namespace phoenix::vobs { - void zone_music::parse(zone_music& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.enabled = ctx.read_bool(); // enabled - obj.priority = ctx.read_int(); // priority - obj.ellipsoid = ctx.read_bool(); // ellipsoid - obj.reverb = ctx.read_float(); // reverbLevel - obj.volume = ctx.read_float(); // volumeLevel - obj.loop = ctx.read_bool(); // loop - - if (ctx.is_save_game()) { +#include "zenkit/vobs/Zone.hh" +#include "zenkit/Archive.hh" + +namespace zenkit::vobs { + void ZoneMusic::parse(ZoneMusic& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void ZoneMusic::load(ReadArchive& r, GameVersion version) { + VirtualObject::load(r, version); + this->enabled = r.read_bool(); // enabled + this->priority = r.read_int(); // priority + this->ellipsoid = r.read_bool(); // ellipsoid + this->reverb = r.read_float(); // reverbLevel + this->volume = r.read_float(); // volumeLevel + this->loop = r.read_bool(); // loop + + if (r.is_save_game()) { // In save-games, zones contain extra variables - obj.s_local_enabled = ctx.read_bool(); // local_enabled - obj.s_day_entrance_done = ctx.read_bool(); // dayEntranceDone - obj.s_night_entrance_done = ctx.read_bool(); // nightEntranceDone + this->s_local_enabled = r.read_bool(); // local_enabled + this->s_day_entrance_done = r.read_bool(); // dayEntranceDone + this->s_night_entrance_done = r.read_bool(); // nightEntranceDone } } - void zone_far_plane::parse(zone_far_plane& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.vob_far_plane_z = ctx.read_float(); // vobFarPlaneZ - obj.inner_range_percentage = ctx.read_float(); // innerRangePerc + void ZoneFarPlane::parse(ZoneFarPlane& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); + } + + void ZoneFarPlane::load(ReadArchive& r, GameVersion version) { + VirtualObject::load(r, version); + this->vob_far_plane_z = r.read_float(); // vobFarPlaneZ + this->inner_range_percentage = r.read_float(); // innerRangePerc + } + + void ZoneFog::parse(ZoneFog& obj, ReadArchive& r, GameVersion version) { + obj.load(r, version); } - void zone_fog::parse(zone_fog& obj, archive_reader& ctx, game_version version) { - vob::parse(obj, ctx, version); - obj.range_center = ctx.read_float(); // fogRangeCenter - obj.inner_range_percentage = ctx.read_float(); // innerRangePerc - obj.color = ctx.read_color(); // fogColor + void ZoneFog::load(ReadArchive& r, GameVersion version) { + VirtualObject::load(r, version); + this->range_center = r.read_float(); // fogRangeCenter + this->inner_range_percentage = r.read_float(); // innerRangePerc + this->color = r.read_color(); // fogColor - if (version == game_version::gothic_2) { - obj.fade_out_sky = ctx.read_bool(); // fadeOutSky - obj.override_color = ctx.read_bool(); // overrideColor + if (version == GameVersion::GOTHIC_2) { + this->fade_out_sky = r.read_bool(); // fadeOutSky + this->override_color = r.read_bool(); // overrideColor } } -} // namespace phoenix::vobs +} // namespace zenkit::vobs diff --git a/src/world/BspTree.cc b/src/world/BspTree.cc index d95ca68e..587bd8bd 100644 --- a/src/world/BspTree.cc +++ b/src/world/BspTree.cc @@ -1,23 +1,25 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/world/BspTree.hh" +#include "zenkit/Stream.hh" -namespace phoenix { +#include "../Internal.hh" + +namespace zenkit { static constexpr auto version_g1 = 0x2090000; [[maybe_unused]] static constexpr auto version_g2 = 0x4090000; - enum class bsp_chunk : std::uint16_t { - unknown, - header = 0xC000, - polygons = 0xC010, - tree = 0xC040, - outdoors = 0xC050, - light = 0xC045, - end = 0xC0FF + enum class BspChunkType : std::uint16_t { + HEADER = 0xC000, + POLYGONS = 0xC010, + TREE = 0xC040, + OUTDOORS = 0xC050, + LIGHT = 0xC045, + END = 0xC0FF }; - static void _parse_bsp_nodes(buffer& in, - std::vector& nodes, + static void _parse_bsp_nodes(Read* in, + std::vector& nodes, std::vector& indices, std::uint32_t version, std::int32_t parent_index, @@ -26,23 +28,23 @@ namespace phoenix { auto& node = nodes.emplace_back(); node.parent_index = parent_index; - node.bbox = bounding_box::parse(in); - node.polygon_index = in.get_uint(); - node.polygon_count = in.get_uint(); + node.bbox.load(in); + node.polygon_index = in->read_uint(); + node.polygon_count = in->read_uint(); if (leaf) { indices.push_back(self_index); } else { - auto flags = in.get(); + auto flags = in->read_ubyte(); node.plane = {}; - node.plane.w = in.get_float(); - node.plane.x = in.get_float(); - node.plane.y = in.get_float(); - node.plane.z = in.get_float(); + node.plane.w = in->read_float(); + node.plane.x = in->read_float(); + node.plane.y = in->read_float(); + node.plane.z = in->read_float(); if (version == version_g1) { - (void) in.get(); // "lod-flag" + (void) in->read_ubyte(); // "lod-flag" } if ((flags & 0x01) != 0) { @@ -57,106 +59,91 @@ namespace phoenix { } } - bsp_tree bsp_tree::parse(buffer& in, std::uint32_t version) { - bsp_tree bsp {}; - bsp_chunk type = bsp_chunk::unknown; - bool finished = false; - - do { - type = static_cast(in.get_ushort()); - - auto length = in.get_uint(); - auto chunk = in.extract(length); - - PX_LOGI("bsp_tree: parsing chunk ", std::hex, std::uint16_t(type)); + void BspTree::load(Read* r, std::uint32_t version) { + proto::read_chunked(r, "BspTree", [this, version](Read* c, BspChunkType type) { + ZKLOGI("BspTree", "Parsing chunk %x", std::uint16_t(type)); switch (type) { - case bsp_chunk::header: - (void) chunk.get_ushort(); - bsp.mode = static_cast(chunk.get_uint()); + case BspChunkType::HEADER: + (void) c->read_ushort(); + this->mode = static_cast(c->read_uint()); break; - case bsp_chunk::polygons: - bsp.polygon_indices.resize(chunk.get_uint()); + case BspChunkType::POLYGONS: + this->polygon_indices.resize(c->read_uint()); - for (uint32_t& index : bsp.polygon_indices) { - index = chunk.get_uint(); + for (uint32_t& index : this->polygon_indices) { + index = c->read_uint(); } break; - case bsp_chunk::tree: { - uint32_t node_count = chunk.get_uint(); - uint32_t leaf_count = chunk.get_uint(); + case BspChunkType::TREE: { + uint32_t node_count = c->read_uint(); + uint32_t leaf_count = c->read_uint(); - bsp.nodes.reserve(node_count); - bsp.leaf_node_indices.reserve(leaf_count); + this->nodes.reserve(node_count); + this->leaf_node_indices.reserve(leaf_count); - _parse_bsp_nodes(chunk, bsp.nodes, bsp.leaf_node_indices, version, -1); + _parse_bsp_nodes(c, this->nodes, this->leaf_node_indices, version, -1); - for (auto idx : bsp.leaf_node_indices) { - auto& node = bsp.nodes[idx]; + for (auto idx : this->leaf_node_indices) { + auto& node = this->nodes[idx]; for (uint32_t i = 0; i < node.polygon_count; ++i) { - bsp.leaf_polygons.push_back(bsp.polygon_indices[node.polygon_index + i]); + this->leaf_polygons.push_back(this->polygon_indices[node.polygon_index + i]); } } - std::sort(bsp.leaf_polygons.begin(), bsp.leaf_polygons.end()); + std::sort(this->leaf_polygons.begin(), this->leaf_polygons.end()); - assert(node_count == bsp.nodes.size()); - assert(leaf_count == bsp.leaf_node_indices.size()); + assert(node_count == this->nodes.size()); + assert(leaf_count == this->leaf_node_indices.size()); break; } - case bsp_chunk::light: { - bsp.light_points.resize(bsp.leaf_node_indices.size()); - - for (std::uint32_t i = 0; i < bsp.light_points.size(); ++i) { - bsp.light_points[i] = chunk.get_vec3(); + case BspChunkType::LIGHT: { + this->light_points.resize(this->leaf_node_indices.size()); + for (auto& light_point : this->light_points) { + light_point = c->read_vec3(); } break; } - case bsp_chunk::outdoors: { - auto sector_count = chunk.get_uint(); - bsp.sectors.reserve(sector_count); + case BspChunkType::OUTDOORS: { + auto sector_count = c->read_uint(); + this->sectors.reserve(sector_count); for (std::uint32_t i = 0; i < sector_count; ++i) { - auto& sector = bsp.sectors.emplace_back(); + auto& sector = this->sectors.emplace_back(); - sector.name = chunk.get_line(false); + sector.name = c->read_line(false); - auto node_count = chunk.get_uint(); - auto polygon_count = chunk.get_uint(); + auto node_count = c->read_uint(); + auto polygon_count = c->read_uint(); sector.node_indices.resize(node_count); sector.portal_polygon_indices.resize(polygon_count); for (std::uint32_t j = 0; j < node_count; ++j) { - sector.node_indices[j] = chunk.get_uint(); + sector.node_indices[j] = c->read_uint(); } for (std::uint32_t j = 0; j < polygon_count; ++j) { - sector.portal_polygon_indices[j] = chunk.get_uint(); + sector.portal_polygon_indices[j] = c->read_uint(); } } - auto portal_count = chunk.get_uint(); - bsp.portal_polygon_indices.resize(portal_count); + auto portal_count = c->read_uint(); + this->portal_polygon_indices.resize(portal_count); for (std::uint32_t i = 0; i < portal_count; ++i) { - bsp.portal_polygon_indices[i] = chunk.get_uint(); + this->portal_polygon_indices[i] = c->read_uint(); } break; } - case bsp_chunk::end: - (void) chunk.get(); - finished = true; - break; + case BspChunkType::END: + (void) c->read_ubyte(); + return true; default: break; } - if (chunk.remaining() != 0) { - PX_LOGW("bsp_tree: ", chunk.remaining(), " bytes remaining in section ", std::hex, std::uint16_t(type)); - } - } while (!finished); - - return bsp; + return false; + }); } -} // namespace phoenix +} // namespace zenkit diff --git a/src/world/VobTree.cc b/src/world/VobTree.cc index 54fa0d3c..cd443096 100644 --- a/src/world/VobTree.cc +++ b/src/world/VobTree.cc @@ -1,237 +1,212 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include - -#include -#include -#include -#include -#include -#include -#include -#include +#include "zenkit/world/VobTree.hh" +#include "zenkit/Archive.hh" +#include "zenkit/vobs/Camera.hh" +#include "zenkit/vobs/Light.hh" +#include "zenkit/vobs/Misc.hh" +#include "zenkit/vobs/MovableObject.hh" +#include "zenkit/vobs/Sound.hh" +#include "zenkit/vobs/Trigger.hh" +#include "zenkit/vobs/VirtualObject.hh" +#include "zenkit/vobs/Zone.hh" + +#include "../Internal.hh" #include -namespace phoenix { - static std::unordered_map vob_type_map = { - {"zCVob", vob_type::zCVob}, - {"zCVobLevelCompo:zCVob", vob_type::zCVobLevelCompo}, - {"oCItem:zCVob", vob_type::oCItem}, - {"oCNpc:zCVob", vob_type::oCNpc}, - {"oCMOB:zCVob", vob_type::oCMOB}, - {"oCMobInter:oCMOB:zCVob", vob_type::oCMobInter}, - {"oCMobBed:oCMobInter:oCMOB:zCVob", vob_type::oCMobBed}, - {"oCMobFire:oCMobInter:oCMOB:zCVob", vob_type::oCMobFire}, - {"oCMobLadder:oCMobInter:oCMOB:zCVob", vob_type::oCMobLadder}, - {"oCMobSwitch:oCMobInter:oCMOB:zCVob", vob_type::oCMobSwitch}, - {"oCMobWheel:oCMobInter:oCMOB:zCVob", vob_type::oCMobWheel}, - {"oCMobContainer:oCMobInter:oCMOB:zCVob", vob_type::oCMobContainer}, - {"oCMobDoor:oCMobInter:oCMOB:zCVob", vob_type::oCMobDoor}, - {"zCPFXControler:zCVob", vob_type::zCPFXController}, - {"zCVobAnimate:zCVob", vob_type::zCVobAnimate}, - {"zCVobLensFlare:zCVob", vob_type::zCVobLensFlare}, - {"zCVobLight:zCVob", vob_type::zCVobLight}, - {"zCVobSpot:zCVob", vob_type::zCVobSpot}, - {"zCVobStartpoint:zCVob", vob_type::zCVobStartpoint}, - {"zCVobSound:zCVob", vob_type::zCVobSound}, - {"zCVobSoundDaytime:zCVobSound:zCVob", vob_type::zCVobSoundDaytime}, - {"oCZoneMusic:zCVob", vob_type::oCZoneMusic}, - {"oCZoneMusicDefault:oCZoneMusic:zCVob", vob_type::oCZoneMusicDefault}, - {"zCZoneZFog:zCVob", vob_type::zCZoneZFog}, - {"zCZoneZFogDefault:zCZoneZFog:zCVob", vob_type::zCZoneZFogDefault}, - {"zCZoneVobFarPlane:zCVob", vob_type::zCZoneVobFarPlane}, - {"zCZoneVobFarPlaneDefault:zCZoneVobFarPlane:zCVob", vob_type::zCZoneVobFarPlaneDefault}, - {"zCMessageFilter:zCVob", vob_type::zCMessageFilter}, - {"zCCodeMaster:zCVob", vob_type::zCCodeMaster}, - {"zCTrigger:zCVob", vob_type::zCTrigger}, - {"zCTriggerList:zCTrigger:zCVob", vob_type::zCTriggerList}, - {"oCTriggerScript:zCTrigger:zCVob", vob_type::oCTriggerScript}, - {"zCMover:zCTrigger:zCVob", vob_type::zCMover}, - {"oCTriggerChangeLevel:zCTrigger:zCVob", vob_type::oCTriggerChangeLevel}, - {"zCTriggerWorldStart:zCVob", vob_type::zCTriggerWorldStart}, - {"zCTriggerUntouch:zCVob", vob_type::zCTriggerUntouch}, - {"zCCSCamera:zCVob", vob_type::zCCSCamera}, - {"zCCamTrj_KeyFrame:zCVob", vob_type::zCCamTrj_KeyFrame}, - {"oCTouchDamage:zCTouchDamage:zCVob", vob_type::oCTouchDamage}, - {"zCEarthquake:zCVob", vob_type::zCEarthquake}, - {"zCMoverControler:zCVob", vob_type::zCMoverController}, - {"zCVobScreenFX:zCVob", vob_type::zCVobScreenFX}, - {"zCVobStair:zCVob", vob_type::zCVobStair}, - {"oCCSTrigger:zCTrigger:zCVob", vob_type::oCCSTrigger}, - {"\xA7", vob_type::ignored}, // some sort of padding object, probably. seems to be always empty +namespace zenkit { + static std::unordered_map TYPES = { + {"zCVob", VobType::zCVob}, + {"zCVobLevelCompo:zCVob", VobType::zCVobLevelCompo}, + {"oCItem:zCVob", VobType::oCItem}, + {"oCNpc:zCVob", VobType::oCNpc}, + {"oCMOB:zCVob", VobType::oCMOB}, + {"oCMobInter:oCMOB:zCVob", VobType::oCMobInter}, + {"oCMobBed:oCMobInter:oCMOB:zCVob", VobType::oCMobBed}, + {"oCMobFire:oCMobInter:oCMOB:zCVob", VobType::oCMobFire}, + {"oCMobLadder:oCMobInter:oCMOB:zCVob", VobType::oCMobLadder}, + {"oCMobSwitch:oCMobInter:oCMOB:zCVob", VobType::oCMobSwitch}, + {"oCMobWheel:oCMobInter:oCMOB:zCVob", VobType::oCMobWheel}, + {"oCMobContainer:oCMobInter:oCMOB:zCVob", VobType::oCMobContainer}, + {"oCMobDoor:oCMobInter:oCMOB:zCVob", VobType::oCMobDoor}, + {"zCPFXControler:zCVob", VobType::zCPFXController}, + {"zCVobAnimate:zCVob", VobType::zCVobAnimate}, + {"zCVobLensFlare:zCVob", VobType::zCVobLensFlare}, + {"zCVobLight:zCVob", VobType::zCVobLight}, + {"zCVobSpot:zCVob", VobType::zCVobSpot}, + {"zCVobStartpoint:zCVob", VobType::zCVobStartpoint}, + {"zCVobSound:zCVob", VobType::zCVobSound}, + {"zCVobSoundDaytime:zCVobSound:zCVob", VobType::zCVobSoundDaytime}, + {"oCZoneMusic:zCVob", VobType::oCZoneMusic}, + {"oCZoneMusicDefault:oCZoneMusic:zCVob", VobType::oCZoneMusicDefault}, + {"zCZoneZFog:zCVob", VobType::zCZoneZFog}, + {"zCZoneZFogDefault:zCZoneZFog:zCVob", VobType::zCZoneZFogDefault}, + {"zCZoneVobFarPlane:zCVob", VobType::zCZoneVobFarPlane}, + {"zCZoneVobFarPlaneDefault:zCZoneVobFarPlane:zCVob", VobType::zCZoneVobFarPlaneDefault}, + {"zCMessageFilter:zCVob", VobType::zCMessageFilter}, + {"zCCodeMaster:zCVob", VobType::zCCodeMaster}, + {"zCTrigger:zCVob", VobType::zCTrigger}, + {"zCTriggerList:zCTrigger:zCVob", VobType::zCTriggerList}, + {"oCTriggerScript:zCTrigger:zCVob", VobType::oCTriggerScript}, + {"zCMover:zCTrigger:zCVob", VobType::zCMover}, + {"oCTriggerChangeLevel:zCTrigger:zCVob", VobType::oCTriggerChangeLevel}, + {"zCTriggerWorldStart:zCVob", VobType::zCTriggerWorldStart}, + {"zCTriggerUntouch:zCVob", VobType::zCTriggerUntouch}, + {"zCCSCamera:zCVob", VobType::zCCSCamera}, + {"zCCamTrj_KeyFrame:zCVob", VobType::zCCamTrj_KeyFrame}, + {"oCTouchDamage:zCTouchDamage:zCVob", VobType::oCTouchDamage}, + {"zCEarthquake:zCVob", VobType::zCEarthquake}, + {"zCMoverControler:zCVob", VobType::zCMoverController}, + {"zCVobScreenFX:zCVob", VobType::zCVobScreenFX}, + {"zCVobStair:zCVob", VobType::zCVobStair}, + {"oCCSTrigger:zCTrigger:zCVob", VobType::oCCSTrigger}, + {"\xA7", VobType::ignored}, // some sort of padding object, probably. seems to be always empty }; - std::unique_ptr parse_vob_tree(archive_reader& in, game_version version) { - std::vector> vobs {}; + std::unique_ptr parse_vob_tree(ReadArchive& in, GameVersion version) { + std::vector> vobs {}; - archive_object obj; + ArchiveObject obj; if (!in.read_object_begin(obj)) { - throw parser_error("vob_tree: expected object where there was none"); + throw zenkit::ParserError("vob_tree: expected object where there was none"); } - vob_type type; + VobType type; - if (const auto& it = vob_type_map.find(obj.class_name); it != vob_type_map.end()) { + if (const auto& it = TYPES.find(obj.class_name); it != TYPES.end()) { type = it->second; } else { - type = vob_type::unknown; + type = VobType::unknown; } - std::unique_ptr object; + std::unique_ptr object; switch (type) { - case vob_type::zCCamTrj_KeyFrame: - case vob_type::zCVobLevelCompo: - case vob_type::zCVobStartpoint: - case vob_type::zCVobScreenFX: - case vob_type::zCVobStair: - case vob_type::zCVobSpot: - case vob_type::zCVob: - object = std::make_unique(); - vob::parse(*object, in, version); - break; - case vob_type::zCCSCamera: - object = std::make_unique(); - vobs::cs_camera::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCVobAnimate: - object = std::make_unique(); - vobs::animate::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCZoneVobFarPlane: - case vob_type::zCZoneVobFarPlaneDefault: - object = std::make_unique(); - vobs::zone_far_plane::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCZoneZFogDefault: - case vob_type::zCZoneZFog: - object = std::make_unique(); - vobs::zone_fog::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCVobLensFlare: - object = std::make_unique(); - vobs::lens_flare::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCItem: - object = std::make_unique(); - vobs::item::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCTrigger: - case vob_type::oCCSTrigger: - object = std::make_unique(); - vobs::trigger::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCMOB: - object = std::make_unique(); - vobs::mob::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCMobInter: - case vob_type::oCMobLadder: - case vob_type::oCMobSwitch: - case vob_type::oCMobWheel: - case vob_type::oCMobBed: - object = std::make_unique(); - vobs::mob_inter::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCMobFire: - object = std::make_unique(); - vobs::mob_fire::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCMobContainer: - object = std::make_unique(); - vobs::mob_container::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCMobDoor: - object = std::make_unique(); - vobs::mob_door::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCPFXController: - object = std::make_unique(); - vobs::pfx_controller::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCVobLight: - object = std::make_unique(); - vobs::light::parse((vobs::light&) *object, in, version); - break; - case vob_type::zCVobSound: - object = std::make_unique(); - vobs::sound::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCVobSoundDaytime: - object = std::make_unique(); - vobs::sound_daytime::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCZoneMusic: - case vob_type::oCZoneMusicDefault: - object = std::make_unique(); - vobs::zone_music::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCMessageFilter: - object = std::make_unique(); - vobs::message_filter::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCCodeMaster: - object = std::make_unique(); - vobs::code_master::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCTriggerList: - object = std::make_unique(); - vobs::trigger_list::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCTriggerScript: - object = std::make_unique(); - vobs::trigger_script::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCMover: - object = std::make_unique(); - vobs::trigger_mover::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCTriggerChangeLevel: - object = std::make_unique(); - vobs::trigger_change_level::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCTriggerWorldStart: - object = std::make_unique(); - vobs::trigger_world_start::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCTouchDamage: - object = std::make_unique(); - vobs::touch_damage::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCTriggerUntouch: - object = std::make_unique(); - vobs::trigger_untouch::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCEarthquake: - object = std::make_unique(); - vobs::earthquake::parse(dynamic_cast(*object), in, version); - break; - case vob_type::zCMoverController: - object = std::make_unique(); - vobs::mover_controller::parse(dynamic_cast(*object), in, version); - break; - case vob_type::oCNpc: - object = std::make_unique(); - vobs::npc::parse(dynamic_cast(*object), in, version); - break; - case vob_type::ignored: - break; - case vob_type::unknown: - PX_LOGW("vob_tree: encountered unknown VOb [", - obj.object_name, - " ", - obj.class_name, - " ", - obj.version, - " ", - obj.index, - "]"); + case VobType::zCCamTrj_KeyFrame: + case VobType::zCVobLevelCompo: + case VobType::zCVobStartpoint: + case VobType::zCVobStair: + case VobType::zCVobSpot: + case VobType::zCVob: + object = std::make_unique(); + break; + case VobType::zCVobScreenFX: + object = std::make_unique(); + break; + case VobType::zCCSCamera: + object = std::make_unique(); + break; + case VobType::zCVobAnimate: + object = std::make_unique(); + break; + case VobType::zCZoneVobFarPlane: + case VobType::zCZoneVobFarPlaneDefault: + object = std::make_unique(); + break; + case VobType::zCZoneZFogDefault: + case VobType::zCZoneZFog: + object = std::make_unique(); + break; + case VobType::zCVobLensFlare: + object = std::make_unique(); + break; + case VobType::oCItem: + object = std::make_unique(); + break; + case VobType::zCTrigger: + case VobType::oCCSTrigger: + object = std::make_unique(); + break; + case VobType::oCMOB: + object = std::make_unique(); + break; + case VobType::oCMobInter: + case VobType::oCMobLadder: + case VobType::oCMobSwitch: + case VobType::oCMobWheel: + case VobType::oCMobBed: + object = std::make_unique(); + break; + case VobType::oCMobFire: + object = std::make_unique(); + break; + case VobType::oCMobContainer: + object = std::make_unique(); + break; + case VobType::oCMobDoor: + object = std::make_unique(); + break; + case VobType::zCPFXController: + object = std::make_unique(); + break; + case VobType::zCVobLight: + object = std::make_unique(); + break; + case VobType::zCVobSound: + object = std::make_unique(); + break; + case VobType::zCVobSoundDaytime: + object = std::make_unique(); break; + case VobType::oCZoneMusic: + case VobType::oCZoneMusicDefault: + object = std::make_unique(); + break; + case VobType::zCMessageFilter: + object = std::make_unique(); + break; + case VobType::zCCodeMaster: + object = std::make_unique(); + break; + case VobType::zCTriggerList: + object = std::make_unique(); + break; + case VobType::oCTriggerScript: + object = std::make_unique(); + break; + case VobType::zCMover: + object = std::make_unique(); + break; + case VobType::oCTriggerChangeLevel: + object = std::make_unique(); + break; + case VobType::zCTriggerWorldStart: + object = std::make_unique(); + break; + case VobType::oCTouchDamage: + object = std::make_unique(); + break; + case VobType::zCTriggerUntouch: + object = std::make_unique(); + break; + case VobType::zCEarthquake: + object = std::make_unique(); + break; + case VobType::zCMoverController: + object = std::make_unique(); + break; + case VobType::oCNpc: + object = std::make_unique(); + break; + case VobType::ignored: + break; + case VobType::unknown: + ZKLOGW("VobTree", + "Encountered unknown VOb [%s %s %u %u]", + obj.object_name.c_str(), + obj.class_name.c_str(), + obj.version, + obj.index); + break; + } + + if (object) { + object->load(in, version); } if (!in.read_object_end()) { - PX_LOGW("vob: VOb \"", obj.class_name, "\" not fully parsed"); + ZKLOGW("VobTree", "\"%s\" not fully parsed", obj.class_name.c_str()); in.skip_object(true); } @@ -265,5 +240,4 @@ namespace phoenix { return object; } - -} // namespace phoenix +} // namespace zenkit diff --git a/src/world/WayNet.cc b/src/world/WayNet.cc index bfac0620..289eae90 100644 --- a/src/world/WayNet.cc +++ b/src/world/WayNet.cc @@ -1,9 +1,12 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include "zenkit/world/WayNet.hh" +#include "zenkit/Archive.hh" -namespace phoenix { - static void read_waypoint_data(way_point& wp, archive_reader& in) { +#include "../Internal.hh" + +namespace zenkit { + static void read_waypoint_data(WayPoint& wp, ReadArchive& in) { wp.name = in.read_string(); // wpName wp.water_depth = in.read_int(); // waterDepth wp.under_water = in.read_bool(); // underWater @@ -12,91 +15,77 @@ namespace phoenix { wp.free_point = true; } - way_net way_net::parse(archive_reader& in) { - try { - way_net net; - archive_object obj; + void WayNet::load(ReadArchive& in) { + ArchiveObject obj; + + if (!in.read_object_begin(obj)) { + throw zenkit::ParserError {"WayNet", "root object missing"}; + } + + (void) /* auto version = */ in.read_int(); // waynetVersion + auto count = in.read_int(); // numWaypoints + this->waypoints.reserve(count); + + std::unordered_map obj_id_to_wp {}; - if (!in.read_object_begin(obj)) { - throw parser_error {"way_net", "root object missing"}; + for (int32_t i = 0; i < count; ++i) { + if (!in.read_object_begin(obj) || obj.class_name != "zCWaypoint") { + throw zenkit::ParserError {"WayNet", "missing waypoint object #" + std::to_string(i)}; } - (void) /* auto version = */ in.read_int(); // waynetVersion - auto count = in.read_int(); // numWaypoints - net.waypoints.reserve(count); + auto& wp = this->waypoints.emplace_back(); + read_waypoint_data(wp, in); + wp.free_point = true; + obj_id_to_wp[obj.index] = this->waypoints.size() - 1; - std::unordered_map obj_id_to_wp {}; + if (!in.read_object_end()) { + ZKLOGE("WayNet", "free point %u not fully parsed", obj.index); + in.skip_object(true); + } + } - for (int32_t i = 0; i < count; ++i) { - if (!in.read_object_begin(obj) || obj.class_name != "zCWaypoint") { - throw parser_error {"way_net", "missing waypoint object #" + std::to_string(i)}; - } + auto edge_count = in.read_int(); // numWays - auto& wp = net.waypoints.emplace_back(); - read_waypoint_data(wp, in); - wp.free_point = true; - net._m_name_to_waypoint[wp.name] = net.waypoints.size() - 1; - obj_id_to_wp[obj.index] = net.waypoints.size() - 1; + for (int32_t i = 0; i < edge_count; ++i) { + auto& edge = this->edges.emplace_back(); - if (!in.read_object_end()) { - PX_LOGW("way_net: free point ", obj.index, " not fully parsed"); - in.skip_object(true); + for (int32_t j = 0; j < 2; ++j) { + if (!in.read_object_begin(obj)) { + throw zenkit::ParserError {"WayNet", "missing edge object #" + std::to_string(i)}; } - } - auto edge_count = in.read_int(); // numWays - - for (int32_t i = 0; i < edge_count; ++i) { - auto& edge = net.edges.emplace_back(); - - for (int32_t j = 0; j < 2; ++j) { - if (!in.read_object_begin(obj)) { - throw parser_error {"way_net", "missing edge object #" + std::to_string(i)}; - } - - std::uint32_t wp; - - if (obj.class_name == "\xA7" /* zReference */) { - wp = obj_id_to_wp[obj.index]; - } else if (obj.class_name == "zCWaypoint") { - auto& new_wp = net.waypoints.emplace_back(); - read_waypoint_data(new_wp, in); - new_wp.free_point = false; - net._m_name_to_waypoint[new_wp.name] = net.waypoints.size() - 1; - obj_id_to_wp[obj.index] = net.waypoints.size() - 1; - wp = net.waypoints.size() - 1; - } else { - throw parser_error {"way_net", - "failed to parse edge #" + std::to_string(i) + ": unknown class name '" + - obj.class_name + "'"}; - } - - if (j == 0) { - edge.a = wp; - } else { - edge.b = wp; - } - - if (!in.read_object_end()) { - PX_LOGW("way_net: edge ", i * 2 + j, " at index ", obj.index, " not fully parsed"); - in.skip_object(true); - } + std::uint32_t wp; + + if (obj.class_name == "\xA7" /* zReference */) { + wp = obj_id_to_wp[obj.index]; + } else if (obj.class_name == "zCWaypoint") { + auto& new_wp = this->waypoints.emplace_back(); + read_waypoint_data(new_wp, in); + new_wp.free_point = false; + obj_id_to_wp[obj.index] = this->waypoints.size() - 1; + wp = this->waypoints.size() - 1; + } else { + throw zenkit::ParserError {"WayNet", + "failed to parse edge #" + std::to_string(i) + ": unknown class name '" + + obj.class_name + "'"}; } - } - if (!in.read_object_end()) { - PX_LOGW("way_net: not fully parsed"); - in.skip_object(true); + if (j == 0) { + edge.a = wp; + } else { + edge.b = wp; + } + + if (!in.read_object_end()) { + ZKLOGW("WayNet", "WayEdge %u at index %u not fully parsed", i * 2 + j, obj.index); + in.skip_object(true); + } } - return net; - } catch (const buffer_error& exc) { - throw parser_error {"way_net", exc, "eof reached"}; } - } - const way_point* way_net::waypoint(const std::string& name) const { - if (auto it = _m_name_to_waypoint.find(name); it != _m_name_to_waypoint.end()) - return &waypoints[it->second]; - return nullptr; + if (!in.read_object_end()) { + ZKLOGW("WayNet", "Not fully parsed"); + in.skip_object(true); + } } -} // namespace phoenix +} // namespace zenkit diff --git a/support/BuildSupport.cmake b/support/BuildSupport.cmake new file mode 100644 index 00000000..192d2f0d --- /dev/null +++ b/support/BuildSupport.cmake @@ -0,0 +1,86 @@ +## Compute the compile and link flags for the current compiler. +## +## Args: +## SANITIZERS(bool): Whether to enable sanitizers or not. +## +## Returns: +## COMPILE(string): A string containing the compiler flags, separated by spaces +## LINK(string): A string containing the linker flags, separated by spaces +function(bs_select_cflags SANITIZERS COMPILE LINK) + if (MSVC) + bs_internal_select_cflags_msvc(${SANITIZERS} _INTERNAL_COMPILE_FLAGS _INTERNAL_LINK_FLAGS) + elseif(CMAKE_C_COMPILER_ID MATCHES "^Clang") + bs_internal_select_cflags_clang(${SANITIZERS} _INTERNAL_COMPILE_FLAGS _INTERNAL_LINK_FLAGS) + elseif(CMAKE_C_COMPILER_ID MATCHES "^GCC") + bs_internal_select_cflags_gcc(${SANITIZERS} _INTERNAL_COMPILE_FLAGS _INTERNAL_LINK_FLAGS) + endif() + + # return _INTERNAL_COMPILE_FLAGS, _INTERNAL_LINK_FLAGS; + set(${COMPILE} ${_INTERNAL_COMPILE_FLAGS} PARENT_SCOPE) + set(${LINK} ${_INTERNAL_LINK_FLAGS} PARENT_SCOPE) + return() +endfunction() + +## Compute the compile and link flags for MSVC +## +## Args: +## SANITIZERS(bool): Whether to enable sanitizers or not. +## +## Returns: +## COMPILE(list): A list containing the compiler flags +## LINK(list): A list containing the linker flags +function(bs_internal_select_cflags_msvc SANITIZERS COMPILE LINK) + list(APPEND _INTERNAL_COMPILE_FLAGS "/W4") + + if (CMAKE_BUILD_TYPE MATCHES "^Debug" AND ${SANITIZERS}) + list(APPEND _INTERNAL_COMPILE_FLAGS "/fsanitize=address") + endif() + + # return _INTERNAL_COMPILE_FLAGS, _INTERNAL_LINK_FLAGS; + set(${COMPILE} ${_INTERNAL_COMPILE_FLAGS} PARENT_SCOPE) + set(${LINK} ${_INTERNAL_LINK_FLAGS} PARENT_SCOPE) + return() +endfunction() + +## Compute the compile and link flags for GCC +## +## Args: +## SANITIZERS(bool): Whether to enable sanitizers or not. +## +## Returns: +## COMPILE(list): A list containing the compiler flags +## LINK(list): A list containing the linker flags +function(bs_internal_select_cflags_gcc SANITIZERS COMPILE LINK) + list(APPEND _INTERNAL_FLAGS "-Wall" "-Wextra" "-Werror" "-Wconversion" "-Wshadow" "-Wpedantic") + + if (CMAKE_BUILD_TYPE MATCHES "^Debug" AND ${SANITIZERS} AND NOT WIN32) + list(APPEND _INTERNAL_FLAGS "-fsanitize=address" "-fsanitize=undefined" "-fsanitize=leak") + endif() + + # return _INTERNAL_FLAGS, _INTERNAL_FLAGS; + set(${COMPILE} ${_INTERNAL_FLAGS} PARENT_SCOPE) + set(${LINK} ${_INTERNAL_FLAGS} PARENT_SCOPE) + return() +endfunction() + +## Compute the compile and link flags for Clang +## +## Args: +## SANITIZERS(bool): Whether to enable sanitizers or not. +## +## Returns: +## COMPILE(list): A list containing the compiler flags +## LINK(list): A list containing the linker flags +function(bs_internal_select_cflags_clang SANITIZERS COMPILE LINK) + bs_internal_select_cflags_gcc(${SANITIZERS} _INTERNAL_COMPILE_FLAGS _INTERNAL_LINK_FLAGS) + + if (CMAKE_BUILD_TYPE MATCHES "^Debug") + list(APPEND _INTERNAL_COMPILE_FLAGS "-fstandalone-debug") + list(APPEND _INTERNAL_LINK_FLAGS "-fstandalone-debug") + endif() + + # return _INTERNAL_COMPILE_FLAGS, _INTERNAL_LINK_FLAGS; + set(${COMPILE} ${_INTERNAL_COMPILE_FLAGS} PARENT_SCOPE) + set(${LINK} ${_INTERNAL_LINK_FLAGS} PARENT_SCOPE) + return() +endfunction() diff --git a/tests/TestArchive.cc b/tests/TestArchive.cc index 16770f33..4997ff7a 100644 --- a/tests/TestArchive.cc +++ b/tests/TestArchive.cc @@ -1,15 +1,18 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include +#include #include -TEST_SUITE("archive") { - TEST_CASE("archive(open:ASCII)") { - auto in = phoenix::buffer::mmap("./samples/ascii.zen"); - auto reader = phoenix::archive_reader::open(in); +TEST_SUITE("ReadArchive") { + TEST_CASE("ReadArchive.open(ASCII)") { + zenkit::Logger::set_default(zenkit::LogLevel::DEBUG); - phoenix::archive_object obj; + auto in = zenkit::Read::from("./samples/ascii.zen"); + auto reader = zenkit::ReadArchive::from(in.get()); + + zenkit::ArchiveObject obj; CHECK(reader->read_object_begin(obj)); CHECK_FALSE(reader->read_object_begin(obj)); @@ -23,7 +26,7 @@ TEST_SUITE("archive") { // This failure will skip the offending entry. I am not yet sure if this is the correct behavior // or if it should just revert to the beginning of the line to enable re-parsing it. - REQUIRE_THROWS_AS(reader->read_string(), phoenix::parser_error); + REQUIRE_THROWS_AS(reader->read_string(), zenkit::ParserError); reader->skip_object(false); @@ -68,11 +71,11 @@ TEST_SUITE("archive") { CHECK_EQ(mat3[1][2], 0.0f); CHECK_EQ(mat3[2][2], 0.994702816f); - auto raw = reader->read_raw_bytes(4); - CHECK_EQ(raw.get(), 0xf2); - CHECK_EQ(raw.get(), 0x42); - CHECK_EQ(raw.get(), 0xa7); - CHECK_EQ(raw.get(), 0x10); + auto raw = reader->read_raw(4); + CHECK_EQ(raw->read_ubyte(), 0xf2); + CHECK_EQ(raw->read_ubyte(), 0x42); + CHECK_EQ(raw->read_ubyte(), 0xa7); + CHECK_EQ(raw->read_ubyte(), 0x10); CHECK(reader->read_object_begin(obj)); CHECK_FALSE(reader->read_object_begin(obj)); @@ -84,15 +87,15 @@ TEST_SUITE("archive") { CHECK(reader->read_object_end()); CHECK(reader->read_object_end()); - REQUIRE_THROWS_AS(reader->read_float(), phoenix::parser_error); + REQUIRE_THROWS_AS(reader->read_float(), zenkit::ParserError); } - TEST_CASE("archive(open:BINARY)") { - auto in = phoenix::buffer::mmap("./samples/binary.zen"); - auto reader = phoenix::archive_reader::open(in); + TEST_CASE("ReadArchive.open(BINARY)") { + auto in = zenkit::Read::from("./samples/binary.zen"); + auto reader = zenkit::ReadArchive::from(in.get()); CHECK_EQ(reader->read_string(), "DT_BOOKSHELF_V1_1"); - phoenix::archive_object obj; + zenkit::ArchiveObject obj; CHECK(reader->read_object_begin(obj)); CHECK_EQ(obj.object_name, "%"); @@ -115,7 +118,7 @@ TEST_SUITE("archive") { CHECK_EQ(reader->read_float(), 0.0f); } - TEST_CASE("archive(open:BIN_SAFE)" * doctest::skip()) { + TEST_CASE("ReadArchive.open(BIN_SAFE)" * doctest::skip()) { // FIXME: Stub } } diff --git a/tests/TestCutsceneLibrary.cc b/tests/TestCutsceneLibrary.cc index 238db92c..22181305 100644 --- a/tests/TestCutsceneLibrary.cc +++ b/tests/TestCutsceneLibrary.cc @@ -1,12 +1,14 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include -TEST_SUITE("messages") { - TEST_CASE("messages(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/ou.proprietary.bin"); - auto msgs = phoenix::messages::parse(buf); +TEST_SUITE("CutsceneLibrary") { + TEST_CASE("CutsceneLibrary.load(GOTHIC1)") { + auto r = zenkit::Read::from("./samples/ou.proprietary.bin"); + zenkit::CutsceneLibrary msgs {}; + msgs.load(r.get()); CHECK_EQ(msgs.blocks.size(), 7360); @@ -33,7 +35,7 @@ TEST_SUITE("messages") { CHECK_EQ(msg200->message.name, "DIA_BAALTARAN_INTOCASTLE_EXACTLY_15_00.WAV"); } - TEST_CASE("messages(parse:g2)" * doctest::skip()) { + TEST_CASE("CutsceneLibrary.load(GOTHIC2)" * doctest::skip()) { // TODO: Stub } } diff --git a/tests/TestDaedalusScript.cc b/tests/TestDaedalusScript.cc index c66ce391..35f5d2f9 100644 --- a/tests/TestDaedalusScript.cc +++ b/tests/TestDaedalusScript.cc @@ -1,18 +1,19 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include -namespace px = phoenix; - -[[maybe_unused]] static bool compare_instruction(phoenix::instruction a, phoenix::instruction b) { +[[maybe_unused]] static bool compare_instruction(zenkit::DaedalusInstruction a, zenkit::DaedalusInstruction b) { return a.op == b.op && a.index == b.index && a.immediate == b.immediate && a.address == b.address && a.symbol == b.symbol; } -TEST_SUITE("script") { - TEST_CASE("script(parse)") { - auto scr = phoenix::script::parse("./samples/menu.proprietary.dat"); +TEST_SUITE("DaedalusScript") { + TEST_CASE("DaedalusScript.load") { + auto r = zenkit::Read::from("./samples/menu.proprietary.dat"); + zenkit::DaedalusScript scr {}; + scr.load(r.get()); auto& syms = scr.symbols(); CHECK_EQ(syms.size(), 1093); @@ -40,42 +41,42 @@ TEST_SUITE("script") { CHECK_EQ(class_symbol->name(), "C_MENU"); CHECK_EQ(class_symbol->count(), 13); - CHECK_EQ(class_symbol->type(), px::datatype::class_); + CHECK_EQ(class_symbol->type(), zenkit::DaedalusDataType::CLASS); CHECK_FALSE(class_symbol->has_return()); CHECK_EQ(class_symbol->class_size(), 3096); // CHECK_EQ(class_symbol->parent(), -1); FIXME CHECK_EQ(member_symbol->name(), "C_MENU.BACKPIC"); CHECK_EQ(member_symbol->count(), 1); - CHECK_EQ(member_symbol->type(), px::datatype::string); + CHECK_EQ(member_symbol->type(), zenkit::DaedalusDataType::STRING); CHECK_FALSE(member_symbol->has_return()); CHECK_EQ(member_symbol->parent(), 118); CHECK_EQ(prototype_symbol->name(), "C_MENU_DEF"); CHECK_EQ(prototype_symbol->count(), 0); CHECK_EQ(prototype_symbol->address(), 236); - CHECK_EQ(prototype_symbol->type(), px::datatype::prototype); + CHECK_EQ(prototype_symbol->type(), zenkit::DaedalusDataType::PROTOTYPE); CHECK_FALSE(prototype_symbol->has_return()); CHECK_EQ(prototype_symbol->parent(), 118); CHECK_EQ(instance_symbol->name(), "MENU_MAIN"); CHECK_EQ(instance_symbol->count(), 0); CHECK_EQ(instance_symbol->address(), 372); - CHECK_EQ(instance_symbol->type(), px::datatype::instance); + CHECK_EQ(instance_symbol->type(), zenkit::DaedalusDataType::INSTANCE); CHECK_FALSE(instance_symbol->has_return()); CHECK_EQ(instance_symbol->parent(), 133); CHECK_EQ(function_symbol->name(), "SHOWINTRO"); CHECK_EQ(function_symbol->count(), 0); CHECK_EQ(function_symbol->address(), 1877); - CHECK_EQ(function_symbol->type(), px::datatype::function); + CHECK_EQ(function_symbol->type(), zenkit::DaedalusDataType::FUNCTION); CHECK(function_symbol->has_return()); - CHECK_EQ(function_symbol->rtype(), px::datatype::integer); + CHECK_EQ(function_symbol->rtype(), zenkit::DaedalusDataType::INT); // CHECK(function_symbol->parent(), -1); FIXME CHECK_EQ(external_symbol->name(), "UPDATE_CHOICEBOX"); CHECK_EQ(external_symbol->count(), 1); - CHECK_EQ(external_symbol->type(), px::datatype::function); + CHECK_EQ(external_symbol->type(), zenkit::DaedalusDataType::FUNCTION); CHECK(external_symbol->is_external()); CHECK(external_symbol->is_const()); CHECK_FALSE(external_symbol->has_return()); @@ -111,12 +112,4 @@ TEST_SUITE("script") { } */ } - - TEST_CASE("script(parse:g1)" * doctest::skip()) { - // TODO: Stub - } - - TEST_CASE("script(parse:g2)" * doctest::skip()) { - // TODO: Stub - } } diff --git a/tests/TestFont.cc b/tests/TestFont.cc index e9b3f896..4271f2b5 100644 --- a/tests/TestFont.cc +++ b/tests/TestFont.cc @@ -1,23 +1,25 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include +#include #include -using namespace phoenix; +using namespace zenkit; -static const glyph G1_GLYPH0 {0, {glm::vec2 {0.0f, 0.0f}, glm::vec2 {0.0f, 0.0f}}}; -static const glyph G1_GLYPH127 {8, {glm::vec2 {0.3984375f, 0.23828125f}, glm::vec2 {0.412109375f, 0.30859375f}}}; -static const glyph G1_GLYPH255 {9, {glm::vec2 {0.95703125f, 0.55078125f}, glm::vec2 {0.97265625f, 0.62109375f}}}; +static const FontGlyph G1_GLYPH0 {0, {glm::vec2 {0.0f, 0.0f}, glm::vec2 {0.0f, 0.0f}}}; +static const FontGlyph G1_GLYPH127 {8, {glm::vec2 {0.3984375f, 0.23828125f}, glm::vec2 {0.412109375f, 0.30859375f}}}; +static const FontGlyph G1_GLYPH255 {9, {glm::vec2 {0.95703125f, 0.55078125f}, glm::vec2 {0.97265625f, 0.62109375f}}}; -static const glyph G2_GLYPH0 {0, {glm::vec2 {0.0f, 0.0f}, glm::vec2 {0.0f, 0.0f}}}; -static const glyph G2_GLYPH127 {8, {glm::vec2 {0.3984375f, 0.23828125f}, glm::vec2 {0.412109375f, 0.30859375f}}}; -static const glyph G2_GLYPH255 {10, {glm::vec2 {0.958984375f, 0.55078125f}, glm::vec2 {0.9765625f, 0.62109375f}}}; +static const FontGlyph G2_GLYPH0 {0, {glm::vec2 {0.0f, 0.0f}, glm::vec2 {0.0f, 0.0f}}}; +static const FontGlyph G2_GLYPH127 {8, {glm::vec2 {0.3984375f, 0.23828125f}, glm::vec2 {0.412109375f, 0.30859375f}}}; +static const FontGlyph G2_GLYPH255 {10, {glm::vec2 {0.958984375f, 0.55078125f}, glm::vec2 {0.9765625f, 0.62109375f}}}; -TEST_SUITE("font") { - TEST_CASE("font(parse:g1)") { - auto in = buffer::mmap("./samples/G1/FONT_OLD_10_WHITE_HI.FNT"); - auto fnt = font::parse(in); +TEST_SUITE("Font") { + TEST_CASE("Font.load(GOTHIC1)") { + auto in = zenkit::Read::from("./samples/G1/FONT_OLD_10_WHITE_HI.FNT"); + zenkit::Font fnt {}; + fnt.load(in.get()); CHECK_EQ(fnt.name, "FONT_OLD_10_WHITE_HI.TGA"); CHECK_EQ(fnt.height, 17); @@ -28,9 +30,10 @@ TEST_SUITE("font") { CHECK_EQ(fnt.glyphs[255], G1_GLYPH255); } - TEST_CASE("font(parse:g2)") { - auto in = buffer::mmap("./samples/G2/FONT_OLD_10_WHITE_HI.FNT"); - auto fnt = font::parse(in); + TEST_CASE("Font.load(GOTHIC2)") { + auto in = zenkit::Read::from("./samples/G2/FONT_OLD_10_WHITE_HI.FNT"); + zenkit::Font fnt {}; + fnt.load(in.get()); CHECK_EQ(fnt.name, "FONT_OLD_10_WHITE_HI.TGA"); CHECK_EQ(fnt.height, 18); diff --git a/tests/TestMaterial.cc b/tests/TestMaterial.cc index 6299184d..6034c390 100644 --- a/tests/TestMaterial.cc +++ b/tests/TestMaterial.cc @@ -1,29 +1,32 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include +#include +#include #include -using namespace phoenix; +using namespace zenkit; -TEST_SUITE("material") { - TEST_CASE("material(parse:g1)") { - auto in = buffer::mmap("./samples/G1/DEMON_DIE_BODY.MAT"); - auto archive = archive_reader::open(in); +TEST_SUITE("Material") { + TEST_CASE("Material.load(GOTHIC1)") { + auto in = Read::from("./samples/G1/DEMON_DIE_BODY.MAT"); + auto archive = ReadArchive::from(in.get()); auto material_count = archive->read_int(); CHECK_EQ(material_count, 1); - auto m1 = material::parse(*archive); + Material m1 {}; + m1.load(*archive); CHECK_EQ(m1.name, "BODY"); - CHECK_EQ(m1.group, material_group::undefined); + CHECK_EQ(m1.group, MaterialGroup::UNDEFINED); CHECK_EQ(m1.color, glm::u8vec4 {115, 91, 77, 255}); CHECK_EQ(m1.smooth_angle, 60.0f); CHECK_EQ(m1.texture, "DEM_BODY_V0.TGA"); CHECK_EQ(m1.texture_scale, glm::vec2 {512.0f, 512.0f}); CHECK_EQ(m1.texture_anim_fps, 0.0f); - CHECK_EQ(m1.texture_anim_map_mode, phoenix::animation_mapping_mode::none); + CHECK_EQ(m1.texture_anim_map_mode, AnimationMapping::NONE); CHECK_EQ(m1.texture_anim_map_dir, glm::vec2 {9.9999997e-005, 0.0f}); CHECK_FALSE(m1.disable_collision); CHECK_FALSE(m1.disable_lightmap); @@ -34,36 +37,37 @@ TEST_SUITE("material") { // The other fields of `material` are specific to Gothic II } - TEST_CASE("material(parse:g2)") { - auto in = buffer::mmap("./samples/G2/DEMON_DIE_BODY.MAT"); - auto archive = archive_reader::open(in); + TEST_CASE("Material.load(GOTHIC2)") { + auto in = Read::from("./samples/G2/DEMON_DIE_BODY.MAT"); + auto archive = ReadArchive::from(in.get()); auto material_count = archive->read_int(); CHECK_EQ(material_count, 1); - auto m1 = material::parse(*archive); + Material m1 {}; + m1.load(*archive); CHECK_EQ(m1.name, "BODY"); - CHECK_EQ(m1.group, material_group::undefined); + CHECK_EQ(m1.group, MaterialGroup::UNDEFINED); CHECK_EQ(m1.color, glm::u8vec4 {115, 91, 77, 255}); CHECK_EQ(m1.smooth_angle, 60.0f); CHECK_EQ(m1.texture, "DEM_BODY_V0.TGA"); CHECK_EQ(m1.texture_scale, glm::vec2 {512.0f, 512.0f}); CHECK_EQ(m1.texture_anim_fps, 0.0f); - CHECK_EQ(m1.texture_anim_map_mode, phoenix::animation_mapping_mode::none); + CHECK_EQ(m1.texture_anim_map_mode, AnimationMapping::NONE); CHECK_EQ(m1.texture_anim_map_dir, glm::vec2 {0.0f, 0.0f}); CHECK_FALSE(m1.disable_collision); CHECK_FALSE(m1.disable_lightmap); CHECK_FALSE(m1.dont_collapse); CHECK_EQ(m1.detail_object, ""); CHECK_EQ(m1.default_mapping, glm::vec2 {2.34375f, 2.34375f}); - CHECK_EQ(m1.alpha_func, alpha_function::none); + CHECK_EQ(m1.alpha_func, AlphaFunction::NONE); CHECK_EQ(m1.detail_texture_scale, 1.0f); CHECK_FALSE(m1.force_occluder); CHECK_FALSE(m1.environment_mapping); CHECK_EQ(m1.environment_mapping_strength, 1.0f); - CHECK_EQ(m1.wave_mode, wave_mode_type::none); - CHECK_EQ(m1.wave_speed, wave_speed_type::normal); + CHECK_EQ(m1.wave_mode, WaveMode::NONE); + CHECK_EQ(m1.wave_speed, WaveSpeed::NORMAL); CHECK_FALSE(m1.force_occluder); CHECK_EQ(m1.wave_max_amplitude, 30.0); CHECK_EQ(m1.wave_grid_size, 100.0f); diff --git a/tests/TestModel.cc b/tests/TestModel.cc index 0fe13b68..e11a838f 100644 --- a/tests/TestModel.cc +++ b/tests/TestModel.cc @@ -1,4 +1,4 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include #include diff --git a/tests/TestModelAnimation.cc b/tests/TestModelAnimation.cc index 9cccfbfc..7cb5f2a2 100644 --- a/tests/TestModelAnimation.cc +++ b/tests/TestModelAnimation.cc @@ -1,9 +1,10 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include -using namespace phoenix; +using namespace zenkit; static const std::vector G1_NODE_INDICES {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, 26, 27, 28, 29, 30, 31, 32, 33}; @@ -11,24 +12,26 @@ static const std::vector G1_NODE_INDICES {0, 1, 2, 3, 4, 5, 6, static const std::vector G2_NODE_INDICES {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, 26, 27, 28, 29, 30, 31, 32, 33}; -static const animation_sample G1_SAMPLE0 {glm::vec3 {12.635274887084961f, 88.75251770019531f, -1.093428611755371f}, - glm::quat {0.7771535515785217f, 0.0f, 0.6293110251426697f, 0.0f}}; -static const animation_sample G1_SAMPLE249 {glm::vec3 {12.626323699951172f, -0.00145721435546875f, 22.643518447875977f}, - glm::quat {0.7071319222450256f, 0.0f, 0.70708167552948f, 0.0f}}; -static const animation_sample G1_SAMPLE499 {glm::vec3 {12.626323699951172, -0.00145721435546875, 22.643518447875977}, - glm::quat {0.7071319222450256, 0.0, 0.70708167552948, 0.0}}; - -static const animation_sample G2_SAMPLE0 {glm::vec3 {12.635274887084961f, 88.75251770019531f, -1.093428611755371f}, - glm::quat {0.7771535515785217f, 0.0f, 0.6293110251426697f, 0.0f}}; -static const animation_sample G2_SAMPLE249 {glm::vec3 {12.626323699951172f, -0.00145721435546875f, 22.643518447875977f}, - glm::quat {0.7071319222450256f, 0.0f, 0.70708167552948f, 0.0f}}; -static const animation_sample G2_SAMPLE499 {glm::vec3 {12.626323699951172, -0.00145721435546875, 22.643518447875977}, - glm::quat {0.7071319222450256, 0.0, 0.70708167552948, 0.0}}; - -TEST_SUITE("animation") { - TEST_CASE("animation(parse:g1)") { - auto in = buffer::mmap("./samples/G1/HUMANS-S_FISTRUN.MAN"); - auto anim = animation::parse(in); +static const AnimationSample G1_SAMPLE0 {glm::vec3 {12.635274887084961f, 88.75251770019531f, -1.093428611755371f}, + glm::quat {0.7771535515785217f, 0.0f, 0.6293110251426697f, 0.0f}}; +static const AnimationSample G1_SAMPLE249 {glm::vec3 {12.626323699951172f, -0.00145721435546875f, 22.643518447875977f}, + glm::quat {0.7071319222450256f, 0.0f, 0.70708167552948f, 0.0f}}; +static const AnimationSample G1_SAMPLE499 {glm::vec3 {12.626323699951172, -0.00145721435546875, 22.643518447875977}, + glm::quat {0.7071319222450256, 0.0, 0.70708167552948, 0.0}}; + +static const AnimationSample G2_SAMPLE0 {glm::vec3 {12.635274887084961f, 88.75251770019531f, -1.093428611755371f}, + glm::quat {0.7771535515785217f, 0.0f, 0.6293110251426697f, 0.0f}}; +static const AnimationSample G2_SAMPLE249 {glm::vec3 {12.626323699951172f, -0.00145721435546875f, 22.643518447875977f}, + glm::quat {0.7071319222450256f, 0.0f, 0.70708167552948f, 0.0f}}; +static const AnimationSample G2_SAMPLE499 {glm::vec3 {12.626323699951172, -0.00145721435546875, 22.643518447875977}, + glm::quat {0.7071319222450256, 0.0, 0.70708167552948, 0.0}}; + +TEST_SUITE("ModelAnimation") { + TEST_CASE("ModelAnimation.load(GOTHIC1)") { + auto in = Read::from("./samples/G1/HUMANS-S_FISTRUN.MAN"); + + ModelAnimation anim {}; + anim.load(in.get()); auto box0 = anim.bbox; CHECK_EQ(box0.max, glm::vec3 {46.33139419555664f, 67.0935287475586f, 49.88602828979492f}); @@ -45,8 +48,8 @@ TEST_SUITE("animation") { CHECK_EQ(anim.node_count, 25); CHECK_EQ(anim.node_indices.size(), 25); CHECK_EQ(anim.node_indices, G1_NODE_INDICES); - CHECK_EQ(anim.sample_position_range_min, -9.01021957397461f); - CHECK_EQ(anim.sample_position_scalar, 0.001491763861849904f); + CHECK_EQ(anim.sample_position_min, -9.01021957397461f); + CHECK_EQ(anim.sample_position_scale, 0.001491763861849904f); CHECK_EQ(anim.samples.size(), 25 * 20 /* node_count * frame_count */); CHECK_EQ(anim.samples[0], G1_SAMPLE0); @@ -60,9 +63,11 @@ TEST_SUITE("animation") { "1\t50\tFPS:10)"); } - TEST_CASE("animation(parse:g2)") { - auto in = buffer::mmap("./samples/G2/HUMANS-S_FISTRUN.MAN"); - auto anim = animation::parse(in); + TEST_CASE("ModelAnimation.load(GOTHIC2)") { + auto in = Read::from("./samples/G2/HUMANS-S_FISTRUN.MAN"); + + ModelAnimation anim {}; + anim.load(in.get()); auto box0 = anim.bbox; CHECK_EQ(box0.max, glm::vec3 {46.33139419555664f, 67.0935287475586f, 49.88602828979492f}); @@ -79,8 +84,8 @@ TEST_SUITE("animation") { CHECK_EQ(anim.node_count, 25); CHECK_EQ(anim.node_indices.size(), 25); CHECK_EQ(anim.node_indices, G2_NODE_INDICES); - CHECK_EQ(anim.sample_position_range_min, -9.01021957397461f); - CHECK_EQ(anim.sample_position_scalar, 0.001491763861849904f); + CHECK_EQ(anim.sample_position_min, -9.01021957397461f); + CHECK_EQ(anim.sample_position_scale, 0.001491763861849904f); CHECK_EQ(anim.samples.size(), 25 * 20 /* node_count * frame_count */); CHECK_EQ(anim.samples[0], G2_SAMPLE0); diff --git a/tests/TestModelHierarchy.cc b/tests/TestModelHierarchy.cc index f3a7773c..3b1cd797 100644 --- a/tests/TestModelHierarchy.cc +++ b/tests/TestModelHierarchy.cc @@ -1,12 +1,15 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include -TEST_SUITE("model_hierarchy") { - TEST_CASE("model_hierarchy(parse:?)") { - auto in = phoenix::buffer::mmap("./samples/hierarchy0.mdh"); - auto mesh = phoenix::model_hierarchy::parse(in); +TEST_SUITE("ModelHierarchy") { + TEST_CASE("ModelHierarchy.load(GOTHIC?)") { + auto in = zenkit::Read::from("./samples/hierarchy0.mdh"); + + zenkit::ModelHierarchy mesh {}; + mesh.load(in.get()); CHECK_EQ(mesh.nodes.size(), 7); CHECK_EQ(mesh.nodes[0].name, "BIP01 MUEHLE"); @@ -28,11 +31,11 @@ TEST_SUITE("model_hierarchy") { CHECK_EQ(mesh.root_translation, glm::vec3 {0, 0, -394.040466}); } - TEST_CASE("model_hierarchy(parse:g1)" * doctest::skip()) { + TEST_CASE("ModelHierarchy.load(GOTHIC1)" * doctest::skip()) { // TODO: Stub } - TEST_CASE("model_hierarchy(parse:g2)" * doctest::skip()) { + TEST_CASE("ModelHierarchy.load(GOTHIC2)" * doctest::skip()) { // TODO: Stub } } diff --git a/tests/TestModelMesh.cc b/tests/TestModelMesh.cc index 6418a64e..0799699b 100644 --- a/tests/TestModelMesh.cc +++ b/tests/TestModelMesh.cc @@ -1,13 +1,15 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include -TEST_SUITE("model_mesh") { +TEST_SUITE("ModelMesh") { // TODO: find a mesh with multiple attachments - TEST_CASE("model_mesh(parse:?)") { - auto in = phoenix::buffer::mmap("./samples/secretdoor.mdm"); - auto mesh = phoenix::model_mesh::parse(in); + TEST_CASE("ModelMesh.load(GOTHIC?)") { + auto in = zenkit::Read::from("./samples/secretdoor.mdm"); + zenkit::ModelMesh mesh {}; + mesh.load(in.get()); CHECK_EQ(mesh.attachments.size(), 1); CHECK_NE(mesh.attachments.find("BIP01 DOOR"), mesh.attachments.end()); @@ -15,9 +17,10 @@ TEST_SUITE("model_mesh") { } // TODO: find a mesh which has actual vertex weights - TEST_CASE("model_mesh(parse:?)") { - auto in = phoenix::buffer::mmap("./samples/smoke_waterpipe.mdm"); - auto mesh = phoenix::model_mesh::parse(in); + TEST_CASE("ModelMesh.load(GOTHIC?)") { + auto in = zenkit::Read::from("./samples/smoke_waterpipe.mdm"); + zenkit::ModelMesh mesh {}; + mesh.load(in.get()); auto& meshes = mesh.meshes; CHECK_EQ(meshes.size(), 1); @@ -58,11 +61,11 @@ TEST_SUITE("model_mesh") { CHECK_EQ(sk.bboxes[0].children.size(), 0); } - TEST_CASE("model_mesh(parse:g1)" * doctest::skip()) { + TEST_CASE("ModelMesh.load(GOTHIC1)" * doctest::skip()) { // TODO: Stub } - TEST_CASE("model_mesh(parse:g2)" * doctest::skip()) { + TEST_CASE("ModelMesh.load(GOTHIC2)" * doctest::skip()) { // TODO: Stub } } diff --git a/tests/TestModelScript.cc b/tests/TestModelScript.cc index ca1f55ad..4d411beb 100644 --- a/tests/TestModelScript.cc +++ b/tests/TestModelScript.cc @@ -1,13 +1,16 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include +#include -TEST_SUITE("model_script") { - TEST_CASE("model_script(parse:?)") { - phoenix::logging::use_default_logger(); - auto buf = phoenix::buffer::mmap("./samples/waran.mds"); - auto script = phoenix::model_script::parse(buf); +TEST_SUITE("ModelScript") { + TEST_CASE("ModelScript.load(GOTHIC?)") { + zenkit::Logger::set_default(zenkit::LogLevel::INFO); + auto r = zenkit::Read::from("./samples/waran.mds"); + zenkit::ModelScript script {}; + script.load(r.get()); CHECK_EQ(script.skeleton.disable_mesh, true); CHECK_EQ(script.skeleton.name, "TestModelMesh.asc"); @@ -18,9 +21,9 @@ TEST_SUITE("model_script") { CHECK_EQ(script.animations[0].next, "aniNext1"); CHECK_EQ(script.animations[0].blend_in, 4.2f); CHECK_EQ(script.animations[0].blend_out, 0.5f); - CHECK_EQ(script.animations[0].flags, (phoenix::mds::af_move | phoenix::mds::af_idle)); + CHECK_EQ(script.animations[0].flags, (zenkit::AnimationFlags::MOVE | zenkit::AnimationFlags::IDLE)); CHECK_EQ(script.animations[0].model, "aniModel1"); - CHECK_EQ(script.animations[0].direction, phoenix::mds::animation_direction::forward); + CHECK_EQ(script.animations[0].direction, zenkit::AnimationDirection::FORWARD); CHECK_EQ(script.animations[0].first_frame, 221); CHECK_EQ(script.animations[0].last_frame, -331); CHECK_EQ(script.animations[0].fps, 25.0f); @@ -39,9 +42,9 @@ TEST_SUITE("model_script") { CHECK_EQ(script.animations[1].next, "aniNext2"); CHECK_EQ(script.animations[1].blend_in, 9.0f); CHECK_EQ(script.animations[1].blend_out, 0.0f); - CHECK_EQ(script.animations[1].flags, phoenix::mds::af_move); + CHECK_EQ(script.animations[1].flags, zenkit::AnimationFlags::MOVE); CHECK_EQ(script.animations[1].model, "aniModel2"); - CHECK_EQ(script.animations[1].direction, phoenix::mds::animation_direction::backward); + CHECK_EQ(script.animations[1].direction, zenkit::AnimationDirection::BACKWARD); CHECK_EQ(script.animations[1].first_frame, 222); CHECK_EQ(script.animations[1].last_frame, 332); CHECK_EQ(script.animations[1].fps, 25.0f); @@ -50,14 +53,14 @@ TEST_SUITE("model_script") { CHECK_EQ(script.animations[1].events.size(), 3); CHECK_EQ(script.animations[1].events[0].frame, 0); - CHECK_EQ(script.animations[1].events[0].type, phoenix::mds::event_tag_type::drop_torch); + CHECK_EQ(script.animations[1].events[0].type, zenkit::MdsEventType::TORCH_DROP); CHECK_EQ(script.animations[1].events[0].attached, true); CHECK_EQ(script.animations[1].events[1].frame, 1); - CHECK_EQ(script.animations[1].events[1].type, phoenix::mds::event_tag_type::window); + CHECK_EQ(script.animations[1].events[1].type, zenkit::MdsEventType::COMBO_WINDOW); CHECK_EQ(script.animations[1].events[1].frames, std::vector {1, 2, 3, 4, 5}); CHECK_EQ(script.animations[1].events[1].attached, false); CHECK_EQ(script.animations[1].events[2].frame, 0); // Defaulted - CHECK_EQ(script.animations[1].events[2].type, phoenix::mds::event_tag_type::create_item); + CHECK_EQ(script.animations[1].events[2].type, zenkit::MdsEventType::ITEM_CREATE); CHECK_EQ(script.animations[1].events[2].slot, "eventSlot"); CHECK_EQ(script.animations[1].events[2].item, "eventItem"); CHECK_EQ(script.animations[1].events[2].attached, false); @@ -128,17 +131,17 @@ TEST_SUITE("model_script") { CHECK_EQ(script.aliases[0].next, "aliasNext1"); CHECK_EQ(script.aliases[0].blend_in, 100.1f); CHECK_EQ(script.aliases[0].blend_out, 200.2f); - CHECK_EQ(script.aliases[0].flags, (phoenix::mds::af_rotate | phoenix::mds::af_queue)); + CHECK_EQ(script.aliases[0].flags, (zenkit::AnimationFlags::ROTATE | zenkit::AnimationFlags::QUEUE)); CHECK_EQ(script.aliases[0].alias, "aliasAlias1"); - CHECK_EQ(script.aliases[0].direction, phoenix::mds::animation_direction::forward); + CHECK_EQ(script.aliases[0].direction, zenkit::AnimationDirection::FORWARD); CHECK_EQ(script.aliases[1].name, "aliasName2"); CHECK_EQ(script.aliases[1].layer, 115); CHECK_EQ(script.aliases[1].next, "aliasNext2"); CHECK_EQ(script.aliases[1].blend_in, 101.1f); CHECK_EQ(script.aliases[1].blend_out, 201.2f); - CHECK_EQ(script.aliases[1].flags, phoenix::mds::af_fly); + CHECK_EQ(script.aliases[1].flags, zenkit::AnimationFlags::FLY); CHECK_EQ(script.aliases[1].alias, "aliasAlias2"); - CHECK_EQ(script.aliases[1].direction, phoenix::mds::animation_direction::backward); + CHECK_EQ(script.aliases[1].direction, zenkit::AnimationDirection::BACKWARD); CHECK_EQ(script.combinations.size(), 2); CHECK_EQ(script.combinations[0].name, "combName1"); @@ -146,7 +149,7 @@ TEST_SUITE("model_script") { CHECK_EQ(script.combinations[0].next, "combNext1"); CHECK_EQ(script.combinations[0].blend_in, 102.1f); CHECK_EQ(script.combinations[0].blend_out, 202.2f); - CHECK_EQ(script.combinations[0].flags, phoenix::mds::af_move); + CHECK_EQ(script.combinations[0].flags, zenkit::AnimationFlags::MOVE); CHECK_EQ(script.combinations[0].model, "combModel1"); CHECK_EQ(script.combinations[0].last_frame, 226); CHECK_EQ(script.combinations[1].name, "combName2"); @@ -154,7 +157,7 @@ TEST_SUITE("model_script") { CHECK_EQ(script.combinations[1].next, "combNext2"); CHECK_EQ(script.combinations[1].blend_in, 103.1f); CHECK_EQ(script.combinations[1].blend_out, 203.2f); - CHECK_EQ(script.combinations[1].flags, phoenix::mds::af_idle); + CHECK_EQ(script.combinations[1].flags, zenkit::AnimationFlags::IDLE); CHECK_EQ(script.combinations[1].model, "combModel2"); CHECK_EQ(script.combinations[1].last_frame, 227); @@ -167,9 +170,10 @@ TEST_SUITE("model_script") { CHECK_EQ(script.model_tags[1].bone, "tag2"); } - TEST_CASE("model_script(parse:?/binary") { - auto buf = phoenix::buffer::mmap("./samples/waran.msb"); - auto script = phoenix::model_script::parse(buf); + TEST_CASE("ModelScript.load(GOTHIC?,BINARY)") { + auto buf = zenkit::Read::from("./samples/waran.msb"); + zenkit::ModelScript script {}; + script.load(buf.get()); CHECK_EQ(script.skeleton.disable_mesh, true); CHECK_EQ(script.skeleton.name, "WAR_BODY"); @@ -180,9 +184,9 @@ TEST_SUITE("model_script") { CHECK_EQ(script.animations[0].next, "S_FISTRUNL"); CHECK_EQ(script.animations[0].blend_in, 0.0f); CHECK_EQ(script.animations[0].blend_out, 0.0f); - CHECK_EQ(script.animations[0].flags, phoenix::mds::af_move); + CHECK_EQ(script.animations[0].flags, zenkit::AnimationFlags::MOVE); CHECK_EQ(script.animations[0].model, "WARAN_RUN_KM01.ASC"); - CHECK_EQ(script.animations[0].direction, phoenix::mds::animation_direction::forward); + CHECK_EQ(script.animations[0].direction, zenkit::AnimationDirection::FORWARD); CHECK_EQ(script.animations[0].first_frame, 1); CHECK_EQ(script.animations[0].last_frame, 8); CHECK_EQ(script.animations[0].fps, 25.0f); @@ -195,9 +199,9 @@ TEST_SUITE("model_script") { CHECK_EQ(script.aliases[0].next, "S_FISTRUN"); CHECK_EQ(script.aliases[0].blend_in, 0.0f); CHECK_EQ(script.aliases[0].blend_out, 0.0f); - CHECK_EQ(script.aliases[0].flags, (phoenix::mds::af_move | phoenix::mds::af_idle)); + CHECK_EQ(script.aliases[0].flags, (zenkit::AnimationFlags::MOVE | zenkit::AnimationFlags::IDLE)); CHECK_EQ(script.aliases[0].alias, "S_FISTWALK"); - CHECK_EQ(script.aliases[0].direction, phoenix::mds::animation_direction::forward); + CHECK_EQ(script.aliases[0].direction, zenkit::AnimationDirection::FORWARD); CHECK_EQ(script.blends.size(), 17); CHECK_EQ(script.blends[0].name, "T_FISTRUNR_2_FISTRUN"); @@ -211,7 +215,7 @@ TEST_SUITE("model_script") { CHECK_EQ(script.combinations[0].next, "T_LOOK"); CHECK_EQ(script.combinations[0].blend_in, 0.3f); CHECK_EQ(script.combinations[0].blend_out, 0.3f); - CHECK_EQ(script.combinations[0].flags, phoenix::mds::af_move); + CHECK_EQ(script.combinations[0].flags, zenkit::AnimationFlags::MOVE); CHECK_EQ(script.combinations[0].model, "C_LOOK_"); CHECK_EQ(script.combinations[0].last_frame, 9); @@ -233,25 +237,25 @@ TEST_SUITE("model_script") { CHECK_EQ(script.animations[47].events.size(), 4); CHECK_EQ(script.animations[47].events[3].frame, 0); - CHECK_EQ(script.animations[47].events[3].type, phoenix::mds::event_tag_type::window); + CHECK_EQ(script.animations[47].events[3].type, zenkit::MdsEventType::COMBO_WINDOW); CHECK_EQ(script.animations[47].events[3].frames.size(), 2); CHECK_EQ(script.animations[47].events[3].frames[0], 8); CHECK_EQ(script.animations[47].events[3].frames[1], 15); } - TEST_CASE("model_script(parse:g1)" * doctest::skip()) { + TEST_CASE("ModelScript.load(GOTHIC1)" * doctest::skip()) { // TODO: Stub } - TEST_CASE("model_script(parse:g1/binary)" * doctest::skip()) { + TEST_CASE("ModelScript.load(GOTHIC1,BINARY)" * doctest::skip()) { // TODO: Stub } - TEST_CASE("model_script(parse:g2)" * doctest::skip()) { + TEST_CASE("ModelScript.load(GOTHIC2)" * doctest::skip()) { // TODO: Stub } - TEST_CASE("model_script(parse:g2/binary)" * doctest::skip()) { + TEST_CASE("ModelScript.load(GOTHIC2,BINARY)" * doctest::skip()) { // TODO: Stub } } diff --git a/tests/TestMorphMesh.cc b/tests/TestMorphMesh.cc index 4769e058..4d1797bb 100644 --- a/tests/TestMorphMesh.cc +++ b/tests/TestMorphMesh.cc @@ -1,12 +1,14 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include -TEST_SUITE("morph_mesh") { - TEST_CASE("morph_mesh(parse:?)") { - auto in = phoenix::buffer::mmap("./samples/morph0.mmb"); - auto mesh = phoenix::morph_mesh::parse(in); +TEST_SUITE("MorphMesh") { + TEST_CASE("MorphMesh.load(GOTHIC?)") { + auto in = zenkit::Read::from("./samples/morph0.mmb"); + zenkit::MorphMesh mesh {}; + mesh.load(in.get()); CHECK_EQ(mesh.name, "ITRWSMALLBOW"); CHECK_EQ(mesh.morph_positions.size(), 28); @@ -47,11 +49,11 @@ TEST_SUITE("morph_mesh") { CHECK_EQ(mesh.sources[1].file_name, "ITRWSMALLBOWSHOOT.ASC"); } - TEST_CASE("model_mesh(parse:g1)" * doctest::skip()) { + TEST_CASE("MorphMesh.load(GOTHIC1)" * doctest::skip()) { // TODO: Stub } - TEST_CASE("model_mesh(parse:g2)" * doctest::skip()) { + TEST_CASE("MorphMesh.load(GOTHIC2)" * doctest::skip()) { // TODO: Stub } } diff --git a/tests/TestMultiResolutionMesh.cc b/tests/TestMultiResolutionMesh.cc index 933a5193..2f6abac2 100644 --- a/tests/TestMultiResolutionMesh.cc +++ b/tests/TestMultiResolutionMesh.cc @@ -1,24 +1,26 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include -static bool compare_triangle(phoenix::triangle a, phoenix::triangle b) { +static bool compare_triangle(zenkit::MeshTriangle a, zenkit::MeshTriangle b) { return a.wedges[0] == b.wedges[0] && a.wedges[1] == b.wedges[1] && a.wedges[2] == b.wedges[2]; } -static bool compare_wedge(phoenix::wedge a, phoenix::wedge b) { +static bool compare_wedge(zenkit::MeshWedge a, zenkit::MeshWedge b) { return a.normal == b.normal && a.texture == b.texture && a.index == b.index; } -static bool compare_plane(phoenix::plane a, phoenix::plane b) { +static bool compare_plane(zenkit::MeshPlane a, zenkit::MeshPlane b) { return a.normal == b.normal && a.distance == b.distance; } -TEST_SUITE("proto_mesh") { - TEST_CASE("proto_mesh(parse:?)") { - auto in = phoenix::buffer::mmap("./samples/mesh0.mrm"); - auto mesh = phoenix::proto_mesh::parse(in); +TEST_SUITE("MultiResolutionMesh") { + TEST_CASE("MultiResolutionMesh.load(GOTHIC?)") { + auto in = zenkit::Read::from("./samples/mesh0.mrm"); + zenkit::MultiResolutionMesh mesh {}; + mesh.load(in.get()); const auto& positions = mesh.positions; CHECK_EQ(positions.size(), 8); @@ -76,11 +78,11 @@ TEST_SUITE("proto_mesh") { CHECK_EQ(submesh.wedge_map[31], 0); } - TEST_CASE("proto_mesh(parse:g1)" * doctest::skip()) { + TEST_CASE("MultiResolutionMesh.load(GOTHIC1)" * doctest::skip()) { // TODO: Stub } - TEST_CASE("proto_mesh(parse:g2)" * doctest::skip()) { + TEST_CASE("MultiResolutionMesh.load(GOTHIC2)" * doctest::skip()) { // TODO: Stub } } diff --git a/tests/TestSaveGame.cc b/tests/TestSaveGame.cc index a42836f8..ce3fdcb2 100644 --- a/tests/TestSaveGame.cc +++ b/tests/TestSaveGame.cc @@ -1,14 +1,14 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include -#include +#include +#include -using namespace phoenix::unstable; +TEST_SUITE("SaveGame") { + TEST_CASE("SaveGame.load(GOTHIC1)") { + zenkit::unstable::SaveGame save {}; + save.load("./samples/G1/Save"); -TEST_SUITE("save_game") { - TEST_CASE("save_game(parse:g1)") { - auto save = save_game::parse("./samples/G1/Save"); CHECK_EQ(save.metadata.title, "sds"); CHECK_EQ(save.metadata.world, "WORLD"); CHECK_EQ(save.metadata.time_day, 0); @@ -24,13 +24,16 @@ TEST_SUITE("save_game") { CHECK_EQ(save.script.minute, 6); // Try to parse the world data. - (void) phoenix::world::parse(*save.open_world_save(save.current_world), phoenix::game_version::gothic_1); + zenkit::World wld {}; + wld.load(save.open_world(save.current_world)->get(), zenkit::GameVersion::GOTHIC_1); // TODO: Add more checks } - TEST_CASE("save_game(parse:g1/fast)") { - auto save = save_game::parse("./samples/G1/SaveFast"); + TEST_CASE("SaveGame.load(GOTHIC1,BINARY)") { + zenkit::unstable::SaveGame save {}; + save.load("./samples/G1/SaveFast"); + CHECK_EQ(save.metadata.title, "sds_fast"); CHECK_EQ(save.metadata.world, "WORLD"); CHECK_EQ(save.metadata.time_day, 0); @@ -46,13 +49,16 @@ TEST_SUITE("save_game") { CHECK_EQ(save.script.minute, 6); // Try to parse the world data. - (void) phoenix::world::parse(*save.open_world_save(save.current_world), phoenix::game_version::gothic_1); + zenkit::World wld {}; + wld.load(save.open_world(save.current_world)->get(), zenkit::GameVersion::GOTHIC_1); // TODO: Add more checks } - TEST_CASE("save_game(parse:g2)") { - auto save = save_game::parse("./samples/G2/Save"); + TEST_CASE("SaveGame.load(GOTHIC2)") { + zenkit::unstable::SaveGame save {}; + save.load("./samples/G2/Save"); + CHECK_EQ(save.metadata.title, "uwuowo"); CHECK_EQ(save.metadata.world, "NEWWORLD"); CHECK_EQ(save.metadata.time_day, 0); @@ -70,12 +76,15 @@ TEST_SUITE("save_game") { CHECK_EQ(save.script.hour, 12); CHECK_EQ(save.script.minute, 28); - (void) phoenix::world::parse(*save.open_world_save(save.current_world), phoenix::game_version::gothic_2); + zenkit::World wld {}; + wld.load(save.open_world(save.current_world)->get(), zenkit::GameVersion::GOTHIC_2); // TODO: Add more checks } - TEST_CASE("save_game(parse:g2/fast)") { - auto save = save_game::parse("./samples/G2/SaveFast"); + TEST_CASE("SaveGame.load(GOTHIC2,BINARY)") { + zenkit::unstable::SaveGame save {}; + save.load("./samples/G2/SaveFast"); + CHECK_EQ(save.metadata.title, "inminevalley"); CHECK_EQ(save.metadata.world, "OLDWORLD"); CHECK_EQ(save.metadata.time_day, 0); @@ -93,7 +102,8 @@ TEST_SUITE("save_game") { CHECK_EQ(save.script.hour, 0); CHECK_EQ(save.script.minute, 0); - (void) phoenix::world::parse(*save.open_world_save(save.current_world), phoenix::game_version::gothic_2); + zenkit::World wld {}; + wld.load(save.open_world(save.current_world)->get(), zenkit::GameVersion::GOTHIC_2); // TODO: Add more checks } } diff --git a/tests/TestStream.cc b/tests/TestStream.cc new file mode 100644 index 00000000..bd20b712 --- /dev/null +++ b/tests/TestStream.cc @@ -0,0 +1,290 @@ +// Copyright © 2023 GothicKit Contributors. +// SPDX-License-Identifier: MIT +#include "zenkit/Stream.hh" + +#include + +template +static std::vector bytes(Args... bytes) { + return std::vector {static_cast(bytes)...}; +} + +TEST_SUITE("Read") { + TEST_CASE("Read.seek") { + auto r = zenkit::Read::from(bytes('a', 'b', 'c', 'd')); + + r->seek(1, zenkit::Whence::BEG); + CHECK_EQ(r->read_char(), 'b'); + + r->seek(-1, zenkit::Whence::CUR); + CHECK_EQ(r->read_char(), 'b'); + + r->seek(-1, zenkit::Whence::END); + CHECK_EQ(r->read_char(), 'd'); + } + + TEST_CASE("Read.read_byte") { + auto r = zenkit::Read::from(bytes(0x1F, 0xFF)); + CHECK_EQ(r->read_byte(), 0x1F); + CHECK_EQ(r->read_byte(), -1); + + CHECK(r->eof()); + CHECK_EQ(r->read_byte(), 0); + } + + TEST_CASE("Read.read_ubyte") { + auto r = zenkit::Read::from(bytes(0x1F, 0xFF)); + CHECK_EQ(r->read_ubyte(), 0x1F); + CHECK_EQ(r->read_ubyte(), 0xFF); + + CHECK(r->eof()); + CHECK_EQ(r->read_ubyte(), 0); + } + + TEST_CASE("Read.read_short") { + auto r = zenkit::Read::from(bytes(0xFF, 0xFF, 0x01, 0x00, 0xFF)); + CHECK_EQ(r->read_short(), -1); + CHECK_EQ(r->read_short(), 1); + + CHECK_FALSE(r->eof()); + CHECK_EQ(r->read_short(), 0x00FF); + + CHECK(r->eof()); + CHECK_EQ(r->read_short(), 0); + } + + TEST_CASE("Read.read_ushort") { + auto r = zenkit::Read::from(bytes(0xFF, 0xFF, 0x01, 0x00, 0xFF)); + CHECK_EQ(r->read_ushort(), 0xFFFF); + CHECK_EQ(r->read_ushort(), 1); + + CHECK_FALSE(r->eof()); + CHECK_EQ(r->read_ushort(), 0x00FF); + + CHECK(r->eof()); + CHECK_EQ(r->read_ushort(), 0); + } + + TEST_CASE("Read.read_int") { + auto r = zenkit::Read::from(bytes(0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF)); + CHECK_EQ(r->read_int(), -1); + CHECK_EQ(r->read_int(), 1); + + CHECK_FALSE(r->eof()); + CHECK_EQ(r->read_int(), 0x00FF'FFFF); + + CHECK(r->eof()); + CHECK_EQ(r->read_int(), 0); + } + + TEST_CASE("Read.read_uint") { + auto r = zenkit::Read::from(bytes(0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF)); + CHECK_EQ(r->read_uint(), 0xFFFF'FFFF); + CHECK_EQ(r->read_uint(), 1); + + CHECK_FALSE(r->eof()); + CHECK_EQ(r->read_uint(), 0x00FF'FFFF); + + CHECK(r->eof()); + CHECK_EQ(r->read_uint(), 0); + } + + TEST_CASE("Read.read_float") { + auto r = zenkit::Read::from(bytes(0x52, 0x58, 0xD2, 0x43, 0x0A, 0xD7, 0x8A, 0xC2, 0xFF, 0xFF, 0xFF)); + CHECK_EQ(r->read_float(), 420.69f); + CHECK_EQ(r->read_float(), -69.420f); + + CHECK_FALSE(r->eof()); + CHECK_EQ(r->read_float(), 2.35098856151e-38f); + + CHECK(r->eof()); + CHECK_EQ(r->read_float(), 0.f); + } + + TEST_CASE("Read.read_string") { + auto r = zenkit::Read::from(bytes('H', 'i', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!')); + CHECK_EQ(r->read_string(2), "Hi"); + CHECK_EQ(r->read_string(13), "Hello, World!"); + + CHECK(r->eof()); + CHECK_EQ(r->read_string(1), std::string_view {"\0", 1}); + } + + TEST_CASE("Read.read_string") { + auto r = zenkit::Read::from(bytes('H', + 'i', + '\n', + ' ', + ' ', + '\r', + '\t', + 'H', + 'e', + 'l', + 'l', + 'o', + ',', + '\\', + 't', + 'W', + 'o', + 'r', + 'l', + 'd', + '!', + '\n')); + CHECK_EQ(r->read_line(true), "Hi"); + CHECK_EQ(r->tell(), 7); + + CHECK_EQ(r->read_line(true), "Hello,\\tWorld!"); + + CHECK(r->eof()); + CHECK(r->read_line(true).empty()); + } +} + +TEST_SUITE("Write") { + static std::vector BUF {}; + + TEST_CASE("Write.write_char") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_char('a'); + w->write_char('b'); + + CHECK_EQ(BUF[0], std::byte {'a'}); + CHECK_EQ(BUF[1], std::byte {'b'}); + } + + TEST_CASE("Write.write_byte") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_byte(-1); + w->write_byte(1); + + CHECK_EQ(BUF[0], std::byte {0xFF}); + CHECK_EQ(BUF[1], std::byte {0x01}); + } + + TEST_CASE("Write.write_ubyte") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_ubyte(0xFF); + w->write_ubyte(1); + + CHECK_EQ(BUF[0], std::byte {0xFF}); + CHECK_EQ(BUF[1], std::byte {0x01}); + } + + TEST_CASE("Write.write_short") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_short(-1); + w->write_short(1); + + CHECK_EQ(BUF[0], std::byte {0xFF}); + CHECK_EQ(BUF[1], std::byte {0xFF}); + CHECK_EQ(BUF[2], std::byte {0x01}); + CHECK_EQ(BUF[3], std::byte {0x00}); + } + + TEST_CASE("Write.write_ushort") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_ushort(0xFFFF); + w->write_ushort(1); + + CHECK_EQ(BUF[0], std::byte {0xFF}); + CHECK_EQ(BUF[1], std::byte {0xFF}); + CHECK_EQ(BUF[2], std::byte {0x01}); + CHECK_EQ(BUF[3], std::byte {0x00}); + } + + TEST_CASE("Write.write_int") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_int(-1); + w->write_int(1); + + CHECK_EQ(BUF[0], std::byte {0xFF}); + CHECK_EQ(BUF[1], std::byte {0xFF}); + CHECK_EQ(BUF[2], std::byte {0xFF}); + CHECK_EQ(BUF[3], std::byte {0xFF}); + + CHECK_EQ(BUF[4], std::byte {0x01}); + CHECK_EQ(BUF[5], std::byte {0x00}); + CHECK_EQ(BUF[6], std::byte {0x00}); + CHECK_EQ(BUF[7], std::byte {0x00}); + } + + TEST_CASE("Write.write_uint") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_uint(0xFFFF'FFFF); + w->write_uint(1); + + CHECK_EQ(BUF[0], std::byte {0xFF}); + CHECK_EQ(BUF[1], std::byte {0xFF}); + CHECK_EQ(BUF[2], std::byte {0xFF}); + CHECK_EQ(BUF[3], std::byte {0xFF}); + + CHECK_EQ(BUF[4], std::byte {0x01}); + CHECK_EQ(BUF[5], std::byte {0x00}); + CHECK_EQ(BUF[6], std::byte {0x00}); + CHECK_EQ(BUF[7], std::byte {0x00}); + } + + TEST_CASE("Write.write_float") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_float(420.69f); + w->write_float(69.420f); + + CHECK_EQ(BUF[0], std::byte {0x52}); + CHECK_EQ(BUF[1], std::byte {0x58}); + CHECK_EQ(BUF[2], std::byte {0xD2}); + CHECK_EQ(BUF[3], std::byte {0x43}); + + CHECK_EQ(BUF[4], std::byte {0x0A}); + CHECK_EQ(BUF[5], std::byte {0xD7}); + CHECK_EQ(BUF[6], std::byte {0x8A}); + CHECK_EQ(BUF[7], std::byte {0x42}); + } + + TEST_CASE("Write.write_string") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_string("Hello!"); + + CHECK_EQ(BUF[0], std::byte {'H'}); + CHECK_EQ(BUF[1], std::byte {'e'}); + CHECK_EQ(BUF[2], std::byte {'l'}); + CHECK_EQ(BUF[3], std::byte {'l'}); + CHECK_EQ(BUF[4], std::byte {'o'}); + CHECK_EQ(BUF[5], std::byte {'!'}); + } + + TEST_CASE("Write.write_line") { + auto w = zenkit::Write::to(&BUF); + BUF.clear(); + + w->write_line("Hello!"); + + CHECK_EQ(BUF[0], std::byte {'H'}); + CHECK_EQ(BUF[1], std::byte {'e'}); + CHECK_EQ(BUF[2], std::byte {'l'}); + CHECK_EQ(BUF[3], std::byte {'l'}); + CHECK_EQ(BUF[4], std::byte {'o'}); + CHECK_EQ(BUF[5], std::byte {'!'}); + CHECK_EQ(BUF[6], std::byte {'\n'}); + } +} diff --git a/tests/TestTexture.cc b/tests/TestTexture.cc index f87257d7..694c71af 100644 --- a/tests/TestTexture.cc +++ b/tests/TestTexture.cc @@ -1,14 +1,14 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include -using namespace phoenix; - -TEST_SUITE("texture") { - TEST_CASE("texture(parse:?)") { - auto in = buffer::mmap("./samples/erz.tex"); - auto texture = texture::parse(in); +TEST_SUITE("Texture") { + TEST_CASE("Texture.load(GOTHIC?)") { + auto in = zenkit::Read::from("./samples/erz.tex"); + zenkit::Texture texture {}; + texture.load(in.get()); CHECK_EQ(texture.height(), 128); CHECK_EQ(texture.width(), 128); @@ -25,14 +25,14 @@ TEST_SUITE("texture") { CHECK_EQ(texture.ref_width(), 128); CHECK_EQ(texture.mipmaps(), 5); - CHECK_EQ(texture.format(), phoenix::tex_dxt1); + CHECK_EQ(texture.format(), zenkit::TextureFormat::DXT1); } - TEST_CASE("texture(parse:g1)" * doctest::skip()) { + TEST_CASE("Texture.load(GOTHIC1)" * doctest::skip()) { // TODO: Stub } - TEST_CASE("texture(parse:g2)" * doctest::skip()) { + TEST_CASE("Texture.load(GOTHIC2)" * doctest::skip()) { // TODO: Stub } } diff --git a/tests/TestVfs.cc b/tests/TestVfs.cc index 37450c18..55b876e1 100644 --- a/tests/TestVfs.cc +++ b/tests/TestVfs.cc @@ -1,12 +1,10 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2023 GothicKit Contributors. // SPDX-License-Identifier: MIT -#include +#include #include -using namespace phoenix; - -void check_vfs(Vfs const& vdf) { +void check_vfs(zenkit::Vfs const& vdf) { // Checks if all entries are here const auto& roots = vdf.root().children(); @@ -14,34 +12,34 @@ void check_vfs(Vfs const& vdf) { const auto* config_yml = vdf.find("config.yml"); CHECK_NE(config_yml, nullptr); - CHECK(config_yml->type() == phoenix::VfsNodeType::FILE); + CHECK(config_yml->type() == zenkit::VfsNodeType::FILE); const auto* readme_md = vdf.find("readme.md"); CHECK_NE(readme_md, nullptr); - CHECK(readme_md->type() == phoenix::VfsNodeType::FILE); + CHECK(readme_md->type() == zenkit::VfsNodeType::FILE); const auto* licenses_dir = vdf.find("licenses"); CHECK_NE(licenses_dir, nullptr); - CHECK(licenses_dir->type() == phoenix::VfsNodeType::DIRECTORY); + CHECK(licenses_dir->type() == zenkit::VfsNodeType::DIRECTORY); CHECK_EQ(licenses_dir->children().size(), 2); const auto* mit_md = vdf.find("MIT.MD"); CHECK_NE(mit_md, nullptr); - CHECK(mit_md->type() == phoenix::VfsNodeType::FILE); + CHECK(mit_md->type() == zenkit::VfsNodeType::FILE); const auto* gpl_dir = licenses_dir->child("gpl"); CHECK_NE(gpl_dir, nullptr); - CHECK(gpl_dir->type() == phoenix::VfsNodeType::DIRECTORY); + CHECK(gpl_dir->type() == zenkit::VfsNodeType::DIRECTORY); CHECK_EQ(gpl_dir->children().size(), 2); const auto* lgpl_md = gpl_dir->child("lgpl-3.0.md"); CHECK_EQ(gpl_dir->child("lgpl"), nullptr); CHECK_NE(lgpl_md, nullptr); - CHECK(lgpl_md->type() == phoenix::VfsNodeType::FILE); + CHECK(lgpl_md->type() == zenkit::VfsNodeType::FILE); const auto* gpl_md = vdf.find("gpl-3.0.MD"); CHECK_NE(gpl_md, nullptr); - CHECK(gpl_md->type() == phoenix::VfsNodeType::FILE); + CHECK(gpl_md->type() == zenkit::VfsNodeType::FILE); CHECK_NE(vdf.find("lGpL-3.0.Md"), nullptr); CHECK_EQ(vdf.find("nonexistent"), nullptr); @@ -60,23 +58,15 @@ void check_vfs(Vfs const& vdf) { } TEST_SUITE("Vfs") { - TEST_CASE("Vfs(mount_disk:?)") { - auto vdf = phoenix::Vfs {}; + TEST_CASE("Vfs.mount_disk(GOTHIC?)") { + auto vdf = zenkit::Vfs {}; vdf.mount_disk("./samples/basic.vdf"); check_vfs(vdf); } - TEST_CASE("Vfs(mount_host)") { - auto vdf = phoenix::Vfs {}; + TEST_CASE("Vfs.mount_host(GOTHIC?)") { + auto vdf = zenkit::Vfs {}; vdf.mount_host("./samples/basic.vdf.dir", "/"); check_vfs(vdf); } - - TEST_CASE("vdf_file(open:g1)" * doctest::skip()) { - // TODO: Stub - } - - TEST_CASE("vdf_file(open:g2)" * doctest::skip()) { - // TODO: Stub - } } diff --git a/tests/TestVobsG1.cc b/tests/TestVobsG1.cc index da637353..a486da10 100644 --- a/tests/TestVobsG1.cc +++ b/tests/TestVobsG1.cc @@ -1,283 +1,285 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include TEST_SUITE("vobs") { - static phoenix::archive_object obj; + static zenkit::ArchiveObject obj; - TEST_CASE("zCVob(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCVob.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVob(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCVob.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVob"); - phoenix::vob::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::VirtualObject vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->bbox.min, glm::vec3 {-18966.623f, -236.707687f, 4373.23486f}); - CHECK_EQ(vob->bbox.max, glm::vec3 {-18772.623f, -42.7076874f, 4567.23486f}); - CHECK_EQ(vob->position, glm::vec3 {-18869.623f, -139.707687f, 4470.23486f}); - CHECK_EQ(vob->rotation, + CHECK_EQ(vob.bbox.min, glm::vec3 {-18966.623f, -236.707687f, 4373.23486f}); + CHECK_EQ(vob.bbox.max, glm::vec3 {-18772.623f, -42.7076874f, 4567.23486f}); + CHECK_EQ(vob.position, glm::vec3 {-18869.623f, -139.707687f, 4470.23486f}); + CHECK_EQ(vob.rotation, glm::mat3x3 { {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, }); - CHECK(vob->show_visual); - CHECK_EQ(vob->sprite_camera_facing_mode, phoenix::sprite_alignment::none); - CHECK_FALSE(vob->cd_static); - CHECK_FALSE(vob->cd_dynamic); - CHECK_FALSE(vob->vob_static); - CHECK_EQ(vob->dynamic_shadows, phoenix::shadow_mode::none); - CHECK_FALSE(vob->physics_enabled); - CHECK_EQ(vob->anim_mode, phoenix::animation_mode::none); - CHECK_EQ(vob->bias, 0); - CHECK_FALSE(vob->ambient); - CHECK_EQ(vob->anim_strength, 0.0f); - CHECK_EQ(vob->far_clip_scale, 0.0f); - CHECK_EQ(vob->preset_name, ""); - CHECK_EQ(vob->vob_name, ""); - CHECK_EQ(vob->visual_name, "FIRE.pfx"); - CHECK_EQ(vob->associated_visual_type, phoenix::visual_type::particle_system); - CHECK_EQ(vob->visual_decal, std::nullopt); - CHECK_EQ(vob->saved, std::nullopt); - - CHECK_FALSE(vob->is_save_game()); + CHECK(vob.show_visual); + CHECK_EQ(vob.sprite_camera_facing_mode, zenkit::SpriteAlignment::NONE); + CHECK_FALSE(vob.cd_static); + CHECK_FALSE(vob.cd_dynamic); + CHECK_FALSE(vob.vob_static); + CHECK_EQ(vob.dynamic_shadows, zenkit::ShadowType::NONE); + CHECK_FALSE(vob.physics_enabled); + CHECK_EQ(vob.anim_mode, zenkit::AnimationType::NONE); + CHECK_EQ(vob.bias, 0); + CHECK_FALSE(vob.ambient); + CHECK_EQ(vob.anim_strength, 0.0f); + CHECK_EQ(vob.far_clip_scale, 0.0f); + CHECK_EQ(vob.preset_name, ""); + CHECK_EQ(vob.vob_name, ""); + CHECK_EQ(vob.visual_name, "FIRE.pfx"); + CHECK_EQ(vob.associated_visual_type, zenkit::VisualType::PARTICLE_EFFECT); + CHECK_EQ(vob.visual_decal, std::nullopt); + CHECK_EQ(vob.saved, std::nullopt); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCVobAnimate(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCVobAnimate.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobAnimate(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCVobAnimate.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobAnimate:zCVob"); - phoenix::vobs::animate::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::Animate vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_FALSE(vob->start_on); + CHECK_FALSE(vob.start_on); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCZoneVobFarPlane(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCZoneVobFarPlane.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCZoneVobFarPlane(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCZoneVobFarPlane.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCZoneVobFarPlane:zCVob"); - phoenix::vobs::zone_far_plane::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::ZoneFarPlane vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->vob_far_plane_z, 9000.0f); - CHECK_EQ(vob->inner_range_percentage, 0.699999988f); + CHECK_EQ(vob.vob_far_plane_z, 9000.0f); + CHECK_EQ(vob.inner_range_percentage, 0.699999988f); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCZoneZFog(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCZoneZFog.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCZoneZFog(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCZoneZFog.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCZoneZFog:zCVob"); - phoenix::vobs::zone_fog::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::ZoneFog vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->range_center, 8000.0f); - CHECK_EQ(vob->inner_range_percentage, 0.850000024f); - CHECK_EQ(vob->color, glm::u8vec4 {120, 120, 120, 20}); - CHECK_FALSE(vob->fade_out_sky); - CHECK_FALSE(vob->override_color); + CHECK_EQ(vob.range_center, 8000.0f); + CHECK_EQ(vob.inner_range_percentage, 0.850000024f); + CHECK_EQ(vob.color, glm::u8vec4 {120, 120, 120, 20}); + CHECK_FALSE(vob.fade_out_sky); + CHECK_FALSE(vob.override_color); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCVobLensFlare(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCVobLensFlare.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobLensFlare(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCVobLensFlare.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobLensFlare:zCVob"); - phoenix::vobs::lens_flare::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::LensFlare vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->fx, "TORCHFX01"); + CHECK_EQ(vob.fx, "TORCHFX01"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCItem(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCItem.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCItem(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCItem.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCItem:zCVob"); - phoenix::vobs::item::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::Item vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->instance, "ITMW_1H_AXE_01"); + CHECK_EQ(vob.instance, "ITMW_1H_AXE_01"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCCSTrigger(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCCSTrigger.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCCSTrigger(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCCSTrigger.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCCSTrigger:zCTrigger:zCVob"); - phoenix::vobs::trigger::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::Trigger vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->target, "AMB_PSI_CS003.CS"); - CHECK_EQ(vob->flags, 3); - CHECK_EQ(vob->filter_flags, 23); - CHECK_EQ(vob->vob_target, ""); - CHECK_EQ(vob->max_activation_count, -1); - CHECK_EQ(vob->retrigger_delay_sec, 0.0f); - CHECK_EQ(vob->damage_threshold, 0.0f); - CHECK_EQ(vob->fire_delay_sec, 0.0f); + CHECK_EQ(vob.target, "AMB_PSI_CS003.CS"); + CHECK_EQ(vob.flags, 3); + CHECK_EQ(vob.filter_flags, 23); + CHECK_EQ(vob.vob_target, ""); + CHECK_EQ(vob.max_activation_count, -1); + CHECK_EQ(vob.retrigger_delay_sec, 0.0f); + CHECK_EQ(vob.damage_threshold, 0.0f); + CHECK_EQ(vob.fire_delay_sec, 0.0f); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMOB(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCMOB.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMOB(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCMOB.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMOB:zCVob"); - phoenix::vobs::mob::parse(*vob, *ar, phoenix::game_version::gothic_1); - - CHECK_EQ(vob->name, ""); - CHECK_EQ(vob->hp, 10); - CHECK_EQ(vob->damage, 0); - CHECK_FALSE(vob->movable); - CHECK_FALSE(vob->takable); - CHECK_FALSE(vob->focus_override); - CHECK_EQ(vob->material, phoenix::sound_material::wood); - CHECK_EQ(vob->visual_destroyed, ""); - CHECK_EQ(vob->owner, ""); - CHECK_EQ(vob->owner_guild, ""); - CHECK_FALSE(vob->destroyed); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::MovableObject vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); + + CHECK_EQ(vob.name, ""); + CHECK_EQ(vob.hp, 10); + CHECK_EQ(vob.damage, 0); + CHECK_FALSE(vob.movable); + CHECK_FALSE(vob.takable); + CHECK_FALSE(vob.focus_override); + CHECK_EQ(vob.material, zenkit::SoundMaterialType::WOOD); + CHECK_EQ(vob.visual_destroyed, ""); + CHECK_EQ(vob.owner, ""); + CHECK_EQ(vob.owner_guild, ""); + CHECK_FALSE(vob.destroyed); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMobInter(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCMobInter.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMobInter(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCMobInter.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMobInter:oCMOB:zCVob"); - phoenix::vobs::mob_inter::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::InteractiveObject vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->state, 1); - CHECK_EQ(vob->target, "OW_ORC_MAINGATE_01"); - CHECK_EQ(vob->item, ""); - CHECK_EQ(vob->condition_function, "MC_ORCCITY_GATE"); - CHECK_EQ(vob->on_state_change_function, ""); - CHECK_FALSE(vob->rewind); + CHECK_EQ(vob.state, 1); + CHECK_EQ(vob.target, "OW_ORC_MAINGATE_01"); + CHECK_EQ(vob.item, ""); + CHECK_EQ(vob.condition_function, "MC_ORCCITY_GATE"); + CHECK_EQ(vob.on_state_change_function, ""); + CHECK_FALSE(vob.rewind); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMobFire(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCMobFire.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMobFire(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCMobFire.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMobFire:oCMobInter:oCMOB:zCVob"); - phoenix::vobs::mob_fire::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::Fire vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->slot, "BIP01 FIRE"); - CHECK_EQ(vob->vob_tree, "FIRETREE_MEDIUM.ZEN"); + CHECK_EQ(vob.slot, "BIP01 FIRE"); + CHECK_EQ(vob.vob_tree, "FIRETREE_MEDIUM.ZEN"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMobContainer(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCMobContainer.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMobContainer(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCMobContainer.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMobContainer:oCMobInter:oCMOB:zCVob"); - phoenix::vobs::mob_container::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::Container vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_FALSE(vob->locked); - CHECK_EQ(vob->key, ""); - CHECK_EQ(vob->pick_string, ""); - CHECK_EQ(vob->contents, ""); + CHECK_FALSE(vob.locked); + CHECK_EQ(vob.key, ""); + CHECK_EQ(vob.pick_string, ""); + CHECK_EQ(vob.contents, ""); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMobDoor(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCMobDoor.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMobDoor(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCMobDoor.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMobDoor:oCMobInter:oCMOB:zCVob"); - phoenix::vobs::mob_door::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::Door vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_FALSE(vob->locked); - CHECK_EQ(vob->key, ""); - CHECK_EQ(vob->pick_string, ""); + CHECK_FALSE(vob.locked); + CHECK_EQ(vob.key, ""); + CHECK_EQ(vob.pick_string, ""); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCPFXControler(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCPFXControler.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCPFXControler(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCPFXControler.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCPFXControler:zCVob"); - phoenix::vobs::pfx_controller::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::ParticleEffectController vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->pfx_name, "CS_miltenfog.PFX"); - CHECK(vob->kill_when_done); - CHECK(vob->initially_running); + CHECK_EQ(vob.pfx_name, "CS_miltenfog.PFX"); + CHECK(vob.kill_when_done); + CHECK(vob.initially_running); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } @@ -296,103 +298,103 @@ TEST_SUITE("vobs") { glm::u8vec4 {225, 197, 100, 255}, glm::u8vec4 {227, 209, 106, 255}, glm::u8vec4 {223, 173, 117, 255}, }; - TEST_CASE("zCVobLight(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCVobLight.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobLight(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCVobLight.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobLight:zCVob"); - phoenix::vobs::light::parse(*vob, *ar, phoenix::game_version::gothic_1); - - CHECK_EQ(vob->preset, ""); - CHECK_EQ(vob->light_type, phoenix::light_mode::point); - CHECK_EQ(vob->range, 2000.0f); - CHECK_EQ(vob->color, glm::u8vec4 {223, 173, 117, 255}); - CHECK_EQ(vob->cone_angle, 0.0f); - CHECK_FALSE(vob->is_static); - CHECK_EQ(vob->quality, phoenix::light_quality::low); - CHECK_EQ(vob->lensflare_fx, ""); - CHECK(vob->on); - CHECK_EQ(vob->range_animation_scale, G1_LIGHT_RANGE_ANIMATION_SCALE); - CHECK_EQ(vob->range_animation_fps, 0.0f); - CHECK(vob->range_animation_smooth); - CHECK_EQ(vob->color_animation_list, G1_LIGHT_COLOR_ANIMATION_LIST); - CHECK_EQ(vob->color_animation_fps, 11.0000067f); - CHECK_FALSE(vob->color_animation_smooth); - CHECK(vob->can_move); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::Light vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); + + CHECK_EQ(vob.preset, ""); + CHECK_EQ(vob.light_type, zenkit::LightType::POINT); + CHECK_EQ(vob.range, 2000.0f); + CHECK_EQ(vob.color, glm::u8vec4 {223, 173, 117, 255}); + CHECK_EQ(vob.cone_angle, 0.0f); + CHECK_FALSE(vob.is_static); + CHECK_EQ(vob.quality, zenkit::LightQuality::LOW); + CHECK_EQ(vob.lensflare_fx, ""); + CHECK(vob.on); + CHECK_EQ(vob.range_animation_scale, G1_LIGHT_RANGE_ANIMATION_SCALE); + CHECK_EQ(vob.range_animation_fps, 0.0f); + CHECK(vob.range_animation_smooth); + CHECK_EQ(vob.color_animation_list, G1_LIGHT_COLOR_ANIMATION_LIST); + CHECK_EQ(vob.color_animation_fps, 11.0000067f); + CHECK_FALSE(vob.color_animation_smooth); + CHECK(vob.can_move); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCVobSound(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCVobSound.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobSound(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCVobSound.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobSound:zCVob"); - phoenix::vobs::sound::parse(*vob, *ar, phoenix::game_version::gothic_1); - - CHECK_EQ(vob->volume, 100.0f); - CHECK_EQ(vob->mode, phoenix::sound_mode::loop); - CHECK_EQ(vob->random_delay, 5.0f); - CHECK_EQ(vob->random_delay_var, 2.0f); - CHECK(vob->initially_playing); - CHECK_FALSE(vob->ambient3d); - CHECK_FALSE(vob->obstruction); - CHECK_EQ(vob->cone_angle, 0.0f); - CHECK_EQ(vob->volume_type, phoenix::sound_trigger_volume::spherical); - CHECK_EQ(vob->radius, 1500.0f); - CHECK_EQ(vob->sound_name, "FIRE_MEDIUM"); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::Sound vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); + + CHECK_EQ(vob.volume, 100.0f); + CHECK_EQ(vob.mode, zenkit::SoundMode::LOOP); + CHECK_EQ(vob.random_delay, 5.0f); + CHECK_EQ(vob.random_delay_var, 2.0f); + CHECK(vob.initially_playing); + CHECK_FALSE(vob.ambient3d); + CHECK_FALSE(vob.obstruction); + CHECK_EQ(vob.cone_angle, 0.0f); + CHECK_EQ(vob.volume_type, zenkit::SoundTriggerVolumeType::SPHERICAL); + CHECK_EQ(vob.radius, 1500.0f); + CHECK_EQ(vob.sound_name, "FIRE_MEDIUM"); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCVobSoundDaytime(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCVobSoundDaytime.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobSoundDaytime(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCVobSoundDaytime.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobSoundDaytime:zCVobSound:zCVob"); - phoenix::vobs::sound_daytime::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::SoundDaytime vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->start_time, 8.0f); - CHECK_EQ(vob->end_time, 18.0f); - CHECK_EQ(vob->sound_name2, "INSECTS_AND_NIGHTINGALES"); + CHECK_EQ(vob.start_time, 8.0f); + CHECK_EQ(vob.end_time, 18.0f); + CHECK_EQ(vob.sound_name2, "INSECTS_AND_NIGHTINGALES"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCZoneMusic(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCZoneMusic.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCZoneMusic(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCZoneMusic.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCZoneMusic:zCVob"); - phoenix::vobs::zone_music::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::ZoneMusic vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK(vob->enabled); - CHECK_EQ(vob->priority, 1); - CHECK_FALSE(vob->ellipsoid); - CHECK_EQ(vob->reverb, -9.0f); - CHECK_EQ(vob->volume, 1.0f); - CHECK(vob->loop); + CHECK(vob.enabled); + CHECK_EQ(vob.priority, 1); + CHECK_FALSE(vob.ellipsoid); + CHECK_EQ(vob.reverb, -9.0f); + CHECK_EQ(vob.volume, 1.0f); + CHECK(vob.loop); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - static const std::vector G1_TRIGGER_LIST_TARGETS { + static const std::vector G1_TRIGGER_LIST_TARGETS { {"EVT_CASTLE_PLATE", 0.0f}, {"EVT_CASTLE_FLOOR_5", 6.0f}, {"EVT_CASTLE_FLOOR_4", 2.0f}, @@ -401,97 +403,97 @@ TEST_SUITE("vobs") { {"EVT_CASTLE_FLOOR_1", 4.0f}, }; - TEST_CASE("zCTriggerList(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCTriggerList.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCTriggerList(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCTriggerList.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCTriggerList:zCTrigger:zCVob"); - phoenix::vobs::trigger_list::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::TriggerList vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->mode, phoenix::trigger_batch_mode::all); - CHECK_EQ(vob->targets, G1_TRIGGER_LIST_TARGETS); + CHECK_EQ(vob.mode, zenkit::TriggerBatchMode::ALL); + CHECK_EQ(vob.targets, G1_TRIGGER_LIST_TARGETS); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCTriggerScript(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCTriggerScript.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCTriggerScript(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCTriggerScript.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCTriggerScript:zCTrigger:zCVob"); - phoenix::vobs::trigger_script::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::TriggerScript vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->function, "ON_NC_GATE_TRIGGER"); + CHECK_EQ(vob.function, "ON_NC_GATE_TRIGGER"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - static const std::vector G1_MOVER_KEYFRAMES { + static const std::vector G1_MOVER_KEYFRAMES { {glm::vec3 {-23325.1992f, 3438.91333f, -21834.9473f}, glm::quat {0.105035283f, 0.091305837f, 0.747364759f, 0.649674594f}}, {glm::vec3 {-23325.1543f, 3438.91333f, -21844.3672f}, glm::quat {-0.00970827043f, 0.138834357f, -0.0690778494f, 0.98785609f}}, }; - TEST_CASE("zCMover(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/zCMover.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCMover(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/zCMover.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCMover:zCTrigger:zCVob"); - phoenix::vobs::trigger_mover::parse(*vob, *ar, phoenix::game_version::gothic_1); - - CHECK_EQ(vob->behavior, phoenix::mover_behavior::trigger_control); - CHECK_EQ(vob->touch_blocker_damage, 0.0f); - CHECK_EQ(vob->stay_open_time_sec, 2.0f); - CHECK_FALSE(vob->locked); - CHECK_FALSE(vob->auto_link); - CHECK_FALSE(vob->auto_rotate); - CHECK_EQ(vob->speed, 0.00200023991f); - CHECK_EQ(vob->lerp_mode, phoenix::mover_lerp_mode::curve); - CHECK_EQ(vob->speed_mode, phoenix::mover_speed_mode::slow_start_end); - CHECK_EQ(vob->keyframes, G1_MOVER_KEYFRAMES); - CHECK_EQ(vob->sfx_open_start, "GATE_START"); - CHECK_EQ(vob->sfx_open_end, "GATE_STOP"); - CHECK_EQ(vob->sfx_transitioning, "GATE_LOOP"); - CHECK_EQ(vob->sfx_close_start, "GATE_START"); - CHECK_EQ(vob->sfx_close_end, "GATE_STOP"); - CHECK_EQ(vob->sfx_lock, ""); - CHECK_EQ(vob->sfx_unlock, ""); - CHECK_EQ(vob->sfx_use_locked, ""); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::Mover vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); + + CHECK_EQ(vob.behavior, zenkit::MoverBehavior::TRIGGER_CONTROL); + CHECK_EQ(vob.touch_blocker_damage, 0.0f); + CHECK_EQ(vob.stay_open_time_sec, 2.0f); + CHECK_FALSE(vob.locked); + CHECK_FALSE(vob.auto_link); + CHECK_FALSE(vob.auto_rotate); + CHECK_EQ(vob.speed, 0.00200023991f); + CHECK_EQ(vob.lerp_mode, zenkit::MoverLerpType::CURVE); + CHECK_EQ(vob.speed_mode, zenkit::MoverSpeedType::SLOW_START_END); + CHECK_EQ(vob.keyframes, G1_MOVER_KEYFRAMES); + CHECK_EQ(vob.sfx_open_start, "GATE_START"); + CHECK_EQ(vob.sfx_open_end, "GATE_STOP"); + CHECK_EQ(vob.sfx_transitioning, "GATE_LOOP"); + CHECK_EQ(vob.sfx_close_start, "GATE_START"); + CHECK_EQ(vob.sfx_close_end, "GATE_STOP"); + CHECK_EQ(vob.sfx_lock, ""); + CHECK_EQ(vob.sfx_unlock, ""); + CHECK_EQ(vob.sfx_use_locked, ""); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCTriggerChangeLevel(parse:g1)") { - auto buf = phoenix::buffer::mmap("./samples/G1/VOb/oCTriggerChangeLevel.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCTriggerChangeLevel(GOTHIC1)") { + auto buf = zenkit::Read::from("./samples/G1/VOb/oCTriggerChangeLevel.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCTriggerChangeLevel:zCTrigger:zCVob"); - phoenix::vobs::trigger_change_level::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::TriggerChangeLevel vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_1); - CHECK_EQ(vob->level_name, "ORCTEMPEL.ZEN"); - CHECK_EQ(vob->start_vob, "ENTRANCE_ORCTEMPLE_SURFACE"); + CHECK_EQ(vob.level_name, "ORCTEMPEL.ZEN"); + CHECK_EQ(vob.start_vob, "ENTRANCE_ORCTEMPLE_SURFACE"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCNpc(parse:g1)") { + TEST_CASE("oCNpc(GOTHIC1)") { // TODO: Stub } } diff --git a/tests/TestVobsG2.cc b/tests/TestVobsG2.cc index 45d9c596..eff05086 100644 --- a/tests/TestVobsG2.cc +++ b/tests/TestVobsG2.cc @@ -1,95 +1,97 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2022-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include TEST_SUITE("vobs") { - static phoenix::archive_object obj; + static zenkit::ArchiveObject obj; - TEST_CASE("zCVob(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCVob.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVob(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCVob.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVob"); - phoenix::vob::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::VirtualObject vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->bbox.min, glm::vec3 {30897.1035f, 4760.24951f, -14865.5723f}); - CHECK_EQ(vob->bbox.max, glm::vec3 {30929.8301f, 4836.17529f, -14817.3135f}); - CHECK_EQ(vob->position, glm::vec3 {30913.4668f, 4798.9751f, -14841.4434f}); - CHECK_EQ(vob->rotation, + CHECK_EQ(vob.bbox.min, glm::vec3 {30897.1035f, 4760.24951f, -14865.5723f}); + CHECK_EQ(vob.bbox.max, glm::vec3 {30929.8301f, 4836.17529f, -14817.3135f}); + CHECK_EQ(vob.position, glm::vec3 {30913.4668f, 4798.9751f, -14841.4434f}); + CHECK_EQ(vob.rotation, glm::mat3x3 { {0.920505285f, 0, -0.390731275f}, {0, 1, 0}, {0.390731275f, 0, 0.920505285f}, }); - CHECK(vob->show_visual); - CHECK_EQ(vob->sprite_camera_facing_mode, phoenix::sprite_alignment::none); - CHECK_FALSE(vob->cd_static); - CHECK_FALSE(vob->cd_dynamic); - CHECK(vob->vob_static); - CHECK_EQ(vob->dynamic_shadows, phoenix::shadow_mode::none); - CHECK_FALSE(vob->physics_enabled); - CHECK_EQ(vob->anim_mode, phoenix::animation_mode::none); - CHECK_EQ(vob->bias, 0); - CHECK_FALSE(vob->ambient); - CHECK_EQ(vob->anim_strength, 0.0f); - CHECK_EQ(vob->far_clip_scale, 1.0f); - CHECK_EQ(vob->preset_name, ""); - CHECK_EQ(vob->vob_name, ""); - CHECK_EQ(vob->visual_name, "OW_MISC_WALL_TORCH_01.3DS"); - CHECK_EQ(vob->associated_visual_type, phoenix::visual_type::proto_mesh); - CHECK_EQ(vob->visual_decal, std::nullopt); - CHECK_EQ(vob->saved, std::nullopt); - - CHECK_FALSE(vob->is_save_game()); + CHECK(vob.show_visual); + CHECK_EQ(vob.sprite_camera_facing_mode, zenkit::SpriteAlignment::NONE); + CHECK_FALSE(vob.cd_static); + CHECK_FALSE(vob.cd_dynamic); + CHECK(vob.vob_static); + CHECK_EQ(vob.dynamic_shadows, zenkit::ShadowType::NONE); + CHECK_FALSE(vob.physics_enabled); + CHECK_EQ(vob.anim_mode, zenkit::AnimationType::NONE); + CHECK_EQ(vob.bias, 0); + CHECK_FALSE(vob.ambient); + CHECK_EQ(vob.anim_strength, 0.0f); + CHECK_EQ(vob.far_clip_scale, 1.0f); + CHECK_EQ(vob.preset_name, ""); + CHECK_EQ(vob.vob_name, ""); + CHECK_EQ(vob.visual_name, "OW_MISC_WALL_TORCH_01.3DS"); + CHECK_EQ(vob.associated_visual_type, zenkit::VisualType::MULTI_RESOLUTION_MESH); + CHECK_EQ(vob.visual_decal, std::nullopt); + CHECK_EQ(vob.saved, std::nullopt); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCCSCamera(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCCSCamera.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCCSCamera(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCCSCamera.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCCSCamera:zCVob"); - phoenix::vobs::cs_camera::parse(*vob, *ar, phoenix::game_version::gothic_2); - - CHECK_EQ(vob->trajectory_for, phoenix::camera_trajectory::world); - CHECK_EQ(vob->target_trajectory_for, phoenix::camera_trajectory::world); - CHECK_EQ(vob->loop_mode, phoenix::camera_loop::none); - CHECK_EQ(vob->lerp_mode, phoenix::camera_lerp_mode::path); - CHECK_FALSE(vob->ignore_for_vob_rotation); - CHECK_FALSE(vob->ignore_for_vob_rotation_target); - CHECK_FALSE(vob->adapt); - CHECK_FALSE(vob->ease_first); - CHECK_FALSE(vob->ease_last); - CHECK_EQ(vob->total_duration, 20.0f); - CHECK_EQ(vob->auto_focus_vob, ""); - CHECK_FALSE(vob->auto_player_movable); - CHECK_FALSE(vob->auto_untrigger_last); - CHECK_EQ(vob->auto_untrigger_last_delay, 0.0f); - CHECK_EQ(vob->position_count, 2); - CHECK_EQ(vob->target_count, 1); - - const auto& frames = vob->frames; + zenkit::vobs::CutsceneCamera vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); + + CHECK_EQ(vob.trajectory_for, zenkit::CameraTrajectory::WORLD); + CHECK_EQ(vob.target_trajectory_for, zenkit::CameraTrajectory::WORLD); + CHECK_EQ(vob.loop_mode, zenkit::CameraLoop::NONE); + CHECK_EQ(vob.lerp_mode, zenkit::CameraLerpType::PATH); + CHECK_FALSE(vob.ignore_for_vob_rotation); + CHECK_FALSE(vob.ignore_for_vob_rotation_target); + CHECK_FALSE(vob.adapt); + CHECK_FALSE(vob.ease_first); + CHECK_FALSE(vob.ease_last); + CHECK_EQ(vob.total_duration, 20.0f); + CHECK_EQ(vob.auto_focus_vob, ""); + CHECK_FALSE(vob.auto_player_movable); + CHECK_FALSE(vob.auto_untrigger_last); + CHECK_EQ(vob.auto_untrigger_last_delay, 0.0f); + CHECK_EQ(vob.position_count, 2); + CHECK_EQ(vob.target_count, 1); + + const auto& frames = vob.frames; CHECK_EQ(frames[0]->time, 0.0f); CHECK_EQ(frames[0]->roll_angle, 0.0f); CHECK_EQ(frames[0]->fov_scale, 1.0f); - CHECK_EQ(frames[0]->motion_type, phoenix::camera_motion::slow); - CHECK_EQ(frames[0]->motion_type_fov, phoenix::camera_motion::smooth); - CHECK_EQ(frames[0]->motion_type_roll, phoenix::camera_motion::smooth); - CHECK_EQ(frames[0]->motion_type_time_scale, phoenix::camera_motion::smooth); + CHECK_EQ(frames[0]->motion_type, zenkit::CameraMotion::SLOW); + CHECK_EQ(frames[0]->motion_type_fov, zenkit::CameraMotion::SMOOTH); + CHECK_EQ(frames[0]->motion_type_roll, zenkit::CameraMotion::SMOOTH); + CHECK_EQ(frames[0]->motion_type_time_scale, zenkit::CameraMotion::SMOOTH); CHECK_EQ(frames[0]->tension, 0.0f); CHECK_EQ(frames[0]->cam_bias, 0.0f); CHECK_EQ(frames[0]->continuity, 0.0f); @@ -106,10 +108,10 @@ TEST_SUITE("vobs") { CHECK_EQ(frames[1]->time, 20.0f); CHECK_EQ(frames[1]->roll_angle, 0.0f); CHECK_EQ(frames[1]->fov_scale, 1.0f); - CHECK_EQ(frames[1]->motion_type, phoenix::camera_motion::slow); - CHECK_EQ(frames[1]->motion_type_fov, phoenix::camera_motion::smooth); - CHECK_EQ(frames[1]->motion_type_roll, phoenix::camera_motion::smooth); - CHECK_EQ(frames[1]->motion_type_time_scale, phoenix::camera_motion::smooth); + CHECK_EQ(frames[1]->motion_type, zenkit::CameraMotion::SLOW); + CHECK_EQ(frames[1]->motion_type_fov, zenkit::CameraMotion::SMOOTH); + CHECK_EQ(frames[1]->motion_type_roll, zenkit::CameraMotion::SMOOTH); + CHECK_EQ(frames[1]->motion_type_time_scale, zenkit::CameraMotion::SMOOTH); CHECK_EQ(frames[1]->tension, 0.0f); CHECK_EQ(frames[1]->cam_bias, 0.0f); CHECK_EQ(frames[1]->continuity, 0.0f); @@ -126,10 +128,10 @@ TEST_SUITE("vobs") { CHECK_EQ(frames[2]->time, 0.0f); CHECK_EQ(frames[2]->roll_angle, 0.0f); CHECK_EQ(frames[2]->fov_scale, 1.0f); - CHECK_EQ(frames[2]->motion_type, phoenix::camera_motion::smooth); - CHECK_EQ(frames[2]->motion_type_fov, phoenix::camera_motion::smooth); - CHECK_EQ(frames[2]->motion_type_roll, phoenix::camera_motion::smooth); - CHECK_EQ(frames[2]->motion_type_time_scale, phoenix::camera_motion::smooth); + CHECK_EQ(frames[2]->motion_type, zenkit::CameraMotion::SMOOTH); + CHECK_EQ(frames[2]->motion_type_fov, zenkit::CameraMotion::SMOOTH); + CHECK_EQ(frames[2]->motion_type_roll, zenkit::CameraMotion::SMOOTH); + CHECK_EQ(frames[2]->motion_type_time_scale, zenkit::CameraMotion::SMOOTH); CHECK_EQ(frames[2]->tension, 0.0f); CHECK_EQ(frames[2]->cam_bias, 0.0f); CHECK_EQ(frames[2]->continuity, 0.0f); @@ -143,335 +145,335 @@ TEST_SUITE("vobs") { {82642.8984f, 3899.0f, 29397.9844f, 1.0f}, }); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCVobAnimate(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCVobAnimate.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobAnimate(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCVobAnimate.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobAnimate:zCVob"); - phoenix::vobs::animate::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::Animate vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK(vob->start_on); + CHECK(vob.start_on); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCZoneVobFarPlane(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCZoneVobFarPlane.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCZoneVobFarPlane(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCZoneVobFarPlane.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCZoneVobFarPlane:zCVob"); - phoenix::vobs::zone_far_plane::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::ZoneFarPlane vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->vob_far_plane_z, 6500.0f); - CHECK_EQ(vob->inner_range_percentage, 0.699999988f); + CHECK_EQ(vob.vob_far_plane_z, 6500.0f); + CHECK_EQ(vob.inner_range_percentage, 0.699999988f); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCZoneZFog(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCZoneZFog.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCZoneZFog(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCZoneZFog.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCZoneZFog:zCVob"); - phoenix::vobs::zone_fog::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::ZoneFog vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->range_center, 16000.0f); - CHECK_EQ(vob->inner_range_percentage, 0.699999988f); - CHECK_EQ(vob->color, glm::u8vec4 {120, 120, 120, 255}); - CHECK_FALSE(vob->fade_out_sky); - CHECK_FALSE(vob->override_color); + CHECK_EQ(vob.range_center, 16000.0f); + CHECK_EQ(vob.inner_range_percentage, 0.699999988f); + CHECK_EQ(vob.color, glm::u8vec4 {120, 120, 120, 255}); + CHECK_FALSE(vob.fade_out_sky); + CHECK_FALSE(vob.override_color); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCItem(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCItem.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCItem(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCItem.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCItem:zCVob"); - phoenix::vobs::item::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::Item vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->instance, "ITPL_BLUEPLANT"); + CHECK_EQ(vob.instance, "ITPL_BLUEPLANT"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCTrigger(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCTrigger.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCTrigger(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCTrigger.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCTrigger:zCVob"); - phoenix::vobs::trigger::parse(*vob, *ar, phoenix::game_version::gothic_1); + zenkit::vobs::Trigger vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->target, ""); - CHECK_EQ(vob->flags, 3); - CHECK_EQ(vob->filter_flags, 0); - CHECK_EQ(vob->vob_target, ""); - CHECK_EQ(vob->max_activation_count, -1); - CHECK_EQ(vob->retrigger_delay_sec, 0.0f); - CHECK_EQ(vob->damage_threshold, 0.0f); - CHECK_EQ(vob->fire_delay_sec, 0.0f); + CHECK_EQ(vob.target, ""); + CHECK_EQ(vob.flags, 3); + CHECK_EQ(vob.filter_flags, 0); + CHECK_EQ(vob.vob_target, ""); + CHECK_EQ(vob.max_activation_count, -1); + CHECK_EQ(vob.retrigger_delay_sec, 0.0f); + CHECK_EQ(vob.damage_threshold, 0.0f); + CHECK_EQ(vob.fire_delay_sec, 0.0f); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMOB(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCMOB.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMOB(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCMOB.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMOB:zCVob"); - phoenix::vobs::mob::parse(*vob, *ar, phoenix::game_version::gothic_2); - - CHECK_EQ(vob->name, "MOBNAME_GRAVE_18"); - CHECK_EQ(vob->hp, 10); - CHECK_EQ(vob->damage, 0); - CHECK_FALSE(vob->movable); - CHECK_FALSE(vob->takable); - CHECK_FALSE(vob->focus_override); - CHECK_EQ(vob->material, phoenix::sound_material::wood); - CHECK_EQ(vob->visual_destroyed, ""); - CHECK_EQ(vob->owner, ""); - CHECK_EQ(vob->owner_guild, ""); - CHECK_FALSE(vob->destroyed); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::MovableObject vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); + + CHECK_EQ(vob.name, "MOBNAME_GRAVE_18"); + CHECK_EQ(vob.hp, 10); + CHECK_EQ(vob.damage, 0); + CHECK_FALSE(vob.movable); + CHECK_FALSE(vob.takable); + CHECK_FALSE(vob.focus_override); + CHECK_EQ(vob.material, zenkit::SoundMaterialType::WOOD); + CHECK_EQ(vob.visual_destroyed, ""); + CHECK_EQ(vob.owner, ""); + CHECK_EQ(vob.owner_guild, ""); + CHECK_FALSE(vob.destroyed); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMobInter(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCMobInter.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMobInter(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCMobInter.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMobInter:oCMOB:zCVob"); - phoenix::vobs::mob_inter::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::InteractiveObject vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->state, 1); - CHECK_EQ(vob->target, ""); - CHECK_EQ(vob->item, ""); - CHECK_EQ(vob->condition_function, ""); - CHECK_EQ(vob->on_state_change_function, "PRAYIDOL"); - CHECK_FALSE(vob->rewind); + CHECK_EQ(vob.state, 1); + CHECK_EQ(vob.target, ""); + CHECK_EQ(vob.item, ""); + CHECK_EQ(vob.condition_function, ""); + CHECK_EQ(vob.on_state_change_function, "PRAYIDOL"); + CHECK_FALSE(vob.rewind); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMobFire(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCMobFire.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMobFire(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCMobFire.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMobFire:oCMobInter:oCMOB:zCVob"); - phoenix::vobs::mob_fire::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::Fire vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->slot, "BIP01 FIRE"); - CHECK_EQ(vob->vob_tree, "FIRETREE_LAMP.ZEN"); + CHECK_EQ(vob.slot, "BIP01 FIRE"); + CHECK_EQ(vob.vob_tree, "FIRETREE_LAMP.ZEN"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMobContainer(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCMobContainer.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMobContainer(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCMobContainer.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMobContainer:oCMobInter:oCMOB:zCVob"); - phoenix::vobs::mob_container::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::Container vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_FALSE(vob->locked); - CHECK_EQ(vob->key, ""); - CHECK_EQ(vob->pick_string, ""); - CHECK_EQ(vob->contents, "ItMi_Gold:35"); + CHECK_FALSE(vob.locked); + CHECK_EQ(vob.key, ""); + CHECK_EQ(vob.pick_string, ""); + CHECK_EQ(vob.contents, "ItMi_Gold:35"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCMobDoor(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCMobDoor.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCMobDoor(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCMobDoor.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCMobDoor:oCMobInter:oCMOB:zCVob"); - phoenix::vobs::mob_door::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::Door vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_FALSE(vob->locked); - CHECK_EQ(vob->key, ""); - CHECK_EQ(vob->pick_string, ""); + CHECK_FALSE(vob.locked); + CHECK_EQ(vob.key, ""); + CHECK_EQ(vob.pick_string, ""); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCPFXControler(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCPFXControler.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCPFXControler(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCPFXControler.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCPFXControler:zCVob"); - phoenix::vobs::pfx_controller::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::ParticleEffectController vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->pfx_name, "STARGATE_EDGES.PFX"); - CHECK_FALSE(vob->kill_when_done); - CHECK_FALSE(vob->initially_running); + CHECK_EQ(vob.pfx_name, "STARGATE_EDGES.PFX"); + CHECK_FALSE(vob.kill_when_done); + CHECK_FALSE(vob.initially_running); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } static const std::vector G2_LIGHT_RANGE_ANIMATION_SCALE {}; static const std::vector G2_LIGHT_COLOR_ANIMATION_LIST {}; - TEST_CASE("zCVobLight(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCVobLight.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobLight(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCVobLight.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobLight:zCVob"); - phoenix::vobs::light::parse(*vob, *ar, phoenix::game_version::gothic_2); - - CHECK_EQ(vob->preset, "NW_STANDART_FIRE_STATIC"); - CHECK_EQ(vob->light_type, phoenix::light_mode::point); - CHECK_EQ(vob->range, 400.0f); - CHECK_EQ(vob->color, glm::u8vec4 {100, 71, 60, 255}); - CHECK_EQ(vob->cone_angle, 0.0f); - CHECK(vob->is_static); - CHECK_EQ(vob->quality, phoenix::light_quality::low); - CHECK_EQ(vob->lensflare_fx, ""); - CHECK_FALSE(vob->on); - CHECK_EQ(vob->range_animation_scale, G2_LIGHT_RANGE_ANIMATION_SCALE); - CHECK_EQ(vob->range_animation_fps, 0.0f); - CHECK(vob->range_animation_smooth); - CHECK_EQ(vob->color_animation_list, G2_LIGHT_COLOR_ANIMATION_LIST); - CHECK_EQ(vob->color_animation_fps, 0.0f); - CHECK(vob->color_animation_smooth); - CHECK(vob->can_move); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::Light vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); + + CHECK_EQ(vob.preset, "NW_STANDART_FIRE_STATIC"); + CHECK_EQ(vob.light_type, zenkit::LightType::POINT); + CHECK_EQ(vob.range, 400.0f); + CHECK_EQ(vob.color, glm::u8vec4 {100, 71, 60, 255}); + CHECK_EQ(vob.cone_angle, 0.0f); + CHECK(vob.is_static); + CHECK_EQ(vob.quality, zenkit::LightQuality::LOW); + CHECK_EQ(vob.lensflare_fx, ""); + CHECK_FALSE(vob.on); + CHECK_EQ(vob.range_animation_scale, G2_LIGHT_RANGE_ANIMATION_SCALE); + CHECK_EQ(vob.range_animation_fps, 0.0f); + CHECK(vob.range_animation_smooth); + CHECK_EQ(vob.color_animation_list, G2_LIGHT_COLOR_ANIMATION_LIST); + CHECK_EQ(vob.color_animation_fps, 0.0f); + CHECK(vob.color_animation_smooth); + CHECK(vob.can_move); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCVobSound(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCVobSound.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobSound(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCVobSound.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobSound:zCVob"); - phoenix::vobs::sound::parse(*vob, *ar, phoenix::game_version::gothic_2); - - CHECK_EQ(vob->volume, 80.0f); - CHECK_EQ(vob->mode, phoenix::sound_mode::random); - CHECK_EQ(vob->random_delay, 30.0f); - CHECK_EQ(vob->random_delay_var, 20.0f); - CHECK(vob->initially_playing); - CHECK_FALSE(vob->ambient3d); - CHECK_FALSE(vob->obstruction); - CHECK_EQ(vob->cone_angle, 0.0f); - CHECK_EQ(vob->volume_type, phoenix::sound_trigger_volume::spherical); - CHECK_EQ(vob->radius, 3000.0f); - CHECK_EQ(vob->sound_name, "OW_CROW"); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::Sound vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); + + CHECK_EQ(vob.volume, 80.0f); + CHECK_EQ(vob.mode, zenkit::SoundMode::RANDOM); + CHECK_EQ(vob.random_delay, 30.0f); + CHECK_EQ(vob.random_delay_var, 20.0f); + CHECK(vob.initially_playing); + CHECK_FALSE(vob.ambient3d); + CHECK_FALSE(vob.obstruction); + CHECK_EQ(vob.cone_angle, 0.0f); + CHECK_EQ(vob.volume_type, zenkit::SoundTriggerVolumeType::SPHERICAL); + CHECK_EQ(vob.radius, 3000.0f); + CHECK_EQ(vob.sound_name, "OW_CROW"); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCVobSoundDaytime(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCVobSoundDaytime.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCVobSoundDaytime(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCVobSoundDaytime.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCVobSoundDaytime:zCVobSound:zCVob"); - phoenix::vobs::sound_daytime::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::SoundDaytime vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->start_time, 5.0f); - CHECK_EQ(vob->end_time, 21.0f); - CHECK_EQ(vob->sound_name2, "InsectsFrogs_Night"); + CHECK_EQ(vob.start_time, 5.0f); + CHECK_EQ(vob.end_time, 21.0f); + CHECK_EQ(vob.sound_name2, "InsectsFrogs_Night"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCZoneMusic(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCZoneMusic.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCZoneMusic(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCZoneMusic.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCZoneMusic:zCVob"); - phoenix::vobs::zone_music::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::ZoneMusic vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK(vob->enabled); - CHECK_EQ(vob->priority, 1); - CHECK_FALSE(vob->ellipsoid); - CHECK_EQ(vob->reverb, -3.2190001f); - CHECK_EQ(vob->volume, 1.0f); - CHECK(vob->loop); + CHECK(vob.enabled); + CHECK_EQ(vob.priority, 1); + CHECK_FALSE(vob.ellipsoid); + CHECK_EQ(vob.reverb, -3.2190001f); + CHECK_EQ(vob.volume, 1.0f); + CHECK(vob.loop); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCMessageFilter(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCMessageFilter.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCMessageFilter(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCMessageFilter.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCMessageFilter:zCVob"); - phoenix::vobs::message_filter::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::MessageFilter vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->target, "EVT_ADDON_TROLLPORTAL_CAMERA_01"); - CHECK_EQ(vob->on_trigger, phoenix::message_filter_action::untrigger); - CHECK_EQ(vob->on_untrigger, phoenix::message_filter_action::untrigger); + CHECK_EQ(vob.target, "EVT_ADDON_TROLLPORTAL_CAMERA_01"); + CHECK_EQ(vob.on_trigger, zenkit::MessageFilterAction::UNTRIGGER); + CHECK_EQ(vob.on_untrigger, zenkit::MessageFilterAction::UNTRIGGER); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } @@ -481,205 +483,205 @@ TEST_SUITE("vobs") { "EVT_ORNAMENT_SWITCH_BIGFARM_03", }; - TEST_CASE("zCCodeMaster(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCCodeMaster.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCCodeMaster(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCCodeMaster.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCCodeMaster:zCVob"); - phoenix::vobs::code_master::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::CodeMaster vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->target, "EVT_ORNAMENT_TRIGGER_BIGFARM_01"); - CHECK_FALSE(vob->ordered); - CHECK_FALSE(vob->first_false_is_failure); - CHECK_EQ(vob->failure_target, ""); - CHECK_FALSE(vob->untriggered_cancels); - CHECK_EQ(vob->slaves, G2_CODE_MASTER_SLAVES); + CHECK_EQ(vob.target, "EVT_ORNAMENT_TRIGGER_BIGFARM_01"); + CHECK_FALSE(vob.ordered); + CHECK_FALSE(vob.first_false_is_failure); + CHECK_EQ(vob.failure_target, ""); + CHECK_FALSE(vob.untriggered_cancels); + CHECK_EQ(vob.slaves, G2_CODE_MASTER_SLAVES); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - static const std::vector G2_TRIGGER_LIST_TARGETS { + static const std::vector G2_TRIGGER_LIST_TARGETS { {"EVT_ADDON_TROLLPORTAL_MASTERTRIGGERLIST_ALPHA_01", 0.0f}, {"EVT_ADDON_TROLLPORTAL_TRIGGERSCRIPT_01", 0.0f}, }; - TEST_CASE("zCTriggerList(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCTriggerList.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCTriggerList(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCTriggerList.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCTriggerList:zCTrigger:zCVob"); - phoenix::vobs::trigger_list::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::TriggerList vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->mode, phoenix::trigger_batch_mode::all); - CHECK_EQ(vob->targets, G2_TRIGGER_LIST_TARGETS); + CHECK_EQ(vob.mode, zenkit::TriggerBatchMode::ALL); + CHECK_EQ(vob.targets, G2_TRIGGER_LIST_TARGETS); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCTriggerScript(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCTriggerScript.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCTriggerScript(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCTriggerScript.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCTriggerScript:zCTrigger:zCVob"); - phoenix::vobs::trigger_script::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::TriggerScript vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->function, "EVT_CAVALORNSGOBBOS_FUNC"); + CHECK_EQ(vob.function, "EVT_CAVALORNSGOBBOS_FUNC"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - static const std::vector G1_MOVER_KEYFRAMES { + static const std::vector G1_MOVER_KEYFRAMES { {glm::vec3 {29785.9609f, 5140.81982f, -16279.8477f}, glm::quat {0.999809802f, -0.000760567724f, 0.0174517576f, 0.00869333092f}}, {glm::vec3 {29785.9609f, 5720.81982f, -16279.8477f}, glm::quat {0.999809802f, -0.000760567724f, 0.0174517576f, 0.00869333092f}}, }; - TEST_CASE("zCMover(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCMover.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCMover(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCMover.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCMover:zCTrigger:zCVob"); - phoenix::vobs::trigger_mover::parse(*vob, *ar, phoenix::game_version::gothic_2); - - CHECK_EQ(vob->behavior, phoenix::mover_behavior::toggle); - CHECK_EQ(vob->touch_blocker_damage, 0.0f); - CHECK_EQ(vob->stay_open_time_sec, 2.0f); - CHECK_FALSE(vob->locked); - CHECK_FALSE(vob->auto_link); - CHECK_FALSE(vob->auto_rotate); - CHECK_EQ(vob->speed, 0.0500000007f); - CHECK_EQ(vob->lerp_mode, phoenix::mover_lerp_mode::curve); - CHECK_EQ(vob->speed_mode, phoenix::mover_speed_mode::slow_start_end); - CHECK_EQ(vob->keyframes, G1_MOVER_KEYFRAMES); - CHECK_EQ(vob->sfx_open_start, "GATE_START"); - CHECK_EQ(vob->sfx_open_end, "GATE_STOP"); - CHECK_EQ(vob->sfx_transitioning, "GATE_LOOP"); - CHECK_EQ(vob->sfx_close_start, "GATE_START"); - CHECK_EQ(vob->sfx_close_end, "GATE_STOP"); - CHECK_EQ(vob->sfx_lock, ""); - CHECK_EQ(vob->sfx_unlock, ""); - CHECK_EQ(vob->sfx_use_locked, ""); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::Mover vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); + + CHECK_EQ(vob.behavior, zenkit::MoverBehavior::TOGGLE); + CHECK_EQ(vob.touch_blocker_damage, 0.0f); + CHECK_EQ(vob.stay_open_time_sec, 2.0f); + CHECK_FALSE(vob.locked); + CHECK_FALSE(vob.auto_link); + CHECK_FALSE(vob.auto_rotate); + CHECK_EQ(vob.speed, 0.0500000007f); + CHECK_EQ(vob.lerp_mode, zenkit::MoverLerpType::CURVE); + CHECK_EQ(vob.speed_mode, zenkit::MoverSpeedType::SLOW_START_END); + CHECK_EQ(vob.keyframes, G1_MOVER_KEYFRAMES); + CHECK_EQ(vob.sfx_open_start, "GATE_START"); + CHECK_EQ(vob.sfx_open_end, "GATE_STOP"); + CHECK_EQ(vob.sfx_transitioning, "GATE_LOOP"); + CHECK_EQ(vob.sfx_close_start, "GATE_START"); + CHECK_EQ(vob.sfx_close_end, "GATE_STOP"); + CHECK_EQ(vob.sfx_lock, ""); + CHECK_EQ(vob.sfx_unlock, ""); + CHECK_EQ(vob.sfx_use_locked, ""); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCTriggerChangeLevel(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCTriggerChangeLevel.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCTriggerChangeLevel(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCTriggerChangeLevel.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCTriggerChangeLevel:zCTrigger:zCVob"); - phoenix::vobs::trigger_change_level::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::TriggerChangeLevel vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->level_name, "ADDON\\ADDONWORLD.ZEN"); - CHECK_EQ(vob->start_vob, "START_ADDON"); + CHECK_EQ(vob.level_name, "ADDON\\ADDONWORLD.ZEN"); + CHECK_EQ(vob.start_vob, "START_ADDON"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCTriggerWorldStart(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCTriggerWorldStart.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCTriggerWorldStart(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCTriggerWorldStart.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCTriggerWorldStart:zCVob"); - phoenix::vobs::trigger_world_start::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::TriggerWorldStart vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->target, "EVT_TROLL_GRAVE_MOVER_01"); - CHECK(vob->fire_once); + CHECK_EQ(vob.target, "EVT_TROLL_GRAVE_MOVER_01"); + CHECK(vob.fire_once); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCTriggerUntouch(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCTriggerUntouch.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCTriggerUntouch(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCTriggerUntouch.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCTriggerUntouch:zCVob"); - phoenix::vobs::trigger_untouch::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::TriggerUntouch vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->target, "EVT_TROLL_GRAVE_TRIGGERLIST_01"); + CHECK_EQ(vob.target, "EVT_TROLL_GRAVE_TRIGGERLIST_01"); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCEarthquake(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/zCEarthquake.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("zCEarthquake(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/zCEarthquake.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "zCEarthquake:zCVob"); - phoenix::vobs::earthquake::parse(*vob, *ar, phoenix::game_version::gothic_2); + zenkit::vobs::Earthquake vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); - CHECK_EQ(vob->radius, 1000.0f); - CHECK_EQ(vob->duration, 5.0f); - CHECK_EQ(vob->amplitude, glm::vec3 {2.0f, 10.0f, 2.0f}); + CHECK_EQ(vob.radius, 1000.0f); + CHECK_EQ(vob.duration, 5.0f); + CHECK_EQ(vob.amplitude, glm::vec3 {2.0f, 10.0f, 2.0f}); - CHECK_FALSE(vob->is_save_game()); + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("oCTouchDamage(parse:g2)") { - auto buf = phoenix::buffer::mmap("./samples/G2/VOb/oCTouchDamage.zen"); - auto ar = phoenix::archive_reader::open(buf); - auto vob = std::make_unique(); + TEST_CASE("oCTouchDamage(GOTHIC2)") { + auto buf = zenkit::Read::from("./samples/G2/VOb/oCTouchDamage.zen"); + auto ar = zenkit::ReadArchive::from(buf.get()); CHECK(ar->read_object_begin(obj)); CHECK_EQ(obj.class_name, "oCTouchDamage:zCTouchDamage:zCVob"); - phoenix::vobs::touch_damage::parse(*vob, *ar, phoenix::game_version::gothic_2); - - CHECK_EQ(vob->damage, 1000.0f); - CHECK(vob->barrier); - CHECK_FALSE(vob->blunt); - CHECK(vob->edge); - CHECK_FALSE(vob->fire); - CHECK_FALSE(vob->fly); - CHECK_FALSE(vob->magic); - CHECK_FALSE(vob->point); - CHECK_FALSE(vob->fall); - CHECK_EQ(vob->repeat_delay_sec, 0.0f); - CHECK_EQ(vob->volume_scale, 1.0f); - CHECK_EQ(vob->collision, phoenix::collision_type::box); - - CHECK_FALSE(vob->is_save_game()); + zenkit::vobs::TouchDamage vob {}; + vob.load(*ar, zenkit::GameVersion::GOTHIC_2); + + CHECK_EQ(vob.damage, 1000.0f); + CHECK(vob.barrier); + CHECK_FALSE(vob.blunt); + CHECK(vob.edge); + CHECK_FALSE(vob.fire); + CHECK_FALSE(vob.fly); + CHECK_FALSE(vob.magic); + CHECK_FALSE(vob.point); + CHECK_FALSE(vob.fall); + CHECK_EQ(vob.repeat_delay_sec, 0.0f); + CHECK_EQ(vob.volume_scale, 1.0f); + CHECK_EQ(vob.collision, zenkit::TouchCollisionType::BOX); + + CHECK_FALSE(vob.is_save_game()); CHECK(ar->read_object_end()); } - TEST_CASE("zCMoverControler(parse:g2)") { + TEST_CASE("zCMoverControler(GOTHIC2)") { // TODO: Stub } - TEST_CASE("oCNpc(parse:g2)") { + TEST_CASE("oCNpc(GOTHIC2)") { // TODO: Stub } } diff --git a/tests/TestWorld.cc b/tests/TestWorld.cc index be564d78..1ccbd3cb 100644 --- a/tests/TestWorld.cc +++ b/tests/TestWorld.cc @@ -1,12 +1,18 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2021-2023 GothicKit Contributors. // SPDX-License-Identifier: MIT #include -#include +#include +#include +#include -TEST_SUITE("world") { - TEST_CASE("world(parse:g1)") { - auto in = phoenix::buffer::mmap("./samples/world.proprietary.zen"); - auto wld = phoenix::world::parse(in); +#include + +TEST_SUITE("World") { + TEST_CASE("World.load(GOTHIC1)") { + auto in = zenkit::Read::from("./samples/world.proprietary.zen"); + + zenkit::World wld {}; + wld.load(in.get()); auto& mesh = wld.world_mesh; CHECK_EQ(mesh.vertices.size(), 55439); @@ -53,11 +59,11 @@ TEST_SUITE("world") { auto& mat500 = mats[500]; CHECK_EQ(mat0.name, "OWODWATSTOP"); - CHECK_EQ(mat0.group, phoenix::material_group::water); + CHECK_EQ(mat0.group, zenkit::MaterialGroup::WATER); CHECK_EQ(mat0.texture, "OWODSEA_A0.TGA"); CHECK_EQ(mat500.name, "OMWABROWNGREEN01"); - CHECK_EQ(mat500.group, phoenix::material_group::stone); + CHECK_EQ(mat500.group, zenkit::MaterialGroup::STONE); CHECK_EQ(mat500.texture, "OMWABROWNGREEN01.TGA"); auto polys = wld.world_mesh.polygons; @@ -103,14 +109,14 @@ TEST_SUITE("world") { CHECK_EQ(polys.vertex_indices[106721 * 3 + 1], 55428); CHECK_EQ(polys.vertex_indices[106721 * 3 + 2], 54576); - CHECK_EQ(polys.flags[0], phoenix::polygon_flags {0, 0, 0, 0, 0, 0, 0, -1, 1, 1}); - CHECK_EQ(polys.flags[26680], phoenix::polygon_flags {0, 0, 0, 0, 0, 0, 0, -1, 1, 1}); - CHECK_EQ(polys.flags[53360], phoenix::polygon_flags {0, 1, 1, 0, 0, 0, 0, -1, 0, 0}); - CHECK_EQ(polys.flags[106721], phoenix::polygon_flags {0, 0, 0, 0, 0, 0, 0, -1, 1, 1}); + CHECK_EQ(polys.flags[0], zenkit::PolygonFlagSet {0, 0, 0, 0, 0, 0, 0, -1, 1, 1}); + CHECK_EQ(polys.flags[26680], zenkit::PolygonFlagSet {0, 0, 0, 0, 0, 0, 0, -1, 1, 1}); + CHECK_EQ(polys.flags[53360], zenkit::PolygonFlagSet {0, 1, 1, 0, 0, 0, 0, -1, 0, 0}); + CHECK_EQ(polys.flags[106721], zenkit::PolygonFlagSet {0, 0, 0, 0, 0, 0, 0, -1, 1, 1}); // Check the BSP tree auto& tree = wld.world_bsp_tree; - CHECK_EQ(tree.mode, phoenix::bsp_tree_mode::outdoor); + CHECK_EQ(tree.mode, zenkit::BspTreeType::OUTDOOR); auto& tree_polys = tree.polygon_indices; CHECK_EQ(tree_polys.size(), 480135); @@ -207,14 +213,14 @@ TEST_SUITE("world") { CHECK(vob0->preset_name.empty()); CHECK_EQ(vob0->position, glm::vec3 {0, 0, 0}); CHECK_FALSE(vob0->show_visual); - CHECK_EQ(vob0->sprite_camera_facing_mode, phoenix::sprite_alignment::none); - CHECK_EQ(vob0->anim_mode, phoenix::animation_mode::none); + CHECK_EQ(vob0->sprite_camera_facing_mode, zenkit::SpriteAlignment::NONE); + CHECK_EQ(vob0->anim_mode, zenkit::AnimationType::NONE); CHECK_EQ(vob0->anim_strength, 0.0f); CHECK_EQ(vob0->far_clip_scale, 0.0f); CHECK(vob0->cd_static); CHECK_FALSE(vob0->cd_dynamic); CHECK_FALSE(vob0->vob_static); - CHECK_EQ(vob0->dynamic_shadows, phoenix::shadow_mode::none); + CHECK_EQ(vob0->dynamic_shadows, zenkit::ShadowType::NONE); CHECK_EQ(vob0->bias, 0); CHECK_FALSE(vob0->ambient); CHECK_FALSE(vob0->physics_enabled); @@ -247,14 +253,14 @@ TEST_SUITE("world") { CHECK(child1->preset_name.empty()); CHECK_EQ(child1->position, glm::vec3 {-18544.4863, -136.171906, 4141.19727}); CHECK_FALSE(child1->show_visual); - CHECK_EQ(child1->sprite_camera_facing_mode, phoenix::sprite_alignment::none); - CHECK_EQ(child1->anim_mode, phoenix::animation_mode::none); + CHECK_EQ(child1->sprite_camera_facing_mode, zenkit::SpriteAlignment::NONE); + CHECK_EQ(child1->anim_mode, zenkit::AnimationType::NONE); CHECK_EQ(child1->anim_strength, 0.0f); CHECK_EQ(child1->far_clip_scale, 0.0f); CHECK_FALSE(child1->cd_static); CHECK_FALSE(child1->cd_dynamic); CHECK_FALSE(child1->vob_static); - CHECK_EQ(child1->dynamic_shadows, phoenix::shadow_mode::none); + CHECK_EQ(child1->dynamic_shadows, zenkit::ShadowType::NONE); CHECK_EQ(child1->bias, 0); CHECK_FALSE(child1->ambient); CHECK_FALSE(child1->physics_enabled); @@ -284,14 +290,14 @@ TEST_SUITE("world") { CHECK(vob13->preset_name.empty()); CHECK_EQ(vob13->position, glm::vec3 {0, 0, 0}); CHECK_FALSE(vob13->show_visual); - CHECK_EQ(vob13->sprite_camera_facing_mode, phoenix::sprite_alignment::none); - CHECK_EQ(vob13->anim_mode, phoenix::animation_mode::none); + CHECK_EQ(vob13->sprite_camera_facing_mode, zenkit::SpriteAlignment::NONE); + CHECK_EQ(vob13->anim_mode, zenkit::AnimationType::NONE); CHECK_EQ(vob13->anim_strength, 0.0f); CHECK_EQ(vob13->far_clip_scale, 0.0f); CHECK_FALSE(vob13->cd_static); CHECK_FALSE(vob13->cd_dynamic); CHECK_FALSE(vob13->vob_static); - CHECK_EQ(vob13->dynamic_shadows, phoenix::shadow_mode::none); + CHECK_EQ(vob13->dynamic_shadows, zenkit::ShadowType::NONE); CHECK_EQ(vob13->bias, 0); CHECK_FALSE(vob13->ambient); CHECK_FALSE(vob13->physics_enabled); @@ -308,8 +314,6 @@ TEST_SUITE("world") { auto& wp0 = waynet.waypoints[0]; auto& wp100 = waynet.waypoints[100]; - auto* wp500 = waynet.waypoint("OW_FOGDUNGEON_32"); - auto* wp_missing = waynet.waypoint("nonexistent"); CHECK_EQ(wp0.name, "LOCATION_28_07"); CHECK_EQ(wp0.water_depth, 0); @@ -325,16 +329,6 @@ TEST_SUITE("world") { CHECK_EQ(wp100.direction, glm::vec3 {-0.342115372, 0, 0.939657927}); CHECK_FALSE(wp100.free_point); - CHECK_NE(wp500, nullptr); - CHECK_EQ(wp500->name, "OW_FOGDUNGEON_32"); - CHECK_EQ(wp500->water_depth, 0); - CHECK_FALSE(wp500->under_water); - CHECK_EQ(wp500->position, glm::vec3 {26636.0645, -1802.15601, 10523.1445}); - CHECK_EQ(wp500->direction, glm::vec3 {-0.999390841, 0, 0.0348994918}); - CHECK_FALSE(wp500->free_point); - - CHECK_EQ(wp_missing, nullptr); - auto& edge0 = waynet.edges[0]; auto& edge5 = waynet.edges[5]; auto& edge100 = waynet.edges[100]; @@ -354,7 +348,7 @@ TEST_SUITE("world") { CHECK_EQ(edge500.b, 515); } - TEST_CASE("world(parse:g2)" * doctest::skip()) { + TEST_CASE("World.load(GOTHIC2)" * doctest::skip()) { // TODO: Stub } } diff --git a/tests/test_buffer.cc b/tests/test_buffer.cc deleted file mode 100644 index 6a9fa1d9..00000000 --- a/tests/test_buffer.cc +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright © 2022 Luis Michaelis -// SPDX-License-Identifier: MIT -#include -#include - -#include -#include - -template -static std::vector bytes(Args... bytes) { - return std::vector {static_cast(bytes)...}; -} - -static std::vector bytes_str(const char* str) { - std::vector v {}; - v.resize(std::strlen(str)); - std::memcpy(v.data(), str, v.size()); - return v; -} - -TEST_SUITE("buffer") { - TEST_CASE("buffer(of)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c')); - CHECK_EQ(buf.limit(), 3); - CHECK_EQ(buf.position(), 0); - CHECK_EQ(buf.remaining(), 3); - CHECK_EQ(buf.capacity(), 3); - CHECK_EQ(buf.direct(), false); - CHECK_EQ(buf.readonly(), true); - } - - TEST_CASE("buffer(limit)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c')); - - SUBCASE("new_limit > capacity") { - CHECK_THROWS(buf.limit(4)); - CHECK_EQ(buf.limit(), 3); - CHECK_EQ(buf.capacity(), 3); - CHECK_EQ(buf.remaining(), 3); - CHECK_EQ(buf.position(), 0); - } - - SUBCASE("new_limit < capacity") { - // limit the buffer to one element - buf.limit(1); - CHECK_EQ(buf.limit(), 1); - CHECK_EQ(buf.capacity(), 3); - CHECK_EQ(buf.remaining(), 1); - CHECK_EQ(buf.position(), 0); - } - - SUBCASE("new_limit, capacity") { - // un-limit the buffer to its capacity again - buf.limit(3); - CHECK_EQ(buf.limit(), 3); - CHECK_EQ(buf.capacity(), 3); - CHECK_EQ(buf.remaining(), 3); - CHECK_EQ(buf.position(), 0); - } - - SUBCASE("position is restored") { - buf.position(3); - buf.limit(1); - CHECK_EQ(buf.limit(), 1); - CHECK_EQ(buf.capacity(), 3); - CHECK_EQ(buf.remaining(), 0); - CHECK_EQ(buf.position(), 1); - } - } - - TEST_CASE("buffer(position)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c')); - CHECK_EQ(buf.position(), 0); - CHECK_EQ(buf.remaining(), 3); - - buf.position(1); - CHECK_EQ(buf.position(), 1); - CHECK_EQ(buf.remaining(), 2); - - CHECK_THROWS(buf.position(4)); - CHECK_EQ(buf.position(), 1); - CHECK_EQ(buf.remaining(), 2); - - buf.position(3); - CHECK_EQ(buf.position(), 3); - CHECK_EQ(buf.remaining(), 0); - - buf.rewind(); - CHECK_EQ(buf.position(), 0); - CHECK_EQ(buf.remaining(), 3); - } - - TEST_CASE("buffer(clear)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c')); - buf.position(3); - buf.limit(2); - - CHECK_EQ(buf.limit(), 2); - CHECK_EQ(buf.capacity(), 3); - CHECK_EQ(buf.remaining(), 0); - CHECK_EQ(buf.position(), 2); - - buf.clear(); - CHECK_EQ(buf.limit(), 3); - CHECK_EQ(buf.capacity(), 3); - CHECK_EQ(buf.remaining(), 3); - CHECK_EQ(buf.position(), 0); - } - - TEST_CASE("buffer(duplicate)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c')); - buf.position(1); - buf.limit(2); - - auto dup = buf.duplicate(); - CHECK_EQ(buf.position(), dup.position()); - CHECK_EQ(buf.capacity(), dup.capacity()); - CHECK_EQ(buf.remaining(), dup.remaining()); - CHECK_EQ(buf.limit(), dup.limit()); - } - - TEST_CASE("buffer(flip)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c')); - buf.position(1); - buf.flip(); - - CHECK_EQ(buf.limit(), 1); - CHECK_EQ(buf.capacity(), 3); - CHECK_EQ(buf.remaining(), 1); - CHECK_EQ(buf.position(), 0); - } - - TEST_CASE("buffer(slice)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c', 'd')); - - SUBCASE("fully") { - buf.position(2); - - auto slice = buf.slice(); - CHECK_EQ(slice.position(), 0); - CHECK_EQ(slice.capacity(), 2); - CHECK_EQ(slice.limit(), 2); - CHECK_EQ(slice.remaining(), 2); - - CHECK_EQ(buf.position(), 2); - } - - SUBCASE("partially") { - auto slice = buf.slice(1, 2); - CHECK_EQ(slice.position(), 0); - CHECK_EQ(slice.capacity(), 2); - CHECK_EQ(slice.limit(), 2); - CHECK_EQ(slice.remaining(), 2); - - CHECK_EQ(buf.position(), 0); - } - } - - TEST_CASE("buffer(mark)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h')); - - SUBCASE("basic") { - buf.position(1); - buf.mark(); - buf.position(5); - CHECK_EQ(buf.position(), 5); - CHECK_EQ(buf.capacity(), 8); - CHECK_EQ(buf.limit(), 8); - CHECK_EQ(buf.remaining(), 3); - - buf.reset(); - CHECK_EQ(buf.position(), 1); - CHECK_EQ(buf.capacity(), 8); - CHECK_EQ(buf.limit(), 8); - CHECK_EQ(buf.remaining(), 7); - } - - SUBCASE("underflow") { - buf.position(3); - buf.mark(); - buf.position(5); - CHECK_EQ(buf.position(), 5); - CHECK_EQ(buf.capacity(), 8); - CHECK_EQ(buf.limit(), 8); - CHECK_EQ(buf.remaining(), 3); - - buf.limit(2); - buf.reset(); - CHECK_EQ(buf.position(), 2); - CHECK_EQ(buf.capacity(), 8); - CHECK_EQ(buf.limit(), 2); - CHECK_EQ(buf.remaining(), 0); - } - - SUBCASE("positioned") { - buf.position(3); - buf.mark(); - buf.position(2); - CHECK_EQ(buf.position(), 2); - CHECK_EQ(buf.capacity(), 8); - CHECK_EQ(buf.limit(), 8); - CHECK_EQ(buf.remaining(), 6); - - buf.reset(); - CHECK_EQ(buf.position(), 2); - CHECK_EQ(buf.capacity(), 8); - CHECK_EQ(buf.limit(), 8); - CHECK_EQ(buf.remaining(), 6); - } - - SUBCASE("sliced") { - buf.position(1); - buf.mark(); - buf.position(5); - CHECK_EQ(buf.position(), 5); - CHECK_EQ(buf.capacity(), 8); - CHECK_EQ(buf.limit(), 8); - CHECK_EQ(buf.remaining(), 3); - - auto slice = buf.slice(); - slice.reset(); - - CHECK_EQ(slice.position(), 0); - CHECK_EQ(slice.capacity(), 3); - CHECK_EQ(slice.limit(), 3); - CHECK_EQ(slice.remaining(), 3); - } - } - - TEST_CASE("buffer(extract)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c', 'd')); - - auto slice = buf.extract(2); - CHECK_EQ(slice.limit(), 2); - CHECK_EQ(slice.position(), 0); - CHECK_EQ(buf.position(), 2); - CHECK_EQ(buf.remaining(), 2); - - CHECK_EQ(slice.get_char(), 'a'); - CHECK_EQ(buf.get_char(), 'c'); - } - - TEST_CASE("buffer(array)") { - auto buf = phoenix::buffer::of(bytes('a', 'b', 'c', 'd')); - buf.limit(3); - - auto array = buf.array(); - CHECK_EQ((char) *array, 'a'); - CHECK(std::equal(array, array + buf.limit(), bytes_str("abc").begin())); - } - - TEST_CASE("buffer(get)") { - auto buf = phoenix::buffer::of(bytes('\x1A', 0xA1, 'c', 'd')); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get(), 0x1A); - CHECK_EQ(buf.position(), 1); - - CHECK_EQ(buf.get(), 0xA1); - CHECK_EQ(buf.position(), 2); - - std::array array {}; - buf.get(array.data(), array.size()); - - CHECK_EQ(buf.position(), 4); - CHECK_EQ(buf.remaining(), 0); - CHECK(std::equal(array.begin(), array.end(), bytes_str("cd").begin())); - - CHECK_THROWS((void) buf.get()); - CHECK_THROWS(buf.get(array.data(), array.size())); - } - - TEST_CASE("buffer(get_char)") { - auto buf = phoenix::buffer::of(bytes('a', 'b')); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_char(), 'a'); - CHECK_EQ(buf.position(), 1); - - CHECK_EQ(buf.get_char(), 'b'); - CHECK_EQ(buf.position(), 2); - - CHECK_THROWS((void) buf.get_char()); - } - - TEST_CASE("buffer(get_short)") { - auto buf = phoenix::buffer::of(bytes(0xFF, 0xFF, 0x01, 0x00, 0xFF)); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_short(), -1); - CHECK_EQ(buf.position(), 2); - - CHECK_EQ(buf.get_short(), 1); - CHECK_EQ(buf.position(), 4); - - CHECK_THROWS((void) buf.get_short()); - } - - TEST_CASE("buffer(get_ushort)") { - auto buf = phoenix::buffer::of(bytes(0xFF, 0xFF, 0x01, 0x00, 0xFF)); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_ushort(), 0xFF'FF); - CHECK_EQ(buf.position(), 2); - - CHECK_EQ(buf.get_ushort(), 1); - CHECK_EQ(buf.position(), 4); - - CHECK_THROWS((void) buf.get_short()); - } - - TEST_CASE("buffer(get_int)") { - auto buf = phoenix::buffer::of(bytes(0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF)); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_int(), -1); - CHECK_EQ(buf.position(), 4); - - CHECK_EQ(buf.get_int(), 1); - CHECK_EQ(buf.position(), 8); - - CHECK_THROWS((void) buf.get_int()); - } - - TEST_CASE("buffer(get_uint)") { - auto buf = phoenix::buffer::of(bytes(0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF)); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_uint(), 0xFFFF'FFFF); - CHECK_EQ(buf.position(), 4); - - CHECK_EQ(buf.get_uint(), 1); - CHECK_EQ(buf.position(), 8); - - CHECK_THROWS((void) buf.get_uint()); - } - - TEST_CASE("buffer(get_long)") { - auto buf = phoenix::buffer::of(bytes(0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF)); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_long(), -1); - CHECK_EQ(buf.position(), 8); - - CHECK_EQ(buf.get_long(), 1); - CHECK_EQ(buf.position(), 16); - - CHECK_THROWS((void) buf.get_long()); - } - - TEST_CASE("buffer(get_ulong)") { - auto buf = phoenix::buffer::of(bytes(0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF)); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_ulong(), 0xFFFFFFFF'FFFFFFFFL); - CHECK_EQ(buf.position(), 8); - - CHECK_EQ(buf.get_ulong(), 1); - CHECK_EQ(buf.position(), 16); - - CHECK_THROWS((void) buf.get_ulong()); - } - - TEST_CASE("buffer(get_float)") { - - auto buf = phoenix::buffer::of(bytes(0x52, 0x58, 0xD2, 0x43, 0x0A, 0xD7, 0x8A, 0xC2, 0xFF, 0xFF, 0xFF)); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_float(), 420.69f); - CHECK_EQ(buf.position(), 4); - - CHECK_EQ(buf.get_float(), -69.420f); - CHECK_EQ(buf.position(), 8); - - CHECK_THROWS((void) buf.get_float()); - } - - TEST_CASE("buffer(get_double)") { - auto buf = phoenix::buffer::of(bytes(0xD7, - 0xA3, - 0x70, - 0x3D, - 0x0A, - 0x4B, - 0x7A, - 0x40, - 0x7B, - 0x14, - 0xAE, - 0x47, - 0xE1, - 0x5A, - 0x51, - 0xC0, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF)); - CHECK_EQ(buf.position(), 0); - - CHECK_EQ(buf.get_double(), 420.69); - CHECK_EQ(buf.position(), 8); - - CHECK_EQ(buf.get_double(), -69.420); - CHECK_EQ(buf.position(), 16); - - CHECK_THROWS((void) buf.get_double()); - } - - TEST_CASE("buffer(get_string)") { - auto buf = - phoenix::buffer::of(bytes('H', 'i', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!')); - - CHECK_EQ(buf.get_string(2), "Hi"); - CHECK_EQ(buf.position(), 2); - - CHECK_EQ(buf.get_string(13), "Hello, World!"); - CHECK_EQ(buf.position(), 15); - - CHECK_THROWS((void) buf.get_string(1)); - } - - TEST_CASE("buffer(get_line)") { - auto buf = phoenix::buffer::of(bytes('H', - 'i', - '\n', - ' ', - ' ', - '\r', - '\t', - 'H', - 'e', - 'l', - 'l', - 'o', - ',', - '\\', - 't', - 'W', - 'o', - 'r', - 'l', - 'd', - '!', - '\n')); - - CHECK_EQ(buf.get_line(true), "Hi"); - CHECK_EQ(buf.position(), 7); - buf.mark(); - - CHECK_EQ(buf.get_line(true), "Hello,\\tWorld!"); - CHECK_EQ(buf.position(), 22); - - buf.reset(); - CHECK_EQ(buf.get_line_escaped(true), "Hello,\tWorld!"); - CHECK_EQ(buf.position(), 22); - - CHECK(buf.get_line().empty()); - } - - TEST_CASE("buffer(get_vec2)" * doctest::skip()) { - // TODO: Stub - } - - TEST_CASE("buffer(get_vec3)" * doctest::skip()) { - // TODO: Stub - } - - TEST_CASE("buffer(get_vec4)" * doctest::skip()) { - // TODO: Stub - } - - TEST_CASE("buffer(put*)") { - auto buf = phoenix::buffer::allocate(57); - - buf.put(0xFF); - buf.put_short(-16); - buf.put_ushort(16); - buf.put_int(-16); - buf.put_uint(16); - buf.put_long(-16); - buf.put_ulong(16); - buf.put_float(69.420f); - buf.put_double(420.69); - buf.put_string("Hi"); - buf.put_line("Hello, World!"); - buf.flip(); - - CHECK_EQ(buf.limit(), 57); - CHECK_EQ(buf.get(), 0xFF); - CHECK_EQ(buf.get_short(), -16); - CHECK_EQ(buf.get_ushort(), 16); - CHECK_EQ(buf.get_int(), -16); - CHECK_EQ(buf.get_uint(), 16); - CHECK_EQ(buf.get_long(), -16); - CHECK_EQ(buf.get_ulong(), 16); - CHECK_EQ(buf.get_float(), 69.420f); - CHECK_EQ(buf.get_double(), 420.69); - CHECK_EQ(buf.get_string(2), "Hi"); - CHECK_EQ(buf.get_line(), "Hello, World!"); - - CHECK_EQ(buf.remaining(), 0); - } - - TEST_CASE("buffer(empty)") { - auto empty = phoenix::buffer::empty(); - CHECK_EQ(empty.limit(), 0); - CHECK_EQ(empty.capacity(), 0); - CHECK_EQ(empty.remaining(), 0); - CHECK_EQ(empty.position(), 0); - - auto buf = phoenix::buffer::allocate(10); - CHECK_FALSE(empty == buf); - } - - TEST_CASE("buffer(mmap)") { - SUBCASE("empty file") { - auto buf = phoenix::buffer::mmap("samples/empty.txt"); - CHECK_EQ(buf.limit(), 0); - CHECK_EQ(buf.capacity(), 0); - CHECK_THROWS_AS((void) buf.get_char(), phoenix::buffer_underflow); - } - } -}