Compare commits
103 Commits
9cc27f5be9
...
release-d2
Author | SHA1 | Date | |
---|---|---|---|
6d649292e2 | |||
7f56a77e7d | |||
055d64b125 | |||
de9f842640 | |||
200e586768 | |||
f1609519a7 | |||
e452d9db4f | |||
43a87b606e | |||
8acc6244d5 | |||
bd2aeee276 | |||
89fab5cc20 | |||
1c06ea677f | |||
6b948ee069 | |||
72dddcaee5 | |||
7c824e910c | |||
a0c8146396 | |||
d5b232f5d5 | |||
c79fe3be43 | |||
1df4e78084 | |||
ffbdb09c31 | |||
b35a956e4f | |||
d3847caab4 | |||
ae066a914c | |||
67543af806 | |||
6b774ec285 | |||
4b9758f478 | |||
bf12b15fe6 | |||
a7328eb5ef | |||
b52124a0c5 | |||
eae9972f85 | |||
d83e392964 | |||
e2d0a784f1 | |||
f08821422a | |||
6a2954f82b | |||
9c19655ce2 | |||
087c834b25 | |||
79bdbf2eaa | |||
2bdc3def74 | |||
e7a663901a | |||
ffdc0ddb97 | |||
e941781f21 | |||
b869f490c3 | |||
caf8d93c21 | |||
afbf2caf97 | |||
20914eaade | |||
c5f76ff558 | |||
050339ba09 | |||
45ec39f77b | |||
319fbb2600 | |||
f43d97a17e | |||
a0974637a3 | |||
c90a8449be | |||
95dcd6bee7 | |||
5b167fd53b | |||
a701a241eb | |||
9907bb8f1d | |||
9cb6bd4a32 | |||
2dba592a42 | |||
978f2f9c4f | |||
2bad4ee416 | |||
46d1313797 | |||
fcf6f00797 | |||
ef6e3af735 | |||
80d0df2f46 | |||
9db10ec4a1 | |||
c460e0f9e0 | |||
3fa9d132ae | |||
907ead2948 | |||
8526b3fa51 | |||
6de0e882e9 | |||
d29118d783 | |||
8395128efa | |||
77c86b9516 | |||
6d2a20e8bd | |||
056284c857 | |||
3787f6adb2 | |||
ce34d450b9 | |||
8fa47e961d | |||
4e4cec1b64 | |||
9840b6fdee | |||
d0c90c39e0 | |||
7ba6fb3ac5 | |||
8dd6adc554 | |||
0093778f64 | |||
59016ee894 | |||
2b8dbb88b2 | |||
7bc1d90be8 | |||
cfb7d4f861 | |||
7a6e94959c | |||
eb1846278b | |||
00c2a39dba | |||
56f59b29fe | |||
9b11fa4e91 | |||
72e54da017 | |||
58d13a3ad9 | |||
5ae6df7e05 | |||
f71d4d3efd | |||
4b824ddef4 | |||
8b3b8d50d6 | |||
1f2e9917f1 | |||
13ddbd981e | |||
3a781f6704 | |||
30909f85a3 |
19
.gitea/workflows/build.yaml
Normal file
19
.gitea/workflows/build.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
name: Build
|
||||
run-name: ${{ gitea.actor }} build and test
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: nostalgia
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- run: make purge configure-debug
|
||||
- run: make build
|
||||
- run: make test
|
||||
- run: make purge configure-asan
|
||||
- run: make build
|
||||
- run: make test
|
||||
- run: make purge configure-release
|
||||
- run: make build
|
||||
- run: make test
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -2,8 +2,10 @@
|
||||
.clangd
|
||||
.current_build
|
||||
.conanbuild
|
||||
.idea
|
||||
.mypy_cache
|
||||
.stfolder
|
||||
.stignore
|
||||
scripts/__pycache__
|
||||
CMakeLists.txt.user
|
||||
ROM.oxfs
|
||||
@ -13,8 +15,7 @@ compile_commands.json
|
||||
dist
|
||||
graph_info.json
|
||||
imgui.ini
|
||||
nostalgia.gba
|
||||
nostalgia.sav
|
||||
nostalgia_media.oxfs
|
||||
*.gba
|
||||
*.sav
|
||||
studio_state.json
|
||||
tags
|
||||
|
@ -1,24 +0,0 @@
|
||||
# This file is a template, and might need editing before it works on your project.
|
||||
# You can copy and paste this template into a new `.gitlab-ci.yml` file.
|
||||
# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
|
||||
#
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/C++.gitlab-ci.yml
|
||||
|
||||
# use the official gcc image, based on debian
|
||||
# can use versions as well, like gcc:5.2
|
||||
# see https://hub.docker.com/_/gcc/
|
||||
|
||||
image: gcc
|
||||
|
||||
build:
|
||||
stage: build
|
||||
variables:
|
||||
OX_NODEBUG: 1
|
||||
before_script:
|
||||
- apt update && apt -y install make cmake ninja-build pkg-config xorg-dev libgtk-3-dev python3 python3-mypy
|
||||
script:
|
||||
- make purge configure-release test install
|
||||
- make purge configure-asan test install
|
1
.lldbinit
Normal file
1
.lldbinit
Normal file
@ -0,0 +1 @@
|
||||
type summary add --summary-string "${var.m_buff.m_items}" ox::String
|
@ -11,16 +11,11 @@ endif()
|
||||
|
||||
include(deps/buildcore/base.cmake)
|
||||
|
||||
set(NOSTALGIA_BUILD_PLAYER ON CACHE BOOL "Build Player")
|
||||
set(NOSTALGIA_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(TURBINE_BUILD_TYPE "GBA")
|
||||
include(deps/gbabuildcore/base.cmake)
|
||||
else()
|
||||
set(TURBINE_BUILD_TYPE "Native")
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
||||
@ -30,22 +25,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_MACOSX_RPATH OFF)
|
||||
set(CMAKE_INSTALL_NAME_DIR "@executable_path/../Library/nostalgia")
|
||||
set(NOSTALGIA_DIST_BIN nostalgia-studio.app/Contents/MacOS)
|
||||
set(NOSTALGIA_DIST_LIB nostalgia-studio.app/Contents/Library)
|
||||
set(NOSTALGIA_DIST_MODULE nostalgia-studio.app/Contents/Plugins)
|
||||
set(NOSTALGIA_DIST_RESOURCES nostalgia-studio.app/Contents/Resources)
|
||||
set(NOSTALGIA_DIST_MAC_APP_CONTENTS nostalgia-studio.app/Contents)
|
||||
else()
|
||||
if(UNIX)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
endif()
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set(NOSTALGIA_DIST_BIN bin)
|
||||
set(NOSTALGIA_DIST_LIB lib)
|
||||
set(NOSTALGIA_DIST_MODULE lib)
|
||||
set(NOSTALGIA_DIST_RESOURCES share)
|
||||
endif()
|
||||
|
||||
add_subdirectory(deps/ox)
|
||||
@ -57,7 +42,6 @@ add_subdirectory(deps/teagba)
|
||||
if(NOT BUILDCORE_TARGET STREQUAL "gba")
|
||||
include_directories(
|
||||
SYSTEM
|
||||
deps/glfw/deps
|
||||
deps/glfw/include
|
||||
deps/imgui
|
||||
deps/imgui/backends
|
||||
|
@ -31,7 +31,6 @@ RUN dnf install -y clang \
|
||||
python3-pip \
|
||||
libglvnd-devel \
|
||||
gtk3-devel
|
||||
RUN pip install conan
|
||||
|
||||
###############################################################################
|
||||
# Install devkitARM
|
||||
|
5
Makefile
5
Makefile
@ -1,12 +1,13 @@
|
||||
BC_VAR_PROJECT_NAME=nostalgia
|
||||
BC_VAR_PROJECT_NAME_CAP=Nostalgia
|
||||
BUILDCORE_PATH=deps/buildcore
|
||||
include ${BUILDCORE_PATH}/base.mk
|
||||
|
||||
ifeq ($(BC_VAR_OS),darwin)
|
||||
NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME}-studio.app/Contents/MacOS/${BC_VAR_PROJECT_NAME}-studio
|
||||
NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio.app/Contents/MacOS/${BC_VAR_PROJECT_NAME_CAP}Studio
|
||||
MGBA=/Applications/mGBA.app/Contents/MacOS/mGBA
|
||||
else
|
||||
NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME}-studio
|
||||
NOSTALGIA_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio
|
||||
MGBA=mgba-qt
|
||||
endif
|
||||
|
||||
|
@ -15,6 +15,7 @@ probably differ), install the following additional packages:
|
||||
* pkg-config
|
||||
* xorg-dev
|
||||
* libgtk-3-dev
|
||||
* python3-mypy
|
||||
|
||||
## Build
|
||||
|
||||
|
4
deps/buildcore/scripts/setup-build.py
vendored
4
deps/buildcore/scripts/setup-build.py
vendored
@ -99,7 +99,9 @@ def main() -> int:
|
||||
if platform.system() == 'Windows':
|
||||
cmake_cmd.append('-A x64')
|
||||
|
||||
subprocess.run(cmake_cmd)
|
||||
cmake_err = subprocess.run(cmake_cmd).returncode
|
||||
if cmake_err != 0:
|
||||
return cmake_err
|
||||
|
||||
util.mkdir_p('dist')
|
||||
if int(args.current_build) != 0:
|
||||
|
5
deps/imgui/CMakeLists.txt
vendored
5
deps/imgui/CMakeLists.txt
vendored
@ -15,3 +15,8 @@ add_library(
|
||||
backends/imgui_impl_glfw.cpp
|
||||
backends/imgui_impl_opengl3.cpp
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
imgui SYSTEM PUBLIC
|
||||
.
|
||||
)
|
2
deps/nfde/CMakeLists.txt
vendored
2
deps/nfde/CMakeLists.txt
vendored
@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(nativefiledialog-extended)
|
||||
|
||||
if(NOT MSVC)
|
||||
|
9
deps/ox/src/ox/claw/read.cpp
vendored
9
deps/ox/src/ox/claw/read.cpp
vendored
@ -72,9 +72,12 @@ Result<Buffer> stripClawHeader(const ox::Buffer &buff) noexcept {
|
||||
return stripClawHeader(buff.data(), buff.size());
|
||||
}
|
||||
|
||||
Result<ModelObject> readClaw(TypeStore *ts, const char *buff, std::size_t buffSz) noexcept {
|
||||
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));
|
||||
auto const [t, tdErr] = ts.getLoad(header.typeName, header.typeVersion, header.typeParams);
|
||||
if (tdErr) {
|
||||
return OxError(3, "Could not load type descriptor");
|
||||
}
|
||||
ModelObject obj;
|
||||
oxReturnError(obj.setType(t));
|
||||
switch (header.fmt) {
|
||||
@ -103,7 +106,7 @@ Result<ModelObject> readClaw(TypeStore *ts, const char *buff, std::size_t buffSz
|
||||
return OxError(1);
|
||||
}
|
||||
|
||||
Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept {
|
||||
Result<ModelObject> readClaw(TypeStore &ts, const Buffer &buff) noexcept {
|
||||
return readClaw(ts, buff.data(), buff.size());
|
||||
}
|
||||
|
||||
|
4
deps/ox/src/ox/claw/read.hpp
vendored
4
deps/ox/src/ox/claw/read.hpp
vendored
@ -88,8 +88,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 char *buff, std::size_t buffSz) noexcept;
|
||||
|
||||
Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept;
|
||||
Result<ModelObject> readClaw(TypeStore &ts, const Buffer &buff) noexcept;
|
||||
|
||||
}
|
||||
|
6
deps/ox/src/ox/fs/CMakeLists.txt
vendored
6
deps/ox/src/ox/fs/CMakeLists.txt
vendored
@ -16,12 +16,6 @@ if(NOT MSVC)
|
||||
endif()
|
||||
|
||||
if(NOT OX_BARE_METAL)
|
||||
if(NOT APPLE AND NOT MSVC AND NOT ${OX_OS_FREEBSD})
|
||||
target_link_libraries(
|
||||
OxFS PUBLIC
|
||||
stdc++fs
|
||||
)
|
||||
endif()
|
||||
set_property(
|
||||
TARGET
|
||||
OxFS
|
||||
|
@ -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))));
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ Result<FileStat> PassThroughFS::statPath(CRStringView path) const noexcept {
|
||||
oxTracef("ox.fs.PassThroughFS.statInode", "{} {}", ec.message(), path);
|
||||
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);
|
||||
oxTracef("ox.fs.PassThroughFS.statInode.size", "{} {}", path, size);
|
||||
oxReturnError(OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: stat failed"));
|
||||
return FileStat{0, 0, size, type};
|
||||
}
|
||||
|
7
deps/ox/src/ox/mc/intops.hpp
vendored
7
deps/ox/src/ox/mc/intops.hpp
vendored
@ -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;
|
||||
}
|
||||
|
55
deps/ox/src/ox/mc/read.hpp
vendored
55
deps/ox/src/ox/mc/read.hpp
vendored
@ -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>
|
||||
|
3
deps/ox/src/ox/mc/test/tests.cpp
vendored
3
deps/ox/src/ox/mc/test/tests.cpp
vendored
@ -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.");
|
||||
|
44
deps/ox/src/ox/mc/write.hpp
vendored
44
deps/ox/src/ox/mc/write.hpp
vendored
@ -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));
|
||||
}
|
||||
|
6
deps/ox/src/ox/model/descwrite.hpp
vendored
6
deps/ox/src/ox/model/descwrite.hpp
vendored
@ -219,9 +219,11 @@ template<typename T>
|
||||
constexpr Error TypeDescWriter::field(CRStringView name, const T *val) noexcept {
|
||||
if (m_type) {
|
||||
if constexpr(isVector_v<T> || isArray_v<T>) {
|
||||
return field(name, val->data(), 0, detail::buildSubscriptStack(val));
|
||||
typename T::value_type *data = nullptr;
|
||||
return field(name, data, 0, detail::buildSubscriptStack(val));
|
||||
} else if constexpr(isSmartPtr_v<T>) {
|
||||
return field(name, val->get(), 0, detail::buildSubscriptStack(val));
|
||||
typename T::value_type *data = nullptr;
|
||||
return field(name, data, 0, detail::buildSubscriptStack(val));
|
||||
} else if constexpr(is_pointer_v<T>) {
|
||||
return field(name, val, 0, detail::buildSubscriptStack(val));
|
||||
} else {
|
||||
|
12
deps/ox/src/ox/model/modelhandleradaptor.hpp
vendored
12
deps/ox/src/ox/model/modelhandleradaptor.hpp
vendored
@ -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 {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
16
deps/ox/src/ox/model/modelvalue.hpp
vendored
16
deps/ox/src/ox/model/modelvalue.hpp
vendored
@ -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;
|
||||
|
2
deps/ox/src/ox/oc/read.hpp
vendored
2
deps/ox/src/ox/oc/read.hpp
vendored
@ -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);
|
||||
|
30
deps/ox/src/ox/oc/write.hpp
vendored
30
deps/ox/src/ox/oc/write.hpp
vendored
@ -37,7 +37,7 @@ class OrganicClawWriter {
|
||||
explicit OrganicClawWriter(Json::Value json, int unionIdx = -1) noexcept;
|
||||
|
||||
Error field(const char *key, const int8_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@ -45,7 +45,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const int16_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@ -53,7 +53,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const int32_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@ -61,7 +61,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const int64_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@ -70,7 +70,7 @@ class OrganicClawWriter {
|
||||
|
||||
|
||||
Error field(const char *key, const uint8_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@ -78,7 +78,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint16_t *val) noexcept {
|
||||
if (targetValid() && *val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@ -86,7 +86,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint32_t *val) noexcept {
|
||||
if (targetValid() && *val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@ -94,15 +94,15 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint64_t *val) noexcept {
|
||||
if (targetValid() && *val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return OxError(0);
|
||||
}
|
||||
|
||||
Error field(const char *key, const bool *val) noexcept {
|
||||
if (targetValid() && *val) {
|
||||
Error field(char const*key, bool const*val) noexcept {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@ -110,10 +110,10 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
template<typename U, bool force = true>
|
||||
Error field(const char*, UnionView<U, force> val) noexcept;
|
||||
Error field(char const*, UnionView<U, force> val) noexcept;
|
||||
|
||||
template<typename T>
|
||||
Error field(const char *key, const HashMap<String, T> *val) noexcept {
|
||||
Error field(char const*key, HashMap<String, T> const*val) noexcept {
|
||||
if (targetValid()) {
|
||||
const auto &keys = val->keys();
|
||||
OrganicClawWriter w;
|
||||
@ -132,7 +132,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
template<std::size_t L>
|
||||
Error field(const char *key, const BString<L> *val) noexcept {
|
||||
Error field(char const*key, BString<L> const*val) noexcept {
|
||||
if (targetValid() && val->len()) {
|
||||
value(key) = val->c_str();
|
||||
}
|
||||
@ -141,7 +141,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
template<std::size_t L>
|
||||
Error field(const char *key, const BasicString<L> *val) noexcept {
|
||||
Error field(char const*key, BasicString<L> const*val) noexcept {
|
||||
if (targetValid() && val->len()) {
|
||||
value(key) = val->c_str();
|
||||
}
|
||||
@ -199,7 +199,7 @@ Error OrganicClawWriter::field(const char *key, const T *val, std::size_t len) n
|
||||
OrganicClawWriter w((Json::Value(Json::arrayValue)));
|
||||
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{&w};
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
oxReturnError(handler.field("", &val[i]));
|
||||
oxReturnError(handler.field({}, &val[i]));
|
||||
}
|
||||
value(key) = w.m_json;
|
||||
}
|
||||
|
2
deps/ox/src/ox/preloader/preloader.hpp
vendored
2
deps/ox/src/ox/preloader/preloader.hpp
vendored
@ -247,7 +247,7 @@ constexpr ox::Error Preloader<PlatSpec>::field(CRStringView, const T **val, std:
|
||||
template<typename PlatSpec>
|
||||
constexpr ox::Result<std::size_t> Preloader<PlatSpec>::startAlloc(std::size_t sz) noexcept {
|
||||
oxRequire(a, ox::allocate(&m_writer, sz));
|
||||
m_allocStack.emplace_back(static_cast<PlatSpec::PtrType>(m_writer.tellp()));
|
||||
m_allocStack.emplace_back(static_cast<typename PlatSpec::PtrType>(m_writer.tellp()));
|
||||
oxReturnError(m_writer.seekp(a));
|
||||
return a;
|
||||
}
|
||||
|
2
deps/ox/src/ox/preloader/sizecatcher.hpp
vendored
2
deps/ox/src/ox/preloader/sizecatcher.hpp
vendored
@ -107,7 +107,7 @@ constexpr std::size_t sizeOf(const T *t) noexcept {
|
||||
if constexpr(ox::is_integral_v<T>) {
|
||||
return sizeof(T);
|
||||
} else if constexpr(ox::is_pointer_v<T>) {
|
||||
return sizeof(PlatSpec::PtrType);
|
||||
return sizeof(typename PlatSpec::PtrType);
|
||||
} else {
|
||||
SizeCatcher<PlatSpec> sc;
|
||||
const auto err = model(sc.interface(), t);
|
||||
|
84
deps/ox/src/ox/std/error.hpp
vendored
84
deps/ox/src/ox/std/error.hpp
vendored
@ -167,28 +167,14 @@ struct [[nodiscard]] Result {
|
||||
|
||||
constexpr Error copyTo(type &val) const& noexcept {
|
||||
if (!error) [[likely]] {
|
||||
*val = value;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
constexpr Error copyTo(type &val) const && noexcept {
|
||||
if (!error) [[likely]] {
|
||||
*val = value;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
constexpr Error copyTo(type &val) & noexcept {
|
||||
if (!error) [[likely]] {
|
||||
*val = value;
|
||||
val = value;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
constexpr Error copyTo(type &val) && noexcept {
|
||||
if (!error) [[likely]] {
|
||||
*val = std::move(value);
|
||||
val = std::move(value);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@ -200,6 +186,7 @@ struct [[nodiscard]] Result {
|
||||
return error;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &unwrap() & noexcept {
|
||||
if (error) {
|
||||
oxPanic(error, "Failed unwrap");
|
||||
@ -207,6 +194,7 @@ struct [[nodiscard]] Result {
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &&unwrap() && noexcept {
|
||||
if (error) {
|
||||
oxPanic(error, "Failed unwrap");
|
||||
@ -214,6 +202,7 @@ struct [[nodiscard]] Result {
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T const&unwrap() const & noexcept {
|
||||
if (error) [[unlikely]] {
|
||||
oxPanic(error, "Failed unwrap");
|
||||
@ -221,6 +210,7 @@ struct [[nodiscard]] Result {
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &unwrapThrow() & {
|
||||
if (error) {
|
||||
throw ox::Exception(error);
|
||||
@ -228,6 +218,7 @@ struct [[nodiscard]] Result {
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T &&unwrapThrow() && {
|
||||
if (error) {
|
||||
throw ox::Exception(error);
|
||||
@ -235,6 +226,7 @@ struct [[nodiscard]] Result {
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr T const&unwrapThrow() const & {
|
||||
if (error) {
|
||||
throw ox::Exception(error);
|
||||
@ -243,13 +235,69 @@ struct [[nodiscard]] Result {
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
constexpr ox::Result<U> to(const auto &f) noexcept {
|
||||
constexpr ox::Result<U> to(auto const&f) & noexcept {
|
||||
if (error) [[unlikely]] {
|
||||
return OxError(1);
|
||||
return error;
|
||||
}
|
||||
return f(value);
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
constexpr ox::Result<U> to(auto const&f) && noexcept {
|
||||
if (error) [[unlikely]] {
|
||||
return error;
|
||||
}
|
||||
return f(std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T &&alt) const& noexcept {
|
||||
if (error) {
|
||||
return std::move(alt);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T &&alt) && noexcept {
|
||||
if (error) {
|
||||
return std::move(alt);
|
||||
}
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T const&alt) const& noexcept {
|
||||
if (error) {
|
||||
return alt;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T const&alt) && noexcept {
|
||||
if (error) {
|
||||
return alt;
|
||||
}
|
||||
return std::move(value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
13
deps/ox/src/ox/std/memory.hpp
vendored
13
deps/ox/src/ox/std/memory.hpp
vendored
@ -39,6 +39,7 @@ constexpr T *construct_at(T *p, Args &&...args ) {
|
||||
|
||||
#endif
|
||||
|
||||
#include "error.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
|
||||
@ -74,6 +75,7 @@ class SharedPtr {
|
||||
int *m_refCnt = nullptr;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
explicit constexpr SharedPtr(T *t = nullptr) noexcept: m_t(t), m_refCnt(new int) {
|
||||
}
|
||||
|
||||
@ -183,6 +185,7 @@ class UniquePtr {
|
||||
T *m_t = nullptr;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
explicit constexpr UniquePtr(T *t = nullptr) noexcept: m_t(t) {
|
||||
}
|
||||
|
||||
@ -288,4 +291,14 @@ constexpr auto make_unique(Args&&... args) {
|
||||
return UniquePtr<U>(new T(ox::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename T, typename U = T, typename ...Args>
|
||||
[[nodiscard]]
|
||||
constexpr Result<UniquePtr<U>> make_unique_catch(Args&&... args) noexcept {
|
||||
try {
|
||||
return UniquePtr<U>(new T(ox::forward<Args>(args)...));
|
||||
} catch (ox::Exception const&ex) {
|
||||
return ex.toError();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
60
deps/ox/src/ox/std/optional.hpp
vendored
60
deps/ox/src/ox/std/optional.hpp
vendored
@ -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) {
|
||||
@ -64,20 +64,68 @@ class Optional {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T &&alt) const& noexcept {
|
||||
if (!m_ptr) {
|
||||
return std::move(alt);
|
||||
}
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T &&alt) && noexcept {
|
||||
if (!m_ptr) {
|
||||
return std::move(alt);
|
||||
}
|
||||
return std::move(*m_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T const&alt) const& noexcept {
|
||||
if (!m_ptr) {
|
||||
return alt;
|
||||
}
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns parameter alt if Result contains an error.
|
||||
* @param alt
|
||||
* @return value of Result or alt
|
||||
*/
|
||||
constexpr T or_value(T const&alt) && noexcept {
|
||||
if (!m_ptr) {
|
||||
return alt;
|
||||
}
|
||||
return std::move(*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 {
|
||||
return m_ptr;
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr const T &&operator*() const && noexcept {
|
||||
return m_ptr;
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
constexpr T *operator->() noexcept {
|
||||
@ -163,7 +211,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)...);
|
||||
}
|
||||
|
||||
|
6
deps/ox/src/ox/std/utility.hpp
vendored
6
deps/ox/src/ox/std/utility.hpp
vendored
@ -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);
|
||||
|
2
deps/teagba/CMakeLists.txt
vendored
2
deps/teagba/CMakeLists.txt
vendored
@ -1,3 +1,5 @@
|
||||
project(TeaGBA ASM CXX)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
install(
|
||||
|
6
deps/teagba/include/teagba/addresses.hpp
vendored
6
deps/teagba/include/teagba/addresses.hpp
vendored
@ -49,7 +49,7 @@ using BgCtl = uint16_t;
|
||||
#define REG_BG3CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000e)
|
||||
|
||||
[[nodiscard]]
|
||||
inline auto ®BgCtl(uintptr_t bgIdx) noexcept {
|
||||
inline volatile BgCtl ®BgCtl(uintptr_t bgIdx) noexcept {
|
||||
return *reinterpret_cast<volatile BgCtl*>(0x0400'0008 + 2 * bgIdx);
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ inline auto ®BgCtl(uintptr_t bgIdx) noexcept {
|
||||
#define REG_BG3HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001c)
|
||||
|
||||
[[nodiscard]]
|
||||
inline volatile auto ®BgHofs(auto bgIdx) noexcept {
|
||||
inline volatile uint32_t ®BgHofs(auto bgIdx) noexcept {
|
||||
return *reinterpret_cast<volatile uint32_t*>(0x0400'0010 + 4 * bgIdx);
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ inline volatile auto ®BgHofs(auto bgIdx) noexcept {
|
||||
#define REG_BG3VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001e)
|
||||
|
||||
[[nodiscard]]
|
||||
inline volatile auto ®BgVofs(auto bgIdx) noexcept {
|
||||
inline volatile uint32_t ®BgVofs(auto bgIdx) noexcept {
|
||||
return *reinterpret_cast<volatile uint32_t*>(0x0400'0012 + 4 * bgIdx);
|
||||
}
|
||||
|
||||
|
3
deps/teagba/include/teagba/gfx.hpp
vendored
3
deps/teagba/include/teagba/gfx.hpp
vendored
@ -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;
|
||||
|
35
deps/teagba/include/teagba/registers.hpp
vendored
35
deps/teagba/include/teagba/registers.hpp
vendored
@ -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 = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) {
|
||||
cb(bgCtl);
|
||||
cb(*bgCtl);
|
||||
}
|
||||
}
|
||||
|
||||
|
2
deps/teagba/src/CMakeLists.txt
vendored
2
deps/teagba/src/CMakeLists.txt
vendored
@ -6,7 +6,7 @@ add_library(
|
||||
gfx.cpp
|
||||
)
|
||||
|
||||
if(TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||
if(BUILDCORE_TARGET STREQUAL "gba")
|
||||
set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm)
|
||||
target_sources(
|
||||
TeaGBA PRIVATE
|
||||
|
22
deps/teagba/src/gfx.cpp
vendored
22
deps/teagba/src/gfx.cpp
vendored
@ -10,30 +10,22 @@
|
||||
|
||||
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];
|
||||
MEM_OAM[oa.idx] = *reinterpret_cast<const uint64_t*>(&oa);
|
||||
for (auto const&oa : g_spriteBuffer) {
|
||||
MEM_OAM[oa.idx] = std::bit_cast<uint64_t>(oa);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,14 +20,6 @@ All components have a platform indicator next to them:
|
||||
(-G) - GBA
|
||||
(P-) - PC
|
||||
|
||||
* GlUtils - OpenGL helpers (P-)
|
||||
* Keel - asset management system (PG)
|
||||
* Turbine - platform abstraction and user I/O (PG)
|
||||
* gba - GBA implementation (PG)
|
||||
* glfw - GLFW implementation (P-)
|
||||
* Studio - where most of the studio code lives as library (P-)
|
||||
* applib - used for per project studio executables
|
||||
* modlib - used for studio modules to interact with studio
|
||||
* Nostalgia
|
||||
* modules
|
||||
* core - graphics system for Nostalgia (PG)
|
||||
@ -42,6 +34,15 @@ All components have a platform indicator next to them:
|
||||
* studio - makes the games (P-)
|
||||
* tools - command line tools (P-)
|
||||
* pack - packs a studio project directory into an OxFS file (P-)
|
||||
* Olympic
|
||||
* Applib - Library for creating apps as libraries that injects Keel and Studio modules
|
||||
* Keel - asset management system (PG)
|
||||
* Studio - where most of the studio code lives as library (P-)
|
||||
* applib - used for per project studio executables
|
||||
* modlib - used for studio modules to interact with studio
|
||||
* Turbine - platform abstraction and user I/O (PG)
|
||||
* gba - GBA implementation (PG)
|
||||
* glfw - GLFW implementation (P-)
|
||||
* deps - project dependencies
|
||||
* Ox - Library of things useful for portable bare metal and userland code. Not really that external...
|
||||
* clargs - Command Line Args processing (PG)
|
||||
@ -54,9 +55,26 @@ All components have a platform indicator next to them:
|
||||
* model - Data structure modelling (PG)
|
||||
* preloader - library for handling preloading of data (PG)
|
||||
* std - Standard-ish Library with a lot missing and some things added (PG)
|
||||
* GlUtils - OpenGL helpers (P-)
|
||||
* teagba - GBA assembly startup code (mostly pulled from devkitPro under MPL
|
||||
2.0), and custom GBA hardware interop code (-G)
|
||||
|
||||
## Platform Notes
|
||||
|
||||
### GBA
|
||||
|
||||
The GBA has two major resources for learning about its hardware:
|
||||
|
||||
* [Tonc](https://www.coranac.com/tonc/text/toc.htm) - This is basically a short
|
||||
book on the GBA and low level development.
|
||||
* [GBATEK](https://rust-console.github.io/gbatek-gbaonly/) - This is a more
|
||||
concise resource that mostly tells about memory ranges and registers.
|
||||
|
||||
#### Graphics
|
||||
|
||||
* Background Palette: 256 colors
|
||||
* Sprite Palette: 256 colors
|
||||
|
||||
## Code Base Conventions
|
||||
|
||||
### Formatting
|
||||
|
45
jenkins/gba/Jenkinsfile
vendored
45
jenkins/gba/Jenkinsfile
vendored
@ -1,45 +0,0 @@
|
||||
pipeline {
|
||||
agent {
|
||||
label 'gba'
|
||||
}
|
||||
stages {
|
||||
stage('Environment') {
|
||||
steps {
|
||||
load 'jenkins/shared/env.gy'
|
||||
sh 'make conan-config'
|
||||
sh 'make conan'
|
||||
}
|
||||
}
|
||||
stage('Build Tools Debug') {
|
||||
steps {
|
||||
sh 'make purge configure-debug'
|
||||
sh 'make install'
|
||||
}
|
||||
}
|
||||
stage('Build GBA Debug') {
|
||||
steps {
|
||||
sh 'make configure-gba-debug'
|
||||
sh 'make'
|
||||
sh 'make pkg-gba'
|
||||
}
|
||||
}
|
||||
stage('Build Tools Release') {
|
||||
steps {
|
||||
sh 'make purge configure-release'
|
||||
sh 'make install'
|
||||
}
|
||||
}
|
||||
stage('Build GBA Release') {
|
||||
steps {
|
||||
sh 'make configure-gba'
|
||||
sh 'make'
|
||||
sh 'make pkg-gba'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
archiveArtifacts artifacts: 'nostalgia.gba', fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
47
jenkins/linux/Jenkinsfile
vendored
47
jenkins/linux/Jenkinsfile
vendored
@ -1,47 +0,0 @@
|
||||
pipeline {
|
||||
agent {
|
||||
label 'linux-x86_64'
|
||||
}
|
||||
stages {
|
||||
stage('Environment') {
|
||||
steps {
|
||||
load 'jenkins/shared/env.gy'
|
||||
sh 'make conan-config'
|
||||
sh 'make conan'
|
||||
}
|
||||
}
|
||||
stage('Build Asan') {
|
||||
steps {
|
||||
sh 'make purge configure-asan'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Asan') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
stage('Build Debug') {
|
||||
steps {
|
||||
sh 'make purge configure-debug'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Debug') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
stage('Build Release') {
|
||||
steps {
|
||||
sh 'make purge configure-release'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Release') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
jenkins/mac/Jenkinsfile
vendored
47
jenkins/mac/Jenkinsfile
vendored
@ -1,47 +0,0 @@
|
||||
pipeline {
|
||||
agent {
|
||||
label 'mac-x86_64'
|
||||
}
|
||||
stages {
|
||||
stage('Environment') {
|
||||
steps {
|
||||
load 'jenkins/shared/env.gy'
|
||||
sh 'make conan-config'
|
||||
sh 'make conan'
|
||||
}
|
||||
}
|
||||
stage('Build Asan') {
|
||||
steps {
|
||||
sh 'make purge configure-asan'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Asan') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
stage('Build Debug') {
|
||||
steps {
|
||||
sh 'make purge configure-debug'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Debug') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
stage('Build Release') {
|
||||
steps {
|
||||
sh 'make purge configure-release'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Release') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
env.OX_NODEBUG = 1
|
||||
env.BUILDCORE_SUPPRESS_CCACHE = 1
|
@ -0,0 +1,27 @@
|
||||
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||
"fieldList" :
|
||||
[
|
||||
{
|
||||
"fieldName" : "bpp",
|
||||
"typeId" : "B.int8;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "defaultPalette",
|
||||
"typeId" : "net.drinkingtea.ox.FileAddress;1"
|
||||
},
|
||||
{
|
||||
"fieldName" : "pixels",
|
||||
"subscriptLevels" : 1,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint8;0"
|
||||
}
|
||||
],
|
||||
"primitiveType" : 5,
|
||||
"typeName" : "net.drinkingtea.nostalgia.core.CompactTileSheet",
|
||||
"typeVersion" : 1
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||
"fieldList" :
|
||||
[
|
||||
{
|
||||
"fieldName" : "bpp",
|
||||
"typeId" : "B.int8;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "rows",
|
||||
"typeId" : "B.int32;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "columns",
|
||||
"typeId" : "B.int32;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "defaultPalette",
|
||||
"typeId" : "net.drinkingtea.ox.FileAddress;1"
|
||||
},
|
||||
{
|
||||
"fieldName" : "pal",
|
||||
"typeId" : "net.drinkingtea.nostalgia.core.Palette;1"
|
||||
},
|
||||
{
|
||||
"fieldName" : "pixels",
|
||||
"subscriptLevels" : 1,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint8;0"
|
||||
}
|
||||
],
|
||||
"primitiveType" : 5,
|
||||
"typeName" : "net.drinkingtea.nostalgia.core.NostalgiaGraphic",
|
||||
"typeVersion" : 1
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||
"fieldList" :
|
||||
[
|
||||
{
|
||||
"fieldName" : "id",
|
||||
"typeId" : "B.int32;0"
|
||||
},
|
||||
{
|
||||
"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;4"
|
||||
},
|
||||
{
|
||||
"fieldName" : "pixels",
|
||||
"subscriptLevels" : 1,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint8;0"
|
||||
}
|
||||
],
|
||||
"primitiveType" : 5,
|
||||
"typeName" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet",
|
||||
"typeVersion" : 4
|
||||
}
|
@ -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;4"
|
||||
}
|
||||
],
|
||||
"primitiveType" : 5,
|
||||
"typeName" : "net.drinkingtea.nostalgia.core.TileSheet",
|
||||
"typeVersion" : 4
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||
"fieldList" :
|
||||
[
|
||||
{
|
||||
"fieldName" : "tilesheet",
|
||||
"typeId" : "net.drinkingtea.ox.BasicString#8#;1"
|
||||
},
|
||||
{
|
||||
"fieldName" : "palettes",
|
||||
"subscriptLevels" : 1,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "net.drinkingtea.ox.BasicString#8#;1"
|
||||
},
|
||||
{
|
||||
"fieldName" : "tiles",
|
||||
"subscriptLevels" : 3,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
},
|
||||
{
|
||||
"subscriptType" : 4
|
||||
},
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "net.drinkingtea.nostalgia.scene.TileDoc;1"
|
||||
}
|
||||
],
|
||||
"preloadable" : true,
|
||||
"primitiveType" : 5,
|
||||
"typeName" : "net.drinkingtea.nostalgia.scene.SceneDoc",
|
||||
"typeVersion" : 1
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||
"fieldList" :
|
||||
[
|
||||
{
|
||||
"fieldName" : "tilesheet",
|
||||
"typeId" : "net.drinkingtea.ox.FileAddress;1"
|
||||
},
|
||||
{
|
||||
"fieldName" : "palettes",
|
||||
"subscriptLevels" : 1,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "net.drinkingtea.ox.FileAddress;1"
|
||||
},
|
||||
{
|
||||
"fieldName" : "columns",
|
||||
"subscriptLevels" : 1,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint16;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "rows",
|
||||
"subscriptLevels" : 1,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint16;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "tileMapIdx",
|
||||
"subscriptLevels" : 2,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
},
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint16;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "tileType",
|
||||
"subscriptLevels" : 2,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
},
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint8;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "layerAttachments",
|
||||
"subscriptLevels" : 2,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"subscriptType" : 4
|
||||
},
|
||||
{
|
||||
"subscriptType" : 4
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint8;0"
|
||||
}
|
||||
],
|
||||
"preloadable" : true,
|
||||
"primitiveType" : 5,
|
||||
"typeName" : "net.drinkingtea.nostalgia.scene.SceneStatic",
|
||||
"typeVersion" : 1
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
O1;net.drinkingtea.ox.TypeDescriptor;1;{
|
||||
"fieldList" :
|
||||
[
|
||||
{
|
||||
"fieldName" : "subsheet_id",
|
||||
"typeId" : "B.int32;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "subsheet_path",
|
||||
"typeId" : "net.drinkingtea.ox.BasicString#8#;1"
|
||||
},
|
||||
{
|
||||
"fieldName" : "type",
|
||||
"typeId" : "B.uint8;0"
|
||||
},
|
||||
{
|
||||
"fieldName" : "layer_attachments",
|
||||
"subscriptLevels" : 1,
|
||||
"subscriptStack" :
|
||||
[
|
||||
{
|
||||
"length" : 4,
|
||||
"subscriptType" : 3
|
||||
}
|
||||
],
|
||||
"typeId" : "B.uint8;0"
|
||||
}
|
||||
],
|
||||
"preloadable" : true,
|
||||
"primitiveType" : 5,
|
||||
"typeName" : "net.drinkingtea.nostalgia.scene.TileDoc",
|
||||
"typeVersion" : 1
|
||||
}
|
@ -1 +1 @@
|
||||
K1;14fc3dd8-42ff-4bf9-81f1-a010cc5ac251;M2;net.drinkingtea.nostalgia.core.Palette;1;<EFBFBD><EFBFBD><07><>
|
||||
K1;14fc3dd8-42ff-4bf9-81f1-a010cc5ac251;M2;net.drinkingtea.nostalgia.core.Palette;1;<EFBFBD><EFBFBD><07><>>
|
Binary file not shown.
1
sample_project/TileSheets/AB.ng
Normal file
1
sample_project/TileSheets/AB.ng
Normal file
@ -0,0 +1 @@
|
||||
K1;e41abcbc-0146-4f74-eaa5-5891d21e5de4;M2;net.drinkingtea.nostalgia.core.TileSheet;3;
|
1
sample_project/TileSheets/CD.ng
Normal file
1
sample_project/TileSheets/CD.ng
Normal file
@ -0,0 +1 @@
|
||||
K1;24d20460-0a28-4642-d846-1a712681cfab;M2;net.drinkingtea.nostalgia.core.TileSheet;3;
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -20,6 +20,8 @@ host_env = f'{os}-{arch}'
|
||||
# get current build type
|
||||
with open(".current_build", "r") as f:
|
||||
current_build = f.readlines()[0]
|
||||
if current_build[len(current_build) - 1] == '\n':
|
||||
current_build = current_build[:len(current_build) - 1]
|
||||
|
||||
project_dir = sys.argv[1]
|
||||
project_name = sys.argv[2]
|
||||
|
@ -1,15 +1,43 @@
|
||||
|
||||
project(nostalgia CXX)
|
||||
|
||||
#project packages
|
||||
|
||||
set(NOSTALGIA_BUILD_PLAYER ON CACHE BOOL "Build Player")
|
||||
set(NOSTALGIA_BUILD_STUDIO ON CACHE BOOL "Build Studio")
|
||||
set(NOSTALGIA_BUILD_STUDIO_APP ON CACHE BOOL "Build Studio App")
|
||||
|
||||
if(BUILDCORE_TARGET STREQUAL "gba")
|
||||
set(NOSTALGIA_BUILD_STUDIO OFF)
|
||||
endif()
|
||||
|
||||
if(NOT NOSTALGIA_BUILD_STUDIO)
|
||||
set(NOSTALGIA_BUILD_STUDIO_APP OFF)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_INSTALL_NAME_DIR "@executable_path/../Library/nostalgia")
|
||||
set(NOSTALGIA_DIST_BIN NostalgiaStudio.app/Contents/MacOS)
|
||||
set(NOSTALGIA_DIST_LIB NostalgiaStudio.app/Contents/Library)
|
||||
set(NOSTALGIA_DIST_MODULE NostalgiaStudio.app/Contents/Plugins)
|
||||
set(NOSTALGIA_DIST_RESOURCES NostalgiaStudio.app/Contents/Resources)
|
||||
set(NOSTALGIA_DIST_MAC_APP_CONTENTS NostalgiaStudio.app/Contents)
|
||||
else()
|
||||
set(NOSTALGIA_DIST_BIN bin)
|
||||
set(NOSTALGIA_DIST_LIB lib)
|
||||
set(NOSTALGIA_DIST_MODULE lib)
|
||||
set(NOSTALGIA_DIST_RESOURCES share)
|
||||
endif()
|
||||
|
||||
add_subdirectory(modules)
|
||||
|
||||
if(NOSTALGIA_BUILD_PLAYER)
|
||||
if(${NOSTALGIA_BUILD_PLAYER})
|
||||
add_subdirectory(player)
|
||||
endif()
|
||||
|
||||
if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
||||
if(NOT BUILDCORE_TARGET STREQUAL "gba")
|
||||
add_subdirectory(tools)
|
||||
if(NOSTALGIA_BUILD_STUDIO)
|
||||
if(${NOSTALGIA_BUILD_STUDIO_APP})
|
||||
add_subdirectory(studio)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -24,7 +24,7 @@ install(
|
||||
)
|
||||
|
||||
# Studio
|
||||
if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
||||
if(NOSTALGIA_BUILD_STUDIO)
|
||||
add_library(
|
||||
NostalgiaStudioModules STATIC
|
||||
studiomodules.cpp
|
||||
|
@ -1,5 +1,9 @@
|
||||
add_subdirectory(src)
|
||||
|
||||
if(NOT BUILDCORE_TARGET STREQUAL "gba")
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
install(
|
||||
DIRECTORY
|
||||
include/nostalgia
|
||||
|
@ -87,6 +87,11 @@ constexpr Color32 color32(uint8_t r, uint8_t g, uint8_t b) noexcept {
|
||||
return static_cast<Color32>(r | (g << 8) | (b << 16));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color32 color32(Color16 c) noexcept {
|
||||
return color32(red32(c), green32(c), blue32(c));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Color32 color32(float r, float g, float b) noexcept {
|
||||
return static_cast<Color32>(static_cast<uint8_t>(r * 255) | (static_cast<uint8_t>(g * 255) << 8) | (static_cast<uint8_t>(b * 255) << 16));
|
||||
|
@ -13,68 +13,155 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
extern ox::Array<char, 128> charMap;
|
||||
|
||||
struct Sprite {
|
||||
unsigned idx = 0;
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Sprite";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
bool enabled = false;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
unsigned tileIdx = 0;
|
||||
unsigned spriteShape = 0;
|
||||
unsigned spriteSize = 0;
|
||||
unsigned flipX = 0;
|
||||
unsigned bpp = 0;
|
||||
/**
|
||||
* Valid priorities: 0-3
|
||||
*/
|
||||
unsigned priority = 0;
|
||||
};
|
||||
|
||||
oxModelBegin(Sprite)
|
||||
oxModelField(idx)
|
||||
oxModelField(x)
|
||||
oxModelField(y)
|
||||
oxModelField(enabled)
|
||||
oxModelField(tileIdx)
|
||||
oxModelField(spriteShape)
|
||||
oxModelField(spriteSize)
|
||||
oxModelField(flipX)
|
||||
oxModelField(bpp)
|
||||
oxModelField(priority)
|
||||
oxModelEnd()
|
||||
|
||||
struct BgTile {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.BgTile";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
unsigned tileIdx = 0;
|
||||
unsigned palBank = 0;
|
||||
unsigned flipX = false;
|
||||
unsigned flipY = false;
|
||||
};
|
||||
|
||||
oxModelBegin(BgTile)
|
||||
oxModelField(tileIdx)
|
||||
oxModelField(palBank)
|
||||
oxModelField(horizontalFlip)
|
||||
oxModelField(verticalFlip)
|
||||
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,
|
||||
size_t palBank,
|
||||
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,
|
||||
ox::Optional<unsigned> const&paletteBank = {}) 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, unsigned tile, unsigned palBank = 0) noexcept;
|
||||
|
||||
void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, BgTile const&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;
|
||||
|
||||
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,
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
ox::FileAddress const&paletteAddr = nullptr) noexcept;
|
||||
void setBgPriority(Context &ctx, uint_t bgIdx, uint_t priority) noexcept;
|
||||
|
||||
ox::Error loadSpriteTileSheet(
|
||||
Context &ctx,
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
ox::FileAddress const&paletteAddr) 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;
|
||||
|
||||
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 setSprite(Context &ctx, unsigned idx, int x, int y, unsigned tileIdx,
|
||||
unsigned spriteShape = 0, unsigned spriteSize = 0, unsigned flipX = 0) noexcept;
|
||||
|
||||
void setSprite(Context &ctx, Sprite const&s) noexcept;
|
||||
|
||||
}
|
||||
|
||||
namespace nostalgia::core::gl {
|
||||
|
@ -8,6 +8,8 @@ namespace nostalgia::core {
|
||||
|
||||
struct InitParams {
|
||||
bool glInstallDrawer = true;
|
||||
uint_t glSpriteCount = 128;
|
||||
uint_t glBlocksPerSprite = 64;
|
||||
};
|
||||
|
||||
}
|
@ -61,7 +61,7 @@ struct TileSheetV2 {
|
||||
|
||||
using SubSheetId = int32_t;
|
||||
|
||||
struct TileSheet {
|
||||
struct TileSheetV3 {
|
||||
using SubSheetIdx = ox::Vector<std::size_t, 4>;
|
||||
|
||||
struct SubSheet {
|
||||
@ -73,119 +73,20 @@ struct TileSheet {
|
||||
int rows = 0;
|
||||
ox::Vector<SubSheet> subsheets;
|
||||
ox::Vector<uint8_t> pixels;
|
||||
|
||||
constexpr SubSheet() noexcept = default;
|
||||
constexpr SubSheet(SubSheet const&other) noexcept = default;
|
||||
SubSheet(SubSheet &&other) noexcept;
|
||||
SubSheet(
|
||||
inline SubSheet(
|
||||
SubSheetId pId,
|
||||
ox::CRStringView pName,
|
||||
int pColumns,
|
||||
int pRows,
|
||||
int bpp) noexcept;
|
||||
SubSheet(
|
||||
SubSheetId pId,
|
||||
ox::CRStringView pName,
|
||||
int pColumns,
|
||||
int pRows,
|
||||
ox::Vector<uint8_t> pPixels) noexcept;
|
||||
|
||||
constexpr SubSheet &operator=(const SubSheet &other) noexcept = default;
|
||||
|
||||
SubSheet &operator=(SubSheet &&other) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
std::size_t idx(ox::Point const&pt) const noexcept;
|
||||
|
||||
/**
|
||||
* Reads all pixels of this sheet or its children into the given pixel list
|
||||
* @param pixels
|
||||
*/
|
||||
void readPixelsTo(ox::Vector<uint8_t> *pPixels, int8_t pBpp) const noexcept;
|
||||
|
||||
/**
|
||||
* Reads all pixels of this sheet or its children into the given pixel list
|
||||
* @param pixels
|
||||
*/
|
||||
void readPixelsTo(ox::Vector<uint8_t> *pPixels) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
|
||||
int bpp) noexcept:
|
||||
id(pId),
|
||||
name(pName),
|
||||
columns(pColumns),
|
||||
rows(pRows),
|
||||
pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::size_t unusedPixels() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(std::size_t idx) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(std::size_t idx) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel(int8_t pBpp, std::size_t idx) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(const ox::Point &pt) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(const ox::Point &pt) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel(int8_t pBpp, const ox::Point &pt) const noexcept;
|
||||
|
||||
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 = static_cast<uint8_t>(pixels[i] & 0xF);
|
||||
const auto colorIdx2 = static_cast<uint8_t>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept;
|
||||
|
||||
void setPixel(int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept;
|
||||
|
||||
ox::Error setPixelCount(int8_t pBpp, std::size_t cnt) noexcept;
|
||||
|
||||
/**
|
||||
* 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]]
|
||||
unsigned pixelCnt(int8_t pBpp) const noexcept;
|
||||
|
||||
/**
|
||||
* Gets the offset in tiles of the desired subsheet.
|
||||
*/
|
||||
ox::Result<unsigned> getTileOffset(
|
||||
ox::SpanView<ox::StringView> const&pNamePath,
|
||||
int8_t pBpp,
|
||||
std::size_t pIt = 0,
|
||||
unsigned pCurrentTotal = 0) const noexcept;
|
||||
|
||||
ox::Result<SubSheetId> getIdFor(
|
||||
ox::SpanView<ox::StringView> const&pNamePath,
|
||||
std::size_t pIt = 0) const noexcept;
|
||||
|
||||
ox::Result<ox::StringView> getNameFor(SubSheetId pId) const noexcept;
|
||||
|
||||
};
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
|
||||
@ -195,24 +96,122 @@ struct TileSheet {
|
||||
ox::FileAddress defaultPalette;
|
||||
SubSheet subsheet{0, "Root", 1, 1, bpp};
|
||||
|
||||
constexpr TileSheet() noexcept = default;
|
||||
TileSheet(TileSheet const&other) noexcept = default;
|
||||
inline TileSheet(TileSheet &&other) noexcept:
|
||||
bpp(other.bpp),
|
||||
idIt(other.idIt),
|
||||
defaultPalette(std::move(other.defaultPalette)),
|
||||
subsheet(std::move(other.subsheet)) {
|
||||
};
|
||||
|
||||
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 = 4;
|
||||
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;
|
||||
inline 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)) {
|
||||
}
|
||||
inline 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)) {
|
||||
}
|
||||
|
||||
TileSheet &operator=(TileSheet const&other) noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
|
||||
}
|
||||
|
||||
TileSheet &operator=(TileSheet &&other) noexcept;
|
||||
};
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
|
||||
static constexpr auto TypeVersion = 4;
|
||||
int8_t bpp = 4;
|
||||
SubSheetId idIt = 0;
|
||||
ox::FileAddress defaultPalette;
|
||||
SubSheet subsheet{0, "Root", 1, 1, bpp};
|
||||
|
||||
constexpr TileSheet() noexcept = default;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
SubSheetIdx validateSubSheetIdx(
|
||||
SubSheetIdx const&pIdx,
|
||||
std::size_t pIdxIt,
|
||||
const SubSheet *pSubsheet) noexcept;
|
||||
std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, std::size_t idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, ox::Point const&pt) noexcept;
|
||||
|
||||
constexpr void walkPixels(TileSheet::SubSheet const&ss, int8_t pBpp, auto callback) noexcept {
|
||||
if (pBpp == 4) {
|
||||
const auto pixelCnt = ox::min<std::size_t>(
|
||||
static_cast<std::size_t>(ss.columns * ss.rows * PixelsPerTile) / 2,
|
||||
ss.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 = static_cast<uint8_t>(ss.pixels[i] & 0xF);
|
||||
const auto colorIdx2 = static_cast<uint8_t>(ss.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>(ss.columns * ss.rows * PixelsPerTile),
|
||||
ss.pixels.size());
|
||||
for (std::size_t i = 0; i < pixelCnt; ++i) {
|
||||
const auto p = ss.pixels[i];
|
||||
callback(i, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept;
|
||||
|
||||
void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept;
|
||||
|
||||
ox::Error setPixelCount(TileSheet::SubSheet &ss, int8_t pBpp, std::size_t cnt) noexcept;
|
||||
|
||||
/**
|
||||
* 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]]
|
||||
unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept;
|
||||
|
||||
/**
|
||||
* validateSubSheetIdx takes a SubSheetIdx and moves the index to the
|
||||
@ -222,59 +221,60 @@ struct TileSheet {
|
||||
* @return a valid version of idx
|
||||
*/
|
||||
[[nodiscard]]
|
||||
SubSheetIdx validateSubSheetIdx(const SubSheetIdx &idx) noexcept;
|
||||
TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
static SubSheet const&getSubSheet(
|
||||
SubSheetIdx const&idx,
|
||||
TileSheet::SubSheet const&getSubSheet(
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
const SubSheet *pSubsheet) noexcept;
|
||||
TileSheet::SubSheet const&pSubsheet) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
static SubSheet &getSubSheet(
|
||||
SubSheetIdx const&idx,
|
||||
TileSheet::SubSheet &getSubSheet(
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
SubSheet *pSubsheet) noexcept;
|
||||
TileSheet::SubSheet &pSubsheet) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const SubSheet &getSubSheet(SubSheetIdx const&idx) const noexcept;
|
||||
TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
SubSheet &getSubSheet(SubSheetIdx const&idx) noexcept;
|
||||
TileSheet::SubSheet &getSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
ox::Error addSubSheet(SubSheetIdx const&idx) noexcept;
|
||||
ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
static ox::Error rmSubSheet(
|
||||
SubSheetIdx const&idx,
|
||||
ox::Error rmSubSheet(
|
||||
TileSheet &ts,
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
SubSheet *pSubsheet) noexcept;
|
||||
TileSheet::SubSheet &pSubsheet) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Error rmSubSheet(SubSheetIdx const&idx) noexcept;
|
||||
ox::Error rmSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel4Bpp(
|
||||
TileSheet const&ts,
|
||||
ox::Point const&pt,
|
||||
SubSheetIdx const&subsheetIdx) const noexcept;
|
||||
TileSheet::SubSheetIdx const&subsheetIdx) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
uint8_t getPixel8Bpp(
|
||||
TileSheet const&ts,
|
||||
ox::Point const&pt,
|
||||
SubSheetIdx const&subsheetIdx) const noexcept;
|
||||
TileSheet::SubSheetIdx const&subsheetIdx) noexcept;
|
||||
|
||||
ox::Result<SubSheetId> getIdFor(ox::CRStringView path) const noexcept;
|
||||
ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::CRStringView path) noexcept;
|
||||
|
||||
ox::Result<unsigned> getTileOffset(ox::CRStringView pNamePath) const noexcept;
|
||||
ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::CRStringView pNamePath) noexcept;
|
||||
|
||||
ox::Result<ox::StringView> getNameFor(SubSheetId pId) const noexcept;
|
||||
ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept;
|
||||
|
||||
ox::Result<ox::StringView> getNameFor(TileSheet const&ss, SubSheetId pId) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Vector<uint8_t> pixels() const noexcept;
|
||||
ox::Vector<uint8_t> pixels(TileSheet &ts) noexcept;
|
||||
|
||||
};
|
||||
|
||||
using TileSheetV3 = TileSheet;
|
||||
using TileSheetV4 = TileSheet;
|
||||
|
||||
struct CompactTileSheet {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet";
|
||||
@ -294,9 +294,9 @@ oxModelBegin(TileSheetV1)
|
||||
oxModelEnd()
|
||||
|
||||
oxModelBegin(TileSheetV2::SubSheet)
|
||||
oxModelField(name);
|
||||
oxModelField(rows);
|
||||
oxModelField(columns);
|
||||
oxModelField(name)
|
||||
oxModelField(rows)
|
||||
oxModelField(columns)
|
||||
oxModelField(subsheets)
|
||||
oxModelField(pixels)
|
||||
oxModelEnd()
|
||||
@ -308,9 +308,9 @@ oxModelBegin(TileSheetV2)
|
||||
oxModelEnd()
|
||||
|
||||
oxModelBegin(TileSheetV3::SubSheet)
|
||||
oxModelField(name);
|
||||
oxModelField(rows);
|
||||
oxModelField(columns);
|
||||
oxModelField(name)
|
||||
oxModelField(rows)
|
||||
oxModelField(columns)
|
||||
oxModelField(subsheets)
|
||||
oxModelField(pixels)
|
||||
oxModelEnd()
|
||||
@ -322,6 +322,22 @@ oxModelBegin(TileSheetV3)
|
||||
oxModelField(subsheet)
|
||||
oxModelEnd()
|
||||
|
||||
oxModelBegin(TileSheetV4::SubSheet)
|
||||
oxModelField(id)
|
||||
oxModelField(name)
|
||||
oxModelField(rows)
|
||||
oxModelField(columns)
|
||||
oxModelField(subsheets)
|
||||
oxModelField(pixels)
|
||||
oxModelEnd()
|
||||
|
||||
oxModelBegin(TileSheetV4)
|
||||
oxModelField(bpp)
|
||||
oxModelField(idIt)
|
||||
oxModelField(defaultPalette)
|
||||
oxModelField(subsheet)
|
||||
oxModelEnd()
|
||||
|
||||
oxModelBegin(CompactTileSheet)
|
||||
oxModelField(bpp)
|
||||
oxModelField(defaultPalette)
|
||||
|
@ -5,7 +5,7 @@ add_library(
|
||||
)
|
||||
|
||||
add_subdirectory(gba)
|
||||
if(NOT TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||
if(NOT BUILDCORE_TARGET STREQUAL "gba")
|
||||
add_subdirectory(opengl)
|
||||
endif()
|
||||
|
||||
|
@ -15,7 +15,7 @@ target_link_libraries(
|
||||
Turbine
|
||||
)
|
||||
|
||||
if(TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||
if(BUILDCORE_TARGET STREQUAL "gba")
|
||||
set_source_files_properties(gfx.cpp PROPERTIES COMPILE_FLAGS -marm)
|
||||
target_link_libraries(NostalgiaCore PUBLIC NostalgiaCore-GBA)
|
||||
endif()
|
||||
|
@ -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;
|
||||
|
@ -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,102 @@ 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 §ionIdx, int tileIdx) noexcept {
|
||||
if (setEntry.sections.size() <= sectionIdx) {
|
||||
return false;
|
||||
}
|
||||
auto §ion = 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) {
|
||||
uint16_t intermediate = 0;
|
||||
size_t const fourBpp = t->bpp == 4;
|
||||
const auto handleTileMap = [t, &intermediate, §ionIdx, 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 (i & 1) { // i is odd
|
||||
intermediate |= static_cast<uint16_t>(*tile) << 8;
|
||||
t->tileMap[t->tileWriteIdx] = intermediate;
|
||||
++t->tileWriteIdx;
|
||||
} else { // i is even
|
||||
intermediate = *tile & 0x00ff;
|
||||
}
|
||||
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, §ionIdx](std::size_t i, uint8_t const*tile) {
|
||||
auto constexpr BytesPerTile4Bpp = 32;
|
||||
auto const tileIdx = static_cast<int>(i / 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 +138,166 @@ 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 = ®_BG0CTL; bgCtl <= ®_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,
|
||||
size_t palBank,
|
||||
ox::FileAddress const&paletteAddr) noexcept {
|
||||
auto &rom = ctx.rom();
|
||||
GbaPaletteTarget const palTarget{.palette = MEM_BG_PALETTE + palBank * 16};
|
||||
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,
|
||||
ox::Optional<unsigned> const&paletteBank) 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 (paletteBank.has_value() && target.defaultPalette) {
|
||||
oxReturnError(loadBgPalette(ctx, *paletteBank, 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, BgTile const&tile) noexcept {
|
||||
auto const tileIdx = static_cast<std::size_t>(row * GbaTileColumns + column);
|
||||
// see Tonc 9.3
|
||||
MEM_BG_MAP[bgIdx][tileIdx] =
|
||||
static_cast<uint16_t>(tile.tileIdx & 0b1'1111'1111) |
|
||||
static_cast<uint16_t>(tile.flipX << 0xa) |
|
||||
static_cast<uint16_t>(tile.flipY << 0xb) |
|
||||
static_cast<uint16_t>(tile.palBank << 0xc);
|
||||
}
|
||||
|
||||
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,151 +317,62 @@ void setBgStatus(Context&, unsigned bg, bool status) noexcept {
|
||||
REG_DISPCTL = REG_DISPCTL | ((REG_DISPCTL & ~mask) | mask);
|
||||
}
|
||||
|
||||
static void setBgCbb(Context*, unsigned bgIdx, unsigned cbb) noexcept {
|
||||
void setBgBpp(Context&, unsigned bgIdx, unsigned bpp) noexcept {
|
||||
auto &bgCtl = regBgCtl(bgIdx);
|
||||
const auto &cbbData = g_cbbData[cbb];
|
||||
teagba::bgSetBpp(&bgCtl, cbbData.bpp);
|
||||
teagba::bgSetCbb(&bgCtl, cbb);
|
||||
}
|
||||
|
||||
void setBgCbb(Context&, unsigned bgIdx, unsigned cbb) noexcept {
|
||||
setBgCbb(nullptr, bgIdx, cbb);
|
||||
}
|
||||
|
||||
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 {};
|
||||
|
||||
void setBgCbb(Context &ctx, unsigned bgIdx, unsigned cbb) noexcept {
|
||||
auto &bgCtl = regBgCtl(bgIdx);
|
||||
const auto &cbbData = ctx.cbbData[cbb];
|
||||
teagba::bgSetBpp(bgCtl, cbbData.bpp);
|
||||
teagba::bgSetCbb(bgCtl, cbb);
|
||||
}
|
||||
|
||||
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);
|
||||
void setBgPriority(Context&, uint_t bgIdx, uint_t priority) noexcept {
|
||||
auto &bgCtl = regBgCtl(bgIdx);
|
||||
bgCtl = (bgCtl & 0b1111'1111'1111'1100u) | (priority & 0b11);
|
||||
}
|
||||
|
||||
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::GbaSpriteAttrUpdate oa;
|
||||
oa.attr0 = 2 << 8;
|
||||
oa.idx = static_cast<uint16_t>(idx);
|
||||
teagba::addSpriteUpdate(oa);
|
||||
teagba::addSpriteUpdate({
|
||||
.attr0 = uint16_t{0b11 << 8},
|
||||
.idx = static_cast<uint16_t>(idx),
|
||||
});
|
||||
}
|
||||
|
||||
void setSprite(Context&,
|
||||
unsigned idx,
|
||||
int x,
|
||||
int y,
|
||||
unsigned tileIdx,
|
||||
unsigned spriteShape,
|
||||
unsigned spriteSize,
|
||||
unsigned flipX) noexcept {
|
||||
void showSprite(Context&, unsigned idx) noexcept {
|
||||
//oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
|
||||
teagba::GbaSpriteAttrUpdate oa;
|
||||
oa.attr0 = static_cast<uint16_t>(
|
||||
static_cast<uint16_t>(y & ox::onMask<uint8_t>(0b111'1111))
|
||||
teagba::addSpriteUpdate({
|
||||
.attr0 = 0,
|
||||
.idx = static_cast<uint16_t>(idx),
|
||||
});
|
||||
}
|
||||
|
||||
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>(spriteShape) << 14));
|
||||
oa.attr1 = static_cast<uint16_t>(
|
||||
(static_cast<uint16_t>(x) & ox::onMask<uint8_t>(8))
|
||||
| (static_cast<uint16_t>(flipX) << 12)
|
||||
| (static_cast<uint16_t>(spriteSize) << 14));
|
||||
oa.attr2 = static_cast<uint16_t>(tileIdx & ox::onMask<uint16_t>(8));
|
||||
oa.idx = static_cast<uint16_t>(idx);
|
||||
teagba::addSpriteUpdate(oa);
|
||||
| (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>(
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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...");
|
||||
|
@ -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,8 +137,36 @@ ox::Array<char, 128> charMap = {
|
||||
50, // ~
|
||||
};
|
||||
|
||||
void setSprite(Context &c, Sprite const&s) noexcept {
|
||||
setSprite(c, s.idx, s.x, s.y, s.tileIdx, s.spriteShape, s.spriteSize, s.flipX);
|
||||
void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, unsigned tile, unsigned palBank) noexcept {
|
||||
setBgTile(ctx, bgIdx, column, row, {
|
||||
.tileIdx = tile,
|
||||
.palBank = palBank,
|
||||
});
|
||||
}
|
||||
|
||||
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, 0, 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])]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ class KeelModule: public keel::Module {
|
||||
private:
|
||||
NostalgiaPaletteToPaletteConverter m_nostalgiaPaletteToPaletteConverter;
|
||||
TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter;
|
||||
TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetConverter;
|
||||
TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetV3Converter;
|
||||
TileSheetV3ToTileSheetV4Converter m_tileSheetV3ToTileSheetV4Converter;
|
||||
TileSheetToCompactTileSheetConverter m_tileSheetToCompactTileSheetConverter;
|
||||
|
||||
public:
|
||||
@ -32,18 +33,20 @@ class KeelModule: public keel::Module {
|
||||
return {
|
||||
keel::generateTypeDesc<TileSheetV1>,
|
||||
keel::generateTypeDesc<TileSheetV2>,
|
||||
keel::generateTypeDesc<TileSheet>,
|
||||
keel::generateTypeDesc<TileSheetV3>,
|
||||
keel::generateTypeDesc<TileSheetV4>,
|
||||
keel::generateTypeDesc<CompactTileSheet>,
|
||||
keel::generateTypeDesc<Palette>,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Vector<const keel::BaseConverter*> converters() const noexcept final {
|
||||
ox::Vector<keel::BaseConverter const*> converters() const noexcept final {
|
||||
return {
|
||||
&m_nostalgiaPaletteToPaletteConverter,
|
||||
&m_tileSheetV1ToTileSheetV2Converter,
|
||||
&m_tileSheetV2ToTileSheetConverter,
|
||||
&m_tileSheetV2ToTileSheetV3Converter,
|
||||
&m_tileSheetV3ToTileSheetV4Converter,
|
||||
&m_tileSheetToCompactTileSheetConverter,
|
||||
};
|
||||
}
|
||||
@ -54,11 +57,12 @@ class KeelModule: public keel::Module {
|
||||
// convert tilesheets to CompactTileSheets
|
||||
[](keel::Context &ctx, ox::Buffer &buff) -> ox::Error {
|
||||
oxRequire(hdr, keel::readAssetHeader(buff));
|
||||
const auto typeId = ox::buildTypeId(
|
||||
auto const 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>()) {
|
||||
typeId == ox::buildTypeId<TileSheetV3>() ||
|
||||
typeId == ox::buildTypeId<TileSheetV4>()) {
|
||||
oxReturnError(keel::convertBuffToBuff<core::CompactTileSheet>(
|
||||
ctx, buff, ox::ClawFormat::Metal).moveTo(buff));
|
||||
}
|
||||
|
@ -53,13 +53,41 @@ ox::Error TileSheetV2ToTileSheetV3Converter::convert(
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
void TileSheetV3ToTileSheetV4Converter::convertSubsheet(
|
||||
TileSheetV3::SubSheet &src,
|
||||
TileSheetV4::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 TileSheetV3ToTileSheetV4Converter::convert(
|
||||
keel::Context&,
|
||||
TileSheetV3 &src,
|
||||
TileSheetV4 &dst) const noexcept {
|
||||
dst.bpp = src.bpp;
|
||||
dst.idIt = src.idIt;
|
||||
dst.defaultPalette = std::move(src.defaultPalette);
|
||||
convertSubsheet(src.subsheet, dst.subsheet, dst.idIt);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error TileSheetToCompactTileSheetConverter::convert(
|
||||
keel::Context&,
|
||||
TileSheet &src,
|
||||
CompactTileSheet &dst) const noexcept {
|
||||
dst.bpp = src.bpp;
|
||||
dst.defaultPalette = std::move(src.defaultPalette);
|
||||
dst.pixels = src.pixels();
|
||||
dst.pixels = pixels(src);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,14 @@ class TileSheetV2ToTileSheetV3Converter: public keel::Converter<TileSheetV2, Til
|
||||
ox::Error convert(keel::Context&, TileSheetV2 &src, TileSheetV3 &dst) const noexcept final;
|
||||
};
|
||||
|
||||
class TileSheetV3ToTileSheetV4Converter: public keel::Converter<TileSheetV3, TileSheetV4> {
|
||||
static void convertSubsheet(
|
||||
TileSheetV3::SubSheet &src,
|
||||
TileSheetV4::SubSheet &dst,
|
||||
SubSheetId &idIt) noexcept;
|
||||
ox::Error convert(keel::Context&, TileSheetV3 &src, TileSheetV4 &dst) const noexcept final;
|
||||
};
|
||||
|
||||
class TileSheetToCompactTileSheetConverter: public keel::Converter<TileSheet, CompactTileSheet> {
|
||||
ox::Error convert(keel::Context&, TileSheet &src, CompactTileSheet &dst) const noexcept final;
|
||||
};
|
||||
|
@ -11,9 +11,12 @@ void ContextDeleter::operator()(Context *p) noexcept {
|
||||
ox::safeDelete(p);
|
||||
}
|
||||
|
||||
Context::Context(turbine::Context &tctx) noexcept:
|
||||
Context::Context(turbine::Context &tctx, InitParams const¶ms) noexcept:
|
||||
turbineCtx(tctx),
|
||||
drawer(*this) {
|
||||
spriteBlocks(params.glSpriteCount, params.glBlocksPerSprite),
|
||||
drawer(*this),
|
||||
spriteCount(params.glSpriteCount),
|
||||
blocksPerSprite(params.glBlocksPerSprite) {
|
||||
}
|
||||
|
||||
Context::~Context() noexcept {
|
||||
@ -21,7 +24,7 @@ Context::~Context() noexcept {
|
||||
}
|
||||
|
||||
ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const¶ms) noexcept {
|
||||
auto ctx = ox::make_unique<Context>(tctx);
|
||||
auto ctx = ox::make_unique<Context>(tctx, params);
|
||||
oxReturnError(initGfx(*ctx, params));
|
||||
return ContextUPtr(ctx.release());
|
||||
}
|
||||
|
@ -24,9 +24,16 @@ class Context {
|
||||
ox::Array<renderer::CBB, 4> cbbs;
|
||||
renderer::SpriteBlockset spriteBlocks;
|
||||
ox::Array<Sprite, 128> spriteStates;
|
||||
ox::Array<GLfloat, 1024> bgPalette;
|
||||
ox::Array<renderer::Background, 4> backgrounds;
|
||||
renderer::Drawer drawer;
|
||||
explicit Context(turbine::Context &tctx) noexcept;
|
||||
uint_t spriteCount = 0;
|
||||
uint_t blocksPerSprite = 0;
|
||||
explicit Context(turbine::Context &tctx, InitParams const¶ms) noexcept;
|
||||
Context(Context const&) = delete;
|
||||
Context(Context&&) = delete;
|
||||
Context &operator=(Context const&) = delete;
|
||||
Context &operator=(Context&&) = delete;
|
||||
~Context() noexcept;
|
||||
};
|
||||
|
||||
|
@ -19,72 +19,91 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
constexpr auto Scale = 1;
|
||||
|
||||
namespace renderer {
|
||||
|
||||
constexpr auto Scale = 1;
|
||||
constexpr auto PriorityScale = 0.01f;
|
||||
|
||||
Drawer::Drawer(Context &ctx) noexcept: m_ctx(ctx) {}
|
||||
|
||||
void Drawer::draw(turbine::Context &tctx) noexcept {
|
||||
core::gl::draw(m_ctx, turbine::getScreenSize(tctx));
|
||||
}
|
||||
|
||||
constexpr ox::StringView bgvshadTmpl = R"glsl(
|
||||
constexpr ox::CStringView bgvshadTmpl = R"glsl(
|
||||
{}
|
||||
in vec2 vTexCoord;
|
||||
in vec2 vPosition;
|
||||
in vec3 vPosition;
|
||||
in float vTileIdx;
|
||||
in float vPalOffset;
|
||||
out vec2 fTexCoord;
|
||||
out float fPalOffset;
|
||||
uniform float vXScale;
|
||||
uniform float vTileHeight;
|
||||
uniform float vBgIdx;
|
||||
void main() {
|
||||
float xScaleInvert = 1.0 - vXScale;
|
||||
gl_Position = vec4(
|
||||
vPosition.x * vXScale - xScaleInvert,
|
||||
vPosition.y,
|
||||
vPosition.z - 0.001 * vBgIdx,
|
||||
1.0);
|
||||
fTexCoord = vec2(
|
||||
vTexCoord.x,
|
||||
vTexCoord.y * vTileHeight + vTileIdx * vTileHeight);
|
||||
fPalOffset = vPalOffset;
|
||||
})glsl";
|
||||
|
||||
constexpr ox::CStringView bgfshadTmpl = R"glsl(
|
||||
{}
|
||||
out vec4 outColor;
|
||||
in float fPalOffset;
|
||||
in vec2 fTexCoord;
|
||||
uniform sampler2D image;
|
||||
uniform vec2 fSrcImgSz;
|
||||
uniform vec4 fPalette[256];
|
||||
void main() {
|
||||
outColor = fPalette[int(texture(image, fTexCoord).rgb.r * 256) + int(fPalOffset)];
|
||||
//outColor = vec4(0.0, 0.7, 1.0, 1.0);
|
||||
if (outColor.a == 0) {
|
||||
discard;
|
||||
}
|
||||
})glsl";
|
||||
|
||||
constexpr ox::CStringView spritevshadTmpl = R"glsl(
|
||||
{}
|
||||
in float vEnabled;
|
||||
in vec3 vPosition;
|
||||
in vec2 vTexCoord;
|
||||
out vec2 fTexCoord;
|
||||
uniform float vXScale;
|
||||
uniform float vTileHeight;
|
||||
void main() {
|
||||
float xScaleInvert = 1.0 - vXScale;
|
||||
gl_Position = vec4(
|
||||
vPosition.x * vXScale - xScaleInvert, vPosition.y,
|
||||
0.0, 1.0);
|
||||
fTexCoord = vec2(
|
||||
vTexCoord.x,
|
||||
vTexCoord.y * vTileHeight + vTileIdx * vTileHeight);
|
||||
vPosition.x * vXScale - xScaleInvert,
|
||||
vPosition.y,
|
||||
// offset to ensure sprites draw on top of BGs by default
|
||||
vPosition.z - 0.004,
|
||||
1.0) * vEnabled;
|
||||
fTexCoord = vTexCoord * vec2(1, vTileHeight);
|
||||
})glsl";
|
||||
|
||||
constexpr ox::StringView bgfshadTmpl = R"glsl(
|
||||
constexpr ox::CStringView spritefshadTmpl = R"glsl(
|
||||
{}
|
||||
out vec4 outColor;
|
||||
in vec2 fTexCoord;
|
||||
uniform sampler2D image;
|
||||
uniform vec2 fSrcImgSz;
|
||||
uniform vec4 fPalette[256];
|
||||
vec2 pixelSz;
|
||||
vec4 getColor(vec2 offset) {
|
||||
vec2 p = fTexCoord + pixelSz * offset;
|
||||
int idx = int(texture(image, p).rgb.r * 256);
|
||||
return fPalette[idx];
|
||||
}
|
||||
void main() {
|
||||
pixelSz = vec2(1, 1) / (fSrcImgSz);
|
||||
vec2 pixelCoord = floor(fTexCoord / pixelSz) * pixelSz;
|
||||
outColor = fPalette[int(texture(image, fTexCoord).rgb.r * 256)];
|
||||
//outColor = vec4(0.0, 0.7, 1.0, 1.0);
|
||||
if (outColor.a == 0) {
|
||||
discard;
|
||||
}
|
||||
})glsl";
|
||||
|
||||
constexpr ox::StringView spritevshadTmpl = R"glsl(
|
||||
{}
|
||||
in float vEnabled;
|
||||
in vec2 vTexCoord;
|
||||
in vec2 vPosition;
|
||||
out vec2 fTexCoord;
|
||||
uniform float vXScale;
|
||||
uniform float vTileHeight;
|
||||
void main() {
|
||||
float xScaleInvert = 1.0 - vXScale;
|
||||
gl_Position = vec4(
|
||||
vPosition.x * vXScale - xScaleInvert, vPosition.y,
|
||||
0.0, 1.0);
|
||||
fTexCoord = vTexCoord * vec2(1, vTileHeight) * vec2(vEnabled, vEnabled);
|
||||
})glsl";
|
||||
|
||||
constexpr ox::StringView spritefshadTmpl = bgfshadTmpl;
|
||||
|
||||
[[nodiscard]]
|
||||
static constexpr auto bgVertexRow(uint_t x, uint_t y) noexcept {
|
||||
return y * TileRows + x;
|
||||
@ -97,6 +116,7 @@ static void setSpriteBufferObject(
|
||||
float y,
|
||||
uint_t textureRow,
|
||||
uint_t flipX,
|
||||
uint_t priority,
|
||||
float *vbo,
|
||||
GLuint *ebo) noexcept {
|
||||
// don't worry, this memcpy gets optimized to something much more ideal
|
||||
@ -106,17 +126,19 @@ static void setSpriteBufferObject(
|
||||
y *= -ymod;
|
||||
x -= 1.f;
|
||||
y += 1.f - ymod;
|
||||
const auto textureRowf = static_cast<float>(textureRow);
|
||||
const float L = flipX ? 1 : 0;
|
||||
const float R = flipX ? 0 : 1;
|
||||
const ox::Array<float, SpriteVertexVboLength> vertices {
|
||||
enabled, x, y, L, textureRowf + 1, // bottom left
|
||||
enabled, x + xmod, y, R, textureRowf + 1, // bottom right
|
||||
enabled, x + xmod, y + ymod, R, textureRowf + 0, // top right
|
||||
enabled, x, y + ymod, L, textureRowf + 0, // top left
|
||||
auto const prif = static_cast<float>(priority) * PriorityScale;
|
||||
auto const textureRowf = static_cast<float>(textureRow);
|
||||
float const L = flipX ? 1 : 0;
|
||||
float const R = flipX ? 0 : 1;
|
||||
ox::Array<float, SpriteVertexVboLength> const vertices {
|
||||
// vEnabled| vPosition | vTexCoord
|
||||
enabled, x, y, prif, L, textureRowf + 1, // bottom left
|
||||
enabled, x + xmod, y, prif, R, textureRowf + 1, // bottom right
|
||||
enabled, x + xmod, y + ymod, prif, R, textureRowf + 0, // top right
|
||||
enabled, x, y + ymod, prif, L, textureRowf + 0, // top left
|
||||
};
|
||||
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||
const ox::Array<GLuint, SpriteVertexEboLength> elms {
|
||||
ox::Array<GLuint, SpriteVertexEboLength> const elms {
|
||||
vi + 0, vi + 1, vi + 2,
|
||||
vi + 2, vi + 3, vi + 0,
|
||||
};
|
||||
@ -128,6 +150,10 @@ static void setTileBufferObject(
|
||||
float x,
|
||||
float y,
|
||||
float textureTileIdx,
|
||||
float priority,
|
||||
float palOffset,
|
||||
bool flipX,
|
||||
bool flipY,
|
||||
float *vbo,
|
||||
GLuint *ebo) noexcept {
|
||||
// don't worry, this memcpy gets optimized to something much more ideal
|
||||
@ -137,11 +163,16 @@ static void setTileBufferObject(
|
||||
y *= -ymod;
|
||||
x -= 1.0f;
|
||||
y += 1.0f - ymod;
|
||||
auto const prif = priority * PriorityScale;
|
||||
float const L = flipX ? 1 : 0;
|
||||
float const R = flipX ? 0 : 1;
|
||||
float const T = flipY ? 1 : 0;
|
||||
float const B = flipY ? 0 : 1;
|
||||
ox::Array<float, BgVertexVboLength> const vertices {
|
||||
x, y, 0, 1, textureTileIdx, // bottom left
|
||||
x + xmod, y, 1, 1, textureTileIdx, // bottom right
|
||||
x + xmod, y + ymod, 1, 0, textureTileIdx, // top right
|
||||
x, y + ymod, 0, 0, textureTileIdx, // top left
|
||||
x, y, prif, L, B, textureTileIdx, palOffset, // bottom left
|
||||
x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right
|
||||
x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right
|
||||
x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left
|
||||
};
|
||||
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||
ox::Array<GLuint, BgVertexEboLength> const elms {
|
||||
@ -151,81 +182,95 @@ static void setTileBufferObject(
|
||||
memcpy(ebo, elms.data(), sizeof(elms));
|
||||
}
|
||||
|
||||
static void initSpriteBufferObjects(glutils::BufferSet &bs) noexcept {
|
||||
for (auto i = 0u; i < SpriteCount; ++i) {
|
||||
static void initSpriteBufferObjects(Context &ctx, glutils::BufferSet &bs) noexcept {
|
||||
for (auto i = 0u; i < ctx.spriteCount; ++i) {
|
||||
auto vbo = &bs.vertices[i * static_cast<std::size_t>(SpriteVertexVboLength)];
|
||||
auto ebo = &bs.elements[i * static_cast<std::size_t>(SpriteVertexEboLength)];
|
||||
setSpriteBufferObject(i * SpriteVertexVboRows, 0, 0, 0, 0, false, vbo, ebo);
|
||||
setSpriteBufferObject(i * static_cast<uint_t>(SpriteVertexVboRows) * ctx.blocksPerSprite, 0, 0, 0, 0, false, 0, vbo, ebo);
|
||||
}
|
||||
}
|
||||
|
||||
static void initBackgroundBufferObjects(glutils::BufferSet &bg) noexcept {
|
||||
static void initBackgroundBufferObjects(glutils::BufferSet &bs) noexcept {
|
||||
for (auto x = 0u; x < TileColumns; ++x) {
|
||||
for (auto y = 0u; y < TileRows; ++y) {
|
||||
const auto i = bgVertexRow(x, y);
|
||||
auto vbo = &bg.vertices[i * static_cast<std::size_t>(BgVertexVboLength)];
|
||||
auto ebo = &bg.elements[i * static_cast<std::size_t>(BgVertexEboLength)];
|
||||
auto vbo = &bs.vertices[i * static_cast<std::size_t>(BgVertexVboLength)];
|
||||
auto ebo = &bs.elements[i * static_cast<std::size_t>(BgVertexEboLength)];
|
||||
setTileBufferObject(
|
||||
static_cast<uint_t>(i * BgVertexVboRows),
|
||||
static_cast<float>(x),
|
||||
static_cast<float>(y),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
vbo,
|
||||
ebo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void initSpritesBufferset(GLuint shader, glutils::BufferSet &bs) noexcept {
|
||||
static void initSpritesBufferset(Context &ctx) noexcept {
|
||||
auto const shader = ctx.spriteShader.id;
|
||||
auto &bs = ctx.spriteBlocks;
|
||||
// vao
|
||||
bs.vao = glutils::generateVertexArrayObject();
|
||||
glBindVertexArray(bs.vao);
|
||||
// vbo & ebo
|
||||
bs.vbo = glutils::generateBuffer();
|
||||
bs.ebo = glutils::generateBuffer();
|
||||
initSpriteBufferObjects(bs);
|
||||
initSpriteBufferObjects(ctx, bs);
|
||||
glutils::sendVbo(bs);
|
||||
glutils::sendEbo(bs);
|
||||
// vbo layout
|
||||
// in float vEnabled;
|
||||
auto const enabledAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vEnabled"));
|
||||
glEnableVertexAttribArray(enabledAttr);
|
||||
glVertexAttribPointer(enabledAttr, 1, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), nullptr);
|
||||
// in vec3 vPosition;
|
||||
auto const posAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vPosition"));
|
||||
glEnableVertexAttribArray(posAttr);
|
||||
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(1 * sizeof(float)));
|
||||
glVertexAttribPointer(posAttr, 3, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
|
||||
std::bit_cast<void*>(uintptr_t{1 * sizeof(float)}));
|
||||
// in vec2 vTexCoord;
|
||||
auto const texCoordAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTexCoord"));
|
||||
glEnableVertexAttribArray(texCoordAttr);
|
||||
glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(3 * sizeof(float)));
|
||||
std::bit_cast<void*>(uintptr_t{4 * sizeof(float)}));
|
||||
}
|
||||
|
||||
static void initBackgroundBufferset(
|
||||
GLuint shader,
|
||||
glutils::BufferSet &bg) noexcept {
|
||||
glutils::BufferSet &bs) noexcept {
|
||||
// vao
|
||||
bg.vao = glutils::generateVertexArrayObject();
|
||||
glBindVertexArray(bg.vao);
|
||||
bs.vao = glutils::generateVertexArrayObject();
|
||||
glBindVertexArray(bs.vao);
|
||||
// vbo & ebo
|
||||
bg.vbo = glutils::generateBuffer();
|
||||
bg.ebo = glutils::generateBuffer();
|
||||
initBackgroundBufferObjects(bg);
|
||||
glutils::sendVbo(bg);
|
||||
glutils::sendEbo(bg);
|
||||
bs.vbo = glutils::generateBuffer();
|
||||
bs.ebo = glutils::generateBuffer();
|
||||
initBackgroundBufferObjects(bs);
|
||||
glutils::sendVbo(bs);
|
||||
glutils::sendEbo(bs);
|
||||
// vbo layout
|
||||
auto const posAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vPosition"));
|
||||
glEnableVertexAttribArray(posAttr);
|
||||
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), nullptr);
|
||||
glVertexAttribPointer(posAttr, 3, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), nullptr);
|
||||
auto const texCoordAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTexCoord"));
|
||||
glEnableVertexAttribArray(texCoordAttr);
|
||||
glVertexAttribPointer(
|
||||
texCoordAttr, 2, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(2 * sizeof(float)));
|
||||
std::bit_cast<void*>(uintptr_t{3 * sizeof(float)}));
|
||||
auto const heightMultAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vTileIdx"));
|
||||
glEnableVertexAttribArray(heightMultAttr);
|
||||
glVertexAttribPointer(
|
||||
heightMultAttr, 1, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(4 * sizeof(float)));
|
||||
std::bit_cast<void*>(uintptr_t{5 * sizeof(float)}));
|
||||
auto const palBankAttr = static_cast<GLuint>(glGetAttribLocation(shader, "vPalOffset"));
|
||||
glEnableVertexAttribArray(palBankAttr);
|
||||
glVertexAttribPointer(
|
||||
palBankAttr, 1, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float),
|
||||
std::bit_cast<void*>(uintptr_t{6 * sizeof(float)}));
|
||||
}
|
||||
|
||||
static glutils::GLTexture createTexture(
|
||||
@ -265,10 +310,12 @@ static void drawBackgrounds(
|
||||
const auto uniformSrcImgSz = glGetUniformLocation(ctx.bgShader, "fSrcImgSz");
|
||||
const auto uniformXScale = static_cast<GLint>(glGetUniformLocation(ctx.bgShader, "vXScale"));
|
||||
const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(ctx.bgShader, "vTileHeight"));
|
||||
const auto uniformBgIdx = static_cast<GLint>(glGetUniformLocation(ctx.bgShader, "vBgIdx"));
|
||||
const auto [wi, hi] = renderSz;
|
||||
const auto wf = static_cast<float>(wi);
|
||||
const auto hf = static_cast<float>(hi);
|
||||
glUniform1f(uniformXScale, hf / wf);
|
||||
auto bgIdx = 0.f;
|
||||
for (const auto &bg : ctx.backgrounds) {
|
||||
if (bg.enabled) {
|
||||
auto &cbb = ctx.cbbs[bg.cbbIdx];
|
||||
@ -278,7 +325,9 @@ static void drawBackgrounds(
|
||||
uniformSrcImgSz,
|
||||
static_cast<float>(cbb.tex.width),
|
||||
static_cast<float>(cbb.tex.height));
|
||||
glUniform1f(uniformBgIdx, bgIdx);
|
||||
drawBackground(cbb);
|
||||
++bgIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -307,50 +356,96 @@ static void drawSprites(Context &ctx, ox::Size const&renderSz) noexcept {
|
||||
}
|
||||
|
||||
static void loadPalette(
|
||||
ox::Array<GLfloat, 1024> &palette,
|
||||
size_t palOffset,
|
||||
GLuint shaderPgrm,
|
||||
Palette const&pal,
|
||||
bool firstIsTransparent = false) 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) {
|
||||
for (auto i = palOffset; auto const c : pal.colors) {
|
||||
palette[i++] = redf(c);
|
||||
palette[i++] = greenf(c);
|
||||
palette[i++] = bluef(c);
|
||||
palette[i++] = 255;
|
||||
}
|
||||
if (firstIsTransparent) {
|
||||
palette[3] = 0;
|
||||
}
|
||||
// make first color transparent
|
||||
palette[palOffset + 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);
|
||||
}
|
||||
|
||||
static void loadSpritePalette(Context &ctx, Palette const&pal) noexcept {
|
||||
loadPalette(ctx.spriteShader, pal, true);
|
||||
}
|
||||
|
||||
static void loadBgTexture(
|
||||
static void setSprite(
|
||||
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);
|
||||
uint_t const idx,
|
||||
Sprite const&s) noexcept {
|
||||
// Tonc Table 8.4
|
||||
static constexpr ox::Array<ox::Vec<uint_t>, 12> dimensions{
|
||||
// col 0
|
||||
{1, 1}, // 0, 0
|
||||
{2, 2}, // 0, 1
|
||||
{4, 4}, // 0, 2
|
||||
{8, 8}, // 0, 3
|
||||
// col 1
|
||||
{2, 1}, // 1, 0
|
||||
{4, 1}, // 1, 1
|
||||
{4, 2}, // 1, 2
|
||||
{8, 4}, // 1, 3
|
||||
// col 2
|
||||
{1, 1}, // 2, 0
|
||||
{1, 4}, // 2, 1
|
||||
{2, 4}, // 2, 2
|
||||
{4, 8}, // 2, 3
|
||||
};
|
||||
oxAssert(idx < ctx.spriteStates.size(), "overflow");
|
||||
auto const dim = dimensions[(s.spriteShape << 2) | s.spriteSize];
|
||||
auto const uX = static_cast<int>(s.x) % 255;
|
||||
auto const uY = static_cast<int>(s.y + 8) % 255 - 8;
|
||||
oxAssert(1 < ctx.spriteBlocks.vertices.size(), "vbo overflow");
|
||||
oxAssert(1 < ctx.spriteBlocks.elements.size(), "ebo overflow");
|
||||
const auto spriteVboSz = ctx.blocksPerSprite * renderer::SpriteVertexVboLength;
|
||||
const auto spriteEboSz = ctx.blocksPerSprite * renderer::SpriteVertexEboLength;
|
||||
auto const vboBase = spriteVboSz * idx;
|
||||
auto const eboBase = spriteEboSz * idx;
|
||||
auto i = 0u;
|
||||
const auto set = [&](int xIt, int yIt, bool enabled) {
|
||||
auto const fX = static_cast<float>(uX + xIt * 8) / 8;
|
||||
auto const fY = static_cast<float>(uY + yIt * 8) / 8;
|
||||
auto const vboIdx = vboBase + renderer::SpriteVertexVboLength * i;
|
||||
auto const eboIdx = eboBase + renderer::SpriteVertexEboLength * i;
|
||||
oxAssert(vboIdx < ctx.spriteBlocks.vertices.size(), "vbo overflow");
|
||||
oxAssert(eboIdx < ctx.spriteBlocks.elements.size(), "ebo overflow");
|
||||
auto const vbo = &ctx.spriteBlocks.vertices[vboIdx];
|
||||
auto const ebo = &ctx.spriteBlocks.elements[eboIdx];
|
||||
renderer::setSpriteBufferObject(
|
||||
static_cast<uint_t>(vboIdx),
|
||||
enabled,
|
||||
fX,
|
||||
fY,
|
||||
s.tileIdx + i,
|
||||
s.flipX,
|
||||
s.priority & 0b11,
|
||||
vbo,
|
||||
ebo);
|
||||
++i;
|
||||
};
|
||||
if (!s.flipX) {
|
||||
for (auto yIt = 0; yIt < static_cast<int>(dim.y); ++yIt) {
|
||||
for (auto xIt = 0u; xIt < dim.x; ++xIt) {
|
||||
set(static_cast<int>(xIt), static_cast<int>(yIt), s.enabled);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
for (auto yIt = 0u; yIt < dim.y; ++yIt) {
|
||||
for (auto xIt = dim.x - 1; xIt < ~0u; --xIt) {
|
||||
set(static_cast<int>(xIt), static_cast<int>(yIt), s.enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
// clear remaining blocks in the sprite
|
||||
for (; i < ctx.blocksPerSprite; ++i) {
|
||||
set(0, 0, false);
|
||||
}
|
||||
ctx.spriteBlocks.updated = true;
|
||||
}
|
||||
|
||||
}
|
||||
@ -358,8 +453,6 @@ static void loadSpriteTexture(
|
||||
ox::Error initGfx(
|
||||
Context &ctx,
|
||||
InitParams const&initParams) noexcept {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
const auto bgVshad = ox::sfmt(renderer::bgvshadTmpl, gl::GlslVersion);
|
||||
const auto bgFshad = ox::sfmt(renderer::bgfshadTmpl, gl::GlslVersion);
|
||||
const auto spriteVshad = ox::sfmt(renderer::spritevshadTmpl, gl::GlslVersion);
|
||||
@ -367,10 +460,10 @@ ox::Error initGfx(
|
||||
oxReturnError(glutils::buildShaderProgram(bgVshad, bgFshad).moveTo(ctx.bgShader));
|
||||
oxReturnError(
|
||||
glutils::buildShaderProgram(spriteVshad, spriteFshad).moveTo(ctx.spriteShader));
|
||||
for (auto &bg : ctx.cbbs) {
|
||||
initBackgroundBufferset(ctx.bgShader, bg);
|
||||
for (auto &cbb : ctx.cbbs) {
|
||||
initBackgroundBufferset(ctx.bgShader, cbb);
|
||||
}
|
||||
initSpritesBufferset(ctx.spriteShader, ctx.spriteBlocks);
|
||||
renderer::initSpritesBufferset(ctx);
|
||||
if (initParams.glInstallDrawer) {
|
||||
turbine::gl::addDrawer(ctx.turbineCtx, &ctx.drawer);
|
||||
}
|
||||
@ -391,8 +484,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;
|
||||
@ -411,66 +503,136 @@ 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,
|
||||
size_t palBank,
|
||||
ox::FileAddress const&paletteAddr) noexcept {
|
||||
auto &kctx = keelCtx(ctx.turbineCtx);
|
||||
oxRequire(palette, readObj<Palette>(kctx, paletteAddr));
|
||||
renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, 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));
|
||||
ox::Array<GLfloat, 1024> pal;
|
||||
renderer::loadPalette(pal, 0, 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 {
|
||||
ox::Optional<unsigned> const&paletteBank) 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 (paletteBank.has_value() && tilesheet->defaultPalette) {
|
||||
oxReturnError(loadBgPalette(ctx, *paletteBank, 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,
|
||||
BgTile const&tile) noexcept {
|
||||
oxTracef(
|
||||
"nostalgia.core.gfx.setBgTile",
|
||||
"bgIdx: {}, column: {}, row: {}, tile: {}, palBank: {}",
|
||||
bgIdx, column, row, tile.tileIdx, tile.palBank);
|
||||
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.tileIdx),
|
||||
bg.priority,
|
||||
static_cast<float>(tile.palBank * 16),
|
||||
tile.flipX,
|
||||
tile.flipY,
|
||||
vbo,
|
||||
ebo);
|
||||
cbb.updated = true;
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -495,112 +657,38 @@ void setBgStatus(Context &ctx, uint_t bg, bool status) noexcept {
|
||||
ctx.backgrounds[bg].enabled = status;
|
||||
}
|
||||
|
||||
void setBgBpp(Context&, unsigned, unsigned) noexcept {}
|
||||
|
||||
void clearTileLayer(Context &ctx, uint_t bgIdx) noexcept {
|
||||
auto &bg = ctx.cbbs[static_cast<std::size_t>(bgIdx)];
|
||||
initBackgroundBufferObjects(bg);
|
||||
bg.updated = true;
|
||||
void setBgCbb(Context &ctx, uint_t bgIdx, uint_t cbbIdx) noexcept {
|
||||
auto &bg = ctx.backgrounds[bgIdx];
|
||||
bg.cbbIdx = cbbIdx;
|
||||
}
|
||||
|
||||
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 {
|
||||
auto vbo = &ctx.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength];
|
||||
auto ebo = &ctx.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength];
|
||||
renderer::setSpriteBufferObject(
|
||||
idx * renderer::SpriteVertexVboRows, 0, 0, 0, 0, false, vbo, ebo);
|
||||
ctx.spriteBlocks.updated = true;
|
||||
auto &s = ctx.spriteStates[idx];
|
||||
s.enabled = false;
|
||||
renderer::setSprite(ctx, idx, s);
|
||||
}
|
||||
|
||||
void setSprite(
|
||||
Context &ctx,
|
||||
uint_t idx,
|
||||
int x,
|
||||
int y,
|
||||
uint_t tileIdx,
|
||||
uint_t spriteShape,
|
||||
uint_t spriteSize,
|
||||
uint_t flipX) noexcept {
|
||||
//oxTracef("nostalgia::core::gfx::gl", "setSprite(ctx, {}, {}, {}, {}, {}, {}, {})",
|
||||
// idx, x, y, tileIdx, spriteShape, spriteSize, flipX);
|
||||
// Tonc Table 8.4
|
||||
static constexpr ox::Array<ox::Vec<uint_t>, 12> dimensions{
|
||||
// col 0
|
||||
{1, 1}, // 0, 0
|
||||
{2, 2}, // 0, 1
|
||||
{4, 4}, // 0, 2
|
||||
{8, 8}, // 0, 3
|
||||
// col 1
|
||||
{2, 1}, // 1, 0
|
||||
{4, 1}, // 1, 1
|
||||
{4, 2}, // 1, 2
|
||||
{8, 4}, // 1, 3
|
||||
// col 2
|
||||
{1, 1}, // 2, 0
|
||||
{1, 4}, // 2, 1
|
||||
{2, 4}, // 2, 2
|
||||
{4, 8}, // 2, 3
|
||||
};
|
||||
const auto dim = dimensions[(spriteShape << 2) | spriteSize];
|
||||
const auto uX = static_cast<int>(x) % 255;
|
||||
const auto uY = static_cast<int>(y + 8) % 255 - 8;
|
||||
auto i = 0u;
|
||||
const auto set = [&](int xIt, int yIt) {
|
||||
const auto fX = static_cast<float>(uX + xIt * 8) / 8;
|
||||
const auto fY = static_cast<float>(uY + yIt * 8) / 8;
|
||||
const auto cidx = idx + i;
|
||||
auto vbo = &ctx.spriteBlocks.vertices[cidx * renderer::SpriteVertexVboLength];
|
||||
auto ebo = &ctx.spriteBlocks.elements[cidx * renderer::SpriteVertexEboLength];
|
||||
renderer::setSpriteBufferObject(
|
||||
cidx * renderer::SpriteVertexVboRows,
|
||||
1,
|
||||
fX,
|
||||
fY,
|
||||
tileIdx + i,
|
||||
flipX,
|
||||
vbo,
|
||||
ebo);
|
||||
++i;
|
||||
};
|
||||
if (!flipX) {
|
||||
for (auto yIt = 0; yIt < static_cast<int>(dim.y); ++yIt) {
|
||||
for (auto xIt = 0u; xIt < dim.x; ++xIt) {
|
||||
set(static_cast<int>(xIt), static_cast<int>(yIt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto yIt = 0u; yIt < dim.y; ++yIt) {
|
||||
for (auto xIt = dim.x - 1; xIt < ~0u; --xIt) {
|
||||
set(static_cast<int>(xIt), static_cast<int>(yIt));
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.spriteBlocks.updated = true;
|
||||
void showSprite(Context &ctx, uint_t idx) noexcept {
|
||||
auto &s = ctx.spriteStates[idx];
|
||||
s.enabled = true;
|
||||
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 &bg = ctx.cbbs[z];
|
||||
const auto vbo = &bg.vertices[i * renderer::BgVertexVboLength];
|
||||
const auto ebo = &bg.elements[i * renderer::BgVertexEboLength];
|
||||
renderer::setTileBufferObject(
|
||||
static_cast<uint_t>(i * renderer::BgVertexVboRows),
|
||||
static_cast<float>(x),
|
||||
static_cast<float>(y),
|
||||
static_cast<float>(tile),
|
||||
vbo,
|
||||
ebo);
|
||||
bg.updated = true;
|
||||
void setSprite(Context &ctx, uint_t idx, Sprite const&sprite) noexcept {
|
||||
auto &s = ctx.spriteStates[idx];
|
||||
s = sprite;
|
||||
renderer::setSprite(ctx, idx, s);
|
||||
}
|
||||
|
||||
uint_t spriteCount(Context &ctx) noexcept {
|
||||
return ctx.spriteCount;
|
||||
}
|
||||
|
||||
namespace gl {
|
||||
@ -611,11 +699,17 @@ ox::Size drawSize(int scale) noexcept {
|
||||
|
||||
void draw(core::Context &ctx, ox::Size const&renderSz) noexcept {
|
||||
glViewport(0, 0, renderSz.width, renderSz.height);
|
||||
glutils::clearScreen();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
renderer::drawBackgrounds(ctx, renderSz);
|
||||
if (ctx.spriteBlocks.tex) {
|
||||
renderer::drawSprites(ctx, renderSz);
|
||||
}
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void draw(core::Context &ctx, int scale) noexcept {
|
||||
|
@ -17,13 +17,12 @@ namespace nostalgia::core::renderer {
|
||||
constexpr uint64_t TileRows = 128;
|
||||
constexpr uint64_t TileColumns = 128;
|
||||
constexpr uint64_t TileCount = TileRows * TileColumns;
|
||||
constexpr uint64_t SpriteCount = 128;
|
||||
constexpr uint64_t BgVertexVboRows = 4;
|
||||
constexpr uint64_t BgVertexVboRowLength = 5;
|
||||
constexpr uint64_t BgVertexVboRowLength = 7;
|
||||
constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength;
|
||||
constexpr uint64_t BgVertexEboLength = 6;
|
||||
constexpr uint64_t SpriteVertexVboRows = 256;
|
||||
constexpr uint64_t SpriteVertexVboRowLength = 5;
|
||||
constexpr uint64_t SpriteVertexVboRows = 4;
|
||||
constexpr uint64_t SpriteVertexVboRowLength = 6;
|
||||
constexpr uint64_t SpriteVertexVboLength = SpriteVertexVboRows * SpriteVertexVboRowLength;
|
||||
constexpr uint64_t SpriteVertexEboLength = 6;
|
||||
|
||||
@ -37,21 +36,18 @@ struct CBB: public glutils::BufferSet {
|
||||
|
||||
struct SpriteBlockset: public glutils::BufferSet {
|
||||
bool updated = false;
|
||||
constexpr SpriteBlockset() noexcept {
|
||||
vertices.resize(SpriteCount * SpriteVertexVboLength);
|
||||
elements.resize(SpriteCount * SpriteVertexEboLength);
|
||||
constexpr SpriteBlockset(uint64_t spriteCount, uint64_t blocksPerSprite) noexcept {
|
||||
vertices.resize(spriteCount * SpriteVertexVboLength * blocksPerSprite);
|
||||
elements.resize(spriteCount * SpriteVertexEboLength * blocksPerSprite);
|
||||
}
|
||||
};
|
||||
|
||||
struct Background {
|
||||
float priority = 0;
|
||||
bool enabled = false;
|
||||
unsigned cbbIdx = 0;
|
||||
};
|
||||
|
||||
struct Sprite {
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
class Drawer: public turbine::gl::Drawer {
|
||||
private:
|
||||
Context &m_ctx;
|
||||
|
@ -10,7 +10,7 @@ AddSubSheetCommand::AddSubSheetCommand(
|
||||
TileSheet &img,
|
||||
TileSheet::SubSheetIdx parentIdx) noexcept:
|
||||
m_img(img), m_parentIdx(std::move(parentIdx)) {
|
||||
auto &parent = m_img.getSubSheet(m_parentIdx);
|
||||
auto &parent = getSubSheet(m_img, m_parentIdx);
|
||||
if (!parent.subsheets.empty()) {
|
||||
auto idx = m_parentIdx;
|
||||
idx.emplace_back(parent.subsheets.size());
|
||||
@ -25,7 +25,7 @@ AddSubSheetCommand::AddSubSheetCommand(
|
||||
}
|
||||
|
||||
void AddSubSheetCommand::redo() noexcept {
|
||||
auto &parent = m_img.getSubSheet(m_parentIdx);
|
||||
auto &parent = getSubSheet(m_img, m_parentIdx);
|
||||
if (m_addedSheets.size() < 2) {
|
||||
auto i = parent.subsheets.size();
|
||||
parent.subsheets.emplace_back(m_img.idIt++, ox::sfmt("Subsheet {}", i), 1, 1, m_img.bpp);
|
||||
@ -38,7 +38,7 @@ void AddSubSheetCommand::redo() noexcept {
|
||||
}
|
||||
|
||||
void AddSubSheetCommand::undo() noexcept {
|
||||
auto &parent = m_img.getSubSheet(m_parentIdx);
|
||||
auto &parent = getSubSheet(m_img, m_parentIdx);
|
||||
if (parent.subsheets.size() == 2) {
|
||||
auto s = parent.subsheets[0];
|
||||
parent.rows = s.rows;
|
||||
@ -47,7 +47,7 @@ void AddSubSheetCommand::undo() noexcept {
|
||||
parent.subsheets.clear();
|
||||
} else {
|
||||
for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) {
|
||||
oxLogError(m_img.rmSubSheet(*idx));
|
||||
oxLogError(rmSubSheet(m_img, *idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,27 +31,27 @@ CutPasteCommand::CutPasteCommand(
|
||||
m_commandId(commandId),
|
||||
m_img(img),
|
||||
m_subSheetIdx(std::move(subSheetIdx)) {
|
||||
const auto &subsheet = m_img.getSubSheet(m_subSheetIdx);
|
||||
const auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
for (const auto &p : cb.pixels()) {
|
||||
const auto dstPt = p.pt + dstStart;
|
||||
if (dstPt.x <= dstEnd.x && dstPt.y <= dstEnd.y) {
|
||||
const auto idx = subsheet.idx(dstPt);
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), p.colorIdx, subsheet.getPixel(m_img.bpp, idx));
|
||||
const auto idx = core::idx(subsheet, dstPt);
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), p.colorIdx, getPixel(subsheet, m_img.bpp, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CutPasteCommand::redo() noexcept {
|
||||
auto &subsheet = m_img.getSubSheet(m_subSheetIdx);
|
||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
for (const auto &c : m_changes) {
|
||||
subsheet.setPixel(m_img.bpp, c.idx, static_cast<uint8_t>(c.newPalIdx));
|
||||
setPixel(subsheet, m_img.bpp, c.idx, static_cast<uint8_t>(c.newPalIdx));
|
||||
}
|
||||
}
|
||||
|
||||
void CutPasteCommand::undo() noexcept {
|
||||
auto &subsheet = m_img.getSubSheet(m_subSheetIdx);
|
||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
for (const auto &c : m_changes) {
|
||||
subsheet.setPixel(m_img.bpp, c.idx, static_cast<uint8_t>(c.oldPalIdx));
|
||||
setPixel(subsheet, m_img.bpp, c.idx, static_cast<uint8_t>(c.oldPalIdx));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ core::DeleteTilesCommand::DeleteTilesCommand(
|
||||
m_deletedPixels.resize(m_deleteSz);
|
||||
// copy pixels to be erased
|
||||
{
|
||||
auto &s = m_img.getSubSheet(m_idx);
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
auto dst = m_deletedPixels.data();
|
||||
auto src = p.data() + m_deletePos;
|
||||
@ -29,7 +29,7 @@ core::DeleteTilesCommand::DeleteTilesCommand(
|
||||
}
|
||||
|
||||
void core::DeleteTilesCommand::redo() noexcept {
|
||||
auto &s = m_img.getSubSheet(m_idx);
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
auto srcPos = m_deletePos + m_deleteSz;
|
||||
const auto src = p.data() + srcPos;
|
||||
@ -40,7 +40,7 @@ void core::DeleteTilesCommand::redo() noexcept {
|
||||
}
|
||||
|
||||
void DeleteTilesCommand::undo() noexcept {
|
||||
auto &s = m_img.getSubSheet(m_idx);
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
const auto src = p.data() + m_deletePos;
|
||||
const auto dst1 = p.data() + m_deletePos + m_deleteSz;
|
||||
|
@ -14,8 +14,8 @@ DrawCommand::DrawCommand(
|
||||
m_img(img),
|
||||
m_subSheetIdx(std::move(subSheetIdx)),
|
||||
m_palIdx(palIdx) {
|
||||
auto &subsheet = m_img.getSubSheet(m_subSheetIdx);
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), subsheet.getPixel(m_img.bpp, idx));
|
||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), getPixel(subsheet, m_img.bpp, idx));
|
||||
}
|
||||
|
||||
DrawCommand::DrawCommand(
|
||||
@ -26,22 +26,22 @@ DrawCommand::DrawCommand(
|
||||
m_img(img),
|
||||
m_subSheetIdx(std::move(subSheetIdx)),
|
||||
m_palIdx(palIdx) {
|
||||
auto &subsheet = m_img.getSubSheet(m_subSheetIdx);
|
||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
for (const auto idx : idxList) {
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), subsheet.getPixel(m_img.bpp, idx));
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), getPixel(subsheet, m_img.bpp, idx));
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawCommand::append(std::size_t idx) noexcept {
|
||||
auto &subsheet = m_img.getSubSheet(m_subSheetIdx);
|
||||
if (m_changes.back().value->idx != idx && subsheet.getPixel(m_img.bpp, idx) != m_palIdx) {
|
||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
if (m_changes.back().value->idx != idx && getPixel(subsheet, m_img.bpp, idx) != m_palIdx) {
|
||||
// duplicate entries are bad
|
||||
auto existing = ox::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) {
|
||||
return c.idx == idx;
|
||||
});
|
||||
if (existing == m_changes.cend()) {
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), subsheet.getPixel(m_img.bpp, idx));
|
||||
subsheet.setPixel(m_img.bpp, idx, static_cast<uint8_t>(m_palIdx));
|
||||
m_changes.emplace_back(static_cast<uint32_t>(idx), getPixel(subsheet, m_img.bpp, idx));
|
||||
setPixel(subsheet, m_img.bpp, idx, static_cast<uint8_t>(m_palIdx));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -57,16 +57,16 @@ bool DrawCommand::append(const ox::Vector<std::size_t> &idxList) noexcept {
|
||||
}
|
||||
|
||||
void DrawCommand::redo() noexcept {
|
||||
auto &subsheet = m_img.getSubSheet(m_subSheetIdx);
|
||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
for (const auto &c : m_changes) {
|
||||
subsheet.setPixel(m_img.bpp, c.idx, static_cast<uint8_t>(m_palIdx));
|
||||
setPixel(subsheet, m_img.bpp, c.idx, static_cast<uint8_t>(m_palIdx));
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCommand::undo() noexcept {
|
||||
auto &subsheet = m_img.getSubSheet(m_subSheetIdx);
|
||||
auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
|
||||
for (const auto &c : m_changes) {
|
||||
subsheet.setPixel(m_img.bpp, c.idx, static_cast<uint8_t>(c.oldPalIdx));
|
||||
setPixel(subsheet, m_img.bpp, c.idx, static_cast<uint8_t>(c.oldPalIdx));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ core::InsertTilesCommand::InsertTilesCommand(
|
||||
m_deletedPixels.resize(m_insertCnt);
|
||||
// copy pixels to be erased
|
||||
{
|
||||
auto &s = m_img.getSubSheet(m_idx);
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
auto dst = m_deletedPixels.data();
|
||||
auto src = p.data() + p.size() - m_insertCnt;
|
||||
@ -29,7 +29,7 @@ core::InsertTilesCommand::InsertTilesCommand(
|
||||
}
|
||||
|
||||
void InsertTilesCommand::redo() noexcept {
|
||||
auto &s = m_img.getSubSheet(m_idx);
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
auto dstPos = m_insertPos + m_insertCnt;
|
||||
const auto dst = p.data() + dstPos;
|
||||
@ -39,7 +39,7 @@ void InsertTilesCommand::redo() noexcept {
|
||||
}
|
||||
|
||||
void InsertTilesCommand::undo() noexcept {
|
||||
auto &s = m_img.getSubSheet(m_idx);
|
||||
auto &s = getSubSheet(m_img, m_idx);
|
||||
auto &p = s.pixels;
|
||||
const auto srcIdx = m_insertPos + m_insertCnt;
|
||||
const auto src = p.data() + srcIdx;
|
||||
|
@ -11,17 +11,17 @@ core::RmSubSheetCommand::RmSubSheetCommand(TileSheet &img, TileSheet::SubSheetId
|
||||
m_idx(std::move(idx)),
|
||||
m_parentIdx(m_idx) {
|
||||
m_parentIdx.pop_back();
|
||||
auto &parent = m_img.getSubSheet(m_parentIdx);
|
||||
auto &parent = getSubSheet(m_img, m_parentIdx);
|
||||
m_sheet = parent.subsheets[*m_idx.back().value];
|
||||
}
|
||||
|
||||
void RmSubSheetCommand::redo() noexcept {
|
||||
auto &parent = m_img.getSubSheet(m_parentIdx);
|
||||
auto &parent = getSubSheet(m_img, m_parentIdx);
|
||||
oxLogError(parent.subsheets.erase(*m_idx.back().value).error);
|
||||
}
|
||||
|
||||
void RmSubSheetCommand::undo() noexcept {
|
||||
auto &parent = m_img.getSubSheet(m_parentIdx);
|
||||
auto &parent = getSubSheet(m_img, m_parentIdx);
|
||||
auto i = *m_idx.back().value;
|
||||
parent.subsheets.insert(i, m_sheet);
|
||||
}
|
||||
|
@ -14,22 +14,22 @@ core::UpdateSubSheetCommand::UpdateSubSheetCommand(
|
||||
int rows) noexcept:
|
||||
m_img(img),
|
||||
m_idx(std::move(idx)),
|
||||
m_sheet(m_img.getSubSheet(m_idx)),
|
||||
m_sheet(getSubSheet(m_img, m_idx)),
|
||||
m_newName(std::move(name)),
|
||||
m_newCols(cols),
|
||||
m_newRows(rows) {
|
||||
}
|
||||
|
||||
void UpdateSubSheetCommand::redo() noexcept {
|
||||
auto &sheet = m_img.getSubSheet(m_idx);
|
||||
auto &sheet = getSubSheet(m_img, m_idx);
|
||||
sheet.name = m_newName;
|
||||
sheet.columns = m_newCols;
|
||||
sheet.rows = m_newRows;
|
||||
oxLogError(sheet.setPixelCount(m_img.bpp, static_cast<std::size_t>(PixelsPerTile * m_newCols * m_newRows)));
|
||||
oxLogError(setPixelCount(sheet, m_img.bpp, static_cast<std::size_t>(PixelsPerTile * m_newCols * m_newRows)));
|
||||
}
|
||||
|
||||
void UpdateSubSheetCommand::undo() noexcept {
|
||||
auto &sheet = m_img.getSubSheet(m_idx);
|
||||
auto &sheet = getSubSheet(m_img, m_idx);
|
||||
sheet = m_sheet;
|
||||
}
|
||||
|
||||
|
@ -12,33 +12,64 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
template<bool alpha = false>
|
||||
ox::Error toPngFile(
|
||||
ox::CStringView const&path, TileSheet::SubSheet const&s, Palette const&pal, int8_t bpp) noexcept {
|
||||
ox::Vector<uint8_t> pixels;
|
||||
s.readPixelsTo(&pixels, bpp);
|
||||
const unsigned rows = s.rows == -1 ?
|
||||
static_cast<unsigned>(pixels.size()) / PixelsPerTile : static_cast<unsigned>(s.rows);
|
||||
const unsigned cols = s.columns == -1 ? 1 : static_cast<unsigned>(s.columns);
|
||||
const auto width = cols * TileWidth;
|
||||
const auto height = rows * TileHeight;
|
||||
constexpr auto bytesPerPixel = alpha ? 4 : 3;
|
||||
ox::Vector<unsigned char> outData(pixels.size() * bytesPerPixel);
|
||||
for (auto idx = 0; const auto colorIdx : pixels) {
|
||||
const auto pt = idxToPt(idx, static_cast<int>(cols));
|
||||
const auto i = static_cast<unsigned>(pt.y * static_cast<int>(width) + pt.x) * bytesPerPixel;
|
||||
const auto c = pal.colors[colorIdx];
|
||||
outData[i + 0] = red32(c);
|
||||
outData[i + 1] = green32(c);
|
||||
outData[i + 2] = blue32(c);
|
||||
if constexpr(alpha) {
|
||||
outData[i + 3] = colorIdx ? 255 : 0;
|
||||
static ox::Vector<uint32_t> normalizePixelSizes(
|
||||
ox::Vector<uint8_t> const&inPixels,
|
||||
int const bpp) noexcept {
|
||||
uint_t const bytesPerTile = bpp == 8 ? PixelsPerTile : PixelsPerTile / 2;
|
||||
ox::Vector<uint32_t> outPixels;
|
||||
if (bytesPerTile == 64) { // 8 BPP
|
||||
outPixels.resize(inPixels.size());
|
||||
for (std::size_t i = 0; i < inPixels.size(); ++i) {
|
||||
outPixels[i] = inPixels[i];
|
||||
}
|
||||
++idx;
|
||||
} else { // 4 BPP
|
||||
outPixels.resize(inPixels.size() * 2);
|
||||
for (std::size_t i = 0; i < inPixels.size(); ++i) {
|
||||
outPixels[i * 2 + 0] = inPixels[i] & 0xF;
|
||||
outPixels[i * 2 + 1] = inPixels[i] >> 4;
|
||||
}
|
||||
constexpr auto fmt = alpha ? LCT_RGBA : LCT_RGB;
|
||||
}
|
||||
return outPixels;
|
||||
}
|
||||
|
||||
static ox::Vector<uint32_t> normalizePixelArrangement(
|
||||
ox::Vector<uint32_t> const&inPixels,
|
||||
int cols,
|
||||
int scale) {
|
||||
auto const scalePt = ox::Point{scale, scale};
|
||||
auto const width = cols * TileWidth;
|
||||
auto const height = static_cast<int>(inPixels.size()) / width;
|
||||
auto const dstWidth = width * scale;
|
||||
ox::Vector<uint32_t> outPixels(static_cast<size_t>((width * scale) * (height * scale)));
|
||||
for (std::size_t dstIdx = 0; dstIdx < outPixels.size(); ++dstIdx) {
|
||||
auto const dstPt = ox::Point{
|
||||
static_cast<int>(dstIdx) % dstWidth,
|
||||
static_cast<int>(dstIdx) / dstWidth};
|
||||
auto const srcPt = dstPt / scalePt;
|
||||
auto const srcIdx = ptToIdx(srcPt, cols);
|
||||
outPixels[dstIdx] = inPixels[srcIdx];
|
||||
}
|
||||
return outPixels;
|
||||
}
|
||||
|
||||
static ox::Error toPngFile(
|
||||
ox::CStringView const&path,
|
||||
ox::Vector<uint32_t> &&pixels,
|
||||
Palette const&pal,
|
||||
unsigned width,
|
||||
unsigned height) noexcept {
|
||||
for (auto &c : pixels) {
|
||||
c = color32(pal.color(c)) | static_cast<Color32>(0XFF << 24);
|
||||
}
|
||||
constexpr auto fmt = LCT_RGBA;
|
||||
return OxError(static_cast<ox::ErrorCode>(
|
||||
lodepng_encode_file(path.c_str(), outData.data(), width, height, fmt, 8)));
|
||||
lodepng_encode_file(
|
||||
path.c_str(),
|
||||
reinterpret_cast<uint8_t const*>(pixels.data()),
|
||||
width,
|
||||
height,
|
||||
fmt,
|
||||
8)));
|
||||
}
|
||||
|
||||
TileSheetEditorImGui::TileSheetEditorImGui(turbine::Context &ctx, ox::CRStringView path):
|
||||
@ -50,11 +81,12 @@ TileSheetEditorImGui::TileSheetEditorImGui(turbine::Context &ctx, ox::CRStringVi
|
||||
// connect signal/slots
|
||||
undoStack()->changeTriggered.connect(this, &TileSheetEditorImGui::markUnsavedChanges);
|
||||
m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet);
|
||||
m_exportMenu.inputSubmitted.connect(this, &TileSheetEditorImGui::exportSubhseetToPng);
|
||||
m_model.paletteChanged.connect(this, &TileSheetEditorImGui::setPaletteSelection);
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::exportFile() {
|
||||
exportSubhseetToPng();
|
||||
m_exportMenu.show();
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::cut() {
|
||||
@ -75,9 +107,11 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
|
||||
}
|
||||
if (key == turbine::Key::Escape) {
|
||||
m_subsheetEditor.close();
|
||||
m_exportMenu.close();
|
||||
}
|
||||
auto pal = m_model.pal();
|
||||
if (pal) {
|
||||
auto const popupOpen = m_subsheetEditor.isOpen() && m_exportMenu.isOpen();
|
||||
auto const pal = m_model.pal();
|
||||
if (pal && !popupOpen) {
|
||||
const auto colorCnt = pal->colors.size();
|
||||
if (key == turbine::Key::Alpha_D) {
|
||||
m_tool = Tool::Draw;
|
||||
@ -158,7 +192,7 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("-", btnSize)) {
|
||||
const auto &activeSubsheetIdx = m_model.activeSubSheetIdx();
|
||||
if (activeSubsheetIdx.size() > 0) {
|
||||
if (!activeSubsheetIdx.empty()) {
|
||||
m_model.rmSubsheet(activeSubsheetIdx);
|
||||
}
|
||||
}
|
||||
@ -168,16 +202,17 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export", ImVec2(51, btnHeight))) {
|
||||
exportSubhseetToPng();
|
||||
m_exportMenu.show();
|
||||
}
|
||||
TileSheet::SubSheetIdx path;
|
||||
static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
|
||||
if (ImGui::BeginTable("Subsheets", 3, flags)) {
|
||||
if (ImGui::BeginTable("Subsheets", 4, flags)) {
|
||||
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
|
||||
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
|
||||
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
|
||||
ImGui::TableHeadersRow();
|
||||
drawSubsheetSelector(&m_view.img().subsheet, &path);
|
||||
drawSubsheetSelector(m_view.img().subsheet, path);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
@ -185,33 +220,37 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
|
||||
}
|
||||
ImGui::EndChild();
|
||||
m_subsheetEditor.draw();
|
||||
m_exportMenu.draw();
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, TileSheet::SubSheetIdx *path) {
|
||||
void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet &subsheet, TileSheet::SubSheetIdx &path) {
|
||||
constexpr auto indentReduce = 14;
|
||||
ImGui::TableNextRow(0, 5);
|
||||
using Str = ox::BasicString<100>;
|
||||
auto pathStr = ox::join<Str>("##", *path).value;
|
||||
auto lbl = ox::sfmt<Str>("{}##{}", subsheet->name, pathStr);
|
||||
const auto rowSelected = *path == m_model.activeSubSheetIdx();
|
||||
auto pathStr = ox::join<Str>("##", path).value;
|
||||
auto lbl = ox::sfmt<Str>("{}##{}", subsheet.name, pathStr);
|
||||
const auto rowSelected = path == m_model.activeSubSheetIdx();
|
||||
const auto flags = ImGuiTreeNodeFlags_SpanFullWidth
|
||||
| ImGuiTreeNodeFlags_OpenOnArrow
|
||||
| ImGuiTreeNodeFlags_DefaultOpen
|
||||
| (subsheet->subsheets.empty() ? ImGuiTreeNodeFlags_Leaf : 0)
|
||||
| (subsheet.subsheets.empty() ? ImGuiTreeNodeFlags_Leaf : 0)
|
||||
| (rowSelected ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
ImGui::TableNextColumn();
|
||||
const auto open = ImGui::TreeNodeEx(lbl.c_str(), flags);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::IsItemClicked()) {
|
||||
m_model.setActiveSubsheet(*path);
|
||||
m_model.setActiveSubsheet(path);
|
||||
}
|
||||
if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) {
|
||||
showSubsheetEditor();
|
||||
}
|
||||
if (subsheet->subsheets.empty()) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", subsheet->columns);
|
||||
ImGui::Text("%d", subsheet.id);
|
||||
if (subsheet.subsheets.empty()) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", subsheet->rows);
|
||||
ImGui::Text("%d", subsheet.columns);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", subsheet.rows);
|
||||
} else {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("--");
|
||||
@ -219,12 +258,14 @@ void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, T
|
||||
ImGui::Text("--");
|
||||
}
|
||||
if (open) {
|
||||
for (auto i = 0ul; auto &child : subsheet->subsheets) {
|
||||
path->push_back(i);
|
||||
for (auto i = 0ul; auto &child : subsheet.subsheets) {
|
||||
path.push_back(i);
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
drawSubsheetSelector(&child, path);
|
||||
ImGui::Indent(-indentReduce);
|
||||
drawSubsheetSelector(child, path);
|
||||
ImGui::Indent(indentReduce);
|
||||
ImGui::PopID();
|
||||
path->pop_back();
|
||||
path.pop_back();
|
||||
++i;
|
||||
}
|
||||
ImGui::TreePop();
|
||||
@ -244,26 +285,33 @@ ox::Error TileSheetEditorImGui::saveItem() noexcept {
|
||||
|
||||
void TileSheetEditorImGui::showSubsheetEditor() noexcept {
|
||||
const auto sheet = m_model.activeSubSheet();
|
||||
if (sheet->subsheets.size()) {
|
||||
if (!sheet->subsheets.empty()) {
|
||||
m_subsheetEditor.show(sheet->name, -1, -1);
|
||||
} else {
|
||||
m_subsheetEditor.show(sheet->name, sheet->columns, sheet->rows);
|
||||
}
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::exportSubhseetToPng() noexcept {
|
||||
auto [path, err] = studio::saveFile({{"PNG", "png"}});
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
ox::Error TileSheetEditorImGui::exportSubhseetToPng(int scale) noexcept {
|
||||
oxRequire(path, studio::saveFile({{"PNG", "png"}}));
|
||||
// subsheet to png
|
||||
const auto &img = m_model.img();
|
||||
const auto &s = *m_model.activeSubSheet();
|
||||
const auto &pal = m_model.pal();
|
||||
err = toPngFile(path, s, *pal, img.bpp);
|
||||
auto const&img = m_model.img();
|
||||
auto const&s = *m_model.activeSubSheet();
|
||||
auto const&pal = m_model.pal();
|
||||
auto const width = s.columns * TileWidth;
|
||||
auto const height = s.rows * TileHeight;
|
||||
auto pixels = normalizePixelSizes(s.pixels, img.bpp);
|
||||
pixels = normalizePixelArrangement(pixels, s.columns, scale);
|
||||
auto const err = toPngFile(
|
||||
path,
|
||||
std::move(pixels),
|
||||
*pal,
|
||||
static_cast<unsigned>(width * scale),
|
||||
static_cast<unsigned>(height * scale));
|
||||
if (err) {
|
||||
oxErrorf("Tilesheet export failed: {}", toStr(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
|
||||
@ -420,13 +468,13 @@ void TileSheetEditorImGui::SubSheetEditor::draw() noexcept {
|
||||
ImGui::InputInt("Columns", &m_cols);
|
||||
ImGui::InputInt("Rows", &m_rows);
|
||||
}
|
||||
if (ImGui::Button("OK")) {
|
||||
if (ImGui::Button("OK", ImVec2{50, 20})) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_show = false;
|
||||
inputSubmitted.emit(m_name, m_cols, m_rows);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
if (ImGui::Button("Cancel", ImVec2{50, 20})) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_show = false;
|
||||
}
|
||||
@ -438,4 +486,34 @@ void TileSheetEditorImGui::SubSheetEditor::close() noexcept {
|
||||
m_show = false;
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::ExportMenu::draw() noexcept {
|
||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||
constexpr auto popupName = "Export Tile Sheet";
|
||||
if (!m_show) {
|
||||
return;
|
||||
}
|
||||
ImGui::OpenPopup(popupName);
|
||||
constexpr auto popupHeight = 80.f;
|
||||
ImGui::SetNextWindowSize(ImVec2(235, popupHeight));
|
||||
if (ImGui::BeginPopupModal(popupName, &m_show, modalFlags)) {
|
||||
ImGui::InputInt("Scale", &m_scale);
|
||||
m_scale = ox::clamp(m_scale, 1, 50);
|
||||
if (ImGui::Button("OK", ImVec2{50, 20})) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_show = false;
|
||||
inputSubmitted.emit(m_scale);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel", ImVec2{50, 20})) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_show = false;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void TileSheetEditorImGui::ExportMenu::close() noexcept {
|
||||
m_show = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,11 +41,28 @@ class TileSheetEditorImGui: public studio::Editor {
|
||||
}
|
||||
void draw() noexcept;
|
||||
void close() noexcept;
|
||||
[[nodiscard]]
|
||||
inline bool isOpen() const noexcept { return m_show; }
|
||||
};
|
||||
class ExportMenu {
|
||||
int m_scale = 0;
|
||||
bool m_show = false;
|
||||
public:
|
||||
ox::Signal<ox::Error(int scale)> inputSubmitted;
|
||||
constexpr void show() noexcept {
|
||||
m_show = true;
|
||||
m_scale = 5;
|
||||
}
|
||||
void draw() noexcept;
|
||||
void close() noexcept;
|
||||
[[nodiscard]]
|
||||
inline bool isOpen() const noexcept { return m_show; }
|
||||
};
|
||||
std::size_t m_selectedPaletteIdx = 0;
|
||||
turbine::Context &m_ctx;
|
||||
ox::Vector<ox::String> m_paletteList;
|
||||
SubSheetEditor m_subsheetEditor;
|
||||
ExportMenu m_exportMenu;
|
||||
glutils::FrameBuffer m_framebuffer;
|
||||
TileSheetEditorView m_view;
|
||||
TileSheetEditorModel &m_model;
|
||||
@ -70,7 +87,7 @@ class TileSheetEditorImGui: public studio::Editor {
|
||||
|
||||
void draw(turbine::Context&) noexcept override;
|
||||
|
||||
void drawSubsheetSelector(TileSheet::SubSheet*, TileSheet::SubSheetIdx *path);
|
||||
void drawSubsheetSelector(TileSheet::SubSheet&, TileSheet::SubSheetIdx &path);
|
||||
|
||||
[[nodiscard]]
|
||||
static ox::Vec2 clickPos(ImVec2 const&winPos, ox::Vec2 clickPos) noexcept;
|
||||
@ -81,7 +98,7 @@ class TileSheetEditorImGui: public studio::Editor {
|
||||
private:
|
||||
void showSubsheetEditor() noexcept;
|
||||
|
||||
void exportSubhseetToPng() noexcept;
|
||||
ox::Error exportSubhseetToPng(int scale) noexcept;
|
||||
|
||||
void drawTileSheet(ox::Vec2 const&fbSize) noexcept;
|
||||
|
||||
|
@ -47,8 +47,8 @@ void TileSheetEditorModel::cut() {
|
||||
for (int y = m_selectionBounds.y; y <= m_selectionBounds.y2(); ++y) {
|
||||
for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) {
|
||||
auto pt = ox::Point(x, y);
|
||||
const auto idx = s->idx(pt);
|
||||
const auto c = s->getPixel(m_img.bpp, idx);
|
||||
const auto idx = core::idx(*s, pt);
|
||||
const auto c = getPixel(*s, m_img.bpp, idx);
|
||||
pt.x -= m_selectionBounds.x;
|
||||
pt.y -= m_selectionBounds.y;
|
||||
cb->addPixel(pt, c);
|
||||
@ -67,8 +67,8 @@ void TileSheetEditorModel::copy() {
|
||||
for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) {
|
||||
auto pt = ox::Point(x, y);
|
||||
const auto s = activeSubSheet();
|
||||
const auto idx = s->idx(pt);
|
||||
const auto c = s->getPixel(m_img.bpp, idx);
|
||||
const auto idx = core::idx(*s, pt);
|
||||
const auto c = getPixel(*s, m_img.bpp, idx);
|
||||
pt.x -= m_selectionBounds.x;
|
||||
pt.y -= m_selectionBounds.y;
|
||||
cb->addPixel(pt, c);
|
||||
@ -115,14 +115,14 @@ ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept {
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept {
|
||||
const auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx);
|
||||
const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
|
||||
return;
|
||||
}
|
||||
const auto idx = activeSubSheet.idx(pt);
|
||||
const auto idx = core::idx(activeSubSheet, pt);
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idx);
|
||||
} else if (activeSubSheet.getPixel(m_img.bpp, idx) != palIdx) {
|
||||
} else if (getPixel(activeSubSheet, m_img.bpp, idx) != palIdx) {
|
||||
pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
|
||||
}
|
||||
}
|
||||
@ -147,7 +147,7 @@ void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::si
|
||||
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, const ox::StringView &name, int cols, int rows) noexcept {
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept {
|
||||
pushCommand(ox::make<UpdateSubSheetCommand>(m_img, idx, ox::String(name), cols, rows));
|
||||
return {};
|
||||
}
|
||||
@ -158,16 +158,16 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n
|
||||
}
|
||||
|
||||
void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
|
||||
const auto &s = m_img.getSubSheet(m_activeSubsSheetIdx);
|
||||
const auto &s = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
// build idx list
|
||||
ox::Array<bool, PixelsPerTile> updateMap = {};
|
||||
const auto oldColor = s.getPixel(m_img.bpp, pt);
|
||||
const auto oldColor = getPixel(s, m_img.bpp, pt);
|
||||
if (pt.x >= s.columns * TileWidth || pt.y >= s.rows * TileHeight) {
|
||||
return;
|
||||
}
|
||||
getFillPixels(updateMap.data(), pt, oldColor);
|
||||
ox::Vector<std::size_t> idxList;
|
||||
auto i = s.idx(pt) / PixelsPerTile * PixelsPerTile;
|
||||
auto i = core::idx(s, pt) / PixelsPerTile * PixelsPerTile;
|
||||
for (auto u : updateMap) {
|
||||
if (u) {
|
||||
idxList.emplace_back(i);
|
||||
@ -177,7 +177,7 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
|
||||
// do updates to sheet
|
||||
if (m_ongoingDrawCommand) {
|
||||
m_updated = m_updated || m_ongoingDrawCommand->append(idxList);
|
||||
} else if (s.getPixel(m_img.bpp, pt) != palIdx) {
|
||||
} else if (getPixel(s, m_img.bpp, pt) != palIdx) {
|
||||
pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
|
||||
}
|
||||
}
|
||||
@ -213,7 +213,7 @@ bool TileSheetEditorModel::updated() const noexcept {
|
||||
return m_updated;
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd) noexcept {
|
||||
ox::Error TileSheetEditorModel::markUpdatedCmdId(studio::UndoCommand const*cmd) noexcept {
|
||||
m_updated = true;
|
||||
const auto cmdId = cmd->commandId();
|
||||
if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) {
|
||||
@ -221,7 +221,7 @@ ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd)
|
||||
paletteChanged.emit();
|
||||
}
|
||||
auto tsCmd = dynamic_cast<const TileSheetCommand*>(cmd);
|
||||
auto idx = m_img.validateSubSheetIdx(tsCmd->subsheetIdx());
|
||||
auto idx = validateSubSheetIdx(m_img, tsCmd->subsheetIdx());
|
||||
if (idx != m_activeSubsSheetIdx) {
|
||||
setActiveSubsheet(idx);
|
||||
}
|
||||
@ -239,7 +239,7 @@ void TileSheetEditorModel::ackUpdate() noexcept {
|
||||
|
||||
ox::Error TileSheetEditorModel::saveFile() noexcept {
|
||||
const auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||
return sctx->project->writeObj(m_path, m_img);
|
||||
return sctx->project->writeObj(m_path, m_img, ox::ClawFormat::Metal);
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept {
|
||||
@ -267,16 +267,16 @@ void TileSheetEditorModel::getFillPixels(bool *pixels, ox::Point const&pt, int o
|
||||
const auto tile = tileIdx(pt);
|
||||
// mark pixels to update
|
||||
pixels[idx % PixelsPerTile] = true;
|
||||
if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && activeSubSheet.getPixel(m_img.bpp, leftIdx) == oldColor) {
|
||||
if (!pixels[leftIdx % PixelsPerTile] && tile == tileIdx(leftPt) && getPixel(activeSubSheet, m_img.bpp, leftIdx) == oldColor) {
|
||||
getFillPixels(pixels, leftPt, oldColor);
|
||||
}
|
||||
if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && activeSubSheet.getPixel(m_img.bpp, rightIdx) == oldColor) {
|
||||
if (!pixels[rightIdx % PixelsPerTile] && tile == tileIdx(rightPt) && getPixel(activeSubSheet, m_img.bpp, rightIdx) == oldColor) {
|
||||
getFillPixels(pixels, rightPt, oldColor);
|
||||
}
|
||||
if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && activeSubSheet.getPixel(m_img.bpp, topIdx) == oldColor) {
|
||||
if (!pixels[topIdx % PixelsPerTile] && tile == tileIdx(topPt) && getPixel(activeSubSheet, m_img.bpp, topIdx) == oldColor) {
|
||||
getFillPixels(pixels, topPt, oldColor);
|
||||
}
|
||||
if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && activeSubSheet.getPixel(m_img.bpp, bottomIdx) == oldColor) {
|
||||
if (!pixels[bottomIdx % PixelsPerTile] && tile == tileIdx(bottomPt) && getPixel(activeSubSheet, m_img.bpp, bottomIdx) == oldColor) {
|
||||
getFillPixels(pixels, bottomPt, oldColor);
|
||||
}
|
||||
}
|
||||
|
@ -61,31 +61,31 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
|
||||
ox::Error setPalette(ox::StringView path) noexcept;
|
||||
|
||||
void drawCommand(const ox::Point &pt, std::size_t palIdx) noexcept;
|
||||
void drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept;
|
||||
|
||||
void endDrawCommand() noexcept;
|
||||
|
||||
void addSubsheet(const TileSheet::SubSheetIdx &parentIdx) noexcept;
|
||||
void addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept;
|
||||
|
||||
void rmSubsheet(const TileSheet::SubSheetIdx &idx) noexcept;
|
||||
void rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
void insertTiles(const TileSheet::SubSheetIdx &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
void insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
|
||||
void deleteTiles(const TileSheet::SubSheetIdx &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
void deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
|
||||
ox::Error updateSubsheet(const TileSheet::SubSheetIdx &idx, const ox::StringView &name, int cols, int rows) noexcept;
|
||||
ox::Error updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept;
|
||||
|
||||
void setActiveSubsheet(const TileSheet::SubSheetIdx&) noexcept;
|
||||
void setActiveSubsheet(TileSheet::SubSheetIdx const&) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const TileSheet::SubSheet *activeSubSheet() const noexcept {
|
||||
auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx);
|
||||
auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
return &activeSubSheet;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
TileSheet::SubSheet *activeSubSheet() noexcept {
|
||||
auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx);
|
||||
auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
|
||||
return &activeSubSheet;
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
[[nodiscard]]
|
||||
bool updated() const noexcept;
|
||||
|
||||
ox::Error markUpdatedCmdId(const studio::UndoCommand *cmd) noexcept;
|
||||
ox::Error markUpdatedCmdId(studio::UndoCommand const*cmd) noexcept;
|
||||
|
||||
ox::Error markUpdated() noexcept;
|
||||
|
||||
@ -118,10 +118,9 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
|
||||
bool pixelSelected(std::size_t idx) const noexcept;
|
||||
|
||||
protected:
|
||||
private:
|
||||
void getFillPixels(bool *pixels, ox::Point const&pt, int oldColor) const noexcept;
|
||||
|
||||
private:
|
||||
void pushCommand(studio::UndoCommand *cmd) noexcept;
|
||||
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ namespace nostalgia::core {
|
||||
|
||||
TileSheetEditorView::TileSheetEditorView(turbine::Context &ctx, ox::StringView path, studio::UndoStack &undoStack):
|
||||
m_model(ctx, path, undoStack),
|
||||
m_pixelsDrawer(&m_model) {
|
||||
m_pixelsDrawer(m_model) {
|
||||
// build shaders
|
||||
oxThrowError(m_pixelsDrawer.buildShader());
|
||||
oxThrowError(m_pixelGridDrawer.buildShader());
|
||||
|
@ -50,11 +50,11 @@ void TileSheetGrid::initBufferSet(ox::Vec2 const&paneSize, TileSheet::SubSheet c
|
||||
const auto pt2Attr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vPt2"));
|
||||
glEnableVertexAttribArray(pt2Attr);
|
||||
glVertexAttribPointer(pt2Attr, 2, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(2 * sizeof(float)));
|
||||
std::bit_cast<void*>(uintptr_t{2 * sizeof(float)}));
|
||||
const auto colorAttr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vColor"));
|
||||
glEnableVertexAttribArray(colorAttr);
|
||||
glVertexAttribPointer(colorAttr, 3, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(4 * sizeof(float)));
|
||||
std::bit_cast<void*>(uintptr_t{4 * sizeof(float)}));
|
||||
}
|
||||
|
||||
void TileSheetGrid::update(ox::Vec2 const&paneSize, TileSheet::SubSheet const&subsheet) noexcept {
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
TileSheetPixels::TileSheetPixels(TileSheetEditorModel *model) noexcept: m_model(model) {
|
||||
TileSheetPixels::TileSheetPixels(TileSheetEditorModel &model) noexcept: m_model(model) {
|
||||
}
|
||||
|
||||
void TileSheetPixels::setPixelSizeMod(float sm) noexcept {
|
||||
@ -47,7 +47,7 @@ void TileSheetPixels::initBufferSet(ox::Vec2 const&paneSize) noexcept {
|
||||
const auto colorAttr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vColor"));
|
||||
glEnableVertexAttribArray(colorAttr);
|
||||
glVertexAttribPointer(colorAttr, 3, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float),
|
||||
reinterpret_cast<void*>(2 * sizeof(float)));
|
||||
std::bit_cast<void*>(uintptr_t{2 * sizeof(float)}));
|
||||
}
|
||||
|
||||
void TileSheetPixels::update(ox::Vec2 const&paneSize) noexcept {
|
||||
@ -94,15 +94,15 @@ void TileSheetPixels::setPixelBufferObject(
|
||||
|
||||
void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept {
|
||||
// set buffer lengths
|
||||
const auto subSheet = m_model->activeSubSheet();
|
||||
const auto pal = m_model->pal();
|
||||
const auto subSheet = m_model.activeSubSheet();
|
||||
const auto pal = m_model.pal();
|
||||
const auto width = subSheet->columns * TileWidth;
|
||||
const auto height = subSheet->rows * TileHeight;
|
||||
const auto pixels = static_cast<unsigned>(width * height);
|
||||
m_bufferSet.vertices.resize(pixels * VertexVboLength);
|
||||
m_bufferSet.elements.resize(pixels * VertexEboLength);
|
||||
// set pixels
|
||||
subSheet->walkPixels(m_model->img().bpp, [&](std::size_t i, uint8_t p) {
|
||||
walkPixels(*subSheet, m_model.img().bpp, [&](std::size_t i, uint8_t p) {
|
||||
auto color = pal->color(p);
|
||||
const auto pt = idxToPt(static_cast<int>(i), subSheet->columns);
|
||||
const auto fx = static_cast<float>(pt.x);
|
||||
@ -115,7 +115,7 @@ void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept {
|
||||
if (i * VertexEboLength + VertexEboLength > m_bufferSet.elements.size()) {
|
||||
return;
|
||||
}
|
||||
if (m_model->pixelSelected(i)) {
|
||||
if (m_model.pixelSelected(i)) {
|
||||
const auto r = red16(color) / 2;
|
||||
const auto g = (green16(color) + 20) / 2;
|
||||
const auto b = (blue16(color) + 31) / 2;
|
||||
|
@ -43,10 +43,10 @@ class TileSheetPixels {
|
||||
float m_pixelSizeMod = 1;
|
||||
glutils::GLProgram m_shader;
|
||||
glutils::BufferSet m_bufferSet;
|
||||
const class TileSheetEditorModel *m_model = nullptr;
|
||||
const class TileSheetEditorModel &m_model;
|
||||
|
||||
public:
|
||||
explicit TileSheetPixels(class TileSheetEditorModel *model) noexcept;
|
||||
explicit TileSheetPixels(class TileSheetEditorModel &model) noexcept;
|
||||
|
||||
void setPixelSizeMod(float sm) noexcept;
|
||||
|
||||
|
@ -10,133 +10,47 @@
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
TileSheet::SubSheet::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 = {};
|
||||
other.rows = {};
|
||||
std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept {
|
||||
return ptToIdx(pt, ss.columns);
|
||||
}
|
||||
|
||||
TileSheet::SubSheet::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)) {
|
||||
}
|
||||
|
||||
TileSheet::SubSheet::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)) {
|
||||
}
|
||||
|
||||
TileSheet::SubSheet &TileSheet::SubSheet::operator=(TileSheet::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;
|
||||
}
|
||||
|
||||
std::size_t TileSheet::SubSheet::idx(ox::Point const&pt) const noexcept {
|
||||
return ptToIdx(pt, columns);
|
||||
}
|
||||
|
||||
void TileSheet::SubSheet::readPixelsTo(ox::Vector<uint8_t> *pPixels, int8_t pBpp) const noexcept {
|
||||
if (!subsheets.empty()) {
|
||||
for (auto &s: subsheets) {
|
||||
s.readPixelsTo(pPixels);
|
||||
}
|
||||
} else {
|
||||
if (pBpp == 4) {
|
||||
for (auto p: this->pixels) {
|
||||
pPixels->emplace_back(static_cast<uint8_t>(p & 0b1111));
|
||||
pPixels->emplace_back(static_cast<uint8_t>(p >> 4));
|
||||
}
|
||||
} else {
|
||||
for (auto p: this->pixels) {
|
||||
pPixels->emplace_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TileSheet::SubSheet::readPixelsTo(ox::Vector<uint8_t> *pPixels) const noexcept {
|
||||
if (!subsheets.empty()) {
|
||||
for (auto &s: subsheets) {
|
||||
s.readPixelsTo(pPixels);
|
||||
}
|
||||
} else {
|
||||
for (auto p : this->pixels) {
|
||||
pPixels->emplace_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t TileSheet::SubSheet::unusedPixels() const noexcept {
|
||||
std::size_t childrenSize = 0;
|
||||
for (auto &c : subsheets) {
|
||||
childrenSize += c.size();
|
||||
}
|
||||
return size() - childrenSize;
|
||||
}
|
||||
|
||||
uint8_t TileSheet::SubSheet::getPixel4Bpp(std::size_t idx) const noexcept {
|
||||
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept {
|
||||
if (idx & 1) {
|
||||
return this->pixels[idx / 2] >> 4;
|
||||
return ss.pixels[idx / 2] >> 4;
|
||||
} else {
|
||||
return this->pixels[idx / 2] & 0b0000'1111;
|
||||
return ss.pixels[idx / 2] & 0b0000'1111;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t TileSheet::SubSheet::getPixel8Bpp(std::size_t idx) const noexcept {
|
||||
return this->pixels[idx];
|
||||
uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept {
|
||||
return ss.pixels[idx];
|
||||
}
|
||||
|
||||
uint8_t TileSheet::SubSheet::getPixel(int8_t pBpp, std::size_t idx) const noexcept {
|
||||
uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, std::size_t idx) noexcept {
|
||||
if (pBpp == 4) {
|
||||
return getPixel4Bpp(idx);
|
||||
return getPixel4Bpp(ss, idx);
|
||||
} else {
|
||||
return getPixel8Bpp(idx);
|
||||
return getPixel8Bpp(ss, idx);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t TileSheet::SubSheet::getPixel4Bpp(ox::Point const&pt) const noexcept {
|
||||
const auto idx = ptToIdx(pt, columns);
|
||||
return getPixel4Bpp(idx);
|
||||
uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept {
|
||||
const auto idx = ptToIdx(pt, ss.columns);
|
||||
return getPixel4Bpp(ss, idx);
|
||||
}
|
||||
|
||||
uint8_t TileSheet::SubSheet::getPixel8Bpp(ox::Point const&pt) const noexcept {
|
||||
const auto idx = ptToIdx(pt, columns);
|
||||
return getPixel8Bpp(idx);
|
||||
uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept {
|
||||
const auto idx = ptToIdx(pt, ss.columns);
|
||||
return getPixel8Bpp(ss, idx);
|
||||
}
|
||||
|
||||
uint8_t TileSheet::SubSheet::getPixel(int8_t pBpp, ox::Point const&pt) const noexcept {
|
||||
const auto idx = ptToIdx(pt, columns);
|
||||
return getPixel(pBpp, idx);
|
||||
uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, ox::Point const&pt) noexcept {
|
||||
const auto idx = ptToIdx(pt, ss.columns);
|
||||
return getPixel(ss, pBpp, idx);
|
||||
}
|
||||
|
||||
void TileSheet::SubSheet::setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept {
|
||||
auto &pixel = this->pixels[static_cast<std::size_t>(idx / 2)];
|
||||
void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept {
|
||||
auto &pixel = ss.pixels[static_cast<std::size_t>(idx / 2)];
|
||||
if (pBpp == 4) {
|
||||
if (idx & 1) {
|
||||
pixel = static_cast<uint8_t>((pixel & 0b0000'1111) | (palIdx << 4));
|
||||
@ -148,72 +62,35 @@ void TileSheet::SubSheet::setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) no
|
||||
}
|
||||
}
|
||||
|
||||
void TileSheet::SubSheet::setPixel(int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept {
|
||||
const auto idx = ptToIdx(pt, columns);
|
||||
setPixel(pBpp, idx, palIdx);
|
||||
void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept {
|
||||
const auto idx = ptToIdx(pt, ss.columns);
|
||||
setPixel(ss, pBpp, idx, palIdx);
|
||||
}
|
||||
|
||||
ox::Error TileSheet::SubSheet::setPixelCount(int8_t pBpp, std::size_t cnt) noexcept {
|
||||
ox::Error setPixelCount(TileSheet::SubSheet &ss, int8_t pBpp, std::size_t cnt) noexcept {
|
||||
switch (pBpp) {
|
||||
case 4:
|
||||
pixels.resize(cnt / 2);
|
||||
ss.pixels.resize(cnt / 2);
|
||||
return OxError(0);
|
||||
case 8:
|
||||
pixels.resize(cnt);
|
||||
ss.pixels.resize(cnt);
|
||||
return OxError(0);
|
||||
default:
|
||||
return OxError(1, "Invalid pBpp used for TileSheet::SubSheet::setPixelCount");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned TileSheet::SubSheet::pixelCnt(int8_t pBpp) const noexcept {
|
||||
const auto pixelsSize = static_cast<unsigned>(pixels.size());
|
||||
unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept {
|
||||
const auto pixelsSize = static_cast<unsigned>(ss.pixels.size());
|
||||
return pBpp == 4 ? pixelsSize * 2 : pixelsSize;
|
||||
}
|
||||
|
||||
ox::Result<unsigned> TileSheet::SubSheet::getTileOffset(
|
||||
ox::SpanView<ox::StringView> const&pNamePath,
|
||||
int8_t pBpp,
|
||||
std::size_t pIt,
|
||||
unsigned pCurrentTotal) const noexcept {
|
||||
// pIt == pNamePath.size() - 1 &&
|
||||
if (name != pNamePath[pIt]) {
|
||||
return OxError(2, "Wrong branch");
|
||||
ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept {
|
||||
if (ss.id == pId) {
|
||||
return ox::StringView(ss.name);
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
ox::Result<SubSheetId> TileSheet::SubSheet::getIdFor(
|
||||
ox::SpanView<ox::StringView> const&pNamePath,
|
||||
std::size_t pIt) 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");
|
||||
}
|
||||
|
||||
ox::Result<ox::StringView> TileSheet::SubSheet::getNameFor(SubSheetId pId) const noexcept {
|
||||
if (id == pId) {
|
||||
return ox::StringView(name);
|
||||
}
|
||||
for (const auto &sub : subsheets) {
|
||||
const auto [name, err] = sub.getNameFor(pId);
|
||||
for (const auto &sub : ss.subsheets) {
|
||||
const auto [name, err] = getNameFor(sub, pId);
|
||||
if (!err) {
|
||||
return name;
|
||||
}
|
||||
@ -222,138 +99,181 @@ ox::Result<ox::StringView> TileSheet::SubSheet::getNameFor(SubSheetId pId) const
|
||||
}
|
||||
|
||||
|
||||
TileSheet &TileSheet::operator=(TileSheet const&other) noexcept {
|
||||
if (this != &other) {
|
||||
bpp = other.bpp;
|
||||
idIt = other.idIt;
|
||||
defaultPalette = other.defaultPalette;
|
||||
subsheet = other.subsheet;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TileSheet &TileSheet::operator=(TileSheet &&other) noexcept {
|
||||
bpp = other.bpp;
|
||||
idIt = other.idIt;
|
||||
defaultPalette = std::move(other.defaultPalette);
|
||||
subsheet = std::move(other.subsheet);
|
||||
return *this;
|
||||
}
|
||||
|
||||
TileSheet::SubSheetIdx TileSheet::validateSubSheetIdx(
|
||||
const SubSheetIdx &pIdx,
|
||||
TileSheet::SubSheetIdx validateSubSheetIdx(
|
||||
TileSheet::SubSheetIdx const&pIdx,
|
||||
std::size_t pIdxIt,
|
||||
const SubSheet *pSubsheet) noexcept {
|
||||
TileSheet::SubSheet const&pSubsheet) noexcept {
|
||||
if (pIdxIt == pIdx.size()) {
|
||||
return pIdx;
|
||||
}
|
||||
const auto currentIdx = pIdx[pIdxIt];
|
||||
if (pSubsheet->subsheets.size() <= currentIdx) {
|
||||
if (pSubsheet.subsheets.size() <= currentIdx) {
|
||||
auto out = pIdx;
|
||||
if (!pSubsheet->subsheets.empty()) {
|
||||
*out.back().value = pSubsheet->subsheets.size() - 1;
|
||||
if (!pSubsheet.subsheets.empty()) {
|
||||
*out.back().value = pSubsheet.subsheets.size() - 1;
|
||||
} else {
|
||||
out.pop_back();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
return validateSubSheetIdx(pIdx, pIdxIt + 1, &pSubsheet->subsheets[pIdx[pIdxIt]]);
|
||||
return validateSubSheetIdx(pIdx, pIdxIt + 1, pSubsheet.subsheets[pIdx[pIdxIt]]);
|
||||
}
|
||||
|
||||
TileSheet::SubSheetIdx TileSheet::validateSubSheetIdx(const SubSheetIdx &idx) noexcept {
|
||||
return validateSubSheetIdx(idx, 0, &subsheet);
|
||||
TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
return validateSubSheetIdx(idx, 0, ts.subsheet);
|
||||
}
|
||||
|
||||
const TileSheet::SubSheet &TileSheet::getSubSheet(
|
||||
const TileSheet::SubSheet &getSubSheet(
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
SubSheet const*pSubsheet) noexcept {
|
||||
TileSheet::SubSheet const&pSubsheet) noexcept {
|
||||
if (idxIt == idx.size()) {
|
||||
return *pSubsheet;
|
||||
return pSubsheet;
|
||||
}
|
||||
const auto currentIdx = idx[idxIt];
|
||||
if (pSubsheet->subsheets.size() < currentIdx) {
|
||||
return *pSubsheet;
|
||||
if (pSubsheet.subsheets.size() < currentIdx) {
|
||||
return pSubsheet;
|
||||
}
|
||||
return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[currentIdx]);
|
||||
return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[currentIdx]);
|
||||
}
|
||||
|
||||
TileSheet::SubSheet &TileSheet::getSubSheet(
|
||||
TileSheet::SubSheet &getSubSheet(
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
TileSheet::SubSheet *pSubsheet) noexcept {
|
||||
TileSheet::SubSheet &pSubsheet) noexcept {
|
||||
if (idxIt == idx.size()) {
|
||||
return *pSubsheet;
|
||||
return pSubsheet;
|
||||
}
|
||||
return getSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]);
|
||||
return getSubSheet(idx, idxIt + 1, pSubsheet.subsheets[idx[idxIt]]);
|
||||
}
|
||||
|
||||
const TileSheet::SubSheet &TileSheet::getSubSheet(TileSheet::SubSheetIdx const&idx) const noexcept {
|
||||
return getSubSheet(idx, 0, &subsheet);
|
||||
TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
return core::getSubSheet(idx, 0, ts.subsheet);
|
||||
}
|
||||
|
||||
TileSheet::SubSheet &TileSheet::getSubSheet(TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
return getSubSheet(idx, 0, &subsheet);
|
||||
TileSheet::SubSheet &getSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
return core::getSubSheet(idx, 0, ts.subsheet);
|
||||
}
|
||||
|
||||
ox::Error TileSheet::addSubSheet(TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
auto &parent = getSubSheet(idx);
|
||||
ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
auto &parent = getSubSheet(ts, idx);
|
||||
if (parent.subsheets.size() < 2) {
|
||||
parent.subsheets.emplace_back(idIt++, ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1, bpp);
|
||||
parent.subsheets.emplace_back(++ts.idIt, ox::sfmt("Subsheet {}", parent.subsheets.size()), 1, 1, ts.bpp);
|
||||
} else {
|
||||
parent.subsheets.emplace_back(idIt++, "Subsheet 0", parent.columns, parent.rows, bpp);
|
||||
parent.subsheets.emplace_back(idIt++, "Subsheet 1", 1, 1, bpp);
|
||||
parent.subsheets.emplace_back(++ts.idIt, "Subsheet 0", parent.columns, parent.rows, ts.bpp);
|
||||
parent.subsheets.emplace_back(++ts.idIt, "Subsheet 1", 1, 1, ts.bpp);
|
||||
}
|
||||
return OxError(0);
|
||||
}
|
||||
|
||||
ox::Error TileSheet::rmSubSheet(
|
||||
SubSheetIdx const&idx,
|
||||
ox::Error rmSubSheet(
|
||||
TileSheet &ts,
|
||||
TileSheet::SubSheetIdx const&idx,
|
||||
std::size_t idxIt,
|
||||
SubSheet *pSubsheet) noexcept {
|
||||
TileSheet::SubSheet &pSubsheet) noexcept {
|
||||
if (idxIt == idx.size() - 1) {
|
||||
return pSubsheet->subsheets.erase(idx[idxIt]).error;
|
||||
return pSubsheet.subsheets.erase(idx[idxIt]).error;
|
||||
}
|
||||
return rmSubSheet(idx, idxIt + 1, &pSubsheet->subsheets[idx[idxIt]]);
|
||||
return rmSubSheet(ts, idx, idxIt + 1, pSubsheet.subsheets[idx[idxIt]]);
|
||||
}
|
||||
|
||||
ox::Error TileSheet::rmSubSheet(TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
return rmSubSheet(idx, 0, &subsheet);
|
||||
ox::Error rmSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
|
||||
return rmSubSheet(ts, idx, 0, ts.subsheet);
|
||||
}
|
||||
|
||||
uint8_t TileSheet::getPixel4Bpp(
|
||||
uint8_t getPixel4Bpp(
|
||||
TileSheet const&ts,
|
||||
ox::Point const&pt,
|
||||
TileSheet::SubSheetIdx const&subsheetIdx) const noexcept {
|
||||
oxAssert(bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp");
|
||||
auto &s = this->getSubSheet(subsheetIdx);
|
||||
TileSheet::SubSheetIdx const&subsheetIdx) noexcept {
|
||||
oxAssert(ts.bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp");
|
||||
auto &s = getSubSheet(ts, subsheetIdx);
|
||||
const auto idx = ptToIdx(pt, s.columns);
|
||||
return s.getPixel4Bpp(idx);
|
||||
return getPixel4Bpp(s, idx);
|
||||
}
|
||||
|
||||
uint8_t TileSheet::getPixel8Bpp(
|
||||
uint8_t getPixel8Bpp(
|
||||
TileSheet const&ts,
|
||||
ox::Point const&pt,
|
||||
TileSheet::SubSheetIdx const&subsheetIdx) const noexcept {
|
||||
oxAssert(bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp");
|
||||
auto &s = this->getSubSheet(subsheetIdx);
|
||||
TileSheet::SubSheetIdx const&subsheetIdx) noexcept {
|
||||
oxAssert(ts.bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp");
|
||||
auto &s = getSubSheet(ts, subsheetIdx);
|
||||
const auto idx = ptToIdx(pt, s.columns);
|
||||
return s.getPixel8Bpp(idx);
|
||||
return getPixel8Bpp(s, idx);
|
||||
}
|
||||
|
||||
ox::Result<SubSheetId> TileSheet::getIdFor(ox::CRStringView path) const noexcept {
|
||||
return subsheet.getIdFor(ox::split<8>(path, '.'));
|
||||
static ox::Result<SubSheetId> getIdFor(
|
||||
TileSheet::SubSheet const&ss,
|
||||
ox::SpanView<ox::StringView> const&pNamePath,
|
||||
std::size_t pIt = 0) noexcept {
|
||||
for (auto &sub : ss.subsheets) {
|
||||
if (sub.name == pNamePath[pIt]) {
|
||||
if (pIt == pNamePath.size()) {
|
||||
return ss.id;
|
||||
}
|
||||
return getIdFor(ss, pNamePath, pIt + 1);
|
||||
}
|
||||
}
|
||||
return OxError(1, "SubSheet not found");
|
||||
}
|
||||
|
||||
ox::Result<unsigned> TileSheet::getTileOffset(ox::CRStringView pNamePath) const noexcept {
|
||||
return subsheet.getTileOffset(ox::split<8>(pNamePath, '.'), bpp);
|
||||
ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::CRStringView path) noexcept {
|
||||
return getIdFor(ts.subsheet, ox::split<8>(path, '.'));
|
||||
}
|
||||
|
||||
ox::Result<ox::StringView> TileSheet::getNameFor(SubSheetId pId) const noexcept {
|
||||
return subsheet.getNameFor(pId);
|
||||
/**
|
||||
* Gets the offset in tiles of the desired subsheet.
|
||||
*/
|
||||
static ox::Result<unsigned> getTileOffset(
|
||||
TileSheet::SubSheet const&ss,
|
||||
ox::SpanView<ox::StringView> const&pNamePath,
|
||||
int8_t pBpp,
|
||||
std::size_t pIt = 0,
|
||||
unsigned pCurrentTotal = 0) noexcept {
|
||||
// pIt == pNamePath.size() - 1 &&
|
||||
if (ss.name != pNamePath[pIt]) {
|
||||
return OxError(2, "Wrong branch");
|
||||
}
|
||||
if (pIt == pNamePath.size() - 1) {
|
||||
return pCurrentTotal;
|
||||
}
|
||||
for (auto &sub : ss.subsheets) {
|
||||
auto [offset, err] = getTileOffset(
|
||||
sub, pNamePath, pBpp, pIt + 1, pCurrentTotal);
|
||||
if (!err) {
|
||||
return offset;
|
||||
}
|
||||
pCurrentTotal += pixelCnt(sub, pBpp) / PixelsPerTile;
|
||||
}
|
||||
return OxError(1, "SubSheet not found");
|
||||
}
|
||||
|
||||
ox::Vector<uint8_t> TileSheet::pixels() const noexcept {
|
||||
ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::CRStringView pNamePath) noexcept {
|
||||
return core::getTileOffset(ts.subsheet, ox::split<8>(pNamePath, '.'), ts.bpp);
|
||||
}
|
||||
|
||||
ox::Result<ox::StringView> getNameFor(TileSheet &ts, SubSheetId pId) noexcept {
|
||||
return core::getNameFor(ts.subsheet, pId);
|
||||
}
|
||||
|
||||
ox::Result<ox::StringView> getNameFor(TileSheet const&ts, SubSheetId pId) noexcept {
|
||||
return core::getNameFor(ts.subsheet, pId);
|
||||
}
|
||||
|
||||
static void readPixelsTo(TileSheet::SubSheet &ss, ox::Vector<uint8_t> &pPixels) noexcept {
|
||||
if (!ss.subsheets.empty()) {
|
||||
for (auto &s: ss.subsheets) {
|
||||
readPixelsTo(s, pPixels);
|
||||
}
|
||||
} else {
|
||||
for (auto p : ss.pixels) {
|
||||
pPixels.emplace_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ox::Vector<uint8_t> pixels(TileSheet &ts) noexcept {
|
||||
ox::Vector<uint8_t> out;
|
||||
subsheet.readPixelsTo(&out);
|
||||
readPixelsTo(ts.subsheet, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
11
src/nostalgia/modules/core/test/CMakeLists.txt
Normal file
11
src/nostalgia/modules/core/test/CMakeLists.txt
Normal 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)
|
38
src/nostalgia/modules/core/test/tests.cpp
Normal file
38
src/nostalgia/modules/core/test/tests.cpp
Normal 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;
|
||||
}
|
@ -42,7 +42,7 @@ struct TileDoc {
|
||||
if (subsheetId > -1) {
|
||||
return subsheetId;
|
||||
}
|
||||
return ts.getIdFor(subsheetPath);
|
||||
return getIdFor(ts, subsheetPath);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
@ -50,7 +50,7 @@ struct TileDoc {
|
||||
core::TileSheet const&ts) const noexcept {
|
||||
// prefer the already present path
|
||||
if (!subsheetPath.len()) {
|
||||
return ts.getNameFor(subsheetId);
|
||||
return core::getNameFor(ts, subsheetId);
|
||||
}
|
||||
return ox::StringView(subsheetPath);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ ox::Error SceneDocToSceneStaticConverter::convert(
|
||||
auto dstTile = dstLayer.tile(tileIdx);
|
||||
dstTile.tileType = srcTile.type;
|
||||
oxRequire(path, srcTile.getSubsheetPath(*ts));
|
||||
oxRequire(mapIdx, ts->getTileOffset(path));
|
||||
oxRequire(mapIdx, getTileOffset(*ts, path));
|
||||
dstTile.tileMapIdx = static_cast<uint16_t>(mapIdx);
|
||||
setLayerAttachments(layerIdx, srcTile, dstTile);
|
||||
++tileIdx;
|
||||
|
@ -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, 0, 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;
|
||||
|
@ -2,8 +2,6 @@
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <keel/module.hpp>
|
||||
|
||||
#include <studioapp/studioapp.hpp>
|
||||
|
||||
#include <nostalgia/core/studiomodule.hpp>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user