From 0b91382f0fea5892684d3f4b026c21caceff3160 Mon Sep 17 00:00:00 2001 From: shweet Date: Sun, 29 Mar 2026 20:13:24 -0400 Subject: [PATCH 1/2] changes + more windows bs --- .codex | 0 .vscode/tasks.json | 4 +- src/anm2/anm2.cpp | 65 ++++++++++++++++++++++++++ src/anm2/anm2.hpp | 4 +- src/anm2/anm2_spritesheets.cpp | 31 ++++++------ src/imgui/window/animation_preview.cpp | 22 +++------ src/imgui/window/frame_properties.cpp | 24 +++++++--- src/imgui/window/spritesheets.cpp | 7 ++- src/imgui/wizard/render_animation.cpp | 2 +- src/log.cpp | 6 +-- src/log.hpp | 3 +- src/manager.cpp | 37 ++++++--------- src/resource/strings.hpp | 2 + src/settings.hpp | 1 + src/util/file_.cpp | 33 ++++++++++++- src/util/file_.hpp | 10 +++- 16 files changed, 179 insertions(+), 72 deletions(-) create mode 100644 .codex diff --git a/.codex b/.codex new file mode 100644 index 0000000..e69de29 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 81fe5dd..77b7821 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,7 +24,7 @@ "--build", "out/build/linux-debug", "--parallel", - "8", + "16", "--target", "anm2ed" ], @@ -67,7 +67,7 @@ "--build", "out/build/linux-release", "--parallel", - "8", + "16", "--target", "anm2ed" ], diff --git a/src/anm2/anm2.cpp b/src/anm2/anm2.cpp index f72cea4..335994f 100644 --- a/src/anm2/anm2.cpp +++ b/src/anm2/anm2.cpp @@ -2,10 +2,12 @@ #include #include +#include #include #include "file_.hpp" #include "map_.hpp" +#include "math_.hpp" #include "time_.hpp" #include "vector_.hpp" #include "working_directory.hpp" @@ -58,6 +60,69 @@ namespace anm2ed::anm2 { Anm2::Anm2() { info.createdOn = time::get("%m/%d/%Y %I:%M:%S %p"); } + Frame Anm2::frame_effective(int layerId, const Frame& frame) const + { + auto resolved = frame; + if (frame.regionID == -1) return resolved; + if (!content.layers.contains(layerId)) return resolved; + + auto spritesheet = const_cast(this)->spritesheet_get(content.layers.at(layerId).spritesheetID); + if (!spritesheet) return resolved; + + auto regionIt = spritesheet->regions.find(frame.regionID); + if (regionIt == spritesheet->regions.end()) return resolved; + + resolved.crop = regionIt->second.crop; + resolved.size = regionIt->second.size; + resolved.pivot = regionIt->second.pivot; + return resolved; + } + + vec4 Anm2::animation_rect(Animation& animation, bool isRootTransform) const + { + constexpr ivec2 CORNERS[4] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; + + float minX = std::numeric_limits::infinity(); + float minY = std::numeric_limits::infinity(); + float maxX = -std::numeric_limits::infinity(); + float maxY = -std::numeric_limits::infinity(); + bool any = false; + + for (float t = 0.0f; t < (float)animation.frameNum; t += 1.0f) + { + mat4 transform(1.0f); + + if (isRootTransform) + { + auto root = animation.rootAnimation.frame_generate(t, ROOT); + transform *= math::quad_model_parent_get(root.position, {}, math::percent_to_unit(root.scale), root.rotation); + } + + for (auto& [id, layerAnimation] : animation.layerAnimations) + { + if (!layerAnimation.isVisible) continue; + + auto frame = frame_effective(id, layerAnimation.frame_generate(t, LAYER)); + if (frame.size == vec2() || !frame.isVisible) continue; + + auto layerTransform = transform * math::quad_model_get(frame.size, frame.position, frame.pivot, + math::percent_to_unit(frame.scale), frame.rotation); + for (auto& corner : CORNERS) + { + vec4 world = layerTransform * vec4(corner, 0.0f, 1.0f); + minX = std::min(minX, world.x); + minY = std::min(minY, world.y); + maxX = std::max(maxX, world.x); + maxY = std::max(maxY, world.y); + any = true; + } + } + } + + if (!any) return vec4(-1.0f); + return {minX, minY, maxX - minX, maxY - minY}; + } + Anm2::Anm2(const std::filesystem::path& path, std::string* errorString) { XMLDocument document; diff --git a/src/anm2/anm2.hpp b/src/anm2/anm2.hpp index 12c0b41..16c40ef 100644 --- a/src/anm2/anm2.hpp +++ b/src/anm2/anm2.hpp @@ -45,7 +45,7 @@ namespace anm2ed::anm2 std::vector spritesheet_labels_get(); std::vector spritesheet_ids_get(); std::set spritesheets_unused(); - bool spritesheets_merge(const std::set&, SpritesheetMergeOrigin, bool, origin::Type); + bool spritesheets_merge(const std::set&, SpritesheetMergeOrigin, bool, bool, origin::Type); bool spritesheets_deserialize(const std::string&, const std::filesystem::path&, types::merge::Type type, std::string*); std::vector region_labels_get(Spritesheet&); @@ -79,6 +79,8 @@ namespace anm2ed::anm2 std::vector animation_labels_get(); int animations_merge(int, std::set&, types::merge::Type = types::merge::APPEND, bool = true); bool animations_deserialize(const std::string&, int, std::set&, std::string* = nullptr); + Frame frame_effective(int, const Frame&) const; + glm::vec4 animation_rect(Animation&, bool) const; Item* item_get(int, Type, int = -1); Reference layer_animation_add(Reference = {}, int = -1, std::string = {}, int = 0, diff --git a/src/anm2/anm2_spritesheets.cpp b/src/anm2/anm2_spritesheets.cpp index b99a7b3..88dc4d7 100644 --- a/src/anm2/anm2_spritesheets.cpp +++ b/src/anm2/anm2_spritesheets.cpp @@ -404,7 +404,7 @@ namespace anm2ed::anm2 } bool Anm2::spritesheets_merge(const std::set& ids, SpritesheetMergeOrigin mergeOrigin, bool isMakeRegions, - origin::Type regionOrigin) + bool isMakePrimaryRegion, origin::Type regionOrigin) { if (ids.size() < 2) return false; @@ -447,19 +447,22 @@ namespace anm2ed::anm2 base.regionOrder.push_back(id); } - auto baseLocationRegionID = map::next_id_get(base.regions); - auto baseFilename = path::to_utf8(base.path.stem()); - auto baseLocationRegionName = baseFilename.empty() ? std::format("#{}", baseId) : baseFilename; - auto baseLocationRegionPivot = - regionOrigin == origin::ORIGIN_CENTER ? glm::vec2(baseTextureSize) * 0.5f : glm::vec2(); - base.regions[baseLocationRegionID] = { - .name = baseLocationRegionName, - .crop = {}, - .pivot = glm::ivec2(baseLocationRegionPivot), - .size = baseTextureSize, - .origin = regionOrigin, - }; - base.regionOrder.push_back(baseLocationRegionID); + if (isMakePrimaryRegion) + { + auto baseLocationRegionID = map::next_id_get(base.regions); + auto baseFilename = path::to_utf8(base.path.stem()); + auto baseLocationRegionName = baseFilename.empty() ? std::format("#{}", baseId) : baseFilename; + auto baseLocationRegionPivot = + regionOrigin == origin::ORIGIN_CENTER ? glm::vec2(baseTextureSize) * 0.5f : glm::vec2(); + base.regions[baseLocationRegionID] = { + .name = baseLocationRegionName, + .crop = {}, + .pivot = glm::ivec2(baseLocationRegionPivot), + .size = baseTextureSize, + .origin = regionOrigin, + }; + base.regionOrder.push_back(baseLocationRegionID); + } for (auto id : ids) { diff --git a/src/imgui/window/animation_preview.cpp b/src/imgui/window/animation_preview.cpp index 82cea0d..5542a3e 100644 --- a/src/imgui/window/animation_preview.cpp +++ b/src/imgui/window/animation_preview.cpp @@ -489,10 +489,10 @@ namespace anm2ed::imgui auto center_view = [&]() { pan = vec2(); }; - auto fit_view = [&]() - { - if (animation) set_to_rect(zoom, pan, animation->rect(isRootTransform)); - }; + auto fit_view = [&]() + { + if (animation) set_to_rect(zoom, pan, anm2.animation_rect(*animation, isRootTransform)); + }; auto zoom_adjust = [&](float delta) { @@ -628,7 +628,7 @@ namespace anm2ed::imgui savedZoom = zoom; savedPan = pan; - if (auto rect = document.animation_get()->rect(isRootTransform); rect != vec4(-1.0f)) + if (auto rect = anm2.animation_rect(*document.animation_get(), isRootTransform); rect != vec4(-1.0f)) { size_set(vec2(rect.z, rect.w) * settings.renderScale); set_to_rect(zoom, pan, rect); @@ -798,21 +798,11 @@ namespace anm2ed::imgui auto texSize = vec2(texture.size); if (texSize.x <= 0.0f || texSize.y <= 0.0f) return; + frame = anm2.frame_effective(id, frame); auto crop = frame.crop; auto size = frame.size; auto pivot = frame.pivot; - if (frame.regionID != -1) - { - auto regionIt = spritesheet->regions.find(frame.regionID); - if (regionIt != spritesheet->regions.end()) - { - crop = regionIt->second.crop; - size = regionIt->second.size; - pivot = regionIt->second.pivot; - } - } - auto layerModel = math::quad_model_get(size, frame.position, pivot, math::percent_to_unit(frame.scale), frame.rotation); auto layerTransform = sampleTransform * layerModel; diff --git a/src/imgui/window/frame_properties.cpp b/src/imgui/window/frame_properties.cpp index c40c214..0588b8b 100644 --- a/src/imgui/window/frame_properties.cpp +++ b/src/imgui/window/frame_properties.cpp @@ -42,6 +42,9 @@ namespace anm2ed::imgui { auto frame = document.frame_get(); auto useFrame = frame ? *frame : anm2::Frame(); + auto displayFrame = frame && type == anm2::LAYER && document.reference.itemID != -1 + ? document.anm2.frame_effective(document.reference.itemID, *frame) + : useFrame; ImGui::BeginDisabled(!frame); { @@ -112,18 +115,20 @@ namespace anm2ed::imgui bool isRegionSet = frame && frame->regionID != -1; ImGui::BeginDisabled(type == anm2::ROOT || type == anm2::NULL_ || isRegionSet); { + auto cropDisplay = frame ? displayFrame.crop : vec2(); auto cropEdit = - drag_float2_persistent(localize.get(BASIC_CROP), frame ? &frame->crop : &dummy_value(), - DRAG_SPEED, 0.0f, 0.0f, frame ? vec2_format_get(frame->crop) : ""); + drag_float2_persistent(localize.get(BASIC_CROP), frame ? &cropDisplay : &dummy_value(), + DRAG_SPEED, 0.0f, 0.0f, frame ? vec2_format_get(displayFrame.crop) : ""); if (cropEdit == edit::START) document.snapshot(localize.get(EDIT_FRAME_CROP)); else if (cropEdit == edit::END) document.change(Document::FRAMES); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CROP)); + auto sizeDisplay = frame ? displayFrame.size : vec2(); auto sizeEdit = - drag_float2_persistent(localize.get(BASIC_SIZE), frame ? &frame->size : &dummy_value(), - DRAG_SPEED, 0.0f, 0.0f, frame ? vec2_format_get(frame->size) : ""); + drag_float2_persistent(localize.get(BASIC_SIZE), frame ? &sizeDisplay : &dummy_value(), + DRAG_SPEED, 0.0f, 0.0f, frame ? vec2_format_get(displayFrame.size) : ""); if (sizeEdit == edit::START) document.snapshot(localize.get(EDIT_FRAME_SIZE)); else if (sizeEdit == edit::END) @@ -143,9 +148,10 @@ namespace anm2ed::imgui ImGui::BeginDisabled(type == anm2::ROOT || type == anm2::NULL_ || isRegionSet); { + auto pivotDisplay = frame ? displayFrame.pivot : vec2(); auto pivotEdit = - drag_float2_persistent(localize.get(BASIC_PIVOT), frame ? &frame->pivot : &dummy_value(), - DRAG_SPEED, 0.0f, 0.0f, frame ? vec2_format_get(frame->pivot) : ""); + drag_float2_persistent(localize.get(BASIC_PIVOT), frame ? &pivotDisplay : &dummy_value(), + DRAG_SPEED, 0.0f, 0.0f, frame ? vec2_format_get(displayFrame.pivot) : ""); if (pivotEdit == edit::START) document.snapshot(localize.get(EDIT_FRAME_PIVOT)); else if (pivotEdit == edit::END) @@ -201,7 +207,11 @@ namespace anm2ed::imgui regionIds, regionLabels) && frame) DOCUMENT_EDIT(document, localize.get(EDIT_SET_REGION_PROPERTIES), Document::FRAMES, - frame->regionID = useFrame.regionID); + frame->regionID = useFrame.regionID; + auto effectiveFrame = document.anm2.frame_effective(document.reference.itemID, *frame); + frame->crop = effectiveFrame.crop; + frame->size = effectiveFrame.size; + frame->pivot = effectiveFrame.pivot); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_REGION)); ImGui::EndDisabled(); diff --git a/src/imgui/window/spritesheets.cpp b/src/imgui/window/spritesheets.cpp index fef8bfd..a5eb90a 100644 --- a/src/imgui/window/spritesheets.cpp +++ b/src/imgui/window/spritesheets.cpp @@ -185,6 +185,7 @@ namespace anm2ed::imgui auto baseID = *mergeSelection.begin(); if (anm2.spritesheets_merge(mergeSelection, (anm2::SpritesheetMergeOrigin)settings.mergeSpritesheetsOrigin, settings.mergeSpritesheetsIsMakeRegions, + settings.mergeSpritesheetsIsMakePrimaryRegion, (origin::Type)settings.mergeSpritesheetsRegionOrigin)) { selection = {baseID}; @@ -610,9 +611,13 @@ namespace anm2ed::imgui ImGui::Checkbox(localize.get(LABEL_MERGE_MAKE_SPRITESHEET_REGIONS), &settings.mergeSpritesheetsIsMakeRegions); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_MERGE_MAKE_SPRITESHEET_REGIONS)); + ImGui::BeginDisabled(!settings.mergeSpritesheetsIsMakeRegions); + ImGui::Checkbox(localize.get(LABEL_MERGE_MAKE_PRIMARY_SPRITESHEET_REGION), + &settings.mergeSpritesheetsIsMakePrimaryRegion); + ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_MERGE_MAKE_PRIMARY_SPRITESHEET_REGION)); + const char* regionOriginOptions[] = {localize.get(LABEL_REGION_ORIGIN_TOP_LEFT), localize.get(LABEL_REGION_ORIGIN_CENTER)}; - ImGui::BeginDisabled(!settings.mergeSpritesheetsIsMakeRegions); ImGui::Combo(localize.get(LABEL_REGION_PROPERTIES_ORIGIN), &settings.mergeSpritesheetsRegionOrigin, regionOriginOptions, IM_ARRAYSIZE(regionOriginOptions)); ImGui::EndDisabled(); diff --git a/src/imgui/wizard/render_animation.cpp b/src/imgui/wizard/render_animation.cpp index 1e976d9..2031b0d 100644 --- a/src/imgui/wizard/render_animation.cpp +++ b/src/imgui/wizard/render_animation.cpp @@ -181,7 +181,7 @@ namespace anm2ed::imgui::wizard ImGui::BeginDisabled(!isRaw); { - input_float_range(localize.get(BASIC_SCALE), scale, 1.0f, 100.0f, STEP, STEP_FAST, "%.1fx"); + input_float_range(localize.get(BASIC_SCALE), scale, 0.1f, 100.0f, 0.1f, STEP_FAST, "%.1fx"); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SCALE_OUTPUT)); } ImGui::EndDisabled(); diff --git a/src/log.cpp b/src/log.cpp index f5a3f24..e0de220 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -3,6 +3,7 @@ #include #include +#include "file_.hpp" #include "path_.hpp" #include "sdl.hpp" #include "time_.hpp" @@ -22,7 +23,7 @@ namespace anm2ed { std::lock_guard lock(mutex); std::println("{}", message); - if (file.is_open()) file << message << '\n' << std::flush; + if (!logPath.empty()) file::write_string(logPath, message + '\n', "ab"); } void Logger::write(const Level level, const std::string& message) @@ -132,7 +133,7 @@ namespace anm2ed void Logger::open(const std::filesystem::path& path) { - file.open(path, std::ios::out | std::ios::app); + logPath = path; stderr_redirect_start(); } @@ -148,7 +149,6 @@ namespace anm2ed { info("Exiting Anm2Ed"); stderr_redirect_stop(); - if (file.is_open()) file.close(); } } diff --git a/src/log.hpp b/src/log.hpp index dd7e692..e62f24b 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -45,7 +44,7 @@ namespace anm2ed class Logger { - std::ofstream file{}; + std::filesystem::path logPath{}; std::mutex mutex{}; std::thread stderrThread{}; bool isStderrRedirecting{}; diff --git a/src/manager.cpp b/src/manager.cpp index 15c8f51..cf07e82 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -5,6 +5,7 @@ #include +#include "file_.hpp" #include "log.hpp" #include "path_.hpp" #include "sdl.hpp" @@ -279,12 +280,13 @@ namespace anm2ed { auto path = recent_files_path_get(); - std::ifstream file(path); - if (!file) + std::string fileData{}; + if (!file::read_to_string(path, &fileData, "rb")) { logger.warning(std::format("Could not load recent files from: {}. Skipping...", path::to_utf8(path))); return; } + std::istringstream file(fileData); logger.info(std::format("Loading recent files from: {}", path::to_utf8(path))); @@ -322,18 +324,12 @@ namespace anm2ed auto path = recent_files_path_get(); ensure_parent_directory_exists(path); - std::ofstream file; - file.open(path, std::ofstream::out | std::ofstream::trunc); - - if (!file.is_open()) - { - logger.warning(std::format("Could not write recent files to: {}. Skipping...", path::to_utf8(path))); - return; - } - + std::ostringstream file; auto ordered = recent_files_ordered(); for (auto& entry : ordered) file << path::to_utf8(entry) << '\n'; + if (!file::write_string(path, file.str(), "wb")) + logger.warning(std::format("Could not write recent files to: {}. Skipping...", path::to_utf8(path))); } void Manager::recent_files_clear() @@ -370,12 +366,13 @@ namespace anm2ed { auto path = autosave_path_get(); - std::ifstream file(path); - if (!file) + std::string fileData{}; + if (!file::read_to_string(path, &fileData, "rb")) { logger.warning(std::format("Could not load autosave files from: {}. Skipping...", path::to_utf8(path))); return; } + std::istringstream file(fileData); logger.info(std::format("Loading autosave files from: {}", path::to_utf8(path))); @@ -393,19 +390,13 @@ namespace anm2ed void Manager::autosave_files_write() { - std::ofstream autosaveWriteFile; ensure_parent_directory_exists(autosave_path_get()); - autosaveWriteFile.open(autosave_path_get(), std::ofstream::out | std::ofstream::trunc); - - if (!autosaveWriteFile.is_open()) - { - logger.warning( - std::format("Could not write autosave files to: {}. Skipping...", path::to_utf8(autosave_path_get()))); - return; - } - + std::ostringstream autosaveWriteFile; for (auto& path : autosaveFiles) autosaveWriteFile << path::to_utf8(path) << "\n"; + if (!file::write_string(autosave_path_get(), autosaveWriteFile.str(), "wb")) + logger.warning( + std::format("Could not write autosave files to: {}. Skipping...", path::to_utf8(autosave_path_get()))); } void Manager::autosave_files_clear() diff --git a/src/resource/strings.hpp b/src/resource/strings.hpp index b2ce13e..f97c2dc 100644 --- a/src/resource/strings.hpp +++ b/src/resource/strings.hpp @@ -304,6 +304,7 @@ namespace anm2ed X(LABEL_MERGE_SPRITESHEETS_APPEND_RIGHT, "Top Right", "Arriba a la derecha", "Сверху справа", "右上", "우측 상단") \ X(LABEL_MERGE_SPRITESHEETS_APPEND_BOTTOM, "Bottom Left", "Abajo a la izquierda", "Снизу слева", "左下", "좌측 하단") \ X(LABEL_MERGE_MAKE_SPRITESHEET_REGIONS, "Make Spritesheet Regions", "Crear regiones de spritesheet", "Создать регионы спрайт-листа", "生成图集区域", "스프라이트 시트 영역 만들기") \ + X(LABEL_MERGE_MAKE_PRIMARY_SPRITESHEET_REGION, "Make Primary Spritesheet Region", "Crear region de spritesheet principal", "Создать регион основного спрайт-листа", "生成主图集区域", "기본 스프라이트 시트 영역 만들기") \ X(LABEL_MOVE_TOOL_SNAP, "Move Tool: Snap to Mouse", "Herramienta Mover: Ajustar al Mouse", "Инструмент перемещения: Привязка к мыши", "移动工具: 吸附到鼠标指针", "이동 도구: 마우스에 맞추기") \ X(LABEL_MULTIPLY, "Multiply", "Multiplicar", "Умножить", "乘", "곱하기") \ X(LABEL_NEW, "New", "Nuevo", "Новый", "新", "새로") \ @@ -576,6 +577,7 @@ namespace anm2ed X(TOOLTIP_MERGE_SPRITESHEETS_BOTTOM_LEFT, "The merged spritesheets will be joined at the bottom left of the previous spritesheet.", "Los spritesheets fusionados se unirán en la parte inferior izquierda del spritesheet anterior.", "Объединяемые спрайт-листы будут стыковаться в нижнем левом углу предыдущего спрайт-листа.", "合并的图集将拼接在前一个图集的左下方。", "병합된 스프라이트 시트는 이전 스프라이트 시트의 좌하단에 이어 붙습니다.") \ X(TOOLTIP_MERGE_SPRITESHEETS_TOP_RIGHT, "The merged spritesheets will be joined at the top right of the previous spritesheet.", "Los spritesheets fusionados se unirán en la parte superior derecha del spritesheet anterior.", "Объединяемые спрайт-листы будут стыковаться в верхнем правом углу предыдущего спрайт-листа.", "合并的图集将拼接在前一个图集的右上方。", "병합된 스프라이트 시트는 이전 스프라이트 시트의 우상단에 이어 붙습니다.") \ X(TOOLTIP_MERGE_MAKE_SPRITESHEET_REGIONS, "The respective spritesheets will be added as regions.", "Los spritesheets correspondientes se agregarán como regiones.", "Соответствующие спрайт-листы будут добавлены как регионы.", "对应图集将作为区域添加。", "해당 스프라이트 시트가 영역으로 추가됩니다.") \ + X(TOOLTIP_MERGE_MAKE_PRIMARY_SPRITESHEET_REGION, "If disabled, the base spritesheet will not be added as a region in the merged spritesheet.", "Si se desactiva, el spritesheet base no se agregará como región en el spritesheet combinado.", "Если отключено, базовый спрайт-лист не будет добавлен как регион в объединённый спрайт-лист.", "禁用后,基础图集不会作为区域添加到合并后的图集中。", "비활성화하면 기본 스프라이트 시트가 병합된 스프라이트 시트에 영역으로 추가되지 않습니다.") \ X(TOOLTIP_PACK_SPRITESHEET, "Pack the spritesheet by its regions and rebuild the texture.", "Empaqueta el spritesheet por sus regiones y reconstruye la textura.", "Упаковать спрайт-лист по его регионам и пересобрать текстуру.", "按区域打包图集并重建纹理。", "영역 기준으로 스프라이트 시트를 패킹하고 텍스처를 다시 만듭니다.") \ X(TOOLTIP_OUTPUT_PATH, "Set the output path or directory for the animation.", "Ajusta la ruta de salida o el directiorio de la animacion.", "Установить путь или директорию вывода для анимации.", "更改动画的输出路径/目录.", "애니메이션의 출력 경로 또는 디렉터리를 설정합니다.") \ X(TOOLTIP_OVERLAY, "Set an animation to be drawn over the current animation.", "Ajusta una animacion para ser dibujada sobre la animacion actual.", "Установить анимацию, которая будет выведена над текущей анимацией.", "设置一个当前动画的覆盖动画.", "현재 애니메이션 위에 그려질 애니메이션을 설정합니다.") \ diff --git a/src/settings.hpp b/src/settings.hpp index bc6cf49..3b0feec 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -155,6 +155,7 @@ namespace anm2ed X(MERGE_IS_DELETE_ANIMATIONS_AFTER, mergeIsDeleteAnimationsAfter, STRING_UNDEFINED, BOOL, false) \ X(MERGE_SPRITESHEETS_ORIGIN, mergeSpritesheetsOrigin, STRING_UNDEFINED, INT, anm2::APPEND_RIGHT) \ X(MERGE_SPRITESHEETS_IS_MAKE_REGIONS, mergeSpritesheetsIsMakeRegions, STRING_UNDEFINED, BOOL, true) \ + X(MERGE_SPRITESHEETS_IS_MAKE_PRIMARY_REGION, mergeSpritesheetsIsMakePrimaryRegion, STRING_UNDEFINED, BOOL, true) \ X(MERGE_SPRITESHEETS_REGION_ORIGIN, mergeSpritesheetsRegionOrigin, STRING_UNDEFINED, INT, origin::TOP_LEFT) \ X(PACK_PADDING, packPadding, STRING_UNDEFINED, INT, 1) \ \ diff --git a/src/util/file_.cpp b/src/util/file_.cpp index f41ae11..a7bc886 100644 --- a/src/util/file_.cpp +++ b/src/util/file_.cpp @@ -1,5 +1,8 @@ #include "file_.hpp" +#include +#include + namespace anm2ed::util { File::File(const std::filesystem::path& path, const char* mode) { open(path, mode); } @@ -38,4 +41,32 @@ namespace anm2ed::util FILE* File::get() const { return handle; } File::operator bool() const { return handle != nullptr; } -} \ No newline at end of file + + namespace file + { + bool read_to_string(const std::filesystem::path& path, std::string* output, const char* mode) + { + if (!output) return false; + + File file(path, mode); + if (!file) return false; + + output->clear(); + std::array buffer{}; + while (true) + { + auto read = std::fread(buffer.data(), 1, buffer.size(), file.get()); + if (read > 0) output->append(buffer.data(), read); + if (read < buffer.size()) return std::feof(file.get()) != 0; + } + } + + bool write_string(const std::filesystem::path& path, std::string_view data, const char* mode) + { + File file(path, mode); + if (!file) return false; + + return std::fwrite(data.data(), 1, data.size(), file.get()) == data.size(); + } + } +} diff --git a/src/util/file_.hpp b/src/util/file_.hpp index 3e3ce07..abb485c 100644 --- a/src/util/file_.hpp +++ b/src/util/file_.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace anm2ed::util { @@ -19,4 +21,10 @@ namespace anm2ed::util private: FILE* handle{}; }; -} \ No newline at end of file + + namespace file + { + bool read_to_string(const std::filesystem::path&, std::string*, const char* mode = "rb"); + bool write_string(const std::filesystem::path&, std::string_view, const char* mode = "wb"); + } +} From 378a7692e24d9bb4968e125e18bfab97df52d872 Mon Sep 17 00:00:00 2001 From: shweet Date: Wed, 8 Apr 2026 20:00:13 -0400 Subject: [PATCH 2/2] add #include format --- src/anm2/anm2.cpp | 25 +++++++++++++++++++++++++ src/anm2/anm2_animations.cpp | 2 +- src/anm2/anm2_sounds.cpp | 2 ++ src/imgui/toast.cpp | 2 ++ src/imgui/window/animation_preview.cpp | 2 ++ src/imgui/window/events.cpp | 1 + src/imgui/window/layers.cpp | 1 + src/imgui/window/nulls.cpp | 1 + src/imgui/window/sounds.cpp | 1 + src/imgui/window/spritesheets.cpp | 2 +- src/imgui/window/timeline.cpp | 1 + src/imgui/window/welcome.cpp | 1 + src/imgui/wizard/configure.cpp | 6 ++++++ src/loader.cpp | 2 +- src/log.cpp | 1 + src/settings.cpp | 14 ++++++++++++++ 16 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/anm2/anm2.cpp b/src/anm2/anm2.cpp index 335994f..54276bf 100644 --- a/src/anm2/anm2.cpp +++ b/src/anm2/anm2.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "file_.hpp" @@ -199,6 +200,28 @@ namespace anm2ed::anm2 Anm2 Anm2::normalized_for_serialize() const { auto normalized = *this; + auto sanitize_layer_order = [](Animation& animation) + { + std::vector sanitized{}; + sanitized.reserve(animation.layerAnimations.size()); + std::set seen{}; + + for (auto id : animation.layerOrder) + { + if (!animation.layerAnimations.contains(id)) continue; + if (!seen.insert(id).second) continue; + sanitized.push_back(id); + } + + std::vector missing{}; + missing.reserve(animation.layerAnimations.size()); + for (auto& id : animation.layerAnimations | std::views::keys) + if (!seen.contains(id)) missing.push_back(id); + + std::sort(missing.begin(), missing.end()); + sanitized.insert(sanitized.end(), missing.begin(), missing.end()); + animation.layerOrder = std::move(sanitized); + }; std::unordered_map layerRemap{}; int normalizedID = 0; @@ -214,6 +237,7 @@ namespace anm2ed::anm2 for (auto& animation : normalized.animations.items) { + sanitize_layer_order(animation); std::unordered_map layerAnimations{}; std::vector layerOrder{}; @@ -231,6 +255,7 @@ namespace anm2ed::anm2 animation.layerAnimations = std::move(layerAnimations); animation.layerOrder = std::move(layerOrder); + sanitize_layer_order(animation); } return normalized; diff --git a/src/anm2/anm2_animations.cpp b/src/anm2/anm2_animations.cpp index 1cfb030..5bce099 100644 --- a/src/anm2/anm2_animations.cpp +++ b/src/anm2/anm2_animations.cpp @@ -136,4 +136,4 @@ namespace anm2ed::anm2 return finalIndex; } -} \ No newline at end of file +} diff --git a/src/anm2/anm2_sounds.cpp b/src/anm2/anm2_sounds.cpp index 425a457..6c07273 100644 --- a/src/anm2/anm2_sounds.cpp +++ b/src/anm2/anm2_sounds.cpp @@ -1,5 +1,7 @@ #include "anm2.hpp" +#include + #include "map_.hpp" #include "path_.hpp" #include "working_directory.hpp" diff --git a/src/imgui/toast.cpp b/src/imgui/toast.cpp index 9e3274a..bc04ea4 100644 --- a/src/imgui/toast.cpp +++ b/src/imgui/toast.cpp @@ -1,5 +1,7 @@ #include "toast.hpp" +#include + #include "log.hpp" #include diff --git a/src/imgui/window/animation_preview.cpp b/src/imgui/window/animation_preview.cpp index 5542a3e..802b05e 100644 --- a/src/imgui/window/animation_preview.cpp +++ b/src/imgui/window/animation_preview.cpp @@ -780,9 +780,11 @@ namespace anm2ed::imgui for (auto& id : animation->layerOrder) { + if (!animation->layerAnimations.contains(id)) continue; auto& layerAnimation = animation->layerAnimations[id]; if (!layerAnimation.isVisible) continue; + if (!anm2.content.layers.contains(id)) continue; auto& layer = anm2.content.layers.at(id); auto spritesheet = anm2.spritesheet_get(layer.spritesheetID); diff --git a/src/imgui/window/events.cpp b/src/imgui/window/events.cpp index b0a9f59..8a99134 100644 --- a/src/imgui/window/events.cpp +++ b/src/imgui/window/events.cpp @@ -1,5 +1,6 @@ #include "events.hpp" +#include #include #include "log.hpp" diff --git a/src/imgui/window/layers.cpp b/src/imgui/window/layers.cpp index 2fa8105..e59235f 100644 --- a/src/imgui/window/layers.cpp +++ b/src/imgui/window/layers.cpp @@ -1,5 +1,6 @@ #include "layers.hpp" +#include #include #include "log.hpp" diff --git a/src/imgui/window/nulls.cpp b/src/imgui/window/nulls.cpp index abb4c08..c93ae9a 100644 --- a/src/imgui/window/nulls.cpp +++ b/src/imgui/window/nulls.cpp @@ -1,5 +1,6 @@ #include "nulls.hpp" +#include #include #include "log.hpp" diff --git a/src/imgui/window/sounds.cpp b/src/imgui/window/sounds.cpp index 3beb8d3..5021ae4 100644 --- a/src/imgui/window/sounds.cpp +++ b/src/imgui/window/sounds.cpp @@ -1,6 +1,7 @@ #include "sounds.hpp" #include +#include #include #include diff --git a/src/imgui/window/spritesheets.cpp b/src/imgui/window/spritesheets.cpp index a5eb90a..595e90d 100644 --- a/src/imgui/window/spritesheets.cpp +++ b/src/imgui/window/spritesheets.cpp @@ -595,7 +595,7 @@ namespace anm2ed::imgui mergePopup.close(); }; - auto optionsSize = child_size_get(5); + auto optionsSize = child_size_get(6); if (ImGui::BeginChild("##Merge Spritesheets Options", optionsSize, ImGuiChildFlags_Borders)) { ImGui::SeparatorText(localize.get(LABEL_REGION_PROPERTIES_ORIGIN)); diff --git a/src/imgui/window/timeline.cpp b/src/imgui/window/timeline.cpp index 81f1c38..af9dfe1 100644 --- a/src/imgui/window/timeline.cpp +++ b/src/imgui/window/timeline.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include diff --git a/src/imgui/window/welcome.cpp b/src/imgui/window/welcome.cpp index 59b9524..3ac1897 100644 --- a/src/imgui/window/welcome.cpp +++ b/src/imgui/window/welcome.cpp @@ -1,5 +1,6 @@ #include "welcome.hpp" +#include #include #include "path_.hpp" diff --git a/src/imgui/wizard/configure.cpp b/src/imgui/wizard/configure.cpp index 5e3dc0b..eab9e0b 100644 --- a/src/imgui/wizard/configure.cpp +++ b/src/imgui/wizard/configure.cpp @@ -1,6 +1,9 @@ #include "configure.hpp" #include "imgui_.hpp" +#include "log.hpp" +#include "path_.hpp" +#include "sdl.hpp" using namespace anm2ed::types; @@ -180,6 +183,7 @@ namespace anm2ed::imgui::wizard shortcut(manager.chords[SHORTCUT_CONFIRM]); if (ImGui::Button(localize.get(BASIC_SAVE), widgetSize)) { + auto settingsPath = util::sdl::preferences_directory_get() / "settings.ini"; settings = temporary; ImGui::GetIO().KeyRepeatDelay = settings.keyboardRepeatDelay; @@ -193,6 +197,8 @@ namespace anm2ed::imgui::wizard for (auto& document : manager.documents) document.snapshots.apply_limit(); + settings.save(settingsPath, ImGui::SaveIniSettingsToMemory(nullptr)); + isSet = true; } ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SETTINGS_SAVE)); diff --git a/src/loader.cpp b/src/loader.cpp index 51c64b4..7fdfd6b 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -22,7 +23,6 @@ #ifdef _WIN32 #include "util/path_.hpp" #include - #include #include #endif diff --git a/src/log.cpp b/src/log.cpp index e0de220..958bc0b 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -1,6 +1,7 @@ #include "log.hpp" #include +#include #include #include "file_.hpp" diff --git a/src/settings.cpp b/src/settings.cpp index be984d5..e0689e8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -338,7 +338,15 @@ DockSpace ID=0x123F8F08 Window=0x6D581B32 Pos=8,62 Size=1496,860 S void Settings::save(const std::filesystem::path& path, const std::string& imguiData) { + auto pathUtf8 = path::to_utf8(path); + logger.info(std::format("Saving settings to: {}", pathUtf8)); std::ofstream file(path, std::ios::out | std::ios::binary); + if (!file.is_open()) + { + logger.error(std::format("Failed to save settings file: {}", pathUtf8)); + return; + } + file << "[Settings]\n"; auto value_save = [&](const std::string& key, const auto& value) @@ -395,6 +403,12 @@ DockSpace ID=0x123F8F08 Window=0x6D581B32 Pos=8,62 Size=1496,860 S << imguiData; file.flush(); + if (file.fail()) + { + logger.error(std::format("Failed while writing settings file: {}", pathUtf8)); + return; + } file.close(); + logger.info(std::format("Saved settings to: {}", pathUtf8)); } }