From 76b8b03a2c7c4bbda397629452eaf45fa7fe5ea2 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Thu, 30 May 2024 23:14:19 -0500 Subject: [PATCH] Squashed 'deps/nostalgia/' changes from eed115b2..7d9f363b 7d9f363b [nostalgia/core/studio] Show color names in TileSheetEditor ababc2a7 [nostalgia/core/studio] Add Name to colors table in PaletteEditor bfd4bc3c [nostalgia/core] Revise Palette system, give TileSheetEditor a config file 8826d51e [studio] Add configio.hpp to studio.hpp c021e5e7 [ox/oc] Fix OC not dealing with certain int types properly 7d8a8e0e [keel] Cleanup 95a69b72 [ox/std] Fix String::c_str to always retrun a valid C str e4c38660 [nostalgia/core] Fix subsheet resize not to read garbage 67cf3ae8 [ox/std] Ensure ox::String always has at least a null terminator 2761f23d [nostalgia/developer-handbook] Update serialization notes 6c170d31 [nostalgia/developer-handbook] Update serialization notes d20bfc77 [nostalgia/developer-handbook] Update serialization notes 07ecbde1 [nostalgia/developer-handbook] Update notes on error handling fbe2fcd3 [nostalgia/developer-handbook] Update notes on error handling a8bb99b6 [turbine] Remove ClipboardObject::typeMatches 667dd21a [turbine] Make ClipboardObject::typId return a StringView 5d89370c [turbine] Rework getClipboardObject to use ox::ModelTypeId_v b968ec8a [keel] Remove redundant move ec45ffb7 [studio] Fix build error 97dc0274 [nostalgia/core/studio] Add acceptsClipboardPayload to TileSheetEditor a138f60f [studio] Add acceptsClipboardPayload to Editor 791d1950 [turbine] Make ClipboardObject use ox::ModelTypeId_v for typeId 78eb8fca [keel] Cleanup pack logging 0b8051b6 [ox/preloader] Fix alignment issue 5a426829 [nostalgia/core/studio] Cleanup TileSheet selection, fix copy/paste bug 9d2fe0e8 [studio] Add size function to Selection f1894699 [keel] Remove setAsset 27b38ed2 [keel,studio] Fix hotloading for files that get loaded as multiple types 2bb7c514 [studio/modlib] Fix type desc writing logic inversion 5177cfb0 [studio/modlib] Make Project::mkdir only mkdir if dir does not exist f9a14433 [studio/modlib] Add variant of ComboBox that takes callback e62426b0 [keel] Ensure consistent asset IDs in AssetManager af634bd4 [ox/fs] Add FileSystem::exists 49b859ec [studio/modlib] Give Selection constructors 19a41201 [studio/modlib] Make iterateSelection return errors properly f69b8afa [nostalgia] Remove use of deleted function 9c98b5e2 [studio/modlib] Remove color.hpp 1f87216d [nostalgia/core] Add applySelectionColor 94c59604 [nostalgia/core/opengl] Fix for Ox changes 8ee016c1 [studio/modlib] Add SelectionTracker dc20c667 [ox/std] Add conversion functions for geo types, cleanup 407e5424 [ox/std] Remove SmallMap dtor, replace timing code with steady_clock 3b188696 [ox/claw] Remove enum type from ClawFormat 0fab6c7c [ox/preloader] Remove debug code a72b865d [studio/modlib] Add function for exporting selection color c0479604 [studio,nostalgia/studio] Make executing UndoCommands report errors a1c89906 [nostalgia/studio] Make UndoCommand undo/redo return ox::Error 7fb0549c [nostalgia/core] Revert some auto formatting done by CLion... 37e65ab0 [nostalgia/core/studio] Fix Subsheet width to update properly 9105b1ec [ox/std] Fix Linux build fbeb0815 [ox/model] Fix type params in buildTypeId b882a47e [ox/std] Fix resize to set null terminator 660f2f56 [ox/std] Rework FileReader into StreamReader aa83c2a6 [nostalgia/core/studio] Remove some unnecessary copying 4a2b1fd7 [studio,keel] Make fileChanged emit UUID as well as path, add uuidUrlToUuid 08f958fb [ox/std] Add IntegerRange_c a651d45a [ox/std] Fix Vector insert functions 9e9f317c [studio] Make UndoCommand::mergeWith take a reference f5a02ce9 [nostalgia/core/gba] Fix build 6971c310 [studio] Add NoChangeException c47f48eb [keel] Add/cleanup UUID/path lookup functions 76771e7b [nostalgia/core] Add tileColumns and tileRows functions f6a0ae20 [ox/std] Fix some Windows warnings 752c8c1d [glutils] Fix type conversion that happened on Windows af3bff1a [glutils] Add FrameBuffer::sizef 87416e13 [ox/std] Make MallocaPtr call destructor 047b4396 [ox/std] Make Point and Size members int32_t 40b8da4d [studio/modlib] Cleanup 123c4125 [ox/std] Add SmallMap::pairs(), SmallMap model 963ec5d3 [ox/std] Add operator-> to SpanIterator 6df77a23 [glutils] Add size function to FrameBuffer df412cf8 [ox/std] Add missing typenames ae30ef36 Merge commit 'b66cef7127e97269fc6072a6f66ccc08990f6d2e' 095a1135 Merge commit 'f48824793cfce315971fe2e699ece198c7a79407' ce1836ab Merge commit '1e041bd2ebfe5ace7bed3906faf60345aa98a8bc' 7d1641fa Merge commit '420fa96463f59c4a4a7cd66b16b0ad01ab0d55e6' 423212b2 [studio] Add missing include 60da1063 Merge commit 'bd416f82e25f1b710ab2b7890274571dd3fcd53d' 60d1996f [keel] Minor optimization git-subtree-dir: deps/nostalgia git-subtree-split: 7d9f363bfa7a2c64f5c4bcfd0b6686f3f5678119 --- deps/glutils/include/glutils/glutils.hpp | 17 ++ deps/glutils/src/glutils.cpp | 2 +- deps/ox/src/ox/claw/format.hpp | 2 +- deps/ox/src/ox/fs/filesystem/filesystem.hpp | 15 ++ deps/ox/src/ox/model/desctypes.hpp | 7 +- deps/ox/src/ox/oc/read.cpp | 129 ---------- deps/ox/src/ox/oc/read.hpp | 68 ++--- deps/ox/src/ox/oc/write.hpp | 12 +- deps/ox/src/ox/preloader/preloader.hpp | 19 +- deps/ox/src/ox/std/CMakeLists.txt | 1 + deps/ox/src/ox/std/bit.hpp | 2 +- deps/ox/src/ox/std/concepts.hpp | 3 + deps/ox/src/ox/std/conv.hpp | 45 ++++ deps/ox/src/ox/std/iterator.hpp | 4 + deps/ox/src/ox/std/new.hpp | 1 + deps/ox/src/ox/std/point.hpp | 5 +- deps/ox/src/ox/std/reader.cpp | 55 ++-- deps/ox/src/ox/std/reader.hpp | 8 +- deps/ox/src/ox/std/size.hpp | 5 +- deps/ox/src/ox/std/smallmap.hpp | 28 ++- deps/ox/src/ox/std/stacktrace.cpp | 2 +- deps/ox/src/ox/std/std.hpp | 1 + deps/ox/src/ox/std/string.cpp | 1 + deps/ox/src/ox/std/string.hpp | 37 +-- deps/ox/src/ox/std/test/tests.cpp | 14 +- deps/ox/src/ox/std/typetraits.hpp | 2 + deps/ox/src/ox/std/vec.hpp | 71 ++++-- deps/ox/src/ox/std/vector.hpp | 48 ++-- developer-handbook.md | 88 ++++--- .../core/include/nostalgia/core/color.hpp | 12 +- .../core/include/nostalgia/core/gfx.hpp | 10 +- .../core/include/nostalgia/core/palette.hpp | 108 +++++++- .../core/include/nostalgia/core/tilesheet.hpp | 8 + src/nostalgia/modules/core/src/gba/gfx.cpp | 22 +- src/nostalgia/modules/core/src/gfx.cpp | 11 + .../modules/core/src/keel/keelmodule.cpp | 10 +- .../modules/core/src/keel/typeconv.cpp | 21 ++ .../modules/core/src/keel/typeconv.hpp | 10 +- src/nostalgia/modules/core/src/opengl/gfx.cpp | 13 +- .../paletteeditor/paletteeditor-imgui.cpp | 116 +++++---- .../paletteeditor/paletteeditor-imgui.hpp | 4 +- .../studio/paletteeditor/paletteeditor.cpp | 234 ++++++++++++------ .../studio/paletteeditor/paletteeditor.hpp | 118 ++++++--- .../commands/addsubsheetcommand.cpp | 8 +- .../commands/addsubsheetcommand.hpp | 4 +- .../commands/cutpastecommand.cpp | 6 +- .../commands/cutpastecommand.hpp | 4 +- .../commands/deletetilescommand.cpp | 6 +- .../commands/deletetilescommand.hpp | 4 +- .../tilesheeteditor/commands/drawcommand.cpp | 6 +- .../tilesheeteditor/commands/drawcommand.hpp | 4 +- .../commands/inserttilescommand.cpp | 6 +- .../commands/inserttilescommand.hpp | 4 +- .../commands/palettechangecommand.cpp | 6 +- .../commands/palettechangecommand.hpp | 4 +- .../commands/rmsubsheetcommand.cpp | 15 +- .../commands/rmsubsheetcommand.hpp | 4 +- .../commands/updatesubsheetcommand.cpp | 10 +- .../commands/updatesubsheetcommand.hpp | 4 +- .../tilesheeteditor/tilesheeteditor-imgui.cpp | 144 +++++++---- .../tilesheeteditor/tilesheeteditor-imgui.hpp | 18 +- .../tilesheeteditor/tilesheeteditormodel.cpp | 101 ++++---- .../tilesheeteditor/tilesheeteditormodel.hpp | 8 +- .../tilesheeteditor/tilesheeteditorview.cpp | 15 +- .../tilesheeteditor/tilesheeteditorview.hpp | 7 +- .../tilesheeteditor/tilesheetpixels.cpp | 5 +- src/nostalgia/modules/core/src/tilesheet.cpp | 58 ++++- .../scene/src/studio/sceneeditor-imgui.cpp | 1 - .../keel/include/keel/assetmanager.hpp | 92 ++++--- src/olympic/keel/include/keel/media.hpp | 97 ++++---- src/olympic/keel/include/keel/pack.hpp | 10 +- src/olympic/keel/src/media.cpp | 84 ++++++- src/olympic/keel/src/pack-applib.cpp | 17 +- src/olympic/keel/src/pack.cpp | 8 +- src/olympic/studio/applib/src/studioapp.cpp | 11 +- .../studio/modlib/include/studio/context.hpp | 5 + .../studio/modlib/include/studio/editor.hpp | 11 +- .../modlib/include/studio/imguiutil.hpp | 15 +- .../studio/modlib/include/studio/project.hpp | 30 ++- .../include/studio/selectiontracker.hpp | 133 ++++++++++ .../studio/modlib/include/studio/studio.hpp | 2 + .../modlib/include/studio/undocommand.hpp | 16 +- .../modlib/include/studio/undostack.hpp | 6 +- src/olympic/studio/modlib/src/editor.cpp | 10 +- src/olympic/studio/modlib/src/imguiutil.cpp | 20 ++ src/olympic/studio/modlib/src/project.cpp | 16 +- src/olympic/studio/modlib/src/undocommand.cpp | 2 +- src/olympic/studio/modlib/src/undostack.cpp | 20 +- .../turbine/include/turbine/clipboard.hpp | 15 +- src/olympic/turbine/src/glfw/clipboard.cpp | 4 +- 90 files changed, 1633 insertions(+), 832 deletions(-) create mode 100644 deps/ox/src/ox/std/conv.hpp create mode 100644 src/olympic/studio/modlib/include/studio/selectiontracker.hpp diff --git a/deps/glutils/include/glutils/glutils.hpp b/deps/glutils/include/glutils/glutils.hpp index 6713e55..3c7db96 100644 --- a/deps/glutils/include/glutils/glutils.hpp +++ b/deps/glutils/include/glutils/glutils.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace glutils { @@ -137,6 +138,22 @@ struct FrameBuffer { constexpr operator const GLuint&() const noexcept { return fbo.id; } + + [[nodiscard]] + constexpr ox::Vec2 sizef() const noexcept { + return { + static_cast(width), + static_cast(height), + }; + } + + [[nodiscard]] + constexpr ox::Size size() const noexcept { + return { + width, + height, + }; + } }; class FrameBufferBind { diff --git a/deps/glutils/src/glutils.cpp b/deps/glutils/src/glutils.cpp index 6f80d66..d84f40b 100644 --- a/deps/glutils/src/glutils.cpp +++ b/deps/glutils/src/glutils.cpp @@ -102,7 +102,7 @@ void setupShaderParams( ox::Vector const&vars, GLsizei vertexRowLen) noexcept { // setup vars - for (auto lenWritten = 0LU; auto const&v : vars) { + for (size_t lenWritten = 0; auto const&v : vars) { auto const attr = static_cast(glGetAttribLocation(shader, v.name.c_str())); glEnableVertexAttribArray(attr); glVertexAttribPointer( diff --git a/deps/ox/src/ox/claw/format.hpp b/deps/ox/src/ox/claw/format.hpp index 7355073..55dbb63 100644 --- a/deps/ox/src/ox/claw/format.hpp +++ b/deps/ox/src/ox/claw/format.hpp @@ -10,7 +10,7 @@ namespace ox { -enum class ClawFormat: int { +enum class ClawFormat { None, Metal, Organic, diff --git a/deps/ox/src/ox/fs/filesystem/filesystem.hpp b/deps/ox/src/ox/fs/filesystem/filesystem.hpp index 5cef155..c39f556 100644 --- a/deps/ox/src/ox/fs/filesystem/filesystem.hpp +++ b/deps/ox/src/ox/fs/filesystem/filesystem.hpp @@ -99,6 +99,21 @@ class FileSystem { Result stat(const FileAddress &addr) const noexcept; + [[nodiscard]] + inline bool exists(uint64_t inode) const noexcept { + return statInode(inode).ok(); + } + + [[nodiscard]] + inline bool exists(ox::StringView path) const noexcept { + return statPath(path).ok(); + } + + [[nodiscard]] + inline bool exists(FileAddress const&addr) const noexcept { + return stat(addr).ok(); + } + [[nodiscard]] virtual uint64_t spaceNeeded(uint64_t size) const noexcept = 0; diff --git a/deps/ox/src/ox/model/desctypes.hpp b/deps/ox/src/ox/model/desctypes.hpp index 9eb2af7..a963ee4 100644 --- a/deps/ox/src/ox/model/desctypes.hpp +++ b/deps/ox/src/ox/model/desctypes.hpp @@ -34,15 +34,16 @@ constexpr auto buildTypeId() noexcept { return ox::sfmt("{};{}", name, version); } -static constexpr auto buildTypeId(CRStringView name, int version, - const TypeParamPack &typeParams = {}) noexcept { +static constexpr auto buildTypeId( + CRStringView name, int version, + const TypeParamPack &typeParams = {}) noexcept { String tp; if (!typeParams.empty()) { tp = "#"; for (const auto &p : typeParams) { tp += p + ","; } - tp.resize(tp.len() - 1); + tp.resize(tp.len()); tp += "#"; } return ox::sfmt("{}{};{}", name, tp, version); diff --git a/deps/ox/src/ox/oc/read.cpp b/deps/ox/src/ox/oc/read.cpp index d2b1ff9..fb5ef3f 100644 --- a/deps/ox/src/ox/oc/read.cpp +++ b/deps/ox/src/ox/oc/read.cpp @@ -36,135 +36,6 @@ OrganicClawReader::OrganicClawReader(Json::Value json, int unionIdx) noexcept: m_unionIdx(unionIdx) { } -Error OrganicClawReader::field(const char *key, int8_t *val) noexcept { - auto err = OxError(0); - if (targetValid()) { - const auto &jv = value(key); - if (jv.empty()) { - *val = 0; - } else if (jv.isInt()) { - *val = static_cast(jv.asInt()); - } else { - err = OxError(1, "Type mismatch"); - } - } - ++m_fieldIt; - return err; -} - -Error OrganicClawReader::field(const char *key, int16_t *val) noexcept { - auto err = OxError(0); - if (targetValid()) { - const auto &jv = value(key); - if (jv.empty()) { - *val = 0; - } else if (jv.isInt()) { - *val = static_cast(jv.asInt()); - } else { - err = OxError(1, "Type mismatch"); - } - } - ++m_fieldIt; - return err; -} - -Error OrganicClawReader::field(const char *key, int32_t *val) noexcept { - auto err = OxError(0); - if (targetValid()) { - const auto &jv = value(key); - if (jv.empty()) { - *val = 0; - } else if (jv.isInt()) { - *val = static_cast(jv.asInt()); - } else { - err = OxError(1, "Type mismatch"); - } - } - ++m_fieldIt; - return err; -} - -Error OrganicClawReader::field(const char *key, int64_t *val) noexcept { - auto err = OxError(0); - if (targetValid()) { - const auto &jv = value(key); - if (jv.empty()) { - *val = 0; - } else if (jv.isInt() || jv.isInt64()) { - *val = static_cast(jv.asInt64()); - } else { - err = OxError(1, "Type mismatch"); - } - } - ++m_fieldIt; - return err; -} - - -Error OrganicClawReader::field(const char *key, uint8_t *val) noexcept { - auto err = OxError(0); - if (targetValid()) { - const auto &jv = value(key); - if (jv.empty()) { - *val = 0; - } else if (jv.isUInt()) { - *val = static_cast(jv.asUInt()); - } else { - err = OxError(1, "Type mismatch"); - } - } - ++m_fieldIt; - return err; -} - -Error OrganicClawReader::field(const char *key, uint16_t *val) noexcept { - auto err = OxError(0); - if (targetValid()) { - const auto &jv = value(key); - if (jv.empty()) { - *val = 0; - } else if (jv.isUInt()) { - *val = static_cast(jv.asUInt()); - } else { - err = OxError(1, "Type mismatch"); - } - } - ++m_fieldIt; - return err; -} - -Error OrganicClawReader::field(const char *key, uint32_t *val) noexcept { - auto err = OxError(0); - if (targetValid()) { - const auto &jv = value(key); - if (jv.empty()) { - *val = 0; - } else if (jv.isUInt()) { - *val = static_cast(jv.asUInt()); - } else { - err = OxError(1, "Type mismatch"); - } - } - ++m_fieldIt; - return err; -} - -Error OrganicClawReader::field(const char *key, uint64_t *val) noexcept { - auto err = OxError(0); - if (targetValid()) { - const auto &jv = value(key); - if (jv.empty()) { - *val = 0; - } else if (jv.isUInt() || jv.isUInt64()) { - *val = static_cast(jv.asUInt64()); - } else { - err = OxError(1, "Type mismatch"); - } - } - ++m_fieldIt; - return err; -} - Error OrganicClawReader::field(const char *key, bool *val) noexcept { auto err = OxError(0); if (targetValid()) { diff --git a/deps/ox/src/ox/oc/read.hpp b/deps/ox/src/ox/oc/read.hpp index b254d49..2db945a 100644 --- a/deps/ox/src/ox/oc/read.hpp +++ b/deps/ox/src/ox/oc/read.hpp @@ -41,16 +41,6 @@ class OrganicClawReader { explicit OrganicClawReader(Json::Value json, int unionIdx = -1) noexcept; - Error field(const char *key, int8_t *val) noexcept; - Error field(const char *key, int16_t *val) noexcept; - Error field(const char *key, int32_t *val) noexcept; - Error field(const char *key, int64_t *val) noexcept; - - Error field(const char *key, uint8_t *val) noexcept; - Error field(const char *key, uint16_t *val) noexcept; - Error field(const char *key, uint32_t *val) noexcept; - Error field(const char *key, uint64_t *val) noexcept; - Error field(const char *key, bool *val) noexcept; // array handler @@ -144,28 +134,46 @@ class OrganicClawReader { template Error OrganicClawReader::field(const char *key, T *val) noexcept { auto err = OxError(0); - if constexpr(isVector_v) { - const auto &srcVal = value(key); - const auto srcSize = srcVal.size(); - oxReturnError(ox::resizeVector(*val, srcSize)); - err = field(key, val->data(), val->size()); - } else if constexpr(isArray_v) { - const auto &srcVal = value(key); - const auto srcSize = srcVal.size(); - if (srcSize > val->size()) { - err = OxError(1, "Input array is too long"); - } else { + try { + if constexpr (is_integer_v) { + if (targetValid()) { + auto const&jv = value(key); + auto const rightType = sizeof(T) == 8 ? + (ox::is_signed_v ? jv.isInt64() : jv.isUInt64()) : + (ox::is_signed_v ? jv.isInt() : jv.isUInt()); + if (jv.empty()) { + *val = 0; + } else if (rightType) { + *val = static_cast(jv.asUInt()); + } else { + err = OxError(1, "Type mismatch"); + } + } + } else if constexpr (isVector_v) { + const auto&srcVal = value(key); + const auto srcSize = srcVal.size(); + oxReturnError(ox::resizeVector(*val, srcSize)); err = field(key, val->data(), val->size()); + } else if constexpr (isArray_v) { + const auto&srcVal = value(key); + const auto srcSize = srcVal.size(); + if (srcSize > val->size()) { + err = OxError(1, "Input array is too long"); + } else { + err = field(key, val->data(), val->size()); + } + } else if (targetValid()) { + const auto&jv = value(key); + if (jv.empty() || jv.isObject()) { + auto reader = child(key); + ModelHandlerInterface handler(&reader); + err = model(&handler, val); + } else { + err = OxError(1, "Type mismatch"); + } } - } else if (targetValid()) { - const auto &jv = value(key); - if (jv.empty() || jv.isObject()) { - auto reader = child(key); - ModelHandlerInterface handler(&reader); - err = model(&handler, val); - } else { - err = OxError(1, "Type mismatch"); - } + } catch (Json::LogicError const&) { + err = OxError(1, "error reading JSON data"); } ++m_fieldIt; return err; diff --git a/deps/ox/src/ox/oc/write.hpp b/deps/ox/src/ox/oc/write.hpp index af05040..10489e3 100644 --- a/deps/ox/src/ox/oc/write.hpp +++ b/deps/ox/src/ox/oc/write.hpp @@ -209,7 +209,17 @@ Error OrganicClawWriter::field(const char *key, const T *val, std::size_t len) n template Error OrganicClawWriter::field(const char *key, const T *val) noexcept { - if constexpr(isVector_v || isArray_v) { + if constexpr(is_integer_v) { + if (targetValid() && (*val || m_json.isArray())) { + // the int type needs to be normalized because jsoncpp doesn't + // factor in every permutation unsigned long, etc. + if constexpr(ox::is_signed_v) { + value(key) = static_cast>(*val); + } else { + value(key) = static_cast>(*val); + } + } + } else if constexpr(isVector_v || isArray_v) { return field(key, val->data(), val->size()); } else if (val && targetValid()) { OrganicClawWriter w; diff --git a/deps/ox/src/ox/preloader/preloader.hpp b/deps/ox/src/ox/preloader/preloader.hpp index 1bc10e9..fd428bb 100644 --- a/deps/ox/src/ox/preloader/preloader.hpp +++ b/deps/ox/src/ox/preloader/preloader.hpp @@ -94,7 +94,7 @@ class Preloader: public ModelHandlerBase, OpType::Reflect> { } template - constexpr ox::Error field(CRStringView, const ox::UnionView val) noexcept; + constexpr ox::Error field(CRStringView, ox::UnionView val) noexcept; template constexpr ox::Error field(CRStringView, const T *val) noexcept; @@ -135,6 +135,9 @@ class Preloader: public ModelHandlerBase, OpType::Reflect> { constexpr ox::Error fieldArray(CRStringView name, ox::ModelValueArray const*val) noexcept; constexpr bool unionCheckAndIt() noexcept; + + [[nodiscard]] + constexpr size_t calcPadding(size_t align) const noexcept; }; template @@ -265,7 +268,7 @@ template constexpr ox::Result Preloader::startAlloc(size_t sz, size_t align) noexcept { m_allocStack.emplace_back(static_cast(m_writer.tellp())); oxReturnError(m_writer.seekp(0, ox::ios_base::end)); - const auto padding = m_writer.tellp() % align; + auto const padding = calcPadding(align); oxRequireM(a, ox::allocate(m_writer, sz + padding)); a += padding; oxReturnError(m_writer.seekp(a)); @@ -278,7 +281,7 @@ constexpr ox::Result Preloader::startAlloc( std::size_t sz, size_t align, std::size_t restore) noexcept { m_allocStack.emplace_back(restore, ox::ios_base::beg); oxReturnError(m_writer.seekp(0, ox::ios_base::end)); - const auto padding = m_writer.tellp() % align; + auto const padding = calcPadding(align); oxRequireM(a, ox::allocate(m_writer, sz + padding)); a += padding; oxReturnError(m_writer.seekp(a)); @@ -358,7 +361,7 @@ constexpr ox::Error Preloader::fieldVector( const auto sz = sizeOf(&(*val)[0]) * val->size(); const auto align = alignOf((*val)[0]); oxReturnError(m_writer.seekp(0, ox::ios_base::end)); - const auto padding = m_writer.tellp() % align; + auto const padding = calcPadding(align); oxRequireM(p, ox::allocate(m_writer, sz + padding)); p += padding; oxReturnError(m_writer.seekp(p)); @@ -381,8 +384,6 @@ constexpr ox::Error Preloader::fieldVector( template constexpr ox::Error Preloader::fieldArray(CRStringView, ox::ModelValueArray const*val) noexcept { - oxDebugf("array size: {}", val->size()); - oxDebugf("array sizeOf: {}", sizeOf(val)); oxReturnError(pad(&(*val)[0])); for (auto const&v : *val) { oxReturnError(this->interface()->field({}, &v)); @@ -396,6 +397,12 @@ constexpr bool Preloader::unionCheckAndIt() noexcept { return u.checkAndIterate(); } +template +constexpr size_t Preloader::calcPadding(size_t align) const noexcept { + auto const excess = m_writer.tellp() % align; + return (align * (excess != 0)) - excess; +} + template constexpr ox::Error preload(Preloader *pl, ox::CommonPtrWith auto *obj) noexcept { oxReturnError(model(pl->interface(), obj)); diff --git a/deps/ox/src/ox/std/CMakeLists.txt b/deps/ox/src/ox/std/CMakeLists.txt index 0aface6..32cdf7c 100644 --- a/deps/ox/src/ox/std/CMakeLists.txt +++ b/deps/ox/src/ox/std/CMakeLists.txt @@ -96,6 +96,7 @@ install( buildinfo.hpp byteswap.hpp concepts.hpp + conv.hpp def.hpp defer.hpp defines.hpp diff --git a/deps/ox/src/ox/std/bit.hpp b/deps/ox/src/ox/std/bit.hpp index 6573ab0..250d087 100644 --- a/deps/ox/src/ox/std/bit.hpp +++ b/deps/ox/src/ox/std/bit.hpp @@ -32,7 +32,7 @@ constexpr To bit_cast(const From &src) noexcept requires(sizeof(To) == sizeof(Fr namespace ox { template -constexpr typename enable_if::type cbit_cast(From src) noexcept { +constexpr To cbit_cast(From src) noexcept requires(sizeof(To) == sizeof(From)) { To dst = {}; ox::memcpy(&dst, &src, sizeof(src)); return dst; diff --git a/deps/ox/src/ox/std/concepts.hpp b/deps/ox/src/ox/std/concepts.hpp index ccc993b..7468768 100644 --- a/deps/ox/src/ox/std/concepts.hpp +++ b/deps/ox/src/ox/std/concepts.hpp @@ -8,6 +8,7 @@ #pragma once +#include "bit.hpp" #include "typetraits.hpp" namespace ox { @@ -29,4 +30,6 @@ concept OxString_c = isOxString_v; template concept Integral_c = ox::is_integral_v; +template +concept IntegerRange_c = ox::is_integer_v && ox::MaxValue >= max; } diff --git a/deps/ox/src/ox/std/conv.hpp b/deps/ox/src/ox/std/conv.hpp new file mode 100644 index 0000000..4329f86 --- /dev/null +++ b/deps/ox/src/ox/std/conv.hpp @@ -0,0 +1,45 @@ +/* + * Copyright 2015 - 2024 gary@drinkingtea.net + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "point.hpp" +#include "size.hpp" +#include "vec.hpp" + +namespace ox { + +constexpr Vec2::operator Point() const noexcept { + return { + static_cast(x), + static_cast(y), + }; +} + +constexpr Vec2::operator Size() const noexcept { + return { + static_cast(x), + static_cast(y), + }; +} + +constexpr Point::operator Vec2() const noexcept { + return { + static_cast(x), + static_cast(y), + }; +} + +constexpr Size::operator Vec2() const noexcept { + return { + static_cast(width), + static_cast(height), + }; +} + +} \ No newline at end of file diff --git a/deps/ox/src/ox/std/iterator.hpp b/deps/ox/src/ox/std/iterator.hpp index a93e75c..b831060 100644 --- a/deps/ox/src/ox/std/iterator.hpp +++ b/deps/ox/src/ox/std/iterator.hpp @@ -128,6 +128,10 @@ struct SpanIterator { return operator-=(1); } + constexpr PtrType operator->() const noexcept { + return &m_t[m_offset]; + } + constexpr RefType operator*() const noexcept { return m_t[m_offset]; } diff --git a/deps/ox/src/ox/std/new.hpp b/deps/ox/src/ox/std/new.hpp index df4a827..e631d0c 100644 --- a/deps/ox/src/ox/std/new.hpp +++ b/deps/ox/src/ox/std/new.hpp @@ -102,6 +102,7 @@ class MallocaPtr { } constexpr ~MallocaPtr() noexcept { + m_val->~T(); if (m_onHeap && m_val) { delete[] reinterpret_cast(m_val); } diff --git a/deps/ox/src/ox/std/point.hpp b/deps/ox/src/ox/std/point.hpp index ca6c57f..94062e1 100644 --- a/deps/ox/src/ox/std/point.hpp +++ b/deps/ox/src/ox/std/point.hpp @@ -17,8 +17,8 @@ class Point { public: static constexpr auto TypeName = "net.drinkingtea.ox.Point"; static constexpr auto TypeVersion = 1; - int x = 0; - int y = 0; + int32_t x = 0; + int32_t y = 0; constexpr Point() noexcept = default; @@ -64,6 +64,7 @@ class Point { constexpr bool operator!=(const Point&) const noexcept; + explicit constexpr operator class Vec2() const noexcept; }; constexpr Point::Point(int x, int y) noexcept { diff --git a/deps/ox/src/ox/std/reader.cpp b/deps/ox/src/ox/std/reader.cpp index 5f21556..7733c2b 100644 --- a/deps/ox/src/ox/std/reader.cpp +++ b/deps/ox/src/ox/std/reader.cpp @@ -8,7 +8,7 @@ #ifdef OX_USE_STDLIB -#include +#include #include "array.hpp" #include "reader.hpp" @@ -16,41 +16,56 @@ namespace ox { [[nodiscard]] -constexpr int sdMap(ox::ios_base::seekdir in) noexcept { +constexpr std::ios_base::seekdir sdMap(ox::ios_base::seekdir in) noexcept { switch (in) { case ox::ios_base::beg: - return SEEK_SET; + return std::ios_base::beg; case ox::ios_base::end: - return SEEK_END; + return std::ios_base::end; case ox::ios_base::cur: - return SEEK_CUR; + return std::ios_base::cur; } - return -1; + return std::ios_base::beg; } -ox::Result FileReader::peek() const noexcept { - auto const c = fgetc(m_file); - auto const ok = c != EOF; - if (ok && ungetc(c, m_file)) [[unlikely]] { - return OxError(1, "Unable to unget character"); +ox::Result StreamReader::peek() const noexcept { + try { + char c{}; + m_strm.get(c); + auto const ok = c != EOF; + if (ok && m_strm.unget()) [[unlikely]] { + return OxError(1, "Unable to unget character"); + } + return {static_cast(c), OxError(!ok, "File peek failed")}; + } catch (std::exception const&) { + return OxError(1, "peek failed"); } - return {static_cast(c), OxError(!ok, "File peek failed")}; } -ox::Result FileReader::read(char *v, std::size_t cnt) noexcept { - return fread(v, 1, cnt, m_file); +ox::Result StreamReader::read(char *v, std::size_t cnt) noexcept { + return static_cast(m_strm.read(v, static_cast(cnt)).gcount()); } -ox::Error FileReader::seekg(std::size_t p) noexcept { - return OxError(fseek(m_file, static_cast(p), SEEK_CUR) != 0); +ox::Error StreamReader::seekg(std::size_t p) noexcept { + try { + m_strm.seekg(static_cast(p), std::ios_base::cur); + } catch (std::exception const&) { + return OxError(1, "seekg failed"); + } + return {}; } -ox::Error FileReader::seekg(int64_t p, ios_base::seekdir sd) noexcept { - return OxError(fseek(m_file, p, sdMap(sd)) != 0); +ox::Error StreamReader::seekg(int64_t p, ios_base::seekdir sd) noexcept { + try { + m_strm.seekg(p, sdMap(sd)); + } catch (std::exception const&) { + return OxError(1, "seekg failed"); + } + return {}; } -ox::Result FileReader::tellg() noexcept { - const auto sz = ftell(m_file); +ox::Result StreamReader::tellg() noexcept { + const auto sz = m_strm.tellg(); return {static_cast(sz), OxError(sz == -1)}; } diff --git a/deps/ox/src/ox/std/reader.hpp b/deps/ox/src/ox/std/reader.hpp index 73e9ea4..b633988 100644 --- a/deps/ox/src/ox/std/reader.hpp +++ b/deps/ox/src/ox/std/reader.hpp @@ -9,7 +9,7 @@ #pragma once #ifdef OX_USE_STDLIB -#include +#include #endif #include "concepts.hpp" @@ -65,11 +65,11 @@ class ReaderT: public Reader_v { }; #ifdef OX_USE_STDLIB -class FileReader: public Reader_v { +class StreamReader: public Reader_v { private: - FILE *m_file = nullptr; + std::istream &m_strm; public: - constexpr explicit FileReader(FILE *file) noexcept: m_file(file) {} + constexpr explicit StreamReader(std::istream &stream) noexcept: m_strm(stream) {} ox::Result peek() const noexcept override; ox::Result read(char *v, std::size_t cnt) noexcept override; ox::Error seekg(std::size_t p) noexcept override; diff --git a/deps/ox/src/ox/std/size.hpp b/deps/ox/src/ox/std/size.hpp index 3b03f53..6684008 100644 --- a/deps/ox/src/ox/std/size.hpp +++ b/deps/ox/src/ox/std/size.hpp @@ -17,8 +17,8 @@ class Size { public: static constexpr auto TypeName = "net.drinkingtea.ox.Size"; static constexpr auto TypeVersion = 1; - int width = 0; - int height = 0; + int32_t width = 0; + int32_t height = 0; constexpr Size() noexcept = default; @@ -64,6 +64,7 @@ class Size { constexpr bool operator!=(const Size&) const noexcept; + explicit constexpr operator class Vec2() const noexcept; }; constexpr Size::Size(int width, int height) noexcept { diff --git a/deps/ox/src/ox/std/smallmap.hpp b/deps/ox/src/ox/std/smallmap.hpp index 28f2d43..66871cf 100644 --- a/deps/ox/src/ox/std/smallmap.hpp +++ b/deps/ox/src/ox/std/smallmap.hpp @@ -8,7 +8,7 @@ #pragma once -#include "algorithm.hpp" +#include "def.hpp" #include "hash.hpp" #include "ignore.hpp" #include "stringview.hpp" @@ -28,7 +28,7 @@ class SmallMap { T value{}; }; - private: + protected: using PairVector = Vector; PairVector m_pairs; @@ -39,8 +39,6 @@ class SmallMap { constexpr SmallMap(SmallMap &&other) noexcept; - constexpr ~SmallMap(); - constexpr bool operator==(SmallMap const&other) const; constexpr SmallMap &operator=(SmallMap const&other); @@ -79,6 +77,11 @@ class SmallMap { [[nodiscard]] constexpr Pair &get(size_t i) noexcept; + [[nodiscard]] + constexpr ox::SpanView pairs() const noexcept { + return m_pairs; + } + constexpr void clear(); private: @@ -100,11 +103,6 @@ constexpr SmallMap::SmallMap(SmallMap &&other) noe m_pairs = std::move(other.m_pairs); } -template -constexpr SmallMap::~SmallMap() { - clear(); -} - template constexpr bool SmallMap::operator==(SmallMap const&other) const { return m_pairs == other.m_pairs; @@ -204,12 +202,12 @@ constexpr T &SmallMap::value(size_t i) noexcept { } template -constexpr SmallMap::Pair const&SmallMap::get(size_t i) const noexcept { +constexpr typename SmallMap::Pair const&SmallMap::get(size_t i) const noexcept { return m_pairs[i]; } template -constexpr SmallMap::Pair &SmallMap::get(size_t i) noexcept { +constexpr typename SmallMap::Pair &SmallMap::get(size_t i) noexcept { return m_pairs[i]; } @@ -246,4 +244,12 @@ constexpr typename SmallMap::Pair &SmallMap::acces return pairs.emplace_back(); } +template +constexpr Error model(T *io, ox::CommonPtrWith> auto *obj) noexcept { + using Map = SmallMap; + oxReturnError(io->template setTypeInfo()); + oxReturnError(io->field("pairs", &obj->m_pairs)); + return {}; +} + } diff --git a/deps/ox/src/ox/std/stacktrace.cpp b/deps/ox/src/ox/std/stacktrace.cpp index 8a23afc..acbfdb4 100644 --- a/deps/ox/src/ox/std/stacktrace.cpp +++ b/deps/ox/src/ox/std/stacktrace.cpp @@ -26,7 +26,7 @@ namespace ox { #if defined(OX_USE_STDLIB) && __has_include() -[[nodiscard]] +[[nodiscard]] [[maybe_unused]] static auto symbolicate([[maybe_unused]]void **frames, [[maybe_unused]]std::size_t frameCnt, [[maybe_unused]]const char *linePrefix) { diff --git a/deps/ox/src/ox/std/std.hpp b/deps/ox/src/ox/std/std.hpp index 6b95d6d..a88416d 100644 --- a/deps/ox/src/ox/std/std.hpp +++ b/deps/ox/src/ox/std/std.hpp @@ -16,6 +16,7 @@ #include "istring.hpp" #include "byteswap.hpp" #include "concepts.hpp" +#include "conv.hpp" #include "cstringview.hpp" #include "cstrops.hpp" #include "def.hpp" diff --git a/deps/ox/src/ox/std/string.cpp b/deps/ox/src/ox/std/string.cpp index dca59e0..a5c63ed 100644 --- a/deps/ox/src/ox/std/string.cpp +++ b/deps/ox/src/ox/std/string.cpp @@ -13,6 +13,7 @@ namespace ox { template class BasicString<8>; static_assert(StringView("Write") != String("")); +static_assert(ox::strcmp(String{}.c_str(), "\0") == 0); static_assert(String("Write") != StringView("")); static_assert(String("Write") == StringView("Write")); static_assert(String(StringView("Write")) == StringView("Write")); diff --git a/deps/ox/src/ox/std/string.hpp b/deps/ox/src/ox/std/string.hpp index 05d1122..324e243 100644 --- a/deps/ox/src/ox/std/string.hpp +++ b/deps/ox/src/ox/std/string.hpp @@ -193,7 +193,9 @@ class BasicString { constexpr BasicString substr(std::size_t begin, std::size_t end) const noexcept; constexpr void resize(size_t sz) noexcept { + ++sz; m_buff.resize(sz); + m_buff[sz - 1] = 0; } [[nodiscard]] @@ -248,38 +250,19 @@ class BasicString { template constexpr BasicString::BasicString() noexcept { - if (!m_buff.empty()) { - m_buff[0] = 0; - } else { - m_buff.push_back(0); - } + m_buff.resize(1); } template -constexpr BasicString::BasicString(std::size_t cap) noexcept: m_buff(cap + 1) { - // GCC complains if you don't do this pretty unnecessary size check - if (!m_buff.empty()) { - m_buff[0] = 0; - } -} +constexpr BasicString::BasicString(std::size_t cap) noexcept: m_buff(cap + 1) {} template constexpr BasicString::BasicString(const char *str) noexcept { - if (!m_buff.empty()) { - m_buff[0] = 0; - } else { - m_buff.push_back(0); - } set(str); } template constexpr BasicString::BasicString(const char8_t *str) noexcept { - if (!m_buff.empty()) { - m_buff[0] = 0; - } else { - m_buff.push_back(0); - } set(str); } @@ -287,21 +270,15 @@ template constexpr BasicString::BasicString(const char *str, std::size_t size) noexcept { m_buff.resize(size + 1); ox::listcpy(m_buff.data(), str, size); - m_buff[size] = 0; } template constexpr BasicString::BasicString(StringLiteral const&str) noexcept: - BasicString(StringView{str.data(), str.bytes()}) { + BasicString(StringView{str.data(), str.len()}) { } template constexpr BasicString::BasicString(CRStringView str) noexcept { - if (m_buff.empty()) { - m_buff.push_back(0); - } else { - m_buff[0] = 0; - } set(str); } @@ -312,6 +289,8 @@ constexpr BasicString::BasicString(const BasicString &other) template constexpr BasicString::BasicString(BasicString &&other) noexcept: m_buff(std::move(other.m_buff)) { + other.m_buff.resize(1); + other.m_buff[0] = 0; } template @@ -357,6 +336,8 @@ template constexpr BasicString &BasicString::operator=(BasicString &&src) noexcept { if (this != &src) { m_buff = std::move(src.m_buff); + src.m_buff.resize(1); + src.m_buff[0] = 0; } return *this; } diff --git a/deps/ox/src/ox/std/test/tests.cpp b/deps/ox/src/ox/std/test/tests.cpp index e0ad716..1a470b2 100644 --- a/deps/ox/src/ox/std/test/tests.cpp +++ b/deps/ox/src/ox/std/test/tests.cpp @@ -18,12 +18,12 @@ #include [[nodiscard]] -static uint64_t nowMs() { +static uint64_t steadyNowMs() { #if __has_include() using namespace std::chrono; return static_cast( duration_cast( - system_clock::now().time_since_epoch()).count()); + steady_clock::now().time_since_epoch()).count()); #else return 0; #endif @@ -41,13 +41,13 @@ uint64_t timeMapStrToUuid(int elemCnt, int lookups, uint64_t seed = 4321) noexce auto const keys = map.keys(); ox::Random rand; // start - auto const startTime = nowMs(); + auto const startTime = steadyNowMs(); for (int i = 0; i < lookups; ++i) { auto const&k = keys[rand.gen() % keys.size()]; map[k]; oxExpect(map[k], ox::UUID::fromString(k).unwrap()); } - return nowMs() - startTime; + return steadyNowMs() - startTime; } template> @@ -62,16 +62,16 @@ uint64_t timeMapUuidToStr(int elemCnt, int lookups, uint64_t seed = 4321) noexce auto const keys = map.keys(); ox::Random rand; // start - auto const startTime = nowMs(); + auto const startTime = steadyNowMs(); for (int i = 0; i < lookups; ++i) { auto const&k = keys[rand.gen() % keys.size()]; oxExpect(map[k], k.toString()); } - return nowMs() - startTime; + return steadyNowMs() - startTime; } static ox::Error compareMaps(int lookupCnt = 1'000'000) { - auto const seed = nowMs(); + auto const seed = steadyNowMs(); uint64_t hashTime{}; uint64_t smallTime{}; int elemCnt = 1; diff --git a/deps/ox/src/ox/std/typetraits.hpp b/deps/ox/src/ox/std/typetraits.hpp index 3bfee6a..9207bab 100644 --- a/deps/ox/src/ox/std/typetraits.hpp +++ b/deps/ox/src/ox/std/typetraits.hpp @@ -185,6 +185,8 @@ struct enable_if { using type = T; }; +template +using enable_if_t = typename enable_if::type; template struct is_pointer { diff --git a/deps/ox/src/ox/std/vec.hpp b/deps/ox/src/ox/std/vec.hpp index 1d22c69..22a78ad 100644 --- a/deps/ox/src/ox/std/vec.hpp +++ b/deps/ox/src/ox/std/vec.hpp @@ -23,17 +23,16 @@ namespace ox { -template -struct Vec { +class Vec2 { public: - using value_type = T; + using value_type = float; using size_type = std::size_t; static constexpr auto TypeName = "net.drinkingtea.ox.Point"; static constexpr auto TypeVersion = 1; - T x = 0; - T y = 0; + float x = 0; + float y = 0; template struct iterator: public ox::Iterator { @@ -141,14 +140,14 @@ struct Vec { }; - constexpr Vec() noexcept = default; + constexpr Vec2() noexcept = default; template - constexpr Vec(T pX, T pY) noexcept: x(pX), y(pY) { + constexpr Vec2(float pX, float pY) noexcept: x(pX), y(pY) { } #if __has_include() - explicit constexpr Vec(const ImVec2 &v) noexcept: Vec(v.x, v.y) { + explicit constexpr Vec2(const ImVec2 &v) noexcept: Vec2(v.x, v.y) { } explicit inline operator ImVec2() const noexcept { @@ -228,7 +227,7 @@ struct Vec { } } - constexpr auto operator==(const Vec &v) const noexcept { + constexpr auto operator==(const Vec2 &v) const noexcept { for (auto i = 0u; i < v.size(); ++i) { if ((*this)[i] != v[i]) { return false; @@ -237,29 +236,71 @@ struct Vec { return true; } - constexpr auto operator!=(const Vec &v) const noexcept { + constexpr auto operator!=(const Vec2 &v) const noexcept { return !operator==(v); } + explicit constexpr operator class Point() const noexcept; + + explicit constexpr operator class Size() const noexcept; + [[nodiscard]] constexpr std::size_t size() const noexcept { return 2; } + constexpr Vec2 operator+(float i) const noexcept { + return {x + i, y + i}; + } + + constexpr Vec2 operator+=(float i) noexcept { + x += i; + y += i; + return *this; + } + + constexpr Vec2 operator-(float i) const noexcept { + return {x - i, y - i}; + } + + constexpr Vec2 operator-=(float i) noexcept { + x -= i; + y -= i; + return *this; + } + + constexpr Vec2 operator*(float i) const noexcept { + return {x * i, y * i}; + } + + constexpr Vec2 operator*=(float i) noexcept { + x *= i; + y *= i; + return *this; + } + + constexpr Vec2 operator/(float i) const noexcept { + return {x / i, y / i}; + } + + constexpr Vec2 operator/=(float i) noexcept { + x /= i; + y /= i; + return *this; + } + protected: [[nodiscard]] - constexpr T *start() noexcept { - return &x; + constexpr float *start() noexcept { + return&x; } [[nodiscard]] - constexpr const T *start() const noexcept { + constexpr const float *start() const noexcept { return &x; } }; -using Vec2 = Vec; - template constexpr Error model(T *io, ox::CommonPtrWith auto *obj) noexcept { oxReturnError(io->template setTypeInfo()); diff --git a/deps/ox/src/ox/std/vector.hpp b/deps/ox/src/ox/std/vector.hpp index 5787db6..a3f763d 100644 --- a/deps/ox/src/ox/std/vector.hpp +++ b/deps/ox/src/ox/std/vector.hpp @@ -271,7 +271,7 @@ class Vector: detail::VectorAllocator { constexpr bool contains(MaybeView_t const&) const noexcept; constexpr iterator insert( - std::size_t pos, std::size_t cnt, T val) noexcept(useNoexcept); + std::size_t pos, std::size_t cnt, T const&val) noexcept(useNoexcept); constexpr iterator insert(std::size_t pos, T val) noexcept(useNoexcept); @@ -531,29 +531,23 @@ constexpr bool Vector::contains(MaybeView_t co template constexpr typename Vector::template iterator Vector::insert( - std::size_t pos, std::size_t cnt, T val) noexcept(useNoexcept) { + std::size_t pos, std::size_t cnt, T const&val) noexcept(useNoexcept) { if (m_size + cnt > m_cap) { reserveInsert(m_cap ? m_size + cnt : initialCap, pos, cnt); - if (pos < m_size) { - m_items[pos] = std::move(val); - } else { - for (auto i = 0u; i < cnt; ++i) { - std::construct_at(&m_items[pos + i], m_items[pos]); - } + } + if (pos < m_size) { + for (auto i = m_size + cnt - 1; i > pos; --i) { + std::construct_at(&m_items[i], std::move(m_items[i - cnt])); + } + for (auto i = pos; i < pos + cnt; ++i) { + m_items[i] = val; } } else { - if (pos < m_size) { - for (auto i = m_size + cnt - 1; i > pos; --i) { - std::construct_at(&m_items[i], std::move(m_items[i - cnt])); - } - m_items[pos] = std::move(val); - } else { - for (auto i = 0u; i < cnt; ++i) { - std::construct_at(&m_items[pos + i], m_items[pos]); - } + for (auto i = 0u; i < cnt; ++i) { + std::construct_at(&m_items[pos + i], m_items[pos]); } } - ++m_size; + m_size += cnt; return begin() + pos; } @@ -562,20 +556,14 @@ constexpr typename Vector::template iterator::insert(std::size_t pos, T val) noexcept(useNoexcept) { if (m_size == m_cap) { reserveInsert(m_cap ? m_cap * 2 : initialCap, pos); - if (pos < m_size) { - m_items[pos] = std::move(val); - } else { - std::construct_at(&m_items[pos], m_items[pos]); + } + if (pos < m_size) { + for (auto i = m_size; i > pos; --i) { + std::construct_at(&m_items[i], std::move(m_items[i - 1])); } + m_items[pos] = std::move(val); } else { - if (pos < m_size) { - for (auto i = m_size; i > pos; --i) { - std::construct_at(&m_items[i], std::move(m_items[i - 1])); - } - m_items[pos] = std::move(val); - } else { - std::construct_at(&m_items[pos], m_items[pos]); - } + std::construct_at(&m_items[pos], std::move(val)); } ++m_size; return begin() + pos; diff --git a/developer-handbook.md b/developer-handbook.md index 58ebe61..1bb1097 100644 --- a/developer-handbook.md +++ b/developer-handbook.md @@ -162,18 +162,10 @@ classes in question. ### Error Handling -Exceptions are clean and nice in userland code running in environments with -expansive system resources, but they are a bit of a pain in small bare metal -environments. -The GBA build has them disabled. -Exceptions cause also the compiler to generate a great deal of extra code that -inflates the size of the binary. -The binary size bloat is often cited as one of the main reasons why many -embedded developers prefer C to C++. - -Instead of throwing exceptions, all engine code must return Ox error codes. -For the sake of consistency, try to stick to Ox error codes in non-engine code -as well. +The GBA build has exceptions disabled. +Instead of throwing exceptions, all engine code must return ```ox::Error```s. +For the sake of consistency, try to stick to ```ox::Error``` in non-engine code +as well, but non-engine code is free to use exceptions when they make sense. Nostalgia and Ox both use ```ox::Error``` to report errors. ```ox::Error``` is a struct that has overloaded operators to behave like an integer error code, plus some extra fields to enhance debuggability. @@ -219,6 +211,30 @@ int caller2() { std::cout << val << '\n'; return 0; } + +ox::Error caller3(int &i) { + return foo(i).moveTo(i); +} + +ox::Error caller4(int &i) { + return foo(i).copyTo(i); +} + +int caller5(int i) { + return foo(i).unwrap(); // unwrap will kill the program if there is an error +} + +int caller6(int i) { + return foo(i).unwrapThrow(); // unwrap will throw if there is an error +} + +int caller7(int i) { + return foo(i).or_value(0); // will return 0 if foo returned an error +} + +ox::Result caller8(int i) { + return foo(i).to(); // will convert the result of foo to uint64_t +} ``` Lastly, there are a few macros available to help in passing ```ox::Error```s @@ -275,6 +291,7 @@ ox::Error engineCode() noexcept { return OxError(0); } ``` + Ox also has the ```oxRequire``` macro, which will initialize a value if there is no error, and return if there is. It aims to somewhat emulate the ```?``` operator in Rust and Swift. @@ -309,6 +326,9 @@ ox::Result f2() noexcept { * ```oxRequireT``` - oxRequire Throw * ```oxRequireMT``` - oxRequire Mutable Throw +The throw variants of ```oxRequire``` are generally legacy code. +```ox::Result::unwrapThrow``` is generally preferred now. + ### Logging and Output Ox provides for logging and debug prints via the ```oxTrace```, ```oxDebug```, and ```oxError``` macros. @@ -526,19 +546,13 @@ a wrapper around the bare formats. ```cpp #include -ox::Result loadPalette1(const Buffer &buff) noexcept { +ox::Result loadPalette1(ox::BufferView const&buff) noexcept { return ox::readMC(buff); } -ox::Result loadPalette2(const Buffer &buff) noexcept { - return ox::readMC(buff.data(), buff.size()); -} - -ox::Result loadPalette3(const Buffer &buff) noexcept { +ox::Result loadPalette2(ox::BufferView const&buff) noexcept { NostalgiaPalette pal; - std::size_t sz = 0; - oxReturnError(ox::readMC(buff.data(), buff.size(), &pal, &sz)); - buffer.resize(sz); + oxReturnError(ox::readMC(buff, pal)); return pal; } ``` @@ -548,7 +562,7 @@ ox::Result loadPalette3(const Buffer &buff) noexcept { ```cpp #include -ox::Result writeSpritePalette1(NostalgiaPalette *pal) noexcept { +ox::Result writeSpritePalette1(NostalgiaPalette const&pal) noexcept { ox::Buffer buffer(ox::units::MB); std::size_t sz = 0; oxReturnError(ox::writeMC(buffer.data(), buffer.size(), pal, &sz)); @@ -556,7 +570,7 @@ ox::Result writeSpritePalette1(NostalgiaPalette *pal) noexcept { return buffer; } -ox::Result writeSpritePalette2(NostalgiaPalette *pal) noexcept { +ox::Result writeSpritePalette2(NostalgiaPalette const&pal) noexcept { return ox::writeMC(pal); } ``` @@ -568,17 +582,13 @@ ox::Result writeSpritePalette2(NostalgiaPalette *pal) noexcept { ```cpp #include -ox::Result loadPalette1(const Buffer &buff) noexcept { +ox::Result loadPalette1(ox::BufferView const&buff) noexcept { return ox::readOC(buff); } -ox::Result loadPalette2(const Buffer &buff) noexcept { - return ox::readOC(buff.data(), buff.size()); -} - -ox::Result loadPalette3(const Buffer &buff) noexcept { +ox::Result loadPalette2(ox::BufferView const&buff) noexcept { NostalgiaPalette pal; - oxReturnError(ox::readOC(buff.data(), buff.size(), &pal)); + oxReturnError(ox::readOC(buff, &pal)); return pal; } ``` @@ -588,13 +598,13 @@ ox::Result loadPalette3(const Buffer &buff) noexcept { ```cpp #include -ox::Result writeSpritePalette1(NostalgiaPalette *pal) noexcept { +ox::Result writeSpritePalette1(NostalgiaPalette const&pal) noexcept { ox::Buffer buffer(ox::units::MB); oxReturnError(ox::writeOC(buffer.data(), buffer.size(), pal)); return buffer; } -ox::Result writeSpritePalette2(NostalgiaPalette *pal) noexcept { +ox::Result writeSpritePalette2(NostalgiaPalette const&pal) noexcept { return ox::writeOC(pal); } ``` @@ -606,17 +616,13 @@ ox::Result writeSpritePalette2(NostalgiaPalette *pal) noexcept { ```cpp #include -ox::Result loadPalette1(const Buffer &buff) noexcept { +ox::Result loadPalette1(ox::BufferView const&buff) noexcept { return ox::readClaw(buff); } -ox::Result loadPalette2(const Buffer &buff) noexcept { - return ox::readClaw(buff.data(), buff.size()); -} - -ox::Result loadPalette3(const Buffer &buff) noexcept { +ox::Result loadPalette2(ox::BufferView const&buff) noexcept { NostalgiaPalette pal; - oxReturnError(ox::readClaw(buff.data(), buff.size(), &pal)); + oxReturnError(ox::readClaw(buff, pal)); return pal; } ``` @@ -626,8 +632,8 @@ ox::Result loadPalette3(const Buffer &buff) noexcept { ```cpp #include -ox::Result writeSpritePalette(NostalgiaPalette *pal) noexcept { - return ox::writeClaw(&pal); +ox::Result writeSpritePalette(NostalgiaPalette const&pal) noexcept { + return ox::writeClaw(pal); } ``` diff --git a/src/nostalgia/modules/core/include/nostalgia/core/color.hpp b/src/nostalgia/modules/core/include/nostalgia/core/color.hpp index b780672..c6b73ad 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/color.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/color.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include namespace nostalgia::core { @@ -135,7 +136,7 @@ constexpr Color16 color16(int r, int g, int b, int a = 0) noexcept { return static_cast(ox::min(static_cast(r), 31)) | static_cast(ox::min(static_cast(g), 31) << 5) | static_cast(ox::min(static_cast(b), 31) << 10) - | static_cast(a << 15); + | static_cast(a << 15); } [[nodiscard]] @@ -151,4 +152,13 @@ static_assert(color16(16, 31, 0) == 1008); static_assert(color16(16, 31, 8) == 9200); static_assert(color16(16, 32, 8) == 9200); +[[nodiscard]] +constexpr Color16 applySelectionColor(Color16 const color) noexcept { + namespace core = nostalgia::core; + auto const r = core::red16(color) / 2; + auto const g = (core::green16(color) + 20) / 2; + auto const b = (core::blue16(color) + 31) / 2; + return core::color16(r, g, b); +} + } diff --git a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp index ca01738..37974d6 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp @@ -103,15 +103,21 @@ oxModelEnd() void addEntry(TileSheetSet &set, ox::FileAddress path, int32_t begin = 0, int32_t size = -1) noexcept; +[[nodiscard]] +int tileColumns(Context&) noexcept; + +[[nodiscard]] +int tileRows(Context&) noexcept; + ox::Error loadBgPalette( Context &ctx, size_t palBank, - Palette const&palette, + CompactPalette const&palette, size_t page = 0) noexcept; ox::Error loadSpritePalette( Context &ctx, - Palette const&palette, + CompactPalette const&palette, size_t page = 0) noexcept; ox::Error loadBgPalette( diff --git a/src/nostalgia/modules/core/include/nostalgia/core/palette.hpp b/src/nostalgia/modules/core/include/nostalgia/core/palette.hpp index 72f9785..454c874 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/palette.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/palette.hpp @@ -36,7 +36,43 @@ struct PaletteV2 { ox::Vector> pages; }; -using Palette = PaletteV2; +struct PaletteV3 { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette"; + static constexpr auto TypeVersion = 3; + static constexpr auto Preloadable = true; + struct ColorInfo { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette.ColorInfo"; + static constexpr auto TypeVersion = 3; + ox::String name; + constexpr ColorInfo() noexcept = default; + constexpr explicit ColorInfo(ox::StringView pName) noexcept: + name(pName) {} + constexpr explicit ColorInfo(ox::String &&pName) noexcept: + name(std::move(pName)) {} + }; + ox::Vector colorInfo; + ox::Vector> pages; +}; + +using Palette = PaletteV3; + + +struct CompactPaletteV1 { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette"; + static constexpr auto TypeVersion = 2; + static constexpr auto Preloadable = true; + ox::Vector> pages{}; +}; + +using CompactPalette = CompactPaletteV1; + +[[nodiscard]] +constexpr bool valid(Palette const&p) noexcept { + auto const colors = p.colorInfo.size(); + return ox::all_of(p.pages.begin(), p.pages.end(), [colors](auto const&page) { + return page.size() == colors; + }); +} [[nodiscard]] constexpr Color16 color(Palette const&pal, size_t page, size_t idx) noexcept { @@ -47,13 +83,53 @@ constexpr Color16 color(Palette const&pal, size_t page, size_t idx) noexcept { } [[nodiscard]] -constexpr Color16 color(Palette const&pal, size_t idx) noexcept { - auto constexpr page = 0; - return color(pal, page, idx); +constexpr Color16 color(CompactPalette const&pal, size_t page, size_t idx) noexcept { + if (page < pal.pages.size() && idx < pal.pages[page].size()) [[likely]] { + return pal.pages[page][idx]; + } + return 0; } [[nodiscard]] -constexpr size_t colors(Palette const&pal, size_t page = 0) noexcept { +constexpr Color16 color(Palette const&pal, size_t idx) noexcept { + return color(pal, 0, idx); +} + +[[nodiscard]] +constexpr Color16 color(CompactPalette const&pal, size_t idx) noexcept { + return color(pal, 0, idx); +} + +[[nodiscard]] +constexpr auto &colors(Palette &pal, size_t page = 0) noexcept { + return pal.pages[page]; +} + +[[nodiscard]] +constexpr auto &colors(CompactPalette &pal, size_t page = 0) noexcept { + return pal.pages[page]; +} + +[[nodiscard]] +constexpr auto &colors(Palette const&pal, size_t page = 0) noexcept { + return pal.pages[page]; +} + +[[nodiscard]] +constexpr auto &colors(CompactPalette const&pal, size_t page = 0) noexcept { + return pal.pages[page]; +} + +[[nodiscard]] +constexpr size_t colorCnt(Palette const&pal, size_t page = 0) noexcept { + if (page < pal.pages.size()) [[likely]] { + return pal.pages[page].size(); + } + return 0; +} + +[[nodiscard]] +constexpr size_t colorCnt(CompactPalette const&pal, size_t page = 0) noexcept { if (page < pal.pages.size()) [[likely]] { return pal.pages[page].size(); } @@ -69,6 +145,15 @@ constexpr size_t largestPage(Palette const&pal) noexcept { return out; } +[[nodiscard]] +constexpr size_t largestPage(CompactPalette const&pal) noexcept { + size_t out{}; + for (auto const&page : pal.pages) { + out = ox::max(out, page.size()); + } + return out; +} + oxModelBegin(NostalgiaPalette) oxModelField(colors) oxModelEnd() @@ -81,4 +166,17 @@ oxModelBegin(PaletteV2) oxModelField(pages) oxModelEnd() +oxModelBegin(PaletteV3::ColorInfo) + oxModelField(name) +oxModelEnd() + +oxModelBegin(PaletteV3) + oxModelField(colorInfo) + oxModelField(pages) +oxModelEnd() + +oxModelBegin(CompactPaletteV1) + oxModelField(pages) +oxModelEnd() + } diff --git a/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp b/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp index 7a673d8..5aa926b 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp @@ -222,6 +222,14 @@ ox::Error setPixelCount(TileSheet::SubSheet &ss, int8_t pBpp, std::size_t cnt) n [[nodiscard]] unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept; +/** + * + * @param ss + * @param pBpp + * @param sz size of Subsheet in tiles (not pixels) + */ +ox::Error resizeSubsheet(TileSheet::SubSheet &ss, int8_t pBpp, ox::Size const&sz) noexcept; + /** * validateSubSheetIdx takes a SubSheetIdx and moves the index to the * preceding or parent sheet if the current corresponding sheet does diff --git a/src/nostalgia/modules/core/src/gba/gfx.cpp b/src/nostalgia/modules/core/src/gba/gfx.cpp index e2404fd..fb73866 100644 --- a/src/nostalgia/modules/core/src/gba/gfx.cpp +++ b/src/nostalgia/modules/core/src/gba/gfx.cpp @@ -22,8 +22,6 @@ namespace nostalgia::core { -constexpr auto GbaTileColumns = 32; -constexpr auto GbaTileRows = 32; constexpr auto SpriteCount = 128; struct GbaTileMapTarget { @@ -138,13 +136,13 @@ ox::Error initGfx(Context&, InitParams const&) noexcept { ox::Error loadBgPalette( Context&, size_t palBank, - Palette const&palette, + CompactPalette const&palette, size_t page) noexcept { if (palette.pages.empty()) { return {}; } auto const paletteMem = MEM_BG_PALETTE + palBank * 16; - for (auto i = 0u; i < colors(palette, page); ++i) { + for (auto i = 0u; i < colorCnt(palette, page); ++i) { paletteMem[i] = color(palette, page, i); } return {}; @@ -152,13 +150,13 @@ ox::Error loadBgPalette( ox::Error loadSpritePalette( Context&, - Palette const&palette, + CompactPalette const&palette, size_t page) noexcept { if (palette.pages.empty()) { return {}; } auto const paletteMem = MEM_SPRITE_PALETTE; - for (auto i = 0u; i < colors(palette, page); ++i) { + for (auto i = 0u; i < colorCnt(palette, page); ++i) { paletteMem[i] = color(palette, page, i); } return {}; @@ -168,14 +166,14 @@ ox::Error loadBgPalette( Context &ctx, size_t palBank, ox::FileAddress const&paletteAddr) noexcept { - oxRequire(pal, keel::readObj(keelCtx(ctx), paletteAddr)); + oxRequire(pal, keel::readObj(keelCtx(ctx), paletteAddr)); return loadBgPalette(ctx, palBank, *pal, 0); } ox::Error loadSpritePalette( Context &ctx, ox::FileAddress const&paletteAddr) noexcept { - oxRequire(pal, keel::readObj(keelCtx(ctx), paletteAddr)); + oxRequire(pal, keel::readObj(keelCtx(ctx), paletteAddr)); return loadSpritePalette(ctx, *pal, 0); } @@ -284,8 +282,8 @@ ox::Error loadSpriteTileSheet( return {}; } -void setBgTile(Context&, uint_t bgIdx, int column, int row, BgTile const&tile) noexcept { - auto const tileIdx = static_cast(row * GbaTileColumns + column); +void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, BgTile const&tile) noexcept { + auto const tileIdx = static_cast(row * tileColumns(ctx) + column); // see Tonc 9.3 MEM_BG_MAP[bgIdx][tileIdx] = static_cast(tile.tileIdx & 0b1'1111'1111) | @@ -294,8 +292,8 @@ void setBgTile(Context&, uint_t bgIdx, int column, int row, BgTile const&tile) n static_cast(tile.palBank << 0xc); } -void clearBg(Context&, uint_t bgIdx) noexcept { - memset(MEM_BG_MAP[bgIdx].data(), 0, GbaTileRows * GbaTileColumns); +void clearBg(Context &ctx, uint_t bgIdx) noexcept { + memset(MEM_BG_MAP[bgIdx].data(), 0, static_cast(tileRows(ctx) * tileColumns(ctx))); } uint8_t bgStatus(Context&) noexcept { diff --git a/src/nostalgia/modules/core/src/gfx.cpp b/src/nostalgia/modules/core/src/gfx.cpp index 2b85f80..3c941f8 100644 --- a/src/nostalgia/modules/core/src/gfx.cpp +++ b/src/nostalgia/modules/core/src/gfx.cpp @@ -6,6 +6,17 @@ namespace nostalgia::core { +constexpr auto GbaTileColumns = 32; +constexpr auto GbaTileRows = 32; + +int tileColumns(Context&) noexcept { + return GbaTileColumns; +} + +int tileRows(Context&) noexcept { + return GbaTileRows; +} + // map ASCII values to the nostalgia charset constexpr ox::Array charMap = { 0, diff --git a/src/nostalgia/modules/core/src/keel/keelmodule.cpp b/src/nostalgia/modules/core/src/keel/keelmodule.cpp index 140dfa7..ff457c4 100644 --- a/src/nostalgia/modules/core/src/keel/keelmodule.cpp +++ b/src/nostalgia/modules/core/src/keel/keelmodule.cpp @@ -18,6 +18,8 @@ static class: public keel::Module { private: NostalgiaPaletteToPaletteV1Converter m_nostalgiaPaletteToPaletteV1Converter; PaletteV1ToPaletteV2Converter m_paletteV1ToPaletteV2Converter; + PaletteV2ToPaletteV3Converter m_paletteV2ToPaletteV3Converter; + PaletteToCompactPaletteConverter m_paletteToCompactPaletteConverter; TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter; TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetV3Converter; TileSheetV3ToTileSheetV4Converter m_tileSheetV3ToTileSheetV4Converter; @@ -47,6 +49,8 @@ static class: public keel::Module { return { &m_nostalgiaPaletteToPaletteV1Converter, &m_paletteV1ToPaletteV2Converter, + &m_paletteV2ToPaletteV3Converter, + &m_paletteToCompactPaletteConverter, &m_tileSheetV1ToTileSheetV2Converter, &m_tileSheetV2ToTileSheetV3Converter, &m_tileSheetV3ToTileSheetV4Converter, @@ -71,8 +75,10 @@ static class: public keel::Module { }, [](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result { if (typeId == ox::ModelTypeId_v || - typeId == ox::ModelTypeId_v) { - oxReturnError(keel::convertBuffToBuff( + typeId == ox::ModelTypeId_v || + typeId == ox::ModelTypeId_v || + typeId == ox::ModelTypeId_v) { + oxReturnError(keel::convertBuffToBuff( ctx, buff, ox::ClawFormat::Metal).moveTo(buff)); return true; } diff --git a/src/nostalgia/modules/core/src/keel/typeconv.cpp b/src/nostalgia/modules/core/src/keel/typeconv.cpp index 7df8d0a..6128732 100644 --- a/src/nostalgia/modules/core/src/keel/typeconv.cpp +++ b/src/nostalgia/modules/core/src/keel/typeconv.cpp @@ -22,6 +22,27 @@ ox::Error PaletteV1ToPaletteV2Converter::convert( return {}; } +ox::Error PaletteV2ToPaletteV3Converter::convert( + keel::Context&, + PaletteV2 &src, + PaletteV3 &dst) const noexcept { + dst.pages = std::move(src.pages); + if (!dst.pages.empty()) { + for (size_t i = 0; i < dst.pages[0].size(); ++i) { + dst.colorInfo.emplace_back(ox::sfmt("Color {}", i)); + } + } + return {}; +} + +ox::Error PaletteToCompactPaletteConverter::convert( + keel::Context&, + Palette &src, + CompactPalette &dst) const noexcept { + dst.pages = std::move(src.pages); + return {}; +} + ox::Error TileSheetV1ToTileSheetV2Converter::convert( keel::Context&, TileSheetV1 &src, diff --git a/src/nostalgia/modules/core/src/keel/typeconv.hpp b/src/nostalgia/modules/core/src/keel/typeconv.hpp index b7b4772..30347b1 100644 --- a/src/nostalgia/modules/core/src/keel/typeconv.hpp +++ b/src/nostalgia/modules/core/src/keel/typeconv.hpp @@ -21,7 +21,15 @@ class NostalgiaPaletteToPaletteV1Converter: public keel::Converter { - ox::Error convert(keel::Context&, PaletteV1 &src, PaletteV2 &dst) const noexcept final; + ox::Error convert(keel::Context&, PaletteV1 &src, PaletteV2 &dst) const noexcept final; +}; + +class PaletteV2ToPaletteV3Converter: public keel::Converter { + ox::Error convert(keel::Context&, PaletteV2 &src, PaletteV3 &dst) const noexcept final; +}; + +class PaletteToCompactPaletteConverter: public keel::Converter { + ox::Error convert(keel::Context&, Palette &src, CompactPalette &dst) const noexcept final; }; class TileSheetV1ToTileSheetV2Converter: public keel::Converter { diff --git a/src/nostalgia/modules/core/src/opengl/gfx.cpp b/src/nostalgia/modules/core/src/opengl/gfx.cpp index 5a220c3..19f77a4 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.cpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.cpp @@ -364,7 +364,7 @@ static void loadPalette( ox::Array &palette, size_t palOffset, GLuint shaderPgrm, - Palette const&pal, + CompactPalette const&pal, size_t page = 0) noexcept { static constexpr std::size_t ColorCnt = 256; for (auto i = palOffset; auto const c : pal.pages[page]) { @@ -385,7 +385,8 @@ static void setSprite( uint_t const idx, Sprite const&s) noexcept { // Tonc Table 8.4 - static constexpr ox::Array, 12> dimensions{ + struct Sz { uint_t x{}, y{}; }; + static constexpr ox::Array dimensions{ // col 0 {1, 1}, // 0, 0 {2, 2}, // 0, 1 @@ -515,7 +516,7 @@ static ox::Result normalizeTileSheet( ox::Error loadBgPalette( Context &ctx, size_t palBank, - Palette const&palette, + CompactPalette const&palette, size_t page) noexcept { renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, palette, page); return {}; @@ -523,7 +524,7 @@ ox::Error loadBgPalette( ox::Error loadSpritePalette( Context &ctx, - Palette const&palette, + CompactPalette const&palette, size_t page) noexcept { ox::Array pal; renderer::loadPalette(pal, 0, ctx.spriteShader, palette, page); @@ -535,7 +536,7 @@ ox::Error loadBgPalette( size_t palBank, ox::FileAddress const&paletteAddr) noexcept { auto &kctx = keelCtx(ctx.turbineCtx); - oxRequire(palette, readObj(kctx, paletteAddr)); + oxRequire(palette, readObj(kctx, paletteAddr)); renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, *palette); return {}; } @@ -544,7 +545,7 @@ ox::Error loadSpritePalette( Context &ctx, ox::FileAddress const&paletteAddr) noexcept { auto &kctx = keelCtx(ctx.turbineCtx); - oxRequire(palette, readObj(kctx, paletteAddr)); + oxRequire(palette, readObj(kctx, paletteAddr)); ox::Array pal; renderer::loadPalette(pal, 0, ctx.spriteShader, *palette); return {}; diff --git a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.cpp b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.cpp index 2abae5a..52bbd23 100644 --- a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.cpp +++ b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.cpp @@ -15,11 +15,16 @@ namespace nostalgia::core { -PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::CRStringView path): - Editor(path), - m_sctx(sctx), - m_tctx(sctx.tctx), - m_pal(*keel::readObj(keelCtx(m_tctx), ox::FileAddress(itemPath())).unwrapThrow()) { +namespace ig = studio::ig; + +PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::StringView const path): + Editor(path), + m_sctx(sctx), + m_tctx(sctx.tctx), + m_pal(*keel::readObj(keelCtx(m_tctx), itemPath()).unwrapThrow()) { + if (!valid(m_pal)) { + throw OxException(1, "PaletteEditorImGui: invalid Palette object"); + } undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand); } @@ -36,10 +41,9 @@ void PaletteEditorImGui::keyStateChanged(turbine::Key key, bool down) { if (turbine::buttonDown(m_tctx, turbine::Key::Mod_Alt)) { m_selectedColorRow = ox::min( - static_cast(key - turbine::Key::Num_1 + 9), m_pal.pages.size() - 1); + static_cast(key - turbine::Key::Num_1 + 9), m_pal.pages.size() - 1); } } - } void PaletteEditorImGui::draw(studio::StudioContext&) noexcept { @@ -58,7 +62,12 @@ void PaletteEditorImGui::draw(studio::StudioContext&) noexcept { } ox::Error PaletteEditorImGui::saveItem() noexcept { - return m_sctx.project->writeObj(itemPath(), m_pal); + return m_sctx.project->writeObj(itemPath(), m_pal, ox::ClawFormat::Organic); +} + +void PaletteEditorImGui::drawColumnLeftAlign(ox::CStringView txt) noexcept { + ImGui::TableNextColumn(); + ImGui::Text("%s", txt.c_str()); } void PaletteEditorImGui::drawColumn(ox::CStringView txt) noexcept { @@ -71,42 +80,39 @@ void PaletteEditorImGui::drawColumn(ox::CStringView txt) noexcept { void PaletteEditorImGui::drawColorsEditor() noexcept { constexpr auto tableFlags = ImGuiTableFlags_RowBg; auto const colorsSz = ImGui::GetContentRegionAvail(); - auto const colorEditor = m_selectedColorRow < colors(m_pal, m_page); + auto const colorEditor = m_selectedColorRow < colorCnt(m_pal, m_page); auto const colorEditorWidth = 220; static constexpr auto toolbarHeight = 40; { auto const sz = ImVec2(70, 24); if (ImGui::Button("Add", sz)) { - auto const colorSz = static_cast(colors(m_pal, m_page)); + auto const colorSz = colorCnt(m_pal, m_page); constexpr Color16 c = 0; - undoStack()->push(ox::make_unique(&m_pal, c, m_page, colorSz)); + std::ignore = pushCommand(m_pal, c, colorSz); } ImGui::SameLine(); - ImGui::BeginDisabled(m_selectedColorRow >= colors(m_pal, m_page)); + ImGui::BeginDisabled(m_selectedColorRow >= colorCnt(m_pal, m_page)); { if (ImGui::Button("Remove", sz)) { - undoStack()->push( - ox::make_unique( - &m_pal, - color(m_pal, m_page, static_cast(m_selectedColorRow)), - m_page, - static_cast(m_selectedColorRow))); - m_selectedColorRow = ox::min(colors(m_pal, m_page) - 1, m_selectedColorRow); + std::ignore = pushCommand(m_pal, m_selectedColorRow); + m_selectedColorRow = ox::min(colorCnt(m_pal, m_page) - 1, m_selectedColorRow); } ImGui::SameLine(); ImGui::BeginDisabled(m_selectedColorRow <= 0); { if (ImGui::Button("Move Up", sz)) { - undoStack()->push(ox::make_unique(&m_pal, m_page, m_selectedColorRow, -1)); + std::ignore = pushCommand( + m_pal, m_page, m_selectedColorRow, m_selectedColorRow - 1); --m_selectedColorRow; } } ImGui::EndDisabled(); ImGui::SameLine(); - ImGui::BeginDisabled(m_selectedColorRow >= colors(m_pal, m_page) - 1); + ImGui::BeginDisabled(m_selectedColorRow >= colorCnt(m_pal, m_page) - 1); { if (ImGui::Button("Move Down", sz)) { - undoStack()->push(ox::make_unique(&m_pal, m_page, m_selectedColorRow, 1)); + std::ignore = pushCommand( + m_pal, m_page, m_selectedColorRow, m_selectedColorRow + 1); ++m_selectedColorRow; } } @@ -114,26 +120,35 @@ void PaletteEditorImGui::drawColorsEditor() noexcept { } ImGui::EndDisabled(); } - auto const tblWidth = (colorsSz.x - colorEditorWidth - 8) * colorEditor; - ImGui::BeginTable("Colors", 5, tableFlags, ImVec2(tblWidth, colorsSz.y - (toolbarHeight + 5))); + auto const tblWidth = (colorsSz.x - static_cast(colorEditorWidth) - 8.f) + * static_cast(colorEditor); + ImGui::BeginTable( + "Colors", + 6, + tableFlags, + ImVec2(tblWidth, colorsSz.y - (toolbarHeight + 5))); { ImGui::TableSetupColumn("Idx", ImGuiTableColumnFlags_WidthFixed, 25); - ImGui::TableSetupColumn("Red", ImGuiTableColumnFlags_WidthFixed, 50); - ImGui::TableSetupColumn("Green", ImGuiTableColumnFlags_WidthFixed, 50); - ImGui::TableSetupColumn("Blue", ImGuiTableColumnFlags_WidthFixed, 50); - ImGui::TableSetupColumn("Color Preview", ImGuiTableColumnFlags_NoHide); + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 100); + ImGui::TableSetupColumn("Red", ImGuiTableColumnFlags_WidthFixed, 40); + ImGui::TableSetupColumn("Green", ImGuiTableColumnFlags_WidthFixed, 40); + ImGui::TableSetupColumn("Blue", ImGuiTableColumnFlags_WidthFixed, 40); + ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_NoHide); ImGui::TableHeadersRow(); - for (auto i = 0u; auto const c : m_pal.pages[m_page]) { + for (auto i = 0u; auto const&c : m_pal.pages[m_page]) { ImGui::PushID(static_cast(i)); ImGui::TableNextRow(); - drawColumn(i); + drawColumn(i + 1); + drawColumnLeftAlign(m_pal.colorInfo[i].name); drawColumn(red16(c)); drawColumn(green16(c)); drawColumn(blue16(c)); ImGui::TableNextColumn(); - auto const ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1)); + auto const ic = ImGui::GetColorU32( + ImVec4(redf(c), greenf(c), bluef(c), 1)); ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic); - if (ImGui::Selectable("##ColorRow", i == m_selectedColorRow, ImGuiSelectableFlags_SpanAllColumns)) { + if (ImGui::Selectable( + "##ColorRow", i == m_selectedColorRow, ImGuiSelectableFlags_SpanAllColumns)) { m_selectedColorRow = i; } ImGui::PopID(); @@ -155,28 +170,30 @@ void PaletteEditorImGui::drawPagesEditor() noexcept { constexpr auto toolbarHeight = 40; auto const btnSz = ImVec2(paneSz.x / 3 - 5.5f, 24); if (ImGui::Button("Add", btnSz)) { - undoStack()->push(ox::make_unique(m_pal)); + std::ignore = pushCommand(m_pal, 0u, m_pal.pages.size()); m_page = m_pal.pages.size() - 1; } ImGui::SameLine(); if (ImGui::Button("Remove", btnSz)) { - undoStack()->push(ox::make_unique(m_pal, m_page)); + std::ignore = pushCommand(m_pal, m_page); m_page = std::min(m_page, m_pal.pages.size() - 1); } ImGui::SameLine(); if (ImGui::Button("Duplicate", btnSz)) { - undoStack()->push(ox::make_unique(m_pal, m_page, m_pal.pages.size())); + std::ignore = pushCommand(m_pal, m_page, m_pal.pages.size()); } - ImGui::BeginTable("PageSelect", 2, tableFlags, ImVec2(paneSz.x, paneSz.y - (toolbarHeight + 5))); + ImGui::BeginTable( + "PageSelect", + 2, + tableFlags, + ImVec2(paneSz.x, paneSz.y - (toolbarHeight + 5))); { ImGui::TableSetupColumn("Page", ImGuiTableColumnFlags_WidthFixed, 60); - ImGui::TableSetupColumn("Colors", ImGuiTableColumnFlags_NoHide); ImGui::TableHeadersRow(); for (auto i = 0u; i < m_pal.pages.size(); ++i) { ImGui::PushID(static_cast(i)); ImGui::TableNextRow(); drawColumn(i + 1); - drawColumn(colors(m_pal, i)); ImGui::SameLine(); if (ImGui::Selectable("##PageRow", i == m_page, ImGuiSelectableFlags_SpanAllColumns)) { m_page = i; @@ -193,20 +210,33 @@ void PaletteEditorImGui::drawColorEditor() noexcept { int g = green16(c); int b = blue16(c); int const a = alpha16(c); + auto const¤tName = m_pal.colorInfo[m_selectedColorRow].name; + ox::IString<50> name; + name = currentName; + ImGui::InputText("Name", name.data(), name.cap() + 1); + ImGui::Separator(); ImGui::InputInt("Red", &r, 1, 5); ImGui::InputInt("Green", &g, 1, 5); ImGui::InputInt("Blue", &b, 1, 5); + if (ig::PushButton("Apply to all pages", {-1, ig::BtnSz.y})) { + std::ignore = pushCommand( + m_pal, m_page, m_selectedColorRow); + } + r = ox::max(r, 0); + g = ox::max(g, 0); + b = ox::max(b, 0); auto const newColor = color16(r, g, b, a); if (c != newColor) { - undoStack()->push(ox::make_unique( - &m_pal, m_page, static_cast(m_selectedColorRow), c, newColor)); + std::ignore = pushCommand(m_pal, m_page, m_selectedColorRow, newColor); + } + if (currentName != name.data()) { + std::ignore = pushCommand( + m_pal, m_selectedColorRow, Palette::ColorInfo{name.data()}); } } ox::Error PaletteEditorImGui::handleCommand(studio::UndoCommand const*cmd) noexcept { - if (dynamic_cast(cmd)) { - m_page = m_pal.pages.size() - 1; - } else if (dynamic_cast(cmd)) { + if (dynamic_cast(cmd)) { m_page = ox::min(m_page, m_pal.pages.size() - 1); } else if (auto const dupPageCmd = dynamic_cast(cmd)) { m_page = ox::clamp(dupPageCmd->insertIdx(), 0, m_pal.pages.size() - 1); diff --git a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.hpp b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.hpp index 1a828ef..c448ec2 100644 --- a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.hpp +++ b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor-imgui.hpp @@ -21,7 +21,7 @@ class PaletteEditorImGui: public studio::Editor { size_t m_page = 0; public: - PaletteEditorImGui(studio::StudioContext &sctx, ox::CRStringView path); + PaletteEditorImGui(studio::StudioContext &sctx, ox::StringView path); void keyStateChanged(turbine::Key key, bool down) override; @@ -31,6 +31,8 @@ class PaletteEditorImGui: public studio::Editor { ox::Error saveItem() noexcept final; private: + static void drawColumnLeftAlign(ox::CStringView txt) noexcept; + static void drawColumn(ox::CStringView txt) noexcept; static void drawColumn(ox::Integer_c auto i) noexcept { diff --git a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.cpp b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.cpp index e81b55c..1a4acd0 100644 --- a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.cpp +++ b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.cpp @@ -6,20 +6,44 @@ namespace nostalgia::core { -AddPageCommand::AddPageCommand(Palette &pal) noexcept: - m_pal(pal) { +ApplyColorAllPagesCommand::ApplyColorAllPagesCommand(Palette &pal, size_t const page, size_t const idx): + m_pal(pal), + m_page(page), + m_idx(idx), + m_origColors([this] { + ox::Vector colors; + colors.reserve(m_pal.pages.size()); + for (auto const&p : m_pal.pages) { + colors.emplace_back(p[m_idx]); + } + return colors; + }()) { + auto const c = color(m_pal, m_page, m_idx); + if (ox::all_of(m_pal.pages.begin(), m_pal.pages.end(), [this, c](ox::SpanView const page) { + return page[m_idx] == c; + })) { + throw studio::NoChangesException(); + } } -int AddPageCommand::commandId() const noexcept { - return static_cast(PaletteEditorCommandId::AddPage); +int ApplyColorAllPagesCommand::commandId() const noexcept { + return static_cast(PaletteEditorCommandId::ApplyColorAllPages); } -void AddPageCommand::redo() noexcept { - m_pal.pages.emplace_back(); +ox::Error ApplyColorAllPagesCommand::redo() noexcept { + auto const c = color(m_pal, m_page, m_idx); + for (auto &page : m_pal.pages) { + page[m_idx] = c; + } + return {}; } -void AddPageCommand::undo() noexcept { - std::ignore = m_pal.pages.erase(static_cast(m_pal.pages.size() - 1)); +ox::Error ApplyColorAllPagesCommand::undo() noexcept { + for (size_t p = 0u; auto &page : m_pal.pages) { + page[m_idx] = m_origColors[p]; + ++p; + } + return {}; } @@ -37,13 +61,14 @@ int DuplicatePageCommand::commandId() const noexcept { return static_cast(PaletteEditorCommandId::DuplicatePage); } -void DuplicatePageCommand::redo() noexcept { +ox::Error DuplicatePageCommand::redo() noexcept { m_pal.pages.emplace(m_dstIdx, std::move(m_page)); + return {}; } -void DuplicatePageCommand::undo() noexcept { +ox::Error DuplicatePageCommand::undo() noexcept { m_page = std::move(m_pal.pages[m_dstIdx]); - std::ignore = m_pal.pages.erase(static_cast(m_dstIdx)); + return m_pal.pages.erase(static_cast(m_dstIdx)).error; } size_t DuplicatePageCommand::insertIdx() const noexcept { @@ -53,86 +78,144 @@ size_t DuplicatePageCommand::insertIdx() const noexcept { RemovePageCommand::RemovePageCommand(Palette &pal, size_t idx) noexcept: m_pal(pal), - m_idx(idx) { -} + m_idx(idx) {} int RemovePageCommand::commandId() const noexcept { return static_cast(PaletteEditorCommandId::RemovePage); } -void RemovePageCommand::redo() noexcept { - m_page = std::move(m_pal.pages[m_idx]); - std::ignore = m_pal.pages.erase(static_cast(m_idx)); +ox::Error RemovePageCommand::redo() noexcept { + m_page = std::move(colors(m_pal, m_idx)); + return m_pal.pages.erase(static_cast(m_idx)).error; } -void RemovePageCommand::undo() noexcept { - m_pal.pages.insert(m_idx, std::move(m_page)); +ox::Error RemovePageCommand::undo() noexcept { + m_pal.pages.emplace(m_idx, std::move(m_page)); + return {}; } -AddColorCommand::AddColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept { - m_pal = pal; - m_color = color; - m_page = page; - m_idx = idx; -} +AddColorCommand::AddColorCommand(Palette &pal, Color16 const color, size_t const idx) noexcept: + m_pal(pal), + m_color(color), + m_idx(idx) {} int AddColorCommand::commandId() const noexcept { return static_cast(PaletteEditorCommandId::AddColor); } -void AddColorCommand::redo() noexcept { - m_pal->pages[m_page].insert(static_cast(m_idx), m_color); +ox::Error AddColorCommand::redo() noexcept { + for (auto &page : m_pal.pages) { + page.emplace(static_cast(m_idx), m_color); + } + return {}; } -void AddColorCommand::undo() noexcept { - std::ignore = m_pal->pages[m_page].erase(static_cast(m_idx)); +ox::Error AddColorCommand::undo() noexcept { + for (auto &page : m_pal.pages) { + oxReturnError(page.erase(static_cast(m_idx))); + } + return {}; } -RemoveColorCommand::RemoveColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept { - m_pal = pal; - m_color = color; - m_page = page; - m_idx = idx; -} +RemoveColorCommand::RemoveColorCommand(Palette &pal, size_t const idx) noexcept: + m_pal(pal), + m_idx(idx), + m_colors([this] { + ox::Vector colors; + colors.reserve(m_pal.pages.size()); + for (auto const&p : m_pal.pages) { + colors.emplace_back(p[m_idx]); + } + return colors; + }()) {} int RemoveColorCommand::commandId() const noexcept { return static_cast(PaletteEditorCommandId::RemoveColor); } -void RemoveColorCommand::redo() noexcept { - std::ignore = m_pal->pages[m_page].erase(static_cast(m_idx)); +ox::Error RemoveColorCommand::redo() noexcept { + for (auto &page : m_pal.pages) { + oxReturnError(page.erase(m_idx)); + } + return {}; } -void RemoveColorCommand::undo() noexcept { -m_pal->pages[m_page].insert(static_cast(m_idx), m_color); +ox::Error RemoveColorCommand::undo() noexcept { + for (size_t p = 0; auto &page : m_pal.pages) { + page.emplace(m_idx, m_colors[p]); + ++p; + } + return {}; +} + + +UpdateColorInfoCommand::UpdateColorInfoCommand( + Palette &pal, + size_t idx, + Palette::ColorInfo newColorInfo): + m_pal(pal), + m_idx(idx), + m_altColorInfo(std::move(newColorInfo)) { + if (m_pal.colorInfo[m_idx].name == m_altColorInfo.name) { + throw studio::NoChangesException(); + } +} + +bool UpdateColorInfoCommand::mergeWith(UndoCommand const&cmd) noexcept { + if (cmd.commandId() != static_cast(PaletteEditorCommandId::UpdateColor)) { + return false; + } + auto ucCmd = static_cast(&cmd); + if (m_idx != ucCmd->m_idx) { + return false; + } + return true; +} + +[[nodiscard]] +int UpdateColorInfoCommand::commandId() const noexcept { + return static_cast(PaletteEditorCommandId::UpdateColor); +} + +ox::Error UpdateColorInfoCommand::redo() noexcept { + swap(); + return {}; +} + +ox::Error UpdateColorInfoCommand::undo() noexcept { + swap(); + return {}; +} + +void UpdateColorInfoCommand::swap() noexcept { + std::swap(m_pal.colorInfo[m_idx], m_altColorInfo); } UpdateColorCommand::UpdateColorCommand( - Palette *pal, - size_t page, - int idx, - Color16 oldColor, - Color16 newColor) noexcept { - m_pal = pal; - m_page = page; - m_idx = idx; - m_oldColor = oldColor; - m_newColor = newColor; - //setObsolete(m_oldColor == m_newColor); + Palette &pal, + size_t page, + size_t idx, + Color16 newColor): + m_pal(pal), + m_page(page), + m_idx(idx), + m_altColor(newColor) { + if (color(m_pal, m_page, m_idx) == newColor) { + throw studio::NoChangesException(); + } } -bool UpdateColorCommand::mergeWith(const UndoCommand *cmd) noexcept { - if (cmd->commandId() != static_cast(PaletteEditorCommandId::UpdateColor)) { +bool UpdateColorCommand::mergeWith(UndoCommand const&cmd) noexcept { + if (cmd.commandId() != static_cast(PaletteEditorCommandId::UpdateColor)) { return false; } - auto ucCmd = static_cast(cmd); + auto ucCmd = static_cast(&cmd); if (m_idx != ucCmd->m_idx) { return false; } - m_newColor = ucCmd->m_newColor; return true; } @@ -141,38 +224,47 @@ int UpdateColorCommand::commandId() const noexcept { return static_cast(PaletteEditorCommandId::UpdateColor); } -void UpdateColorCommand::redo() noexcept { - m_pal->pages[m_page][static_cast(m_idx)] = m_newColor; +ox::Error UpdateColorCommand::redo() noexcept { + swap(); + return {}; } -void UpdateColorCommand::undo() noexcept { - m_pal->pages[m_page][static_cast(m_idx)] = m_oldColor; +ox::Error UpdateColorCommand::undo() noexcept { + swap(); + return {}; +} + +void UpdateColorCommand::swap() noexcept { + auto &dst = colors(m_pal, m_page)[m_idx]; + std::swap(dst, m_altColor); } -MoveColorCommand::MoveColorCommand(Palette *pal, size_t page, std::size_t idx, int offset) noexcept { - m_pal = pal; - m_page = page; - m_idx = idx; - m_offset = offset; -} +MoveColorCommand::MoveColorCommand( + Palette &pal, size_t page, size_t srcIdx, size_t dstIdx) noexcept: + m_pal(pal), + m_page(page), + m_srcIdx(srcIdx), + m_dstIdx(dstIdx) {} int MoveColorCommand::commandId() const noexcept { return static_cast(PaletteEditorCommandId::MoveColor); } -void MoveColorCommand::redo() noexcept { - moveColor(static_cast(m_idx), m_offset); +ox::Error MoveColorCommand::redo() noexcept { + moveColor(m_srcIdx, m_dstIdx); + return {}; } -void MoveColorCommand::undo() noexcept { - moveColor(static_cast(m_idx) + m_offset, -m_offset); +ox::Error MoveColorCommand::undo() noexcept { + moveColor(m_dstIdx, m_srcIdx); + return {}; } -void MoveColorCommand::moveColor(int idx, int offset) noexcept { - const auto c = m_pal->pages[m_page][static_cast(idx)]; - std::ignore = m_pal->pages[m_page].erase(static_cast(idx)); - m_pal->pages[m_page].insert(static_cast(idx + offset), c); +void MoveColorCommand::moveColor(size_t srcIdx, size_t dstIdx) noexcept { + auto const c = color(m_pal, m_page, srcIdx); + std::ignore = colors(m_pal, m_page).erase(srcIdx); + colors(m_pal, m_page).emplace(dstIdx, c); } } diff --git a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.hpp b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.hpp index 92b68b9..a12db67 100644 --- a/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.hpp +++ b/src/nostalgia/modules/core/src/studio/paletteeditor/paletteeditor.hpp @@ -13,32 +13,35 @@ namespace nostalgia::core { enum class PaletteEditorCommandId { - AddPage, + ApplyColorAllPages, DuplicatePage, RemovePage, AddColor, RemoveColor, + UpdateColorInfo, UpdateColor, MoveColor, }; -class AddPageCommand: public studio::UndoCommand { +class ApplyColorAllPagesCommand: public studio::UndoCommand { private: Palette &m_pal; + size_t const m_page{}; + size_t const m_idx{}; + ox::Vector const m_origColors; public: - AddPageCommand(Palette &pal) noexcept; + ApplyColorAllPagesCommand(Palette &pal, size_t page, size_t idx); - ~AddPageCommand() noexcept override = default; + ~ApplyColorAllPagesCommand() noexcept override = default; [[nodiscard]] int commandId() const noexcept final; - void redo() noexcept final; - - void undo() noexcept final; + ox::Error redo() noexcept final; + ox::Error undo() noexcept final; }; class DuplicatePageCommand: public studio::UndoCommand { @@ -55,9 +58,9 @@ class DuplicatePageCommand: public studio::UndoCommand { [[nodiscard]] int commandId() const noexcept final; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] size_t insertIdx() const noexcept; @@ -78,88 +81,121 @@ class RemovePageCommand: public studio::UndoCommand { [[nodiscard]] int commandId() const noexcept final; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; }; class AddColorCommand: public studio::UndoCommand { private: - Palette *m_pal = nullptr; + Palette &m_pal; Color16 m_color = 0; - int m_idx = -1; - size_t m_page = 0; + size_t const m_idx = 0; public: - AddColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept; + AddColorCommand(Palette &pal, Color16 color, size_t idx) noexcept; ~AddColorCommand() noexcept override = default; [[nodiscard]] int commandId() const noexcept override; - void redo() noexcept override; + ox::Error redo() noexcept override; - void undo() noexcept override; + ox::Error undo() noexcept override; }; class RemoveColorCommand: public studio::UndoCommand { private: - Palette *m_pal = nullptr; - Color16 m_color = 0; - size_t m_page = 0; - int m_idx = -1; + Palette &m_pal; + size_t const m_idx = 0; + ox::Vector const m_colors; public: - RemoveColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept; + RemoveColorCommand(Palette &pal, size_t idx) noexcept; ~RemoveColorCommand() noexcept override = default; [[nodiscard]] int commandId() const noexcept override; - void redo() noexcept override; + ox::Error redo() noexcept override; - void undo() noexcept override; + ox::Error undo() noexcept override; + +}; + +class UpdateColorInfoCommand: public studio::UndoCommand { + private: + Palette &m_pal; + size_t const m_idx{}; + Palette::ColorInfo m_altColorInfo; + + public: + UpdateColorInfoCommand( + Palette &pal, + size_t idx, + Palette::ColorInfo newColorInfo); + + ~UpdateColorInfoCommand() noexcept override = default; + + [[nodiscard]] + bool mergeWith(const UndoCommand &cmd) noexcept final; + + [[nodiscard]] + int commandId() const noexcept final; + + ox::Error redo() noexcept final; + + ox::Error undo() noexcept final; + + private: + void swap() noexcept; }; class UpdateColorCommand: public studio::UndoCommand { private: - Palette *m_pal = nullptr; - Color16 m_oldColor = 0; - Color16 m_newColor = 0; - size_t m_page = 0; - int m_idx = -1; + Palette &m_pal; + size_t const m_page = 0; + size_t const m_idx{}; + Color16 m_altColor{}; public: - UpdateColorCommand(Palette *pal, size_t page, int idx, Color16 oldColor, Color16 newColor) noexcept; + UpdateColorCommand( + Palette &pal, + size_t page, + size_t idx, + Color16 newColor); ~UpdateColorCommand() noexcept override = default; [[nodiscard]] - bool mergeWith(const UndoCommand *cmd) noexcept final; + bool mergeWith(const UndoCommand &cmd) noexcept final; [[nodiscard]] int commandId() const noexcept final; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; + + private: + void swap() noexcept; }; class MoveColorCommand: public studio::UndoCommand { private: - Palette *m_pal = nullptr; - size_t m_page = 0; - std::size_t m_idx = 0; - int m_offset = 0; + Palette &m_pal; + size_t const m_page = 0; + std::size_t const m_srcIdx = 0; + std::size_t const m_dstIdx = 0; public: - MoveColorCommand(Palette *pal, size_t page, std::size_t idx, int offset) noexcept; + MoveColorCommand(Palette &pal, size_t page, size_t srcIdx, size_t dstIdx) noexcept; ~MoveColorCommand() noexcept override = default; @@ -167,12 +203,12 @@ class MoveColorCommand: public studio::UndoCommand { int commandId() const noexcept override; public: - void redo() noexcept override; + ox::Error redo() noexcept override; - void undo() noexcept override; + ox::Error undo() noexcept override; private: - void moveColor(int idx, int offset) noexcept; + void moveColor(size_t srcIdx, size_t dstIdx) noexcept; }; } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.cpp index c567973..a822cc7 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.cpp @@ -24,7 +24,7 @@ AddSubSheetCommand::AddSubSheetCommand( } } -void AddSubSheetCommand::redo() noexcept { +ox::Error AddSubSheetCommand::redo() noexcept { auto &parent = getSubSheet(m_img, m_parentIdx); if (m_addedSheets.size() < 2) { auto i = parent.subsheets.size(); @@ -35,9 +35,10 @@ void AddSubSheetCommand::redo() noexcept { parent.columns = 0; parent.subsheets.emplace_back(m_img.idIt++, "Subsheet 1", 1, 1, m_img.bpp); } + return {}; } -void AddSubSheetCommand::undo() noexcept { +ox::Error AddSubSheetCommand::undo() noexcept { auto &parent = getSubSheet(m_img, m_parentIdx); if (parent.subsheets.size() == 2) { auto s = parent.subsheets[0]; @@ -47,9 +48,10 @@ void AddSubSheetCommand::undo() noexcept { parent.subsheets.clear(); } else { for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) { - oxLogError(rmSubSheet(m_img, *idx)); + oxReturnError(rmSubSheet(m_img, *idx)); } } + return {}; } int AddSubSheetCommand::commandId() const noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.hpp index 2f1cdb9..64863fc 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/addsubsheetcommand.hpp @@ -17,9 +17,9 @@ class AddSubSheetCommand: public TileSheetCommand { public: AddSubSheetCommand(TileSheet &img, TileSheet::SubSheetIdx parentIdx) noexcept; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] int commandId() const noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.cpp index 2158292..5f01df7 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.cpp @@ -41,18 +41,20 @@ CutPasteCommand::CutPasteCommand( } } -void CutPasteCommand::redo() noexcept { +ox::Error CutPasteCommand::redo() noexcept { auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &c : m_changes) { setPixel(subsheet, m_img.bpp, c.idx, static_cast(c.newPalIdx)); } + return {}; } -void CutPasteCommand::undo() noexcept { +ox::Error CutPasteCommand::undo() noexcept { auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &c : m_changes) { setPixel(subsheet, m_img.bpp, c.idx, static_cast(c.oldPalIdx)); } + return {}; } int CutPasteCommand::commandId() const noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.hpp index a0b1a59..0129337 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.hpp @@ -69,9 +69,9 @@ class CutPasteCommand: public TileSheetCommand { ox::Point const&dstEnd, TileSheetClipboard const&cb) noexcept; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] int commandId() const noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.cpp index f7ca361..09e5749 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.cpp @@ -28,7 +28,7 @@ core::DeleteTilesCommand::DeleteTilesCommand( } } -void core::DeleteTilesCommand::redo() noexcept { +ox::Error core::DeleteTilesCommand::redo() noexcept { auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; auto srcPos = m_deletePos + m_deleteSz; @@ -37,9 +37,10 @@ void core::DeleteTilesCommand::redo() noexcept { const auto dst2 = p.data() + (p.size() - m_deleteSz); ox::memmove(dst1, src, p.size() - srcPos); ox::memset(dst2, 0, m_deleteSz * sizeof(decltype(p[0]))); + return {}; } -void DeleteTilesCommand::undo() noexcept { +ox::Error DeleteTilesCommand::undo() noexcept { auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; const auto src = p.data() + m_deletePos; @@ -48,6 +49,7 @@ void DeleteTilesCommand::undo() noexcept { const auto sz = p.size() - m_deletePos - m_deleteSz; ox::memmove(dst1, src, sz); ox::memcpy(dst2, m_deletedPixels.data(), m_deletedPixels.size()); + return {}; } int DeleteTilesCommand::commandId() const noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.hpp index d692b9f..d96be81 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/deletetilescommand.hpp @@ -23,9 +23,9 @@ class DeleteTilesCommand: public TileSheetCommand { std::size_t tileIdx, std::size_t tileCnt) noexcept; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] int commandId() const noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.cpp index 7af1bed..f99b783 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.cpp @@ -56,18 +56,20 @@ bool DrawCommand::append(const ox::Vector &idxList) noexcept { return out; } -void DrawCommand::redo() noexcept { +ox::Error DrawCommand::redo() noexcept { auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &c : m_changes) { setPixel(subsheet, m_img.bpp, c.idx, static_cast(m_palIdx)); } + return {}; } -void DrawCommand::undo() noexcept { +ox::Error DrawCommand::undo() noexcept { auto &subsheet = getSubSheet(m_img, m_subSheetIdx); for (const auto &c : m_changes) { setPixel(subsheet, m_img.bpp, c.idx, static_cast(c.oldPalIdx)); } + return {}; } int DrawCommand::commandId() const noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.hpp index 7ac8e5d..9c517fb 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/drawcommand.hpp @@ -40,9 +40,9 @@ class DrawCommand: public TileSheetCommand { bool append(const ox::Vector &idxList) noexcept; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] int commandId() const noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.cpp index 3ef9618..d4a2437 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.cpp @@ -28,7 +28,7 @@ core::InsertTilesCommand::InsertTilesCommand( } } -void InsertTilesCommand::redo() noexcept { +ox::Error InsertTilesCommand::redo() noexcept { auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; auto dstPos = m_insertPos + m_insertCnt; @@ -36,9 +36,10 @@ void InsertTilesCommand::redo() noexcept { const auto src = p.data() + m_insertPos; ox::memmove(dst, src, p.size() - dstPos); ox::memset(src, 0, m_insertCnt * sizeof(decltype(p[0]))); + return {}; } -void InsertTilesCommand::undo() noexcept { +ox::Error InsertTilesCommand::undo() noexcept { auto &s = getSubSheet(m_img, m_idx); auto &p = s.pixels; const auto srcIdx = m_insertPos + m_insertCnt; @@ -48,6 +49,7 @@ void InsertTilesCommand::undo() noexcept { const auto sz = p.size() - srcIdx; ox::memmove(dst1, src, sz); ox::memcpy(dst2, m_deletedPixels.data(), m_deletedPixels.size()); + return {}; } int InsertTilesCommand::commandId() const noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.hpp index d0ea74e..4bbb32b 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/inserttilescommand.hpp @@ -23,9 +23,9 @@ class InsertTilesCommand: public TileSheetCommand { std::size_t tileIdx, std::size_t tileCnt) noexcept; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] int commandId() const noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/palettechangecommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/palettechangecommand.cpp index 4503eef..2138c85 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/palettechangecommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/palettechangecommand.cpp @@ -16,12 +16,14 @@ core::PaletteChangeCommand::PaletteChangeCommand( m_newPalette(ox::FileAddress(ox::sfmt>("uuid://{}", newPalette))) { } -void PaletteChangeCommand::redo() noexcept { +ox::Error PaletteChangeCommand::redo() noexcept { m_img.defaultPalette = m_newPalette; + return {}; } -void PaletteChangeCommand::undo() noexcept { +ox::Error PaletteChangeCommand::undo() noexcept { m_img.defaultPalette = m_oldPalette; + return {}; } int PaletteChangeCommand::commandId() const noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/palettechangecommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/palettechangecommand.hpp index e94a2f5..8561099 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/palettechangecommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/palettechangecommand.hpp @@ -21,9 +21,9 @@ class PaletteChangeCommand: public TileSheetCommand { TileSheet &img, ox::CRStringView newPalette) noexcept; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] int commandId() const noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.cpp index a0c0a8c..4ea13a2 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.cpp @@ -11,19 +11,20 @@ core::RmSubSheetCommand::RmSubSheetCommand(TileSheet &img, TileSheet::SubSheetId m_idx(std::move(idx)), m_parentIdx(m_idx) { m_parentIdx.pop_back(); - auto &parent = getSubSheet(m_img, m_parentIdx); - m_sheet = parent.subsheets[*m_idx.back().value]; } -void RmSubSheetCommand::redo() noexcept { +ox::Error RmSubSheetCommand::redo() noexcept { auto &parent = getSubSheet(m_img, m_parentIdx); - oxLogError(parent.subsheets.erase(*m_idx.back().value).error); + m_sheet = std::move(parent.subsheets[*m_idx.back().value]); + oxReturnError(parent.subsheets.erase(*m_idx.back().value).error); + return {}; } -void RmSubSheetCommand::undo() noexcept { +ox::Error RmSubSheetCommand::undo() noexcept { auto &parent = getSubSheet(m_img, m_parentIdx); - auto i = *m_idx.back().value; - parent.subsheets.insert(i, m_sheet); + auto const i = *m_idx.back().value; + parent.subsheets.insert(i, std::move(m_sheet)); + return {}; } int RmSubSheetCommand::commandId() const noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.hpp index d27de89..f65e963 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/rmsubsheetcommand.hpp @@ -18,9 +18,9 @@ class RmSubSheetCommand: public TileSheetCommand { public: RmSubSheetCommand(TileSheet &img, TileSheet::SubSheetIdx idx) noexcept; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] int commandId() const noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp index 2083653..56a4e75 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.cpp @@ -20,17 +20,17 @@ core::UpdateSubSheetCommand::UpdateSubSheetCommand( m_newRows(rows) { } -void UpdateSubSheetCommand::redo() noexcept { +ox::Error UpdateSubSheetCommand::redo() noexcept { auto &sheet = getSubSheet(m_img, m_idx); sheet.name = m_newName; - sheet.columns = m_newCols; - sheet.rows = m_newRows; - oxLogError(setPixelCount(sheet, m_img.bpp, static_cast(PixelsPerTile * m_newCols * m_newRows))); + oxLogError(resizeSubsheet(sheet, m_img.bpp, {m_newCols, m_newRows})); + return {}; } -void UpdateSubSheetCommand::undo() noexcept { +ox::Error UpdateSubSheetCommand::undo() noexcept { auto &sheet = getSubSheet(m_img, m_idx); sheet = m_sheet; + return {}; } int UpdateSubSheetCommand::commandId() const noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.hpp index 1ba917d..fbfaebb 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/updatesubsheetcommand.hpp @@ -25,9 +25,9 @@ class UpdateSubSheetCommand: public TileSheetCommand { int cols, int rows) noexcept; - void redo() noexcept final; + ox::Error redo() noexcept final; - void undo() noexcept final; + ox::Error undo() noexcept final; [[nodiscard]] int commandId() const noexcept final; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp index 1a8546f..c52838c 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "tilesheeteditor-imgui.hpp" @@ -14,6 +15,36 @@ namespace nostalgia::core { namespace ig = studio::ig; +static ox::String configName(ox::StringView str) noexcept { + auto out = ox::String{str}; + for (auto &c : out) { + if (c == '/' || c == '\\') { + c = '%'; + } + } + return out; +} + +struct TileSheetEditorConfig { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetEditorConfig"; + static constexpr auto TypeVersion = 1; + TileSheet::SubSheetIdx activeSubsheet{}; +}; + +oxModelBegin(TileSheetEditorConfig) + oxModelFieldRename(activeSubsheet, active_subsheet) +oxModelEnd() + +struct TileSheetEditorConfigs { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetEditorConfigs"; + static constexpr auto TypeVersion = 1; + ox::HashMap configs; +}; + +oxModelBegin(TileSheetEditorConfigs) + oxModelField(configs) +oxModelEnd() + static ox::Vector normalizePixelSizes( ox::Vector const&inPixels, int const bpp) noexcept { @@ -75,7 +106,7 @@ static ox::Error toPngFile( 8))); } -TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::CRStringView path): +TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::StringView const path): Editor(path), m_sctx(sctx), m_tctx(m_sctx.tctx), @@ -87,6 +118,12 @@ TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::CRSt m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet); m_exportMenu.inputSubmitted.connect(this, &TileSheetEditorImGui::exportSubhseetToPng); m_model.paletteChanged.connect(this, &TileSheetEditorImGui::setPaletteSelection); + // load config + auto const&config = studio::readConfig( + keelCtx(m_sctx), configName(itemPath())); + if (config.ok()) { + m_model.setActiveSubsheet(validateSubSheetIdx(m_model.img(), config.value.activeSubsheet)); + } } void TileSheetEditorImGui::exportFile() { @@ -105,6 +142,10 @@ void TileSheetEditorImGui::paste() { m_model.paste(); } +bool TileSheetEditorImGui::acceptsClipboardPayload() const noexcept { + return m_model.acceptsClipboardPayload(); +} + void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) { if (!down) { return; @@ -116,20 +157,20 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) { auto const popupOpen = m_subsheetEditor.isOpen() && m_exportMenu.isOpen(); auto const pal = m_model.pal(); if (!popupOpen) { - auto const colorCnt = pal.pages[m_model.palettePage()].size(); + auto const colorCnt = core::colorCnt(pal, m_model.palettePage()); if (key == turbine::Key::Alpha_D) { - m_tool = Tool::Draw; + m_tool = TileSheetTool::Draw; setCopyEnabled(false); setCutEnabled(false); setPasteEnabled(false); m_model.clearSelection(); } else if (key == turbine::Key::Alpha_S) { - m_tool = Tool::Select; + m_tool = TileSheetTool::Select; setCopyEnabled(true); setCutEnabled(true); setPasteEnabled(true); } else if (key == turbine::Key::Alpha_F) { - m_tool = Tool::Fill; + m_tool = TileSheetTool::Fill; setCopyEnabled(false); setCutEnabled(false); setPasteEnabled(false); @@ -140,7 +181,8 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) { static_cast(key - turbine::Key::Num_1), m_model.pal().pages.size() - 1); m_model.setPalettePage(idx); } else if (key <= turbine::Key::Num_0 + colorCnt) { - auto const idx = ox::min(static_cast(key - turbine::Key::Num_1), colorCnt - 1); + auto const idx = ox::min( + static_cast(key - turbine::Key::Num_1), colorCnt - 1); m_view.setPalIdx(idx); } } else if (key == turbine::Key::Num_0) { @@ -173,17 +215,17 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept { ImGui::BeginChild("ToolBox", ImVec2(m_palViewWidth - 24, 30), true); { auto const btnSz = ImVec2(45, 14); - if (ImGui::Selectable("Select", m_tool == Tool::Select, 0, btnSz)) { - m_tool = Tool::Select; + if (ImGui::Selectable("Select", m_tool == TileSheetTool::Select, 0, btnSz)) { + m_tool = TileSheetTool::Select; } ImGui::SameLine(); - if (ImGui::Selectable("Draw", m_tool == Tool::Draw, 0, btnSz)) { - m_tool = Tool::Draw; + if (ImGui::Selectable("Draw", m_tool == TileSheetTool::Draw, 0, btnSz)) { + m_tool = TileSheetTool::Draw; m_model.clearSelection(); } ImGui::SameLine(); - if (ImGui::Selectable("Fill", m_tool == Tool::Fill, 0, btnSz)) { - m_tool = Tool::Fill; + if (ImGui::Selectable("Fill", m_tool == TileSheetTool::Fill, 0, btnSz)) { + m_tool = TileSheetTool::Fill; m_model.clearSelection(); } } @@ -204,7 +246,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept { auto const&parent = m_model.activeSubSheet(); m_model.addSubsheet(insertOnIdx); insertOnIdx.emplace_back(parent.subsheets.size() - 1); - m_model.setActiveSubsheet(insertOnIdx); + setActiveSubsheet(insertOnIdx); } ImGui::SameLine(); if (ig::PushButton("-", btnSize)) { @@ -237,7 +279,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept { } ImGui::EndChild(); m_subsheetEditor.draw(m_tctx); - m_exportMenu.draw(m_sctx); + m_exportMenu.draw(m_tctx); } void TileSheetEditorImGui::drawSubsheetSelector( @@ -257,7 +299,7 @@ void TileSheetEditorImGui::drawSubsheetSelector( auto const open = ImGui::TreeNodeEx(lbl.c_str(), flags); ImGui::SameLine(); if (ImGui::IsItemClicked()) { - m_model.setActiveSubsheet(path); + setActiveSubsheet(path); } if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) { showSubsheetEditor(); @@ -370,16 +412,16 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept { if (io.MouseDown[0] && m_prevMouseDownPos != mousePos) { m_prevMouseDownPos = mousePos; switch (m_tool) { - case Tool::Draw: + case TileSheetTool::Draw: m_view.clickDraw(fbSize, clickPos(winPos, mousePos)); break; - case Tool::Fill: + case TileSheetTool::Fill: m_view.clickFill(fbSize, clickPos(winPos, mousePos)); break; - case Tool::Select: + case TileSheetTool::Select: m_view.clickSelect(fbSize, clickPos(winPos, mousePos)); break; - case Tool::None: + case TileSheetTool::None: break; } } @@ -396,7 +438,7 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept { } if (io.MouseReleased[0]) { m_prevMouseDownPos = {-1, -1}; - m_view.releaseMouseButton(); + m_view.releaseMouseButton(m_tool); } } @@ -428,31 +470,39 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept { ImGui::Indent(-20); } // header - if (ImGui::BeginTable("PaletteTable", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) { - ImGui::TableSetupColumn("No.", 0, 0.45f); + if (ImGui::BeginTable( + "PaletteTable", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("Idx", 0, 0.6f); ImGui::TableSetupColumn("", 0, 0.22f); + ImGui::TableSetupColumn("Name", 0, 3); ImGui::TableSetupColumn("Color16", 0, 3); ImGui::TableHeadersRow(); { auto const&pal = m_model.pal(); - for (auto i = 0u; auto c: pal.pages[m_model.palettePage()]) { - ImGui::PushID(static_cast(i)); - // Column: color idx - ImGui::TableNextColumn(); - auto const label = ox::itoa(i + 1); - auto const rowSelected = i == m_view.palIdx(); - if (ImGui::Selectable(label.c_str(), rowSelected, ImGuiSelectableFlags_SpanAllColumns)) { - m_view.setPalIdx(i); + if (pal.pages.size() > m_model.palettePage()) { + for (auto i = 0u; auto const&c: pal.pages[m_model.palettePage()]) { + ImGui::PushID(static_cast(i)); + // Column: color idx + ImGui::TableNextColumn(); + auto const label = ox::itoa(i + 1); + auto const rowSelected = i == m_view.palIdx(); + if (ImGui::Selectable( + label.c_str(), rowSelected, ImGuiSelectableFlags_SpanAllColumns)) { + m_view.setPalIdx(i); + } + // Column: color RGB + ImGui::TableNextColumn(); + auto ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1)); + ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic); + ImGui::TableNextColumn(); + auto const&name = pal.colorInfo[i].name; + ImGui::Text("%s", name.c_str()); + ImGui::TableNextColumn(); + ImGui::Text("(%02d, %02d, %02d)", red16(c), green16(c), blue16(c)); + ImGui::TableNextRow(); + ImGui::PopID(); + ++i; } - // Column: color RGB - ImGui::TableNextColumn(); - auto ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1)); - ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic); - ImGui::TableNextColumn(); - ImGui::Text("(%02d, %02d, %02d)", red16(c), green16(c), blue16(c)); - ImGui::TableNextRow(); - ImGui::PopID(); - ++i; } } ImGui::EndTable(); @@ -476,12 +526,20 @@ ox::Error TileSheetEditorImGui::setPaletteSelection() noexcept { return {}; } +void TileSheetEditorImGui::setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept { + m_model.setActiveSubsheet(path); + studio::editConfig(keelCtx(m_sctx), configName(itemPath()), + [&path](TileSheetEditorConfig *config) { + config->activeSubsheet = std::move(path); + }); +} + ox::Error TileSheetEditorImGui::markUnsavedChanges(studio::UndoCommand const*) noexcept { setUnsavedChanges(true); return {}; } -void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &sctx) noexcept { +void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &tctx) noexcept { constexpr auto popupName = "Edit Subsheet"; if (!m_show) { return; @@ -490,7 +548,7 @@ void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &sctx) noexcept auto constexpr popupWidth = 235.f; auto const popupHeight = modSize ? 130.f : 85.f; auto const popupSz = ImVec2(popupWidth, popupHeight); - if (ig::BeginPopup(sctx, popupName, m_show, popupSz)) { + if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) { ImGui::InputText("Name", m_name.data(), m_name.cap()); if (modSize) { ImGui::InputInt("Columns", &m_cols); @@ -507,7 +565,7 @@ void TileSheetEditorImGui::SubSheetEditor::close() noexcept { m_show = false; } -void TileSheetEditorImGui::ExportMenu::draw(studio::StudioContext &sctx) noexcept { +void TileSheetEditorImGui::ExportMenu::draw(turbine::Context &tctx) noexcept { constexpr auto popupName = "Export Tile Sheet"; if (!m_show) { return; @@ -515,7 +573,7 @@ void TileSheetEditorImGui::ExportMenu::draw(studio::StudioContext &sctx) noexcep constexpr auto popupWidth = 235.f; constexpr auto popupHeight = 85.f; constexpr auto popupSz = ImVec2(popupWidth, popupHeight); - if (ig::BeginPopup(sctx.tctx, popupName, m_show, popupSz)) { + if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) { ImGui::InputInt("Scale", &m_scale); m_scale = ox::clamp(m_scale, 1, 50); if (ig::PopupControlsOkCancel(popupWidth, m_show) == ig::PopupResponse::OK) { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp index dc9b2c0..deb45d4 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.hpp @@ -16,13 +16,6 @@ namespace nostalgia::core { -enum class Tool { - None, - Draw, - Fill, - Select, -}; - class TileSheetEditorImGui: public studio::Editor { private: @@ -55,7 +48,7 @@ class TileSheetEditorImGui: public studio::Editor { m_show = true; m_scale = 5; } - void draw(studio::StudioContext &sctx) noexcept; + void draw(turbine::Context &sctx) noexcept; void close() noexcept; [[nodiscard]] inline bool isOpen() const noexcept { return m_show; } @@ -71,10 +64,10 @@ class TileSheetEditorImGui: public studio::Editor { TileSheetEditorModel &m_model; float m_palViewWidth = 300; ox::Vec2 m_prevMouseDownPos; - Tool m_tool = Tool::Draw; + TileSheetTool m_tool = TileSheetTool::Draw; public: - TileSheetEditorImGui(studio::StudioContext &sctx, ox::CRStringView path); + TileSheetEditorImGui(studio::StudioContext &sctx, ox::StringView path); ~TileSheetEditorImGui() override = default; @@ -86,6 +79,9 @@ class TileSheetEditorImGui: public studio::Editor { void paste() override; + [[nodiscard]] + bool acceptsClipboardPayload() const noexcept override; + void keyStateChanged(turbine::Key key, bool down) override; void draw(studio::StudioContext&) noexcept override; @@ -115,6 +111,8 @@ class TileSheetEditorImGui: public studio::Editor { private: ox::Error markUnsavedChanges(studio::UndoCommand const*) noexcept; + void setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept; + }; } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp index d534825..f94d68d 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.cpp @@ -27,7 +27,8 @@ namespace nostalgia::core { Palette const TileSheetEditorModel::s_defaultPalette = { - .pages = {ox::Vector(128)}, + .colorInfo = {ox::Vector{{}}}, + .pages = {{ox::Vector(128)}}, }; // delete pixels of all non-leaf nodes @@ -55,55 +56,63 @@ TileSheetEditorModel::TileSheetEditorModel(studio::StudioContext &sctx, ox::Stri } void TileSheetEditorModel::cut() { + if (!m_selection) { + return; + } TileSheetClipboard blankCb; auto cb = ox::make_unique(); - const auto &s = activeSubSheet(); - for (int y = m_selectionBounds.y; y <= m_selectionBounds.y2(); ++y) { - for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) { - auto pt = ox::Point(x, y); - const auto idx = core::idx(s, pt); - const auto c = getPixel(s, m_img.bpp, idx); - pt.x -= m_selectionBounds.x; - pt.y -= m_selectionBounds.y; - cb->addPixel(pt, c); - blankCb.addPixel(pt, 0); - } - } - const auto pt1 = m_selectionOrigin == ox::Point(-1, -1) ? ox::Point(0, 0) : m_selectionOrigin; - const auto pt2 = ox::Point(s.columns * TileWidth, s.rows * TileHeight); + auto const&s = activeSubSheet(); + iterateSelectionRows(*m_selection, [&](int x, int y) { + auto pt = ox::Point{x, y}; + auto const idx = core::idx(s, pt); + auto const c = getPixel(s, m_img.bpp, idx); + pt -= m_selection->a; + cb->addPixel(pt, c); + blankCb.addPixel(pt, 0); + }); + auto const pt1 = m_selection->a; + auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; turbine::setClipboardObject(m_tctx, std::move(cb)); pushCommand(ox::make(CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb)); } void TileSheetEditorModel::copy() { - auto cb = ox::make_unique(); - for (int y = m_selectionBounds.y; y <= m_selectionBounds.y2(); ++y) { - for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) { - auto pt = ox::Point(x, y); - const auto &s = activeSubSheet(); - const auto idx = core::idx(s, pt); - const auto c = getPixel(s, m_img.bpp, idx); - pt.x -= m_selectionBounds.x; - pt.y -= m_selectionBounds.y; - cb->addPixel(pt, c); - } + if (!m_selection) { + return; } + auto cb = ox::make_unique(); + iterateSelectionRows(*m_selection, [&](int x, int y) { + auto pt = ox::Point{x, y}; + const auto&s = activeSubSheet(); + const auto idx = core::idx(s, pt); + const auto c = getPixel(s, m_img.bpp, idx); + pt -= m_selection->a; + cb->addPixel(pt, c); + }); turbine::setClipboardObject(m_tctx, std::move(cb)); } void TileSheetEditorModel::paste() { + if (!m_selection) { + return; + } auto [cb, err] = turbine::getClipboardObject(m_tctx); if (err) { oxLogError(err); oxErrf("Could not read clipboard: {}", toStr(err)); return; } - const auto &s = activeSubSheet(); - const auto pt1 = m_selectionOrigin == ox::Point(-1, -1) ? ox::Point(0, 0) : m_selectionOrigin; - const auto pt2 = ox::Point(s.columns * TileWidth, s.rows * TileHeight); + auto const&s = activeSubSheet(); + auto const pt1 = m_selection->a; + auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; pushCommand(ox::make(CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb)); } +bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept { + auto const cb = getClipboardObject(m_tctx); + return cb.ok(); +} + ox::StringView TileSheetEditorModel::palPath() const noexcept { auto [path, err] = m_img.defaultPalette.getPath(); if (err) { @@ -206,30 +215,26 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept { } void TileSheetEditorModel::select(ox::Point const&pt) noexcept { - if (!m_selectionOngoing) { - m_selectionOrigin = pt; - m_selectionOngoing = true; - m_selectionBounds = {pt, pt}; - m_updated = true; - } else if (m_selectionBounds.pt2() != pt) { - m_selectionBounds = {m_selectionOrigin, pt}; + if (m_selTracker.updateCursorPoint(pt)) { + m_selection.emplace(m_selTracker.selection()); m_updated = true; } } void TileSheetEditorModel::completeSelection() noexcept { - m_selectionOngoing = false; - auto &s = activeSubSheet(); - auto pt = m_selectionBounds.pt2(); - pt.x = ox::min(s.columns * TileWidth, pt.x); - pt.y = ox::min(s.rows * TileHeight, pt.y); - m_selectionBounds.setPt2(pt); + if (m_selTracker.selectionOngoing()) { + m_selTracker.finishSelection(); + m_selection.emplace(m_selTracker.selection()); + auto&pt = m_selection->b; + auto&s = activeSubSheet(); + pt.x = ox::min(s.columns * TileWidth - 1, pt.x); + pt.y = ox::min(s.rows * TileHeight - 1, pt.y); + } } void TileSheetEditorModel::clearSelection() noexcept { m_updated = true; - m_selectionOrigin = {-1, -1}; - m_selectionBounds = {{-1, -1}, {-1, -1}}; + m_selection.reset(); } bool TileSheetEditorModel::updated() const noexcept { @@ -266,9 +271,9 @@ ox::Error TileSheetEditorModel::saveFile() noexcept { } bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept { - const auto &s = activeSubSheet(); - const auto pt = idxToPt(static_cast(idx), s.columns); - return m_selectionBounds.contains(pt); + auto const&s = activeSubSheet(); + auto const pt = idxToPt(static_cast(idx), s.columns); + return m_selection && m_selection->contains(pt); } void TileSheetEditorModel::getFillPixels(bool *pixels, ox::Point const&pt, int oldColor) const noexcept { @@ -305,7 +310,7 @@ void TileSheetEditorModel::getFillPixels(bool *pixels, ox::Point const&pt, int o } void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept { - m_undoStack.push(ox::UPtr(cmd)); + std::ignore = m_undoStack.push(ox::UPtr(cmd)); m_ongoingDrawCommand = dynamic_cast(cmd); m_updated = true; } diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp index f4fe90e..496bc96 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditormodel.hpp @@ -33,10 +33,9 @@ class TileSheetEditorModel: public ox::SignalHandler { size_t m_palettePage{}; studio::UndoStack &m_undoStack; class DrawCommand *m_ongoingDrawCommand = nullptr; + studio::SelectionTracker m_selTracker; + ox::Optional m_selection; bool m_updated = false; - bool m_selectionOngoing = false; - ox::Point m_selectionOrigin = {-1, -1}; - ox::Bounds m_selectionBounds = {{-1, -1}, {-1, -1}}; public: TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack); @@ -49,6 +48,9 @@ class TileSheetEditorModel: public ox::SignalHandler { void paste(); + [[nodiscard]] + bool acceptsClipboardPayload() const noexcept; + [[nodiscard]] constexpr TileSheet const&img() const noexcept; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.cpp index 666ce14..5583c8e 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.cpp @@ -84,9 +84,18 @@ void TileSheetEditorView::clickFill(ox::Vec2 const&paneSize, ox::Vec2 const&clic m_model.fill(pt, static_cast(m_palIdx)); } -void TileSheetEditorView::releaseMouseButton() noexcept { - m_model.endDrawCommand(); - m_model.completeSelection(); +void TileSheetEditorView::releaseMouseButton(TileSheetTool tool) noexcept { + switch (tool) { + case TileSheetTool::Draw: + m_model.endDrawCommand(); + break; + case TileSheetTool::Select: + m_model.completeSelection(); + break; + case TileSheetTool::Fill: + case TileSheetTool::None: + break; + } } void TileSheetEditorView::resizeView(ox::Vec2 const&sz) noexcept { diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.hpp index d421237..5289738 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditorview.hpp @@ -18,7 +18,8 @@ namespace nostalgia::core { -enum class TileSheetTool: int { +enum class TileSheetTool { + None, Select, Draw, Fill, @@ -33,6 +34,8 @@ constexpr auto toString(TileSheetTool t) noexcept { return "Draw"; case TileSheetTool::Fill: return "Fill"; + case TileSheetTool::None: + return "None"; } return ""; } @@ -66,7 +69,7 @@ class TileSheetEditorView: public ox::SignalHandler { void clickFill(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept; - void releaseMouseButton() noexcept; + void releaseMouseButton(TileSheetTool tool) noexcept; void scrollV(ox::Vec2 const&paneSz, float wheel, bool zoomMod) noexcept; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.cpp index 1185f2f..2fa55a3 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheetpixels.cpp @@ -139,10 +139,7 @@ void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept { return; } if (m_model.pixelSelected(i)) { - auto const r = red16(color) / 2; - auto const g = (green16(color) + 20) / 2; - auto const b = (blue16(color) + 31) / 2; - color = color16(r, g, b); + color = applySelectionColor(color); } setPixelBufferObject(paneSize, static_cast(i * VertexVboRows), fx, fy, color, vbo, ebo); }); diff --git a/src/nostalgia/modules/core/src/tilesheet.cpp b/src/nostalgia/modules/core/src/tilesheet.cpp index 58ebc15..9f622ad 100644 --- a/src/nostalgia/modules/core/src/tilesheet.cpp +++ b/src/nostalgia/modules/core/src/tilesheet.cpp @@ -15,11 +15,11 @@ std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept { } [[nodiscard]] -static TileSheet::SubSheet const*getSubsheet(TileSheet::SubSheet const&ss, SubSheetId const id) noexcept { +static TileSheet::SubSheet const *getSubsheet(TileSheet::SubSheet const&ss, SubSheetId const id) noexcept { if (ss.id == id) { return &ss; } - for (auto const&child : ss.subsheets) { + for (auto const&child: ss.subsheets) { if (auto out = getSubsheet(child, id)) { return out; } @@ -34,7 +34,7 @@ static size_t getTileCnt(TileSheet::SubSheet const&ss, int const bpp) noexcept { return ss.pixels.size() / bytesPerTile; } else { size_t out{}; - for (auto const&child : ss.subsheets) { + for (auto const&child: ss.subsheets) { out += getTileCnt(child, bpp); } return out; @@ -45,7 +45,7 @@ size_t getTileCnt(TileSheet const&ts) noexcept { return getTileCnt(ts.subsheet, ts.bpp); } -TileSheet::SubSheet const*getSubsheet(TileSheet const&ts, SubSheetId const id) noexcept { +TileSheet::SubSheet const *getSubsheet(TileSheet const&ts, SubSheetId const id) noexcept { return getSubsheet(ts.subsheet, id); } @@ -54,7 +54,7 @@ static ox::Optional getPixelIdx( SubSheetId const id, size_t idx, int8_t const bpp) noexcept { - for (auto const&child : ss.subsheets) { + for (auto const&child: ss.subsheets) { if (child.id == id) { return ox::Optional(ox::in_place, idx); } @@ -106,8 +106,8 @@ uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, ox::Point const&pt) return getPixel(ss, pBpp, idx); } -void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept { - auto &pixel = ss.pixels[static_cast(idx / 2)]; +static void setPixel(ox::Vector &pixels, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept { + auto &pixel = pixels[static_cast(idx / 2)]; if (pBpp == 4) { if (idx & 1) { pixel = static_cast((pixel & 0b0000'1111) | (palIdx << 4)); @@ -119,22 +119,39 @@ void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx } } +void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept { + setPixel(ss.pixels, pBpp, idx, palIdx); +} + +static void setPixel(ox::Vector &pixels, int columns, int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept { + const auto idx = ptToIdx(pt, columns); + setPixel(pixels, pBpp, idx, palIdx); +} + void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept { const auto idx = ptToIdx(pt, ss.columns); setPixel(ss, pBpp, idx, palIdx); } -ox::Error setPixelCount(TileSheet::SubSheet &ss, int8_t pBpp, std::size_t cnt) noexcept { +static ox::Error setPixelCount(ox::Vector &pixels, int8_t pBpp, std::size_t cnt) noexcept { + size_t sz{}; switch (pBpp) { case 4: - ss.pixels.resize(cnt / 2); - return OxError(0); + sz = cnt / 2; + break; case 8: - ss.pixels.resize(cnt); - return OxError(0); + sz = cnt; + break; default: return OxError(1, "Invalid pBpp used for TileSheet::SubSheet::setPixelCount"); } + pixels.reserve(sz); + pixels.resize(sz); + return {}; +} + +ox::Error setPixelCount(TileSheet::SubSheet &ss, int8_t pBpp, std::size_t cnt) noexcept { + return setPixelCount(ss.pixels, pBpp, cnt); } unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept { @@ -142,6 +159,23 @@ unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept { return pBpp == 4 ? pixelsSize * 2 : pixelsSize; } +ox::Error resizeSubsheet(TileSheet::SubSheet &ss, int8_t pBpp, ox::Size const&sz) noexcept { + ox::Vector out; + oxReturnError(setPixelCount(out, pBpp, static_cast(sz.width * sz.height) * PixelsPerTile)); + auto const w = ss.columns * TileWidth; + auto const h = ss.rows * TileHeight; + for (auto x = 0; x < w; ++x) { + for (auto y = 0; y < h; ++y) { + auto const palIdx = getPixel(ss, pBpp, {x, y}); + setPixel(out, sz.width, pBpp, {x, y}, palIdx); + } + } + ss.columns = sz.width; + ss.rows = sz.height; + ss.pixels = std::move(out); + return {}; +} + ox::Result getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept { if (ss.id == pId) { return ox::StringView(ss.name); diff --git a/src/nostalgia/modules/scene/src/studio/sceneeditor-imgui.cpp b/src/nostalgia/modules/scene/src/studio/sceneeditor-imgui.cpp index beef45f..4ec3e14 100644 --- a/src/nostalgia/modules/scene/src/studio/sceneeditor-imgui.cpp +++ b/src/nostalgia/modules/scene/src/studio/sceneeditor-imgui.cpp @@ -50,7 +50,6 @@ void SceneEditorImGui::onActivated() noexcept { ox::Error SceneEditorImGui::saveItem() noexcept { const auto sctx = applicationData(m_ctx); oxReturnError(sctx->project->writeObj(itemPath(), m_editor.scene())); - oxReturnError(keelCtx(m_ctx).assetManager.setAsset(itemPath(), m_editor.scene())); return {}; } diff --git a/src/olympic/keel/include/keel/assetmanager.hpp b/src/olympic/keel/include/keel/assetmanager.hpp index b13a131..3daefc6 100644 --- a/src/olympic/keel/include/keel/assetmanager.hpp +++ b/src/olympic/keel/include/keel/assetmanager.hpp @@ -4,6 +4,10 @@ #pragma once +#ifndef OX_BARE_METAL +#include +#endif + #include #include #include @@ -178,9 +182,11 @@ constexpr AssetRef::AssetRef(AssetRef &&h) noexcept: m_ctr(h.m_ctr) { h.m_ctr = nullptr; } +class Context; + class AssetManager { private: - class AssetTypeManagerBase { + class AssetTypeManagerBase: public ox::SignalHandler { public: virtual ~AssetTypeManagerBase() = default; @@ -189,31 +195,28 @@ class AssetManager { template class AssetTypeManager: public AssetTypeManagerBase { + public: + using Loader = std::function(ox::StringView assetId)>; private: + Loader m_loader{}; ox::HashMap>> m_cache; public: - ox::Result> getAsset(ox::StringView const&assetId) const noexcept { - auto out = m_cache.at(assetId); - oxReturnError(out); - return AssetRef(out.value->get()); - } + AssetTypeManager(Loader loader) noexcept: m_loader(loader) {} - ox::Result> setAsset(ox::StringView const&assetId, T const&obj) noexcept { - auto &p = m_cache[assetId]; - if (!p) { - p = ox::make_unique>(obj); - } else { - p->set(obj); - p->updated.emit(); + ox::Result> getAsset(ox::StringView const assetId) const noexcept { + oxRequire(out, m_cache.at(assetId)); + if (!out || !*out) { + return OxError(1, "asset is null"); } - return AssetRef(p.get()); + return AssetRef(out->get()); } - ox::Result> setAsset(ox::StringView const&assetId, T &&obj) noexcept { + ox::Result> loadAsset(ox::StringView const assetId) noexcept { auto &p = m_cache[assetId]; + oxRequireM(obj, m_loader(assetId)); if (!p) { - p = ox::make_unique>(obj); + p = ox::make_unique>(std::move(obj)); } else { p->set(std::move(obj)); p->updated.emit(); @@ -221,6 +224,18 @@ class AssetManager { return AssetRef(p.get()); } + ox::Error reloadAsset(ox::StringView const assetId) noexcept { + auto &p = m_cache[assetId]; + oxRequireM(obj, m_loader(assetId)); + if (!p) { + p = ox::make_unique>(std::move(obj)); + } else { + p->set(std::move(obj)); + p->updated.emit(); + } + return {}; + } + void gc() noexcept final { for (auto const&ack : m_cache.keys()) { auto &ac = m_cache[ack]; @@ -232,29 +247,48 @@ class AssetManager { }; ox::HashMap> m_assetTypeManagers; + ox::HashMap> m_fileUpdated; template - AssetTypeManager *getTypeManager() noexcept { - constexpr auto typeName = ox::requireModelTypeName(); - static_assert(ox::StringView(typeName) != "", "Types must have TypeName to use AssetManager"); - auto &am = m_assetTypeManagers[typeName]; - if (!am) { - am = ox::make_unique>(); + ox::Result*> getTypeManager() noexcept { + constexpr auto &typeId = ox::ModelTypeId_v; + static_assert(typeId != "", "Types must have TypeName to use AssetManager"); + auto &am = m_assetTypeManagers[typeId]; + auto const out = dynamic_cast*>(am.get()); + if (!out) { + return OxError(1, "no AssetTypeManager for type"); } - return dynamic_cast*>(am.get()); + return out; } public: template - ox::Result> getAsset(ox::CRStringView assetId) noexcept { - auto m = getTypeManager(); - return m->getAsset(assetId); + void initTypeManager(auto const&makeLoader, Context &ctx) noexcept { + constexpr auto &typeId = ox::ModelTypeId_v; + static_assert(typeId != "", "Types must have TypeName to use AssetManager"); + auto &am = m_assetTypeManagers[typeId]; + if (!am) { + am = ox::make_unique>(makeLoader(ctx)); + } } template - ox::Result> setAsset(ox::CRStringView assetId, T const&obj) noexcept { - auto m = getTypeManager(); - return m->setAsset(assetId, obj); + ox::Result> getAsset(ox::StringView assetId) noexcept { + oxRequire(m, getTypeManager()); + return m->getAsset(assetId); + } + + ox::Error reloadAsset(ox::StringView assetId) noexcept { + m_fileUpdated[assetId].emit(assetId); + return {}; + } + + template + ox::Result> loadAsset(ox::StringView assetId) noexcept { + oxRequire(m, getTypeManager()); + oxRequire(out, m->loadAsset(assetId)); + m_fileUpdated[assetId].connect(m, &AssetTypeManager::reloadAsset); + return out; } void gc() noexcept { diff --git a/src/olympic/keel/include/keel/media.hpp b/src/olympic/keel/include/keel/media.hpp index 5c20f34..1af4e49 100644 --- a/src/olympic/keel/include/keel/media.hpp +++ b/src/olympic/keel/include/keel/media.hpp @@ -32,14 +32,43 @@ oxModelEnd() ox::Result getPreloadAddr(keel::Context &ctx, ox::FileAddress const&file) noexcept; ox::Result getPreloadAddr(keel::Context &ctx, ox::CRStringView file) noexcept; + +void createUuidMapping(Context &ctx, ox::StringView filePath, ox::UUID const&uuid) noexcept; + +ox::Error buildUuidMap(Context &ctx) noexcept; + +ox::Result pathToUuid(Context &ctx, ox::CRStringView path) noexcept; + +ox::Result getUuid(Context &ctx, ox::FileAddress const&fileAddr) noexcept; + +ox::Result getUuid(Context &ctx, ox::StringView path) noexcept; + +ox::Result getPath(Context &ctx, ox::FileAddress const&fileAddr) noexcept; + +ox::Result getPath(Context &ctx, ox::CStringView fileId) noexcept; + +constexpr ox::Result uuidUrlToUuid(ox::StringView uuidUrl) noexcept { + return ox::UUID::fromString(substr(uuidUrl, 7)); +} + +ox::Result uuidUrlToPath(Context &ctx, ox::StringView uuid) noexcept; + +ox::Result uuidToPath(Context &ctx, ox::CRStringView uuid) noexcept; + +ox::Result uuidToPath(Context &ctx, ox::UUID const&uuid) noexcept; + +ox::Error performPackTransforms(Context &ctx, ox::Buffer &clawData) noexcept; + #ifndef OX_BARE_METAL +namespace detail { template -ox::Result> readObjFile( - keel::Context &ctx, - ox::StringView assetId, - bool forceLoad) noexcept { - constexpr auto readConvert = [](Context &ctx, const ox::Buffer &buff) -> ox::Result { +constexpr auto makeLoader(Context &ctx) { + return [&ctx](ox::StringView assetId) -> ox::Result { + ox::StringView path; + oxRequire(p, ctx.uuidToPath.at(assetId)); + path = *p; + oxRequire(buff, ctx.rom->read(path)); auto [obj, err] = readAsset(buff); if (err) { if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) { @@ -49,25 +78,34 @@ ox::Result> readObjFile( } return std::move(obj); }; - ox::StringView path; +}; +} + +template +ox::Result> readObjFile( + keel::Context &ctx, + ox::StringView assetId, + bool forceLoad) noexcept { + ox::UUIDStr uuidStr; if (beginsWith(assetId, "uuid://")) { assetId = substr(assetId, 7); - oxRequire(p, ctx.uuidToPath.at(assetId)); - path = *p; + oxRequire(p, keel::uuidToPath(ctx, assetId)); } else { - path = assetId; + auto const [uuid, uuidErr] = getUuid(ctx, assetId); + if (!uuidErr) { + uuidStr = uuid.toString(); + assetId = uuidStr; + } } if (forceLoad) { - oxRequire(buff, ctx.rom->read(path)); - oxRequire(obj, readConvert(ctx, buff)); - oxRequire(cached, ctx.assetManager.setAsset(assetId, obj)); + ctx.assetManager.initTypeManager(detail::makeLoader, ctx); + oxRequire(cached, ctx.assetManager.loadAsset(assetId)); return cached; } else { auto [cached, err] = ctx.assetManager.getAsset(assetId); if (err) { - oxRequire(buff, ctx.rom->read(path)); - oxRequire(obj, readConvert(ctx, buff)); - oxReturnError(ctx.assetManager.setAsset(assetId, obj).moveTo(cached)); + ctx.assetManager.initTypeManager(detail::makeLoader, ctx); + oxReturnError(ctx.assetManager.loadAsset(assetId).moveTo(cached)); } return cached; } @@ -89,34 +127,7 @@ ox::Result> readObjNoCache( #endif -void createUuidMapping(Context &ctx, ox::StringView filePath, ox::UUID const&uuid) noexcept; - -ox::Error buildUuidMap(Context &ctx) noexcept; - -ox::Result uuidToPath(Context &ctx, ox::CRStringView uuid) noexcept; - -ox::Result uuidToPath(Context &ctx, ox::UUID const&uuid) noexcept; - -ox::Error performPackTransforms(Context &ctx, ox::Buffer &clawData) noexcept; - -template -ox::Result> setAsset(keel::Context &ctx, ox::StringView assetId, T const&asset) noexcept { -#ifndef OX_BARE_METAL - if (assetId.len() == 0) { - return OxError(1, "Invalid asset ID"); - } - ox::UUIDStr idStr; - if (assetId[0] == '/') { - auto const [id, err] = ctx.pathToUuid.at(assetId); - oxReturnError(err); - idStr = id->toString(); - assetId = idStr; - } - return ctx.assetManager.setAsset(assetId, asset); -#else - return OxError(1, "Not supported on this platform"); -#endif -} +ox::Error reloadAsset(keel::Context &ctx, ox::StringView assetId) noexcept; template ox::Result> readObj( diff --git a/src/olympic/keel/include/keel/pack.hpp b/src/olympic/keel/include/keel/pack.hpp index ed21ee2..635fc8a 100644 --- a/src/olympic/keel/include/keel/pack.hpp +++ b/src/olympic/keel/include/keel/pack.hpp @@ -86,19 +86,21 @@ ox::Error preloadObj( ox::TypeStore &ts, ox::FileSystem &romFs, ox::Preloader &pl, - ox::CRStringView path) noexcept { + ox::StringView const path) noexcept { // load file oxRequireM(buff, romFs.read(path)); oxRequireM(obj, keel::readAsset(ts, buff)); if (obj.type()->preloadable) { - oxOutf("preloading {} as a {}\n", path, obj.type()->typeName); // preload - oxRequire(a, pl.startAlloc(ox::sizeOf(&obj), ox::alignOf(obj))); + auto const size = ox::sizeOf(&obj); + auto const alignment = ox::alignOf(obj); + oxRequire(a, pl.startAlloc(size, alignment)); auto const err = ox::preload(&pl, &obj); oxReturnError(pl.endAlloc()); oxReturnError(err); - keel::PreloadPtr const p{.preloadAddr = static_cast(a)}; + keel::PreloadPtr const p{.preloadAddr = a}; oxReturnError(ox::writeMC(p).moveTo(buff)); + oxOutf("preloaded {} as a {} @ {} to {}\n", path, obj.type()->typeName, a, a + size); } else { // strip the Claw header (it is not needed after preloading) and write back out to dest fs oxReturnError(ox::writeMC(obj).moveTo(buff)); diff --git a/src/olympic/keel/src/media.cpp b/src/olympic/keel/src/media.cpp index 29572f1..ca3c857 100644 --- a/src/olympic/keel/src/media.cpp +++ b/src/olympic/keel/src/media.cpp @@ -83,19 +83,72 @@ ox::Result pathToUuid(Context &ctx, ox::CRStringView path) noexcept { #endif } -ox::Result uuidToPath(Context &ctx, ox::CRStringView uuid) noexcept { +ox::Result getUuid(Context &ctx, ox::FileAddress const&fileAddr) noexcept { + oxRequire(path, fileAddr.getPath()); + return getUuid(ctx, path); +} + +ox::Result getUuid(Context &ctx, ox::StringView path) noexcept { + if (beginsWith(path, "uuid://")) { + auto const uuid = substr(path, 7); + return ox::UUID::fromString(uuid); + } else { + return pathToUuid(ctx, path); + } +} + +ox::Result getPath(Context &ctx, ox::FileAddress const&fileAddr) noexcept { + oxRequire(path, fileAddr.getPath()); + if (beginsWith(path, "uuid://")) { + auto const uuid = substr(path, 7); #ifndef OX_BARE_METAL - oxRequire(out, ctx.uuidToPath.at(uuid)); - return *out; + oxRequireM(out, ctx.uuidToPath.at(uuid)); + return ox::CStringView{*out}; +#else + return OxError(1, "UUID to path conversion not supported on this platform"); +#endif + } else { + return ox::CStringView{path}; + } +} + +ox::Result getPath(Context &ctx, ox::CStringView fileId) noexcept { + if (beginsWith(fileId, "uuid://")) { + auto const uuid = substr(fileId, 7); +#ifndef OX_BARE_METAL + oxRequireM(out, ctx.uuidToPath.at(uuid)); + return ox::CStringView{*out}; +#else + return OxError(1, "UUID to path conversion not supported on this platform"); +#endif + } else { + return ox::CStringView{fileId}; + } +} + +ox::Result uuidUrlToPath(Context &ctx, ox::StringView uuid) noexcept { + uuid = substr(uuid, 7); +#ifndef OX_BARE_METAL + oxRequireM(out, ctx.uuidToPath.at(uuid)); + return ox::CStringView(*out); #else return OxError(1, "UUID to path conversion not supported on this platform"); #endif } -ox::Result uuidToPath(Context &ctx, ox::UUID const&uuid) noexcept { +ox::Result uuidToPath(Context &ctx, ox::CRStringView uuid) noexcept { #ifndef OX_BARE_METAL - oxRequire(out, ctx.uuidToPath.at(uuid.toString())); - return *out; + oxRequireM(out, ctx.uuidToPath.at(uuid)); + return ox::CStringView(*out); +#else + return OxError(1, "UUID to path conversion not supported on this platform"); +#endif +} + +ox::Result uuidToPath(Context &ctx, ox::UUID const&uuid) noexcept { +#ifndef OX_BARE_METAL + oxRequireM(out, ctx.uuidToPath.at(uuid.toString())); + return ox::CStringView(*out); #else return OxError(1, "UUID to path conversion not supported on this platform"); #endif @@ -113,6 +166,21 @@ ox::Error performPackTransforms(Context &ctx, ox::Buffer &clawData) noexcept { return {}; } +ox::Error reloadAsset(keel::Context &ctx, ox::StringView assetId) noexcept { + ox::UUIDStr uuidStr; + if (beginsWith(assetId, "uuid://")) { + assetId = substr(assetId, 7); + oxRequire(p, keel::uuidToPath(ctx, assetId)); + } else { + auto const [uuid, uuidErr] = getUuid(ctx, assetId); + if (!uuidErr) { + uuidStr = uuid.toString(); + assetId = uuidStr; + } + } + return ctx.assetManager.reloadAsset(assetId); +} + } #else @@ -166,6 +234,10 @@ ox::Result getPreloadAddr(keel::Context &ctx, ox::FileAddress const return static_cast(p.preloadAddr) + ctx.preloadSectionOffset; } +ox::Error reloadAsset(keel::Context&, ox::StringView) noexcept { + return OxError(1, "reloadAsset is unsupported on this platform"); +} + } #endif diff --git a/src/olympic/keel/src/pack-applib.cpp b/src/olympic/keel/src/pack-applib.cpp index 1ea389a..a830e98 100644 --- a/src/olympic/keel/src/pack-applib.cpp +++ b/src/olympic/keel/src/pack-applib.cpp @@ -11,7 +11,7 @@ #include -static ox::Error writeFileBuff(ox::StringView path, ox::Buffer const&buff) noexcept { +static ox::Error writeFileBuff(ox::StringView path, ox::BufferView const buff) noexcept { try { std::ofstream f(std::string(toStdStringView(path)), std::ios::binary); f.write(buff.data(), static_cast(buff.size())); @@ -39,10 +39,10 @@ static ox::Result readFileBuff(ox::StringView path) noexcept { } } -static ox::Error generateTypes(ox::TypeStore *ts) noexcept { +static ox::Error generateTypes(ox::TypeStore &ts) noexcept { for (auto const mod : keel::modules()) { for (auto gen : mod->types()) { - oxReturnError(gen(*ts)); + oxReturnError(gen(ts)); } } return {}; @@ -54,7 +54,7 @@ static ox::Error pack(ox::StringView argSrc, ox::StringView argRomBin, ox::Strin ox::FileSystem32 dst(dstBuff); oxRequire(ctx, keel::init(ox::make_unique(argSrc), "keel-pack")); keel::TypeStore ts(*ctx->rom, ox::sfmt("{}/type_descriptors", projectDataDir)); - oxReturnError(generateTypes(&ts)); + oxReturnError(generateTypes(ts)); oxReturnError(keel::pack(*ctx, ts, dst)); oxRequireM(pl, keel::GbaPreloader::make()); oxReturnError(preload(ts, dst, *pl)); @@ -62,14 +62,13 @@ static ox::Error pack(ox::StringView argSrc, ox::StringView argRomBin, ox::Strin // resize buffer oxRequire(dstSize, dst.size()); dstBuff.resize(dstSize); - + // concatenate ROM segments oxRequireM(romBuff, readFileBuff(argRomBin)); - oxReturnError(appendBinary(romBuff, dstBuff, *pl)); - + oxOutf("Input exe size: {} bytes\n", romBuff.size()); oxOutf("Dest FS size: {} bytes\n", dstSize); oxOutf("Preload buff size: {} bytes\n", pl->buff().size()); - oxOutf("ROM buff size: {} bytes\n", romBuff.size()); - + oxReturnError(appendBinary(romBuff, dstBuff, *pl)); + oxOutf("Final ROM buff size: {} bytes\n", romBuff.size()); oxReturnError(writeFileBuff(argRomBin, romBuff)); return {}; } diff --git a/src/olympic/keel/src/pack.cpp b/src/olympic/keel/src/pack.cpp index b94e364..09bda0e 100644 --- a/src/olympic/keel/src/pack.cpp +++ b/src/olympic/keel/src/pack.cpp @@ -30,11 +30,11 @@ static ox::Error pathToInode( } if (beginsWith(path, "uuid://")) { auto const uuid = ox::substr(path, 7); - oxReturnError(keel::uuidToPath(ctx, uuid).moveTo(path)); + oxReturnError(keel::uuidToPath(ctx, uuid).to().moveTo(path)); } oxRequire(s, dest.stat(path)); oxReturnError(o.at("type").unwrap()->set(static_cast(ox::FileAddressType::Inode))); - oxOutf("path to inode: {} => {}\n", path, s.inode); + oxOutf("\tpath to inode: {} => {}\n", path, s.inode); return data.set(2, s.inode); } @@ -97,6 +97,7 @@ static ox::Error doTransformations( oxReturnError(keel::performPackTransforms(ctx, buff)); // transform FileAddresses oxRequireM(obj, keel::readAsset(ts, buff)); + oxOutf("transforming {}\n", filePath); oxReturnError(transformFileAddressesObj(ctx, dest, obj)); oxReturnError(ox::writeClaw(obj).moveTo(buff)); // write file to dest @@ -143,16 +144,15 @@ static ox::Error copy( if (beginsWith(name, ".")) { continue; } - oxOutf("reading {}\n", currentFile); oxRequire(stat, src.stat(currentFile)); if (stat.fileType == ox::FileType::Directory) { oxReturnError(dest.mkdir(currentFile, true)); oxReturnError(copy(src, dest, currentFile + '/')); } else { // load file + oxOutf("copying file: {}\n", currentFile); oxRequireM(buff, src.read(currentFile)); // write file to dest - oxOutf("writing {}\n", currentFile); oxReturnError(dest.write(currentFile, buff)); } } diff --git a/src/olympic/studio/applib/src/studioapp.cpp b/src/olympic/studio/applib/src/studioapp.cpp index 22daae1..0cf5617 100644 --- a/src/olympic/studio/applib/src/studioapp.cpp +++ b/src/olympic/studio/applib/src/studioapp.cpp @@ -2,6 +2,7 @@ * Copyright 2016 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved. */ +#include #include #include @@ -196,10 +197,10 @@ void StudioUI::drawMenu() noexcept { if (ImGui::BeginMenu("Edit")) { auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; if (ImGui::MenuItem("Undo", "Ctrl+Z", false, undoStack && undoStack->canUndo())) { - undoStack->undo(); + oxLogError(undoStack->undo()); } if (ImGui::MenuItem("Redo", "Ctrl+Y", false, undoStack && undoStack->canRedo())) { - undoStack->redo(); + oxLogError(undoStack->redo()); } ImGui::Separator(); if (ImGui::MenuItem("Copy", "Ctrl+C", false, m_activeEditor && m_activeEditor->copyEnabled())) { @@ -208,7 +209,7 @@ void StudioUI::drawMenu() noexcept { if (ImGui::MenuItem("Cut", "Ctrl+X", false, m_activeEditor && m_activeEditor->cutEnabled())) { m_activeEditor->cut(); } - if (ImGui::MenuItem("Paste", "Ctrl+V", false, m_activeEditor && m_activeEditor->pasteEnabled()) && m_activeEditor) { + if (ImGui::MenuItem("Paste", "Ctrl+V", false, m_activeEditor && m_activeEditor->pasteEnabled())) { m_activeEditor->paste(); } ImGui::EndMenu(); @@ -314,14 +315,14 @@ void StudioUI::toggleProjectExplorer() noexcept { void StudioUI::redo() noexcept { auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; if (undoStack && undoStack->canRedo()) { - m_activeEditor->undoStack()->redo(); + oxLogError(m_activeEditor->undoStack()->redo()); } } void StudioUI::undo() noexcept { auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; if (undoStack && undoStack->canUndo()) { - m_activeEditor->undoStack()->undo(); + oxLogError(m_activeEditor->undoStack()->undo()); } } diff --git a/src/olympic/studio/modlib/include/studio/context.hpp b/src/olympic/studio/modlib/include/studio/context.hpp index de15c8c..21cb042 100644 --- a/src/olympic/studio/modlib/include/studio/context.hpp +++ b/src/olympic/studio/modlib/include/studio/context.hpp @@ -22,4 +22,9 @@ struct StudioContext { ui(pUi), tctx(pTctx) {} }; +[[nodiscard]] +inline keel::Context &keelCtx(StudioContext &ctx) noexcept { + return keelCtx(ctx.tctx); +} + } diff --git a/src/olympic/studio/modlib/include/studio/editor.hpp b/src/olympic/studio/modlib/include/studio/editor.hpp index 59b7e2f..ab096b8 100644 --- a/src/olympic/studio/modlib/include/studio/editor.hpp +++ b/src/olympic/studio/modlib/include/studio/editor.hpp @@ -43,6 +43,9 @@ class BaseEditor: public Widget { virtual void paste(); + [[nodiscard]] + virtual bool acceptsClipboardPayload() const noexcept; + virtual void exportFile(); virtual void keyStateChanged(turbine::Key key, bool down); @@ -131,14 +134,14 @@ class Editor: public studio::BaseEditor { [[nodiscard]] UndoStack *undoStack() noexcept final; - void pushCommand(ox::UPtr &&cmd) noexcept; + ox::Error pushCommand(ox::UPtr &&cmd) noexcept; template - void pushCommand(Args&&... args) noexcept { + ox::Error pushCommand(Args&&... args) noexcept { try { - m_undoStack.push(ox::make_unique(ox::forward(args)...)); + return m_undoStack.push(ox::make_unique(ox::forward(args)...)); } catch (ox::Exception const&ex) { - oxLogError(ex.toError()); + return ex.toError(); } } diff --git a/src/olympic/studio/modlib/include/studio/imguiutil.hpp b/src/olympic/studio/modlib/include/studio/imguiutil.hpp index 4632b6e..bab5e7d 100644 --- a/src/olympic/studio/modlib/include/studio/imguiutil.hpp +++ b/src/olympic/studio/modlib/include/studio/imguiutil.hpp @@ -34,7 +34,7 @@ ox::Result getDragDropPayload(ox::CStringView name) noexcept { static_cast(payload->DataSize)}); } -ox::Error setDragDropPayload(ox::CStringView name, auto const &obj) noexcept { +ox::Error setDragDropPayload(ox::CStringView name, auto const&obj) noexcept { oxRequire(buff, ox::writeClaw(obj, ox::ClawFormat::Metal)); ImGui::SetDragDropPayload(name.c_str(), buff.data(), buff.size()); return {}; @@ -148,6 +148,19 @@ bool BeginPopup(turbine::Context &ctx, ox::CStringView popupName, bool &show, Im */ bool ComboBox(ox::CStringView lbl, ox::SpanView list, size_t &selectedIdx) noexcept; +/** + * + * @param lbl + * @param callback + * @param selectedIdx + * @return true if new value selected, false otherwise + */ +bool ComboBox( + ox::CStringView lbl, + std::function const&f, + size_t strCnt, + size_t &selectedIdx) noexcept; + bool FileComboBox( ox::CStringView lbl, studio::StudioContext &sctx, diff --git a/src/olympic/studio/modlib/include/studio/project.hpp b/src/olympic/studio/modlib/include/studio/project.hpp index 2b2d9f3..223fc09 100644 --- a/src/olympic/studio/modlib/include/studio/project.hpp +++ b/src/olympic/studio/modlib/include/studio/project.hpp @@ -31,7 +31,7 @@ enum class ProjectEvent { constexpr ox::Result fileExt(ox::CRStringView path) noexcept { auto const extStart = ox::find(path.crbegin(), path.crend(), '.').offset(); if (!extStart) { - return OxError(1, "Cannot open a file without valid extension."); + return OxError(1, "file path does not have valid extension"); } return substr(path, extStart + 1); } @@ -47,6 +47,7 @@ constexpr ox::StringView parentDir(ox::StringView path) noexcept { class Project { private: + ox::SmallMap> m_typeFmt; keel::Context &m_ctx; ox::String m_path; ox::String m_projectDataDir; @@ -75,7 +76,15 @@ class Project { ox::Error writeObj( ox::CRStringView path, T const&obj, - ox::ClawFormat fmt = ox::ClawFormat::Metal) noexcept; + ox::ClawFormat fmt) noexcept; + + /** + * Writes a MetalClaw object to the project at the given path. + */ + template + ox::Error writeObj( + ox::CRStringView path, + T const&obj) noexcept; template ox::Result loadObj(ox::CRStringView path) const noexcept; @@ -115,7 +124,7 @@ class Project { // file. ox::Signal fileRecognized; ox::Signal fileDeleted; - ox::Signal fileUpdated; + ox::Signal fileUpdated; }; @@ -130,15 +139,24 @@ ox::Error Project::writeObj(ox::CRStringView path, T const&obj, ox::ClawFormat f oxReturnError(ox::buildTypeDef(&m_typeStore, &obj)); } oxRequire(desc, m_typeStore.get()); - auto const descExists = m_fs.stat(ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*desc))).error != 0; + auto const descPath = ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*desc)); + auto const descExists = m_fs.exists(descPath); if (!descExists) { oxReturnError(writeTypeStore()); } - oxReturnError(keel::setAsset(m_ctx, path, obj)); - fileUpdated.emit(path); + oxReturnError(keel::reloadAsset(m_ctx, path)); + oxRequire(uuid, pathToUuid(m_ctx, path)); + fileUpdated.emit(path, uuid); return {}; } +template +ox::Error Project::writeObj(ox::CRStringView path, T const&obj) noexcept { + oxRequire(ext, fileExt(path)); + auto const fmt = m_typeFmt[ext].or_value(ox::ClawFormat::Metal); + return writeObj(path, obj, fmt); +} + template ox::Result Project::loadObj(ox::CRStringView path) const noexcept { oxRequire(buff, loadBuff(path)); diff --git a/src/olympic/studio/modlib/include/studio/selectiontracker.hpp b/src/olympic/studio/modlib/include/studio/selectiontracker.hpp new file mode 100644 index 0000000..ad6882b --- /dev/null +++ b/src/olympic/studio/modlib/include/studio/selectiontracker.hpp @@ -0,0 +1,133 @@ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace studio { + +struct Selection { + ox::Point a, b; + constexpr Selection() noexcept = default; + constexpr Selection(ox::Point const&pA, ox::Point const&pB) noexcept: a(pA), b(pB) {} + [[nodiscard]] + constexpr ox::Size size() const noexcept { + return {b.x - a.x, b.y - a.y}; + } + [[nodiscard]] + constexpr bool contains(ox::Point const&pt) const noexcept { + return a.x <= pt.x && a.y <= pt.y + && b.x >= pt.x && b.y >= pt.y; + } +}; + +constexpr auto iterateSelection(studio::Selection const&sel, auto const&cb) { + constexpr auto retErr = ox::is_same_v; + for (auto x = sel.a.x; x <= sel.b.x; ++x) { + for (auto y = sel.a.y; y <= sel.b.y; ++y) { + if constexpr(retErr) { + oxReturnError(cb(x, y)); + } else { + cb(x, y); + } + } + } + if constexpr(retErr) { + return ox::Error{}; + } +}; + +constexpr auto iterateSelectionRows(studio::Selection const&sel, auto const&cb) { + constexpr auto retErr = ox::is_same_v; + for (auto y = sel.a.y; y <= sel.b.y; ++y) { + for (auto x = sel.a.x; x <= sel.b.x; ++x) { + if constexpr(retErr) { + oxReturnError(cb(x, y)); + } else { + cb(x, y); + } + } + } + if constexpr(retErr) { + return ox::Error{}; + } +}; + +class SelectionTracker { + private: + bool m_selectionOngoing{}; + ox::Point m_pointA; + ox::Point m_pointB; + public: + [[nodiscard]] + constexpr bool selectionOngoing() const noexcept { + return m_selectionOngoing; + } + + constexpr void startSelection(ox::Point cursor) noexcept { + m_pointA = cursor; + m_pointB = cursor; + m_selectionOngoing = true; + } + + /** + * + * @param cursor + * @param allowStart + * @return true if changed, false otherwise + */ + constexpr bool updateCursorPoint(ox::Point cursor, bool allowStart = true) noexcept { + auto changed = false; + if (!m_selectionOngoing && allowStart) { + m_pointA = cursor; + m_selectionOngoing = true; + } + if (m_selectionOngoing) { + m_pointB = cursor; + changed = true; + } + return changed; + } + + constexpr void updateCursorPoint(ox::Vec2 cursor, bool allowStart = true) noexcept { + updateCursorPoint( + ox::Point{ + static_cast(cursor.x), + static_cast(cursor.y), + }, + allowStart); + } + + constexpr void updateCursorPoint(ImVec2 cursor, bool allowStart = true) noexcept { + updateCursorPoint( + ox::Point{ + static_cast(cursor.x), + static_cast(cursor.y), + }, + allowStart); + } + + constexpr void finishSelection() noexcept { + m_selectionOngoing = {}; + } + + [[nodiscard]] + constexpr Selection selection() const noexcept { + return { + { + ox::min(m_pointA.x, m_pointB.x), + ox::min(m_pointA.y, m_pointB.y), + }, + { + ox::max(m_pointA.x, m_pointB.x), + ox::max(m_pointA.y, m_pointB.y), + }, + }; + } +}; + +} \ No newline at end of file diff --git a/src/olympic/studio/modlib/include/studio/studio.hpp b/src/olympic/studio/modlib/include/studio/studio.hpp index 9e47156..674186e 100644 --- a/src/olympic/studio/modlib/include/studio/studio.hpp +++ b/src/olympic/studio/modlib/include/studio/studio.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/olympic/studio/modlib/include/studio/undocommand.hpp b/src/olympic/studio/modlib/include/studio/undocommand.hpp index 9f7bb84..93aa2a2 100644 --- a/src/olympic/studio/modlib/include/studio/undocommand.hpp +++ b/src/olympic/studio/modlib/include/studio/undocommand.hpp @@ -4,16 +4,26 @@ #pragma once +#include + +#include + namespace studio { +class NoChangesException: public ox::Exception { + public: + inline NoChangesException(std::source_location sloc = std::source_location::current()): + ox::Exception(sloc.file_name(), sloc.line(), 1, "Command makes no changes.") {} +}; + class UndoCommand { public: virtual ~UndoCommand() noexcept = default; - virtual void redo() noexcept = 0; - virtual void undo() noexcept = 0; + virtual ox::Error redo() noexcept = 0; + virtual ox::Error undo() noexcept = 0; [[nodiscard]] virtual int commandId() const noexcept = 0; - virtual bool mergeWith(UndoCommand const*cmd) noexcept; + virtual bool mergeWith(UndoCommand const&cmd) noexcept; }; } diff --git a/src/olympic/studio/modlib/include/studio/undostack.hpp b/src/olympic/studio/modlib/include/studio/undostack.hpp index 3ef654b..b6a5d56 100644 --- a/src/olympic/studio/modlib/include/studio/undostack.hpp +++ b/src/olympic/studio/modlib/include/studio/undostack.hpp @@ -19,11 +19,11 @@ class UndoStack { std::size_t m_stackIdx = 0; public: - void push(ox::UPtr &&cmd) noexcept; + ox::Error push(ox::UPtr &&cmd) noexcept; - void redo() noexcept; + ox::Error redo() noexcept; - void undo() noexcept; + ox::Error undo() noexcept; [[nodiscard]] constexpr bool canRedo() const noexcept { diff --git a/src/olympic/studio/modlib/src/editor.cpp b/src/olympic/studio/modlib/src/editor.cpp index 1c78de6..4edc39c 100644 --- a/src/olympic/studio/modlib/src/editor.cpp +++ b/src/olympic/studio/modlib/src/editor.cpp @@ -23,6 +23,10 @@ void BaseEditor::copy() { void BaseEditor::paste() { } +bool BaseEditor::acceptsClipboardPayload() const noexcept { + return {}; +} + void BaseEditor::exportFile() { } @@ -95,7 +99,7 @@ void BaseEditor::setPasteEnabled(bool v) { } bool BaseEditor::pasteEnabled() const noexcept { - return m_pasteEnabled; + return m_pasteEnabled && acceptsClipboardPayload(); } ox::Error BaseEditor::saveItem() noexcept { @@ -127,8 +131,8 @@ ox::CStringView Editor::itemDisplayName() const noexcept { return m_itemName; } -void Editor::pushCommand(ox::UPtr &&cmd) noexcept { - m_undoStack.push(std::move(cmd)); +ox::Error Editor::pushCommand(ox::UPtr &&cmd) noexcept { + return m_undoStack.push(std::move(cmd)); } UndoStack *Editor::undoStack() noexcept { diff --git a/src/olympic/studio/modlib/src/imguiutil.cpp b/src/olympic/studio/modlib/src/imguiutil.cpp index a90f7c2..0af15b9 100644 --- a/src/olympic/studio/modlib/src/imguiutil.cpp +++ b/src/olympic/studio/modlib/src/imguiutil.cpp @@ -100,6 +100,26 @@ bool ComboBox( return out; } +bool ComboBox( + ox::CStringView lbl, + std::function const&f, + size_t strCnt, + size_t &selectedIdx) noexcept { + bool out{}; + auto const first = selectedIdx < strCnt ? f(selectedIdx).c_str() : ""; + if (ImGui::BeginCombo(lbl.c_str(), first, 0)) { + for (auto i = 0u; i < strCnt; ++i) { + const auto selected = (selectedIdx == i); + if (ImGui::Selectable(f(i).c_str(), selected) && selectedIdx != i) { + selectedIdx = i; + out = true; + } + } + ImGui::EndCombo(); + } + return out; +} + bool FileComboBox( ox::CStringView lbl, studio::StudioContext &sctx, diff --git a/src/olympic/studio/modlib/src/project.cpp b/src/olympic/studio/modlib/src/project.cpp index 02b19ac..9b9843a 100644 --- a/src/olympic/studio/modlib/src/project.cpp +++ b/src/olympic/studio/modlib/src/project.cpp @@ -56,9 +56,13 @@ ox::FileSystem &Project::romFs() noexcept { } ox::Error Project::mkdir(ox::CRStringView path) const noexcept { - oxReturnError(m_fs.mkdir(path, true)); - fileUpdated.emit(path); - return {}; + auto const [stat, err] = m_fs.stat(path); + if (err) { + oxReturnError(m_fs.mkdir(path, true)); + fileUpdated.emit(path, {}); + } + return stat.fileType == ox::FileType::Directory ? + ox::Error{} : OxError(1, "path exists as normal file"); } ox::Result Project::stat(ox::CRStringView path) const noexcept { @@ -115,9 +119,9 @@ ox::Error Project::writeBuff(ox::CRStringView path, ox::Buffer const&buff) noexc ox::Buffer outBuff; outBuff.reserve(buff.size() + HdrSz); ox::BufferWriter writer(&outBuff); - auto const [uuid, err] = m_ctx.pathToUuid.at(path); + auto const [uuid, err] = pathToUuid(m_ctx, path); if (!err) { - oxReturnError(keel::writeUuidHeader(writer, *uuid)); + oxReturnError(keel::writeUuidHeader(writer, uuid)); } oxReturnError(writer.write(buff.data(), buff.size())); auto const newFile = m_fs.stat(path).error != 0; @@ -126,7 +130,7 @@ ox::Error Project::writeBuff(ox::CRStringView path, ox::Buffer const&buff) noexc fileAdded.emit(path); indexFile(path); } else { - fileUpdated.emit(path); + fileUpdated.emit(path, uuid); } return {}; } diff --git a/src/olympic/studio/modlib/src/undocommand.cpp b/src/olympic/studio/modlib/src/undocommand.cpp index 72fee36..6a929ca 100644 --- a/src/olympic/studio/modlib/src/undocommand.cpp +++ b/src/olympic/studio/modlib/src/undocommand.cpp @@ -3,7 +3,7 @@ namespace studio { -bool UndoCommand::mergeWith(UndoCommand const*) noexcept { +bool UndoCommand::mergeWith(UndoCommand const&) noexcept { return false; } diff --git a/src/olympic/studio/modlib/src/undostack.cpp b/src/olympic/studio/modlib/src/undostack.cpp index 3d34858..77cb049 100644 --- a/src/olympic/studio/modlib/src/undostack.cpp +++ b/src/olympic/studio/modlib/src/undostack.cpp @@ -6,35 +6,39 @@ namespace studio { -void UndoStack::push(ox::UPtr &&cmd) noexcept { +ox::Error UndoStack::push(ox::UPtr &&cmd) noexcept { for (auto const i = m_stackIdx; i < m_stack.size();) { std::ignore = m_stack.erase(i); } - cmd->redo(); + oxReturnError(cmd->redo()); redoTriggered.emit(cmd.get()); changeTriggered.emit(cmd.get()); - if (m_stack.empty() || !(*m_stack.back().value)->mergeWith(cmd.get())) { + if (m_stack.empty() || !(*m_stack.back().value)->mergeWith(*cmd)) { m_stack.emplace_back(std::move(cmd)); ++m_stackIdx; } + return {}; } -void UndoStack::redo() noexcept { +ox::Error UndoStack::redo() noexcept { if (m_stackIdx < m_stack.size()) { - auto &c = m_stack[m_stackIdx++]; - c->redo(); + auto &c = m_stack[m_stackIdx]; + oxReturnError(c->redo()); + ++m_stackIdx; redoTriggered.emit(c.get()); changeTriggered.emit(c.get()); } + return {}; } -void UndoStack::undo() noexcept { +ox::Error UndoStack::undo() noexcept { if (m_stackIdx) { auto &c = m_stack[--m_stackIdx]; - c->undo(); + oxReturnError(c->undo()); undoTriggered.emit(c.get()); changeTriggered.emit(c.get()); } + return {}; } } diff --git a/src/olympic/turbine/include/turbine/clipboard.hpp b/src/olympic/turbine/include/turbine/clipboard.hpp index 009b0b7..83de726 100644 --- a/src/olympic/turbine/include/turbine/clipboard.hpp +++ b/src/olympic/turbine/include/turbine/clipboard.hpp @@ -17,19 +17,14 @@ class BaseClipboardObject { virtual ~BaseClipboardObject() noexcept = default; [[nodiscard]] - virtual ox::String typeId() const noexcept = 0; - - [[nodiscard]] - constexpr auto typeMatch(ox::StringView name, int version) const noexcept { - return typeId() == ox::buildTypeId(name, version); - } + virtual ox::StringView typeId() const noexcept = 0; }; template class ClipboardObject: public BaseClipboardObject { [[nodiscard]] - ox::String typeId() const noexcept final { - return ox::buildTypeId(T::TypeName, T::TypeVersion); + ox::StringView typeId() const noexcept final { + return ox::ModelTypeId_v; } }; @@ -39,11 +34,11 @@ void setClipboardText(Context &ctx, ox::CRStringView text) noexcept; void setClipboardObject(Context &ctx, ox::UPtr &&obj) noexcept; -ox::Result getClipboardData(Context &ctx, ox::StringView typeName, int typeVersion) noexcept; +ox::Result getClipboardData(Context &ctx, ox::StringView typeId) noexcept; template ox::Result getClipboardObject(Context &ctx) noexcept { - oxRequire(p, getClipboardData(ctx, T::TypeName, T::TypeVersion)); + oxRequire(p, getClipboardData(ctx, ox::ModelTypeId_v)); return dynamic_cast(p); } diff --git a/src/olympic/turbine/src/glfw/clipboard.cpp b/src/olympic/turbine/src/glfw/clipboard.cpp index e23d3ce..2fd824b 100644 --- a/src/olympic/turbine/src/glfw/clipboard.cpp +++ b/src/olympic/turbine/src/glfw/clipboard.cpp @@ -26,8 +26,8 @@ void setClipboardObject(Context &ctx, ox::UPtr &&obj) noexc ctx.clipboard = std::move(obj); } -ox::Result getClipboardData(Context &ctx, ox::StringView typeName, int typeVersion) noexcept { - if (ctx.clipboard && ctx.clipboard->typeMatch(typeName, typeVersion)) { +ox::Result getClipboardData(Context &ctx, ox::StringView typeId) noexcept { + if (ctx.clipboard && ctx.clipboard->typeId() == typeId) { return ctx.clipboard.get(); } return OxError(1);