Compare commits
62 Commits
release-d2
...
7eb37c5318
Author | SHA1 | Date | |
---|---|---|---|
7eb37c5318 | |||
7a21b20711 | |||
894be237f2 | |||
92e9d9cbfc | |||
b29b9a9b3a | |||
721f844214 | |||
a3d6a58cc8 | |||
e598e7fe27 | |||
ba9e720f9f | |||
8e816a261f | |||
5b9929ab3d | |||
ceb54b3f1b | |||
8764444758 | |||
ce9a0b1fdb | |||
f7a468ea1e | |||
861d177a27 | |||
3936756b36 | |||
3e78ec3fe5 | |||
3c3d53b40c | |||
151d7c5736 | |||
4e4d8d2c3f | |||
03d1fd2857 | |||
6701decc91 | |||
6cff526647 | |||
dd50bd0249 | |||
55a1660242 | |||
ed365dfef5 | |||
23a09e4a13 | |||
b69e7ebb98 | |||
418d6e3f22 | |||
c44d8678cb | |||
eb4cd7106d | |||
d259770f32 | |||
80bad608f7 | |||
2bce9a2baf | |||
791b7746f3 | |||
842e3587fd | |||
318e79004b | |||
9f338a7429 | |||
645e48af7b | |||
ef92c8df13 | |||
849d50be8e | |||
845092f114 | |||
75819a1797 | |||
d66da85753 | |||
98ddb08abd | |||
8d1701b0bb | |||
1048e522fd | |||
ee59da4aa3 | |||
1ba64cb5d8 | |||
462bebf6dd | |||
e3f84c4e75 | |||
6837a0556d | |||
ede2c8ca37 | |||
f50367f7d5 | |||
e758e03d2b | |||
835e3270ce | |||
480dd5ece4 | |||
dba6bb5800 | |||
40a456e54a | |||
bf5be00c12 | |||
dc7c2559d6 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,7 +6,7 @@
|
||||
.mypy_cache
|
||||
.stfolder
|
||||
.stignore
|
||||
scripts/__pycache__
|
||||
util/scripts/__pycache__
|
||||
pyenv
|
||||
CMakeLists.txt.user
|
||||
ROM.oxfs
|
||||
|
3
Makefile
3
Makefile
@ -1,5 +1,6 @@
|
||||
BC_VAR_PROJECT_NAME=nostalgia
|
||||
BC_VAR_PROJECT_NAME_CAP=Nostalgia
|
||||
BC_VAR_DEVENV_ROOT=util
|
||||
BUILDCORE_PATH=deps/buildcore
|
||||
include ${BUILDCORE_PATH}/base.mk
|
||||
|
||||
@ -13,7 +14,7 @@ endif
|
||||
|
||||
.PHONY: pkg-gba
|
||||
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
|
||||
run: build
|
||||
|
4
deps/gbabuildcore/base.cmake
vendored
4
deps/gbabuildcore/base.cmake
vendored
@ -1,8 +1,8 @@
|
||||
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} -nostdinc++")
|
||||
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-unwind-tables")
|
||||
#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-strict-aliasing")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthumb-interwork")
|
||||
|
13
deps/glad/CMakeLists.txt
vendored
13
deps/glad/CMakeLists.txt
vendored
@ -1,2 +1,11 @@
|
||||
add_library(glad OBJECT src/glad.c)
|
||||
target_include_directories(glad PUBLIC include)
|
||||
add_library(glad src/glad.c)
|
||||
|
||||
target_include_directories(glad PUBLIC include)
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
glad
|
||||
DESTINATION
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
12
deps/imgui/CMakeLists.txt
vendored
12
deps/imgui/CMakeLists.txt
vendored
@ -6,7 +6,7 @@ endif()
|
||||
# DrinkingTea: end
|
||||
|
||||
add_library(
|
||||
imgui OBJECT
|
||||
imgui
|
||||
imgui.cpp
|
||||
imgui_demo.cpp
|
||||
imgui_draw.cpp
|
||||
@ -19,4 +19,12 @@ add_library(
|
||||
target_include_directories(
|
||||
imgui SYSTEM PUBLIC
|
||||
.
|
||||
)
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
imgui
|
||||
DESTINATION
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
2
deps/ox/src/ox/event/signal.hpp
vendored
2
deps/ox/src/ox/event/signal.hpp
vendored
@ -215,7 +215,7 @@ Error Signal<Args...>::emitCheckError(Args... args) const noexcept {
|
||||
}
|
||||
return ox::Error(0);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
2
deps/ox/src/ox/logconn/def.hpp
vendored
2
deps/ox/src/ox/logconn/def.hpp
vendored
@ -14,7 +14,7 @@
|
||||
{ \
|
||||
const auto loggerErr = (loggerName).initConn(appName); \
|
||||
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 { \
|
||||
ox::trace::setLogger(&(loggerName)); \
|
||||
} \
|
||||
|
6
deps/ox/src/ox/model/typenamecatcher.hpp
vendored
6
deps/ox/src/ox/model/typenamecatcher.hpp
vendored
@ -140,16 +140,16 @@ constexpr Str getModelTypeName() noexcept {
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename T, typename Str = const char*>
|
||||
[[nodiscard]]
|
||||
consteval auto requireModelTypeName() noexcept {
|
||||
constexpr auto name = getModelTypeName<T>();
|
||||
constexpr auto name = getModelTypeName<T, Str>();
|
||||
static_assert(ox::StringView{name}.len(), "Type lacks required TypeName");
|
||||
return name;
|
||||
}
|
||||
|
||||
template<typename T, typename Str = const char*>
|
||||
constexpr auto ModelTypeName_v = getModelTypeName<T, Str>();
|
||||
constexpr auto ModelTypeName_v = requireModelTypeName<T, Str>();
|
||||
|
||||
template<typename T, typename Str = const char*>
|
||||
constexpr auto ModelTypeVersion_v = requireModelTypeVersion<T>();
|
||||
|
14
deps/ox/src/ox/std/algorithm.hpp
vendored
14
deps/ox/src/ox/std/algorithm.hpp
vendored
@ -9,13 +9,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "def.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
|
||||
namespace ox {
|
||||
|
||||
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) {
|
||||
if (*begin == value) {
|
||||
return begin;
|
||||
|
87
deps/ox/src/ox/std/anyptr.hpp
vendored
87
deps/ox/src/ox/std/anyptr.hpp
vendored
@ -15,18 +15,22 @@
|
||||
|
||||
namespace ox {
|
||||
|
||||
class AnyPtr {
|
||||
namespace detail {
|
||||
|
||||
template<bool unique>
|
||||
class AnyPtrT {
|
||||
private:
|
||||
struct WrapBase {
|
||||
virtual constexpr ~WrapBase() = default;
|
||||
virtual constexpr WrapBase *copyTo(ox::Span<char> s) noexcept = 0;
|
||||
virtual constexpr operator bool() const noexcept = 0;
|
||||
virtual void free() noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Wrap: public WrapBase {
|
||||
struct Wrap final: WrapBase {
|
||||
T *data{};
|
||||
constexpr Wrap(T *pData) noexcept: data(pData) {
|
||||
explicit constexpr Wrap(T *pData) noexcept: data(pData) {
|
||||
}
|
||||
constexpr WrapBase *copyTo(ox::Span<char> s) noexcept override {
|
||||
oxAssert(s.size() >= sizeof(Wrap), "too small buffer");
|
||||
@ -39,16 +43,20 @@ class AnyPtr {
|
||||
constexpr operator bool() const noexcept override {
|
||||
return data != nullptr;
|
||||
}
|
||||
constexpr void free() noexcept override {
|
||||
safeDelete(data);
|
||||
data = {};
|
||||
}
|
||||
};
|
||||
|
||||
WrapBase *m_wrapPtr{};
|
||||
ox::Array<char, sizeof(Wrap<void*>)> m_wrapData;
|
||||
|
||||
public:
|
||||
constexpr AnyPtr() noexcept = default;
|
||||
constexpr AnyPtrT() noexcept = default;
|
||||
|
||||
template<typename T>
|
||||
constexpr AnyPtr(T *ptr) noexcept {
|
||||
constexpr AnyPtrT(T *ptr) noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
m_wrapPtr = new Wrap(ptr);
|
||||
} else {
|
||||
@ -56,22 +64,39 @@ class AnyPtr {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr AnyPtr(AnyPtr const&other) noexcept {
|
||||
constexpr AnyPtrT(AnyPtrT const&other) noexcept {
|
||||
if (other) {
|
||||
m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ~AnyPtr() noexcept {
|
||||
constexpr AnyPtrT(AnyPtrT &&other) noexcept {
|
||||
if (other) {
|
||||
m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData);
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
other.m_wrapPtr = {};
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ~AnyPtrT() noexcept {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr AnyPtr &operator=(T *ptr) noexcept {
|
||||
if (std::is_constant_evaluated()) {
|
||||
constexpr AnyPtrT &operator=(T *ptr) noexcept {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
} else if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
m_wrapPtr = new Wrap(ptr);
|
||||
} else {
|
||||
m_wrapPtr = new(m_wrapData.data()) Wrap(ptr);
|
||||
@ -79,10 +104,14 @@ class AnyPtr {
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr AnyPtr &operator=(AnyPtr const&ptr) noexcept {
|
||||
constexpr AnyPtrT &operator=(AnyPtrT const&ptr) noexcept {
|
||||
if (this != &ptr) {
|
||||
if (ptr) {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
} else if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
if (ptr) {
|
||||
m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData);
|
||||
} else {
|
||||
m_wrapPtr = nullptr;
|
||||
@ -91,10 +120,40 @@ class AnyPtr {
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr AnyPtrT &operator=(AnyPtrT &&ptr) noexcept {
|
||||
if (this != &ptr) {
|
||||
if constexpr(unique) {
|
||||
free();
|
||||
} else if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
if (ptr) {
|
||||
m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData);
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(ptr.m_wrapPtr);
|
||||
ptr.m_wrapPtr = nullptr;
|
||||
}
|
||||
} else {
|
||||
m_wrapPtr = nullptr;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept {
|
||||
return m_wrapPtr && *m_wrapPtr;
|
||||
}
|
||||
|
||||
constexpr void free() noexcept {
|
||||
if (m_wrapPtr) {
|
||||
m_wrapPtr->free();
|
||||
}
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_wrapPtr);
|
||||
}
|
||||
m_wrapPtr = nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr T *get() const noexcept {
|
||||
@ -104,6 +163,12 @@ class AnyPtr {
|
||||
return dynamic_cast<Wrap<T>*>(m_wrapPtr)->data;
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AnyPtr = detail::AnyPtrT<false>;
|
||||
using UAnyPtr = detail::AnyPtrT<true>;
|
||||
|
||||
}
|
35
deps/ox/src/ox/std/assert.cpp
vendored
35
deps/ox/src/ox/std/assert.cpp
vendored
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include "fmt.hpp"
|
||||
#include "realstd.hpp"
|
||||
#include "stacktrace.hpp"
|
||||
#include "trace.hpp"
|
||||
|
||||
@ -14,14 +15,14 @@
|
||||
|
||||
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);
|
||||
if (err.msg) {
|
||||
oxErrf("\tError Message:\t{}\n", err.msg);
|
||||
}
|
||||
oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
|
||||
if (err.file != nullptr) {
|
||||
oxErrf("\tError Location:\t{}:{}\n", err.file, err.line);
|
||||
if (err.src.file_name() != nullptr) {
|
||||
oxErrf("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line());
|
||||
}
|
||||
#ifdef OX_USE_STDLIB
|
||||
printStackTrace(2);
|
||||
@ -32,16 +33,19 @@ void panic(StringViewCR file, int line, StringViewCR panicMsg, const Error &err)
|
||||
#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);
|
||||
}
|
||||
|
||||
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
|
||||
auto output = sfmt("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg);
|
||||
output += genStackTrace(2);
|
||||
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line);
|
||||
std::abort();
|
||||
auto const st = genStackTrace(2);
|
||||
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]:\n{}", msg, assertTxt, file, line, st);
|
||||
abort();
|
||||
#else
|
||||
oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, msg);
|
||||
oxTracef("assert", "Failed assert: {} ({}) [{}:{}]", msg, assertTxt, file, line);
|
||||
@ -49,20 +53,25 @@ void assertFailFuncRuntime(StringViewCR file, int line, StringViewCR assertTxt,
|
||||
#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)
|
||||
auto msg = sfmt("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, assertMsg);
|
||||
if (err.msg) {
|
||||
msg += sfmt("\tError Message:\t{}\n", err.msg);
|
||||
}
|
||||
msg += sfmt("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
|
||||
if (err.file != nullptr) {
|
||||
msg += sfmt("\tError Location:\t{}:{}\n", err.file, err.line);
|
||||
if (err.src.file_name() != nullptr) {
|
||||
msg += sfmt("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line());
|
||||
}
|
||||
msg += genStackTrace(2);
|
||||
oxErr(msg);
|
||||
oxTracef("assert", "Failed assert: {} [{}:{}]", assertMsg, file, line);
|
||||
std::abort();
|
||||
abort();
|
||||
#else
|
||||
constexprPanic(file, line, assertMsg);
|
||||
#endif
|
||||
|
43
deps/ox/src/ox/std/assert.hpp
vendored
43
deps/ox/src/ox/std/assert.hpp
vendored
@ -22,9 +22,15 @@
|
||||
|
||||
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()) {
|
||||
panic(file, line, panicMsg, err);
|
||||
} 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(StringViewCR file, int line, const Error &err, StringViewCR, StringViewCR assertMsg) noexcept;
|
||||
void assertFailFuncRuntime(
|
||||
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 (!std::is_constant_evaluated()) {
|
||||
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 (!std::is_constant_evaluated()) {
|
||||
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 (!std::is_constant_evaluated()) {
|
||||
#if defined(OX_USE_STDLIB)
|
||||
|
47
deps/ox/src/ox/std/error.hpp
vendored
47
deps/ox/src/ox/std/error.hpp
vendored
@ -36,28 +36,16 @@ using ErrorCode = uint16_t;
|
||||
|
||||
|
||||
struct [[nodiscard]] Error {
|
||||
std::source_location src;
|
||||
ox::CString msg = nullptr;
|
||||
ox::CString file = nullptr;
|
||||
uint16_t line = 0;
|
||||
ErrorCode errCode = 0;
|
||||
|
||||
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(
|
||||
ErrorCode const errCode,
|
||||
std::source_location const&src = std::source_location::current()) noexcept:
|
||||
file{src.file_name()},
|
||||
line{static_cast<uint16_t>(src.line())},
|
||||
src{src},
|
||||
errCode{errCode}
|
||||
{}
|
||||
|
||||
@ -65,9 +53,8 @@ struct [[nodiscard]] Error {
|
||||
ErrorCode const errCode,
|
||||
ox::CString msg,
|
||||
std::source_location const&src = std::source_location::current()) noexcept:
|
||||
src{src},
|
||||
msg{msg},
|
||||
file{src.file_name()},
|
||||
line{static_cast<uint16_t>(src.line())},
|
||||
errCode{errCode}
|
||||
{}
|
||||
|
||||
@ -89,42 +76,31 @@ constexpr auto toStr(Error const&err) noexcept {
|
||||
}
|
||||
|
||||
struct Exception: public std::exception {
|
||||
std::source_location src;
|
||||
ox::CString msg = nullptr;
|
||||
ox::CString file = nullptr;
|
||||
uint16_t line = 0;
|
||||
ErrorCode errCode = 0;
|
||||
|
||||
explicit inline Exception(ox::CString file, uint32_t line, ErrorCode errCode, char const*msg = "") noexcept {
|
||||
this->file = file;
|
||||
this->line = static_cast<uint16_t>(line);
|
||||
this->msg = msg;
|
||||
this->errCode = errCode;
|
||||
}
|
||||
|
||||
explicit inline Exception(
|
||||
explicit Exception(
|
||||
ErrorCode const errCode,
|
||||
std::source_location const&src = std::source_location::current()) noexcept:
|
||||
file{src.file_name()},
|
||||
line{static_cast<uint16_t>(src.line())},
|
||||
src{src},
|
||||
errCode{errCode} {}
|
||||
|
||||
explicit inline Exception(
|
||||
explicit Exception(
|
||||
ErrorCode const errCode,
|
||||
ox::CString msg,
|
||||
std::source_location const&src = std::source_location::current()) noexcept:
|
||||
src{src},
|
||||
msg{msg},
|
||||
file{src.file_name()},
|
||||
line{static_cast<uint16_t>(src.line())},
|
||||
errCode{errCode} {}
|
||||
|
||||
explicit inline Exception(Error const&err) noexcept:
|
||||
explicit Exception(Error const&err) noexcept:
|
||||
src{err.src},
|
||||
msg{err.msg ? err.msg : ""},
|
||||
file{err.file},
|
||||
line{err.line},
|
||||
errCode{err.errCode} {}
|
||||
|
||||
constexpr Error toError() const noexcept {
|
||||
return Error(file, line, errCode, msg);
|
||||
return Error(errCode, msg, src);
|
||||
}
|
||||
|
||||
[[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;
|
||||
|
||||
template<typename T>
|
||||
|
1
deps/ox/src/ox/std/reader.hpp
vendored
1
deps/ox/src/ox/std/reader.hpp
vendored
@ -31,7 +31,6 @@ concept Reader_c = requires(T v) {
|
||||
class Reader_v {
|
||||
public:
|
||||
virtual constexpr ~Reader_v() noexcept = default;
|
||||
[[nodiscard]]
|
||||
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> tellg() noexcept = 0;
|
||||
|
9
deps/ox/src/ox/std/realstd.hpp
vendored
9
deps/ox/src/ox/std/realstd.hpp
vendored
@ -12,4 +12,13 @@
|
||||
#include <cassert>
|
||||
#else
|
||||
#define assert(e) while (!(e));
|
||||
#endif
|
||||
|
||||
#if __has_include(<cstdlib>)
|
||||
#include <cstdlib>
|
||||
#else
|
||||
extern "C" {
|
||||
[[noreturn]]
|
||||
void abort();
|
||||
}
|
||||
#endif
|
59
deps/ox/src/ox/std/smallmap.hpp
vendored
59
deps/ox/src/ox/std/smallmap.hpp
vendored
@ -62,6 +62,9 @@ class SmallMap {
|
||||
[[nodiscard]]
|
||||
constexpr Vector<K> keys() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Vector<T> values() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr K const&key(size_t i) const noexcept;
|
||||
|
||||
@ -82,14 +85,22 @@ class SmallMap {
|
||||
return m_pairs;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::Span<Pair> pairs() noexcept {
|
||||
return m_pairs;
|
||||
}
|
||||
|
||||
constexpr void clear();
|
||||
|
||||
private:
|
||||
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>
|
||||
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>
|
||||
constexpr T &SmallMap<K, T, SmallSz>::operator[](MaybeView_t<K> const&k) {
|
||||
bool isNew{};
|
||||
auto p = &access(m_pairs, k, isNew);
|
||||
auto p = access(m_pairs, k, isNew);
|
||||
if (isNew) {
|
||||
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>
|
||||
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) {
|
||||
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>
|
||||
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) {
|
||||
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>
|
||||
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>
|
||||
@ -186,6 +199,16 @@ constexpr Vector<K> SmallMap<K, T, SmallSz>::keys() const noexcept {
|
||||
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>
|
||||
constexpr K const&SmallMap<K, T, SmallSz>::key(size_t i) const noexcept {
|
||||
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 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 {
|
||||
for (auto const&p : pairs) {
|
||||
if (p.key == k) {
|
||||
isNew = false;
|
||||
return p;
|
||||
return &p;
|
||||
}
|
||||
}
|
||||
isNew = true;
|
||||
return pairs.emplace_back();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename K, typename T, size_t SmallSz>
|
||||
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) {
|
||||
for (auto &p : pairs) {
|
||||
if (p.key == k) {
|
||||
isNew = false;
|
||||
return p;
|
||||
return &p;
|
||||
}
|
||||
}
|
||||
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>
|
||||
|
20
deps/ox/src/ox/std/span.hpp
vendored
20
deps/ox/src/ox/std/span.hpp
vendored
@ -14,7 +14,7 @@
|
||||
#include "iterator.hpp"
|
||||
#include "vector.hpp"
|
||||
|
||||
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
|
||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||
|
||||
namespace ox {
|
||||
|
||||
@ -133,7 +133,7 @@ class Span {
|
||||
return m_items[i];
|
||||
}
|
||||
|
||||
constexpr const T &operator[](std::size_t i) const noexcept {
|
||||
constexpr T const&operator[](std::size_t i) const noexcept {
|
||||
ox::primitiveAssert(__FILE__, __LINE__, i < size(), "Span access overflow");
|
||||
return m_items[i];
|
||||
}
|
||||
@ -168,8 +168,20 @@ class Span {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using SpanView = Span<const T>;
|
||||
using SpanView = Span<T const>;
|
||||
|
||||
template<typename T>
|
||||
constexpr void spancpy(ox::Span<T> const dst, ox::SpanView<T> const src) noexcept {
|
||||
auto const sz = ox::min(dst.size(), src.size());
|
||||
if (std::is_constant_evaluated() || std::is_trivially_copyable_v<T>) {
|
||||
for (size_t i{}; i < sz; ++i) {
|
||||
dst.data()[i] = src.data()[i];
|
||||
}
|
||||
} else {
|
||||
memcpy(dst.data(), src.data(), sz * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OX_CLANG_NOWARN_END
|
||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
|
2
deps/ox/src/ox/std/test/CMakeLists.txt
vendored
2
deps/ox/src/ox/std/test/CMakeLists.txt
vendored
@ -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] SmallMap2" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "SmallMap2")
|
||||
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] HeapMgr" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest malloc)
|
||||
add_test("[ox/std] Serialize-Int" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/StdTest "Serialize-Int")
|
||||
|
57
deps/ox/src/ox/std/test/tests.cpp
vendored
57
deps/ox/src/ox/std/test/tests.cpp
vendored
@ -237,6 +237,50 @@ OX_CLANG_NOWARN_END
|
||||
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",
|
||||
[] {
|
||||
@ -246,7 +290,18 @@ OX_CLANG_NOWARN_END
|
||||
oxExpect(map.size(), 1u);
|
||||
oxExpect(map["aoeu"], "");
|
||||
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);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
8
deps/ox/src/ox/std/trace.hpp
vendored
8
deps/ox/src/ox/std/trace.hpp
vendored
@ -269,8 +269,8 @@ using TraceStream = NullStream;
|
||||
inline void logError(const char *file, int line, const char *fmt, const Error &err) noexcept {
|
||||
if (err) {
|
||||
TraceStream trc(file, line, "ox::error");
|
||||
if (err.file != nullptr) {
|
||||
trc << "Error: (" << err.file << ":" << err.line << "):";
|
||||
if (err.src.file_name() != nullptr) {
|
||||
trc << "Error: (" << err.src.file_name() << ":" << err.src.line() << "):";
|
||||
} else {
|
||||
trc << "Error:";
|
||||
}
|
||||
@ -282,8 +282,8 @@ inline void logError(const char *file, int line, const Error &err) noexcept {
|
||||
if (err) {
|
||||
TraceStream trc(file, line, "ox::error");
|
||||
trc << "Error:" << err;
|
||||
if (err.file != nullptr) {
|
||||
trc << "(" << err.file << ":" << err.line << ")";
|
||||
if (err.src.file_name() != nullptr) {
|
||||
trc << "(" << err.src.file_name() << ":" << err.src.line() << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
deps/ox/src/ox/std/typetraits.hpp
vendored
10
deps/ox/src/ox/std/typetraits.hpp
vendored
@ -19,12 +19,15 @@
|
||||
namespace std {
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_union_v = __is_union(T);
|
||||
inline constexpr bool is_union_v = __is_union(T);
|
||||
|
||||
constexpr bool is_constant_evaluated() noexcept {
|
||||
inline constexpr bool is_constant_evaluated() noexcept {
|
||||
return __builtin_is_constant_evaluated();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -156,6 +159,9 @@ static_assert(is_class<int>::value == false);
|
||||
template<typename T>
|
||||
constexpr bool is_class_v = is_class<T>();
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_trivially_copyable_v = std::is_trivially_copyable_v<T>;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_signed_v = integral_constant<bool, T(-1) < T(0)>::value;
|
||||
|
||||
|
21
deps/ox/src/ox/std/vector.hpp
vendored
21
deps/ox/src/ox/std/vector.hpp
vendored
@ -313,6 +313,8 @@ class Vector: detail::VectorAllocator<T, Allocator, SmallVectorSize> {
|
||||
|
||||
constexpr void reserve(std::size_t cap) noexcept(useNoexcept);
|
||||
|
||||
constexpr void shrink_to_fit() noexcept(useNoexcept);
|
||||
|
||||
private:
|
||||
constexpr void reserveInsert(
|
||||
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>
|
||||
constexpr Vector<T, SmallVectorSize, Allocator>::Vector(std::initializer_list<T> list) noexcept {
|
||||
reserve(list.size());
|
||||
for (auto &item : list) {
|
||||
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>
|
||||
constexpr void Vector<T, SmallVectorSize, Allocator>::reserveInsert(
|
||||
std::size_t cap,
|
||||
|
21
deps/teagba/src/cstartup.cpp
vendored
21
deps/teagba/src/cstartup.cpp
vendored
@ -4,17 +4,20 @@
|
||||
|
||||
#include <ox/std/heapmgr.hpp>
|
||||
|
||||
#include <teagba/bios.hpp>
|
||||
#include <teagba/registers.hpp>
|
||||
|
||||
namespace mgba {
|
||||
void initConsole();
|
||||
}
|
||||
|
||||
#define MEM_EWRAM_BEGIN reinterpret_cast<char*>(0x02000000)
|
||||
#define MEM_EWRAM_END reinterpret_cast<char*>(0x0203FFFF)
|
||||
#define MEM_HEAP_BEGIN reinterpret_cast<char*>(0x02000000)
|
||||
#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
|
||||
#define HEAP_SIZE ((MEM_EWRAM_END - MEM_EWRAM_BEGIN) / 2)
|
||||
#define HEAP_END reinterpret_cast<char*>(MEM_EWRAM_BEGIN + HEAP_SIZE)
|
||||
#define HEAP_SIZE ((MEM_HEAP_END - MEM_HEAP_BEGIN) / 2)
|
||||
#define HEAP_END reinterpret_cast<char*>(MEM_HEAP_BEGIN + HEAP_SIZE)
|
||||
|
||||
extern void (*__preinit_array_start[]) (void);
|
||||
extern void (*__preinit_array_end[]) (void);
|
||||
@ -25,6 +28,14 @@ int main(int argc, const char **argv);
|
||||
|
||||
extern "C" {
|
||||
|
||||
void abort() {
|
||||
REG_IE = 0;
|
||||
teagba::intrwait(0, 0);
|
||||
while (true);
|
||||
}
|
||||
|
||||
void *__gxx_personality_v0{};
|
||||
|
||||
void __libc_init_array() {
|
||||
auto preInits = __preinit_array_end - __preinit_array_start;
|
||||
for (decltype(preInits) i = 0; i < preInits; i++) {
|
||||
|
@ -162,11 +162,9 @@ The Ox way of doing things is the Olympic way of doing things.
|
||||
|
||||
### Error Handling
|
||||
|
||||
The GBA build has exceptions disabled.
|
||||
Instead of throwing exceptions, all engine code should return
|
||||
[ox::Errors](deps/ox/ox-docs.md#error-handling) for error reporting.
|
||||
For the sake of consistency, try to stick to ```ox::Error``` in non-engine code
|
||||
as well, but non-engine code is free to use exceptions when they make sense.
|
||||
Instead of throwing exceptions, generally try to use
|
||||
[ox::Errors](deps/ox/ox-docs.md#error-handling) for error reporting,
|
||||
but exceptions may be used where they make sense.
|
||||
|
||||
Exceptions should generally just use ```OxException```, which is bascially an
|
||||
exception form of ```ox::Error```.
|
||||
|
@ -17,11 +17,9 @@ namespace nostalgia::core {
|
||||
|
||||
class Context;
|
||||
|
||||
struct ContextDeleter {
|
||||
void operator()(Context *p) noexcept;
|
||||
};
|
||||
void safeDelete(Context *ctx) noexcept;
|
||||
|
||||
using ContextUPtr = ox::UPtr<Context, ContextDeleter>;
|
||||
using ContextUPtr = ox::UPtr<Context>;
|
||||
|
||||
ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const¶ms = {}) noexcept;
|
||||
|
||||
|
@ -102,8 +102,6 @@ OX_MODEL_BEGIN(TileSheetSet)
|
||||
OX_MODEL_FIELD(entries)
|
||||
OX_MODEL_END()
|
||||
|
||||
void addEntry(TileSheetSet &set, ox::FileAddress path, int32_t begin = 0, int32_t size = -1) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
int tileColumns(Context&) noexcept;
|
||||
|
||||
@ -144,6 +142,10 @@ ox::Error loadBgTileSheet(
|
||||
unsigned cbb,
|
||||
TileSheetSet const&set) noexcept;
|
||||
|
||||
void clearCbb(Context &ctx, unsigned cbb) noexcept;
|
||||
|
||||
void clearCbbs(Context &ctx) noexcept;
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned cbb,
|
||||
|
11
src/nostalgia/modules/core/include/nostalgia/core/studio.hpp
Normal file
11
src/nostalgia/modules/core/include/nostalgia/core/studio.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <studio/studio.hpp>
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
}
|
@ -18,6 +18,23 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
struct SubSheetTemplate {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.SubSheetTemplate";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
ox::String name;
|
||||
int32_t width{};
|
||||
int32_t height{};
|
||||
ox::Vector<SubSheetTemplate> subsheets;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(SubSheetTemplate)
|
||||
OX_MODEL_FIELD(name)
|
||||
OX_MODEL_FIELD(width)
|
||||
OX_MODEL_FIELD(height)
|
||||
OX_MODEL_FIELD(subsheets)
|
||||
OX_MODEL_END()
|
||||
|
||||
|
||||
// Predecessor to TileSheet, kept for backward compatibility
|
||||
struct TileSheetV1 {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic";
|
||||
@ -212,6 +229,10 @@ struct TileSheetV4 {
|
||||
pixels(std::move(pPixels)) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the dimensional size of the SubSheet (e.g. width * height)
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
|
||||
@ -233,10 +254,12 @@ struct TileSheetV4 {
|
||||
[[nodiscard]]
|
||||
constexpr bool valid(TileSheetV4::SubSheet const&ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
return ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
|
||||
[bpp, bytes](TileSheetV4::SubSheet const&s) {
|
||||
return bytes == s.pixels.size() && valid(s, bpp);
|
||||
});
|
||||
return
|
||||
(ss.pixels.empty() || ss.subsheets.empty()) &&
|
||||
ox::all_of(ss.subsheets.begin(), ss.subsheets.end(),
|
||||
[bpp, bytes](TileSheetV4::SubSheet const&s) {
|
||||
return bytes == s.pixels.size() && valid(s, bpp);
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
@ -245,8 +268,14 @@ constexpr bool valid(TileSheetV4 const&ts) noexcept {
|
||||
}
|
||||
|
||||
constexpr void repair(TileSheetV4::SubSheet &ss, int bpp) noexcept {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
ss.pixels.resize(bytes);
|
||||
if (ss.subsheets.empty()) {
|
||||
auto const bytes = static_cast<size_t>(ss.columns * ss.rows * PixelsPerTile) / (bpp == 4 ? 2 : 1);
|
||||
ss.pixels.resize(bytes);
|
||||
} else {
|
||||
ss.pixels.clear();
|
||||
ss.columns = -1;
|
||||
ss.rows = -1;
|
||||
}
|
||||
for (auto &s : ss.subsheets) {
|
||||
repair(s, bpp);
|
||||
}
|
||||
@ -273,7 +302,7 @@ size_t getTileCnt(TileSheet const&ts) noexcept;
|
||||
TileSheet::SubSheet const*getSubsheet(TileSheet const&ts, SubSheetId id) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
size_t getTileIdx(TileSheet const&ts, SubSheetId id) noexcept;
|
||||
ox::Optional<size_t> getTileIdx(TileSheet const&ts, SubSheetId id) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept;
|
||||
@ -392,6 +421,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<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 const&ts, SubSheetId pId) noexcept;
|
||||
|
@ -10,8 +10,8 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
void ContextDeleter::operator()(Context *p) noexcept {
|
||||
ox::safeDelete(p);
|
||||
void safeDelete(Context *ctx) noexcept {
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
Context::Context(turbine::Context &tctx) noexcept: turbineCtx(tctx) {
|
||||
|
@ -63,6 +63,19 @@ ox::Error loadSpritePalette(
|
||||
return {};
|
||||
}
|
||||
|
||||
void clearCbb(Context&, unsigned const cbb) noexcept {
|
||||
for (auto &v : MEM_BG_TILES[cbb]) {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void clearCbbs(Context &ctx) noexcept {
|
||||
clearCbb(ctx, 0);
|
||||
clearCbb(ctx, 1);
|
||||
clearCbb(ctx, 2);
|
||||
clearCbb(ctx, 3);
|
||||
}
|
||||
|
||||
static ox::Error loadTileSheetSet(
|
||||
Context &ctx,
|
||||
ox::Span<uint16_t> tileMapTargetMem,
|
||||
@ -99,10 +112,10 @@ ox::Error loadBgTileSheet(
|
||||
size_t const tileCnt) noexcept {
|
||||
size_t const bppMod = ts.bpp == 4;
|
||||
size_t const bytesPerTile = PixelsPerTile >> bppMod;
|
||||
auto const pixCnt = tileCnt * bytesPerTile;
|
||||
auto const cnt = (tileCnt * bytesPerTile) / 2;
|
||||
auto const srcPxIdx = srcTileIdx * bytesPerTile;
|
||||
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 p1 = static_cast<uint16_t>(ts.pixels[srcIdx]);
|
||||
auto const p2 = static_cast<uint16_t>(ts.pixels[srcIdx + 1]);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
|
||||
#include <ox/std/def.hpp>
|
||||
#include <ox/std/realstd.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/turbine.hpp>
|
||||
@ -48,12 +49,10 @@ OX_ALLOW_UNSAFE_BUFFERS_END
|
||||
oxErrf("\tError Message:\t{}\n", err.msg);
|
||||
}
|
||||
oxErrf("\tError Code:\t{}\n", static_cast<ErrorCode>(err));
|
||||
if (err.file != nullptr) {
|
||||
oxErrf("\tError Location:\t{}:{}\n", err.file, err.line);
|
||||
if (err.src.file_name() != nullptr) {
|
||||
oxErrf("\tError Location:\t{}:{}\n", err.src.file_name(), err.src.line());
|
||||
}
|
||||
// disable all interrupt handling and IntrWait on no interrupts
|
||||
REG_IE = 0;
|
||||
teagba::intrwait(0, 0);
|
||||
abort();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ static class: public keel::Module {
|
||||
ox::Vector<keel::PackTransform> packTransforms() const noexcept final {
|
||||
return {
|
||||
// convert tilesheets to CompactTileSheets
|
||||
[](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result<bool> {
|
||||
[](keel::Context &ctx, ox::Buffer &buff, ox::StringViewCR typeId) -> ox::Result<bool> {
|
||||
if (typeId == ox::ModelTypeId_v<TileSheetV1> ||
|
||||
typeId == ox::ModelTypeId_v<TileSheetV2> ||
|
||||
typeId == ox::ModelTypeId_v<TileSheetV3> ||
|
||||
@ -78,7 +78,7 @@ static class: public keel::Module {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result<bool> {
|
||||
[](keel::Context &ctx, ox::Buffer &buff, ox::StringViewCR typeId) -> ox::Result<bool> {
|
||||
if (typeId == ox::ModelTypeId_v<NostalgiaPalette> ||
|
||||
typeId == ox::ModelTypeId_v<PaletteV1> ||
|
||||
typeId == ox::ModelTypeId_v<PaletteV2> ||
|
||||
|
@ -7,8 +7,8 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
void ContextDeleter::operator()(Context *p) noexcept {
|
||||
ox::safeDelete(p);
|
||||
void safeDelete(Context *ctx) noexcept {
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
Context::Context(turbine::Context &tctx, InitParams const¶ms) noexcept:
|
||||
|
@ -111,15 +111,15 @@ static constexpr auto bgVertexRow(uint_t x, uint_t y) noexcept {
|
||||
}
|
||||
|
||||
static void setSpriteBufferObject(
|
||||
uint_t vi,
|
||||
float enabled,
|
||||
uint_t const vi,
|
||||
float const enabled,
|
||||
float x,
|
||||
float y,
|
||||
uint_t textureRow,
|
||||
uint_t flipX,
|
||||
uint_t priority,
|
||||
float *vbo,
|
||||
GLuint *ebo) noexcept {
|
||||
uint_t const textureRow,
|
||||
uint_t const flipX,
|
||||
uint_t const priority,
|
||||
ox::Span<float> const vbo,
|
||||
ox::Span<GLuint> const ebo) noexcept {
|
||||
// don't worry, this memcpy gets optimized to something much more ideal
|
||||
constexpr float xmod = 0.1f;
|
||||
constexpr float ymod = 0.1f;
|
||||
@ -138,25 +138,25 @@ static void setSpriteBufferObject(
|
||||
enabled, x + xmod, y + ymod, prif, R, textureRowf + 0, // top right
|
||||
enabled, x, y + ymod, prif, L, textureRowf + 0, // top left
|
||||
};
|
||||
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||
ox::spancpy<float>(vbo, vertices);
|
||||
ox::Array<GLuint, SpriteVertexEboLength> const elms {
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
};
|
||||
memcpy(ebo, elms.data(), sizeof(elms));
|
||||
ox::spancpy<GLuint>(ebo, elms);
|
||||
}
|
||||
|
||||
static void setTileBufferObject(
|
||||
uint_t vi,
|
||||
uint_t const vi,
|
||||
float x,
|
||||
float y,
|
||||
float textureTileIdx,
|
||||
float priority,
|
||||
float palOffset,
|
||||
bool flipX,
|
||||
bool flipY,
|
||||
float *vbo,
|
||||
GLuint *ebo) noexcept {
|
||||
float const textureTileIdx,
|
||||
float const priority,
|
||||
float const palOffset,
|
||||
bool const flipX,
|
||||
bool const flipY,
|
||||
ox::Span<float> const vbo,
|
||||
ox::Span<GLuint> const ebo) noexcept {
|
||||
// don't worry, this memcpy gets optimized to something much more ideal
|
||||
constexpr float ymod = 0.1f;
|
||||
constexpr float xmod = 0.1f;
|
||||
@ -170,24 +170,35 @@ static void setTileBufferObject(
|
||||
float const T = flipY ? 1 : 0;
|
||||
float const B = flipY ? 0 : 1;
|
||||
ox::Array<float, BgVertexVboLength> const vertices {
|
||||
x, y, prif, L, B, textureTileIdx, palOffset, // bottom left
|
||||
x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right
|
||||
x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right
|
||||
x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left
|
||||
x, y, prif, L, B, textureTileIdx, palOffset, // bottom left
|
||||
x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right
|
||||
x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right
|
||||
x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left
|
||||
};
|
||||
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||
ox::spancpy<float>(vbo, vertices);
|
||||
ox::Array<GLuint, BgVertexEboLength> const elms {
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
};
|
||||
memcpy(ebo, elms.data(), sizeof(elms));
|
||||
ox::spancpy<GLuint>(ebo, elms);
|
||||
}
|
||||
|
||||
static void initSpriteBufferObjects(Context &ctx, glutils::BufferSet &bs) noexcept {
|
||||
for (auto i = 0u; i < ctx.spriteCount; ++i) {
|
||||
auto vbo = &bs.vertices[i * static_cast<std::size_t>(SpriteVertexVboLength)];
|
||||
auto ebo = &bs.elements[i * static_cast<std::size_t>(SpriteVertexEboLength)];
|
||||
setSpriteBufferObject(i * static_cast<uint_t>(SpriteVertexVboRows) * ctx.blocksPerSprite, 0, 0, 0, 0, false, 0, vbo, ebo);
|
||||
auto const vbo = ox::Span{bs.vertices}
|
||||
+ i * static_cast<std::size_t>(SpriteVertexVboLength);
|
||||
auto const ebo = ox::Span{bs.elements}
|
||||
+ i * static_cast<std::size_t>(SpriteVertexEboLength);
|
||||
setSpriteBufferObject(
|
||||
i * static_cast<uint_t>(SpriteVertexVboRows) * ctx.blocksPerSprite,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
0,
|
||||
vbo,
|
||||
ebo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,8 +206,10 @@ static void initBackgroundBufferObjects(glutils::BufferSet &bs) noexcept {
|
||||
for (auto x = 0u; x < TileColumns; ++x) {
|
||||
for (auto y = 0u; y < TileRows; ++y) {
|
||||
const auto i = bgVertexRow(x, y);
|
||||
auto vbo = &bs.vertices[i * static_cast<std::size_t>(BgVertexVboLength)];
|
||||
auto ebo = &bs.elements[i * static_cast<std::size_t>(BgVertexEboLength)];
|
||||
auto const vbo = ox::Span{bs.vertices}
|
||||
+ i * static_cast<std::size_t>(BgVertexVboLength);
|
||||
auto const ebo = ox::Span{bs.elements}
|
||||
+ i * static_cast<std::size_t>(BgVertexEboLength);
|
||||
setTileBufferObject(
|
||||
static_cast<uint_t>(i * BgVertexVboRows),
|
||||
static_cast<float>(x),
|
||||
@ -421,8 +434,8 @@ static void setSprite(
|
||||
auto const eboIdx = eboBase + renderer::SpriteVertexEboLength * i;
|
||||
oxAssert(vboIdx < ctx.spriteBlocks.vertices.size(), "vbo overflow");
|
||||
oxAssert(eboIdx < ctx.spriteBlocks.elements.size(), "ebo overflow");
|
||||
auto const vbo = &ctx.spriteBlocks.vertices[vboIdx];
|
||||
auto const ebo = &ctx.spriteBlocks.elements[eboIdx];
|
||||
auto const vbo = ox::Span{ctx.spriteBlocks.vertices} + vboIdx;
|
||||
auto const ebo = ox::Span{ctx.spriteBlocks.elements} + eboIdx;
|
||||
renderer::setSpriteBufferObject(
|
||||
static_cast<uint_t>(vboIdx),
|
||||
enabled,
|
||||
@ -556,7 +569,7 @@ static void copyPixels(
|
||||
CompactTileSheet const&ts,
|
||||
ox::Span<uint32_t> dst,
|
||||
size_t const srcPxIdx,
|
||||
size_t pxlCnt) noexcept {
|
||||
size_t const pxlCnt) noexcept {
|
||||
size_t idx{};
|
||||
if (ts.bpp == 4) {
|
||||
for (size_t i = 0; i < pxlCnt; i += 2) {
|
||||
@ -573,6 +586,18 @@ static void copyPixels(
|
||||
}
|
||||
}
|
||||
|
||||
void clearCbb(Context &ctx, unsigned const cbb) noexcept {
|
||||
for (auto &v : ctx.cbbs[cbb].pixels) {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void clearCbbs(Context &ctx) noexcept {
|
||||
for (unsigned i = 0 ; i < ctx.cbbs.size(); ++i) {
|
||||
clearCbb(ctx, i);
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error loadBgTileSheet(
|
||||
Context &ctx,
|
||||
unsigned const cbb,
|
||||
@ -656,8 +681,8 @@ void setBgTile(
|
||||
const auto x = static_cast<uint_t>(column);
|
||||
const auto i = renderer::bgVertexRow(x, y);
|
||||
auto &cbb = ctx.cbbs[z];
|
||||
const auto vbo = &cbb.vertices[i * renderer::BgVertexVboLength];
|
||||
const auto ebo = &cbb.elements[i * renderer::BgVertexEboLength];
|
||||
const auto vbo = ox::Span{cbb.vertices} + i * renderer::BgVertexVboLength;
|
||||
const auto ebo = ox::Span{cbb.elements} + i * renderer::BgVertexEboLength;
|
||||
auto &bg = ctx.backgrounds[bgIdx];
|
||||
renderer::setTileBufferObject(
|
||||
static_cast<uint_t>(i * renderer::BgVertexVboRows),
|
||||
|
@ -3,7 +3,6 @@ add_library(NostalgiaCore-Studio)
|
||||
add_library(
|
||||
NostalgiaCore-Studio-ImGui
|
||||
studiomodule.cpp
|
||||
tilesheeteditor/tilesheeteditor-imgui.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
@ -15,7 +14,6 @@ target_link_libraries(
|
||||
target_link_libraries(
|
||||
NostalgiaCore-Studio-ImGui PUBLIC
|
||||
NostalgiaCore-Studio
|
||||
Studio
|
||||
)
|
||||
|
||||
install(
|
||||
|
@ -1,6 +1,7 @@
|
||||
target_sources(
|
||||
NostalgiaCore-Studio PRIVATE
|
||||
commands/addcolorcommand.cpp
|
||||
commands/addpagecommand.cpp
|
||||
commands/applycolorallpagescommand.cpp
|
||||
commands/duplicatepagecommand.cpp
|
||||
commands/movecolorcommand.cpp
|
||||
@ -9,5 +10,9 @@ target_sources(
|
||||
commands/renamepagecommand.cpp
|
||||
commands/updatecolorcommand.cpp
|
||||
commands/updatecolorinfocommand.cpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
NostalgiaCore-Studio-ImGui PRIVATE
|
||||
paletteeditor-imgui.cpp
|
||||
)
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 {};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -9,6 +9,7 @@ namespace nostalgia::core {
|
||||
enum class PaletteEditorCommandId {
|
||||
ApplyColorAllPages,
|
||||
RenamePage,
|
||||
AddPage,
|
||||
DuplicatePage,
|
||||
RemovePage,
|
||||
AddColor,
|
||||
|
@ -23,7 +23,7 @@ int DuplicatePageCommand::commandId() const noexcept {
|
||||
}
|
||||
|
||||
ox::Error DuplicatePageCommand::redo() noexcept {
|
||||
m_pal.pages.emplace(m_dstIdx, "", std::move(m_page));
|
||||
m_pal.pages.emplace(m_dstIdx, ox::sfmt("Page {}", m_pal.pages.size() + 1), std::move(m_page));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <keel/media.hpp>
|
||||
|
||||
#include "commands/addcolorcommand.hpp"
|
||||
#include "commands/addpagecommand.hpp"
|
||||
#include "commands/applycolorallpagescommand.hpp"
|
||||
#include "commands/duplicatepagecommand.hpp"
|
||||
#include "commands/movecolorcommand.hpp"
|
||||
@ -196,7 +197,11 @@ void PaletteEditorImGui::drawPagesEditor() noexcept {
|
||||
constexpr auto toolbarHeight = 40;
|
||||
auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24};
|
||||
if (ImGui::Button("Add", btnSz)) {
|
||||
std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size());
|
||||
if (m_pal.pages.empty()) {
|
||||
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;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -6,6 +6,11 @@ target_sources(
|
||||
tilesheetpixels.cpp
|
||||
)
|
||||
|
||||
target_sources(
|
||||
NostalgiaCore-Studio-ImGui PRIVATE
|
||||
tilesheeteditor-imgui.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
NostalgiaCore-Studio-ImGui PUBLIC
|
||||
lodepng
|
||||
|
@ -9,27 +9,24 @@ namespace nostalgia::core {
|
||||
core::UpdateSubSheetCommand::UpdateSubSheetCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx idx,
|
||||
ox::String name,
|
||||
int cols,
|
||||
int rows) noexcept:
|
||||
m_img(img),
|
||||
m_idx(std::move(idx)),
|
||||
m_sheet(getSubSheet(m_img, m_idx)),
|
||||
m_newName(std::move(name)),
|
||||
m_newCols(cols),
|
||||
m_newRows(rows) {
|
||||
ox::StringParam name,
|
||||
int const cols,
|
||||
int const rows):
|
||||
m_img{img},
|
||||
m_idx{std::move(idx)},
|
||||
m_sheet{getSubSheet(m_img, m_idx)} {
|
||||
m_sheet = getSubSheet(m_img, m_idx);
|
||||
m_sheet.name = std::move(name);
|
||||
OX_THROW_ERROR(resizeSubsheet(m_sheet, m_img.bpp, {cols, rows}));
|
||||
}
|
||||
|
||||
ox::Error UpdateSubSheetCommand::redo() noexcept {
|
||||
auto &sheet = getSubSheet(m_img, m_idx);
|
||||
sheet.name = m_newName;
|
||||
oxLogError(resizeSubsheet(sheet, m_img.bpp, {m_newCols, m_newRows}));
|
||||
std::swap(m_sheet, getSubSheet(m_img, m_idx));
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error UpdateSubSheetCommand::undo() noexcept {
|
||||
auto &sheet = getSubSheet(m_img, m_idx);
|
||||
sheet = m_sheet;
|
||||
std::swap(m_sheet, getSubSheet(m_img, m_idx));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -13,17 +13,14 @@ class UpdateSubSheetCommand: public TileSheetCommand {
|
||||
TileSheet &m_img;
|
||||
TileSheet::SubSheetIdx m_idx;
|
||||
TileSheet::SubSheet m_sheet;
|
||||
ox::String m_newName;
|
||||
int m_newCols = 0;
|
||||
int m_newRows = 0;
|
||||
|
||||
public:
|
||||
UpdateSubSheetCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx idx,
|
||||
ox::String name,
|
||||
ox::StringParam name,
|
||||
int cols,
|
||||
int rows) noexcept;
|
||||
int rows);
|
||||
|
||||
ox::Error redo() noexcept final;
|
||||
|
||||
|
@ -234,7 +234,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
||||
ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true);
|
||||
{
|
||||
static constexpr auto btnHeight = ig::BtnSz.y;
|
||||
auto const btnSize = ImVec2{btnHeight, btnHeight};
|
||||
auto constexpr btnSize = ImVec2{btnHeight, btnHeight};
|
||||
if (ig::PushButton("+", btnSize)) {
|
||||
auto insertOnIdx = m_model.activeSubSheetIdx();
|
||||
auto const&parent = m_model.activeSubSheet();
|
||||
@ -258,16 +258,19 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
|
||||
m_exportMenu.show();
|
||||
}
|
||||
TileSheet::SubSheetIdx path;
|
||||
static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
|
||||
if (ImGui::BeginTable("Subsheets", 4, flags)) {
|
||||
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
|
||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
|
||||
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableHeadersRow();
|
||||
drawSubsheetSelector(m_view.img().subsheet, path);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
static constexpr auto flags =
|
||||
ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_NoBordersInBody |
|
||||
ImGuiTableFlags_ScrollY;
|
||||
if (ImGui::BeginTable("Subsheets", 4, flags)) {
|
||||
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
|
||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
|
||||
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableHeadersRow();
|
||||
drawSubsheetSelector(m_view.img().subsheet, path);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
@ -462,8 +465,12 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
|
||||
}
|
||||
}
|
||||
// header
|
||||
auto constexpr palTblFlags =
|
||||
ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_SizingStretchProp |
|
||||
ImGuiTableFlags_ScrollY;
|
||||
if (ImGui::BeginTable(
|
||||
"PaletteTable", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
|
||||
"PaletteTable", 4, palTblFlags)) {
|
||||
ImGui::TableSetupColumn("Idx", 0, 0.6f);
|
||||
ImGui::TableSetupColumn("", 0, 0.22f);
|
||||
ImGui::TableSetupColumn("Name", 0, 3);
|
||||
@ -544,7 +551,7 @@ void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &tctx) noexcept
|
||||
auto const popupHeight = modSize ? 130.f : 85.f;
|
||||
auto const popupSz = ImVec2{popupWidth, popupHeight};
|
||||
if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) {
|
||||
ImGui::InputText("Name", m_name.data(), m_name.cap());
|
||||
ig::InputText("Name", m_name);
|
||||
if (modSize) {
|
||||
ImGui::InputInt("Columns", &m_cols);
|
||||
ImGui::InputInt("Rows", &m_rows);
|
||||
|
@ -26,11 +26,6 @@
|
||||
|
||||
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
|
||||
static void normalizeSubsheets(TileSheet::SubSheet &ss) noexcept {
|
||||
if (ss.subsheets.empty()) {
|
||||
@ -42,7 +37,14 @@ 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_tctx(m_sctx.tctx),
|
||||
m_path(path),
|
||||
@ -62,7 +64,7 @@ void TileSheetEditorModel::cut() {
|
||||
TileSheetClipboard blankCb;
|
||||
auto cb = ox::make_unique<TileSheetClipboard>();
|
||||
auto const&s = activeSubSheet();
|
||||
iterateSelectionRows(*m_selection, [&](int x, int y) {
|
||||
iterateSelectionRows(*m_selection, [&](int const x, int const y) {
|
||||
auto pt = ox::Point{x, y};
|
||||
auto const idx = core::idx(s, pt);
|
||||
auto const c = getPixel(s, m_img.bpp, idx);
|
||||
@ -73,7 +75,8 @@ void TileSheetEditorModel::cut() {
|
||||
auto const pt1 = m_selection->a;
|
||||
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
|
||||
turbine::setClipboardObject(m_tctx, std::move(cb));
|
||||
pushCommand(ox::make<CutPasteCommand>(CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
|
||||
pushCommand(ox::make<CutPasteCommand>(
|
||||
CommandId::Cut, m_img, m_activeSubsSheetIdx, pt1, pt2, blankCb));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::copy() {
|
||||
@ -81,7 +84,7 @@ void TileSheetEditorModel::copy() {
|
||||
return;
|
||||
}
|
||||
auto cb = ox::make_unique<TileSheetClipboard>();
|
||||
iterateSelectionRows(*m_selection, [&](int x, int y) {
|
||||
iterateSelectionRows(*m_selection, [&](int const x, int const y) {
|
||||
auto pt = ox::Point{x, y};
|
||||
const auto&s = activeSubSheet();
|
||||
const auto idx = core::idx(s, pt);
|
||||
@ -105,7 +108,8 @@ void TileSheetEditorModel::paste() {
|
||||
auto const&s = activeSubSheet();
|
||||
auto const pt1 = m_selection->a;
|
||||
auto const pt2 = ox::Point{s.columns * TileWidth, s.rows * TileHeight};
|
||||
pushCommand(ox::make<CutPasteCommand>(CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
|
||||
pushCommand(ox::make<CutPasteCommand>(
|
||||
CommandId::Paste, m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::acceptsClipboardPayload() const noexcept {
|
||||
@ -120,8 +124,8 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
}
|
||||
constexpr ox::StringView uuidPrefix = "uuid://";
|
||||
if (ox::beginsWith(path, uuidPrefix)) {
|
||||
auto uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes());
|
||||
auto out = keelCtx(m_tctx).uuidToPath.at(uuid);
|
||||
auto const uuid = ox::StringView(&path[uuidPrefix.bytes()], path.bytes() - uuidPrefix.bytes());
|
||||
auto const out = keelCtx(m_tctx).uuidToPath.at(uuid);
|
||||
if (out.error) {
|
||||
return {};
|
||||
}
|
||||
@ -131,13 +135,14 @@ ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept {
|
||||
ox::Error TileSheetEditorModel::setPalette(ox::StringViewCR path) noexcept {
|
||||
OX_REQUIRE(uuid, keelCtx(m_tctx).pathToUuid.at(path));
|
||||
pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), m_img, uuid->toString()));
|
||||
pushCommand(ox::make<PaletteChangeCommand>(
|
||||
activeSubSheetIdx(), m_img, uuid->toString()));
|
||||
return {};
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::setPalettePage(size_t pg) noexcept {
|
||||
void TileSheetEditorModel::setPalettePage(size_t const pg) noexcept {
|
||||
m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1);
|
||||
m_updated = true;
|
||||
}
|
||||
@ -146,7 +151,7 @@ size_t TileSheetEditorModel::palettePage() const noexcept {
|
||||
return m_palettePage;
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept {
|
||||
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t const palIdx) noexcept {
|
||||
const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
|
||||
return;
|
||||
@ -155,7 +160,8 @@ void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) n
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
|
||||
} else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) {
|
||||
pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
pushCommand(ox::make<DrawCommand>(
|
||||
m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,16 +177,20 @@ void TileSheetEditorModel::rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept
|
||||
pushCommand(ox::make<RmSubSheetCommand>(m_img, idx));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
|
||||
void TileSheetEditorModel::insertTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
pushCommand(ox::make<InsertTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept {
|
||||
void TileSheetEditorModel::deleteTiles(
|
||||
TileSheet::SubSheetIdx const&idx, std::size_t const tileIdx, std::size_t const tileCnt) noexcept {
|
||||
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept {
|
||||
pushCommand(ox::make<UpdateSubSheetCommand>(m_img, idx, ox::String(name), cols, rows));
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(
|
||||
TileSheet::SubSheetIdx const&idx, ox::StringViewCR name, int const cols, int const rows) noexcept {
|
||||
OX_REQUIRE(cmd, ox::makeCatch<UpdateSubSheetCommand>(m_img, idx, name, cols, rows));
|
||||
pushCommand(cmd);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -189,7 +199,7 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n
|
||||
this->activeSubsheetChanged.emit(m_activeSubsSheetIdx);
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
|
||||
void TileSheetEditorModel::fill(ox::Point const&pt, int const palIdx) noexcept {
|
||||
auto const&activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
// build idx list
|
||||
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
|
||||
@ -197,10 +207,10 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
|
||||
}
|
||||
ox::Array<bool, PixelsPerTile> updateMap = {};
|
||||
auto const oldColor = getPixel(activeSubSheet, m_img.bpp, pt);
|
||||
getFillPixels(updateMap, pt, oldColor);
|
||||
getFillPixels(activeSubSheet, updateMap, pt, oldColor);
|
||||
ox::Vector<std::size_t> idxList;
|
||||
auto i = core::idx(activeSubSheet, pt) / PixelsPerTile * PixelsPerTile;
|
||||
for (auto u : updateMap) {
|
||||
for (auto const u : updateMap) {
|
||||
if (u) {
|
||||
idxList.emplace_back(i);
|
||||
}
|
||||
@ -230,7 +240,7 @@ void TileSheetEditorModel::completeSelection() noexcept {
|
||||
m_selTracker.finishSelection();
|
||||
m_selection.emplace(m_selTracker.selection());
|
||||
auto&pt = m_selection->b;
|
||||
auto&s = activeSubSheet();
|
||||
auto const&s = activeSubSheet();
|
||||
pt.x = ox::min(s.columns * TileWidth - 1, pt.x);
|
||||
pt.y = ox::min(s.rows * TileHeight - 1, pt.y);
|
||||
}
|
||||
@ -275,47 +285,44 @@ ox::Error TileSheetEditorModel::saveFile() noexcept {
|
||||
return m_sctx.project->writeObj(m_path, m_img, ox::ClawFormat::Metal);
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept {
|
||||
bool TileSheetEditorModel::pixelSelected(std::size_t const idx) const noexcept {
|
||||
auto const&s = activeSubSheet();
|
||||
auto const pt = idxToPt(static_cast<int>(idx), s.columns);
|
||||
return m_selection && m_selection->contains(pt);
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept {
|
||||
const auto &activeSubSheet = this->activeSubSheet();
|
||||
const auto tileIdx = [activeSubSheet](const ox::Point &pt) noexcept {
|
||||
return ptToIdx(pt, activeSubSheet.columns) / PixelsPerTile;
|
||||
};
|
||||
// get points
|
||||
const auto leftPt = pt + ox::Point(-1, 0);
|
||||
const auto rightPt = pt + ox::Point(1, 0);
|
||||
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);
|
||||
void TileSheetEditorModel::getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
ox::Span<bool> pixels,
|
||||
ox::Point const&pt,
|
||||
int const oldColor) const noexcept {
|
||||
auto const idx = ptToIdx(pt, activeSubSheet.columns);
|
||||
auto const relIdx = idx % PixelsPerTile;
|
||||
if (pixels[relIdx] || getPixel(activeSubSheet, m_img.bpp, idx) != oldColor) {
|
||||
return;
|
||||
}
|
||||
// mark pixels to update
|
||||
pixels[idx % PixelsPerTile] = true;
|
||||
if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) {
|
||||
getFillPixels(pixels, leftPt, oldColor);
|
||||
pixels[relIdx] = true;
|
||||
if (pt.x % TileWidth != 0) {
|
||||
auto const leftPt = pt + ox::Point{-1, 0};
|
||||
getFillPixels(activeSubSheet, pixels, leftPt, oldColor);
|
||||
}
|
||||
if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) {
|
||||
getFillPixels(pixels, rightPt, oldColor);
|
||||
if (pt.x % TileWidth != TileWidth - 1) {
|
||||
auto const rightPt = pt + ox::Point{1, 0};
|
||||
getFillPixels(activeSubSheet, pixels, rightPt, oldColor);
|
||||
}
|
||||
if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) {
|
||||
getFillPixels(pixels, topPt, oldColor);
|
||||
if (pt.y % TileHeight != 0) {
|
||||
auto const topPt = pt + ox::Point{0, -1};
|
||||
getFillPixels(activeSubSheet, pixels, topPt, oldColor);
|
||||
}
|
||||
if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) {
|
||||
getFillPixels(pixels, bottomPt, oldColor);
|
||||
if (pt.y % TileHeight != TileHeight - 1) {
|
||||
auto const bottomPt = pt + ox::Point{0, 1};
|
||||
getFillPixels(activeSubSheet, pixels, bottomPt, oldColor);
|
||||
}
|
||||
}
|
||||
|
||||
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_updated = true;
|
||||
}
|
||||
|
@ -4,9 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/bounds.hpp>
|
||||
#include <ox/std/point.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <studio/studio.hpp>
|
||||
@ -38,7 +36,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
bool m_updated = false;
|
||||
|
||||
public:
|
||||
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringView path, studio::UndoStack &undoStack);
|
||||
TileSheetEditorModel(studio::StudioContext &sctx, ox::StringViewCR path, studio::UndoStack &undoStack);
|
||||
|
||||
~TileSheetEditorModel() override = default;
|
||||
|
||||
@ -63,7 +61,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
[[nodiscard]]
|
||||
ox::StringView palPath() const noexcept;
|
||||
|
||||
ox::Error setPalette(ox::StringView path) noexcept;
|
||||
ox::Error setPalette(ox::StringViewCR path) noexcept;
|
||||
|
||||
void setPalettePage(size_t pg) noexcept;
|
||||
|
||||
@ -128,7 +126,11 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
bool pixelSelected(std::size_t idx) const noexcept;
|
||||
|
||||
private:
|
||||
void getFillPixels(ox::Span<bool> pixels, ox::Point const&pt, int oldColor) const noexcept;
|
||||
void getFillPixels(
|
||||
TileSheet::SubSheet const&activeSubSheet,
|
||||
ox::Span<bool> pixels,
|
||||
ox::Point const&pt,
|
||||
int oldColor) const noexcept;
|
||||
|
||||
void pushCommand(studio::UndoCommand *cmd) noexcept;
|
||||
|
||||
|
@ -66,10 +66,10 @@ static ox::Optional<size_t> getPixelIdx(
|
||||
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{};
|
||||
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 {
|
||||
@ -355,12 +355,12 @@ ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::StringViewCR path) noexc
|
||||
/**
|
||||
* Gets the offset in tiles of the desired subsheet.
|
||||
*/
|
||||
static ox::Result<unsigned> getTileOffset(
|
||||
static ox::Result<uint32_t> getTileOffset(
|
||||
TileSheet::SubSheet const&ss,
|
||||
ox::SpanView<ox::StringView> const&pNamePath,
|
||||
int8_t pBpp,
|
||||
std::size_t pIt = 0,
|
||||
unsigned pCurrentTotal = 0) noexcept {
|
||||
uint32_t pCurrentTotal = 0) noexcept {
|
||||
// pIt == pNamePath.size() - 1 &&
|
||||
if (ss.name != pNamePath[pIt]) {
|
||||
return ox::Error(2, "Wrong branch");
|
||||
@ -374,12 +374,14 @@ static ox::Result<unsigned> getTileOffset(
|
||||
if (!err) {
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ target_link_libraries(
|
||||
|
||||
target_compile_definitions(
|
||||
NostalgiaStudio PUBLIC
|
||||
OLYMPIC_APP_VERSION="d2024.12.1"
|
||||
OLYMPIC_APP_VERSION="dev build"
|
||||
)
|
||||
|
||||
install(
|
||||
|
@ -196,15 +196,15 @@ class AssetManager {
|
||||
template<typename T>
|
||||
class AssetTypeManager: public AssetTypeManagerBase {
|
||||
public:
|
||||
using Loader = std::function<ox::Result<T>(ox::StringView assetId)>;
|
||||
using Loader = std::function<ox::Result<T>(ox::StringViewCR assetId)>;
|
||||
private:
|
||||
Loader m_loader{};
|
||||
ox::HashMap<ox::String, ox::UPtr<AssetContainer<T>>> m_cache;
|
||||
|
||||
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));
|
||||
if (!out || !*out) {
|
||||
return ox::Error(1, "asset is null");
|
||||
@ -212,7 +212,7 @@ class AssetManager {
|
||||
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];
|
||||
OX_REQUIRE_M(obj, m_loader(assetId));
|
||||
if (!p) {
|
||||
@ -224,7 +224,7 @@ class AssetManager {
|
||||
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];
|
||||
OX_REQUIRE_M(obj, m_loader(assetId));
|
||||
if (!p) {
|
||||
@ -247,7 +247,7 @@ class AssetManager {
|
||||
};
|
||||
|
||||
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>
|
||||
ox::Result<AssetTypeManager<T>*> getTypeManager() noexcept {
|
||||
@ -273,18 +273,18 @@ class AssetManager {
|
||||
}
|
||||
|
||||
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>());
|
||||
return m->getAsset(assetId);
|
||||
}
|
||||
|
||||
ox::Error reloadAsset(ox::StringView assetId) noexcept {
|
||||
ox::Error reloadAsset(ox::StringViewCR assetId) noexcept {
|
||||
m_fileUpdated[assetId].emit(assetId);
|
||||
return {};
|
||||
}
|
||||
|
||||
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(out, m->loadAsset(assetId));
|
||||
m_fileUpdated[assetId].connect(m, &AssetTypeManager<T>::reloadAsset);
|
||||
|
@ -12,7 +12,7 @@
|
||||
namespace keel {
|
||||
|
||||
class Context;
|
||||
using PackTransform = ox::Result<bool>(*)(Context&, ox::Buffer &clawData, ox::StringView);
|
||||
using PackTransform = ox::Result<bool>(*)(Context&, ox::Buffer &clawData, ox::StringViewCR);
|
||||
|
||||
class Context {
|
||||
public:
|
||||
|
@ -70,7 +70,7 @@ constexpr auto makeLoader(Context &ctx) {
|
||||
if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) {
|
||||
return err;
|
||||
}
|
||||
OX_RETURN_ERROR(convert<T>(ctx, buff, &obj));
|
||||
OX_RETURN_ERROR(convert<T>(ctx, buff, obj));
|
||||
}
|
||||
return std::move(obj);
|
||||
};
|
||||
|
@ -125,7 +125,9 @@ ox::Error preloadObj(
|
||||
OX_RETURN_ERROR(err);
|
||||
keel::PreloadPtr const p{.preloadAddr = a};
|
||||
OX_RETURN_ERROR(ox::writeMC(p).moveTo(buff));
|
||||
oxOutf("preloaded {} as a {} @ {} to {}\n", path, obj.type()->typeName, a, a + size);
|
||||
auto const&pbufSz = pl.buff().size();
|
||||
oxOutf("preloaded {} as a {} @ {} to {} / {}, total size: {}\n",
|
||||
path, obj.type()->typeName, a, a + size, pbufSz - 1, pbufSz - a);
|
||||
} else {
|
||||
// strip the Claw header (it is not needed after preloading) and write back out to dest fs
|
||||
OX_RETURN_ERROR(ox::writeMC(obj).moveTo(buff));
|
||||
|
@ -16,10 +16,50 @@ namespace keel {
|
||||
class Wrap {
|
||||
public:
|
||||
virtual ~Wrap() = default;
|
||||
[[nodiscard]]
|
||||
virtual ox::CStringView typeName() const noexcept = 0;
|
||||
[[nodiscard]]
|
||||
virtual int typeVersion() const noexcept = 0;
|
||||
[[nodiscard]]
|
||||
virtual ox::UAnyPtr moveToCopy() noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class WrapInline: public Wrap {
|
||||
class WrapT: public Wrap {
|
||||
public:
|
||||
[[nodiscard]]
|
||||
virtual constexpr T &obj() noexcept = 0;
|
||||
|
||||
ox::UAnyPtr moveToCopy() noexcept final {
|
||||
return new T{std::move(obj())};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class WrapRef final: public WrapT<T> {
|
||||
private:
|
||||
T &m_obj;
|
||||
|
||||
public:
|
||||
constexpr explicit WrapRef(T &obj): m_obj{obj} {}
|
||||
|
||||
ox::CStringView typeName() const noexcept override {
|
||||
return ox::ModelTypeName_v<T>;
|
||||
}
|
||||
|
||||
int typeVersion() const noexcept override {
|
||||
return ox::ModelTypeVersion_v<T>;
|
||||
}
|
||||
|
||||
constexpr T &obj() noexcept override {
|
||||
return m_obj;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class WrapInline final: public WrapT<T> {
|
||||
private:
|
||||
T m_obj;
|
||||
|
||||
@ -30,8 +70,15 @@ class WrapInline: public Wrap {
|
||||
constexpr explicit WrapInline(Args &&...args): m_obj(ox::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &obj() noexcept {
|
||||
ox::CStringView typeName() const noexcept override {
|
||||
return ox::ModelTypeName_v<T>;
|
||||
}
|
||||
|
||||
int typeVersion() const noexcept override {
|
||||
return ox::ModelTypeVersion_v<T>;
|
||||
}
|
||||
|
||||
constexpr T &obj() noexcept override {
|
||||
return m_obj;
|
||||
}
|
||||
|
||||
@ -44,7 +91,7 @@ constexpr ox::UPtr<Wrap> makeWrap(Args &&...args) noexcept {
|
||||
|
||||
template<typename T>
|
||||
constexpr T &wrapCast(Wrap &ptr) noexcept {
|
||||
return static_cast<WrapInline<T>&>(ptr).obj();
|
||||
return static_cast<WrapT<T>&>(ptr).obj();
|
||||
}
|
||||
|
||||
class BaseConverter {
|
||||
@ -70,10 +117,10 @@ class BaseConverter {
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool matches(
|
||||
ox::StringViewCR srcTypeName, int srcTypeVersion,
|
||||
ox::StringViewCR dstTypeName, int dstTypeVersion) const noexcept {
|
||||
ox::StringViewCR srcTypeName, int const srcTypeVersion,
|
||||
ox::StringViewCR dstTypeName, int const dstTypeVersion) const noexcept {
|
||||
return srcMatches(srcTypeName, srcTypeVersion)
|
||||
&& dstMatches(dstTypeName, dstTypeVersion);
|
||||
&& dstMatches(dstTypeName, dstTypeVersion);
|
||||
}
|
||||
|
||||
};
|
||||
@ -109,17 +156,17 @@ class Converter: public BaseConverter {
|
||||
|
||||
ox::Result<ox::UPtr<Wrap>> convertPtrToPtr(
|
||||
keel::Context &ctx, Wrap &src) const noexcept final {
|
||||
auto dst = makeWrap<DstType>();
|
||||
OX_RETURN_ERROR(convert(ctx, wrapCast<SrcType>(src), wrapCast<DstType>(*dst)));
|
||||
return {std::move(dst)};
|
||||
ox::Result<ox::UPtr<Wrap>> dst{makeWrap<DstType>()};
|
||||
OX_RETURN_ERROR(convert(ctx, wrapCast<SrcType>(src), wrapCast<DstType>(*dst.value)));
|
||||
return dst;
|
||||
}
|
||||
|
||||
ox::Result<ox::UPtr<Wrap>> convertBuffToPtr(
|
||||
keel::Context &ctx, ox::BufferView const&srcBuff) const noexcept final {
|
||||
OX_REQUIRE_M(src, readAsset<SrcType>(srcBuff));
|
||||
auto dst = makeWrap<DstType>();
|
||||
OX_RETURN_ERROR(convert(ctx, src, wrapCast<DstType>(*dst)));
|
||||
return {std::move(dst)};
|
||||
ox::Result<ox::UPtr<Wrap>> dst{makeWrap<DstType>()};
|
||||
OX_RETURN_ERROR(convert(ctx, src, wrapCast<DstType>(*dst.value)));
|
||||
return dst;
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -133,34 +180,68 @@ ox::Result<ox::UPtr<Wrap>> convert(
|
||||
ox::StringViewCR dstTypeName,
|
||||
int dstTypeVersion) noexcept;
|
||||
|
||||
ox::Result<ox::UPtr<Wrap>> convert(
|
||||
keel::Context &ctx,
|
||||
Wrap &src,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int dstTypeVersion) noexcept;
|
||||
|
||||
ox::Result<ox::UPtr<Wrap>> convert(
|
||||
keel::Context &ctx,
|
||||
auto &src,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int const dstTypeVersion) noexcept {
|
||||
WrapRef ref{src};
|
||||
return convert(ctx, static_cast<Wrap&>(ref), dstTypeName, dstTypeVersion);
|
||||
}
|
||||
|
||||
ox::Result<ox::UPtr<Wrap>> convert(
|
||||
keel::Context &ctx,
|
||||
auto const&src,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int const dstTypeVersion) noexcept {
|
||||
auto srcCpy = src;
|
||||
WrapRef ref{srcCpy};
|
||||
return convert(ctx, ref, dstTypeName, dstTypeVersion);
|
||||
}
|
||||
|
||||
template<typename DstType>
|
||||
ox::Result<DstType> convert(keel::Context &ctx, ox::BufferView const&srcBuffer) noexcept {
|
||||
static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
|
||||
static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
|
||||
OX_REQUIRE(out, convert(ctx, srcBuffer, DstTypeName, DstTypeVersion));
|
||||
ox::Result<DstType> convertObjToObj(
|
||||
keel::Context &ctx,
|
||||
auto &src) noexcept {
|
||||
OX_REQUIRE_M(out, convert(ctx, WrapRef{src}, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>));
|
||||
return std::move(wrapCast(*out));
|
||||
}
|
||||
|
||||
template<typename DstType>
|
||||
ox::Result<DstType> convert(keel::Context &ctx, ox::BufferView const&src) noexcept {
|
||||
OX_REQUIRE(out, convert(ctx, src, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>));
|
||||
return std::move(wrapCast<DstType>(out));
|
||||
}
|
||||
|
||||
template<typename DstType>
|
||||
ox::Error convert(keel::Context &ctx, ox::BufferView const&buff, DstType *outObj) noexcept {
|
||||
static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
|
||||
static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
|
||||
OX_REQUIRE(outPtr, convert(ctx, buff, DstTypeName, DstTypeVersion));
|
||||
*outObj = std::move(wrapCast<DstType>(*outPtr));
|
||||
ox::Error convert(keel::Context &ctx, ox::BufferView const&buff, DstType &outObj) noexcept {
|
||||
OX_REQUIRE(out, convert(ctx, buff, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>));
|
||||
outObj = std::move(wrapCast<DstType>(*out));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename DstType>
|
||||
ox::Error convertObjToObj(keel::Context &ctx, auto &src, DstType &outObj) noexcept {
|
||||
OX_REQUIRE(outPtr, convert(ctx, src, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>));
|
||||
outObj = std::move(wrapCast<DstType>(*outPtr));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename DstType>
|
||||
ox::Result<ox::Buffer> convertBuffToBuff(
|
||||
keel::Context &ctx, ox::BufferView const&srcBuffer, ox::ClawFormat fmt) noexcept {
|
||||
static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
|
||||
static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
|
||||
OX_REQUIRE(out, convert(ctx, srcBuffer, DstTypeName, DstTypeVersion));
|
||||
keel::Context &ctx, ox::BufferView const&src, ox::ClawFormat const fmt) noexcept {
|
||||
OX_REQUIRE(out, convert(ctx, src, ox::ModelTypeName_v<DstType>, ox::ModelTypeVersion_v<DstType>));
|
||||
return ox::writeClaw<DstType>(wrapCast<DstType>(*out), fmt);
|
||||
}
|
||||
|
||||
template<typename From, typename To, ox::ClawFormat fmt = ox::ClawFormat::Metal>
|
||||
ox::Result<bool> transformRule(keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) noexcept {
|
||||
ox::Result<bool> transformRule(keel::Context &ctx, ox::Buffer &buff, ox::StringViewCR typeId) noexcept {
|
||||
if (typeId == ox::ModelTypeId_v<From>) {
|
||||
OX_RETURN_ERROR(keel::convertBuffToBuff<To>(ctx, buff, fmt).moveTo(buff));
|
||||
return true;
|
||||
@ -168,5 +249,4 @@ ox::Result<bool> transformRule(keel::Context &ctx, ox::Buffer &buff, ox::StringV
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
@ -10,30 +10,38 @@ namespace keel {
|
||||
static ox::Result<BaseConverter const*> findConverter(
|
||||
ox::SpanView<BaseConverter const*> const&converters,
|
||||
ox::StringViewCR srcTypeName,
|
||||
int srcTypeVersion,
|
||||
int const srcTypeVersion,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int dstTypeVersion) noexcept {
|
||||
int const dstTypeVersion) noexcept {
|
||||
for (auto const&c : converters) {
|
||||
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return ox::Error(1, "Could not find converter");
|
||||
return ox::Error{1, "Could not find converter"};
|
||||
};
|
||||
|
||||
static ox::Result<ox::UPtr<Wrap>> convert(BaseConverter const&c, Context &ctx, ox::BufferView const&src) noexcept {
|
||||
return c.convertBuffToPtr(ctx, src);
|
||||
}
|
||||
|
||||
static ox::Result<ox::UPtr<Wrap>> convert(BaseConverter const&c, Context &ctx, Wrap &src) noexcept {
|
||||
return c.convertPtrToPtr(ctx, src);
|
||||
}
|
||||
|
||||
static ox::Result<ox::UPtr<Wrap>> convert(
|
||||
keel::Context &ctx,
|
||||
Context &ctx,
|
||||
ox::SpanView<BaseConverter const*> const&converters,
|
||||
ox::BufferView const&srcBuffer,
|
||||
auto &src,
|
||||
ox::StringViewCR srcTypeName,
|
||||
int srcTypeVersion,
|
||||
int const srcTypeVersion,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int dstTypeVersion) noexcept {
|
||||
int const dstTypeVersion) noexcept {
|
||||
// look for direct converter
|
||||
auto [c, err] = findConverter(
|
||||
auto const [c, err] = findConverter(
|
||||
converters, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
|
||||
if (!err) {
|
||||
return c->convertBuffToPtr(ctx, srcBuffer);
|
||||
return convert(*c, ctx, src);
|
||||
}
|
||||
// try to chain multiple converters
|
||||
for (auto const&subConverter : converters) {
|
||||
@ -41,20 +49,20 @@ static ox::Result<ox::UPtr<Wrap>> convert(
|
||||
continue;
|
||||
}
|
||||
const auto [intermediate, chainErr] =
|
||||
convert(ctx, converters, srcBuffer, srcTypeName, srcTypeVersion,
|
||||
convert(ctx, converters, src, srcTypeName, srcTypeVersion,
|
||||
subConverter->srcTypeName(), subConverter->srcTypeVersion());
|
||||
if (!chainErr) {
|
||||
return subConverter->convertPtrToPtr(ctx, *intermediate);
|
||||
}
|
||||
}
|
||||
return ox::Error(1, "Could not convert between types");
|
||||
return ox::Error{1, "Could not convert between types"};
|
||||
}
|
||||
|
||||
ox::Result<ox::UPtr<Wrap>> convert(
|
||||
keel::Context &ctx,
|
||||
Context &ctx,
|
||||
ox::BufferView const&srcBuffer,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int dstTypeVersion) noexcept {
|
||||
int const dstTypeVersion) noexcept {
|
||||
OX_REQUIRE(hdr, readAssetHeader(srcBuffer));
|
||||
return convert(
|
||||
ctx,
|
||||
@ -66,4 +74,19 @@ ox::Result<ox::UPtr<Wrap>> convert(
|
||||
dstTypeVersion);
|
||||
}
|
||||
|
||||
ox::Result<ox::UPtr<Wrap>> convert(
|
||||
Context &ctx,
|
||||
Wrap &src,
|
||||
ox::StringViewCR dstTypeName,
|
||||
int const dstTypeVersion) noexcept {
|
||||
return convert(
|
||||
ctx,
|
||||
converters(ctx),
|
||||
src,
|
||||
src.typeName(),
|
||||
src.typeVersion(),
|
||||
dstTypeName,
|
||||
dstTypeVersion);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace studio {
|
||||
|
||||
NewMenu::NewMenu() noexcept {
|
||||
setTitle("New Item");
|
||||
setSize({230, 140});
|
||||
setSize({280, 180});
|
||||
}
|
||||
|
||||
void NewMenu::open() noexcept {
|
||||
@ -33,7 +33,7 @@ bool NewMenu::isOpen() const noexcept {
|
||||
return m_open;
|
||||
}
|
||||
|
||||
void NewMenu::draw(studio::StudioContext &sctx) noexcept {
|
||||
void NewMenu::draw(StudioContext &sctx) noexcept {
|
||||
switch (m_stage) {
|
||||
case Stage::Opening:
|
||||
ImGui::OpenPopup(title().c_str());
|
||||
@ -46,51 +46,73 @@ void NewMenu::draw(studio::StudioContext &sctx) noexcept {
|
||||
case Stage::NewItemName:
|
||||
drawNewItemName(sctx);
|
||||
break;
|
||||
case Stage::NewItemTemplate:
|
||||
drawNewItemTemplate(sctx);
|
||||
break;
|
||||
case Stage::Closed:
|
||||
m_open = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::addItemMaker(ox::UniquePtr<studio::ItemMaker> &&im) noexcept {
|
||||
void NewMenu::addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept {
|
||||
m_types.emplace_back(std::move(im));
|
||||
std::sort(
|
||||
m_types.begin(), m_types.end(),
|
||||
[](ox::UPtr<ItemMaker> const&im1, ox::UPtr<ItemMaker> const&im2) {
|
||||
return im1->typeName < im2->typeName;
|
||||
return im1->typeDisplayName() < im2->typeDisplayName();
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::drawNewItemType(studio::StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, &m_open, [this] {
|
||||
auto const allocSz = m_types.size() * sizeof(char const*);
|
||||
auto mem = ox_malloca(allocSz, char const*, nullptr);
|
||||
auto items = ox::Span{mem.get(), allocSz};
|
||||
for (auto i = 0u; auto const&im : m_types) {
|
||||
items[i] = im->typeName.c_str();
|
||||
++i;
|
||||
void NewMenu::installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept {
|
||||
for (auto const&im : m_types) {
|
||||
if (im->installTemplate(tmplt)) {
|
||||
break;
|
||||
}
|
||||
ImGui::ListBox("Item Type", &m_selectedType, items.data(), static_cast<int>(m_types.size()));
|
||||
drawFirstPageButtons();
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::drawNewItemType(StudioContext const&sctx) noexcept {
|
||||
drawWindow(sctx.tctx, m_open, [this] {
|
||||
ig::ListBox("Item Type", [&](size_t const i) -> ox::CStringView {
|
||||
return m_types[i]->typeDisplayName();
|
||||
}, m_types.size(), m_selectedType, {200, 100});
|
||||
auto const&im = *m_types[m_selectedType];
|
||||
drawFirstPageButtons(im.itemTemplates().size() == 1 ?
|
||||
Stage::NewItemName : Stage::NewItemTemplate);
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::drawNewItemName(studio::StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, &m_open, [this, &sctx] {
|
||||
auto const typeIdx = static_cast<std::size_t>(m_selectedType);
|
||||
if (typeIdx < m_types.size()) {
|
||||
void NewMenu::drawNewItemTemplate(StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, m_open, [this] {
|
||||
auto const&templates =
|
||||
m_types[m_selectedType]->itemTemplates();
|
||||
ig::ListBox("Template", [&](size_t const i) -> ox::CStringView {
|
||||
return templates[i]->name();
|
||||
}, templates.size(), m_selectedTemplate, {200, 100});
|
||||
drawButtons(Stage::NewItemType, Stage::NewItemName);
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::drawNewItemName(StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, m_open, [this, &sctx] {
|
||||
if (m_selectedType < m_types.size()) {
|
||||
ig::InputText("Name", m_itemName);
|
||||
}
|
||||
drawLastPageButtons(sctx);
|
||||
});
|
||||
}
|
||||
|
||||
void NewMenu::drawFirstPageButtons() noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
|
||||
void NewMenu::drawButtons(Stage const prev, Stage const next) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
||||
auto const btnSz = ImVec2(60, 20);
|
||||
constexpr ImVec2 btnSz{60, 20};
|
||||
if (ImGui::Button("Back", btnSz)) {
|
||||
m_stage = prev;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Next", btnSz)) {
|
||||
m_stage = Stage::NewItemName;
|
||||
m_stage = next;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel", btnSz)) {
|
||||
@ -99,34 +121,50 @@ void NewMenu::drawFirstPageButtons() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::drawLastPageButtons(studio::StudioContext &sctx) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 138);
|
||||
void NewMenu::drawFirstPageButtons(Stage const next) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
||||
if (ImGui::Button("Back")) {
|
||||
m_stage = Stage::NewItemType;
|
||||
constexpr ImVec2 btnSz{60, 20};
|
||||
if (ImGui::Button("Next", btnSz)) {
|
||||
m_stage = next;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Finish")) {
|
||||
finish(sctx);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Quit")) {
|
||||
if (ImGui::Button("Cancel", btnSz)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_stage = Stage::Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::finish(studio::StudioContext &sctx) noexcept {
|
||||
void NewMenu::drawLastPageButtons(StudioContext &sctx) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
||||
constexpr ImVec2 btnSz{60, 20};
|
||||
if (ImGui::Button("Back", btnSz)) {
|
||||
m_stage = Stage::NewItemType;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Finish", btnSz)) {
|
||||
finish(sctx);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Quit", btnSz)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_stage = Stage::Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void NewMenu::finish(StudioContext &sctx) noexcept {
|
||||
if (m_itemName.len() == 0) {
|
||||
oxLogError(ox::Error(1, "New file error: no file name"));
|
||||
oxLogError(ox::Error{1, "New file error: no file name"});
|
||||
return;
|
||||
}
|
||||
auto const&typeMaker = *m_types[static_cast<std::size_t>(m_selectedType)];
|
||||
if (sctx.project->exists(typeMaker.itemPath(m_itemName))) {
|
||||
oxLogError(ox::Error(1, "New file error: file already exists"));
|
||||
auto const&im = *m_types[static_cast<std::size_t>(m_selectedType)];
|
||||
if (sctx.project->exists(im.itemPath(m_itemName))) {
|
||||
oxLogError(ox::Error{1, "New file error: file already exists"});
|
||||
return;
|
||||
}
|
||||
auto const [path, err] = typeMaker.write(sctx, m_itemName);
|
||||
auto const [path, err] =
|
||||
im.write(sctx, m_itemName, m_selectedTemplate);
|
||||
if (err) {
|
||||
oxLogError(err);
|
||||
return;
|
||||
|
@ -13,13 +13,14 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
class NewMenu: public studio::Popup {
|
||||
class NewMenu final: public Popup {
|
||||
public:
|
||||
enum class Stage {
|
||||
Closed,
|
||||
Opening,
|
||||
NewItemType,
|
||||
NewItemName,
|
||||
NewItemTemplate,
|
||||
};
|
||||
|
||||
// emits path parameter
|
||||
@ -29,8 +30,9 @@ class NewMenu: public studio::Popup {
|
||||
Stage m_stage = Stage::Closed;
|
||||
ox::String m_typeName;
|
||||
ox::IString<255> m_itemName;
|
||||
ox::Vector<ox::UniquePtr<studio::ItemMaker>> m_types;
|
||||
int m_selectedType = 0;
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> m_types;
|
||||
size_t m_selectedType = 0;
|
||||
size_t m_selectedTemplate = 0;
|
||||
bool m_open = false;
|
||||
|
||||
public:
|
||||
@ -43,37 +45,70 @@ class NewMenu: public studio::Popup {
|
||||
[[nodiscard]]
|
||||
bool isOpen() const noexcept override;
|
||||
|
||||
void draw(studio::StudioContext &sctx) noexcept override;
|
||||
void draw(StudioContext &sctx) noexcept override;
|
||||
|
||||
template<typename T>
|
||||
void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
|
||||
void addItemType(
|
||||
ox::StringParam displayName,
|
||||
ox::StringParam parentDir,
|
||||
ox::StringParam fileExt,
|
||||
T itemTempl,
|
||||
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
|
||||
|
||||
template<typename T>
|
||||
void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
|
||||
void addItemType(
|
||||
ox::StringParam displayName,
|
||||
ox::StringParam parentDir,
|
||||
ox::StringParam fileExt,
|
||||
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
|
||||
|
||||
void addItemMaker(ox::UniquePtr<studio::ItemMaker> &&im) noexcept;
|
||||
void addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept;
|
||||
|
||||
void installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept;
|
||||
|
||||
private:
|
||||
void drawNewItemType(studio::StudioContext &sctx) noexcept;
|
||||
void drawNewItemType(StudioContext const&sctx) noexcept;
|
||||
|
||||
void drawNewItemName(studio::StudioContext &sctx) noexcept;
|
||||
void drawNewItemName(StudioContext &sctx) noexcept;
|
||||
|
||||
void drawFirstPageButtons() noexcept;
|
||||
void drawNewItemTemplate(StudioContext &sctx) noexcept;
|
||||
|
||||
void drawLastPageButtons(studio::StudioContext &sctx) noexcept;
|
||||
void drawButtons(Stage prev, Stage next) noexcept;
|
||||
|
||||
void finish(studio::StudioContext &sctx) noexcept;
|
||||
void drawFirstPageButtons(Stage next) noexcept;
|
||||
|
||||
void drawLastPageButtons(StudioContext &sctx) noexcept;
|
||||
|
||||
void finish(StudioContext &sctx) noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void NewMenu::addItemType(ox::String displayName, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt) noexcept {
|
||||
m_types.emplace_back(ox::make<studio::ItemMakerT<T>>(std::move(displayName), std::move(parentDir), std::move(fileExt), std::move(itemTempl), pFmt));
|
||||
void NewMenu::addItemType(
|
||||
ox::StringParam displayName,
|
||||
ox::StringParam parentDir,
|
||||
ox::StringParam fileExt,
|
||||
T itemTempl,
|
||||
ox::ClawFormat const pFmt) noexcept {
|
||||
m_types.emplace_back(ox::make<ItemMakerT<T>>(
|
||||
std::move(displayName),
|
||||
std::move(parentDir),
|
||||
std::move(fileExt),
|
||||
std::move(itemTempl),
|
||||
pFmt));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void NewMenu::addItemType(ox::String displayName, ox::String parentDir, ox::String fileExt, ox::ClawFormat pFmt) noexcept {
|
||||
m_types.emplace_back(ox::make<studio::ItemMakerT<T>>(std::move(displayName), std::move(parentDir), std::move(fileExt), pFmt));
|
||||
void NewMenu::addItemType(
|
||||
ox::StringParam displayName,
|
||||
ox::StringParam parentDir,
|
||||
ox::StringParam fileExt,
|
||||
ox::ClawFormat const pFmt) noexcept {
|
||||
m_types.emplace_back(ox::make<ItemMakerT<T>>(
|
||||
std::move(displayName),
|
||||
std::move(parentDir),
|
||||
std::move(fileExt),
|
||||
pFmt));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,8 +49,8 @@ void NewProject::draw(studio::StudioContext &ctx) noexcept {
|
||||
}
|
||||
|
||||
void NewProject::drawNewProjectName(studio::StudioContext &sctx) noexcept {
|
||||
drawWindow(sctx.tctx, &m_open, [this, &sctx] {
|
||||
ImGui::InputText("Name", m_projectName.data(), m_projectName.cap());
|
||||
drawWindow(sctx.tctx, m_open, [this, &sctx] {
|
||||
ig::InputText("Name", m_projectName);
|
||||
ImGui::Text("Path: %s", m_projectPath.c_str());
|
||||
if (ImGui::Button("Browse")) {
|
||||
oxLogError(studio::chooseDirectory().moveTo(m_projectPath));
|
||||
|
@ -18,9 +18,9 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
static ox::Vector<studio::Module const*> modules;
|
||||
static ox::Vector<Module const*> modules;
|
||||
|
||||
void registerModule(studio::Module const*mod) noexcept {
|
||||
void registerModule(Module const*mod) noexcept {
|
||||
if (mod) {
|
||||
modules.emplace_back(mod);
|
||||
}
|
||||
@ -45,25 +45,25 @@ OX_MODEL_END()
|
||||
|
||||
StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept:
|
||||
m_sctx(*this, ctx),
|
||||
m_ctx(ctx),
|
||||
m_tctx(ctx),
|
||||
m_projectDataDir(std::move(projectDataDir)),
|
||||
m_projectExplorer(m_ctx),
|
||||
m_projectExplorer(m_tctx),
|
||||
m_newProject(m_projectDataDir),
|
||||
m_aboutPopup(m_ctx) {
|
||||
turbine::setApplicationData(m_ctx, &m_sctx);
|
||||
m_aboutPopup(m_tctx) {
|
||||
turbine::setApplicationData(m_tctx, &m_sctx);
|
||||
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
||||
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
||||
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
loadModules();
|
||||
// open project and files
|
||||
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_ctx));
|
||||
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx));
|
||||
m_showProjectExplorer = config.showProjectExplorer;
|
||||
if (!err) {
|
||||
auto const openProjErr = openProjectPath(config.projectPath);
|
||||
if (!openProjErr) {
|
||||
for (auto const&f: config.openFiles) {
|
||||
auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
|
||||
auto const openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
|
||||
if (openFileErr) {
|
||||
oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
|
||||
continue;
|
||||
@ -77,13 +77,13 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
||||
} else {
|
||||
oxErrf(
|
||||
"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 {
|
||||
for (auto p : m_popups) {
|
||||
void StudioUI::handleKeyEvent(turbine::Key const key, bool const down) noexcept {
|
||||
for (auto const p : m_popups) {
|
||||
if (p->isOpen()) {
|
||||
if (key == turbine::Key::Escape) {
|
||||
p->close();
|
||||
@ -118,13 +118,13 @@ void StudioUI::draw() noexcept {
|
||||
for (auto &w: m_widgets) {
|
||||
w->draw(m_sctx);
|
||||
}
|
||||
for (auto p: m_popups) {
|
||||
for (auto const p: m_popups) {
|
||||
p->draw(m_sctx);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
handleKeyInput();
|
||||
m_taskRunner.update(m_ctx);
|
||||
m_taskRunner.update(m_tctx);
|
||||
}
|
||||
|
||||
void StudioUI::drawMenu() noexcept {
|
||||
@ -143,7 +143,7 @@ void StudioUI::drawMenu() noexcept {
|
||||
m_activeEditor->save();
|
||||
}
|
||||
if (ImGui::MenuItem("Quit", "Ctrl+Q")) {
|
||||
turbine::requestShutdown(m_ctx);
|
||||
turbine::requestShutdown(m_tctx);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
@ -168,7 +168,7 @@ void StudioUI::drawMenu() noexcept {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
if (ImGui::MenuItem("Project Explorer", "Ctrl+1", m_showProjectExplorer)) {
|
||||
if (ImGui::MenuItem("Project Explorer", "Ctrl+Shift+1", m_showProjectExplorer)) {
|
||||
toggleProjectExplorer();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
@ -204,9 +204,10 @@ void StudioUI::drawTabs() noexcept {
|
||||
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open, flags)) {
|
||||
if (m_activeEditor != e.get()) [[unlikely]] {
|
||||
m_activeEditor = e.get();
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
||||
config.activeTabItemName = m_activeEditor->itemPath();
|
||||
});
|
||||
turbine::setRefreshWithin(m_tctx, 0);
|
||||
} else [[likely]] {
|
||||
if (m_activeEditorUpdatePending == e.get()) [[unlikely]] {
|
||||
m_activeEditorUpdatePending = nullptr;
|
||||
@ -227,7 +228,7 @@ void StudioUI::drawTabs() noexcept {
|
||||
try {
|
||||
OX_THROW_ERROR(m_editors.erase(it).moveTo(it));
|
||||
} 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) {
|
||||
oxErrf("Editor tab deletion failed: {}\n", ex.what());
|
||||
}
|
||||
@ -237,19 +238,23 @@ void StudioUI::drawTabs() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::loadEditorMaker(studio::EditorMaker const&editorMaker) noexcept {
|
||||
void StudioUI::loadEditorMaker(EditorMaker const&editorMaker) noexcept {
|
||||
for (auto const&ext : editorMaker.fileTypes) {
|
||||
m_editorMakers[ext] = editorMaker.make;
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::loadModule(studio::Module const&mod) noexcept {
|
||||
void StudioUI::loadModule(Module const&mod) noexcept {
|
||||
for (auto const&editorMaker : mod.editors(m_sctx)) {
|
||||
loadEditorMaker(editorMaker);
|
||||
}
|
||||
for (auto &im : mod.itemMakers(m_sctx)) {
|
||||
m_newMenu.addItemMaker(std::move(im));
|
||||
}
|
||||
auto tmplts = mod.itemTemplates(m_sctx);
|
||||
for (auto &t : tmplts) {
|
||||
m_newMenu.installItemTemplate(t);
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::loadModules() noexcept {
|
||||
@ -260,20 +265,20 @@ void StudioUI::loadModules() noexcept {
|
||||
|
||||
void StudioUI::toggleProjectExplorer() noexcept {
|
||||
m_showProjectExplorer = !m_showProjectExplorer;
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
|
||||
editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
||||
config.showProjectExplorer = m_showProjectExplorer;
|
||||
});
|
||||
}
|
||||
|
||||
void StudioUI::redo() noexcept {
|
||||
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||
if (undoStack && undoStack->canRedo()) {
|
||||
oxLogError(m_activeEditor->undoStack()->redo());
|
||||
}
|
||||
}
|
||||
|
||||
void StudioUI::undo() noexcept {
|
||||
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||
if (undoStack && undoStack->canUndo()) {
|
||||
oxLogError(m_activeEditor->undoStack()->undo());
|
||||
}
|
||||
@ -292,7 +297,7 @@ void StudioUI::handleKeyInput() noexcept {
|
||||
m_activeEditor->copy();
|
||||
}
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_N)) {
|
||||
if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Shift)) {
|
||||
if (turbine::buttonDown(m_tctx, turbine::Key::Mod_Shift)) {
|
||||
m_newProject.open();
|
||||
} else {
|
||||
m_newMenu.open();
|
||||
@ -302,7 +307,7 @@ void StudioUI::handleKeyInput() noexcept {
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_S)) {
|
||||
save();
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_Q)) {
|
||||
turbine::requestShutdown(m_ctx);
|
||||
turbine::requestShutdown(m_tctx);
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_V)) {
|
||||
if (m_activeEditor && m_activeEditor->pasteEnabled()) {
|
||||
m_activeEditor->paste();
|
||||
@ -317,8 +322,22 @@ void StudioUI::handleKeyInput() noexcept {
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
|
||||
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||
if (undoStack) { oxLogError(undoStack->undo()); }
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_1)) {
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_1) && ImGui::IsKeyDown(ImGuiKey_ModShift)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -333,18 +352,18 @@ ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept {
|
||||
|
||||
ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
||||
OX_REQUIRE_M(fs, keel::loadRomFs(path.view()));
|
||||
OX_RETURN_ERROR(keel::setRomFs(keelCtx(m_ctx), std::move(fs)));
|
||||
OX_RETURN_ERROR(keel::setRomFs(keelCtx(m_tctx), std::move(fs)));
|
||||
OX_RETURN_ERROR(
|
||||
ox::make_unique_catch<studio::Project>(keelCtx(m_ctx), std::move(path), m_projectDataDir)
|
||||
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
||||
.moveTo(m_project));
|
||||
auto const sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||
auto const sctx = applicationData<StudioContext>(m_tctx);
|
||||
sctx->project = m_project.get();
|
||||
turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, m_project->projectPath()));
|
||||
turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
||||
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||
m_openFiles.clear();
|
||||
m_editors.clear();
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
||||
config.projectPath = ox::String(m_project->projectPath());
|
||||
config.openFiles.clear();
|
||||
});
|
||||
@ -355,7 +374,7 @@ ox::Error StudioUI::openFile(ox::StringViewCR path) noexcept {
|
||||
return openFileActiveTab(path, true);
|
||||
}
|
||||
|
||||
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept {
|
||||
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept {
|
||||
if (!m_project) {
|
||||
return ox::Error(1, "No project open to open a file from");
|
||||
}
|
||||
@ -369,7 +388,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab)
|
||||
}
|
||||
return {};
|
||||
}
|
||||
OX_REQUIRE(ext, studio::fileExt(path));
|
||||
OX_REQUIRE(ext, fileExt(path));
|
||||
// create Editor
|
||||
BaseEditor *editor = nullptr;
|
||||
auto const err = m_editorMakers.contains(ext) ?
|
||||
@ -379,7 +398,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab)
|
||||
if constexpr(!ox::defines::Debug) {
|
||||
oxErrf("Could not open Editor: {}\n", toStr(err));
|
||||
} 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;
|
||||
}
|
||||
@ -391,7 +410,7 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool makeActiveTab)
|
||||
m_activeEditorUpdatePending = editor;
|
||||
}
|
||||
// save to config
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&path](StudioConfig &config) {
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&path](StudioConfig &config) {
|
||||
if (!config.openFiles.contains(path)) {
|
||||
config.openFiles.emplace_back(path);
|
||||
}
|
||||
@ -405,7 +424,7 @@ ox::Error StudioUI::closeFile(ox::StringViewCR path) noexcept {
|
||||
}
|
||||
std::ignore = m_openFiles.erase(std::remove(m_openFiles.begin(), m_openFiles.end(), path));
|
||||
// save to config
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig &config) {
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
||||
std::ignore = config.openFiles.erase(std::remove(config.openFiles.begin(), config.openFiles.end(), path));
|
||||
});
|
||||
return {};
|
||||
|
@ -24,23 +24,23 @@ class StudioUI: public ox::SignalHandler {
|
||||
friend class StudioUIDrawer;
|
||||
|
||||
private:
|
||||
studio::StudioContext m_sctx;
|
||||
turbine::Context &m_ctx;
|
||||
StudioContext m_sctx;
|
||||
turbine::Context &m_tctx;
|
||||
ox::String m_projectDataDir;
|
||||
ox::UPtr<studio::Project> m_project;
|
||||
studio::TaskRunner m_taskRunner;
|
||||
ox::Vector<ox::UPtr<studio::BaseEditor>> m_editors;
|
||||
ox::Vector<ox::UPtr<studio::Widget>> m_widgets;
|
||||
ox::HashMap<ox::String, studio::EditorMaker::Func> m_editorMakers;
|
||||
ox::UPtr<Project> m_project;
|
||||
TaskRunner m_taskRunner;
|
||||
ox::Vector<ox::UPtr<BaseEditor>> m_editors;
|
||||
ox::Vector<ox::UPtr<Widget>> m_widgets;
|
||||
ox::HashMap<ox::String, EditorMaker::Func> m_editorMakers;
|
||||
ProjectExplorer m_projectExplorer;
|
||||
ox::Vector<ox::String> m_openFiles;
|
||||
studio::BaseEditor *m_activeEditorOnLastDraw = nullptr;
|
||||
studio::BaseEditor *m_activeEditor = nullptr;
|
||||
studio::BaseEditor *m_activeEditorUpdatePending = nullptr;
|
||||
BaseEditor *m_activeEditorOnLastDraw = nullptr;
|
||||
BaseEditor *m_activeEditor = nullptr;
|
||||
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
||||
NewMenu m_newMenu;
|
||||
NewProject m_newProject;
|
||||
AboutPopup m_aboutPopup;
|
||||
ox::Array<studio::Popup*, 3> const m_popups = {
|
||||
ox::Array<Popup*, 3> const m_popups = {
|
||||
&m_newMenu,
|
||||
&m_newProject,
|
||||
&m_aboutPopup
|
||||
@ -53,7 +53,7 @@ class StudioUI: public ox::SignalHandler {
|
||||
void handleKeyEvent(turbine::Key, bool down) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr studio::Project *project() noexcept {
|
||||
constexpr Project *project() noexcept {
|
||||
return m_project.get();
|
||||
}
|
||||
|
||||
@ -67,9 +67,9 @@ class StudioUI: public ox::SignalHandler {
|
||||
|
||||
void drawTabs() noexcept;
|
||||
|
||||
void loadEditorMaker(studio::EditorMaker const&editorMaker) noexcept;
|
||||
void loadEditorMaker(EditorMaker const&editorMaker) noexcept;
|
||||
|
||||
void loadModule(studio::Module const&mod) noexcept;
|
||||
void loadModule(Module const&mod) noexcept;
|
||||
|
||||
void loadModules() noexcept;
|
||||
|
||||
|
@ -214,7 +214,8 @@ bool ListBox(
|
||||
ox::CStringViewCR name,
|
||||
std::function<ox::CStringView(size_t)> const&f,
|
||||
size_t strCnt,
|
||||
size_t &selIdx) noexcept;
|
||||
size_t &selIdx,
|
||||
ImVec2 const&sz = {0, 0}) noexcept;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ox/model/typenamecatcher.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/context.hpp>
|
||||
@ -13,75 +15,212 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
class ItemMaker {
|
||||
class ItemTemplate {
|
||||
private:
|
||||
ox::String const m_name{"Default"};
|
||||
|
||||
public:
|
||||
ox::String const typeName;
|
||||
ox::String const parentDir;
|
||||
ox::String const fileExt;
|
||||
constexpr explicit ItemMaker(
|
||||
explicit ItemTemplate() noexcept = default;
|
||||
|
||||
explicit ItemTemplate(ox::StringParam name) noexcept: m_name{std::move(name)} {}
|
||||
|
||||
virtual ~ItemTemplate() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::String const&name() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
constexpr bool operator<=>(ItemTemplate const&other) const noexcept {
|
||||
return m_name != other.name() ? (m_name < other.name() ? -1 : 1) : 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::CStringView displayName() const noexcept {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::CStringView typeName() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual int typeVersion() const noexcept = 0;
|
||||
|
||||
virtual ox::Result<ox::UAnyPtr> getItem(
|
||||
keel::Context &ctx, ox::StringViewCR name, int version) noexcept = 0;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ItemTemplateT: public ItemTemplate {
|
||||
private:
|
||||
T const m_item;
|
||||
|
||||
public:
|
||||
constexpr ItemTemplateT() noexcept = default;
|
||||
|
||||
explicit ItemTemplateT(ox::StringParam name, T item) noexcept:
|
||||
ItemTemplate{std::move(name)}, m_item{std::move(item)} {}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::CStringView typeName() const noexcept final {
|
||||
return ox::ModelTypeName_v<T>;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int typeVersion() const noexcept final {
|
||||
return ox::ModelTypeVersion_v<T>;
|
||||
}
|
||||
|
||||
ox::Result<ox::UAnyPtr> getItem(
|
||||
keel::Context &kctx, ox::StringViewCR name, int const version) noexcept final {
|
||||
if (ox::ModelTypeVersion_v<T> != version || ox::ModelTypeName_v<T> != name) {
|
||||
OX_REQUIRE_M(item, keel::convert(kctx, m_item, name, version));
|
||||
auto out = ox::Result<ox::UAnyPtr>{item->moveToCopy()};
|
||||
ox::safeDelete(item.release());
|
||||
return out;
|
||||
}
|
||||
return ox::UAnyPtr{new T{m_item}};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class ItemMaker {
|
||||
private:
|
||||
ox::Vector<ox::UPtr<ItemTemplate>> m_templates;
|
||||
ox::String const m_parentDir;
|
||||
ox::String const m_fileExt;
|
||||
ox::String const m_typeDisplayName;
|
||||
|
||||
public:
|
||||
constexpr ItemMaker(
|
||||
ox::StringParam pName,
|
||||
ox::StringParam pParentDir,
|
||||
ox::StringParam pFileExt) noexcept:
|
||||
typeName{std::move(pName)},
|
||||
parentDir{std::move(pParentDir)},
|
||||
fileExt{std::move(pFileExt)} {
|
||||
m_parentDir{std::move(pParentDir)},
|
||||
m_fileExt{std::move(pFileExt)},
|
||||
m_typeDisplayName{std::move(pName)} {
|
||||
}
|
||||
|
||||
virtual ~ItemMaker() noexcept = default;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::String itemPath(ox::StringView pName) const noexcept {
|
||||
return ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
|
||||
ox::String const&typeDisplayName() const noexcept {
|
||||
return m_typeDisplayName;
|
||||
}
|
||||
|
||||
bool installTemplate(ox::UPtr<ItemTemplate> &tmpl) {
|
||||
if (typeName() == tmpl->typeName() &&
|
||||
typeVersion() <= tmpl->typeVersion()) {
|
||||
m_templates.emplace_back(std::move(tmpl));
|
||||
// begin() + 1 because 'Default' should always be first
|
||||
std::sort(m_templates.begin() + 1, m_templates.end());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
|
||||
return installTemplate(tmpl);
|
||||
}
|
||||
|
||||
constexpr ox::Vector<ox::UPtr<ItemTemplate>> const&itemTemplates() const noexcept {
|
||||
return m_templates;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::String itemPath(ox::StringViewCR pName) const noexcept {
|
||||
return ox::sfmt("/{}/{}.{}", m_parentDir, pName, m_fileExt);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::StringView typeName() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual int typeVersion() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Returns path of the file created.
|
||||
* @param ctx
|
||||
* @param pName
|
||||
* @param pTemplateIdx
|
||||
* @return path of file or error in Result
|
||||
*/
|
||||
virtual ox::Result<ox::String> write(StudioContext &ctx, ox::StringView pName) const noexcept = 0;
|
||||
virtual ox::Result<ox::String> write(
|
||||
StudioContext &ctx, ox::StringViewCR pName, size_t pTemplateIdx) const noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ItemMakerT: public ItemMaker {
|
||||
class ItemMakerT final: public ItemMaker {
|
||||
private:
|
||||
T const m_item;
|
||||
ox::ClawFormat const m_fmt;
|
||||
|
||||
public:
|
||||
constexpr ItemMakerT(
|
||||
ox::StringParam pDisplayName,
|
||||
ox::StringParam pParentDir,
|
||||
ox::StringParam fileExt,
|
||||
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept:
|
||||
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
|
||||
m_fmt(pFmt) {
|
||||
ox::ClawFormat const pFmt = ox::ClawFormat::Metal) noexcept:
|
||||
ItemMaker(
|
||||
std::move(pDisplayName),
|
||||
std::move(pParentDir),
|
||||
std::move(fileExt)),
|
||||
m_fmt{pFmt} {
|
||||
installTemplate(ox::make_unique<ItemTemplateT<T>>());
|
||||
}
|
||||
|
||||
constexpr ItemMakerT(
|
||||
ox::StringParam pDisplayName,
|
||||
ox::StringParam pParentDir,
|
||||
ox::StringParam fileExt,
|
||||
T pItem,
|
||||
ox::ClawFormat pFmt) noexcept:
|
||||
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
|
||||
m_item(std::move(pItem)),
|
||||
m_fmt(pFmt) {
|
||||
T const&pItem,
|
||||
ox::ClawFormat const pFmt) noexcept:
|
||||
ItemMaker(
|
||||
std::move(pDisplayName),
|
||||
std::move(pParentDir),
|
||||
std::move(fileExt)),
|
||||
m_fmt{pFmt} {
|
||||
installTemplate(ox::make_unique<ItemTemplateT<T>>(std::move(pItem)));
|
||||
}
|
||||
|
||||
constexpr ItemMakerT(
|
||||
ox::StringParam pDisplayName,
|
||||
ox::StringParam pParentDir,
|
||||
ox::StringParam fileExt,
|
||||
T &&pItem,
|
||||
ox::ClawFormat pFmt) noexcept:
|
||||
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)),
|
||||
m_item(std::move(pItem)),
|
||||
m_fmt(pFmt) {
|
||||
ox::ClawFormat const pFmt) noexcept:
|
||||
ItemMaker(
|
||||
std::move(pDisplayName),
|
||||
std::move(pParentDir),
|
||||
std::move(fileExt)),
|
||||
m_fmt{pFmt} {
|
||||
installTemplate(ox::make_unique<ItemTemplateT<T>>(std::move(pItem)));
|
||||
}
|
||||
ox::Result<ox::String> write(studio::StudioContext &sctx, ox::StringView const pName) const noexcept override {
|
||||
|
||||
ox::StringView typeName() const noexcept override {
|
||||
return ox::ModelTypeName_v<T>;
|
||||
}
|
||||
|
||||
int typeVersion() const noexcept override {
|
||||
return ox::ModelTypeVersion_v<T>;
|
||||
}
|
||||
|
||||
ox::Result<ox::String> write(
|
||||
StudioContext &sctx,
|
||||
ox::StringViewCR pName,
|
||||
size_t const pTemplateIdx) const noexcept override {
|
||||
auto const path = itemPath(pName);
|
||||
createUuidMapping(keelCtx(sctx.tctx), path, ox::UUID::generate().unwrap());
|
||||
OX_RETURN_ERROR(sctx.project->writeObj(path, m_item, m_fmt));
|
||||
auto const&templates = itemTemplates();
|
||||
auto const tmplIdx = pTemplateIdx < templates.size() ? pTemplateIdx : 0;
|
||||
OX_REQUIRE_M(tmpl, templates[tmplIdx]->getItem(
|
||||
keelCtx(sctx), typeName(), typeVersion()));
|
||||
auto item = tmpl.template get<T>();
|
||||
OX_RETURN_ERROR(sctx.project->writeObj(path, *item, m_fmt));
|
||||
tmpl.free();
|
||||
return path;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ class Module {
|
||||
|
||||
virtual ox::Vector<ox::UPtr<ItemMaker>> itemMakers(studio::StudioContext&) const;
|
||||
|
||||
virtual ox::Vector<ox::UPtr<ItemTemplate>> itemTemplates(studio::StudioContext&) const;
|
||||
|
||||
};
|
||||
|
||||
template<typename Editor>
|
||||
|
@ -47,7 +47,7 @@ class Popup {
|
||||
return m_title;
|
||||
}
|
||||
|
||||
void drawWindow(turbine::Context &ctx, bool *open, std::function<void()> const&drawContents);
|
||||
void drawWindow(turbine::Context &ctx, bool &open, std::function<void()> const&drawContents);
|
||||
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,7 @@ namespace studio {
|
||||
class NoChangesException: public ox::Exception {
|
||||
public:
|
||||
inline NoChangesException(std::source_location sloc = std::source_location::current()):
|
||||
ox::Exception(sloc.file_name(), sloc.line(), 1, "Command makes no changes.") {}
|
||||
ox::Exception(1, "Command makes no changes.", sloc) {}
|
||||
};
|
||||
|
||||
class UndoCommand {
|
||||
|
@ -46,7 +46,7 @@ void BaseEditor::save() noexcept {
|
||||
setUnsavedChanges(false);
|
||||
} else {
|
||||
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 {
|
||||
oxErrorf("Could not save file {}: {}", itemPath(), toStr(err));
|
||||
}
|
||||
|
@ -136,11 +136,12 @@ bool FileComboBox(
|
||||
bool ListBox(
|
||||
ox::CStringViewCR name,
|
||||
std::function<ox::CStringView(size_t)> const&f,
|
||||
size_t strCnt,
|
||||
size_t &selIdx) noexcept {
|
||||
size_t const strCnt,
|
||||
size_t &selIdx,
|
||||
ImVec2 const&sz) noexcept {
|
||||
auto out = false;
|
||||
if (ImGui::BeginListBox(name.c_str())) {
|
||||
for (auto i = 0u; i < strCnt; ++i) {
|
||||
if (ImGui::BeginListBox(name.c_str(), sz)) {
|
||||
for (size_t i = 0; i < strCnt; ++i) {
|
||||
auto str = f(i);
|
||||
ig::IDStackItem const idStackItem2(static_cast<int>(i));
|
||||
if (ImGui::Selectable(str.c_str(), selIdx == i)) {
|
||||
@ -161,6 +162,12 @@ bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t
|
||||
}, list.size(), selIdx);
|
||||
}
|
||||
|
||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const&list, size_t &selIdx) noexcept {
|
||||
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
||||
return list[i];
|
||||
}, list.size(), selIdx);
|
||||
}
|
||||
|
||||
|
||||
FilePicker::FilePicker(
|
||||
StudioContext &sctx,
|
||||
|
@ -6,11 +6,15 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
ox::Vector<EditorMaker> Module::editors(studio::StudioContext&) const {
|
||||
ox::Vector<EditorMaker> Module::editors(StudioContext&) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Vector<ox::UPtr<ItemMaker>> Module::itemMakers(studio::StudioContext&) const {
|
||||
ox::Vector<ox::UPtr<ItemMaker>> Module::itemMakers(StudioContext&) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Vector<ox::UPtr<ItemTemplate>> Module::itemTemplates(StudioContext&) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
void Popup::drawWindow(turbine::Context &ctx, bool *open, std::function<void()> const&drawContents) {
|
||||
void Popup::drawWindow(turbine::Context &ctx, bool &open, std::function<void()> const&drawContents) {
|
||||
studio::ig::centerNextWindow(ctx);
|
||||
ImGui::SetNextWindowSize(static_cast<ImVec2>(m_size));
|
||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||
if (ImGui::BeginPopupModal(m_title.c_str(), open, modalFlags)) {
|
||||
if (ImGui::BeginPopupModal(m_title.c_str(), &open, modalFlags)) {
|
||||
drawContents();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ target_include_directories(
|
||||
)
|
||||
|
||||
target_sources(
|
||||
Turbine PUBLIC
|
||||
Turbine PRIVATE
|
||||
turbine.cpp
|
||||
)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM fedora:36
|
||||
FROM fedora:41
|
||||
|
||||
RUN dnf update -y
|
||||
|
@ -28,11 +28,11 @@ project_name = sys.argv[2]
|
||||
bin = f'./build/{host_env}-{current_build}/bin/'
|
||||
project_bin = f'build/gba-release/bin/{project_name}.bin'
|
||||
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)
|
||||
subprocess.run([
|
||||
f'{bin}/{project_name}-pack',
|
||||
f'{bin}/{project_name.lower()}-pack',
|
||||
'-src', project_dir,
|
||||
'-rom-bin', project_gba,
|
||||
'-manifest', project_manifest])
|
Reference in New Issue
Block a user