Compare commits
36 Commits
4ddf7a88c9
...
58b7f813cc
Author | SHA1 | Date | |
---|---|---|---|
58b7f813cc | |||
762804905a | |||
b53e8626d7 | |||
71354fcbbc | |||
044a87b1c4 | |||
53229b05da | |||
d17f536832 | |||
b6ed919b01 | |||
777a6d54f0 | |||
86a3bf1248 | |||
a96d173fdc | |||
5e43eff631 | |||
ba7ee92ad2 | |||
6d4c57d37d | |||
54eebf81da | |||
1b7b6e306e | |||
2b821b73ff | |||
e19559d7a7 | |||
af3de01e1b | |||
9561a68483 | |||
08899074cf | |||
86f639c7f7 | |||
014daa6b57 | |||
4f906f6e47 | |||
3b05d4e16b | |||
dba31d2cd9 | |||
8077aaf0ae | |||
eb55144211 | |||
5de5eee215 | |||
d571d49cce | |||
6769bb63d9 | |||
b064239ab1 | |||
24ea7fee39 | |||
8a4ce3c8f1 | |||
3c9e6d10ea | |||
4b9b70a90e |
@ -2,4 +2,4 @@
|
|||||||
source:
|
source:
|
||||||
- src
|
- src
|
||||||
copyright_notice: |-
|
copyright_notice: |-
|
||||||
Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
16
.tracy.json
16
.tracy.json
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"init_functions": [
|
|
||||||
{
|
|
||||||
"bin_path": "dist/linux-x86_64-debug/lib/ox/libOxTraceHook.so",
|
|
||||||
"function": "oxTraceInitHook",
|
|
||||||
"ignore_frames": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"log_functions": [
|
|
||||||
{
|
|
||||||
"bin_path": "dist/linux-x86_64-debug/lib/ox/libOxTraceHook.so",
|
|
||||||
"function": "oxTraceHook",
|
|
||||||
"ignore_frames": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
8
deps/ox/src/ox/claw/read.cpp
vendored
8
deps/ox/src/ox/claw/read.cpp
vendored
@ -72,8 +72,8 @@ Result<Buffer> stripClawHeader(const ox::Buffer &buff) noexcept {
|
|||||||
return stripClawHeader(buff.data(), buff.size());
|
return stripClawHeader(buff.data(), buff.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept {
|
Result<ModelObject> readClaw(TypeStore *ts, const char *buff, std::size_t buffSz) noexcept {
|
||||||
oxRequire(header, readClawHeader(buff));
|
oxRequire(header, readClawHeader(buff, buffSz));
|
||||||
oxRequire(t, ts->getLoad(header.typeName, header.typeVersion, header.typeParams));
|
oxRequire(t, ts->getLoad(header.typeName, header.typeVersion, header.typeParams));
|
||||||
ModelObject obj;
|
ModelObject obj;
|
||||||
oxReturnError(obj.setType(t));
|
oxReturnError(obj.setType(t));
|
||||||
@ -102,4 +102,8 @@ Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept {
|
|||||||
return OxError(1);
|
return OxError(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept {
|
||||||
|
return readClaw(ts, buff.data(), buff.size());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
2
deps/ox/src/ox/claw/read.hpp
vendored
2
deps/ox/src/ox/claw/read.hpp
vendored
@ -87,6 +87,8 @@ Result<T> readClaw(const Buffer &buff) {
|
|||||||
return readClaw<T>(buff.data(), buff.size());
|
return readClaw<T>(buff.data(), buff.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<ModelObject> readClaw(TypeStore *ts, const char *buff, std::size_t buffSz) noexcept;
|
||||||
|
|
||||||
Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept;
|
Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ Result<FileStat> PassThroughFS::statPath(CRStringView path) const noexcept {
|
|||||||
const uint64_t size = type == FileType::Directory ? 0 : std::filesystem::file_size(p, ec);
|
const uint64_t size = type == FileType::Directory ? 0 : std::filesystem::file_size(p, ec);
|
||||||
oxTracef("ox::fs::PassThroughFS::statInode", "{} {}", ec.message(), path);
|
oxTracef("ox::fs::PassThroughFS::statInode", "{} {}", ec.message(), path);
|
||||||
oxTracef("ox::fs::PassThroughFS::statInode::size", "{} {}", path, size);
|
oxTracef("ox::fs::PassThroughFS::statInode::size", "{} {}", path, size);
|
||||||
oxReturnError(OxError(ec.value()));
|
oxReturnError(OxError(ec.value(), "PassThroughFS: stat failed"));
|
||||||
return FileStat{0, 0, size, type};
|
return FileStat{0, 0, size, type};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
deps/ox/src/ox/model/modelvalue.hpp
vendored
2
deps/ox/src/ox/model/modelvalue.hpp
vendored
@ -532,12 +532,14 @@ class ModelUnion {
|
|||||||
m_fields[field->name] = field.get();
|
m_fields[field->name] = field.get();
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
m_type = other.m_type;
|
||||||
m_unionIdx = other.m_unionIdx;
|
m_unionIdx = other.m_unionIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ModelUnion(ModelUnion &&other) noexcept {
|
constexpr ModelUnion(ModelUnion &&other) noexcept {
|
||||||
m_fieldsOrder = std::move(other.m_fieldsOrder);
|
m_fieldsOrder = std::move(other.m_fieldsOrder);
|
||||||
m_fields = std::move(other.m_fields);
|
m_fields = std::move(other.m_fields);
|
||||||
|
m_type = other.m_type;
|
||||||
m_unionIdx = other.m_unionIdx;
|
m_unionIdx = other.m_unionIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
deps/ox/src/ox/oc/read.cpp
vendored
6
deps/ox/src/ox/oc/read.cpp
vendored
@ -290,6 +290,12 @@ Error OrganicClawReader::fieldCString(const char *key, char **val, std::size_t b
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error OrganicClawReader::field(const char *key, UUID *val) noexcept {
|
||||||
|
UUIDStr str;
|
||||||
|
oxReturnError(field(key, &str));
|
||||||
|
return UUID::fromString(str).moveTo(val);
|
||||||
|
}
|
||||||
|
|
||||||
Result<std::size_t> OrganicClawReader::arrayLength(const char *key, bool) noexcept {
|
Result<std::size_t> OrganicClawReader::arrayLength(const char *key, bool) noexcept {
|
||||||
const auto &jv = value(key);
|
const auto &jv = value(key);
|
||||||
if (jv.empty()) {
|
if (jv.empty()) {
|
||||||
|
3
deps/ox/src/ox/oc/read.hpp
vendored
3
deps/ox/src/ox/oc/read.hpp
vendored
@ -21,6 +21,7 @@
|
|||||||
#include <ox/std/memops.hpp>
|
#include <ox/std/memops.hpp>
|
||||||
#include <ox/std/memory.hpp>
|
#include <ox/std/memory.hpp>
|
||||||
#include <ox/std/string.hpp>
|
#include <ox/std/string.hpp>
|
||||||
|
#include <ox/std/uuid.hpp>
|
||||||
|
|
||||||
namespace ox {
|
namespace ox {
|
||||||
|
|
||||||
@ -79,6 +80,8 @@ class OrganicClawReader {
|
|||||||
|
|
||||||
Error fieldCString(const char *key, char **val, std::size_t buffLen) noexcept;
|
Error fieldCString(const char *key, char **val, std::size_t buffLen) noexcept;
|
||||||
|
|
||||||
|
Error field(const char *key, UUID *val) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads an array length from the current location in the buffer.
|
* Reads an array length from the current location in the buffer.
|
||||||
* @param pass indicates that the parsing should iterate past the array length
|
* @param pass indicates that the parsing should iterate past the array length
|
||||||
|
9
deps/ox/src/ox/oc/write.cpp
vendored
9
deps/ox/src/ox/oc/write.cpp
vendored
@ -46,6 +46,15 @@ Error OrganicClawWriter::fieldCString(const char *key, char **val) noexcept {
|
|||||||
return fieldCString(key, const_cast<const char**>(val), {});
|
return fieldCString(key, const_cast<const char**>(val), {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error OrganicClawWriter::field(const char *key, const UUID *uuid) noexcept {
|
||||||
|
const auto uuidStr = uuid->toString();
|
||||||
|
if (targetValid() && uuidStr.len()) {
|
||||||
|
value(key) = uuidStr.c_str();
|
||||||
|
}
|
||||||
|
++m_fieldIt;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
Json::Value &OrganicClawWriter::value(const char *key) noexcept {
|
Json::Value &OrganicClawWriter::value(const char *key) noexcept {
|
||||||
if (m_json.isArray()) {
|
if (m_json.isArray()) {
|
||||||
return m_json[m_fieldIt];
|
return m_json[m_fieldIt];
|
||||||
|
7
deps/ox/src/ox/oc/write.hpp
vendored
7
deps/ox/src/ox/oc/write.hpp
vendored
@ -18,6 +18,7 @@
|
|||||||
#include <ox/std/buffer.hpp>
|
#include <ox/std/buffer.hpp>
|
||||||
#include <ox/std/hashmap.hpp>
|
#include <ox/std/hashmap.hpp>
|
||||||
#include <ox/std/string.hpp>
|
#include <ox/std/string.hpp>
|
||||||
|
#include <ox/std/uuid.hpp>
|
||||||
|
|
||||||
namespace ox {
|
namespace ox {
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ class OrganicClawWriter {
|
|||||||
value(key) = *val;
|
value(key) = *val;
|
||||||
}
|
}
|
||||||
++m_fieldIt;
|
++m_fieldIt;
|
||||||
return OxError(0);
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@ -130,7 +131,7 @@ class OrganicClawWriter {
|
|||||||
value(key) = w.m_json;
|
value(key) = w.m_json;
|
||||||
}
|
}
|
||||||
++m_fieldIt;
|
++m_fieldIt;
|
||||||
return OxError(0);
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@ -172,6 +173,8 @@ class OrganicClawWriter {
|
|||||||
|
|
||||||
Error fieldCString(const char *name, char **val) noexcept;
|
Error fieldCString(const char *name, char **val) noexcept;
|
||||||
|
|
||||||
|
Error field(const char *key, const UUID *uuid) noexcept;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Error field(const char*, T *val) noexcept;
|
Error field(const char*, T *val) noexcept;
|
||||||
|
|
||||||
|
2
deps/ox/src/ox/preloader/preloader.hpp
vendored
2
deps/ox/src/ox/preloader/preloader.hpp
vendored
@ -67,12 +67,12 @@ class Preloader: public ModelHandlerBase<Preloader<PlatSpec>> {
|
|||||||
|
|
||||||
constexpr Preloader() noexcept: m_writer(&m_buff) {}
|
constexpr Preloader() noexcept: m_writer(&m_buff) {}
|
||||||
|
|
||||||
|
public:
|
||||||
Preloader(const Preloader &src) = delete;
|
Preloader(const Preloader &src) = delete;
|
||||||
Preloader(Preloader &&src) = delete;
|
Preloader(Preloader &&src) = delete;
|
||||||
const Preloader &operator=(const Preloader &src) = delete;
|
const Preloader &operator=(const Preloader &src) = delete;
|
||||||
const Preloader &operator=(Preloader &&src) = delete;
|
const Preloader &operator=(Preloader &&src) = delete;
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr static ox::Result<ox::UniquePtr<Preloader>> make(ox::ios_base::seekdir anchor = ox::ios_base::cur,
|
constexpr static ox::Result<ox::UniquePtr<Preloader>> make(ox::ios_base::seekdir anchor = ox::ios_base::cur,
|
||||||
std::size_t sz = 0) noexcept;
|
std::size_t sz = 0) noexcept;
|
||||||
|
|
||||||
|
2
deps/ox/src/ox/std/CMakeLists.txt
vendored
2
deps/ox/src/ox/std/CMakeLists.txt
vendored
@ -37,6 +37,7 @@ add_library(
|
|||||||
strops.cpp
|
strops.cpp
|
||||||
trace.cpp
|
trace.cpp
|
||||||
typetraits.cpp
|
typetraits.cpp
|
||||||
|
uuid.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
@ -109,6 +110,7 @@ install(
|
|||||||
types.hpp
|
types.hpp
|
||||||
typetraits.hpp
|
typetraits.hpp
|
||||||
units.hpp
|
units.hpp
|
||||||
|
uuid.hpp
|
||||||
vector.hpp
|
vector.hpp
|
||||||
writer.hpp
|
writer.hpp
|
||||||
DESTINATION
|
DESTINATION
|
||||||
|
2
deps/ox/src/ox/std/assert.hpp
vendored
2
deps/ox/src/ox/std/assert.hpp
vendored
@ -80,7 +80,7 @@ constexpr void expect(const char *file, int line, const auto &actual, const auto
|
|||||||
if (!std::is_constant_evaluated()) {
|
if (!std::is_constant_evaluated()) {
|
||||||
#if defined(OX_USE_STDLIB)
|
#if defined(OX_USE_STDLIB)
|
||||||
oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, "Value incorrect");
|
oxErrf("\n\033[31;1;1mASSERT FAILURE:\033[0m [{}:{}]: {}\n", file, line, "Value incorrect");
|
||||||
oxErrf("expected: {}, actual: {}\n", detail::toStringView<true>(expected), detail::toStringView<true>(actual));
|
oxErrf("expected: {}\nactual: {}\n", detail::toStringView<true>(expected), detail::toStringView<true>(actual));
|
||||||
printStackTrace(2);
|
printStackTrace(2);
|
||||||
oxTracef("assert::expect", "Failed assert: {} == {} [{}:{}]", detail::toStringView<true>(actual), detail::toStringView<true>(expected), file, line);
|
oxTracef("assert::expect", "Failed assert: {} == {} [{}:{}]", detail::toStringView<true>(actual), detail::toStringView<true>(expected), file, line);
|
||||||
std::abort();
|
std::abort();
|
||||||
|
62
deps/ox/src/ox/std/bstring.hpp
vendored
62
deps/ox/src/ox/std/bstring.hpp
vendored
@ -28,6 +28,8 @@ class BString {
|
|||||||
|
|
||||||
constexpr BString(const char *str) noexcept;
|
constexpr BString(const char *str) noexcept;
|
||||||
|
|
||||||
|
constexpr BString &operator=(CRStringView str) noexcept;
|
||||||
|
|
||||||
constexpr BString &operator=(const char *str) noexcept;
|
constexpr BString &operator=(const char *str) noexcept;
|
||||||
|
|
||||||
constexpr BString &operator=(char *str) noexcept;
|
constexpr BString &operator=(char *str) noexcept;
|
||||||
@ -48,9 +50,13 @@ class BString {
|
|||||||
|
|
||||||
constexpr BString operator+(Integer_c auto i) const noexcept;
|
constexpr BString operator+(Integer_c auto i) const noexcept;
|
||||||
|
|
||||||
constexpr bool operator==(const BString &other) const noexcept;
|
constexpr bool operator==(const char *other) const noexcept;
|
||||||
|
|
||||||
constexpr bool operator!=(const BString &other) noexcept;
|
constexpr bool operator==(const OxString_c auto &other) const noexcept;
|
||||||
|
|
||||||
|
constexpr bool operator!=(const char *other) const noexcept;
|
||||||
|
|
||||||
|
constexpr bool operator!=(const OxString_c auto &other) noexcept;
|
||||||
|
|
||||||
constexpr char operator[](std::size_t i) const noexcept;
|
constexpr char operator[](std::size_t i) const noexcept;
|
||||||
|
|
||||||
@ -58,6 +64,10 @@ class BString {
|
|||||||
|
|
||||||
constexpr Error append(const char *str, std::size_t strLen) noexcept;
|
constexpr Error append(const char *str, std::size_t strLen) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr const char *data() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
constexpr char *data() noexcept;
|
constexpr char *data() noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
@ -88,12 +98,12 @@ constexpr BString<size>::BString() noexcept: m_buff{{0}} {
|
|||||||
|
|
||||||
template<std::size_t size>
|
template<std::size_t size>
|
||||||
constexpr BString<size>::BString(StringView str) noexcept: m_buff{{0}} {
|
constexpr BString<size>::BString(StringView str) noexcept: m_buff{{0}} {
|
||||||
*this = str;
|
operator=(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t size>
|
template<std::size_t size>
|
||||||
constexpr BString<size>::BString(const char *str) noexcept: m_buff{{0}} {
|
constexpr BString<size>::BString(const char *str) noexcept: m_buff{{0}} {
|
||||||
*this = str;
|
operator=(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t size>
|
template<std::size_t size>
|
||||||
@ -103,6 +113,18 @@ constexpr BString<size> &BString<size>::operator=(Integer_c auto i) noexcept {
|
|||||||
return this->operator=(str);
|
return this->operator=(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<std::size_t size>
|
||||||
|
constexpr BString<size> &BString<size>::operator=(ox::CRStringView str) noexcept {
|
||||||
|
std::size_t strLen = str.bytes() + 1;
|
||||||
|
if (cap() < strLen) {
|
||||||
|
strLen = cap();
|
||||||
|
}
|
||||||
|
ox_memcpy(m_buff, str.data(), strLen);
|
||||||
|
// make sure last element is a null terminator
|
||||||
|
m_buff[strLen] = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<std::size_t size>
|
template<std::size_t size>
|
||||||
constexpr BString<size> &BString<size>::operator=(const char *str) noexcept {
|
constexpr BString<size> &BString<size>::operator=(const char *str) noexcept {
|
||||||
std::size_t strLen = ox_strlen(str) + 1;
|
std::size_t strLen = ox_strlen(str) + 1;
|
||||||
@ -111,7 +133,7 @@ constexpr BString<size> &BString<size>::operator=(const char *str) noexcept {
|
|||||||
}
|
}
|
||||||
ox_memcpy(m_buff, str, strLen);
|
ox_memcpy(m_buff, str, strLen);
|
||||||
// make sure last element is a null terminator
|
// make sure last element is a null terminator
|
||||||
m_buff[cap() - 1] = 0;
|
m_buff[cap()] = 0;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,21 +189,22 @@ constexpr BString<size> BString<size>::operator+(Integer_c auto i) const noexcep
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t buffLen>
|
template<std::size_t buffLen>
|
||||||
constexpr bool BString<buffLen>::operator==(const BString<buffLen> &other) const noexcept {
|
constexpr bool BString<buffLen>::operator==(const char *other) const noexcept {
|
||||||
bool retval = true;
|
return ox::StringView(*this) == other;
|
||||||
std::size_t i = 0;
|
|
||||||
while (i < buffLen && (m_buff[i] || other.m_buff[i])) {
|
|
||||||
if (m_buff[i] != other.m_buff[i]) {
|
|
||||||
retval = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t buffLen>
|
template<std::size_t buffLen>
|
||||||
constexpr bool BString<buffLen>::operator!=(const BString<buffLen> &other) noexcept {
|
constexpr bool BString<buffLen>::operator==(const OxString_c auto &other) const noexcept {
|
||||||
|
return ox::StringView(*this) == ox::StringView(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t buffLen>
|
||||||
|
constexpr bool BString<buffLen>::operator!=(const char *other) const noexcept {
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t buffLen>
|
||||||
|
constexpr bool BString<buffLen>::operator!=(const OxString_c auto &other) noexcept {
|
||||||
return !operator==(other);
|
return !operator==(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +232,11 @@ constexpr Error BString<buffLen>::append(const char *str, std::size_t strLen) no
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<std::size_t buffLen>
|
||||||
|
constexpr const char *BString<buffLen>::data() const noexcept {
|
||||||
|
return static_cast<const char*>(m_buff);
|
||||||
|
}
|
||||||
|
|
||||||
template<std::size_t buffLen>
|
template<std::size_t buffLen>
|
||||||
constexpr char *BString<buffLen>::data() noexcept {
|
constexpr char *BString<buffLen>::data() noexcept {
|
||||||
return static_cast<char*>(m_buff);
|
return static_cast<char*>(m_buff);
|
||||||
|
6
deps/ox/src/ox/std/concepts.hpp
vendored
6
deps/ox/src/ox/std/concepts.hpp
vendored
@ -16,6 +16,10 @@ template<typename T, typename U>
|
|||||||
concept CommonPtrWith = ox::is_same_v<typename ox::remove_pointer<const T*>::type,
|
concept CommonPtrWith = ox::is_same_v<typename ox::remove_pointer<const T*>::type,
|
||||||
typename ox::remove_pointer<const U*>::type>;
|
typename ox::remove_pointer<const U*>::type>;
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
concept CommonRefWith = ox::is_same_v<typename ox::remove_reference_t<const T&>,
|
||||||
|
typename ox::remove_reference_t<const U&>>;
|
||||||
|
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
concept same_as = ox::is_same_v<T, T>;
|
concept same_as = ox::is_same_v<T, T>;
|
||||||
|
|
||||||
@ -50,4 +54,4 @@ constexpr auto isOxString(const StringView*) noexcept {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept OxString_c = detail::isOxString(static_cast<T*>(nullptr));
|
concept OxString_c = detail::isOxString(static_cast<T*>(nullptr));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
2
deps/ox/src/ox/std/fmt.hpp
vendored
2
deps/ox/src/ox/std/fmt.hpp
vendored
@ -207,7 +207,7 @@ constexpr Result<T> join(auto d, const auto &list) {
|
|||||||
return T("");
|
return T("");
|
||||||
}
|
}
|
||||||
T out;
|
T out;
|
||||||
out += list.front().value;
|
out += list[0];
|
||||||
for (auto i = 1ul; i < list.size(); ++i) {
|
for (auto i = 1ul; i < list.size(); ++i) {
|
||||||
out += d;
|
out += d;
|
||||||
out += list[i];
|
out += list[i];
|
||||||
|
24
deps/ox/src/ox/std/math.hpp
vendored
24
deps/ox/src/ox/std/math.hpp
vendored
@ -14,19 +14,37 @@ namespace ox {
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr const T &min(const T &a, const T &b) noexcept {
|
constexpr T min(T a, T b) noexcept requires(ox::is_integral_v<T>) {
|
||||||
return a < b ? a : b;
|
return a < b ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr const T &max(const T &a, const T &b) noexcept {
|
constexpr const T &min(const T &a, const T &b) noexcept requires(!ox::is_integral_v<T>) {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr T max(T a, T b) noexcept requires(ox::is_integral_v<T>) {
|
||||||
return a > b ? a : b;
|
return a > b ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr const T &clamp(const T &v, const T &lo, const T &hi) noexcept {
|
constexpr const T &max(const T &a, const T &b) noexcept requires(!ox::is_integral_v<T>) {
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr T clamp(T v, T lo, T hi) noexcept requires(ox::is_integral_v<T>) {
|
||||||
|
return min(ox::max(v, lo), hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr const T &clamp(const T &v, const T &lo, const T &hi) noexcept requires(!ox::is_integral_v<T>) {
|
||||||
return min(ox::max(v, lo), hi);
|
return min(ox::max(v, lo), hi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
deps/ox/src/ox/std/optional.hpp
vendored
41
deps/ox/src/ox/std/optional.hpp
vendored
@ -11,6 +11,7 @@
|
|||||||
#include "bit.hpp"
|
#include "bit.hpp"
|
||||||
#include "initializerlist.hpp"
|
#include "initializerlist.hpp"
|
||||||
#include "iterator.hpp"
|
#include "iterator.hpp"
|
||||||
|
#include "memory.hpp"
|
||||||
#include "new.hpp"
|
#include "new.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
@ -21,19 +22,17 @@ template<typename T, std::size_t buffSize = sizeof(T)>
|
|||||||
class Optional {
|
class Optional {
|
||||||
private:
|
private:
|
||||||
T *m_ptr = nullptr;
|
T *m_ptr = nullptr;
|
||||||
char m_data[buffSize] = {};
|
AllocAlias<T> m_data = {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr Optional() noexcept = default;
|
constexpr Optional() noexcept = default;
|
||||||
|
|
||||||
template<typename ...Args>
|
template<typename ...Args>
|
||||||
constexpr Optional(Args &&... args);
|
explicit constexpr Optional(Args &&... args);
|
||||||
|
|
||||||
constexpr Optional(const Optional &other) {
|
constexpr Optional(const Optional &other) {
|
||||||
if (other.m_ptr) {
|
if (other.m_ptr) {
|
||||||
m_ptr = new(m_data) T(*other.m_ptr);
|
m_ptr = new(m_data.data()) T(*other.m_ptr);
|
||||||
} else {
|
|
||||||
m_ptr = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,15 +41,13 @@ class Optional {
|
|||||||
m_ptr->~T();
|
m_ptr->~T();
|
||||||
}
|
}
|
||||||
if (other.m_ptr) {
|
if (other.m_ptr) {
|
||||||
m_ptr = new(m_data) T(std::move(*other.m_ptr));
|
m_ptr = new(m_data.data()) T(std::move(*other.m_ptr));
|
||||||
} else {
|
|
||||||
m_ptr = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ~Optional() {
|
constexpr ~Optional() {
|
||||||
if (m_ptr) {
|
if (m_ptr) {
|
||||||
m_ptr->~T();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,8 +95,7 @@ class Optional {
|
|||||||
}
|
}
|
||||||
*m_ptr = *other.m_ptr;
|
*m_ptr = *other.m_ptr;
|
||||||
} else if (m_ptr) {
|
} else if (m_ptr) {
|
||||||
m_ptr->~T();
|
reset();
|
||||||
m_ptr = nullptr;
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -114,22 +110,29 @@ class Optional {
|
|||||||
}
|
}
|
||||||
*m_ptr = std::move(*other.m_ptr);
|
*m_ptr = std::move(*other.m_ptr);
|
||||||
} else if (m_ptr) {
|
} else if (m_ptr) {
|
||||||
m_ptr->~T();
|
reset();
|
||||||
m_ptr = nullptr;
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
constexpr T &emplace(Args &&...args) {
|
constexpr T &emplace(Args &&...args) {
|
||||||
m_ptr = std::construct_at<T>(m_ptr, ox::forward<Args>(args)...);
|
if (std::is_constant_evaluated()) {
|
||||||
|
m_ptr = new T(ox::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
m_ptr = std::construct_at<T>(reinterpret_cast<T*>(m_data.data()), ox::forward<Args>(args)...);
|
||||||
|
}
|
||||||
return *m_ptr;
|
return *m_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U, class... Args>
|
template<typename U, class... Args>
|
||||||
constexpr T &emplace_subclass(Args &&...args) {
|
constexpr T &emplace_subclass(Args &&...args) {
|
||||||
static_assert(sizeof(U) <= buffSize, "Subclass is too large for this Optional");
|
static_assert(sizeof(U) <= buffSize, "Subclass is too large for this Optional");
|
||||||
m_ptr = std::construct_at<U>(m_ptr, ox::forward<Args>(args)...);
|
if (std::is_constant_evaluated()) {
|
||||||
|
m_ptr = new U(ox::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
m_ptr = std::construct_at<U>(reinterpret_cast<U*>(m_data.data()), ox::forward<Args>(args)...);
|
||||||
|
}
|
||||||
return *m_ptr;
|
return *m_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +146,11 @@ class Optional {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr void reset() noexcept {
|
constexpr void reset() noexcept {
|
||||||
get()->~T();
|
if (std::is_constant_evaluated()) {
|
||||||
|
ox::safeDelete(m_ptr);
|
||||||
|
} else {
|
||||||
|
get()->~T();
|
||||||
|
}
|
||||||
m_ptr = nullptr;
|
m_ptr = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,4 +162,4 @@ constexpr Optional<T, buffSize>::Optional(Args &&... args) {
|
|||||||
emplace(ox::forward<Args>(args)...);
|
emplace(ox::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
2
deps/ox/src/ox/std/random.cpp
vendored
2
deps/ox/src/ox/std/random.cpp
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015 - 2022 gary@drinkingtea.net
|
* Copyright 2015 - 2023 gary@drinkingtea.net
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
9
deps/ox/src/ox/std/random.hpp
vendored
9
deps/ox/src/ox/std/random.hpp
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015 - 2022 gary@drinkingtea.net
|
* Copyright 2015 - 2023 gary@drinkingtea.net
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -25,12 +25,19 @@ class OX_PACKED Random {
|
|||||||
|
|
||||||
explicit constexpr Random(const RandomSeed &seed) noexcept;
|
explicit constexpr Random(const RandomSeed &seed) noexcept;
|
||||||
|
|
||||||
|
constexpr void seed(const RandomSeed &seed) noexcept;
|
||||||
|
|
||||||
constexpr uint64_t gen() noexcept;
|
constexpr uint64_t gen() noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr Random::Random(const RandomSeed &seed) noexcept: m_seed{seed[0], seed[1]} {
|
constexpr Random::Random(const RandomSeed &seed) noexcept: m_seed{seed[0], seed[1]} {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr void Random::seed(const RandomSeed &seed) noexcept {
|
||||||
|
m_seed[0] = seed[0];
|
||||||
|
m_seed[1] = seed[1];
|
||||||
|
}
|
||||||
|
|
||||||
constexpr uint64_t Random::gen() noexcept {
|
constexpr uint64_t Random::gen() noexcept {
|
||||||
auto s0 = m_seed[0];
|
auto s0 = m_seed[0];
|
||||||
auto s1 = m_seed[1];
|
auto s1 = m_seed[1];
|
||||||
|
1
deps/ox/src/ox/std/std.hpp
vendored
1
deps/ox/src/ox/std/std.hpp
vendored
@ -42,4 +42,5 @@
|
|||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include "typetraits.hpp"
|
#include "typetraits.hpp"
|
||||||
#include "units.hpp"
|
#include "units.hpp"
|
||||||
|
#include "uuid.hpp"
|
||||||
#include "vector.hpp"
|
#include "vector.hpp"
|
||||||
|
14
deps/ox/src/ox/std/string.hpp
vendored
14
deps/ox/src/ox/std/string.hpp
vendored
@ -439,19 +439,7 @@ constexpr bool BasicString<SmallStringSize_v>::operator==(const char *other) con
|
|||||||
|
|
||||||
template<std::size_t SmallStringSize_v>
|
template<std::size_t SmallStringSize_v>
|
||||||
constexpr bool BasicString<SmallStringSize_v>::operator==(const OxString_c auto &other) const noexcept {
|
constexpr bool BasicString<SmallStringSize_v>::operator==(const OxString_c auto &other) const noexcept {
|
||||||
if (len() != other.len()) {
|
return ox::StringView(*this) == ox::StringView(other);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool retval = true;
|
|
||||||
std::size_t i = 0;
|
|
||||||
while (i < m_buff.size() && (m_buff[i] || other[i])) {
|
|
||||||
if (m_buff[i] != other[i]) {
|
|
||||||
retval = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t SmallStringSize_v>
|
template<std::size_t SmallStringSize_v>
|
||||||
|
75
deps/ox/src/ox/std/stringview.hpp
vendored
75
deps/ox/src/ox/std/stringview.hpp
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015 - 2022 gary@drinkingtea.net
|
* Copyright 2015 - 2023 gary@drinkingtea.net
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -12,10 +12,12 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "bit.hpp"
|
||||||
#include "iterator.hpp"
|
#include "iterator.hpp"
|
||||||
#include "strops.hpp"
|
#include "strops.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include "bit.hpp"
|
#include "vector.hpp"
|
||||||
|
#include "writer.hpp"
|
||||||
|
|
||||||
namespace ox {
|
namespace ox {
|
||||||
|
|
||||||
@ -223,7 +225,18 @@ class StringView {
|
|||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr auto substr(std::size_t pos) const noexcept {
|
constexpr auto substr(std::size_t pos) const noexcept {
|
||||||
return StringView(m_str + pos, m_len - pos);
|
if (m_len >= pos) {
|
||||||
|
return StringView(m_str + pos, m_len - pos);
|
||||||
|
}
|
||||||
|
return StringView();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto substr(std::size_t start, std::size_t end) const noexcept {
|
||||||
|
if (m_len >= start && end >= start) {
|
||||||
|
return StringView(m_str + start, end - start);
|
||||||
|
}
|
||||||
|
return StringView();
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto operator[](std::size_t i) const noexcept {
|
constexpr auto operator[](std::size_t i) const noexcept {
|
||||||
@ -276,6 +289,62 @@ constexpr bool endsWith(CRStringView base, CRStringView ending) noexcept {
|
|||||||
return base.len() >= endingLen && ox_strcmp(base.data() + (base.len() - endingLen), ending) == 0;
|
return base.len() >= endingLen && ox_strcmp(base.data() + (base.len() - endingLen), ending) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t find(CRStringView str, char search) noexcept {
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i < str.len(); ++i) {
|
||||||
|
if (str[i] == search) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t find(CRStringView str, CRStringView search) noexcept {
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i < str.len(); ++i) {
|
||||||
|
if (beginsWith(str.substr(i), search)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t smallSz = 0>
|
||||||
|
constexpr ox::Vector<ox::StringView, smallSz> split(CRStringView str, char del) noexcept {
|
||||||
|
ox::Vector<ox::StringView, smallSz> out;
|
||||||
|
constexpr auto nextSeg = [](CRStringView current, char del) {
|
||||||
|
return current.substr(find(current, del) + 1);
|
||||||
|
};
|
||||||
|
for (auto current = str; current.len(); current = nextSeg(current, del)) {
|
||||||
|
const auto next = find(current, del);
|
||||||
|
if (const auto s = current.substr(0, next); s.len()) {
|
||||||
|
out.emplace_back(s);
|
||||||
|
}
|
||||||
|
current = current.substr(next);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t smallSz = 0>
|
||||||
|
constexpr ox::Vector<ox::StringView, smallSz> split(CRStringView str, CRStringView del) noexcept {
|
||||||
|
ox::Vector<ox::StringView, smallSz> out;
|
||||||
|
constexpr auto nextSeg = [](CRStringView current, CRStringView del) {
|
||||||
|
return current.substr(find(current, del) + del.len());
|
||||||
|
};
|
||||||
|
for (auto current = str; current.len(); current = nextSeg(current, del)) {
|
||||||
|
const auto next = find(current, del);
|
||||||
|
if (const auto s = current.substr(0, next); s.len()) {
|
||||||
|
out.emplace_back(s);
|
||||||
|
}
|
||||||
|
current = current.substr(next);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto write(Writer_c auto *writer, ox::CRStringView sv) noexcept {
|
||||||
|
return writer->write(sv.data(), sv.bytes());
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef OX_USE_STDLIB
|
#ifdef OX_USE_STDLIB
|
||||||
constexpr auto toStdStringView(CRStringView sv) noexcept {
|
constexpr auto toStdStringView(CRStringView sv) noexcept {
|
||||||
return std::string_view(sv.data(), sv.bytes());
|
return std::string_view(sv.data(), sv.bytes());
|
||||||
|
5
deps/ox/src/ox/std/test/CMakeLists.txt
vendored
5
deps/ox/src/ox/std/test/CMakeLists.txt
vendored
@ -18,3 +18,8 @@ add_test("[ox/std] HashMap" StdTest "HashMap")
|
|||||||
add_test("[ox/std] HeapMgr" StdTest malloc)
|
add_test("[ox/std] HeapMgr" StdTest malloc)
|
||||||
add_test("[ox/std] Serialize-Int" StdTest "Serialize-Int")
|
add_test("[ox/std] Serialize-Int" StdTest "Serialize-Int")
|
||||||
add_test("[ox/std] BufferWriter" StdTest "BufferWriter")
|
add_test("[ox/std] BufferWriter" StdTest "BufferWriter")
|
||||||
|
add_test("[ox/std] StringSplit" StdTest "StringSplit")
|
||||||
|
add_test("[ox/std] FromHex" StdTest "FromHex")
|
||||||
|
add_test("[ox/std] ToHex" StdTest "ToHex")
|
||||||
|
add_test("[ox/std] UUID" StdTest "UUID")
|
||||||
|
add_test("[ox/std] UUID::generate" StdTest "UUID::generate")
|
||||||
|
134
deps/ox/src/ox/std/test/tests.cpp
vendored
134
deps/ox/src/ox/std/test/tests.cpp
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015 - 2022 gary@drinkingtea.net
|
* Copyright 2015 - 2023 gary@drinkingtea.net
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -9,6 +9,7 @@
|
|||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <ox/std/uuid.hpp>
|
||||||
#include <ox/std/std.hpp>
|
#include <ox/std/std.hpp>
|
||||||
|
|
||||||
static std::map<ox::String, ox::Error(*)()> tests = {
|
static std::map<ox::String, ox::Error(*)()> tests = {
|
||||||
@ -59,6 +60,8 @@ static std::map<ox::String, ox::Error(*)()> tests = {
|
|||||||
oxAssert(s == "AB9C", "BString append broken");
|
oxAssert(s == "AB9C", "BString append broken");
|
||||||
s = "asdf";
|
s = "asdf";
|
||||||
oxAssert(s == "asdf", "String assign broken");
|
oxAssert(s == "asdf", "String assign broken");
|
||||||
|
oxAssert(s != "aoeu", "String assign broken");
|
||||||
|
oxAssert(s.len() == 4, "String assign broken");
|
||||||
return OxError(0);
|
return OxError(0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -155,6 +158,135 @@ static std::map<ox::String, ox::Error(*)()> tests = {
|
|||||||
return OxError(0);
|
return OxError(0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"FromHex",
|
||||||
|
[] {
|
||||||
|
oxExpect(ox::detail::fromHex("01").unwrap(), 0x01);
|
||||||
|
oxExpect(ox::detail::fromHex("02").unwrap(), 0x02);
|
||||||
|
oxExpect(ox::detail::fromHex("03").unwrap(), 0x03);
|
||||||
|
oxExpect(ox::detail::fromHex("04").unwrap(), 0x04);
|
||||||
|
oxExpect(ox::detail::fromHex("05").unwrap(), 0x05);
|
||||||
|
oxExpect(ox::detail::fromHex("06").unwrap(), 0x06);
|
||||||
|
oxExpect(ox::detail::fromHex("07").unwrap(), 0x07);
|
||||||
|
oxExpect(ox::detail::fromHex("08").unwrap(), 0x08);
|
||||||
|
oxExpect(ox::detail::fromHex("0d").unwrap(), 0x0d);
|
||||||
|
oxExpect(ox::detail::fromHex("0e").unwrap(), 0x0e);
|
||||||
|
oxExpect(ox::detail::fromHex("0f").unwrap(), 0x0f);
|
||||||
|
oxExpect(ox::detail::fromHex("0F").unwrap(), 0x0f);
|
||||||
|
oxExpect(ox::detail::fromHex("fF").unwrap(), 0xff);
|
||||||
|
oxExpect(ox::detail::fromHex("ff").unwrap(), 0xff);
|
||||||
|
oxExpect(ox::detail::fromHex("a0").unwrap(), 0xa0);
|
||||||
|
oxExpect(ox::detail::fromHex("93").unwrap(), 0x93);
|
||||||
|
oxExpect(ox::detail::fromHex("40").unwrap(), 0x40);
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ToHex",
|
||||||
|
[] {
|
||||||
|
oxExpect(ox::detail::toHex(0x01), "01");
|
||||||
|
oxExpect(ox::detail::toHex(0x02), "02");
|
||||||
|
oxExpect(ox::detail::toHex(0x03), "03");
|
||||||
|
oxExpect(ox::detail::toHex(0x04), "04");
|
||||||
|
oxExpect(ox::detail::toHex(0x05), "05");
|
||||||
|
oxExpect(ox::detail::toHex(0x06), "06");
|
||||||
|
oxExpect(ox::detail::toHex(0x07), "07");
|
||||||
|
oxExpect(ox::detail::toHex(0x08), "08");
|
||||||
|
oxExpect(ox::detail::toHex(0x0d), "0d");
|
||||||
|
oxExpect(ox::detail::toHex(0x0e), "0e");
|
||||||
|
oxExpect(ox::detail::toHex(0x0f), "0f");
|
||||||
|
oxExpect(ox::detail::toHex(0x93), "93");
|
||||||
|
oxExpect(ox::detail::toHex(0x40), "40");
|
||||||
|
oxExpect(ox::detail::toHex(0xf0), "f0");
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UUID",
|
||||||
|
[] {
|
||||||
|
constexpr ox::StringView uuidStr = "8d814442-f46e-4cc3-8edc-ca3c01cc86db";
|
||||||
|
oxRequire(uuid, ox::UUID::fromString(uuidStr));
|
||||||
|
oxExpect(uuid.toString(), uuidStr);
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UUID::generate",
|
||||||
|
[] {
|
||||||
|
ox::UUID::seedGenerator({1234, 4321});
|
||||||
|
oxExpect(ox::UUID::generate().unwrap().toString(), "c8f4bff1-d403-4576-fd3b-0c41f1cd8b7d");
|
||||||
|
oxExpect(ox::UUID::generate().unwrap().toString(), "0d1b7dad-7d50-49b6-916d-c3d811b5af0a");
|
||||||
|
oxExpect(ox::UUID::generate().unwrap().toString(), "e3889e0b-4dcd-47b2-b3eb-492ea958d3e4");
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StringSplit",
|
||||||
|
[] {
|
||||||
|
ox::StringView sv = "ab.cd";
|
||||||
|
auto list = ox::split(sv, ".");
|
||||||
|
oxExpect(list.size(), 2u);
|
||||||
|
oxExpect(list[0], "ab");
|
||||||
|
oxExpect(list[1], "cd");
|
||||||
|
sv = "ab.cd.fg";
|
||||||
|
list = ox::split(sv, ".");
|
||||||
|
oxExpect(list.size(), 3u);
|
||||||
|
oxExpect(list[0], "ab");
|
||||||
|
oxExpect(list[1], "cd");
|
||||||
|
oxExpect(list[2], "fg");
|
||||||
|
sv = "ab.cd.";
|
||||||
|
list = ox::split(sv, ".");
|
||||||
|
oxExpect(list.size(), 2u);
|
||||||
|
oxExpect(list[0], "ab");
|
||||||
|
oxExpect(list[1], "cd");
|
||||||
|
sv = ".ab.cd.";
|
||||||
|
list = ox::split(sv, ".");
|
||||||
|
oxExpect(list.size(), 2u);
|
||||||
|
oxExpect(list[0], "ab");
|
||||||
|
oxExpect(list[1], "cd");
|
||||||
|
sv = ".";
|
||||||
|
list = ox::split(sv, ".");
|
||||||
|
oxExpect(list.size(), 0u);
|
||||||
|
sv = ".";
|
||||||
|
list = ox::split(sv, ".");
|
||||||
|
oxExpect(list.size(), 0u);
|
||||||
|
sv = "";
|
||||||
|
list = ox::split(sv, ".");
|
||||||
|
oxExpect(list.size(), 0u);
|
||||||
|
// split by single char
|
||||||
|
sv = "ab.cd";
|
||||||
|
list = ox::split(sv, '.');
|
||||||
|
oxExpect(list.size(), 2u);
|
||||||
|
oxExpect(list[0], "ab");
|
||||||
|
oxExpect(list[1], "cd");
|
||||||
|
sv = "ab.cd.fg";
|
||||||
|
list = ox::split(sv, '.');
|
||||||
|
oxExpect(list.size(), 3u);
|
||||||
|
oxExpect(list[0], "ab");
|
||||||
|
oxExpect(list[1], "cd");
|
||||||
|
oxExpect(list[2], "fg");
|
||||||
|
sv = "ab.cd.";
|
||||||
|
list = ox::split(sv, '.');
|
||||||
|
oxExpect(list.size(), 2u);
|
||||||
|
oxExpect(list[0], "ab");
|
||||||
|
oxExpect(list[1], "cd");
|
||||||
|
sv = ".ab.cd.";
|
||||||
|
list = ox::split(sv, '.');
|
||||||
|
oxExpect(list.size(), 2u);
|
||||||
|
oxExpect(list[0], "ab");
|
||||||
|
oxExpect(list[1], "cd");
|
||||||
|
sv = ".";
|
||||||
|
list = ox::split(sv, '.');
|
||||||
|
oxExpect(list.size(), 0u);
|
||||||
|
sv = ".";
|
||||||
|
list = ox::split(sv, '.');
|
||||||
|
oxExpect(list.size(), 0u);
|
||||||
|
sv = "";
|
||||||
|
list = ox::split(sv, '.');
|
||||||
|
oxExpect(list.size(), 0u);
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, const char **args) {
|
int main(int argc, const char **args) {
|
||||||
|
8
deps/ox/src/ox/std/types.hpp
vendored
8
deps/ox/src/ox/std/types.hpp
vendored
@ -67,6 +67,14 @@ template<typename T, std::size_t sz = sizeof(T)>
|
|||||||
struct alignas(alignof(T)) AllocAlias {
|
struct alignas(alignof(T)) AllocAlias {
|
||||||
char buff[sz];
|
char buff[sz];
|
||||||
constexpr AllocAlias() noexcept = default;
|
constexpr AllocAlias() noexcept = default;
|
||||||
|
[[nodiscard]]
|
||||||
|
auto data() noexcept {
|
||||||
|
return reinterpret_cast<T*>(this);
|
||||||
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
auto data() const noexcept {
|
||||||
|
return reinterpret_cast<const T*>(this);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
35
deps/ox/src/ox/std/uuid.cpp
vendored
Normal file
35
deps/ox/src/ox/std/uuid.cpp
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 - 2023 gary@drinkingtea.net
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uuid.hpp"
|
||||||
|
|
||||||
|
namespace ox {
|
||||||
|
|
||||||
|
bool UUID::s_seeded = false;
|
||||||
|
Random UUID::s_rand;
|
||||||
|
|
||||||
|
void UUID::seedGenerator(const RandomSeed &seed) noexcept {
|
||||||
|
s_seeded = true;
|
||||||
|
s_rand.seed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUID v4
|
||||||
|
Result<UUID> UUID::generate() noexcept {
|
||||||
|
if (!s_seeded) {
|
||||||
|
return OxError(1, "UUID generator not seeded.");
|
||||||
|
}
|
||||||
|
UUID out;
|
||||||
|
for (auto &v : out.m_value) {
|
||||||
|
v = static_cast<uint8_t>(s_rand.gen() % 255);
|
||||||
|
}
|
||||||
|
out.m_value[6] &= 0x0f;
|
||||||
|
out.m_value[6] |= 4 << 4;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
170
deps/ox/src/ox/std/uuid.hpp
vendored
Normal file
170
deps/ox/src/ox/std/uuid.hpp
vendored
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 - 2023 gary@drinkingtea.net
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "array.hpp"
|
||||||
|
#include "bstring.hpp"
|
||||||
|
#include "random.hpp"
|
||||||
|
#include "stringview.hpp"
|
||||||
|
#include "strops.hpp"
|
||||||
|
#include "trace.hpp"
|
||||||
|
|
||||||
|
namespace ox {
|
||||||
|
|
||||||
|
using UUIDStr = ox::BString<36>;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto isHexChar(char c) noexcept {
|
||||||
|
return (c >= '0' && c <= '9')
|
||||||
|
|| (c >= 'a' && c <= 'f')
|
||||||
|
|| (c >= 'A' && c <= 'F');
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ox::Result<uint8_t> fromHex(ox::CRStringView v) noexcept {
|
||||||
|
constexpr auto valMap = [] {
|
||||||
|
ox::Array<uint8_t, 128> out;
|
||||||
|
out['A'] = out['a'] = 10;
|
||||||
|
out['B'] = out['b'] = 11;
|
||||||
|
out['C'] = out['c'] = 12;
|
||||||
|
out['D'] = out['d'] = 13;
|
||||||
|
out['E'] = out['e'] = 14;
|
||||||
|
out['F'] = out['f'] = 15;
|
||||||
|
out['0'] = 0;
|
||||||
|
out['1'] = 1;
|
||||||
|
out['2'] = 2;
|
||||||
|
out['3'] = 3;
|
||||||
|
out['4'] = 4;
|
||||||
|
out['5'] = 5;
|
||||||
|
out['6'] = 6;
|
||||||
|
out['7'] = 7;
|
||||||
|
out['8'] = 8;
|
||||||
|
out['9'] = 9;
|
||||||
|
return out;
|
||||||
|
}();
|
||||||
|
if (!detail::isHexChar(v[0]) || !detail::isHexChar(v[1])) {
|
||||||
|
return OxError(1, "Invalid UUID");
|
||||||
|
}
|
||||||
|
if (v.len() != 2) {
|
||||||
|
return OxError(2);
|
||||||
|
}
|
||||||
|
uint8_t out = 0;
|
||||||
|
out += valMap[static_cast<unsigned>(v[0])] * 16u;
|
||||||
|
out += valMap[static_cast<unsigned>(v[1])];
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ox::BString<2> toHex(uint8_t v) noexcept {
|
||||||
|
constexpr ox::Array<char, 16> valMap {
|
||||||
|
'0',
|
||||||
|
'1',
|
||||||
|
'2',
|
||||||
|
'3',
|
||||||
|
'4',
|
||||||
|
'5',
|
||||||
|
'6',
|
||||||
|
'7',
|
||||||
|
'8',
|
||||||
|
'9',
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'd',
|
||||||
|
'e',
|
||||||
|
'f',
|
||||||
|
};
|
||||||
|
ox::Array<char, 3> out;
|
||||||
|
out[0] = valMap[static_cast<unsigned>((v & 0xf0) / 16)];
|
||||||
|
out[1] = valMap[static_cast<unsigned>(v & 0x0f)];
|
||||||
|
out[2] = 0;
|
||||||
|
return out.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class UUID {
|
||||||
|
template<typename T>
|
||||||
|
friend constexpr Error model(T *io, ox::CommonPtrWith<UUID> auto *obj) noexcept;
|
||||||
|
protected:
|
||||||
|
static bool s_seeded;
|
||||||
|
static Random s_rand;
|
||||||
|
ox::Array<uint8_t, 16> m_value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void seedGenerator(const RandomSeed &seed) noexcept;
|
||||||
|
|
||||||
|
static ox::Result<UUID> generate() noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto value() const noexcept {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr ox::Result<ox::UUID> fromString(ox::CRStringView s) noexcept {
|
||||||
|
if (s.len() < 36) {
|
||||||
|
return OxError(1, "Insufficient data contain complete UUID");
|
||||||
|
}
|
||||||
|
UUID out;
|
||||||
|
auto valueI = 0u;
|
||||||
|
for (auto i = 0u; i < s.len();) {
|
||||||
|
if (s[i] == '-') {
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto seg = s.substr(i, i + 2);
|
||||||
|
if (seg.len() != 2) {
|
||||||
|
return OxError(1, "Invalid UUID");
|
||||||
|
}
|
||||||
|
oxRequire(val, detail::fromHex(seg));
|
||||||
|
out.m_value[valueI] = val;
|
||||||
|
i += 2;
|
||||||
|
++valueI;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr UUIDStr toString() const noexcept {
|
||||||
|
UUIDStr out;
|
||||||
|
auto valueI = 0u;
|
||||||
|
constexpr auto printChars = [](
|
||||||
|
ox::BString<36> *out,
|
||||||
|
const Array<uint8_t, 16> &value,
|
||||||
|
std::size_t cnt,
|
||||||
|
unsigned valueI) {
|
||||||
|
for (auto i = 0u; i < cnt; ++i) {
|
||||||
|
const auto v = value[valueI];
|
||||||
|
const auto h = detail::toHex(v);
|
||||||
|
oxIgnoreError(out->append(h.c_str(), h.len()));
|
||||||
|
++valueI;
|
||||||
|
}
|
||||||
|
return valueI;
|
||||||
|
};
|
||||||
|
valueI = printChars(&out, m_value, 4, valueI);
|
||||||
|
out += "-";
|
||||||
|
valueI = printChars(&out, m_value, 2, valueI);
|
||||||
|
out += "-";
|
||||||
|
valueI = printChars(&out, m_value, 2, valueI);
|
||||||
|
out += "-";
|
||||||
|
valueI = printChars(&out, m_value, 2, valueI);
|
||||||
|
out += "-";
|
||||||
|
valueI = printChars(&out, m_value, 6, valueI);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Error model(T *io, ox::CommonPtrWith<UUID> auto *obj) noexcept {
|
||||||
|
io->template setTypeInfo<UUID>();
|
||||||
|
oxReturnError(io->field("value", &obj->m_value));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4
sample_project/.nostalgia/type_descriptors/B.bool;0
Normal file
4
sample_project/.nostalgia/type_descriptors/B.bool;0
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||||
|
"primitiveType" : 2,
|
||||||
|
"typeName" : "B.bool"
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||||
|
"fieldList" :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"fieldName" : "name",
|
||||||
|
"typeId" : "net.drinkingtea.ox.BasicString#8#;1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName" : "rows",
|
||||||
|
"typeId" : "B.int32;0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName" : "columns",
|
||||||
|
"typeId" : "B.int32;0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName" : "subsheets",
|
||||||
|
"subscriptLevels" : 1,
|
||||||
|
"subscriptStack" :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"subscriptType" : 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName" : "pixels",
|
||||||
|
"subscriptLevels" : 1,
|
||||||
|
"subscriptStack" :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"subscriptType" : 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeId" : "B.uint8;0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primitiveType" : 5,
|
||||||
|
"typeName" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet",
|
||||||
|
"typeVersion" : 3
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||||
|
"fieldList" :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"fieldName" : "bpp",
|
||||||
|
"typeId" : "B.int8;0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName" : "idIt",
|
||||||
|
"typeId" : "B.int32;0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName" : "defaultPalette",
|
||||||
|
"typeId" : "net.drinkingtea.ox.FileAddress;1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldName" : "subsheet",
|
||||||
|
"typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primitiveType" : 5,
|
||||||
|
"typeName" : "net.drinkingtea.nostalgia.core.TileSheet",
|
||||||
|
"typeVersion" : 3
|
||||||
|
}
|
@ -14,6 +14,7 @@ O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
|||||||
"typeId" : "B.uint64;0"
|
"typeId" : "B.uint64;0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"preloadable" : true,
|
||||||
"primitiveType" : 6,
|
"primitiveType" : 6,
|
||||||
"typeName" : "net.drinkingtea.ox.FileAddress.Data",
|
"typeName" : "net.drinkingtea.ox.FileAddress.Data",
|
||||||
"typeVersion" : 1
|
"typeVersion" : 1
|
||||||
|
@ -10,6 +10,7 @@ O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
|||||||
"typeId" : "net.drinkingtea.ox.FileAddress.Data"
|
"typeId" : "net.drinkingtea.ox.FileAddress.Data"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"preloadable" : true,
|
||||||
"primitiveType" : 5,
|
"primitiveType" : 5,
|
||||||
"typeName" : "net.drinkingtea.ox.FileAddress",
|
"typeName" : "net.drinkingtea.ox.FileAddress",
|
||||||
"typeVersion" : 1
|
"typeVersion" : 1
|
||||||
|
Binary file not shown.
1
sample_project/Palettes/Chester.npal
Normal file
1
sample_project/Palettes/Chester.npal
Normal file
@ -0,0 +1 @@
|
|||||||
|
N1;14fc3dd8-42ff-4bf9-81f1-a010cc5ac251;M2;net.drinkingtea.nostalgia.core.Palette;1;ûÿ³Ö
|
@ -1 +1 @@
|
|||||||
M2;net.drinkingtea.nostalgia.core.Palette;1;ûÿ³Ö
|
N1;0f75977f-1c52-45f8-9793-52ea2dc200a0;M2;net.drinkingtea.nostalgia.core.Palette;1;ûÿ³Ö
|
||||||
|
@ -1 +1 @@
|
|||||||
M2;net.drinkingtea.nostalgia.core.Palette;1;PÛ{³ÖCˆ
|
N1;c79f21e2-f74f-4ad9-90ed-32b0ef7da6ed;M2;net.drinkingtea.nostalgia.core.Palette;1;PÛ{³ÖCˆ
|
||||||
|
BIN
sample_project/Scenes/Chester.nscn
Normal file
BIN
sample_project/Scenes/Chester.nscn
Normal file
Binary file not shown.
Binary file not shown.
BIN
sample_project/TileSheets/Chester.ng
Normal file
BIN
sample_project/TileSheets/Chester.ng
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -6,7 +6,6 @@ add_subdirectory(core)
|
|||||||
add_subdirectory(foundation)
|
add_subdirectory(foundation)
|
||||||
add_subdirectory(geo)
|
add_subdirectory(geo)
|
||||||
add_subdirectory(scene)
|
add_subdirectory(scene)
|
||||||
add_subdirectory(world)
|
|
||||||
|
|
||||||
if(NOSTALGIA_BUILD_PLAYER)
|
if(NOSTALGIA_BUILD_PLAYER)
|
||||||
add_subdirectory(player)
|
add_subdirectory(player)
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
|
|
||||||
namespace nostalgia {
|
namespace nostalgia {
|
||||||
|
|
||||||
|
static bool modulesLoaded = false;
|
||||||
void loadModules() noexcept {
|
void loadModules() noexcept {
|
||||||
static bool done = false;
|
if (modulesLoaded) {
|
||||||
if (done) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ox::Array<foundation::Module*, 2> mods = {
|
const ox::Array<foundation::Module*, 2> mods = {
|
||||||
@ -23,7 +23,7 @@ void loadModules() noexcept {
|
|||||||
for (const auto m : mods) {
|
for (const auto m : mods) {
|
||||||
foundation::registerModule(m);
|
foundation::registerModule(m);
|
||||||
}
|
}
|
||||||
done = true;
|
modulesLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ add_library(
|
|||||||
NostalgiaCore-Common OBJECT
|
NostalgiaCore-Common OBJECT
|
||||||
gfx.cpp
|
gfx.cpp
|
||||||
module.cpp
|
module.cpp
|
||||||
|
tilesheet.cpp
|
||||||
|
typeconv.cpp
|
||||||
typestore.cpp
|
typestore.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "color.hpp"
|
#include "color.hpp"
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
#include "ptidxconv.hpp"
|
#include "ptidxconv.hpp"
|
||||||
|
#include "tilesheet.hpp"
|
||||||
|
|
||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
@ -31,431 +32,6 @@ enum class TileSheetSpace {
|
|||||||
Sprite
|
Sprite
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NostalgiaPalette {
|
|
||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaPalette";
|
|
||||||
static constexpr auto TypeVersion = 1;
|
|
||||||
ox::Vector<Color16> colors = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Palette {
|
|
||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
|
|
||||||
static constexpr auto TypeVersion = 1;
|
|
||||||
ox::Vector<Color16> colors = {};
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr Color16 color(auto idx) const noexcept {
|
|
||||||
if (idx < colors.size()) [[likely]] {
|
|
||||||
return colors[idx];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Predecessor to TileSheet, kept for backward compatibility
|
|
||||||
struct NostalgiaGraphic {
|
|
||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic";
|
|
||||||
static constexpr auto TypeVersion = 1;
|
|
||||||
int8_t bpp = 0;
|
|
||||||
// rows and columns are really only used by TileSheetEditor
|
|
||||||
int rows = 1;
|
|
||||||
int columns = 1;
|
|
||||||
ox::FileAddress defaultPalette;
|
|
||||||
Palette pal;
|
|
||||||
ox::Vector<uint8_t> pixels = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TileSheet {
|
|
||||||
using SubSheetIdx = ox::Vector<std::size_t, 4>;
|
|
||||||
|
|
||||||
struct SubSheet {
|
|
||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet.SubSheet";
|
|
||||||
static constexpr auto TypeVersion = 1;
|
|
||||||
ox::String name;
|
|
||||||
int columns = 0;
|
|
||||||
int rows = 0;
|
|
||||||
ox::Vector<SubSheet> subsheets;
|
|
||||||
ox::Vector<uint8_t> pixels;
|
|
||||||
|
|
||||||
constexpr SubSheet() noexcept = default;
|
|
||||||
constexpr SubSheet(const SubSheet &other) noexcept {
|
|
||||||
name = other.name;
|
|
||||||
columns = other.columns;
|
|
||||||
rows = other.rows;
|
|
||||||
subsheets = other.subsheets;
|
|
||||||
pixels = other.pixels;
|
|
||||||
}
|
|
||||||
constexpr SubSheet(SubSheet &&other) noexcept {
|
|
||||||
name = std::move(other.name);
|
|
||||||
columns = other.columns;
|
|
||||||
rows = other.rows;
|
|
||||||
subsheets = std::move(other.subsheets);
|
|
||||||
pixels = std::move(other.pixels);
|
|
||||||
other.name = "";
|
|
||||||
other.columns = 0;
|
|
||||||
other.rows = 0;
|
|
||||||
}
|
|
||||||
constexpr SubSheet(ox::CRStringView pName, int pColumns, int pRows, int bpp) noexcept:
|
|
||||||
name(pName), columns(pColumns), rows(pRows),
|
|
||||||
pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
|
|
||||||
}
|
|
||||||
constexpr SubSheet(ox::CRStringView pName, int pColumns, int pRows, ox::Vector<uint8_t> pPixels) noexcept:
|
|
||||||
name(pName), columns(pColumns), rows(pRows), pixels(std::move(pPixels)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr SubSheet &operator=(const SubSheet &other) noexcept = default;
|
|
||||||
|
|
||||||
constexpr SubSheet &operator=(SubSheet &&other) noexcept {
|
|
||||||
name = std::move(other.name);
|
|
||||||
columns = other.columns;
|
|
||||||
rows = other.rows;
|
|
||||||
subsheets = std::move(other.subsheets);
|
|
||||||
pixels = std::move(other.pixels);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto idx(const geo::Point &pt) const noexcept {
|
|
||||||
return ptToIdx(pt, columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads all pixels of this sheet or its children into the given pixel list
|
|
||||||
* @param pixels
|
|
||||||
*/
|
|
||||||
constexpr void readPixelsTo(ox::Vector<uint8_t> *pPixels, int8_t bpp) const noexcept {
|
|
||||||
if (subsheets.size()) {
|
|
||||||
for (auto &s: subsheets) {
|
|
||||||
s.readPixelsTo(pPixels);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (bpp == 4) {
|
|
||||||
for (auto p: this->pixels) {
|
|
||||||
pPixels->emplace_back(p & 0b1111);
|
|
||||||
pPixels->emplace_back(p >> 4);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (auto p: this->pixels) {
|
|
||||||
pPixels->emplace_back(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads all pixels of this sheet or its children into the given pixel list
|
|
||||||
* @param pixels
|
|
||||||
*/
|
|
||||||
constexpr void readPixelsTo(ox::Vector<uint8_t> *pPixels) const noexcept {
|
|
||||||
if (subsheets.size()) {
|
|
||||||
for (auto &s: subsheets) {
|
|
||||||
s.readPixelsTo(pPixels);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (auto p : this->pixels) {
|
|
||||||
pPixels->emplace_back(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr std::size_t size() const noexcept {
|
|
||||||
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr std::size_t unusedPixels() const noexcept {
|
|
||||||
std::size_t childrenSize = 0;
|
|
||||||
for (auto &c : subsheets) {
|
|
||||||
childrenSize += c.size();
|
|
||||||
}
|
|
||||||
return size() - childrenSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr uint8_t getPixel4Bpp(std::size_t idx) const noexcept {
|
|
||||||
if (idx & 1) {
|
|
||||||
return this->pixels[idx / 2] >> 4;
|
|
||||||
} else {
|
|
||||||
return this->pixels[idx / 2] & 0b0000'1111;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr uint8_t getPixel8Bpp(std::size_t idx) const noexcept {
|
|
||||||
return this->pixels[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto getPixel(int8_t pBpp, std::size_t idx) const noexcept {
|
|
||||||
if (pBpp == 4) {
|
|
||||||
return getPixel4Bpp(idx);
|
|
||||||
} else {
|
|
||||||
return getPixel8Bpp(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto getPixel4Bpp(const geo::Point &pt) const noexcept {
|
|
||||||
const auto idx = ptToIdx(pt, columns);
|
|
||||||
return getPixel4Bpp(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto getPixel8Bpp(const geo::Point &pt) const noexcept {
|
|
||||||
const auto idx = ptToIdx(pt, columns);
|
|
||||||
return getPixel8Bpp(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto getPixel(int8_t pBpp, const geo::Point &pt) const noexcept {
|
|
||||||
const auto idx = ptToIdx(pt, columns);
|
|
||||||
return getPixel(pBpp, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto walkPixels(int8_t pBpp, auto callback) const noexcept {
|
|
||||||
if (pBpp == 4) {
|
|
||||||
const auto pixelCnt = ox::min<std::size_t>(static_cast<std::size_t>(columns * rows * PixelsPerTile) / 2,
|
|
||||||
pixels.size());
|
|
||||||
//oxAssert(pixels.size() == pixelCnt, "Pixel count does not match rows and columns");
|
|
||||||
for (std::size_t i = 0; i < pixelCnt; ++i) {
|
|
||||||
const auto colorIdx1 = pixels[i] & 0xF;
|
|
||||||
const auto colorIdx2 = pixels[i] >> 4;
|
|
||||||
callback(i * 2 + 0, colorIdx1);
|
|
||||||
callback(i * 2 + 1, colorIdx2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const auto pixelCnt = ox::min<std::size_t>(static_cast<std::size_t>(columns * rows * PixelsPerTile),
|
|
||||||
pixels.size());
|
|
||||||
for (std::size_t i = 0; i < pixelCnt; ++i) {
|
|
||||||
const auto p = pixels[i];
|
|
||||||
callback(i, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept {
|
|
||||||
auto &pixel = this->pixels[idx / 2];
|
|
||||||
if (pBpp == 4) {
|
|
||||||
if (idx & 1) {
|
|
||||||
pixel = (pixel & 0b0000'1111) | (palIdx << 4);
|
|
||||||
} else {
|
|
||||||
pixel = (pixel & 0b1111'0000) | (palIdx);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pixel = palIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void setPixel(int8_t pBpp, const geo::Point &pt, uint8_t palIdx) noexcept {
|
|
||||||
const auto idx = ptToIdx(pt, columns);
|
|
||||||
setPixel(pBpp, idx, palIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto setPixelCount(int8_t pBpp, std::size_t cnt) noexcept {
|
|
||||||
switch (pBpp) {
|
|
||||||
case 4:
|
|
||||||
pixels.resize(cnt / 2);
|
|
||||||
return OxError(0);
|
|
||||||
case 8:
|
|
||||||
pixels.resize(cnt);
|
|
||||||
return OxError(0);
|
|
||||||
default:
|
|
||||||
return OxError(1, "Invalid pBpp used for TileSheet::SubSheet::setPixelCount");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a count of the pixels in this sheet, and not that of its children.
|
|
||||||
* @param pBpp bits per pixel, need for knowing how to count the pixels
|
|
||||||
* @return a count of the pixels in this sheet
|
|
||||||
*/
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto pixelCnt(int8_t pBpp) const noexcept {
|
|
||||||
return pBpp == 4 ? pixels.size() * 2 : pixels.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
|
|
||||||
static constexpr auto TypeVersion = 2;
|
|
||||||
int8_t bpp = 4;
|
|
||||||
ox::FileAddress defaultPalette;
|
|
||||||
SubSheet subsheet{"Root", 1, 1, bpp};
|
|
||||||
|
|
||||||
constexpr TileSheet() noexcept = default;
|
|
||||||
TileSheet(const TileSheet &other) noexcept = default;
|
|
||||||
inline TileSheet(TileSheet &&other) noexcept:
|
|
||||||
bpp(other.bpp),
|
|
||||||
defaultPalette(std::move(other.defaultPalette)),
|
|
||||||
subsheet(std::move(other.subsheet)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto &operator=(const TileSheet &other) noexcept {
|
|
||||||
if (this != &other) {
|
|
||||||
bpp = other.bpp;
|
|
||||||
defaultPalette = other.defaultPalette;
|
|
||||||
subsheet = other.subsheet; } return *this;
|
|
||||||
}
|
|
||||||
inline auto &operator=(TileSheet &&other) noexcept {
|
|
||||||
bpp = other.bpp;
|
|
||||||
defaultPalette = std::move(other.defaultPalette);
|
|
||||||
subsheet = std::move(other.subsheet);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto validateSubSheetIdx(const SubSheetIdx &pIdx, std::size_t pIdxIt, const SubSheet *pSubsheet) noexcept {
|
|
||||||
if (pIdxIt == pIdx.size()) {
|
|
||||||
return pIdx;
|
|
||||||
}
|
|
||||||
const auto currentIdx = pIdx[pIdxIt];
|
|
||||||
if (pSubsheet->subsheets.size() <= currentIdx) {
|
|
||||||
auto out = pIdx;
|
|
||||||
if (pSubsheet->subsheets.size()) {
|
|
||||||
out.back().value = pSubsheet->subsheets.size() - 1;
|
|
||||||
} else {
|
|
||||||
out.pop_back();
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
return validateSubSheetIdx(pIdx, pIdxIt + 1, &pSubsheet->subsheets[pIdx[pIdxIt]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* validateSubSheetIdx takes a SubSheetIdx and moves the index to the
|
|
||||||
* preceding or parent sheet if the current corresponding sheet does
|
|
||||||
* not exist.
|
|
||||||
* @param idx SubSheetIdx to validate and correct
|
|
||||||
* @return a valid version of idx
|
|
||||||
*/
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto validateSubSheetIdx(const SubSheetIdx &idx) noexcept {
|
|
||||||
return validateSubSheetIdx(idx, 0, &subsheet);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr static const auto &getSubSheet(const SubSheetIdx &idx, std::size_t idxIt,
|
|
||||||
const SubSheet *pSubsheet) noexcept {
|
|
||||||
if (idxIt == idx.size()) {
|
|
||||||
return *pSubsheet;
|
|
||||||
}
|
|
||||||
const auto currentIdx = idx[idxIt];
|
|
||||||
if (pSubsheet->subsheets.size() < currentIdx) {
|
|
||||||
return *pSubsheet;
|
|
||||||
}
|
|
||||||
return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[currentIdx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr static auto &getSubSheet(const SubSheetIdx &idx, std::size_t idxIt, SubSheet *pSubsheet) noexcept {
|
|
||||||
if (idxIt == idx.size()) {
|
|
||||||
return *pSubsheet;
|
|
||||||
}
|
|
||||||
return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr const auto &getSubSheet(const SubSheetIdx &idx) const noexcept {
|
|
||||||
return getSubSheet(idx, 0, &subsheet);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto &getSubSheet(const SubSheetIdx &idx) noexcept {
|
|
||||||
return getSubSheet(idx, 0, &subsheet);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ox::Error addSubSheet(const SubSheetIdx &idx) noexcept {
|
|
||||||
auto &parent = getSubSheet(idx);
|
|
||||||
if (parent.subsheets.size() < 2) {
|
|
||||||
parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1, bpp);
|
|
||||||
} else {
|
|
||||||
parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, bpp);
|
|
||||||
parent.subsheets.emplace_back("Subsheet 1", 1, 1, bpp);
|
|
||||||
}
|
|
||||||
return OxError(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr static auto rmSubSheet(const SubSheetIdx &idx, std::size_t idxIt, SubSheet *pSubsheet) noexcept {
|
|
||||||
if (idxIt == idx.size() - 1) {
|
|
||||||
return pSubsheet->subsheets.erase(idx[idxIt]).error;
|
|
||||||
}
|
|
||||||
return rmSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto rmSubSheet(const SubSheetIdx &idx) noexcept {
|
|
||||||
return rmSubSheet(idx, 0, &subsheet);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto getPixel4Bpp(const geo::Point &pt, const SubSheetIdx &subsheetIdx) const noexcept {
|
|
||||||
oxAssert(bpp == 4, "TileSheetV1::getPixel4Bpp: wrong bpp");
|
|
||||||
auto &s = this->getSubSheet(subsheetIdx);
|
|
||||||
const auto idx = ptToIdx(pt, s.columns);
|
|
||||||
return s.getPixel4Bpp(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr auto getPixel8Bpp(const geo::Point &pt, const SubSheetIdx &subsheetIdx) const noexcept {
|
|
||||||
oxAssert(bpp == 8, "TileSheetV1::getPixel8Bpp: wrong bpp");
|
|
||||||
auto &s = this->getSubSheet(subsheetIdx);
|
|
||||||
const auto idx = ptToIdx(pt, s.columns);
|
|
||||||
return s.getPixel8Bpp(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
auto pixels() const noexcept {
|
|
||||||
ox::Vector<uint8_t> out;
|
|
||||||
subsheet.readPixelsTo(&out);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompactTileSheet {
|
|
||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet";
|
|
||||||
static constexpr auto TypeVersion = 1;
|
|
||||||
int8_t bpp = 0;
|
|
||||||
ox::FileAddress defaultPalette;
|
|
||||||
ox::Vector<uint8_t> pixels = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
oxModelBegin(NostalgiaPalette)
|
|
||||||
oxModelField(colors)
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
oxModelBegin(Palette)
|
|
||||||
oxModelField(colors)
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
oxModelBegin(NostalgiaGraphic)
|
|
||||||
oxModelField(bpp)
|
|
||||||
oxModelField(rows)
|
|
||||||
oxModelField(columns)
|
|
||||||
oxModelField(defaultPalette)
|
|
||||||
oxModelField(pal)
|
|
||||||
oxModelField(pixels)
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
oxModelBegin(TileSheet::SubSheet)
|
|
||||||
oxModelField(name);
|
|
||||||
oxModelField(rows);
|
|
||||||
oxModelField(columns);
|
|
||||||
oxModelField(subsheets)
|
|
||||||
oxModelField(pixels)
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
oxModelBegin(TileSheet)
|
|
||||||
oxModelField(bpp)
|
|
||||||
oxModelField(defaultPalette)
|
|
||||||
oxModelField(subsheet)
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
oxModelBegin(CompactTileSheet)
|
|
||||||
oxModelField(bpp)
|
|
||||||
oxModelField(defaultPalette)
|
|
||||||
oxModelField(pixels)
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
struct Sprite {
|
struct Sprite {
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
int x = 0;
|
int x = 0;
|
||||||
@ -511,8 +87,8 @@ void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbb) noexcept;
|
|||||||
/**
|
/**
|
||||||
* @param section describes which section of the selected TileSheetSpace to use (e.g. MEM_PALLETE_BG[section])
|
* @param section describes which section of the selected TileSheetSpace to use (e.g. MEM_PALLETE_BG[section])
|
||||||
*/
|
*/
|
||||||
ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &tilesheet,
|
ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &tilesheetAddr,
|
||||||
const ox::FileAddress &palette = nullptr) noexcept;
|
const ox::FileAddress &paletteAddr = nullptr) noexcept;
|
||||||
|
|
||||||
ox::Error loadSpriteTileSheet(Context *ctx,
|
ox::Error loadSpriteTileSheet(Context *ctx,
|
||||||
const ox::FileAddress &tilesheetAddr,
|
const ox::FileAddress &tilesheetAddr,
|
||||||
|
@ -15,14 +15,14 @@
|
|||||||
namespace nostalgia::core {
|
namespace nostalgia::core {
|
||||||
|
|
||||||
ox::Result<ox::UPtr<Context>> init(ox::UPtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
|
ox::Result<ox::UPtr<Context>> init(ox::UPtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
|
||||||
auto ctx = foundation::init<Context>(std::move(fs), appName);
|
oxRequireM(ctx, foundation::init<Context>(std::move(fs), appName));
|
||||||
const auto id = ox::make<GlfwImplData>();
|
const auto id = ox::make<GlfwImplData>();
|
||||||
ctx->setWindowerData(id);
|
ctx->setWindowerData(id);
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
id->startTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
id->startTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||||
glfwInit();
|
glfwInit();
|
||||||
oxReturnError(initGfx(ctx.get()));
|
oxReturnError(initGfx(ctx.get()));
|
||||||
return ctx;
|
return std::move(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error run(Context *ctx) noexcept {
|
ox::Error run(Context *ctx) noexcept {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <ox/model/model.hpp>
|
#include <ox/model/model.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/foundation/asset.hpp>
|
||||||
#include <nostalgia/foundation/module.hpp>
|
#include <nostalgia/foundation/module.hpp>
|
||||||
|
|
||||||
#include "gfx.hpp"
|
#include "gfx.hpp"
|
||||||
@ -15,11 +16,23 @@ namespace nostalgia::core {
|
|||||||
|
|
||||||
CoreModule CoreModule::mod;
|
CoreModule CoreModule::mod;
|
||||||
|
|
||||||
ox::Vector<foundation::BaseConverter*> CoreModule::converters() const noexcept {
|
ox::Vector<foundation::TypeDescGenerator> CoreModule::types() const noexcept {
|
||||||
|
return {
|
||||||
|
foundation::generateTypeDesc<TileSheetV1>,
|
||||||
|
foundation::generateTypeDesc<TileSheetV2>,
|
||||||
|
foundation::generateTypeDesc<TileSheet>,
|
||||||
|
foundation::generateTypeDesc<CompactTileSheet>,
|
||||||
|
foundation::generateTypeDesc<NostalgiaPalette>,
|
||||||
|
foundation::generateTypeDesc<Palette>,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Vector<const foundation::BaseConverter*> CoreModule::converters() const noexcept {
|
||||||
return {
|
return {
|
||||||
&nostalgiaPaletteToPaletteConverter,
|
&nostalgiaPaletteToPaletteConverter,
|
||||||
&nostalgiaGraphicToTileSheetConverter,
|
&nostalgiaGraphicToTileSheetConverter,
|
||||||
&tileSheetToCompactTileSheetConverter,
|
&tileSheetToCompactTileSheetConverter,
|
||||||
|
&tileSheetV2ToTileSheetConverter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,11 +40,13 @@ ox::Vector<foundation::PackTransform> CoreModule::packTransforms() const noexcep
|
|||||||
return {
|
return {
|
||||||
// convert tilesheets to CompactTileSheets
|
// convert tilesheets to CompactTileSheets
|
||||||
[](foundation::Context *ctx, ox::Buffer *buff) -> ox::Error {
|
[](foundation::Context *ctx, ox::Buffer *buff) -> ox::Error {
|
||||||
oxRequire(hdr, ox::readClawHeader(*buff));
|
oxRequire(hdr, foundation::readAssetHeader(*buff));
|
||||||
const auto typeId = ox::buildTypeId(hdr.typeName, hdr.typeVersion);
|
const auto typeId = ox::buildTypeId(hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams);
|
||||||
if (typeId == ox::buildTypeId<TileSheet>() ||
|
if (typeId == ox::buildTypeId<TileSheetV1>() ||
|
||||||
typeId == ox::buildTypeId<NostalgiaGraphic>()) {
|
typeId == ox::buildTypeId<TileSheetV2>() ||
|
||||||
oxReturnError(foundation::convertBuffToBuff<core::CompactTileSheet>(ctx, *buff, ox::ClawFormat::Metal).moveTo(buff));
|
typeId == ox::buildTypeId<TileSheet>()) {
|
||||||
|
oxReturnError(foundation::convertBuffToBuff<core::CompactTileSheet>(
|
||||||
|
ctx, *buff, ox::ClawFormat::Metal).moveTo(buff));
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
@ -12,14 +12,17 @@ namespace nostalgia::core {
|
|||||||
|
|
||||||
class CoreModule: public foundation::Module {
|
class CoreModule: public foundation::Module {
|
||||||
private:
|
private:
|
||||||
mutable NostalgiaPaletteToPaletteConverter nostalgiaPaletteToPaletteConverter;
|
NostalgiaPaletteToPaletteConverter nostalgiaPaletteToPaletteConverter;
|
||||||
mutable NostalgiaGraphicToTileSheetConverter nostalgiaGraphicToTileSheetConverter;
|
TileSheetV1ToTileSheetConverter nostalgiaGraphicToTileSheetConverter;
|
||||||
mutable TileSheetToCompactTileSheetConverter tileSheetToCompactTileSheetConverter;
|
TileSheetToCompactTileSheetConverter tileSheetToCompactTileSheetConverter;
|
||||||
|
TileSheetV2ToTileSheetConverter tileSheetV2ToTileSheetConverter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static CoreModule mod;
|
static CoreModule mod;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::Vector<foundation::BaseConverter*> converters() const noexcept override;
|
ox::Vector<foundation::TypeDescGenerator> types() const noexcept override;
|
||||||
|
[[nodiscard]]
|
||||||
|
ox::Vector<const foundation::BaseConverter*> converters() const noexcept override;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::Vector<foundation::PackTransform> packTransforms() const noexcept override;
|
ox::Vector<foundation::PackTransform> packTransforms() const noexcept override;
|
||||||
};
|
};
|
||||||
|
47
src/nostalgia/core/palette.hpp
Normal file
47
src/nostalgia/core/palette.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/array.hpp>
|
||||||
|
#include <ox/std/types.hpp>
|
||||||
|
#include <ox/model/def.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/geo/point.hpp>
|
||||||
|
#include <nostalgia/geo/size.hpp>
|
||||||
|
|
||||||
|
#include "color.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
#include "ptidxconv.hpp"
|
||||||
|
|
||||||
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
struct NostalgiaPalette {
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaPalette";
|
||||||
|
static constexpr auto TypeVersion = 1;
|
||||||
|
ox::Vector<Color16> colors = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Palette {
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
|
||||||
|
static constexpr auto TypeVersion = 1;
|
||||||
|
ox::Vector<Color16> colors = {};
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr Color16 color(auto idx) const noexcept {
|
||||||
|
if (idx < colors.size()) [[likely]] {
|
||||||
|
return colors[idx];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
oxModelBegin(NostalgiaPalette)
|
||||||
|
oxModelField(colors)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
oxModelBegin(Palette)
|
||||||
|
oxModelField(colors)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
}
|
@ -87,6 +87,9 @@ void TileSheetEditorImGui::keyStateChanged(core::Key key, bool down) {
|
|||||||
if (!down) {
|
if (!down) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (key == core::Key::Escape) {
|
||||||
|
m_subsheetEditor.close();
|
||||||
|
}
|
||||||
auto pal = model()->pal();
|
auto pal = model()->pal();
|
||||||
if (pal) {
|
if (pal) {
|
||||||
const auto colorCnt = pal->colors.size();
|
const auto colorCnt = pal->colors.size();
|
||||||
@ -427,4 +430,8 @@ void TileSheetEditorImGui::SubSheetEditor::draw() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileSheetEditorImGui::SubSheetEditor::close() noexcept {
|
||||||
|
m_show = false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,10 +40,11 @@ class TileSheetEditorImGui: public studio::BaseEditor {
|
|||||||
m_rows = rows;
|
m_rows = rows;
|
||||||
}
|
}
|
||||||
void draw() noexcept;
|
void draw() noexcept;
|
||||||
|
void close() noexcept;
|
||||||
};
|
};
|
||||||
std::size_t m_selectedPaletteIdx = 0;
|
std::size_t m_selectedPaletteIdx = 0;
|
||||||
Context *m_ctx = nullptr;
|
Context *m_ctx = nullptr;
|
||||||
ox::Vector<ox::String> m_paletteList{};
|
ox::Vector<ox::String> m_paletteList;
|
||||||
SubSheetEditor m_subsheetEditor;
|
SubSheetEditor m_subsheetEditor;
|
||||||
ox::String m_itemPath;
|
ox::String m_itemPath;
|
||||||
ox::String m_itemName;
|
ox::String m_itemName;
|
||||||
|
@ -256,12 +256,12 @@ class AddSubSheetCommand: public TileSheetCommand {
|
|||||||
auto &parent = m_img->getSubSheet(m_parentIdx);
|
auto &parent = m_img->getSubSheet(m_parentIdx);
|
||||||
if (m_addedSheets.size() < 2) {
|
if (m_addedSheets.size() < 2) {
|
||||||
auto i = parent.subsheets.size();
|
auto i = parent.subsheets.size();
|
||||||
parent.subsheets.emplace_back(ox::sfmt("Subsheet {}", i), 1, 1, m_img->bpp);
|
parent.subsheets.emplace_back(m_img->idIt++, ox::sfmt("Subsheet {}", i), 1, 1, m_img->bpp);
|
||||||
} else {
|
} else {
|
||||||
parent.subsheets.emplace_back("Subsheet 0", parent.columns, parent.rows, std::move(parent.pixels));
|
parent.subsheets.emplace_back(m_img->idIt++, "Subsheet 0", parent.columns, parent.rows, std::move(parent.pixels));
|
||||||
parent.rows = 0;
|
parent.rows = 0;
|
||||||
parent.columns = 0;
|
parent.columns = 0;
|
||||||
parent.subsheets.emplace_back("Subsheet 1", 1, 1, m_img->bpp);
|
parent.subsheets.emplace_back(m_img->idIt++, "Subsheet 1", 1, 1, m_img->bpp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,11 +507,11 @@ class PaletteChangeCommand: public TileSheetCommand {
|
|||||||
ox::FileAddress m_newPalette;
|
ox::FileAddress m_newPalette;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PaletteChangeCommand(const TileSheet::SubSheetIdx &idx, TileSheet *img, const ox::String &newPalette) noexcept {
|
PaletteChangeCommand(const TileSheet::SubSheetIdx &idx, TileSheet *img, ox::CRStringView newPalette) noexcept {
|
||||||
m_idx = idx;
|
m_idx = idx;
|
||||||
m_img = img;
|
m_img = img;
|
||||||
m_oldPalette = m_img->defaultPalette;
|
m_oldPalette = m_img->defaultPalette;
|
||||||
m_newPalette = ox::FileAddress(newPalette);
|
m_newPalette = ox::FileAddress(ox::sfmt<ox::BString<43>>("uuid://{}", newPalette));
|
||||||
}
|
}
|
||||||
|
|
||||||
void redo() noexcept final {
|
void redo() noexcept final {
|
||||||
@ -597,12 +597,27 @@ void TileSheetEditorModel::paste() {
|
|||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
const ox::FileAddress &TileSheetEditorModel::palPath() const noexcept {
|
ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||||
return m_img.defaultPalette;
|
auto [path, err] = m_img.defaultPalette.getPath();
|
||||||
|
if (err) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
constexpr ox::StringView uuidPrefix = "uuid://";
|
||||||
|
if (ox::beginsWith(path, uuidPrefix)) {
|
||||||
|
auto uuid = ox::StringView(path + uuidPrefix.bytes(), ox_strlen(path) - uuidPrefix.bytes());
|
||||||
|
auto out = m_ctx->uuidToPath.at(uuid);
|
||||||
|
if (out.error) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return *out.value;
|
||||||
|
} else {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error TileSheetEditorModel::setPalette(const ox::String &path) noexcept {
|
ox::Error TileSheetEditorModel::setPalette(const ox::String &path) noexcept {
|
||||||
pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), &m_img, path));
|
oxRequire(uuid, m_ctx->pathToUuid.at(path));
|
||||||
|
pushCommand(ox::make<PaletteChangeCommand>(activeSubSheetIdx(), &m_img, uuid->toString()));
|
||||||
return OxError(0);
|
return OxError(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
|||||||
constexpr const Palette *pal() const noexcept;
|
constexpr const Palette *pal() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
const ox::FileAddress &palPath() const noexcept;
|
ox::StringView palPath() const noexcept;
|
||||||
|
|
||||||
ox::Error setPalette(const ox::String &path) noexcept;
|
ox::Error setPalette(const ox::String &path) noexcept;
|
||||||
|
|
||||||
|
9
src/nostalgia/core/tilesheet.cpp
Normal file
9
src/nostalgia/core/tilesheet.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tilesheet.hpp"
|
||||||
|
|
||||||
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
}
|
566
src/nostalgia/core/tilesheet.hpp
Normal file
566
src/nostalgia/core/tilesheet.hpp
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/array.hpp>
|
||||||
|
#include <ox/std/types.hpp>
|
||||||
|
#include <ox/model/def.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/geo/point.hpp>
|
||||||
|
#include <nostalgia/geo/size.hpp>
|
||||||
|
|
||||||
|
#include "color.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
#include "ptidxconv.hpp"
|
||||||
|
#include "palette.hpp"
|
||||||
|
|
||||||
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
// Predecessor to TileSheet, kept for backward compatibility
|
||||||
|
struct TileSheetV1 {
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.NostalgiaGraphic";
|
||||||
|
static constexpr auto TypeVersion = 1;
|
||||||
|
int8_t bpp = 0;
|
||||||
|
// rows and columns are really only used by TileSheetEditor
|
||||||
|
int rows = 1;
|
||||||
|
int columns = 1;
|
||||||
|
ox::FileAddress defaultPalette;
|
||||||
|
Palette pal;
|
||||||
|
ox::Vector<uint8_t> pixels = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TileSheetV2 {
|
||||||
|
using SubSheetIdx = ox::Vector<std::size_t, 4>;
|
||||||
|
|
||||||
|
struct SubSheet {
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet.SubSheet";
|
||||||
|
static constexpr auto TypeVersion = 1;
|
||||||
|
ox::String name;
|
||||||
|
int columns = 0;
|
||||||
|
int rows = 0;
|
||||||
|
ox::Vector<SubSheet> subsheets;
|
||||||
|
ox::Vector<uint8_t> pixels;
|
||||||
|
constexpr SubSheet() noexcept = default;
|
||||||
|
constexpr SubSheet(ox::CRStringView pName, int pColumns, int pRows, int bpp) noexcept:
|
||||||
|
name(pName), columns(pColumns), rows(pRows),
|
||||||
|
pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
|
||||||
|
static constexpr auto TypeVersion = 2;
|
||||||
|
int8_t bpp = 4;
|
||||||
|
ox::FileAddress defaultPalette;
|
||||||
|
SubSheet subsheet{"Root", 1, 1, bpp};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
using SubSheetId = int32_t;
|
||||||
|
|
||||||
|
struct TileSheet {
|
||||||
|
using SubSheetIdx = ox::Vector<std::size_t, 4>;
|
||||||
|
|
||||||
|
struct SubSheet {
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet.SubSheet";
|
||||||
|
static constexpr auto TypeVersion = 3;
|
||||||
|
SubSheetId id = 0;
|
||||||
|
ox::String name;
|
||||||
|
int columns = 0;
|
||||||
|
int rows = 0;
|
||||||
|
ox::Vector<SubSheet> subsheets;
|
||||||
|
ox::Vector<uint8_t> pixels;
|
||||||
|
|
||||||
|
constexpr SubSheet() noexcept = default;
|
||||||
|
constexpr SubSheet(const SubSheet &other) noexcept {
|
||||||
|
id = other.id;
|
||||||
|
name = other.name;
|
||||||
|
columns = other.columns;
|
||||||
|
rows = other.rows;
|
||||||
|
subsheets = other.subsheets;
|
||||||
|
pixels = other.pixels;
|
||||||
|
}
|
||||||
|
constexpr SubSheet(SubSheet &&other) noexcept {
|
||||||
|
id = other.id;
|
||||||
|
name = std::move(other.name);
|
||||||
|
columns = other.columns;
|
||||||
|
rows = other.rows;
|
||||||
|
subsheets = std::move(other.subsheets);
|
||||||
|
pixels = std::move(other.pixels);
|
||||||
|
other.name = "";
|
||||||
|
other.columns = 0;
|
||||||
|
other.rows = 0;
|
||||||
|
}
|
||||||
|
constexpr SubSheet(
|
||||||
|
SubSheetId pId,
|
||||||
|
ox::CRStringView pName,
|
||||||
|
int pColumns,
|
||||||
|
int pRows,
|
||||||
|
int bpp) noexcept:
|
||||||
|
id(pId), name(pName), columns(pColumns), rows(pRows),
|
||||||
|
pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
|
||||||
|
}
|
||||||
|
constexpr SubSheet(
|
||||||
|
SubSheetId pId,
|
||||||
|
ox::CRStringView pName,
|
||||||
|
int pColumns,
|
||||||
|
int pRows,
|
||||||
|
ox::Vector<uint8_t> pPixels) noexcept:
|
||||||
|
id(pId), name(pName), columns(pColumns), rows(pRows), pixels(std::move(pPixels)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr SubSheet &operator=(const SubSheet &other) noexcept = default;
|
||||||
|
|
||||||
|
constexpr SubSheet &operator=(SubSheet &&other) noexcept {
|
||||||
|
name = std::move(other.name);
|
||||||
|
columns = other.columns;
|
||||||
|
rows = other.rows;
|
||||||
|
subsheets = std::move(other.subsheets);
|
||||||
|
pixels = std::move(other.pixels);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto idx(const geo::Point &pt) const noexcept {
|
||||||
|
return ptToIdx(pt, columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads all pixels of this sheet or its children into the given pixel list
|
||||||
|
* @param pixels
|
||||||
|
*/
|
||||||
|
constexpr void readPixelsTo(ox::Vector<uint8_t> *pPixels, int8_t bpp) const noexcept {
|
||||||
|
if (subsheets.size()) {
|
||||||
|
for (auto &s: subsheets) {
|
||||||
|
s.readPixelsTo(pPixels);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (bpp == 4) {
|
||||||
|
for (auto p: this->pixels) {
|
||||||
|
pPixels->emplace_back(p & 0b1111);
|
||||||
|
pPixels->emplace_back(p >> 4);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto p: this->pixels) {
|
||||||
|
pPixels->emplace_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads all pixels of this sheet or its children into the given pixel list
|
||||||
|
* @param pixels
|
||||||
|
*/
|
||||||
|
constexpr void readPixelsTo(ox::Vector<uint8_t> *pPixels) const noexcept {
|
||||||
|
if (subsheets.size()) {
|
||||||
|
for (auto &s: subsheets) {
|
||||||
|
s.readPixelsTo(pPixels);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto p : this->pixels) {
|
||||||
|
pPixels->emplace_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr std::size_t size() const noexcept {
|
||||||
|
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr std::size_t unusedPixels() const noexcept {
|
||||||
|
std::size_t childrenSize = 0;
|
||||||
|
for (auto &c : subsheets) {
|
||||||
|
childrenSize += c.size();
|
||||||
|
}
|
||||||
|
return size() - childrenSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr uint8_t getPixel4Bpp(std::size_t idx) const noexcept {
|
||||||
|
if (idx & 1) {
|
||||||
|
return this->pixels[idx / 2] >> 4;
|
||||||
|
} else {
|
||||||
|
return this->pixels[idx / 2] & 0b0000'1111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr uint8_t getPixel8Bpp(std::size_t idx) const noexcept {
|
||||||
|
return this->pixels[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto getPixel(int8_t pBpp, std::size_t idx) const noexcept {
|
||||||
|
if (pBpp == 4) {
|
||||||
|
return getPixel4Bpp(idx);
|
||||||
|
} else {
|
||||||
|
return getPixel8Bpp(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto getPixel4Bpp(const geo::Point &pt) const noexcept {
|
||||||
|
const auto idx = ptToIdx(pt, columns);
|
||||||
|
return getPixel4Bpp(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto getPixel8Bpp(const geo::Point &pt) const noexcept {
|
||||||
|
const auto idx = ptToIdx(pt, columns);
|
||||||
|
return getPixel8Bpp(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto getPixel(int8_t pBpp, const geo::Point &pt) const noexcept {
|
||||||
|
const auto idx = ptToIdx(pt, columns);
|
||||||
|
return getPixel(pBpp, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto walkPixels(int8_t pBpp, auto callback) const noexcept {
|
||||||
|
if (pBpp == 4) {
|
||||||
|
const auto pixelCnt = ox::min<std::size_t>(static_cast<std::size_t>(columns * rows * PixelsPerTile) / 2,
|
||||||
|
pixels.size());
|
||||||
|
//oxAssert(pixels.size() == pixelCnt, "Pixel count does not match rows and columns");
|
||||||
|
for (std::size_t i = 0; i < pixelCnt; ++i) {
|
||||||
|
const auto colorIdx1 = pixels[i] & 0xF;
|
||||||
|
const auto colorIdx2 = pixels[i] >> 4;
|
||||||
|
callback(i * 2 + 0, colorIdx1);
|
||||||
|
callback(i * 2 + 1, colorIdx2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto pixelCnt = ox::min<std::size_t>(static_cast<std::size_t>(columns * rows * PixelsPerTile),
|
||||||
|
pixels.size());
|
||||||
|
for (std::size_t i = 0; i < pixelCnt; ++i) {
|
||||||
|
const auto p = pixels[i];
|
||||||
|
callback(i, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept {
|
||||||
|
auto &pixel = this->pixels[idx / 2];
|
||||||
|
if (pBpp == 4) {
|
||||||
|
if (idx & 1) {
|
||||||
|
pixel = (pixel & 0b0000'1111) | (palIdx << 4);
|
||||||
|
} else {
|
||||||
|
pixel = (pixel & 0b1111'0000) | (palIdx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pixel = palIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void setPixel(int8_t pBpp, const geo::Point &pt, uint8_t palIdx) noexcept {
|
||||||
|
const auto idx = ptToIdx(pt, columns);
|
||||||
|
setPixel(pBpp, idx, palIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto setPixelCount(int8_t pBpp, std::size_t cnt) noexcept {
|
||||||
|
switch (pBpp) {
|
||||||
|
case 4:
|
||||||
|
pixels.resize(cnt / 2);
|
||||||
|
return OxError(0);
|
||||||
|
case 8:
|
||||||
|
pixels.resize(cnt);
|
||||||
|
return OxError(0);
|
||||||
|
default:
|
||||||
|
return OxError(1, "Invalid pBpp used for TileSheet::SubSheet::setPixelCount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a count of the pixels in this sheet, and not that of its children.
|
||||||
|
* @param pBpp bits per pixel, need for knowing how to count the pixels
|
||||||
|
* @return a count of the pixels in this sheet
|
||||||
|
*/
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr unsigned pixelCnt(int8_t pBpp) const noexcept {
|
||||||
|
return pBpp == 4 ? pixels.size() * 2 : pixels.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the offset in tiles of the desired subsheet.
|
||||||
|
*/
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr ox::Result<unsigned> getTileOffset(
|
||||||
|
const auto &pNamePath,
|
||||||
|
int8_t pBpp,
|
||||||
|
std::size_t pIt = 0,
|
||||||
|
unsigned pCurrentTotal = 0) const noexcept {
|
||||||
|
// pIt == pNamePath.size() - 1 &&
|
||||||
|
if (name != pNamePath[pIt]) {
|
||||||
|
return OxError(2, "Wrong branch");
|
||||||
|
}
|
||||||
|
if (pIt == pNamePath.size() - 1) {
|
||||||
|
return pCurrentTotal;
|
||||||
|
}
|
||||||
|
for (auto &sub : subsheets) {
|
||||||
|
auto [offset, err] = sub.getTileOffset(
|
||||||
|
pNamePath, pBpp, pIt + 1, pCurrentTotal);
|
||||||
|
if (!err) {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
pCurrentTotal += sub.pixelCnt(pBpp) / PixelsPerTile;
|
||||||
|
}
|
||||||
|
return OxError(1, "SubSheet not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr ox::Result<SubSheetId> getIdFor(
|
||||||
|
const auto &pNamePath,
|
||||||
|
std::size_t pIt = 0) const noexcept {
|
||||||
|
for (auto &sub : subsheets) {
|
||||||
|
if (sub.name == pNamePath[pIt]) {
|
||||||
|
if (pIt == pNamePath.size()) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
return getIdFor(pNamePath, pIt + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OxError(1, "SubSheet not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr ox::Result<ox::StringView> getNameFor(SubSheetId pId) const noexcept {
|
||||||
|
if (id == pId) {
|
||||||
|
return ox::StringView(name);
|
||||||
|
}
|
||||||
|
for (const auto &sub : subsheets) {
|
||||||
|
const auto [name, err] = sub.getNameFor(pId);
|
||||||
|
if (!err) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OxError(1, "SubSheet not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
|
||||||
|
static constexpr auto TypeVersion = 3;
|
||||||
|
int8_t bpp = 4;
|
||||||
|
SubSheetId idIt = 0;
|
||||||
|
ox::FileAddress defaultPalette;
|
||||||
|
SubSheet subsheet{0, "Root", 1, 1, bpp};
|
||||||
|
|
||||||
|
constexpr TileSheet() noexcept = default;
|
||||||
|
TileSheet(const TileSheet &other) noexcept = default;
|
||||||
|
inline TileSheet(TileSheet &&other) noexcept:
|
||||||
|
bpp(other.bpp),
|
||||||
|
idIt(other.idIt),
|
||||||
|
defaultPalette(std::move(other.defaultPalette)),
|
||||||
|
subsheet(std::move(other.subsheet)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto &operator=(const TileSheet &other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
bpp = other.bpp;
|
||||||
|
idIt = other.idIt;
|
||||||
|
defaultPalette = other.defaultPalette;
|
||||||
|
subsheet = other.subsheet;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline auto &operator=(TileSheet &&other) noexcept {
|
||||||
|
bpp = other.bpp;
|
||||||
|
idIt = other.idIt;
|
||||||
|
defaultPalette = std::move(other.defaultPalette);
|
||||||
|
subsheet = std::move(other.subsheet);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto validateSubSheetIdx(
|
||||||
|
const SubSheetIdx &pIdx,
|
||||||
|
std::size_t pIdxIt,
|
||||||
|
const SubSheet *pSubsheet) noexcept {
|
||||||
|
if (pIdxIt == pIdx.size()) {
|
||||||
|
return pIdx;
|
||||||
|
}
|
||||||
|
const auto currentIdx = pIdx[pIdxIt];
|
||||||
|
if (pSubsheet->subsheets.size() <= currentIdx) {
|
||||||
|
auto out = pIdx;
|
||||||
|
if (pSubsheet->subsheets.size()) {
|
||||||
|
out.back().value = pSubsheet->subsheets.size() - 1;
|
||||||
|
} else {
|
||||||
|
out.pop_back();
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
return validateSubSheetIdx(pIdx, pIdxIt + 1, &pSubsheet->subsheets[pIdx[pIdxIt]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* validateSubSheetIdx takes a SubSheetIdx and moves the index to the
|
||||||
|
* preceding or parent sheet if the current corresponding sheet does
|
||||||
|
* not exist.
|
||||||
|
* @param idx SubSheetIdx to validate and correct
|
||||||
|
* @return a valid version of idx
|
||||||
|
*/
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto validateSubSheetIdx(const SubSheetIdx &idx) noexcept {
|
||||||
|
return validateSubSheetIdx(idx, 0, &subsheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr static const SubSheet &getSubSheet(
|
||||||
|
const SubSheetIdx &idx,
|
||||||
|
std::size_t idxIt,
|
||||||
|
const SubSheet *pSubsheet) noexcept {
|
||||||
|
if (idxIt == idx.size()) {
|
||||||
|
return *pSubsheet;
|
||||||
|
}
|
||||||
|
const auto currentIdx = idx[idxIt];
|
||||||
|
if (pSubsheet->subsheets.size() < currentIdx) {
|
||||||
|
return *pSubsheet;
|
||||||
|
}
|
||||||
|
return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[currentIdx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr static SubSheet &getSubSheet(
|
||||||
|
const SubSheetIdx &idx,
|
||||||
|
std::size_t idxIt,
|
||||||
|
SubSheet *pSubsheet) noexcept {
|
||||||
|
if (idxIt == idx.size()) {
|
||||||
|
return *pSubsheet;
|
||||||
|
}
|
||||||
|
return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr const SubSheet &getSubSheet(const SubSheetIdx &idx) const noexcept {
|
||||||
|
return getSubSheet(idx, 0, &subsheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr SubSheet &getSubSheet(const SubSheetIdx &idx) noexcept {
|
||||||
|
return getSubSheet(idx, 0, &subsheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ox::Error addSubSheet(const SubSheetIdx &idx) noexcept {
|
||||||
|
auto &parent = getSubSheet(idx);
|
||||||
|
if (parent.subsheets.size() < 2) {
|
||||||
|
parent.subsheets.emplace_back(idIt++, ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1, bpp);
|
||||||
|
} else {
|
||||||
|
parent.subsheets.emplace_back(idIt++, "Subsheet 0", parent.columns, parent.rows, bpp);
|
||||||
|
parent.subsheets.emplace_back(idIt++, "Subsheet 1", 1, 1, bpp);
|
||||||
|
}
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr static auto rmSubSheet(
|
||||||
|
const SubSheetIdx &idx,
|
||||||
|
std::size_t idxIt,
|
||||||
|
SubSheet *pSubsheet) noexcept {
|
||||||
|
if (idxIt == idx.size() - 1) {
|
||||||
|
return pSubsheet->subsheets.erase(idx[idxIt]).error;
|
||||||
|
}
|
||||||
|
return rmSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto rmSubSheet(const SubSheetIdx &idx) noexcept {
|
||||||
|
return rmSubSheet(idx, 0, &subsheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto getPixel4Bpp(
|
||||||
|
const geo::Point &pt,
|
||||||
|
const SubSheetIdx &subsheetIdx) const noexcept {
|
||||||
|
oxAssert(bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp");
|
||||||
|
auto &s = this->getSubSheet(subsheetIdx);
|
||||||
|
const auto idx = ptToIdx(pt, s.columns);
|
||||||
|
return s.getPixel4Bpp(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto getPixel8Bpp(
|
||||||
|
const geo::Point &pt,
|
||||||
|
const SubSheetIdx &subsheetIdx) const noexcept {
|
||||||
|
oxAssert(bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp");
|
||||||
|
auto &s = this->getSubSheet(subsheetIdx);
|
||||||
|
const auto idx = ptToIdx(pt, s.columns);
|
||||||
|
return s.getPixel8Bpp(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto getIdFor(ox::CRStringView path) const noexcept {
|
||||||
|
return subsheet.getIdFor(ox::split<8>(path, '.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ox::Result<unsigned> getTileOffset(const auto &pNamePath) const noexcept {
|
||||||
|
return subsheet.getTileOffset(ox::split<8>(pNamePath, '.'), bpp);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ox::Result<ox::StringView> getNameFor(SubSheetId pId) const noexcept {
|
||||||
|
return subsheet.getNameFor(pId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
auto pixels() const noexcept {
|
||||||
|
ox::Vector<uint8_t> out;
|
||||||
|
subsheet.readPixelsTo(&out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompactTileSheet {
|
||||||
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet";
|
||||||
|
static constexpr auto TypeVersion = 1;
|
||||||
|
int8_t bpp = 0;
|
||||||
|
ox::FileAddress defaultPalette;
|
||||||
|
ox::Vector<uint8_t> pixels = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
oxModelBegin(TileSheetV1)
|
||||||
|
oxModelField(bpp)
|
||||||
|
oxModelField(rows)
|
||||||
|
oxModelField(columns)
|
||||||
|
oxModelField(defaultPalette)
|
||||||
|
oxModelField(pal)
|
||||||
|
oxModelField(pixels)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
oxModelBegin(TileSheetV2::SubSheet)
|
||||||
|
oxModelField(name);
|
||||||
|
oxModelField(rows);
|
||||||
|
oxModelField(columns);
|
||||||
|
oxModelField(subsheets)
|
||||||
|
oxModelField(pixels)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
oxModelBegin(TileSheetV2)
|
||||||
|
oxModelField(bpp)
|
||||||
|
oxModelField(defaultPalette)
|
||||||
|
oxModelField(subsheet)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
oxModelBegin(TileSheet::SubSheet)
|
||||||
|
oxModelField(name);
|
||||||
|
oxModelField(rows);
|
||||||
|
oxModelField(columns);
|
||||||
|
oxModelField(subsheets)
|
||||||
|
oxModelField(pixels)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
oxModelBegin(TileSheet)
|
||||||
|
oxModelField(bpp)
|
||||||
|
oxModelField(idIt)
|
||||||
|
oxModelField(defaultPalette)
|
||||||
|
oxModelField(subsheet)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
oxModelBegin(CompactTileSheet)
|
||||||
|
oxModelField(bpp)
|
||||||
|
oxModelField(defaultPalette)
|
||||||
|
oxModelField(pixels)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
}
|
67
src/nostalgia/core/typeconv.cpp
Normal file
67
src/nostalgia/core/typeconv.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "typeconv.hpp"
|
||||||
|
|
||||||
|
namespace nostalgia::core {
|
||||||
|
|
||||||
|
ox::Error NostalgiaPaletteToPaletteConverter::convert(
|
||||||
|
foundation::Context*,
|
||||||
|
NostalgiaPalette *src,
|
||||||
|
Palette *dst) const noexcept {
|
||||||
|
dst->colors = std::move(src->colors);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error TileSheetV1ToTileSheetConverter::convert(
|
||||||
|
foundation::Context*,
|
||||||
|
TileSheetV1 *src,
|
||||||
|
TileSheet *dst) const noexcept {
|
||||||
|
dst->bpp = src->bpp;
|
||||||
|
dst->defaultPalette = std::move(src->defaultPalette);
|
||||||
|
dst->subsheet.name = "Root";
|
||||||
|
dst->subsheet.rows = src->rows;
|
||||||
|
dst->subsheet.columns = src->columns;
|
||||||
|
dst->subsheet.pixels = std::move(src->pixels);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error TileSheetToCompactTileSheetConverter::convert(
|
||||||
|
foundation::Context*,
|
||||||
|
TileSheet *src,
|
||||||
|
CompactTileSheet *dst) const noexcept {
|
||||||
|
dst->bpp = src->bpp;
|
||||||
|
dst->defaultPalette = std::move(src->defaultPalette);
|
||||||
|
dst->pixels = src->pixels();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TileSheetV2ToTileSheetConverter::convertSubsheet(
|
||||||
|
TileSheetV2::SubSheet *src,
|
||||||
|
TileSheet::SubSheet *dst,
|
||||||
|
SubSheetId *idIt) noexcept {
|
||||||
|
dst->id = *idIt;
|
||||||
|
dst->name = std::move(src->name);
|
||||||
|
dst->columns = src->columns;
|
||||||
|
dst->rows = src->rows;
|
||||||
|
dst->pixels = std::move(src->pixels);
|
||||||
|
++*idIt;
|
||||||
|
dst->subsheets.resize(src->subsheets.size());
|
||||||
|
for (auto i = 0u; i < src->subsheets.size(); ++i) {
|
||||||
|
convertSubsheet(&src->subsheets[i], &dst->subsheets[i], idIt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error TileSheetV2ToTileSheetConverter::convert(
|
||||||
|
foundation::Context*,
|
||||||
|
TileSheetV2 *src,
|
||||||
|
TileSheet *dst) const noexcept {
|
||||||
|
dst->bpp = src->bpp;
|
||||||
|
dst->defaultPalette = std::move(src->defaultPalette);
|
||||||
|
convertSubsheet(&src->subsheet, &dst->subsheet, &dst->idIt);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,32 +15,26 @@ namespace nostalgia::core {
|
|||||||
|
|
||||||
// Type converters
|
// Type converters
|
||||||
|
|
||||||
struct NostalgiaPaletteToPaletteConverter: public foundation::Converter<NostalgiaPalette, Palette> {
|
class NostalgiaPaletteToPaletteConverter: public foundation::Converter<NostalgiaPalette, Palette> {
|
||||||
ox::Error convert(foundation::Context*, NostalgiaPalette *src, Palette *dst) noexcept final {
|
ox::Error convert(foundation::Context*, NostalgiaPalette *src, Palette *dst) const noexcept final;
|
||||||
dst->colors = std::move(src->colors);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NostalgiaGraphicToTileSheetConverter: public foundation::Converter<NostalgiaGraphic, TileSheet> {
|
class TileSheetV1ToTileSheetConverter: public foundation::Converter<TileSheetV1, TileSheet> {
|
||||||
ox::Error convert(foundation::Context*, NostalgiaGraphic *src, TileSheet *dst) noexcept final {
|
ox::Error convert(foundation::Context*, TileSheetV1 *src, TileSheet *dst) const noexcept final;
|
||||||
dst->bpp = src->bpp;
|
|
||||||
dst->subsheet.name = "Root";
|
|
||||||
dst->subsheet.rows = src->rows;
|
|
||||||
dst->subsheet.columns = src->columns;
|
|
||||||
dst->defaultPalette = std::move(src->defaultPalette);
|
|
||||||
dst->subsheet.pixels = std::move(src->pixels);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TileSheetToCompactTileSheetConverter: public foundation::Converter<TileSheet, CompactTileSheet> {
|
class TileSheetToCompactTileSheetConverter: public foundation::Converter<TileSheet, CompactTileSheet> {
|
||||||
ox::Error convert(foundation::Context*, TileSheet *src, CompactTileSheet *dst) noexcept final {
|
ox::Error convert(foundation::Context*, TileSheet *src, CompactTileSheet *dst) const noexcept final;
|
||||||
dst->bpp = src->bpp;
|
};
|
||||||
dst->defaultPalette = std::move(src->defaultPalette);
|
|
||||||
dst->pixels = src->pixels();
|
class TileSheetV2ToTileSheetConverter: public foundation::Converter<TileSheetV2, TileSheet> {
|
||||||
return {};
|
static void convertSubsheet(
|
||||||
}
|
TileSheetV2::SubSheet *src,
|
||||||
|
TileSheet::SubSheet *dst,
|
||||||
|
SubSheetId *idIt) noexcept;
|
||||||
|
|
||||||
|
ox::Error convert(foundation::Context*, TileSheetV2 *src, TileSheet *dst) const noexcept final;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ struct TileSheetData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ox::Result<TileSheetData> loadTileSheet(Context *ctx, const CompactTileSheet &tilesheet) noexcept {
|
ox::Result<TileSheetData> loadTileSheet(Context *ctx, const CompactTileSheet &tilesheet) noexcept {
|
||||||
const unsigned bytesPerTile = tilesheet.bpp == 8 ? 64 : 32;
|
const unsigned bytesPerTile = tilesheet.bpp == 8 ? PixelsPerTile : PixelsPerTile / 2;
|
||||||
const auto tiles = tilesheet.pixels.size() / bytesPerTile;
|
const auto tiles = tilesheet.pixels.size() / bytesPerTile;
|
||||||
constexpr int width = 8;
|
constexpr int width = 8;
|
||||||
const int height = 8 * static_cast<int>(tiles);
|
const int height = 8 * static_cast<int>(tiles);
|
||||||
|
@ -511,7 +511,10 @@ void setSprite(Context *ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept {
|
void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept {
|
||||||
//oxTracef("nostalgia::core::gfx::gl", "setTile(ctx, {}, {}, {}, {})", bgIdx, column, row, tile);
|
oxTracef(
|
||||||
|
"nostalgia::core::gfx::setTile",
|
||||||
|
"bgIdx: {}, column: {}, row: {}, tile: {}",
|
||||||
|
bgIdx, column, row, tile);
|
||||||
const auto id = ctx->rendererData<renderer::GlImplData>();
|
const auto id = ctx->rendererData<renderer::GlImplData>();
|
||||||
const auto z = static_cast<unsigned>(bgIdx);
|
const auto z = static_cast<unsigned>(bgIdx);
|
||||||
const auto y = static_cast<unsigned>(row);
|
const auto y = static_cast<unsigned>(row);
|
||||||
@ -520,8 +523,10 @@ void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) no
|
|||||||
auto &bg = id->cbbs[z];
|
auto &bg = id->cbbs[z];
|
||||||
auto vbo = &bg.vertices[i * renderer::BgVertexVboLength];
|
auto vbo = &bg.vertices[i * renderer::BgVertexVboLength];
|
||||||
auto ebo = &bg.elements[i * renderer::BgVertexEboLength];
|
auto ebo = &bg.elements[i * renderer::BgVertexEboLength];
|
||||||
renderer::setTileBufferObject(ctx, i * renderer::BgVertexVboRows,
|
renderer::setTileBufferObject(
|
||||||
static_cast<float>(x), static_cast<float>(y), tile, vbo, ebo);
|
ctx, i * renderer::BgVertexVboRows,
|
||||||
|
static_cast<float>(x), static_cast<float>(y),
|
||||||
|
tile, vbo, ebo);
|
||||||
bg.updated = true;
|
bg.updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
add_library(
|
add_library(
|
||||||
NostalgiaFoundation
|
NostalgiaFoundation
|
||||||
foundation.hpp
|
asset.cpp
|
||||||
media.cpp
|
media.cpp
|
||||||
module.cpp
|
module.cpp
|
||||||
typeconv.cpp
|
typeconv.cpp
|
||||||
@ -15,12 +15,16 @@ target_link_libraries(
|
|||||||
OxModel
|
OxModel
|
||||||
OxStd
|
OxStd
|
||||||
)
|
)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
FILES
|
FILES
|
||||||
|
assetmanager.hpp
|
||||||
|
context.hpp
|
||||||
|
foundation.hpp
|
||||||
|
asset.hpp
|
||||||
media.hpp
|
media.hpp
|
||||||
module.hpp
|
module.hpp
|
||||||
typeconv.hpp
|
typeconv.hpp
|
||||||
DESTINATION
|
DESTINATION
|
||||||
include/nostalgia/core
|
include/nostalgia/foundation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
45
src/nostalgia/foundation/asset.cpp
Normal file
45
src/nostalgia/foundation/asset.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "asset.hpp"
|
||||||
|
|
||||||
|
namespace nostalgia::foundation {
|
||||||
|
|
||||||
|
ox::Result<ox::UUID> readUuidHeader(const ox::Buffer &buff) noexcept {
|
||||||
|
return readUuidHeader(buff.data(), buff.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Result<ox::UUID> readUuidHeader(const char *buff, std::size_t buffLen) noexcept {
|
||||||
|
if (buffLen < 40) {
|
||||||
|
return OxError(1, "Insufficient data contain complete Nostalgia header");
|
||||||
|
}
|
||||||
|
if (ox_memcmp(buff, "N1;", 3) != 0) {
|
||||||
|
return OxError(2, "No Nostalgia header data");
|
||||||
|
}
|
||||||
|
return ox::UUID::fromString(ox::StringView(buff + 3, 36));
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Result<ox::ModelObject> readAsset(ox::TypeStore *ts, const ox::Buffer &buff) noexcept {
|
||||||
|
std::size_t offset = 0;
|
||||||
|
if (!readUuidHeader(buff).error) {
|
||||||
|
offset = 40; // the size of N1 headers
|
||||||
|
}
|
||||||
|
return ox::readClaw(ts, buff.data() + offset, buff.size() - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Result<AssetHdr> readAssetHeader(const char *buff, std::size_t buffLen) noexcept {
|
||||||
|
AssetHdr out;
|
||||||
|
const auto err = readUuidHeader(buff, buffLen).moveTo(&out.uuid);
|
||||||
|
const auto offset = err ? 0 : 40;
|
||||||
|
buff = buff + offset;
|
||||||
|
buffLen = buffLen - offset;
|
||||||
|
oxReturnError(ox::readClawHeader(buff, buffLen).moveTo(&out.clawHdr));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Result<AssetHdr> readAssetHeader(const ox::Buffer &buff) noexcept {
|
||||||
|
return readAssetHeader(buff.data(), buff.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
src/nostalgia/foundation/asset.hpp
Normal file
47
src/nostalgia/foundation/asset.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/defines.hpp>
|
||||||
|
|
||||||
|
#include <ox/claw/claw.hpp>
|
||||||
|
#include <ox/fs/fs.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/foundation/context.hpp>
|
||||||
|
|
||||||
|
namespace nostalgia::foundation {
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ox::Result<ox::UUID> readUuidHeader(const ox::Buffer &buff) noexcept;
|
||||||
|
|
||||||
|
ox::Result<ox::UUID> readUuidHeader(const char *buff, std::size_t buffLen) noexcept;
|
||||||
|
|
||||||
|
ox::Error writeUuidHeader(ox::Writer_c auto *writer, const ox::UUID &uuid) noexcept {
|
||||||
|
const auto hdr = ox::sfmt<ox::BString<40>>("N1;{};", uuid.toString());
|
||||||
|
return write(writer, hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ox::Result<T> readAsset(const ox::Buffer &buff) noexcept {
|
||||||
|
std::size_t offset = 0;
|
||||||
|
const auto err = readUuidHeader(buff).error;
|
||||||
|
if (!err) {
|
||||||
|
offset = 40; // the size of N1 headers
|
||||||
|
}
|
||||||
|
return ox::readClaw<T>(buff.data() + offset, buff.size() - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Result<ox::ModelObject> readAsset(ox::TypeStore *ts, const ox::Buffer &buff) noexcept;
|
||||||
|
|
||||||
|
struct AssetHdr {
|
||||||
|
ox::UUID uuid;
|
||||||
|
ox::ClawHeader clawHdr;
|
||||||
|
};
|
||||||
|
|
||||||
|
ox::Result<AssetHdr> readAssetHeader(const char *buff, std::size_t buffLen) noexcept;
|
||||||
|
|
||||||
|
ox::Result<AssetHdr> readAssetHeader(const ox::Buffer &buff) noexcept;
|
||||||
|
|
||||||
|
}
|
@ -28,6 +28,7 @@ class AssetContainer {
|
|||||||
ox::Signal<ox::Error()> updated;
|
ox::Signal<ox::Error()> updated;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ox::UUID uuid;
|
||||||
T m_obj;
|
T m_obj;
|
||||||
mutable int m_references = 0;
|
mutable int m_references = 0;
|
||||||
|
|
||||||
@ -190,14 +191,14 @@ class AssetManager {
|
|||||||
ox::HashMap<ox::String, ox::UniquePtr<AssetContainer<T>>> m_cache;
|
ox::HashMap<ox::String, ox::UniquePtr<AssetContainer<T>>> m_cache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ox::Result<AssetRef<T>> getAsset(const ox::String &path) const noexcept {
|
ox::Result<AssetRef<T>> getAsset(const ox::String &assetId) const noexcept {
|
||||||
auto out = m_cache.at(path);
|
auto out = m_cache.at(assetId);
|
||||||
oxReturnError(out);
|
oxReturnError(out);
|
||||||
return AssetRef<T>(out.value->get());
|
return AssetRef<T>(out.value->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<AssetRef<T>> setAsset(const ox::String &path, const T &obj) noexcept {
|
ox::Result<AssetRef<T>> setAsset(const ox::String &assetId, const T &obj) noexcept {
|
||||||
auto &p = m_cache[path];
|
auto &p = m_cache[assetId];
|
||||||
if (!p) {
|
if (!p) {
|
||||||
p = ox::make_unique<AssetContainer<T>>(obj);
|
p = ox::make_unique<AssetContainer<T>>(obj);
|
||||||
} else {
|
} else {
|
||||||
@ -207,8 +208,8 @@ class AssetManager {
|
|||||||
return AssetRef<T>(p.get());
|
return AssetRef<T>(p.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<AssetRef<T>> setAsset(const ox::String &path, T &&obj) noexcept {
|
ox::Result<AssetRef<T>> setAsset(const ox::String &assetId, T &&obj) noexcept {
|
||||||
auto &p = m_cache[path];
|
auto &p = m_cache[assetId];
|
||||||
if (!p) {
|
if (!p) {
|
||||||
p = ox::make_unique<AssetContainer<T>>(obj);
|
p = ox::make_unique<AssetContainer<T>>(obj);
|
||||||
} else {
|
} else {
|
||||||
@ -221,7 +222,7 @@ class AssetManager {
|
|||||||
void gc() noexcept final {
|
void gc() noexcept final {
|
||||||
for (const auto &ack : m_cache.keys()) {
|
for (const auto &ack : m_cache.keys()) {
|
||||||
auto &ac = m_cache[ack];
|
auto &ac = m_cache[ack];
|
||||||
if (ac->references()) {
|
if (!ac->references()) {
|
||||||
m_cache.erase(ack);
|
m_cache.erase(ack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,15 +244,15 @@ class AssetManager {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Result<AssetRef<T>> getAsset(const ox::String &path) noexcept {
|
ox::Result<AssetRef<T>> getAsset(const ox::String &assetId) noexcept {
|
||||||
auto m = getTypeManager<T>();
|
auto m = getTypeManager<T>();
|
||||||
return m->getAsset(path);
|
return m->getAsset(assetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Result<AssetRef<T>> setAsset(const ox::String &path, const T &obj) noexcept {
|
ox::Result<AssetRef<T>> setAsset(const ox::String &assetId, const T &obj) noexcept {
|
||||||
auto m = getTypeManager<T>();
|
auto m = getTypeManager<T>();
|
||||||
return m->setAsset(path, obj);
|
return m->setAsset(assetId, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gc() noexcept {
|
void gc() noexcept {
|
||||||
|
@ -21,7 +21,9 @@ class Context {
|
|||||||
ox::StringView appName = "Nostalgia Foundation App";
|
ox::StringView appName = "Nostalgia Foundation App";
|
||||||
#ifndef OX_BARE_METAL
|
#ifndef OX_BARE_METAL
|
||||||
AssetManager assetManager;
|
AssetManager assetManager;
|
||||||
ox::Vector<class BaseConverter*> converters;
|
ox::HashMap<ox::String, ox::UUID> pathToUuid;
|
||||||
|
ox::HashMap<ox::UUIDStr, ox::String> uuidToPath;
|
||||||
|
ox::Vector<const class BaseConverter*> converters;
|
||||||
ox::Vector<PackTransform> packTransforms;
|
ox::Vector<PackTransform> packTransforms;
|
||||||
#else
|
#else
|
||||||
std::size_t preloadSectionOffset = 0;
|
std::size_t preloadSectionOffset = 0;
|
||||||
|
@ -7,15 +7,16 @@
|
|||||||
#include <ox/std/memory.hpp>
|
#include <ox/std/memory.hpp>
|
||||||
|
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
|
#include "media.hpp"
|
||||||
#include "module.hpp"
|
#include "module.hpp"
|
||||||
|
|
||||||
namespace nostalgia::foundation {
|
namespace nostalgia::foundation {
|
||||||
|
|
||||||
template<typename Ctx = foundation::Context>
|
template<typename Ctx = foundation::Context>
|
||||||
ox::UPtr<Ctx> init(ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView appName) noexcept {
|
ox::Result<ox::UPtr<Ctx>> init(ox::UPtr<ox::FileSystem> &&fs, ox::CRStringView appName) noexcept {
|
||||||
auto ctx = ox::make_unique<Ctx>();
|
auto ctx = ox::make_unique<Ctx>();
|
||||||
ctx->rom = std::move(fs);
|
|
||||||
ctx->appName = appName;
|
ctx->appName = appName;
|
||||||
|
oxIgnoreError(setRomFs(ctx.get(), std::move(fs)));
|
||||||
auto mods = modules();
|
auto mods = modules();
|
||||||
if (mods) {
|
if (mods) {
|
||||||
for (auto &mod : *mods) {
|
for (auto &mod : *mods) {
|
||||||
|
@ -40,6 +40,43 @@ ox::Result<void*> findPreloadSection() noexcept {
|
|||||||
return OxError(1, "findPreloadSection is unsupported on this platform");
|
return OxError(1, "findPreloadSection is unsupported on this platform");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clearUuidMap(Context *ctx) noexcept {
|
||||||
|
ctx->uuidToPath.clear();
|
||||||
|
ctx->pathToUuid.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createUuidMapping(Context *ctx, const ox::String &filePath, const ox::UUID &uuid) noexcept {
|
||||||
|
ctx->pathToUuid[filePath] = uuid;
|
||||||
|
ctx->uuidToPath[uuid.toString()] = filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ox::Error buildUuidMap(Context *ctx, ox::CRStringView path) noexcept {
|
||||||
|
oxRequire(files, ctx->rom->ls(path));
|
||||||
|
for (const auto &f : files) {
|
||||||
|
oxRequire(filePath, ox::join("/", ox::Array<ox::StringView, 2>{path, f}));
|
||||||
|
oxRequire(stat, ctx->rom->stat(filePath));
|
||||||
|
if (stat.fileType == ox::FileType::NormalFile) {
|
||||||
|
oxRequire(data, ctx->rom->read(filePath));
|
||||||
|
const auto [hdr, err] = readAssetHeader(data);
|
||||||
|
if (!err) {
|
||||||
|
createUuidMapping(ctx, filePath, hdr.uuid);
|
||||||
|
}
|
||||||
|
} else if (stat.fileType == ox::FileType::Directory) {
|
||||||
|
if (!beginsWith(f, ".")) {
|
||||||
|
oxReturnError(buildUuidMap(ctx, filePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error buildUuidMap(Context *ctx) noexcept {
|
||||||
|
if (!ctx->rom) {
|
||||||
|
return OxError(1, "No ROM FS");
|
||||||
|
}
|
||||||
|
return buildUuidMap(ctx, "");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@ -50,6 +87,13 @@ ox::Result<void*> findPreloadSection() noexcept {
|
|||||||
|
|
||||||
namespace nostalgia::foundation {
|
namespace nostalgia::foundation {
|
||||||
|
|
||||||
|
static void clearUuidMap(Context*) noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error buildUuidMap(Context*) noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ox::Result<char*> loadRom(ox::CRStringView) noexcept {
|
ox::Result<char*> loadRom(ox::CRStringView) noexcept {
|
||||||
// put the header in the wrong order to prevent mistaking this code for the
|
// put the header in the wrong order to prevent mistaking this code for the
|
||||||
// media section
|
// media section
|
||||||
@ -92,6 +136,12 @@ ox::Result<std::size_t> getPreloadAddr(foundation::Context *ctx, const ox::FileA
|
|||||||
|
|
||||||
namespace nostalgia::foundation {
|
namespace nostalgia::foundation {
|
||||||
|
|
||||||
|
ox::Error setRomFs(Context *ctx, ox::UPtr<ox::FileSystem> fs) noexcept {
|
||||||
|
ctx->rom = std::move(fs);
|
||||||
|
clearUuidMap(ctx);
|
||||||
|
return buildUuidMap(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<ox::FileSystem>> loadRomFs(ox::CRStringView path) noexcept {
|
ox::Result<ox::UniquePtr<ox::FileSystem>> loadRomFs(ox::CRStringView path) noexcept {
|
||||||
const auto lastDot = ox_lastIndexOf(path, '.');
|
const auto lastDot = ox_lastIndexOf(path, '.');
|
||||||
const auto fsExt = lastDot != -1 ? path.substr(static_cast<std::size_t>(lastDot)) : "";
|
const auto fsExt = lastDot != -1 ? path.substr(static_cast<std::size_t>(lastDot)) : "";
|
||||||
|
@ -10,12 +10,15 @@
|
|||||||
#include <ox/fs/fs.hpp>
|
#include <ox/fs/fs.hpp>
|
||||||
#include <ox/model/metadata.hpp>
|
#include <ox/model/metadata.hpp>
|
||||||
|
|
||||||
#include <nostalgia/foundation/context.hpp>
|
|
||||||
|
|
||||||
|
#include "asset.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
#include "typeconv.hpp"
|
#include "typeconv.hpp"
|
||||||
|
|
||||||
namespace nostalgia::foundation {
|
namespace nostalgia::foundation {
|
||||||
|
|
||||||
|
// Pointer to preloaded data that can be stored in FS in place of the actual
|
||||||
|
// data.
|
||||||
struct PreloadPtr {
|
struct PreloadPtr {
|
||||||
static constexpr auto TypeName = "net.drinkingtea.ox.PreloadPtr";
|
static constexpr auto TypeName = "net.drinkingtea.ox.PreloadPtr";
|
||||||
static constexpr auto TypeVersion = 1;
|
static constexpr auto TypeVersion = 1;
|
||||||
@ -29,12 +32,15 @@ oxModelEnd()
|
|||||||
ox::Result<std::size_t> getPreloadAddr(foundation::Context *ctx, const ox::FileAddress &file) noexcept;
|
ox::Result<std::size_t> getPreloadAddr(foundation::Context *ctx, const ox::FileAddress &file) noexcept;
|
||||||
ox::Result<std::size_t> getPreloadAddr(foundation::Context *ctx, ox::CRStringView file) noexcept;
|
ox::Result<std::size_t> getPreloadAddr(foundation::Context *ctx, ox::CRStringView file) noexcept;
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
ox::Result<foundation::AssetRef<T>> readObj([[maybe_unused]] foundation::Context *ctx, [[maybe_unused]] ox::CRStringView path,
|
|
||||||
[[maybe_unused]] bool forceLoad = false) noexcept {
|
|
||||||
#ifndef OX_BARE_METAL
|
#ifndef OX_BARE_METAL
|
||||||
const auto readConvert = [ctx](const ox::Buffer &buff) -> ox::Result<T> {
|
|
||||||
auto [obj, err] = ox::readClaw<T>(buff);
|
template<typename T>
|
||||||
|
ox::Result<foundation::AssetRef<T>> readObjFile(
|
||||||
|
foundation::Context *ctx,
|
||||||
|
ox::StringView assetId,
|
||||||
|
bool forceLoad) noexcept {
|
||||||
|
constexpr auto readConvert = [](Context *ctx, const ox::Buffer &buff) -> ox::Result<T> {
|
||||||
|
auto [obj, err] = readAsset<T>(buff);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) {
|
if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) {
|
||||||
return err;
|
return err;
|
||||||
@ -43,36 +49,72 @@ ox::Result<foundation::AssetRef<T>> readObj([[maybe_unused]] foundation::Context
|
|||||||
}
|
}
|
||||||
return std::move(obj);
|
return std::move(obj);
|
||||||
};
|
};
|
||||||
|
ox::StringView path;
|
||||||
|
ox::UUIDStr uuidStr;
|
||||||
|
if (beginsWith(assetId, "uuid://")) {
|
||||||
|
assetId = assetId.substr(7);
|
||||||
|
path = ctx->uuidToPath[assetId];
|
||||||
|
} else {
|
||||||
|
path = assetId;
|
||||||
|
uuidStr = ctx->pathToUuid[path].toString();
|
||||||
|
assetId = uuidStr;
|
||||||
|
}
|
||||||
if (forceLoad) {
|
if (forceLoad) {
|
||||||
oxRequire(buff, ctx->rom->read(path));
|
oxRequire(buff, ctx->rom->read(path));
|
||||||
oxRequire(obj, readConvert(buff));
|
oxRequire(obj, readConvert(ctx, buff));
|
||||||
oxRequire(cached, ctx->assetManager.setAsset(path, obj));
|
oxRequire(cached, ctx->assetManager.setAsset(assetId, obj));
|
||||||
return std::move(cached);
|
return cached;
|
||||||
} else {
|
} else {
|
||||||
auto [cached, err] = ctx->assetManager.getAsset<T>(path);
|
auto [cached, err] = ctx->assetManager.getAsset<T>(assetId);
|
||||||
if (err) {
|
if (err) {
|
||||||
oxRequire(buff, ctx->rom->read(path));
|
oxRequire(buff, ctx->rom->read(path));
|
||||||
oxRequire(obj, readConvert(buff));
|
oxRequire(obj, readConvert(ctx, buff));
|
||||||
oxReturnError(ctx->assetManager.setAsset(path, obj).moveTo(&cached));
|
oxReturnError(ctx->assetManager.setAsset(assetId, obj).moveTo(&cached));
|
||||||
}
|
}
|
||||||
return std::move(cached);
|
return cached;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ox::Result<foundation::AssetRef<T>> readObjNoCache(
|
||||||
|
foundation::Context *ctx,
|
||||||
|
ox::CRStringView assetId) noexcept {
|
||||||
if constexpr(ox::preloadable<T>::value) {
|
if constexpr(ox::preloadable<T>::value) {
|
||||||
oxRequire(addr, getPreloadAddr(ctx, path));
|
oxRequire(addr, getPreloadAddr(ctx, assetId));
|
||||||
return foundation::AssetRef<T>(reinterpret_cast<const T*>(addr));
|
return foundation::AssetRef<T>(reinterpret_cast<const T*>(addr));
|
||||||
} else {
|
} else {
|
||||||
return OxError(1);
|
return OxError(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void createUuidMapping(Context *ctx, const ox::String &filePath, const ox::UUID &uuid) noexcept;
|
||||||
|
|
||||||
|
ox::Error buildUuidMap(Context *ctx) noexcept;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ox::Result<foundation::AssetRef<T>> readObj(
|
||||||
|
[[maybe_unused]] foundation::Context *ctx,
|
||||||
|
[[maybe_unused]] ox::CRStringView assetId,
|
||||||
|
[[maybe_unused]] bool forceLoad = false) noexcept {
|
||||||
|
#ifndef OX_BARE_METAL
|
||||||
|
return readObjFile<T>(ctx, assetId, forceLoad);
|
||||||
|
#else
|
||||||
|
return readObjNoCache<T>(ctx, assetId);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Result<foundation::AssetRef<T>> readObj(foundation::Context *ctx, const ox::FileAddress &file,
|
ox::Result<foundation::AssetRef<T>> readObj(
|
||||||
[[maybe_unused]] bool forceLoad = false) noexcept {
|
foundation::Context *ctx,
|
||||||
|
const ox::FileAddress &file,
|
||||||
|
[[maybe_unused]] bool forceLoad = false) noexcept {
|
||||||
#ifndef OX_BARE_METAL
|
#ifndef OX_BARE_METAL
|
||||||
oxRequire(path, file.getPath());
|
oxRequire(assetId, file.getPath());
|
||||||
return readObj<T>(ctx, ox::StringView(path), forceLoad);
|
return readObj<T>(ctx, ox::StringView(assetId), forceLoad);
|
||||||
#else
|
#else
|
||||||
if constexpr(ox::preloadable<T>::value) {
|
if constexpr(ox::preloadable<T>::value) {
|
||||||
oxRequire(addr, getPreloadAddr(ctx, file));
|
oxRequire(addr, getPreloadAddr(ctx, file));
|
||||||
@ -84,15 +126,20 @@ ox::Result<foundation::AssetRef<T>> readObj(foundation::Context *ctx, const ox::
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Error writeObj(foundation::Context *ctx, const ox::FileAddress &file, const T &obj,
|
ox::Error writeObj(
|
||||||
ox::ClawFormat fmt = ox::ClawFormat::Metal) noexcept {
|
foundation::Context *ctx,
|
||||||
|
const ox::FileAddress &file,
|
||||||
|
const T &obj,
|
||||||
|
ox::ClawFormat fmt = ox::ClawFormat::Metal) noexcept {
|
||||||
oxRequire(objBuff, ox::writeClaw(&obj, fmt));
|
oxRequire(objBuff, ox::writeClaw(&obj, fmt));
|
||||||
return ctx->rom->write(file, objBuff.data(), objBuff.size());
|
return ctx->rom->write(file, objBuff.data(), objBuff.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error setRomFs(Context *ctx, ox::UPtr<ox::FileSystem> fs) noexcept;
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<ox::FileSystem>> loadRomFs(ox::CRStringView path) noexcept;
|
ox::Result<ox::UniquePtr<ox::FileSystem>> loadRomFs(ox::CRStringView path) noexcept;
|
||||||
|
|
||||||
ox::Result<char*> loadRom(ox::CRStringView path = "") noexcept;
|
ox::Result<char*> loadRom(ox::CRStringView assetId = "") noexcept;
|
||||||
|
|
||||||
void unloadRom(char*) noexcept;
|
void unloadRom(char*) noexcept;
|
||||||
|
|
||||||
|
@ -18,7 +18,11 @@ const ox::Vector<const Module*> *modules() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ox::Vector<foundation::BaseConverter*> Module::converters() const noexcept {
|
ox::Vector<TypeDescGenerator> Module::types() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Vector<const foundation::BaseConverter*> Module::converters() const noexcept {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ox/std/vector.hpp>
|
#include <ox/std/vector.hpp>
|
||||||
|
#include <ox/model/descwrite.hpp>
|
||||||
|
|
||||||
#include "typeconv.hpp"
|
#include "typeconv.hpp"
|
||||||
|
|
||||||
namespace nostalgia::foundation {
|
namespace nostalgia::foundation {
|
||||||
|
|
||||||
|
using TypeDescGenerator = ox::Error(*)(ox::TypeStore*);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ox::Error generateTypeDesc(ox::TypeStore *ts) noexcept {
|
||||||
|
return ox::buildTypeDef<T>(ts).error;
|
||||||
|
}
|
||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
public:
|
public:
|
||||||
constexpr Module() noexcept = default;
|
constexpr Module() noexcept = default;
|
||||||
@ -18,8 +26,11 @@ class Module {
|
|||||||
Module &operator=(const Module&) noexcept = delete;
|
Module &operator=(const Module&) noexcept = delete;
|
||||||
Module &operator=(Module&&) noexcept = delete;
|
Module &operator=(Module&&) noexcept = delete;
|
||||||
constexpr virtual ~Module() noexcept = default;
|
constexpr virtual ~Module() noexcept = default;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
virtual ox::Vector<foundation::BaseConverter*> converters() const noexcept;
|
virtual ox::Vector<TypeDescGenerator> types() const noexcept;
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual ox::Vector<const foundation::BaseConverter*> converters() const noexcept;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
virtual ox::Vector<PackTransform> packTransforms() const noexcept;
|
virtual ox::Vector<PackTransform> packTransforms() const noexcept;
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <ox/claw/read.hpp>
|
#include <ox/claw/read.hpp>
|
||||||
|
|
||||||
|
#include "media.hpp"
|
||||||
#include "typeconv.hpp"
|
#include "typeconv.hpp"
|
||||||
|
|
||||||
namespace nostalgia::foundation {
|
namespace nostalgia::foundation {
|
||||||
@ -11,9 +12,10 @@ namespace nostalgia::foundation {
|
|||||||
#ifndef OX_BARE_METAL
|
#ifndef OX_BARE_METAL
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static auto findConverter(foundation::Context *ctx,
|
static ox::Result<const BaseConverter*> findConverter(
|
||||||
ox::CRStringView srcTypeName, int srcTypeVersion,
|
foundation::Context *ctx,
|
||||||
ox::CRStringView dstTypeName, int dstTypeVersion) noexcept -> ox::Result<BaseConverter*> {
|
ox::CRStringView srcTypeName, int srcTypeVersion,
|
||||||
|
ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
|
||||||
for (auto &c : ctx->converters) {
|
for (auto &c : ctx->converters) {
|
||||||
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
|
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
|
||||||
return c;
|
return c;
|
||||||
@ -22,13 +24,14 @@ static auto findConverter(foundation::Context *ctx,
|
|||||||
return OxError(1, "Could not find converter");
|
return OxError(1, "Could not find converter");
|
||||||
};
|
};
|
||||||
|
|
||||||
static ox::Result<ox::UniquePtr<Wrap>> convert(foundation::Context *ctx, const ox::Buffer &srcBuffer,
|
static ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||||
ox::CRStringView srcTypeName, int srcTypeVersion,
|
foundation::Context *ctx, const ox::Buffer &srcBuffer,
|
||||||
ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
|
ox::CRStringView srcTypeName, int srcTypeVersion,
|
||||||
|
ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
|
||||||
// look for direct converter
|
// look for direct converter
|
||||||
auto [c, err] = findConverter(ctx, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
|
auto [c, err] = findConverter(ctx, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
return c->convertBuffToPtr(nullptr, srcBuffer);
|
return c->convertBuffToPtr(ctx, srcBuffer);
|
||||||
}
|
}
|
||||||
// try to chain multiple converters
|
// try to chain multiple converters
|
||||||
for (const auto &subConverter : ctx->converters) {
|
for (const auto &subConverter : ctx->converters) {
|
||||||
@ -45,9 +48,13 @@ static ox::Result<ox::UniquePtr<Wrap>> convert(foundation::Context *ctx, const o
|
|||||||
return OxError(1, "Could not convert between types");
|
return OxError(1, "Could not convert between types");
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<Wrap>> convert(foundation::Context *ctx, const ox::Buffer &srcBuffer, ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
|
ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||||
oxRequire(hdr, ox::readClawHeader(srcBuffer));
|
foundation::Context *ctx,
|
||||||
return convert(ctx, srcBuffer, hdr.typeName, hdr.typeVersion, dstTypeName, dstTypeVersion);
|
const ox::Buffer &srcBuffer,
|
||||||
|
ox::CRStringView dstTypeName,
|
||||||
|
int dstTypeVersion) noexcept {
|
||||||
|
oxRequire(hdr, readAssetHeader(srcBuffer));
|
||||||
|
return convert(ctx, srcBuffer, hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, dstTypeName, dstTypeVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <ox/claw/write.hpp>
|
#include <ox/claw/write.hpp>
|
||||||
|
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
|
#include "media.hpp"
|
||||||
|
|
||||||
namespace nostalgia::foundation {
|
namespace nostalgia::foundation {
|
||||||
|
|
||||||
@ -19,6 +20,28 @@ class Wrap {
|
|||||||
virtual ~Wrap() = default;
|
virtual ~Wrap() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class WrapIndirect: public Wrap {
|
||||||
|
private:
|
||||||
|
T *m_obj = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... Args>
|
||||||
|
constexpr explicit WrapIndirect(Args &&...args): m_obj(ox::forward<Args>(args)...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto obj() const noexcept {
|
||||||
|
return &m_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto obj() noexcept {
|
||||||
|
return &m_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class WrapInline: public Wrap {
|
class WrapInline: public Wrap {
|
||||||
private:
|
private:
|
||||||
@ -53,20 +76,20 @@ class BaseConverter {
|
|||||||
virtual ~BaseConverter() noexcept = default;
|
virtual ~BaseConverter() noexcept = default;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
virtual ox::StringView srcTypeName() noexcept = 0;
|
virtual ox::StringView srcTypeName() const noexcept = 0;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
virtual int srcTypeVersion() noexcept = 0;
|
virtual int srcTypeVersion() const noexcept = 0;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
virtual bool srcMatches(ox::CRStringView srcTypeName, int srcTypeVersion) const noexcept = 0;
|
virtual bool srcMatches(ox::CRStringView pSrcTypeName, int pSrcTypeVersion) const noexcept = 0;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
virtual bool dstMatches(ox::CRStringView dstTypeName, int dstTypeVersion) const noexcept = 0;
|
virtual bool dstMatches(ox::CRStringView dstTypeName, int dstTypeVersion) const noexcept = 0;
|
||||||
|
|
||||||
virtual ox::Result<ox::UniquePtr<Wrap>> convertPtrToPtr(foundation::Context *ctx, Wrap *src) noexcept = 0;
|
virtual ox::Result<ox::UniquePtr<Wrap>> convertPtrToPtr(foundation::Context *ctx, Wrap *src) const noexcept = 0;
|
||||||
|
|
||||||
virtual ox::Result<ox::UniquePtr<Wrap>> convertBuffToPtr(foundation::Context *ctx, const ox::Buffer &srcBuff) noexcept = 0;
|
virtual ox::Result<ox::UniquePtr<Wrap>> convertBuffToPtr(foundation::Context *ctx, const ox::Buffer &srcBuff) const noexcept = 0;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
inline bool matches(ox::CRStringView srcTypeName, int srcTypeVersion,
|
inline bool matches(ox::CRStringView srcTypeName, int srcTypeVersion,
|
||||||
@ -80,47 +103,48 @@ class BaseConverter {
|
|||||||
template<typename SrcType, typename DstType>
|
template<typename SrcType, typename DstType>
|
||||||
class Converter: public BaseConverter {
|
class Converter: public BaseConverter {
|
||||||
public:
|
public:
|
||||||
virtual ox::Error convert(foundation::Context *ctx, SrcType*, DstType*) noexcept = 0;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::StringView srcTypeName() noexcept final {
|
ox::StringView srcTypeName() const noexcept final {
|
||||||
return ox::requireModelTypeName<SrcType>();
|
return ox::requireModelTypeName<SrcType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
int srcTypeVersion() noexcept final {
|
int srcTypeVersion() const noexcept final {
|
||||||
return ox::requireModelTypeVersion<SrcType>();
|
return ox::requireModelTypeVersion<SrcType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool srcMatches(ox::CRStringView srcTypeName, int srcTypeVersion) const noexcept final {
|
bool srcMatches(ox::CRStringView pSrcTypeName, int pSrcTypeVersion) const noexcept final {
|
||||||
static constexpr auto SrcTypeName = ox::requireModelTypeName<SrcType>();
|
static const auto SrcTypeName = ox::requireModelTypeName<SrcType>();
|
||||||
static constexpr auto SrcTypeVersion = ox::requireModelTypeVersion<SrcType>();
|
static const auto SrcTypeVersion = ox::requireModelTypeVersion<SrcType>();
|
||||||
return ox_strcmp(srcTypeName, SrcTypeName) == 0
|
return pSrcTypeName == SrcTypeName
|
||||||
&& srcTypeVersion == SrcTypeVersion;
|
&& pSrcTypeVersion == SrcTypeVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool dstMatches(ox::CRStringView dstTypeName, int dstTypeVersion) const noexcept final {
|
bool dstMatches(ox::CRStringView dstTypeName, int dstTypeVersion) const noexcept final {
|
||||||
static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
|
static const auto DstTypeName = ox::StringView(ox::requireModelTypeName<DstType>());
|
||||||
static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
|
static const auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
|
||||||
return ox_strcmp(dstTypeName, DstTypeName) == 0
|
return dstTypeName == DstTypeName
|
||||||
&& dstTypeVersion == DstTypeVersion;
|
&& dstTypeVersion == DstTypeVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<Wrap>> convertPtrToPtr(foundation::Context *ctx, Wrap *src) noexcept final {
|
ox::Result<ox::UniquePtr<Wrap>> convertPtrToPtr(foundation::Context *ctx, Wrap *src) const noexcept final {
|
||||||
auto dst = makeWrap<DstType>();
|
auto dst = makeWrap<DstType>();
|
||||||
oxReturnError(convert(ctx, wrapCast<SrcType>(src), wrapCast<DstType>(dst.get())));
|
oxReturnError(convert(ctx, wrapCast<SrcType>(src), wrapCast<DstType>(dst.get())));
|
||||||
return ox::Result<ox::UniquePtr<Wrap>>(std::move(dst));
|
return ox::Result<ox::UniquePtr<Wrap>>(std::move(dst));
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<Wrap>> convertBuffToPtr(foundation::Context *ctx, const ox::Buffer &srcBuff) noexcept final {
|
ox::Result<ox::UniquePtr<Wrap>> convertBuffToPtr(foundation::Context *ctx, const ox::Buffer &srcBuff) const noexcept final {
|
||||||
oxRequireM(src, ox::readClaw<SrcType>(srcBuff));
|
oxRequireM(src, readAsset<SrcType>(srcBuff));
|
||||||
auto dst = makeWrap<DstType>();
|
auto dst = makeWrap<DstType>();
|
||||||
oxReturnError(convert(ctx, &src, wrapCast<DstType>(dst.get())));
|
oxReturnError(convert(ctx, &src, wrapCast<DstType>(dst.get())));
|
||||||
return ox::Result<ox::UniquePtr<Wrap>>(std::move(dst));
|
return ox::Result<ox::UniquePtr<Wrap>>(std::move(dst));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ox::Error convert(foundation::Context *ctx, SrcType*, DstType*) const noexcept = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ox::Result<ox::UniquePtr<Wrap>> convert(foundation::Context *ctx, const ox::Buffer &srcBuffer,
|
ox::Result<ox::UniquePtr<Wrap>> convert(foundation::Context *ctx, const ox::Buffer &srcBuffer,
|
||||||
@ -153,8 +177,9 @@ ox::Result<ox::Buffer> convertBuffToBuff(foundation::Context *ctx, const ox::Buf
|
|||||||
|
|
||||||
template<typename From, typename To, ox::ClawFormat fmt = ox::ClawFormat::Metal>
|
template<typename From, typename To, ox::ClawFormat fmt = ox::ClawFormat::Metal>
|
||||||
auto transformRule(foundation::Context *ctx, ox::Buffer *buff) -> ox::Error {
|
auto transformRule(foundation::Context *ctx, ox::Buffer *buff) -> ox::Error {
|
||||||
oxRequire(hdr, ox::readClawHeader(*buff));
|
oxRequire(hdr, readAssetHeader(*buff));
|
||||||
const auto typeId = ox::buildTypeId(hdr.typeName, hdr.typeVersion, hdr.typeParams);
|
const auto typeId = ox::buildTypeId(
|
||||||
|
hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams);
|
||||||
if (typeId == ox::buildTypeId<From>()) {
|
if (typeId == ox::buildTypeId<From>()) {
|
||||||
oxReturnError(foundation::convertBuffToBuff<To>(ctx, *buff, fmt).moveTo(buff));
|
oxReturnError(foundation::convertBuffToBuff<To>(ctx, *buff, fmt).moveTo(buff));
|
||||||
}
|
}
|
||||||
|
@ -4,41 +4,19 @@
|
|||||||
|
|
||||||
#include <nostalgia/core/core.hpp>
|
#include <nostalgia/core/core.hpp>
|
||||||
#include <nostalgia/foundation/media.hpp>
|
#include <nostalgia/foundation/media.hpp>
|
||||||
|
#include <nostalgia/scene/scene.hpp>
|
||||||
|
|
||||||
using namespace nostalgia;
|
using namespace nostalgia;
|
||||||
|
|
||||||
static int spriteX = 72;
|
static bool s_paused = false;
|
||||||
static int spriteY = 64;
|
static ox::Optional<scene::Scene> s_scene;
|
||||||
static ox::StringView sprites = "nostalgia";
|
|
||||||
static bool paused = false;
|
|
||||||
|
|
||||||
static int updateHandler(core::Context *ctx) noexcept {
|
static int updateHandler(core::Context*) noexcept {
|
||||||
constexpr auto sleepTime = 16;
|
constexpr auto sleepTime = 16;
|
||||||
if (paused) {
|
if (s_paused) {
|
||||||
return sleepTime;
|
return sleepTime;
|
||||||
}
|
}
|
||||||
int xmod = 0;
|
// do stuff
|
||||||
int ymod = 0;
|
|
||||||
if (buttonDown(ctx, core::Alpha_D) || buttonDown(ctx, core::GamePad_Right)) {
|
|
||||||
xmod = 2;
|
|
||||||
} else if (buttonDown(ctx, core::Alpha_A) || buttonDown(ctx, core::GamePad_Left)) {
|
|
||||||
xmod = -2;
|
|
||||||
}
|
|
||||||
if (buttonDown(ctx, core::Alpha_S) || buttonDown(ctx, core::GamePad_Down)) {
|
|
||||||
ymod = 2;
|
|
||||||
} else if (buttonDown(ctx, core::Alpha_W) || buttonDown(ctx, core::GamePad_Up)) {
|
|
||||||
ymod = -2;
|
|
||||||
}
|
|
||||||
if (!xmod && !ymod) {
|
|
||||||
spriteX += 1;
|
|
||||||
}
|
|
||||||
spriteX += xmod;
|
|
||||||
spriteY += ymod;
|
|
||||||
//constexpr ox::StringView sprites = "nostalgia";
|
|
||||||
for (unsigned i = 0; i < sprites.len(); ++i) {
|
|
||||||
const auto c = static_cast<unsigned>(sprites[i] - ('a' - 1));
|
|
||||||
core::setSprite(ctx, i, spriteX + 8 * (static_cast<int>(i) + 1), spriteY, c, 0, 0, 0);
|
|
||||||
}
|
|
||||||
return sleepTime;
|
return sleepTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +25,7 @@ static void keyEventHandler(core::Context *ctx, core::Key key, bool down) noexce
|
|||||||
if (key == core::Key::Alpha_Q) {
|
if (key == core::Key::Alpha_Q) {
|
||||||
core::shutdown(ctx);
|
core::shutdown(ctx);
|
||||||
} else if (key == core::Key::Alpha_P) {
|
} else if (key == core::Key::Alpha_P) {
|
||||||
paused = !paused;
|
s_paused = !s_paused;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,13 +33,11 @@ static void keyEventHandler(core::Context *ctx, core::Key key, bool down) noexce
|
|||||||
ox::Error run(ox::UniquePtr<ox::FileSystem> fs) noexcept {
|
ox::Error run(ox::UniquePtr<ox::FileSystem> fs) noexcept {
|
||||||
oxTraceInitHook();
|
oxTraceInitHook();
|
||||||
oxRequireM(ctx, core::init(std::move(fs)));
|
oxRequireM(ctx, core::init(std::move(fs)));
|
||||||
constexpr ox::FileAddress TileSheetAddr("/TileSheets/Charset.ng");
|
constexpr ox::FileAddress SceneAddr("/Scenes/Chester.nscn");
|
||||||
constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal");
|
oxRequire(scn, foundation::readObj<scene::SceneStatic>(ctx.get(), SceneAddr));
|
||||||
oxRequire(tsStat, ctx->rom->stat(PaletteAddr));
|
|
||||||
oxReturnError(core::loadSpriteTileSheet(ctx.get(), TileSheetAddr, PaletteAddr));
|
|
||||||
oxReturnError(core::initConsole(ctx.get()));
|
|
||||||
core::puts(ctx.get(), 10, 9, "DOPENESS!!!");
|
|
||||||
core::setUpdateHandler(ctx.get(), updateHandler);
|
core::setUpdateHandler(ctx.get(), updateHandler);
|
||||||
core::setKeyEventHandler(ctx.get(), keyEventHandler);
|
core::setKeyEventHandler(ctx.get(), keyEventHandler);
|
||||||
|
s_scene.emplace(scn.get());
|
||||||
|
oxReturnError(s_scene->setupDisplay(ctx.get()));
|
||||||
return core::run(ctx.get());
|
return core::run(ctx.get());
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,11 @@
|
|||||||
|
|
||||||
static ox::Error run(int argc, const char **argv) noexcept {
|
static ox::Error run(int argc, const char **argv) noexcept {
|
||||||
ox::trace::init();
|
ox::trace::init();
|
||||||
|
#ifdef OX_USE_STDLIB
|
||||||
|
// GBA doesn't need the modules and calling this doubles the size of the
|
||||||
|
// binary.
|
||||||
nostalgia::loadModules();
|
nostalgia::loadModules();
|
||||||
|
#endif
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
oxErr("Please provide path to project directory or OxFS file.\n");
|
oxErr("Please provide path to project directory or OxFS file.\n");
|
||||||
return OxError(1);
|
return OxError(1);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
add_library(
|
add_library(
|
||||||
NostalgiaScene
|
NostalgiaScene
|
||||||
scene.cpp
|
scene.cpp
|
||||||
|
scenestatic.cpp
|
||||||
scenemodule.cpp
|
scenemodule.cpp
|
||||||
typeconv.cpp
|
typeconv.cpp
|
||||||
)
|
)
|
||||||
@ -9,11 +10,12 @@ add_library(
|
|||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
NostalgiaScene PUBLIC
|
NostalgiaScene PUBLIC
|
||||||
NostalgiaCore
|
NostalgiaCore
|
||||||
|
NostalgiaGeo
|
||||||
)
|
)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
FILES
|
FILES
|
||||||
scene.hpp
|
scenestatic.hpp
|
||||||
scenemodule.hpp
|
scenemodule.hpp
|
||||||
typeconv.hpp
|
typeconv.hpp
|
||||||
DESTINATION
|
DESTINATION
|
||||||
|
@ -1,11 +1,46 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <nostalgia/core/gfx.hpp>
|
||||||
|
|
||||||
#include "scene.hpp"
|
#include "scene.hpp"
|
||||||
|
|
||||||
namespace nostalgia::scene {
|
namespace nostalgia::scene {
|
||||||
|
|
||||||
|
Scene::Scene(const SceneStatic *sceneStatic) noexcept:
|
||||||
|
m_sceneStatic(sceneStatic) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error Scene::setupDisplay(core::Context *ctx) noexcept {
|
||||||
|
if (m_sceneStatic->palettes.empty()) {
|
||||||
|
return OxError(1, "Scene has no palettes");
|
||||||
|
}
|
||||||
|
const auto &palette = m_sceneStatic->palettes[0];
|
||||||
|
oxReturnError(core::loadBgTileSheet(
|
||||||
|
ctx, 0, m_sceneStatic->tilesheet, palette));
|
||||||
|
// disable all backgrounds
|
||||||
|
core::setBgStatus(ctx, 0);
|
||||||
|
for (auto layerNo = 0u; const auto &layer : m_sceneStatic->tileMapIdx) {
|
||||||
|
core::setBgStatus(ctx, layerNo, true);
|
||||||
|
core::setBgCbb(ctx, layerNo, 0);
|
||||||
|
auto x = 0;
|
||||||
|
auto y = 0;
|
||||||
|
auto width = m_sceneStatic->rows[layerNo];
|
||||||
|
for (const auto &tile : layer) {
|
||||||
|
core::setTile(ctx, layerNo, x, y, tile);
|
||||||
|
core::setTile(ctx, layerNo, x + 1, y, tile + 1);
|
||||||
|
core::setTile(ctx, layerNo, x, y + 1, tile + 2);
|
||||||
|
core::setTile(ctx, layerNo, x + 1, y + 1, tile + 3);
|
||||||
|
x += 2;
|
||||||
|
if (x >= width * 2) {
|
||||||
|
x = 0;
|
||||||
|
y += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++layerNo;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,104 +4,19 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ox/fs/fs.hpp>
|
#include "scenestatic.hpp"
|
||||||
#include <ox/std/error.hpp>
|
|
||||||
#include <ox/std/types.hpp>
|
|
||||||
#include <ox/std/vector.hpp>
|
|
||||||
|
|
||||||
namespace nostalgia::scene {
|
namespace nostalgia::scene {
|
||||||
|
|
||||||
struct TileDoc {
|
class Scene {
|
||||||
|
private:
|
||||||
|
const SceneStatic *m_sceneStatic = nullptr;
|
||||||
|
|
||||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.TileDoc";
|
public:
|
||||||
constexpr static auto TypeVersion = 1;
|
explicit Scene(const SceneStatic *sceneStatic) noexcept;
|
||||||
constexpr static auto Preloadable = true;
|
|
||||||
|
|
||||||
ox::String sheetIdx;
|
ox::Error setupDisplay(core::Context *ctx) noexcept;
|
||||||
uint8_t type = 0;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
oxModelBegin(TileDoc)
|
|
||||||
oxModelFieldRename(sheet_idx, sheetIdx);
|
|
||||||
oxModelField(type);
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
struct SceneDoc {
|
|
||||||
|
|
||||||
using TileMapRow = ox::Vector<TileDoc>;
|
|
||||||
using TileMapLayer = ox::Vector<TileMapRow>;
|
|
||||||
using TileMap = ox::Vector<TileMapLayer>;
|
|
||||||
|
|
||||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SceneDoc";
|
|
||||||
constexpr static auto TypeVersion = 1;
|
|
||||||
constexpr static auto Preloadable = true;
|
|
||||||
|
|
||||||
ox::FileAddress tilesheet;
|
|
||||||
ox::FileAddress palette;
|
|
||||||
TileMap tiles;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
oxModelBegin(SceneDoc)
|
|
||||||
oxModelField(tilesheet)
|
|
||||||
oxModelField(palette)
|
|
||||||
oxModelField(tiles)
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
struct Scene {
|
|
||||||
|
|
||||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.Scene";
|
|
||||||
constexpr static auto TypeVersion = 1;
|
|
||||||
constexpr static auto Preloadable = true;
|
|
||||||
|
|
||||||
struct Tile {
|
|
||||||
uint16_t &tileMapIdx;
|
|
||||||
uint8_t &tileType;
|
|
||||||
constexpr Tile(uint16_t &pTileMapIdx, uint8_t &pTileType) noexcept:
|
|
||||||
tileMapIdx(pTileMapIdx),
|
|
||||||
tileType(pTileType) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct Layer {
|
|
||||||
uint16_t &columns;
|
|
||||||
uint16_t &rows;
|
|
||||||
uint16_t *tileMapIdx = nullptr;
|
|
||||||
uint8_t *tileType = nullptr;
|
|
||||||
constexpr Layer(uint16_t &pColumns,
|
|
||||||
uint16_t &pRows,
|
|
||||||
uint16_t *pTileMapIdx,
|
|
||||||
uint8_t *pTileType) noexcept:
|
|
||||||
columns(pColumns),
|
|
||||||
rows(pRows),
|
|
||||||
tileMapIdx(pTileMapIdx),
|
|
||||||
tileType(pTileType) {
|
|
||||||
}
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr Tile tile(std::size_t i) const noexcept {
|
|
||||||
return {tileMapIdx[i], tileType[i]};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
uint16_t layers = 0;
|
|
||||||
ox::Vector<uint16_t> columns;
|
|
||||||
ox::Vector<uint16_t> rows;
|
|
||||||
ox::Vector<ox::Vector<uint16_t>> tileMapIdx;
|
|
||||||
ox::Vector<ox::Vector<uint8_t>> tileType;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
constexpr Layer layer(std::size_t i) noexcept {
|
|
||||||
return {columns[i], rows[i], tileMapIdx[i].data(), tileType[i].data()};
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
oxModelBegin(Scene)
|
|
||||||
oxModelField(layers)
|
|
||||||
oxModelField(columns)
|
|
||||||
oxModelField(rows)
|
|
||||||
oxModelFieldRename(tile_map_idx, tileMapIdx)
|
|
||||||
oxModelFieldRename(tile_type, tileType)
|
|
||||||
oxModelEnd()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,29 @@
|
|||||||
|
|
||||||
#include <ox/model/model.hpp>
|
#include <ox/model/model.hpp>
|
||||||
|
|
||||||
|
#include "scenestatic.hpp"
|
||||||
#include "scenemodule.hpp"
|
#include "scenemodule.hpp"
|
||||||
|
|
||||||
namespace nostalgia::scene {
|
namespace nostalgia::scene {
|
||||||
|
|
||||||
SceneModule SceneModule::mod;
|
SceneModule SceneModule::mod;
|
||||||
|
|
||||||
ox::Vector<foundation::BaseConverter*> SceneModule::converters() const noexcept {
|
ox::Vector<foundation::TypeDescGenerator> SceneModule::types() const noexcept {
|
||||||
return {
|
return {
|
||||||
&sceneToSceneInstaceConverter,
|
foundation::generateTypeDesc<SceneDoc>,
|
||||||
|
foundation::generateTypeDesc<SceneStatic>,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Vector<const foundation::BaseConverter*> SceneModule::converters() const noexcept {
|
||||||
|
return {
|
||||||
|
&sceneDocToSceneStaticConverter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Vector<foundation::PackTransform> SceneModule::packTransforms() const noexcept {
|
ox::Vector<foundation::PackTransform> SceneModule::packTransforms() const noexcept {
|
||||||
return {
|
return {
|
||||||
foundation::transformRule<SceneDoc, Scene>,
|
foundation::transformRule<SceneDoc, SceneStatic>,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,12 +12,14 @@ namespace nostalgia::scene {
|
|||||||
|
|
||||||
class SceneModule: public foundation::Module {
|
class SceneModule: public foundation::Module {
|
||||||
private:
|
private:
|
||||||
mutable SceneDocToSceneConverter sceneToSceneInstaceConverter;
|
SceneDocToSceneStaticConverter sceneDocToSceneStaticConverter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static SceneModule mod;
|
static SceneModule mod;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::Vector<foundation::BaseConverter*> converters() const noexcept override;
|
ox::Vector<foundation::TypeDescGenerator> types() const noexcept override;
|
||||||
|
[[nodiscard]]
|
||||||
|
ox::Vector<const foundation::BaseConverter*> converters() const noexcept override;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::Vector<foundation::PackTransform> packTransforms() const noexcept override;
|
ox::Vector<foundation::PackTransform> packTransforms() const noexcept override;
|
||||||
};
|
};
|
||||||
|
11
src/nostalgia/scene/scenestatic.cpp
Normal file
11
src/nostalgia/scene/scenestatic.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scenestatic.hpp"
|
||||||
|
|
||||||
|
namespace nostalgia::scene {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
164
src/nostalgia/scene/scenestatic.hpp
Normal file
164
src/nostalgia/scene/scenestatic.hpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/fs/fs.hpp>
|
||||||
|
#include <ox/std/error.hpp>
|
||||||
|
#include <ox/std/types.hpp>
|
||||||
|
#include <ox/std/vector.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/core/tilesheet.hpp>
|
||||||
|
#include <nostalgia/geo/size.hpp>
|
||||||
|
|
||||||
|
namespace nostalgia::scene {
|
||||||
|
|
||||||
|
struct TileDoc {
|
||||||
|
|
||||||
|
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.TileDoc";
|
||||||
|
constexpr static auto TypeVersion = 1;
|
||||||
|
constexpr static auto Preloadable = true;
|
||||||
|
|
||||||
|
core::SubSheetId subsheetId = -1;
|
||||||
|
ox::String subsheetPath;
|
||||||
|
uint8_t type = 0;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr ox::Result<core::SubSheetId> getSubsheetId(const core::TileSheet &ts) const noexcept {
|
||||||
|
// prefer the already present ID
|
||||||
|
if (subsheetId > -1) {
|
||||||
|
return subsheetId;
|
||||||
|
}
|
||||||
|
return ts.getIdFor(subsheetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr ox::Result<ox::StringView> getSubsheetPath(
|
||||||
|
const core::TileSheet &ts) const noexcept {
|
||||||
|
// prefer the already present path
|
||||||
|
if (!subsheetPath.len()) {
|
||||||
|
return ts.getNameFor(subsheetId);
|
||||||
|
}
|
||||||
|
return ox::StringView(subsheetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
oxModelBegin(TileDoc)
|
||||||
|
oxModelFieldRename(subsheet_id, subsheetId);
|
||||||
|
oxModelFieldRename(subsheet_path, subsheetPath);
|
||||||
|
oxModelField(type);
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
struct SceneDoc {
|
||||||
|
|
||||||
|
using TileMapRow = ox::Vector<TileDoc>;
|
||||||
|
using TileMapLayer = ox::Vector<TileMapRow>;
|
||||||
|
using TileMap = ox::Vector<TileMapLayer>;
|
||||||
|
|
||||||
|
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SceneDoc";
|
||||||
|
constexpr static auto TypeVersion = 1;
|
||||||
|
constexpr static auto Preloadable = true;
|
||||||
|
|
||||||
|
ox::String tilesheet; // path
|
||||||
|
ox::Vector<ox::String> palettes; // paths
|
||||||
|
TileMap tiles;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr geo::Size size(std::size_t layerIdx) const noexcept {
|
||||||
|
const auto &layer = this->tiles[layerIdx];
|
||||||
|
const auto rowCnt = static_cast<int>(layer.size());
|
||||||
|
if (!rowCnt) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto colCnt = layer[0].size();
|
||||||
|
// find shortest row (they should all be the same, but you know this data
|
||||||
|
// could come from a file)
|
||||||
|
for (const auto &row : layer) {
|
||||||
|
colCnt = ox::min(colCnt, row.size());
|
||||||
|
}
|
||||||
|
return {static_cast<int>(colCnt), rowCnt};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
oxModelBegin(SceneDoc)
|
||||||
|
oxModelField(tilesheet)
|
||||||
|
oxModelField(palettes)
|
||||||
|
oxModelField(tiles)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
struct SceneStatic {
|
||||||
|
|
||||||
|
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.SceneStatic";
|
||||||
|
constexpr static auto TypeVersion = 1;
|
||||||
|
constexpr static auto Preloadable = true;
|
||||||
|
|
||||||
|
struct Tile {
|
||||||
|
uint16_t &tileMapIdx;
|
||||||
|
uint8_t &tileType;
|
||||||
|
constexpr Tile(uint16_t *pTileMapIdx, uint8_t *pTileType) noexcept:
|
||||||
|
tileMapIdx(*pTileMapIdx),
|
||||||
|
tileType(*pTileType) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct Layer {
|
||||||
|
uint16_t &columns;
|
||||||
|
uint16_t &rows;
|
||||||
|
ox::Vector<uint16_t> &tileMapIdx;
|
||||||
|
ox::Vector<uint8_t> &tileType;
|
||||||
|
constexpr Layer(
|
||||||
|
uint16_t *pColumns,
|
||||||
|
uint16_t *pRows,
|
||||||
|
ox::Vector<uint16_t> *pTileMapIdx,
|
||||||
|
ox::Vector<uint8_t> *pTileType) noexcept:
|
||||||
|
columns(*pColumns),
|
||||||
|
rows(*pRows),
|
||||||
|
tileMapIdx(*pTileMapIdx),
|
||||||
|
tileType(*pTileType) {
|
||||||
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr Tile tile(std::size_t i) noexcept {
|
||||||
|
return {&tileMapIdx[i], &tileType[i]};
|
||||||
|
}
|
||||||
|
constexpr auto setDimensions(geo::Size dim) noexcept {
|
||||||
|
columns = dim.width;
|
||||||
|
rows = dim.height;
|
||||||
|
const auto tileCnt = static_cast<unsigned>(columns * rows);
|
||||||
|
tileMapIdx.resize(tileCnt);
|
||||||
|
tileType.resize(tileCnt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ox::FileAddress tilesheet;
|
||||||
|
ox::Vector<ox::FileAddress> palettes;
|
||||||
|
// tile layer data
|
||||||
|
ox::Vector<uint16_t> columns;
|
||||||
|
ox::Vector<uint16_t> rows;
|
||||||
|
ox::Vector<ox::Vector<uint16_t>> tileMapIdx;
|
||||||
|
ox::Vector<ox::Vector<uint8_t>> tileType;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr Layer layer(std::size_t i) noexcept {
|
||||||
|
return {&columns[i], &rows[i], &tileMapIdx[i], &tileType[i]};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto setLayerCnt(std::size_t layerCnt) noexcept {
|
||||||
|
this->columns.resize(layerCnt);
|
||||||
|
this->rows.resize(layerCnt);
|
||||||
|
this->tileMapIdx.resize(layerCnt);
|
||||||
|
this->tileType.resize(layerCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
oxModelBegin(SceneStatic)
|
||||||
|
oxModelField(tilesheet)
|
||||||
|
oxModelField(palettes)
|
||||||
|
oxModelField(columns)
|
||||||
|
oxModelField(rows)
|
||||||
|
oxModelFieldRename(tile_map_idx, tileMapIdx)
|
||||||
|
oxModelFieldRename(tile_type, tileType)
|
||||||
|
oxModelEnd()
|
||||||
|
|
||||||
|
}
|
@ -2,13 +2,41 @@
|
|||||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <nostalgia/core/gfx.hpp>
|
||||||
|
#include <nostalgia/foundation/media.hpp>
|
||||||
|
|
||||||
#include "typeconv.hpp"
|
#include "typeconv.hpp"
|
||||||
|
|
||||||
namespace nostalgia::scene {
|
namespace nostalgia::scene {
|
||||||
|
|
||||||
// Type converters
|
ox::Error SceneDocToSceneStaticConverter::convert(
|
||||||
|
foundation::Context *ctx,
|
||||||
ox::Error SceneDocToSceneConverter::convert(foundation::Context*, SceneDoc *, Scene *) noexcept {
|
SceneDoc *src,
|
||||||
|
SceneStatic *dst) const noexcept {
|
||||||
|
oxRequire(ts, foundation::readObj<core::TileSheet>(ctx, src->tilesheet));
|
||||||
|
const auto layerCnt = src->tiles.size();
|
||||||
|
dst->setLayerCnt(layerCnt);
|
||||||
|
dst->tilesheet = ox::FileAddress(src->tilesheet);
|
||||||
|
dst->palettes.reserve(src->palettes.size());
|
||||||
|
for (const auto &pal : src->palettes) {
|
||||||
|
dst->palettes.emplace_back(pal);
|
||||||
|
}
|
||||||
|
for (auto layerIdx = 0u; const auto &layer : src->tiles) {
|
||||||
|
const auto layerDim = src->size(layerIdx);
|
||||||
|
auto dstLayer = dst->layer(layerIdx);
|
||||||
|
dstLayer.setDimensions(layerDim);
|
||||||
|
for (auto tileIdx = 0u; const auto &row : layer) {
|
||||||
|
for (const auto &srcTile : row) {
|
||||||
|
auto dstTile = dstLayer.tile(tileIdx);
|
||||||
|
dstTile.tileType = srcTile.type;
|
||||||
|
oxRequire(path, srcTile.getSubsheetPath(*ts));
|
||||||
|
oxRequire(mapIdx, ts->getTileOffset(path));
|
||||||
|
dstTile.tileMapIdx = static_cast<uint16_t>(mapIdx);
|
||||||
|
++tileIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++layerIdx;
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
#include <nostalgia/foundation/typeconv.hpp>
|
#include <nostalgia/foundation/typeconv.hpp>
|
||||||
|
|
||||||
#include "scene.hpp"
|
#include "scenestatic.hpp"
|
||||||
|
|
||||||
namespace nostalgia::scene {
|
namespace nostalgia::scene {
|
||||||
|
|
||||||
struct SceneDocToSceneConverter: public foundation::Converter<SceneDoc, Scene> {
|
class SceneDocToSceneStaticConverter: public foundation::Converter<SceneDoc, SceneStatic> {
|
||||||
ox::Error convert(foundation::Context*, SceneDoc *src, Scene *dst) noexcept final;
|
ox::Error convert(foundation::Context*, SceneDoc *src, SceneStatic *dst) const noexcept final;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ ox::String configPath(const core::Context *ctx) noexcept;
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Result<T> readConfig(core::Context *ctx, ox::CRStringView name) noexcept {
|
ox::Result<T> readConfig(core::Context *ctx, ox::CRStringView name) noexcept {
|
||||||
oxAssert(ox_strcmp(name, ""), "Config type has no TypeName");
|
oxAssert(name != "", "Config type has no TypeName");
|
||||||
const auto path = ox::sfmt("/{}.json", name);
|
const auto path = ox::sfmt("/{}.json", name);
|
||||||
ox::PassThroughFS fs(configPath(ctx));
|
ox::PassThroughFS fs(configPath(ctx));
|
||||||
const auto [buff, err] = fs.read(path);
|
const auto [buff, err] = fs.read(path);
|
||||||
@ -41,7 +41,7 @@ ox::Result<T> readConfig(core::Context *ctx) noexcept {
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Error writeConfig(core::Context *ctx, ox::CRStringView name, T *data) noexcept {
|
ox::Error writeConfig(core::Context *ctx, ox::CRStringView name, T *data) noexcept {
|
||||||
oxAssert(ox_strcmp(name, ""), "Config type has no TypeName");
|
oxAssert(name != "", "Config type has no TypeName");
|
||||||
const auto path = ox::sfmt("/{}.json", name);
|
const auto path = ox::sfmt("/{}.json", name);
|
||||||
ox::PassThroughFS fs(configPath(ctx));
|
ox::PassThroughFS fs(configPath(ctx));
|
||||||
if (const auto err = fs.mkdir("/", true)) {
|
if (const auto err = fs.mkdir("/", true)) {
|
||||||
@ -66,8 +66,9 @@ ox::Error writeConfig(core::Context *ctx, T *data) noexcept {
|
|||||||
template<typename T, typename Func>
|
template<typename T, typename Func>
|
||||||
void openConfig(core::Context *ctx, const auto &name, Func f) noexcept {
|
void openConfig(core::Context *ctx, const auto &name, Func f) noexcept {
|
||||||
oxAssert(name != "", "Config type has no TypeName");
|
oxAssert(name != "", "Config type has no TypeName");
|
||||||
const auto c = readConfig<T>(ctx, name);
|
const auto [c, err] = readConfig<T>(ctx, name);
|
||||||
f(&c.value);
|
oxLogError(err);
|
||||||
|
f(&c);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename Func>
|
template<typename T, typename Func>
|
||||||
@ -79,9 +80,10 @@ void openConfig(core::Context *ctx, Func f) noexcept {
|
|||||||
template<typename T, typename Func>
|
template<typename T, typename Func>
|
||||||
void editConfig(core::Context *ctx, const auto &name, Func f) noexcept {
|
void editConfig(core::Context *ctx, const auto &name, Func f) noexcept {
|
||||||
oxAssert(ox_strcmp(name, ""), "Config type has no TypeName");
|
oxAssert(ox_strcmp(name, ""), "Config type has no TypeName");
|
||||||
auto c = readConfig<T>(ctx, name);
|
auto [c, err] = readConfig<T>(ctx, name);
|
||||||
f(&c.value);
|
oxLogError(err);
|
||||||
oxLogError(writeConfig(ctx, name, &c.value));
|
f(&c);
|
||||||
|
oxLogError(writeConfig(ctx, name, &c));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename Func>
|
template<typename T, typename Func>
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ox/claw/claw.hpp>
|
#include <ox/claw/claw.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/foundation/media.hpp>
|
||||||
#include <nostalgia/core/context.hpp>
|
#include <nostalgia/core/context.hpp>
|
||||||
|
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
namespace nostalgia::studio {
|
namespace nostalgia::studio {
|
||||||
|
|
||||||
class ItemMaker {
|
class ItemMaker {
|
||||||
@ -45,8 +48,9 @@ class ItemMakerT: public ItemMaker {
|
|||||||
fmt(pFmt) {
|
fmt(pFmt) {
|
||||||
}
|
}
|
||||||
ox::Error write(core::Context *ctx, ox::CRStringView pName) const noexcept override {
|
ox::Error write(core::Context *ctx, ox::CRStringView pName) const noexcept override {
|
||||||
const auto path = ox::sfmt("{}/{}.{}", parentDir, pName, fileExt);
|
const auto path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
|
||||||
auto sctx = core::applicationData<studio::StudioContext>(ctx);
|
auto sctx = core::applicationData<studio::StudioContext>(ctx);
|
||||||
|
foundation::createUuidMapping(ctx, path, ox::UUID::generate().unwrap());
|
||||||
return sctx->project->writeObj(path, &item, fmt);
|
return sctx->project->writeObj(path, &item, fmt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -7,12 +7,27 @@
|
|||||||
|
|
||||||
#include <ox/std/std.hpp>
|
#include <ox/std/std.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/foundation/module.hpp>
|
||||||
|
|
||||||
#include "project.hpp"
|
#include "project.hpp"
|
||||||
|
|
||||||
namespace nostalgia::studio {
|
namespace nostalgia::studio {
|
||||||
|
|
||||||
Project::Project(ox::FileSystem *fs, ox::String path) noexcept: m_path(std::move(path)), m_typeStore(fs), m_fs(fs) {
|
static void generateTypes(ox::TypeStore *ts) noexcept {
|
||||||
|
for (const auto mod : *foundation::modules()) {
|
||||||
|
for (auto gen : mod->types()) {
|
||||||
|
oxLogError(gen(ts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Project::Project(foundation::Context *ctx, ox::String path) noexcept:
|
||||||
|
m_path(std::move(path)),
|
||||||
|
m_typeStore(ctx->rom.get()),
|
||||||
|
m_fs(ctx->rom.get()),
|
||||||
|
m_ctx(ctx) {
|
||||||
oxTracef("nostalgia::studio", "Project: {}", m_path);
|
oxTracef("nostalgia::studio", "Project: {}", m_path);
|
||||||
|
generateTypes(&m_typeStore);
|
||||||
buildFileIndex();
|
buildFileIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,15 +83,24 @@ void Project::indexFile(ox::CRStringView path) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Error Project::writeBuff(const ox::StringView &path, const ox::Buffer &buff) noexcept {
|
ox::Error Project::writeBuff(const ox::StringView &path, const ox::Buffer &buff) noexcept {
|
||||||
|
constexpr auto HdrSz = 40;
|
||||||
|
ox::Buffer outBuff;
|
||||||
|
outBuff.reserve(buff.size() + HdrSz);
|
||||||
|
ox::BufferWriter writer(&outBuff);
|
||||||
|
const auto [uuid, err] = m_ctx->pathToUuid.at(path);
|
||||||
|
if (!err) {
|
||||||
|
oxReturnError(foundation::writeUuidHeader(&writer, *uuid));
|
||||||
|
}
|
||||||
|
oxReturnError(writer.write(buff.data(), buff.size()));
|
||||||
const auto newFile = m_fs->stat(path).error != 0;
|
const auto newFile = m_fs->stat(path).error != 0;
|
||||||
oxReturnError(m_fs->write(path, buff.data(), buff.size(), ox::FileType::NormalFile));
|
oxReturnError(m_fs->write(path, outBuff.data(), outBuff.size(), ox::FileType::NormalFile));
|
||||||
if (newFile) {
|
if (newFile) {
|
||||||
fileAdded.emit(path);
|
fileAdded.emit(path);
|
||||||
indexFile(path);
|
indexFile(path);
|
||||||
} else {
|
} else {
|
||||||
fileUpdated.emit(path);
|
fileUpdated.emit(path);
|
||||||
}
|
}
|
||||||
return OxError(0);
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::Buffer> Project::loadBuff(const ox::String &path) const noexcept {
|
ox::Result<ox::Buffer> Project::loadBuff(const ox::String &path) const noexcept {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@ -13,6 +13,7 @@
|
|||||||
#include <ox/std/hashmap.hpp>
|
#include <ox/std/hashmap.hpp>
|
||||||
|
|
||||||
#include <nostalgia/core/typestore.hpp>
|
#include <nostalgia/core/typestore.hpp>
|
||||||
|
#include <nostalgia/foundation/media.hpp>
|
||||||
|
|
||||||
#include "nostalgiastudio_export.h"
|
#include "nostalgiastudio_export.h"
|
||||||
|
|
||||||
@ -42,10 +43,11 @@ class NOSTALGIASTUDIO_EXPORT Project {
|
|||||||
ox::String m_path;
|
ox::String m_path;
|
||||||
mutable core::TypeStore m_typeStore;
|
mutable core::TypeStore m_typeStore;
|
||||||
mutable ox::FileSystem *m_fs = nullptr;
|
mutable ox::FileSystem *m_fs = nullptr;
|
||||||
|
foundation::Context *m_ctx = nullptr;
|
||||||
ox::HashMap<ox::String, ox::Vector<ox::String>> m_fileExtFileMap;
|
ox::HashMap<ox::String, ox::Vector<ox::String>> m_fileExtFileMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Project(ox::FileSystem *fs, ox::String path) noexcept;
|
explicit Project(foundation::Context *ctx, ox::String path) noexcept;
|
||||||
|
|
||||||
ox::Error create() noexcept;
|
ox::Error create() noexcept;
|
||||||
|
|
||||||
@ -118,7 +120,7 @@ ox::Error Project::writeObj(const ox::String &path, const T *obj, ox::ClawFormat
|
|||||||
// replace garbage last character with new line
|
// replace garbage last character with new line
|
||||||
typeOut.back().value = '\n';
|
typeOut.back().value = '\n';
|
||||||
// write to FS
|
// write to FS
|
||||||
const auto typePath = ox::sfmt("{}/{}", descPath, buildTypeId(*t));
|
const auto typePath = ox::sfmt("/{}/{}", descPath, buildTypeId(*t));
|
||||||
oxReturnError(writeBuff(typePath, typeOut));
|
oxReturnError(writeBuff(typePath, typeOut));
|
||||||
}
|
}
|
||||||
fileUpdated.emit(path);
|
fileUpdated.emit(path);
|
||||||
@ -129,9 +131,9 @@ template<typename T>
|
|||||||
ox::Result<T> Project::loadObj(const ox::String &path) const noexcept {
|
ox::Result<T> Project::loadObj(const ox::String &path) const noexcept {
|
||||||
oxRequire(buff, loadBuff(path));
|
oxRequire(buff, loadBuff(path));
|
||||||
if constexpr (ox::is_same_v<T, ox::ModelObject>) {
|
if constexpr (ox::is_same_v<T, ox::ModelObject>) {
|
||||||
return ox::readClaw(&m_typeStore, buff);
|
return foundation::readAsset(&m_typeStore, buff);
|
||||||
} else {
|
} else {
|
||||||
return ox::readClaw<T>(buff);
|
return foundation::readAsset<T>(buff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
#include <ox/logconn/logconn.hpp>
|
#include <ox/logconn/logconn.hpp>
|
||||||
#include <ox/std/trace.hpp>
|
#include <ox/std/trace.hpp>
|
||||||
|
#include <ox/std/uuid.hpp>
|
||||||
|
|
||||||
#include <nostalgia/appmodules/appmodules.hpp>
|
#include <nostalgia/appmodules/appmodules.hpp>
|
||||||
#include <nostalgia/core/core.hpp>
|
#include <nostalgia/core/core.hpp>
|
||||||
@ -54,16 +57,15 @@ static ox::Error run(ox::UniquePtr<ox::FileSystem> fs) noexcept {
|
|||||||
return core::run(ctx.get());
|
return core::run(ctx.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static ox::Error run(int argc, const char **argv) noexcept {
|
static ox::Error run(int, const char**) noexcept {
|
||||||
ox::trace::init();
|
ox::trace::init();
|
||||||
|
const auto time = std::time(nullptr);
|
||||||
|
ox::UUID::seedGenerator({
|
||||||
|
static_cast<uint64_t>(time),
|
||||||
|
static_cast<uint64_t>(time << 1)
|
||||||
|
});
|
||||||
loadModules();
|
loadModules();
|
||||||
if (argc >= 2) {
|
return run(ox::UniquePtr<ox::FileSystem>(nullptr));
|
||||||
const auto path = argv[1];
|
|
||||||
oxRequireM(fs, foundation::loadRomFs(path));
|
|
||||||
return run(std::move(fs));
|
|
||||||
} else {
|
|
||||||
return run(ox::UniquePtr<ox::FileSystem>(nullptr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
@ -108,7 +108,8 @@ void NewMenu::drawLastPageButtons(core::Context *ctx) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NewMenu::finish(core::Context *ctx) noexcept {
|
void NewMenu::finish(core::Context *ctx) noexcept {
|
||||||
const auto err = m_types[static_cast<std::size_t>(m_selectedType)]->write(ctx, m_itemName);
|
const auto itemName = ox::String(m_itemName);
|
||||||
|
const auto err = m_types[static_cast<std::size_t>(m_selectedType)]->write(ctx, itemName);
|
||||||
if (err) {
|
if (err) {
|
||||||
oxLogError(err);
|
oxLogError(err);
|
||||||
return;
|
return;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
@ -286,9 +286,9 @@ void StudioUI::save() noexcept {
|
|||||||
|
|
||||||
ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
|
ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
|
||||||
oxRequireM(fs, foundation::loadRomFs(path));
|
oxRequireM(fs, foundation::loadRomFs(path));
|
||||||
m_ctx->rom = std::move(fs);
|
oxReturnError(foundation::setRomFs(m_ctx, std::move(fs)));
|
||||||
core::setWindowTitle(m_ctx, ox::sfmt("Nostalgia Studio - {}", path));
|
core::setWindowTitle(m_ctx, ox::sfmt("Nostalgia Studio - {}", path));
|
||||||
m_project = ox::make_unique<studio::Project>(m_ctx->rom.get(), path);
|
m_project = ox::make_unique<studio::Project>(m_ctx, path);
|
||||||
auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||||
sctx->project = m_project.get();
|
sctx->project = m_project.get();
|
||||||
m_project->fileAdded.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
m_project->fileAdded.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <nostalgia/appmodules/appmodules.hpp>
|
#include <nostalgia/appmodules/appmodules.hpp>
|
||||||
#include <nostalgia/core/typestore.hpp>
|
#include <nostalgia/core/typestore.hpp>
|
||||||
#include <nostalgia/foundation/foundation.hpp>
|
#include <nostalgia/foundation/foundation.hpp>
|
||||||
|
#include <nostalgia/foundation/module.hpp>
|
||||||
|
|
||||||
#include "pack/pack.hpp"
|
#include "pack/pack.hpp"
|
||||||
|
|
||||||
@ -44,6 +45,15 @@ static ox::Result<ox::Buffer> readFileBuff(ox::CRStringView path) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ox::Error generateTypes(ox::TypeStore *ts) noexcept {
|
||||||
|
for (const auto mod : *foundation::modules()) {
|
||||||
|
for (auto gen : mod->types()) {
|
||||||
|
oxReturnError(gen(ts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static ox::Error run(const ox::ClArgs &args) noexcept {
|
static ox::Error run(const ox::ClArgs &args) noexcept {
|
||||||
loadModules();
|
loadModules();
|
||||||
const auto argSrc = args.getString("src", "");
|
const auto argSrc = args.getString("src", "");
|
||||||
@ -59,8 +69,9 @@ static ox::Error run(const ox::ClArgs &args) noexcept {
|
|||||||
ox::Buffer dstBuff(32 * ox::units::MB);
|
ox::Buffer dstBuff(32 * ox::units::MB);
|
||||||
oxReturnError(ox::FileSystem32::format(dstBuff.data(), dstBuff.size()));
|
oxReturnError(ox::FileSystem32::format(dstBuff.data(), dstBuff.size()));
|
||||||
ox::FileSystem32 dst(ox::FileStore32(dstBuff.data(), dstBuff.size()));
|
ox::FileSystem32 dst(ox::FileStore32(dstBuff.data(), dstBuff.size()));
|
||||||
const auto ctx = foundation::init(ox::make_unique<ox::PassThroughFS>(argSrc), "nost-pack");
|
oxRequire(ctx, foundation::init(ox::make_unique<ox::PassThroughFS>(argSrc), "nost-pack"));
|
||||||
core::TypeStore ts(ctx->rom.get());
|
core::TypeStore ts(ctx->rom.get());
|
||||||
|
oxReturnError(generateTypes(&ts));
|
||||||
oxReturnError(pack(ctx.get(), &ts, &dst));
|
oxReturnError(pack(ctx.get(), &ts, &dst));
|
||||||
oxRequireM(pl, GbaPreloader::make());
|
oxRequireM(pl, GbaPreloader::make());
|
||||||
oxReturnError(preload(&ts, &dst, pl.get()));
|
oxReturnError(preload(&ts, &dst, pl.get()));
|
||||||
|
@ -7,15 +7,13 @@
|
|||||||
#include <ox/model/descwrite.hpp>
|
#include <ox/model/descwrite.hpp>
|
||||||
#include <ox/model/modelvalue.hpp>
|
#include <ox/model/modelvalue.hpp>
|
||||||
|
|
||||||
#include <nostalgia/core/gfx.hpp>
|
|
||||||
#include <nostalgia/core/typestore.hpp>
|
|
||||||
#include <nostalgia/foundation/media.hpp>
|
#include <nostalgia/foundation/media.hpp>
|
||||||
|
|
||||||
#include "pack.hpp"
|
#include "pack.hpp"
|
||||||
|
|
||||||
namespace nostalgia {
|
namespace nostalgia {
|
||||||
|
|
||||||
static ox::Error pathToInode(ox::FileSystem *dest, ox::ModelObject *obj) noexcept {
|
static ox::Error pathToInode(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelObject *obj) noexcept {
|
||||||
auto &o = *obj;
|
auto &o = *obj;
|
||||||
auto type = static_cast<ox::FileAddressType>(o["type"].get<int8_t>());
|
auto type = static_cast<ox::FileAddressType>(o["type"].get<int8_t>());
|
||||||
auto &data = o["data"].get<ox::ModelUnion>();
|
auto &data = o["data"].get<ox::ModelUnion>();
|
||||||
@ -31,33 +29,52 @@ static ox::Error pathToInode(ox::FileSystem *dest, ox::ModelObject *obj) noexcep
|
|||||||
case ox::FileAddressType::None:
|
case ox::FileAddressType::None:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
if (beginsWith(path, "uuid://")) {
|
||||||
|
const auto uuid = ox::StringView(path).substr(7);
|
||||||
|
path = ctx->uuidToPath[uuid];
|
||||||
|
}
|
||||||
oxRequire(s, dest->stat(path));
|
oxRequire(s, dest->stat(path));
|
||||||
oxReturnError(o["type"].set(static_cast<int8_t>(ox::FileAddressType::Inode)));
|
oxReturnError(o["type"].set(static_cast<int8_t>(ox::FileAddressType::Inode)));
|
||||||
return data.set(2, s.inode);
|
return data.set(2, s.inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static ox::Error transformFileAddressesObj(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelObject *obj) noexcept;
|
||||||
* Convert path references in Claw data to inodes to save space
|
static ox::Error transformFileAddressesVec(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelValueVector *v) noexcept;
|
||||||
* @param buff buffer holding file
|
|
||||||
* @return error
|
static ox::Error transformFileAddresses(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelValue *v) noexcept {
|
||||||
*/
|
if (v->type() == ox::ModelValue::Type::Object) {
|
||||||
static ox::Error transformFileAddresses(ox::FileSystem *dest, ox::ModelObject *obj) noexcept {
|
auto &obj = v->get<ox::ModelObject>();
|
||||||
for (auto &f : *obj) {
|
return transformFileAddressesObj(ctx, dest, &obj);
|
||||||
auto &v = f->value;
|
} else if (v->type() == ox::ModelValue::Type::Vector) {
|
||||||
if (v.type() != ox::ModelValue::Type::Object) {
|
auto &vec = v->get<ox::ModelValueVector>();
|
||||||
continue;
|
return transformFileAddressesVec(ctx, dest, &vec);
|
||||||
}
|
|
||||||
auto &o = v.get<ox::ModelObject>();
|
|
||||||
if (o.typeName() == "net.drinkingtea.ox.FileAddress" && o.typeVersion() == 1) {
|
|
||||||
oxReturnError(pathToInode(dest, &o));
|
|
||||||
} else {
|
|
||||||
oxReturnError(transformFileAddresses(dest, &o));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static ox::Error doTransformations(foundation::Context *ctx, core::TypeStore *ts, ox::FileSystem *dest, ox::CRStringView filePath) noexcept {
|
static ox::Error transformFileAddressesVec(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelValueVector *v) noexcept {
|
||||||
|
for (auto &f : *v) {
|
||||||
|
oxReturnError(transformFileAddresses(ctx, dest, &f));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert path references in Claw data to inodes to save space
|
||||||
|
* @return error
|
||||||
|
*/
|
||||||
|
static ox::Error transformFileAddressesObj(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelObject *obj) noexcept {
|
||||||
|
if (obj->typeName() == "net.drinkingtea.ox.FileAddress" && obj->typeVersion() == 1) {
|
||||||
|
return pathToInode(ctx, dest, obj);
|
||||||
|
}
|
||||||
|
for (auto &f : *obj) {
|
||||||
|
auto &v = f->value;
|
||||||
|
oxReturnError(transformFileAddresses(ctx, dest, &v));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static ox::Error doTransformations(foundation::Context *ctx, ox::TypeStore *ts, ox::FileSystem *dest, ox::CRStringView filePath) noexcept {
|
||||||
// load file
|
// load file
|
||||||
oxRequire(s, dest->stat(filePath));
|
oxRequire(s, dest->stat(filePath));
|
||||||
// do transformations
|
// do transformations
|
||||||
@ -66,8 +83,8 @@ static ox::Error doTransformations(foundation::Context *ctx, core::TypeStore *ts
|
|||||||
oxReturnError(tr(ctx, &buff));
|
oxReturnError(tr(ctx, &buff));
|
||||||
}
|
}
|
||||||
// transform FileAddresses
|
// transform FileAddresses
|
||||||
oxRequireM(obj, ox::readClaw(ts, buff));
|
oxRequireM(obj, foundation::readAsset(ts, buff));
|
||||||
oxReturnError(transformFileAddresses(dest, &obj));
|
oxReturnError(transformFileAddressesObj(ctx, dest, &obj));
|
||||||
oxReturnError(ox::writeClaw(&obj).moveTo(&buff));
|
oxReturnError(ox::writeClaw(&obj).moveTo(&buff));
|
||||||
// write file to dest
|
// write file to dest
|
||||||
oxReturnError(dest->write(s.inode, buff.data(), buff.size()));
|
oxReturnError(dest->write(s.inode, buff.data(), buff.size()));
|
||||||
@ -76,7 +93,7 @@ static ox::Error doTransformations(foundation::Context *ctx, core::TypeStore *ts
|
|||||||
|
|
||||||
// claw file transformations are broken out from copy because path to inode
|
// claw file transformations are broken out from copy because path to inode
|
||||||
// transformations need to be done after the copy to the new FS is complete
|
// transformations need to be done after the copy to the new FS is complete
|
||||||
static ox::Error transformClaw(foundation::Context *ctx, core::TypeStore *ts, ox::FileSystem *dest, ox::CRStringView path) noexcept {
|
static ox::Error transformClaw(foundation::Context *ctx, ox::TypeStore *ts, ox::FileSystem *dest, ox::CRStringView path) noexcept {
|
||||||
// copy
|
// copy
|
||||||
oxTracef("pack::transformClaw", "path: {}", path);
|
oxTracef("pack::transformClaw", "path: {}", path);
|
||||||
oxRequire(fileList, dest->ls(path));
|
oxRequire(fileList, dest->ls(path));
|
||||||
@ -108,9 +125,8 @@ struct VerificationPair {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRStringView path) noexcept {
|
static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRStringView path, ox::Vector<VerificationPair> *verificationPairs) noexcept {
|
||||||
oxOutf("copying directory: {}\n", path);
|
oxOutf("copying directory: {}\n", path);
|
||||||
ox::Vector<VerificationPair> verificationPairs;
|
|
||||||
// copy
|
// copy
|
||||||
oxRequire(fileList, src->ls(path));
|
oxRequire(fileList, src->ls(path));
|
||||||
for (const auto &name : fileList) {
|
for (const auto &name : fileList) {
|
||||||
@ -122,7 +138,7 @@ static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRStringVie
|
|||||||
oxRequire(stat, src->stat(currentFile));
|
oxRequire(stat, src->stat(currentFile));
|
||||||
if (stat.fileType == ox::FileType::Directory) {
|
if (stat.fileType == ox::FileType::Directory) {
|
||||||
oxReturnError(dest->mkdir(currentFile, true));
|
oxReturnError(dest->mkdir(currentFile, true));
|
||||||
oxReturnError(copy(src, dest, currentFile + '/'));
|
oxReturnError(copy(src, dest, currentFile + '/', verificationPairs));
|
||||||
} else {
|
} else {
|
||||||
// load file
|
// load file
|
||||||
oxRequireM(buff, src->read(currentFile));
|
oxRequireM(buff, src->read(currentFile));
|
||||||
@ -130,10 +146,17 @@ static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRStringVie
|
|||||||
oxOutf("writing {}\n", currentFile);
|
oxOutf("writing {}\n", currentFile);
|
||||||
oxReturnError(dest->write(currentFile, buff.data(), buff.size()));
|
oxReturnError(dest->write(currentFile, buff.data(), buff.size()));
|
||||||
oxReturnError(verifyFile(dest, currentFile, buff));
|
oxReturnError(verifyFile(dest, currentFile, buff));
|
||||||
verificationPairs.emplace_back(std::move(currentFile), std::move(buff));
|
verificationPairs->emplace_back(std::move(currentFile), std::move(buff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static ox::Error copyFS(ox::FileSystem *src, ox::FileSystem *dest) noexcept {
|
||||||
|
ox::Vector<VerificationPair> verificationPairs;
|
||||||
|
oxReturnError(copy(src, dest, "/", &verificationPairs));
|
||||||
// verify all at once in addition to right after the files are written
|
// verify all at once in addition to right after the files are written
|
||||||
|
oxOutf("Verifying completed destination\n");
|
||||||
for (const auto &v : verificationPairs) {
|
for (const auto &v : verificationPairs) {
|
||||||
oxReturnError(verifyFile(dest, v.path, v.buff));
|
oxReturnError(verifyFile(dest, v.path, v.buff));
|
||||||
}
|
}
|
||||||
@ -141,11 +164,14 @@ static ox::Error copy(ox::FileSystem *src, ox::FileSystem *dest, ox::CRStringVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transformations need to be done after the copy to the new FS is complete
|
// transformations need to be done after the copy to the new FS is complete
|
||||||
static ox::Error preloadObj(core::TypeStore *ts, ox::FileSystem *romFs, GbaPreloader *pl, ox::CRStringView path) noexcept {
|
static ox::Error preloadObj(
|
||||||
|
ox::TypeStore *ts, ox::FileSystem *romFs,
|
||||||
|
GbaPreloader *pl, ox::CRStringView path) noexcept {
|
||||||
// load file
|
// load file
|
||||||
oxRequireM(buff, romFs->read(path));
|
oxRequireM(buff, romFs->read(path));
|
||||||
oxRequireM(obj, ox::readClaw(ts, buff));
|
oxRequireM(obj, foundation::readAsset(ts, buff));
|
||||||
if (obj.type()->preloadable) {
|
if (obj.type()->preloadable) {
|
||||||
|
oxOutf("preloading {}\n", path);
|
||||||
// preload
|
// preload
|
||||||
oxRequire(a, pl->startAlloc(ox::sizeOf<GbaPlatSpec>(&obj)));
|
oxRequire(a, pl->startAlloc(ox::sizeOf<GbaPlatSpec>(&obj)));
|
||||||
const auto err = ox::preload<GbaPlatSpec, decltype(obj)>(pl, &obj);
|
const auto err = ox::preload<GbaPlatSpec, decltype(obj)>(pl, &obj);
|
||||||
@ -163,7 +189,7 @@ static ox::Error preloadObj(core::TypeStore *ts, ox::FileSystem *romFs, GbaPrelo
|
|||||||
|
|
||||||
// claw file transformations are broken out because path to inode
|
// claw file transformations are broken out because path to inode
|
||||||
// transformations need to be done after the copy to the new FS is complete
|
// transformations need to be done after the copy to the new FS is complete
|
||||||
static ox::Error preloadDir(core::TypeStore *ts, ox::FileSystem *romFs, GbaPreloader *pl, ox::CRStringView path) noexcept {
|
static ox::Error preloadDir(ox::TypeStore *ts, ox::FileSystem *romFs, GbaPreloader *pl, ox::CRStringView path) noexcept {
|
||||||
// copy
|
// copy
|
||||||
oxTracef("pack::preload", "path: {}", path);
|
oxTracef("pack::preload", "path: {}", path);
|
||||||
oxRequire(fileList, romFs->ls(path));
|
oxRequire(fileList, romFs->ls(path));
|
||||||
@ -202,14 +228,15 @@ ox::Error appendBinary(ox::Buffer *binBuff, ox::Buffer *fsBuff, GbaPreloader *pl
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error pack(foundation::Context *ctx, core::TypeStore *ts, ox::FileSystem *dest) noexcept {
|
ox::Error pack(foundation::Context *ctx, ox::TypeStore *ts, ox::FileSystem *dest) noexcept {
|
||||||
oxReturnError(copy(ctx->rom.get(), dest, "/"));
|
oxReturnError(copyFS(ctx->rom.get(), dest));
|
||||||
oxReturnError(ox::buildTypeDef<core::CompactTileSheet>(ts));
|
oxOut("Doing transforms\n");
|
||||||
oxReturnError(transformClaw(ctx, ts, dest, "/"));
|
oxReturnError(transformClaw(ctx, ts, dest, "/"));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error preload(core::TypeStore *ts, ox::FileSystem *src, GbaPreloader *pl) noexcept {
|
ox::Error preload(ox::TypeStore *ts, ox::FileSystem *src, GbaPreloader *pl) noexcept {
|
||||||
|
oxOut("Preloading\n");
|
||||||
return preloadDir(ts, src, pl, "/");
|
return preloadDir(ts, src, pl, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,10 +11,6 @@ namespace foundation {
|
|||||||
class Context;
|
class Context;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace core {
|
|
||||||
class TypeStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GbaPlatSpec {
|
struct GbaPlatSpec {
|
||||||
using PtrType = uint32_t;
|
using PtrType = uint32_t;
|
||||||
using size_t = uint32_t;
|
using size_t = uint32_t;
|
||||||
@ -95,8 +91,8 @@ using GbaPreloader = ox::Preloader<GbaPlatSpec>;
|
|||||||
|
|
||||||
ox::Error appendBinary(ox::Buffer *binBuff, ox::Buffer *fsBuff, GbaPreloader *pl) noexcept;
|
ox::Error appendBinary(ox::Buffer *binBuff, ox::Buffer *fsBuff, GbaPreloader *pl) noexcept;
|
||||||
|
|
||||||
auto pack(foundation::Context *ctx, core::TypeStore *ts, ox::FileSystem *dest) noexcept -> ox::Error;
|
auto pack(foundation::Context *ctx, ox::TypeStore *ts, ox::FileSystem *dest) noexcept -> ox::Error;
|
||||||
|
|
||||||
auto preload(core::TypeStore *ts, ox::FileSystem *src, GbaPreloader *ph) noexcept -> ox::Error;
|
auto preload(ox::TypeStore *ts, ox::FileSystem *src, GbaPreloader *ph) noexcept -> ox::Error;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
add_library(
|
|
||||||
NostalgiaWorld
|
|
||||||
world.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(
|
|
||||||
NostalgiaWorld PUBLIC
|
|
||||||
NostalgiaCore
|
|
||||||
OxMetalClaw
|
|
||||||
)
|
|
||||||
|
|
||||||
#install(TARGETS NostalgiaCommon DESTINATION lib)
|
|
||||||
install(
|
|
||||||
FILES
|
|
||||||
world.hpp
|
|
||||||
DESTINATION
|
|
||||||
include/nostalgia/world
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOSTALGIA_BUILD_STUDIO)
|
|
||||||
#add_subdirectory(studio)
|
|
||||||
endif()
|
|
@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
add_library(
|
|
||||||
NostalgiaWorld-Studio OBJECT
|
|
||||||
consts.cpp
|
|
||||||
newworldwizard.cpp
|
|
||||||
worldstudioplugin.cpp
|
|
||||||
worldeditor.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(
|
|
||||||
NostalgiaWorld-Studio
|
|
||||||
NostalgiaStudio
|
|
||||||
)
|
|
||||||
|
|
||||||
install(
|
|
||||||
TARGETS
|
|
||||||
NostalgiaWorld-Studio
|
|
||||||
LIBRARY DESTINATION
|
|
||||||
${NOSTALGIA_DIST_MODULE}
|
|
||||||
)
|
|
@ -1,11 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "consts.hpp"
|
|
||||||
|
|
||||||
namespace nostalgia::world {
|
|
||||||
|
|
||||||
QString PATH_ZONES = "/World/Zones/";
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace nostalgia::world {
|
|
||||||
|
|
||||||
extern QString PATH_ZONES;
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "consts.hpp"
|
|
||||||
#include "newworldwizard.hpp"
|
|
||||||
|
|
||||||
namespace nostalgia::world {
|
|
||||||
|
|
||||||
using namespace studio;
|
|
||||||
|
|
||||||
const QString NewWorldWizard::FIELD_WORLD_PATH = "World.WorldPath";
|
|
||||||
|
|
||||||
NewWorldWizard::NewWorldWizard(const Context *ctx) {
|
|
||||||
addLineEdit(tr("&Name:"), FIELD_WORLD_PATH, "", [this, ctx](QString worldName) {
|
|
||||||
worldName = PATH_ZONES + worldName;
|
|
||||||
auto exists = ctx->project->exists(worldName);
|
|
||||||
if (exists) {
|
|
||||||
this->showValidationError(tr("World already exists: %1").arg(worldName));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QPluginLoader>
|
|
||||||
|
|
||||||
#include <nostalgia/studio/studio.hpp>
|
|
||||||
|
|
||||||
namespace nostalgia::world {
|
|
||||||
|
|
||||||
struct NewWorldWizard: public studio::WizardFormPage {
|
|
||||||
|
|
||||||
static const QString FIELD_WORLD_PATH;
|
|
||||||
|
|
||||||
NewWorldWizard(const studio::Context *ctx);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user