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:
|
||||
- src
|
||||
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());
|
||||
}
|
||||
|
||||
Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept {
|
||||
oxRequire(header, readClawHeader(buff));
|
||||
Result<ModelObject> readClaw(TypeStore *ts, const char *buff, std::size_t buffSz) noexcept {
|
||||
oxRequire(header, readClawHeader(buff, buffSz));
|
||||
oxRequire(t, ts->getLoad(header.typeName, header.typeVersion, header.typeParams));
|
||||
ModelObject obj;
|
||||
oxReturnError(obj.setType(t));
|
||||
@ -102,4 +102,8 @@ Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept {
|
||||
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());
|
||||
}
|
||||
|
||||
Result<ModelObject> readClaw(TypeStore *ts, const char *buff, std::size_t buffSz) 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);
|
||||
oxTracef("ox::fs::PassThroughFS::statInode", "{} {}", ec.message(), path);
|
||||
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};
|
||||
}
|
||||
|
||||
|
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();
|
||||
++i;
|
||||
}
|
||||
m_type = other.m_type;
|
||||
m_unionIdx = other.m_unionIdx;
|
||||
}
|
||||
|
||||
constexpr ModelUnion(ModelUnion &&other) noexcept {
|
||||
m_fieldsOrder = std::move(other.m_fieldsOrder);
|
||||
m_fields = std::move(other.m_fields);
|
||||
m_type = other.m_type;
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
const auto &jv = value(key);
|
||||
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/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/uuid.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
@ -79,6 +80,8 @@ class OrganicClawReader {
|
||||
|
||||
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.
|
||||
* @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), {});
|
||||
}
|
||||
|
||||
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 {
|
||||
if (m_json.isArray()) {
|
||||
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/hashmap.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/uuid.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
@ -105,7 +106,7 @@ class OrganicClawWriter {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return OxError(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -130,7 +131,7 @@ class OrganicClawWriter {
|
||||
value(key) = w.m_json;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return OxError(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -172,6 +173,8 @@ class OrganicClawWriter {
|
||||
|
||||
Error fieldCString(const char *name, char **val) noexcept;
|
||||
|
||||
Error field(const char *key, const UUID *uuid) noexcept;
|
||||
|
||||
template<typename T>
|
||||
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) {}
|
||||
|
||||
public:
|
||||
Preloader(const Preloader &src) = delete;
|
||||
Preloader(Preloader &&src) = delete;
|
||||
const Preloader &operator=(const 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,
|
||||
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
|
||||
trace.cpp
|
||||
typetraits.cpp
|
||||
uuid.cpp
|
||||
)
|
||||
|
||||
if(NOT MSVC)
|
||||
@ -109,6 +110,7 @@ install(
|
||||
types.hpp
|
||||
typetraits.hpp
|
||||
units.hpp
|
||||
uuid.hpp
|
||||
vector.hpp
|
||||
writer.hpp
|
||||
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 defined(OX_USE_STDLIB)
|
||||
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);
|
||||
oxTracef("assert::expect", "Failed assert: {} == {} [{}:{}]", detail::toStringView<true>(actual), detail::toStringView<true>(expected), file, line);
|
||||
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 &operator=(CRStringView str) noexcept;
|
||||
|
||||
constexpr BString &operator=(const 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 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;
|
||||
|
||||
@ -58,6 +64,10 @@ class BString {
|
||||
|
||||
constexpr Error append(const char *str, std::size_t strLen) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const char *data() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr char *data() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
@ -88,12 +98,12 @@ constexpr BString<size>::BString() noexcept: m_buff{{0}} {
|
||||
|
||||
template<std::size_t size>
|
||||
constexpr BString<size>::BString(StringView str) noexcept: m_buff{{0}} {
|
||||
*this = str;
|
||||
operator=(str);
|
||||
}
|
||||
|
||||
template<std::size_t size>
|
||||
constexpr BString<size>::BString(const char *str) noexcept: m_buff{{0}} {
|
||||
*this = str;
|
||||
operator=(str);
|
||||
}
|
||||
|
||||
template<std::size_t size>
|
||||
@ -103,6 +113,18 @@ constexpr BString<size> &BString<size>::operator=(Integer_c auto i) noexcept {
|
||||
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>
|
||||
constexpr BString<size> &BString<size>::operator=(const char *str) noexcept {
|
||||
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);
|
||||
// make sure last element is a null terminator
|
||||
m_buff[cap() - 1] = 0;
|
||||
m_buff[cap()] = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -167,21 +189,22 @@ constexpr BString<size> BString<size>::operator+(Integer_c auto i) const noexcep
|
||||
}
|
||||
|
||||
template<std::size_t buffLen>
|
||||
constexpr bool BString<buffLen>::operator==(const BString<buffLen> &other) const noexcept {
|
||||
bool retval = true;
|
||||
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;
|
||||
constexpr bool BString<buffLen>::operator==(const char *other) const noexcept {
|
||||
return ox::StringView(*this) == other;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -209,6 +232,11 @@ constexpr Error BString<buffLen>::append(const char *str, std::size_t strLen) no
|
||||
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>
|
||||
constexpr char *BString<buffLen>::data() noexcept {
|
||||
return static_cast<char*>(m_buff);
|
||||
|
4
deps/ox/src/ox/std/concepts.hpp
vendored
4
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,
|
||||
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>
|
||||
concept same_as = ox::is_same_v<T, T>;
|
||||
|
||||
|
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("");
|
||||
}
|
||||
T out;
|
||||
out += list.front().value;
|
||||
out += list[0];
|
||||
for (auto i = 1ul; i < list.size(); ++i) {
|
||||
out += d;
|
||||
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>
|
||||
[[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;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[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;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[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);
|
||||
}
|
||||
|
||||
|
39
deps/ox/src/ox/std/optional.hpp
vendored
39
deps/ox/src/ox/std/optional.hpp
vendored
@ -11,6 +11,7 @@
|
||||
#include "bit.hpp"
|
||||
#include "initializerlist.hpp"
|
||||
#include "iterator.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "new.hpp"
|
||||
#include "types.hpp"
|
||||
#include "utility.hpp"
|
||||
@ -21,19 +22,17 @@ template<typename T, std::size_t buffSize = sizeof(T)>
|
||||
class Optional {
|
||||
private:
|
||||
T *m_ptr = nullptr;
|
||||
char m_data[buffSize] = {};
|
||||
AllocAlias<T> m_data = {};
|
||||
|
||||
public:
|
||||
constexpr Optional() noexcept = default;
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr Optional(Args &&... args);
|
||||
explicit constexpr Optional(Args &&... args);
|
||||
|
||||
constexpr Optional(const Optional &other) {
|
||||
if (other.m_ptr) {
|
||||
m_ptr = new(m_data) T(*other.m_ptr);
|
||||
} else {
|
||||
m_ptr = nullptr;
|
||||
m_ptr = new(m_data.data()) T(*other.m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,15 +41,13 @@ class Optional {
|
||||
m_ptr->~T();
|
||||
}
|
||||
if (other.m_ptr) {
|
||||
m_ptr = new(m_data) T(std::move(*other.m_ptr));
|
||||
} else {
|
||||
m_ptr = nullptr;
|
||||
m_ptr = new(m_data.data()) T(std::move(*other.m_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ~Optional() {
|
||||
if (m_ptr) {
|
||||
m_ptr->~T();
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,8 +95,7 @@ class Optional {
|
||||
}
|
||||
*m_ptr = *other.m_ptr;
|
||||
} else if (m_ptr) {
|
||||
m_ptr->~T();
|
||||
m_ptr = nullptr;
|
||||
reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -114,22 +110,29 @@ class Optional {
|
||||
}
|
||||
*m_ptr = std::move(*other.m_ptr);
|
||||
} else if (m_ptr) {
|
||||
m_ptr->~T();
|
||||
m_ptr = nullptr;
|
||||
reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... 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;
|
||||
}
|
||||
|
||||
template<typename U, class... Args>
|
||||
constexpr T &emplace_subclass(Args &&...args) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -143,7 +146,11 @@ class Optional {
|
||||
}
|
||||
|
||||
constexpr void reset() noexcept {
|
||||
get()->~T();
|
||||
if (std::is_constant_evaluated()) {
|
||||
ox::safeDelete(m_ptr);
|
||||
} else {
|
||||
get()->~T();
|
||||
}
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
|
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
|
||||
* 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
|
||||
* 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;
|
||||
|
||||
constexpr void seed(const RandomSeed &seed) noexcept;
|
||||
|
||||
constexpr uint64_t gen() noexcept;
|
||||
};
|
||||
|
||||
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 {
|
||||
auto s0 = m_seed[0];
|
||||
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 "typetraits.hpp"
|
||||
#include "units.hpp"
|
||||
#include "uuid.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>
|
||||
constexpr bool BasicString<SmallStringSize_v>::operator==(const OxString_c auto &other) const noexcept {
|
||||
if (len() != other.len()) {
|
||||
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;
|
||||
return ox::StringView(*this) == ox::StringView(other);
|
||||
}
|
||||
|
||||
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
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -12,10 +12,12 @@
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
#include "bit.hpp"
|
||||
#include "iterator.hpp"
|
||||
#include "strops.hpp"
|
||||
#include "types.hpp"
|
||||
#include "bit.hpp"
|
||||
#include "vector.hpp"
|
||||
#include "writer.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
@ -223,7 +225,18 @@ class StringView {
|
||||
|
||||
[[nodiscard]]
|
||||
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 {
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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
|
||||
constexpr auto toStdStringView(CRStringView sv) noexcept {
|
||||
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] Serialize-Int" StdTest "Serialize-Int")
|
||||
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
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -9,6 +9,7 @@
|
||||
#undef NDEBUG
|
||||
|
||||
#include <map>
|
||||
#include <ox/std/uuid.hpp>
|
||||
#include <ox/std/std.hpp>
|
||||
|
||||
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");
|
||||
s = "asdf";
|
||||
oxAssert(s == "asdf", "String assign broken");
|
||||
oxAssert(s != "aoeu", "String assign broken");
|
||||
oxAssert(s.len() == 4, "String assign broken");
|
||||
return OxError(0);
|
||||
}
|
||||
},
|
||||
@ -155,6 +158,135 @@ static std::map<ox::String, ox::Error(*)()> tests = {
|
||||
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) {
|
||||
|
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 {
|
||||
char buff[sz];
|
||||
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"
|
||||
}
|
||||
],
|
||||
"preloadable" : true,
|
||||
"primitiveType" : 6,
|
||||
"typeName" : "net.drinkingtea.ox.FileAddress.Data",
|
||||
"typeVersion" : 1
|
||||
|
@ -10,6 +10,7 @@ O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||
"typeId" : "net.drinkingtea.ox.FileAddress.Data"
|
||||
}
|
||||
],
|
||||
"preloadable" : true,
|
||||
"primitiveType" : 5,
|
||||
"typeName" : "net.drinkingtea.ox.FileAddress",
|
||||
"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(geo)
|
||||
add_subdirectory(scene)
|
||||
add_subdirectory(world)
|
||||
|
||||
if(NOSTALGIA_BUILD_PLAYER)
|
||||
add_subdirectory(player)
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
namespace nostalgia {
|
||||
|
||||
static bool modulesLoaded = false;
|
||||
void loadModules() noexcept {
|
||||
static bool done = false;
|
||||
if (done) {
|
||||
if (modulesLoaded) {
|
||||
return;
|
||||
}
|
||||
const ox::Array<foundation::Module*, 2> mods = {
|
||||
@ -23,7 +23,7 @@ void loadModules() noexcept {
|
||||
for (const auto m : mods) {
|
||||
foundation::registerModule(m);
|
||||
}
|
||||
done = true;
|
||||
modulesLoaded = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ add_library(
|
||||
NostalgiaCore-Common OBJECT
|
||||
gfx.cpp
|
||||
module.cpp
|
||||
tilesheet.cpp
|
||||
typeconv.cpp
|
||||
typestore.cpp
|
||||
)
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "color.hpp"
|
||||
#include "context.hpp"
|
||||
#include "ptidxconv.hpp"
|
||||
#include "tilesheet.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
@ -31,431 +32,6 @@ enum class TileSheetSpace {
|
||||
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 {
|
||||
unsigned idx = 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])
|
||||
*/
|
||||
ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &tilesheet,
|
||||
const ox::FileAddress &palette = nullptr) noexcept;
|
||||
ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &tilesheetAddr,
|
||||
const ox::FileAddress &paletteAddr = nullptr) noexcept;
|
||||
|
||||
ox::Error loadSpriteTileSheet(Context *ctx,
|
||||
const ox::FileAddress &tilesheetAddr,
|
||||
|
@ -15,14 +15,14 @@
|
||||
namespace nostalgia::core {
|
||||
|
||||
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>();
|
||||
ctx->setWindowerData(id);
|
||||
using namespace std::chrono;
|
||||
id->startTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
glfwInit();
|
||||
oxReturnError(initGfx(ctx.get()));
|
||||
return ctx;
|
||||
return std::move(ctx);
|
||||
}
|
||||
|
||||
ox::Error run(Context *ctx) noexcept {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <ox/model/model.hpp>
|
||||
|
||||
#include <nostalgia/foundation/asset.hpp>
|
||||
#include <nostalgia/foundation/module.hpp>
|
||||
|
||||
#include "gfx.hpp"
|
||||
@ -15,11 +16,23 @@ namespace nostalgia::core {
|
||||
|
||||
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 {
|
||||
&nostalgiaPaletteToPaletteConverter,
|
||||
&nostalgiaGraphicToTileSheetConverter,
|
||||
&tileSheetToCompactTileSheetConverter,
|
||||
&tileSheetV2ToTileSheetConverter,
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,11 +40,13 @@ ox::Vector<foundation::PackTransform> CoreModule::packTransforms() const noexcep
|
||||
return {
|
||||
// convert tilesheets to CompactTileSheets
|
||||
[](foundation::Context *ctx, ox::Buffer *buff) -> ox::Error {
|
||||
oxRequire(hdr, ox::readClawHeader(*buff));
|
||||
const auto typeId = ox::buildTypeId(hdr.typeName, hdr.typeVersion);
|
||||
if (typeId == ox::buildTypeId<TileSheet>() ||
|
||||
typeId == ox::buildTypeId<NostalgiaGraphic>()) {
|
||||
oxReturnError(foundation::convertBuffToBuff<core::CompactTileSheet>(ctx, *buff, ox::ClawFormat::Metal).moveTo(buff));
|
||||
oxRequire(hdr, foundation::readAssetHeader(*buff));
|
||||
const auto typeId = ox::buildTypeId(hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams);
|
||||
if (typeId == ox::buildTypeId<TileSheetV1>() ||
|
||||
typeId == ox::buildTypeId<TileSheetV2>() ||
|
||||
typeId == ox::buildTypeId<TileSheet>()) {
|
||||
oxReturnError(foundation::convertBuffToBuff<core::CompactTileSheet>(
|
||||
ctx, *buff, ox::ClawFormat::Metal).moveTo(buff));
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
@ -12,14 +12,17 @@ namespace nostalgia::core {
|
||||
|
||||
class CoreModule: public foundation::Module {
|
||||
private:
|
||||
mutable NostalgiaPaletteToPaletteConverter nostalgiaPaletteToPaletteConverter;
|
||||
mutable NostalgiaGraphicToTileSheetConverter nostalgiaGraphicToTileSheetConverter;
|
||||
mutable TileSheetToCompactTileSheetConverter tileSheetToCompactTileSheetConverter;
|
||||
NostalgiaPaletteToPaletteConverter nostalgiaPaletteToPaletteConverter;
|
||||
TileSheetV1ToTileSheetConverter nostalgiaGraphicToTileSheetConverter;
|
||||
TileSheetToCompactTileSheetConverter tileSheetToCompactTileSheetConverter;
|
||||
TileSheetV2ToTileSheetConverter tileSheetV2ToTileSheetConverter;
|
||||
|
||||
public:
|
||||
static CoreModule mod;
|
||||
[[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]]
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
if (key == core::Key::Escape) {
|
||||
m_subsheetEditor.close();
|
||||
}
|
||||
auto pal = model()->pal();
|
||||
if (pal) {
|
||||
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;
|
||||
}
|
||||
void draw() noexcept;
|
||||
void close() noexcept;
|
||||
};
|
||||
std::size_t m_selectedPaletteIdx = 0;
|
||||
Context *m_ctx = nullptr;
|
||||
ox::Vector<ox::String> m_paletteList{};
|
||||
ox::Vector<ox::String> m_paletteList;
|
||||
SubSheetEditor m_subsheetEditor;
|
||||
ox::String m_itemPath;
|
||||
ox::String m_itemName;
|
||||
|
@ -256,12 +256,12 @@ class AddSubSheetCommand: public TileSheetCommand {
|
||||
auto &parent = m_img->getSubSheet(m_parentIdx);
|
||||
if (m_addedSheets.size() < 2) {
|
||||
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 {
|
||||
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.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;
|
||||
|
||||
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_img = img;
|
||||
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 {
|
||||
@ -597,12 +597,27 @@ void TileSheetEditorModel::paste() {
|
||||
pushCommand(ox::make<CutPasteCommand<CommandId::Paste>>(&m_img, m_activeSubsSheetIdx, pt1, pt2, *cb));
|
||||
}
|
||||
|
||||
const ox::FileAddress &TileSheetEditorModel::palPath() const noexcept {
|
||||
return m_img.defaultPalette;
|
||||
ox::StringView TileSheetEditorModel::palPath() const noexcept {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
constexpr const Palette *pal() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const ox::FileAddress &palPath() const noexcept;
|
||||
ox::StringView palPath() const 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
|
||||
|
||||
struct NostalgiaPaletteToPaletteConverter: public foundation::Converter<NostalgiaPalette, Palette> {
|
||||
ox::Error convert(foundation::Context*, NostalgiaPalette *src, Palette *dst) noexcept final {
|
||||
dst->colors = std::move(src->colors);
|
||||
return {};
|
||||
}
|
||||
class NostalgiaPaletteToPaletteConverter: public foundation::Converter<NostalgiaPalette, Palette> {
|
||||
ox::Error convert(foundation::Context*, NostalgiaPalette *src, Palette *dst) const noexcept final;
|
||||
};
|
||||
|
||||
struct NostalgiaGraphicToTileSheetConverter: public foundation::Converter<NostalgiaGraphic, TileSheet> {
|
||||
ox::Error convert(foundation::Context*, NostalgiaGraphic *src, TileSheet *dst) 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 {};
|
||||
}
|
||||
class TileSheetV1ToTileSheetConverter: public foundation::Converter<TileSheetV1, TileSheet> {
|
||||
ox::Error convert(foundation::Context*, TileSheetV1 *src, TileSheet *dst) const noexcept final;
|
||||
};
|
||||
|
||||
struct TileSheetToCompactTileSheetConverter: public foundation::Converter<TileSheet, CompactTileSheet> {
|
||||
ox::Error convert(foundation::Context*, TileSheet *src, CompactTileSheet *dst) noexcept final {
|
||||
dst->bpp = src->bpp;
|
||||
dst->defaultPalette = std::move(src->defaultPalette);
|
||||
dst->pixels = src->pixels();
|
||||
return {};
|
||||
}
|
||||
class TileSheetToCompactTileSheetConverter: public foundation::Converter<TileSheet, CompactTileSheet> {
|
||||
ox::Error convert(foundation::Context*, TileSheet *src, CompactTileSheet *dst) const noexcept final;
|
||||
};
|
||||
|
||||
class TileSheetV2ToTileSheetConverter: public foundation::Converter<TileSheetV2, TileSheet> {
|
||||
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 {
|
||||
const unsigned bytesPerTile = tilesheet.bpp == 8 ? 64 : 32;
|
||||
const unsigned bytesPerTile = tilesheet.bpp == 8 ? PixelsPerTile : PixelsPerTile / 2;
|
||||
const auto tiles = tilesheet.pixels.size() / bytesPerTile;
|
||||
constexpr int width = 8;
|
||||
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 {
|
||||
//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 z = static_cast<unsigned>(bgIdx);
|
||||
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 vbo = &bg.vertices[i * renderer::BgVertexVboLength];
|
||||
auto ebo = &bg.elements[i * renderer::BgVertexEboLength];
|
||||
renderer::setTileBufferObject(ctx, i * renderer::BgVertexVboRows,
|
||||
static_cast<float>(x), static_cast<float>(y), tile, vbo, ebo);
|
||||
renderer::setTileBufferObject(
|
||||
ctx, i * renderer::BgVertexVboRows,
|
||||
static_cast<float>(x), static_cast<float>(y),
|
||||
tile, vbo, ebo);
|
||||
bg.updated = true;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
add_library(
|
||||
NostalgiaFoundation
|
||||
foundation.hpp
|
||||
asset.cpp
|
||||
media.cpp
|
||||
module.cpp
|
||||
typeconv.cpp
|
||||
@ -15,12 +15,16 @@ target_link_libraries(
|
||||
OxModel
|
||||
OxStd
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
assetmanager.hpp
|
||||
context.hpp
|
||||
foundation.hpp
|
||||
asset.hpp
|
||||
media.hpp
|
||||
module.hpp
|
||||
typeconv.hpp
|
||||
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;
|
||||
|
||||
private:
|
||||
ox::UUID uuid;
|
||||
T m_obj;
|
||||
mutable int m_references = 0;
|
||||
|
||||
@ -190,14 +191,14 @@ class AssetManager {
|
||||
ox::HashMap<ox::String, ox::UniquePtr<AssetContainer<T>>> m_cache;
|
||||
|
||||
public:
|
||||
ox::Result<AssetRef<T>> getAsset(const ox::String &path) const noexcept {
|
||||
auto out = m_cache.at(path);
|
||||
ox::Result<AssetRef<T>> getAsset(const ox::String &assetId) const noexcept {
|
||||
auto out = m_cache.at(assetId);
|
||||
oxReturnError(out);
|
||||
return AssetRef<T>(out.value->get());
|
||||
}
|
||||
|
||||
ox::Result<AssetRef<T>> setAsset(const ox::String &path, const T &obj) noexcept {
|
||||
auto &p = m_cache[path];
|
||||
ox::Result<AssetRef<T>> setAsset(const ox::String &assetId, const T &obj) noexcept {
|
||||
auto &p = m_cache[assetId];
|
||||
if (!p) {
|
||||
p = ox::make_unique<AssetContainer<T>>(obj);
|
||||
} else {
|
||||
@ -207,8 +208,8 @@ class AssetManager {
|
||||
return AssetRef<T>(p.get());
|
||||
}
|
||||
|
||||
ox::Result<AssetRef<T>> setAsset(const ox::String &path, T &&obj) noexcept {
|
||||
auto &p = m_cache[path];
|
||||
ox::Result<AssetRef<T>> setAsset(const ox::String &assetId, T &&obj) noexcept {
|
||||
auto &p = m_cache[assetId];
|
||||
if (!p) {
|
||||
p = ox::make_unique<AssetContainer<T>>(obj);
|
||||
} else {
|
||||
@ -221,7 +222,7 @@ class AssetManager {
|
||||
void gc() noexcept final {
|
||||
for (const auto &ack : m_cache.keys()) {
|
||||
auto &ac = m_cache[ack];
|
||||
if (ac->references()) {
|
||||
if (!ac->references()) {
|
||||
m_cache.erase(ack);
|
||||
}
|
||||
}
|
||||
@ -243,15 +244,15 @@ class AssetManager {
|
||||
|
||||
public:
|
||||
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>();
|
||||
return m->getAsset(path);
|
||||
return m->getAsset(assetId);
|
||||
}
|
||||
|
||||
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>();
|
||||
return m->setAsset(path, obj);
|
||||
return m->setAsset(assetId, obj);
|
||||
}
|
||||
|
||||
void gc() noexcept {
|
||||
|
@ -21,7 +21,9 @@ class Context {
|
||||
ox::StringView appName = "Nostalgia Foundation App";
|
||||
#ifndef OX_BARE_METAL
|
||||
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;
|
||||
#else
|
||||
std::size_t preloadSectionOffset = 0;
|
||||
|
@ -7,15 +7,16 @@
|
||||
#include <ox/std/memory.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "media.hpp"
|
||||
#include "module.hpp"
|
||||
|
||||
namespace nostalgia::foundation {
|
||||
|
||||
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>();
|
||||
ctx->rom = std::move(fs);
|
||||
ctx->appName = appName;
|
||||
oxIgnoreError(setRomFs(ctx.get(), std::move(fs)));
|
||||
auto mods = modules();
|
||||
if (mods) {
|
||||
for (auto &mod : *mods) {
|
||||
|
@ -40,6 +40,43 @@ ox::Result<void*> findPreloadSection() noexcept {
|
||||
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
|
||||
@ -50,6 +87,13 @@ ox::Result<void*> findPreloadSection() noexcept {
|
||||
|
||||
namespace nostalgia::foundation {
|
||||
|
||||
static void clearUuidMap(Context*) noexcept {
|
||||
}
|
||||
|
||||
ox::Error buildUuidMap(Context*) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Result<char*> loadRom(ox::CRStringView) noexcept {
|
||||
// put the header in the wrong order to prevent mistaking this code for the
|
||||
// media section
|
||||
@ -92,6 +136,12 @@ ox::Result<std::size_t> getPreloadAddr(foundation::Context *ctx, const ox::FileA
|
||||
|
||||
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 {
|
||||
const auto lastDot = ox_lastIndexOf(path, '.');
|
||||
const auto fsExt = lastDot != -1 ? path.substr(static_cast<std::size_t>(lastDot)) : "";
|
||||
|
@ -10,12 +10,15 @@
|
||||
#include <ox/fs/fs.hpp>
|
||||
#include <ox/model/metadata.hpp>
|
||||
|
||||
#include <nostalgia/foundation/context.hpp>
|
||||
|
||||
#include "asset.hpp"
|
||||
#include "context.hpp"
|
||||
#include "typeconv.hpp"
|
||||
|
||||
namespace nostalgia::foundation {
|
||||
|
||||
// Pointer to preloaded data that can be stored in FS in place of the actual
|
||||
// data.
|
||||
struct PreloadPtr {
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.PreloadPtr";
|
||||
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, 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
|
||||
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 != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) {
|
||||
return err;
|
||||
@ -43,36 +49,72 @@ ox::Result<foundation::AssetRef<T>> readObj([[maybe_unused]] foundation::Context
|
||||
}
|
||||
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) {
|
||||
oxRequire(buff, ctx->rom->read(path));
|
||||
oxRequire(obj, readConvert(buff));
|
||||
oxRequire(cached, ctx->assetManager.setAsset(path, obj));
|
||||
return std::move(cached);
|
||||
oxRequire(obj, readConvert(ctx, buff));
|
||||
oxRequire(cached, ctx->assetManager.setAsset(assetId, obj));
|
||||
return cached;
|
||||
} else {
|
||||
auto [cached, err] = ctx->assetManager.getAsset<T>(path);
|
||||
auto [cached, err] = ctx->assetManager.getAsset<T>(assetId);
|
||||
if (err) {
|
||||
oxRequire(buff, ctx->rom->read(path));
|
||||
oxRequire(obj, readConvert(buff));
|
||||
oxReturnError(ctx->assetManager.setAsset(path, obj).moveTo(&cached));
|
||||
oxRequire(obj, readConvert(ctx, buff));
|
||||
oxReturnError(ctx->assetManager.setAsset(assetId, obj).moveTo(&cached));
|
||||
}
|
||||
return std::move(cached);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename T>
|
||||
ox::Result<foundation::AssetRef<T>> readObjNoCache(
|
||||
foundation::Context *ctx,
|
||||
ox::CRStringView assetId) noexcept {
|
||||
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));
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Result<foundation::AssetRef<T>> readObj(foundation::Context *ctx, const ox::FileAddress &file,
|
||||
[[maybe_unused]] bool forceLoad = false) noexcept {
|
||||
ox::Result<foundation::AssetRef<T>> readObj(
|
||||
foundation::Context *ctx,
|
||||
const ox::FileAddress &file,
|
||||
[[maybe_unused]] bool forceLoad = false) noexcept {
|
||||
#ifndef OX_BARE_METAL
|
||||
oxRequire(path, file.getPath());
|
||||
return readObj<T>(ctx, ox::StringView(path), forceLoad);
|
||||
oxRequire(assetId, file.getPath());
|
||||
return readObj<T>(ctx, ox::StringView(assetId), forceLoad);
|
||||
#else
|
||||
if constexpr(ox::preloadable<T>::value) {
|
||||
oxRequire(addr, getPreloadAddr(ctx, file));
|
||||
@ -84,15 +126,20 @@ ox::Result<foundation::AssetRef<T>> readObj(foundation::Context *ctx, const ox::
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ox::Error writeObj(foundation::Context *ctx, const ox::FileAddress &file, const T &obj,
|
||||
ox::ClawFormat fmt = ox::ClawFormat::Metal) noexcept {
|
||||
ox::Error writeObj(
|
||||
foundation::Context *ctx,
|
||||
const ox::FileAddress &file,
|
||||
const T &obj,
|
||||
ox::ClawFormat fmt = ox::ClawFormat::Metal) noexcept {
|
||||
oxRequire(objBuff, ox::writeClaw(&obj, fmt));
|
||||
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<char*> loadRom(ox::CRStringView path = "") noexcept;
|
||||
ox::Result<char*> loadRom(ox::CRStringView assetId = "") 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 {};
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/vector.hpp>
|
||||
#include <ox/model/descwrite.hpp>
|
||||
|
||||
#include "typeconv.hpp"
|
||||
|
||||
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 {
|
||||
public:
|
||||
constexpr Module() noexcept = default;
|
||||
@ -18,8 +26,11 @@ class Module {
|
||||
Module &operator=(const Module&) noexcept = delete;
|
||||
Module &operator=(Module&&) noexcept = delete;
|
||||
constexpr virtual ~Module() noexcept = default;
|
||||
|
||||
[[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]]
|
||||
virtual ox::Vector<PackTransform> packTransforms() const noexcept;
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <ox/claw/read.hpp>
|
||||
|
||||
#include "media.hpp"
|
||||
#include "typeconv.hpp"
|
||||
|
||||
namespace nostalgia::foundation {
|
||||
@ -11,9 +12,10 @@ namespace nostalgia::foundation {
|
||||
#ifndef OX_BARE_METAL
|
||||
|
||||
[[nodiscard]]
|
||||
static auto findConverter(foundation::Context *ctx,
|
||||
ox::CRStringView srcTypeName, int srcTypeVersion,
|
||||
ox::CRStringView dstTypeName, int dstTypeVersion) noexcept -> ox::Result<BaseConverter*> {
|
||||
static ox::Result<const BaseConverter*> findConverter(
|
||||
foundation::Context *ctx,
|
||||
ox::CRStringView srcTypeName, int srcTypeVersion,
|
||||
ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
|
||||
for (auto &c : ctx->converters) {
|
||||
if (c->matches(srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion)) {
|
||||
return c;
|
||||
@ -22,13 +24,14 @@ static auto findConverter(foundation::Context *ctx,
|
||||
return OxError(1, "Could not find converter");
|
||||
};
|
||||
|
||||
static ox::Result<ox::UniquePtr<Wrap>> convert(foundation::Context *ctx, const ox::Buffer &srcBuffer,
|
||||
ox::CRStringView srcTypeName, int srcTypeVersion,
|
||||
ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
|
||||
static ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||
foundation::Context *ctx, const ox::Buffer &srcBuffer,
|
||||
ox::CRStringView srcTypeName, int srcTypeVersion,
|
||||
ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
|
||||
// look for direct converter
|
||||
auto [c, err] = findConverter(ctx, srcTypeName, srcTypeVersion, dstTypeName, dstTypeVersion);
|
||||
if (!err) {
|
||||
return c->convertBuffToPtr(nullptr, srcBuffer);
|
||||
return c->convertBuffToPtr(ctx, srcBuffer);
|
||||
}
|
||||
// try to chain multiple 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");
|
||||
}
|
||||
|
||||
ox::Result<ox::UniquePtr<Wrap>> convert(foundation::Context *ctx, const ox::Buffer &srcBuffer, ox::CRStringView dstTypeName, int dstTypeVersion) noexcept {
|
||||
oxRequire(hdr, ox::readClawHeader(srcBuffer));
|
||||
return convert(ctx, srcBuffer, hdr.typeName, hdr.typeVersion, dstTypeName, dstTypeVersion);
|
||||
ox::Result<ox::UniquePtr<Wrap>> convert(
|
||||
foundation::Context *ctx,
|
||||
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
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <ox/claw/write.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "media.hpp"
|
||||
|
||||
namespace nostalgia::foundation {
|
||||
|
||||
@ -19,6 +20,28 @@ class Wrap {
|
||||
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>
|
||||
class WrapInline: public Wrap {
|
||||
private:
|
||||
@ -53,20 +76,20 @@ class BaseConverter {
|
||||
virtual ~BaseConverter() noexcept = default;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual ox::StringView srcTypeName() noexcept = 0;
|
||||
virtual ox::StringView srcTypeName() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual int srcTypeVersion() noexcept = 0;
|
||||
virtual int srcTypeVersion() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual bool srcMatches(ox::CRStringView srcTypeName, int srcTypeVersion) const noexcept = 0;
|
||||
virtual bool srcMatches(ox::CRStringView pSrcTypeName, int pSrcTypeVersion) const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
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]]
|
||||
inline bool matches(ox::CRStringView srcTypeName, int srcTypeVersion,
|
||||
@ -80,47 +103,48 @@ class BaseConverter {
|
||||
template<typename SrcType, typename DstType>
|
||||
class Converter: public BaseConverter {
|
||||
public:
|
||||
virtual ox::Error convert(foundation::Context *ctx, SrcType*, DstType*) noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::StringView srcTypeName() noexcept final {
|
||||
ox::StringView srcTypeName() const noexcept final {
|
||||
return ox::requireModelTypeName<SrcType>();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int srcTypeVersion() noexcept final {
|
||||
int srcTypeVersion() const noexcept final {
|
||||
return ox::requireModelTypeVersion<SrcType>();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool srcMatches(ox::CRStringView srcTypeName, int srcTypeVersion) const noexcept final {
|
||||
static constexpr auto SrcTypeName = ox::requireModelTypeName<SrcType>();
|
||||
static constexpr auto SrcTypeVersion = ox::requireModelTypeVersion<SrcType>();
|
||||
return ox_strcmp(srcTypeName, SrcTypeName) == 0
|
||||
&& srcTypeVersion == SrcTypeVersion;
|
||||
bool srcMatches(ox::CRStringView pSrcTypeName, int pSrcTypeVersion) const noexcept final {
|
||||
static const auto SrcTypeName = ox::requireModelTypeName<SrcType>();
|
||||
static const auto SrcTypeVersion = ox::requireModelTypeVersion<SrcType>();
|
||||
return pSrcTypeName == SrcTypeName
|
||||
&& pSrcTypeVersion == SrcTypeVersion;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool dstMatches(ox::CRStringView dstTypeName, int dstTypeVersion) const noexcept final {
|
||||
static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
|
||||
static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
|
||||
return ox_strcmp(dstTypeName, DstTypeName) == 0
|
||||
static const auto DstTypeName = ox::StringView(ox::requireModelTypeName<DstType>());
|
||||
static const auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
|
||||
return dstTypeName == DstTypeName
|
||||
&& 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>();
|
||||
oxReturnError(convert(ctx, wrapCast<SrcType>(src), wrapCast<DstType>(dst.get())));
|
||||
return ox::Result<ox::UniquePtr<Wrap>>(std::move(dst));
|
||||
}
|
||||
|
||||
ox::Result<ox::UniquePtr<Wrap>> convertBuffToPtr(foundation::Context *ctx, const ox::Buffer &srcBuff) noexcept final {
|
||||
oxRequireM(src, ox::readClaw<SrcType>(srcBuff));
|
||||
ox::Result<ox::UniquePtr<Wrap>> convertBuffToPtr(foundation::Context *ctx, const ox::Buffer &srcBuff) const noexcept final {
|
||||
oxRequireM(src, readAsset<SrcType>(srcBuff));
|
||||
auto dst = makeWrap<DstType>();
|
||||
oxReturnError(convert(ctx, &src, wrapCast<DstType>(dst.get())));
|
||||
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,
|
||||
@ -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>
|
||||
auto transformRule(foundation::Context *ctx, ox::Buffer *buff) -> ox::Error {
|
||||
oxRequire(hdr, ox::readClawHeader(*buff));
|
||||
const auto typeId = ox::buildTypeId(hdr.typeName, hdr.typeVersion, hdr.typeParams);
|
||||
oxRequire(hdr, readAssetHeader(*buff));
|
||||
const auto typeId = ox::buildTypeId(
|
||||
hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams);
|
||||
if (typeId == ox::buildTypeId<From>()) {
|
||||
oxReturnError(foundation::convertBuffToBuff<To>(ctx, *buff, fmt).moveTo(buff));
|
||||
}
|
||||
|
@ -4,41 +4,19 @@
|
||||
|
||||
#include <nostalgia/core/core.hpp>
|
||||
#include <nostalgia/foundation/media.hpp>
|
||||
#include <nostalgia/scene/scene.hpp>
|
||||
|
||||
using namespace nostalgia;
|
||||
|
||||
static int spriteX = 72;
|
||||
static int spriteY = 64;
|
||||
static ox::StringView sprites = "nostalgia";
|
||||
static bool paused = false;
|
||||
static bool s_paused = false;
|
||||
static ox::Optional<scene::Scene> s_scene;
|
||||
|
||||
static int updateHandler(core::Context *ctx) noexcept {
|
||||
static int updateHandler(core::Context*) noexcept {
|
||||
constexpr auto sleepTime = 16;
|
||||
if (paused) {
|
||||
if (s_paused) {
|
||||
return sleepTime;
|
||||
}
|
||||
int xmod = 0;
|
||||
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);
|
||||
}
|
||||
// do stuff
|
||||
return sleepTime;
|
||||
}
|
||||
|
||||
@ -47,7 +25,7 @@ static void keyEventHandler(core::Context *ctx, core::Key key, bool down) noexce
|
||||
if (key == core::Key::Alpha_Q) {
|
||||
core::shutdown(ctx);
|
||||
} 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 {
|
||||
oxTraceInitHook();
|
||||
oxRequireM(ctx, core::init(std::move(fs)));
|
||||
constexpr ox::FileAddress TileSheetAddr("/TileSheets/Charset.ng");
|
||||
constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal");
|
||||
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!!!");
|
||||
constexpr ox::FileAddress SceneAddr("/Scenes/Chester.nscn");
|
||||
oxRequire(scn, foundation::readObj<scene::SceneStatic>(ctx.get(), SceneAddr));
|
||||
core::setUpdateHandler(ctx.get(), updateHandler);
|
||||
core::setKeyEventHandler(ctx.get(), keyEventHandler);
|
||||
s_scene.emplace(scn.get());
|
||||
oxReturnError(s_scene->setupDisplay(ctx.get()));
|
||||
return core::run(ctx.get());
|
||||
}
|
||||
|
@ -13,7 +13,11 @@
|
||||
|
||||
static ox::Error run(int argc, const char **argv) noexcept {
|
||||
ox::trace::init();
|
||||
#ifdef OX_USE_STDLIB
|
||||
// GBA doesn't need the modules and calling this doubles the size of the
|
||||
// binary.
|
||||
nostalgia::loadModules();
|
||||
#endif
|
||||
if (argc < 2) {
|
||||
oxErr("Please provide path to project directory or OxFS file.\n");
|
||||
return OxError(1);
|
||||
|
@ -2,6 +2,7 @@
|
||||
add_library(
|
||||
NostalgiaScene
|
||||
scene.cpp
|
||||
scenestatic.cpp
|
||||
scenemodule.cpp
|
||||
typeconv.cpp
|
||||
)
|
||||
@ -9,11 +10,12 @@ add_library(
|
||||
target_link_libraries(
|
||||
NostalgiaScene PUBLIC
|
||||
NostalgiaCore
|
||||
NostalgiaGeo
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
scene.hpp
|
||||
scenestatic.hpp
|
||||
scenemodule.hpp
|
||||
typeconv.hpp
|
||||
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"
|
||||
|
||||
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
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
#include <ox/std/error.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/std/vector.hpp>
|
||||
#include "scenestatic.hpp"
|
||||
|
||||
namespace nostalgia::scene {
|
||||
|
||||
struct TileDoc {
|
||||
class Scene {
|
||||
private:
|
||||
const SceneStatic *m_sceneStatic = nullptr;
|
||||
|
||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.scene.TileDoc";
|
||||
constexpr static auto TypeVersion = 1;
|
||||
constexpr static auto Preloadable = true;
|
||||
public:
|
||||
explicit Scene(const SceneStatic *sceneStatic) noexcept;
|
||||
|
||||
ox::String sheetIdx;
|
||||
uint8_t type = 0;
|
||||
ox::Error setupDisplay(core::Context *ctx) noexcept;
|
||||
|
||||
};
|
||||
|
||||
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 "scenestatic.hpp"
|
||||
#include "scenemodule.hpp"
|
||||
|
||||
namespace nostalgia::scene {
|
||||
|
||||
SceneModule SceneModule::mod;
|
||||
|
||||
ox::Vector<foundation::BaseConverter*> SceneModule::converters() const noexcept {
|
||||
ox::Vector<foundation::TypeDescGenerator> SceneModule::types() const noexcept {
|
||||
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 {
|
||||
return {
|
||||
foundation::transformRule<SceneDoc, Scene>,
|
||||
foundation::transformRule<SceneDoc, SceneStatic>,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -12,12 +12,14 @@ namespace nostalgia::scene {
|
||||
|
||||
class SceneModule: public foundation::Module {
|
||||
private:
|
||||
mutable SceneDocToSceneConverter sceneToSceneInstaceConverter;
|
||||
SceneDocToSceneStaticConverter sceneDocToSceneStaticConverter;
|
||||
|
||||
public:
|
||||
static SceneModule mod;
|
||||
[[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]]
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
#include <nostalgia/foundation/media.hpp>
|
||||
|
||||
#include "typeconv.hpp"
|
||||
|
||||
namespace nostalgia::scene {
|
||||
|
||||
// Type converters
|
||||
|
||||
ox::Error SceneDocToSceneConverter::convert(foundation::Context*, SceneDoc *, Scene *) noexcept {
|
||||
ox::Error SceneDocToSceneStaticConverter::convert(
|
||||
foundation::Context *ctx,
|
||||
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 {};
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,12 @@
|
||||
|
||||
#include <nostalgia/foundation/typeconv.hpp>
|
||||
|
||||
#include "scene.hpp"
|
||||
#include "scenestatic.hpp"
|
||||
|
||||
namespace nostalgia::scene {
|
||||
|
||||
struct SceneDocToSceneConverter: public foundation::Converter<SceneDoc, Scene> {
|
||||
ox::Error convert(foundation::Context*, SceneDoc *src, Scene *dst) noexcept final;
|
||||
class SceneDocToSceneStaticConverter: public foundation::Converter<SceneDoc, SceneStatic> {
|
||||
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>
|
||||
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);
|
||||
ox::PassThroughFS fs(configPath(ctx));
|
||||
const auto [buff, err] = fs.read(path);
|
||||
@ -41,7 +41,7 @@ ox::Result<T> readConfig(core::Context *ctx) noexcept {
|
||||
|
||||
template<typename T>
|
||||
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);
|
||||
ox::PassThroughFS fs(configPath(ctx));
|
||||
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>
|
||||
void openConfig(core::Context *ctx, const auto &name, Func f) noexcept {
|
||||
oxAssert(name != "", "Config type has no TypeName");
|
||||
const auto c = readConfig<T>(ctx, name);
|
||||
f(&c.value);
|
||||
const auto [c, err] = readConfig<T>(ctx, name);
|
||||
oxLogError(err);
|
||||
f(&c);
|
||||
}
|
||||
|
||||
template<typename T, typename Func>
|
||||
@ -79,9 +80,10 @@ void openConfig(core::Context *ctx, Func f) noexcept {
|
||||
template<typename T, typename Func>
|
||||
void editConfig(core::Context *ctx, const auto &name, Func f) noexcept {
|
||||
oxAssert(ox_strcmp(name, ""), "Config type has no TypeName");
|
||||
auto c = readConfig<T>(ctx, name);
|
||||
f(&c.value);
|
||||
oxLogError(writeConfig(ctx, name, &c.value));
|
||||
auto [c, err] = readConfig<T>(ctx, name);
|
||||
oxLogError(err);
|
||||
f(&c);
|
||||
oxLogError(writeConfig(ctx, name, &c));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
|
||||
#include <nostalgia/foundation/media.hpp>
|
||||
#include <nostalgia/core/context.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace nostalgia::studio {
|
||||
|
||||
class ItemMaker {
|
||||
@ -45,8 +48,9 @@ class ItemMakerT: public ItemMaker {
|
||||
fmt(pFmt) {
|
||||
}
|
||||
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);
|
||||
foundation::createUuidMapping(ctx, path, ox::UUID::generate().unwrap());
|
||||
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>
|
||||
@ -7,12 +7,27 @@
|
||||
|
||||
#include <ox/std/std.hpp>
|
||||
|
||||
#include <nostalgia/foundation/module.hpp>
|
||||
|
||||
#include "project.hpp"
|
||||
|
||||
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);
|
||||
generateTypes(&m_typeStore);
|
||||
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 {
|
||||
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;
|
||||
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) {
|
||||
fileAdded.emit(path);
|
||||
indexFile(path);
|
||||
} else {
|
||||
fileUpdated.emit(path);
|
||||
}
|
||||
return OxError(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
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
|
||||
@ -13,6 +13,7 @@
|
||||
#include <ox/std/hashmap.hpp>
|
||||
|
||||
#include <nostalgia/core/typestore.hpp>
|
||||
#include <nostalgia/foundation/media.hpp>
|
||||
|
||||
#include "nostalgiastudio_export.h"
|
||||
|
||||
@ -42,10 +43,11 @@ class NOSTALGIASTUDIO_EXPORT Project {
|
||||
ox::String m_path;
|
||||
mutable core::TypeStore m_typeStore;
|
||||
mutable ox::FileSystem *m_fs = nullptr;
|
||||
foundation::Context *m_ctx = nullptr;
|
||||
ox::HashMap<ox::String, ox::Vector<ox::String>> m_fileExtFileMap;
|
||||
|
||||
public:
|
||||
explicit Project(ox::FileSystem *fs, ox::String path) noexcept;
|
||||
explicit Project(foundation::Context *ctx, ox::String path) 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
|
||||
typeOut.back().value = '\n';
|
||||
// write to FS
|
||||
const auto typePath = ox::sfmt("{}/{}", descPath, buildTypeId(*t));
|
||||
const auto typePath = ox::sfmt("/{}/{}", descPath, buildTypeId(*t));
|
||||
oxReturnError(writeBuff(typePath, typeOut));
|
||||
}
|
||||
fileUpdated.emit(path);
|
||||
@ -129,9 +131,9 @@ template<typename T>
|
||||
ox::Result<T> Project::loadObj(const ox::String &path) const noexcept {
|
||||
oxRequire(buff, loadBuff(path));
|
||||
if constexpr (ox::is_same_v<T, ox::ModelObject>) {
|
||||
return ox::readClaw(&m_typeStore, buff);
|
||||
return foundation::readAsset(&m_typeStore, buff);
|
||||
} 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.
|
||||
*/
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include <ox/logconn/logconn.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/uuid.hpp>
|
||||
|
||||
#include <nostalgia/appmodules/appmodules.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());
|
||||
}
|
||||
|
||||
static ox::Error run(int argc, const char **argv) noexcept {
|
||||
static ox::Error run(int, const char**) noexcept {
|
||||
ox::trace::init();
|
||||
const auto time = std::time(nullptr);
|
||||
ox::UUID::seedGenerator({
|
||||
static_cast<uint64_t>(time),
|
||||
static_cast<uint64_t>(time << 1)
|
||||
});
|
||||
loadModules();
|
||||
if (argc >= 2) {
|
||||
const auto path = argv[1];
|
||||
oxRequireM(fs, foundation::loadRomFs(path));
|
||||
return run(std::move(fs));
|
||||
} else {
|
||||
return run(ox::UniquePtr<ox::FileSystem>(nullptr));
|
||||
}
|
||||
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>
|
||||
@ -108,7 +108,8 @@ void NewMenu::drawLastPageButtons(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) {
|
||||
oxLogError(err);
|
||||
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>
|
||||
@ -286,9 +286,9 @@ void StudioUI::save() noexcept {
|
||||
|
||||
ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
|
||||
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));
|
||||
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);
|
||||
sctx->project = m_project.get();
|
||||
m_project->fileAdded.connect(m_projectExplorer.get(), &ProjectExplorer::refreshProjectTreeModel);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <nostalgia/appmodules/appmodules.hpp>
|
||||
#include <nostalgia/core/typestore.hpp>
|
||||
#include <nostalgia/foundation/foundation.hpp>
|
||||
#include <nostalgia/foundation/module.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 {
|
||||
loadModules();
|
||||
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);
|
||||
oxReturnError(ox::FileSystem32::format(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());
|
||||
oxReturnError(generateTypes(&ts));
|
||||
oxReturnError(pack(ctx.get(), &ts, &dst));
|
||||
oxRequireM(pl, GbaPreloader::make());
|
||||
oxReturnError(preload(&ts, &dst, pl.get()));
|
||||
|
@ -7,15 +7,13 @@
|
||||
#include <ox/model/descwrite.hpp>
|
||||
#include <ox/model/modelvalue.hpp>
|
||||
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
#include <nostalgia/core/typestore.hpp>
|
||||
#include <nostalgia/foundation/media.hpp>
|
||||
|
||||
#include "pack.hpp"
|
||||
|
||||
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 type = static_cast<ox::FileAddressType>(o["type"].get<int8_t>());
|
||||
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:
|
||||
return {};
|
||||
}
|
||||
if (beginsWith(path, "uuid://")) {
|
||||
const auto uuid = ox::StringView(path).substr(7);
|
||||
path = ctx->uuidToPath[uuid];
|
||||
}
|
||||
oxRequire(s, dest->stat(path));
|
||||
oxReturnError(o["type"].set(static_cast<int8_t>(ox::FileAddressType::Inode)));
|
||||
return data.set(2, s.inode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert path references in Claw data to inodes to save space
|
||||
* @param buff buffer holding file
|
||||
* @return error
|
||||
*/
|
||||
static ox::Error transformFileAddresses(ox::FileSystem *dest, ox::ModelObject *obj) noexcept {
|
||||
for (auto &f : *obj) {
|
||||
auto &v = f->value;
|
||||
if (v.type() != ox::ModelValue::Type::Object) {
|
||||
continue;
|
||||
}
|
||||
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));
|
||||
}
|
||||
static ox::Error transformFileAddressesObj(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelObject *obj) noexcept;
|
||||
static ox::Error transformFileAddressesVec(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelValueVector *v) noexcept;
|
||||
|
||||
static ox::Error transformFileAddresses(foundation::Context *ctx, ox::FileSystem *dest, ox::ModelValue *v) noexcept {
|
||||
if (v->type() == ox::ModelValue::Type::Object) {
|
||||
auto &obj = v->get<ox::ModelObject>();
|
||||
return transformFileAddressesObj(ctx, dest, &obj);
|
||||
} else if (v->type() == ox::ModelValue::Type::Vector) {
|
||||
auto &vec = v->get<ox::ModelValueVector>();
|
||||
return transformFileAddressesVec(ctx, dest, &vec);
|
||||
}
|
||||
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
|
||||
oxRequire(s, dest->stat(filePath));
|
||||
// do transformations
|
||||
@ -66,8 +83,8 @@ static ox::Error doTransformations(foundation::Context *ctx, core::TypeStore *ts
|
||||
oxReturnError(tr(ctx, &buff));
|
||||
}
|
||||
// transform FileAddresses
|
||||
oxRequireM(obj, ox::readClaw(ts, buff));
|
||||
oxReturnError(transformFileAddresses(dest, &obj));
|
||||
oxRequireM(obj, foundation::readAsset(ts, buff));
|
||||
oxReturnError(transformFileAddressesObj(ctx, dest, &obj));
|
||||
oxReturnError(ox::writeClaw(&obj).moveTo(&buff));
|
||||
// write file to dest
|
||||
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
|
||||
// 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
|
||||
oxTracef("pack::transformClaw", "path: {}", 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);
|
||||
ox::Vector<VerificationPair> verificationPairs;
|
||||
// copy
|
||||
oxRequire(fileList, src->ls(path));
|
||||
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));
|
||||
if (stat.fileType == ox::FileType::Directory) {
|
||||
oxReturnError(dest->mkdir(currentFile, true));
|
||||
oxReturnError(copy(src, dest, currentFile + '/'));
|
||||
oxReturnError(copy(src, dest, currentFile + '/', verificationPairs));
|
||||
} else {
|
||||
// load file
|
||||
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);
|
||||
oxReturnError(dest->write(currentFile, buff.data(), buff.size()));
|
||||
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
|
||||
oxOutf("Verifying completed destination\n");
|
||||
for (const auto &v : verificationPairs) {
|
||||
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
|
||||
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
|
||||
oxRequireM(buff, romFs->read(path));
|
||||
oxRequireM(obj, ox::readClaw(ts, buff));
|
||||
oxRequireM(obj, foundation::readAsset(ts, buff));
|
||||
if (obj.type()->preloadable) {
|
||||
oxOutf("preloading {}\n", path);
|
||||
// preload
|
||||
oxRequire(a, pl->startAlloc(ox::sizeOf<GbaPlatSpec>(&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
|
||||
// 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
|
||||
oxTracef("pack::preload", "path: {}", path);
|
||||
oxRequire(fileList, romFs->ls(path));
|
||||
@ -202,14 +228,15 @@ ox::Error appendBinary(ox::Buffer *binBuff, ox::Buffer *fsBuff, GbaPreloader *pl
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error pack(foundation::Context *ctx, core::TypeStore *ts, ox::FileSystem *dest) noexcept {
|
||||
oxReturnError(copy(ctx->rom.get(), dest, "/"));
|
||||
oxReturnError(ox::buildTypeDef<core::CompactTileSheet>(ts));
|
||||
ox::Error pack(foundation::Context *ctx, ox::TypeStore *ts, ox::FileSystem *dest) noexcept {
|
||||
oxReturnError(copyFS(ctx->rom.get(), dest));
|
||||
oxOut("Doing transforms\n");
|
||||
oxReturnError(transformClaw(ctx, ts, dest, "/"));
|
||||
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, "/");
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,6 @@ namespace foundation {
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace core {
|
||||
class TypeStore;
|
||||
}
|
||||
|
||||
struct GbaPlatSpec {
|
||||
using PtrType = 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;
|
||||
|
||||
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