Compare commits

...

36 Commits

Author SHA1 Message Date
58b7f813cc [nostalgia/core/studio] Make Edit Subsheet popup close on escape press 2023-02-18 00:52:49 -06:00
762804905a [ox] Make UUID serializable, and make serialize as a string in OC 2023-02-16 01:36:20 -06:00
b53e8626d7 [nostalgia/core/studio] Make TileSheets use UUIDs to refer to Palettes 2023-02-14 00:45:56 -06:00
71354fcbbc [nostalgia] Fix the initialization of UUIDs for new assets 2023-02-13 23:16:41 -06:00
044a87b1c4 [nostalgia/studio] Seed UUID generator at the start of Studio 2023-02-13 21:55:19 -06:00
53229b05da [nostalgia] Fix to allow building UUID map later 2023-02-13 21:45:54 -06:00
d17f536832 [ox/std] Add write(Writer_c, StringView) 2023-02-13 21:35:37 -06:00
b6ed919b01 [ox/std] Fix ox::join to work with list types that done have a front function 2023-02-13 19:08:46 -06:00
777a6d54f0 [ox/std] Add test for UUID::generate() and fix bug 2023-02-13 19:08:04 -06:00
86a3bf1248 [nostalgia] Make assets loadable through UUID references 2023-02-13 19:00:25 -06:00
a96d173fdc [ox/std] Fix BString(StringView) constructor 2023-02-12 22:30:28 -06:00
5e43eff631 [nostalgia] Add UUID to file path mapping 2023-02-12 22:13:32 -06:00
ba7ee92ad2 [ox/fs] Add error messgae to PassthroughFS::statPath 2023-02-12 21:45:18 -06:00
6d4c57d37d [ox/std] Fix BString == and != operators 2023-02-12 21:14:16 -06:00
54eebf81da [ox/std] Add UUID str conv test and fix bugs found 2023-02-12 20:38:29 -06:00
1b7b6e306e [nostalgia] Add support for asset UUID headers 2023-02-12 00:02:41 -06:00
2b821b73ff [ox/std] Add UUID fromString 2023-02-12 00:02:07 -06:00
e19559d7a7 [ox/claw] Add non-ox::Buffer version of readClaw function 2023-02-11 23:59:37 -06:00
af3de01e1b [ox/preloader] clang-tidy fix 2023-02-11 23:56:03 -06:00
9561a68483 [ox/std] Fix BString assign and compare 2023-02-11 02:32:24 -06:00
08899074cf [nostalgia/studio] Add Module types to Project TypeStore 2023-02-09 01:21:12 -06:00
86f639c7f7 [ox/std] Add tests for UUID int to hex str conversion 2023-02-08 21:40:29 -06:00
014daa6b57 [nostalgia/core/userland] Fix magic numbers 2023-02-08 21:37:25 -06:00
4f906f6e47 [ox/std] Add UUID implementation 2023-02-08 21:37:17 -06:00
3b05d4e16b [nostalgia] Add type registration to Module, fix FileAddress conversion in vectors for nost-pack 2023-02-08 03:06:57 -06:00
dba31d2cd9 [ox/model] Fix ModelUnion to copy type on copy and move 2023-02-07 23:19:18 -06:00
8077aaf0ae [nostalgia] Fill out basic Scene system functionality 2023-02-07 23:17:58 -06:00
eb55144211 [ox/std] Make ox::min/max use values instead of refs for integrals, add CommonRefWith 2023-02-07 01:35:50 -06:00
5de5eee215 [ox/std] Fix alignment of Optional's buffer 2023-02-07 01:33:30 -06:00
d571d49cce [ox/std] Fix String to StringView compare 2023-02-07 01:30:59 -06:00
6769bb63d9 [ox/std] Fix Optional 2023-02-06 22:43:30 -06:00
b064239ab1 [nostalgia] Remove World package 2023-02-06 00:18:16 -06:00
24ea7fee39 [nostalgia] Update liccor file 2023-02-06 00:17:24 -06:00
8a4ce3c8f1 [nostalgia/foundation] Fix CMake install 2023-02-05 02:25:22 -06:00
3c9e6d10ea [ox/std] Add StringView split and find functions 2023-02-05 00:33:27 -06:00
4b9b70a90e [nostalgia] Remove .trace.json 2023-02-04 16:57:28 -06:00
107 changed files with 2172 additions and 1184 deletions

View File

@ -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.

View File

@ -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
}
]
}

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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};
}

View File

@ -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;
}

View File

@ -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()) {

View File

@ -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

View File

@ -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];

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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>;

View File

@ -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];

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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];

View File

@ -42,4 +42,5 @@
#include "types.hpp"
#include "typetraits.hpp"
#include "units.hpp"
#include "uuid.hpp"
#include "vector.hpp"

View File

@ -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>

View File

@ -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());

View File

@ -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")

View File

@ -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) {

View File

@ -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
View 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
View 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 {};
}
}

View File

@ -0,0 +1,4 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"primitiveType" : 2,
"typeName" : "B.bool"
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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.

View File

@ -0,0 +1 @@
N1;14fc3dd8-42ff-4bf9-81f1-a010cc5ac251;M2;net.drinkingtea.nostalgia.core.Palette;1;ûÿ³Ö

View File

@ -1 +1 @@
M2;net.drinkingtea.nostalgia.core.Palette;1;ûÿ³Ö
N1;0f75977f-1c52-45f8-9793-52ea2dc200a0;M2;net.drinkingtea.nostalgia.core.Palette;1;ûÿ³Ö

View File

@ -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ˆ

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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)

View File

@ -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;
}
}

View File

@ -2,6 +2,8 @@ add_library(
NostalgiaCore-Common OBJECT
gfx.cpp
module.cpp
tilesheet.cpp
typeconv.cpp
typestore.cpp
)

View File

@ -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,

View File

@ -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 {

View File

@ -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 {};
},

View File

@ -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;
};

View 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()
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -0,0 +1,9 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include "tilesheet.hpp"
namespace nostalgia::core {
}

View 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()
}

View 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 {};
}
}

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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
)

View 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());
}
}

View 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;
}

View File

@ -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 {

View File

@ -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;

View File

@ -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) {

View File

@ -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)) : "";

View File

@ -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;

View File

@ -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 {};
}

View File

@ -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;
};

View File

@ -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

View File

@ -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));
}

View File

@ -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());
}

View File

@ -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);

View File

@ -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

View File

@ -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 {};
}
}

View File

@ -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()
}

View File

@ -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>,
};
}

View File

@ -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;
};

View File

@ -0,0 +1,11 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include "scenestatic.hpp"
namespace nostalgia::scene {
}

View 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()
}

View File

@ -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 {};
}

View File

@ -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;
};
}

View File

@ -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>

View File

@ -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);
}
};

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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()));

View File

@ -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, "/");
}

View File

@ -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;
}

View File

@ -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()

View File

@ -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}
)

View File

@ -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/";
}

View File

@ -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;
}

View File

@ -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;
}
);
}
}

View File

@ -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