From 6fabc6b9b27d5d856d78949434a544fdce72d938 Mon Sep 17 00:00:00 2001 From: Luis Michaelis Date: Sun, 12 Mar 2023 11:25:36 +0100 Subject: [PATCH] chore(release): release v1.1.1 --- .clang-format | 1 + .gitmodules | 3 - CMakeLists.txt | 29 +- changelog.md | 27 + docs/assets/javascript/mathjax.js | 16 + docs/engine/formats/animation.md | 189 ++++++- docs/library/formats/animation.md | 29 ++ docs/library/reference.md | 2 +- include/phoenix/Api.hh | 30 ++ include/phoenix/animation.hh | 7 +- include/phoenix/archive.hh | 7 +- include/phoenix/buffer.hh | 450 ++++------------ include/phoenix/ext/daedalus_classes.hh | 49 +- include/phoenix/ext/dds_convert.hh | 4 +- include/phoenix/font.hh | 9 +- include/phoenix/material.hh | 2 +- include/phoenix/math.hh | 70 +-- include/phoenix/mesh.hh | 9 +- include/phoenix/messages.hh | 7 +- include/phoenix/model.hh | 4 +- include/phoenix/model_hierarchy.hh | 8 +- include/phoenix/model_mesh.hh | 8 +- include/phoenix/model_script.hh | 20 +- include/phoenix/morph_mesh.hh | 5 +- include/phoenix/phoenix.hh | 42 +- include/phoenix/proto_mesh.hh | 9 +- include/phoenix/save_game.hh | 12 +- include/phoenix/script.hh | 167 +++--- include/phoenix/softskin_mesh.hh | 5 +- include/phoenix/texture.hh | 30 +- include/phoenix/vdfs.hh | 57 ++- include/phoenix/vm.hh | 73 +-- include/phoenix/vobs/camera.hh | 5 +- include/phoenix/vobs/light.hh | 7 +- include/phoenix/vobs/misc.hh | 21 +- include/phoenix/vobs/mob.hh | 10 +- include/phoenix/vobs/sound.hh | 4 +- include/phoenix/vobs/trigger.hh | 14 +- include/phoenix/vobs/vob.hh | 7 +- include/phoenix/vobs/zone.hh | 7 +- include/phoenix/world.hh | 9 +- include/phoenix/world/bsp_tree.hh | 7 +- include/phoenix/world/vob_tree.hh | 2 +- include/phoenix/world/way_net.hh | 6 +- mkdocs.yml | 6 + source/animation.cc | 46 +- source/archive.cc | 10 +- source/archive/archive_ascii.cc | 49 +- source/archive/archive_binary.cc | 17 +- source/archive/archive_binsafe.cc | 5 +- source/buffer.cc | 271 ++++++---- source/math.cc | 72 +++ source/mesh.cc | 2 +- source/messages.cc | 11 +- source/model_hierarchy.cc | 5 +- source/model_mesh.cc | 2 +- source/model_script.cc | 34 +- source/model_script_dsl.cc | 522 +++++++++++++++++++ source/model_script_dsl.hh | 652 ++++-------------------- source/script.cc | 29 +- source/vdfs.cc | 7 +- source/vm.cc | 101 +++- source/vobs/camera.cc | 2 +- source/vobs/vob.cc | 2 +- tests/samples/waran.mds | 1 - tests/test_buffer.cc | 318 +++--------- tests/test_model_script.cc | 1 + vendor/CMakeLists.txt | 7 +- vendor/lexy | 1 - 69 files changed, 1929 insertions(+), 1723 deletions(-) create mode 100644 docs/assets/javascript/mathjax.js create mode 100644 docs/library/formats/animation.md create mode 100644 include/phoenix/Api.hh create mode 100644 source/math.cc create mode 100644 source/model_script_dsl.cc delete mode 160000 vendor/lexy diff --git a/.clang-format b/.clang-format index 59876af9..28c0e923 100644 --- a/.clang-format +++ b/.clang-format @@ -52,3 +52,4 @@ SpacesBeforeTrailingComments: 1 TabWidth: 4 UseTab: ForIndentation AccessModifierOffset: -4 +IndentPPDirectives: BeforeHash diff --git a/.gitmodules b/.gitmodules index 93924ba9..4f3db436 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "vendor/mio"] path = vendor/mio url = https://github.com/mandreyel/mio.git -[submodule "vendor/lexy"] - path = vendor/lexy - url = https://github.com/foonathan/lexy.git [submodule "vendor/libsquish"] path = vendor/libsquish url = https://github.com/lmichaelis/phoenix-libsquish.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 363a9c1b..a02204bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,13 @@ cmake_minimum_required(VERSION 3.10) include(CheckIncludeFiles) -project(phoenix VERSION 1.0.1) +project(phoenix VERSION 1.1.1) 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) @@ -57,12 +58,14 @@ set(PHOENIX_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 @@ -112,15 +115,24 @@ set(PHOENIX_TESTS tests/test_world.cc) # add the phoenix library definition -add_library(phoenix ${PHOENIX_SOURCES} ${PHOENIX_EXTENSIONS} ${PHOENIX_HEADERS}) +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) +else () + add_library(phoenix STATIC) + set(PHOENIX_DEFINES ${PHOENIX_DEFINES} PHOENIX_STATIC=1) + set_target_properties(phoenix PROPERTIES COMPILE_FLAGS "-DPHOENIX_STATIC=1") +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 lexy) + PRIVATE mio) -target_compile_definitions(phoenix PUBLIC ${PHOENIX_DEFINES}) +target_compile_definitions(phoenix PUBLIC ${PHOENIX_DEFINES} PRIVATE PHOENIX_EXPORTS=1) target_compile_options(phoenix PRIVATE ${PHOENIX_CXX_FLAGS}) if (NOT MSVC) @@ -128,16 +140,17 @@ if (NOT MSVC) endif () set_target_properties(phoenix PROPERTIES - DEBUG_POSTFIX "${PHOENIX_DEBUG_POSTFIX}") + DEBUG_POSTFIX "${PHOENIX_DEBUG_POSTFIX}" + VERSION ${PROJECT_VERSION}) if (PHOENIX_INSTALL) install(TARGETS phoenix ARCHIVE LIBRARY RUNTIME) - install(DIRECTORY "include/phoenix" TYPE INCLUDE) - install(DIRECTORY "${glm_SOURCE_DIR}/glm" TYPE INCLUDE FILES_MATCHING PATTERN "*.hpp" PATTERN "*.inl" PATTERN "*.h") - if (NOT BUILD_SHARED_LIBS) + if (NOT PHOENIX_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") + foreach (lib glm::glm_static squish) install(FILES "$" TYPE LIB) endforeach () diff --git a/changelog.md b/changelog.md index 4a0d024b..3af48122 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,33 @@ found in [readme.md](readme.md#versioning). --- +## v1.1.1 + +This update again brings many bugfixes and smaller improvements in addition to updates to the documentation. +_phoenix_ can now also be built as a shared library which should be considered an experimental feature. This update +also finally replaces the old [lexy](https://github.com/foonathan/lexy) parser for model scripts with a self-rolled +implementation. + +### Bugfixes +* [03cb97bd] Fixed a stack corruption issue in the VM which could be triggered if the `movvf` or `movf` was called + with a member but no current instance was present +* [c6cb69de] Fixed broken VM execution flag `allow_null_instance_access` for instructions `addmovi`, `submovi`, + `mulmovi` and `divmovi`. +* [cae1c118, f0d6751f] Fixed a VM/script bug which could occur when using higher-order functions. +* [2cd3da6f] Added checks for division by zero errors in the VM. +* [3693450b] Prevent null-pointer de-reference in `vm::initialize_instance` if the instance's parent symbol can + not be found. +* [1bf25106] VDF entries with a size larger than the VDF file itself are now no longer loaded. +* [5d78aa49, 4e7b8630] Fix an issues with ignoring whitespace in binary archives. +* [c7d4115f] Catch numeric conversion errors in archives and re-throw them as `parser_error`s. + +### Misc + +* [4e9ae25e] Move the const-ness check for script symbols into the VM. +* [c7c6b94a] (experimental) Allow for building phoenix as a shared library. +* [15cd5893] Save the checksums for animations, model meshes and skeletons. +* [16679249] Switch to a custom model script parser, dropping the dependency on [lexy](https://github.com/foonathan/lexy) + ## v1.1.0 Oh boy, this is a big one! There are a lot of additions, some changes and also some deprecations here. Also, the diff --git a/docs/assets/javascript/mathjax.js b/docs/assets/javascript/mathjax.js new file mode 100644 index 00000000..fd764a73 --- /dev/null +++ b/docs/assets/javascript/mathjax.js @@ -0,0 +1,16 @@ +window.MathJax = { + tex: { + inlineMath: [["\\(", "\\)"]], + displayMath: [["\\[", "\\]"]], + processEscapes: true, + processEnvironments: true + }, + options: { + ignoreHtmlClass: ".*|", + processHtmlClass: "arithmatex" + } +}; + +document$.subscribe(() => { + MathJax.typesetPromise() +}) diff --git a/docs/engine/formats/animation.md b/docs/engine/formats/animation.md index a388d33f..f895ce84 100644 --- a/docs/engine/formats/animation.md +++ b/docs/engine/formats/animation.md @@ -53,8 +53,8 @@ chunks. Also refer to the [Datatype Reference](../datatypes.md) for general info float fps; float fpsSource; - float samplePositionRangeMin; - float samplePositionScalar; + float samplePositionMin; + float samplePositionScale; zTBBox3D bounds; string next; @@ -85,7 +85,188 @@ chunks. Also refer to the [Datatype Reference](../datatypes.md) for general info struct zCModelAni_Samples { uint checksum; uint nodeIndices[/* zCModelAni_Header.numNodes */]; - ushort rotation[3]; - ushort position[3]; + + struct zTMdl_AniSample { + ushort rotation[3]; + ushort position[3]; + } samples[/* zCModelAni_Header.numNodes * zCModelAni_Header.numFrames */]; }; ``` + + The `zCModelAni.checksum` field is used to match compatible the animation to a specific model. It is the same for + assets belonging to the same model. Linked assets are `zCModelAni.checksum`, `zCModelMeshLib.checksum` and + `zCModelHierarchy.checksum`. + + It is important to understand that the animation samples do not contain the actual values for the rotation and + position. To save on memory and disk space consumption, the values are packed into shorts which are converted + back to floats on demand. See [Sample Positions](#sample-positions) and [Sample Rotations](#sample-rotations) for + more information. + +--- + +### Sample Positions + +The positions are represented as multiples of `zCModelAni_Header.samplePositionScale`. The formula for packing a +given `zVEC3` position into the stored format can be summarized like this: + +$$ + \begin{bmatrix} + x_{p} \\ + y_{p} \\ + z_{p} + \end{bmatrix} + = + \Bigg(-v_{min} + + \begin{bmatrix} + x \\ + y \\ + z + \end{bmatrix} + \Bigg) \cdot \frac{65535}{v_{max} - v_{min}} +$$ + +where $v_{min}$ is the smallest value across all components of all sample positions of the animation and $v_{max}$ +is the maximum value. The output vector $\begin{smallmatrix}x_{p} \\ y_{p} \\ z_{p} \end{smallmatrix}$ then contains a +scaled representation of the input vector $\begin{smallmatrix}x \\ y \\ z\end{smallmatrix}$ as three 16-bit unsigned +integers. This operation is performed by `zCModelAni::AddTrafoMatrix` and `zTMdl_AniSample::PackTrans`. + +Alongside these packed positions, both $v_{min}$ and the constant $s = (\frac{65535}{v_{max} - v_{min}})^{-1}$ are saved +as `zCModelAni_Header.samplePositionMin` and `zCModelAni_Header.samplePositionScale` respectively. Thus, to convert +the stored position back to its floating point representation, the following calculation may be applied: + +$$ + \begin{bmatrix} + x \\ + y \\ + z + \end{bmatrix} + = + \begin{bmatrix} + x_{p} \\ + y_{p} \\ + z_{p} + \end{bmatrix} + \cdot s + v_{min} +$$ + +where $x_{p}$ , $y_{p}$ and $z_{p}$ are `zTMdl_AniSample.position[0]`, `zTMdl_AniSample.position[1]` and +`zTMdl_AniSample.position[2]` respectively. This operation is performed by `zTMdl_AniSample::UnpackTrans` . + +### Sample Rotations + +The rotations are represented as a packed quaternion which is calculated in the following way: + +$$ + \begin{bmatrix} + x_{p} \\ + y_{p} \\ + z_{p} + \end{bmatrix} + = + \begin{cases} + \begin{bmatrix} + x \\ + y \\ + z + \end{bmatrix} + \div s + h & \quad \text{if } w \geq 0 \\ + -\Bigg( + \begin{bmatrix} + x \\ + y \\ + z + \end{bmatrix} + \div s + h\Bigg) - 2 & \quad \text{if } w < 0 \\ + \end{cases} +$$ + +where + +$$ + s = \frac{1}{2^{16} - 1} * 2.1 = \frac{1}{65535} * 2.1 +$$ + +and + +$$ + h = 2^{15} - 1 = 32767 +$$ + +This operation packs a quaternion of the form $r = \begin{smallmatrix}x \\ y \\ z \\ w\end{smallmatrix}$ into a set of +three 16-bit unsigned integers $x_{p}$ , $y_{p}$ and $z_{p}$ which are stored into the sample. **The input quaternion +$r$ is required to be a unit vector**. This operation was performed by `zCModelAni::AddTrafoMatrix` and +`zTMdl_AniSample::PackQuat`. + +They can be unpacked using some quaternion wizardry as described below. + +$$ + \begin{bmatrix} + x \\ + y \\ + z \\ + w + \end{bmatrix} + = + \begin{cases} + \begin{bmatrix} + x_{t} \\ + y_{t} \\ + z_{t} \\ + \sqrt{1 - l} + \end{bmatrix} & \quad \text{if } l \leq 1 \\ + \begin{bmatrix} + x_{t} \\ + y_{t} \\ + z_{t} \\ + 0 + \end{bmatrix} + \cdot \frac{1}{\sqrt{l}} & \quad \text{if } l > 1 + \end{cases} +$$ + +where + +$$ + \begin{bmatrix} + x_{t} \\ + y_{t} \\ + z_{t} \\ + \end{bmatrix} + = + \Bigg(\begin{bmatrix} + x_{p} \\ + y_{p} \\ + z_{p} \\ + \end{bmatrix} - h\Bigg) \cdot s +$$ + +and + +$$ + l = (x_{t})^2 + (y_{t})^2 + (z_{t})^2 +$$ + +The values $x_{p}$ , $y_{p}$ and $z_{p}$ for this computation are stored in `zTMdl_AniSample.rotation[0]`, +`zTMdl_AniSample.rotation[1]` and `zTMdl_AniSample.rotation[2]` respectively. This is possible since the quaternion +$r$ used to calculate the packed rotation is guaranteed to be a unit-quaternion, thus, if we have three components +(like we do), it is possible to calculate the third component. This operation is performed by +`zTMdl_AniSample::UnpackQuat`. + +--- + +### (todo) How animated models are loaded + +Animated models are loaded by using their associated model script file. While parsing the model script, the parser +will encounter a `meshAndTree` directive (in MSB files a chunk of type `0xf300`) which points to a model hierarchy. + +This model hierarchy is then loaded and acts as the ground truth of the model. If a hierarchy can't be loaded for any +reason, the engine tries to load a model mesh instead. + +The model script parser continues and eventually hits the `aniEnum` section. While loading it, it will come across +`ani` sections. Those sections contain a model and animation name which are then loaded as animations and model meshes +respectively. + +Linking these parts together is a checksum calculates from the list of hierarchy nodes. It is just the CRC32 checksum +of all node names appended after each other in the order they are saved in. I.e. a model hierarchy with two nodes +`"BIP01"` and `"BIP01 LEVER STICK"` (in that order) will have a checksum of `ea809bf7` (which +is `crc32("BIP01BIP01 LEVER STICK")`. diff --git a/docs/library/formats/animation.md b/docs/library/formats/animation.md new file mode 100644 index 00000000..4f5b03dd --- /dev/null +++ b/docs/library/formats/animation.md @@ -0,0 +1,29 @@ + + +Animations (also called _Model Animations_) form part of the animations system of the _ZenGin_ they contain only +animation samples, i.e. the position and orientation of each bone of the skeleton they're applied to. While there is +space for additional data within animation files, it is mostly empty. + +## Overview + +*phoenix'* implementation of animations lives in `include/phoenix/animation.hh` and `source/animation.cc`. The most +important part of an animation are its samples. They are stored in `animation::samples` after it has been parsed. +Animation files themselves don't contain information about when to run animation or any other effects which should be +applied during it. Those parts of the animation system are defined in [Model Script](formats/model-script.md) files +which should be loaded before animations. The `animation::events` field will always be empty for that reason. + + +### Loading a Font + +Like most data structures in *phoenix*, animations can be loaded using the `#!cpp phoenix::animation::parse()` function. +It takes a `phoenix::buffer` as a parameter and loads the animation from it. + +```cpp title="Example" +#include + +int main(int, const char** argv) { + auto anim_buffer = phoenix::buffer::mmap("A.man"); + [[maybe_unused]] auto anim = phoenix::animation::parse(anim_buffer); + return 0; +} +``` diff --git a/docs/library/reference.md b/docs/library/reference.md index 8eae68ec..9eb7a159 100644 --- a/docs/library/reference.md +++ b/docs/library/reference.md @@ -15,7 +15,7 @@ The following is a list of file types and formats used by ZenGin. | Format | Extension | Description | _phoenix_ Class Name | |-----------------------------------------------------------|:------------------------------:|----------------------------------------------------------------------------------------------------------------------------|----------------------| -| [Model Animation](formats/model-animation.md) | `.MAN` | Contains animations for a model | `animation` | +| [Model Animation](formats/animation.md) | `.MAN` | Contains animations for a model | `animation` | | [Model Hierarchy](formats/model-hierarchy.md) | `.MDH` | Contains skeletal information for a model | `model_hierarchy` | | [Model Mesh](formats/model-mesh.md) | `.MDM` | Contains the mesh of a model | `model_mesh` | | [Model](formats/model.md) | `.MDL` | Contains a mesh and a hierarchy which make up a model | `model` | diff --git a/include/phoenix/Api.hh b/include/phoenix/Api.hh new file mode 100644 index 00000000..efc4dc0b --- /dev/null +++ b/include/phoenix/Api.hh @@ -0,0 +1,30 @@ +// Copyright © 2023 Luis Michaelis +// SPDX-License-Identifier: MIT +#pragma once + +#ifndef PHOENIX_STATIC + #if defined(_WIN32) || defined(__CYGWIN__) + #ifdef PHOENIX_EXPORTS + #ifdef __GNUC__ + #define PHOENIX_API __attribute__((dllexport)) + #else + #define PHOENIX_API __declspec(dllexport) + #endif + #else + #ifdef __GNUC__ + #define PHOENIX_API __attribute__((dllimport)) + #else + #define PHOENIX_API __declspec(dllimport) + #endif + #endif + #define PHOENIX_INTERNAL + #else + #define PHOENIX_API __attribute__((visibility("default"))) + #define PHOENIX_INTERNAL __attribute__((visibility("hidden"))) + #endif +#else + #define PHOENIX_API + #define PHOENIX_INTERNAL +#endif + +#define PHOENIX_DEPRECATED(reason) [[deprecated(reason)]] diff --git a/include/phoenix/animation.hh b/include/phoenix/animation.hh index bd2f2f24..1caf3b78 100644 --- a/include/phoenix/animation.hh +++ b/include/phoenix/animation.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include #include #include #include @@ -66,14 +67,14 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static animation parse(buffer& in); + [[nodiscard]] PHOENIX_API static animation parse(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]] inline static animation parse(buffer&& in) { + [[nodiscard]] PHOENIX_API inline static animation parse(buffer&& in) { return animation::parse(in); } @@ -103,7 +104,7 @@ namespace phoenix { /// \brief The bounding box of the animation. bounding_box bbox {}; - /// \brief A checksum for this animation (inner workings unknown). + /// \brief The checksum of the model hierarchy this animation was made for. std::uint32_t checksum {}; /// \brief The original path of the animation script this animation was generated from. diff --git a/include/phoenix/archive.hh b/include/phoenix/archive.hh index 35bf06f2..144e5a9c 100644 --- a/include/phoenix/archive.hh +++ b/include/phoenix/archive.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "phoenix/Api.hh" #include #include #include @@ -41,7 +42,7 @@ namespace phoenix { /// \param in The reader to read from. /// \return The header read. /// \throws parser_error if parsing fails. - [[nodiscard]] static archive_header parse(buffer& in); + [[nodiscard]] PHOENIX_INTERNAL static archive_header parse(buffer& in); }; /// \brief Represents the header of an object stored in a ZenGin archive. @@ -87,7 +88,7 @@ namespace phoenix { std::function&, const std::optional&)>; /// \brief A reader for ZenGin archives. - class archive_reader { + class PHOENIX_API archive_reader { public: /// \brief Constructs a new archive_reader from the given reader and header. /// \note This constructor should never be called explicitly. Use #open instead! @@ -183,7 +184,7 @@ namespace phoenix { /// \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 - [[deprecated("unsafe api: use read_raw_bytes(uint32_t) instead")]] virtual buffer read_raw_bytes() = 0; + 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) diff --git a/include/phoenix/buffer.hh b/include/phoenix/buffer.hh index 7d02e65b..25e3e596 100644 --- a/include/phoenix/buffer.hh +++ b/include/phoenix/buffer.hh @@ -32,9 +32,9 @@ namespace phoenix { /// is more than the number of bytes remaining. class buffer_underflow : public buffer_error { public: - buffer_underflow(std::uint64_t byte, std::uint64_t size); - buffer_underflow(std::uint64_t byte, std::uint64_t size, std::string&& context); - buffer_underflow(std::uint64_t byte, std::string&& context); + PHOENIX_INTERNAL buffer_underflow(std::uint64_t byte, std::uint64_t size); + PHOENIX_INTERNAL buffer_underflow(std::uint64_t byte, std::uint64_t size, std::string&& context); + PHOENIX_INTERNAL buffer_underflow(std::uint64_t byte, std::string&& context); public: const std::uint64_t byte, size; @@ -47,8 +47,8 @@ namespace phoenix { /// is more than the number of bytes remaining. class buffer_overflow : public buffer_error { public: - buffer_overflow(std::uint64_t byte, std::uint64_t size); - buffer_overflow(std::uint64_t byte, std::uint64_t size, std::string&& context); + PHOENIX_INTERNAL buffer_overflow(std::uint64_t byte, std::uint64_t size); + PHOENIX_INTERNAL buffer_overflow(std::uint64_t byte, std::uint64_t size, std::string&& context); public: const std::uint64_t byte, size; @@ -58,7 +58,7 @@ namespace phoenix { /// \brief Exception thrown if a write is attempted on a readonly buffer. class buffer_readonly : public buffer_error { public: - explicit buffer_readonly() : buffer_error("buffer is not readonly") {} + PHOENIX_INTERNAL explicit buffer_readonly() : buffer_error("buffer is not readonly") {} }; /// \brief Base class for all buffer backings. @@ -67,7 +67,7 @@ namespace phoenix { /// each referencing a subsection of the backing. For this reason, buffer backings should be stateless. class buffer_backing { public: - virtual ~buffer_backing() = default; + PHOENIX_API virtual ~buffer_backing() = default; /// \brief Returns whether this backing considered direct or not. /// @@ -76,22 +76,22 @@ namespace phoenix { /// file. /// /// \return `true` if this backing is direct and `false` if not. - [[nodiscard]] virtual bool direct() const noexcept = 0; + [[nodiscard]] PHOENIX_API 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]] virtual bool readonly() const noexcept = 0; + [[nodiscard]] PHOENIX_API 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]] virtual std::uint64_t size() const noexcept = 0; + [[nodiscard]] PHOENIX_API 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]] virtual const std::byte* array() const = 0; + [[nodiscard]] PHOENIX_API virtual const std::byte* array() const = 0; /// \brief Fills the given \p buf with bytes from this backing starting at \p offset. /// @@ -101,7 +101,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. - virtual void read(std::byte* buf, std::uint64_t size, std::uint64_t offset) const = 0; + PHOENIX_API 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 +112,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. - virtual void write([[maybe_unused]] const std::byte* buf, - [[maybe_unused]] std::uint64_t size, - [[maybe_unused]] std::uint64_t offset) { + PHOENIX_API 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 +122,13 @@ namespace phoenix { /// \brief A buffer implementation inspired by Java's ByteBuffer class buffer { private: - buffer(std::shared_ptr backing, std::uint64_t begin, std::uint64_t end); - buffer(std::shared_ptr backing, - std::uint64_t begin, - std::uint64_t end, - std::uint64_t capacity, - std::uint64_t position, - std::optional mark); + 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); /// \brief Reads a scalar of type \p T at the current #position. /// @@ -141,11 +141,7 @@ namespace phoenix { template < typename T, typename = typename std::enable_if::value || std::is_floating_point::value>::type> - [[nodiscard]] T _get_t() { - auto tmp = this->_get_t(this->position()); - _m_position += sizeof(T); - return tmp; - } + [[nodiscard]] PHOENIX_INTERNAL 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. @@ -155,15 +151,7 @@ namespace phoenix { template < typename T, typename = typename std::enable_if::value || std::is_floating_point::value>::type> - [[nodiscard]] T _get_t(std::uint64_t pos) const { - if (pos + sizeof(T) > limit()) { - throw buffer_underflow {pos, sizeof(T)}; - } - - T tmp; - _m_backing->read((std::byte*) &tmp, sizeof(T), _m_backing_begin + pos); - return tmp; - } + [[nodiscard]] PHOENIX_INTERNAL T _get_t(std::uint64_t pos) const; /// \brief Writes a scalar of type \p T at the current #position. /// @@ -175,14 +163,7 @@ namespace phoenix { template < typename T, typename = typename std::enable_if::value || std::is_floating_point::value>::type> - void _put_t(T value) { - if (this->remaining() < sizeof(T)) { - throw buffer_overflow {this->position(), sizeof(T)}; - } - - _m_backing->write((std::byte*) &value, sizeof(T), _m_backing_begin + _m_position); - _m_position += sizeof(T); - } + PHOENIX_INTERNAL void _put_t(T value); public: /// \brief Constructs a new buffer from the given backing. @@ -191,22 +172,22 @@ namespace phoenix { /// To shrink the buffer, see #slice or #extract. /// /// \param backing The buffer backing to use. - explicit buffer(std::shared_ptr backing); + PHOENIX_API explicit buffer(std::shared_ptr backing); /// \brief Gets the current position of this buffer. /// \return The current position of this buffer. - [[nodiscard]] inline std::uint64_t position() const noexcept { + [[nodiscard]] PHOENIX_API 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. - void position(std::uint64_t pos); + PHOENIX_API void position(std::uint64_t pos); /// \brief Returns the number of bytes available in this buffer. /// \return The limit of this buffer. - [[nodiscard]] inline std::uint64_t limit() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint64_t limit() const noexcept { return _m_backing_end - _m_backing_begin; } @@ -216,12 +197,12 @@ namespace phoenix { /// /// \param limit The new limit to set. /// \throws buffer_underflow if \p limit is greater than #capacity. - void limit(std::uint64_t limit); + PHOENIX_API 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. - inline void rewind() { + PHOENIX_API inline void rewind() { _m_position = 0; _m_mark.reset(); } @@ -230,7 +211,7 @@ namespace phoenix { /// \param count The number of bytes to skip. /// \throws buffer_underflow if #position + \p count > #limit /// \see #position - inline void skip(std::uint64_t count) { + PHOENIX_API inline void skip(std::uint64_t count) { return this->position(this->position() + count); } @@ -239,7 +220,7 @@ namespace phoenix { /// The number of remaining bytes is equal to #limit - #position. /// /// \return The number of bytes remaining in this buffer. - [[nodiscard]] inline std::uint64_t remaining() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint64_t remaining() const noexcept { return this->limit() - this->position(); } @@ -249,21 +230,21 @@ namespace phoenix { /// the total number of bytes available in the backing. /// /// \return The capacity of this buffer. - [[nodiscard]] inline std::uint64_t capacity() const noexcept { + [[nodiscard]] PHOENIX_API 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]] inline bool direct() const noexcept { + [[nodiscard]] PHOENIX_API 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]] inline bool readonly() const noexcept { + [[nodiscard]] PHOENIX_API inline bool readonly() const noexcept { return _m_backing->readonly(); } @@ -271,22 +252,22 @@ namespace phoenix { /// /// #limit is set to #capacity and #position is set to 0. The mark is discarded if /// it is set. - void clear() noexcept; + PHOENIX_API void clear() noexcept; /// \brief Flips this buffer. /// /// Its limit is set to the current position and its current position is set to 0. - void flip() noexcept; + PHOENIX_API void flip() noexcept; /// \brief Sets this buffer's mark at its position. /// \return This buffer. - inline void mark() noexcept { + PHOENIX_API inline void mark() noexcept { _m_mark = position(); } /// \brief Resets this buffer's position to the previously-marked position. /// \return This buffer. - inline void reset() { + PHOENIX_API inline void reset() { if (_m_mark) { position(*_m_mark); } @@ -298,14 +279,14 @@ namespace phoenix { /// capacity and limit. /// /// \return The newly created buffer. - [[nodiscard]] buffer duplicate() const noexcept; + [[nodiscard]] PHOENIX_API 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]] buffer slice() const noexcept; + [[nodiscard]] PHOENIX_API buffer slice() const noexcept; /// \brief Creates a new buffer which shares a subsequence of this buffer. /// @@ -315,7 +296,7 @@ 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]] buffer slice(std::uint64_t index, std::uint64_t size) const; + [[nodiscard]] PHOENIX_API buffer slice(std::uint64_t index, std::uint64_t size) const; /// \brief Creates a new buffer which shares a subsequence of this buffer. /// @@ -325,14 +306,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]] buffer extract(std::uint64_t size) { + [[nodiscard]] PHOENIX_API 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]] inline const std::byte* array() const noexcept { + [[nodiscard]] PHOENIX_API inline const std::byte* array() const noexcept { return _m_backing->array() + _m_backing_begin; } @@ -340,201 +321,85 @@ 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. - void get(std::byte* buf, std::uint64_t size); + PHOENIX_API 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. - inline void get(std::uint8_t* buf, std::uint64_t size) { + PHOENIX_API 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]] inline std::uint8_t get() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline char get_char() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline std::int16_t get_short() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline std::uint16_t get_ushort() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline std::int32_t get_int() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline std::uint32_t get_uint() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline std::int64_t get_long() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline std::uint64_t get_ulong() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline float get_float() { - return _get_t(); - } + [[nodiscard]] PHOENIX_API 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]] inline double get_double() { - return _get_t(); - } - - /// \brief Get bytes from the buffer, put them into buf. - /// \param index The index at which to start reading. - /// \param buf The buffer to write into. - /// \param size The number of bytes to get. - /// \throws buffer_underflow if the values can't be read. - void get(std::uint64_t index, std::byte* buf, std::uint64_t size) const; - - /// \brief Get bytes from the buffer, put them into buf. - /// \param index The index at which to start reading. - /// \param buf The buffer to write into. - /// \param size The number of bytes to get. - /// \throws buffer_underflow if the values can't be read. - inline void get(std::uint64_t index, std::uint8_t* buf, std::uint64_t size) const { - return this->get(index, (std::byte*) buf, size); - } - - /// \brief Get a value of type std::uint8_t from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline std::uint8_t get(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type char from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline char get_char(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type std::int16_t from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline std::int16_t get_short(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type std::uint16_t from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline std::uint16_t get_ushort(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type std::int32_t from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline std::int32_t get_int(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type std::uint32_t from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline std::uint32_t get_uint(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type std::int64_t from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline std::int64_t get_long(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type std::uint64_t from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline std::uint64_t get_ulong(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type float from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline float get_float(std::uint64_t index) const { - return _get_t(index); - } - - /// \brief Get a value of type double from the buffer. - /// \param index The index at which to start reading. - /// \return The value just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline double get_double(std::uint64_t index) const { - return _get_t(index); - } + [[nodiscard]] PHOENIX_API 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]] std::string get_string(std::uint64_t size); + [[nodiscard]] PHOENIX_API std::string get_string(std::uint64_t size); - /// \brief Get a string of the given size from the buffer. - /// \param index The index at which to start. - /// \param size The number of characters to read. - /// \return The string just read. + /// \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. - [[nodiscard]] std::string get_string(std::uint64_t index, std::uint64_t size) const; + /// \see isspace + [[nodiscard]] PHOENIX_API 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]] std::string get_line(bool skip_whitespace = true); + [[nodiscard]] PHOENIX_API 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. @@ -542,143 +407,32 @@ namespace phoenix { /// \return The line just read. /// \throws buffer_underflow if the string can't be read. /// \see isspace - [[nodiscard]] std::string get_line_escaped(bool skip_whitespace = true); - - /// \brief Get a line from the buffer. - /// \param index The index at which to start. - /// \return The line just read - /// \throws buffer_underflow if the string can't be read. - /// \see isspace - [[nodiscard]] std::string get_line_at(std::uint64_t index) const; + [[nodiscard]] PHOENIX_API 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]] inline glm::vec2 get_vec2() { - float content[2]; - this->get((std::byte*) content, sizeof(content)); - return {content[0], content[1]}; - } - - /// \brief Get a 2D-vector from the buffer. - /// \param index The index at which to start. - /// \return The vector just read. - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline glm::vec2 get_vec2(std::uint64_t index) const { - return {get_float(index), get_float(index + sizeof(float))}; - } + [[nodiscard]] PHOENIX_API 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]] inline glm::vec3 get_vec3() { - float content[3]; - this->get((std::byte*) content, sizeof(content)); - return glm::vec3 {content[0], content[1], content[2]}; - } + [[nodiscard]] PHOENIX_API 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]] inline glm::mat3x3 get_mat3x3() { - float content[3 * 3]; - this->get((std::byte*) content, sizeof(content)); - return glm::mat3x3 { - content[0], - content[1], - content[2], - content[3], - content[4], - content[5], - content[6], - content[7], - content[8], - }; - } + [[nodiscard]] PHOENIX_API 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. - inline glm::mat4x4 get_mat4x4() { - float content[4 * 4]; - this->get((std::byte*) content, sizeof(content)); - return glm::mat4x4 { - content[0], - content[1], - content[2], - content[3], - content[4], - content[5], - content[6], - content[7], - content[8], - content[9], - content[10], - content[11], - content[12], - content[13], - content[14], - content[15], - }; - } - - /// \brief Get a 3D-vector from the buffer. - /// \param index The index at which to start. - /// \return The vector just read - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline glm::vec3 get_vec3(std::uint64_t index) const { - return {get_float(index), get_float(index + sizeof(float)), get_float(index + sizeof(float) * 2)}; - } + [[nodiscard]] PHOENIX_API 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]] inline glm::vec4 get_vec4() { - float content[4]; - this->get((std::byte*) content, sizeof(content)); - return glm::vec4 {content[0], content[1], content[2], content[3]}; - } - - /// \brief Get a 4D-vector from the buffer. - /// \param index The index at which to start. - /// \return The vector just read - /// \throws buffer_underflow if the value can't be read. - [[nodiscard]] inline glm::vec4 get_vec4(std::uint64_t index) const { - return {get_float(index), - get_float(index + sizeof(float)), - get_float(index + sizeof(float) * 2), - get_float(index + sizeof(float) * 3)}; - } - - /// \brief Finds and returns the relative index of the first mismatch between this buffer and the given buffer - /// \param other The byte buffer to be tested for a mismatch with this buffer - /// \return The relative index of the first mismatch between this and the given buffer, otherwise -1 if no - /// mismatch. - [[nodiscard]] std::int64_t mismatch(const buffer& other) const; - - /// \brief Finds and returns the relative index of the first mismatch between this buffer and the given callback - /// \param other A function to be called for each char. If it return `true` it is considered a mismatch. - /// \return The relative index of the first mismatch between this and the given buffer, otherwise -1 if no - /// mismatch. - [[nodiscard]] std::int64_t mismatch(const std::function& each) const; - - /// \brief Finds and returns the relative index of the first mismatch between this buffer and the - /// given buffer at the given index in this buffer - /// \param index The index in this buffer to start comparing at. - /// \param other The byte buffer to be tested for a mismatch with this buffer - /// \return The relative index of the first mismatch between this and the given buffer, otherwise -1 if no - /// mismatch. - /// \throw buffer_underflow if \p index > #limit - [[nodiscard]] std::int64_t mismatch(std::uint64_t index, const buffer& other) const; - - /// \brief Finds and returns the relative index of the first mismatch between this buffer and - /// the given callback at the given index in this buffer - /// \param index The index in this buffer to start comparing at. - /// \param other A function to be called for each char. If it return `true` it is considered a mismatch. - /// \return The relative index of the first mismatch between this and the given buffer, otherwise -1 if no - /// mismatch. - /// \throw buffer_underflow if \p index > #limit - [[nodiscard]] std::int64_t mismatch(std::uint64_t index, const std::function& each) const; + [[nodiscard]] PHOENIX_API glm::vec4 get_vec4(); /// \brief Put bytes from buf into the buffer and advance the position accordingly. /// \param buf The data to write. @@ -686,7 +440,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. - void put(const std::byte* buf, std::uint64_t size); + PHOENIX_API 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. @@ -694,113 +448,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. - inline void put(const std::uint8_t* buf, std::uint64_t size) { - this->put((const std::byte*) buf, size); - } + PHOENIX_API 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. - inline void put(std::uint8_t value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_char(char value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_short(std::int16_t value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_ushort(std::uint16_t value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_int(std::int32_t value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_uint(std::uint32_t value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_long(std::int64_t value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_ulong(std::uint64_t value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_float(float value) { - _put_t(value); - } + PHOENIX_API 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. - inline void put_double(double value) { - _put_t(value); - } + PHOENIX_API 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. - void put_string(std::string_view str); + PHOENIX_API 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. - void put_line(std::string_view str); + PHOENIX_API void put_line(std::string_view str); /// \brief Allocates a new buffer with the given size. /// @@ -809,7 +541,7 @@ namespace phoenix { /// /// \param size The number of bytes to allocate. /// \return The newly allocated buffer. - static buffer allocate(std::uint64_t size); + PHOENIX_API static buffer allocate(std::uint64_t size); /// \brief Creates a new buffer from the given vector. /// @@ -819,7 +551,7 @@ 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. - static buffer of(std::vector&& buf, bool readonly = true); + PHOENIX_API static buffer of(std::vector&& buf, bool readonly = true); /// \brief Opens the given file as a direct buffer. /// @@ -828,7 +560,7 @@ 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. - static buffer mmap(const std::filesystem::path& path, bool readonly = true); + PHOENIX_API static buffer mmap(const std::filesystem::path& path, bool readonly = true); /// \brief Opens the given file as an indirect buffer. /// @@ -840,15 +572,15 @@ 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. - static buffer read(const std::filesystem::path& path, bool readonly = true); + PHOENIX_API static buffer read(const std::filesystem::path& path, bool readonly = true); /// \brief Returns a duplicate of the empty buffer. /// \return The empty buffer. - static buffer empty(); + PHOENIX_API static buffer empty(); private: static std::unique_ptr _m_empty; - friend bool operator==(const buffer&, const buffer&); + PHOENIX_API friend bool operator==(const buffer&, const buffer&); std::shared_ptr _m_backing; std::uint64_t _m_backing_begin, _m_backing_end; @@ -857,4 +589,6 @@ namespace phoenix { std::optional _m_mark; }; + + PHOENIX_API bool operator==(const buffer&, const buffer&); } // namespace phoenix diff --git a/include/phoenix/ext/daedalus_classes.hh b/include/phoenix/ext/daedalus_classes.hh index 36c16433..c2039283 100644 --- a/include/phoenix/ext/daedalus_classes.hh +++ b/include/phoenix/ext/daedalus_classes.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "../Api.hh" #include #define var @@ -51,7 +52,7 @@ namespace phoenix { var string blood_texture[count]; var int32_t turn_speed[count]; - static void register_(script& s) { + 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); @@ -174,7 +175,7 @@ namespace phoenix { var int32_t bodystate_interruptable_override; var int32_t no_focus; - static void register_(script& s) { + 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); @@ -229,7 +230,7 @@ namespace phoenix { var func obsolete; var func running; - static void register_(script& s) { + 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); @@ -310,7 +311,7 @@ namespace phoenix { var int32_t inv_rot_z; var int32_t inv_animate; - static void register_(script& s) { + 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); @@ -383,7 +384,7 @@ namespace phoenix { var float mob_elevup; var int32_t mob_prio; - static void register_(script& s) { + 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); @@ -423,15 +424,15 @@ namespace phoenix { std::vector choices {}; - void add_choice(const c_info_choice& ch) { + PHOENIX_API void add_choice(const c_info_choice& ch) { choices.insert(choices.begin(), ch); } - void remove_choice(std::size_t index) { + PHOENIX_API void remove_choice(std::size_t index) { choices.erase(choices.begin() + static_cast(index)); } - static void register_(script& s) { + 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); @@ -452,7 +453,7 @@ namespace phoenix { var int32_t requested_amount; var func reaction; - static void register_(script& s) { + 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); @@ -477,7 +478,7 @@ namespace phoenix { var int32_t target_collect_azi; var int32_t target_collect_elev; - static void register_(script& s) { + 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); @@ -815,7 +816,7 @@ namespace phoenix { } \ } while (false) - static void register_(script& s) { + 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); @@ -1163,7 +1164,7 @@ namespace phoenix { var int32_t default_outgame; var int32_t default_ingame; - static void register_(script& s) { + 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); @@ -1265,7 +1266,7 @@ namespace phoenix { var string hide_if_option_set; var int32_t hide_on_value; - static void register_(script& s) { + 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); @@ -1324,7 +1325,7 @@ namespace phoenix { var int32_t rotate; var int32_t collision; - static void register_(script& s) { + 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); @@ -1372,7 +1373,7 @@ namespace phoenix { var int32_t num_channels; var int32_t reverb_buffer_size; - static void register_(script& s) { + 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); @@ -1391,7 +1392,7 @@ namespace phoenix { var music_transition_type transtype; var music_transition_subtype transsubtype; - static void register_(script& s) { + 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); @@ -1408,7 +1409,7 @@ namespace phoenix { var float vol; var int32_t transsubtype; - static void register_(script& s) { + 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); @@ -1475,7 +1476,7 @@ namespace phoenix { var string time_start_end_s; var int32_t m_bis_ambient_pfx; - static void register_(script& s) { + 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); @@ -1590,7 +1591,7 @@ namespace phoenix { var float secs_per_damage; var string em_fx_coll_dyn_perc_s; - static void register_(script& 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); @@ -1677,7 +1678,7 @@ namespace phoenix { var int32_t em_check_collision; var float em_fx_lifespan; - static void register_(script& s) { + 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); @@ -1744,7 +1745,7 @@ namespace phoenix { var c_fight_ai_move move[move_count]; - static void register_(script& s) { + PHOENIX_API static void register_(script& s) { s.register_member("C_FIGHTAI.MOVE", &c_fight_ai::move); } }; @@ -1760,7 +1761,7 @@ namespace phoenix { var float reverb_level; var string pfx_name; - static void register_(script& s) { + 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); @@ -1781,7 +1782,7 @@ namespace phoenix { var int32_t num_sfx_channels; var string used_3d_provider_name; - static void register_(script& s) { + 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); @@ -1791,7 +1792,7 @@ namespace phoenix { } }; - void register_all_script_classes(script& s); + PHOENIX_API void register_all_script_classes(script& s); } // namespace phoenix #undef var diff --git a/include/phoenix/ext/dds_convert.hh b/include/phoenix/ext/dds_convert.hh index 7b487b7b..820327a4 100644 --- a/include/phoenix/ext/dds_convert.hh +++ b/include/phoenix/ext/dds_convert.hh @@ -1,12 +1,12 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once - +#include "../Api.hh" #include namespace phoenix { /// \brief Converts a texture to the DDS format. /// \param tex The texture to convert. /// \return A buffer containing the DDS file. - [[nodiscard]] buffer texture_to_dds(const texture& tex); + [[nodiscard]] PHOENIX_API buffer texture_to_dds(const texture& tex); } // namespace phoenix diff --git a/include/phoenix/font.hh b/include/phoenix/font.hh index 7a6d94c2..941ca5e0 100644 --- a/include/phoenix/font.hh +++ b/include/phoenix/font.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -19,7 +20,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]] inline bool operator==(const glyph& g) const noexcept { + [[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]; } }; @@ -40,7 +41,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. - font(std::string name, std::uint32_t height, std::vector glyphs); + PHOENIX_API font(std::string name, std::uint32_t height, std::vector glyphs); /// \brief Parses a font from the data in the given buffer. /// @@ -56,14 +57,14 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static font parse(buffer& buf); + [[nodiscard]] PHOENIX_API static font parse(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]] inline static font parse(buffer&& in) { + [[nodiscard]] PHOENIX_API inline static font parse(buffer&& in) { return font::parse(in); } diff --git a/include/phoenix/material.hh b/include/phoenix/material.hh index 5064b30d..7c91b319 100644 --- a/include/phoenix/material.hh +++ b/include/phoenix/material.hh @@ -77,7 +77,7 @@ namespace phoenix { /// \return The parsed material object. /// \throws parser_error if parsing fails. /// \see #parse(archive_reader&&) for an owning version this function. - [[nodiscard]] static material parse(archive_reader& ctx); + [[nodiscard]] PHOENIX_API static material parse(archive_reader& ctx); public: std::string name; diff --git a/include/phoenix/math.hh b/include/phoenix/math.hh index fa92a0a0..4ffc9a24 100644 --- a/include/phoenix/math.hh +++ b/include/phoenix/math.hh @@ -1,13 +1,13 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once -#include - #include -#include +#include namespace phoenix { + class buffer; + /// \brief Represents a axis-aligned bounding box (AABB) struct bounding_box { /// \brief The coordinates of the minimum corner of the bounding box. @@ -19,12 +19,7 @@ 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. - static bounding_box parse(buffer& in) { - bounding_box bbox {}; - bbox.min = in.get_vec3(); - bbox.max = in.get_vec3(); - return bbox; - } + PHOENIX_API static bounding_box parse(buffer& in); }; /// \brief Represents an oriented bounding box. @@ -42,64 +37,11 @@ namespace phoenix { /// \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]] bounding_box as_bbox() const { - const float sign[8][3] = {{-1, -1, -1}, - {-1, -1, +1}, - {-1, +1, -1}, - {-1, +1, +1}, - {+1, -1, -1}, - {+1, -1, +1}, - {+1, +1, -1}, - {+1, +1, +1}}; - - bounding_box box {}; - box.min = {std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::max()}; - - box.max = {std::numeric_limits::min(), - std::numeric_limits::min(), - std::numeric_limits::min()}; - - for (auto i : sign) { - auto point = center; - auto axis0 = axes[0] * half_width.x * i[0]; - auto axis1 = axes[1] * half_width.y * i[1]; - auto axis2 = axes[2] * half_width.z * i[2]; - - point.x += axis0.x + axis1.x + axis2.x; - point.y += axis0.y + axis1.y + axis2.y; - point.z += axis0.z + axis1.z + axis2.z; - - box.min.x = std::min(box.min.x, point.x); - box.min.y = std::min(box.min.y, point.y); - box.min.z = std::min(box.min.z, point.z); - - box.max.x = std::max(box.max.x, point.x); - box.max.y = std::max(box.max.y, point.y); - box.max.z = std::max(box.max.z, point.z); - } - - return box; - } + [[nodiscard]] PHOENIX_API bounding_box 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]] static 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)); - } - - return bbox; - } + [[nodiscard]] PHOENIX_API static obb parse(buffer& in); }; } // namespace phoenix diff --git a/include/phoenix/mesh.hh b/include/phoenix/mesh.hh index 8c9fab51..e03c8e36 100644 --- a/include/phoenix/mesh.hh +++ b/include/phoenix/mesh.hh @@ -44,7 +44,7 @@ namespace phoenix { uint8_t is_lod : 1; uint8_t normal_axis : 2; - bool operator==(const polygon_flags& b) const; + PHOENIX_API bool operator==(const polygon_flags& b) const; }; /// \brief List of data indices for polygons of meshes. @@ -86,7 +86,8 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&, const std::vector&) - [[nodiscard]] static mesh parse(buffer& buf, const std::unordered_set& include_polygons = {}); + [[nodiscard]] PHOENIX_API static mesh parse(buffer& buf, + const std::unordered_set& include_polygons = {}); /// \brief Parses a mesh from the data in the given buffer. /// @@ -100,8 +101,8 @@ namespace phoenix { /// \return The parsed mesh object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&, const std::vector&) - [[nodiscard]] inline static mesh parse(buffer&& buf, - const std::unordered_set& include_polygons = {}) { + [[nodiscard]] PHOENIX_API inline static mesh + parse(buffer&& buf, const std::unordered_set& include_polygons = {}) { return mesh::parse(buf, include_polygons); } diff --git a/include/phoenix/messages.hh b/include/phoenix/messages.hh index 86f5ae9e..37d3837e 100644 --- a/include/phoenix/messages.hh +++ b/include/phoenix/messages.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -53,21 +54,21 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static messages parse(buffer& path); + [[nodiscard]] PHOENIX_API static messages parse(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]] inline static messages parse(buffer&& path) { + [[nodiscard]] PHOENIX_API inline static messages parse(buffer&& path) { return messages::parse(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. - const message_block* block_by_name(std::string_view name) const; + [[nodiscard]] PHOENIX_API const message_block* block_by_name(std::string_view name) const; public: /// \brief A list of all message blocks in the database. diff --git a/include/phoenix/model.hh b/include/phoenix/model.hh index dc85a789..3e4a8e68 100644 --- a/include/phoenix/model.hh +++ b/include/phoenix/model.hh @@ -21,14 +21,14 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static model parse(buffer& buf); + [[nodiscard]] PHOENIX_API static model parse(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]] inline static model parse(buffer&& buf) { + [[nodiscard]] PHOENIX_API inline static model parse(buffer&& buf) { return model::parse(buf); } diff --git a/include/phoenix/model_hierarchy.hh b/include/phoenix/model_hierarchy.hh index 389dcc3e..ea216746 100644 --- a/include/phoenix/model_hierarchy.hh +++ b/include/phoenix/model_hierarchy.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -40,7 +41,7 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static model_hierarchy parse(buffer& in); + [[nodiscard]] PHOENIX_API static model_hierarchy parse(buffer& in); /// \brief Parses a model hierarchy from the data in the given buffer. /// @@ -51,7 +52,7 @@ namespace phoenix { /// \return The parsed model hierarchy object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] inline static model_hierarchy parse(buffer&& in) { + [[nodiscard]] PHOENIX_API inline static model_hierarchy parse(buffer&& in) { return model_hierarchy::parse(in); } @@ -67,5 +68,8 @@ namespace phoenix { /// \brief The translation of the root node of this hierarchy. glm::vec3 root_translation {}; + + /// \brief The checksum of this hierarchy. + std::uint32_t checksum; }; } // namespace phoenix diff --git a/include/phoenix/model_mesh.hh b/include/phoenix/model_mesh.hh index 5371a587..12cf26b1 100644 --- a/include/phoenix/model_mesh.hh +++ b/include/phoenix/model_mesh.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -25,14 +26,14 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static model_mesh parse(buffer& buf); + [[nodiscard]] PHOENIX_API static model_mesh parse(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]] inline static model_mesh parse(buffer&& buf) { + [[nodiscard]] PHOENIX_API inline static model_mesh parse(buffer&& buf) { return model_mesh::parse(buf); } @@ -42,5 +43,8 @@ namespace phoenix { /// \brief A map of attachment names to attachment meshes of this model mesh. std::unordered_map attachments {}; + + /// \brief The checksum of the model hierarchy this model was made for. + std::uint32_t checksum; }; } // namespace phoenix diff --git a/include/phoenix/model_script.hh b/include/phoenix/model_script.hh index 6c3682d5..9c7d3453 100644 --- a/include/phoenix/model_script.hh +++ b/include/phoenix/model_script.hh @@ -1,9 +1,15 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2023 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include namespace phoenix { + struct syntax_error : public phoenix::parser_error { + public: + syntax_error(std::string&& location, std::string&& msg); + }; + namespace mds { enum class event_tag_type { unknown, @@ -199,7 +205,7 @@ namespace phoenix { std::int32_t last_frame; }; - animation_flags animation_flags_from_string(std::string_view str); + PHOENIX_INTERNAL animation_flags animation_flags_from_string(std::string_view str); } // namespace mds /// \brief Represents a *ZenGin* model script. @@ -216,14 +222,14 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static model_script parse(buffer& buf); + [[nodiscard]] PHOENIX_API static model_script parse(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. /// \see #parse(buffer&) - [[nodiscard]] inline static model_script parse(buffer&& buf) { + [[nodiscard]] PHOENIX_API inline static model_script parse(buffer&& buf) { return model_script::parse(buf); } @@ -236,7 +242,8 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \deprecated model_script::parse can now handle both binary and text file types. /// \see #parse_binary(buffer&&) - [[nodiscard, deprecated("use model_script::parse()")]] static model_script parse_binary(buffer& buf); + [[nodiscard]] PHOENIX_DEPRECATED("use model_script::parse()") PHOENIX_API static model_script + parse_binary(buffer& buf); // \brief Parses a compiled model script from the data in the given buffer. /// \param[in] buf The buffer to read from (by rvalue-reference). @@ -244,7 +251,8 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \deprecated model_script::parse can now handle both binary and text file types. /// \see #parse_binary(buffer&) - [[nodiscard, deprecated("use model_script::parse()")]] inline static model_script parse_binary(buffer&& buf) { + [[nodiscard]] PHOENIX_DEPRECATED("use model_script::parse()") PHOENIX_API inline static model_script + parse_binary(buffer&& buf) { return model_script::parse(buf); } diff --git a/include/phoenix/morph_mesh.hh b/include/phoenix/morph_mesh.hh index 04d3c59d..4cf9f2d7 100644 --- a/include/phoenix/morph_mesh.hh +++ b/include/phoenix/morph_mesh.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include #include @@ -54,14 +55,14 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static morph_mesh parse(buffer& buf); + [[nodiscard]] PHOENIX_API static morph_mesh parse(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]] inline static morph_mesh parse(buffer&& buf) { + [[nodiscard]] PHOENIX_API inline static morph_mesh parse(buffer&& buf) { return morph_mesh::parse(buf); } diff --git a/include/phoenix/phoenix.hh b/include/phoenix/phoenix.hh index a9c3f9b4..7f3c4acf 100644 --- a/include/phoenix/phoenix.hh +++ b/include/phoenix/phoenix.hh @@ -1,6 +1,8 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" + #include #include #include @@ -10,27 +12,27 @@ #include #if PHOENIX_LOG_LEVEL > 0 -#define PX_LOGE(...) phoenix::logging::log(phoenix::logging::level::error, __VA_ARGS__) + #define PX_LOGE(...) phoenix::logging::log(phoenix::logging::level::error, __VA_ARGS__) #else -#define PX_LOGE(...) + #define PX_LOGE(...) #endif #if PHOENIX_LOG_LEVEL > 1 -#define PX_LOGW(...) phoenix::logging::log(phoenix::logging::level::warn, __VA_ARGS__) + #define PX_LOGW(...) phoenix::logging::log(phoenix::logging::level::warn, __VA_ARGS__) #else -#define PX_LOGW(...) + #define PX_LOGW(...) #endif #if PHOENIX_LOG_LEVEL > 2 -#define PX_LOGI(...) phoenix::logging::log(phoenix::logging::level::info, __VA_ARGS__) + #define PX_LOGI(...) phoenix::logging::log(phoenix::logging::level::info, __VA_ARGS__) #else -#define PX_LOGI(...) + #define PX_LOGI(...) #endif #if PHOENIX_LOG_LEVEL > 3 -#define PX_LOGD(...) phoenix::logging::log(phoenix::logging::level::debug, __VA_ARGS__) + #define PX_LOGD(...) phoenix::logging::log(phoenix::logging::level::debug, __VA_ARGS__) #else -#define PX_LOGD(...) + #define PX_LOGD(...) #endif namespace phoenix { @@ -50,7 +52,7 @@ namespace phoenix { /// \param a A string. /// \param b Another string. /// \return ``true`` if both strings are equal when ignoring case. - bool iequals(std::string_view a, std::string_view b); + PHOENIX_API bool iequals(std::string_view a, std::string_view b); /// \brief Tests whether \p a is lexicographically less than \p b. /// @@ -59,14 +61,14 @@ namespace phoenix { /// \param a A string. /// \param b Another string. /// \return ``true`` if \p a is lexicographically less than \p b. - bool icompare(std::string_view a, std::string_view 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. - static date parse(buffer& buf); + PHOENIX_API static date parse(buffer& buf); std::uint32_t year; std::uint16_t month; @@ -83,10 +85,10 @@ namespace phoenix { /// \brief Supply a custom logger callback to be used for log output from phoenix. /// \param callback The callback to use. - static void use_logger(std::function&& callback); + PHOENIX_API static void use_logger(std::function&& callback); /// \brief Use the default logger callback for phoenix. - static void use_default_logger(); + PHOENIX_API static void use_default_logger(); /// \brief Send a logging event to the underlying log callback. /// \param lvl The level of the log message. @@ -116,9 +118,9 @@ namespace phoenix { /// \brief Base class for all exceptions. class error : public std::exception { public: - explicit error(std::string&& message); + PHOENIX_API explicit error(std::string&& message); - [[nodiscard]] inline const char* what() const noexcept override { + [[nodiscard]] PHOENIX_API inline const char* what() const noexcept override { return message.c_str(); } @@ -129,10 +131,12 @@ namespace phoenix { /// \brief An error representing a parsing failure of any kind. class parser_error : public error { public: - explicit parser_error(std::string&& resource_type); - explicit parser_error(std::string&& resource_type, std::string&& context); - explicit parser_error(std::string&& resource_type, const std::exception& cause); - explicit parser_error(std::string&& resource_type, const std::exception& cause, std::string&& context); + 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; diff --git a/include/phoenix/proto_mesh.hh b/include/phoenix/proto_mesh.hh index a27b8755..12703825 100644 --- a/include/phoenix/proto_mesh.hh +++ b/include/phoenix/proto_mesh.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include #include @@ -69,7 +70,7 @@ namespace phoenix { /// \param in The reader to read from /// \param map A a section map for the sub-mesh. /// \return The sub-mesh read. - [[nodiscard]] static sub_mesh parse(buffer& in, const sub_mesh_section& map); + [[nodiscard]] PHOENIX_INTERNAL static sub_mesh parse(buffer& in, const sub_mesh_section& map); }; /// \brief Represents a *ZenGin* proto mesh. @@ -86,14 +87,14 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static proto_mesh parse(buffer& in); + [[nodiscard]] PHOENIX_API static proto_mesh parse(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]] static proto_mesh parse(buffer&& in) { + [[nodiscard]] PHOENIX_API static proto_mesh parse(buffer&& in) { return proto_mesh::parse(in); } @@ -108,7 +109,7 @@ namespace phoenix { /// 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]] static proto_mesh parse_from_section(buffer& in); + [[nodiscard]] PHOENIX_INTERNAL static proto_mesh parse_from_section(buffer& in); public: /// \brief The vertex positions associated with the mesh. diff --git a/include/phoenix/save_game.hh b/include/phoenix/save_game.hh index d7b32dbb..4ac98dcb 100644 --- a/include/phoenix/save_game.hh +++ b/include/phoenix/save_game.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -34,7 +35,7 @@ namespace phoenix::unstable { /// \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. - static save_info parse(buffer&& buf); + PHOENIX_API static save_info parse(buffer&& buf); }; /// \brief The section log entries are in. @@ -85,7 +86,7 @@ namespace phoenix::unstable { /// \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. - static script_state parse(buffer&& buf, bool g2); + PHOENIX_INTERNAL static script_state parse(buffer&& buf, bool g2); }; struct save_game { @@ -94,7 +95,7 @@ namespace phoenix::unstable { /// \param path The path of the save-game folder. /// \return The parsed save-game. /// \throws parser_error if parsing fails. - static save_game parse(const std::filesystem::path& path); + PHOENIX_API static save_game parse(const std::filesystem::path& path); /// \brief Opens the saved world file with the given world name as a buffer and returns it. /// @@ -103,7 +104,7 @@ 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. - std::optional open_world_save(std::string_view world_name) const; + PHOENIX_API std::optional open_world_save(std::string_view world_name) const; public: /// \brief Contains metadata about the save-game, like its name and version numbers. @@ -125,5 +126,4 @@ namespace phoenix::unstable { private: std::filesystem::path _m_root_path; }; - -} // namespace phoenix::unstable \ No newline at end of file +} // namespace phoenix::unstable diff --git a/include/phoenix/script.hh b/include/phoenix/script.hh index d5e4f582..b28608b1 100644 --- a/include/phoenix/script.hh +++ b/include/phoenix/script.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -205,10 +206,10 @@ namespace phoenix { /// Every class defined in C++ that can be used as an instance has to inherit from this class. class instance { public: - virtual ~instance() = default; + PHOENIX_API virtual ~instance() = default; /// \return The index of the symbol this instance is bound to. - [[nodiscard]] inline uint32_t symbol_index() const { + [[nodiscard]] PHOENIX_API inline uint32_t symbol_index() const { return _m_symbol_index; } @@ -232,7 +233,7 @@ namespace phoenix { /// \brief An exception thrown if the symbol with a given name could not be found. struct symbol_not_found : public script_error { public: - explicit symbol_not_found(std::string&& name); + PHOENIX_API explicit symbol_not_found(std::string&& name); public: std::string name; @@ -241,7 +242,7 @@ namespace phoenix { /// \brief An exception thrown if registering a class member was unsuccessful. struct member_registration_error : public script_error { public: - explicit member_registration_error(const symbol* sym, std::string&& message); + PHOENIX_API explicit member_registration_error(const symbol* sym, std::string&& message); public: /// \brief The symbol being registered. @@ -251,7 +252,7 @@ namespace phoenix { /// \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 { public: - explicit invalid_registration_datatype(const symbol* sym, std::string&& given); + PHOENIX_API explicit invalid_registration_datatype(const symbol* sym, std::string&& given); public: std::string given; @@ -265,7 +266,7 @@ namespace phoenix { /// \brief An exception thrown when the type of a symbol does not match the type expected. struct illegal_type_access final : public illegal_access { public: - illegal_type_access(const symbol* sym, datatype expected); + PHOENIX_INTERNAL illegal_type_access(const symbol* sym, datatype expected); public: /// \brief The symbol being accessed. @@ -278,7 +279,7 @@ namespace phoenix { /// \brief An exception thrown when an out-of-bounds index is accessed. struct illegal_index_access final : public illegal_access { public: - illegal_index_access(const symbol* sym, std::uint8_t index); + PHOENIX_INTERNAL illegal_index_access(const symbol* sym, std::uint8_t index); public: /// \brief The symbol being accessed. @@ -291,7 +292,7 @@ namespace phoenix { /// \brief An exception thrown when a constant symbol is accessed as mutable struct illegal_const_access final : public illegal_access { public: - explicit illegal_const_access(const symbol* sym); + PHOENIX_INTERNAL explicit illegal_const_access(const symbol* sym); public: /// \brief The symbol being accessed. @@ -301,7 +302,7 @@ namespace phoenix { /// \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 { public: - illegal_instance_access(const symbol* sym, std::uint32_t expected_parent); + PHOENIX_INTERNAL illegal_instance_access(const symbol* sym, std::uint32_t expected_parent); public: /// \brief The symbol being accessed. @@ -314,7 +315,7 @@ namespace phoenix { /// \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 { public: - explicit unbound_member_access(const symbol* sym); + PHOENIX_API explicit unbound_member_access(const symbol* sym); public: /// \brief The symbol being accessed. @@ -324,7 +325,7 @@ namespace phoenix { /// \brief An exception thrown if a member symbol is being access without a context set. struct no_context final : public illegal_access { public: - explicit no_context(const symbol* sym); + PHOENIX_INTERNAL explicit no_context(const symbol* sym); public: /// \brief The symbol being accessed. @@ -334,7 +335,7 @@ namespace phoenix { /// \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 { public: - illegal_context_type(const symbol* sym, const std::type_info& context_type); + PHOENIX_API illegal_context_type(const symbol* sym, const std::type_info& context_type); public: /// \brief The symbol being accessed. @@ -350,7 +351,7 @@ namespace phoenix { /// \brief Parses a symbol from the given reader. /// \param[in,out] in The reader to read the symbol from. /// \return The symbol parsed. - [[nodiscard]] static symbol parse(buffer& in); + [[nodiscard]] PHOENIX_API static symbol parse(buffer& 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. @@ -361,8 +362,8 @@ namespace phoenix { /// \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]] const std::string& get_string(std::size_t index = 0, - const std::shared_ptr& context = nullptr) const; + [[nodiscard]] PHOENIX_API 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. @@ -373,7 +374,8 @@ namespace phoenix { /// \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]] float get_float(std::size_t index = 0, const std::shared_ptr& context = nullptr) const; + [[nodiscard]] PHOENIX_API 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. @@ -384,13 +386,13 @@ namespace phoenix { /// \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]] std::int32_t get_int(std::size_t index = 0, - const std::shared_ptr& context = nullptr) const; + [[nodiscard]] PHOENIX_API 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]] const std::shared_ptr& get_instance(); + [[nodiscard]] PHOENIX_API const std::shared_ptr& get_instance(); // -=-= Value setters =-=- // @@ -398,164 +400,164 @@ 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_const_access if this symbol #is_const /// \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. - void + PHOENIX_API 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_const_access if this symbol #is_const /// \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. - void set_float(float value, std::size_t index = 0, const std::shared_ptr& context = nullptr); + PHOENIX_API 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_const_access if this symbol #is_const /// \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. - void set_int(std::int32_t value, std::size_t index = 0, const std::shared_ptr& context = nullptr); + PHOENIX_API 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. - void set_instance(const std::shared_ptr& inst); + PHOENIX_API 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 - typename std::enable_if, bool>::type inline is_instance_of() { // clang-format on + PHOENIX_API typename std::enable_if, + bool>::type inline is_instance_of() { // clang-format on return this->type() == datatype::instance && this->get_instance() != nullptr && this->get_instance()->_m_type == &typeid(T); } /// \brief Tests whether the symbol is a constant. /// \return `true` if the symbol is a constant, `false` if not. - [[nodiscard]] inline bool is_const() const noexcept { + [[nodiscard]] PHOENIX_API inline bool is_const() const noexcept { return (_m_flags & symbol_flag::const_) != 0; } /// \brief Tests whether the symbol is a member variable. /// \return `true` if the symbol is a member, `false` if not. - [[nodiscard]] inline bool is_member() const noexcept { + [[nodiscard]] PHOENIX_API inline bool is_member() const noexcept { return (_m_flags & symbol_flag::member) != 0; } /// \brief Tests whether the symbol is an extern symbol. /// \return `true` if the symbol is an extern symbol, `false` if not. - [[nodiscard]] inline bool is_external() const noexcept { + [[nodiscard]] PHOENIX_API inline bool is_external() const noexcept { return (_m_flags & symbol_flag::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]] inline bool is_merged() const noexcept { + [[nodiscard]] PHOENIX_API inline bool is_merged() const noexcept { return (_m_flags & symbol_flag::merged) != 0; } /// \brief brief Tests whether the symbol is a compiler-generated symbol /// \return return `true` if the symbol is generated, `false` if not. - [[nodiscard]] inline bool is_generated() const noexcept { + [[nodiscard]] PHOENIX_API 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]] inline bool has_return() const noexcept { + [[nodiscard]] PHOENIX_API inline bool has_return() const noexcept { return (_m_flags & symbol_flag::return_) != 0; } /// \return The name of the symbol. - [[nodiscard]] inline const std::string& name() const noexcept { + [[nodiscard]] PHOENIX_API inline const std::string& name() const noexcept { return _m_name; } /// \return The address of the symbol. - [[nodiscard]] inline std::uint32_t address() const noexcept { + [[nodiscard]] PHOENIX_API 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]] inline std::uint32_t parent() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t parent() const noexcept { return _m_parent; } /// \return The count of values stored in the symbol. - [[nodiscard]] inline std::uint32_t count() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t count() const noexcept { return _m_count; } /// \return The type of the symbol. - [[nodiscard]] inline datatype type() const noexcept { + [[nodiscard]] PHOENIX_API inline datatype type() const noexcept { return _m_type; } /// \return The index of the symbol. - [[nodiscard]] inline std::uint32_t index() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t index() const noexcept { return _m_index; } /// \return The return type of the symbol. - [[nodiscard]] inline datatype rtype() const noexcept { + [[nodiscard]] PHOENIX_API inline datatype rtype() const noexcept { return _m_return_type; } /// \return The index of the file the symbol was in. - [[nodiscard]] inline std::uint32_t file_index() const noexcept { + [[nodiscard]] PHOENIX_API 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]] inline std::uint32_t offset_as_member() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t offset_as_member() const noexcept { return _m_member_offset; } - [[nodiscard]] inline std::uint32_t line_start() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t line_start() const noexcept { return _m_line_start; } - [[nodiscard]] inline std::uint32_t line_count() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t line_count() const noexcept { return _m_line_count; } - [[nodiscard]] inline std::uint32_t char_start() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t char_start() const noexcept { return _m_char_start; } - [[nodiscard]] inline std::uint32_t char_count() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t char_count() const noexcept { return _m_char_count; } - [[nodiscard]] inline std::uint32_t class_size() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t class_size() const noexcept { return _m_class_size; } - [[nodiscard]] inline const std::type_info& registered_to() const noexcept { + [[nodiscard]] PHOENIX_API inline const std::type_info& registered_to() const noexcept { return *_m_registered_to; }; protected: - symbol() = default; + PHOENIX_INTERNAL symbol() = default; template - 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); if (*_m_registered_to != *context->_m_type) @@ -566,7 +568,7 @@ namespace phoenix { } template - 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); if (*_m_registered_to != *context->_m_type) @@ -618,7 +620,7 @@ namespace phoenix { /// \brief Reads an instruction from a reader. /// \param[in,out] in The reader to read from /// \return The instruction read. - static instruction decode(buffer& in); + PHOENIX_INTERNAL static instruction decode(buffer& in); }; /// \brief Represents a compiled daedalus script @@ -628,12 +630,12 @@ namespace phoenix { /// \brief Parses in a compiled daedalus script. /// \param path The path of the script file. /// \return The script parsed - [[nodiscard]] static script parse(const std::string& path); + [[nodiscard]] PHOENIX_API static script 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]] static script parse(phoenix::buffer& buf); + [[nodiscard]] PHOENIX_API static script parse(phoenix::buffer& buf); /// \brief Registers a member offset /// \param name The name of the member in the script @@ -678,63 +680,64 @@ namespace phoenix { } /// \return All symbols in the script - [[nodiscard]] inline const std::vector& symbols() const noexcept { + [[nodiscard]] PHOENIX_API 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]] const symbol* find_symbol_by_index(std::uint32_t index) const; + [[nodiscard]] PHOENIX_API const symbol* 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]] std::vector find_parameters_for_function(const symbol* parent) const; + [[nodiscard]] PHOENIX_API std::vector find_parameters_for_function(const symbol* 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]] const symbol* find_symbol_by_address(std::uint32_t address) const; + [[nodiscard]] PHOENIX_API const symbol* 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]] const symbol* find_symbol_by_name(std::string_view name) const; + [[nodiscard]] PHOENIX_API const symbol* 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]] symbol* find_symbol_by_index(std::uint32_t index); + [[nodiscard]] PHOENIX_API symbol* 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]] symbol* find_symbol_by_address(std::uint32_t address); + [[nodiscard]] PHOENIX_API symbol* 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]] std::vector find_parameters_for_function(const symbol* parent); + [[nodiscard]] PHOENIX_API std::vector find_parameters_for_function(const symbol* 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]] symbol* find_symbol_by_name(std::string_view name); + [[nodiscard]] PHOENIX_API symbol* 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. - void enumerate_instances_by_class_name(std::string_view name, const std::function& callback); + PHOENIX_API 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]] instruction instruction_at(std::uint32_t address) const; + [[nodiscard]] PHOENIX_API instruction instruction_at(std::uint32_t address) const; /// \return The total size of the script. - [[nodiscard]] std::uint32_t size() const noexcept { + [[nodiscard]] PHOENIX_API std::uint32_t size() const noexcept { return _m_text.limit() & 0xFFFFFF; } @@ -742,7 +745,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. - inline const symbol* find_symbol_by_instance(const instance& inst) const { + PHOENIX_API inline const symbol* find_symbol_by_instance(const instance& inst) const { return find_symbol_by_index(inst._m_symbol_index); } @@ -750,7 +753,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. - inline symbol* find_symbol_by_instance(const instance& inst) { + PHOENIX_API inline symbol* find_symbol_by_instance(const instance& inst) { return find_symbol_by_index(inst._m_symbol_index); } @@ -759,8 +762,9 @@ namespace phoenix { /// \return The symbol associated with that instance or nullptr if the symbol is not associated /// with any instance. template - typename std::enable_if, const symbol*>::type inline find_symbol_by_instance( - const std::shared_ptr& inst) const { // clang-format on + PHOENIX_API + typename std::enable_if, const symbol*>::type inline find_symbol_by_instance( + const std::shared_ptr& inst) const { // clang-format on return find_symbol_by_index(inst->_m_symbol_index); } @@ -769,15 +773,16 @@ namespace phoenix { /// \return The symbol associated with that instance or nullptr if the symbol is not associated /// with any instance. template - typename std::enable_if, symbol*>::type inline find_symbol_by_instance( - const std::shared_ptr& inst) { + PHOENIX_API + typename std::enable_if, symbol*>::type inline find_symbol_by_instance( + const std::shared_ptr& inst) { return find_symbol_by_index(inst->_m_symbol_index); } protected: - script() = default; - script(const script& copy) = default; - script(script&& move) = default; + PHOENIX_INTERNAL script() = default; + PHOENIX_INTERNAL script(const script& copy) = default; + PHOENIX_INTERNAL script(script&& move) = default; template symbol* _check_member(std::string_view name, const std::type_info* type) { @@ -822,17 +827,7 @@ namespace phoenix { return sym; } - symbol* add_temporary_strings_symbol() { - symbol sym {}; - sym._m_name = "$PHOENIX_FAKE_STRINGS"; - sym._m_generated = true; - sym._m_type = datatype::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()); - - return &_m_symbols.emplace_back(std::move(sym)); - } + PHOENIX_API symbol* add_temporary_strings_symbol(); private: std::vector _m_symbols; diff --git a/include/phoenix/softskin_mesh.hh b/include/phoenix/softskin_mesh.hh index 8b9f6ae7..926fe5f9 100644 --- a/include/phoenix/softskin_mesh.hh +++ b/include/phoenix/softskin_mesh.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include #include @@ -28,14 +29,14 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static softskin_mesh parse(buffer& in); + [[nodiscard]] PHOENIX_API static softskin_mesh parse(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]] inline static softskin_mesh parse(buffer&& in) { + [[nodiscard]] PHOENIX_API inline static softskin_mesh parse(buffer&& in) { return softskin_mesh::parse(in); } diff --git a/include/phoenix/texture.hh b/include/phoenix/texture.hh index 6b0c387d..18d155d1 100644 --- a/include/phoenix/texture.hh +++ b/include/phoenix/texture.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -50,72 +51,73 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static texture parse(buffer& in); + [[nodiscard]] PHOENIX_API static texture parse(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]] inline static texture parse(buffer&& in) { + [[nodiscard]] PHOENIX_API inline static texture parse(buffer&& in) { return parse(in); } /// \return The format of the texture. - [[nodiscard]] inline texture_format format() const noexcept { + [[nodiscard]] PHOENIX_API inline texture_format format() const noexcept { return _m_format; } /// \return The width in pixels of the first mipmap level. - [[nodiscard]] inline std::uint32_t width() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t width() const noexcept { return _m_width; } /// \return The height in pixels of the first mipmap level. - [[nodiscard]] inline std::uint32_t height() const noexcept { + [[nodiscard]] PHOENIX_API 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]] inline std::uint32_t mipmap_width(std::uint32_t level) const noexcept { + [[nodiscard]] PHOENIX_API 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]] inline std::uint32_t mipmap_height(std::uint32_t level) const noexcept { + [[nodiscard]] PHOENIX_API 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]] inline std::uint32_t ref_width() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t ref_width() const noexcept { return _m_reference_width; } /// \return The height of the texture in-engine. - [[nodiscard]] inline std::uint32_t ref_height() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t ref_height() const noexcept { return _m_reference_height; } /// \return The number of mipmaps of the texture. - [[nodiscard]] inline std::uint32_t mipmaps() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t mipmaps() const noexcept { return _m_mipmap_count; } /// \return The average color of the texture. - [[nodiscard]] inline std::uint32_t average_color() const noexcept { + [[nodiscard]] PHOENIX_API inline std::uint32_t average_color() const noexcept { return _m_average_color; } /// \return The palette of the texture. - [[nodiscard]] inline argb* palette() const noexcept { + [[nodiscard]] PHOENIX_API inline argb* palette() const noexcept { return (argb*) _m_palette; } /// \param mipmap_level The mipmap level to get. /// \return The texture data at the given mipmap level. - [[nodiscard]] inline const std::vector& data(std::uint32_t mipmap_level = 0) const noexcept { + [[nodiscard]] PHOENIX_API inline const std::vector& + data(std::uint32_t mipmap_level = 0) const noexcept { return _m_textures.at(_m_mipmap_count - 1 - mipmap_level); } @@ -123,7 +125,7 @@ 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]] std::vector as_rgba8(std::uint32_t mipmap_level = 0) const; + [[nodiscard]] PHOENIX_API std::vector as_rgba8(std::uint32_t mipmap_level = 0) const; private: texture() = default; diff --git a/include/phoenix/vdfs.hh b/include/phoenix/vdfs.hh index ab22973c..6e089560 100644 --- a/include/phoenix/vdfs.hh +++ b/include/phoenix/vdfs.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -28,13 +29,13 @@ namespace phoenix { /// \param dos The timestamp to convert. /// \return The converted unix timestamp. /// \note Assumes the ``DOS`` timestamp is in the ``GMT`` timezone. - std::time_t dos_to_unix_time(std::uint32_t dos) noexcept; + 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. - std::uint32_t unix_time_to_dos(std::time_t tm) noexcept; + 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. /// @@ -43,7 +44,7 @@ namespace phoenix { /// phoenix::VDF_SIGNATURE_G1 and phoenix::VDF_SIGNATURE_G2. class vdfs_signature_error : public error { public: - explicit vdfs_signature_error(const std::string& signature); + PHOENIX_INTERNAL explicit vdfs_signature_error(const std::string& signature); }; /// \brief Represents the header of a VDF. @@ -52,14 +53,14 @@ namespace phoenix { /// \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.# - explicit vdf_header(std::string_view comment, std::time_t timestamp = -1); + PHOENIX_API explicit vdf_header(std::string_view comment, std::time_t timestamp = -1); - vdf_header() = default; + 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. - static vdf_header read(buffer& in); + 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); @@ -95,9 +96,9 @@ namespace phoenix { public: using is_transparent = std::true_type; - bool operator()(const vdf_entry& a, const vdf_entry& b) const; - bool operator()(const vdf_entry& a, std::string_view b) const; - bool operator()(std::string_view a, const vdf_entry& b) const; + 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. @@ -106,53 +107,54 @@ namespace phoenix { /// \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. - explicit vdf_entry(std::string_view name, std::uint32_t attributes = 0); + PHOENIX_API explicit vdf_entry(std::string_view name, std::uint32_t attributes = 0); - vdf_entry() = default; + 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. - static vdf_entry read(buffer& in, std::uint32_t catalog_offset); + 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]] const vdf_entry* find_child(std::string_view name) const; + [[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]] const vdf_entry* resolve_path(std::string_view path) const; + [[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. - [[deprecated("mutating vdf_entry children is broken!")]] vdf_entry* find_child(std::string_view name); + 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. - void merge(const vdf_entry& itm, bool override_existing = true); + PHOENIX_API void merge(const vdf_entry& itm, bool override_existing = true); /// \return A new reader for the contents of the entry. - [[nodiscard]] inline buffer open() const noexcept { + [[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]] inline bool is_file() const noexcept { + [[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]] inline bool is_directory() const noexcept { + [[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]] inline bool is_last() const noexcept { + [[nodiscard]] PHOENIX_API inline bool is_last() const noexcept { return (type & VDF_MASK_LAST) != 0; } @@ -187,37 +189,38 @@ namespace phoenix { /// \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. - explicit vdf_file(std::string_view comment, std::time_t timestamp = -1); + 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. - static vdf_file open(const std::filesystem::path& path); + 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. - static vdf_file open(phoenix::buffer& buf); + 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]] const vdf_entry* find_entry(std::string_view name) const; + [[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]] const vdf_entry* resolve_path(std::string_view path) const; + [[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. - [[deprecated("mutating vdf_entry children is broken!")]] vdf_entry* find_entry(std::string_view name); + 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. - void merge(const vdf_file& file, bool override_existing = true); + PHOENIX_API void merge(const vdf_file& file, bool override_existing = true); public: /// \brief A list of root entries in the VDF file. diff --git a/include/phoenix/vm.hh b/include/phoenix/vm.hh index fefbe7df..f3ff8443 100644 --- a/include/phoenix/vm.hh +++ b/include/phoenix/vm.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include @@ -26,7 +27,7 @@ namespace phoenix { /// \brief An exception thrown if the definition of an external is incorrect. class illegal_external_definition : public script_error { public: - illegal_external_definition(const symbol* sym, std::string&& message); + PHOENIX_API illegal_external_definition(const symbol* sym, std::string&& message); public: /// \brief The symbol the external is being registered for. @@ -37,14 +38,14 @@ namespace phoenix { /// the return type defined in the script. class illegal_external_rtype : public illegal_external_definition { public: - illegal_external_rtype(const symbol* sym, std::string&& provided); + PHOENIX_API illegal_external_rtype(const symbol* 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 { public: - illegal_external_param(const symbol* sym, std::string&& provided, std::uint8_t i); + PHOENIX_API illegal_external_param(const symbol* sym, std::string&& provided, std::uint8_t i); }; class vm_exception : public script_error { @@ -81,6 +82,7 @@ namespace phoenix { 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 class vm : public script { @@ -89,7 +91,7 @@ namespace phoenix { /// \brief Creates a DaedalusVM instance for the given script. /// \param scr The script to load into the VM. - explicit vm(script&& scr, uint8_t flags = execution_flag::none); + PHOENIX_API explicit vm(script&& scr, uint8_t flags = execution_flag::none); /// \brief Calls a function by it's name. /// \tparam P The types for the argument values. @@ -163,6 +165,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 init_instance(std::string_view name) { return init_instance<_instance_t>(find_symbol_by_name(name)); @@ -187,6 +190,7 @@ namespace phoenix { /// \param sym The symbol to initialize. /// \return The initialized instance. template + typename std::enable_if, std::shared_ptr<_instance_t>>::type init_instance(symbol* sym) { // create the instance @@ -230,6 +234,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 allocate_instance(std::string_view name) { return allocate_instance<_instance_t>(find_symbol_by_name(name)); @@ -258,6 +263,7 @@ namespace phoenix { /// \param sym The symbol to initialize. /// \return The initialized instance. template + typename std::enable_if, std::shared_ptr<_instance_t>>::type allocate_instance(symbol* sym) { // create the instance @@ -286,6 +292,13 @@ namespace phoenix { // check that the parent class is registered for the given instance type auto* parent = find_symbol_by_index(sym->parent()); + + 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?)"}; + } + while (parent->type() != datatype::class_) { parent = find_symbol_by_index(parent->parent()); } @@ -303,17 +316,17 @@ namespace phoenix { sym->set_instance(instance); } - void push_int(std::int32_t value); - void push_float(float value); - void push_instance(std::shared_ptr value); - void push_reference(symbol* value, std::uint8_t index = 0); - void push_string(std::string_view value); + 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); - [[nodiscard]] std::int32_t pop_int(); - [[nodiscard]] float pop_float(); - [[nodiscard]] std::shared_ptr pop_instance(); - [[nodiscard]] const std::string& pop_string(); - [[nodiscard]] std::tuple> pop_reference(); + [[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(); /// \brief Registers a Daedalus external function. /// @@ -583,7 +596,7 @@ namespace phoenix { /// /// \param callback The function to call. The one parameter of the function is the name of the unresolved /// external. - void register_default_external(const std::function& callback); + PHOENIX_API void register_default_external(const std::function& callback); /// \brief Registers a function to be called when script execution fails. /// @@ -594,39 +607,39 @@ 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. - void register_exception_handler( + PHOENIX_API void register_exception_handler( const std::function& callback); /// \return the symbol referring to the global var C_NPC self. - inline symbol* global_self() { + PHOENIX_API inline symbol* global_self() { return _m_self_sym; } /// \return the symbol referring to the global var C_NPC other. - inline symbol* global_other() { + PHOENIX_API inline symbol* global_other() { return _m_other_sym; } /// \return the symbol referring to the global var C_NPC victim. - inline symbol* global_victim() { + PHOENIX_API inline symbol* global_victim() { return _m_victim_sym; } /// \return the symbol referring to the global var C_NPC hero. - inline symbol* global_hero() { + PHOENIX_API inline symbol* global_hero() { return _m_hero_sym; } /// \return the symbol referring to the global var C_NPC item. - inline symbol* global_item() { + PHOENIX_API inline symbol* global_item() { return _m_item_sym; } /// \brief Prints the contents of the function call stack and the VMs stack to stderr. - void print_stack_trace() const; + PHOENIX_API void print_stack_trace() const; /// \return The current program counter (or instruction index) the VM is at. - [[nodiscard]] inline uint32_t pc() const noexcept { + [[nodiscard]] PHOENIX_API inline uint32_t pc() const noexcept { return _m_pc; } @@ -637,15 +650,15 @@ namespace phoenix { /// is required to deal with it appropriately. /// /// \param sym The symbol to call. - void call(const symbol* sym); + PHOENIX_INTERNAL void call(const symbol* sym); /// \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. - bool exec(); + PHOENIX_INTERNAL bool exec(); /// \brief Validates the given address and jumps to it (sets the program counter). /// \param address The address to jump to. - void jump(std::uint32_t address); + PHOENIX_INTERNAL void jump(std::uint32_t address); /// \brief Pushes a call stack frame onto the call stack. /// @@ -653,13 +666,13 @@ namespace phoenix { /// as #pop_call is invoked. /// /// \param sym The symbol referring to the function called. - void push_call(const symbol* sym); + PHOENIX_INTERNAL void push_call(const symbol* 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. - void pop_call(); + PHOENIX_INTERNAL void pop_call(); /// \brief Checks that the type of each symbol in the given set of defined symbols matches the given type /// parameters. @@ -927,5 +940,7 @@ namespace phoenix { /// \param exc The exception being handled. /// \param instr The instruction being executed. /// \return vm_exception_strategy::continue_ - vm_exception_strategy lenient_vm_exception_handler(vm& v, const script_error& exc, const instruction& instr); + PHOENIX_API vm_exception_strategy lenient_vm_exception_handler(vm& v, + const script_error& exc, + const instruction& instr); } // namespace phoenix diff --git a/include/phoenix/vobs/camera.hh b/include/phoenix/vobs/camera.hh index 1b8fe0a9..52fca77e 100644 --- a/include/phoenix/vobs/camera.hh +++ b/include/phoenix/vobs/camera.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "../Api.hh" #include namespace phoenix { @@ -55,7 +56,7 @@ namespace phoenix { /// \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 - static std::unique_ptr parse(archive_reader& ctx, game_version version); + PHOENIX_API static std::unique_ptr parse(archive_reader& ctx, game_version version); }; /// \brief A VOb which defined the movement of the camera during a cutscene. @@ -91,7 +92,7 @@ namespace phoenix { /// \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 - static void parse(cs_camera& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(cs_camera& obj, archive_reader& ctx, game_version version); }; } // namespace vobs } // namespace phoenix \ No newline at end of file diff --git a/include/phoenix/vobs/light.hh b/include/phoenix/vobs/light.hh index f6a9d3f1..22396785 100644 --- a/include/phoenix/vobs/light.hh +++ b/include/phoenix/vobs/light.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "../Api.hh" #include namespace phoenix { @@ -45,7 +46,7 @@ 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. /// \throws parser_error if parsing fails. - static void parse(light_preset& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(light_preset& obj, archive_reader& ctx, game_version version); /// \brief Parses a light preset the given *ZenGin* archive. /// \param[in,out] ctx The archive reader to read from. @@ -53,7 +54,7 @@ namespace phoenix { /// \return The parsed light preset. /// \throws parser_error if parsing fails. /// \see vob::parse - static light_preset parse(archive_reader& in, game_version version); + PHOENIX_API static light_preset parse(archive_reader& in, game_version version); }; /// \brief A VOb which acts as a light source. @@ -65,7 +66,7 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \see vob::parse /// \see light_preset::parse - static void parse(light& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(light& obj, archive_reader& ctx, game_version version); }; } // namespace vobs } // namespace phoenix diff --git a/include/phoenix/vobs/misc.hh b/include/phoenix/vobs/misc.hh index 7a849523..8532a9c6 100644 --- a/include/phoenix/vobs/misc.hh +++ b/include/phoenix/vobs/misc.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "../Api.hh" #include namespace phoenix { @@ -40,7 +41,7 @@ namespace phoenix { /// \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 - static void parse(animate& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(animate& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb representing an in-game item. @@ -57,7 +58,7 @@ namespace phoenix { /// \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 - static void parse(item& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(item& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb representing a [lens flare](https://en.wikipedia.org/wiki/Lens_flare). @@ -70,7 +71,7 @@ namespace phoenix { /// \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 - static void parse(lens_flare& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(lens_flare& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb representing a particle system controller. @@ -85,7 +86,7 @@ namespace phoenix { /// \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 - static void parse(pfx_controller& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(pfx_controller& obj, archive_reader& ctx, game_version version); }; struct message_filter : public vob { @@ -99,7 +100,7 @@ namespace phoenix { /// \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 - static void parse(message_filter& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(message_filter& obj, archive_reader& ctx, game_version version); }; struct code_master : public vob { @@ -119,7 +120,7 @@ namespace phoenix { /// \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 - static void parse(code_master& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(code_master& obj, archive_reader& ctx, game_version version); }; struct mover_controller : public vob { @@ -133,7 +134,7 @@ namespace phoenix { /// \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 - static void parse(mover_controller& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(mover_controller& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb which represents a damage source. @@ -159,7 +160,7 @@ namespace phoenix { /// \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 - static void parse(touch_damage& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(touch_damage& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb which represents an earthquake-like effect. @@ -174,7 +175,7 @@ namespace phoenix { /// \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 - static void parse(earthquake& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(earthquake& obj, archive_reader& ctx, game_version version); }; struct npc : public vob { @@ -266,7 +267,7 @@ namespace phoenix { /// \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 - static void parse(npc& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(npc& obj, archive_reader& ctx, game_version version); }; } // namespace vobs } // namespace phoenix \ No newline at end of file diff --git a/include/phoenix/vobs/mob.hh b/include/phoenix/vobs/mob.hh index a5d5f97d..f24459af 100644 --- a/include/phoenix/vobs/mob.hh +++ b/include/phoenix/vobs/mob.hh @@ -35,7 +35,7 @@ namespace phoenix { /// \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 - static void parse(mob& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(mob& obj, archive_reader& ctx, game_version version); }; struct mob_inter : public mob { @@ -52,7 +52,7 @@ namespace phoenix { /// \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 - static void parse(mob_inter& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(mob_inter& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb representing a campfire. @@ -67,7 +67,7 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \see vob::parse /// \see mob::parse - static void parse(mob_fire& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(mob_fire& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb representing a container. @@ -88,7 +88,7 @@ namespace phoenix { /// \see vob::parse /// \see mob::parse /// \see mob_container::parse - static void parse(mob_container& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(mob_container& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb representing a door. @@ -105,7 +105,7 @@ namespace phoenix { /// \see vob::parse /// \see mob::parse /// \see mob_container::parse - static void parse(mob_door& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(mob_door& obj, archive_reader& ctx, game_version version); }; } // namespace vobs } // namespace phoenix diff --git a/include/phoenix/vobs/sound.hh b/include/phoenix/vobs/sound.hh index 4f4cc2e4..e9e5382f 100644 --- a/include/phoenix/vobs/sound.hh +++ b/include/phoenix/vobs/sound.hh @@ -47,7 +47,7 @@ namespace phoenix { /// \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 - static void parse(sound& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(sound& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb which emits a sound only during certain times of the day. @@ -62,7 +62,7 @@ namespace phoenix { /// \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 - static void parse(sound_daytime& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(sound_daytime& obj, archive_reader& ctx, game_version version); }; } // namespace vobs } // namespace phoenix \ No newline at end of file diff --git a/include/phoenix/vobs/trigger.hh b/include/phoenix/vobs/trigger.hh index d20b39a7..f3303e78 100644 --- a/include/phoenix/vobs/trigger.hh +++ b/include/phoenix/vobs/trigger.hh @@ -58,7 +58,7 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \see vob::parse /// \see trigger::parse - static void parse(trigger& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(trigger& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb which can move upon player interaction. @@ -103,7 +103,7 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \see vob::parse /// \see trigger::parse - static void parse(trigger_mover& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(trigger_mover& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb which can call multiple script function upon being triggered. @@ -131,7 +131,7 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \see vob::parse /// \see trigger::parse - static void parse(trigger_list& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(trigger_list& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb which calls a script function upon being triggered. @@ -145,7 +145,7 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \see vob::parse /// \see trigger::parse - static void parse(trigger_script& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(trigger_script& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb which triggers a level change if the player moves close to it. @@ -160,7 +160,7 @@ namespace phoenix { /// \throws parser_error if parsing fails. /// \see vob::parse /// \see trigger::parse - static void parse(trigger_change_level& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(trigger_change_level& obj, archive_reader& ctx, game_version version); }; /// \brief A VOb which triggers a world start event. @@ -177,7 +177,7 @@ namespace phoenix { /// \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 - static void parse(trigger_world_start& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(trigger_world_start& obj, archive_reader& ctx, game_version version); }; struct trigger_untouch : public vob { @@ -189,7 +189,7 @@ namespace phoenix { /// \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 - static void parse(trigger_untouch& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(trigger_untouch& obj, archive_reader& ctx, game_version version); }; } // namespace vobs } // namespace phoenix diff --git a/include/phoenix/vobs/vob.hh b/include/phoenix/vobs/vob.hh index ce642a5a..875f7b1f 100644 --- a/include/phoenix/vobs/vob.hh +++ b/include/phoenix/vobs/vob.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "../Api.hh" #include #include #include @@ -117,7 +118,7 @@ namespace phoenix { /// \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. - static decal parse(archive_reader& ctx, game_version version); + PHOENIX_API static decal parse(archive_reader& ctx, game_version version); }; /// \brief The base class for all VObs. @@ -170,7 +171,7 @@ namespace phoenix { virtual ~vob() = default; /// \return `true` if this VOb is from a save-game and `false` if not. - [[nodiscard]] inline bool is_save_game() const noexcept { + [[nodiscard]] PHOENIX_API inline bool is_save_game() const noexcept { return saved.has_value(); } @@ -183,6 +184,6 @@ 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. /// \throws parser_error if parsing fails. - static void parse(vob& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(vob& obj, archive_reader& ctx, game_version version); }; } // namespace phoenix diff --git a/include/phoenix/vobs/zone.hh b/include/phoenix/vobs/zone.hh index 77251e8b..16df1dee 100644 --- a/include/phoenix/vobs/zone.hh +++ b/include/phoenix/vobs/zone.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "../Api.hh" #include namespace phoenix::vobs { @@ -24,7 +25,7 @@ namespace phoenix::vobs { /// \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 - static void parse(zone_music& obj, archive_reader& ctx, game_version version); + 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. @@ -38,7 +39,7 @@ namespace phoenix::vobs { /// \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 - static void parse(zone_far_plane& obj, archive_reader& ctx, game_version version); + 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. @@ -55,6 +56,6 @@ namespace phoenix::vobs { /// \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 - static void parse(zone_fog& obj, archive_reader& ctx, game_version version); + PHOENIX_API static void parse(zone_fog& obj, archive_reader& ctx, game_version version); }; } // namespace phoenix::vobs \ No newline at end of file diff --git a/include/phoenix/world.hh b/include/phoenix/world.hh index 8992d306..ad8de62f 100644 --- a/include/phoenix/world.hh +++ b/include/phoenix/world.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "Api.hh" #include #include #include @@ -27,7 +28,7 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] static world parse(buffer& buf, game_version version); + [[nodiscard]] PHOENIX_API static world parse(buffer& buf, game_version version); /// \brief Parses a world from the data in the given buffer. /// @@ -48,7 +49,7 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static world parse(buffer& buf); + [[nodiscard]] PHOENIX_API static world parse(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). @@ -56,7 +57,7 @@ namespace phoenix { /// \return The parsed world object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] inline static world parse(buffer&& buf, game_version version) { + [[nodiscard]] PHOENIX_API inline static world parse(buffer&& buf, game_version version) { return world::parse(buf, version); } @@ -65,7 +66,7 @@ namespace phoenix { /// \return The parsed world object. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] inline static world parse(buffer&& buf) { + [[nodiscard]] PHOENIX_API inline static world parse(buffer&& buf) { return world::parse(buf); } diff --git a/include/phoenix/world/bsp_tree.hh b/include/phoenix/world/bsp_tree.hh index baf1b278..a7785ba2 100644 --- a/include/phoenix/world/bsp_tree.hh +++ b/include/phoenix/world/bsp_tree.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "../Api.hh" #include #include @@ -27,7 +28,7 @@ namespace phoenix { std::int32_t back_index {-1}; std::int32_t parent_index {-1}; - inline bool is_leaf() const noexcept { + PHOENIX_API inline bool is_leaf() const noexcept { return front_index == -1 && back_index == -1; } }; @@ -58,7 +59,7 @@ namespace phoenix { /// using buffer::duplicate. /// \throws parser_error if parsing fails. /// \see #parse(buffer&&) - [[nodiscard]] static bsp_tree parse(buffer& in, std::uint32_t version); + [[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). @@ -66,7 +67,7 @@ namespace phoenix { /// \return The parsed BSP tree. /// \throws parser_error if parsing fails. /// \see #parse(buffer&) - [[nodiscard]] inline static bsp_tree parse(buffer&& in, std::uint32_t version) { + [[nodiscard]] PHOENIX_INTERNAL inline static bsp_tree parse(buffer&& in, std::uint32_t version) { return parse(in, version); } diff --git a/include/phoenix/world/vob_tree.hh b/include/phoenix/world/vob_tree.hh index d189e0dc..d30cc4d8 100644 --- a/include/phoenix/world/vob_tree.hh +++ b/include/phoenix/world/vob_tree.hh @@ -15,5 +15,5 @@ namespace phoenix { /// \param in The reader to read from. /// \param version The version of Gothic being used. /// \return The tree parsed. - std::unique_ptr parse_vob_tree(archive_reader& in, game_version version); + PHOENIX_API std::unique_ptr parse_vob_tree(archive_reader& in, game_version version); } // namespace phoenix diff --git a/include/phoenix/world/way_net.hh b/include/phoenix/world/way_net.hh index 99e40921..6c7fa7ef 100644 --- a/include/phoenix/world/way_net.hh +++ b/include/phoenix/world/way_net.hh @@ -1,6 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include "phoenix/Api.hh" #include #include @@ -35,12 +36,13 @@ namespace phoenix { /// \brief PParses a way-net from the given reader. /// \param in The reader to read from. /// \return The way-net parsed. - static way_net parse(archive_reader& in); + 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, deprecated("unsupported API")]] const way_point* waypoint(const std::string& name) const; + [[nodiscard]] PHOENIX_DEPRECATED("unsupported API") + PHOENIX_API const way_point* waypoint(const std::string& name) const; public: /// \brief All waypoints of this way-net. diff --git a/mkdocs.yml b/mkdocs.yml index a7ff74a6..881aa254 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -42,6 +42,10 @@ theme: - 'search.highlight' extra_css: - 'assets/stylesheets/extra.css' +extra_javascript: + - 'assets/javascript/mathjax.js' + - 'https://polyfill.io/v3/polyfill.min.js?features=es6' + - 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js' plugins: - 'search' markdown_extensions: @@ -55,6 +59,8 @@ markdown_extensions: emoji_generator: !!python/name:materialx.emoji.to_svg - pymdownx.tabbed: alternate_style: true + - pymdownx.arithmatex: + generic: true - 'abbr' - 'admonition' - 'footnotes' diff --git a/source/animation.cc b/source/animation.cc index 646cc931..9cc6388c 100644 --- a/source/animation.cc +++ b/source/animation.cc @@ -5,9 +5,14 @@ #include namespace phoenix { - static const float SAMPLE_ROT_BITS = float(1 << 16) - 1.0f; - static const float SAMPLE_QUAT_SCALAR = (1.0f / SAMPLE_ROT_BITS) * 2.1f; - static const std::uint16_t SAMPLE_QUAT_MIDDLE = (1 << 15) - 1; + /// \brief The highest number representable by a single rotation component. + static const float SAMPLE_ROTATION_RANGE = float(1 << 16) - 1.0f; + + /// \brief The scaling factor applied to each rotation component. + static const float SAMPLE_ROTATION_SCALE = (1.0f / SAMPLE_ROTATION_RANGE) * 2.1f; + + /// \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, @@ -18,29 +23,29 @@ namespace phoenix { unknown }; - /** - * Adapted from ZenLib - * (https://github.com/ataulien/ZenLib/blob/e1a5e1b12e71690a5470f3be2aa3d0d6419f5191/zenload/zCModelAni.cpp#L43). - * Thanks to Andre Taulien and Alexander Stillich! - */ - static glm::vec3 read_sample_position(buffer& in, float sample_pos_s, float sample_pos_range_min) { + /// \brief Reads the position of a single animation sample from the given buffer. + /// \param in The buffer 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) { glm::vec3 v {}; - v.x = (float) in.get_ushort() * sample_pos_s + sample_pos_range_min; - v.y = (float) in.get_ushort() * sample_pos_s + sample_pos_range_min; - v.z = (float) in.get_ushort() * sample_pos_s + sample_pos_range_min; + v.x = (float) in.get_ushort() * scale + minimum; + v.y = (float) in.get_ushort() * scale + minimum; + v.z = (float) in.get_ushort() * scale + minimum; return v; } - /** - * Adapted from ZenLib - * (https://github.com/ataulien/ZenLib/blob/e1a5e1b12e71690a5470f3be2aa3d0d6419f5191/zenload/zCModelAni.cpp#L50). - * Thanks to Andre Taulien and Alexander Stillich! - */ + /// \brief Reads the rotation of a single animation sample from the given buffer. + /// \param in The buffer 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) { glm::quat v {}; - v.x = ((float) in.get_ushort() - SAMPLE_QUAT_MIDDLE) * SAMPLE_QUAT_SCALAR; - v.y = ((float) in.get_ushort() - SAMPLE_QUAT_MIDDLE) * SAMPLE_QUAT_SCALAR; - v.z = ((float) in.get_ushort() - SAMPLE_QUAT_MIDDLE) * SAMPLE_QUAT_SCALAR; + 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; float len_q = v.x * v.x + v.y * v.y + v.z * v.z; @@ -51,6 +56,7 @@ namespace phoenix { 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); } diff --git a/source/archive.cc b/source/archive.cc index d1c11996..1ad4349a 100644 --- a/source/archive.cc +++ b/source/archive.cc @@ -40,7 +40,7 @@ namespace phoenix { } std::string version = in.get_line(); - if (!version.starts_with("ver ")) { + if (version.find("ver ") != 0) { throw parser_error {"archive_header", "ver field missing"}; } header.version = std::stoi(version.substr(version.find(' ') + 1)); @@ -57,18 +57,18 @@ namespace phoenix { } std::string save_game = in.get_line(); - if (!save_game.starts_with("saveGame ")) { + if (save_game.find("saveGame ") != 0) { throw parser_error {"archive_header", "saveGame field missing"}; } header.save = std::stoi(save_game.substr(save_game.find(' ') + 1)) != 0; std::string optional = in.get_line(); - if (optional.starts_with("date ")) { + if (optional.find("date ") == 0) { header.date = optional.substr(optional.find(' ') + 1); optional = in.get_line(); } - if (optional.starts_with("user ")) { + if (optional.find("user ") == 0) { header.user = optional.substr(optional.find(' ') + 1); optional = in.get_line(); } @@ -80,6 +80,8 @@ namespace phoenix { 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"}; } } diff --git a/source/archive/archive_ascii.cc b/source/archive/archive_ascii.cc index 5fb73483..a9c23e87 100644 --- a/source/archive/archive_ascii.cc +++ b/source/archive/archive_ascii.cc @@ -1,11 +1,13 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT #include "archive_ascii.hh" +#include "phoenix/phoenix.hh" #include #include -#include +#include #include +#include namespace phoenix { static const std::unordered_map type_name_to_enum { @@ -26,10 +28,15 @@ namespace phoenix { void archive_reader_ascii::read_header() { { std::string objects = input.get_line(); - if (!objects.starts_with("objects ")) { + if (objects.find("objects ") != 0) { throw parser_error {"archive_reader_ascii", "objects field missing"}; } - _m_objects = std::stoi(objects.substr(objects.find(' ') + 1)); + + 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"}; + } } if (input.get_line() != "END") { @@ -108,27 +115,51 @@ namespace phoenix { } std::int32_t archive_reader_ascii::read_int() { - return std::stoi(read_entry("int")); + try { + return std::stoi(read_entry("int")); + } catch (std::invalid_argument const& e) { + throw parser_error {"archive_reader_ascii", e, "reading int"}; + } } float archive_reader_ascii::read_float() { - return std::stof(read_entry("float")); + try { + return std::stof(read_entry("float")); + } catch (std::invalid_argument const& e) { + throw parser_error {"archive_reader_ascii", e, "reading int"}; + } } std::uint8_t archive_reader_ascii::read_byte() { - return std::stoul(read_entry("int")) & 0xFF; + try { + return std::stoul(read_entry("int")) & 0xFF; + } catch (std::invalid_argument const& e) { + throw parser_error {"archive_reader_ascii", e, "reading int"}; + } } std::uint16_t archive_reader_ascii::read_word() { - return std::stoul(read_entry("int")) & 0xFF'FF; + try { + return std::stoul(read_entry("int")) & 0xFF'FF; + } catch (std::invalid_argument const& e) { + throw parser_error {"archive_reader_ascii", e, "reading int"}; + } } std::uint32_t archive_reader_ascii::read_enum() { - return std::stoul(read_entry("enum")) & 0xFFFF'FFFF; + try { + return std::stoul(read_entry("enum")) & 0xFFFF'FFFF; + } catch (std::invalid_argument const& e) { + throw parser_error {"archive_reader_ascii", e, "reading int"}; + } } bool archive_reader_ascii::read_bool() { - return std::stoul(read_entry("bool")) != 0; + try { + return std::stoul(read_entry("bool")) != 0; + } catch (std::invalid_argument const& e) { + throw parser_error {"archive_reader_ascii", e, "reading int"}; + } } glm::u8vec4 archive_reader_ascii::read_color() { diff --git a/source/archive/archive_binary.cc b/source/archive/archive_binary.cc index 926f1fe8..16c1e80d 100644 --- a/source/archive/archive_binary.cc +++ b/source/archive/archive_binary.cc @@ -8,13 +8,18 @@ namespace phoenix { void archive_reader_binary::read_header() { { std::string objects = input.get_line(); - if (!objects.starts_with("objects ")) { + if (objects.find("objects ") != 0) { throw parser_error {"archive_reader_binary", "objects header field missing"}; } - _m_objects = std::stoi(objects.substr(objects.find(' ') + 1)); + + 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"}; + } } - if (input.get_line() != "END") { + if (input.get_line_and_ignore("\n") != "END") { throw parser_error {"archive_reader_binary", "second END missing"}; } } @@ -28,8 +33,8 @@ namespace phoenix { obj.version = input.get_ushort(); obj.index = input.get_uint(); - obj.object_name = input.get_line(); - obj.class_name = input.get_line(); + obj.object_name = input.get_line(false); + obj.class_name = input.get_line(false); return true; } @@ -92,7 +97,7 @@ namespace phoenix { } glm::mat3x3 archive_reader_binary::read_mat3x3() { - return glm::transpose(input.get_mat3x3()); + return input.get_mat3x3(); } buffer archive_reader_binary::read_raw_bytes() { diff --git a/source/archive/archive_binsafe.cc b/source/archive/archive_binsafe.cc index 0ddc26b8..27cadccc 100644 --- a/source/archive/archive_binsafe.cc +++ b/source/archive/archive_binsafe.cc @@ -97,11 +97,10 @@ namespace phoenix { } const std::string& archive_reader_binsafe::get_entry_key() { - if (static_cast(input.get(input.position())) != archive_entry_type::hash) { + if (static_cast(input.get()) != archive_entry_type::hash) { throw parser_error {"archive_reader_binsafe", "invalid format"}; } - input.skip(1); auto hash = input.get_uint(); return _m_hash_table_entries[hash].key; } @@ -197,7 +196,7 @@ namespace phoenix { auto v = input.get_mat3x3(); input.skip(unused); - return glm::transpose(v); + return v; } buffer archive_reader_binsafe::read_raw_bytes() { diff --git a/source/buffer.cc b/source/buffer.cc index 4ce2d97e..3e98ebd3 100644 --- a/source/buffer.cc +++ b/source/buffer.cc @@ -1,5 +1,7 @@ // Copyright © 2022 Luis Michaelis // SPDX-License-Identifier: MIT +#include +#include #include #include @@ -249,14 +251,6 @@ namespace phoenix { _m_position += size; } - void buffer::get(std::uint64_t index, std::byte* buf, std::uint64_t size) const { - if (index + size > this->limit()) { - throw buffer_underflow {index, size, "absolute bulk get"}; - } - - _m_backing->read(buf, size, _m_backing_begin + index); - } - std::string buffer::get_string(std::uint64_t size) { if (this->remaining() < size) { throw buffer_overflow {position(), size, "relative string get"}; @@ -268,33 +262,31 @@ namespace phoenix { return tmp; } - std::string buffer::get_string(std::uint64_t index, std::uint64_t size) const { - if (index + size > this->limit()) { - throw buffer_overflow {index, size, "absolute string get"}; + std::string buffer::get_line(bool skip_whitespace) { + if (skip_whitespace) { + return this->get_line_and_ignore(" \f\n\r\t\v"); } - std::string tmp {}; - tmp.resize(size); - this->get(index, (std::byte*) tmp.data(), size); - return tmp; + return this->get_line_and_ignore(""); } - std::string buffer::get_line(bool skip_whitespace) { - auto lf = mismatch([](char chr) { return chr == '\n' || chr == '\0' || chr == '\r'; }); + std::string buffer::get_line_and_ignore(std::string_view whitespace) { + std::string tmp {}; - if (lf == -1) { - throw buffer_underflow {position(), "relative line get"}; + char c = this->get_char(); + while (c != '\n' && c != '\r' && c != '\0') { + tmp.push_back(c); + c = this->get_char(); } - auto tmp = get_string(lf); - (void) get_char(); // ignore the character itself + if (!whitespace.empty() && remaining() > 0) { + c = this->get_char(); + while (whitespace.find(c) != std::string_view::npos && remaining() > 0) { + c = this->get_char(); + } - if (skip_whitespace) { - auto count = mismatch([](char chr) { return !std::isspace(static_cast(chr)); }); - if (count == -1) { - position(limit()); // the end of the buffer has been reached - } else { - position(position() + count); + if (remaining() != 0) { + this->position(this->position() - 1); } } @@ -322,90 +314,197 @@ namespace phoenix { return tmp; } - std::string buffer::get_line_at(std::uint64_t index) const { - auto lf = mismatch(index, [](char chr) { return chr == '\n' || chr == '\0'; }); - - if (lf == -1) { - throw buffer_underflow {index, "absolute line get"}; + void buffer::put(const std::byte* buf, std::uint64_t size) { + if (this->remaining() < size) { + throw buffer_overflow {this->position(), size, "relative bulk put"}; } - return get_string(index, lf); + _m_backing->write(buf, size, _m_backing_begin + _m_position); + _m_position += size; } - std::int64_t buffer::mismatch(const buffer& other) const { - auto remaining = std::min(this->remaining(), other.remaining()); + void buffer::put_string(std::string_view str) { + this->put((const std::byte*) str.data(), str.size()); + } - for (std::uint64_t i = 0; i < remaining; ++i) { - if (this->get_char(this->position() + i) != other.get_char(other.position() + i)) { - return static_cast(i); - } - } + void buffer::put_line(std::string_view str) { + put_string(str); + put_char('\n'); + } - return -1; + bool operator==(const buffer& self, const buffer& other) { + return &other == &self || + (other._m_backing == self._m_backing && other._m_backing_begin == self._m_backing_begin && + other._m_backing_end == self._m_backing_end && other._m_capacity == self._m_capacity && + other._m_position == self._m_position); } - std::int64_t buffer::mismatch(std::uint64_t index, const buffer& other) const { - if (index > this->limit()) { - throw buffer_underflow {index, "absolute mismatch"}; - } + std::uint8_t buffer::get() { + return _get_t(); + } - auto remaining = std::min(this->limit() - index, other.remaining()); - for (std::uint64_t i = 0; i < remaining; ++i) { - if (this->get_char(index + i) != other.get_char(other.position() + i)) { - return static_cast(i); - } - } + char buffer::get_char() { + return _get_t(); + } - return -1; + std::int16_t buffer::get_short() { + return _get_t(); } - std::int64_t buffer::mismatch(const std::function& each) const { - for (std::uint64_t i = 0; i < this->remaining(); ++i) { - if (each(this->get_char(this->position() + i))) { - return static_cast(i); - } - } + std::uint16_t buffer::get_ushort() { + return _get_t(); + } - return -1; + std::int32_t buffer::get_int() { + return _get_t(); } - std::int64_t buffer::mismatch(std::uint64_t index, const std::function& each) const { - if (index > this->limit()) { - throw buffer_underflow {index, "absolute mismatch"}; - } + std::uint32_t buffer::get_uint() { + return _get_t(); + } - auto remaining = limit() - index; - for (std::uint64_t i = 0; i < remaining; ++i) { - if (each(this->get_char(index + i))) { - return static_cast(i); - } - } + std::int64_t buffer::get_long() { + return _get_t(); + } - return -1; + std::uint64_t buffer::get_ulong() { + return _get_t(); } - void buffer::put(const std::byte* buf, std::uint64_t size) { - if (this->remaining() < size) { - throw buffer_overflow {this->position(), size, "relative bulk put"}; - } + float buffer::get_float() { + return _get_t(); + } - _m_backing->write(buf, size, _m_backing_begin + _m_position); - _m_position += size; + double buffer::get_double() { + return _get_t(); } - void buffer::put_string(std::string_view str) { - this->put((const std::byte*) str.data(), str.size()); + glm::vec2 buffer::get_vec2() { + float content[2]; + this->get((std::byte*) content, sizeof(content)); + return {content[0], content[1]}; } - void buffer::put_line(std::string_view str) { - put_string(str); - put_char('\n'); + glm::vec3 buffer::get_vec3() { + float content[3]; + this->get((std::byte*) content, sizeof(content)); + return glm::vec3 {content[0], content[1], content[2]}; } - bool operator==(const buffer& self, const buffer& other) { - return &other == &self || - (other._m_backing == self._m_backing && other._m_backing_begin == self._m_backing_begin && - other._m_backing_end == self._m_backing_end && other._m_capacity == self._m_capacity && - other._m_position == self._m_position); + glm::mat3x3 buffer::get_mat3x3() { + float content[3 * 3]; + this->get((std::byte*) content, sizeof(content)); + return glm::transpose(glm::mat3x3 { + content[0], + content[1], + content[2], + content[3], + content[4], + content[5], + content[6], + content[7], + content[8], + }); + } + + glm::mat4x4 buffer::get_mat4x4() { + float content[4 * 4]; + this->get((std::byte*) content, sizeof(content)); + return glm::transpose(glm::mat4x4 { + content[0], + content[1], + content[2], + content[3], + content[4], + content[5], + content[6], + content[7], + content[8], + content[9], + content[10], + content[11], + content[12], + content[13], + content[14], + content[15], + }); + } + + glm::vec4 buffer::get_vec4() { + float content[4]; + this->get((std::byte*) content, sizeof(content)); + return glm::vec4 {content[0], content[1], content[2], content[3]}; + } + + void buffer::put(const std::uint8_t* buf, std::uint64_t size) { + this->put((const std::byte*) buf, size); + } + + void buffer::put(std::uint8_t value) { + _put_t(value); + } + + void buffer::put_char(char value) { + _put_t(value); + } + + void buffer::put_short(std::int16_t value) { + _put_t(value); + } + + void buffer::put_ushort(std::uint16_t value) { + _put_t(value); + } + + void buffer::put_int(std::int32_t value) { + _put_t(value); + } + + void buffer::put_uint(std::uint32_t value) { + _put_t(value); + } + + void buffer::put_long(std::int64_t value) { + _put_t(value); + } + + void buffer::put_ulong(std::uint64_t value) { + _put_t(value); + } + + void buffer::put_float(float value) { + _put_t(value); + } + + void buffer::put_double(double value) { + _put_t(value); + } + + template + PHOENIX_INTERNAL void buffer::_put_t(T value) { + if (this->remaining() < sizeof(T)) { + throw buffer_overflow {this->position(), sizeof(T)}; + } + + _m_backing->write((std::byte*) &value, sizeof(T), _m_backing_begin + _m_position); + _m_position += sizeof(T); + } + + template + PHOENIX_INTERNAL T buffer::_get_t(std::uint64_t pos) const { + if (pos + sizeof(T) > limit()) { + throw buffer_underflow {pos, sizeof(T)}; + } + + T tmp; + _m_backing->read((std::byte*) &tmp, sizeof(T), _m_backing_begin + pos); + return tmp; + } + + template + PHOENIX_INTERNAL T buffer::_get_t() { + auto tmp = this->_get_t(this->position()); + _m_position += sizeof(T); + return tmp; } } // namespace phoenix diff --git a/source/math.cc b/source/math.cc new file mode 100644 index 00000000..cfb9f559 --- /dev/null +++ b/source/math.cc @@ -0,0 +1,72 @@ +// Copyright © 2023 Luis Michaelis +// SPDX-License-Identifier: MIT +#include +#include + +#include + +namespace phoenix { + bounding_box bounding_box::parse(buffer& in) { + bounding_box bbox {}; + bbox.min = in.get_vec3(); + bbox.max = in.get_vec3(); + 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)); + } + + return bbox; + } + + bounding_box obb::as_bbox() const { + const float sign[8][3] = {{-1, -1, -1}, + {-1, -1, +1}, + {-1, +1, -1}, + {-1, +1, +1}, + {+1, -1, -1}, + {+1, -1, +1}, + {+1, +1, -1}, + {+1, +1, +1}}; + + bounding_box box {}; + box.min = {std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()}; + + box.max = {std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()}; + + for (auto i : sign) { + auto point = center; + auto axis0 = axes[0] * half_width.x * i[0]; + auto axis1 = axes[1] * half_width.y * i[1]; + auto axis2 = axes[2] * half_width.z * i[2]; + + point.x += axis0.x + axis1.x + axis2.x; + point.y += axis0.y + axis1.y + axis2.y; + point.z += axis0.z + axis1.z + axis2.z; + + box.min.x = std::min(box.min.x, point.x); + box.min.y = std::min(box.min.y, point.y); + box.min.z = std::min(box.min.z, point.z); + + box.max.x = std::max(box.max.x, point.x); + box.max.y = std::max(box.max.y, point.y); + box.max.z = std::max(box.max.z, point.z); + } + + return box; + } +} // namespace phoenix \ No newline at end of file diff --git a/source/mesh.cc b/source/mesh.cc index c4f8e041..63f7437c 100644 --- a/source/mesh.cc +++ b/source/mesh.cc @@ -125,7 +125,7 @@ namespace phoenix { // 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.contains(i)) { + if (leaf_polygons.find(i) == leaf_polygons.end()) { // If the current polygon is not a leaf polygon, skip it. chunk.skip((version == mesh_version_g2 ? 8 : 6) * vertex_count); continue; diff --git a/source/messages.cc b/source/messages.cc index 88ca6f9a..ddaa0bff 100644 --- a/source/messages.cc +++ b/source/messages.cc @@ -43,20 +43,11 @@ namespace phoenix { throw parser_error {"messages", "expected oCMsgConversation not found for " + itm.name}; } - // Quirk: Binary message dbs have a byte here instead of an enum. - if (archive->get_header().format == archive_format::binary) { - itm.message.type = archive->read_byte(); - } else { - itm.message.type = archive->read_enum(); - } - + itm.message.type = archive->read_enum(); itm.message.text = archive->read_string(); itm.message.name = archive->read_string(); if (!archive->read_object_end()) { - // FIXME: in binary archives this might skip whole sections of the file due to faulty object - // extents in the archive. This might be due to encoding errors the version of ZenGin - // used with Gothic I archive->skip_object(true); PX_LOGW("messages: oCMsgConversation(\"", itm.name, "\") not fully parsed"); } diff --git a/source/model_hierarchy.cc b/source/model_hierarchy.cc index 01914770..6cf4f537 100644 --- a/source/model_hierarchy.cc +++ b/source/model_hierarchy.cc @@ -26,14 +26,13 @@ namespace phoenix { auto& node = hierarchy.nodes.emplace_back(); node.name = chunk.get_line(false); node.parent_index = chunk.get_short(); - node.transform = glm::transpose(chunk.get_mat4x4()); + node.transform = chunk.get_mat4x4(); } hierarchy.bbox = bounding_box::parse(chunk); hierarchy.collision_bbox = bounding_box::parse(chunk); hierarchy.root_translation = chunk.get_vec3(); - - (void) /* checksum = */ chunk.get_uint(); + hierarchy.checksum = chunk.get_uint(); break; } case hierarchy_chunk::stats: diff --git a/source/model_mesh.cc b/source/model_mesh.cc index a79b08e0..1b389a94 100644 --- a/source/model_mesh.cc +++ b/source/model_mesh.cc @@ -48,7 +48,7 @@ namespace phoenix { msh.attachments[attachment_names[msh.attachments.size()]] = proto_mesh::parse_from_section(chunk); break; case model_mesh_chunk::softskins: { - (void) /* checksum = */ chunk.get_uint(); + msh.checksum = chunk.get_uint(); auto count = chunk.get_ushort(); msh.meshes.reserve(count); diff --git a/source/model_script.cc b/source/model_script.cc index d701bc53..9a0ff799 100644 --- a/source/model_script.cc +++ b/source/model_script.cc @@ -1,15 +1,11 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2023 Luis Michaelis // SPDX-License-Identifier: MIT #include #include #include "model_script_dsl.hh" -#include -#include -#include - -#include +#include #include namespace phoenix { @@ -213,8 +209,8 @@ namespace phoenix { 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).starts_with('R') ? mds::animation_direction::backward - : mds::animation_direction::forward; + 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(); @@ -233,8 +229,8 @@ namespace phoenix { 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).starts_with('R') ? mds::animation_direction::backward - : mds::animation_direction::forward; + 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; } @@ -452,21 +448,21 @@ namespace phoenix { return script; } + static model_script parse_source_script(buffer& buf) { + parser::parser p {buf.duplicate()}; + return p.parse_script(); + } + model_script model_script::parse(buffer& buf) { - auto potential_chunk_type = buf.get_ushort(buf.position()); + auto peek = buf.position(); + auto potential_chunk_type = buf.get_ushort(); + buf.position(peek); if (potential_chunk_type >= 0xF000 || potential_chunk_type == 0xD000) { return parse_binary_script(buf); } - lexy::buffer b {(char*) buf.array(), buf.remaining()}; - auto result = lexy::parse(b, lexy_ext::report_error); - - if (!result.has_value()) { - throw parser_error {"syntax error"}; - } - - return result.value(); + return parse_source_script(buf); } model_script model_script::parse_binary(buffer& buf) { diff --git a/source/model_script_dsl.cc b/source/model_script_dsl.cc new file mode 100644 index 00000000..a6c83ca1 --- /dev/null +++ b/source/model_script_dsl.cc @@ -0,0 +1,522 @@ +// Copyright © 2023 Luis Michaelis +// SPDX-License-Identifier: MIT +#include "model_script_dsl.hh" + +#include + +#define WARN_SYNTAX(...) \ + PX_LOGW("model script: syntax error (line ", _m_line, ", column ", _m_column, "): ", __VA_ARGS__) + +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 parser { + constexpr std::string_view token_names[] = + {"keyword", "integer", "float", "string", "rparen", "lparen", "rbrace", "lbrace", "colon", "eof", "null"}; + + tokenizer::tokenizer(buffer buf) : _m_buffer(std::move(buf)) {} + + 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 and parentheses + // NOTE: Quirk of original implementation: parens are not significant. + if (std::isspace(chr) || chr == '(' || chr == ')') { + if (chr == '\n') { + _m_line += 1; + _m_column = 1; + // TODO? return token::line_feed; + } + + continue; + } + + // ignore comments + if (chr == '/') { + if (_m_buffer.get_char() != '/') { + WARN_SYNTAX("comments must start with two slashes"); + } + + // skip everything until the end of the line + while (_m_buffer.get_char() != '\n') + _m_column += 1; + + _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; + + return token::keyword; + } + + // parse strings + if (chr == '"') { + chr = static_cast(_m_buffer.get_char()); + _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; + + do { + _m_value.push_back(static_cast(chr)); + chr = static_cast(_m_buffer.get_char()); + _m_column += 1; + + // (allow floating point numbers) + if (chr == '.') { + floating_point = true; + + _m_value.push_back(static_cast(chr)); + chr = static_cast(_m_buffer.get_char()); + _m_column += 1; + } + } while (std::isdigit(chr)); + + // (backtrack one) + _m_buffer.position(_m_buffer.position() - 1); + _m_column -= 1; + + return floating_point ? token::float_ : token::integer; + } + + switch (chr) { + case '{': + return token::lbrace; + case '}': + return token::rbrace; + case ':': + return token::colon; + default: + break; + } + + WARN_SYNTAX("unknown token \"", chr, "\""); + return token::null; + } + + 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 ========================== // + + parser::parser(buffer buf) : _m_stream(std::move(buf)) {} + + template + void parser::expect() { + if (!this->maybe()) { + throw syntax_error {_m_stream.format_location(), "expected " + std::string {token_names[int(kind)]}}; + } + } + + std::string parser::expect_string() { + this->expect(); + return _m_stream.token_value(); + } + + std::string parser::expect_keyword() { + this->expect(); + return _m_stream.token_value(); + } + + 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} + "\""}; + } + } + + float parser::expect_number() { + auto mb = this->maybe_number(); + if (!mb) { + throw syntax_error {_m_stream.format_location(), "expected a number"}; + } + return *mb; + } + + int parser::expect_int() { + this->expect(); + return std::stoi(_m_stream.token_value()); + } + + 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); + } + + template + bool parser::maybe() { + if (_m_stream.next() != kind) { + _m_stream.backtrack(); + return false; + } + + return true; + } + + std::optional parser::maybe_int() { + if (!this->maybe()) { + return std::nullopt; + } + + return std::stoi(_m_stream.token_value()); + } + + 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::optional parser::maybe_string() { + if (!this->maybe()) { + return std::nullopt; + } + + return _m_stream.token_value(); + } + + 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; + } + + 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; + } + + this->expect(); + return this->expect_number(); + } + + // ===================== 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; + } + + 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; + } + + mds::skeleton parser::parse_meshAndTree() { + mds::skeleton skel {}; + skel.name = this->expect_string(); + skel.disable_mesh = this->maybe_keyword("DONT_USE_MESH"); + return skel; + } + + std::string parser::parse_registerMesh() { + return this->expect_string(); + } + + void parser::parse_aniEnum(model_script& into) { + this->expect(); + + while (!this->eof()) { + if (this->maybe() || this->maybe()) { + break; + } + + 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}; + } + } + } + + 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}; + } + } + } + + void parser::ignore_block() { + while (this->_m_stream.next() != token::rbrace) {} + } + + 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); + } + + 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); + + // 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; + } + + 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; + } + + mds::event_sfx_ground parser::parse_eventSFXGrnd() { + mds::event_sfx_ground sfx {}; + sfx.frame = this->expect_int(); + sfx.name = this->expect_string(); + return sfx; + } + + mds::event_pfx_stop parser::parse_eventPFXStop() { + mds::event_pfx_stop pfx {}; + pfx.frame = this->expect_int(); + pfx.index = this->expect_int(); + return pfx; + } + + 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; + } + + 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(); + + 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\""}; + } + + ani.direction = + phoenix::iequals(kw, "R") ? mds::animation_direction::backward : mds::animation_direction::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; + } + + 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(); + } + + 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; + } + + 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); + + // Optional events block. + if (this->maybe()) { + this->ignore_block(); + } + + 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\""}; + } + + tag.bone = this->expect_string(); + return tag; + } + } // namespace parser +} // namespace phoenix \ No newline at end of file diff --git a/source/model_script_dsl.hh b/source/model_script_dsl.hh index 2e8be21e..860e554c 100644 --- a/source/model_script_dsl.hh +++ b/source/model_script_dsl.hh @@ -1,558 +1,108 @@ -// Copyright © 2022 Luis Michaelis +// Copyright © 2023 Luis Michaelis // SPDX-License-Identifier: MIT #pragma once +#include +#include +#include #include +#include -#include -#include - -#include +#include +#include +#include namespace phoenix::mds { - // TODO: case folding for everything - namespace dsl = lexy::dsl; - - namespace sinks { - struct dsl_script_body_sink { - using return_type = model_script; - - struct _sink { - using return_type = model_script; - model_script _result; - - void operator()(skeleton&& skel) { - _result.skeleton.name = std::move(skel.name); - _result.skeleton.disable_mesh = skel.disable_mesh; - } - - void operator()(std::string&& mesh) { - _result.meshes.push_back(mesh); - } - - void operator()(model_script&& mesh) { - _result.animations = std::move(mesh.animations); - _result.blends = std::move(mesh.blends); - _result.aliases = std::move(mesh.aliases); - _result.combinations = std::move(mesh.combinations); - _result.disabled_animations = std::move(mesh.disabled_animations); - _result.model_tags = std::move(mesh.model_tags); - } - - void operator()(lexy::nullopt) {} - - void operator()(model_script&& mesh, lexy::nullopt) { - this->operator()(std::move(mesh)); - } - - model_script&& finish() && { - return LEXY_MOV(_result); - } - }; - - model_script operator()(lexy::nullopt&&) const { - return model_script {}; - } - - model_script operator()(lexy::nullopt&&, lexy::nullopt&&) const { - return model_script {}; - } - - model_script operator()(model_script&& s, lexy::nullopt&&) const { - return LEXY_MOV(s); - } - - [[nodiscard]] auto sink() const { - return _sink {model_script {}}; - } - }; - - struct dsl_ani_enum_sink { - using return_type = model_script; - - struct _sink { - using return_type = model_script; - model_script _result; - - void operator()(std::string&& disable) { - _result.disabled_animations.push_back(disable); - } - void operator()(animation&& a) { - _result.animations.push_back(a); - } - - void operator()(animation_alias&& a) { - _result.aliases.push_back(a); - } - - void operator()(animation_blending&& a) { - _result.blends.push_back(a); - } - - void operator()(animation_combination&& a) { - _result.combinations.push_back(a); - } - - void operator()(model_tag&& a) { - _result.model_tags.push_back(a); - } - - void operator()(lexy::nullopt&&) {} - - model_script&& finish() && { - return LEXY_MOV(_result); - } - }; - - model_script operator()(lexy::nullopt&&) const { - return model_script {}; - } - - model_script operator()(lexy::nullopt&&, lexy::nullopt&&) const { - return model_script {}; - } - - model_script operator()(model_script&& s, lexy::nullopt&&) const { - return LEXY_MOV(s); - } - - [[nodiscard]] auto sink() const { - return _sink {model_script {}}; - } - }; - - struct dsl_ani_event_sink { - using return_type = animation; - - struct _sink { - using return_type = animation; - animation _result; - - void operator()(lexy::nullopt&&) {} - - void operator()(event_tag&& a) { - _result.events.push_back(a); - } - - void operator()(event_sfx&& a) { - _result.sfx.push_back(a); - } - - void operator()(event_sfx_ground&& a) { - _result.sfx_ground.push_back(a); - } - - void operator()(event_pfx&& a) { - _result.pfx.push_back(a); - } - - void operator()(event_pfx_stop&& a) { - _result.pfx_stop.push_back(a); - } - - void operator()(event_morph_animate&& a) { - _result.morph.push_back(a); - } - - void operator()(event_camera_tremor&& a) { - _result.tremors.push_back(a); - } - - animation&& finish() && { - return LEXY_MOV(_result); - } - }; - - animation operator()(lexy::nullopt&&) const { - return animation {}; - } - - [[nodiscard]] auto sink() const { - return _sink {animation {}}; - } - }; - } // namespace sinks - - static constexpr auto ws = LEXY_LIT("/") >> dsl::until(dsl::newline) | dsl::ascii::space | LEXY_ASCII_ONE_OF("()"); - - struct dsl_string { - static constexpr auto rule = dsl::quoted(-dsl::lit_c<'"'>); - static constexpr auto value = lexy::as_string; - }; - - struct dsl_integer { - static constexpr auto rule = dsl::peek(dsl::lit_c<'-'> / dsl::digit<>) >> - (dsl::minus_sign + dsl::integer); - static constexpr auto value = lexy::as_integer; - }; - - struct dsl_float { - static constexpr auto rule = - dsl::capture(dsl::token(dsl::digits<> + dsl::opt(dsl::lit_c<'.'> >> dsl::digits<>))); - static constexpr auto value = - lexy::as_string | lexy::callback([](std::string&& v) { return std::stof(v); }); - }; - - struct dsl_ani_flags { - static constexpr auto rule = - dsl::capture(dsl::token(dsl::list(dsl::lit_c<'M'> / dsl::lit_c<'R'> / dsl::lit_c<'E'> / dsl::lit_c<'F'> / - dsl::lit_c<'I'> / dsl::lit_c<'.'> / dsl::lit_c<':'>))); - static constexpr auto value = lexy::as_string | - lexy::callback([](std::string&& v) { return animation_flags_from_string(v); }); - }; - event_tag make_event_tag(int32_t, std::string&&, std::optional&&, std::optional&&, bool); - - struct dsl_event_morph { - static constexpr auto whitespace = ws; - static constexpr auto rule = (dsl::p + dsl::p + dsl::opt(dsl::p)); - static constexpr auto value = lexy::callback([](int32_t frame, - std::string&& animation, - std::optional&& node) { - return event_morph_animate {.frame = frame, .animation = std::move(animation), .node = node.value_or("")}; - }); - }; - - struct dsl_event_camera_tremor { - static constexpr auto whitespace = ws; - static constexpr auto rule = (dsl::p + dsl::p + dsl::p + - dsl::p + dsl::p); - static constexpr auto value = lexy::callback( - [](int32_t frame, int32_t field1, int32_t field2, int32_t field3, int32_t field4) { - return event_camera_tremor {.frame = frame, - .field1 = field1, - .field2 = field2, - .field3 = field3, - .field4 = field4}; - }); - }; - - struct dsl_event_pfx_stop { - static constexpr auto whitespace = ws; - static constexpr auto rule = (dsl::p + dsl::p); - static constexpr auto value = lexy::callback( - [](int32_t frame, int32_t index) { return event_pfx_stop {.frame = frame, .index = index}; }); - }; - - struct dsl_event_pfx { - static constexpr auto whitespace = ws; - static constexpr auto rule = - (dsl::p + dsl::opt(dsl::p) + dsl::p + dsl::p + - dsl::opt(LEXY_LIT("ATTACH") | LEXY_LIT("\"ATTACH\""))); // TODO: More Weirdness - static constexpr auto value = lexy::callback( - [](int32_t frame, std::optional index, std::string&& name, std::string&& position) { - return event_pfx {.frame = frame, - .index = index.value_or(0), - .name = std::move(name), - .position = std::move(position), - .attached = true}; - }, - [](int32_t frame, std::optional index, std::string&& name, std::string&& position, lexy::nullopt) { - return event_pfx {.frame = frame, - .index = index.value_or(0), - .name = std::move(name), - .position = std::move(position), - .attached = false}; - }); - }; - - // TODO: Get rid of this Gothic compatibility fix - struct opt_quote { - static constexpr auto rule = dsl::opt(dsl::lit_c<'"'>); - static constexpr auto value = lexy::noop; - }; - - struct string_without_closing_quote { - static constexpr auto rule = dsl::lit_c<'"'> + - dsl::capture(dsl::token(dsl::while_(-(LEXY_ASCII_ONE_OF("\")") / dsl::ascii::space)))) + dsl::p; - static constexpr auto value = lexy::as_string; - }; - - struct dsl_event_sfx_grnd { - static constexpr auto whitespace = ws; - static constexpr auto rule = (dsl::p + dsl::p); - static constexpr auto value = lexy::callback( - [](int32_t frame, std::string&& name) { return event_sfx_ground {.frame = frame, .name = name}; }); - }; - - struct dsl_event_sfx { - static constexpr auto whitespace = ws; - static constexpr auto rule = dsl::p + dsl::p + - dsl::opt((dsl::lit_c<'r'> | dsl::lit_c<'R'>) >> (dsl::lit_c<':'> + dsl::p) ) + - dsl::opt(LEXY_LIT("EMPTY_SLOT") | LEXY_LIT("EMTPY_SLOT")); - static constexpr auto value = lexy::callback( - [](int32_t frame, std::string&& name, std::optional range) { - return event_sfx {.frame = frame, .name = name, .range = range.value_or(1000), .empty_slot = true}; - }, - [](int32_t frame, std::string&& name, std::optional range, lexy::nullopt) { - return event_sfx {.frame = frame, .name = name, .range = range.value_or(1000), .empty_slot = false}; - }); - }; - - struct dsl_event_tag { - static constexpr auto whitespace = ws; - static constexpr auto rule = - (dsl::opt(dsl::p) + dsl::p + dsl::opt(dsl::p) + - dsl::opt(dsl::p) + dsl::opt(LEXY_LIT("ATTACH"))); - static constexpr auto value = lexy::callback( - [](std::optional frame, - std::string&& a, - std::optional&& b, - std::optional&& c) { - return make_event_tag(frame.value_or(0), std::move(a), std::move(b), std::move(c), true); - }, - [](std::optional frame, - std::string&& a, - std::optional&& b, - std::optional&& c, - lexy::nullopt) { - return make_event_tag(frame.value_or(0), std::move(a), std::move(b), std::move(c), false); - }); - }; - - struct dsl_ani_events { - static constexpr auto whitespace = ws; - static constexpr auto rule = dsl::opt(dsl::list( - LEXY_LIT("*eventTag") >> dsl::p | LEXY_LIT("*eventSFXGrnd") >> dsl::p | - LEXY_LIT("*eventSFX") >> dsl::p | LEXY_LIT("*eventPFXStop") >> dsl::p | - LEXY_LIT("*eventPFX") >> dsl::p | LEXY_LIT("*eventMMStartAni") >> dsl::p | - LEXY_LIT("*eventCamTremor") >> dsl::p)); - static constexpr auto value = sinks::dsl_ani_event_sink {}; - }; - - struct dsl_ani { - static constexpr auto whitespace = ws; - static constexpr auto rule = - (dsl::p + dsl::p + dsl::p + dsl::p + dsl::p + - dsl::p + dsl::p + dsl::capture(dsl::token(dsl::lit_c<'F'> | dsl::lit_c<'R'>)) + - dsl::p + dsl::p + dsl::opt(LEXY_LIT("FPS:") >> dsl::p) + - dsl::opt(LEXY_LIT("CVS:") >> dsl::p)) + - dsl::opt(dsl::curly_bracketed(dsl::p) + dsl::while_(dsl::lit_c<'}'>)); - static constexpr auto value = lexy::callback( - [](std::string&& name, - int32_t layer, - std::string&& next, - float blend_in, - float blend_out, - animation_flags flags, - std::string&& model, - auto direction, - int32_t first_frame, - int32_t last_frame, - std::optional fps, - std::optional cvs, - animation&& _enum) { - return animation {.name = std::move(name), - .layer = static_cast(layer), - .next = std::move(next), - .blend_in = blend_in, - .blend_out = blend_out, - .flags = flags, - .model = std::move(model), - .direction = *direction.begin() == 'R' ? animation_direction::backward - : animation_direction::forward, - .first_frame = first_frame, - .last_frame = last_frame, - .fps = fps.value_or(25.0f), - .speed = 0, - .collision_volume_scale = cvs.value_or(1), - .events = std::move(_enum.events), - .pfx = std::move(_enum.pfx), - .pfx_stop = std::move(_enum.pfx_stop), - .sfx = std::move(_enum.sfx), - .sfx_ground = std::move(_enum.sfx_ground), - .morph = std::move(_enum.morph), - .tremors = std::move(_enum.tremors)}; - }, - [](std::string&& name, - int32_t layer, - std::string&& next, - float blend_in, - float blend_out, - animation_flags flags, - std::string&& model, - auto direction, - int32_t first_frame, - int32_t last_frame, - std::optional fps, - std::optional cvs, - lexy::nullopt) { - return animation { - .name = std::move(name), - .layer = static_cast(layer), - .next = std::move(next), - .blend_in = blend_in, - .blend_out = blend_out, - .flags = flags, - .model = std::move(model), - .direction = - *direction.begin() == 'R' ? animation_direction::backward : animation_direction::forward, - .first_frame = first_frame, - .last_frame = last_frame, - .fps = fps.value_or(25.0f), - .speed = 0, - .collision_volume_scale = cvs.value_or(1), - }; - }); - }; - - struct dsl_ani_blend { - static constexpr auto whitespace = ws; - static constexpr auto rule = - (dsl::p + dsl::opt(dsl::p) + dsl::p + - dsl::opt(dsl::p) + dsl::opt(dsl::p)) + - dsl::if_(dsl::lit_c<'{'> >> - (dsl::while_(-LEXY_ASCII_ONE_OF("}")) + dsl::lit_c<'}'>) ); // TODO: Ani events on blend? - static constexpr auto value = lexy::callback([](std::string&& name, - std::optional, - std::string&& next, - std::optional blend_in, - std::optional blend_out) { - return animation_blending { - .name = std::move(name), - .next = std::move(next), - .blend_in = blend_in.value_or(0), - .blend_out = blend_out.value_or(0), - }; - }); - }; - - struct dsl_ani_alias { - static constexpr auto whitespace = ws; - static constexpr auto rule = - (dsl::p + dsl::p + dsl::p + dsl::p + dsl::p + - dsl::p + dsl::p + - dsl::opt(dsl::capture(dsl::token(dsl::lit_c<'F'> | dsl::lit_c<'R'>)))); - static constexpr auto value = lexy::callback( - [](std::string&& name, - int32_t layer, - std::string&& next, - float blend_in, - float blend_out, - animation_flags flags, - std::string&& alias, - auto direction) { - return animation_alias {.name = std::move(name), - .layer = static_cast(layer), - .next = std::move(next), - .blend_in = blend_in, - .blend_out = blend_out, - .flags = flags, - .alias = std::move(alias), - .direction = *direction.begin() == 'R' ? animation_direction::backward - : animation_direction::forward}; - }, - [](std::string&& name, - int32_t layer, - std::string&& next, - float blend_in, - float blend_out, - animation_flags flags, - std::string&& alias, - lexy::nullopt) { - return animation_alias {.name = std::move(name), - .layer = static_cast(layer), - .next = std::move(next), - .blend_in = blend_in, - .blend_out = blend_out, - .flags = flags, - .alias = std::move(alias), - .direction = animation_direction::forward}; - }); - }; - - struct dsl_ani_comb { - static constexpr auto whitespace = ws; - static constexpr auto rule = - (dsl::p + dsl::p + dsl::p + dsl::p + dsl::p + - dsl::p + dsl::p + - dsl::p) +dsl::if_(dsl::lit_c<'{'> >> (dsl::while_(-LEXY_ASCII_ONE_OF("}")) + - dsl::lit_c<'}'>) ); // TODO: Ani events on comb? - static constexpr auto value = lexy::callback([](std::string&& name, - int32_t layer, - std::string&& next, - float blend_in, - float blend_out, - animation_flags flags, - std::string&& model, - int32_t last_frame) { - return animation_combination { - .name = std::move(name), - .layer = static_cast(layer), - .next = std::move(next), - .blend_in = blend_in, - .blend_out = blend_out, - .flags = flags, - .model = std::move(model), - .last_frame = last_frame, - }; - }); - }; - - struct dsl_ani_disable { - static constexpr auto whitespace = ws; - static constexpr auto rule = (dsl::p); - static constexpr auto value = lexy::forward; - }; - - struct dsl_model_tag { - static constexpr auto whitespace = ws; - static constexpr auto rule = (LEXY_LIT("\"DEF_HIT_LIMB\"") + dsl::p); - static constexpr auto value = - lexy::callback([](std::string&& name) { return model_tag {.bone = name}; }); - }; - - struct dsl_ani_enum { - static constexpr auto whitespace = ws; - static constexpr auto rule = dsl::lit_c<'{'> + - dsl::opt(dsl::list( - LEXY_LIT("aniBlend") >> dsl::p | LEXY_LIT("aniAlias") >> dsl::p | - LEXY_LIT("AniDisable") >> dsl::p | LEXY_LIT("aniComb") >> dsl::p | - LEXY_LIT("aniDisable") >> dsl::p | LEXY_LIT("ani") >> dsl::p | - LEXY_LIT("modelTag") >> dsl::p)) + - dsl::opt(dsl::lit_c<'}'>); - static constexpr auto value = sinks::dsl_ani_enum_sink {}; - }; - - struct dsl_skeleton { - static constexpr auto whitespace = ws; - static constexpr auto rule = (dsl::p + dsl::opt(LEXY_LIT("DONT_USE_MESH"))); - static constexpr auto value = lexy::callback( - [](std::string&& name) { - skeleton sk {}; - sk.name = std::forward(name); - sk.disable_mesh = true; - return sk; - }, - [](std::string&& name, lexy::nullopt) { - skeleton sk {}; - sk.name = std::forward(name); - sk.disable_mesh = false; - return sk; - }); - }; - - struct dsl_mesh { - static constexpr auto whitespace = ws; - static constexpr auto rule = (dsl::p); - static constexpr auto value = lexy::as_string; - }; - - struct dsl_script_body { - static constexpr auto whitespace = ws; - static constexpr auto rule = dsl::lit_c<'{'> + - dsl::opt(dsl::list( - LEXY_LIT("meshAndTree") >> dsl::p | LEXY_LIT("MeshAndTree") >> dsl::p | - LEXY_LIT("registerMesh") >> dsl::p | LEXY_LIT("RegisterMesh") >> dsl::p | - LEXY_LIT("aniEnum") >> dsl::p)) + - dsl::opt(dsl::lit_c<'}'>); - static constexpr auto value = sinks::dsl_script_body_sink {}; - }; - - struct d_script { - static constexpr auto whitespace = ws; - static constexpr auto rule = LEXY_LIT("Model") + (dsl::p) +dsl::p; - - static constexpr auto value = lexy::callback( - [](std::string&&, model_script&& script) { return std::forward(script); }); - }; -} // namespace phoenix::mds +} + +namespace phoenix::parser { + enum class token { + keyword = 0, + integer = 1, + float_ = 2, + string = 3, + rbrace = 6, + lbrace = 7, + colon = 8, + eof = 9, + null = 10, + }; + + class tokenizer { + public: + explicit tokenizer(buffer buf); + + token next(); + + void backtrack() { + this->_m_buffer.reset(); + } + + [[nodiscard]] std::string const& token_value() const { + return _m_value; + } + + [[nodiscard]] bool eof() const { + return _m_buffer.remaining() == 0; + } + + [[nodiscard]] std::string format_location() const; + + private: + buffer _m_buffer; + uint32_t _m_line {1}, _m_column {1}; + std::string _m_value; + }; + + class parser { + public: + explicit parser(buffer buf); + + model_script parse_script(); + mds::skeleton parse_meshAndTree(); + std::string parse_registerMesh(); + void parse_aniEnum(model_script& into); + void parse_events(mds::animation& 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(); + std::string parse_aniDisable(); + mds::model_tag parse_modelTag(); + + private: + [[nodiscard]] bool eof() const { + return this->_m_stream.eof(); + } + + template + void expect(); + + [[nodiscard]] std::string expect_string(); + [[nodiscard]] std::string expect_keyword(); + void expect_keyword(std::string_view value); + [[nodiscard]] float expect_number(); + [[nodiscard]] int expect_int(); + [[nodiscard]] mds::animation_flags expect_flags(); + + template + bool maybe(); + + [[nodiscard]] std::optional maybe_int(); + [[nodiscard]] std::optional maybe_number(); + [[nodiscard]] std::optional maybe_string(); + [[nodiscard]] bool maybe_keyword(std::string_view value); + [[nodiscard]] std::optional maybe_named(std::string_view name); + + private: + tokenizer _m_stream; + }; +} // namespace phoenix::parser diff --git a/source/script.cc b/source/script.cc index 72af2f09..c6050fc2 100644 --- a/source/script.cc +++ b/source/script.cc @@ -211,6 +211,18 @@ namespace phoenix { return syms; } + symbol* script::add_temporary_strings_symbol() { + symbol sym {}; + sym._m_name = "$PHOENIX_FAKE_STRINGS"; + sym._m_generated = true; + sym._m_type = datatype::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()); + + return &_m_symbols.emplace_back(std::move(sym)); + } + symbol symbol::parse(buffer& in) { symbol sym {}; @@ -272,8 +284,14 @@ namespace phoenix { break; case datatype::instance: sym._m_value = std::shared_ptr {nullptr}; - [[fallthrough]]; + sym._m_address = in.get_int(); + break; case datatype::function: + if (!sym.is_const()) { + sym._m_value = std::unique_ptr(new int32_t[1]); + } + sym._m_address = in.get_int(); + break; case datatype::prototype: sym._m_address = in.get_int(); break; @@ -341,9 +359,6 @@ namespace phoenix { } void symbol::set_string(std::string_view value, std::size_t index, const std::shared_ptr& context) { - if (is_const()) { - throw illegal_const_access(this); - } if (type() != datatype::string) { throw illegal_type_access(this, datatype::string); } @@ -362,9 +377,6 @@ namespace phoenix { } void symbol::set_float(float value, std::size_t index, const std::shared_ptr& context) { - if (is_const()) { - throw illegal_const_access(this); - } if (type() != datatype::float_) { throw illegal_type_access(this, datatype::float_); } @@ -383,9 +395,6 @@ namespace phoenix { } void symbol::set_int(std::int32_t value, std::size_t index, const std::shared_ptr& context) { - if (is_const()) { - throw illegal_const_access(this); - } if (type() != datatype::integer && type() != datatype::function) { throw illegal_type_access(this, datatype::integer); } diff --git a/source/vdfs.cc b/source/vdfs.cc index 37ceeb57..d90c6fc3 100644 --- a/source/vdfs.cc +++ b/source/vdfs.cc @@ -145,7 +145,12 @@ namespace phoenix { in.position(self_offset); } else { - entry._m_data = in.slice(entry.offset, entry.size); + 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; diff --git a/source/vm.cc b/source/vm.cc index 3b746768..2213ba05 100644 --- a/source/vm.cc +++ b/source/vm.cc @@ -116,11 +116,19 @@ namespace phoenix { case opcode::div: a = pop_int(); b = pop_int(); + + if (b == 0) + throw vm_exception {"vm: division by zero"}; + push_int(a / b); break; case opcode::mod: a = pop_int(); b = pop_int(); + + if (b == 0) + throw vm_exception {"vm: division by zero"}; + push_int(a % b); break; case opcode::or_: @@ -258,32 +266,34 @@ namespace phoenix { case opcode::movi: case opcode::movvf: { 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_member() || context != nullptr || !(_m_flags & execution_flag::vm_allow_null_instance_access)) { - ref->set_int(pop_int(), idx, context); + ref->set_int(value, idx, context); } else if (ref->is_member()) { PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); - - if (_m_stack_ptr > 0) { - _m_stack[--_m_stack_ptr].~daedalus_stack_frame(); - } } break; } case opcode::movf: { auto [ref, idx, context] = pop_reference(); + auto value = pop_float(); + + 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(pop_float(), idx, context); + ref->set_float(value, idx, context); } else if (ref->is_member()) { PX_LOGE("vm: accessing member \"", ref->name(), "\" without an instance set"); - - if (_m_stack_ptr > 0) { - _m_stack[--_m_stack_ptr].~daedalus_stack_frame(); - } } break; @@ -292,6 +302,10 @@ namespace phoenix { auto [target, target_idx, context] = pop_reference(); auto source = pop_string(); + if (target->is_const() && !(_m_flags & execution_flag::vm_ignore_const_specifier)) { + throw illegal_const_access(target); + } + if (!target->is_member() || context != nullptr || !(_m_flags & execution_flag::vm_allow_null_instance_access)) { target->set_string(source, target_idx, context); @@ -305,26 +319,77 @@ namespace phoenix { throw vm_exception {"not implemented: movss"}; case opcode::addmovi: { auto [ref, idx, context] = pop_reference(); - auto result = ref->get_int(idx, context) + pop_int(); - ref->set_int(result, idx, context); + auto value = pop_int(); + + 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)) { + 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"); + } + break; } case opcode::submovi: { auto [ref, idx, context] = pop_reference(); - auto result = ref->get_int(idx, context) - pop_int(); - ref->set_int(result, idx, context); + auto value = pop_int(); + + 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)) { + 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"); + } break; } case opcode::mulmovi: { auto [ref, idx, context] = pop_reference(); - auto result = ref->get_int(idx, context) * pop_int(); - ref->set_int(result, idx, context); + auto value = pop_int(); + + 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)) { + 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"); + } + break; } case opcode::divmovi: { auto [ref, idx, context] = pop_reference(); - auto result = ref->get_int(idx, context) / pop_int(); - ref->set_int(result, idx, context); + auto value = pop_int(); + + if (value == 0) { + throw vm_exception {"vm: division by zero"}; + } + + 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)) { + 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"); + } + break; } case opcode::movvi: { diff --git a/source/vobs/camera.cc b/source/vobs/camera.cc index bd986b54..1af99354 100644 --- a/source/vobs/camera.cc +++ b/source/vobs/camera.cc @@ -21,7 +21,7 @@ namespace phoenix::vobs { obj->time_fixed = ctx.read_bool(); // timeIsFixed auto buf = ctx.read_raw_bytes(sizeof(float) * 4 * 4); // originalPose - obj->original_pose = glm::transpose(buf.get_mat4x4()); + obj->original_pose = buf.get_mat4x4(); return obj; } diff --git a/source/vobs/vob.cc b/source/vobs/vob.cc index b3ae2145..480c259e 100644 --- a/source/vobs/vob.cc +++ b/source/vobs/vob.cc @@ -47,7 +47,7 @@ namespace phoenix { obj.bbox = bounding_box::parse(bin); obj.position = bin.get_vec3(); - obj.rotation = glm::transpose(bin.get_mat3x3()); + obj.rotation = bin.get_mat3x3(); std::uint8_t bit0 = bin.get(); std::uint16_t bit1; diff --git a/tests/samples/waran.mds b/tests/samples/waran.mds index b909f893..ac3c24c5 100644 --- a/tests/samples/waran.mds +++ b/tests/samples/waran.mds @@ -28,7 +28,6 @@ Model("TestModel") { *eventCamTremor(11 881 882 883 884) } - } // Another comment. / Maybe even more :> diff --git a/tests/test_buffer.cc b/tests/test_buffer.cc index fa73af82..2a0ca275 100644 --- a/tests/test_buffer.cc +++ b/tests/test_buffer.cc @@ -253,147 +253,86 @@ TEST_SUITE("buffer") { auto buf = phoenix::buffer::of(bytes('\x1A', 0xA1, 'c', 'd')); CHECK_EQ(buf.position(), 0); - SUBCASE("relative") { - 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())); - } + CHECK_EQ(buf.get(), 0x1A); + CHECK_EQ(buf.position(), 1); - SUBCASE("absolute") { - CHECK_EQ(buf.get(1), 0xA1); - CHECK_EQ(buf.position(), 0); + CHECK_EQ(buf.get(), 0xA1); + CHECK_EQ(buf.position(), 2); - std::array array {}; - buf.get(2, array.data(), array.size()); + std::array array {}; + buf.get(array.data(), array.size()); - CHECK_EQ(buf.position(), 0); - CHECK(std::equal(array.begin(), array.end(), bytes_str("cd").begin())); + 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(4)); - CHECK_THROWS((void) buf.get(3, array.data(), array.size())); - } + 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); - SUBCASE("relative") { - 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()); - } + CHECK_EQ(buf.get_char(), 'a'); + CHECK_EQ(buf.position(), 1); - SUBCASE("absolute") { - CHECK_EQ(buf.get_char(1), 'b'); - CHECK_EQ(buf.position(), 0); + CHECK_EQ(buf.get_char(), 'b'); + CHECK_EQ(buf.position(), 2); - CHECK_THROWS((void) buf.get_char(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); - SUBCASE("relative") { - 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()); - } + CHECK_EQ(buf.get_short(), -1); + CHECK_EQ(buf.position(), 2); - SUBCASE("absolute") { - CHECK_EQ(buf.get_short(2), 1); - CHECK_EQ(buf.position(), 0); + CHECK_EQ(buf.get_short(), 1); + CHECK_EQ(buf.position(), 4); - CHECK_THROWS((void) buf.get_short(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); - SUBCASE("relative") { - 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()); - } + CHECK_EQ(buf.get_ushort(), 0xFF'FF); + CHECK_EQ(buf.position(), 2); - SUBCASE("absolute") { - CHECK_EQ(buf.get_ushort(2), 1); - CHECK_EQ(buf.position(), 0); + CHECK_EQ(buf.get_ushort(), 1); + CHECK_EQ(buf.position(), 4); - CHECK_THROWS((void) buf.get_ushort(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); - SUBCASE("relative") { - 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()); - } + CHECK_EQ(buf.get_int(), -1); + CHECK_EQ(buf.position(), 4); - SUBCASE("absolute") { - CHECK_EQ(buf.get_int(4), 1); - CHECK_EQ(buf.position(), 0); + CHECK_EQ(buf.get_int(), 1); + CHECK_EQ(buf.position(), 8); - CHECK_THROWS((void) buf.get_int(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); - SUBCASE("relative") { - CHECK_EQ(buf.get_uint(), 0xFFFF'FFFF); - CHECK_EQ(buf.position(), 4); + 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()); - } + CHECK_EQ(buf.get_uint(), 1); + CHECK_EQ(buf.position(), 8); - SUBCASE("absolute") { - CHECK_EQ(buf.get_uint(4), 1); - CHECK_EQ(buf.position(), 0); - - CHECK_THROWS((void) buf.get_uint(8)); - } + CHECK_THROWS((void) buf.get_uint()); } TEST_CASE("buffer(get_long)") { @@ -422,22 +361,13 @@ TEST_SUITE("buffer") { 0xFF)); CHECK_EQ(buf.position(), 0); - SUBCASE("relative") { - CHECK_EQ(buf.get_long(), -1); - CHECK_EQ(buf.position(), 8); + CHECK_EQ(buf.get_long(), -1); + CHECK_EQ(buf.position(), 8); - CHECK_EQ(buf.get_long(), 1); - CHECK_EQ(buf.position(), 16); + CHECK_EQ(buf.get_long(), 1); + CHECK_EQ(buf.position(), 16); - CHECK_THROWS((void) buf.get_long()); - } - - SUBCASE("absolute") { - CHECK_EQ(buf.get_long(8), 1); - CHECK_EQ(buf.position(), 0); - - CHECK_THROWS((void) buf.get_long(16)); - } + CHECK_THROWS((void) buf.get_long()); } TEST_CASE("buffer(get_ulong)") { @@ -466,22 +396,13 @@ TEST_SUITE("buffer") { 0xFF)); CHECK_EQ(buf.position(), 0); - SUBCASE("relative") { - CHECK_EQ(buf.get_ulong(), 0xFFFFFFFF'FFFFFFFFL); - CHECK_EQ(buf.position(), 8); + CHECK_EQ(buf.get_ulong(), 0xFFFFFFFF'FFFFFFFFL); + CHECK_EQ(buf.position(), 8); - CHECK_EQ(buf.get_ulong(), 1); - CHECK_EQ(buf.position(), 16); + CHECK_EQ(buf.get_ulong(), 1); + CHECK_EQ(buf.position(), 16); - CHECK_THROWS((void) buf.get_ulong()); - } - - SUBCASE("absolute") { - CHECK_EQ(buf.get_ulong(8), 1); - CHECK_EQ(buf.position(), 0); - - CHECK_THROWS((void) buf.get_ulong(16)); - } + CHECK_THROWS((void) buf.get_ulong()); } TEST_CASE("buffer(get_float)") { @@ -489,22 +410,13 @@ TEST_SUITE("buffer") { auto buf = phoenix::buffer::of(bytes(0x52, 0x58, 0xD2, 0x43, 0x0A, 0xD7, 0x8A, 0xC2, 0xFF, 0xFF, 0xFF)); CHECK_EQ(buf.position(), 0); - SUBCASE("relative") { - CHECK_EQ(buf.get_float(), 420.69f); - CHECK_EQ(buf.position(), 4); + 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_EQ(buf.get_float(), -69.420f); + CHECK_EQ(buf.position(), 8); - CHECK_THROWS((void) buf.get_float()); - } - - SUBCASE("absolute") { - CHECK_EQ(buf.get_float(4), -69.420f); - CHECK_EQ(buf.position(), 0); - - CHECK_THROWS((void) buf.get_float(8)); - } + CHECK_THROWS((void) buf.get_float()); } TEST_CASE("buffer(get_double)") { @@ -533,44 +445,26 @@ TEST_SUITE("buffer") { 0xFF)); CHECK_EQ(buf.position(), 0); - SUBCASE("relative") { - 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()); - } + CHECK_EQ(buf.get_double(), 420.69); + CHECK_EQ(buf.position(), 8); - SUBCASE("absolute") { - CHECK_EQ(buf.get_double(8), -69.420); - CHECK_EQ(buf.position(), 0); + CHECK_EQ(buf.get_double(), -69.420); + CHECK_EQ(buf.position(), 16); - CHECK_THROWS((void) buf.get_double(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', '!')); - SUBCASE("relative") { - 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)); - } + CHECK_EQ(buf.get_string(2), "Hi"); + CHECK_EQ(buf.position(), 2); - SUBCASE("absolute") { - CHECK_EQ(buf.get_string(2, 13), "Hello, World!"); - CHECK_EQ(buf.position(), 0); + CHECK_EQ(buf.get_string(13), "Hello, World!"); + CHECK_EQ(buf.position(), 15); - CHECK_THROWS((void) buf.get_string(14, 2)); - } + CHECK_THROWS((void) buf.get_string(1)); } TEST_CASE("buffer(get_line)") { @@ -597,27 +491,18 @@ TEST_SUITE("buffer") { '!', '\n')); - SUBCASE("relative") { - 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_EQ(buf.get_line(true), "Hi"); + CHECK_EQ(buf.position(), 7); + buf.mark(); - CHECK_THROWS((void) buf.get_line()); - } + CHECK_EQ(buf.get_line(true), "Hello,\\tWorld!"); + CHECK_EQ(buf.position(), 22); - SUBCASE("absolute") { - CHECK_EQ(buf.get_line_at(1), "i"); - CHECK_EQ(buf.position(), 0); + buf.reset(); + CHECK_EQ(buf.get_line_escaped(true), "Hello,\tWorld!"); + CHECK_EQ(buf.position(), 22); - CHECK_THROWS((void) buf.get_line_at(22)); - } + CHECK_THROWS((void) buf.get_line()); } TEST_CASE("buffer(get_vec2)" * doctest::skip()) { @@ -632,69 +517,6 @@ TEST_SUITE("buffer") { // TODO: Stub } - TEST_CASE("buffer(mismatch)") { - auto buf = phoenix::buffer::of(bytes('H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\n')); - - SUBCASE("relative functional") { - buf.position(5); - - auto index = buf.mismatch([](char chr) { return chr == 'W'; }); - CHECK_EQ(index, 2); - CHECK_EQ(buf.position(), 5); - - index = buf.mismatch([](char) { return false; }); - CHECK_EQ(index, -1); - } - - SUBCASE("relative buffer") { - auto comp = phoenix::buffer::of(bytes('W', 'o', 'r', 'l', 'd', '.')); - auto index = buf.mismatch(comp); - CHECK_EQ(index, 0); - CHECK_EQ(buf.position(), 0); - CHECK_EQ(comp.position(), 0); - - buf.position(7); - index = buf.mismatch(comp); - CHECK_EQ(index, 5); - CHECK_EQ(buf.position(), 7); - CHECK_EQ(comp.position(), 0); - - comp.limit(comp.limit() - 1); - index = buf.mismatch(comp); - CHECK_EQ(index, -1); - CHECK_EQ(buf.position(), 7); - CHECK_EQ(comp.position(), 0); - } - - SUBCASE("absolute functional") { - auto index = buf.mismatch(5, [](char chr) { return chr == 'W'; }); - CHECK_EQ(index, 2); - CHECK_EQ(buf.position(), 0); - - index = buf.mismatch(7, [](char) { return false; }); - CHECK_EQ(index, -1); - } - - SUBCASE("absolute buffer") { - auto comp = phoenix::buffer::of(bytes('W', 'o', 'r', 'l', 'd', '.')); - auto index = buf.mismatch(0, comp); - CHECK_EQ(index, 0); - CHECK_EQ(buf.position(), 0); - CHECK_EQ(comp.position(), 0); - - index = buf.mismatch(7, comp); - CHECK_EQ(index, 5); - CHECK_EQ(buf.position(), 0); - CHECK_EQ(comp.position(), 0); - - comp.limit(comp.limit() - 1); - index = buf.mismatch(7, comp); - CHECK_EQ(index, -1); - CHECK_EQ(buf.position(), 0); - CHECK_EQ(comp.position(), 0); - } - } - TEST_CASE("buffer(put*)") { auto buf = phoenix::buffer::allocate(57); diff --git a/tests/test_model_script.cc b/tests/test_model_script.cc index a8bec009..ca1f55ad 100644 --- a/tests/test_model_script.cc +++ b/tests/test_model_script.cc @@ -5,6 +5,7 @@ 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); diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 07b49ed6..f787e887 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -1,5 +1,11 @@ include(FetchContent) +# This warning only occurs in CMake 3.24 or greater. +# https://cmake.org/cmake/help/latest/policy/CMP0135.html#cmp0135 +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24") +cmake_policy(SET CMP0135 NEW) +endif () + set(BUILD_STATIC_LIBS ON) set(BUILD_SHARED_LIBS OFF) set(BUILD_SQUISH_WITH_OPENMP OFF CACHE BOOL "" FORCE) @@ -27,7 +33,6 @@ endfunction() px_add_dependency(doctest https://github.com/doctest/doctest/archive/refs/tags/v2.4.9.zip d1563419fa370c34c90e028c2e903a70c8dc07b2) px_add_dependency(mio https://github.com/mandreyel/mio/archive/3f86a95c0784d73ce6815237ec33ed25f233b643.zip 62a0e43b07a6b66e415ad01f4b225c72a3c9935b) -px_add_dependency(lexy https://github.com/foonathan/lexy/releases/download/v2022.05.1/lexy-src.zip 5b1599a1a3fc177c1a1bd78ec4ddf399b22a2b51) px_add_dependency(glm https://github.com/g-truc/glm/releases/download/0.9.9.8/glm-0.9.9.8.zip 45408897f419944fb28d8fd835791f237be2ec19) px_add_dependency(libsquish https://downloads.sourceforge.net/project/libsquish/libsquish-1.15.tgz 51844b9a8bc815a27e2cc0ffbede5fee3ef75110) diff --git a/vendor/lexy b/vendor/lexy deleted file mode 160000 index 5b7095da..00000000 --- a/vendor/lexy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5b7095dab03041b4daf7c6c521118645491b7062