Refactor + render animation tweaks + updated frame properties + bug fixes

This commit is contained in:
2025-12-17 23:02:00 -05:00
parent b4b4fe3714
commit 119bbc4081
63 changed files with 1964 additions and 1701 deletions

41
src/util/file_.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "file_.h"
namespace anm2ed::util
{
File::File(const std::filesystem::path& path, const char* mode) { open(path, mode); }
File::~File() { close(); }
bool File::open(const std::filesystem::path& path, const char* mode)
{
close();
#ifdef _WIN32
std::wstring wideMode{};
if (mode)
wideMode.assign(mode, mode + std::strlen(mode));
else
wideMode = L"rb";
handle = _wfopen(path.native().c_str(), wideMode.c_str());
#else
handle = std::fopen(path.string().c_str(), mode);
#endif
return handle != nullptr;
}
int File::close()
{
if (handle)
{
auto result = std::fclose(handle);
handle = nullptr;
return result;
}
return -1;
}
FILE* File::get() const { return handle; }
File::operator bool() const { return handle != nullptr; }
}

22
src/util/file_.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <filesystem>
namespace anm2ed::util
{
class File
{
public:
File() = default;
File(const std::filesystem::path&, const char* mode);
~File();
bool open(const std::filesystem::path&, const char* mode);
int close();
FILE* get() const;
explicit operator bool() const;
private:
FILE* handle{};
};
}

View File

@@ -1,140 +0,0 @@
#include "filesystem_.h"
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_stdinc.h>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <cwctype>
#include <filesystem>
#include <type_traits>
namespace anm2ed::util::filesystem
{
namespace
{
template <typename CharT> CharT to_lower_char(CharT character)
{
if constexpr (std::is_same_v<CharT, wchar_t>)
return static_cast<CharT>(std::towlower(static_cast<wint_t>(character)));
else
return static_cast<CharT>(std::tolower(static_cast<unsigned char>(character)));
}
}
std::filesystem::path path_preferences_get()
{
auto sdlPath = SDL_GetPrefPath(nullptr, "anm2ed");
if (!sdlPath) return {};
auto filePath = path_from_utf8(sdlPath);
SDL_free(sdlPath);
return filePath;
}
std::filesystem::path path_to_lower(const std::filesystem::path& path)
{
auto native = path.native();
for (auto& character : native)
character = to_lower_char(character);
return std::filesystem::path(native);
}
std::filesystem::path path_backslash_replace(const std::filesystem::path& path)
{
auto native = path.native();
constexpr auto backslash = static_cast<std::filesystem::path::value_type>('\\');
constexpr auto slash = static_cast<std::filesystem::path::value_type>('/');
for (auto& character : native)
if (character == backslash) character = slash;
return std::filesystem::path(native);
}
std::string path_to_utf8(const std::filesystem::path& path)
{
auto u8 = path.u8string();
return std::string(u8.begin(), u8.end());
}
std::filesystem::path path_from_utf8(const std::string& utf8)
{
return std::filesystem::path(std::u8string(utf8.begin(), utf8.end()));
}
FILE* open(const std::filesystem::path& path, const char* mode)
{
#ifdef _WIN32
std::wstring wideMode{};
if (mode)
wideMode.assign(mode, mode + std::strlen(mode));
else
wideMode = L"rb";
return _wfopen(path.native().c_str(), wideMode.c_str());
#else
return std::fopen(path.string().c_str(), mode);
#endif
}
bool path_is_exist(const std::filesystem::path& path)
{
std::error_code errorCode;
return std::filesystem::exists(path, errorCode) && ((void)std::filesystem::status(path, errorCode), !errorCode);
}
bool path_is_extension(const std::filesystem::path& path, const std::string& extension)
{
auto extensionUtf8 = path_to_utf8(std::filesystem::path(path).extension());
std::transform(extensionUtf8.begin(), extensionUtf8.end(), extensionUtf8.begin(), ::tolower);
return extensionUtf8 == ("." + extension);
}
std::filesystem::path path_lower_case_backslash_handle(const std::filesystem::path& path)
{
auto newPath = path;
if (path_is_exist(newPath)) return newPath;
newPath = path_backslash_replace(newPath);
if (path_is_exist(newPath)) return newPath;
newPath = path_to_lower(newPath);
return newPath;
}
WorkingDirectory::WorkingDirectory(const std::filesystem::path& path, bool isFile)
{
previous = std::filesystem::current_path();
if (isFile)
{
std::filesystem::path filePath = path;
std::filesystem::path parentPath = filePath.parent_path();
std::filesystem::current_path(parentPath);
}
else
std::filesystem::current_path(path);
}
WorkingDirectory::~WorkingDirectory() { std::filesystem::current_path(previous); }
File::File(const std::filesystem::path& path, const char* mode) { open(path, mode); }
File::~File() { close(); }
bool File::open(const std::filesystem::path& path, const char* mode)
{
close();
handle = filesystem::open(path, mode);
return handle != nullptr;
}
void File::close()
{
if (handle)
{
std::fclose(handle);
handle = nullptr;
}
}
FILE* File::get() const { return handle; }
File::operator bool() const { return handle != nullptr; }
}

View File

@@ -1,47 +0,0 @@
#pragma once
#include <cstdio>
#include <filesystem>
#include <string>
namespace anm2ed::util::filesystem
{
std::filesystem::path path_preferences_get();
std::filesystem::path path_to_lower(const std::filesystem::path&);
std::filesystem::path path_backslash_replace(const std::filesystem::path&);
std::string path_to_utf8(const std::filesystem::path&);
std::filesystem::path path_from_utf8(const std::string&);
bool path_is_exist(const std::filesystem::path&);
bool path_is_extension(const std::filesystem::path&, const std::string&);
std::filesystem::path path_lower_case_backslash_handle(const std::filesystem::path&);
FILE* open(const std::filesystem::path&, const char*);
class File
{
public:
File() = default;
File(const std::filesystem::path&, const char* mode);
~File();
bool open(const std::filesystem::path&, const char* mode);
void close();
FILE* get() const;
explicit operator bool() const;
private:
FILE* handle{};
};
class WorkingDirectory
{
public:
std::filesystem::path previous;
WorkingDirectory(const std::filesystem::path&, bool = false);
~WorkingDirectory();
};
}

120
src/util/path_.cpp Normal file
View File

@@ -0,0 +1,120 @@
#include "path_.h"
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_stdinc.h>
#include <cctype>
#include <cwctype>
#include <filesystem>
#include <type_traits>
namespace anm2ed::util::path
{
namespace
{
template <typename CharT> CharT to_lower_char(CharT character)
{
if constexpr (std::is_same_v<CharT, wchar_t>)
return static_cast<CharT>(std::towlower(static_cast<wint_t>(character)));
else
return static_cast<CharT>(std::tolower(static_cast<unsigned char>(character)));
}
}
std::filesystem::path to_lower(const std::filesystem::path& path)
{
auto native = path.native();
for (auto& character : native)
character = to_lower_char(character);
return std::filesystem::path(native);
}
std::filesystem::path backslash_replace(const std::filesystem::path& path)
{
auto native = path.native();
constexpr auto backslash = static_cast<std::filesystem::path::value_type>('\\');
constexpr auto slash = static_cast<std::filesystem::path::value_type>('/');
for (auto& character : native)
if (character == backslash) character = slash;
return std::filesystem::path(native);
}
std::string to_utf8(const std::filesystem::path& path)
{
auto u8 = path.u8string();
return std::string(u8.begin(), u8.end());
}
std::filesystem::path from_utf8(const std::string& utf8)
{
return std::filesystem::path(std::u8string(utf8.begin(), utf8.end()));
}
bool is_exist(const std::filesystem::path& path)
{
if (path.empty()) return false;
std::error_code errorCode;
return std::filesystem::exists(path, errorCode) && ((void)std::filesystem::status(path, errorCode), !errorCode);
}
bool is_extension(const std::filesystem::path& path, const std::string& extension)
{
if (path.empty()) return false;
auto extensionUtf8 = to_utf8(std::filesystem::path(path).extension());
std::transform(extensionUtf8.begin(), extensionUtf8.end(), extensionUtf8.begin(), ::tolower);
return extensionUtf8 == ("." + extension);
}
bool is_executable(const std::filesystem::path& path)
{
if (path.empty()) return false;
std::error_code ec;
auto status = std::filesystem::status(path, ec);
if (ec) return false;
if (!std::filesystem::exists(status)) return false;
if (!std::filesystem::is_regular_file(status)) return false;
#ifdef WIN32
auto extension = to_lower(path.extension());
if (extension != ".exe" && extension != ".bat" && extension != ".cmd") return false;
#else
auto permissions = status.permissions();
auto executableMask =
std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec;
if ((permissions & executableMask) == std::filesystem::perms::none) return false;
#endif
return true;
}
bool ensure_directory(const std::filesystem::path& path)
{
if (path.empty()) return false;
std::error_code ec;
if (std::filesystem::create_directories(path, ec)) return true;
return is_exist(path.parent_path());
}
std::filesystem::path make_relative(const std::filesystem::path& path)
{
if (path.empty()) return path;
std::error_code ec{};
auto relative = std::filesystem::relative(path, ec);
if (!ec) return relative;
return path;
}
std::filesystem::path lower_case_backslash_handle(const std::filesystem::path& path)
{
auto newPath = path;
if (is_exist(newPath)) return newPath;
newPath = backslash_replace(newPath);
if (is_exist(newPath)) return newPath;
newPath = to_lower(newPath);
return newPath;
}
}

21
src/util/path_.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include <filesystem>
#include <string>
namespace anm2ed::util::path
{
std::filesystem::path to_lower(const std::filesystem::path&);
std::filesystem::path backslash_replace(const std::filesystem::path&);
std::string to_utf8(const std::filesystem::path&);
std::filesystem::path from_utf8(const std::string&);
std::filesystem::path make_relative(const std::filesystem::path&);
bool is_exist(const std::filesystem::path&);
bool is_executable(const std::filesystem::path&);
bool is_extension(const std::filesystem::path&, const std::string&);
bool ensure_directory(const std::filesystem::path&);
std::filesystem::path lower_case_backslash_handle(const std::filesystem::path&);
}

55
src/util/process_.cpp Normal file
View File

@@ -0,0 +1,55 @@
#include "process_.h"
namespace anm2ed::util
{
Process::Process(const char* command, const char* mode) { open(command, mode); }
Process::~Process() { close(); }
bool Process::open(const char* command, const char* mode)
{
close();
#ifdef WIN32
pipe = _popen(command, mode);
#else
pipe = popen(command, mode);
#endif
return pipe != nullptr;
}
int Process::close()
{
if (pipe)
{
#ifdef WIN32
auto result = _pclose(pipe);
#else
auto result = pclose(pipe);
#endif
pipe = nullptr;
return result;
}
return -1;
}
std::string Process::output_get_and_close()
{
std::string output{};
if (!pipe) return {};
char buffer[256];
while (fgets(buffer, sizeof(buffer), pipe))
output += buffer;
close();
return output;
}
FILE* Process::get() const { return pipe; }
Process::operator bool() const { return pipe != nullptr; }
}

24
src/util/process_.h Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <string>
namespace anm2ed::util
{
class Process
{
public:
Process() = default;
Process(const char*, const char*);
~Process();
bool open(const char*, const char*);
int close();
FILE* get() const;
explicit operator bool() const;
std::string output_get_and_close();
private:
FILE* pipe{};
};
}

17
src/util/sdl.cpp Normal file
View File

@@ -0,0 +1,17 @@
#include "sdl.h"
#include <SDL3/SDL.h>
#include "path_.h"
namespace anm2ed::util::sdl
{
std::filesystem::path preferences_directory_get()
{
auto sdlPath = SDL_GetPrefPath(nullptr, "anm2ed");
if (!sdlPath) return {};
auto filePath = path::from_utf8(sdlPath);
SDL_free(sdlPath);
return filePath;
}
}

8
src/util/sdl.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <filesystem>
namespace anm2ed::util::sdl
{
std::filesystem::path preferences_directory_get();
}

View File

@@ -0,0 +1,19 @@
#include "working_directory.h"
namespace anm2ed::util
{
WorkingDirectory::WorkingDirectory(const std::filesystem::path& path, Type type)
{
previous = std::filesystem::current_path();
if (type == FILE)
{
std::filesystem::path filePath = path;
std::filesystem::path parentPath = filePath.parent_path();
std::filesystem::current_path(parentPath);
}
else
std::filesystem::current_path(path);
}
WorkingDirectory::~WorkingDirectory() { std::filesystem::current_path(previous); }
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <filesystem>
namespace anm2ed::util
{
class WorkingDirectory
{
public:
enum Type
{
FILE,
DIRECTORY
};
std::filesystem::path previous{};
WorkingDirectory(const std::filesystem::path&, Type type = DIRECTORY);
~WorkingDirectory();
};
}

View File

@@ -1,10 +1,10 @@
#include "xml_.h"
#include "filesystem_.h"
#include "math_.h"
#include "path_.h"
using namespace anm2ed::util;
using namespace tinyxml2;
namespace filesystem = anm2ed::util::filesystem;
namespace anm2ed::util::xml
{
@@ -27,7 +27,7 @@ namespace anm2ed::util::xml
{
std::string temp{};
auto result = query_string_attribute(element, attribute, &temp);
if (result == XML_SUCCESS) *out = filesystem::path_from_utf8(temp);
if (result == XML_SUCCESS) *out = path::from_utf8(temp);
return result;
}