diff --git a/Makefile b/Makefile index 47756a9b..ab84a3e2 100755 --- a/Makefile +++ b/Makefile @@ -410,7 +410,7 @@ endif build_assets: $(info Building Assets...) - $(TOOLS_DIR)/dkr_assets_tool build -o $(ASSETS_OUTPUT) -dkrv $(REGION).$(VERSION) $(MODDED_ARG) >&2 || echo FAIL + @$(TOOLS_DIR)/dkr_assets_tool build -o $(ASSETS_OUTPUT) -dkrv $(REGION).$(VERSION) $(MODDED_ARG) >&2 || echo FAIL ############################### @@ -447,11 +447,6 @@ $(BUILD_DIR)/%.s.o: %.s | build_assets $(call print,Assembling:,$<,$@) $(V)$(AS) $(ASFLAGS) -MD $(BUILD_DIR)/$*.d -o $@ $< -# Specifically override the assets bin output location to match what splat output to the ld file. -$(BUILD_DIR)/asm/assets/assets.s.o: asm/assets/assets.s | build_assets - $(call print,Assembling Assets:,$<,$@) - $(V)$(AS) $(ASFLAGS) -o $(BUILD_DIR)/assets/assets.bin.o $< - # Specifically override the header file from what splat extracted to be replaced by what we have in the hasm folder $(BUILD_DIR)/asm/header.s.o: src/hasm/header.s | build_assets $(call print,Assembling Header:,$<,$@) diff --git a/tools/dkr_assets_tool_src/builder/buildInfo.cpp b/tools/dkr_assets_tool_src/builder/buildInfo.cpp index a500249b..be98d3da 100644 --- a/tools/dkr_assets_tool_src/builder/buildInfo.cpp +++ b/tools/dkr_assets_tool_src/builder/buildInfo.cpp @@ -6,69 +6,6 @@ using namespace DkrAssetsTool; -/**************************************************************************/ - -BuildInfoContext::BuildInfoContext(CContext &cContext, BuildStats &stats, BuildInfoCollection &collection) - : _cContext(cContext), _stats(stats), _collection(collection) {} - -CContext &BuildInfoContext::get_c_context() const { - return _cContext; -} - -BuildStats &BuildInfoContext::get_stats() const { - return _stats; -} - -BuildInfoCollection &BuildInfoContext::get_collection() const { - return _collection; -} - -void BuildInfoContext::init_obj_beh_to_entry_map() { - if(!_objBehaviorToEntry.empty()) { - return; // Don't redo this if the map is already filled! - } - - // TODO: This was copy-pasted from extract/config.cpp; need to refactor to not require the config file. - - fs::path pathToConfig = GlobalSettings::get_decomp_path("tools", "tools/") / "dkr_assets_tool_extract.json"; - auto tryGetConfigFile = JsonHelper::get_file(pathToConfig); - DebugHelper::assert_(tryGetConfigFile.has_value(), - "(BuildInfoContext::init_obj_beh_to_entry_map) Could not find the config file at: ", pathToConfig); - JsonFile &configJson = tryGetConfigFile.value(); - - CContext &cContext = get_c_context(); - - CEnum *objBehaviors = cContext.get_enum("ObjectBehaviours"); - - DebugHelper::assert_(objBehaviors != nullptr, - "(BuildInfoContext::init_obj_beh_to_entry_map) The enum ObjectBehaviours could not be loaded!"); - - std::vector defaultObjEntriesOrder; - configJson.get_array("/misc/default-object-entries-order", defaultObjEntriesOrder); - - for(int i = 0; i < 128; i++) { - std::string symbol; - DebugHelper::assert_(objBehaviors->get_symbol_of_value(i, symbol), - "(BuildInfoContext::init_obj_beh_to_entry_map) Could not get a symbol for the value ", i, " in the ObjectBehaviors enum."); - - CStruct *entryStruct = cContext.get_struct(defaultObjEntriesOrder[i]); - DebugHelper::assert_(entryStruct != nullptr, - "(BuildInfoContext::init_obj_beh_to_entry_map) Could not find struct \"", defaultObjEntriesOrder[i], "\""); - - _objBehaviorToEntry[symbol] = defaultObjEntriesOrder[i]; - } -} - -std::string BuildInfoContext::get_object_entry_from_behavior(std::string objBehavior) const { - DebugHelper::assert_(_objBehaviorToEntry.find(objBehavior) != _objBehaviorToEntry.end(), - "(AssetExtractConfig::get_object_entry_from_behavior) ", objBehavior, - " was not in the _objBehaviorToEntry map!"); - - return _objBehaviorToEntry.at(objBehavior); -} - -/**************************************************************************/ - BuildInfo::BuildInfo() { DebugHelper::error("Must not initalize empty build info."); } @@ -85,15 +22,15 @@ BuildInfo::BuildInfo(JsonFile *src, const fs::path &dir) } */ -BuildInfo::BuildInfo(std::string buildId, const JsonFile &src, size_t fileIndex, const fs::path &dir, const BuildInfoContext &infoContext) - : _dstPath(""), _localDirectory(dir), _jsonFile(src), _infoContext(infoContext), _buildId(buildId), _fileIndex(fileIndex) { +BuildInfo::BuildInfo(std::string buildId, std::string buildSectionId, const JsonFile &src, size_t fileIndex, const fs::path &dir, const BuildInfoContext &infoContext) + : _dstPath(""), _localDirectory(dir), _jsonFile(src), _infoContext(infoContext), _buildId(buildId), _buildSectionId(buildSectionId), _fileIndex(fileIndex) { _buildType = BUILD_TO_BINARY; const JsonFile &jsonFile = get_src_json_file(); _type = jsonFile.get_string("/type", "NoType"); } -BuildInfo::BuildInfo(std::string buildId, const std::vector &outData, size_t fileIndex, const fs::path &dir, const BuildInfoContext &infoContext) - : out(outData), _dstPath(""), _localDirectory(dir), _jsonFile(std::nullopt), _infoContext(infoContext), _buildId(buildId), _type("Binary"), _fileIndex(fileIndex) { +BuildInfo::BuildInfo(std::string buildId, std::string buildSectionId, const std::vector &outData, size_t fileIndex, const fs::path &dir, const BuildInfoContext &infoContext) + : out(outData), _dstPath(""), _localDirectory(dir), _jsonFile(std::nullopt), _infoContext(infoContext), _buildId(buildId), _buildSectionId(buildSectionId), _type("Binary"), _fileIndex(fileIndex) { _buildType = BUILD_TO_BINARY; } @@ -145,11 +82,11 @@ CContext &BuildInfo::get_c_context() const { return infoContext.get_c_context(); } -BuildStats &BuildInfo::get_stats() const { +BuildTextureCache &BuildInfo::get_texture_cache() const { const BuildInfoContext &infoContext = _infoContext.value(); - return infoContext.get_stats(); + return infoContext.get_texture_cache(); } - + BuildInfoCollection &BuildInfo::get_collection() const { const BuildInfoContext &infoContext = _infoContext.value(); return infoContext.get_collection(); @@ -175,6 +112,10 @@ std::string BuildInfo::get_build_id() const { return _buildId; } +std::string BuildInfo::get_section_build_id() const { + return _buildSectionId; +} + size_t BuildInfo::get_file_index() const { return _fileIndex; } diff --git a/tools/dkr_assets_tool_src/builder/buildInfo.h b/tools/dkr_assets_tool_src/builder/buildInfo.h index a66fb8e3..7c7dd74a 100644 --- a/tools/dkr_assets_tool_src/builder/buildInfo.h +++ b/tools/dkr_assets_tool_src/builder/buildInfo.h @@ -10,6 +10,8 @@ #include "helpers/jsonHelper.h" #include "helpers/c/cContext.h" +#include "builder/buildInfoContext.h" + namespace DkrAssetsTool { typedef enum BuildInfoType { @@ -17,38 +19,17 @@ typedef enum BuildInfoType { BUILD_TO_BINARY // When building all assets } BuildInfoType; -class BuildStats; class BuildInfoCollection; -class BuildInfoContext { -public: - BuildInfoContext(CContext &cContext, BuildStats &stats, BuildInfoCollection &collection); - - CContext &get_c_context() const; - BuildStats &get_stats() const; - BuildInfoCollection &get_collection() const; - - // Used in BuildObjectMap - void init_obj_beh_to_entry_map(); - std::string get_object_entry_from_behavior(std::string objBehavior) const; -private: - std::reference_wrapper _cContext; - std::reference_wrapper _stats; - std::reference_wrapper _collection; - - std::unordered_map _objBehaviorToEntry; -}; - class BuildInfo { public: BuildInfo(); //BuildInfo(std::string buildId, JsonFile *src, const fs::path &dst, const fs::path &dir); // BUILD_TO_FILE, TODO //BuildInfo(std::string buildId, JsonFile *src, const fs::path &dir); // BUILD_TO_BINARY - BuildInfo(std::string buildId, const JsonFile &src, size_t fileIndex, const fs::path &dir, const BuildInfoContext &infoContext); // BUILD_TO_BINARY - BuildInfo(std::string buildId, const std::vector &outData, size_t fileIndex, const fs::path &dir, const BuildInfoContext &infoContext); // BUILD_TO_BINARY + BuildInfo(std::string buildId, std::string buildSectionId, const JsonFile &src, size_t fileIndex, const fs::path &dir, const BuildInfoContext &infoContext); + BuildInfo(std::string buildId, std::string buildSectionId, const std::vector &outData, size_t fileIndex, const fs::path &dir, const BuildInfoContext &infoContext); ~BuildInfo(); - //JsonFile *srcFile; // Deprecated! std::vector out; BuildInfoType get_build_type() const; @@ -63,14 +44,10 @@ public: fs::path get_dst_folder() const; fs::path get_dst_filename() const; - //void set_c_context(CContext &cContext); - //void set_stats(BuildStats &stats); - //void set_collection(BuildInfoCollection &collection); - fs::path get_path_to_directory() const; CContext &get_c_context() const; - BuildStats &get_stats() const; + BuildTextureCache &get_texture_cache() const; BuildInfoCollection &get_collection() const; const JsonFile &get_src_json_file() const; @@ -78,6 +55,7 @@ public: std::string get_type() const; std::string get_build_id() const; + std::string get_section_build_id() const; size_t get_file_index() const; bool is_complete() const; @@ -96,6 +74,7 @@ private: std::optional> _infoContext; std::string _buildId; + std::string _buildSectionId; std::string _type; size_t _fileIndex; BuildInfoType _buildType; diff --git a/tools/dkr_assets_tool_src/builder/buildInfoCollection.cpp b/tools/dkr_assets_tool_src/builder/buildInfoCollection.cpp index e8cc5b34..f875ad43 100644 --- a/tools/dkr_assets_tool_src/builder/buildInfoCollection.cpp +++ b/tools/dkr_assets_tool_src/builder/buildInfoCollection.cpp @@ -38,7 +38,7 @@ void BuildInfoCollection::add_build_info(std::string sectionBuildId, std::string "(BuildInfoCollection::add_build_info) Max number of assets reached! Limit was ", MAX_NUMBER_OF_BUILD_INFOS); size_t fileIndex = _buildInfoSections[sectionBuildId].size(); - _buildInfos.emplace_back(buildId, src, fileIndex, dir, infoContext); + _buildInfos.emplace_back(buildId, sectionBuildId, src, fileIndex, dir, infoContext); // Add the index to the section. _buildInfoSections[sectionBuildId].emplace_back(index); @@ -59,7 +59,7 @@ size_t BuildInfoCollection::add_deferred_build_info(std::string sectionBuildId, "(BuildInfoCollection::add_build_info) Max number of assets reached! Limit was ", MAX_NUMBER_OF_BUILD_INFOS); size_t fileIndex = _buildInfoSections[sectionBuildId].size(); - _buildInfos.emplace_back(buildId, out, fileIndex, dir, infoContext); + _buildInfos.emplace_back(buildId, sectionBuildId, out, fileIndex, dir, infoContext); // Do not process deferred assets. _buildInfos.back().done(); @@ -89,7 +89,7 @@ void BuildInfoCollection::add_deferred_build_info(std::string sectionBuildId, st DebugHelper::assert_(index < MAX_NUMBER_OF_BUILD_INFOS, "(BuildInfoCollection::add_build_info) Max number of assets reached! Limit was ", MAX_NUMBER_OF_BUILD_INFOS); - _buildInfos.emplace_back(buildId, out, fileIndex, dir, infoContext); + _buildInfos.emplace_back(buildId, sectionBuildId, out, fileIndex, dir, infoContext); // Do not process deferred assets. _buildInfos.back().done(); @@ -100,7 +100,7 @@ void BuildInfoCollection::add_deferred_build_info(std::string sectionBuildId, st _buildInfoMutex.unlock(); } -void BuildInfoCollection::run_builds(std::function callbackFunction) { +void BuildInfoCollection::run_builds(std::function callbackFunction, const std::vector §ionsOrder) { size_t threadCount = GlobalSettings::get_max_thread_count(); bool multithreaded = threadCount != 1; DebugHelper::info_verbose("Using ", threadCount, " thread", (multithreaded ? "s" : "")); @@ -108,8 +108,12 @@ void BuildInfoCollection::run_builds(std::function callbackFu if(multithreaded) { // Multi-threaded (Better for performance) ThreadPool pool(threadCount); - for(auto &pair : _buildInfoSections) { - for(int buildInfoIndex : pair.second) { + for(const std::string §ionBuildId : sectionsOrder) { + if(_buildInfoSections.find(sectionBuildId) == _buildInfoSections.end()) { + // Skip empty sections. + continue; + } + for(int buildInfoIndex : _buildInfoSections[sectionBuildId]) { BuildInfo &info = _buildInfos[buildInfoIndex]; pool.enqueue([&info, &callbackFunction] { if(!info.is_complete()) { @@ -121,8 +125,12 @@ void BuildInfoCollection::run_builds(std::function callbackFu } } else { // Single-threaded (Better for debugging) - for(auto &pair : _buildInfoSections) { - for(int buildInfoIndex : pair.second) { + for(const std::string §ionBuildId : sectionsOrder) { + if(_buildInfoSections.find(sectionBuildId) == _buildInfoSections.end()) { + // Skip empty sections. + continue; + } + for(int buildInfoIndex : _buildInfoSections[sectionBuildId]) { BuildInfo &info = _buildInfos[buildInfoIndex]; if(!info.is_complete()) { callbackFunction(info); diff --git a/tools/dkr_assets_tool_src/builder/buildInfoCollection.h b/tools/dkr_assets_tool_src/builder/buildInfoCollection.h index f84451fd..147697a4 100644 --- a/tools/dkr_assets_tool_src/builder/buildInfoCollection.h +++ b/tools/dkr_assets_tool_src/builder/buildInfoCollection.h @@ -26,7 +26,7 @@ public: void add_deferred_build_info(std::string sectionBuildId, std::string buildId, size_t fileIndex, const std::vector &out, const fs::path &dir, const BuildInfoContext &infoContext); - void run_builds(std::function callbackFunction); + void run_builds(std::function callbackFunction, const std::vector §ionsOrder); void get_infos_for_section(std::string sectionBuildId, std::function callbackFunction); @@ -49,8 +49,6 @@ private: // Note: Should only ever be added to! Do NOT erase any entries during the building process! std::vector _buildInfos; - std::vector _sectionOrder; - // std::unordered_map> _buildInfoSections; diff --git a/tools/dkr_assets_tool_src/builder/buildInfoContext.cpp b/tools/dkr_assets_tool_src/builder/buildInfoContext.cpp new file mode 100644 index 00000000..db42caca --- /dev/null +++ b/tools/dkr_assets_tool_src/builder/buildInfoContext.cpp @@ -0,0 +1,66 @@ +#include "buildInfo.h" + +#include "misc/globalSettings.h" +#include "helpers/c/cContext.h" +#include "helpers/jsonHelper.h" + +using namespace DkrAssetsTool; + +BuildInfoContext::BuildInfoContext(CContext &cContext, BuildTextureCache &textureCache, BuildInfoCollection &collection) + : _cContext(cContext), _textureCache(textureCache), _collection(collection) {} + +CContext &BuildInfoContext::get_c_context() const { + return _cContext; +} + +BuildTextureCache &BuildInfoContext::get_texture_cache() const { + return _textureCache; +} + +BuildInfoCollection &BuildInfoContext::get_collection() const { + return _collection; +} + +void BuildInfoContext::init_obj_beh_to_entry_map() { + if(!_objBehaviorToEntry.empty()) { + return; // Don't redo this if the map is already filled! + } + + // TODO: This was copy-pasted from extract/config.cpp; need to refactor to not require the config file. + + fs::path pathToConfig = GlobalSettings::get_decomp_path("tools", "tools/") / "dkr_assets_tool_extract.json"; + auto tryGetConfigFile = JsonHelper::get_file(pathToConfig); + DebugHelper::assert_(tryGetConfigFile.has_value(), + "(BuildInfoContext::init_obj_beh_to_entry_map) Could not find the config file at: ", pathToConfig); + JsonFile &configJson = tryGetConfigFile.value(); + + CContext &cContext = get_c_context(); + + CEnum *objBehaviors = cContext.get_enum("ObjectBehaviours"); + + DebugHelper::assert_(objBehaviors != nullptr, + "(BuildInfoContext::init_obj_beh_to_entry_map) The enum ObjectBehaviours could not be loaded!"); + + std::vector defaultObjEntriesOrder; + configJson.get_array("/misc/default-object-entries-order", defaultObjEntriesOrder); + + for(int i = 0; i < 128; i++) { + std::string symbol; + DebugHelper::assert_(objBehaviors->get_symbol_of_value(i, symbol), + "(BuildInfoContext::init_obj_beh_to_entry_map) Could not get a symbol for the value ", i, " in the ObjectBehaviors enum."); + + CStruct *entryStruct = cContext.get_struct(defaultObjEntriesOrder[i]); + DebugHelper::assert_(entryStruct != nullptr, + "(BuildInfoContext::init_obj_beh_to_entry_map) Could not find struct \"", defaultObjEntriesOrder[i], "\""); + + _objBehaviorToEntry[symbol] = defaultObjEntriesOrder[i]; + } +} + +std::string BuildInfoContext::get_object_entry_from_behavior(std::string objBehavior) const { + DebugHelper::assert_(_objBehaviorToEntry.find(objBehavior) != _objBehaviorToEntry.end(), + "(AssetExtractConfig::get_object_entry_from_behavior) ", objBehavior, + " was not in the _objBehaviorToEntry map!"); + + return _objBehaviorToEntry.at(objBehavior); +} diff --git a/tools/dkr_assets_tool_src/builder/buildInfoContext.h b/tools/dkr_assets_tool_src/builder/buildInfoContext.h new file mode 100644 index 00000000..95f58086 --- /dev/null +++ b/tools/dkr_assets_tool_src/builder/buildInfoContext.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "helpers/fileHelper.h" +#include "helpers/jsonHelper.h" +#include "helpers/c/cContext.h" + +#include "builder/textureCache.h" + +namespace DkrAssetsTool { + +class BuildInfoCollection; + +class BuildInfoContext { +public: + BuildInfoContext(CContext &cContext, BuildTextureCache &textureCache, BuildInfoCollection &collection); + + CContext &get_c_context() const; + BuildTextureCache &get_texture_cache() const; + BuildInfoCollection &get_collection() const; + + // Used in BuildObjectMap + void init_obj_beh_to_entry_map(); + std::string get_object_entry_from_behavior(std::string objBehavior) const; + +private: + std::reference_wrapper _cContext; + std::reference_wrapper _textureCache; + std::reference_wrapper _collection; + + std::unordered_map _objBehaviorToEntry; +}; + +} \ No newline at end of file diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModel.cpp b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModel.cpp index 0601ab8f..1096d8f9 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModel.cpp +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModel.cpp @@ -53,12 +53,16 @@ void BuildModel::generate_object_model(BuildInfo &info) { DkrTriangle *triangles = reinterpret_cast(&info.out[objectModelHeader->triangles]); DkrBatch *batches = reinterpret_cast(&info.out[objectModelHeader->batches]); + bool hasAnimatedTexture = false; for(size_t materialIndex = 0; materialIndex < numberOfTextures; materialIndex++) { - _materials[materialIndex].write_to(&textures[materialIndex], info); + BuildModelMaterial &material = _materials[materialIndex]; + material.write_to(&textures[materialIndex], info); + hasAnimatedTexture = hasAnimatedTexture || material.is_texture_animated(); } + objectModelHeader->hasAnimatedTexture = (int)hasAnimatedTexture; for(auto &block : _blocks) { - block.write_batches(_materialIds, batches, vertices, triangles); + block.write_batches(_materialIds, _materials, batches, vertices, triangles); } bool debugKeepUncompressed = GlobalSettings::get_value("/debug/keep-uncompressed", false); diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBatch.cpp b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBatch.cpp index 945acf28..b9acbc4a 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBatch.cpp +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBatch.cpp @@ -46,23 +46,39 @@ size_t BuildModelBatch::number_of_vertices() { return _vertices.size(); } -void BuildModelBatch::write_batch(const std::map &materialIds, DkrBatch* outBatch, DkrVertex*& outVertices, DkrTriangle*& outTriangles) { +void BuildModelBatch::write_batch(const std::map &materialIds, std::vector &materials, + DkrBatch* outBatch, DkrVertex*& outVertices, DkrTriangle*& outTriangles) { + uint32_t flags = 0x00000003; + bool materialDoubleSided = false; + if(_materialId.has_value()) { outBatch->textureIndex = materialIds.at(_materialId.value()); + BuildModelMaterial &material = materials[outBatch->textureIndex]; + if(material.is_texture_animated()) { + flags |= 0x10000; // BATCH_FLAGS_TEXTURE_ANIM + } + materialDoubleSided = material.is_texture_double_sided(); } else { outBatch->textureIndex = 0xFF; // No texture } - //outBatch->lightSource = 0; // 0xFF = vertex colors, 0 = Use generated normals. - outBatch->flags = 0x00000003; // TODO + outBatch->flags = flags; + bool useVertexColors = false; for(auto &vertex : _vertices) { vertex.write_to(outVertices); outVertices++; // Next out vertex. + if(!vertex.is_color_white()) { + useVertexColors = true; + } } + outBatch->lightSource = useVertexColors ? 0xFF : 0x00; + + uint8_t triFlags = materialDoubleSided ? 0x40 : 0x00; + for(auto &tri : _triangles) { - outTriangles->flags = 0x40; // double-sided (debug) + outTriangles->flags = triFlags; outTriangles->vi0 = tri.a; outTriangles->vi1 = tri.b; outTriangles->vi2 = tri.c; diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBatch.h b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBatch.h index a9b0d39e..b0eabe5c 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBatch.h +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBatch.h @@ -7,6 +7,7 @@ #include "helpers/mathHelper.h" #include "buildModelVertex.h" +#include "buildModelMaterial.h" #include "fileTypes/vertex.hpp" #include "fileTypes/triangle.hpp" @@ -35,7 +36,8 @@ public: size_t number_of_triangles(); size_t number_of_vertices(); - void write_batch(const std::map &materialIds, DkrBatch* outBatch, DkrVertex*& outVertices, DkrTriangle*& outTriangles); + void write_batch(const std::map &materialIds, std::vector &materials, + DkrBatch* outBatch, DkrVertex*& outVertices, DkrTriangle*& outTriangles); private: std::vector _triangles; diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBlock.cpp b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBlock.cpp index 39949cab..9990d935 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBlock.cpp +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBlock.cpp @@ -61,11 +61,12 @@ size_t BuildModelBlock::number_of_batches_to_allocate() { return _batches.size() + 1; // Must have an extra entry to calculate the sizes for the last batch. } -void BuildModelBlock::write_batches(const std::map &materialIds, DkrBatch*& outBatches, DkrVertex*& outVertices, DkrTriangle*& outTriangles) { +void BuildModelBlock::write_batches(const std::map &materialIds, std::vector &materials, + DkrBatch*& outBatches, DkrVertex*& outVertices, DkrTriangle*& outTriangles) { size_t vertexCount = 0; size_t triCount = 0; for(auto &batch : _batches) { - batch.write_batch(materialIds, outBatches, outVertices, outTriangles); + batch.write_batch(materialIds, materials, outBatches, outVertices, outTriangles); outBatches->verticesOffset = vertexCount; outBatches->trianglesOffset = triCount; outBatches->unk1 = (int8_t)batch.number_of_vertices(); diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBlock.h b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBlock.h index c7709e62..4d5c9f7c 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBlock.h +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelBlock.h @@ -5,6 +5,7 @@ #include #include "buildModelVertex.h" +#include "buildModelMaterial.h" #include "buildModelBatch.h" #include "fileTypes/vertex.hpp" @@ -25,7 +26,8 @@ public: size_t number_of_batches(); size_t number_of_batches_to_allocate(); - void write_batches(const std::map &materialIds, DkrBatch*& outBatches, DkrVertex*& outVertices, DkrTriangle*& outTriangles); + void write_batches(const std::map &materialIds, std::vector &materials, + DkrBatch*& outBatches, DkrVertex*& outVertices, DkrTriangle*& outTriangles); private: std::vector _batches; diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelMaterial.cpp b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelMaterial.cpp index d994498a..c4c12c7d 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelMaterial.cpp +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelMaterial.cpp @@ -36,6 +36,26 @@ void BuildModelMaterial::write_to(DkrTextureInfo *outTexInfo, BuildInfo &info) { bool BuildModelMaterial::has_texture() const { return _texturePath.has_value(); } + +bool BuildModelMaterial::is_texture_animated() { + if(!_texturePath.has_value()) { + return false; + } + if(!_isTextureAnimated.has_value()) { + _isTextureAnimated = ImageHelper::guess_if_texture_is_animated(_texturePath.value()); + } + return _isTextureAnimated.value(); +} + +bool BuildModelMaterial::is_texture_double_sided() { + if(!_texturePath.has_value()) { + return false; + } + if(!_isTextureDoubleSided.has_value()) { + _isTextureDoubleSided = ImageHelper::guess_if_texture_double_sided(_texturePath.value()); + } + return _isTextureDoubleSided.value(); +} void BuildModelMaterial::get_texture_width_and_height(int &outWidth, int &outHeight) { if(!_texturePath.has_value()) { diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelMaterial.h b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelMaterial.h index d59dfdd6..5ec5097c 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelMaterial.h +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelMaterial.h @@ -19,11 +19,15 @@ public: std::optional get_texture_path(); bool has_texture() const; + bool is_texture_animated(); + bool is_texture_double_sided(); void get_texture_width_and_height(int &outWidth, int &outHeight); private: std::optional _texturePath; + std::optional _isTextureAnimated = std::nullopt; + std::optional _isTextureDoubleSided = std::nullopt; }; } diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelVertex.cpp b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelVertex.cpp index 5dbc4153..70a197ff 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelVertex.cpp +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelVertex.cpp @@ -34,6 +34,10 @@ Vec4f BuildModelVertex::color() const { return _color; } +bool BuildModelVertex::is_color_white() const { + return _color == COLOR_WHITE; +} + void BuildModelVertex::write_to(DkrVertex *outVertex) { // Write position outVertex->x = static_cast(_position.x * 10); diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelVertex.h b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelVertex.h index 1e77024d..3695fae4 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelVertex.h +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/buildModelVertex.h @@ -26,6 +26,8 @@ public: Vec3f position() const; Vec4f color() const; + bool is_color_white() const; + void write_to(DkrVertex *outVertex); private: diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/objModel.cpp b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/objModel.cpp index 9f762b76..1f471b0e 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/objModel.cpp +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildModel/objModel.cpp @@ -1,10 +1,88 @@ #include "objModel.h" +#include #include "helpers/fileHelper.h" +#include "helpers/dataHelper.h" #include "helpers/debugHelper.h" +#include "libs/TriangulatePolygon.hpp" + using namespace DkrAssetsTool; +typedef struct ObjFaceComponent { + int vertexIndex = -1; + int uvIndex = -1; + int normalIndex = -1; // Not used in DKR, since normals are generated automatically. +} ObjFaceComponent; + +typedef struct ObjTriangle { + ObjFaceComponent a; + ObjFaceComponent b; + ObjFaceComponent c; + + ObjTriangle(ObjFaceComponent newA, ObjFaceComponent newB, ObjFaceComponent newC) + : a(newA), b(newB), c(newC) {} +} ObjTriangle; + +ObjFaceComponent parse_face_component(std::string component) { + std::vector faceComponents; + StringHelper::split(component, '/', faceComponents); + ObjFaceComponent out; + + out.vertexIndex = std::stoi(faceComponents[0]) - 1; + + if((faceComponents.size() > 1) && (!faceComponents[1].empty())) { + out.uvIndex = std::stoi(faceComponents[1]) - 1; + } + + if((faceComponents.size() > 2) && (!faceComponents[2].empty())) { + out.normalIndex = std::stoi(faceComponents[2]) - 1; + } + + return out; +} + +std::vector triangulate_polygon(const std::vector &components, const std::vector &vertices) { + size_t numberOfOutTriangles = components.size() - 3; + + std::vector out; + + if(numberOfOutTriangles == 1) { + // No need to triangulate. + out.push_back({ + parse_face_component(components[1]), + parse_face_component(components[2]), + parse_face_component(components[3]) + }); + } else { + std::vector faceComponents(components.size() - 1); + std::vector polygonPoints; + + for(size_t i = 1; i < components.size(); i++) { + faceComponents[i - 1] = parse_face_component(components[i]); + const BuildModelVertex &vertex = vertices.at(faceComponents[i - 1].vertexIndex); + Vec3f vertexPos = vertex.position(); + polygonPoints.emplace_back(vertexPos.x, vertexPos.y, vertexPos.z); + } + + const auto triangles = triangulate::triangulate(polygonPoints); + + for(auto &tri : triangles) { + int index0 = DataHelper::vector_index_of(polygonPoints, tri.p0); + int index1 = DataHelper::vector_index_of(polygonPoints, tri.p1); + int index2 = DataHelper::vector_index_of(polygonPoints, tri.p2); + + DebugHelper::assert_(index0 >= 0, "(triangulate_polygon) Invalid point 0"); + DebugHelper::assert_(index1 >= 0, "(triangulate_polygon) Invalid point 1"); + DebugHelper::assert_(index2 >= 0, "(triangulate_polygon) Invalid point 2"); + + out.emplace_back(faceComponents[index0], faceComponents[index1], faceComponents[index2]); + } + } + + return out; +} + ObjBuildModel::ObjBuildModel(fs::path filepath) { std::string objText = FileHelper::read_text_file(filepath); @@ -16,8 +94,6 @@ ObjBuildModel::ObjBuildModel(fs::path filepath) { std::string currentMaterial = ""; - size_t faceIndex = 0; - for(std::string &line : lines) { if(line[0] == '#' || line.empty()) { // Skip this line if it is empty or a comment. @@ -30,15 +106,18 @@ ObjBuildModel::ObjBuildModel(fs::path filepath) { double x = std::stod(components[1]); double y = std::stod(components[2]); double z = std::stod(components[3]); - - // Scale the model according that is specified in settings. - //settings.adjust_from_model_scale(x); - //settings.adjust_from_model_scale(y); - // settings.adjust_from_model_scale(z); - - // Add the new vertex - vertices.emplace_back(Vec3f(x, y, z)); - continue; + + if(components.size() < 6) { + // Add the new vertex + vertices.emplace_back(Vec3f(x, y, z)); + continue; + } + + double r = std::stod(components[4]); + double g = std::stod(components[5]); + double b = std::stod(components[6]); + vertices.emplace_back(Vec3f(x, y, z), Vec4f(r, g, b, 1.0f)); + continue; } if(components[0] == "vn") { // User-generated normals are not used in DKR. @@ -52,47 +131,24 @@ ObjBuildModel::ObjBuildModel(fs::path filepath) { continue; } if(components[0] == "f") { - std::vector faceComponentsA; - std::vector faceComponentsB; - std::vector faceComponentsC; + std::vector tris = triangulate_polygon(components, vertices); - // split the different indicies for a face. - StringHelper::split(components[1], '/', faceComponentsA); - StringHelper::split(components[2], '/', faceComponentsB); - StringHelper::split(components[3], '/', faceComponentsC); - - // parse index for vertex. - int positionAIndex = std::stoi(faceComponentsA[0]) - 1; - int positionBIndex = std::stoi(faceComponentsB[0]) - 1; - int positionCIndex = std::stoi(faceComponentsC[0]) - 1; - - // Make sure the indicies are in the correct range. - DebugHelper::assert_((positionAIndex >= 0) && (positionAIndex < vertices.size()), - "(ObjBuildModel::ObjBuildModel) Face ", faceIndex, " first index is out of range!"); - DebugHelper::assert_((positionBIndex >= 0) && (positionBIndex < vertices.size()), - "(ObjBuildModel::ObjBuildModel) Face ", faceIndex, " second index is out of range!"); - DebugHelper::assert_((positionCIndex >= 0) && (positionCIndex < vertices.size()), - "(ObjBuildModel::ObjBuildModel) Face ", faceIndex, " third index is out of range!"); - - if(faceComponentsA.size() == 1 || faceComponentsA[1].empty()) { - DebugHelper::info("No UVs!"); - // Add the new triangle with no UV coordinates. - _add_new_triangle_to_current_block(vertices[positionAIndex], vertices[positionBIndex], vertices[positionCIndex]); - } else { - int uvAIndex = std::stoi(faceComponentsA[1]) - 1; - int uvBIndex = std::stoi(faceComponentsB[1]) - 1; - int uvCIndex = std::stoi(faceComponentsC[1]) - 1; - - // Adjust the uv coordinates to the N64 format. - Vec2f uv0 = _get_adjusted_uv(uvs[uvAIndex], currentMaterial); - Vec2f uv1 = _get_adjusted_uv(uvs[uvBIndex], currentMaterial); - Vec2f uv2 = _get_adjusted_uv(uvs[uvCIndex], currentMaterial); - - // Add the new triangle with UV coordinates. - _add_new_triangle_to_current_block(vertices[positionAIndex], vertices[positionBIndex], vertices[positionCIndex], uv0, uv1, uv2); + for(ObjTriangle &tri : tris) { + BuildModelVertex &vertexA = vertices[tri.a.vertexIndex]; + BuildModelVertex &vertexB = vertices[tri.b.vertexIndex]; + BuildModelVertex &vertexC = vertices[tri.c.vertexIndex]; + if(currentMaterial.empty() || (tri.a.uvIndex == -1)) { + // Add triangle without UV coordinates. + _add_new_triangle_to_current_block(vertexA, vertexB, vertexC); + } else { + Vec2f uv0 = _get_adjusted_uv(uvs[tri.a.uvIndex], currentMaterial); + Vec2f uv1 = _get_adjusted_uv(uvs[tri.b.uvIndex], currentMaterial); + Vec2f uv2 = _get_adjusted_uv(uvs[tri.c.uvIndex], currentMaterial); + + // Add the new triangle with UV coordinates. + _add_new_triangle_to_current_block(vertexA, vertexB, vertexC, uv0, uv1, uv2); + } } - - faceIndex++; continue; } if(components[0] == "usemtl") { @@ -113,8 +169,6 @@ ObjBuildModel::ObjBuildModel(fs::path filepath) { continue; } } - - DebugHelper::info("Done!"); } Vec2f ObjBuildModel::_get_adjusted_uv(Vec2f uv, std::string materialId) { diff --git a/tools/dkr_assets_tool_src/builder/buildTypes/buildTexture.cpp b/tools/dkr_assets_tool_src/builder/buildTypes/buildTexture.cpp index 53f7d493..04ab9ea5 100644 --- a/tools/dkr_assets_tool_src/builder/buildTypes/buildTexture.cpp +++ b/tools/dkr_assets_tool_src/builder/buildTypes/buildTexture.cpp @@ -2,6 +2,7 @@ #include // for std::round +#include "helpers/assetsHelper.h" #include "helpers/dataHelper.h" #include "helpers/imageHelper.h" #include "helpers/gzipHelper.h" @@ -32,7 +33,9 @@ size_t calculate_out_size(BuildInfo &info, const JsonFile &jsonFile) { } void BuildTexture::build(BuildInfo &info) { + BuildTextureCache &textureCache = info.get_texture_cache(); const JsonFile &jsonFile = info.get_src_json_file(); + bool is3dTexture = info.get_section_build_id() == "ASSET_TEXTURES_3D"; info.out.resize(calculate_out_size(info, jsonFile)); @@ -124,6 +127,12 @@ void BuildTexture::build(BuildInfo &info) { } } + if(is3dTexture) { + // Calculate sha1 of texture data, then add it to the texture cache. Need this so that we can reuse existing textures. + std::string textureSha1 = DataHelper::make_sha1_hash_of_bytes(BytesView(info.out)); + textureCache.add_texture_to_sha1_cache(textureSha1, info.get_build_id()); + } + bool debugKeepUncompressed = GlobalSettings::get_value("/debug/keep-uncompressed", false); if(debugKeepUncompressed) { @@ -154,44 +163,105 @@ void BuildTexture::build(BuildInfo &info) { } size_t BuildTexture::build_deferred(BuildInfo &baseInfo, fs::path pathToImage) { - std::string format = "RGBA16"; - std::string renderMode = "OPAQUE"; + std::string format, renderMode; + ImageHelper::guess_texture_format_and_render_mode(pathToImage, format, renderMode); - N64Image img(pathToImage, format); + std::vector texturePaths = ImageHelper::get_multiple_textures_from_one(pathToImage); - int imgWidth = img.get_width(); - int imgHeight = img.get_height(); + DebugHelper::assert_(texturePaths.size() > 0, + "(BuildTexture::build_deferred) ImageHelper::get_multiple_textures_from_one() failed for ", pathToImage.filename()); - std::vector out(sizeof(TextureHeader) + ImageHelper::image_size(imgWidth, imgHeight, format)); + size_t totalSize = 0; - TextureHeader *header = reinterpret_cast(&out[0]); - uint8_t *textureData = &out[sizeof(TextureHeader)]; + for(auto &path : texturePaths) { + int width, height; + ImageHelper::get_width_and_height(path, width, height); + totalSize += DataHelper::align16(sizeof(TextureHeader) + ImageHelper::image_size(width, height, format)); + } - uint8_t formatVal = DataHelper::vector_index_of(TEXTURE_FORMAT_INT_TO_STRING, format); - uint8_t renderModeVal = DataHelper::vector_index_of(TEXTURE_RENDER_MODES, renderMode); + DebugHelper::assert_(totalSize > 0, + "(BuildTexture::build_deferred) Could not calculate texture size for ", pathToImage.filename()); - header->width = imgWidth; - header->height = imgHeight; - header->format = (renderModeVal << 4) | formatVal; - header->numberOfInstances = 1; - header->numOfTextures = 1; - header->textureSize = DataHelper::align16(out.size()); + int frameAdvanceDelay = 0; - be_int16_t flags = ( - ((int)1 << 10) | - ((int)1 << 6) | - ((int)1 << 7) - ); + if(texturePaths.size() > 1) { + float animTexTiming = ImageHelper::guess_animated_texture_timing(pathToImage); + frameAdvanceDelay = std::round(animTexTiming * TEXTURE_FPS); + } - header->flags = flags; + std::string wrapS, wrapT; + ImageHelper::guess_texture_wrap_mode(pathToImage, wrapS, wrapT); + bool wrapSClamped = wrapS == "clamp"; + bool wrapTClamped = wrapT == "clamp"; - img.flip_vertically(); - img.interlace(); - img.copy_to(textureData); + std::vector out(totalSize); + + size_t offset = 0; + for(auto &path : texturePaths) { + N64Image img(path, format); + + int imgWidth = img.get_width(); + int imgHeight = img.get_height(); + + TextureHeader *header = reinterpret_cast(&out[offset]); + offset += sizeof(TextureHeader); + uint8_t *textureData = &out[offset]; + + uint8_t formatVal = DataHelper::vector_index_of(TEXTURE_FORMAT_INT_TO_STRING, format); + uint8_t renderModeVal = DataHelper::vector_index_of(TEXTURE_RENDER_MODES, renderMode); + + header->width = imgWidth; + header->height = imgHeight; + header->format = (renderModeVal << 4) | formatVal; + header->numberOfInstances = 1; + header->numOfTextures = texturePaths.size(); + header->textureSize = DataHelper::align16(img.size() + sizeof(TextureHeader)); + + if(header->numOfTextures > 1) { + header->frameAdvanceDelay = frameAdvanceDelay; + } + + int16_t flags = 0; + + if(wrapSClamped) { + flags |= (1 << 6); + } + + if(wrapTClamped) { + flags |= (1 << 7); + } + + img.flip_vertically(); + + if(!DataHelper::is_power_of_two(imgWidth)) { + flags |= (1 << 10); + img.interlace(); + } + + header->flags = flags; + + img.copy_to(textureData); + offset += img.size(); + } + + + // Need to check the texture cache to see if the texture already exists or not. + // Avoiding duplicate textures saves a lot of space in the ROM file. + BuildTextureCache &textureCache = baseInfo.get_texture_cache(); + std::string textureSha1 = DataHelper::make_sha1_hash_of_bytes(BytesView(out)); + std::optional foundTexBuildId = textureCache.get_build_id_of_texture_hash(textureSha1); + if(foundTexBuildId.has_value()) { + DebugHelper::info_verbose("Found existing texture for ", pathToImage.filename(), ". Will use ID \"", foundTexBuildId.value(), "\"."); + return AssetsHelper::get_asset_index("ASSET_TEXTURES_3D", foundTexBuildId.value()); + } + + // Add the new texture to the cache. + std::string buildId = baseInfo.get_build_id() + "_" + pathToImage.stem().generic_string(); + DebugHelper::info_verbose("Created new texture: ", pathToImage.filename(), ", ID: ", buildId); + textureCache.add_texture_to_sha1_cache(textureSha1, buildId); BuildInfoCollection &collection = baseInfo.get_collection(); - std::string buildId = baseInfo.get_build_id() + "_" + pathToImage.stem().generic_string(); StringHelper::make_uppercase(buildId); bool debugKeepUncompressed = GlobalSettings::get_value("/debug/keep-uncompressed", false); diff --git a/tools/dkr_assets_tool_src/builder/builder.cpp b/tools/dkr_assets_tool_src/builder/builder.cpp index 01e62a10..2547f86b 100644 --- a/tools/dkr_assets_tool_src/builder/builder.cpp +++ b/tools/dkr_assets_tool_src/builder/builder.cpp @@ -25,8 +25,9 @@ #include "builder/buildInfo.h" #include "builder/buildInfoCollection.h" +#include "builder/buildInfoContext.h" #include "builder/buildAssetTable.h" -#include "builder/stats.h" +#include "builder/textureCache.h" #include "buildTypes/buildAudio.h" #include "buildTypes/buildBinary.h" @@ -204,11 +205,11 @@ void AssetBuilder::build_all(const fs::path &dstPath) { size_t numberOfAssetSections = sectionIds.size(); CContext cContext; - BuildStats stats; + BuildTextureCache textureCache; BuildInfoCollection collection; // BuildInfoContext holds info that is shared between all assets. - BuildInfoContext infoContext(cContext, stats, collection); + BuildInfoContext infoContext(cContext, textureCache, collection); fs::path includeFolder = GlobalSettings::get_decomp_path("include_subpath", "include/"); CEnumsHelper::load_enums_from_file(cContext, includeFolder / "enums.h"); @@ -254,7 +255,7 @@ void AssetBuilder::build_all(const fs::path &dstPath) { collection.run_builds([](BuildInfo &info) { std::string type = info.get_type(); builderMap[type](info); - }); + }, sectionIds); // Second loop to create the tables for the asset sections. for(size_t sectionIndex = 0; sectionIndex < numberOfAssetSections; sectionIndex++) { diff --git a/tools/dkr_assets_tool_src/builder/stats.cpp b/tools/dkr_assets_tool_src/builder/stats.cpp deleted file mode 100644 index 3df3497b..00000000 --- a/tools/dkr_assets_tool_src/builder/stats.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "stats.h" \ No newline at end of file diff --git a/tools/dkr_assets_tool_src/builder/stats.h b/tools/dkr_assets_tool_src/builder/stats.h deleted file mode 100644 index 68f9c835..00000000 --- a/tools/dkr_assets_tool_src/builder/stats.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -namespace DkrAssetsTool { - -class BuildInfo; - -class BuildStats { - public: - - private: - std::unordered_map _numberOfFilesInSection; -}; - -} \ No newline at end of file diff --git a/tools/dkr_assets_tool_src/builder/textureCache.cpp b/tools/dkr_assets_tool_src/builder/textureCache.cpp new file mode 100644 index 00000000..559d06ca --- /dev/null +++ b/tools/dkr_assets_tool_src/builder/textureCache.cpp @@ -0,0 +1,26 @@ +#include "textureCache.h" + +#include "misc/globalSettings.h" + +using namespace DkrAssetsTool; + +std::optional BuildTextureCache::get_build_id_of_texture_hash(std::string textureSha1) { + _mutex.lock(); + if(_textureCache.find(textureSha1) == _textureCache.end()) { + _mutex.unlock(); + return std::nullopt; + } + std::string result = _textureCache.at(textureSha1); + _mutex.unlock(); + return result; +} + +void BuildTextureCache::add_texture_to_sha1_cache(std::string textureSha1, std::string buildId) { + _mutex.lock(); + if(_textureCache.find(textureSha1) != _textureCache.end()) { + _mutex.unlock(); + return; // If an entry already exists, then exit early to not overwrite it. + } + _textureCache[textureSha1] = buildId; + _mutex.unlock(); +} diff --git a/tools/dkr_assets_tool_src/builder/textureCache.h b/tools/dkr_assets_tool_src/builder/textureCache.h new file mode 100644 index 00000000..56269d25 --- /dev/null +++ b/tools/dkr_assets_tool_src/builder/textureCache.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +namespace DkrAssetsTool { + +class BuildTextureCache { +public: + // Used in custom models to see if a texture already exists or not. Retruns std::nullopt if the texture does not exist. + std::optional get_build_id_of_texture_hash(std::string textureSha1); + + // Note: Will not overwrite a existing id if one already exists. + void add_texture_to_sha1_cache(std::string textureSha1, std::string buildId); + +private: + // Key: sha1 hash of a image, Value: Build id of that image. + std::unordered_map _textureCache; + + std::mutex _mutex; +}; + +} \ No newline at end of file diff --git a/tools/dkr_assets_tool_src/fileTypes/objectModel.hpp b/tools/dkr_assets_tool_src/fileTypes/objectModel.hpp index 4fc146e4..8a0f9ab7 100644 --- a/tools/dkr_assets_tool_src/fileTypes/objectModel.hpp +++ b/tools/dkr_assets_tool_src/fileTypes/objectModel.hpp @@ -38,7 +38,7 @@ typedef struct ObjectModel { /* 0x48 */ be_int16_t reservedNumberOfAnimations; // Reserved for in-game only. /* 0x4A */ be_int16_t numberOfAnimatedVertices; // Number of animated vertices /* 0x4C */ be_int32_t animatedVertexIndices; // Animated vertices list. - /* 0x50 */ be_int16_t unk50; // Update rate maybe? Only checked if greater than 0. + /* 0x50 */ be_int16_t hasAnimatedTexture; // Game only checks if this is zero or not. /* 0x52 */ be_int16_t reserved52; // Reserved for in-game only. /* 0x54 */ be_int32_t unused54; // Not used? } ObjectModel; diff --git a/tools/dkr_assets_tool_src/helpers/fileHelper.cpp b/tools/dkr_assets_tool_src/helpers/fileHelper.cpp index 3b769f6f..97fcd8f2 100644 --- a/tools/dkr_assets_tool_src/helpers/fileHelper.cpp +++ b/tools/dkr_assets_tool_src/helpers/fileHelper.cpp @@ -211,7 +211,7 @@ std::vector FileHelper::get_files_from_directory_that_start_with(const } } else { for (const auto& file : fs::directory_iterator(dirPath)) { - if (StringHelper::starts_with(file.path().generic_string(), prefix)) { + if (StringHelper::starts_with(file.path().filename().generic_string(), prefix)) { out.push_back(file); } } diff --git a/tools/dkr_assets_tool_src/helpers/imageHelper.cpp b/tools/dkr_assets_tool_src/helpers/imageHelper.cpp index 7c9d4413..54c4b19e 100644 --- a/tools/dkr_assets_tool_src/helpers/imageHelper.cpp +++ b/tools/dkr_assets_tool_src/helpers/imageHelper.cpp @@ -5,12 +5,16 @@ using namespace DkrAssetsTool; #include // for memcpy #include #include +#include +#include "jsonHelper.h" #include "fileHelper.h" #include "debugHelper.h" #include "libs/n64graphics/n64graphics.h" #include "libs/stb_image.h" #include "helpers/dataHelper.h" +#include "misc/globalSettings.h" +#include "fileTypes/texture.hpp" N64Image::N64Image(fs::path filepath, std::string format, bool alignData) : _format(format) { bool isColourImage = (format == "RGBA16") || (format == "RGBA32") || (format == "CI4") || (format == "CI8"); @@ -432,3 +436,251 @@ void ImageHelper::get_width_and_height(fs::path filepath, int &outWidth, int &ou DebugHelper::assert_(gotInfo, "(ImageHelper::get_width_and_height) ", filepath, " could not be loaded!"); } +// If a texture uses multiple images, it will have a number at the end like "_2". +// so this function should remove that so we can find the correct json file. +fs::path strip_postfix_number(fs::path path) { + std::string filename = path.filename().stem().generic_string(); + + int offset = filename.size() - 1; + while((offset >= 0) && ((filename[offset] >= '0') && (filename[offset] <= '9'))) { + offset--; + } + + if(filename[offset] != '_') { + return path; + } + + if((offset > 0) && (offset < filename.size() - 1)) { + fs::path outPath = path.parent_path() / filename.substr(0, offset); + outPath.replace_extension(path.extension()); // Add the extension back in. + return outPath; + } + + return path; +} + +int get_postfix_number(fs::path path) { + std::string filename = path.filename().stem().generic_string(); + + int offset = filename.size() - 1; + while((offset >= 0) && ((filename[offset] >= '0') && (filename[offset] <= '9'))) { + offset--; + } + + if(filename[offset] != '_') { + return -1; + } + + if((offset > 0) && (offset < filename.size() - 1)) { + return std::stoi(filename.substr(offset + 1)); + } + + return -1; +} + +std::optional ImageHelper::guess_associated_json_file(fs::path imgFilepath) { + fs::path filepath = imgFilepath; + filepath.replace_extension(".json"); + if(FileHelper::path_exists(filepath)) { + return filepath; + } + filepath = strip_postfix_number(filepath); + if(FileHelper::path_exists(filepath)) { + return filepath; + } + return std::nullopt; +} + +void ImageHelper::guess_texture_wrap_mode(fs::path filepath, std::string &outWrapS, std::string &outWrapT) { + // Use "wrap" as the default. + outWrapS = "wrap"; + outWrapT = "wrap"; + + // Check to see if there is an accompanying json file with the image file. + // That json file more than likely has a format/render-mode associated with it. + std::optional jsonFilepath = guess_associated_json_file(filepath); + + if(jsonFilepath.has_value()) { + JsonFile &jsonFile = JsonHelper::get_file_or_error(jsonFilepath.value(), + "Could not load json file ", jsonFilepath.value()); + + outWrapS = jsonFile.get_string_lowercase("/flags/wrap-s", "wrap"); + outWrapT = jsonFile.get_string_lowercase("/flags/wrap-t", "wrap"); + } +} + +void ImageHelper::guess_texture_format_and_render_mode(fs::path filepath, std::string &outFormat, std::string &outRenderMode, bool ignoreTextureSize) { + outFormat = ""; + outRenderMode = ""; + + // Check to see if there is an accompanying json file with the image file. + // That json file more than likely has a format/render-mode associated with it. + std::optional jsonFilepath = guess_associated_json_file(filepath); + + if(jsonFilepath.has_value()) { + JsonFile &jsonFile = JsonHelper::get_file_or_error(jsonFilepath.value(), + "Could not load json file ", jsonFilepath.value()); + + outFormat = jsonFile.get_string("/format"); + outRenderMode = jsonFile.get_string("/render-mode"); + } + + if(!outFormat.empty() && !outRenderMode.empty()) { + return; + } + + // Check and see if the image has the format in the filename. Example: `tex.rgba16.png` + + if(outFormat.empty()) { + std::vector filenameSplit; + std::string filenameAsString = filepath.filename().generic_string(); + StringHelper::split(filenameAsString, '.', filenameSplit); // Split filename using the dots. + for(size_t i = 1; i < filenameSplit.size() - 1; i++) { + std::string &filenamePart = filenameSplit[i]; + StringHelper::make_uppercase(filenamePart); + if(DataHelper::vector_has(TEXTURE_FORMAT_INT_TO_STRING, filenamePart)) { + outFormat = filenamePart; + break; + } + } + } + + // Load the image and take a guess from the pixels. + + int imgWidth, imgHeight; + + rgba *data = (rgba*)png2rgba(filepath.c_str(), &imgWidth, &imgHeight); + + int numberOfPixels = imgWidth * imgHeight; + + bool isTransparent = false; + bool isSemiTransparent = false; + bool hasColor = false; + + for(int y = 0; y < imgHeight; y++) { + for(int x = 0; x < imgWidth; x++) { + int i = (y * imgWidth) + x; + bool isGray = (data[i].red == data[i].green) && (data[i].green == data[i].blue); + + hasColor = hasColor || !isGray; + isTransparent = isTransparent || (data[i].alpha < 255); + isSemiTransparent = isSemiTransparent || (isTransparent && (data[i].alpha > 0)); + } + } + + if(outFormat.empty()) { + if(hasColor) { + // Color image (RGBA16, RGBA32) + if(!ignoreTextureSize && (numberOfPixels > 32*32)) { + // Can't use 32-bit formats at this point + if(numberOfPixels > 64*32) { + DebugHelper::error("(ImageHelper::guess_texture_format_and_render_mode) ", + filepath.filename(), " is too large for a colored image!"); + } else { + outFormat = "RGBA16"; + } + } else { + outFormat = (isSemiTransparent ? "RGBA32" : "RGBA16"); + } + } else { + // Grayscale image (I4, IA4, I8, IA8, IA16) + if(!ignoreTextureSize && (numberOfPixels > 64*32)) { + // Can't use 16-bit formats at this point + if(numberOfPixels > 64*64) { + // Can't use 8-bit formats at this point. + if(numberOfPixels > 128*64) { + DebugHelper::error("(ImageHelper::guess_texture_format_and_render_mode) ", + filepath.filename(), " is too large for a grayscale image!"); + } else { + outFormat = (isTransparent ? "IA4" : "I4"); + } + } else { + outFormat = (isTransparent ? "IA8" : "I8"); + } + } else { + outFormat = (isTransparent ? "IA16" : "I8"); + } + } + } + + if(outRenderMode.empty()) { + outRenderMode = (isTransparent ? "TRANSPARENT" : "OPAQUE"); + } +} + +bool ImageHelper::guess_if_texture_is_animated(fs::path imgFilepath) { + // Check to see if there is an accompanying json file with the image file. + std::optional jsonFilepath = guess_associated_json_file(imgFilepath); + + if(jsonFilepath.has_value()) { + JsonFile &jsonFile = JsonHelper::get_file_or_error(jsonFilepath.value(), + "Could not load json file ", jsonFilepath.value()); + return jsonFile.length_of_array("/images") > 1; + } + + // Check if there exist multiple texture paths based off the imgFilepath provided. + return get_multiple_textures_from_one(imgFilepath).size() > 1; +} + +bool ImageHelper::guess_if_texture_double_sided(fs::path filepath) { + // Check to see if there is an accompanying json file with the image file. + // That json file more than likely has a format/render-mode associated with it. + std::optional jsonFilepath = guess_associated_json_file(filepath); + + if(jsonFilepath.has_value()) { + JsonFile &jsonFile = JsonHelper::get_file_or_error(jsonFilepath.value(), + "Could not load json file ", jsonFilepath.value()); + return jsonFile.get_bool("/doubleSided", false); + } + + return false; +} + +float ImageHelper::guess_animated_texture_timing(fs::path imgFilepath) { + // Check to see if there is an accompanying json file with the image file. + std::optional jsonFilepath = guess_associated_json_file(imgFilepath); + + if(jsonFilepath.has_value()) { + JsonFile &jsonFile = JsonHelper::get_file_or_error(jsonFilepath.value(), + "Could not load json file ", jsonFilepath.value()); + return jsonFile.get_float("/frame-advance-delay", 1.0f); + } + + // Use 1 second as the default timing. + return (float)GlobalSettings::get_value("/build/default-frame-advance-delay", 1.0); +} + +// Returns a list of paths of .png images (for animated textures) +std::vector ImageHelper::get_multiple_textures_from_one(fs::path imgFilepath) { + fs::path filepath = strip_postfix_number(imgFilepath); + + if(filepath == imgFilepath) { + return { imgFilepath }; + } + + std::string prefix = filepath.filename().stem().generic_string(); + std::vector paths = FileHelper::get_files_from_directory_that_start_with(imgFilepath.parent_path(), prefix, false); + + std::map order; + + for(fs::path &p : paths) { + int index = get_postfix_number(p); + if(index >= 0) { + order[index] = p; + } + } + + if(order.size() == 0) { + return { imgFilepath }; + } + + std::vector out; + out.reserve(order.size()); + + for(auto& pair : order) { + out.emplace_back(pair.second); + } + + return out; +} + diff --git a/tools/dkr_assets_tool_src/helpers/imageHelper.h b/tools/dkr_assets_tool_src/helpers/imageHelper.h index 56df5caf..cd843faa 100644 --- a/tools/dkr_assets_tool_src/helpers/imageHelper.h +++ b/tools/dkr_assets_tool_src/helpers/imageHelper.h @@ -60,5 +60,14 @@ namespace ImageHelper { size_t image_size(int width, int height, std::string &format); size_t image_size(fs::path filepath, std::string &format); // Get byte-size of .png image void get_width_and_height(fs::path filepath, int &outWidth, int &outHeight); // Get width and height of a .png image + + std::optional guess_associated_json_file(fs::path imgFilepath); + void guess_texture_wrap_mode(fs::path filepath, std::string &outWrapS, std::string &outWrapT); + void guess_texture_format_and_render_mode(fs::path filepath, std::string &outFormat, std::string &outRenderMode, bool ignoreTextureSize=false); + bool guess_if_texture_is_animated(fs::path imgFilepath); + bool guess_if_texture_double_sided(fs::path filepath); + float guess_animated_texture_timing(fs::path imgFilepath); // Returns time to advance texture in seconds. + + std::vector get_multiple_textures_from_one(fs::path imgFilepath); } } diff --git a/tools/dkr_assets_tool_src/helpers/jsonHelper.h b/tools/dkr_assets_tool_src/helpers/jsonHelper.h index 9729027a..31167586 100644 --- a/tools/dkr_assets_tool_src/helpers/jsonHelper.h +++ b/tools/dkr_assets_tool_src/helpers/jsonHelper.h @@ -132,6 +132,17 @@ class StatJsonFile { namespace JsonHelper { std::optional> get_file(fs::path filepath); void patch_json(const fs::path &dst, const fs::path &patch); + + template + JsonFile &get_file_or_error(fs::path filepath, Args... args) { + std::optional> tryGetJsonFile = get_file(filepath); + + if(!tryGetJsonFile.has_value()) { + DebugHelper::error(args...); + } + + return tryGetJsonFile.value(); + } } } diff --git a/tools/dkr_assets_tool_src/libs/TriangulatePolygon.hpp b/tools/dkr_assets_tool_src/libs/TriangulatePolygon.hpp new file mode 100644 index 00000000..8a8dba54 --- /dev/null +++ b/tools/dkr_assets_tool_src/libs/TriangulatePolygon.hpp @@ -0,0 +1,513 @@ +#pragma once +/* + TriangulatePolygon.hpp + + C++ Template Class for triangulate a 3D polygon + + Copyright (c) 2024 FalconCoding + + Author: Stefan Falk Johnsen + Email: stefan.johnsen@outlook.com + + This software is released under the MIT License. + */ + +#ifndef TRIANGULATE_POLYGON +#define TRIANGULATE_POLYGON + +#include +#include + +namespace triangulate +{ + struct Point + { + Point() : x(0.0f), y(0.0f), z(0.0f) {} + + Point(const float& x, const float& y, const float& z) : x(x), y(y), z(z) {} + + float x; + float y; + float z; + + bool operator==(const Point& other) const { + return (x == other.x) && (y == other.y) && (z == other.z); + } + + bool operator!=(const Point& other) const { + return !operator==(other); + } + }; + + template + struct Triangle + { + Triangle(const T& p0, const T& p1, const T& p2) : p0(p0), p1(p1), p2(p2) {} + + Triangle(const Triangle& t) : p0(t.p0), p1(t.p1), p2(t.p2) {} + + T p0, p1, p2; + }; + + static constexpr float epsilon = 1e-6f; + + inline bool equal(const double& a, const double& b) + { + return std::fabs(a - b) <= epsilon; + } + + inline bool equal(const float& a, const float& b) + { + return std::fabs(a - b) <= epsilon; + } + + inline bool zero(const double& f) + { + return equal(f, 0); + } + + inline bool zero(const float& f) + { + return equal(f, 0); + } + + inline double magnitude(const double& x, const double& y, const double& z) + { + return std::sqrt(x * x + y * y + z * z); + } + + inline float magnitude(const float& x, const float& y, const float& z) + { + return std::sqrt(x * x + y * y + z * z); + } + + template + bool equal(const T& p, const T& q) + { + if( !equal(p.x, q.x) ) return false; + if( !equal(p.y, q.y) ) return false; + if( !equal(p.z, q.z) ) return false; + + return true; + } + + template + T subtract(const T& p, const T& q) + { + return {p.x - q.x , p.y - q.y , p.z - q.z}; + } + + template + float magnitude(const T& u) + { + return magnitude(u.x, u.y, u.z); + } + + template + float distance(const T& p, const T& q) + { + T u = subtract(p, q); + + return std::sqrt(magnitude(u)); + } + + template + T normalize(const T& p) + { + const auto m = magnitude(p); + + if( m == 0. ) return {}; + + return {p.x / m , p.y / m , p.z / m}; + } + + template + double dot(const T& u, const T& v) + { + const auto dx = static_cast(u.x) * static_cast(v.x); + const auto dy = static_cast(u.y) * static_cast(v.y); + const auto dz = static_cast(u.z) * static_cast(v.z); + + return dx + dy + dz; + } + + template + T cross(const T& u, const T& v) + { + return {u.y * v.z - u.z * v.y , u.z * v.x - u.x * v.z , u.x * v.y - u.y * v.x}; + } + + enum class TurnDirection + { + Right = 1, + Left = -1, + NoTurn = 0 + }; + + template + TurnDirection turn(const T& p, const T& u, const T& n, const T& q) + { + const T v = subtract(q, p); + + const auto dot = triangulate::dot(cross(v, u), n); + + return dot > 0. ? TurnDirection::Right : (dot < 0 ? TurnDirection::Left : TurnDirection::NoTurn); + } + + template + float triangleAreaSquared(const T& a, const T& b, const T& c) + { + const T u = subtract(b, a); + const T v = subtract(c, a); + + const auto cross = triangulate::cross(u, v); + + return static_cast(dot(cross, cross)) / 4.0f; + } + + //------------------------------------------------------------------------------------------------------- + + template + size_t sizePolygon(const std::vector& polygon) + { + const auto n = polygon.size(); + + if( n < 2 ) return 0; + + return equal(polygon.front(), polygon.back()) ? n - 1 : n; + } + + template + T normalPolygon(const std::vector& polygon) //Newel's method + { + T normal = T(); + + const auto n = sizePolygon(polygon); + + if( n < 3 ) return {}; + + for( size_t i = 0; i < n; i++ ) + { + const auto& item = polygon[i]; + const auto& next = polygon[(i + 1) % n]; + + normal.x += (next.y - item.y) * (next.z + item.z); + normal.y += (next.z - item.z) * (next.x + item.x); + normal.z += (next.x - item.x) * (next.y + item.y); + } + + return normalize(normal); + } + + template + bool convex(const std::vector& polygon, const T& normal) + { + const auto n = sizePolygon(polygon); + + if( n < 3 ) return false; + + if( n == 3 ) return true; + + auto polygonTurn = TurnDirection::NoTurn; + + for( size_t index = 0; index < n; index++ ) + { + const auto& prev = polygon[(index - 1 + n) % n]; + const auto& item = polygon[index % n]; + const auto& next = polygon[(index + 1) % n]; + + const T v = subtract(item, prev); + + const auto u = normalize(v); + + const auto itemTurn = turn(prev, u, normal, next); + + if( itemTurn == TurnDirection::NoTurn ) + continue; + + if( polygonTurn == TurnDirection::NoTurn ) + polygonTurn = itemTurn; + + if( polygonTurn != itemTurn ) + return false; + } + + return true; + } + + template + bool clockwiseOriented(const std::vector& polygon, const T& normal) + { + const auto n = sizePolygon(polygon); + + if( n < 3 ) return false; + + double orientationSum(0.0); + + for( size_t index = 0; index < n; index++ ) + { + const auto& prev = polygon[(index - 1 + n) % n]; + const auto& item = polygon[index % n]; + const auto& next = polygon[(index + 1) % n]; + + const T edge = subtract(item, prev); + const T toNextPoint = subtract(next, item); + + const auto cross = triangulate::cross(edge, toNextPoint); + + orientationSum += dot(cross, normal); + } + + return orientationSum < 0.0; + } + + template + void makeClockwiseOrientation(std::vector& polygon, const T& normal) + { + const auto n = sizePolygon(polygon); + + if( n < 3 ) return; + + if( !clockwiseOriented(polygon, normal) ) + polygon = {polygon.rbegin() , polygon.rend()}; + } + + template + bool pointInsideOrEdgeTriangle(const T& a, const T& b, const T& c, const T& p, bool& edge) + { + static double zero = std::numeric_limits::epsilon(); + + // Initialize edge to false + edge = false; + + // Vectors from point p to vertices of the triangle + const T v0 = subtract(c, a); + const T v1 = subtract(b, a); + const T v2 = subtract(p, a); + + const auto dot00 = dot(v0, v0); + const auto dot01 = dot(v0, v1); + const auto dot02 = dot(v0, v2); + const auto dot11 = dot(v1, v1); + const auto dot12 = dot(v1, v2); + + // Check for degenerate triangle + const auto denom = dot00 * dot11 - dot01 * dot01; + + if( std::abs(denom) < zero ) + { + // The triangle is degenerate (i.e., has no area) + return false; + } + + // Compute barycentric coordinates + const auto invDenom = 1.0 / denom; + + const auto u = (dot11 * dot02 - dot01 * dot12) * invDenom; + const auto v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + // Check for edge condition + if( std::abs(u) < zero || std::abs(v) < zero || std::abs(u + v - 1) < zero ) + edge = true; + + // Check if point is inside the triangle (including edges) + return (u >= 0.0) && (v >= 0.0) && (u + v < 1.0); + } + + template + void removeConsecutiveEqualItems(std::vector& polygon) + { + if (sizePolygon(polygon) < 2) return; + + auto new_end = std::unique(polygon.begin(), polygon.end(), [](const T& a, const T& b) { + return equal(a, b); + }); + + polygon.erase(new_end, polygon.end()); + } + + //------------------------------------------------------------------------------------------------------- + + template + bool isEar(const size_t index, const std::vector& polygon, const T& normal) + { + const auto n = sizePolygon(polygon); + + if( n < 3 ) return false; + + if( n == 3 ) return true; + + bool edge(false); + + const auto prevIndex = (index - 1 + n) % n; + const auto itemIndex = index % n; + const auto nextIndex = (index + 1) % n; + + const auto& prev = polygon[prevIndex]; + const auto& item = polygon[itemIndex]; + const auto& next = polygon[nextIndex]; + + const T v = subtract(item, prev); + + const auto u = normalize(v); + + if( turn(prev, u, normal, next) != TurnDirection::Right ) + return false; + + for( size_t i = 0; i < n; i++ ) + { + if( i == prevIndex ) continue; + if( i == itemIndex ) continue; + if( i == nextIndex ) continue; + + if( pointInsideOrEdgeTriangle(prev, item, next, polygon[i], edge) ) + return false; + } + + return true; + } + + template + int getBiggestEar(const std::vector& polygon, const T& normal) + { + const auto n = sizePolygon(polygon); + + if( n == 3 ) return 0; + + if( n == 0 ) return -1; + + int maxIndex(-1); + + double maxArea(std::numeric_limits::min()); + + for( size_t index = 0; index < n; index++ ) + { + if( isEar(index, polygon, normal) ) + { + const auto& prev = polygon[(index - 1 + n) % n]; + const auto& item = polygon[index % n]; + const auto& next = polygon[(index + 1) % n]; + + const auto area = triangleAreaSquared(prev, item, next); + + if( area > maxArea ) + { + maxIndex = static_cast(index); + + maxArea = area; + } + } + } + + return maxIndex; + } + + template + int getOverlappingEar(const std::vector& polygon, const T& normal) + { + const auto n = sizePolygon(polygon); + + if( n == 3 ) return 0; + + if( n == 0 ) return -1; + + for( size_t index = 0; index < n; index++ ) + { + const auto& prev = polygon[(index - 1 + n) % n]; + const auto& item = polygon[index % n]; + const auto& next = polygon[(index + 1) % n]; + + const T v_prev = subtract(item, prev); + + const auto n_prev = normalize(v_prev); + + if( turn(prev, n_prev, normal, next) != TurnDirection::NoTurn ) + continue; + + const T v_next = subtract(next, item); + + const auto n_next = normalize(v_next); + + if( dot(n_prev, n_next) < 0.0 ) //Opposite direction -> ear + return static_cast(index); + } + + return -1; + } + + //------------------------------------------------------------------------------------------------------- + + template > + std::vector fanTriangulation(std::vector

& polygon) + { + std::vector triangles; + + const auto n = sizePolygon(polygon); + + if( n < 3 ) return {}; + + for( size_t index = 1; index < n - 1; ++index ) + triangles.emplace_back(polygon[0], polygon[index], polygon[index + 1]); + + return triangles; + } + + template > + std::vector cutTriangulation(std::vector

& polygon, const P& normal) + { + std::vector triangles; + + makeClockwiseOrientation(polygon, normal); + + while( !polygon.empty() ) + { + int index = getBiggestEar(polygon, normal); + + if( index == -1 ) + index = getOverlappingEar(polygon, normal); + + if( index < 0 ) + return {}; + + const auto n = sizePolygon(polygon); + const auto i = static_cast(index); + + const auto& prev = polygon[(i - 1 + n) % n]; + const auto& item = polygon[i % n]; + const auto& next = polygon[(i + 1) % n]; + + triangles.emplace_back(prev, item, next); + + polygon.erase(polygon.begin() + index); + + if( polygon.size() < 3 ) break; + } + + return polygon.size() == 2 ? triangles : std::vector(); + } + + template > + std::vector triangulate(const std::vector

& polygon) + { + std::vector

copy(polygon); + + triangulate::removeConsecutiveEqualItems(copy); + + if( copy.size() < 3 ) return {}; + + if( copy.size() == 3 ) + { + std::vector triangle; + + triangle.emplace_back(copy[0], copy[1], copy[2]); + + return triangle; + } + + const auto normal = triangulate::normalPolygon(copy); + + return convex

(copy, normal) ? fanTriangulation(copy) : cutTriangulation(copy, normal); + } +} + +#endif // TRIANGULATE_POLYGON diff --git a/tools/dkr_assets_tool_src/libs/json.hpp b/tools/dkr_assets_tool_src/libs/json.hpp index a6b4a1f2..0414a205 100644 --- a/tools/dkr_assets_tool_src/libs/json.hpp +++ b/tools/dkr_assets_tool_src/libs/json.hpp @@ -1,41 +1,23 @@ -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.10.4 -|_____|_____|_____|_|___| https://github.com/nlohmann/json +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT -Licensed under the MIT License . -SPDX-License-Identifier: MIT -Copyright (c) 2013-2019 Niels Lohmann . - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#pragma once +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 10 -#define NLOHMANN_JSON_VERSION_PATCH 4 - #include // all_of, find, for_each #include // nullptr_t, ptrdiff_t, size_t #include // hash, less @@ -45,18 +27,145 @@ SOFTWARE. #endif // JSON_NO_IO #include // random_access_iterator_tag #include // unique_ptr -#include // accumulate #include // string, stoi, to_string #include // declval, forward, move, pair, swap #include // vector // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + -#include #include +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_DIAGNOSTIC_POSITIONS + #define JSON_DIAGNOSTIC_POSITIONS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_DIAGNOSTIC_POSITIONS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // transform @@ -72,14 +181,34 @@ SOFTWARE. #include // valarray // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include // nullptr_t #include // exception +#if JSON_DIAGNOSTICS + #include // accumulate +#endif #include // runtime_error #include // to_string #include // vector // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // array @@ -87,102 +216,130 @@ SOFTWARE. #include // uint8_t #include // string -namespace nlohmann -{ -namespace detail -{ -/////////////////////////// -// JSON type enumeration // -/////////////////////////// - -/*! -@brief the JSON type enumeration - -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. - -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. - -@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type - -@since version 1.0.0 -*/ -enum class value_t : std::uint8_t -{ - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function -}; - -/*! -@brief comparison operator for JSON types - -Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string < binary -- furthermore, each type is not smaller than itself -- discarded values are not comparable -- binary is represented as a b"" string in python and directly comparable to a - string; however, making a binary array directly comparable with a string would - be surprising behavior in a JSON file. - -@since version 1.0.0 -*/ -inline bool operator<(const value_t lhs, const value_t rhs) noexcept -{ - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; - - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; -} -} // namespace detail -} // namespace nlohmann - -// #include - - -#include // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-FileCopyrightText: 2016 - 2021 Evan Nemerson +// SPDX-License-Identifier: MIT + /* Hedley - https://nemequ.github.io/hedley * Created by Evan Nemerson - * - * To the extent possible under law, the author(s) have dedicated all - * copyright and related and neighboring rights to this software to - * the public domain worldwide. This software is distributed without - * any warranty. - * - * For details, see . - * SPDX-License-Identifier: CC0-1.0 */ #if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) @@ -2216,87 +2373,13 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ -// #include - -#include - -// #include - - -namespace nlohmann -{ -namespace detail -{ -template struct make_void -{ - using type = void; -}; -template using void_t = typename make_void::type; -} // namespace detail -} // namespace nlohmann - - -// https://en.cppreference.com/w/cpp/experimental/is_detected -namespace nlohmann -{ -namespace detail -{ -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; - -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; - -template class Op, class... Args> -struct detector>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op; -}; - -template class Op, class... Args> -using is_detected = typename detector::value_t; - -template class Op, class... Args> -struct is_detected_lazy : is_detected { }; - -template class Op, class... Args> -using detected_t = typename detector::type; - -template class Op, class... Args> -using detected_or = detector; - -template class Op, class... Args> -using detected_or_t = typename detected_or::type; - -template class Op, class... Args> -using is_detected_exact = std::is_same>; - -template class Op, class... Args> -using is_detected_convertible = - std::is_convertible, To>; -} // namespace detail -} // namespace nlohmann - - -// This file contains all internal macro definitions +// This file contains all internal macro definitions (except those affecting ABI) // You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them +// #include + + // exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) #if defined(__clang__) @@ -2311,22 +2394,139 @@ using is_detected_convertible = #endif // C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +// if the user manually specified the used C++ version, this is skipped +#if !defined(JSON_HAS_CPP_26) && !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus > 202302L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202302L) + #define JSON_HAS_CPP_26 + #define JSON_HAS_CPP_23 #define JSON_HAS_CPP_20 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #elif (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L) + #define JSON_HAS_CPP_23 + #define JSON_HAS_CPP_20 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define JSON_HAS_CPP_14 #endif // the cpp 11 flag is always specified because it is the minimal required version #define JSON_HAS_CPP_11 #endif +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has a syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifndef JSON_HAS_STATIC_RTTI + #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 + #define JSON_HAS_STATIC_RTTI 1 + #else + #define JSON_HAS_STATIC_RTTI 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push @@ -2334,7 +2534,7 @@ using is_detected_convertible = #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" #endif -// allow to disable exceptions +// allow disabling exceptions #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception #define JSON_TRY try @@ -2368,13 +2568,13 @@ using is_detected_convertible = #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif -// allow to override assert +// allow overriding assert #if !defined(JSON_ASSERT) #include // assert #define JSON_ASSERT(x) assert(x) #endif -// allow to access some private functions (needed by the test suite) +// allow accessing some private functions (needed by the test suite) #if defined(JSON_TESTS_PRIVATE) #define JSON_PRIVATE_UNLESS_TESTED public #else @@ -2390,7 +2590,9 @@ using is_detected_convertible = template \ inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [e](const std::pair& ej_pair) -> bool \ @@ -2402,7 +2604,9 @@ using is_detected_convertible = template \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ { \ + /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ static const std::pair m[] = __VA_ARGS__; \ auto it = std::find_if(std::begin(m), std::end(m), \ [&j](const std::pair& ej_pair) -> bool \ @@ -2422,12 +2626,13 @@ using is_detected_convertible = class NumberUnsignedType, class NumberFloatType, \ template class AllocatorType, \ template class JSONSerializer, \ - class BinaryType> + class BinaryType, \ + class CustomBaseClass> #define NLOHMANN_BASIC_JSON_TPL \ basic_json + AllocatorType, JSONSerializer, BinaryType, CustomBaseClass> // Macros to simplify conversion from/to types @@ -2564,28 +2769,146 @@ using is_detected_convertible = #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = !nlohmann_json_j.is_null() ? nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1) : nlohmann_json_default_obj.v1; /*! @brief macro @def NLOHMANN_DEFINE_TYPE_INTRUSIVE @since version 3.9.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ */ #define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT +@since version 3.11.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE +@since version 3.11.3 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @since version 3.9.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ */ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT +@since version 3.11.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE +@since version 3.11.3 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ + template::value, int> = 0> \ + friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + template::value, int> = 0> \ + void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE +@since version 3.12.0 +@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ +*/ +#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ + template::value, int> = 0> \ + void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } // inspired from https://stackoverflow.com/a/26745591 -// allows to call any std function as if (e.g. with begin): +// allows calling any std function as if (e.g., with begin): // using std::begin; begin(x); // // it allows using the detected idiom to retrieve the return type @@ -2632,13 +2955,132 @@ using is_detected_convertible = #define JSON_EXPLICIT explicit #endif -#ifndef JSON_DIAGNOSTICS - #define JSON_DIAGNOSTICS 0 +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 #endif +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif -namespace nlohmann +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail { + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -2655,14 +3097,15 @@ enforced with an assertion.** @since version 2.0.0 */ -inline void replace_substring(std::string& s, const std::string& f, - const std::string& t) +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) { JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found + for (auto pos = s.find(f); // find the first occurrence of f + pos != StringType::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f + pos = s.find(f, pos + t.size())) // find the next occurrence of f {} } @@ -2673,10 +3116,11 @@ inline void replace_substring(std::string& s, const std::string& f, * * Note the order of escaping "~" to "~0" and "/" to "~1" is important. */ -inline std::string escape(std::string s) +template +inline StringType escape(StringType s) { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); return s; } @@ -2687,24 +3131,36 @@ inline std::string escape(std::string s) * * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ -static void unescape(std::string& s) +template +inline void unescape(StringType& s) { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); } -} // namespace detail -} // namespace nlohmann +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // size_t -namespace nlohmann -{ +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { + /// struct to capture the start position of the current token struct position_t { @@ -2722,435 +3178,24 @@ struct position_t } }; -} // namespace detail -} // namespace nlohmann - -// #include - - -namespace nlohmann -{ -namespace detail -{ -//////////////// -// exceptions // -//////////////// - -/*! -@brief general exception of the @ref basic_json class - -This class is an extension of `std::exception` objects with a member @a id for -exception ids. It is used as the base class for all exceptions thrown by the -@ref basic_json class. This class can hence be used as "wildcard" to catch -exceptions. - -Subclasses: -- @ref parse_error for exceptions indicating a parse error -- @ref invalid_iterator for exceptions indicating errors with iterators -- @ref type_error for exceptions indicating executing a member function with - a wrong type -- @ref out_of_range for exceptions indicating access out of the defined range -- @ref other_error for exceptions indicating other library errors - -@internal -@note To have nothrow-copy-constructible exceptions, we internally use - `std::runtime_error` which can cope with arbitrary-length error messages. - Intermediate strings are built with static functions and then passed to - the actual constructor. -@endinternal - -@liveexample{The following code shows how arbitrary library exceptions can be -caught.,exception} - -@since version 3.0.0 -*/ -class exception : public std::exception -{ - public: - /// returns the explanatory string - const char* what() const noexcept override - { - return m.what(); - } - - /// the id of the exception - const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) - - protected: - JSON_HEDLEY_NON_NULL(3) - exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} - - static std::string name(const std::string& ename, int id_) - { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; - } - - template - static std::string diagnostics(const BasicJsonType& leaf_element) - { -#if JSON_DIAGNOSTICS - std::vector tokens; - for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) - { - switch (current->m_parent->type()) - { - case value_t::array: - { - for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) - { - if (¤t->m_parent->m_value.array->operator[](i) == current) - { - tokens.emplace_back(std::to_string(i)); - break; - } - } - break; - } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_value.object) - { - if (&element.second == current) - { - tokens.emplace_back(element.first.c_str()); - break; - } - } - break; - } - - case value_t::null: // LCOV_EXCL_LINE - case value_t::string: // LCOV_EXCL_LINE - case value_t::boolean: // LCOV_EXCL_LINE - case value_t::number_integer: // LCOV_EXCL_LINE - case value_t::number_unsigned: // LCOV_EXCL_LINE - case value_t::number_float: // LCOV_EXCL_LINE - case value_t::binary: // LCOV_EXCL_LINE - case value_t::discarded: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - } - } - - if (tokens.empty()) - { - return ""; - } - - return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + detail::escape(b); - }) + ") "; -#else - static_cast(leaf_element); - return ""; -#endif - } - - private: - /// an exception object as storage for error messages - std::runtime_error m; -}; - -/*! -@brief exception indicating a parse error - -This exception is thrown by the library when a parse error occurs. Parse errors -can occur during the deserialization of JSON text, CBOR, MessagePack, as well -as when using JSON Patch. - -Member @a byte holds the byte index of the last read character in the input -file. - -Exceptions have ids 1xx. - -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. -json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. -json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. -json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. -json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. -json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. -json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. -json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. -json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. -json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. -json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. -json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. -json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). -json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. - -@note For an input with n bytes, 1 is the index of the first character and n+1 - is the index of the terminating null byte or the end of file. This also - holds true when reading a byte vector (CBOR or MessagePack). - -@liveexample{The following code shows how a `parse_error` exception can be -caught.,parse_error} - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class parse_error : public exception -{ - public: - /*! - @brief create a parse error exception - @param[in] id_ the id of the exception - @param[in] pos the position where the error occurred (or with - chars_read_total=0 if the position cannot be - determined) - @param[in] what_arg the explanatory string - @return parse_error object - */ - template - static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + exception::diagnostics(context) + what_arg; - return parse_error(id_, pos.chars_read_total, w.c_str()); - } - - template - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + exception::diagnostics(context) + what_arg; - return parse_error(id_, byte_, w.c_str()); - } - - /*! - @brief byte index of the parse error - - The byte index of the last read character in the input file. - - @note For an input with n bytes, 1 is the index of the first character and - n+1 is the index of the terminating null byte or the end of file. - This also holds true when reading a byte vector (CBOR or MessagePack). - */ - const std::size_t byte; - - private: - parse_error(int id_, std::size_t byte_, const char* what_arg) - : exception(id_, what_arg), byte(byte_) {} - - static std::string position_string(const position_t& pos) - { - return " at line " + std::to_string(pos.lines_read + 1) + - ", column " + std::to_string(pos.chars_read_current_line); - } -}; - -/*! -@brief exception indicating errors with iterators - -This exception is thrown if iterators passed to a library function do not match -the expected semantics. - -Exceptions have ids 2xx. - -name / id | example message | description ------------------------------------ | --------------- | ------------------------- -json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. -json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. -json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. -json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. -json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. -json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. -json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. -json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. -json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. -json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). - -@liveexample{The following code shows how an `invalid_iterator` exception can be -caught.,invalid_iterator} - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class invalid_iterator : public exception -{ - public: - template - static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; - return invalid_iterator(id_, w.c_str()); - } - - private: - JSON_HEDLEY_NON_NULL(3) - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) {} -}; - -/*! -@brief exception indicating executing a member function with a wrong type - -This exception is thrown in case of a type error; that is, a library function is -executed on a JSON value whose type does not match the expected semantics. - -Exceptions have ids 3xx. - -name / id | example message | description ------------------------------ | --------------- | ------------------------- -json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. -json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. -json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. -json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. -json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. -json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. -json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. -json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. -json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. -json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. -json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. -json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. -json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. -json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. -json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. -json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | -json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | - -@liveexample{The following code shows how a `type_error` exception can be -caught.,type_error} - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class type_error : public exception -{ - public: - template - static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; - return type_error(id_, w.c_str()); - } - - private: - JSON_HEDLEY_NON_NULL(3) - type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/*! -@brief exception indicating access out of the defined range - -This exception is thrown in case a library function is called on an input -parameter that exceeds the expected range, for instance in case of array -indices or nonexisting object keys. - -Exceptions have ids 4xx. - -name / id | example message | description -------------------------------- | --------------- | ------------------------- -json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. -json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. -json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. -json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. -json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. -json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. -json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | -json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | -json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | - -@liveexample{The following code shows how an `out_of_range` exception can be -caught.,out_of_range} - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class out_of_range : public exception -{ - public: - template - static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; - return out_of_range(id_, w.c_str()); - } - - private: - JSON_HEDLEY_NON_NULL(3) - out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/*! -@brief exception indicating other library errors - -This exception is thrown in case of errors that cannot be classified with the -other exception types. - -Exceptions have ids 5xx. - -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range - -@liveexample{The following code shows how an `other_error` exception can be -caught.,other_error} - -@since version 3.0.0 -*/ -class other_error : public exception -{ - public: - template - static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; - return other_error(id_, w.c_str()); - } - - private: - JSON_HEDLEY_NON_NULL(3) - other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; } // namespace detail -} // namespace nlohmann +NLOHMANN_JSON_NAMESPACE_END // #include // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + +#include // array #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type #include // index_sequence, make_index_sequence, index_sequence_for @@ -3158,8 +3203,7 @@ class other_error : public exception // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -3296,52 +3340,66 @@ template<> struct priority_tag<0> {}; template struct static_const { - static constexpr T value{}; + static JSON_INLINE_VARIABLE constexpr T value{}; }; -template -constexpr T static_const::value; +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} } // namespace detail -} // namespace nlohmann - -// #include - - -namespace nlohmann -{ -namespace detail -{ -// dispatching helper struct -template struct identity_tag {}; -} // namespace detail -} // namespace nlohmann +NLOHMANN_JSON_NAMESPACE_END // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // numeric_limits +#include // char_traits +#include // tuple #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval -#include // tuple - -// #include - - +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L + #include // byte +#endif // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // random_access_iterator_tag +// #include + // #include // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { + template struct iterator_types {}; @@ -3367,7 +3425,7 @@ struct iterator_traits template struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types + : iterator_types { }; @@ -3380,118 +3438,136 @@ struct iterator_traits::value>> using pointer = T*; using reference = T&; }; -} // namespace detail -} // namespace nlohmann + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN + NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); -} // namespace nlohmann + +NLOHMANN_JSON_NAMESPACE_END // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN + NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); -} // namespace nlohmann + +NLOHMANN_JSON_NAMESPACE_END // #include // #include // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann +// SPDX-License-Identifier: MIT + #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ -#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ -#include // int64_t, uint64_t -#include // map -#include // allocator -#include // string -#include // vector + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ -/*! -@brief default JSONSerializer template argument + // #include -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template -struct adl_serializer; -template class ObjectType = - std::map, - template class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = - adl_serializer, - class BinaryType = std::vector> -class basic_json; + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN -/*! -@brief JSON Pointer + /*! + @brief default JSONSerializer template argument -A JSON pointer defines a string syntax for identifying a specific value -within a JSON document. It can be used with functions `at` and -`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; -@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> + class basic_json; -@since version 2.0.0 -*/ -template -class json_pointer; + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; -/*! -@brief default JSON class + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; -This type is the default specialization of the @ref basic_json class which -uses the standard template types. + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; -@since version 1.0.0 -*/ -using json = basic_json<>; + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; -template -struct ordered_map; - -/*! -@brief ordered JSON class - -This type preserves the insertion order of object keys. - -@since version 3.9.0 -*/ -using ordered_json = basic_json; - -} // namespace nlohmann + NLOHMANN_JSON_NAMESPACE_END #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN /*! @brief detail namespace with internal helper functions @@ -3502,24 +3578,35 @@ implementations of some @ref basic_json methods, and meta-programming helpers. */ namespace detail { + ///////////// // helpers // ///////////// // Note to maintainers: // -// Every trait in this file expects a non CV-qualified type. +// Every trait in this file expects a non-CV-qualified type. // The only exceptions are in the 'aliases for detected' section -// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// (i.e., those of the form: decltype(T::member_function(std::declval()))) // // In this case, T has to be properly CV-qualified to constraint the function arguments -// (e.g. to_json(BasicJsonType&, const T&)) +// (e.g., to_json(BasicJsonType&, const T&)) template struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; +// used by exceptions create() member functions +// true_type for the pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + ////////////////////// // json_ref helpers // ////////////////////// @@ -3621,6 +3708,105 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> T>::value; }; +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +///////////////// +// char_traits // +///////////////// + +// Primary template of char_traits calls std char_traits +template +struct char_traits : std::char_traits +{}; + +// Explicitly define char traits for unsigned char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = unsigned char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(std::char_traits::eof()); + } +}; + +// Explicitly define char traits for signed char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = signed char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(std::char_traits::eof()); + } +}; + +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L +template<> +struct char_traits : std::char_traits +{ + using char_type = std::byte; + using int_type = uint64_t; + + static int_type to_int_type(char_type c) noexcept + { + return static_cast(std::to_integer(c)); + } + + static char_type to_char_type(int_type i) noexcept + { + return std::byte(static_cast(i)); + } + + static constexpr int_type eof() noexcept + { + return static_cast(std::char_traits::eof()); + } +}; +#endif /////////////////// // is_ functions // @@ -3628,36 +3814,35 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> // https://en.cppreference.com/w/cpp/types/conjunction template struct conjunction : std::true_type { }; -template struct conjunction : B1 { }; -template -struct conjunction -: std::conditional, B1>::type {}; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; // https://en.cppreference.com/w/cpp/types/negation template struct negation : std::integral_constant < bool, !B::value > { }; // Reimplementation of is_constructible and is_default_constructible, due to them being broken for // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). -// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +// This causes compile errors in e.g., Clang 3.5 or GCC 4.9. template struct is_default_constructible : std::is_default_constructible {}; template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> {}; template struct is_default_constructible> - : conjunction, is_default_constructible> {}; + : conjunction, is_default_constructible> {}; template struct is_default_constructible> - : conjunction...> {}; + : conjunction...> {}; template struct is_default_constructible> - : conjunction...> {}; - + : conjunction...> {}; template struct is_constructible : std::is_constructible {}; @@ -3674,7 +3859,6 @@ struct is_constructible> : is_default_constructible struct is_constructible> : is_default_constructible> {}; - template struct is_iterator_traits : std::false_type {}; @@ -3720,7 +3904,7 @@ using range_value_t = value_type_t>>; // The following implementation of is_complete_type is taken from // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ -// and is written by Xiang Fan who agreed to using it in this library. +// and is written by Xiang Fan who agreed to use it in this library. template struct is_complete_type : std::false_type {}; @@ -3795,9 +3979,18 @@ struct is_compatible_string_type template struct is_constructible_string_type { + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + static constexpr auto value = - is_constructible::value; + conjunction < + is_constructible, + is_detected_exact>::value; }; template @@ -3847,8 +4040,8 @@ is_detected::value&& // special case for types like std::filesystem::path whose iterator's value_type are themselves // c.f. https://github.com/nlohmann/json/pull/3073 !std::is_same>::value&& - is_complete_type < - detected_t>::value >> +is_complete_type < +detected_t>::value >> { using value_type = range_value_t; @@ -3915,6 +4108,81 @@ struct is_constructible_tuple : std::false_type {}; template struct is_constructible_tuple> : conjunction...> {}; +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template