// Aseprite Document Library // Copyright (c) 2023 Igara Studio S.A. // Copyright (c) 2001-2015 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "doc/user_data_io.h" #include "base/serialization.h" #include "doc/string_io.h" #include "doc/user_data.h" #include namespace doc { using namespace base::serialization; using namespace base::serialization::little_endian; static void write_point(std::ostream& os, const gfx::Point& point) { write32(os, point.x); write32(os, point.y); } static void write_size(std::ostream& os, const gfx::Size& size) { write32(os, size.w); write32(os, size.h); } static void write_property_value(std::ostream& os, const UserData::Variant& variant) { switch (variant.type()) { case USER_DATA_PROPERTY_TYPE_BOOL: write8(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_INT8: write8(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_UINT8: write8(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_INT16: write16(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_UINT16: write16(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_INT32: write32(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_UINT32: write32(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_INT64: write64(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_UINT64: write64(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_FIXED: write32(os, get_value(variant).value); break; case USER_DATA_PROPERTY_TYPE_FLOAT: write_float(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_DOUBLE: write_double(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_STRING: write_string(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_POINT: write_point(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_SIZE: write_size(os, get_value(variant)); break; case USER_DATA_PROPERTY_TYPE_RECT: { const gfx::Rect& rect = get_value(variant); write_point(os, rect.origin()); write_size(os, rect.size()); break; } case USER_DATA_PROPERTY_TYPE_VECTOR: { const std::vector& vector = get_value>(variant); write32(os, vector.size()); for (auto elem : vector) { write16(os, elem.type()); write_property_value(os, elem); } break; } case USER_DATA_PROPERTY_TYPE_PROPERTIES: { auto properties = get_value(variant); write32(os, properties.size()); for (auto property : properties) { const std::string& name = property.first; write_string(os, name); const UserData::Variant& value = property.second; write16(os, value.type()); write_property_value(os, value); } break; } } } static void write_properties_maps(std::ostream& os, const UserData::PropertiesMaps& propertiesMaps) { write32(os, propertiesMaps.size()); for (auto propertiesMap : propertiesMaps) { const UserData::Properties& properties = propertiesMap.second; const std::string& extensionKey = propertiesMap.first; write_string(os, extensionKey); write_property_value(os, properties); } } void write_user_data(std::ostream& os, const UserData& userData) { write_string(os, userData.text()); write32(os, userData.color()); write_properties_maps(os, userData.propertiesMaps()); } static UserData::Variant read_property_value(std::istream& is, uint16_t type) { switch (type) { case USER_DATA_PROPERTY_TYPE_NULLPTR: { return nullptr; } case USER_DATA_PROPERTY_TYPE_BOOL: { bool value = read8(is); return value; } case USER_DATA_PROPERTY_TYPE_INT8: { int8_t value = read8(is); return value; } case USER_DATA_PROPERTY_TYPE_UINT8: { uint8_t value = read8(is); return value; } case USER_DATA_PROPERTY_TYPE_INT16: { int16_t value = read16(is); return value; } case USER_DATA_PROPERTY_TYPE_UINT16: { uint16_t value = read16(is); return value; } case USER_DATA_PROPERTY_TYPE_INT32: { int32_t value = read32(is); return value; } case USER_DATA_PROPERTY_TYPE_UINT32: { uint32_t value = read32(is); return value; } case USER_DATA_PROPERTY_TYPE_INT64: { int64_t value = read64(is); return value; } case USER_DATA_PROPERTY_TYPE_UINT64: { uint64_t value = read64(is); return value; } case USER_DATA_PROPERTY_TYPE_FIXED: { int32_t value = read32(is); return doc::UserData::Fixed{value}; } case USER_DATA_PROPERTY_TYPE_FLOAT: { float value = read_float(is); return value; } case USER_DATA_PROPERTY_TYPE_DOUBLE: { double value = read_double(is); return value; } case USER_DATA_PROPERTY_TYPE_STRING: { std::string value = read_string(is); return value; } case USER_DATA_PROPERTY_TYPE_POINT: { int32_t x = read32(is); int32_t y = read32(is); return gfx::Point(x, y); } case USER_DATA_PROPERTY_TYPE_SIZE: { int32_t w = read32(is); int32_t h = read32(is); return gfx::Size(w, h); } case USER_DATA_PROPERTY_TYPE_RECT: { int32_t x = read32(is); int32_t y = read32(is); int32_t w = read32(is); int32_t h = read32(is); return gfx::Rect(x, y, w, h); } case USER_DATA_PROPERTY_TYPE_VECTOR: { auto numElems = read32(is); std::vector value; for (int k=0; k(properties); } return propertiesMaps; } UserData read_user_data(std::istream& is, const int docFormatVer) { UserData userData; userData.setText(read_string(is)); // This check is here because we've been restoring sprites from // old sessions where the color is restored incorrectly if we // don't check if there is enough space to read from the file // (e.g. reading a random color or just white, maybe -1 which is // 0xffffffff in 32-bit). if (!is.eof()) { userData.setColor(read32(is)); // When recovering a session from an old Aseprite version, we need // to skip reading the parts that it doesn't contains. Otherwise // it is very likely to fail. if (docFormatVer >= DOC_FORMAT_VERSION_2) { userData.propertiesMaps() = read_properties_maps(is); } } return userData; } }