Compare commits

..

44 Commits

Author SHA1 Message Date
3c3d53b40c [studio] Ensure Editor tabs do first draw immediately, fix shift key being missed with tab shortcuts
All checks were successful
Build / build (push) Successful in 3m14s
2025-01-13 22:29:48 -06:00
151d7c5736 [nostalgia/core/gba] Fix partial tilesheet loading overrun
All checks were successful
Build / build (push) Successful in 3m11s
2025-01-13 22:03:36 -06:00
4e4d8d2c3f [nostalgia/core/gba] Make panic use standard abort call 2025-01-13 21:37:29 -06:00
03d1fd2857 [ox/std] Add and integrate standard abort call 2025-01-13 20:39:21 -06:00
6701decc91 [gbabuildcore] Enable exceptions 2025-01-13 20:18:28 -06:00
6cff526647 [teagba] Add symbols needed for enabling exceptions 2025-01-13 20:17:10 -06:00
dd50bd0249 [studio] Remap toggle explorer keyboard shortcut, add Ctrl+1-0 mappings for jumping between tabs
All checks were successful
Build / build (push) Successful in 3m17s
2025-01-13 01:14:57 -06:00
55a1660242 [nostalgia/core] Fix TileSheet validation/repair to ensure pixels gets cleared if there are subsheets
All checks were successful
Build / build (push) Successful in 3m10s
2025-01-12 16:06:24 -06:00
ed365dfef5 [studio] Fix new project menu to return an appropriately sized string for name 2025-01-12 15:04:31 -06:00
23a09e4a13 [nostalgia/core/studio] Fix SubSheet editor to return an appropriately sized string 2025-01-12 14:55:50 -06:00
b69e7ebb98 [nostalgia/core/studio/tilesheeteditor] Fix select all not to go beyond end
All checks were successful
Build / build (push) Successful in 3m10s
2025-01-11 16:21:10 -06:00
418d6e3f22 [nostalgia/core/studio] Fix crash that occurs when a non-leaf node subsheet is selected
All checks were successful
Build / build (push) Successful in 3m11s
2025-01-11 16:06:48 -06:00
c44d8678cb [nostalgia/core/studio] Fix tile insert to correct input when inserting past the last tile
All checks were successful
Build / build (push) Successful in 3m11s
2025-01-11 15:38:11 -06:00
eb4cd7106d [nostalgia/core/studio] Fix tile insert to work on last tile
All checks were successful
Build / build (push) Successful in 3m9s
2025-01-11 15:23:57 -06:00
d259770f32 Merge commit '4ea4a61d542777a270c4e2c283e0e986fc9eec9c'
All checks were successful
Build / build (push) Successful in 3m9s
2025-01-11 12:32:00 -06:00
80bad608f7 [keel] Fix reloadAsset 2025-01-11 03:39:38 -06:00
2bce9a2baf [ox/std] Add non-const SmallMap::pairs 2025-01-11 03:37:27 -06:00
791b7746f3 [nostalgia] Update liccor file
All checks were successful
Build / build (push) Successful in 3m18s
2025-01-08 23:13:14 -06:00
842e3587fd [nostalgia] Update .gitignore for new location of scripts dir 2025-01-08 23:13:02 -06:00
318e79004b [ox] Update liccor file 2025-01-08 23:12:28 -06:00
9f338a7429 [ox] Run liccor
All checks were successful
Build / build (push) Successful in 3m18s
2025-01-08 23:03:05 -06:00
645e48af7b [nostalgia,olympic] Run liccor 2025-01-08 23:02:08 -06:00
ef92c8df13 [nostalgia] Make pkg-gba.py force lower case for pack tool
All checks were successful
Build / build (push) Successful in 3m9s
2025-01-08 22:01:27 -06:00
849d50be8e [nostalgia/core] Make getTileIdx return an Optional 2025-01-08 21:34:01 -06:00
845092f114 [turbine] Make common turbine.cpp file private to its target 2025-01-08 21:33:23 -06:00
75819a1797 [ox/std] Add SmallMap::values() 2025-01-08 21:31:46 -06:00
d66da85753 [ox/std] SmallMap fixes, add findIdx function 2025-01-07 20:59:04 -06:00
98ddb08abd [nostalgia] Cleanup
All checks were successful
Build / build (push) Successful in 3m18s
2025-01-05 20:55:49 -06:00
8d1701b0bb [turbine/glfw] Ensure window opens with a standard mandatory refresh period
All checks were successful
Build / build (push) Successful in 3m19s
2025-01-04 23:49:04 -06:00
1048e522fd [imgui] Make ImGui not an object lib 2025-01-04 23:42:43 -06:00
ee59da4aa3 [glad] Make glad not an object lib 2025-01-04 23:42:43 -06:00
1ba64cb5d8 Merge commit '07610a5af2aaaac9cfcdcf8359b33f7df40d46cd'
All checks were successful
Build / build (push) Successful in 3m15s
2025-01-04 01:29:09 -06:00
462bebf6dd [nostalgia/core] Cleanup unused function declaration 2025-01-04 01:26:02 -06:00
e3f84c4e75 [studio] Make first tab not draw before selected tab when window opens 2025-01-04 01:12:48 -06:00
6837a0556d [keel] Make AssetManager take StringViewCR for assetId
All checks were successful
Build / build (push) Successful in 3m16s
2025-01-04 01:11:05 -06:00
ede2c8ca37 [keel] Make AssetTypeManager(Loader) move loader
All checks were successful
Build / build (push) Successful in 3m23s
2025-01-04 01:05:04 -06:00
f50367f7d5 [ox/std] Add hash.hpp to install
All checks were successful
Build / build (push) Successful in 3m31s
2025-01-03 00:26:10 -06:00
e758e03d2b [nostalgia,olympic] Update for ox::Error changes
All checks were successful
Build / build (push) Successful in 3m20s
2025-01-01 23:43:32 -06:00
835e3270ce [ox] Make Error use std::source_location 2025-01-01 23:42:46 -06:00
480dd5ece4 [ox/std] Cleanup
All checks were successful
Build / build (push) Successful in 3m25s
2025-01-01 22:57:20 -06:00
dba6bb5800 [ox/std] Make Vector(initializer_list) use list size as capacity
All checks were successful
Build / build (push) Successful in 3m55s
2025-01-01 22:37:28 -06:00
40a456e54a [ox/std] Add Vector::shrink_to_fit 2025-01-01 22:32:57 -06:00
bf5be00c12 Merge commit 'dc96270ca5e882e41f6b657be14a20e8bd2ad501'
All checks were successful
Build / build (push) Successful in 3m14s
2024-12-21 20:13:20 -06:00
dc7c2559d6 [studio] Make selection tracker not go below 0 2024-12-21 20:06:48 -06:00
51 changed files with 486 additions and 389 deletions

2
.gitignore vendored
View File

@ -6,7 +6,7 @@
.mypy_cache .mypy_cache
.stfolder .stfolder
.stignore .stignore
scripts/__pycache__ util/scripts/__pycache__
pyenv pyenv
CMakeLists.txt.user CMakeLists.txt.user
ROM.oxfs ROM.oxfs

View File

@ -1,5 +1,6 @@
BC_VAR_PROJECT_NAME=nostalgia BC_VAR_PROJECT_NAME=nostalgia
BC_VAR_PROJECT_NAME_CAP=Nostalgia BC_VAR_PROJECT_NAME_CAP=Nostalgia
BC_VAR_DEVENV_ROOT=util
BUILDCORE_PATH=deps/buildcore BUILDCORE_PATH=deps/buildcore
include ${BUILDCORE_PATH}/base.mk include ${BUILDCORE_PATH}/base.mk
@ -13,7 +14,7 @@ endif
.PHONY: pkg-gba .PHONY: pkg-gba
pkg-gba: build pkg-gba: build
${BC_CMD_ENVRUN} ${BC_PY3} ./scripts/pkg-gba.py sample_project ${BC_VAR_PROJECT_NAME} ${BC_CMD_ENVRUN} ${BC_PY3} ./util/scripts/pkg-gba.py sample_project ${BC_VAR_PROJECT_NAME}
.PHONY: run .PHONY: run
run: build run: build

View File

@ -1,8 +1,8 @@
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/modules) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/modules)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unwind-tables") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unwind-tables")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthumb-interwork") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthumb-interwork")

View File

@ -1,2 +1,11 @@
add_library(glad OBJECT src/glad.c) add_library(glad src/glad.c)
target_include_directories(glad PUBLIC include) target_include_directories(glad PUBLIC include)
install(
TARGETS
glad
DESTINATION
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View File

@ -6,7 +6,7 @@ endif()
# DrinkingTea: end # DrinkingTea: end
add_library( add_library(
imgui OBJECT imgui
imgui.cpp imgui.cpp
imgui_demo.cpp imgui_demo.cpp
imgui_draw.cpp imgui_draw.cpp
@ -20,3 +20,11 @@ target_include_directories(
imgui SYSTEM PUBLIC imgui SYSTEM PUBLIC
. .
) )
install(
TARGETS
imgui
DESTINATION
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View File

@ -215,7 +215,7 @@ Error Signal<Args...>::emitCheckError(Args... args) const noexcept {
} }
return ox::Error(0); return ox::Error(0);
} catch (const ox::Exception &ex) { } catch (const ox::Exception &ex) {
return ox::Error(ex.file, ex.line, ex.errCode, ex.msg); return ox::Error(ex.errCode, ex.msg, ex.src);
} }
} }

View File

@ -14,7 +14,7 @@
{ \ { \
const auto loggerErr = (loggerName).initConn(appName); \ const auto loggerErr = (loggerName).initConn(appName); \
if (loggerErr) { \ if (loggerErr) { \
oxErrf("Could not connect to logger: {} ({}:{})\n", toStr(loggerErr), loggerErr.file, loggerErr.line); \ oxErrf("Could not connect to logger: {} ({}:{})\n", toStr(loggerErr), loggerErr.src.file_name(), loggerErr.src.line()); \
} else { \ } else { \
ox::trace::setLogger(&(loggerName)); \ ox::trace::setLogger(&(loggerName)); \
} \ } \

View File

@ -58,11 +58,7 @@ class TypeStore {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
OX_REQUIRE_M(dt, loadDescriptor(typeId)); OX_REQUIRE_M(dt, loadDescriptor(typeId));
for (auto &f : dt->fieldList) { for (auto &f : dt->fieldList) {
if (typeId == f.typeId) { OX_RETURN_ERROR(this->getLoad(f.typeId).moveTo(f.type));
f.type = dt.get();
} else {
OX_RETURN_ERROR(this->getLoad(f.typeId).moveTo(f.type));
}
} }
auto &out = m_cache[typeId]; auto &out = m_cache[typeId];
out = std::move(dt); out = std::move(dt);

View File

@ -9,13 +9,25 @@
#pragma once #pragma once
#include "def.hpp" #include "def.hpp"
#include "error.hpp"
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage) OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
namespace ox { namespace ox {
template<typename It, typename T> template<typename It, typename T>
constexpr It find(It begin, It end, const T &value) { constexpr ox::Result<size_t> findIdx(It begin, It end, T const&value) {
auto it = begin;
for (; it != end; ++it) {
if (*it == value) {
return it.offset();
}
}
return ox::Error{1, "item not found"};
}
template<typename It, typename T>
constexpr It find(It begin, It end, T const&value) {
for (; begin != end; ++begin) { for (; begin != end; ++begin) {
if (*begin == value) { if (*begin == value) {
return begin; return begin;

View File

@ -7,6 +7,7 @@
*/ */
#include "fmt.hpp" #include "fmt.hpp"
#include "realstd.hpp"
#include "stacktrace.hpp" #include "stacktrace.hpp"
#include "trace.hpp" #include "trace.hpp"
@ -14,14 +15,14 @@
namespace ox { namespace ox {
void panic(StringViewCR file, int line, StringViewCR panicMsg, const Error &err) noexcept { void panic(StringViewCR file, int const line, StringViewCR panicMsg, Error const&err) noexcept {
oxErrf("\033[31;1;1mPANIC:\033[0m [{}:{}]: {}\n", file, line, panicMsg); oxErrf("\033[31;1;1mPANIC:\033[0m [{}:{}]: {}\n", file, line, panicMsg);
if (err.msg) { if (err.msg) {
oxErrf("\tError Message:\t{}\n", err.msg); oxErrf("\tError Message:\t{}\n", err.msg);
} }
oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err)); oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
if (err.file != nullptr) { if (err.src.file_name() != nullptr) {
oxErrf("\tError Location:\t{}:{}\n", err.file, err.line); oxErrf("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line());
} }
#ifdef OX_USE_STDLIB #ifdef OX_USE_STDLIB
printStackTrace(2); printStackTrace(2);
@ -32,16 +33,19 @@ void panic(StringViewCR file, int line, StringViewCR panicMsg, const Error &err)
#endif #endif
} }
void panic(const char *file, int line, const char *panicMsg, const Error &err) noexcept { void panic(const char *file, int const line, char const*panicMsg, Error const&err) noexcept {
panic(StringView{file}, line, StringView{panicMsg}, err); panic(StringView{file}, line, StringView{panicMsg}, err);
} }
void assertFailFuncRuntime(StringViewCR file, int line, StringViewCR assertTxt, StringViewCR msg) noexcept { void assertFailFuncRuntime(
StringViewCR file,
int const line,
StringViewCR assertTxt,
StringViewCR msg) noexcept {
#ifdef OX_USE_STDLIB #ifdef OX_USE_STDLIB
auto output = sfmt("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg); auto const st = genStackTrace(2);
output += genStackTrace(2); oxTracef("assert", "Failed assert: {} ({}) [{}:{}]:\n{}", msg, assertTxt, file, line, st);
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line); abort();
std::abort();
#else #else
oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg); oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg);
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line); oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line);
@ -49,20 +53,25 @@ void assertFailFuncRuntime(StringViewCR file, int line, StringViewCR assertTxt,
#endif #endif
} }
void assertFailFuncRuntime(StringViewCR file, int line, [[maybe_unused]] const Error &err, StringViewCR, StringViewCR assertMsg) noexcept { void assertFailFuncRuntime(
StringViewCR file,
int const line,
[[maybe_unused]] Error const&err,
StringViewCR,
StringViewCR assertMsg) noexcept {
#if defined(OX_USE_STDLIB) #if defined(OX_USE_STDLIB)
auto msg = sfmt("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, assertMsg); auto msg = sfmt("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, assertMsg);
if (err.msg) { if (err.msg) {
msg += sfmt("\tError Message:\t{}\n", err.msg); msg += sfmt("\tError Message:\t{}\n", err.msg);
} }
msg += sfmt("\tError Code:\t{}\n", static_cast<ErrorCode>(err)); msg += sfmt("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
if (err.file != nullptr) { if (err.src.file_name() != nullptr) {
msg += sfmt("\tError Location:\t{}:{}\n", err.file, err.line); msg += sfmt("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line());
} }
msg += genStackTrace(2); msg += genStackTrace(2);
oxErr(msg); oxErr(msg);
oxTracef("assert", "Failed assert: {} [{}:{}]", assertMsg, file, line); oxTracef("assert", "Failed assert: {} [{}:{}]", assertMsg, file, line);
std::abort(); abort();
#else #else
constexprPanic(file, line, assertMsg); constexprPanic(file, line, assertMsg);
#endif #endif

View File

@ -22,9 +22,15 @@
namespace ox { namespace ox {
void panic(StringViewCR file, int line, StringViewCR panicMsg, const Error &err = ox::Error(0)) noexcept; [[noreturn]]
void panic(StringViewCR file, int line, StringViewCR panicMsg, Error const&err = {}) noexcept;
constexpr void constexprPanic(StringViewCR file, int line, StringViewCR panicMsg, const Error &err = ox::Error(0)) noexcept { [[noreturn]]
constexpr void constexprPanic(
StringViewCR file,
int const line,
StringViewCR panicMsg,
Error const&err = {}) noexcept {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
panic(file, line, panicMsg, err); panic(file, line, panicMsg, err);
} else { } else {
@ -32,10 +38,24 @@ constexpr void constexprPanic(StringViewCR file, int line, StringViewCR panicMsg
} }
} }
void assertFailFuncRuntime(StringViewCR file, int line, StringViewCR assertTxt, StringViewCR msg) noexcept; void assertFailFuncRuntime(
void assertFailFuncRuntime(StringViewCR file, int line, const Error &err, StringViewCR, StringViewCR assertMsg) noexcept; StringViewCR file,
int line,
StringViewCR assertTxt,
StringViewCR msg) noexcept;
void assertFailFuncRuntime(
StringViewCR file,
int line,
Error const&err,
StringViewCR,
StringViewCR assertMsg) noexcept;
constexpr void assertFunc(StringViewCR file, int line, bool pass, [[maybe_unused]]StringViewCR assertTxt, [[maybe_unused]]StringViewCR msg) noexcept { constexpr void assertFunc(
StringViewCR file,
int const line,
bool const pass,
[[maybe_unused]]StringViewCR assertTxt,
[[maybe_unused]]StringViewCR msg) noexcept {
if (!pass) { if (!pass) {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
assertFailFuncRuntime(file, line, assertTxt, msg); assertFailFuncRuntime(file, line, assertTxt, msg);
@ -45,7 +65,12 @@ constexpr void assertFunc(StringViewCR file, int line, bool pass, [[maybe_unused
} }
} }
constexpr void assertFunc(StringViewCR file, int line, const Error &err, StringViewCR, StringViewCR assertMsg) noexcept { constexpr void assertFunc(
StringViewCR file,
int const line,
Error const&err,
StringViewCR,
StringViewCR assertMsg) noexcept {
if (err) { if (err) {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
assertFailFuncRuntime(file, line, err, {}, assertMsg); assertFailFuncRuntime(file, line, err, {}, assertMsg);
@ -55,7 +80,11 @@ constexpr void assertFunc(StringViewCR file, int line, const Error &err, StringV
} }
} }
constexpr void expect(StringViewCR file, int line, const auto &actual, const auto &expected) noexcept { constexpr void expect(
StringViewCR file,
int const line,
auto const&actual,
auto const&expected) noexcept {
if (actual != expected) { if (actual != expected) {
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
#if defined(OX_USE_STDLIB) #if defined(OX_USE_STDLIB)

View File

@ -36,28 +36,16 @@ using ErrorCode = uint16_t;
struct [[nodiscard]] Error { struct [[nodiscard]] Error {
std::source_location src;
ox::CString msg = nullptr; ox::CString msg = nullptr;
ox::CString file = nullptr;
uint16_t line = 0;
ErrorCode errCode = 0; ErrorCode errCode = 0;
constexpr Error() noexcept = default; constexpr Error() noexcept = default;
explicit constexpr Error(
ox::CString file,
uint32_t const line,
ErrorCode const errCode,
ox::CString msg = nullptr) noexcept:
msg{msg},
file{file},
line{static_cast<uint16_t>(line)},
errCode{errCode} {}
explicit constexpr Error( explicit constexpr Error(
ErrorCode const errCode, ErrorCode const errCode,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
file{src.file_name()}, src{src},
line{static_cast<uint16_t>(src.line())},
errCode{errCode} errCode{errCode}
{} {}
@ -65,9 +53,8 @@ struct [[nodiscard]] Error {
ErrorCode const errCode, ErrorCode const errCode,
ox::CString msg, ox::CString msg,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
src{src},
msg{msg}, msg{msg},
file{src.file_name()},
line{static_cast<uint16_t>(src.line())},
errCode{errCode} errCode{errCode}
{} {}
@ -89,42 +76,31 @@ constexpr auto toStr(Error const&err) noexcept {
} }
struct Exception: public std::exception { struct Exception: public std::exception {
std::source_location src;
ox::CString msg = nullptr; ox::CString msg = nullptr;
ox::CString file = nullptr;
uint16_t line = 0;
ErrorCode errCode = 0; ErrorCode errCode = 0;
explicit inline Exception(ox::CString file, uint32_t line, ErrorCode errCode, char const*msg = "") noexcept { explicit Exception(
this->file = file;
this->line = static_cast<uint16_t>(line);
this->msg = msg;
this->errCode = errCode;
}
explicit inline Exception(
ErrorCode const errCode, ErrorCode const errCode,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
file{src.file_name()}, src{src},
line{static_cast<uint16_t>(src.line())},
errCode{errCode} {} errCode{errCode} {}
explicit inline Exception( explicit Exception(
ErrorCode const errCode, ErrorCode const errCode,
ox::CString msg, ox::CString msg,
std::source_location const&src = std::source_location::current()) noexcept: std::source_location const&src = std::source_location::current()) noexcept:
src{src},
msg{msg}, msg{msg},
file{src.file_name()},
line{static_cast<uint16_t>(src.line())},
errCode{errCode} {} errCode{errCode} {}
explicit inline Exception(Error const&err) noexcept: explicit Exception(Error const&err) noexcept:
src{err.src},
msg{err.msg ? err.msg : ""}, msg{err.msg ? err.msg : ""},
file{err.file},
line{err.line},
errCode{err.errCode} {} errCode{err.errCode} {}
constexpr Error toError() const noexcept { constexpr Error toError() const noexcept {
return Error(file, line, errCode, msg); return Error(errCode, msg, src);
} }
[[nodiscard]] [[nodiscard]]
@ -133,6 +109,7 @@ struct Exception: public std::exception {
} }
}; };
[[noreturn]]
void panic(char const*file, int line, char const*panicMsg, Error const&err) noexcept; void panic(char const*file, int line, char const*panicMsg, Error const&err) noexcept;
template<typename T> template<typename T>

View File

@ -11,7 +11,6 @@
#include "algorithm.hpp" #include "algorithm.hpp"
#include "hash.hpp" #include "hash.hpp"
#include "ignore.hpp" #include "ignore.hpp"
#include "optional.hpp"
#include "stringview.hpp" #include "stringview.hpp"
#include "strops.hpp" #include "strops.hpp"
#include "vector.hpp" #include "vector.hpp"
@ -27,12 +26,11 @@ class HashMap {
private: private:
struct Pair { struct Pair {
UPtr<Pair> next;
K key = {}; K key = {};
T value{}; T value{};
}; };
Vector<K> m_keys; Vector<K> m_keys;
Vector<UPtr<Pair>> m_pairs; Vector<Pair*> m_pairs;
public: public:
explicit constexpr HashMap(std::size_t size = 127); explicit constexpr HashMap(std::size_t size = 127);
@ -75,10 +73,10 @@ class HashMap {
constexpr void expand(); constexpr void expand();
template<typename KK> template<typename KK>
constexpr UPtr<Pair> const &access(Vector<UPtr<Pair>> const &pairs, KK const &key) const; constexpr Pair *const&access(Vector<Pair*> const&pairs, KK const&key) const;
template<typename KK> template<typename KK>
constexpr UPtr<Pair> &access(Vector<UPtr<Pair>> &pairs, KK const &key); constexpr Pair *&access(Vector<Pair*> &pairs, KK const&key);
}; };
@ -87,13 +85,14 @@ constexpr HashMap<K, T>::HashMap(std::size_t size): m_pairs(size) {
} }
template<typename K, typename T> template<typename K, typename T>
constexpr HashMap<K, T>::HashMap(HashMap const &other) { constexpr HashMap<K, T>::HashMap(HashMap<K, T> const&other) {
operator=(other); m_pairs = other.m_pairs;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr HashMap<K, T>::HashMap(HashMap &&other) noexcept { constexpr HashMap<K, T>::HashMap(HashMap<K, T> &&other) noexcept {
operator=(std::move(other)); m_keys = std::move(other.m_keys);
m_pairs = std::move(other.m_pairs);
} }
template<typename K, typename T> template<typename K, typename T>
@ -102,7 +101,7 @@ constexpr HashMap<K, T>::~HashMap() {
} }
template<typename K, typename T> template<typename K, typename T>
constexpr bool HashMap<K, T>::operator==(HashMap const &other) const { constexpr bool HashMap<K, T>::operator==(HashMap const&other) const {
if (m_keys != other.m_keys) { if (m_keys != other.m_keys) {
return false; return false;
} }
@ -116,25 +115,19 @@ constexpr bool HashMap<K, T>::operator==(HashMap const &other) const {
} }
template<typename K, typename T> template<typename K, typename T>
constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap const &other) { constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap<K, T> const&other) {
if (this != &other) { if (this != &other) {
clear(); clear();
m_keys = other.m_keys; m_keys = other.m_keys;
m_pairs.resize(other.m_pairs.size()); m_pairs = other.m_pairs;
for (auto const&k : m_keys) {
auto const &src = access(other.m_pairs, k);
auto &dst = access(m_pairs, k);
dst = ox::make_unique<Pair>();
dst->key = src->key;
dst->value = src->value;
}
} }
return *this; return *this;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap &&other) noexcept { constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap<K, T> &&other) noexcept {
if (this != &other) { if (this != &other) {
clear();
m_keys = std::move(other.m_keys); m_keys = std::move(other.m_keys);
m_pairs = std::move(other.m_pairs); m_pairs = std::move(other.m_pairs);
} }
@ -142,52 +135,60 @@ constexpr HashMap<K, T> &HashMap<K, T>::operator=(HashMap &&other) noexcept {
} }
template<typename K, typename T> template<typename K, typename T>
constexpr T &HashMap<K, T>::operator[](MaybeView_t<K> const &key) { constexpr T &HashMap<K, T>::operator[](MaybeView_t<K> const&k) {
auto p = &access(m_pairs, key); auto p = &access(m_pairs, k);
if (*p == nullptr) { if (*p == nullptr) {
if (static_cast<double>(m_pairs.size()) * 0.7 < if (static_cast<double>(m_pairs.size()) * 0.7 <
static_cast<double>(m_keys.size())) { static_cast<double>(m_keys.size())) {
expand(); expand();
p = &access(m_pairs, key); p = &access(m_pairs, k);
} }
*p = ox::make_unique<Pair>(); *p = new Pair;
(*p)->key = key; (*p)->key = k;
m_keys.emplace_back(key); m_keys.emplace_back(k);
} }
return (*p)->value; return (*p)->value;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr Result<T*> HashMap<K, T>::at(MaybeView_t<K> const &key) noexcept { constexpr Result<T*> HashMap<K, T>::at(MaybeView_t<K> const&k) noexcept {
auto &p = access(m_pairs, key); auto p = access(m_pairs, k);
if (!p) { if (!p) {
return ox::Error{1, "value not found for given key"}; return {nullptr, ox::Error(1, "value not found for given key")};
} }
return &p->value; return &p->value;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr Result<const T*> HashMap<K, T>::at(MaybeView_t<K> const &key) const noexcept { constexpr Result<const T*> HashMap<K, T>::at(MaybeView_t<K> const&k) const noexcept {
auto &p = access(m_pairs, key); auto p = access(m_pairs, k);
if (!p) { if (!p) {
return ox::Error{1, "value not found for given key"}; return {nullptr, ox::Error(1, "value not found for given key")};
} }
return &p->value; return &p->value;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr void HashMap<K, T>::erase(MaybeView_t<K> const &key) { constexpr void HashMap<K, T>::erase(MaybeView_t<K> const&k) {
if (!contains(key)) { if (!contains(k)) {
return; return;
} }
auto &c = access(m_pairs, key); auto h = ox::hash<MaybeView_t<K>>{}(k) % m_pairs.size();
c = std::move(c->next); while (true) {
std::ignore = m_keys.erase(ox::find(m_keys.begin(), m_keys.end(), key)); const auto &p = m_pairs[h];
if (p == nullptr || p->key == k) {
std::ignore = m_pairs.erase(h);
break;
} else {
h = ox::hash<MaybeView_t<K>>{}(k) % m_pairs.size();
}
}
std::ignore = m_keys.erase(ox::find(m_keys.begin(), m_keys.end(), k));
} }
template<typename K, typename T> template<typename K, typename T>
constexpr bool HashMap<K, T>::contains(MaybeView_t<K> const &key) const noexcept { constexpr bool HashMap<K, T>::contains(MaybeView_t<K> const&k) const noexcept {
return access(m_pairs, key).get() != nullptr; return access(m_pairs, k) != nullptr;
} }
template<typename K, typename T> template<typename K, typename T>
@ -203,26 +204,27 @@ constexpr Vector<K> const&HashMap<K, T>::keys() const noexcept {
template<typename K, typename T> template<typename K, typename T>
constexpr Vector<T> HashMap<K, T>::values() const noexcept { constexpr Vector<T> HashMap<K, T>::values() const noexcept {
Vector<T> out; Vector<T> out;
out.reserve(m_keys.size()); out.reserve(m_pairs.size());
for (auto const &p : m_pairs) { for (auto const&p : m_pairs) {
if (out) { out.emplace_back(p->value);
out.emplace_back(p->value);
}
} }
return out; return out;
} }
template<typename K, typename T> template<typename K, typename T>
constexpr void HashMap<K, T>::clear() { constexpr void HashMap<K, T>::clear() {
for (std::size_t i = 0; i < m_pairs.size(); i++) {
delete m_pairs[i];
}
m_pairs.clear(); m_pairs.clear();
m_pairs.resize(127); m_pairs.resize(127);
} }
template<typename K, typename T> template<typename K, typename T>
constexpr void HashMap<K, T>::expand() { constexpr void HashMap<K, T>::expand() {
Vector<UPtr<Pair>> r{m_pairs.size() * 2}; Vector<Pair*> r(m_pairs.size() * 2);
for (std::size_t i = 0; i < m_keys.size(); ++i) { for (std::size_t i = 0; i < m_keys.size(); ++i) {
auto const &k = m_keys[i]; auto const&k = m_keys[i];
access(r, k) = std::move(access(m_pairs, k)); access(r, k) = std::move(access(m_pairs, k));
} }
m_pairs = std::move(r); m_pairs = std::move(r);
@ -230,39 +232,29 @@ constexpr void HashMap<K, T>::expand() {
template<typename K, typename T> template<typename K, typename T>
template<typename KK> template<typename KK>
constexpr UPtr<typename HashMap<K, T>::Pair> const &HashMap<K, T>::access( constexpr typename HashMap<K, T>::Pair *const&HashMap<K, T>::access(Vector<Pair*> const&pairs, KK const&k) const {
Vector<UPtr<Pair>> const& pairs, auto h = static_cast<std::size_t>(ox::hash<KK>{}(k) % pairs.size());
KK const &key) const {
auto const h = static_cast<std::size_t>(ox::hash<KK>{}(key) % pairs.size());
auto const &p = *pairs.at(h).unwrap();
if (p == nullptr || p->key == key) {
return p;
}
auto c = &p->next;
while (true) { while (true) {
if (*c == nullptr || (*c)->key == key) { auto const&p = *pairs.at(h).unwrap();
return *c; if (p == nullptr || p->key == k) {
return p;
} else {
h = (h + 1) % pairs.size();
} }
c = &(*c)->next;
} }
} }
template<typename K, typename T> template<typename K, typename T>
template<typename KK> template<typename KK>
constexpr UPtr<typename HashMap<K, T>::Pair> &HashMap<K, T>::access( constexpr typename HashMap<K, T>::Pair *&HashMap<K, T>::access(Vector<Pair*> &pairs, KK const&k) {
Vector<UPtr<Pair>> &pairs, auto h = static_cast<std::size_t>(ox::hash<KK>{}(k) % pairs.size());
KK const &key) {
auto const h = static_cast<std::size_t>(ox::hash<KK>{}(key) % pairs.size());
auto &p = *pairs.at(h).unwrap();
if (p == nullptr || p->key == key) {
return p;
}
auto c = &p->next;
while (true) { while (true) {
if (*c == nullptr || (*c)->key == key) { auto &p = *pairs.at(h).unwrap();
return *c; if (p == nullptr || p->key == k) {
return p;
} else {
h = (h + 1) % pairs.size();
} }
c = &(*c)->next;
} }
} }

View File

@ -260,12 +260,12 @@ constexpr bool operator==(const UniquePtr<T> &p1, const UniquePtr<T> &p2) noexce
template<typename T> template<typename T>
constexpr bool operator==(const UniquePtr<T> &p1, std::nullptr_t) noexcept { constexpr bool operator==(const UniquePtr<T> &p1, std::nullptr_t) noexcept {
return p1.get() == nullptr; return p1.get();
} }
template<typename T> template<typename T>
constexpr bool operator==(std::nullptr_t, const UniquePtr<T> &p2) noexcept { constexpr bool operator==(std::nullptr_t, const UniquePtr<T> &p2) noexcept {
return p2.get() == nullptr; return p2.get();
} }

View File

@ -31,7 +31,6 @@ concept Reader_c = requires(T v) {
class Reader_v { class Reader_v {
public: public:
virtual constexpr ~Reader_v() noexcept = default; virtual constexpr ~Reader_v() noexcept = default;
[[nodiscard]]
virtual constexpr ox::Result<char> peek() const noexcept = 0; virtual constexpr ox::Result<char> peek() const noexcept = 0;
virtual constexpr ox::Result<std::size_t> read(char*, std::size_t) noexcept = 0; virtual constexpr ox::Result<std::size_t> read(char*, std::size_t) noexcept = 0;
virtual constexpr ox::Result<std::size_t> tellg() noexcept = 0; virtual constexpr ox::Result<std::size_t> tellg() noexcept = 0;

View File

@ -13,3 +13,12 @@
#else #else
#define assert(e) while (!(e)); #define assert(e) while (!(e));
#endif #endif
#if __has_include(<cstdlib>)
#include <cstdlib>
#else
extern "C" {
[[noreturn]]
void abort();
}
#endif

View File

@ -62,6 +62,9 @@ class SmallMap {
[[nodiscard]] [[nodiscard]]
constexpr Vector<K> keys() const noexcept; constexpr Vector<K> keys() const noexcept;
[[nodiscard]]
constexpr Vector<T> values() const noexcept;
[[nodiscard]] [[nodiscard]]
constexpr K const&key(size_t i) const noexcept; constexpr K const&key(size_t i) const noexcept;
@ -82,14 +85,22 @@ class SmallMap {
return m_pairs; return m_pairs;
} }
[[nodiscard]]
constexpr ox::Span<Pair> pairs() noexcept {
return m_pairs;
}
constexpr void clear(); constexpr void clear();
private: private:
template<typename KK> template<typename KK>
constexpr Pair const&access(PairVector const&pairs, KK const&key, bool &isNew) const; constexpr Pair const*access(PairVector const&pairs, KK const&key, bool &isNew) const;
template<typename KK> template<typename KK>
constexpr Pair &access(PairVector &pairs, KK const&key, bool &isNew); constexpr Pair *access(PairVector &pairs, KK const&key, bool &isNew);
template<typename KK>
constexpr Pair *accessNoCreate(PairVector &pairs, KK const&key);
}; };
@ -129,7 +140,7 @@ constexpr SmallMap<K, T, SmallSz> &SmallMap<K, T, SmallSz>::operator=(SmallMap<K
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr T &SmallMap<K, T, SmallSz>::operator[](MaybeView_t<K> const&k) { constexpr T &SmallMap<K, T, SmallSz>::operator[](MaybeView_t<K> const&k) {
bool isNew{}; bool isNew{};
auto p = &access(m_pairs, k, isNew); auto p = access(m_pairs, k, isNew);
if (isNew) { if (isNew) {
p->key = k; p->key = k;
} }
@ -138,7 +149,7 @@ constexpr T &SmallMap<K, T, SmallSz>::operator[](MaybeView_t<K> const&k) {
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr Result<T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) noexcept { constexpr Result<T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) noexcept {
auto p = access(m_pairs, k); auto const p = accessNoCreate(m_pairs, k);
if (!p) { if (!p) {
return {nullptr, ox::Error(1, "value not found for given key")}; return {nullptr, ox::Error(1, "value not found for given key")};
} }
@ -147,7 +158,8 @@ constexpr Result<T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) noexcep
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr Result<const T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) const noexcept { constexpr Result<const T*> SmallMap<K, T, SmallSz>::at(MaybeView_t<K> const&k) const noexcept {
auto p = access(m_pairs, k); bool isNew{};
auto p = access(m_pairs, k, isNew);
if (!p) { if (!p) {
return {nullptr, ox::Error(1, "value not found for given key")}; return {nullptr, ox::Error(1, "value not found for given key")};
} }
@ -168,7 +180,8 @@ constexpr void SmallMap<K, T, SmallSz>::erase(MaybeView_t<K> const&k) {
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr bool SmallMap<K, T, SmallSz>::contains(MaybeView_t<K> const&k) const noexcept { constexpr bool SmallMap<K, T, SmallSz>::contains(MaybeView_t<K> const&k) const noexcept {
return access(m_pairs, k) != nullptr; bool isNew{};
return access(m_pairs, k, isNew) != nullptr;
} }
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
@ -186,6 +199,16 @@ constexpr Vector<K> SmallMap<K, T, SmallSz>::keys() const noexcept {
return keys; return keys;
} }
template<typename K, typename T, size_t SmallSz>
constexpr Vector<T> SmallMap<K, T, SmallSz>::values() const noexcept {
ox::Vector<T> keys;
keys.reserve(m_pairs.size());
for (auto const&p : m_pairs) {
keys.emplace_back(p.key);
}
return keys;
}
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
constexpr K const&SmallMap<K, T, SmallSz>::key(size_t i) const noexcept { constexpr K const&SmallMap<K, T, SmallSz>::key(size_t i) const noexcept {
return m_pairs[i].key; return m_pairs[i].key;
@ -218,30 +241,42 @@ constexpr void SmallMap<K, T, SmallSz>::clear() {
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
template<typename KK> template<typename KK>
constexpr typename SmallMap<K, T, SmallSz>::Pair const&SmallMap<K, T, SmallSz>::access( constexpr typename SmallMap<K, T, SmallSz>::Pair const*SmallMap<K, T, SmallSz>::access(
PairVector const&pairs, KK const&k, bool &isNew) const { PairVector const&pairs, KK const&k, bool &isNew) const {
for (auto const&p : pairs) { for (auto const&p : pairs) {
if (p.key == k) { if (p.key == k) {
isNew = false; isNew = false;
return p; return &p;
} }
} }
isNew = true; isNew = true;
return pairs.emplace_back(); return nullptr;
} }
template<typename K, typename T, size_t SmallSz> template<typename K, typename T, size_t SmallSz>
template<typename KK> template<typename KK>
constexpr typename SmallMap<K, T, SmallSz>::Pair &SmallMap<K, T, SmallSz>::access( constexpr typename SmallMap<K, T, SmallSz>::Pair *SmallMap<K, T, SmallSz>::access(
PairVector &pairs, KK const&k, bool &isNew) { PairVector &pairs, KK const&k, bool &isNew) {
for (auto &p : pairs) { for (auto &p : pairs) {
if (p.key == k) { if (p.key == k) {
isNew = false; isNew = false;
return p; return &p;
} }
} }
isNew = true; isNew = true;
return pairs.emplace_back(); return &pairs.emplace_back();
}
template<typename K, typename T, size_t SmallSz>
template<typename KK>
constexpr typename SmallMap<K, T, SmallSz>::Pair *SmallMap<K, T, SmallSz>::accessNoCreate(
PairVector &pairs, KK const&k) {
for (auto &p : pairs) {
if (p.key == k) {
return &p;
}
}
return nullptr;
} }
template<typename T, typename K, typename V, size_t SmallSz> template<typename T, typename K, typename V, size_t SmallSz>

View File

@ -392,7 +392,7 @@ template<std::size_t SmallStringSize_v>
constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(const char *str) const noexcept { constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operator+(const char *str) const noexcept {
const std::size_t strLen = ox::strlen(str); const std::size_t strLen = ox::strlen(str);
const auto currentLen = len(); const auto currentLen = len();
BasicString<SmallStringSize_v> cpy; BasicString<SmallStringSize_v> cpy(currentLen + strLen);
cpy.m_buff.resize(m_buff.size() + strLen); cpy.m_buff.resize(m_buff.size() + strLen);
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
ox::listcpy(&cpy.m_buff[currentLen], str, strLen); ox::listcpy(&cpy.m_buff[currentLen], str, strLen);
@ -425,8 +425,7 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operato
BasicString<SmallStringSize_v> cpy(currentLen + strLen); BasicString<SmallStringSize_v> cpy(currentLen + strLen);
cpy.m_buff.resize(m_buff.size() + strLen); cpy.m_buff.resize(m_buff.size() + strLen);
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen); ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen + 1);
cpy.m_buff[cpy.m_buff.size() - 1] = 0;
return cpy; return cpy;
} }
@ -437,8 +436,7 @@ constexpr BasicString<SmallStringSize_v> BasicString<SmallStringSize_v>::operato
BasicString<SmallStringSize_v> cpy(currentLen + strLen); BasicString<SmallStringSize_v> cpy(currentLen + strLen);
cpy.m_buff.resize(m_buff.size() + strLen); cpy.m_buff.resize(m_buff.size() + strLen);
ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen); ox::listcpy(&cpy.m_buff[0], m_buff.data(), currentLen);
ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen); ox::listcpy(&cpy.m_buff[currentLen], src.data(), strLen + 1);
cpy.m_buff[cpy.m_buff.size() - 1] = 0;
return cpy; return cpy;
} }

View File

@ -17,6 +17,8 @@ add_test("[ox/std] String" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "String")
add_test("[ox/std] SmallMap" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap") add_test("[ox/std] SmallMap" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap")
add_test("[ox/std] SmallMap2" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap2") add_test("[ox/std] SmallMap2" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap2")
add_test("[ox/std] Vector" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Vector") add_test("[ox/std] Vector" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Vector")
add_test("[ox/std] Vector::shrink_to_fit" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Vector::shrink_to_fit")
add_test("[ox/std] findIdx" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "findIdx")
add_test("[ox/std] HashMap" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "HashMap") add_test("[ox/std] HashMap" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "HashMap")
add_test("[ox/std] HeapMgr" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest malloc) add_test("[ox/std] HeapMgr" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest malloc)
add_test("[ox/std] Serialize-Int" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Serialize-Int") add_test("[ox/std] Serialize-Int" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Serialize-Int")

View File

@ -237,6 +237,50 @@ OX_CLANG_NOWARN_END
return ox::Error(0); return ox::Error(0);
} }
}, },
{
"Vector::shrink_to_fit",
[] {
{
ox::Vector<ox::IString<8>> v;
v.reserve(50);
v.emplace_back("asdf");
v.emplace_back("aoeu");
auto const origData = v.data();
v.shrink_to_fit();
oxExpect(v[0], "asdf");
oxExpect(v[1], "aoeu");
oxExpect(v.capacity(), 2u);
oxAssert(origData != v.data(), "shrink_to_fit did not create a new allocation");
}
{
ox::Vector<ox::IString<8>> v;
v.reserve(2);
v.emplace_back("asdf");
v.emplace_back("aoeu");
auto const origData = v.data();
v.shrink_to_fit();
oxExpect(v[0], "asdf");
oxExpect(v[1], "aoeu");
oxExpect(v.capacity(), 2u);
oxAssert(origData == v.data(), "shrink_to_fit inappropriately created a new allocation");
}
return ox::Error{};
}
},
{
"findIdx",
[] {
ox::Vector<ox::IString<8>> const v {"zero", "one", "two", "three", "four"};
oxExpect(ox::findIdx(v.begin(), v.end(), "zero").or_value(5), 0u);
oxExpect(ox::findIdx(v.begin(), v.end(), "one").or_value(5), 1u);
oxExpect(ox::findIdx(v.begin(), v.end(), "two").or_value(5), 2u);
oxExpect(ox::findIdx(v.begin(), v.end(), "three").or_value(5), 3u);
oxExpect(ox::findIdx(v.begin(), v.end(), "four").or_value(5), 4u);
oxExpect(ox::findIdx(v.begin(), v.end(), "five").or_value(5), 5u);
oxExpect(ox::findIdx(v.begin(), v.end(), "six").or_value(6), 6u);
return ox::Error{};
}
},
{ {
"SmallMap", "SmallMap",
[] { [] {
@ -246,7 +290,18 @@ OX_CLANG_NOWARN_END
oxExpect(map.size(), 1u); oxExpect(map.size(), 1u);
oxExpect(map["aoeu"], ""); oxExpect(map["aoeu"], "");
oxExpect(map.size(), 2u); oxExpect(map.size(), 2u);
return ox::Error(0); ox::SmallMap<ox::String, ox::String> cmap;
cmap["asdf"] = "aoeu";
auto constexpr constTest = [](ox::SmallMap<ox::String, ox::String> const&map) {
OX_REQUIRE(asdf, map.at("asdf"));
oxExpect(*asdf, "aoeu");
oxExpect(map.size(), 1u);
auto const aoeu = map.at("aoeu");
oxExpect(aoeu.ok(), false);
oxExpect(map.size(), 1u);
return ox::Error{};
};
return constTest(cmap);
} }
}, },
{ {
@ -273,16 +328,6 @@ OX_CLANG_NOWARN_END
si["aoeu"] = 100; si["aoeu"] = 100;
oxAssert(si["asdf"] == 42, "asdf != 42"); oxAssert(si["asdf"] == 42, "asdf != 42");
oxAssert(si["aoeu"] == 100, "aoeu != 100"); oxAssert(si["aoeu"] == 100, "aoeu != 100");
si.erase("asdf");
oxAssert(!si.contains("asdf"), "wrongly contains asdf");
oxAssert(si.contains("aoeu"), "does not contains aoeu");
oxAssert(!si.at("asdf").ok(), "asdf != 0");
oxExpect(si["asdf"], 0);
oxAssert(si["aoeu"] == 100, "aoeu != 100");
auto si2 = si;
oxDebugf("{}", si2["asdf"]);
oxExpect(si2["asdf"], 0);
oxAssert(si2["aoeu"] == 100, "aoeu != 100");
ox::HashMap<int, int> ii; ox::HashMap<int, int> ii;
ii[4] = 42; ii[4] = 42;
ii[5] = 100; ii[5] = 100;

View File

@ -269,8 +269,8 @@ using TraceStream = NullStream;
inline void logError(const char *file, int line, const char *fmt, const Error &err) noexcept { inline void logError(const char *file, int line, const char *fmt, const Error &err) noexcept {
if (err) { if (err) {
TraceStream trc(file, line, "ox::error"); TraceStream trc(file, line, "ox::error");
if (err.file != nullptr) { if (err.src.file_name() != nullptr) {
trc << "Error: (" << err.file << ":" << err.line << "):"; trc << "Error: (" << err.src.file_name() << ":" << err.src.line() << "):";
} else { } else {
trc << "Error:"; trc << "Error:";
} }
@ -282,8 +282,8 @@ inline void logError(const char *file, int line, const Error &err) noexcept {
if (err) { if (err) {
TraceStream trc(file, line, "ox::error"); TraceStream trc(file, line, "ox::error");
trc << "Error:" << err; trc << "Error:" << err;
if (err.file != nullptr) { if (err.src.file_name() != nullptr) {
trc << "(" << err.file << ":" << err.line << ")"; trc << "(" << err.src.file_name() << ":" << err.src.line() << ")";
} }
} }
} }

View File

@ -313,6 +313,8 @@ class Vector: detail::VectorAllocator<T, Allocator, SmallVectorSize> {
constexpr void reserve(std::size_t cap) noexcept(useNoexcept); constexpr void reserve(std::size_t cap) noexcept(useNoexcept);
constexpr void shrink_to_fit() noexcept(useNoexcept);
private: private:
constexpr void reserveInsert( constexpr void reserveInsert(
std::size_t cap, std::size_t pos, std::size_t offset = 1) noexcept(useNoexcept); std::size_t cap, std::size_t pos, std::size_t offset = 1) noexcept(useNoexcept);
@ -341,6 +343,7 @@ constexpr Vector<T, SmallVectorSize, Allocator>::Vector(std::size_t size) noexce
template<typename T, std::size_t SmallVectorSize, typename Allocator> template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr Vector<T, SmallVectorSize, Allocator>::Vector(std::initializer_list<T> list) noexcept { constexpr Vector<T, SmallVectorSize, Allocator>::Vector(std::initializer_list<T> list) noexcept {
reserve(list.size());
for (auto &item : list) { for (auto &item : list) {
emplace_back(std::move(item)); emplace_back(std::move(item));
} }
@ -675,6 +678,24 @@ constexpr void Vector<T, SmallVectorSize, Allocator>::reserve(std::size_t cap) n
} }
} }
template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr void Vector<T, SmallVectorSize, Allocator>::shrink_to_fit() noexcept(useNoexcept) {
if (m_size == m_cap) {
return;
}
const auto oldItems = m_items;
const auto oldCap = m_cap;
m_cap = m_size;
this->allocate(&m_items, m_size);
if (oldItems) { // move over old items
for (std::size_t i = 0; i < m_size; ++i) {
std::construct_at(&m_items[i], std::move(oldItems[i]));
oldItems[i].~T();
}
this->deallocate(oldItems, oldCap);
}
}
template<typename T, std::size_t SmallVectorSize, typename Allocator> template<typename T, std::size_t SmallVectorSize, typename Allocator>
constexpr void Vector<T, SmallVectorSize, Allocator>::reserveInsert( constexpr void Vector<T, SmallVectorSize, Allocator>::reserveInsert(
std::size_t cap, std::size_t cap,

View File

@ -4,17 +4,20 @@
#include <ox/std/heapmgr.hpp> #include <ox/std/heapmgr.hpp>
#include <teagba/bios.hpp>
#include <teagba/registers.hpp>
namespace mgba { namespace mgba {
void initConsole(); void initConsole();
} }
#define MEM_EWRAM_BEGIN reinterpret_cast<char*>(0x02000000) #define MEM_HEAP_BEGIN reinterpret_cast<char*>(0x02000000)
#define MEM_EWRAM_END reinterpret_cast<char*>(0x0203FFFF) #define MEM_HEAP_END reinterpret_cast<char*>(0x0203FFFF)
#define HEAP_BEGIN reinterpret_cast<char*>(MEM_EWRAM_BEGIN) #define HEAP_BEGIN reinterpret_cast<char*>(MEM_HEAP_BEGIN)
// set size to half of EWRAM // set size to half of EWRAM
#define HEAP_SIZE ((MEM_EWRAM_END - MEM_EWRAM_BEGIN) / 2) #define HEAP_SIZE ((MEM_HEAP_END - MEM_HEAP_BEGIN) / 2)
#define HEAP_END reinterpret_cast<char*>(MEM_EWRAM_BEGIN + HEAP_SIZE) #define HEAP_END reinterpret_cast<char*>(MEM_HEAP_BEGIN + HEAP_SIZE)
extern void (*__preinit_array_start[]) (void); extern void (*__preinit_array_start[]) (void);
extern void (*__preinit_array_end[]) (void); extern void (*__preinit_array_end[]) (void);
@ -25,6 +28,14 @@ int main(int argc, const char **argv);
extern "C" { extern "C" {
void abort() {
REG_IE = 0;
teagba::intrwait(0, 0);
while (true);
}
void *__gxx_personality_v0{};
void __libc_init_array() { void __libc_init_array() {
auto preInits = __preinit_array_end - __preinit_array_start; auto preInits = __preinit_array_end - __preinit_array_start;
for (decltype(preInits) i = 0; i < preInits; i++) { for (decltype(preInits) i = 0; i < preInits; i++) {

View File

@ -102,8 +102,6 @@ OX_MODEL_BEGIN(TileSheetSet)
OX_MODEL_FIELD(entries) OX_MODEL_FIELD(entries)
OX_MODEL_END() OX_MODEL_END()
void addEntry(TileSheetSet &set, ox::FileAddress path, int32_t begin = 0, int32_t size = -1) noexcept;
[[nodiscard]] [[nodiscard]]
int tileColumns(Context&) noexcept; int tileColumns(Context&) noexcept;

View File

@ -212,6 +212,10 @@ struct TileSheetV4 {
pixels(std::move(pPixels)) { pixels(std::move(pPixels)) {
} }
/**
*
* @return the dimensional size of the SubSheet (e.g. width * height)
*/
[[nodiscard]] [[nodiscard]]
constexpr std::size_t size() const noexcept { constexpr std::size_t size() const noexcept {
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows); return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
@ -281,7 +285,7 @@ size_t getTileCnt(TileSheet const&ts) noexcept;
TileSheet::SubSheet const*getSubsheet(TileSheet const&ts, SubSheetId id) noexcept; TileSheet::SubSheet const*getSubsheet(TileSheet const&ts, SubSheetId id) noexcept;
[[nodiscard]] [[nodiscard]]
size_t getTileIdx(TileSheet const&ts, SubSheetId id) noexcept; ox::Optional<size_t> getTileIdx(TileSheet const&ts, SubSheetId id) noexcept;
[[nodiscard]] [[nodiscard]]
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept; uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept;
@ -400,6 +404,8 @@ ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::StringViewCR path) noexc
ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept; ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept;
ox::Result<uint32_t> getTileOffset(TileSheet const&ts, SubSheetId pId) noexcept;
ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept; ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept;
ox::Result<ox::StringView> getNameFor(TileSheet const&ts, SubSheetId pId) noexcept; ox::Result<ox::StringView> getNameFor(TileSheet const&ts, SubSheetId pId) noexcept;

View File

@ -99,10 +99,10 @@ ox::Error loadBgTileSheet(
size_t const tileCnt) noexcept { size_t const tileCnt) noexcept {
size_t const bppMod = ts.bpp == 4; size_t const bppMod = ts.bpp == 4;
size_t const bytesPerTile = PixelsPerTile >> bppMod; size_t const bytesPerTile = PixelsPerTile >> bppMod;
auto const pixCnt = tileCnt * bytesPerTile; auto const cnt = (tileCnt * bytesPerTile) / 2;
auto const srcPxIdx = srcTileIdx * bytesPerTile; auto const srcPxIdx = srcTileIdx * bytesPerTile;
auto const dstPxIdx = (dstTileIdx * bytesPerTile) / 2; auto const dstPxIdx = (dstTileIdx * bytesPerTile) / 2;
for (size_t i = 0; i < pixCnt; ++i) { for (size_t i = 0; i < cnt; ++i) {
auto const srcIdx = srcPxIdx + i * 2; auto const srcIdx = srcPxIdx + i * 2;
auto const p1 = static_cast<uint16_t>(ts.pixels[srcIdx]); auto const p1 = static_cast<uint16_t>(ts.pixels[srcIdx]);
auto const p2 = static_cast<uint16_t>(ts.pixels[srcIdx + 1]); auto const p2 = static_cast<uint16_t>(ts.pixels[srcIdx + 1]);

View File

@ -4,6 +4,7 @@
#include <ox/std/def.hpp> #include <ox/std/def.hpp>
#include <ox/std/realstd.hpp>
#include <keel/media.hpp> #include <keel/media.hpp>
#include <turbine/turbine.hpp> #include <turbine/turbine.hpp>
@ -48,12 +49,10 @@ OX_ALLOW_UNSAFE_BUFFERS_END
oxErrf("\tError Message:\t{}\n", err.msg); oxErrf("\tError Message:\t{}\n", err.msg);
} }
oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err)); oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
if (err.file != nullptr) { if (err.src.file_name() != nullptr) {
oxErrf("\tError Location:\t{}:{}\n", err.file, err.line); oxErrf("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line());
} }
// disable all interrupt handling and IntrWait on no interrupts abort();
REG_IE = 0;
teagba::intrwait(0, 0);
} }
} }

View File

@ -1,7 +1,6 @@
target_sources( target_sources(
NostalgiaCore-Studio PRIVATE NostalgiaCore-Studio PRIVATE
commands/addcolorcommand.cpp commands/addcolorcommand.cpp
commands/addpagecommand.cpp
commands/applycolorallpagescommand.cpp commands/applycolorallpagescommand.cpp
commands/duplicatepagecommand.cpp commands/duplicatepagecommand.cpp
commands/movecolorcommand.cpp commands/movecolorcommand.cpp

View File

@ -1,28 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include "commands.hpp"
#include "addpagecommand.hpp"
namespace nostalgia::core {
AddPageCommand::AddPageCommand(Palette &pal) noexcept:
m_pal(pal) {}
int AddPageCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::AddPage);
}
ox::Error AddPageCommand::redo() noexcept {
m_pal.pages.emplace_back(ox::sfmt("Page {}", m_pal.pages.size() + 1), ox::Vector<PaletteColor>{});
return {};
}
ox::Error AddPageCommand::undo() noexcept {
m_pal.pages.pop_back();
return {};
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <studio/studio.hpp>
#include <nostalgia/core/palette.hpp>
namespace nostalgia::core {
class AddPageCommand: public studio::UndoCommand {
private:
Palette &m_pal;
public:
explicit AddPageCommand(Palette &pal) noexcept;
~AddPageCommand() noexcept override = default;
[[nodiscard]]
int commandId() const noexcept final;
ox::Error redo() noexcept final;
ox::Error undo() noexcept final;
};
}

View File

@ -9,7 +9,6 @@ namespace nostalgia::core {
enum class PaletteEditorCommandId { enum class PaletteEditorCommandId {
ApplyColorAllPages, ApplyColorAllPages,
RenamePage, RenamePage,
AddPage,
DuplicatePage, DuplicatePage,
RemovePage, RemovePage,
AddColor, AddColor,

View File

@ -23,7 +23,7 @@ int DuplicatePageCommand::commandId() const noexcept {
} }
ox::Error DuplicatePageCommand::redo() noexcept { ox::Error DuplicatePageCommand::redo() noexcept {
m_pal.pages.emplace(m_dstIdx, ox::sfmt("Page {}", m_pal.pages.size() + 1), std::move(m_page)); m_pal.pages.emplace(m_dstIdx, "", std::move(m_page));
return {}; return {};
} }

View File

@ -7,7 +7,6 @@
#include <keel/media.hpp> #include <keel/media.hpp>
#include "commands/addcolorcommand.hpp" #include "commands/addcolorcommand.hpp"
#include "commands/addpagecommand.hpp"
#include "commands/applycolorallpagescommand.hpp" #include "commands/applycolorallpagescommand.hpp"
#include "commands/duplicatepagecommand.hpp" #include "commands/duplicatepagecommand.hpp"
#include "commands/movecolorcommand.hpp" #include "commands/movecolorcommand.hpp"
@ -197,11 +196,7 @@ void PaletteEditorImGui::drawPagesEditor() noexcept {
constexpr auto toolbarHeight = 40; constexpr auto toolbarHeight = 40;
auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24}; auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24};
if (ImGui::Button("Add", btnSz)) { if (ImGui::Button("Add", btnSz)) {
if (m_pal.pages.empty()) { std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size());
std::ignore = pushCommand<AddPageCommand>(m_pal);
} else {
std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size());
}
m_page = m_pal.pages.size() - 1; m_page = m_pal.pages.size() - 1;
} }
ImGui::SameLine(); ImGui::SameLine();

View File

@ -9,24 +9,27 @@ namespace nostalgia::core {
core::UpdateSubSheetCommand::UpdateSubSheetCommand( core::UpdateSubSheetCommand::UpdateSubSheetCommand(
TileSheet &img, TileSheet &img,
TileSheet::SubSheetIdx idx, TileSheet::SubSheetIdx idx,
ox::StringParam name, ox::String name,
int const cols, int cols,
int const rows): int rows) noexcept:
m_img{img}, m_img(img),
m_idx{std::move(idx)}, m_idx(std::move(idx)),
m_sheet{getSubSheet(m_img, m_idx)} { m_sheet(getSubSheet(m_img, m_idx)),
m_sheet = getSubSheet(m_img, m_idx); m_newName(std::move(name)),
m_sheet.name = std::move(name); m_newCols(cols),
OX_THROW_ERROR(resizeSubsheet(m_sheet, m_img.bpp, {cols, rows})); m_newRows(rows) {
} }
ox::Error UpdateSubSheetCommand::redo() noexcept { ox::Error UpdateSubSheetCommand::redo() noexcept {
std::swap(m_sheet, getSubSheet(m_img, m_idx)); auto &sheet = getSubSheet(m_img, m_idx);
sheet.name = m_newName;
oxLogError(resizeSubsheet(sheet, m_img.bpp, {m_newCols, m_newRows}));
return {}; return {};
} }
ox::Error UpdateSubSheetCommand::undo() noexcept { ox::Error UpdateSubSheetCommand::undo() noexcept {
std::swap(m_sheet, getSubSheet(m_img, m_idx)); auto &sheet = getSubSheet(m_img, m_idx);
sheet = m_sheet;
return {}; return {};
} }

View File

@ -13,14 +13,17 @@ class UpdateSubSheetCommand: public TileSheetCommand {
TileSheet &m_img; TileSheet &m_img;
TileSheet::SubSheetIdx m_idx; TileSheet::SubSheetIdx m_idx;
TileSheet::SubSheet m_sheet; TileSheet::SubSheet m_sheet;
ox::String m_newName;
int m_newCols = 0;
int m_newRows = 0;
public: public:
UpdateSubSheetCommand( UpdateSubSheetCommand(
TileSheet &img, TileSheet &img,
TileSheet::SubSheetIdx idx, TileSheet::SubSheetIdx idx,
ox::StringParam name, ox::String name,
int cols, int cols,
int rows); int rows) noexcept;
ox::Error redo() noexcept final; ox::Error redo() noexcept final;

View File

@ -234,7 +234,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true); ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true);
{ {
static constexpr auto btnHeight = ig::BtnSz.y; static constexpr auto btnHeight = ig::BtnSz.y;
auto constexpr btnSize = ImVec2{btnHeight, btnHeight}; auto const btnSize = ImVec2{btnHeight, btnHeight};
if (ig::PushButton("+", btnSize)) { if (ig::PushButton("+", btnSize)) {
auto insertOnIdx = m_model.activeSubSheetIdx(); auto insertOnIdx = m_model.activeSubSheetIdx();
auto const&parent = m_model.activeSubSheet(); auto const&parent = m_model.activeSubSheet();
@ -258,19 +258,16 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
m_exportMenu.show(); m_exportMenu.show();
} }
TileSheet::SubSheetIdx path; TileSheet::SubSheetIdx path;
static constexpr auto flags = static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
ImGuiTableFlags_RowBg | if (ImGui::BeginTable("Subsheets", 4, flags)) {
ImGuiTableFlags_NoBordersInBody | ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
ImGuiTableFlags_ScrollY; ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
if (ImGui::BeginTable("Subsheets", 4, flags)) { ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide); ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25); ImGui::TableHeadersRow();
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50); drawSubsheetSelector(m_view.img().subsheet, path);
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::EndTable();
ImGui::TableHeadersRow(); }
drawSubsheetSelector(m_view.img().subsheet, path);
ImGui::EndTable();
}
} }
ImGui::EndChild(); ImGui::EndChild();
} }
@ -465,12 +462,8 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
} }
} }
// header // header
auto constexpr palTblFlags =
ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingStretchProp |
ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable( if (ImGui::BeginTable(
"PaletteTable", 4, palTblFlags)) { "PaletteTable", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("Idx", 0, 0.6f); ImGui::TableSetupColumn("Idx", 0, 0.6f);
ImGui::TableSetupColumn("", 0, 0.22f); ImGui::TableSetupColumn("", 0, 0.22f);
ImGui::TableSetupColumn("Name", 0, 3); ImGui::TableSetupColumn("Name", 0, 3);

View File

@ -26,6 +26,11 @@
namespace nostalgia::core { namespace nostalgia::core {
Palette const TileSheetEditorModel::s_defaultPalette = {
.colorNames = {ox::Vector<ox::String>{{}}},
.pages = {{"Page 1", ox::Vector<Color16>(128)}},
};
// delete pixels of all non-leaf nodes // delete pixels of all non-leaf nodes
static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept { static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
if (ss.subsheets.empty()) { if (ss.subsheets.empty()) {
@ -37,14 +42,7 @@ static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
} }
} }
TileSheetEditorModel::TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack):
Palette const TileSheetEditorModel::s_defaultPalette = {
.colorNames = {ox::Vector<ox::String>{{}}},
.pages = {{"Page 1", ox::Vector<Color16>(128)}},
};
TileSheetEditorModel::TileSheetEditorModel(
studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack):
m_sctx(sctx), m_sctx(sctx),
m_tctx(m_sctx.tctx), m_tctx(m_sctx.tctx),
m_path(path), m_path(path),
@ -64,7 +62,7 @@ void TileSheetEditorModel::cut() {
TileSheetClipboard blankCb; TileSheetClipboard blankCb;
auto cb = ox::make_unique<TileSheetClipboard>(); auto cb = ox::make_unique<TileSheetClipboard>();
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
iterateSelectionRows(*m_selection, [&](int const x, int const y) { iterateSelectionRows(*m_selection, [&](int x, int y) {
auto pt = ox::Point{x, y}; auto pt = ox::Point{x, y};
auto const idx = core::idx(s, pt); auto const idx = core::idx(s, pt);
auto const c = getPixel(s, m_img.bpp, idx); auto const c = getPixel(s, m_img.bpp, idx);
@ -75,8 +73,7 @@ void TileSheetEditorModel::cut() {
auto const pt1 = m_selection->a; auto const pt1 = m_selection->a;
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
turbine::setClipboardObject(m_tctx, std::move(cb)); turbine::setClipboardObject(m_tctx, std::move(cb));
pushCommand(ox::make<CutPasteCommand>( pushCommand(ox::make<CutPasteCommand>(CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
} }
void TileSheetEditorModel::copy() { void TileSheetEditorModel::copy() {
@ -84,7 +81,7 @@ void TileSheetEditorModel::copy() {
return; return;
} }
auto cb = ox::make_unique<TileSheetClipboard>(); auto cb = ox::make_unique<TileSheetClipboard>();
iterateSelectionRows(*m_selection, [&](int const x, int const y) { iterateSelectionRows(*m_selection, [&](int x, int y) {
auto pt = ox::Point{x, y}; auto pt = ox::Point{x, y};
const auto&s = activeSubSheet(); const auto&s = activeSubSheet();
const auto idx = core::idx(s, pt); const auto idx = core::idx(s, pt);
@ -108,8 +105,7 @@ void TileSheetEditorModel::paste() {
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
auto const pt1 = m_selection->a; auto const pt1 = m_selection->a;
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight}; auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
pushCommand(ox::make<CutPasteCommand>( pushCommand(ox::make<CutPasteCommand>(CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
} }
bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept { bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept {
@ -124,8 +120,8 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
} }
constexpr ox::StringView uuidPrefix = "uuid://"; constexpr ox::StringView uuidPrefix = "uuid://";
if (ox::beginsWith(path, uuidPrefix)) { if (ox::beginsWith(path, uuidPrefix)) {
auto const uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes()); auto uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes());
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid); auto out = keelCtx(m_tctx).uuidToPath.at(uuid);
if (out.error) { if (out.error) {
return {}; return {};
} }
@ -135,14 +131,13 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
} }
} }
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept { ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept {
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path)); OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
pushCommand(ox::make<PaletteChangeCommand>( pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), m_img, uuid->toString()));
activeSubSheetIdx(), m_img, uuid->toString()));
return {}; return {};
} }
void TileSheetEditorModel::setPalettePage(size_t const pg) noexcept { void TileSheetEditorModel::setPalettePage(size_t pg) noexcept {
m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1); m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1);
m_updated = true; m_updated = true;
} }
@ -151,7 +146,7 @@ size_t TileSheetEditorModel::palettePage() const noexcept {
return m_palettePage; return m_palettePage;
} }
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const palIdx) noexcept { void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept {
const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
return; return;
@ -160,8 +155,7 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const pal
if (m_ongoingDrawCommand) { if (m_ongoingDrawCommand) {
m_updated = m_updated || m_ongoingDrawCommand->append(idx); m_updated = m_updated || m_ongoingDrawCommand->append(idx);
} else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) { } else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) {
pushCommand(ox::make<DrawCommand>( pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
} }
} }
@ -177,20 +171,16 @@ void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept
pushCommand(ox::make<RmSubSheetCommand>(m_img, idx)); pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
} }
void TileSheetEditorModel::insertTiles( void TileSheetEditorModel::insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt)); pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
} }
void TileSheetEditorModel::deleteTiles( void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt)); pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
} }
ox::Error TileSheetEditorModel::updateSubsheet( ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept {
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept { pushCommand(ox::make<UpdateSubSheetCommand>(m_img, idx, ox::String(name), cols, rows));
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
pushCommand(cmd);
return {}; return {};
} }
@ -199,7 +189,7 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n
this->activeSubsheetChanged.emit(m_activeSubsSheetIdx); this->activeSubsheetChanged.emit(m_activeSubsSheetIdx);
} }
void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept { void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx); auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
// build idx list // build idx list
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) { if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
@ -207,10 +197,10 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
} }
ox::Array<bool, PixelsPerTile> updateMap = {}; ox::Array<bool, PixelsPerTile> updateMap = {};
auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt); auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt);
getFillPixels(activeSubSheet, updateMap, pt, oldColor); getFillPixels(updateMap, pt, oldColor);
ox::Vector<std::size_t> idxList; ox::Vector<std::size_t> idxList;
auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile; auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile;
for (auto const u : updateMap) { for (auto u : updateMap) {
if (u) { if (u) {
idxList.emplace_back(i); idxList.emplace_back(i);
} }
@ -240,7 +230,7 @@ void TileSheetEditorModel::completeSelection() noexcept {
m_selTracker.finishSelection(); m_selTracker.finishSelection();
m_selection.emplace(m_selTracker.selection()); m_selection.emplace(m_selTracker.selection());
auto&pt = m_selection->b; auto&pt = m_selection->b;
auto const&s = activeSubSheet(); auto&s = activeSubSheet();
pt.x = ox::min(s.columns * TileWidth - 1, pt.x); pt.x = ox::min(s.columns * TileWidth - 1, pt.x);
pt.y = ox::min(s.rows * TileHeight - 1, pt.y); pt.y = ox::min(s.rows * TileHeight - 1, pt.y);
} }
@ -285,44 +275,47 @@ ox::Error TileSheetEditorModel::saveFile() noexcept {
return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal); return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal);
} }
bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept { bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept {
auto const&s = activeSubSheet(); auto const&s = activeSubSheet();
auto const pt = idxToPt(static_cast<int>(idx), s.columns); auto const pt = idxToPt(static_cast<int>(idx), s.columns);
return m_selection && m_selection->contains(pt); return m_selection && m_selection->contains(pt);
} }
void TileSheetEditorModel::getFillPixels( void TileSheetEditorModel::getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept {
TileSheet::SubSheet const&activeSubSheet, const auto &activeSubSheet = this->activeSubSheet();
ox::Span<bool> pixels, const auto tileIdx = [activeSubSheet](const ox::Point &pt) noexcept {
ox::Point const&pt, return ptToIdx(pt, activeSubSheet.columns) / PixelsPerTile;
int const oldColor) const noexcept { };
auto const idx = ptToIdx(pt, activeSubSheet.columns); // get points
auto const relIdx = idx % PixelsPerTile; const auto leftPt = pt + ox::Point(-1, 0);
if (pixels[relIdx] || getPixel(activeSubSheet, m_img.bpp, idx) != oldColor) { const auto rightPt = pt + ox::Point(1, 0);
return; const auto topPt = pt + ox::Point(0, -1);
} const auto bottomPt = pt + ox::Point(0, 1);
// calculate indices
const auto idx = ptToIdx(pt, activeSubSheet.columns);
const auto leftIdx = ptToIdx(leftPt, activeSubSheet.columns);
const auto rightIdx = ptToIdx(rightPt, activeSubSheet.columns);
const auto topIdx = ptToIdx(topPt, activeSubSheet.columns);
const auto bottomIdx = ptToIdx(bottomPt, activeSubSheet.columns);
const auto tile = tileIdx(pt);
// mark pixels to update // mark pixels to update
pixels[relIdx] = true; pixels[idx % PixelsPerTile] = true;
if (pt.x % TileWidth != 0) { if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) {
auto const leftPt = pt + ox::Point{-1, 0}; getFillPixels(pixels, leftPt, oldColor);
getFillPixels(activeSubSheet, pixels, leftPt, oldColor);
} }
if (pt.x % TileWidth != TileWidth - 1) { if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) {
auto const rightPt = pt + ox::Point{1, 0}; getFillPixels(pixels, rightPt, oldColor);
getFillPixels(activeSubSheet, pixels, rightPt, oldColor);
} }
if (pt.y % TileHeight != 0) { if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) {
auto const topPt = pt + ox::Point{0, -1}; getFillPixels(pixels, topPt, oldColor);
getFillPixels(activeSubSheet, pixels, topPt, oldColor);
} }
if (pt.y % TileHeight != TileHeight - 1) { if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) {
auto const bottomPt = pt + ox::Point{0, 1}; getFillPixels(pixels, bottomPt, oldColor);
getFillPixels(activeSubSheet, pixels, bottomPt, oldColor);
} }
} }
void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept { void TileSheetEditorModel::pushCommand(studio::UndoCommand *cmd) noexcept {
std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>{cmd}); std::ignore = m_undoStack.push(ox::UPtr<studio::UndoCommand>(cmd));
m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd); m_ongoingDrawCommand = dynamic_cast<DrawCommand*>(cmd);
m_updated = true; m_updated = true;
} }

View File

@ -4,7 +4,9 @@
#pragma once #pragma once
#include <ox/std/bounds.hpp>
#include <ox/std/point.hpp> #include <ox/std/point.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/string.hpp> #include <ox/std/string.hpp>
#include <studio/studio.hpp> #include <studio/studio.hpp>
@ -36,7 +38,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
bool m_updated = false; bool m_updated = false;
public: public:
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack); TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack);
~TileSheetEditorModel() override = default; ~TileSheetEditorModel() override = default;
@ -61,7 +63,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
[[nodiscard]] [[nodiscard]]
ox::StringView palPath() const noexcept; ox::StringView palPath() const noexcept;
ox::Error setPalette(ox::StringViewCR path) noexcept; ox::Error setPalette(ox::StringView path) noexcept;
void setPalettePage(size_t pg) noexcept; void setPalettePage(size_t pg) noexcept;
@ -126,11 +128,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
bool pixelSelected(std::size_t idx) const noexcept; bool pixelSelected(std::size_t idx) const noexcept;
private: private:
void getFillPixels( void getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept;
TileSheet::SubSheet const&activeSubSheet,
ox::Span<bool> pixels,
ox::Point const&pt,
int oldColor) const noexcept;
void pushCommand(studio::UndoCommand *cmd) noexcept; void pushCommand(studio::UndoCommand *cmd) noexcept;

View File

@ -66,10 +66,10 @@ static ox::Optional<size_t> getPixelIdx(
return ox::Optional<size_t>{}; return ox::Optional<size_t>{};
} }
size_t getTileIdx(TileSheet const&ts, SubSheetId const id) noexcept { ox::Optional<size_t> getTileIdx(TileSheet const&ts, SubSheetId const id) noexcept {
size_t idx{}; size_t idx{};
auto const out = getPixelIdx(ts.subsheet, id, idx, ts.bpp); auto const out = getPixelIdx(ts.subsheet, id, idx, ts.bpp);
return out.or_value(0) / PixelsPerTile; return out ? ox::Optional<size_t>{ox::in_place, *out / PixelsPerTile} : out;
} }
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept { uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept {
@ -355,12 +355,12 @@ ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::StringViewCR path) noexc
/** /**
* Gets the offset in tiles of the desired subsheet. * Gets the offset in tiles of the desired subsheet.
*/ */
static ox::Result<unsigned> getTileOffset( static ox::Result<uint32_t> getTileOffset(
TileSheet::SubSheet const&ss, TileSheet::SubSheet const&ss,
ox::SpanView<ox::StringView> const&pNamePath, ox::SpanView<ox::StringView> const&pNamePath,
int8_t pBpp, int8_t pBpp,
std::size_t pIt = 0, std::size_t pIt = 0,
unsigned pCurrentTotal = 0) noexcept { uint32_t pCurrentTotal = 0) noexcept {
// pIt == pNamePath.size() - 1 && // pIt == pNamePath.size() - 1 &&
if (ss.name != pNamePath[pIt]) { if (ss.name != pNamePath[pIt]) {
return ox::Error(2, "Wrong branch"); return ox::Error(2, "Wrong branch");
@ -374,12 +374,14 @@ static ox::Result<unsigned> getTileOffset(
if (!err) { if (!err) {
return offset; return offset;
} }
// Possible bug? Shoud this be usinga a recursive version of
// pixelCnt will count pixels in subsheets of sub as well.
pCurrentTotal += pixelCnt(sub, pBpp) / PixelsPerTile; pCurrentTotal += pixelCnt(sub, pBpp) / PixelsPerTile;
} }
return ox::Error(1, "SubSheet not found"); return ox::Error(1, "SubSheet not found");
} }
ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept { ox::Result<uint32_t> getTileOffset(TileSheet const&ts, ox::StringViewCR pNamePath) noexcept {
return core::getTileOffset(ts.subsheet, ox::split<8>(pNamePath, '.'), ts.bpp); return core::getTileOffset(ts.subsheet, ox::split<8>(pNamePath, '.'), ts.bpp);
} }

View File

@ -11,7 +11,7 @@ target_link_libraries(
target_compile_definitions( target_compile_definitions(
NostalgiaStudio PUBLIC NostalgiaStudio PUBLIC
OLYMPIC_APP_VERSION="d2024.12.4" OLYMPIC_APP_VERSION="dev build"
) )
install( install(

View File

@ -18,7 +18,7 @@
<string>APPL</string> <string>APPL</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>d2024.12.4</string> <string>0.0.0</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>12.0.0</string> <string>12.0.0</string>
@ -30,6 +30,6 @@
<string>True</string> <string>True</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2016-2025 Gary Talent &lt;gary@drinkingtea.net&gt;</string> <string>Copyright (c) 2016-2023 Gary Talent &lt;gary@drinkingtea.net&gt;</string>
</dict> </dict>
</plist> </plist>

View File

@ -196,15 +196,15 @@ class AssetManager {
template<typename T> template<typename T>
class AssetTypeManager: public AssetTypeManagerBase { class AssetTypeManager: public AssetTypeManagerBase {
public: public:
using Loader = std::function<ox::Result<T>(ox::StringView assetId)>; using Loader = std::function<ox::Result<T>(ox::StringViewCR assetId)>;
private: private:
Loader m_loader{}; Loader m_loader{};
ox::HashMap<ox::String, ox::UPtr<AssetContainer<T>>> m_cache; ox::HashMap<ox::String, ox::UPtr<AssetContainer<T>>> m_cache;
public: public:
AssetTypeManager(Loader loader) noexcept: m_loader(loader) {} AssetTypeManager(Loader &&loader) noexcept: m_loader(std::move(loader)) {}
ox::Result<AssetRef<T>> getAsset(ox::StringView const assetId) const noexcept { ox::Result<AssetRef<T>> getAsset(ox::StringViewCR assetId) const noexcept {
OX_REQUIRE(out, m_cache.at(assetId)); OX_REQUIRE(out, m_cache.at(assetId));
if (!out || !*out) { if (!out || !*out) {
return ox::Error(1, "asset is null"); return ox::Error(1, "asset is null");
@ -212,7 +212,7 @@ class AssetManager {
return AssetRef<T>(out->get()); return AssetRef<T>(out->get());
} }
ox::Result<AssetRef<T>> loadAsset(ox::StringView const assetId) noexcept { ox::Result<AssetRef<T>> loadAsset(ox::StringViewCR assetId) noexcept {
auto &p = m_cache[assetId]; auto &p = m_cache[assetId];
OX_REQUIRE_M(obj, m_loader(assetId)); OX_REQUIRE_M(obj, m_loader(assetId));
if (!p) { if (!p) {
@ -224,7 +224,7 @@ class AssetManager {
return AssetRef<T>(p.get()); return AssetRef<T>(p.get());
} }
ox::Error reloadAsset(ox::StringView const assetId) noexcept { ox::Error reloadAsset(ox::StringViewCR assetId) noexcept {
auto &p = m_cache[assetId]; auto &p = m_cache[assetId];
OX_REQUIRE_M(obj, m_loader(assetId)); OX_REQUIRE_M(obj, m_loader(assetId));
if (!p) { if (!p) {
@ -247,7 +247,7 @@ class AssetManager {
}; };
ox::HashMap<ox::String, ox::UPtr<AssetTypeManagerBase>> m_assetTypeManagers; ox::HashMap<ox::String, ox::UPtr<AssetTypeManagerBase>> m_assetTypeManagers;
ox::HashMap<ox::String, ox::Signal<ox::Error(ox::StringView assetId)>> m_fileUpdated; ox::HashMap<ox::String, ox::Signal<ox::Error(ox::StringViewCR assetId)>> m_fileUpdated;
template<typename T> template<typename T>
ox::Result<AssetTypeManager<T>*> getTypeManager() noexcept { ox::Result<AssetTypeManager<T>*> getTypeManager() noexcept {
@ -273,18 +273,18 @@ class AssetManager {
} }
template<typename T> template<typename T>
ox::Result<AssetRef<T>> getAsset(ox::StringView assetId) noexcept { ox::Result<AssetRef<T>> getAsset(ox::StringViewCR assetId) noexcept {
OX_REQUIRE(m, getTypeManager<T>()); OX_REQUIRE(m, getTypeManager<T>());
return m->getAsset(assetId); return m->getAsset(assetId);
} }
ox::Error reloadAsset(ox::StringView assetId) noexcept { ox::Error reloadAsset(ox::StringViewCR assetId) noexcept {
m_fileUpdated[assetId].emit(assetId); m_fileUpdated[assetId].emit(assetId);
return {}; return {};
} }
template<typename T> template<typename T>
ox::Result<AssetRef<T>> loadAsset(ox::StringView assetId) noexcept { ox::Result<AssetRef<T>> loadAsset(ox::StringViewCR assetId) noexcept {
OX_REQUIRE(m, getTypeManager<T>()); OX_REQUIRE(m, getTypeManager<T>());
OX_REQUIRE(out, m->loadAsset(assetId)); OX_REQUIRE(out, m->loadAsset(assetId));
m_fileUpdated[assetId].connect(m, &AssetTypeManager<T>::reloadAsset); m_fileUpdated[assetId].connect(m, &AssetTypeManager<T>::reloadAsset);

View File

@ -77,13 +77,13 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
} else { } else {
oxErrf( oxErrf(
"Could not open studio config file: {}: {} ({}:{})\n", "Could not open studio config file: {}: {} ({}:{})\n",
err.errCode, toStr(err), err.file, err.line); err.errCode, toStr(err), err.src.file_name(), err.src.line());
} }
} }
} }
void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept { void StudioUI::handleKeyEvent(turbine::Key const key, bool const down) noexcept {
for (auto p : m_popups) { for (auto const p : m_popups) {
if (p->isOpen()) { if (p->isOpen()) {
if (key == turbine::Key::Escape) { if (key == turbine::Key::Escape) {
p->close(); p->close();
@ -118,7 +118,7 @@ void StudioUI::draw() noexcept {
for (auto &w: m_widgets) { for (auto &w: m_widgets) {
w->draw(m_sctx); w->draw(m_sctx);
} }
for (auto p: m_popups) { for (auto const p: m_popups) {
p->draw(m_sctx); p->draw(m_sctx);
} }
} }
@ -168,7 +168,7 @@ void StudioUI::drawMenu() noexcept {
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("View")) { if (ImGui::BeginMenu("View")) {
if (ImGui::MenuItem("Project Explorer", "Ctrl+1", m_showProjectExplorer)) { if (ImGui::MenuItem("Project Explorer", "Ctrl+Shift+1", m_showProjectExplorer)) {
toggleProjectExplorer(); toggleProjectExplorer();
} }
ImGui::EndMenu(); ImGui::EndMenu();
@ -207,6 +207,7 @@ void StudioUI::drawTabs() noexcept {
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) { studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
config.activeTabItemName = m_activeEditor->itemPath(); config.activeTabItemName = m_activeEditor->itemPath();
}); });
turbine::setRefreshWithin(m_ctx, 0);
} else [[likely]] { } else [[likely]] {
if (m_activeEditorUpdatePending == e.get()) [[unlikely]] { if (m_activeEditorUpdatePending == e.get()) [[unlikely]] {
m_activeEditorUpdatePending = nullptr; m_activeEditorUpdatePending = nullptr;
@ -227,7 +228,7 @@ void StudioUI::drawTabs() noexcept {
try { try {
OX_THROW_ERROR(m_editors.erase(it).moveTo(it)); OX_THROW_ERROR(m_editors.erase(it).moveTo(it));
} catch (ox::Exception const&ex) { } catch (ox::Exception const&ex) {
oxErrf("Editor tab deletion failed: {} ({}:{})\n", ex.what(), ex.file, ex.line); oxErrf("Editor tab deletion failed: {} ({}:{})\n", ex.what(), ex.src.file_name(), ex.src.line());
} catch (std::exception const&ex) { } catch (std::exception const&ex) {
oxErrf("Editor tab deletion failed: {}\n", ex.what()); oxErrf("Editor tab deletion failed: {}\n", ex.what());
} }
@ -317,8 +318,22 @@ void StudioUI::handleKeyInput() noexcept {
} else if (ImGui::IsKeyPressed(ImGuiKey_Z)) { } else if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr; auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
if (undoStack) { oxLogError(undoStack->undo()); } if (undoStack) { oxLogError(undoStack->undo()); }
} else if (ImGui::IsKeyPressed(ImGuiKey_1)) { } else if (ImGui::IsKeyPressed(ImGuiKey_1) && ImGui::IsKeyDown(ImGuiKey_ModShift)) {
toggleProjectExplorer(); toggleProjectExplorer();
} else {
if (!m_editors.empty()) {
auto const range = ox::min<size_t>(9u, m_editors.size()) - 1;
for (auto i = 0u; i < 9; ++i) {
if (ImGui::IsKeyPressed(static_cast<ImGuiKey>(static_cast<int>(ImGuiKey_1) + i))) {
m_activeEditor = m_editors[i < m_editors.size() ? i : range].get();
m_activeEditorUpdatePending = m_activeEditor;
}
}
if (ImGui::IsKeyPressed(ImGuiKey_0)) {
m_activeEditor = m_editors[10 < m_editors.size() ? 10 : range].get();
m_activeEditorUpdatePending = m_activeEditor;
}
}
} }
} }
} }
@ -379,7 +394,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab)
if constexpr(!ox::defines::Debug) { if constexpr(!ox::defines::Debug) {
oxErrf("Could not open Editor: {}\n", toStr(err)); oxErrf("Could not open Editor: {}\n", toStr(err));
} else { } else {
oxErrf("Could not open Editor: {} ({}:{})\n", err.errCode, err.file, err.line); oxErrf("Could not open Editor: {} ({}:{})\n", err.errCode, err.src.file_name(), err.src.line());
} }
return err; return err;
} }

View File

@ -13,7 +13,7 @@ namespace studio {
class NoChangesException: public ox::Exception { class NoChangesException: public ox::Exception {
public: public:
inline NoChangesException(std::source_location sloc = std::source_location::current()): inline NoChangesException(std::source_location sloc = std::source_location::current()):
ox::Exception(sloc.file_name(), sloc.line(), 1, "Command makes no changes.") {} ox::Exception(1, "Command makes no changes.", sloc) {}
}; };
class UndoCommand { class UndoCommand {

View File

@ -46,7 +46,7 @@ void BaseEditor::save() noexcept {
setUnsavedChanges(false); setUnsavedChanges(false);
} else { } else {
if constexpr(ox::defines::Debug) { if constexpr(ox::defines::Debug) {
oxErrorf("Could not save file {}: {} ({}:{})", itemPath(), toStr(err), err.file, err.line); oxErrorf("Could not save file {}: {} ({}:{})", itemPath(), toStr(err), err.src.file_name(), err.src.line());
} else { } else {
oxErrorf("Could not save file {}: {}", itemPath(), toStr(err)); oxErrorf("Could not save file {}: {}", itemPath(), toStr(err));
} }

View File

@ -19,7 +19,7 @@ target_include_directories(
) )
target_sources( target_sources(
Turbine PUBLIC Turbine PRIVATE
turbine.cpp turbine.cpp
) )

View File

@ -1,4 +1,4 @@
FROM fedora:36 FROM fedora:41
RUN dnf update -y RUN dnf update -y

View File

@ -28,11 +28,11 @@ project_name = sys.argv[2]
bin = f'./build/{host_env}-{current_build}/bin/' bin = f'./build/{host_env}-{current_build}/bin/'
project_bin = f'build/gba-release/bin/{project_name}.bin' project_bin = f'build/gba-release/bin/{project_name}.bin'
project_gba = f'{project_name}.gba' project_gba = f'{project_name}.gba'
project_manifest = f'{project_name}-manifest.json' project_manifest = f'{project_name.lower()}-manifest.json'
shutil.copyfile(project_bin, project_gba) shutil.copyfile(project_bin, project_gba)
subprocess.run([ subprocess.run([
f'{bin}/{project_name}-pack', f'{bin}/{project_name.lower()}-pack',
'-src', project_dir, '-src', project_dir,
'-rom-bin', project_gba, '-rom-bin', project_gba,
'-manifest', project_manifest]) '-manifest', project_manifest])