Compare commits

..

13 Commits

29 changed files with 753 additions and 441 deletions

View File

@ -13,10 +13,12 @@ include(deps/buildcore/base.cmake)
set(NOSTALGIA_BUILD_PLAYER ON CACHE BOOL "Build Player")
set(NOSTALGIA_BUILD_STUDIO ON CACHE BOOL "Build Studio")
set(OLYMPIC_BUILD_STUDIO ON CACHE BOOL "Build Studio")
set(OX_ENABLE_TRACEHOOK OFF CACHE BOOL "Generate OxTraceHook shared library for uprobes")
if(BUILDCORE_TARGET STREQUAL "gba")
set(NOSTALGIA_BUILD_STUDIO OFF)
set(OLYMPIC_BUILD_STUDIO OFF)
set(TURBINE_BUILD_TYPE "GBA")
include(deps/gbabuildcore/base.cmake)
else()

View File

@ -161,16 +161,16 @@ template<typename T>
constexpr Error model(T *h, CommonPtrWith<FileAddress> auto *fa) noexcept {
oxReturnError(h->template setTypeInfo<FileAddress>());
if constexpr(T::opType() == OpType::Reflect) {
int8_t type = 0;
int8_t type = -1;
oxReturnError(h->field("type", &type));
oxReturnError(h->field("data", UnionView(&fa->m_data, 0)));
oxReturnError(h->field("data", UnionView(&fa->m_data, type)));
} else if constexpr(T::opType() == OpType::Read) {
auto type = static_cast<int8_t>(fa->m_type);
oxReturnError(h->field("type", &type));
fa->m_type = static_cast<FileAddressType>(type);
oxReturnError(h->field("data", UnionView(&fa->m_data, static_cast<int>(fa->m_type))));
} else if constexpr(T::opType() == OpType::Write) {
auto type = static_cast<int8_t>(fa->m_type);
auto const type = static_cast<int8_t>(fa->m_type);
oxReturnError(h->field("type", &type));
oxReturnError(h->field("data", UnionView(&fa->m_data, static_cast<int>(fa->m_type))));
}

View File

@ -64,13 +64,14 @@ struct McInt {
template<typename I>
[[nodiscard]]
constexpr McInt encodeInteger(I input) noexcept {
constexpr McInt encodeInteger(I pInput) noexcept {
auto const input = ox::ResizedInt_t<I, 64>{pInput};
McInt out;
const auto inputNegative = is_signed_v<I> && input < 0;
// move input to uint64_t to allow consistent bit manipulation, and to avoid
// overflow concerns
uint64_t val = 0;
ox_memcpy(&val, &input, sizeof(I));
ox_memcpy(&val, &input, sizeof(input));
if (val) {
// bits needed to represent number factoring in space possibly
// needed for signed bit
@ -93,7 +94,7 @@ constexpr McInt encodeInteger(I input) noexcept {
}
if (bytes == 9) {
out.data[0] = bytesIndicator;
ox_memcpy(&out.data[1], &leVal, sizeof(I));
ox_memcpy(&out.data[1], &leVal, 8);
if (inputNegative) {
out.data[1] |= 0b1000'0000;
}

View File

@ -15,6 +15,7 @@
#include <ox/model/types.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/optional.hpp>
#include <ox/std/string.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/vector.hpp>
@ -33,15 +34,13 @@ class MetalClawReaderTemplate: public ModelHandlerBase<MetalClawReaderTemplate<R
FieldBitmapReader<Reader> m_fieldPresence;
std::size_t m_fields = 0;
std::size_t m_field = 0;
int m_unionIdx = -1;
ox::Optional<int> m_unionIdx;
Reader &m_reader;
MetalClawReaderTemplate<Reader> *m_parent = nullptr;
public:
explicit constexpr MetalClawReaderTemplate(
Reader &reader,
int unionIdx = -1,
MetalClawReaderTemplate<Reader> *parent = nullptr) noexcept;
ox::Optional<int> const&unionIdx = {}) noexcept;
constexpr ~MetalClawReaderTemplate() noexcept;
@ -108,7 +107,7 @@ class MetalClawReaderTemplate: public ModelHandlerBase<MetalClawReaderTemplate<R
* Returns a MetalClawReader to parse a child object.
*/
[[nodiscard]]
constexpr MetalClawReaderTemplate<Reader> child(const char *name, int unionIdx = -1) noexcept;
constexpr MetalClawReaderTemplate<Reader> child(const char *name, ox::Optional<int> unionIdx = {}) noexcept;
/**
* Indicates whether or not the next field to be read is present.
@ -136,12 +135,10 @@ class MetalClawReaderTemplate: public ModelHandlerBase<MetalClawReaderTemplate<R
template<Reader_c Reader>
constexpr MetalClawReaderTemplate<Reader>::MetalClawReaderTemplate(
Reader &reader,
int unionIdx,
MetalClawReaderTemplate *parent) noexcept:
ox::Optional<int> const&unionIdx) noexcept:
m_fieldPresence(reader),
m_unionIdx(unionIdx),
m_reader(reader),
m_parent(parent) {
m_reader(reader) {
}
template<Reader_c Reader>
@ -194,7 +191,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, uint64_t *va
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, bool *val) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
auto const result = m_fieldPresence.get(static_cast<std::size_t>(m_field));
*val = result.value;
oxReturnError(result);
@ -206,7 +203,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, bool *val) n
// array handler
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, auto *val, std::size_t valLen) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
@ -232,7 +229,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, auto *v
template<Reader_c Reader>
template<typename T>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, HashMap<String, T> *val) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
oxRequire(g, m_reader.tellg());
@ -264,19 +261,19 @@ template<Reader_c Reader>
template<typename T>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, T *val) noexcept {
if constexpr(isVector_v<T>) {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
// set size of val if the field is present, don't worry about it if not
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
oxRequire(len, arrayLength(name, false));
val->resize(len);
oxReturnError(ox::resizeVector(*val, len));
return field(name, val->data(), val->size());
}
val->resize(0);
oxReturnError(ox::resizeVector(*val, 0));
}
++m_field;
return {};
} else if constexpr(isArray_v<T>) {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
// set size of val if the field is present, don't worry about it if not
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
oxRequire(len, arrayLength(name, false));
@ -289,7 +286,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, T *val)
++m_field;
return {};
} else {
if ((m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) && val) {
if ((!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) && val) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
auto reader = child("");
oxReturnError(model(reader.interface(), val));
@ -303,20 +300,20 @@ constexpr Error MetalClawReaderTemplate<Reader>::field(const char *name, T *val)
template<Reader_c Reader>
template<typename U, bool force>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, UnionView<U, force> val) noexcept {
if ((m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) && val.get()) {
if ((!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) && val.get()) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
auto reader = child("", val.idx());
auto reader = child("", ox::Optional<int>(ox::in_place, val.idx()));
oxReturnError(model(reader.interface(), val.get()));
}
}
++m_field;
return OxError(0);
return {};
}
template<Reader_c Reader>
template<std::size_t SmallStringSize>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, BasicString<SmallStringSize> *val) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
@ -382,7 +379,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char
template<Reader_c Reader>
constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char **val, std::size_t buffLen) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
@ -410,7 +407,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::fieldCString(const char*, char
template<Reader_c Reader>
constexpr Result<ArrayLength> MetalClawReaderTemplate<Reader>::arrayLength(const char*, bool pass) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
@ -427,7 +424,7 @@ constexpr Result<ArrayLength> MetalClawReaderTemplate<Reader>::arrayLength(const
template<Reader_c Reader>
constexpr Result<StringLength> MetalClawReaderTemplate<Reader>::stringLength(const char*) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
@ -442,7 +439,7 @@ constexpr Result<StringLength> MetalClawReaderTemplate<Reader>::stringLength(con
template<Reader_c Reader>
template<typename I>
constexpr Error MetalClawReaderTemplate<Reader>::readInteger(I *val) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
std::size_t bytesRead = 0;
auto const result = mc::decodeInteger<I>(m_reader, &bytesRead);
@ -459,7 +456,7 @@ constexpr Error MetalClawReaderTemplate<Reader>::readInteger(I *val) noexcept {
template<Reader_c Reader>
template<typename T, typename CB>
constexpr Error MetalClawReaderTemplate<Reader>::field(const char*, CB cb) noexcept {
if (m_unionIdx == -1 || static_cast<std::size_t>(m_unionIdx) == m_field) {
if (!m_unionIdx.has_value() || static_cast<std::size_t>(*m_unionIdx) == m_field) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
@ -491,8 +488,10 @@ constexpr ox::Error MetalClawReaderTemplate<Reader>::setTypeInfo(
}
template<Reader_c Reader>
constexpr MetalClawReaderTemplate<Reader> MetalClawReaderTemplate<Reader>::child(const char*, int unionIdx) noexcept {
return MetalClawReaderTemplate<Reader>(m_reader, unionIdx, this);
constexpr MetalClawReaderTemplate<Reader> MetalClawReaderTemplate<Reader>::child(
const char*,
ox::Optional<int> unionIdx) noexcept {
return MetalClawReaderTemplate<Reader>(m_reader, unionIdx);
}
template<Reader_c Reader>

View File

@ -267,6 +267,9 @@ std::map<ox::StringView, ox::Error(*)()> tests = {
return OxError(0);
};
oxAssert(check(uint32_t(14)), "Decode of 14 failed.");
oxAssert(check(int8_t(-1)), "Decode of -1 failed.");
oxAssert(check(int16_t(-1)), "Decode of -1 failed.");
oxAssert(check(int32_t(-1)), "Decode of -1 failed.");
oxAssert(check(int64_t(-1)), "Decode of -1 failed.");
oxAssert(check(int64_t(-2)), "Decode of -2 failed.");
oxAssert(check(int64_t(-127)), "Decode of -127 failed.");

View File

@ -16,6 +16,7 @@
#include <ox/std/buffer.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/optional.hpp>
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>
#include <ox/std/units.hpp>
@ -34,12 +35,12 @@ class MetalClawWriter {
ox::Vector<uint8_t, 16> m_presenceMapBuff{};
FieldBitmap m_fieldPresence;
int m_field = 0;
int m_unionIdx = -1;
ox::Optional<int> m_unionIdx;
std::size_t m_writerBeginP{};
Writer &m_writer;
public:
constexpr explicit MetalClawWriter(Writer &writer, int unionIdx = -1) noexcept;
constexpr explicit MetalClawWriter(Writer &writer, ox::Optional<int> const&unionIdx = {}) noexcept;
constexpr ~MetalClawWriter() noexcept = default;
@ -114,7 +115,7 @@ class MetalClawWriter {
private:
constexpr Error appendInteger(Integer_c auto val) noexcept {
bool fieldSet = false;
if (val && (m_unionIdx == -1 || m_unionIdx == m_field)) {
if (val && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
auto mi = mc::encodeInteger(val);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(mi.data), mi.length));
fieldSet = true;
@ -130,7 +131,7 @@ extern template class ModelHandlerInterface<MetalClawWriter<BufferWriter>>;
extern template class ModelHandlerInterface<MetalClawWriter<CharBuffWriter>>;
template<Writer_c Writer>
constexpr MetalClawWriter<Writer>::MetalClawWriter(Writer &writer, int unionIdx) noexcept:
constexpr MetalClawWriter<Writer>::MetalClawWriter(Writer &writer, ox::Optional<int> const&unionIdx) noexcept:
m_fieldPresence(m_presenceMapBuff.data(), m_presenceMapBuff.size()),
m_unionIdx(unionIdx),
m_writerBeginP(writer.tellp()),
@ -179,7 +180,7 @@ constexpr Error MetalClawWriter<Writer>::field(const char*, const uint64_t *val)
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::field(const char*, const bool *val) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (!m_unionIdx.has_value() || *m_unionIdx == m_field) {
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), *val));
}
++m_field;
@ -190,7 +191,7 @@ template<Writer_c Writer>
template<std::size_t SmallStringSize>
constexpr Error MetalClawWriter<Writer>::field(const char*, const BasicString<SmallStringSize> *val) noexcept {
bool fieldSet = false;
if (val->len() && (m_unionIdx == -1 || m_unionIdx == m_field)) {
if (val->len() && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
// write the length
const auto strLen = mc::encodeInteger(val->len());
oxReturnError(m_writer.write(reinterpret_cast<const char*>(strLen.data), strLen.length));
@ -212,7 +213,7 @@ constexpr Error MetalClawWriter<Writer>::field(const char *name, const BString<L
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::fieldCString(const char*, const char *const*val, std::size_t) noexcept {
bool fieldSet = false;
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (!m_unionIdx.has_value() || *m_unionIdx == m_field) {
const auto strLen = *val ? ox_strlen(*val) : 0;
// write the length
const auto strLenBuff = mc::encodeInteger(strLen);
@ -239,7 +240,7 @@ constexpr Error MetalClawWriter<Writer>::fieldCString(const char *name, const ch
template<Writer_c Writer>
constexpr Error MetalClawWriter<Writer>::fieldCString(const char*, const char *val, std::size_t strLen) noexcept {
bool fieldSet = false;
if (strLen && (m_unionIdx == -1 || m_unionIdx == m_field)) {
if (strLen && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
// write the length
const auto strLenBuff = mc::encodeInteger(strLen);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(strLenBuff.data), strLenBuff.length));
@ -259,16 +260,17 @@ constexpr Error MetalClawWriter<Writer>::field(const char*, const T *val) noexce
return field(nullptr, val->data(), val->size());
} else {
bool fieldSet = false;
if (val && (m_unionIdx == -1 || m_unionIdx == m_field)) {
if (val && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer);
ModelHandlerInterface<MetalClawWriter<Writer>> handler{&writer};
oxReturnError(model(&handler, val));
oxReturnError(writer.finalize());
fieldSet = true;
fieldSet = writeIdx != m_writer.tellp();
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
return {};
}
}
@ -276,35 +278,37 @@ template<Writer_c Writer>
template<typename U, bool force>
constexpr Error MetalClawWriter<Writer>::field(const char*, UnionView<U, force> val) noexcept {
bool fieldSet = false;
if (val.get() && (m_unionIdx == -1 || m_unionIdx == m_field)) {
MetalClawWriter<Writer> writer(m_writer, val.idx());
if (val.get() && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer, ox::Optional<int>(ox::in_place, val.idx()));
ModelHandlerInterface handler{&writer};
oxReturnError(model(&handler, val.get()));
oxReturnError(writer.finalize());
fieldSet = true;
fieldSet = writeIdx != m_writer.tellp();
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
return {};
}
template<Writer_c Writer>
template<typename T>
constexpr Error MetalClawWriter<Writer>::field(const char*, const T *val, std::size_t len) noexcept {
bool fieldSet = false;
if (len && (m_unionIdx == -1 || m_unionIdx == m_field)) {
if (len && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
// write the length
const auto arrLen = mc::encodeInteger(len);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(arrLen.data), arrLen.length));
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer);
ModelHandlerInterface handler{&writer};
oxReturnError(handler.template setTypeInfo<T>("List", 0, {}, static_cast<std::size_t>(len)));
// write the array
for (std::size_t i = 0; i < len; i++) {
for (std::size_t i = 0; i < len; ++i) {
oxReturnError(handler.field("", &val[i]));
}
oxReturnError(writer.finalize());
fieldSet = true;
fieldSet = writeIdx != m_writer.tellp();
}
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
@ -317,7 +321,7 @@ constexpr Error MetalClawWriter<Writer>::field(const char*, const HashMap<String
const auto &keys = val->keys();
const auto len = keys.size();
bool fieldSet = false;
if (len && (m_unionIdx == -1 || m_unionIdx == m_field)) {
if (len && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
// write the length
const auto arrLen = mc::encodeInteger(len);
oxReturnError(m_writer.write(reinterpret_cast<const char*>(arrLen.data), arrLen.length));
@ -336,7 +340,7 @@ constexpr Error MetalClawWriter<Writer>::field(const char*, const HashMap<String
return handler.field("", value);
};
// write the array
for (std::size_t i = 0; i < len; i++) {
for (std::size_t i = 0; i < len; ++i) {
auto const&key = keys[i];
oxReturnError(loopBody(handler, key, *val));
}

View File

@ -196,7 +196,6 @@ class ModelHandlerInterface {
constexpr auto handler() noexcept {
return m_handler;
}
};
template<typename Handler, ox::OpType opType_v = Handler::opType()>
@ -215,7 +214,16 @@ class ModelHandlerBase {
static constexpr ox::OpType opType() noexcept {
return opType_v;
}
};
constexpr ox::Error resizeVector(auto &vec, size_t sz) {
if constexpr(ox::is_same_v<decltype(vec.resize(0)), ox::Error>) {
return vec.resize(sz);
} else {
vec.resize(sz);
return {};
}
}
}

View File

@ -202,7 +202,8 @@ class ModelValue {
class ModelValueVector {
private:
Vector<ModelValue> m_vec;
ModelValue m_templateValue;
const DescriptorType *m_type = nullptr;
int m_typeSubscriptLevels = 0;
String m_typeName;
int m_typeVersion = 0;
@ -227,14 +228,15 @@ class ModelValueVector {
return m_vec.data();
}
constexpr void resize(std::size_t sz) noexcept {
constexpr ox::Error resize(std::size_t sz) noexcept {
const auto oldSz = m_vec.size();
m_vec.resize(sz);
if (sz > oldSz) {
for (auto i = oldSz; i < sz; ++i) {
m_vec[i] = m_templateValue;
oxReturnError(m_vec[i].setType(m_type, m_typeSubscriptLevels));
}
}
return {};
}
[[nodiscard]]
@ -248,7 +250,9 @@ class ModelValueVector {
}
constexpr Error setType(const DescriptorType *type, int subscriptLevels) noexcept {
return m_templateValue.setType(type, subscriptLevels);
m_type = type;
m_typeSubscriptLevels = subscriptLevels;
return {};
}
[[nodiscard]]
@ -1089,14 +1093,12 @@ constexpr ModelValueVector::ModelValueVector(const ModelValueVector &other) noex
for (auto &v : other.m_vec) {
m_vec.emplace_back(v);
}
m_templateValue = other.m_templateValue;
m_typeName = other.m_typeName;
m_typeVersion = other.m_typeVersion;
}
constexpr ModelValueVector::ModelValueVector(ModelValueVector &&other) noexcept {
m_vec = std::move(other.m_vec);
m_templateValue = std::move(other.m_templateValue);
m_typeName = std::move(other.m_typeName);
m_typeVersion = other.m_typeVersion;
}
@ -1108,7 +1110,6 @@ constexpr ModelValueVector &ModelValueVector::operator=(const ModelValueVector &
for (auto &v : other.m_vec) {
m_vec.emplace_back(v);
}
m_templateValue = other.m_templateValue;
m_typeName = other.m_typeName;
m_typeVersion = other.m_typeVersion;
return *this;
@ -1119,7 +1120,6 @@ constexpr ModelValueVector &ModelValueVector::operator=(ModelValueVector &&other
return *this;
}
m_vec = std::move(other.m_vec);
m_templateValue = std::move(other.m_templateValue);
m_typeName = std::move(other.m_typeName);
m_typeVersion = other.m_typeVersion;
return *this;

View File

@ -147,7 +147,7 @@ Error OrganicClawReader::field(const char *key, T *val) noexcept {
if constexpr(isVector_v<T>) {
const auto &srcVal = value(key);
const auto srcSize = srcVal.size();
val->resize(srcSize);
oxReturnError(ox::resizeVector(*val, srcSize));
err = field(key, val->data(), val->size());
} else if constexpr(isArray_v<T>) {
const auto &srcVal = value(key);

View File

@ -28,7 +28,7 @@ class Optional {
constexpr Optional() noexcept = default;
template<typename ...Args>
explicit constexpr Optional(Args &&... args);
explicit constexpr Optional(ox::in_place_t, Args &&... args);
constexpr Optional(const Optional &other) {
if (other.m_ptr) {
@ -65,19 +65,19 @@ class Optional {
}
constexpr T &operator*() & noexcept {
return m_ptr;
return *m_ptr;
}
constexpr const T &operator*() const & noexcept {
return m_ptr;
return *m_ptr;
}
constexpr T &&operator*() && noexcept {
return m_ptr;
return *m_ptr;
}
constexpr const T &&operator*() const && noexcept {
return m_ptr;
return *m_ptr;
}
constexpr T *operator->() noexcept {
@ -163,7 +163,7 @@ class Optional {
template<typename T, std::size_t buffSize>
template<typename... Args>
constexpr Optional<T, buffSize>::Optional(Args &&... args) {
constexpr Optional<T, buffSize>::Optional(ox::in_place_t, Args &&... args) {
emplace(ox::forward<Args>(args)...);
}

View File

@ -12,6 +12,12 @@
namespace ox {
struct in_place_t {
explicit constexpr in_place_t() = default;
};
inline constexpr ox::in_place_t in_place;
template<class T>
constexpr T &&forward(remove_reference_t<T> &t) noexcept {
return static_cast<T&&>(t);

View File

@ -49,7 +49,7 @@ using BgCtl = uint16_t;
#define REG_BG3CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000e)
[[nodiscard]]
inline auto &regBgCtl(uintptr_t bgIdx) noexcept {
inline volatile BgCtl &regBgCtl(uintptr_t bgIdx) noexcept {
return *reinterpret_cast<volatile BgCtl*>(0x0400'0008 + 2 * bgIdx);
}
@ -60,7 +60,7 @@ inline auto &regBgCtl(uintptr_t bgIdx) noexcept {
#define REG_BG3HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001c)
[[nodiscard]]
inline volatile auto &regBgHofs(auto bgIdx) noexcept {
inline volatile uint32_t &regBgHofs(auto bgIdx) noexcept {
return *reinterpret_cast<volatile uint32_t*>(0x0400'0010 + 4 * bgIdx);
}
@ -71,7 +71,7 @@ inline volatile auto &regBgHofs(auto bgIdx) noexcept {
#define REG_BG3VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001e)
[[nodiscard]]
inline volatile auto &regBgVofs(auto bgIdx) noexcept {
inline volatile uint32_t &regBgVofs(auto bgIdx) noexcept {
return *reinterpret_cast<volatile uint32_t*>(0x0400'0012 + 4 * bgIdx);
}

View File

@ -33,9 +33,10 @@ struct OX_ALIGN8 GbaSpriteAttrUpdate {
uint16_t attr1 = 0;
uint16_t attr2 = 0;
uint16_t idx = 0;
};
GbaSpriteAttrUpdate &spriteAttr(size_t i) noexcept;
void addSpriteUpdate(const GbaSpriteAttrUpdate &upd) noexcept;
void applySpriteUpdates() noexcept;

View File

@ -8,8 +8,8 @@
namespace teagba {
inline auto bgSetSbb(volatile BgCtl *bgCtl, unsigned sbb) noexcept {
*bgCtl = static_cast<BgCtl>(*bgCtl & ~0b11111'0000'0000u) | static_cast<BgCtl>(sbb << 8);
inline auto bgSetSbb(volatile BgCtl &bgCtl, unsigned sbb) noexcept {
bgCtl = static_cast<BgCtl>(bgCtl & ~0b11111'0000'0000u) | static_cast<BgCtl>(sbb << 8);
}
[[nodiscard]]
@ -17,14 +17,9 @@ constexpr unsigned bgPri(BgCtl bgCtl) noexcept {
return bgCtl & 1;
}
[[nodiscard]]
inline auto bgPri(const volatile BgCtl *bgCtl) noexcept {
return bgPri(*bgCtl);
}
inline auto bgSetPri(volatile BgCtl *bgCtl, unsigned pri) noexcept {
inline auto bgSetPri(volatile BgCtl &bgCtl, unsigned pri) noexcept {
pri = pri & 0b1;
*bgCtl = static_cast<BgCtl>(*bgCtl & ~0b1u) | static_cast<BgCtl>(pri << 0);
bgCtl = static_cast<BgCtl>(bgCtl & ~0b1u) | static_cast<BgCtl>(pri << 0);
}
[[nodiscard]]
@ -32,17 +27,12 @@ constexpr unsigned bgBpp(BgCtl bgCtl) noexcept {
return ((bgCtl >> 7) & 1) ? 8 : 4;
}
[[nodiscard]]
inline auto bgBpp(const volatile BgCtl *bgCtl) noexcept {
return bgBpp(*bgCtl);
}
inline auto bgSetBpp(volatile BgCtl *bgCtl, unsigned bpp) noexcept {
inline auto bgSetBpp(volatile BgCtl &bgCtl, unsigned bpp) noexcept {
constexpr auto Bpp8 = 1 << 7;
if (bpp == 4) {
*bgCtl = *bgCtl | ((*bgCtl | Bpp8) ^ Bpp8); // set to use 4 bits per pixel
bgCtl = bgCtl | ((bgCtl | Bpp8) ^ Bpp8); // set to use 4 bits per pixel
} else {
*bgCtl = *bgCtl | Bpp8; // set to use 8 bits per pixel
bgCtl = bgCtl | Bpp8; // set to use 8 bits per pixel
}
}
@ -51,19 +41,14 @@ constexpr auto bgCbb(BgCtl bgCtl) noexcept {
return (bgCtl >> 2) & 0b11u;
}
[[nodiscard]]
inline auto bgCbb(const volatile BgCtl *bgCtl) noexcept {
return bgCbb(*bgCtl);
}
inline auto bgSetCbb(volatile BgCtl *bgCtl, unsigned cbb) noexcept {
inline auto bgSetCbb(volatile BgCtl &bgCtl, unsigned cbb) noexcept {
cbb = cbb & 0b11;
*bgCtl = static_cast<BgCtl>(*bgCtl & ~0b1100u) | static_cast<BgCtl>(cbb << 2);
bgCtl = static_cast<BgCtl>(bgCtl & ~0b1100u) | static_cast<BgCtl>(cbb << 2);
}
constexpr void iterateBgCtl(auto cb) noexcept {
for (auto bgCtl = &REG_BG0CTL; bgCtl <= &REG_BG3CTL; bgCtl += 2) {
cb(bgCtl);
cb(*bgCtl);
}
}

View File

@ -10,32 +10,23 @@
namespace teagba {
static volatile uint16_t g_spriteUpdates = 0;
static ox::Array<GbaSpriteAttrUpdate, 128> g_spriteBuffer;
void addSpriteUpdate(const GbaSpriteAttrUpdate &upd) noexcept {
// block until g_spriteUpdates is less than buffer len
if (g_spriteUpdates >= g_spriteBuffer.size()) [[unlikely]] {
teagba_vblankintrwait();
GbaSpriteAttrUpdate &spriteAttr(size_t i) noexcept {
return g_spriteBuffer[i];
}
void addSpriteUpdate(const GbaSpriteAttrUpdate &upd) noexcept {
const auto ie = REG_IE; // disable vblank interrupt handler
REG_IE = REG_IE & static_cast<uint16_t>(~teagba::Int_vblank); // disable vblank interrupt handler
const auto updateCnt = g_spriteUpdates;
g_spriteBuffer[updateCnt] = upd;
g_spriteUpdates = updateCnt + 1;
g_spriteBuffer[upd.idx] = upd;
REG_IE = ie; // enable vblank interrupt handler
}
void applySpriteUpdates() noexcept {
// copy g_spriteUpdates to allow it to use a register instead of reading
// from memory every iteration of the loop, needed because g_spriteUpdates
// is volatile
const unsigned updates = g_spriteUpdates;
for (unsigned i = 0; i < updates; ++i) {
const auto &oa = g_spriteBuffer[i];
for (auto const&oa : g_spriteBuffer) {
MEM_OAM[oa.idx] = std::bit_cast<uint64_t>(oa);
}
g_spriteUpdates = 0;
}
}

View File

@ -23,6 +23,7 @@ install(
include/nostalgia/modules
)
if(${OLYMPIC_BUILD_STUDIO})
# Studio
if(TURBINE_BUILD_TYPE STREQUAL "Native")
add_library(
@ -42,6 +43,7 @@ if(TURBINE_BUILD_TYPE STREQUAL "Native")
include/nostalgia/modules
)
endif()
endif()
add_library(NostalgiaProfile INTERFACE)
target_compile_definitions(

View File

@ -1,4 +1,7 @@
add_subdirectory(src)
if(NOT TURBINE_BUILD_TYPE STREQUAL "GBA")
add_subdirectory(test)
endif()
install(
DIRECTORY

View File

@ -13,9 +13,9 @@
namespace nostalgia::core {
extern ox::Array<char, 128> charMap;
struct Sprite {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Sprite";
static constexpr auto TypeVersion = 1;
bool enabled = false;
int x = 0;
int y = 0;
@ -23,6 +23,7 @@ struct Sprite {
unsigned spriteShape = 0;
unsigned spriteSize = 0;
unsigned flipX = 0;
unsigned bpp = 0;
/**
* Valid priorities: 0-3
*/
@ -38,14 +39,89 @@ oxModelBegin(Sprite)
oxModelField(spriteShape)
oxModelField(spriteSize)
oxModelField(flipX)
oxModelField(bpp)
oxModelField(priority)
oxModelEnd()
struct TileSheetSetEntrySection {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection";
static constexpr auto TypeVersion = 1;
int32_t begin = 0;
int32_t tiles = 0;
[[nodiscard]]
constexpr auto end() const noexcept {
return begin + tiles - 1;
}
};
oxModelBegin(TileSheetSetEntrySection)
oxModelField(begin)
oxModelField(size)
oxModelEnd()
struct TileSheetSetEntry {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntry";
static constexpr auto TypeVersion = 1;
ox::FileAddress tilesheet;
ox::Vector<TileSheetSetEntrySection> sections;
};
oxModelBegin(TileSheetSetEntry)
oxModelField(tilesheet)
oxModelField(sections)
oxModelEnd()
struct TileSheetSet {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSet";
static constexpr auto TypeVersion = 1;
static constexpr auto Preloadable = true;
int bpp = 0;
ox::Vector<TileSheetSetEntry> entries;
};
oxModelBegin(TileSheetSet)
oxModelField(bpp)
oxModelField(entries)
oxModelEnd()
ox::Error loadBgPalette(
Context &ctx,
ox::FileAddress const&paletteAddr) noexcept;
ox::Error loadSpritePalette(
Context &ctx,
ox::FileAddress const&paletteAddr) noexcept;
ox::Error loadBgTileSheet(
Context &ctx,
unsigned cbb,
TileSheetSet const&set) noexcept;
ox::Error loadBgTileSheet(
Context &ctx,
unsigned cbb,
ox::FileAddress const&tilesheetAddr,
bool loadDefaultPalette = false) noexcept;
ox::Error loadSpriteTileSheet(
Context &ctx,
ox::FileAddress const&tilesheetAddr,
bool loadDefaultPalette = false) noexcept;
ox::Error loadSpriteTileSheet(
Context &ctx,
TileSheetSet const&set) noexcept;
void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, uint8_t tile) noexcept;
void clearBg(Context &ctx, uint_t bgIdx) noexcept;
[[nodiscard]]
uint8_t bgStatus(Context &ctx) noexcept;
void setBgStatus(Context &ctx, uint32_t status) noexcept;
[[nodiscard]]
bool bgStatus(Context &ctx, unsigned bg) noexcept;
void setBgStatus(Context &ctx, unsigned bg, bool status) noexcept;
@ -54,34 +130,19 @@ void setBgCbb(Context &ctx, unsigned bgIdx, unsigned cbb) noexcept;
void setBgPriority(Context &ctx, uint_t bgIdx, uint_t priority) 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,
ox::FileAddress const&tilesheetAddr,
ox::FileAddress const&paletteAddr = nullptr) noexcept;
ox::Error loadSpriteTileSheet(
Context &ctx,
ox::FileAddress const&tilesheetAddr,
ox::FileAddress const&paletteAddr) noexcept;
ox::Error initConsole(Context &ctx) noexcept;
void puts(Context &ctx, int column, int row, ox::CRStringView str) noexcept;
void setTile(Context &ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept;
void clearTileLayer(Context &ctx, unsigned bgIdx) noexcept;
void hideSprite(Context &ctx, unsigned) noexcept;
void showSprite(Context &ctx, unsigned) noexcept;
void setSprite(Context &c, uint_t idx, Sprite const&s) noexcept;
[[nodiscard]]
uint_t spriteCount(Context &ctx) noexcept;
ox::Error initConsole(Context &ctx) noexcept;
void puts(Context &ctx, int column, int row, ox::CRStringView str) noexcept;
}
namespace nostalgia::core::gl {

View File

@ -8,10 +8,15 @@
namespace nostalgia::core {
struct BgCbbData {
unsigned bpp = 4;
};
class Context {
public:
turbine::Context &turbineCtx;
ox::Array<BgCbbData, 4> cbbData;
explicit Context(turbine::Context &tctx) noexcept;
Context(Context &other) noexcept = delete;

View File

@ -10,7 +10,6 @@
#include <teagba/gfx.hpp>
#include <teagba/registers.hpp>
#include <keel/media.hpp>
#include <turbine/turbine.hpp>
#include <nostalgia/core/color.hpp>
@ -22,13 +21,9 @@
namespace nostalgia::core {
struct BgCbbData {
unsigned bpp = 4;
};
static ox::Array<BgCbbData, 4> g_cbbData;
constexpr auto GbaTileColumns = 32;
constexpr auto GbaTileRows = 32;
constexpr auto SpriteCount = 128;
struct GbaPaletteTarget {
static constexpr auto TypeName = Palette::TypeName;
@ -39,25 +34,105 @@ struct GbaPaletteTarget {
struct GbaTileMapTarget {
static constexpr auto TypeName = CompactTileSheet::TypeName;
static constexpr auto TypeVersion = CompactTileSheet::TypeVersion;
BgCbbData *cbbData = nullptr;
unsigned &bpp;
ox::FileAddress defaultPalette;
GbaPaletteTarget pal;
volatile uint16_t *tileMap = nullptr;
// the following values are not actually in CompactTileSheet,
// and only exist to communicate with the loading process
size_t tileWriteIdx = 0;
unsigned targetBpp = 0;
TileSheetSetEntry const*setEntry = nullptr;
};
constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaPaletteTarget> auto *t) noexcept {
oxReturnError(io->template setTypeInfo<Palette>());
if (t->palette) {
const auto colorHandler = [t](std::size_t i, const Color16 *c) {
t->palette[i] = *c;
return OxError(0);
return ox::Error{};
};
return io->template field<Color16, decltype(colorHandler)>("colors", colorHandler);
} else {
constexpr auto colorHandler = [](std::size_t, const Color16*) {
return ox::Error{};
};
return io->template field<Color16, decltype(colorHandler)>("colors", colorHandler);
}
}
[[nodiscard]]
static bool loadPixel(TileSheetSetEntry const&setEntry, size_t &sectionIdx, int tileIdx) noexcept {
if (setEntry.sections.size() <= sectionIdx) {
return false;
}
auto &section = setEntry.sections[sectionIdx];
if (tileIdx < section.begin) {
return false;
}
if (tileIdx > section.end()) {
if (sectionIdx >= setEntry.sections.size()) {
return false;
}
++sectionIdx;
return tileIdx > section.begin && tileIdx <= section.end();
}
return true;
}
constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t) noexcept {
oxReturnError(io->template setTypeInfo<CompactTileSheet>());
oxReturnError(io->field("bpp", &t->cbbData->bpp));
oxReturnError(io->field("bpp", &t->bpp));
oxReturnError(io->field("defaultPalette", &t->defaultPalette));
if (t->targetBpp == 0) {
t->targetBpp = t->bpp;
}
if (t->targetBpp != t->bpp && t->bpp == 8) {
return OxError(1, "Cannot load an 8 BPP tilesheet into a 4 BPP CBB");
}
ox::Error out{};
if (t->setEntry) {
// The following code is atrocious, but it works.
// It might be possible to clean it up a little, but it probably
// cannot be seriously optimized without preloading TileSheets.
size_t sectionIdx = 0;
if (t->targetBpp == t->bpp) {
TileSheetSetEntrySection const*section = &t->setEntry->sections[0];
size_t writeIt = 0;
uint16_t intermediate = 0;
size_t const fourBpp = t->bpp == 4;
const auto handleTileMap = [t, &intermediate, &section, &sectionIdx, &writeIt, fourBpp]
(std::size_t i, uint8_t const*tile) {
auto const tileIdx = static_cast<int>((i * (2 * fourBpp)) / PixelsPerTile);
if (!loadPixel(*t->setEntry, sectionIdx, tileIdx)) {
return ox::Error{};
}
if (writeIt & 1) { // i is odd
intermediate |= static_cast<uint16_t>(*tile) << 8;
t->tileMap[writeIt / 2] = intermediate;
++t->tileWriteIdx;
} else { // i is even
intermediate = *tile & 0x00ff;
}
++writeIt;
return ox::Error{};
};
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
} else if (t->targetBpp > t->bpp) { // 4 -> 8 bits
const auto handleTileMap = [t, &sectionIdx](std::size_t writeIt, uint8_t const*tile) {
auto constexpr BytesPerTile4Bpp = 32;
auto const tileIdx = static_cast<int>(writeIt / BytesPerTile4Bpp);
if (!loadPixel(*t->setEntry, sectionIdx, tileIdx)) {
return ox::Error{};
}
uint16_t const px1 = *tile & 0xf;
uint16_t const px2 = *tile >> 4;
t->tileMap[t->tileWriteIdx] = static_cast<uint16_t>(px1 | (px2 << 8));
++t->tileWriteIdx;
return ox::Error{};
};
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
}
} else {
uint16_t intermediate = 0;
const auto handleTileMap = [t, &intermediate](std::size_t i, const uint8_t*tile) {
if (i & 1) { // i is odd
@ -66,18 +141,160 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t)
} else { // i is even
intermediate = *tile & 0x00ff;
}
return OxError(0);
return ox::Error{};
};
return io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
}
return out;
}
ox::Error initGfx(Context&, InitParams const&) noexcept {
for (auto bgCtl = &REG_BG0CTL; bgCtl <= &REG_BG3CTL; bgCtl += 2) {
teagba::bgSetSbb(bgCtl, 28);
teagba::bgSetSbb(*bgCtl, 28);
}
for (uint16_t i = 0; i < SpriteCount; ++i) {
auto &sa = teagba::spriteAttr(i);
sa.idx = i;
}
return {};
}
ox::Error loadBgPalette(
Context &ctx,
ox::FileAddress const&paletteAddr) noexcept {
auto &rom = ctx.rom();
GbaPaletteTarget const palTarget{.palette = MEM_BG_PALETTE};
oxRequire(palStat, rom.stat(paletteAddr));
oxRequire(pal, rom.directAccess(paletteAddr));
oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &palTarget));
return {};
}
ox::Error loadSpritePalette(
Context &ctx,
ox::FileAddress const&paletteAddr) noexcept {
auto &rom = ctx.rom();
GbaPaletteTarget const palTarget{.palette = MEM_SPRITE_PALETTE};
oxRequire(palStat, rom.stat(paletteAddr));
oxRequire(pal, rom.directAccess(paletteAddr));
oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &palTarget));
return {};
}
static ox::Error loadTileSheetSet(
Context &ctx,
uint16_t *tileMapTargetMem,
TileSheetSet const&set) noexcept {
auto &rom = ctx.rom();
size_t tileWriteIdx = 0;
for (auto const&entry : set.entries) {
oxRequire(tsStat, rom.stat(entry.tilesheet));
oxRequire(ts, rom.directAccess(entry.tilesheet));
unsigned tilesheetBpp{};
GbaTileMapTarget target{
.bpp = tilesheetBpp,
.defaultPalette = {},
.tileMap = tileMapTargetMem + tileWriteIdx,
.targetBpp = static_cast<unsigned>(set.bpp),
.setEntry = &entry,
};
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
tileWriteIdx += target.tileWriteIdx;
}
return {};
}
ox::Error loadBgTileSheet(
Context &ctx,
unsigned cbb,
ox::FileAddress const&tilesheetAddr,
bool loadDefaultPalette) noexcept {
auto &rom = ctx.rom();
oxRequire(tsStat, rom.stat(tilesheetAddr));
oxRequire(ts, rom.directAccess(tilesheetAddr));
GbaTileMapTarget target{
.bpp = ctx.cbbData[cbb].bpp,
.defaultPalette = {},
.tileMap = MEM_BG_TILES[cbb].data(),
};
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
// update bpp of all bgs with the updated cbb
const auto bpp = ctx.cbbData[cbb].bpp;
teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) {
if (teagba::bgCbb(bgCtl) == cbb) {
teagba::bgSetBpp(bgCtl, bpp);
}
});
if (loadDefaultPalette && target.defaultPalette) {
oxReturnError(loadBgPalette(ctx, target.defaultPalette));
}
return {};
}
ox::Error loadBgTileSheet(
Context &ctx,
unsigned cbb,
TileSheetSet const&set) noexcept {
auto const bpp = static_cast<unsigned>(set.bpp);
oxReturnError(loadTileSheetSet(ctx, MEM_BG_TILES[cbb].data(), set));
// update bpp of all bgs with the updated cbb
ctx.cbbData[cbb].bpp = bpp;
teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) {
if (teagba::bgCbb(bgCtl) == cbb) {
teagba::bgSetBpp(bgCtl, bpp);
}
});
return {};
}
static void setSpritesBpp(unsigned const bpp) noexcept {
auto const eightBpp = static_cast<uint16_t >(bpp == 8);
for (auto i = 0u; i < SpriteCount; ++i) {
auto &sa = teagba::spriteAttr(i);
auto &a = sa.attr0;
a |= static_cast<uint16_t>((a & ~static_cast<uint16_t>(1u << 13)) | (eightBpp << 13));
}
}
ox::Error loadSpriteTileSheet(
Context &ctx,
ox::FileAddress const&tilesheetAddr,
bool loadDefaultPalette) noexcept {
auto &rom = ctx.rom();
oxRequire(tsStat, rom.stat(tilesheetAddr));
oxRequire(ts, rom.directAccess(tilesheetAddr));
unsigned bpp{};
GbaTileMapTarget target{
.bpp = bpp,
.defaultPalette = {},
.tileMap = MEM_SPRITE_TILES,
};
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
if (loadDefaultPalette && target.defaultPalette) {
oxReturnError(loadSpritePalette(ctx, target.defaultPalette));
}
setSpritesBpp(bpp);
return {};
}
ox::Error loadSpriteTileSheet(
Context &ctx,
TileSheetSet const&set) noexcept {
auto const bpp = static_cast<unsigned>(set.bpp);
oxReturnError(loadTileSheetSet(ctx, MEM_SPRITE_TILES, set));
setSpritesBpp(bpp);
return {};
}
void setBgTile(Context&, uint_t bgIdx, int column, int row, uint8_t tile) noexcept {
const auto tileIdx = static_cast<std::size_t>(row * GbaTileColumns + column);
MEM_BG_MAP[bgIdx][tileIdx] = tile;
}
void clearBg(Context&, uint_t bgIdx) noexcept {
memset(MEM_BG_MAP[bgIdx].data(), 0, GbaTileRows * GbaTileColumns);
}
uint8_t bgStatus(Context&) noexcept {
return (REG_DISPCTL >> 8u) & 0b1111u;
}
@ -97,11 +314,11 @@ void setBgStatus(Context&, unsigned bg, bool status) noexcept {
REG_DISPCTL = REG_DISPCTL | ((REG_DISPCTL & ~mask) | mask);
}
void setBgCbb(Context&, unsigned bgIdx, unsigned cbb) noexcept {
void setBgCbb(Context &ctx, unsigned bgIdx, unsigned cbb) noexcept {
auto &bgCtl = regBgCtl(bgIdx);
const auto &cbbData = g_cbbData[cbb];
teagba::bgSetBpp(&bgCtl, cbbData.bpp);
teagba::bgSetCbb(&bgCtl, cbb);
const auto &cbbData = ctx.cbbData[cbb];
teagba::bgSetBpp(bgCtl, cbbData.bpp);
teagba::bgSetCbb(bgCtl, cbb);
}
void setBgPriority(Context&, uint_t bgIdx, uint_t priority) noexcept {
@ -109,111 +326,6 @@ void setBgPriority(Context&, uint_t bgIdx, uint_t priority) noexcept {
bgCtl = (bgCtl & 0b1111'1111'1111'1100u) | (priority & 0b11);
}
static ox::Error loadBgTileSheet(
ox::MemFS const&rom,
unsigned cbb,
ox::FileAddress const&tilesheetAddr,
ox::FileAddress const&paletteAddr) noexcept {
oxRequire(tsStat, rom.stat(tilesheetAddr));
oxRequire(ts, rom.directAccess(tilesheetAddr));
GbaTileMapTarget target;
target.pal.palette = MEM_BG_PALETTE;
target.cbbData = &g_cbbData[cbb];
target.tileMap = MEM_BG_TILES[cbb].data();
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
// load external palette if available
if (paletteAddr) {
oxRequire(palStat, rom.stat(paletteAddr));
oxRequire(pal, rom.directAccess(paletteAddr));
oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &target.pal));
}
// update bpp of all bgs with the updated cbb
const auto bpp = g_cbbData[cbb].bpp;
teagba::iterateBgCtl([bpp, cbb](auto bgCtl) {
if (teagba::bgCbb(bgCtl) == cbb) {
teagba::bgSetBpp(bgCtl, bpp);
}
});
return {};
}
ox::Error loadBgTileSheet(
Context &ctx,
unsigned cbb,
ox::FileAddress const&tilesheetAddr,
ox::FileAddress const&paletteAddr) noexcept {
auto &rom = ctx.rom();
return loadBgTileSheet(rom, cbb, tilesheetAddr, paletteAddr);
}
ox::Error loadSpriteTileSheet(
Context &ctx,
ox::FileAddress const&tilesheetAddr,
ox::FileAddress const&paletteAddr) noexcept {
auto &rom = ctx.rom();
oxRequire(tsStat, ctx.rom().stat(tilesheetAddr));
oxRequire(ts, rom.directAccess(tilesheetAddr));
GbaTileMapTarget target;
target.pal.palette = MEM_SPRITE_PALETTE;
target.tileMap = MEM_SPRITE_TILES;
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
// load external palette if available
if (paletteAddr) {
oxRequire(palStat, ctx.rom().stat(paletteAddr));
oxRequire(pal, rom.directAccess(paletteAddr));
oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &target.pal));
}
return {};
}
ox::Error loadBgPalette(Context &ctx, unsigned, ox::FileAddress const&paletteAddr) noexcept {
auto &rom = ctx.rom();
GbaPaletteTarget target;
target.palette = MEM_BG_PALETTE;
oxRequire(palStat, ctx.rom().stat(paletteAddr));
oxRequire(pal, rom.directAccess(paletteAddr));
oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &target));
return {};
}
ox::Error loadSpritePalette(Context &ctx, unsigned cbb, ox::FileAddress const&paletteAddr) noexcept {
auto &rom = ctx.rom();
GbaPaletteTarget target;
target.palette = &MEM_SPRITE_PALETTE[cbb];
oxRequire(palStat, rom.stat(paletteAddr));
oxRequire(pal, rom.directAccess(paletteAddr));
oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &target));
return {};
}
ox::Error initConsole(Context &ctx) noexcept {
constexpr ox::FileAddress TilesheetAddr = ox::StringLiteral("/TileSheets/Charset.ng");
constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal");
setBgStatus(ctx, 0b0001);
oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr));
setBgCbb(ctx, 0, 0);
return {};
}
void puts(Context &ctx, int column, int row, ox::CRStringView str) noexcept {
const auto col = static_cast<unsigned>(column);
for (auto i = 0u; i < str.bytes(); i++) {
const auto c = charMap[static_cast<std::size_t>(str[i])];
setTile(ctx, 0, static_cast<int>(col + i), row, static_cast<uint8_t>(c));
}
}
void setTile(Context&, unsigned bgIdx, int column, int row, uint8_t tile) noexcept {
const auto tileIdx = static_cast<std::size_t>(row * GbaTileColumns + column);
MEM_BG_MAP[bgIdx][tileIdx] = tile;
}
// Do NOT use Context in the GBA version of this function.
void clearTileLayer(Context&, unsigned bgIdx) noexcept {
memset(MEM_BG_MAP[bgIdx].data(), 0, GbaTileRows * GbaTileColumns);
}
[[maybe_unused]]
void hideSprite(Context&, unsigned idx) noexcept {
//oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
teagba::addSpriteUpdate({
@ -222,7 +334,6 @@ void hideSprite(Context&, unsigned idx) noexcept {
});
}
[[maybe_unused]]
void showSprite(Context&, unsigned idx) noexcept {
//oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
teagba::addSpriteUpdate({
@ -233,20 +344,27 @@ void showSprite(Context&, unsigned idx) noexcept {
void setSprite(Context&, uint_t idx, Sprite const&s) noexcept {
//oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
uint16_t const eightBpp = s.bpp == 8;
teagba::addSpriteUpdate({
.attr0 = static_cast<uint16_t>(
(static_cast<uint16_t>(s.y & ox::onMask<uint8_t>(0b111'1111)))
| (static_cast<uint16_t>(1) << 10) // enable alpha
| (static_cast<uint16_t>(eightBpp) << 13)
| (static_cast<uint16_t>(s.spriteShape) << 14)),
.attr1 = static_cast<uint16_t>(
(static_cast<uint16_t>(s.x) & ox::onMask<uint8_t>(8))
| (static_cast<uint16_t>(s.flipX) << 12)
| (static_cast<uint16_t>(s.spriteSize) << 14)),
.attr2 = static_cast<uint16_t>(
(static_cast<uint16_t>(s.tileIdx & ox::onMask<uint16_t>(8)))
// double tileIdx if 8 bpp
(static_cast<uint16_t>((s.tileIdx * (1 + eightBpp)) & ox::onMask<uint16_t>(8)))
| (static_cast<uint16_t>(s.priority & 0b11) << 10)),
.idx = static_cast<uint16_t>(idx),
});
}
uint_t spriteCount(Context&) noexcept {
return SpriteCount;
}
}

View File

@ -31,7 +31,7 @@ void panic(const char *file, int line, const char *panicMsg, ox::Error const&err
oxIgnoreError(initGfx(*ctx, {}));
oxIgnoreError(initConsole(*ctx));
setBgStatus(*ctx, 0, true);
clearTileLayer(*ctx, 0);
clearBg(*ctx, 0);
ox::BString<23> serr = "Error code: ";
serr += static_cast<int64_t>(err);
puts(*ctx, 32 + 1, 1, "SADNESS...");

View File

@ -7,7 +7,7 @@
namespace nostalgia::core {
// map ASCII values to the nostalgia charset
ox::Array<char, 128> charMap = {
constexpr ox::Array<char, 128> charMap = {
0,
0,
0,
@ -137,4 +137,29 @@ ox::Array<char, 128> charMap = {
50, // ~
};
ox::Error initConsole(Context &ctx) noexcept {
constexpr ox::FileAddress TilesheetAddr = ox::StringLiteral("/TileSheets/Charset.ng");
constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal");
setBgStatus(ctx, 0b0001);
setBgCbb(ctx, 0, 0);
oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr));
return loadBgPalette(ctx, PaletteAddr);
}
void puts(
Context &ctx,
int const column,
int const row,
ox::CRStringView str) noexcept {
auto const col = static_cast<uint_t>(column);
for (auto i = 0u; i < str.bytes(); ++i) {
setBgTile(
ctx,
0,
static_cast<int>(col + i),
row,
static_cast<uint8_t>(charMap[static_cast<uint8_t>(str[i])]));
}
}
}

View File

@ -19,11 +19,11 @@
namespace nostalgia::core {
namespace renderer {
constexpr auto Scale = 1;
constexpr auto PriorityScale = 0.01f;
namespace renderer {
Drawer::Drawer(Context &ctx) noexcept: m_ctx(ctx) {}
void Drawer::draw(turbine::Context &tctx) noexcept {
@ -98,7 +98,7 @@ constexpr ox::CStringView spritefshadTmpl = R"glsl(
if (outColor.a == 0) {
discard;
}
})glsl";;
})glsl";
[[nodiscard]]
static constexpr auto bgVertexRow(uint_t x, uint_t y) noexcept {
@ -338,8 +338,7 @@ static void drawSprites(Context &ctx, ox::Size const&renderSz) noexcept {
static void loadPalette(
GLuint shaderPgrm,
Palette const&pal,
bool firstIsTransparent) noexcept {
Palette const&pal) noexcept {
static constexpr std::size_t ColorCnt = 256;
ox::Array<GLfloat, ColorCnt * 4> palette{};
for (auto i = 0u; const auto c : pal.colors) {
@ -348,41 +347,13 @@ static void loadPalette(
palette[i++] = bluef(c);
palette[i++] = 255;
}
if (firstIsTransparent) {
// make first color transparent
palette[3] = 0;
}
glUseProgram(shaderPgrm);
const auto uniformPalette = static_cast<GLint>(glGetUniformLocation(shaderPgrm, "fPalette"));
glUniform4fv(uniformPalette, ColorCnt, palette.data());
}
static void loadBgPalette(Context &ctx, Palette const&pal) noexcept {
loadPalette(ctx.bgShader, pal, true);
}
static void loadSpritePalette(Context &ctx, Palette const&pal) noexcept {
loadPalette(ctx.spriteShader, pal, true);
}
static void loadBgTexture(
Context &ctx,
uint_t cbbIdx,
const void *pixels,
int w,
int h) noexcept {
oxTracef("nostalgia.core.gfx.gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h);
ctx.cbbs[cbbIdx].tex = createTexture(w, h, pixels);
}
static void loadSpriteTexture(
Context &ctx,
const void *pixels,
int w,
int h) noexcept {
oxTracef("nostalgia.core.gfx.gl", "loadSpriteTexture: { w: {}, h: {} }", w, h);
ctx.spriteBlocks.tex = createTexture(w, h, pixels);
}
static void setSprite(
Context &ctx,
uint_t const idx,
@ -493,8 +464,7 @@ struct TileSheetData {
}
};
static ox::Result<TileSheetData> loadTileSheet(
Context &ctx,
static ox::Result<TileSheetData> normalizeTileSheet(
CompactTileSheet const&tilesheet) noexcept {
const uint_t bytesPerTile = tilesheet.bpp == 8 ? PixelsPerTile : PixelsPerTile / 2;
const auto tiles = tilesheet.pixels.size() / bytesPerTile;
@ -513,71 +483,131 @@ static ox::Result<TileSheetData> loadTileSheet(
pixels[i * 2 + 1] = tilesheet.pixels[i] >> 4;
}
}
renderer::loadSpriteTexture(ctx, pixels.data(), width, height);
return TileSheetData{std::move(pixels), width, height};
}
ox::Error loadBgPalette(
Context &ctx,
ox::FileAddress const&paletteAddr) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(palette, readObj<Palette>(kctx, paletteAddr));
renderer::loadPalette(ctx.bgShader, *palette);
return {};
}
ox::Error loadSpritePalette(
Context &ctx,
ox::FileAddress const&paletteAddr) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(palette, readObj<Palette>(kctx, paletteAddr));
renderer::loadPalette(ctx.spriteShader, *palette);
return {};
}
static ox::Result<TileSheetData> buildSetTsd(
Context &ctx,
TileSheetSet const&set) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx);
TileSheetData setTsd;
setTsd.width = TileWidth;
for (auto const&entry : set.entries) {
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, entry.tilesheet));
oxRequire(tsd, normalizeTileSheet(*tilesheet));
for (auto const&s : entry.sections) {
auto const size = s.tiles * PixelsPerTile;
for (auto i = 0; i < size; ++i) {
auto const srcIdx = static_cast<size_t>(i) + static_cast<size_t>(s.begin * PixelsPerTile);
setTsd.pixels.push_back(tsd.pixels[srcIdx]);
}
setTsd.height += TileHeight * s.tiles;
}
}
return setTsd;
}
ox::Error loadBgTileSheet(
Context &ctx,
uint_t cbb,
ox::FileAddress const&tilesheetAddr,
ox::FileAddress const&paletteAddr) noexcept {
bool loadDefaultPalette) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
oxRequire(palette, readObj<Palette>(kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
oxRequire(tsd, loadTileSheet(ctx, *tilesheet).to([](TileSheetData const&t) -> TileSheetData {
return {
.pixels = resizeTileSheetData(t.pixels, t.size(), Scale),
.width = t.width * Scale,
.height = t.height * Scale,
};
}));
renderer::loadBgTexture(ctx, cbb, tsd.pixels.data(), tsd.width, tsd.height);
renderer::loadBgPalette(ctx, *palette);
oxRequire(tsd, normalizeTileSheet(*tilesheet));
oxTracef("nostalgia.core.gfx.gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbb, tsd.width, tsd.height);
ctx.cbbs[cbb].tex = renderer::createTexture(tsd.width, tsd.height, tsd.pixels.data());
if (loadDefaultPalette) {
oxReturnError(loadBgPalette(ctx, tilesheet->defaultPalette));
}
return {};
}
ox::Error loadBgTileSheet(
Context &ctx,
unsigned cbb,
TileSheetSet const&set) noexcept {
oxRequire(setTsd, buildSetTsd(ctx, set));
ctx.cbbs[cbb].tex = renderer::createTexture(setTsd.width, setTsd.height, setTsd.pixels.data());
return {};
}
ox::Error loadSpriteTileSheet(
Context &ctx,
ox::FileAddress const&tilesheetAddr,
ox::FileAddress const&paletteAddr) noexcept {
bool loadDefaultPalette) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
oxRequire(palette, readObj<Palette>(kctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette));
oxRequire(tsd, loadTileSheet(ctx, *tilesheet));
renderer::loadSpriteTexture(ctx, tsd.pixels.data(), tsd.width, tsd.height);
renderer::loadSpritePalette(ctx, *palette);
oxRequire(tsd, normalizeTileSheet(*tilesheet));
oxTracef("nostalgia.core.gfx.gl", "loadSpriteTexture: { w: {}, h: {} }", tsd.width, tsd.height);
ctx.spriteBlocks.tex = renderer::createTexture(tsd.width, tsd.height, tsd.pixels.data());
if (loadDefaultPalette) {
oxReturnError(loadSpritePalette(ctx, tilesheet->defaultPalette));
}
return {};
}
ox::Error initConsole(Context &ctx) noexcept {
constexpr ox::FileAddress TilesheetAddr = ox::StringLiteral("/TileSheets/Charset.ng");
constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal");
setBgStatus(ctx, 0b0001);
setBgCbb(ctx, 0, 0);
return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr);
ox::Error loadSpriteTileSheet(
Context &ctx,
TileSheetSet const&set) noexcept {
oxRequire(setTsd, buildSetTsd(ctx, set));
ctx.spriteBlocks.tex = renderer::createTexture(setTsd.width, setTsd.height, setTsd.pixels.data());
return {};
}
void puts(Context &ctx, int column, int row, ox::CRStringView str) noexcept {
const auto col = static_cast<uint_t>(column);
for (auto i = 0u; i < str.bytes(); ++i) {
setTile(
ctx,
0,
static_cast<int>(col + i),
row,
static_cast<uint8_t>(charMap[static_cast<uint8_t>(str[i])]));
}
}
void setBgCbb(Context &ctx, uint_t bgIdx, uint_t cbbIdx) noexcept {
void setBgTile(
Context &ctx,
uint_t bgIdx,
int column,
int row,
uint8_t tile) noexcept {
oxTracef(
"nostalgia.core.gfx.setBgTile",
"bgIdx: {}, column: {}, row: {}, tile: {}",
bgIdx, column, row, tile);
const auto z = static_cast<uint_t>(bgIdx);
const auto y = static_cast<uint_t>(row);
const auto x = static_cast<uint_t>(column);
const auto i = renderer::bgVertexRow(x, y);
auto &cbb = ctx.cbbs[z];
const auto vbo = &cbb.vertices[i * renderer::BgVertexVboLength];
const auto ebo = &cbb.elements[i * renderer::BgVertexEboLength];
auto &bg = ctx.backgrounds[bgIdx];
bg.cbbIdx = cbbIdx;
renderer::setTileBufferObject(
static_cast<uint_t>(i * renderer::BgVertexVboRows),
static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(tile),
bg.priority,
vbo,
ebo);
cbb.updated = true;
}
void setBgPriority(Context &ctx, uint_t bgIdx, uint_t priority) noexcept {
auto &bg = ctx.backgrounds[bgIdx];
bg.priority = static_cast<float>(priority & 0b11);
void clearBg(Context &ctx, uint_t bgIdx) noexcept {
auto &cbb = ctx.cbbs[static_cast<std::size_t>(bgIdx)];
initBackgroundBufferObjects(cbb);
cbb.updated = true;
auto &bg = ctx.backgrounds[static_cast<std::size_t>(bgIdx)];
bg.priority = 0;
}
uint8_t bgStatus(Context &ctx) noexcept {
@ -602,13 +632,14 @@ void setBgStatus(Context &ctx, uint_t bg, bool status) noexcept {
ctx.backgrounds[bg].enabled = status;
}
void setBgCbb(Context &ctx, uint_t bgIdx, uint_t cbbIdx) noexcept {
auto &bg = ctx.backgrounds[bgIdx];
bg.cbbIdx = cbbIdx;
}
void clearTileLayer(Context &ctx, uint_t bgIdx) noexcept {
auto &cbb = ctx.cbbs[static_cast<std::size_t>(bgIdx)];
initBackgroundBufferObjects(cbb);
cbb.updated = true;
auto &bg = ctx.backgrounds[static_cast<std::size_t>(bgIdx)];
bg.priority = 0;
void setBgPriority(Context &ctx, uint_t bgIdx, uint_t priority) noexcept {
auto &bg = ctx.backgrounds[bgIdx];
bg.priority = static_cast<float>(priority & 0b11);
}
void hideSprite(Context &ctx, uint_t idx) noexcept {
@ -629,33 +660,8 @@ void setSprite(Context &ctx, uint_t idx, Sprite const&sprite) noexcept {
renderer::setSprite(ctx, idx, s);
}
void setTile(
Context &ctx,
uint_t bgIdx,
int column,
int row,
uint8_t tile) noexcept {
oxTracef(
"nostalgia.core.gfx.setTile",
"bgIdx: {}, column: {}, row: {}, tile: {}",
bgIdx, column, row, tile);
const auto z = static_cast<uint_t>(bgIdx);
const auto y = static_cast<uint_t>(row);
const auto x = static_cast<uint_t>(column);
const auto i = renderer::bgVertexRow(x, y);
auto &cbb = ctx.cbbs[z];
const auto vbo = &cbb.vertices[i * renderer::BgVertexVboLength];
const auto ebo = &cbb.elements[i * renderer::BgVertexEboLength];
auto &bg = ctx.backgrounds[bgIdx];
renderer::setTileBufferObject(
static_cast<uint_t>(i * renderer::BgVertexVboRows),
static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(tile),
bg.priority,
vbo,
ebo);
cbb.updated = true;
uint_t spriteCount(Context &ctx) noexcept {
return ctx.spriteCount;
}
namespace gl {

View File

@ -0,0 +1,11 @@
add_executable(
NostalgiaCoreTest
tests.cpp
)
target_link_libraries(
NostalgiaCoreTest
NostalgiaCore
)
add_test("[NostalgiaCore] readWriteTileSheet" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/NostalgiaCoreTest readWriteTileSheet)

View File

@ -0,0 +1,38 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#undef NDEBUG
#include <map>
#include <ox/std/error.hpp>
#include <ox/mc/mc.hpp>
#include <nostalgia/core/core.hpp>
using namespace nostalgia;
static std::map<ox::StringView, ox::Error(*)()> tests = {
{
"readWriteTileSheet",
[]() -> ox::Error {
core::TileSheet in;
oxRequire(buff, ox::writeMC(in));
oxRequire(out, ox::readMC<core::TileSheet>(buff));
oxAssert(in.subsheet.name == out.subsheet.name, "subsheet.name serialization broken");
return {};
}
},
};
int main(int argc, const char **args) {
int retval = -1;
if (argc > 0) {
auto const testName = ox::StringView(args[1]);
if (tests.find(testName) != tests.end()) {
retval = static_cast<int>(tests[testName]());
} else {
retval = 1;
}
}
return retval;
}

View File

@ -17,8 +17,8 @@ ox::Error Scene::setupDisplay(core::Context &ctx) const noexcept {
return OxError(1, "Scene has no palettes");
}
auto const&palette = m_sceneStatic.palettes[0];
oxReturnError(core::loadBgTileSheet(
ctx, 0, m_sceneStatic.tilesheet, palette));
oxReturnError(core::loadBgTileSheet(ctx, 0, m_sceneStatic.tilesheet));
oxReturnError(core::loadBgPalette(ctx, palette));
// disable all backgrounds
core::setBgStatus(ctx, 0);
for (auto layerNo = 0u; auto const&layer : m_sceneStatic.tileMapIdx) {
@ -39,10 +39,10 @@ void Scene::setupLayer(
const auto width = m_sceneStatic.rows[layerNo];
for (auto const&tile : layer) {
const auto tile8 = static_cast<uint8_t>(tile);
core::setTile(ctx, layerNo, x, y, tile8);
core::setTile(ctx, layerNo, x + 1, y, tile8 + 1);
core::setTile(ctx, layerNo, x, y + 1, tile8 + 2);
core::setTile(ctx, layerNo, x + 1, y + 1, tile8 + 3);
core::setBgTile(ctx, layerNo, x, y, tile8);
core::setBgTile(ctx, layerNo, x + 1, y, tile8 + 1);
core::setBgTile(ctx, layerNo, x, y + 1, tile8 + 2);
core::setBgTile(ctx, layerNo, x + 1, y + 1, tile8 + 3);
x += 2;
if (x >= width * 2) {
x = 0;

View File

@ -14,25 +14,6 @@ static int spriteX{};
static int spriteY{};
static bool s_paused = false;
static int sceneUpdateHandler(turbine::Context&) noexcept {
constexpr auto sleepTime = 16;
if (s_paused) {
return sleepTime;
}
// do stuff
return sleepTime;
}
static void sceneKeyEventHandler(turbine::Context &tctx, turbine::Key key, bool down) noexcept {
if (down) {
if (key == turbine::Key::Alpha_Q) {
turbine::requestShutdown(tctx);
} else if (key == turbine::Key::Alpha_P) {
s_paused = !s_paused;
}
}
}
[[maybe_unused]]
static int testUpdateHandler(turbine::Context &tctx) noexcept {
auto &cctx = *turbine::applicationData<core::Context>(tctx);
@ -82,7 +63,6 @@ static void testKeyEventHandler(turbine::Context &tctx, turbine::Key key, bool d
}
}
[[maybe_unused]]
static ox::Error runTest(turbine::Context &tctx) {
constexpr ox::FileAddress TileSheetAddr = ox::StringLiteral("/TileSheets/Charset.ng");
@ -90,17 +70,78 @@ static ox::Error runTest(turbine::Context &tctx) {
oxRequireM(cctx, core::init(tctx));
turbine::setApplicationData(tctx, cctx.get());
oxRequire(tsStat, turbine::rom(tctx)->stat(PaletteAddr));
oxReturnError(core::loadSpriteTileSheet(*cctx, TileSheetAddr, PaletteAddr));
oxReturnError(core::loadSpriteTileSheet(*cctx, TileSheetAddr));
oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr));
oxReturnError(core::initConsole(*cctx));
core::puts(*cctx, 10, 9, "DOPENESS!!!");
turbine::setUpdateHandler(tctx, testUpdateHandler);
turbine::setKeyEventHandler(tctx, testKeyEventHandler);
//core::setBgStatus(*cctx, 1, true);
//core::setBgStatus(*cctx, 2, true);
//core::setBgStatus(*cctx, 3, true);
return turbine::run(tctx);
}
[[maybe_unused]]
static ox::Error runTileSheetSetTest(turbine::Context &tctx) {
// this should make the screen display 'ABCDB'
constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal");
oxRequireM(cctx, core::init(tctx));
turbine::setApplicationData(tctx, cctx.get());
oxRequire(tsStat, turbine::rom(tctx)->stat(PaletteAddr));
core::TileSheetSet const set{
.bpp = 4,
.entries = {
{ .tilesheet = ox::StringLiteral("/TileSheets/Chester.ng"), .sections{{.begin = 0, .tiles = 1}} },
{ .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 0, .tiles = 2}} },
{ .tilesheet = ox::StringLiteral("/TileSheets/CD.ng"), .sections{{.begin = 0, .tiles = 2}} },
{ .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 1, .tiles = 1}} },
},
};
oxReturnError(core::loadBgTileSheet(*cctx, 0, set));
oxReturnError(core::loadSpriteTileSheet(*cctx, set));
oxReturnError(core::loadBgPalette(*cctx, PaletteAddr));
oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr));
core::setBgStatus(*cctx, 0, true);
core::setBgTile(*cctx, 0, 10, 9, 1);
core::setBgTile(*cctx, 0, 11, 9, 2);
core::setBgTile(*cctx, 0, 13, 9, 4);
core::setSprite(*cctx, 16, {
.enabled = true,
.x = 12 * 8,
.y = 9 * 8,
.tileIdx = 3,
.bpp = static_cast<unsigned>(set.bpp),
});
core::setSprite(*cctx, 17, {
.enabled = true,
.x = 14 * 8,
.y = 9 * 8,
.tileIdx = 5,
.bpp = static_cast<unsigned>(set.bpp),
});
turbine::setKeyEventHandler(tctx, testKeyEventHandler);
return turbine::run(tctx);
}
static int sceneUpdateHandler(turbine::Context&) noexcept {
constexpr auto sleepTime = 16;
if (s_paused) {
return sleepTime;
}
// do stuff
return sleepTime;
}
static void sceneKeyEventHandler(turbine::Context &tctx, turbine::Key key, bool down) noexcept {
if (down) {
if (key == turbine::Key::Alpha_Q) {
turbine::requestShutdown(tctx);
} else if (key == turbine::Key::Alpha_P) {
s_paused = !s_paused;
}
}
}
[[maybe_unused]]
static ox::Error runScene(turbine::Context &tctx) {
constexpr ox::FileAddress SceneAddr = ox::StringLiteral("/Scenes/Chester.nscn");
@ -115,5 +156,5 @@ static ox::Error runScene(turbine::Context &tctx) {
ox::Error run(ox::UniquePtr<ox::FileSystem> &&fs) noexcept {
oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
return runTest(*tctx);
return runTileSheetSetTest(*tctx);
}

View File

@ -1,6 +1,6 @@
add_subdirectory(applib)
add_subdirectory(keel)
add_subdirectory(turbine)
if(TURBINE_BUILD_TYPE STREQUAL "Native")
if(${OLYMPIC_BUILD_STUDIO})
add_subdirectory(studio)
endif()

View File

@ -20,6 +20,8 @@ NewMenu::NewMenu() noexcept {
void NewMenu::open() noexcept {
m_stage = Stage::Opening;
m_selectedType = 0;
m_itemName = "";
m_typeName = "";
}
void NewMenu::close() noexcept {