anm2 experiments

This commit is contained in:
2026-04-24 13:44:33 -04:00
parent d39a1dac3c
commit e9deac2f38
6 changed files with 806 additions and 7 deletions
+2
View File
@@ -88,6 +88,8 @@ set(TINYXML2_SRC external/tinyxml2/tinyxml2.cpp)
file(GLOB PROJECT_SRC CONFIGURE_DEPENDS
src/anm2/*.cpp
src/anm2/*.hpp
src/anm2_new/*.cpp
src/anm2_new/*.hpp
src/resource/*.cpp
src/resource/*.hpp
src/imgui/*.cpp
+1 -1
View File
@@ -1 +1 @@
/home/anon/sda/Personal/Repos/anm2ed/out/build/linux-debug/compile_commands.json
/home/anon/sda/Personal/Repos/anm2ed/out/build/linux-release/compile_commands.json
+474
View File
@@ -0,0 +1,474 @@
#include "anm2.hpp"
#include <tinyxml2/tinyxml2.h>
#include "file_.hpp"
#include "path_.hpp"
#include "xml_.hpp"
namespace anm2ed::anm2_new
{
using namespace tinyxml2;
template <typename Enum, std::size_t N>
constexpr std::string_view enum_string_get(const std::array<std::pair<Enum, std::string_view>, N>& mappings,
Enum value)
{
for (const auto& [mappedValue, mappedString] : mappings)
if (mappedValue == value) return mappedString;
return {};
}
template <typename Enum, std::size_t N>
constexpr Enum string_enum_get(const std::array<std::pair<Enum, std::string_view>, N>& mappings,
std::string_view value, Enum fallback)
{
for (const auto& [mappedValue, mappedString] : mappings)
if (mappedString == value) return mappedValue;
return fallback;
}
constexpr bool is_flag_set(Anm2::Flags flags, Anm2::Flag flag) { return (flags & flag) != 0; }
std::filesystem::path asset_path_resolve(const std::filesystem::path& directory,
const std::filesystem::path& assetPath)
{
if (assetPath.empty()) return {};
auto resolved = assetPath.is_absolute() ? assetPath : directory / assetPath;
resolved = util::path::lower_case_backslash_handle(resolved);
if (util::path::is_exist(resolved)) return resolved;
return util::path::lower_case_backslash_handle(assetPath);
}
std::string_view Anm2::Element::tag_get(Type type) { return enum_string_get(TYPE_TAGS, type); }
Anm2::Element::Type Anm2::Element::type_get(std::string_view tag, Type parentType)
{
(void)parentType;
return string_enum_get(TYPE_TAGS, tag, UNKNOWN);
}
Anm2::Element::Element(const std::filesystem::path& path, std::string* errorString)
{
XMLDocument document{};
if (!document_load(document, path, errorString)) return;
if (auto* root = document.RootElement()) *this = from_xml(root);
}
Anm2::Element::Element(std::string_view xml, Source, std::string* errorString)
{
XMLDocument document{};
if (!document_parse(document, xml, errorString)) return;
if (auto* root = document.RootElement()) *this = from_xml(root);
}
bool Anm2::Element::document_load(XMLDocument& document, const std::filesystem::path& path, std::string* errorString)
{
util::File file(path, "rb");
if (!file)
{
if (errorString) *errorString = "Could not open file.";
return false;
}
if (document.LoadFile(file.get()) != XML_SUCCESS)
{
if (errorString) *errorString = document.ErrorStr();
return false;
}
return true;
}
bool Anm2::Element::document_parse(XMLDocument& document, std::string_view xml, std::string* errorString)
{
if (document.Parse(xml.data(), xml.size()) != XML_SUCCESS)
{
if (errorString) *errorString = document.ErrorStr();
return false;
}
return true;
}
void Anm2::Element::attributes_read(const XMLElement* element, Type parentType)
{
if (!element) return;
util::xml::query_string_attribute(element, "CreatedBy", createdBy);
util::xml::query_string_attribute(element, "CreatedOn", createdOn);
util::xml::query_string_attribute(element, "DefaultAnimation", defaultAnimation);
util::xml::query_string_attribute(element, "Name", name);
util::xml::query_path_attribute(element, "Path", path);
util::xml::query_int_attribute(element, "Id", id);
util::xml::query_int_attribute(element, "LayerId", layerId);
util::xml::query_int_attribute(element, "NullId", nullId);
util::xml::query_int_attribute(element, "SpritesheetId", spritesheetId);
util::xml::query_int_attribute(element, "EventId", eventId);
util::xml::query_int_attribute(element, "RegionId", regionId);
util::xml::query_int_attribute(element, "AtFrame", atFrame);
util::xml::query_int_attribute(element, "FrameNum", frameNum);
util::xml::query_int_attribute(element, "Fps", fps);
util::xml::query_int_attribute(element, "Version", version);
util::xml::query_int_attribute(element, "Delay", delay);
util::xml::query_float_attribute(element, "XCrop", crop.x);
util::xml::query_float_attribute(element, "YCrop", crop.y);
util::xml::query_float_attribute(element, "Width", size.x);
util::xml::query_float_attribute(element, "Height", size.y);
util::xml::query_float_attribute(element, "XPivot", pivot.x);
util::xml::query_float_attribute(element, "YPivot", pivot.y);
util::xml::query_float_attribute(element, "XPosition", position.x);
util::xml::query_float_attribute(element, "YPosition", position.y);
util::xml::query_float_attribute(element, "XScale", scale.x);
util::xml::query_float_attribute(element, "YScale", scale.y);
util::xml::query_float_attribute(element, "Rotation", rotation);
util::xml::query_color_attribute(element, "RedTint", tint.r);
util::xml::query_color_attribute(element, "GreenTint", tint.g);
util::xml::query_color_attribute(element, "BlueTint", tint.b);
util::xml::query_color_attribute(element, "AlphaTint", tint.a);
util::xml::query_color_attribute(element, "RedOffset", colorOffset.r);
util::xml::query_color_attribute(element, "GreenOffset", colorOffset.g);
util::xml::query_color_attribute(element, "BlueOffset", colorOffset.b);
util::xml::query_bool_attribute(element, "Visible", isVisible);
util::xml::query_bool_attribute(element, "ShowRect", isShowRect);
util::xml::query_bool_attribute(element, "Loop", isLoop);
if (const char* value = element->Attribute("Origin")) origin = string_enum_get(ORIGIN_STRINGS, value, CUSTOM);
if (const char* value = element->Attribute("Interpolated"))
{
auto view = std::string_view(value);
if (view == "true" || view == "1")
interpolation = LINEAR;
else if (view == "false" || view == "0")
interpolation = NONE;
else
interpolation = string_enum_get(INTERPOLATION_STRINGS, view, NONE);
}
if (type == SOUND && parentType == TRIGGER) path.clear();
}
void Anm2::Element::attributes_write(XMLElement* element, Type parentType, Flags flags) const
{
if (!element) return;
switch (type)
{
case INFO:
util::xml::set_string_attribute(element, "CreatedBy", createdBy);
util::xml::set_string_attribute(element, "CreatedOn", createdOn);
util::xml::set_int_attribute(element, "Fps", fps);
util::xml::set_int_attribute(element, "Version", version);
break;
case SPRITESHEET:
if (id > -1) util::xml::set_int_attribute(element, "Id", id);
util::xml::set_path_attribute(element, "Path", path);
break;
case REGION:
if (id > -1) util::xml::set_int_attribute(element, "Id", id);
util::xml::set_string_attribute(element, "Name", name);
util::xml::set_float_attribute(element, "XCrop", crop.x);
util::xml::set_float_attribute(element, "YCrop", crop.y);
util::xml::set_float_attribute(element, "Width", size.x);
util::xml::set_float_attribute(element, "Height", size.y);
if (origin == CUSTOM)
{
util::xml::set_float_attribute(element, "XPivot", pivot.x);
util::xml::set_float_attribute(element, "YPivot", pivot.y);
}
else if (auto value = enum_string_get(ORIGIN_STRINGS, origin); !value.empty())
element->SetAttribute("Origin", value.data());
break;
case LAYER:
if (id > -1) util::xml::set_int_attribute(element, "Id", id);
util::xml::set_string_attribute(element, "Name", name);
if (spritesheetId > -1) util::xml::set_int_attribute(element, "SpritesheetId", spritesheetId);
break;
case NULL_ELEMENT:
if (id > -1) util::xml::set_int_attribute(element, "Id", id);
util::xml::set_string_attribute(element, "Name", name);
util::xml::set_bool_attribute(element, "ShowRect", isShowRect);
break;
case EVENT:
if (id > -1) util::xml::set_int_attribute(element, "Id", id);
util::xml::set_string_attribute(element, "Name", name);
break;
case SOUND:
if (id > -1)
{
if (parentType == TRIGGER)
util::xml::set_int_attribute(element, "Id", id);
else
{
util::xml::set_int_attribute(element, "Id", id);
util::xml::set_path_attribute(element, "Path", path);
}
}
break;
case ANIMATIONS:
util::xml::set_string_attribute(element, "DefaultAnimation", defaultAnimation);
break;
case ANIMATION:
util::xml::set_string_attribute(element, "Name", name);
util::xml::set_int_attribute(element, "FrameNum", frameNum);
util::xml::set_bool_attribute(element, "Loop", isLoop);
break;
case LAYER_ANIMATION:
if (layerId > -1) util::xml::set_int_attribute(element, "LayerId", layerId);
util::xml::set_bool_attribute(element, "Visible", isVisible);
break;
case NULL_ANIMATION:
if (nullId > -1) util::xml::set_int_attribute(element, "NullId", nullId);
util::xml::set_bool_attribute(element, "Visible", isVisible);
break;
case TRIGGER:
if (eventId > -1) util::xml::set_int_attribute(element, "EventId", eventId);
util::xml::set_int_attribute(element, "AtFrame", atFrame);
break;
case FRAME:
{
bool isNoRegions = is_flag_set(flags, NO_REGIONS);
bool isFrameNoRegionValues = is_flag_set(flags, FRAME_NO_REGION_VALUES);
bool isRegionValid = parentType == LAYER_ANIMATION && !isNoRegions && regionId != -1;
bool isWriteRegionValues = parentType == LAYER_ANIMATION && (!isFrameNoRegionValues || !isRegionValid);
if (isRegionValid) util::xml::set_int_attribute(element, "RegionId", regionId);
util::xml::set_float_attribute(element, "XPosition", position.x);
util::xml::set_float_attribute(element, "YPosition", position.y);
if (isWriteRegionValues)
{
util::xml::set_float_attribute(element, "XPivot", pivot.x);
util::xml::set_float_attribute(element, "YPivot", pivot.y);
util::xml::set_float_attribute(element, "XCrop", crop.x);
util::xml::set_float_attribute(element, "YCrop", crop.y);
util::xml::set_float_attribute(element, "Width", size.x);
util::xml::set_float_attribute(element, "Height", size.y);
}
util::xml::set_float_attribute(element, "XScale", scale.x);
util::xml::set_float_attribute(element, "YScale", scale.y);
util::xml::set_int_attribute(element, "Delay", delay);
util::xml::set_bool_attribute(element, "Visible", isVisible);
util::xml::set_color_attribute(element, "RedTint", tint.r);
util::xml::set_color_attribute(element, "GreenTint", tint.g);
util::xml::set_color_attribute(element, "BlueTint", tint.b);
util::xml::set_color_attribute(element, "AlphaTint", tint.a);
util::xml::set_color_attribute(element, "RedOffset", colorOffset.r);
util::xml::set_color_attribute(element, "GreenOffset", colorOffset.g);
util::xml::set_color_attribute(element, "BlueOffset", colorOffset.b);
util::xml::set_float_attribute(element, "Rotation", rotation);
if (is_flag_set(flags, INTERPOLATION_BOOL_ONLY) || interpolation == LINEAR || interpolation == NONE)
element->SetAttribute("Interpolated", interpolation == LINEAR);
else if (auto value = enum_string_get(INTERPOLATION_STRINGS, interpolation); !value.empty())
element->SetAttribute("Interpolated", value.data());
break;
}
case ACTOR:
case CONTENT:
case SPRITESHEETS:
case LAYERS:
case NULLS:
case EVENTS:
case SOUNDS:
case ROOT_ANIMATION:
case LAYER_ANIMATIONS:
case NULL_ANIMATIONS:
case TRIGGERS:
case UNKNOWN:
case TYPE_COUNT:
break;
}
}
Anm2::Element Anm2::Element::from_xml(const XMLElement* element, Type parentType)
{
Element parsed{};
if (!element) return parsed;
parsed.type = type_get(element->Name(), parentType);
if (parsed.type == UNKNOWN) return parsed;
parsed.attributes_read(element, parentType);
for (const XMLElement* child = element->FirstChildElement(); child; child = child->NextSiblingElement())
if (auto parsedChild = from_xml(child, parsed.type); parsedChild.type != UNKNOWN)
parsed.children.push_back(std::move(parsedChild));
return parsed;
}
XMLElement* Anm2::Element::to_xml(XMLDocument& document, Type parentType, Flags flags) const
{
auto tagName = tag_get(type);
if (tagName.empty()) return nullptr;
auto* element = document.NewElement(tagName.data());
attributes_write(element, parentType, flags);
for (const auto& child : children)
{
if (is_flag_set(flags, NO_SOUNDS) && (child.type == SOUNDS || child.type == SOUND)) continue;
if (is_flag_set(flags, NO_REGIONS) && child.type == REGION) continue;
if (auto* childElement = child.to_xml(document, type, flags)) element->InsertEndChild(childElement);
}
return element;
}
bool Anm2::Element::serialize(const std::filesystem::path& path, std::string* errorString,
Compatibility compatibility) const
{
XMLDocument document{};
if (auto* element = to_xml(document, UNKNOWN, Anm2::flags_get(compatibility)))
document.InsertFirstChild(element);
else
{
if (errorString) *errorString = "No supported element to serialize.";
return false;
}
util::File file(path, "wb");
if (!file)
{
if (errorString) *errorString = "Could not open file for writing.";
return false;
}
if (document.SaveFile(file.get()) != XML_SUCCESS)
{
if (errorString) *errorString = document.ErrorStr();
return false;
}
return true;
}
std::string Anm2::Element::to_string(Compatibility compatibility) const
{
XMLDocument document{};
if (auto* element = to_xml(document, UNKNOWN, Anm2::flags_get(compatibility))) document.InsertFirstChild(element);
return util::xml::document_to_string(document);
}
Anm2::Element* Anm2::Element::child_get(Type childType, std::size_t index)
{
std::size_t matchIndex{};
for (auto& child : children)
if (child.type == childType && matchIndex++ == index) return &child;
return nullptr;
}
const Anm2::Element* Anm2::Element::child_get(Type childType, std::size_t index) const
{
std::size_t matchIndex{};
for (const auto& child : children)
if (child.type == childType && matchIndex++ == index) return &child;
return nullptr;
}
std::vector<Anm2::Element*> Anm2::Element::children_get(Type childType)
{
std::vector<Element*> matches{};
for (auto& child : children)
if (child.type == childType) matches.push_back(&child);
return matches;
}
std::vector<const Anm2::Element*> Anm2::Element::children_get(Type childType) const
{
std::vector<const Element*> matches{};
for (const auto& child : children)
if (child.type == childType) matches.push_back(&child);
return matches;
}
Anm2::Anm2(const std::filesystem::path& filepath, std::string* errorString)
: path(filepath), root(filepath, errorString)
{
isValid = root.type == Element::ACTOR;
assets_reload();
}
bool Anm2::serialize(const std::filesystem::path& filepath, std::string* errorString,
Compatibility compatibility) const
{
return root.serialize(filepath, errorString, compatibility);
}
std::string Anm2::to_string(Compatibility compatibility) const { return root.to_string(compatibility); }
void Anm2::assets_reload()
{
spritesheets.clear();
sounds.clear();
auto directory = path.parent_path();
for (auto* element : elements_get(Element::SPRITESHEET))
if (element->id > -1 && !element->path.empty())
spritesheets[element->id] = resource::Texture(asset_path_resolve(directory, element->path));
for (auto* element : elements_get(Element::SOUND))
if (element->id > -1 && !element->path.empty())
sounds[element->id] = resource::Audio(asset_path_resolve(directory, element->path));
}
Anm2::Element* Anm2::element_get(Element::Type type, std::size_t index)
{
auto matches = elements_get(type);
return index < matches.size() ? matches[index] : nullptr;
}
const Anm2::Element* Anm2::element_get(Element::Type type, std::size_t index) const
{
auto matches = elements_get(type);
return index < matches.size() ? matches[index] : nullptr;
}
std::vector<Anm2::Element*> Anm2::elements_get(Element::Type type)
{
std::vector<Element*> matches{};
elements_collect(root, type, matches);
return matches;
}
std::vector<const Anm2::Element*> Anm2::elements_get(Element::Type type) const
{
std::vector<const Element*> matches{};
elements_collect(root, type, matches);
return matches;
}
void Anm2::elements_collect(Element& element, Element::Type type, std::vector<Element*>& out)
{
if (element.type == type) out.push_back(&element);
for (auto& child : element.children)
elements_collect(child, type, out);
}
void Anm2::elements_collect(const Element& element, Element::Type type, std::vector<const Element*>& out)
{
if (element.type == type) out.push_back(&element);
for (const auto& child : element.children)
elements_collect(child, type, out);
}
}
+233
View File
@@ -0,0 +1,233 @@
#pragma once
#include <array>
#include <filesystem>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
#include <glm/glm/vec2.hpp>
#include <glm/glm/vec3.hpp>
#include <glm/glm/vec4.hpp>
#include "audio.hpp"
#include "texture.hpp"
namespace tinyxml2
{
class XMLDocument;
class XMLElement;
}
namespace anm2ed::anm2_new
{
class Anm2
{
public:
enum Compatibility
{
ISAAC,
ANM2ED,
ANM2ED_LIMITED,
COMPATIBILITY_COUNT
};
enum Flag
{
NO_SOUNDS = 1 << 0,
NO_REGIONS = 1 << 1,
FRAME_NO_REGION_VALUES = 1 << 2,
INTERPOLATION_BOOL_ONLY = 1 << 3
};
using Flags = int;
inline static constexpr std::array<Flags, COMPATIBILITY_COUNT> COMPATIBILITY_FLAGS = {{
NO_SOUNDS | NO_REGIONS | FRAME_NO_REGION_VALUES | INTERPOLATION_BOOL_ONLY,
0,
FRAME_NO_REGION_VALUES,
}};
class Element
{
public:
enum Source
{
STRING
};
enum Type
{
UNKNOWN,
ACTOR,
INFO,
CONTENT,
SPRITESHEETS,
SPRITESHEET,
REGION,
LAYERS,
LAYER,
NULLS,
NULL_ELEMENT,
EVENTS,
EVENT,
SOUNDS,
SOUND,
ANIMATIONS,
ANIMATION,
ROOT_ANIMATION,
LAYER_ANIMATIONS,
LAYER_ANIMATION,
NULL_ANIMATIONS,
NULL_ANIMATION,
TRIGGERS,
TRIGGER,
FRAME,
TYPE_COUNT
};
enum Origin
{
TOP_LEFT,
ORIGIN_CENTER,
CUSTOM
};
enum Interpolation
{
NONE,
LINEAR,
EASE_IN,
EASE_OUT,
EASE_IN_OUT
};
inline static constexpr std::array<std::pair<Type, std::string_view>, TYPE_COUNT - 1> TYPE_TAGS = {{
{ACTOR, "AnimatedActor"},
{INFO, "Info"},
{CONTENT, "Content"},
{SPRITESHEETS, "Spritesheets"},
{SPRITESHEET, "Spritesheet"},
{REGION, "Region"},
{LAYERS, "Layers"},
{LAYER, "Layer"},
{NULLS, "Nulls"},
{NULL_ELEMENT, "Null"},
{EVENTS, "Events"},
{EVENT, "Event"},
{SOUNDS, "Sounds"},
{SOUND, "Sound"},
{ANIMATIONS, "Animations"},
{ANIMATION, "Animation"},
{ROOT_ANIMATION, "RootAnimation"},
{LAYER_ANIMATIONS, "LayerAnimations"},
{LAYER_ANIMATION, "LayerAnimation"},
{NULL_ANIMATIONS, "NullAnimations"},
{NULL_ANIMATION, "NullAnimation"},
{TRIGGERS, "Triggers"},
{TRIGGER, "Trigger"},
{FRAME, "Frame"},
}};
inline static constexpr std::array<std::pair<Origin, std::string_view>, 2> ORIGIN_STRINGS = {{
{TOP_LEFT, "TopLeft"},
{ORIGIN_CENTER, "Center"},
}};
inline static constexpr std::array<std::pair<Interpolation, std::string_view>, 3> INTERPOLATION_STRINGS = {{
{EASE_IN, "EaseIn"},
{EASE_OUT, "EaseOut"},
{EASE_IN_OUT, "EaseInOut"},
}};
Type type{UNKNOWN};
std::vector<Element> children{};
std::string createdBy{};
std::string createdOn{};
std::string defaultAnimation{};
std::string name{};
std::filesystem::path path{};
int id{-1};
int layerId{-1};
int nullId{-1};
int spritesheetId{-1};
int eventId{-1};
int regionId{-1};
int atFrame{-1};
int frameNum{};
int fps{30};
int version{};
int delay{1};
glm::vec2 crop{};
glm::vec2 size{};
glm::vec2 pivot{};
glm::vec2 position{};
glm::vec2 scale{100.0f, 100.0f};
glm::vec4 tint{1.0f, 1.0f, 1.0f, 1.0f};
glm::vec3 colorOffset{};
float rotation{};
bool isVisible{true};
bool isShowRect{false};
bool isLoop{false};
Origin origin{CUSTOM};
Interpolation interpolation{NONE};
Element() = default;
explicit Element(const std::filesystem::path&, std::string* = nullptr);
Element(std::string_view, Source, std::string* = nullptr);
bool serialize(const std::filesystem::path&, std::string* = nullptr, Compatibility = ANM2ED) const;
std::string to_string(Compatibility = ANM2ED) const;
Element* child_get(Type, std::size_t index = 0);
const Element* child_get(Type, std::size_t index = 0) const;
std::vector<Element*> children_get(Type);
std::vector<const Element*> children_get(Type) const;
static std::string_view tag_get(Type);
static Type type_get(std::string_view, Type = UNKNOWN);
static Element from_xml(const tinyxml2::XMLElement*, Type = UNKNOWN);
static bool document_load(tinyxml2::XMLDocument&, const std::filesystem::path&, std::string*);
static bool document_parse(tinyxml2::XMLDocument&, std::string_view, std::string*);
tinyxml2::XMLElement* to_xml(tinyxml2::XMLDocument&, Type = UNKNOWN, Flags = 0) const;
private:
void attributes_read(const tinyxml2::XMLElement*, Type = UNKNOWN);
void attributes_write(tinyxml2::XMLElement*, Type = UNKNOWN, Flags = 0) const;
};
bool isValid{true};
std::filesystem::path path{};
Element root{};
std::unordered_map<int, resource::Texture> spritesheets{};
std::unordered_map<int, resource::Audio> sounds{};
Anm2() = default;
explicit Anm2(const std::filesystem::path&, std::string* = nullptr);
bool serialize(const std::filesystem::path&, std::string* = nullptr, Compatibility = ANM2ED) const;
std::string to_string(Compatibility = ANM2ED) const;
void assets_reload();
static constexpr Flags flags_get(Compatibility compatibility)
{
return COMPATIBILITY_FLAGS[static_cast<std::size_t>(compatibility)];
}
Element* element_get(Element::Type, std::size_t index = 0);
const Element* element_get(Element::Type, std::size_t index = 0) const;
std::vector<Element*> elements_get(Element::Type);
std::vector<const Element*> elements_get(Element::Type) const;
private:
static void elements_collect(Element&, Element::Type, std::vector<Element*>&);
static void elements_collect(const Element&, Element::Type, std::vector<const Element*>&);
};
}
+81 -6
View File
@@ -15,16 +15,93 @@ namespace anm2ed::util::xml
return std::string(printer.CStr());
}
bool query_string_attribute(const XMLElement* element, const char* attribute, std::string& out)
{
if (!element || !attribute) return false;
const char* value = nullptr;
if (element->QueryStringAttribute(attribute, &value) != XML_SUCCESS || !value) return false;
out = value;
return true;
}
bool query_path_attribute(const XMLElement* element, const char* attribute, std::filesystem::path& out)
{
std::string temp{};
if (!query_string_attribute(element, attribute, temp)) return false;
out = path::from_utf8(temp);
return true;
}
bool query_int_attribute(const XMLElement* element, const char* attribute, int& out)
{
return element && attribute && element->QueryIntAttribute(attribute, &out) == XML_SUCCESS;
}
bool query_float_attribute(const XMLElement* element, const char* attribute, float& out)
{
return element && attribute && element->QueryFloatAttribute(attribute, &out) == XML_SUCCESS;
}
bool query_bool_attribute(const XMLElement* element, const char* attribute, bool& out)
{
return element && attribute && element->QueryBoolAttribute(attribute, &out) == XML_SUCCESS;
}
bool query_color_attribute(const XMLElement* element, const char* attribute, float& out)
{
int value{};
if (!query_int_attribute(element, attribute, value)) return false;
out = math::uint8_to_float(value);
return true;
}
void set_string_attribute(XMLElement* element, const char* attribute, const std::string& value)
{
if (element && attribute && !value.empty()) element->SetAttribute(attribute, value.c_str());
}
void set_path_attribute(XMLElement* element, const char* attribute, const std::filesystem::path& value)
{
if (!element || !attribute || value.empty()) return;
auto utf8 = path::to_utf8(value);
element->SetAttribute(attribute, utf8.c_str());
}
void set_int_attribute(XMLElement* element, const char* attribute, int value)
{
if (element && attribute) element->SetAttribute(attribute, value);
}
void set_float_attribute(XMLElement* element, const char* attribute, float value)
{
if (element && attribute) element->SetAttribute(attribute, value);
}
void set_bool_attribute(XMLElement* element, const char* attribute, bool value)
{
if (element && attribute) element->SetAttribute(attribute, value);
}
void set_color_attribute(XMLElement* element, const char* attribute, float value)
{
if (element && attribute) element->SetAttribute(attribute, math::float_to_uint8(value));
}
XMLError query_string_attribute(XMLElement* element, const char* attribute, std::string* out)
{
const char* temp = nullptr;
auto result = element->QueryStringAttribute(attribute, &temp);
if (result == XML_SUCCESS && temp) *out = temp;
if (!element || !attribute || !out) return XML_NO_ATTRIBUTE;
const char* value = nullptr;
auto result = element->QueryStringAttribute(attribute, &value);
if (result == XML_SUCCESS && value) *out = value;
return result;
}
XMLError query_path_attribute(XMLElement* element, const char* attribute, std::filesystem::path* out)
{
if (!out) return XML_NO_ATTRIBUTE;
std::string temp{};
auto result = query_string_attribute(element, attribute, &temp);
if (result == XML_SUCCESS) *out = path::from_utf8(temp);
@@ -33,8 +110,6 @@ namespace anm2ed::util::xml
void query_color_attribute(XMLElement* element, const char* attribute, float& out)
{
int value{};
element->QueryIntAttribute(attribute, &value);
out = math::uint8_to_float(value);
query_color_attribute(static_cast<const XMLElement*>(element), attribute, out);
}
}
+15
View File
@@ -8,6 +8,21 @@
namespace anm2ed::util::xml
{
std::string document_to_string(tinyxml2::XMLDocument&);
bool query_string_attribute(const tinyxml2::XMLElement*, const char*, std::string&);
bool query_path_attribute(const tinyxml2::XMLElement*, const char*, std::filesystem::path&);
bool query_int_attribute(const tinyxml2::XMLElement*, const char*, int&);
bool query_float_attribute(const tinyxml2::XMLElement*, const char*, float&);
bool query_bool_attribute(const tinyxml2::XMLElement*, const char*, bool&);
bool query_color_attribute(const tinyxml2::XMLElement*, const char*, float&);
void set_string_attribute(tinyxml2::XMLElement*, const char*, const std::string&);
void set_path_attribute(tinyxml2::XMLElement*, const char*, const std::filesystem::path&);
void set_int_attribute(tinyxml2::XMLElement*, const char*, int);
void set_float_attribute(tinyxml2::XMLElement*, const char*, float);
void set_bool_attribute(tinyxml2::XMLElement*, const char*, bool);
void set_color_attribute(tinyxml2::XMLElement*, const char*, float);
tinyxml2::XMLError query_string_attribute(tinyxml2::XMLElement*, const char*, std::string*);
tinyxml2::XMLError query_path_attribute(tinyxml2::XMLElement*, const char*, std::filesystem::path*);
void query_color_attribute(tinyxml2::XMLElement*, const char*, float&);