diff --git a/assets/atlas.png b/assets/atlas.png index 4b0b1e6..c47c6f1 100644 Binary files a/assets/atlas.png and b/assets/atlas.png differ diff --git a/assets/atlas_data_write.sh b/assets/atlas_data_write.sh new file mode 100755 index 0000000..1cf391f --- /dev/null +++ b/assets/atlas_data_write.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -euo pipefail + +INPUT="atlas.png" +OUTPUT="../src/PACKED.h" +TMP="atlas.bytes" + +# Ensure deps +command -v optipng >/dev/null 2>&1 || { echo "error: optipng not found in PATH" >&2; exit 1; } +command -v xxd >/dev/null 2>&1 || { echo "error: xxd not found in PATH" >&2; exit 1; } + +# 1) Optimize PNG in place +optipng -o7 "$INPUT" >/dev/null + +# 2) Extract ONLY the bytes from xxd -i (between '= {' and '};') +# Using awk avoids the earlier '{' vs '= {' mismatch bug. +xxd -i "$INPUT" \ + | awk ' + /= *\{/ {inside=1; next} + inside && /};/ {inside=0; exit} + inside {print} + ' > "$TMP" + +# Sanity check: make sure we got something +if ! [ -s "$TMP" ]; then + echo "error: failed to extract bytes from xxd output" >&2 + exit 1 +fi + +# 3) Replace ONLY the bytes inside TEXTURE_ATLAS[] initializer +# - Find the exact declaration line for TEXTURE_ATLAS +# - Print that line +# - On the following line with just '{', print it, then insert bytes, +# then skip everything until the matching '};' and print that once. +awk -v tmpfile="$TMP" ' + BEGIN { state=0 } # 0=normal, 1=after decl waiting for {, 2=skipping old bytes until }; + + # Match the TEXTURE_ATLAS declaration line precisely + $0 ~ /^[[:space:]]*const[[:space:]]+u8[[:space:]]+TEXTURE_ATLAS\[\][[:space:]]*=/ { + print; state=1; next + } + + # After the decl, the next line with a lone "{" starts the initializer + state==1 && $0 ~ /^[[:space:]]*{[[:space:]]*$/ { + print # print the opening brace line + while ((getline line < tmpfile) > 0) print line # insert fresh bytes + close(tmpfile) + state=2 # now skip old initializer content until we hit the closing "};" + next + } + + # While skipping, suppress lines until the closing "};", which we reprint once + state==2 { + if ($0 ~ /^[[:space:]]*};[[:space:]]*$/) { + print # print the closing brace+semicolon + state=0 + } + next + } + + # Default: pass through unchanged + { print } +' "$OUTPUT" > "$OUTPUT.tmp" && mv "$OUTPUT.tmp" "$OUTPUT" + +rm -f "$TMP" +echo "Updated $OUTPUT with optimized bytes from $INPUT." diff --git a/src/COMMON.h b/src/COMMON.h index a888787..550f296 100644 --- a/src/COMMON.h +++ b/src/COMMON.h @@ -44,6 +44,7 @@ typedef double f64; #define TAU (PI * 2) using namespace glm; +using namespace tinyxml2; #define PREFERENCES_DIRECTORY "anm2ed" @@ -203,11 +204,6 @@ static inline bool path_is_valid(const std::filesystem::path& pathCheck) return isValid; } -static inline const char* enum_to_string(const char* array[], s32 count, s32 index) -{ - return (index >= 0 && index < count) ? array[index] : ""; -}; - static inline s32 string_to_enum(const std::string& string, const char* const* array, s32 n) { for (s32 i = 0; i < n; i++) @@ -326,12 +322,6 @@ static inline mat4 quad_model_parent_get(vec2 position = {}, vec2 pivot = {}, ve return glm::translate(mat4(1.0f), vec3(position, 0.0f)) * local; } -#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \ - static inline std::string function(s32 index) \ - { \ - return enum_to_string(array, count, index); \ - }; - #define DEFINE_STRING_TO_ENUM_FUNCTION(function, enumType, stringArray, count) \ static inline enumType function(const std::string& string) \ { \ diff --git a/src/PACKED.h b/src/PACKED.h index 2adc9ee..a72ce1a 100644 --- a/src/PACKED.h +++ b/src/PACKED.h @@ -1,5 +1,3 @@ -// Includes packed resources (i.e., textures, shaders) - #pragma once #include "COMMON.h" @@ -11,100 +9,110 @@ const u8 TEXTURE_ATLAS[] = 0x04, 0x03, 0x00, 0x00, 0x00, 0x01, 0x5e, 0x74, 0xbf, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x0f, 0x50, 0x4c, - 0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x60, 0x60, 0x60, 0xff, - 0xff, 0xff, 0x60, 0x60, 0x60, 0x15, 0x68, 0x14, 0xc2, 0x00, 0x00, 0x00, + 0x54, 0x45, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0xff, 0xff, 0xff, 0x60, + 0x60, 0x60, 0xff, 0xff, 0xff, 0xb1, 0x03, 0xf0, 0x56, 0x00, 0x00, 0x00, 0x03, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x00, 0x00, 0xfa, 0x76, 0xc4, 0xde, - 0x00, 0x00, 0x04, 0x17, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x96, - 0x0d, 0x6e, 0xe3, 0x3a, 0x0c, 0x84, 0x07, 0x18, 0x9e, 0xe0, 0xdd, 0xe0, - 0xdd, 0x80, 0xc0, 0xf0, 0x00, 0x04, 0xc4, 0xfb, 0x9f, 0x69, 0x61, 0xc9, - 0x82, 0xbc, 0x75, 0xda, 0xd8, 0x1b, 0xb4, 0x01, 0x16, 0x3b, 0x40, 0x2b, - 0xd1, 0xd1, 0x17, 0xfe, 0x88, 0xb2, 0x82, 0x3f, 0x91, 0x84, 0x87, 0xa2, - 0xa3, 0xab, 0x45, 0x5f, 0x75, 0x58, 0x2f, 0xf5, 0x21, 0x29, 0xe5, 0x61, - 0xbd, 0xe4, 0x87, 0x2f, 0x54, 0x05, 0x60, 0xfd, 0x39, 0x20, 0x07, 0x3b, - 0x39, 0x96, 0x34, 0x49, 0x60, 0x37, 0x17, 0x50, 0x14, 0xc0, 0xdd, 0x95, - 0xd4, 0xa7, 0x6b, 0x85, 0x44, 0x75, 0xf9, 0x0a, 0x89, 0x0a, 0xd3, 0x00, - 0x28, 0xe1, 0xb0, 0xc0, 0x1c, 0x38, 0xda, 0xf4, 0x1d, 0x90, 0x34, 0x2c, - 0x39, 0x9d, 0x3d, 0x7a, 0x5b, 0x0b, 0x56, 0x48, 0x27, 0x80, 0x02, 0x9d, - 0xe8, 0x72, 0xfa, 0x58, 0x60, 0xd5, 0xa4, 0xa8, 0xfc, 0x1d, 0x88, 0x6e, - 0xc9, 0x8f, 0x40, 0xa0, 0x65, 0x07, 0xaa, 0x45, 0x6d, 0x80, 0x8d, 0xda, - 0xa9, 0x65, 0xf7, 0x48, 0xa7, 0x70, 0x00, 0x4c, 0x10, 0x30, 0x88, 0xbe, - 0x1e, 0xcd, 0x19, 0x00, 0x34, 0xb3, 0xef, 0x98, 0x30, 0x01, 0x4c, 0x20, - 0xb7, 0xf5, 0x99, 0x26, 0x01, 0x52, 0x42, 0xc7, 0x7d, 0x3e, 0x02, 0x4d, - 0xd1, 0x1d, 0x98, 0x59, 0x99, 0x81, 0x3d, 0x0c, 0xf9, 0x04, 0xd6, 0xc6, - 0xcd, 0x2a, 0x51, 0x8e, 0xae, 0xdc, 0x74, 0x06, 0x66, 0x6b, 0x98, 0xba, - 0x72, 0x35, 0x8d, 0x99, 0x01, 0x30, 0xe5, 0xf8, 0xa7, 0xe7, 0xcd, 0x87, - 0xc4, 0x0f, 0x89, 0x8e, 0x87, 0x32, 0x29, 0x4f, 0xf6, 0x67, 0x21, 0xaf, - 0xe4, 0xd1, 0x34, 0x5a, 0xa0, 0x24, 0xa9, 0xe6, 0xb9, 0x38, 0x6b, 0x96, - 0x17, 0x81, 0x8d, 0xda, 0x40, 0x40, 0xf1, 0x45, 0x28, 0xaa, 0x2a, 0x09, - 0xf0, 0x4e, 0x03, 0xca, 0x4f, 0x82, 0xb1, 0x51, 0x7e, 0x53, 0x55, 0x93, - 0x72, 0x02, 0x26, 0x80, 0xc3, 0xa6, 0x83, 0x58, 0x62, 0x54, 0x35, 0xef, - 0x83, 0xa2, 0xf9, 0x0e, 0xac, 0x73, 0xe2, 0xff, 0xd1, 0xff, 0xa7, 0x2f, - 0x07, 0xd1, 0x5a, 0x44, 0x4b, 0x46, 0x35, 0x55, 0xf3, 0xf9, 0x8d, 0xf3, - 0x9c, 0xc8, 0x29, 0x39, 0x85, 0x29, 0x25, 0xe9, 0x6e, 0x6a, 0x5e, 0x8a, - 0x62, 0x0c, 0xc0, 0xe6, 0x39, 0x39, 0x03, 0xe6, 0xcc, 0xed, 0x8f, 0x41, - 0x01, 0x33, 0xa4, 0x75, 0x4e, 0x4e, 0x21, 0x81, 0x90, 0xe0, 0x30, 0x29, - 0xaa, 0xf6, 0xa4, 0xa5, 0xcd, 0x76, 0xd4, 0xa3, 0xa4, 0x2d, 0x80, 0x96, - 0x80, 0x54, 0x55, 0xa3, 0xac, 0x6c, 0xe1, 0x1d, 0xda, 0x80, 0xb3, 0x9a, - 0x14, 0x00, 0xa8, 0xaa, 0x7d, 0xe3, 0x82, 0xde, 0xd0, 0x84, 0x52, 0x14, - 0x4e, 0x3a, 0xb7, 0x46, 0x32, 0xcd, 0x51, 0xdd, 0xce, 0xd1, 0x1a, 0xcf, - 0x9a, 0x4f, 0x1a, 0x76, 0xd5, 0xb0, 0x5e, 0x6d, 0x6f, 0xfa, 0x1a, 0x2f, - 0x49, 0x5a, 0xe3, 0x0a, 0x8d, 0x2e, 0xba, 0xd6, 0xd8, 0x55, 0xb9, 0xfa, - 0xbd, 0x8f, 0x2b, 0x79, 0x62, 0x88, 0x38, 0xc8, 0x2a, 0x31, 0xf5, 0xf1, - 0x25, 0xcc, 0x3e, 0x73, 0xdf, 0x81, 0xf1, 0x51, 0x56, 0xce, 0x10, 0x16, - 0x20, 0xa5, 0xed, 0xc1, 0x6d, 0x00, 0x0e, 0x40, 0xc8, 0x00, 0x78, 0x4b, - 0x0b, 0x62, 0xb6, 0xc0, 0x7c, 0x71, 0x11, 0x40, 0x1b, 0x9e, 0xa6, 0xd8, - 0x94, 0x00, 0x3d, 0x80, 0x46, 0xa7, 0xa3, 0x05, 0xe8, 0xec, 0xee, 0x06, - 0xf0, 0x21, 0xa4, 0x50, 0x38, 0x20, 0x77, 0x3a, 0xa9, 0x29, 0x0a, 0x1d, - 0x00, 0x40, 0xb5, 0x63, 0x48, 0x6c, 0x6a, 0x78, 0x0c, 0x38, 0xe4, 0xec, - 0xd4, 0x0a, 0x69, 0x3a, 0x78, 0x18, 0x92, 0x28, 0xe7, 0xe8, 0x2c, 0xef, - 0x00, 0xdd, 0x81, 0xee, 0xe0, 0x9c, 0xb4, 0xc9, 0x21, 0x29, 0xf7, 0x5d, - 0x71, 0xcc, 0xfa, 0x91, 0x31, 0x8c, 0x73, 0x59, 0x5d, 0xd2, 0xda, 0xce, - 0xb9, 0xa5, 0xf2, 0xf6, 0xb8, 0xc9, 0xb8, 0x01, 0x3e, 0x01, 0x5f, 0x80, - 0xd0, 0x75, 0x6e, 0x8d, 0xe5, 0xe1, 0x18, 0x92, 0x35, 0x7f, 0xd4, 0x7c, - 0x73, 0x22, 0xdf, 0x00, 0xd1, 0x01, 0xb1, 0x8f, 0x96, 0x2f, 0xb4, 0xf7, - 0xab, 0xf2, 0x35, 0x25, 0x86, 0x4c, 0xd8, 0x95, 0xc3, 0x1e, 0x03, 0xb3, - 0x05, 0x40, 0x3f, 0x05, 0xab, 0x9a, 0x40, 0x8e, 0x07, 0xbb, 0x6d, 0x21, - 0x01, 0x3a, 0x02, 0x02, 0xc0, 0x50, 0x0e, 0x37, 0xb1, 0x7f, 0x68, 0x6d, - 0x8c, 0x68, 0x12, 0xa8, 0x4d, 0x39, 0xef, 0x61, 0x87, 0x5a, 0xec, 0x61, - 0x39, 0x53, 0x3e, 0x1c, 0x28, 0x46, 0xc4, 0x0a, 0x68, 0x08, 0x98, 0x13, - 0x25, 0xba, 0x5a, 0x39, 0x4b, 0x39, 0x1c, 0xa8, 0x46, 0xb6, 0x4c, 0xf0, - 0x77, 0xc0, 0x51, 0x2a, 0x95, 0x60, 0x2a, 0xa9, 0x3a, 0x9c, 0x52, 0xe4, - 0x28, 0x89, 0x05, 0x20, 0x5f, 0xbf, 0x60, 0x28, 0x4c, 0x80, 0x92, 0xd4, - 0xbc, 0xc7, 0xc6, 0xe9, 0x20, 0xbd, 0xed, 0x95, 0xa1, 0xef, 0x03, 0x30, - 0x12, 0x06, 0xb1, 0x01, 0x23, 0x53, 0x9f, 0x0e, 0x88, 0x4d, 0x47, 0x00, - 0x40, 0x75, 0x75, 0x20, 0x84, 0x51, 0x0c, 0x9b, 0x0e, 0x30, 0x34, 0x80, - 0xb5, 0x55, 0x06, 0x03, 0xa5, 0xea, 0x00, 0x24, 0xe5, 0x00, 0x88, 0xa1, - 0x95, 0xc3, 0x11, 0x70, 0x56, 0xcc, 0xae, 0xc6, 0x00, 0x1c, 0x43, 0x9c, - 0x55, 0xf2, 0x05, 0xf4, 0xc7, 0x7b, 0x87, 0x2b, 0xca, 0x90, 0x68, 0xc2, - 0x19, 0x58, 0x39, 0x00, 0x36, 0x01, 0xab, 0xcc, 0xc4, 0x41, 0x9c, 0x3b, - 0xed, 0xf8, 0x51, 0xd1, 0xd7, 0xdc, 0xfc, 0x12, 0xb0, 0x20, 0x25, 0x56, - 0x5b, 0x53, 0x49, 0xe5, 0xa8, 0xc6, 0x27, 0x00, 0xf7, 0xfd, 0xf0, 0x67, - 0xc0, 0x7a, 0xaf, 0x0c, 0x40, 0x7e, 0x0d, 0xa0, 0xb0, 0x03, 0xf2, 0x2f, - 0x80, 0x95, 0xb8, 0x22, 0x27, 0x20, 0x5f, 0x40, 0xb7, 0x63, 0x8e, 0x2b, - 0x87, 0x71, 0x14, 0x27, 0xa0, 0xfc, 0x0a, 0x58, 0x8d, 0x10, 0x13, 0x88, - 0x4f, 0x81, 0xa3, 0x4a, 0xbe, 0x03, 0x91, 0x2b, 0xa4, 0x2a, 0x45, 0xd5, - 0xec, 0xa5, 0xf5, 0x42, 0x3e, 0x94, 0x55, 0x91, 0x9f, 0x26, 0xdd, 0x81, - 0x8f, 0x65, 0x8d, 0xc4, 0x25, 0xc0, 0x84, 0x59, 0xac, 0x6b, 0x00, 0xc7, - 0xc2, 0x4a, 0xcc, 0x6e, 0xb7, 0x31, 0xaf, 0x7a, 0x04, 0x5c, 0xd4, 0x4a, - 0xfa, 0xb6, 0xea, 0x83, 0xf2, 0xfc, 0xd5, 0x8f, 0x81, 0x99, 0xc1, 0x53, - 0xa0, 0xa9, 0x2b, 0x7e, 0x0e, 0xc0, 0x3f, 0xe0, 0xef, 0x00, 0x28, 0xe9, - 0x0e, 0x40, 0x6d, 0xba, 0x01, 0xc8, 0x29, 0xc8, 0x6f, 0x00, 0xa0, 0xc0, - 0xeb, 0x00, 0x1d, 0x14, 0xa0, 0xef, 0x00, 0xf4, 0x40, 0xdf, 0x0f, 0xbc, - 0x21, 0xe9, 0x97, 0xf7, 0x61, 0xed, 0xf4, 0xf7, 0xf4, 0xd2, 0x22, 0xfe, - 0x9d, 0x69, 0x53, 0x57, 0x5e, 0x02, 0x96, 0x8b, 0xc0, 0x25, 0x60, 0xb9, - 0x48, 0x5c, 0xb9, 0x1f, 0x96, 0x8b, 0xc0, 0x1d, 0xc0, 0xa4, 0xbc, 0x05, - 0xa0, 0x05, 0x2e, 0x03, 0x6b, 0xd1, 0x93, 0x6b, 0xf7, 0xba, 0x24, 0xdd, - 0x21, 0xa8, 0x2e, 0xbf, 0xb1, 0x1e, 0x00, 0x2e, 0x13, 0x94, 0xaf, 0xc9, - 0x15, 0xc9, 0xef, 0x65, 0x4e, 0x3f, 0xcd, 0xbf, 0x96, 0x4e, 0xc6, 0x73, - 0x07, 0xb7, 0x5c, 0xe8, 0x64, 0xde, 0x71, 0x70, 0xb6, 0x5f, 0x07, 0xf4, - 0xe4, 0xc1, 0xcb, 0x00, 0xfd, 0xfc, 0xe4, 0xcd, 0x80, 0x9e, 0x3c, 0x7a, - 0x27, 0x40, 0xff, 0x4e, 0xe0, 0xfd, 0x49, 0xbf, 0xbf, 0x35, 0xbe, 0xfd, - 0x3c, 0xbc, 0xfd, 0x4c, 0x43, 0x27, 0xf3, 0xf6, 0x8b, 0xec, 0x96, 0x0b, - 0xdd, 0x7f, 0x19, 0xdf, 0x7f, 0xdd, 0xbf, 0x7e, 0xa1, 0xbc, 0x7e, 0x65, - 0xad, 0x4b, 0xf1, 0x85, 0x6b, 0xf7, 0x17, 0x03, 0x7b, 0x85, 0x59, 0xd8, - 0xe0, 0xeb, 0xee, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, - 0x42, 0x60, 0x82 - + 0x00, 0x00, 0x04, 0x99, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x58, + 0x5b, 0x92, 0xdc, 0x36, 0x0c, 0xec, 0x0a, 0x70, 0x80, 0x1c, 0x21, 0x95, + 0x13, 0xa0, 0x0c, 0x1e, 0x00, 0x65, 0xf4, 0xfd, 0xcf, 0xe4, 0x0f, 0x10, + 0x14, 0xe7, 0xb1, 0x3b, 0xd2, 0x3a, 0xbb, 0x76, 0xb9, 0xc2, 0x8f, 0x91, + 0x38, 0x62, 0x0b, 0x00, 0xd9, 0x68, 0x50, 0x04, 0x3e, 0xd0, 0xc8, 0xe7, + 0xff, 0x6b, 0xd4, 0x75, 0x24, 0x00, 0x70, 0x1b, 0x4f, 0x02, 0x20, 0x4d, + 0x49, 0xdb, 0xc6, 0x93, 0xb1, 0xbd, 0x90, 0x9e, 0x80, 0x00, 0x50, 0x02, + 0x0c, 0x68, 0x21, 0x03, 0x00, 0x06, 0x49, 0xe8, 0xea, 0x16, 0xc0, 0x95, + 0x80, 0x4e, 0x53, 0x64, 0xdd, 0xae, 0x11, 0xa4, 0x92, 0x8d, 0x98, 0x2e, + 0x29, 0x53, 0x58, 0x00, 0xed, 0x17, 0xd6, 0x00, 0x89, 0x72, 0xa8, 0xfb, + 0x15, 0x4b, 0xbd, 0xa2, 0x7a, 0x0c, 0x0d, 0x35, 0xac, 0xb1, 0x81, 0x46, + 0x04, 0x9e, 0x01, 0x94, 0xd0, 0xd0, 0x8a, 0x34, 0x34, 0x6a, 0x80, 0xf8, + 0x20, 0xd3, 0xed, 0x16, 0x90, 0x80, 0x06, 0x18, 0x3b, 0x20, 0x31, 0x0c, + 0x80, 0xb8, 0x8f, 0x74, 0x37, 0x40, 0x6a, 0xee, 0x38, 0x8c, 0x0c, 0x40, + 0x43, 0x89, 0x0d, 0x20, 0x9c, 0x33, 0x2e, 0xee, 0xee, 0x06, 0x60, 0x84, + 0x66, 0xc5, 0x3a, 0xa3, 0x67, 0x00, 0x35, 0x4b, 0x00, 0x02, 0x0d, 0x30, + 0x77, 0x37, 0x33, 0x21, 0x01, 0xd2, 0x8e, 0x85, 0xab, 0x55, 0x3b, 0x00, + 0x83, 0x09, 0x00, 0x22, 0x22, 0x2e, 0x02, 0x65, 0x00, 0xca, 0x38, 0x00, + 0xbd, 0x70, 0x3d, 0x4b, 0x3a, 0xd7, 0x17, 0x66, 0x66, 0xf6, 0x04, 0x30, + 0xa9, 0x21, 0x35, 0xef, 0x76, 0x90, 0x46, 0x44, 0x00, 0x08, 0xad, 0x7e, + 0xf8, 0x9a, 0x7c, 0x30, 0x7c, 0x51, 0x6b, 0x9a, 0xcb, 0x9d, 0x45, 0xb9, + 0x21, 0xf7, 0xd1, 0x6f, 0x9a, 0xdf, 0x3f, 0xef, 0xbc, 0x18, 0x2c, 0x0a, + 0x38, 0x49, 0xfa, 0xc1, 0x29, 0x99, 0x73, 0x82, 0xbb, 0xe9, 0x45, 0x02, + 0xc0, 0x00, 0x06, 0x01, 0xe6, 0x62, 0xad, 0x0c, 0xba, 0x33, 0xed, 0x30, + 0x45, 0x77, 0x27, 0x81, 0x22, 0x28, 0x40, 0xab, 0x3c, 0x69, 0x40, 0x3d, + 0xb7, 0x65, 0x4a, 0xe8, 0x3e, 0x48, 0x6b, 0x80, 0x54, 0x9e, 0x58, 0x21, + 0xbe, 0x03, 0xf5, 0x1c, 0x80, 0xa6, 0xfb, 0x08, 0x68, 0xba, 0x33, 0x47, + 0x34, 0xa0, 0xf3, 0xa4, 0x69, 0xbe, 0x9e, 0x4b, 0x8e, 0x91, 0x39, 0x4c, + 0xd3, 0x07, 0x7d, 0x04, 0x02, 0x1a, 0x05, 0x50, 0x42, 0x63, 0xd2, 0xfc, + 0xdb, 0x7a, 0x4e, 0x53, 0x8d, 0x10, 0x8e, 0x70, 0xa6, 0x6b, 0x16, 0x40, + 0x56, 0x9e, 0x14, 0xcd, 0xbf, 0xfd, 0xab, 0xe9, 0x4c, 0x1f, 0x01, 0x09, + 0x35, 0x09, 0x35, 0x4d, 0x25, 0xd0, 0x2e, 0x1d, 0x79, 0x02, 0x73, 0x77, + 0xfc, 0xf5, 0x8f, 0xc6, 0xa0, 0x43, 0x03, 0x50, 0x90, 0x08, 0x08, 0x99, + 0xee, 0x33, 0x68, 0x12, 0x42, 0x06, 0x9c, 0xa6, 0x92, 0x62, 0xd5, 0x4f, + 0x73, 0x02, 0x90, 0x04, 0x86, 0x01, 0xa4, 0xbb, 0xd7, 0xb4, 0xea, 0xc8, + 0x00, 0x09, 0x71, 0x1a, 0x68, 0x36, 0xfb, 0x26, 0x4e, 0xab, 0xa5, 0x4d, + 0x00, 0x4a, 0xf7, 0xb9, 0x70, 0xa9, 0x31, 0x30, 0x08, 0x67, 0x3a, 0x28, + 0x5b, 0x3f, 0xfc, 0x19, 0x35, 0x4c, 0x4d, 0xa2, 0xa8, 0x01, 0x53, 0x7c, + 0xff, 0x5b, 0x4d, 0x42, 0x40, 0x92, 0x37, 0xfc, 0xdb, 0xc8, 0x45, 0x56, + 0xdf, 0x7d, 0xcf, 0x98, 0xa3, 0xff, 0x51, 0xbe, 0xf7, 0xf5, 0x74, 0x7d, + 0x98, 0xd7, 0xe9, 0x9a, 0x06, 0x35, 0x78, 0x5c, 0x01, 0x00, 0xa5, 0x84, + 0x2d, 0xa1, 0x9b, 0x76, 0xa3, 0x65, 0x67, 0x5d, 0xa7, 0xb8, 0xd9, 0xa3, + 0x6b, 0x33, 0x61, 0xb4, 0xde, 0xb0, 0x14, 0x11, 0x00, 0xd4, 0xdc, 0xf6, + 0xd9, 0x99, 0xa2, 0xcb, 0x52, 0xbb, 0x12, 0xc5, 0x0e, 0x2b, 0x00, 0x20, + 0x29, 0x00, 0x62, 0x98, 0xa4, 0x62, 0xb1, 0x74, 0x0a, 0x97, 0x02, 0x18, + 0x65, 0x69, 0x79, 0x30, 0x68, 0x80, 0x46, 0x02, 0x43, 0x43, 0x03, 0x23, + 0xa1, 0xa1, 0x95, 0x7e, 0xa1, 0xd3, 0xc2, 0xe6, 0x52, 0x32, 0x03, 0x60, + 0x84, 0x86, 0xce, 0x44, 0x24, 0xa9, 0xb5, 0x46, 0x55, 0x84, 0xc6, 0xee, + 0x92, 0x0e, 0x0e, 0x3c, 0x07, 0x44, 0x59, 0x60, 0x60, 0x77, 0xa9, 0x0c, + 0x3c, 0x75, 0x89, 0xca, 0xd0, 0x62, 0x56, 0xb9, 0xa4, 0x11, 0x40, 0x19, + 0x78, 0x08, 0x5a, 0x18, 0x60, 0xd5, 0xec, 0xe8, 0xd9, 0xa9, 0xbc, 0xd1, + 0x8c, 0x3b, 0xd2, 0xf5, 0xb4, 0xc6, 0x2c, 0xd3, 0x2d, 0x38, 0x75, 0xcf, + 0x18, 0xcf, 0x39, 0xa5, 0x0c, 0x32, 0x1a, 0x10, 0x07, 0x80, 0xf7, 0x5b, + 0x84, 0x45, 0x8d, 0xdd, 0xc2, 0x72, 0x49, 0x46, 0xbc, 0x45, 0xbe, 0xda, + 0x11, 0x28, 0x19, 0xd4, 0x00, 0xa8, 0x41, 0x8d, 0x1b, 0xe9, 0xbe, 0x4c, + 0xef, 0x9f, 0x6d, 0x9b, 0x1d, 0x5d, 0xd9, 0x7b, 0x5b, 0xc5, 0xa6, 0x7f, + 0x6a, 0x23, 0x6f, 0x1d, 0x9b, 0xf7, 0xf4, 0x06, 0x58, 0xfd, 0x31, 0xfb, + 0x92, 0x9c, 0x35, 0x62, 0xaf, 0x19, 0xd0, 0xac, 0x75, 0x12, 0xe6, 0x7c, + 0x28, 0x63, 0x0e, 0x1a, 0xab, 0x1e, 0x74, 0x19, 0x60, 0x80, 0xb5, 0x61, + 0x80, 0x30, 0xd4, 0x6a, 0xa0, 0x97, 0x92, 0x41, 0xc1, 0xc4, 0xe4, 0x20, + 0xd6, 0x4d, 0x6b, 0xcf, 0xf0, 0xd0, 0x52, 0x3e, 0x19, 0xa4, 0x57, 0xb4, + 0x6a, 0x5d, 0x71, 0x1a, 0x10, 0x70, 0x3a, 0x9d, 0x10, 0x3a, 0x59, 0x29, + 0x6d, 0xac, 0xd2, 0xa5, 0xa5, 0xb3, 0xb5, 0x6b, 0xab, 0x1d, 0x8c, 0x12, + 0x0d, 0x50, 0x92, 0x1c, 0x01, 0x40, 0xa8, 0x6d, 0xc0, 0x62, 0xcc, 0x99, + 0xd1, 0xd0, 0xde, 0x9b, 0x55, 0xc0, 0x50, 0x90, 0xc5, 0xef, 0xc1, 0x68, + 0x03, 0xba, 0xd6, 0xa1, 0x01, 0x00, 0xdc, 0xdd, 0xdd, 0xa1, 0x20, 0x73, + 0x2a, 0x28, 0xa5, 0x0d, 0x6c, 0x0b, 0xb0, 0x96, 0xc3, 0x00, 0x81, 0x40, + 0x49, 0xcf, 0x29, 0xb1, 0x35, 0x0f, 0x37, 0x02, 0x31, 0x63, 0xd8, 0x01, + 0xa1, 0x9e, 0xcd, 0xea, 0xe9, 0x49, 0x1c, 0x7c, 0xe7, 0xb1, 0x89, 0x9d, + 0x80, 0xd2, 0x84, 0x5a, 0xf8, 0x74, 0x81, 0x61, 0xec, 0x09, 0xb2, 0x01, + 0x66, 0x0c, 0x80, 0x34, 0x40, 0xdc, 0xec, 0x56, 0x32, 0x7b, 0xa5, 0xbf, + 0x8a, 0xee, 0x78, 0x4c, 0x2f, 0x89, 0x93, 0x80, 0x06, 0x4d, 0x39, 0xed, + 0x08, 0xac, 0xf4, 0x15, 0xcc, 0x37, 0x00, 0x53, 0x4e, 0x19, 0xaf, 0x00, + 0x87, 0xae, 0xb4, 0x54, 0x9c, 0x03, 0x28, 0x57, 0x99, 0x8e, 0x77, 0x00, + 0x9b, 0x36, 0xa6, 0x35, 0xa0, 0x8a, 0x49, 0x01, 0x38, 0xf7, 0x7e, 0x7d, + 0x5d, 0x31, 0xc8, 0xfa, 0x68, 0xaa, 0xdc, 0x7c, 0x0f, 0xb0, 0x88, 0x90, + 0x0d, 0xc8, 0xb7, 0x01, 0x5b, 0x73, 0xc6, 0x04, 0xa4, 0x1d, 0x2e, 0xb9, + 0x33, 0xdd, 0x8f, 0x7c, 0xd8, 0x04, 0xb9, 0xa7, 0x75, 0x66, 0xea, 0xf3, + 0xa0, 0x75, 0xea, 0xed, 0x3e, 0xad, 0x69, 0x38, 0x05, 0x98, 0x3a, 0x38, + 0xc7, 0x9f, 0x00, 0xd4, 0xa7, 0x06, 0x7a, 0x57, 0x20, 0x6e, 0x73, 0x87, + 0x70, 0xb3, 0xd9, 0xd9, 0x5d, 0x3a, 0x47, 0xd6, 0xdb, 0x2a, 0x74, 0xa1, + 0xf9, 0x5d, 0xb3, 0xc7, 0x57, 0x3f, 0x07, 0x74, 0x04, 0x2f, 0x01, 0x83, + 0xbd, 0xc4, 0x5f, 0x05, 0xc0, 0xff, 0x80, 0x3f, 0x03, 0x30, 0xf7, 0x7e, + 0xa7, 0x01, 0x3a, 0xab, 0xf6, 0x69, 0x00, 0x43, 0x79, 0xb3, 0xfd, 0x78, + 0x09, 0xa8, 0xcf, 0xbf, 0xd3, 0x80, 0xfa, 0x5e, 0xdc, 0xcf, 0x03, 0xfe, + 0x3b, 0x00, 0x9f, 0xb4, 0x5f, 0x0c, 0xf8, 0xfc, 0xa0, 0x3f, 0xb8, 0x0e, + 0xd7, 0x57, 0xfa, 0x32, 0x97, 0x2e, 0xb3, 0xf5, 0xcf, 0xcc, 0xe9, 0x3e, + 0x1b, 0x3b, 0x0d, 0xe8, 0xf3, 0xa4, 0xf3, 0x80, 0x79, 0xfa, 0x73, 0xba, + 0x3e, 0xf4, 0xb9, 0xc6, 0x05, 0x40, 0x7d, 0x29, 0x5e, 0x00, 0xd4, 0x99, + 0xe5, 0x15, 0x80, 0x18, 0x1e, 0xaa, 0xe9, 0xbb, 0x80, 0x77, 0xdb, 0x3d, + 0x59, 0x5f, 0xb4, 0xed, 0xe4, 0xf8, 0xec, 0xf8, 0xb2, 0x12, 0x67, 0xc7, + 0xc7, 0xdd, 0xcd, 0x2b, 0xff, 0xe3, 0x5a, 0xe4, 0x4f, 0x3e, 0x32, 0x5f, + 0x18, 0x78, 0xb3, 0xf3, 0xda, 0xc0, 0x29, 0x13, 0x7c, 0xb7, 0xfb, 0xca, + 0xc0, 0x09, 0x13, 0x97, 0x01, 0x7c, 0xf9, 0xc7, 0x4f, 0x02, 0x1e, 0x3d, + 0x78, 0xe1, 0xd3, 0xe7, 0x03, 0x78, 0xea, 0xaf, 0x5f, 0x03, 0x38, 0x5c, + 0xff, 0x2c, 0xc0, 0xef, 0x13, 0xf4, 0x6f, 0x48, 0x8d, 0xcf, 0xcf, 0x87, + 0x2f, 0xc8, 0xe9, 0xcb, 0x32, 0x73, 0x5d, 0xc8, 0x2e, 0x4b, 0xe5, 0x75, + 0x31, 0xbe, 0x2c, 0xf7, 0xd7, 0x0b, 0xca, 0xe5, 0x92, 0x75, 0xbd, 0x28, + 0xe2, 0xb1, 0xec, 0xfe, 0x00, 0x02, 0x11, 0xe6, 0x1b, 0x65, 0x7c, 0x86, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, + 0x82 }; const u32 TEXTURE_ATLAS_LENGTH = (u32)std::size(TEXTURE_ATLAS); @@ -122,8 +130,8 @@ enum AtlasType ATLAS_INVISIBLE, ATLAS_SHOW_RECT, ATLAS_HIDE_RECT, - ATLAS_PLACEHOLDER, - ATLAS_PLACEHOLDER2, + ATLAS_SHOW_UNUSED, + ATLAS_HIDE_UNUSED, ATLAS_PAN, ATLAS_MOVE, ATLAS_ROTATE, @@ -341,4 +349,4 @@ const ShaderData SHADER_DATA[SHADER_COUNT] = {SHADER_VERTEX, SHADER_TEXTURE_FRAGMENT}, {SHADER_AXIS_VERTEX, SHADER_FRAGMENT}, {SHADER_GRID_VERTEX, SHADER_GRID_FRAGMENT} -}; \ No newline at end of file +}; diff --git a/src/anm2.cpp b/src/anm2.cpp index f9875d7..1c7f686 100644 --- a/src/anm2.cpp +++ b/src/anm2.cpp @@ -1,7 +1,5 @@ #include "anm2.h" -using namespace tinyxml2; - static void _anm2_created_on_set(Anm2* self) { auto now = std::chrono::system_clock::now(); @@ -13,19 +11,137 @@ static void _anm2_created_on_set(Anm2* self) self->createdOn = timeString.str(); } +void anm2_frame_serialize(Anm2Frame* frame, Anm2Type type, XMLDocument* document = nullptr, XMLElement* addElement = nullptr, std::string* string = nullptr) +{ + XMLDocument localDocument; + XMLDocument* useDocument = document ? document : &localDocument; + + XMLElement* element = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]); + + if (type == ANM2_TRIGGERS) + { + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_EVENT_ID], frame->eventID); // EventID + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_AT_FRAME], frame->atFrame); // AtFrame + } + else + { + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame->position.x); // XPosition + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame->position.y); // YPosition + + if (type == ANM2_LAYER) + { + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame->pivot.x); // XPivot + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame->pivot.y); // YPivot + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_CROP], frame->crop.x); // XCrop + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_CROP], frame->crop.y); // YCrop + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_WIDTH], frame->size.x); // Width + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_HEIGHT], frame->size.y); // Height + } + + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame->scale.x); // XScale + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame->scale.y); // YScale + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame->delay); /* Delay */ + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame->isVisible); // Visible + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], FLOAT_TO_U8(frame->tintRGBA.r)); // RedTint + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], FLOAT_TO_U8(frame->tintRGBA.g)); // GreenTint + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], FLOAT_TO_U8(frame->tintRGBA.b)); // BlueTint + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], FLOAT_TO_U8(frame->tintRGBA.a)); // AlphaTint + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], FLOAT_TO_U8(frame->offsetRGB.r)); // RedOffset + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], FLOAT_TO_U8(frame->offsetRGB.g)); // GreenOffset + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], FLOAT_TO_U8(frame->offsetRGB.b)); // BlueOffset + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame->rotation); // Rotation + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame->isInterpolated); // Interpolated + } + + if (addElement) addElement->InsertEndChild(element); + + if (string && !document) + { + useDocument->InsertEndChild(element); + XMLPrinter printer; + useDocument->Print(&printer); + *string = std::string(printer.CStr()); + } +} + +void anm2_animation_serialize(Anm2* self, Anm2Animation* animation, XMLDocument* document = nullptr, XMLElement* addElement = nullptr, std::string* string = nullptr) +{ + XMLDocument localDocument; + XMLDocument* useDocument = document ? document : &localDocument; + + // Animation + XMLElement* element = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]); + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], animation->name.c_str()); // Name + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_FRAME_NUM], animation->frameNum); // FrameNum + element->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LOOP], animation->isLoop); // Loop + + // RootAnimation + XMLElement* rootElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ROOT_ANIMATION]); + + for (auto& frame : animation->rootAnimation.frames) + anm2_frame_serialize(&frame, ANM2_ROOT, useDocument, rootElement); + + element->InsertEndChild(rootElement); + + // LayerAnimations + XMLElement* layersElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]); + + for (auto& [i, id] : self->layerMap) + { + // LayerAnimation + Anm2Item& layerAnimation = animation->layerAnimations[id]; + XMLElement* layerElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATION]); + layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LAYER_ID], id); // LayerId + layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], layerAnimation.isVisible); // Visible + + for (auto& frame : layerAnimation.frames) + anm2_frame_serialize(&frame, ANM2_LAYER, useDocument, layerElement); + + layersElement->InsertEndChild(layerElement); + } + + element->InsertEndChild(layersElement); + + // Nulls + XMLElement* nullsElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATIONS]); + + for (auto& [id, null] : animation->nullAnimations) + { + // NullAnimation + XMLElement* nullElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATION]); + nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NULL_ID], id); // NullId + nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], null.isVisible); // Visible + + for (auto& frame : null.frames) + anm2_frame_serialize(&frame, ANM2_NULL, useDocument, nullElement); + + nullsElement->InsertEndChild(nullElement); + } + + element->InsertEndChild(nullsElement); + + // Triggers + XMLElement* triggersElement = useDocument->NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS]); + + for (auto& frame : animation->triggers.frames) + anm2_frame_serialize(&frame, ANM2_TRIGGERS, useDocument, triggersElement); + + element->InsertEndChild(triggersElement); + + if (addElement) addElement->InsertEndChild(element); + + if (string && !document) + { + useDocument->InsertEndChild(element); + XMLPrinter printer; + useDocument->Print(&printer); + *string = std::string(printer.CStr()); + } +} + bool anm2_serialize(Anm2* self, const std::string& path) { XMLDocument document; - XMLError error; - - XMLElement* animatedActorElement; - XMLElement* infoElement; - XMLElement* contentElement; - XMLElement* spritesheetsElement; - XMLElement* layersElement; - XMLElement* nullsElement; - XMLElement* eventsElement; - XMLElement* animationsElement; if (!self || path.empty()) return false; @@ -35,11 +151,11 @@ bool anm2_serialize(Anm2* self, const std::string& path) self->version++; // AnimatedActor - animatedActorElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]); + XMLElement* animatedActorElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATED_ACTOR]); document.InsertFirstChild(animatedActorElement); // Info - infoElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_INFO]); + XMLElement* infoElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_INFO]); infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_CREATED_BY], self->createdBy.c_str()); // CreatedBy infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_CREATED_ON], self->createdOn.c_str()); // CreatedOn infoElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VERSION], self->version); // Version @@ -47,17 +163,15 @@ bool anm2_serialize(Anm2* self, const std::string& path) animatedActorElement->InsertEndChild(infoElement); // Content - contentElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_CONTENT]); + XMLElement* contentElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_CONTENT]); // Spritesheets - spritesheetsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEETS]); + XMLElement* spritesheetsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEETS]); for (auto& [id, spritesheet] : self->spritesheets) { - XMLElement* spritesheetElement; - // Spritesheet - spritesheetElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEET]); + XMLElement* spritesheetElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEET]); spritesheetElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_PATH], spritesheet.path.c_str()); // Path spritesheetElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); // ID spritesheetsElement->InsertEndChild(spritesheetElement); @@ -66,233 +180,62 @@ bool anm2_serialize(Anm2* self, const std::string& path) contentElement->InsertEndChild(spritesheetsElement); // Layers - layersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYERS]); + XMLElement* layersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYERS]); for (auto& [id, layer] : self->layers) { - XMLElement* layerElement; - // Layer - layerElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER]); + XMLElement* layerElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER]); layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], layer.name.c_str()); // Path layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); // ID layerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SPRITESHEET_ID], layer.spritesheetID); // SpritesheetId - layersElement->InsertEndChild(layerElement); } contentElement->InsertEndChild(layersElement); // Nulls - nullsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULLS]); + XMLElement* nullsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULLS]); for (auto& [id, null] : self->nulls) { - XMLElement* nullElement; - // Null - nullElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL]); + XMLElement* nullElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL]); nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], null.name.c_str()); // Name nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); // ID - - if (null.isShowRect) - nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SHOW_RECT], null.isShowRect); // ShowRect - + if (null.isShowRect) nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SHOW_RECT], null.isShowRect); // ShowRect nullsElement->InsertEndChild(nullElement); } contentElement->InsertEndChild(nullsElement); // Events - eventsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENTS]); + XMLElement* eventsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENTS]); for (auto& [id, event] : self->events) { - XMLElement* eventElement; - // Event - eventElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENT]); + XMLElement* eventElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENT]); eventElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], event.name.c_str()); // Name eventElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); // ID eventsElement->InsertEndChild(eventElement); } - + contentElement->InsertEndChild(eventsElement); animatedActorElement->InsertEndChild(contentElement); - + // Animations - animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]); - - if (self->defaultAnimationID != ID_NONE) + XMLElement* animationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS]); + if (self->defaultAnimationID != ID_NONE) animationsElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DEFAULT_ANIMATION], self->animations[self->defaultAnimationID].name.c_str()); // DefaultAnimation - - for (auto& [id, animation] : self->animations) - { - XMLElement* animationElement; - XMLElement* rootAnimationElement; - XMLElement* layerAnimationsElement; - XMLElement* nullAnimationsElement; - XMLElement* triggersElement; - - // Animation - animationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]); - - animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], animation.name.c_str()); // Name - animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_FRAME_NUM], animation.frameNum); // FrameNum - animationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LOOP], animation.isLoop); // Loop - - // RootAnimation - rootAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ROOT_ANIMATION]); - - for (auto& frame : animation.rootAnimation.frames) - { - XMLElement* frameElement; - - // Frame - frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]); - - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); // XPosition - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); // YPosition - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); // XPivot - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); // YPivot - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); // XScale - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); // YScale - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); // Delay - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); // Visible - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], FLOAT_TO_U8(frame.tintRGBA.r)); // RedTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], FLOAT_TO_U8(frame.tintRGBA.g)); // GreenTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], FLOAT_TO_U8(frame.tintRGBA.b)); // BlueTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], FLOAT_TO_U8(frame.tintRGBA.a)); // AlphaTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], FLOAT_TO_U8(frame.offsetRGB.r)); // RedOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], FLOAT_TO_U8(frame.offsetRGB.g)); // GreenOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], FLOAT_TO_U8(frame.offsetRGB.b)); // BlueOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated - - rootAnimationElement->InsertEndChild(frameElement); - } - - animationElement->InsertEndChild(rootAnimationElement); - - // LayerAnimations - layerAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS]); - - for (auto& [layerIndex, layerID] : self->layerMap) - { - Anm2Item& layerAnimation = animation.layerAnimations[layerID]; - - XMLElement* layerAnimationElement; - - // LayerAnimation - layerAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATION]); - layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_LAYER_ID], layerID); // LayerId - layerAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], layerAnimation.isVisible); // Visible - for (auto& frame : layerAnimation.frames) - { - XMLElement* frameElement; - - // Frame - frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]); - - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); // XPosition - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); // YPosition - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); // XPivot - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); // YPivot - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_CROP], frame.crop.x); // XCrop - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_CROP], frame.crop.y); // YCrop - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_WIDTH], frame.size.x); // Width - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_HEIGHT], frame.size.y); // Height - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); // XScale - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); // YScale - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); /* Delay */ - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); // Visible - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], FLOAT_TO_U8(frame.tintRGBA.r)); // RedTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], FLOAT_TO_U8(frame.tintRGBA.g)); // GreenTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], FLOAT_TO_U8(frame.tintRGBA.b)); // BlueTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], FLOAT_TO_U8(frame.tintRGBA.a)); // AlphaTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], FLOAT_TO_U8(frame.offsetRGB.r)); // RedOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], FLOAT_TO_U8(frame.offsetRGB.g)); // GreenOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], FLOAT_TO_U8(frame.offsetRGB.b)); // BlueOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated - - layerAnimationElement->InsertEndChild(frameElement); - } - - layerAnimationsElement->InsertEndChild(layerAnimationElement); - } - - animationElement->InsertEndChild(layerAnimationsElement); - - // NullAnimations - nullAnimationsElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATIONS]); - - for (const auto& [nullID, nullAnimation] : animation.nullAnimations) - { - XMLElement* nullAnimationElement; - - // NullAnimation - nullAnimationElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATION]); - nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NULL_ID], nullID); // NullId - nullAnimationElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], nullAnimation.isVisible); // Visible - - for (const auto& frame : nullAnimation.frames) - { - XMLElement* frameElement; - - // Frame - frameElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]); - - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_POSITION], frame.position.x); // XPosition - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_POSITION], frame.position.y); // YPosition - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_PIVOT], frame.pivot.x); // XPivot - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_PIVOT], frame.pivot.y); // YPivot - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_X_SCALE], frame.scale.x); // XScale - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_Y_SCALE], frame.scale.y); // XScale - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_DELAY], frame.delay); // Delay - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_VISIBLE], frame.isVisible); // Visible - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_TINT], FLOAT_TO_U8(frame.tintRGBA.r)); // RedTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_TINT], FLOAT_TO_U8(frame.tintRGBA.g)); // GreenTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_TINT], FLOAT_TO_U8(frame.tintRGBA.b)); // BlueTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ALPHA_TINT], FLOAT_TO_U8(frame.tintRGBA.a)); // AlphaTint - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_RED_OFFSET], FLOAT_TO_U8(frame.offsetRGB.r)); // RedOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_GREEN_OFFSET], FLOAT_TO_U8(frame.offsetRGB.g)); // GreenOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_BLUE_OFFSET], FLOAT_TO_U8(frame.offsetRGB.b)); // BlueOffset - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ROTATION], frame.rotation); // Rotation - frameElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_INTERPOLATED], frame.isInterpolated); // Interpolated - - nullAnimationElement->InsertEndChild(frameElement); - } - - nullAnimationsElement->InsertEndChild(nullAnimationElement); - } - - animationElement->InsertEndChild(nullAnimationsElement); - - // Triggers - triggersElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS]); - - for (const auto& frame : animation.triggers.frames) - { - XMLElement* triggerElement; - - // Trigger - triggerElement = document.NewElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]); - triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_EVENT_ID], frame.eventID); // EventID - triggerElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_AT_FRAME], frame.atFrame); // AtFrame - triggersElement->InsertEndChild(triggerElement); - } - - animationElement->InsertEndChild(triggersElement); - - animationsElement->InsertEndChild(animationElement); - } + for (auto& [id, animation] : self->animations) + anm2_animation_serialize(self, &animation, &document, animationsElement); animatedActorElement->InsertEndChild(animationsElement); - error = document.SaveFile(path.c_str()); + XMLError error = document.SaveFile(path.c_str()); if (error != XML_SUCCESS) { @@ -305,30 +248,150 @@ bool anm2_serialize(Anm2* self, const std::string& path) return true; } +static void _anm2_frame_deserialize(Anm2* self, Anm2Frame* frame, const XMLElement* element) +{ + for (const XMLAttribute* attribute = element->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_X_POSITION: frame->position.x = std::atof(attribute->Value()); break; // XPosition + case ANM2_ATTRIBUTE_Y_POSITION: frame->position.y = std::atof(attribute->Value()); break; // YPosition + case ANM2_ATTRIBUTE_X_PIVOT: frame->pivot.x = std::atof(attribute->Value()); break; // XPivot + case ANM2_ATTRIBUTE_Y_PIVOT: frame->pivot.y = std::atof(attribute->Value()); break; // YPivot + case ANM2_ATTRIBUTE_X_CROP: frame->crop.x = std::atof(attribute->Value()); break; // XCrop + case ANM2_ATTRIBUTE_Y_CROP: frame->crop.y = std::atof(attribute->Value()); break; // YCrop + case ANM2_ATTRIBUTE_WIDTH: frame->size.x = std::atof(attribute->Value()); break; // Width + case ANM2_ATTRIBUTE_HEIGHT: frame->size.y = std::atof(attribute->Value()); break; // Height + case ANM2_ATTRIBUTE_X_SCALE: frame->scale.x = std::atof(attribute->Value()); break; // XScale + case ANM2_ATTRIBUTE_Y_SCALE: frame->scale.y = std::atof(attribute->Value()); break; // YScale + case ANM2_ATTRIBUTE_RED_TINT: frame->tintRGBA.r = U8_TO_FLOAT(std::atoi(attribute->Value())); break; // RedTint + case ANM2_ATTRIBUTE_GREEN_TINT: frame->tintRGBA.g = U8_TO_FLOAT(std::atoi(attribute->Value())); break; // GreenTint + case ANM2_ATTRIBUTE_BLUE_TINT: frame->tintRGBA.b = U8_TO_FLOAT(std::atoi(attribute->Value())); break; // BlueTint + case ANM2_ATTRIBUTE_ALPHA_TINT: frame->tintRGBA.a = U8_TO_FLOAT(std::atoi(attribute->Value())); break; // AlphaTint + case ANM2_ATTRIBUTE_RED_OFFSET: frame->offsetRGB.r = U8_TO_FLOAT(std::atoi(attribute->Value())); break; // RedOffset + case ANM2_ATTRIBUTE_GREEN_OFFSET: frame->offsetRGB.g = U8_TO_FLOAT(std::atoi(attribute->Value())); break; // GreenOffset + case ANM2_ATTRIBUTE_BLUE_OFFSET: frame->offsetRGB.b = U8_TO_FLOAT(std::atoi(attribute->Value())); break; // BlueOffset + case ANM2_ATTRIBUTE_ROTATION: frame->rotation = std::atof(attribute->Value()); break; // Rotation + case ANM2_ATTRIBUTE_VISIBLE: frame->isVisible = string_to_bool(attribute->Value()); break; // Visible + case ANM2_ATTRIBUTE_INTERPOLATED: frame->isInterpolated = string_to_bool(attribute->Value()); break; // Interpolated + case ANM2_ATTRIBUTE_AT_FRAME: frame->atFrame = std::atoi(attribute->Value()); break; // AtFrame + case ANM2_ATTRIBUTE_DELAY: frame->delay = std::atoi(attribute->Value()); break; // Delay + case ANM2_ATTRIBUTE_EVENT_ID: // EventID + { + s32 eventID = std::atoi(attribute->Value()); + frame->eventID = map_find(self->events, eventID) ? eventID : ID_NONE; + break; + } + default: break; + } + } +} + +static void _anm2_animation_deserialize(Anm2* self, Anm2Animation* animation, const XMLElement* element) +{ + auto frames_deserialize = [&](const XMLElement* itemElement, Anm2Item* item) + { + // Frame + for + ( + const XMLElement* frame = itemElement->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]); + frame; frame = frame->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]) + ) + _anm2_frame_deserialize(self, &item->frames.emplace_back(Anm2Frame()), frame); + }; + + s32 id{}; + + for (const XMLAttribute* attribute = element->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_NAME: animation->name = std::string(attribute->Value()); break; // Name + case ANM2_ATTRIBUTE_FRAME_NUM: animation->frameNum = std::atoi(attribute->Value()); break; // FrameNum + case ANM2_ATTRIBUTE_LOOP: animation->isLoop = string_to_bool(attribute->Value()); break; // Loop + default: break; + } + } + + // RootAnimation + if (const XMLElement* rootAnimation = element->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ROOT_ANIMATION])) + frames_deserialize(rootAnimation, &animation->rootAnimation); + + // LayerAnimations + if (const XMLElement* layerAnimations = element->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATIONS])) + { + s32 layerMapIndex = 0; + + // LayerAnimation + for + ( + const XMLElement* layerAnimation = layerAnimations->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATION]); + layerAnimation; layerAnimation = layerAnimation->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER_ANIMATION]) + ) + { + Anm2Item layerAnimationItem; + + + for (const XMLAttribute* attribute = layerAnimation->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_LAYER_ID: id = std::atoi(attribute->Value()); break; // LayerID + case ANM2_ATTRIBUTE_VISIBLE: layerAnimationItem.isVisible = string_to_bool(attribute->Value()); break; // Visible + default: break; + } + } + + frames_deserialize(layerAnimation, &layerAnimationItem); + animation->layerAnimations[id] = layerAnimationItem; + + self->layerMap[id] = layerMapIndex; + layerMapIndex++; + } + } + + // NullAnimations + if (const XMLElement* nullAnimations = element->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATIONS])) + { + // NullAnimation + for + ( + const XMLElement* nullAnimation = nullAnimations->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATION]); + nullAnimation; nullAnimation = nullAnimation->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL_ANIMATION]) + ) + { + Anm2Item nullAnimationItem; + + for (const XMLAttribute* attribute = nullAnimation->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_NULL_ID: id = std::atoi(attribute->Value()); break; + case ANM2_ATTRIBUTE_VISIBLE: nullAnimationItem.isVisible = string_to_bool(attribute->Value()); break; + default: break; + } + } + + frames_deserialize(nullAnimation, &nullAnimationItem); + animation->nullAnimations[id] = nullAnimationItem; + } + } + + // Triggers + if (const XMLElement* triggers = element->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGERS])) + { + // Trigger + for + ( + const XMLElement* trigger = triggers->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]); + trigger; trigger = trigger->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]) + ) + _anm2_frame_deserialize(self, &animation->triggers.frames.emplace_back(Anm2Frame()), trigger); + } +} + bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures) { - XMLDocument xmlDocument; - XMLError xmlError; - const XMLElement* xmlElement; - const XMLElement* xmlRoot; - Anm2Animation* animation = nullptr; - Anm2Layer* layer = nullptr; - Anm2Null* null = nullptr; - Anm2Item* item = nullptr; - Anm2Event* event = nullptr; - Anm2Frame* frame = nullptr; - Anm2Spritesheet* spritesheet = nullptr; - Anm2Element anm2Element = ANM2_ELEMENT_ANIMATED_ACTOR; - Anm2Attribute anm2Attribute = ANM2_ATTRIBUTE_ID; - Anm2Item addItem; - Anm2Layer addLayer; - Anm2Null addNull; - Anm2Event addEvent; - Anm2Spritesheet addSpritesheet; - s32 layerMapIndex = 0; - bool isLayerMapSet = false; - std::string defaultAnimation{}; - if (!self) return false; if (path.empty()) @@ -337,306 +400,181 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures) return false; } - anm2_new(self); - - xmlError = xmlDocument.LoadFile(path.c_str()); - - if (xmlError != XML_SUCCESS) + XMLDocument document; + if (document.LoadFile(path.c_str()) != XML_SUCCESS) { - log_error(std::format(ANM2_PARSE_ERROR, path, xmlDocument.ErrorStr())); + log_error(std::format(ANM2_PARSE_ERROR, path, document.ErrorStr())); return false; } + anm2_new(self); + self->path = path; + std::string defaultAnimation{}; + s32 id{}; + // Save old working directory and then use anm2's path as directory // (used for loading textures from anm2 correctly which are relative) std::filesystem::path workingPath = std::filesystem::current_path(); working_directory_from_file_set(path); - self->path = path; + const XMLElement* root = document.RootElement(); - xmlRoot = xmlDocument.FirstChildElement(ANM2_ELEMENT_ENUM_TO_STRING(ANM2_ELEMENT_ANIMATED_ACTOR).c_str()); - xmlElement = xmlRoot; - - // Iterate through elements - while (xmlElement) + // Info + if (const XMLElement* info = root->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_INFO])) { - const XMLAttribute* xmlAttribute = nullptr; - const XMLElement* xmlChild = nullptr; - s32 id = 0; - - anm2Element = ANM2_ELEMENT_STRING_TO_ENUM(xmlElement->Name()); - - switch (anm2Element) + for (const XMLAttribute* attribute = info->FirstAttribute(); attribute; attribute = attribute->Next()) { - case ANM2_ELEMENT_SPRITESHEET: // Spritesheet - spritesheet = &addSpritesheet; - break; - case ANM2_ELEMENT_LAYER: // Layer - layer = &addLayer; - break; - case ANM2_ELEMENT_NULL: // Null - null = &addNull; - break; - case ANM2_ELEMENT_EVENT: // Event - event = &addEvent; - break; - case ANM2_ELEMENT_ANIMATION: // Animation - id = map_next_id_get(self->animations); - self->animations[id] = Anm2Animation{}; - animation = &self->animations[id]; - break; - case ANM2_ELEMENT_ROOT_ANIMATION: // RootAnimation - item = &animation->rootAnimation; - break; - case ANM2_ELEMENT_LAYER_ANIMATION: // LayerAnimation - case ANM2_ELEMENT_NULL_ANIMATION: // NullAnimation - item = &addItem; - break; - case ANM2_ELEMENT_TRIGGERS: // Triggers - item = &animation->triggers; - break; - case ANM2_ELEMENT_FRAME: // Frame - case ANM2_ELEMENT_TRIGGER: // Trigger - item->frames.push_back(Anm2Frame{}); - frame = &item->frames.back(); - default: - break; - } - - /* Attributes */ - xmlAttribute = xmlElement->FirstAttribute(); - - while (xmlAttribute) - { - anm2Attribute = ANM2_ATTRIBUTE_STRING_TO_ENUM(xmlAttribute->Name()); - - switch (anm2Attribute) + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) { - case ANM2_ATTRIBUTE_CREATED_BY: // CreatedBy - self->createdBy = xmlAttribute->Value(); - break; - case ANM2_ATTRIBUTE_CREATED_ON: // CreatedOn - self->createdOn = xmlAttribute->Value(); - break; - case ANM2_ATTRIBUTE_VERSION: // Version - self->version = std::atoi(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_FPS: // FPS - self->fps = std::atoi(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_ID: // ID - id = std::atoi(xmlAttribute->Value()); - switch (anm2Element) - { - case ANM2_ELEMENT_SPRITESHEET: // Spritesheet - self->spritesheets[id] = addSpritesheet; - spritesheet = &self->spritesheets[id]; - break; - case ANM2_ELEMENT_LAYER: // Layer - self->layers[id] = addLayer; - layer = &self->layers[id]; - break; - case ANM2_ELEMENT_NULL: // Null - self->nulls[id] = addNull; - null = &self->nulls[id]; - break; - case ANM2_ELEMENT_EVENT: // Event - self->events[id] = addEvent; - event = &self->events[id]; - break; - default: - break; - } - break; - case ANM2_ATTRIBUTE_LAYER_ID: // LayerId - id = std::atoi(xmlAttribute->Value()); + case ANM2_ATTRIBUTE_CREATED_BY: self->createdBy = std::string(attribute->Value()); break; // CreatedBy + case ANM2_ATTRIBUTE_CREATED_ON: self->createdOn = std::string(attribute->Value()); break; // CreatedOn + case ANM2_ATTRIBUTE_VERSION: self->version = std::atoi(attribute->Value()); break; // Version + case ANM2_ATTRIBUTE_FPS: self->fps = std::atoi(attribute->Value()); break; // FPS + default: break; + } + } + } - self->layerMap[layerMapIndex] = id; - layerMapIndex++; - - animation->layerAnimations[id] = addItem; - item = &animation->layerAnimations[id]; - break; - case ANM2_ATTRIBUTE_NULL_ID: // NullId - id = std::atoi(xmlAttribute->Value()); - animation->nullAnimations[id] = addItem; - item = &animation->nullAnimations[id]; - break; - case ANM2_ATTRIBUTE_PATH: // Path - spritesheet->path = xmlAttribute->Value(); - break; - case ANM2_ATTRIBUTE_NAME: // Name - switch (anm2Element) - { - case ANM2_ELEMENT_LAYER: - layer->name = std::string(xmlAttribute->Value()); - break; - case ANM2_ELEMENT_NULL: - null->name = std::string(xmlAttribute->Value()); - break; - case ANM2_ELEMENT_ANIMATION: - animation->name = std::string(xmlAttribute->Value()); - break; - case ANM2_ELEMENT_EVENT: - event->name = std::string(xmlAttribute->Value()); - break; - default: - break; - } - break; - case ANM2_ATTRIBUTE_SPRITESHEET_ID: - layer->spritesheetID = std::atoi(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_SHOW_RECT: - null->isShowRect = string_to_bool(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_DEFAULT_ANIMATION: - defaultAnimation = xmlAttribute->Value(); - break; - case ANM2_ATTRIBUTE_FRAME_NUM: - animation->frameNum = std::atoi(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_LOOP: - animation->isLoop = string_to_bool(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_X_POSITION: - frame->position.x = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_Y_POSITION: - frame->position.y = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_X_PIVOT: - frame->pivot.x = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_Y_PIVOT: - frame->pivot.y = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_X_CROP: - frame->crop.x = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_Y_CROP: - frame->crop.y = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_WIDTH: - frame->size.x = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_HEIGHT: - frame->size.y = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_X_SCALE: - frame->scale.x = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_Y_SCALE: - frame->scale.y = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_DELAY: - frame->delay = std::atoi(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_VISIBLE: - switch (anm2Element) - { - case ANM2_ELEMENT_FRAME: - frame->isVisible = string_to_bool(xmlAttribute->Value()); - break; - case ANM2_ELEMENT_ROOT_ANIMATION: - case ANM2_ELEMENT_LAYER_ANIMATION: - case ANM2_ELEMENT_NULL_ANIMATION: - item->isVisible = string_to_bool(xmlAttribute->Value()); - break; - default: - break; - } - break; - case ANM2_ATTRIBUTE_RED_TINT: - frame->tintRGBA.r = U8_TO_FLOAT(std::atoi(xmlAttribute->Value())); - break; - case ANM2_ATTRIBUTE_GREEN_TINT: - frame->tintRGBA.g = U8_TO_FLOAT(std::atoi(xmlAttribute->Value())); - break; - case ANM2_ATTRIBUTE_BLUE_TINT: - frame->tintRGBA.b = U8_TO_FLOAT(std::atoi(xmlAttribute->Value())); - break; - case ANM2_ATTRIBUTE_ALPHA_TINT: - frame->tintRGBA.a = U8_TO_FLOAT(std::atoi(xmlAttribute->Value())); - break; - case ANM2_ATTRIBUTE_RED_OFFSET: - frame->offsetRGB.r = U8_TO_FLOAT(std::atoi(xmlAttribute->Value())); - break; - case ANM2_ATTRIBUTE_GREEN_OFFSET: - frame->offsetRGB.g = U8_TO_FLOAT(std::atoi(xmlAttribute->Value())); - break; - case ANM2_ATTRIBUTE_BLUE_OFFSET: - frame->offsetRGB.b = U8_TO_FLOAT(std::atoi(xmlAttribute->Value())); - break; - case ANM2_ATTRIBUTE_ROTATION: - frame->rotation = std::atof(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_INTERPOLATED: - frame->isInterpolated = string_to_bool(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_EVENT_ID: - frame->eventID = std::atoi(xmlAttribute->Value()); - break; - case ANM2_ATTRIBUTE_AT_FRAME: - frame->atFrame = std::atoi(xmlAttribute->Value()); - break; - default: - break; - } - - xmlAttribute = xmlAttribute->Next(); - } - - if (anm2Element == ANM2_ELEMENT_SPRITESHEET && isTextures) + // Content + if (const XMLElement* content = root->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_CONTENT])) + { + // Spritesheets + if (const XMLElement* spritesheets = content->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEETS])) { - // Spritesheet paths from Isaac Rebirth are made with the assumption that the paths are case-insensitive (developed on Windows) - // However when using the resource dumper, the spritesheet paths are all lowercase (on Linux anyways) - // If the check doesn't work, set the spritesheet path to lowercase - // If it doesn't work beyond that then that's on the user :^) - - if (!path_exists(spritesheet->path)) - spritesheet->path = string_to_lowercase(spritesheet->path); - texture_from_path_init(&spritesheet->texture, spritesheet->path); - } - - if (anm2Element == ANM2_ELEMENT_ANIMATION) - layerMapIndex = 0; - - xmlChild = xmlElement->FirstChildElement(); - - if (xmlChild) - { - xmlElement = xmlChild; - continue; - } - - while (xmlElement) - { - const XMLElement* xmlNext; - - xmlNext = xmlElement->NextSiblingElement(); - - if (xmlNext) + for + ( + const XMLElement* spritesheet = spritesheets->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEET]); + spritesheet; spritesheet = spritesheet->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_SPRITESHEET]) + ) { - xmlElement = xmlNext; - break; - } + Anm2Spritesheet addSpritesheet; + + for (const XMLAttribute* attribute = spritesheet->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_PATH: + // Spritesheet paths from Isaac Rebirth are made with the assumption that + // the paths are case-insensitive (as the game was developed on Windows) + // However when using the resource dumper, the spritesheet paths are all lowercase (on Linux anyways) + // If the check doesn't work, set the spritesheet path to lowercase + // If it doesn't work beyond that then that's on the user :^) + addSpritesheet.path = attribute->Value(); + if (!path_exists(addSpritesheet.path)) addSpritesheet.path = string_to_lowercase(addSpritesheet.path); + if (isTextures) texture_from_path_init(&addSpritesheet.texture, addSpritesheet.path); + break; + case ANM2_ATTRIBUTE_ID: id = std::atoi(attribute->Value()); break; + default: break; + } + } - xmlElement = xmlElement->Parent() ? xmlElement->Parent()->ToElement() : nullptr; + self->spritesheets[id] = addSpritesheet; + } } + + // Layers + if (const XMLElement* layers = content->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYERS])) + { + for + ( + const XMLElement* layer = layers->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER]); + layer; layer = layer->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_LAYER]) + ) + { + Anm2Layer addLayer; + + for (const XMLAttribute* attribute = layer->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_NAME: addLayer.name = std::string(attribute->Value()); break; // Name + case ANM2_ATTRIBUTE_ID: id = std::atoi(attribute->Value()); break; // ID + case ANM2_ATTRIBUTE_SPRITESHEET_ID: addLayer.spritesheetID = std::atoi(attribute->Value()); break; // ID + default: break; + } + } + + self->layers[id] = addLayer; + } + } + + // Nulls + if (const XMLElement* nulls = content->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULLS])) + { + for + ( + const XMLElement* null = nulls->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL]); + null; null = null->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_NULL]) + ) + { + Anm2Null addNull; + + for (const XMLAttribute* attribute = null->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_NAME: addNull.name = std::string(attribute->Value()); break; // Name + case ANM2_ATTRIBUTE_ID: id = std::atoi(attribute->Value()); break; // IDs + default: break; + } + } + + self->nulls[id] = addNull; + } + } + + // Events + if (const XMLElement* events = content->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENTS])) + { + // Event + for + ( + const XMLElement* event = events->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENT]); + event; event = event->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_EVENT]) + ) + { + Anm2Event addEvent; + + for (const XMLAttribute* attribute = event->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_NAME: addEvent.name = std::string(attribute->Value()); break; // Name + case ANM2_ATTRIBUTE_ID: id = std::atoi(attribute->Value()); break; // ID + default: break; + } + } + + self->events[id] = addEvent; + } + } + } + + // Animations + if (const XMLElement* animations = root->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATIONS])) + { + for (const XMLAttribute* attribute = animations->FirstAttribute(); attribute; attribute = attribute->Next()) + { + switch (ANM2_ATTRIBUTE_STRING_TO_ENUM(attribute->Name())) + { + case ANM2_ATTRIBUTE_DEFAULT_ANIMATION: defaultAnimation = std::string(attribute->Value()); break; // DefaultAnimation + default: break; + } + } + + // Animation + for + ( + const XMLElement* animation = animations->FirstChildElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]); + animation; animation = animation->NextSiblingElement(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION]) + ) + _anm2_animation_deserialize(self, &self->animations[map_next_id_get(self->animations)], animation); } for (auto& [id, animation] : self->animations) - { if (animation.name == defaultAnimation) self->defaultAnimationID = id; - for (auto& [i, id] : self->layerMap) - { - - } - } - if (isTextures) anm2_spritesheet_texture_pixels_download(self); std::filesystem::current_path(workingPath); @@ -723,21 +661,31 @@ void anm2_null_remove(Anm2* self, s32 id) } } -s32 anm2_animation_add(Anm2* self) +s32 anm2_animation_add(Anm2* self, bool isAddRootFrame, Anm2Animation* animation, s32 id) { - s32 id = map_next_id_get(self->animations); - Anm2Animation animation; + s32 addID = map_next_id_get(self->animations); - for (auto& [layerID, layer] : self->layers) - animation.layerAnimations[layerID] = Anm2Item{}; - for (auto& [nullID, null] : self->nulls) - animation.nullAnimations[nullID] = Anm2Item{}; + Anm2Animation localAnimation; + Anm2Animation* addAnimation = animation ? animation : &localAnimation; - animation.rootAnimation.frames.push_back(Anm2Frame{}); + for (auto& [layerID, layer] : self->layers) + if (!map_find(addAnimation->layerAnimations, layerID)) + addAnimation->layerAnimations[layerID] = Anm2Item{}; + for (auto& [nullID, null] : self->nulls) + if (!map_find(addAnimation->nullAnimations, nullID)) + addAnimation->nullAnimations[nullID] = Anm2Item{}; + + if (isAddRootFrame) + addAnimation->rootAnimation.frames.push_back(Anm2Frame{}); - self->animations[id] = animation; - - return id; + if (id != ID_NONE) + { + map_insert_shift(self->animations, id, *addAnimation); + return id + 1; + } + else + self->animations[addID] = *addAnimation; + return addID; } void anm2_animation_remove(Anm2* self, s32 id) @@ -773,17 +721,6 @@ Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference) default: return nullptr; } } -Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference) -{ - Anm2Item* item = anm2_item_from_reference(self, reference); - - if (!item) return nullptr; - - if (reference->frameIndex <= INDEX_NONE || reference->frameIndex >= (s32)item->frames.size()) - return nullptr; - - return &item->frames[reference->frameIndex]; -} s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time) { @@ -796,6 +733,10 @@ s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time) if (!item) return INDEX_NONE; + if (reference.itemType == ANM2_TRIGGERS) + for (auto [i, frame] : std::views::enumerate(item->frames)) + if (frame.atFrame == (s32)time) return i; + s32 delayCurrent = 0; s32 delayNext = 0; @@ -812,13 +753,24 @@ s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time) return INDEX_NONE; } +Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference) +{ + Anm2Item* item = anm2_item_from_reference(self, reference); + + if (!item) return nullptr; + + if (reference->frameIndex <= INDEX_NONE || reference->frameIndex >= (s32)item->frames.size()) + return nullptr; + + return &item->frames[reference->frameIndex]; +} + void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time) { Anm2Animation* animation = anm2_animation_from_reference(self, &reference); - if (!animation) return; - time = std::clamp(time, 0.0f, animation->frameNum - 1.0f); + time = std::clamp(ROUND_NEAREST_MULTIPLE(time, 1.0f), 0.0f, animation->frameNum - 1.0f); Anm2Item* item = anm2_item_from_reference(self, &reference); @@ -905,36 +857,36 @@ void anm2_animation_length_set(Anm2Animation* self) self->frameNum = anm2_animation_length_get(self); } -Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference, s32 time) +Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference) { Anm2Animation* animation = anm2_animation_from_reference(self, reference); Anm2Item* item = anm2_item_from_reference(self, reference); - - if (!animation || !item) - return nullptr; + if (!animation || !item) return nullptr; Anm2Frame frameAdd = frame ? *frame : Anm2Frame{}; - s32 index = reference->frameIndex + 1; if (reference->itemType == ANM2_TRIGGERS) { - s32 triggerIndex = time; + s32 atFrame = reference->time != VALUE_NONE ? (s32)reference->time : 0; for (auto& frameCheck : item->frames) { - if (frameCheck.atFrame == time) + if (frameCheck.atFrame == atFrame) { - triggerIndex++; + atFrame++; break; } } - frameAdd.atFrame = triggerIndex; + frameAdd.delay = 1; + frameAdd.atFrame = atFrame; return &item->frames.emplace_back(frameAdd); } else { - if (index >= static_cast(item->frames.size())) + s32 index = reference->frameIndex + 1; + + if (index >= (s32)item->frames.size()) { item->frames.push_back(frameAdd); return &item->frames.back(); @@ -942,12 +894,12 @@ Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference else { item->frames.insert(item->frames.begin() + index, frameAdd); - return &item->frames[static_cast(index)]; + return &item->frames[(size_t)(index)]; } } } -void anm2_frame_erase(Anm2* self, Anm2Reference* reference) +void anm2_frame_remove(Anm2* self, Anm2Reference* reference) { Anm2Item* item = anm2_item_from_reference(self, reference); if (!item) return; @@ -1258,4 +1210,51 @@ vec4 anm2_animation_rect_get(Anm2* self, Anm2Reference* reference, bool isRootTr if (!any) return vec4(-1.0f); return {minX, minY, maxX - minX, maxY - minY}; +} + +bool anm2_animation_deserialize_from_xml(Anm2* self, Anm2Animation* animation, const std::string& xml) +{ + XMLDocument document; + + auto animation_deserialize_error = [&]() + { + log_error(std::format(ANM2_ANIMATION_PARSE_ERROR, xml, document.ErrorStr())); + return false; + }; + + if (document.Parse(xml.c_str()) != XML_SUCCESS) return animation_deserialize_error(); + + const XMLElement* element = document.RootElement(); + if (element && std::string(element->Name()) != std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_ANIMATION])) + return animation_deserialize_error(); + + _anm2_animation_deserialize(self, animation, element); + return true; +} + +bool anm2_frame_deserialize_from_xml(Anm2* self, Anm2Frame* frame, const std::string& xml) +{ + XMLDocument document; + + auto frame_deserialize_error = [&]() + { + log_error(std::format(ANM2_FRAME_PARSE_ERROR, xml, document.ErrorStr())); + return false; + }; + + if (document.Parse(xml.c_str()) != XML_SUCCESS) return frame_deserialize_error(); + + const XMLElement* element = document.RootElement(); + if + ( + element && + ( + std::string(element->Name()) == std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_FRAME]) || + std::string(element->Name()) == std::string(ANM2_ELEMENT_STRINGS[ANM2_ELEMENT_TRIGGER]) + ) + ) + return frame_deserialize_error(); + + _anm2_frame_deserialize(self, frame, element); + return true; } \ No newline at end of file diff --git a/src/anm2.h b/src/anm2.h index f021f00..73ac1f0 100644 --- a/src/anm2.h +++ b/src/anm2.h @@ -16,6 +16,8 @@ #define ANM2_EMPTY_ERROR "No path given for anm2" #define ANM2_READ_ERROR "Failed to read anm2 from file: {}" #define ANM2_PARSE_ERROR "Failed to parse anm2: {} ({})" +#define ANM2_FRAME_PARSE_ERROR "Failed to parse frame: {} ({})" +#define ANM2_ANIMATION_PARSE_ERROR "Failed to parse frame: {} ({})" #define ANM2_READ_INFO "Read anm2 from file: {}" #define ANM2_WRITE_ERROR "Failed to write anm2 to file: {}" #define ANM2_WRITE_INFO "Wrote anm2 to file: {}" @@ -63,7 +65,6 @@ const inline char* ANM2_ELEMENT_STRINGS[] = }; DEFINE_STRING_TO_ENUM_FUNCTION(ANM2_ELEMENT_STRING_TO_ENUM, Anm2Element, ANM2_ELEMENT_STRINGS, ANM2_ELEMENT_COUNT) -DEFINE_ENUM_TO_STRING_FUNCTION(ANM2_ELEMENT_ENUM_TO_STRING, ANM2_ELEMENT_STRINGS, ANM2_ELEMENT_COUNT) #define ANM2_ATTRIBUTE_LIST \ X(CREATED_BY, "CreatedBy") \ @@ -120,7 +121,6 @@ static const char* ANM2_ATTRIBUTE_STRINGS[] = }; DEFINE_STRING_TO_ENUM_FUNCTION(ANM2_ATTRIBUTE_STRING_TO_ENUM, Anm2Attribute, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT) -DEFINE_ENUM_TO_STRING_FUNCTION(ANM2_ATTRIBUTE_ENUM_TO_STRING, ANM2_ATTRIBUTE_STRINGS, ANM2_ATTRIBUTE_COUNT) enum Anm2Type { @@ -199,6 +199,7 @@ struct Anm2Animation s32 frameNum = ANM2_FRAME_NUM_MIN; std::string name = "New Animation"; bool isLoop = true; + bool isShowUnused = true; Anm2Item rootAnimation; std::map layerAnimations; std::map nullAnimations; @@ -227,27 +228,10 @@ struct Anm2Reference Anm2Type itemType = ANM2_NONE; s32 itemID = ID_NONE; s32 frameIndex = INDEX_NONE; + f32 time = VALUE_NONE; auto operator<=>(const Anm2Reference&) const = default; }; -struct Anm2AnimationWithID -{ - s32 id; - Anm2Animation animation; -}; - -struct Anm2EventWithID -{ - s32 id; - Anm2Event event; -}; - -struct Anm2FrameWithReference -{ - Anm2Reference reference; - Anm2Frame frame; -}; - enum Anm2MergeType { ANM2_MERGE_APPEND_FRAMES, @@ -278,14 +262,14 @@ bool anm2_deserialize(Anm2* self, const std::string& path, bool isTextures = tru void anm2_new(Anm2* self); void anm2_free(Anm2* self); void anm2_created_on_set(Anm2* self); -s32 anm2_animation_add(Anm2* self); +s32 anm2_animation_add(Anm2* self, bool isAddRootFrame = true, Anm2Animation* animation = nullptr, s32 id = ID_NONE); void anm2_animation_remove(Anm2* self, s32 id); Anm2Animation* anm2_animation_from_reference(Anm2* self, Anm2Reference* reference); Anm2Item* anm2_item_from_reference(Anm2* self, Anm2Reference* reference); Anm2Frame* anm2_frame_from_reference(Anm2* self, Anm2Reference* reference); s32 anm2_frame_index_from_time(Anm2* self, Anm2Reference reference, f32 time); -Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference, s32 time = 0.0f); -void anm2_frame_erase(Anm2* self, Anm2Reference* reference); +Anm2Frame* anm2_frame_add(Anm2* self, Anm2Frame* frame, Anm2Reference* reference); +void anm2_frame_remove(Anm2* self, Anm2Reference* reference); void anm2_frame_from_time(Anm2* self, Anm2Frame* frame, Anm2Reference reference, f32 time); void anm2_reference_clear(Anm2Reference* self); void anm2_reference_item_clear(Anm2Reference* self); @@ -299,4 +283,8 @@ void anm2_scale(Anm2* self, f32 scale); void anm2_generate_from_grid(Anm2* self, Anm2Reference* reference, vec2 startPosition, vec2 size, vec2 pivot, s32 columns, s32 count, s32 delay); void anm2_spritesheet_texture_pixels_upload(Anm2* self); void anm2_spritesheet_texture_pixels_download(Anm2* self); -vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference* reference, bool isRootTransform); \ No newline at end of file +vec4 anm2_animation_rect_get(Anm2* anm2, Anm2Reference* reference, bool isRootTransform); +void anm2_frame_serialize(Anm2Frame* frame, Anm2Type type, XMLDocument* document, XMLElement* addElement, std::string* string); +void anm2_animation_serialize(Anm2* self, Anm2Animation* animation, XMLDocument* document, XMLElement* addElement, std::string* string); +bool anm2_frame_deserialize_from_xml(Anm2* self, Anm2Frame* frame, const std::string& xml); +bool anm2_animation_deserialize_from_xml(Anm2* self, Anm2Animation* frame, const std::string& xml); \ No newline at end of file diff --git a/src/clipboard.cpp b/src/clipboard.cpp index 5214103..b43607c 100644 --- a/src/clipboard.cpp +++ b/src/clipboard.cpp @@ -1,94 +1,101 @@ #include "clipboard.h" -static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2) -{ - switch (self->type) - { - case CLIPBOARD_FRAME: - { - Anm2FrameWithReference* frameWithReference = std::get_if(&self->data); - if (!frameWithReference) break; - - anm2_frame_erase(anm2, &frameWithReference->reference); - break; - } - case CLIPBOARD_ANIMATION: - { - Anm2AnimationWithID* animationWithID = std::get_if(&self->data); - if (!animationWithID) break; - - for (auto & [id, animation] : anm2->animations) - { - if (id == animationWithID->id) - { - anm2->animations.erase(animationWithID->id); - break; - } - } - break; - } - default: - break; - } -} - -static void _clipboard_item_paste(ClipboardItem* self, ClipboardLocation* location, Anm2* anm2) -{ - switch (self->type) - { - case CLIPBOARD_FRAME: - { - Anm2FrameWithReference* frameWithReference = std::get_if(&self->data); - Anm2Reference* reference = std::get_if(location); - - if (!frameWithReference || !reference) break; - if (frameWithReference->reference.itemType != reference->itemType) break; - - Anm2Animation* animation = anm2_animation_from_reference(anm2, reference); - Anm2Item* anm2Item = anm2_item_from_reference(anm2, reference); - - if (!animation || !anm2Item) break; - - anm2_frame_add(anm2, &frameWithReference->frame, reference, reference->frameIndex); - - break; - } - case CLIPBOARD_ANIMATION: - { - Anm2AnimationWithID* animationWithID = std::get_if(&self->data); - if (!animationWithID) break; - - s32 index = 0; - - if (std::holds_alternative(*location)) - index = std::get(*location); - else - break; - - index = std::clamp(index, 0, (s32)anm2->animations.size()); - - map_insert_shift(anm2->animations, index, animationWithID->animation); - break; - } - default: - break; - } -} - void clipboard_copy(Clipboard* self) { - self->item = self->hoveredItem; + std::string clipboardText{}; + + auto clipboard_text_set = [&]() + { + if (!SDL_SetClipboardText(clipboardText.c_str())) + log_warning(std::format(CLIPBOARD_TEXT_SET_WARNING, SDL_GetError())); + }; + + switch (self->type) + { + case CLIPBOARD_FRAME: + { + Anm2Reference* reference = std::get_if(&self->location); + if (!reference) break; + Anm2Frame* frame = anm2_frame_from_reference(self->anm2, reference); + if (!frame) break; + anm2_frame_serialize(frame, reference->itemType, nullptr, nullptr, &clipboardText); + clipboard_text_set(); + break; + } + case CLIPBOARD_ANIMATION: + { + s32* id = std::get_if(&self->location); + if (!id) break; + Anm2Animation* animation = map_find(self->anm2->animations, *id); + if (!animation) break; + anm2_animation_serialize(self->anm2, animation, nullptr, nullptr, &clipboardText); + clipboard_text_set(); + break; + } + break; + default: + break; + } } void clipboard_cut(Clipboard* self) { - self->item = self->hoveredItem; - _clipboard_item_remove(&self->item, self->anm2); + clipboard_copy(self); + + switch (self->type) + { + case CLIPBOARD_FRAME: + { + Anm2Reference* reference = std::get_if(&self->location); + if (!reference) break; + anm2_frame_remove(self->anm2, reference); + break; + } + case CLIPBOARD_ANIMATION: + { + s32* id = std::get_if(&self->location); + if (!id) break; + anm2_animation_remove(self->anm2, *id); + break; + } + default: + break; + } } void clipboard_paste(Clipboard* self) { - _clipboard_item_paste(&self->item, &self->location, self->anm2); + auto clipboard_string = [&]() + { + char* clipboardText = SDL_GetClipboardText(); + std::string clipboardString = std::string(clipboardText); + SDL_free(clipboardText); + return clipboardString; + }; + + switch (self->type) + { + case CLIPBOARD_FRAME: + { + Anm2Reference* reference = std::get_if(&self->location); + if (!reference) break; + Anm2Frame frame; + if (anm2_frame_deserialize_from_xml(self->anm2, &frame, clipboard_string())) + anm2_frame_add(self->anm2, &frame, reference); + break; + } + case CLIPBOARD_ANIMATION: + { + s32* id = std::get_if(&self->location); + if (!id) break; + Anm2Animation animation; + if (anm2_animation_deserialize_from_xml(self->anm2, &animation, clipboard_string())) + anm2_animation_add(self->anm2, false, &animation, *id); + break; + } + default: + break; + } } void clipboard_init(Clipboard* self, Anm2* anm2) @@ -96,3 +103,7 @@ void clipboard_init(Clipboard* self, Anm2* anm2) self->anm2 = anm2; } +bool clipboard_is_value(void) +{ + return SDL_HasClipboardText(); +} \ No newline at end of file diff --git a/src/clipboard.h b/src/clipboard.h index 092cf97..c448586 100644 --- a/src/clipboard.h +++ b/src/clipboard.h @@ -2,33 +2,25 @@ #include "anm2.h" -enum ClipboardItemType +#define CLIPBOARD_TEXT_SET_WARNING "Unable to set clipboard text! ({})" + +enum ClipboardType { CLIPBOARD_NONE, CLIPBOARD_FRAME, - CLIPBOARD_ANIMATION, + CLIPBOARD_ANIMATION }; -struct ClipboardItem -{ - std::variant data = std::monostate(); - ClipboardItemType type = CLIPBOARD_NONE; - - ClipboardItem() = default; - ClipboardItem(const Anm2FrameWithReference& frame) : data(frame), type(CLIPBOARD_FRAME) {} - ClipboardItem(const Anm2AnimationWithID& animation) : data(animation), type(CLIPBOARD_ANIMATION) {} -}; - using ClipboardLocation = std::variant; struct Clipboard { Anm2* anm2 = nullptr; - ClipboardItem item; - ClipboardItem hoveredItem; + ClipboardType type; ClipboardLocation location; }; +bool clipboard_is_value(void); void clipboard_copy(Clipboard* self); void clipboard_cut(Clipboard* self); void clipboard_paste(Clipboard* self); diff --git a/src/imgui.cpp b/src/imgui.cpp index 7076719..fe8ae47 100644 --- a/src/imgui.cpp +++ b/src/imgui.cpp @@ -94,14 +94,13 @@ static bool _imgui_is_input_default(void) return ImGui::IsItemHovered() && (ImGui::IsKeyPressed(IMGUI_INPUT_DEFAULT) || ImGui::IsMouseClicked(IMGUI_MOUSE_DEFAULT)); } -static std::string_view imgui_window_get(void) +static bool _imgui_is_focus_window(const std::string& focus) { - ImGuiWindow* navWindow = ImGui::GetCurrentContext()->NavWindow; - if (!navWindow) return {}; + ImGuiContext* ctx = ImGui::GetCurrentContext(); + if (!ctx || !ctx->NavWindow) return false; - std::string_view name(navWindow->Name); - size_t slash = name.find('/'); - return (slash == std::string_view::npos) ? name : name.substr(0, slash); + std::string_view name(ctx->NavWindow->Name); + return name.find(focus) != std::string_view::npos; } static void _imgui_atlas(const AtlasType& self, Imgui* imgui) @@ -217,7 +216,7 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType if ( imgui->isContextualActionsEnabled && _imgui_chord_pressed(self.chord_get()) && - self.is_focus_window() && (imgui_window_get() == self.focusWindow) + (self.is_focus_window() && _imgui_is_focus_window(self.focusWindow)) ) if (!self.isDisabled) isActivated = true; @@ -616,7 +615,7 @@ static void _imgui_context_menu(Imgui* self) { _imgui_selectable(IMGUI_CUT, self); _imgui_selectable(IMGUI_COPY, self); - _imgui_selectable(IMGUI_PASTE.copy({self->clipboard->item.type == CLIPBOARD_NONE}), self); + _imgui_selectable(IMGUI_PASTE.copy({!clipboard_is_value()}), self); imgui_end_popup(self); } @@ -983,11 +982,11 @@ static void _imgui_timeline(Imgui* self) if (_imgui_is_window_hovered()) { hoverReference = reference; - if (reference.itemType == ANM2_TRIGGERS) - hoverReference.frameIndex = frameTime; - else - hoverReference.frameIndex = anm2_frame_index_from_time(self->anm2, reference, frameTime); - + hoverReference.time = frameTime; + hoverReference.frameIndex = anm2_frame_index_from_time(self->anm2, reference, frameTime); + self->clipboard->location = hoverReference; + self->clipboard->type = CLIPBOARD_FRAME; + if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { *self->reference = reference; @@ -1230,14 +1229,14 @@ static void _imgui_timeline(Imgui* self) if (_imgui_button(IMGUI_ADD_FRAME.copy({!item}), self)) { Anm2Reference frameReference = *self->reference; - frameReference.frameIndex = item->frames.empty() ? 0 : std::clamp(frameReference.frameIndex, 0, static_cast(item->frames.size() - 1)); + frameReference.frameIndex = item->frames.empty() ? 0 : std::clamp(frameReference.frameIndex, 0, (s32)(item->frames.size() - 1)); Anm2Frame* addFrame = anm2_frame_from_reference(self->anm2, &frameReference); anm2_frame_add(self->anm2, addFrame, &frameReference); } if(_imgui_button(IMGUI_REMOVE_FRAME.copy({!frame}), self)) { - anm2_frame_erase(self->anm2, self->reference); + anm2_frame_remove(self->anm2, self->reference); anm2_reference_frame_clear(self->reference); } @@ -1850,6 +1849,12 @@ static void _imgui_animations(Imgui* self) self->reference->animationID = id; anm2_reference_item_clear(self->reference); } + + if (ImGui::IsItemHovered()) + { + self->clipboard->type = CLIPBOARD_ANIMATION; + self->clipboard->location = (s32)id; + } if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { @@ -1895,11 +1900,7 @@ static void _imgui_animations(Imgui* self) } if (_imgui_button(IMGUI_ANIMATION_DUPLICATE.copy({!animation}), self)) - { - s32 id = map_next_id_get(self->anm2->animations); - self->anm2->animations.insert({id, *animation}); - self->reference->animationID = id; - } + self->reference->animationID = anm2_animation_add(self->anm2, false, animation, self->reference->animationID); _imgui_button(IMGUI_ANIMATION_MERGE.copy({!animation}), self); @@ -2844,10 +2845,9 @@ void imgui_update(Imgui* self) { for (const auto& item : imgui_item_registry()) { - if (item->is_chord() && _imgui_chord_pressed(item->chord_get())) + if (item->is_chord() && _imgui_chord_pressed(item->chord_get()) && !item->is_focus_window()) { if (item->is_undoable()) imgui_snapshot(self, item->snapshotAction); - if (item->is_focus_window()) continue; if (item->is_function()) item->function(self); } } diff --git a/src/imgui.h b/src/imgui.h index 9f57bfd..1032b69 100644 --- a/src/imgui.h +++ b/src/imgui.h @@ -66,7 +66,7 @@ #define IMGUI_ACTION_FRAME_SWAP "Frame Swap" #define IMGUI_ACTION_ANIMATION_SWAP "Animation Swap" -#define IMGUI_ACTION_TRIGGER_MOVE "Trigger AtFrame" +#define IMGUI_ACTION_TRIGGER_MOVE "Trigger At Frame" #define IMGUI_ACTION_FRAME_DELAY "Frame Delay" #define IMGUI_ACTION_MOVE_PLAYHEAD "Move Playhead" #define IMGUI_ACTION_DRAW "Draw" @@ -1268,7 +1268,6 @@ IMGUI_ITEM(IMGUI_ANIMATIONS_CHILD, self.label = "## Animations Child", self.flag IMGUI_ITEM(IMGUI_ANIMATION, self.label = "## Animation Item", - self.snapshotAction = "Select Animation", self.dragDrop = "## Animation Drag Drop", self.atlas = ATLAS_ANIMATION, self.idOffset = 2000 @@ -1965,7 +1964,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_SELECTABLE, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE, self.label = "Root", self.tooltip = "The root item of an animation.\nChanging its properties will transform the rest of the animation.", - self.snapshotAction = "Root Item Select", self.atlas = ATLAS_ROOT, self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE ); @@ -1973,7 +1971,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_ROOT_SELECTABLE, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE, self.label = "## Layer Selectable", self.tooltip = "A layer item.\nA graphical item within the animation.", - self.snapshotAction = "Layer Item Select", self.dragDrop = "## Layer Drag Drop", self.atlas = ATLAS_LAYER, self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE @@ -1982,7 +1979,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_LAYER_SELECTABLE, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_NULL_SELECTABLE, self.label = "## Null Selectable", self.tooltip = "A null item.\nAn invisible item within the animation that is accessible via a game engine.", - self.snapshotAction = "Null Item Select", self.dragDrop = "## Null Drag Drop", self.atlas = ATLAS_NULL, self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE @@ -1991,7 +1987,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_ITEM_NULL_SELECTABLE, IMGUI_ITEM(IMGUI_TIMELINE_ITEM_TRIGGERS_SELECTABLE, self.label = "Triggers", self.tooltip = "The animation's triggers.\nWill fire based on an event.", - self.snapshotAction = "Triggers Item Select", self.atlas = ATLAS_TRIGGERS, self.size = IMGUI_TIMELINE_ITEM_SELECTABLE_SIZE ); @@ -2056,7 +2051,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_FRAME, self.label = "## Frame"); static const vec4 IMGUI_FRAME_BORDER_COLOR = {1.0f, 1.0f, 1.0f, 0.25f}; IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME, self.label = "## Root Frame", - self.snapshotAction = "Root Frame Select", self.color = {{0.14f, 0.27f, 0.39f, 1.0f}, {0.28f, 0.54f, 0.78f, 1.0f}, {0.36f, 0.70f, 0.95f, 1.0f}, IMGUI_FRAME_BORDER_COLOR}, self.size = IMGUI_TIMELINE_FRAME_SIZE, self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET, @@ -2065,7 +2059,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_ROOT_FRAME, IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME, self.label = "## Layer Frame", - self.snapshotAction = "Layer Frame Select", self.dragDrop = "## Layer Frame Drag Drop", self.color = {{0.45f, 0.18f, 0.07f, 1.0f}, {0.78f, 0.32f, 0.12f, 1.0f}, {0.95f, 0.40f, 0.15f, 1.0f}, IMGUI_FRAME_BORDER_COLOR}, self.size = IMGUI_TIMELINE_FRAME_SIZE, @@ -2075,7 +2068,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_LAYER_FRAME, IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME, self.label = "## Null Frame", - self.snapshotAction = "Null Frame Select", self.dragDrop = "## Null Frame Drag Drop", self.color = {{0.17f, 0.33f, 0.17f, 1.0f}, {0.34f, 0.68f, 0.34f, 1.0f}, {0.44f, 0.88f, 0.44f, 1.0f}, IMGUI_FRAME_BORDER_COLOR}, self.size = IMGUI_TIMELINE_FRAME_SIZE, @@ -2085,7 +2077,6 @@ IMGUI_ITEM(IMGUI_TIMELINE_NULL_FRAME, IMGUI_ITEM(IMGUI_TIMELINE_TRIGGERS_FRAME, self.label = "## Triggers Frame", - self.snapshotAction = "Trigger Select", self.color = {{0.36f, 0.14f, 0.24f, 1.0f}, {0.72f, 0.28f, 0.48f, 1.0f}, {0.92f, 0.36f, 0.60f, 1.0f}, IMGUI_FRAME_BORDER_COLOR}, self.size = IMGUI_TIMELINE_FRAME_SIZE, self.atlasOffset = IMGUI_TIMELINE_FRAME_ATLAS_OFFSET, diff --git a/src/texture.h b/src/texture.h index 84d577f..6339d4b 100644 --- a/src/texture.h +++ b/src/texture.h @@ -23,5 +23,4 @@ bool texture_from_rgba_init(Texture* self, ivec2 size, s32 channels, const u8* d bool texture_from_rgba_write(const std::string& path, const u8* data, ivec2 size); bool texture_pixel_set(Texture* self, ivec2 position, vec4 color); void texture_free(Texture* self); -std::vector texture_download(const Texture* self); -Texture texture_copy(Texture* self); \ No newline at end of file +std::vector texture_download(const Texture* self); \ No newline at end of file