Compare commits

...

31 Commits

Author SHA1 Message Date
d5b232f5d5 [olympic/studio] Fix array bounds issue
All checks were successful
Build / build (push) Successful in 2m5s
2023-12-28 23:52:31 -06:00
c79fe3be43 [buildcore] Make CMake configure failure trigger failed return code
Some checks failed
Build / build (push) Has been cancelled
2023-12-28 23:50:30 -06:00
1df4e78084 [olympic/studio] Add new project menu, make file creation open file
Some checks failed
Build / build (push) Failing after 2m7s
2023-12-28 23:45:38 -06:00
ffbdb09c31 [olympic/turbine/glfw] Add shift key support 2023-12-28 23:41:33 -06:00
b35a956e4f [olympic/studio] Cleanup unused expression
All checks were successful
Build / build (push) Successful in 2m10s
2023-12-28 01:08:35 -06:00
d3847caab4 [olympic/studio] Make Project::writeObj only write descriptor if it does not already exist or if debug build
All checks were successful
Build / build (push) Successful in 2m9s
2023-12-28 00:24:18 -06:00
ae066a914c [olympic/studio] Fix Project::writeObj to ensure parent directory of file exists
All checks were successful
Build / build (push) Successful in 2m7s
2023-12-28 00:06:15 -06:00
67543af806 [ox/oc] Remove some unnecessary code
All checks were successful
Build / build (push) Successful in 2m3s
2023-12-27 23:50:02 -06:00
6b774ec285 [ox/oc] Fix array writing to write all values
All checks were successful
Build / build (push) Successful in 2m6s
This is necessary because OC array sizes are determined through the presence of the data.
2023-12-27 23:47:52 -06:00
4b9758f478 [nostalgia/core] Move most TileSheet member functions out of class
All checks were successful
Build / build (push) Successful in 2m4s
2023-12-27 23:10:38 -06:00
bf12b15fe6 [nostalgia/sample_project] Update some tilesheets to version 4 format 2023-12-26 20:28:55 -06:00
a7328eb5ef [nostalgia/core/studio] Reduce indent for Subsheet editor 2023-12-26 20:18:07 -06:00
b52124a0c5 [nostalgia/core/studio] Revert new subsheet index increment to happen after index assignment 2023-12-26 20:14:30 -06:00
eae9972f85 [nostalgia/sample_project] Add missing type descriptors 2023-12-26 18:55:35 -06:00
d83e392964 [nostalgia/core] Cleanup, revert CompactTileSheet version to 1
All checks were successful
Build / build (push) Successful in 2m2s
2023-12-26 18:50:33 -06:00
e2d0a784f1 [olympic/keel] Cleanup 2023-12-26 18:37:02 -06:00
f08821422a [nostalgia/core/studio] Change TileSheets to back to MC
All checks were successful
Build / build (push) Successful in 2m4s
2023-12-26 17:56:03 -06:00
6a2954f82b [nostalgia/core] Remove id from TileSheetV3::Subsheet, add TileSheetV4
Some checks failed
Build / build (push) Has been cancelled
2023-12-26 17:28:41 -06:00
9c19655ce2 [nostalgia/core] Fix build, add SubSheet ID to SubSheet Editor view
All checks were successful
Build / build (push) Successful in 2m5s
2023-12-26 16:37:09 -06:00
087c834b25 [nostalgia/core/studio] Fix Add SubSheet to increment idIt before using it
Some checks failed
Build / build (push) Failing after 23s
2023-12-26 16:34:40 -06:00
79bdbf2eaa [nostalgia/core] Add id to TileSheetV3::SubSheet model 2023-12-26 16:31:54 -06:00
2bdc3def74 [nostalgia/core/opengl] Implement flip X and flip Y for BG tiles
All checks were successful
Build / build (push) Successful in 2m4s
2023-12-26 12:34:35 -06:00
e7a663901a [nostalgia/core/opengl] Fix duplicate and missing symbol
All checks were successful
Build / build (push) Successful in 2m6s
2023-12-26 11:59:14 -06:00
ffdc0ddb97 [nostalgia/core] Add support for specifying palette banks
Some checks failed
Build / build (push) Failing after 40s
2023-12-26 11:54:31 -06:00
e941781f21 [ox/std] Cleanup Result::copyTo variants
All checks were successful
Build / build (push) Successful in 2m4s
2023-12-25 00:54:59 -06:00
b869f490c3 [ox/std] Add or_value to Optional, Result
All checks were successful
Build / build (push) Successful in 2m4s
2023-12-25 00:51:17 -06:00
caf8d93c21 [nostalgia] Delete .gitlab-ci.yml
All checks were successful
Build / build (push) Successful in 2m6s
2023-12-25 00:42:36 -06:00
afbf2caf97 [nostalgia] Remove conan from devenv
All checks were successful
Build / build (push) Successful in 2m5s
2023-12-24 06:41:18 -06:00
20914eaade [nostalgia] Add Gitea action file 2023-12-24 06:38:28 -06:00
c5f76ff558 [nostalgia/core] Add setBgBpp function 2023-12-23 21:33:52 -06:00
050339ba09 [nostalgia] Update developer-handbook 2023-12-23 19:08:18 -06:00
53 changed files with 1031 additions and 648 deletions

View 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

View File

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

View File

@ -31,7 +31,6 @@ RUN dnf install -y clang \
python3-pip \ python3-pip \
libglvnd-devel \ libglvnd-devel \
gtk3-devel gtk3-devel
RUN pip install conan
############################################################################### ###############################################################################
# Install devkitARM # Install devkitARM

View File

@ -99,7 +99,9 @@ def main() -> int:
if platform.system() == 'Windows': if platform.system() == 'Windows':
cmake_cmd.append('-A x64') 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') util.mkdir_p('dist')
if int(args.current_build) != 0: if int(args.current_build) != 0:

View File

@ -37,7 +37,7 @@ class OrganicClawWriter {
explicit OrganicClawWriter(Json::Value json, int unionIdx = -1) noexcept; explicit OrganicClawWriter(Json::Value json, int unionIdx = -1) noexcept;
Error field(const char *key, const int8_t *val) noexcept { Error field(const char *key, const int8_t *val) noexcept {
if (*val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
@ -45,7 +45,7 @@ class OrganicClawWriter {
} }
Error field(const char *key, const int16_t *val) noexcept { Error field(const char *key, const int16_t *val) noexcept {
if (*val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
@ -53,7 +53,7 @@ class OrganicClawWriter {
} }
Error field(const char *key, const int32_t *val) noexcept { Error field(const char *key, const int32_t *val) noexcept {
if (*val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
@ -61,7 +61,7 @@ class OrganicClawWriter {
} }
Error field(const char *key, const int64_t *val) noexcept { Error field(const char *key, const int64_t *val) noexcept {
if (*val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
@ -70,7 +70,7 @@ class OrganicClawWriter {
Error field(const char *key, const uint8_t *val) noexcept { Error field(const char *key, const uint8_t *val) noexcept {
if (*val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
@ -78,7 +78,7 @@ class OrganicClawWriter {
} }
Error field(const char *key, const uint16_t *val) noexcept { Error field(const char *key, const uint16_t *val) noexcept {
if (targetValid() && *val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
@ -86,7 +86,7 @@ class OrganicClawWriter {
} }
Error field(const char *key, const uint32_t *val) noexcept { Error field(const char *key, const uint32_t *val) noexcept {
if (targetValid() && *val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
@ -94,15 +94,15 @@ class OrganicClawWriter {
} }
Error field(const char *key, const uint64_t *val) noexcept { Error field(const char *key, const uint64_t *val) noexcept {
if (targetValid() && *val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
return OxError(0); return OxError(0);
} }
Error field(const char *key, const bool *val) noexcept { Error field(char const*key, bool const*val) noexcept {
if (targetValid() && *val) { if (targetValid() && (*val || m_json.isArray())) {
value(key) = *val; value(key) = *val;
} }
++m_fieldIt; ++m_fieldIt;
@ -110,10 +110,10 @@ class OrganicClawWriter {
} }
template<typename U, bool force = true> 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> 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()) { if (targetValid()) {
const auto &keys = val->keys(); const auto &keys = val->keys();
OrganicClawWriter w; OrganicClawWriter w;
@ -132,7 +132,7 @@ class OrganicClawWriter {
} }
template<std::size_t L> 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()) { if (targetValid() && val->len()) {
value(key) = val->c_str(); value(key) = val->c_str();
} }
@ -141,7 +141,7 @@ class OrganicClawWriter {
} }
template<std::size_t L> 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()) { if (targetValid() && val->len()) {
value(key) = val->c_str(); 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))); OrganicClawWriter w((Json::Value(Json::arrayValue)));
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{&w}; ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{&w};
for (std::size_t i = 0; i < len; ++i) { for (std::size_t i = 0; i < len; ++i) {
oxReturnError(handler.field("", &val[i])); oxReturnError(handler.field({}, &val[i]));
} }
value(key) = w.m_json; value(key) = w.m_json;
} }

View File

@ -165,21 +165,7 @@ struct [[nodiscard]] Result {
return error == 0; return error == 0;
} }
constexpr Error copyTo(type &val) const & noexcept { 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]] { if (!error) [[likely]] {
val = value; val = value;
} }
@ -269,7 +255,7 @@ struct [[nodiscard]] Result {
* @param alt * @param alt
* @return value of Result or alt * @return value of Result or alt
*/ */
constexpr T orVal(T &&alt) & noexcept { constexpr T or_value(T &&alt) const& noexcept {
if (error) { if (error) {
return std::move(alt); return std::move(alt);
} }
@ -281,7 +267,7 @@ struct [[nodiscard]] Result {
* @param alt * @param alt
* @return value of Result or alt * @return value of Result or alt
*/ */
constexpr T orVal(T &&alt) && noexcept { constexpr T or_value(T &&alt) && noexcept {
if (error) { if (error) {
return std::move(alt); return std::move(alt);
} }
@ -293,7 +279,7 @@ struct [[nodiscard]] Result {
* @param alt * @param alt
* @return value of Result or alt * @return value of Result or alt
*/ */
constexpr T orVal(T const&alt) & noexcept { constexpr T or_value(T const&alt) const& noexcept {
if (error) { if (error) {
return alt; return alt;
} }
@ -305,7 +291,7 @@ struct [[nodiscard]] Result {
* @param alt * @param alt
* @return value of Result or alt * @return value of Result or alt
*/ */
constexpr T orVal(T const&alt) && noexcept { constexpr T or_value(T const&alt) && noexcept {
if (error) { if (error) {
return alt; return alt;
} }

View File

@ -64,6 +64,54 @@ class Optional {
return *m_ptr; 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 { constexpr T &operator*() & noexcept {
return *m_ptr; return *m_ptr;
} }

View File

@ -20,14 +20,6 @@ All components have a platform indicator next to them:
(-G) - GBA (-G) - GBA
(P-) - PC (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 * Nostalgia
* modules * modules
* core - graphics system for Nostalgia (PG) * core - graphics system for Nostalgia (PG)
@ -42,6 +34,15 @@ All components have a platform indicator next to them:
* studio - makes the games (P-) * studio - makes the games (P-)
* tools - command line tools (P-) * tools - command line tools (P-)
* pack - packs a studio project directory into an OxFS file (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 * deps - project dependencies
* Ox - Library of things useful for portable bare metal and userland code. Not really that external... * Ox - Library of things useful for portable bare metal and userland code. Not really that external...
* clargs - Command Line Args processing (PG) * clargs - Command Line Args processing (PG)
@ -54,9 +55,19 @@ All components have a platform indicator next to them:
* model - Data structure modelling (PG) * model - Data structure modelling (PG)
* preloader - library for handling preloading of data (PG) * preloader - library for handling preloading of data (PG)
* std - Standard-ish Library with a lot missing and some things added (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 * teagba - GBA assembly startup code (mostly pulled from devkitPro under MPL
2.0), and custom GBA hardware interop code (-G) 2.0), and custom GBA hardware interop code (-G)
## Platform Notes
### GBA
#### Graphics
* Background Palette: 256 colors
* Sprite Palette: 256 colors
## Code Base Conventions ## Code Base Conventions
### Formatting ### Formatting

View File

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

View File

@ -0,0 +1,24 @@
O1;net.drinkingtea.ox.TypeDescriptor;1;{
"fieldList" :
[
{
"fieldName" : "bpp",
"typeId" : "B.int8;0"
},
{
"fieldName" : "idIt",
"typeId" : "B.int32;0"
},
{
"fieldName" : "defaultPalette",
"typeId" : "net.drinkingtea.ox.FileAddress;1"
},
{
"fieldName" : "subsheet",
"typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;4"
}
],
"primitiveType" : 5,
"typeName" : "net.drinkingtea.nostalgia.core.TileSheet",
"typeVersion" : 4
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -43,6 +43,22 @@ oxModelBegin(Sprite)
oxModelField(priority) oxModelField(priority)
oxModelEnd() 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 { struct TileSheetSetEntrySection {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection"; static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection";
static constexpr auto TypeVersion = 1; static constexpr auto TypeVersion = 1;
@ -86,6 +102,7 @@ oxModelEnd()
ox::Error loadBgPalette( ox::Error loadBgPalette(
Context &ctx, Context &ctx,
size_t palBank,
ox::FileAddress const&paletteAddr) noexcept; ox::FileAddress const&paletteAddr) noexcept;
ox::Error loadSpritePalette( ox::Error loadSpritePalette(
@ -101,7 +118,7 @@ ox::Error loadBgTileSheet(
Context &ctx, Context &ctx,
unsigned cbb, unsigned cbb,
ox::FileAddress const&tilesheetAddr, ox::FileAddress const&tilesheetAddr,
bool loadDefaultPalette = false) noexcept; ox::Optional<unsigned> const&paletteBank = {}) noexcept;
ox::Error loadSpriteTileSheet( ox::Error loadSpriteTileSheet(
Context &ctx, Context &ctx,
@ -112,7 +129,9 @@ ox::Error loadSpriteTileSheet(
Context &ctx, Context &ctx,
TileSheetSet const&set) noexcept; TileSheetSet const&set) noexcept;
void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, uint8_t tile) 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; void clearBg(Context &ctx, uint_t bgIdx) noexcept;

View File

@ -61,7 +61,7 @@ struct TileSheetV2 {
using SubSheetId = int32_t; using SubSheetId = int32_t;
struct TileSheet { struct TileSheetV3 {
using SubSheetIdx = ox::Vector<std::size_t, 4>; using SubSheetIdx = ox::Vector<std::size_t, 4>;
struct SubSheet { struct SubSheet {
@ -73,119 +73,20 @@ struct TileSheet {
int rows = 0; int rows = 0;
ox::Vector<SubSheet> subsheets; ox::Vector<SubSheet> subsheets;
ox::Vector<uint8_t> pixels; ox::Vector<uint8_t> pixels;
constexpr SubSheet() noexcept = default; constexpr SubSheet() noexcept = default;
constexpr SubSheet(SubSheet const&other) noexcept = default; inline SubSheet(
SubSheet(SubSheet &&other) noexcept;
SubSheet(
SubSheetId pId, SubSheetId pId,
ox::CRStringView pName, ox::CRStringView pName,
int pColumns, int pColumns,
int pRows, int pRows,
int bpp) noexcept; int bpp) noexcept:
SubSheet( id(pId),
SubSheetId pId, name(pName),
ox::CRStringView pName, columns(pColumns),
int pColumns, rows(pRows),
int pRows, pixels(static_cast<std::size_t>(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) {
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);
} }
[[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"; static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheet";
@ -195,86 +96,185 @@ struct TileSheet {
ox::FileAddress defaultPalette; ox::FileAddress defaultPalette;
SubSheet subsheet{0, "Root", 1, 1, bpp}; SubSheet subsheet{0, "Root", 1, 1, bpp};
};
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)) {
}
[[nodiscard]]
constexpr std::size_t size() const noexcept {
return static_cast<std::size_t>(columns) * static_cast<std::size_t>(rows);
}
};
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; 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)) {
}
TileSheet &operator=(TileSheet const&other) noexcept;
TileSheet &operator=(TileSheet &&other) noexcept;
[[nodiscard]]
SubSheetIdx validateSubSheetIdx(
SubSheetIdx const&pIdx,
std::size_t pIdxIt,
const SubSheet *pSubsheet) noexcept;
/**
* validateSubSheetIdx takes a SubSheetIdx and moves the index to the
* preceding or parent sheet if the current corresponding sheet does
* not exist.
* @param idx SubSheetIdx to validate and correct
* @return a valid version of idx
*/
[[nodiscard]]
SubSheetIdx validateSubSheetIdx(const SubSheetIdx &idx) noexcept;
[[nodiscard]]
static SubSheet const&getSubSheet(
SubSheetIdx const&idx,
std::size_t idxIt,
const SubSheet *pSubsheet) noexcept;
[[nodiscard]]
static SubSheet &getSubSheet(
SubSheetIdx const&idx,
std::size_t idxIt,
SubSheet *pSubsheet) noexcept;
[[nodiscard]]
const SubSheet &getSubSheet(SubSheetIdx const&idx) const noexcept;
[[nodiscard]]
SubSheet &getSubSheet(SubSheetIdx const&idx) noexcept;
ox::Error addSubSheet(SubSheetIdx const&idx) noexcept;
[[nodiscard]]
static ox::Error rmSubSheet(
SubSheetIdx const&idx,
std::size_t idxIt,
SubSheet *pSubsheet) noexcept;
[[nodiscard]]
ox::Error rmSubSheet(SubSheetIdx const&idx) noexcept;
[[nodiscard]]
uint8_t getPixel4Bpp(
ox::Point const&pt,
SubSheetIdx const&subsheetIdx) const noexcept;
[[nodiscard]]
uint8_t getPixel8Bpp(
ox::Point const&pt,
SubSheetIdx const&subsheetIdx) const noexcept;
ox::Result<SubSheetId> getIdFor(ox::CRStringView path) const noexcept;
ox::Result<unsigned> getTileOffset(ox::CRStringView pNamePath) const noexcept;
ox::Result<ox::StringView> getNameFor(SubSheetId pId) const noexcept;
[[nodiscard]]
ox::Vector<uint8_t> pixels() const noexcept;
}; };
using TileSheetV3 = TileSheet; [[nodiscard]]
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
* preceding or parent sheet if the current corresponding sheet does
* not exist.
* @param idx SubSheetIdx to validate and correct
* @return a valid version of idx
*/
[[nodiscard]]
TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept;
[[nodiscard]]
TileSheet::SubSheet const&getSubSheet(
TileSheet::SubSheetIdx const&idx,
std::size_t idxIt,
TileSheet::SubSheet const&pSubsheet) noexcept;
[[nodiscard]]
TileSheet::SubSheet &getSubSheet(
TileSheet::SubSheetIdx const&idx,
std::size_t idxIt,
TileSheet::SubSheet &pSubsheet) noexcept;
[[nodiscard]]
TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept;
[[nodiscard]]
TileSheet::SubSheet &getSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
ox::Error rmSubSheet(
TileSheet &ts,
TileSheet::SubSheetIdx const&idx,
std::size_t idxIt,
TileSheet::SubSheet &pSubsheet) noexcept;
ox::Error rmSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept;
[[nodiscard]]
uint8_t getPixel4Bpp(
TileSheet const&ts,
ox::Point const&pt,
TileSheet::SubSheetIdx const&subsheetIdx) noexcept;
[[nodiscard]]
uint8_t getPixel8Bpp(
TileSheet const&ts,
ox::Point const&pt,
TileSheet::SubSheetIdx const&subsheetIdx) noexcept;
ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::CRStringView path) noexcept;
ox::Result<unsigned> getTileOffset(TileSheet const&ts, ox::CRStringView pNamePath) 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(TileSheet &ts) noexcept;
using TileSheetV4 = TileSheet;
struct CompactTileSheet { struct CompactTileSheet {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet"; static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet";
@ -294,9 +294,9 @@ oxModelBegin(TileSheetV1)
oxModelEnd() oxModelEnd()
oxModelBegin(TileSheetV2::SubSheet) oxModelBegin(TileSheetV2::SubSheet)
oxModelField(name); oxModelField(name)
oxModelField(rows); oxModelField(rows)
oxModelField(columns); oxModelField(columns)
oxModelField(subsheets) oxModelField(subsheets)
oxModelField(pixels) oxModelField(pixels)
oxModelEnd() oxModelEnd()
@ -308,9 +308,9 @@ oxModelBegin(TileSheetV2)
oxModelEnd() oxModelEnd()
oxModelBegin(TileSheetV3::SubSheet) oxModelBegin(TileSheetV3::SubSheet)
oxModelField(name); oxModelField(name)
oxModelField(rows); oxModelField(rows)
oxModelField(columns); oxModelField(columns)
oxModelField(subsheets) oxModelField(subsheets)
oxModelField(pixels) oxModelField(pixels)
oxModelEnd() oxModelEnd()
@ -322,6 +322,22 @@ oxModelBegin(TileSheetV3)
oxModelField(subsheet) oxModelField(subsheet)
oxModelEnd() 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) oxModelBegin(CompactTileSheet)
oxModelField(bpp) oxModelField(bpp)
oxModelField(defaultPalette) oxModelField(defaultPalette)

View File

@ -158,9 +158,10 @@ ox::Error initGfx(Context&, InitParams const&) noexcept {
ox::Error loadBgPalette( ox::Error loadBgPalette(
Context &ctx, Context &ctx,
size_t palBank,
ox::FileAddress const&paletteAddr) noexcept { ox::FileAddress const&paletteAddr) noexcept {
auto &rom = ctx.rom(); auto &rom = ctx.rom();
GbaPaletteTarget const palTarget{.palette = MEM_BG_PALETTE}; GbaPaletteTarget const palTarget{.palette = MEM_BG_PALETTE + palBank * 16};
oxRequire(palStat, rom.stat(paletteAddr)); oxRequire(palStat, rom.stat(paletteAddr));
oxRequire(pal, rom.directAccess(paletteAddr)); oxRequire(pal, rom.directAccess(paletteAddr));
oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &palTarget)); oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &palTarget));
@ -189,11 +190,11 @@ static ox::Error loadTileSheetSet(
oxRequire(ts, rom.directAccess(entry.tilesheet)); oxRequire(ts, rom.directAccess(entry.tilesheet));
unsigned tilesheetBpp{}; unsigned tilesheetBpp{};
GbaTileMapTarget target{ GbaTileMapTarget target{
.bpp = tilesheetBpp, .bpp = tilesheetBpp,
.defaultPalette = {}, .defaultPalette = {},
.tileMap = tileMapTargetMem + tileWriteIdx, .tileMap = tileMapTargetMem + tileWriteIdx,
.targetBpp = static_cast<unsigned>(set.bpp), .targetBpp = static_cast<unsigned>(set.bpp),
.setEntry = &entry, .setEntry = &entry,
}; };
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target)); oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
tileWriteIdx += target.tileWriteIdx; tileWriteIdx += target.tileWriteIdx;
@ -205,7 +206,7 @@ ox::Error loadBgTileSheet(
Context &ctx, Context &ctx,
unsigned cbb, unsigned cbb,
ox::FileAddress const&tilesheetAddr, ox::FileAddress const&tilesheetAddr,
bool loadDefaultPalette) noexcept { ox::Optional<unsigned> const&paletteBank) noexcept {
auto &rom = ctx.rom(); auto &rom = ctx.rom();
oxRequire(tsStat, rom.stat(tilesheetAddr)); oxRequire(tsStat, rom.stat(tilesheetAddr));
oxRequire(ts, rom.directAccess(tilesheetAddr)); oxRequire(ts, rom.directAccess(tilesheetAddr));
@ -222,8 +223,8 @@ ox::Error loadBgTileSheet(
teagba::bgSetBpp(bgCtl, bpp); teagba::bgSetBpp(bgCtl, bpp);
} }
}); });
if (loadDefaultPalette && target.defaultPalette) { if (paletteBank.has_value() && target.defaultPalette) {
oxReturnError(loadBgPalette(ctx, target.defaultPalette)); oxReturnError(loadBgPalette(ctx, *paletteBank, target.defaultPalette));
} }
return {}; return {};
} }
@ -283,9 +284,14 @@ ox::Error loadSpriteTileSheet(
return {}; return {};
} }
void setBgTile(Context&, uint_t bgIdx, int column, int row, uint8_t tile) noexcept { void setBgTile(Context&, uint_t bgIdx, int column, int row, BgTile const&tile) noexcept {
const auto tileIdx = static_cast<std::size_t>(row * GbaTileColumns + column); auto const tileIdx = static_cast<std::size_t>(row * GbaTileColumns + column);
MEM_BG_MAP[bgIdx][tileIdx] = tile; // 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 { void clearBg(Context&, uint_t bgIdx) noexcept {
@ -311,6 +317,11 @@ void setBgStatus(Context&, unsigned bg, bool status) noexcept {
REG_DISPCTL = REG_DISPCTL | ((REG_DISPCTL & ~mask) | mask); REG_DISPCTL = REG_DISPCTL | ((REG_DISPCTL & ~mask) | mask);
} }
void setBgBpp(Context&, unsigned bgIdx, unsigned bpp) noexcept {
auto &bgCtl = regBgCtl(bgIdx);
teagba::bgSetBpp(bgCtl, bpp);
}
void setBgCbb(Context &ctx, unsigned bgIdx, unsigned cbb) noexcept { void setBgCbb(Context &ctx, unsigned bgIdx, unsigned cbb) noexcept {
auto &bgCtl = regBgCtl(bgIdx); auto &bgCtl = regBgCtl(bgIdx);
const auto &cbbData = ctx.cbbData[cbb]; const auto &cbbData = ctx.cbbData[cbb];

View File

@ -137,13 +137,20 @@ constexpr ox::Array<char, 128> charMap = {
50, // ~ 50, // ~
}; };
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 { ox::Error initConsole(Context &ctx) noexcept {
constexpr ox::FileAddress TilesheetAddr = ox::StringLiteral("/TileSheets/Charset.ng"); constexpr ox::FileAddress TilesheetAddr = ox::StringLiteral("/TileSheets/Charset.ng");
constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal"); constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal");
setBgStatus(ctx, 0b0001); setBgStatus(ctx, 0b0001);
setBgCbb(ctx, 0, 0); setBgCbb(ctx, 0, 0);
oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr)); oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr));
return loadBgPalette(ctx, PaletteAddr); return loadBgPalette(ctx, 0, PaletteAddr);
} }
void puts( void puts(

View File

@ -18,7 +18,8 @@ class KeelModule: public keel::Module {
private: private:
NostalgiaPaletteToPaletteConverter m_nostalgiaPaletteToPaletteConverter; NostalgiaPaletteToPaletteConverter m_nostalgiaPaletteToPaletteConverter;
TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter; TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter;
TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetConverter; TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetV3Converter;
TileSheetV3ToTileSheetV4Converter m_tileSheetV3ToTileSheetV4Converter;
TileSheetToCompactTileSheetConverter m_tileSheetToCompactTileSheetConverter; TileSheetToCompactTileSheetConverter m_tileSheetToCompactTileSheetConverter;
public: public:
@ -32,18 +33,20 @@ class KeelModule: public keel::Module {
return { return {
keel::generateTypeDesc<TileSheetV1>, keel::generateTypeDesc<TileSheetV1>,
keel::generateTypeDesc<TileSheetV2>, keel::generateTypeDesc<TileSheetV2>,
keel::generateTypeDesc<TileSheet>, keel::generateTypeDesc<TileSheetV3>,
keel::generateTypeDesc<TileSheetV4>,
keel::generateTypeDesc<CompactTileSheet>, keel::generateTypeDesc<CompactTileSheet>,
keel::generateTypeDesc<Palette>, keel::generateTypeDesc<Palette>,
}; };
} }
[[nodiscard]] [[nodiscard]]
ox::Vector<const keel::BaseConverter*> converters() const noexcept final { ox::Vector<keel::BaseConverter const*> converters() const noexcept final {
return { return {
&m_nostalgiaPaletteToPaletteConverter, &m_nostalgiaPaletteToPaletteConverter,
&m_tileSheetV1ToTileSheetV2Converter, &m_tileSheetV1ToTileSheetV2Converter,
&m_tileSheetV2ToTileSheetConverter, &m_tileSheetV2ToTileSheetV3Converter,
&m_tileSheetV3ToTileSheetV4Converter,
&m_tileSheetToCompactTileSheetConverter, &m_tileSheetToCompactTileSheetConverter,
}; };
} }
@ -54,11 +57,12 @@ class KeelModule: public keel::Module {
// convert tilesheets to CompactTileSheets // convert tilesheets to CompactTileSheets
[](keel::Context &ctx, ox::Buffer &buff) -> ox::Error { [](keel::Context &ctx, ox::Buffer &buff) -> ox::Error {
oxRequire(hdr, keel::readAssetHeader(buff)); oxRequire(hdr, keel::readAssetHeader(buff));
const auto typeId = ox::buildTypeId( auto const typeId = ox::buildTypeId(
hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams); hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams);
if (typeId == ox::buildTypeId<TileSheetV1>() || if (typeId == ox::buildTypeId<TileSheetV1>() ||
typeId == ox::buildTypeId<TileSheetV2>() || typeId == ox::buildTypeId<TileSheetV2>() ||
typeId == ox::buildTypeId<TileSheet>()) { typeId == ox::buildTypeId<TileSheetV3>() ||
typeId == ox::buildTypeId<TileSheetV4>()) {
oxReturnError(keel::convertBuffToBuff<core::CompactTileSheet>( oxReturnError(keel::convertBuffToBuff<core::CompactTileSheet>(
ctx, buff, ox::ClawFormat::Metal).moveTo(buff)); ctx, buff, ox::ClawFormat::Metal).moveTo(buff));
} }

View File

@ -53,13 +53,41 @@ ox::Error TileSheetV2ToTileSheetV3Converter::convert(
return {}; 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( ox::Error TileSheetToCompactTileSheetConverter::convert(
keel::Context&, keel::Context&,
TileSheet &src, TileSheet &src,
CompactTileSheet &dst) const noexcept { CompactTileSheet &dst) const noexcept {
dst.bpp = src.bpp; dst.bpp = src.bpp;
dst.defaultPalette = std::move(src.defaultPalette); dst.defaultPalette = std::move(src.defaultPalette);
dst.pixels = src.pixels(); dst.pixels = pixels(src);
return {}; return {};
} }

View File

@ -25,11 +25,19 @@ class TileSheetV1ToTileSheetV2Converter: public keel::Converter<TileSheetV1, Til
}; };
class TileSheetV2ToTileSheetV3Converter: public keel::Converter<TileSheetV2, TileSheetV3> { class TileSheetV2ToTileSheetV3Converter: public keel::Converter<TileSheetV2, TileSheetV3> {
static void convertSubsheet(
TileSheetV2::SubSheet &src,
TileSheetV3::SubSheet &dst,
SubSheetId &idIt) noexcept;
ox::Error convert(keel::Context&, TileSheetV2 &src, TileSheetV3 &dst) const noexcept final;
};
class TileSheetV3ToTileSheetV4Converter: public keel::Converter<TileSheetV3, TileSheetV4> {
static void convertSubsheet( static void convertSubsheet(
TileSheetV2::SubSheet &src, TileSheetV3::SubSheet &src,
TileSheetV3::SubSheet &dst, TileSheetV4::SubSheet &dst,
SubSheetId &idIt) noexcept; SubSheetId &idIt) noexcept;
ox::Error convert(keel::Context&, TileSheetV2 &src, TileSheetV3 &dst) const noexcept final; ox::Error convert(keel::Context&, TileSheetV3 &src, TileSheetV4 &dst) const noexcept final;
}; };
class TileSheetToCompactTileSheetConverter: public keel::Converter<TileSheet, CompactTileSheet> { class TileSheetToCompactTileSheetConverter: public keel::Converter<TileSheet, CompactTileSheet> {

View File

@ -24,6 +24,7 @@ class Context {
ox::Array<renderer::CBB, 4> cbbs; ox::Array<renderer::CBB, 4> cbbs;
renderer::SpriteBlockset spriteBlocks; renderer::SpriteBlockset spriteBlocks;
ox::Array<Sprite, 128> spriteStates; ox::Array<Sprite, 128> spriteStates;
ox::Array<GLfloat, 1024> bgPalette;
ox::Array<renderer::Background, 4> backgrounds; ox::Array<renderer::Background, 4> backgrounds;
renderer::Drawer drawer; renderer::Drawer drawer;
uint_t spriteCount = 0; uint_t spriteCount = 0;

View File

@ -35,7 +35,9 @@ constexpr ox::CStringView bgvshadTmpl = R"glsl(
in vec2 vTexCoord; in vec2 vTexCoord;
in vec3 vPosition; in vec3 vPosition;
in float vTileIdx; in float vTileIdx;
in float vPalOffset;
out vec2 fTexCoord; out vec2 fTexCoord;
out float fPalOffset;
uniform float vXScale; uniform float vXScale;
uniform float vTileHeight; uniform float vTileHeight;
uniform float vBgIdx; uniform float vBgIdx;
@ -49,17 +51,19 @@ constexpr ox::CStringView bgvshadTmpl = R"glsl(
fTexCoord = vec2( fTexCoord = vec2(
vTexCoord.x, vTexCoord.x,
vTexCoord.y * vTileHeight + vTileIdx * vTileHeight); vTexCoord.y * vTileHeight + vTileIdx * vTileHeight);
fPalOffset = vPalOffset;
})glsl"; })glsl";
constexpr ox::CStringView bgfshadTmpl = R"glsl( constexpr ox::CStringView bgfshadTmpl = R"glsl(
{} {}
out vec4 outColor; out vec4 outColor;
in float fPalOffset;
in vec2 fTexCoord; in vec2 fTexCoord;
uniform sampler2D image; uniform sampler2D image;
uniform vec2 fSrcImgSz; uniform vec2 fSrcImgSz;
uniform vec4 fPalette[256]; uniform vec4 fPalette[256];
void main() { void main() {
outColor = fPalette[int(texture(image, fTexCoord).rgb.r * 256)]; outColor = fPalette[int(texture(image, fTexCoord).rgb.r * 256) + int(fPalOffset)];
//outColor = vec4(0.0, 0.7, 1.0, 1.0); //outColor = vec4(0.0, 0.7, 1.0, 1.0);
if (outColor.a == 0) { if (outColor.a == 0) {
discard; discard;
@ -147,6 +151,9 @@ static void setTileBufferObject(
float y, float y,
float textureTileIdx, float textureTileIdx,
float priority, float priority,
float palOffset,
bool flipX,
bool flipY,
float *vbo, float *vbo,
GLuint *ebo) noexcept { GLuint *ebo) noexcept {
// don't worry, this memcpy gets optimized to something much more ideal // don't worry, this memcpy gets optimized to something much more ideal
@ -157,11 +164,15 @@ static void setTileBufferObject(
x -= 1.0f; x -= 1.0f;
y += 1.0f - ymod; y += 1.0f - ymod;
auto const prif = priority * PriorityScale; 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 { ox::Array<float, BgVertexVboLength> const vertices {
x, y, prif, 0, 1, textureTileIdx, // bottom left x, y, prif, L, B, textureTileIdx, palOffset, // bottom left
x + xmod, y, prif, 1, 1, textureTileIdx, // bottom right x + xmod, y, prif, R, B, textureTileIdx, palOffset, // bottom right
x + xmod, y + ymod, prif, 1, 0, textureTileIdx, // top right x + xmod, y + ymod, prif, R, T, textureTileIdx, palOffset, // top right
x, y + ymod, prif, 0, 0, textureTileIdx, // top left x, y + ymod, prif, L, T, textureTileIdx, palOffset, // top left
}; };
memcpy(vbo, vertices.data(), sizeof(vertices)); memcpy(vbo, vertices.data(), sizeof(vertices));
ox::Array<GLuint, BgVertexEboLength> const elms { ox::Array<GLuint, BgVertexEboLength> const elms {
@ -191,6 +202,9 @@ static void initBackgroundBufferObjects(glutils::BufferSet &bs) noexcept {
static_cast<float>(y), static_cast<float>(y),
0, 0,
0, 0,
0,
false,
false,
vbo, vbo,
ebo); ebo);
} }
@ -252,6 +266,11 @@ static void initBackgroundBufferset(
glVertexAttribPointer( glVertexAttribPointer(
heightMultAttr, 1, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), heightMultAttr, 1, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float),
std::bit_cast<void*>(uintptr_t{5 * 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( static glutils::GLTexture createTexture(
@ -337,18 +356,19 @@ static void drawSprites(Context &ctx, ox::Size const&renderSz) noexcept {
} }
static void loadPalette( static void loadPalette(
ox::Array<GLfloat, 1024> &palette,
size_t palOffset,
GLuint shaderPgrm, GLuint shaderPgrm,
Palette const&pal) noexcept { Palette const&pal) noexcept {
static constexpr std::size_t ColorCnt = 256; static constexpr std::size_t ColorCnt = 256;
ox::Array<GLfloat, ColorCnt * 4> palette{}; for (auto i = palOffset; auto const c : pal.colors) {
for (auto i = 0u; const auto c : pal.colors) {
palette[i++] = redf(c); palette[i++] = redf(c);
palette[i++] = greenf(c); palette[i++] = greenf(c);
palette[i++] = bluef(c); palette[i++] = bluef(c);
palette[i++] = 255; palette[i++] = 255;
} }
// make first color transparent // make first color transparent
palette[3] = 0; palette[palOffset + 3] = 0;
glUseProgram(shaderPgrm); glUseProgram(shaderPgrm);
const auto uniformPalette = static_cast<GLint>(glGetUniformLocation(shaderPgrm, "fPalette")); const auto uniformPalette = static_cast<GLint>(glGetUniformLocation(shaderPgrm, "fPalette"));
glUniform4fv(uniformPalette, ColorCnt, palette.data()); glUniform4fv(uniformPalette, ColorCnt, palette.data());
@ -488,10 +508,11 @@ static ox::Result<TileSheetData> normalizeTileSheet(
ox::Error loadBgPalette( ox::Error loadBgPalette(
Context &ctx, Context &ctx,
size_t palBank,
ox::FileAddress const&paletteAddr) noexcept { ox::FileAddress const&paletteAddr) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx); auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(palette, readObj<Palette>(kctx, paletteAddr)); oxRequire(palette, readObj<Palette>(kctx, paletteAddr));
renderer::loadPalette(ctx.bgShader, *palette); renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, *palette);
return {}; return {};
} }
@ -500,7 +521,8 @@ ox::Error loadSpritePalette(
ox::FileAddress const&paletteAddr) noexcept { ox::FileAddress const&paletteAddr) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx); auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(palette, readObj<Palette>(kctx, paletteAddr)); oxRequire(palette, readObj<Palette>(kctx, paletteAddr));
renderer::loadPalette(ctx.spriteShader, *palette); ox::Array<GLfloat, 1024> pal;
renderer::loadPalette(pal, 0, ctx.spriteShader, *palette);
return {}; return {};
} }
@ -529,14 +551,14 @@ ox::Error loadBgTileSheet(
Context &ctx, Context &ctx,
uint_t cbb, uint_t cbb,
ox::FileAddress const&tilesheetAddr, ox::FileAddress const&tilesheetAddr,
bool loadDefaultPalette) noexcept { ox::Optional<unsigned> const&paletteBank) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx); auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr)); oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, tilesheetAddr));
oxRequire(tsd, normalizeTileSheet(*tilesheet)); oxRequire(tsd, normalizeTileSheet(*tilesheet));
oxTracef("nostalgia.core.gfx.gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbb, tsd.width, tsd.height); 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()); ctx.cbbs[cbb].tex = renderer::createTexture(tsd.width, tsd.height, tsd.pixels.data());
if (loadDefaultPalette) { if (paletteBank.has_value() && tilesheet->defaultPalette) {
oxReturnError(loadBgPalette(ctx, tilesheet->defaultPalette)); oxReturnError(loadBgPalette(ctx, *paletteBank, tilesheet->defaultPalette));
} }
return {}; return {};
} }
@ -578,11 +600,11 @@ void setBgTile(
uint_t bgIdx, uint_t bgIdx,
int column, int column,
int row, int row,
uint8_t tile) noexcept { BgTile const&tile) noexcept {
oxTracef( oxTracef(
"nostalgia.core.gfx.setBgTile", "nostalgia.core.gfx.setBgTile",
"bgIdx: {}, column: {}, row: {}, tile: {}", "bgIdx: {}, column: {}, row: {}, tile: {}, palBank: {}",
bgIdx, column, row, tile); bgIdx, column, row, tile.tileIdx, tile.palBank);
const auto z = static_cast<uint_t>(bgIdx); const auto z = static_cast<uint_t>(bgIdx);
const auto y = static_cast<uint_t>(row); const auto y = static_cast<uint_t>(row);
const auto x = static_cast<uint_t>(column); const auto x = static_cast<uint_t>(column);
@ -595,8 +617,11 @@ void setBgTile(
static_cast<uint_t>(i * renderer::BgVertexVboRows), static_cast<uint_t>(i * renderer::BgVertexVboRows),
static_cast<float>(x), static_cast<float>(x),
static_cast<float>(y), static_cast<float>(y),
static_cast<float>(tile), static_cast<float>(tile.tileIdx),
bg.priority, bg.priority,
static_cast<float>(tile.palBank * 16),
tile.flipX,
tile.flipY,
vbo, vbo,
ebo); ebo);
cbb.updated = true; cbb.updated = true;
@ -628,10 +653,12 @@ bool bgStatus(Context &ctx, uint_t bg) noexcept {
return ctx.backgrounds[bg].enabled; return ctx.backgrounds[bg].enabled;
} }
void setBgStatus(Context &ctx, uint_t bg, bool status) noexcept { void setBgStatus(Context&ctx, uint_t bg, bool status) noexcept {
ctx.backgrounds[bg].enabled = status; ctx.backgrounds[bg].enabled = status;
} }
void setBgBpp(Context&, unsigned, unsigned) noexcept {}
void setBgCbb(Context &ctx, uint_t bgIdx, uint_t cbbIdx) noexcept { void setBgCbb(Context &ctx, uint_t bgIdx, uint_t cbbIdx) noexcept {
auto &bg = ctx.backgrounds[bgIdx]; auto &bg = ctx.backgrounds[bgIdx];
bg.cbbIdx = cbbIdx; bg.cbbIdx = cbbIdx;

View File

@ -18,7 +18,7 @@ constexpr uint64_t TileRows = 128;
constexpr uint64_t TileColumns = 128; constexpr uint64_t TileColumns = 128;
constexpr uint64_t TileCount = TileRows * TileColumns; constexpr uint64_t TileCount = TileRows * TileColumns;
constexpr uint64_t BgVertexVboRows = 4; constexpr uint64_t BgVertexVboRows = 4;
constexpr uint64_t BgVertexVboRowLength = 6; constexpr uint64_t BgVertexVboRowLength = 7;
constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength;
constexpr uint64_t BgVertexEboLength = 6; constexpr uint64_t BgVertexEboLength = 6;
constexpr uint64_t SpriteVertexVboRows = 4; constexpr uint64_t SpriteVertexVboRows = 4;

View File

@ -10,7 +10,7 @@ AddSubSheetCommand::AddSubSheetCommand(
TileSheet &img, TileSheet &img,
TileSheet::SubSheetIdx parentIdx) noexcept: TileSheet::SubSheetIdx parentIdx) noexcept:
m_img(img), m_parentIdx(std::move(parentIdx)) { 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()) { if (!parent.subsheets.empty()) {
auto idx = m_parentIdx; auto idx = m_parentIdx;
idx.emplace_back(parent.subsheets.size()); idx.emplace_back(parent.subsheets.size());
@ -25,7 +25,7 @@ AddSubSheetCommand::AddSubSheetCommand(
} }
void AddSubSheetCommand::redo() noexcept { void AddSubSheetCommand::redo() noexcept {
auto &parent = m_img.getSubSheet(m_parentIdx); auto &parent = getSubSheet(m_img, m_parentIdx);
if (m_addedSheets.size() < 2) { if (m_addedSheets.size() < 2) {
auto i = parent.subsheets.size(); auto i = parent.subsheets.size();
parent.subsheets.emplace_back(m_img.idIt++, ox::sfmt("Subsheet {}", i), 1, 1, m_img.bpp); parent.subsheets.emplace_back(m_img.idIt++, ox::sfmt("Subsheet {}", i), 1, 1, m_img.bpp);
@ -38,7 +38,7 @@ void AddSubSheetCommand::redo() noexcept {
} }
void AddSubSheetCommand::undo() noexcept { void AddSubSheetCommand::undo() noexcept {
auto &parent = m_img.getSubSheet(m_parentIdx); auto &parent = getSubSheet(m_img, m_parentIdx);
if (parent.subsheets.size() == 2) { if (parent.subsheets.size() == 2) {
auto s = parent.subsheets[0]; auto s = parent.subsheets[0];
parent.rows = s.rows; parent.rows = s.rows;
@ -47,7 +47,7 @@ void AddSubSheetCommand::undo() noexcept {
parent.subsheets.clear(); parent.subsheets.clear();
} else { } else {
for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) { for (auto idx = m_addedSheets.rbegin(); idx != m_addedSheets.rend(); ++idx) {
oxLogError(m_img.rmSubSheet(*idx)); oxLogError(rmSubSheet(m_img, *idx));
} }
} }
} }

View File

@ -31,27 +31,27 @@ CutPasteCommand::CutPasteCommand(
m_commandId(commandId), m_commandId(commandId),
m_img(img), m_img(img),
m_subSheetIdx(std::move(subSheetIdx)) { 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()) { for (const auto &p : cb.pixels()) {
const auto dstPt = p.pt + dstStart; const auto dstPt = p.pt + dstStart;
if (dstPt.x <= dstEnd.x && dstPt.y <= dstEnd.y) { if (dstPt.x <= dstEnd.x && dstPt.y <= dstEnd.y) {
const auto idx = subsheet.idx(dstPt); const auto idx = core::idx(subsheet, dstPt);
m_changes.emplace_back(static_cast<uint32_t>(idx), p.colorIdx, subsheet.getPixel(m_img.bpp, idx)); m_changes.emplace_back(static_cast<uint32_t>(idx), p.colorIdx, getPixel(subsheet, m_img.bpp, idx));
} }
} }
} }
void CutPasteCommand::redo() noexcept { void CutPasteCommand::redo() noexcept {
auto &subsheet = m_img.getSubSheet(m_subSheetIdx); auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
for (const auto &c : m_changes) { 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 { void CutPasteCommand::undo() noexcept {
auto &subsheet = m_img.getSubSheet(m_subSheetIdx); auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
for (const auto &c : m_changes) { 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));
} }
} }

View File

@ -19,7 +19,7 @@ core::DeleteTilesCommand::DeleteTilesCommand(
m_deletedPixels.resize(m_deleteSz); m_deletedPixels.resize(m_deleteSz);
// copy pixels to be erased // copy pixels to be erased
{ {
auto &s = m_img.getSubSheet(m_idx); auto &s = getSubSheet(m_img, m_idx);
auto &p = s.pixels; auto &p = s.pixels;
auto dst = m_deletedPixels.data(); auto dst = m_deletedPixels.data();
auto src = p.data() + m_deletePos; auto src = p.data() + m_deletePos;
@ -29,7 +29,7 @@ core::DeleteTilesCommand::DeleteTilesCommand(
} }
void core::DeleteTilesCommand::redo() noexcept { void core::DeleteTilesCommand::redo() noexcept {
auto &s = m_img.getSubSheet(m_idx); auto &s = getSubSheet(m_img, m_idx);
auto &p = s.pixels; auto &p = s.pixels;
auto srcPos = m_deletePos + m_deleteSz; auto srcPos = m_deletePos + m_deleteSz;
const auto src = p.data() + srcPos; const auto src = p.data() + srcPos;
@ -40,7 +40,7 @@ void core::DeleteTilesCommand::redo() noexcept {
} }
void DeleteTilesCommand::undo() noexcept { void DeleteTilesCommand::undo() noexcept {
auto &s = m_img.getSubSheet(m_idx); auto &s = getSubSheet(m_img, m_idx);
auto &p = s.pixels; auto &p = s.pixels;
const auto src = p.data() + m_deletePos; const auto src = p.data() + m_deletePos;
const auto dst1 = p.data() + m_deletePos + m_deleteSz; const auto dst1 = p.data() + m_deletePos + m_deleteSz;

View File

@ -14,8 +14,8 @@ DrawCommand::DrawCommand(
m_img(img), m_img(img),
m_subSheetIdx(std::move(subSheetIdx)), m_subSheetIdx(std::move(subSheetIdx)),
m_palIdx(palIdx) { m_palIdx(palIdx) {
auto &subsheet = m_img.getSubSheet(m_subSheetIdx); auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
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));
} }
DrawCommand::DrawCommand( DrawCommand::DrawCommand(
@ -26,22 +26,22 @@ DrawCommand::DrawCommand(
m_img(img), m_img(img),
m_subSheetIdx(std::move(subSheetIdx)), m_subSheetIdx(std::move(subSheetIdx)),
m_palIdx(palIdx) { m_palIdx(palIdx) {
auto &subsheet = m_img.getSubSheet(m_subSheetIdx); auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
for (const auto idx : idxList) { 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 { bool DrawCommand::append(std::size_t idx) noexcept {
auto &subsheet = m_img.getSubSheet(m_subSheetIdx); auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
if (m_changes.back().value->idx != idx && subsheet.getPixel(m_img.bpp, idx) != m_palIdx) { if (m_changes.back().value->idx != idx && getPixel(subsheet, m_img.bpp, idx) != m_palIdx) {
// duplicate entries are bad // duplicate entries are bad
auto existing = ox::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) { auto existing = ox::find_if(m_changes.cbegin(), m_changes.cend(), [idx](const auto &c) {
return c.idx == idx; return c.idx == idx;
}); });
if (existing == m_changes.cend()) { if (existing == m_changes.cend()) {
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));
subsheet.setPixel(m_img.bpp, idx, static_cast<uint8_t>(m_palIdx)); setPixel(subsheet, m_img.bpp, idx, static_cast<uint8_t>(m_palIdx));
return true; return true;
} }
} }
@ -57,16 +57,16 @@ bool DrawCommand::append(const ox::Vector<std::size_t> &idxList) noexcept {
} }
void DrawCommand::redo() 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) { 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 { void DrawCommand::undo() noexcept {
auto &subsheet = m_img.getSubSheet(m_subSheetIdx); auto &subsheet = getSubSheet(m_img, m_subSheetIdx);
for (const auto &c : m_changes) { 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));
} }
} }

View File

@ -19,7 +19,7 @@ core::InsertTilesCommand::InsertTilesCommand(
m_deletedPixels.resize(m_insertCnt); m_deletedPixels.resize(m_insertCnt);
// copy pixels to be erased // copy pixels to be erased
{ {
auto &s = m_img.getSubSheet(m_idx); auto &s = getSubSheet(m_img, m_idx);
auto &p = s.pixels; auto &p = s.pixels;
auto dst = m_deletedPixels.data(); auto dst = m_deletedPixels.data();
auto src = p.data() + p.size() - m_insertCnt; auto src = p.data() + p.size() - m_insertCnt;
@ -29,7 +29,7 @@ core::InsertTilesCommand::InsertTilesCommand(
} }
void InsertTilesCommand::redo() noexcept { void InsertTilesCommand::redo() noexcept {
auto &s = m_img.getSubSheet(m_idx); auto &s = getSubSheet(m_img, m_idx);
auto &p = s.pixels; auto &p = s.pixels;
auto dstPos = m_insertPos + m_insertCnt; auto dstPos = m_insertPos + m_insertCnt;
const auto dst = p.data() + dstPos; const auto dst = p.data() + dstPos;
@ -39,7 +39,7 @@ void InsertTilesCommand::redo() noexcept {
} }
void InsertTilesCommand::undo() noexcept { void InsertTilesCommand::undo() noexcept {
auto &s = m_img.getSubSheet(m_idx); auto &s = getSubSheet(m_img, m_idx);
auto &p = s.pixels; auto &p = s.pixels;
const auto srcIdx = m_insertPos + m_insertCnt; const auto srcIdx = m_insertPos + m_insertCnt;
const auto src = p.data() + srcIdx; const auto src = p.data() + srcIdx;

View File

@ -11,17 +11,17 @@ core::RmSubSheetCommand::RmSubSheetCommand(TileSheet &img, TileSheet::SubSheetId
m_idx(std::move(idx)), m_idx(std::move(idx)),
m_parentIdx(m_idx) { m_parentIdx(m_idx) {
m_parentIdx.pop_back(); 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]; m_sheet = parent.subsheets[*m_idx.back().value];
} }
void RmSubSheetCommand::redo() noexcept { 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); oxLogError(parent.subsheets.erase(*m_idx.back().value).error);
} }
void RmSubSheetCommand::undo() noexcept { void RmSubSheetCommand::undo() noexcept {
auto &parent = m_img.getSubSheet(m_parentIdx); auto &parent = getSubSheet(m_img, m_parentIdx);
auto i = *m_idx.back().value; auto i = *m_idx.back().value;
parent.subsheets.insert(i, m_sheet); parent.subsheets.insert(i, m_sheet);
} }

View File

@ -14,22 +14,22 @@ core::UpdateSubSheetCommand::UpdateSubSheetCommand(
int rows) noexcept: int rows) noexcept:
m_img(img), m_img(img),
m_idx(std::move(idx)), 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_newName(std::move(name)),
m_newCols(cols), m_newCols(cols),
m_newRows(rows) { m_newRows(rows) {
} }
void UpdateSubSheetCommand::redo() noexcept { void UpdateSubSheetCommand::redo() noexcept {
auto &sheet = m_img.getSubSheet(m_idx); auto &sheet = getSubSheet(m_img, m_idx);
sheet.name = m_newName; sheet.name = m_newName;
sheet.columns = m_newCols; sheet.columns = m_newCols;
sheet.rows = m_newRows; 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 { void UpdateSubSheetCommand::undo() noexcept {
auto &sheet = m_img.getSubSheet(m_idx); auto &sheet = getSubSheet(m_img, m_idx);
sheet = m_sheet; sheet = m_sheet;
} }

View File

@ -191,7 +191,7 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("-", btnSize)) { if (ImGui::Button("-", btnSize)) {
const auto &activeSubsheetIdx = m_model.activeSubSheetIdx(); const auto &activeSubsheetIdx = m_model.activeSubSheetIdx();
if (activeSubsheetIdx.size() > 0) { if (!activeSubsheetIdx.empty()) {
m_model.rmSubsheet(activeSubsheetIdx); m_model.rmSubsheet(activeSubsheetIdx);
} }
} }
@ -205,12 +205,13 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
} }
TileSheet::SubSheetIdx path; TileSheet::SubSheetIdx path;
static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; 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("Subsheet", ImGuiTableColumnFlags_NoHide);
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
drawSubsheetSelector(&m_view.img().subsheet, &path); drawSubsheetSelector(m_view.img().subsheet, path);
ImGui::EndTable(); ImGui::EndTable();
} }
} }
@ -221,31 +222,34 @@ void TileSheetEditorImGui::draw(turbine::Context&) noexcept {
m_exportMenu.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); ImGui::TableNextRow(0, 5);
using Str = ox::BasicString<100>; using Str = ox::BasicString<100>;
auto pathStr = ox::join<Str>("##", *path).value; auto pathStr = ox::join<Str>("##", path).value;
auto lbl = ox::sfmt<Str>("{}##{}", subsheet->name, pathStr); auto lbl = ox::sfmt<Str>("{}##{}", subsheet.name, pathStr);
const auto rowSelected = *path == m_model.activeSubSheetIdx(); const auto rowSelected = path == m_model.activeSubSheetIdx();
const auto flags = ImGuiTreeNodeFlags_SpanFullWidth const auto flags = ImGuiTreeNodeFlags_SpanFullWidth
| ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnArrow
| ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DefaultOpen
| (subsheet->subsheets.empty() ? ImGuiTreeNodeFlags_Leaf : 0) | (subsheet.subsheets.empty() ? ImGuiTreeNodeFlags_Leaf : 0)
| (rowSelected ? ImGuiTreeNodeFlags_Selected : 0); | (rowSelected ? ImGuiTreeNodeFlags_Selected : 0);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
const auto open = ImGui::TreeNodeEx(lbl.c_str(), flags); const auto open = ImGui::TreeNodeEx(lbl.c_str(), flags);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
m_model.setActiveSubsheet(*path); m_model.setActiveSubsheet(path);
} }
if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) { if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) {
showSubsheetEditor(); showSubsheetEditor();
} }
if (subsheet->subsheets.empty()) { ImGui::TableNextColumn();
ImGui::Text("%d", subsheet.id);
if (subsheet.subsheets.empty()) {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%d", subsheet->columns); ImGui::Text("%d", subsheet.columns);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%d", subsheet->rows); ImGui::Text("%d", subsheet.rows);
} else { } else {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("--"); ImGui::Text("--");
@ -253,12 +257,14 @@ void TileSheetEditorImGui::drawSubsheetSelector(TileSheet::SubSheet *subsheet, T
ImGui::Text("--"); ImGui::Text("--");
} }
if (open) { if (open) {
for (auto i = 0ul; auto &child : subsheet->subsheets) { for (auto i = 0ul; auto &child : subsheet.subsheets) {
path->push_back(i); path.push_back(i);
ImGui::PushID(static_cast<int>(i)); ImGui::PushID(static_cast<int>(i));
drawSubsheetSelector(&child, path); ImGui::Indent(-indentReduce);
drawSubsheetSelector(child, path);
ImGui::Indent(indentReduce);
ImGui::PopID(); ImGui::PopID();
path->pop_back(); path.pop_back();
++i; ++i;
} }
ImGui::TreePop(); ImGui::TreePop();

View File

@ -83,7 +83,7 @@ class TileSheetEditorImGui: public studio::Editor {
void draw(turbine::Context&) noexcept override; void draw(turbine::Context&) noexcept override;
void drawSubsheetSelector(TileSheet::SubSheet*, TileSheet::SubSheetIdx *path); void drawSubsheetSelector(TileSheet::SubSheet&, TileSheet::SubSheetIdx &path);
[[nodiscard]] [[nodiscard]]
static ox::Vec2 clickPos(ImVec2 const&winPos, ox::Vec2 clickPos) noexcept; static ox::Vec2 clickPos(ImVec2 const&winPos, ox::Vec2 clickPos) noexcept;

View File

@ -47,8 +47,8 @@ void TileSheetEditorModel::cut() {
for (int y = m_selectionBounds.y; y <= m_selectionBounds.y2(); ++y) { for (int y = m_selectionBounds.y; y <= m_selectionBounds.y2(); ++y) {
for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) { for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) {
auto pt = ox::Point(x, y); auto pt = ox::Point(x, y);
const auto idx = s->idx(pt); const auto idx = core::idx(*s, pt);
const auto c = s->getPixel(m_img.bpp, idx); const auto c = getPixel(*s, m_img.bpp, idx);
pt.x -= m_selectionBounds.x; pt.x -= m_selectionBounds.x;
pt.y -= m_selectionBounds.y; pt.y -= m_selectionBounds.y;
cb->addPixel(pt, c); cb->addPixel(pt, c);
@ -67,8 +67,8 @@ void TileSheetEditorModel::copy() {
for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) { for (int x = m_selectionBounds.x; x <= m_selectionBounds.x2(); ++x) {
auto pt = ox::Point(x, y); auto pt = ox::Point(x, y);
const auto s = activeSubSheet(); const auto s = activeSubSheet();
const auto idx = s->idx(pt); const auto idx = core::idx(*s, pt);
const auto c = s->getPixel(m_img.bpp, idx); const auto c = getPixel(*s, m_img.bpp, idx);
pt.x -= m_selectionBounds.x; pt.x -= m_selectionBounds.x;
pt.y -= m_selectionBounds.y; pt.y -= m_selectionBounds.y;
cb->addPixel(pt, c); 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 { 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) { if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
return; return;
} }
const auto idx = activeSubSheet.idx(pt); const auto idx = core::idx(activeSubSheet, pt);
if (m_ongoingDrawCommand) { if (m_ongoingDrawCommand) {
m_updated = m_updated || m_ongoingDrawCommand->append(idx); 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))); pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idx, static_cast<int>(palIdx)));
} }
} }
@ -158,16 +158,16 @@ void TileSheetEditorModel::setActiveSubsheet(TileSheet::SubSheetIdx const&idx) n
} }
void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept { 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 // build idx list
ox::Array<bool, PixelsPerTile> updateMap = {}; 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) { if (pt.x >= s.columns * TileWidth || pt.y >= s.rows * TileHeight) {
return; return;
} }
getFillPixels(updateMap.data(), pt, oldColor); getFillPixels(updateMap.data(), pt, oldColor);
ox::Vector<std::size_t> idxList; 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) { for (auto u : updateMap) {
if (u) { if (u) {
idxList.emplace_back(i); idxList.emplace_back(i);
@ -177,7 +177,7 @@ void TileSheetEditorModel::fill(ox::Point const&pt, int palIdx) noexcept {
// do updates to sheet // do updates to sheet
if (m_ongoingDrawCommand) { if (m_ongoingDrawCommand) {
m_updated = m_updated || m_ongoingDrawCommand->append(idxList); 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)); pushCommand(ox::make<DrawCommand>(m_img, m_activeSubsSheetIdx, idxList, palIdx));
} }
} }
@ -213,7 +213,7 @@ bool TileSheetEditorModel::updated() const noexcept {
return m_updated; return m_updated;
} }
ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd) noexcept { ox::Error TileSheetEditorModel::markUpdatedCmdId(studio::UndoCommand const*cmd) noexcept {
m_updated = true; m_updated = true;
const auto cmdId = cmd->commandId(); const auto cmdId = cmd->commandId();
if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) { if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) {
@ -221,7 +221,7 @@ ox::Error TileSheetEditorModel::markUpdatedCmdId(const studio::UndoCommand *cmd)
paletteChanged.emit(); paletteChanged.emit();
} }
auto tsCmd = dynamic_cast<const TileSheetCommand*>(cmd); 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) { if (idx != m_activeSubsSheetIdx) {
setActiveSubsheet(idx); setActiveSubsheet(idx);
} }
@ -267,16 +267,16 @@ void TileSheetEditorModel::getFillPixels(bool *pixels, ox::Point const&pt, int o
const auto tile = tileIdx(pt); const auto tile = tileIdx(pt);
// mark pixels to update // mark pixels to update
pixels[idx % PixelsPerTile] = true; 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); 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); 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); 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); getFillPixels(pixels, bottomPt, oldColor);
} }
} }

View File

@ -79,13 +79,13 @@ class TileSheetEditorModel: public ox::SignalHandler {
[[nodiscard]] [[nodiscard]]
const TileSheet::SubSheet *activeSubSheet() const noexcept { const TileSheet::SubSheet *activeSubSheet() const noexcept {
auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
return &activeSubSheet; return &activeSubSheet;
} }
[[nodiscard]] [[nodiscard]]
TileSheet::SubSheet *activeSubSheet() noexcept { TileSheet::SubSheet *activeSubSheet() noexcept {
auto &activeSubSheet = m_img.getSubSheet(m_activeSubsSheetIdx); auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
return &activeSubSheet; return &activeSubSheet;
} }
@ -105,7 +105,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
[[nodiscard]] [[nodiscard]]
bool updated() const noexcept; bool updated() const noexcept;
ox::Error markUpdatedCmdId(const studio::UndoCommand *cmd) noexcept; ox::Error markUpdatedCmdId(studio::UndoCommand const*cmd) noexcept;
ox::Error markUpdated() noexcept; ox::Error markUpdated() noexcept;
@ -118,10 +118,9 @@ class TileSheetEditorModel: public ox::SignalHandler {
bool pixelSelected(std::size_t idx) const noexcept; bool pixelSelected(std::size_t idx) const noexcept;
protected: private:
void getFillPixels(bool *pixels, ox::Point const&pt, int oldColor) const noexcept; void getFillPixels(bool *pixels, ox::Point const&pt, int oldColor) const noexcept;
private:
void pushCommand(studio::UndoCommand *cmd) noexcept; void pushCommand(studio::UndoCommand *cmd) noexcept;
}; };

View File

@ -13,7 +13,7 @@ namespace nostalgia::core {
TileSheetEditorView::TileSheetEditorView(turbine::Context &ctx, ox::StringView path, studio::UndoStack &undoStack): TileSheetEditorView::TileSheetEditorView(turbine::Context &ctx, ox::StringView path, studio::UndoStack &undoStack):
m_model(ctx, path, undoStack), m_model(ctx, path, undoStack),
m_pixelsDrawer(&m_model) { m_pixelsDrawer(m_model) {
// build shaders // build shaders
oxThrowError(m_pixelsDrawer.buildShader()); oxThrowError(m_pixelsDrawer.buildShader());
oxThrowError(m_pixelGridDrawer.buildShader()); oxThrowError(m_pixelGridDrawer.buildShader());

View File

@ -9,7 +9,7 @@
namespace nostalgia::core { 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 { void TileSheetPixels::setPixelSizeMod(float sm) noexcept {
@ -94,15 +94,15 @@ void TileSheetPixels::setPixelBufferObject(
void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept { void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept {
// set buffer lengths // set buffer lengths
const auto subSheet = m_model->activeSubSheet(); const auto subSheet = m_model.activeSubSheet();
const auto pal = m_model->pal(); const auto pal = m_model.pal();
const auto width = subSheet->columns * TileWidth; const auto width = subSheet->columns * TileWidth;
const auto height = subSheet->rows * TileHeight; const auto height = subSheet->rows * TileHeight;
const auto pixels = static_cast<unsigned>(width * height); const auto pixels = static_cast<unsigned>(width * height);
m_bufferSet.vertices.resize(pixels * VertexVboLength); m_bufferSet.vertices.resize(pixels * VertexVboLength);
m_bufferSet.elements.resize(pixels * VertexEboLength); m_bufferSet.elements.resize(pixels * VertexEboLength);
// set pixels // 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); auto color = pal->color(p);
const auto pt = idxToPt(static_cast<int>(i), subSheet->columns); const auto pt = idxToPt(static_cast<int>(i), subSheet->columns);
const auto fx = static_cast<float>(pt.x); 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()) { if (i * VertexEboLength + VertexEboLength > m_bufferSet.elements.size()) {
return; return;
} }
if (m_model->pixelSelected(i)) { if (m_model.pixelSelected(i)) {
const auto r = red16(color) / 2; const auto r = red16(color) / 2;
const auto g = (green16(color) + 20) / 2; const auto g = (green16(color) + 20) / 2;
const auto b = (blue16(color) + 31) / 2; const auto b = (blue16(color) + 31) / 2;

View File

@ -43,10 +43,10 @@ class TileSheetPixels {
float m_pixelSizeMod = 1; float m_pixelSizeMod = 1;
glutils::GLProgram m_shader; glutils::GLProgram m_shader;
glutils::BufferSet m_bufferSet; glutils::BufferSet m_bufferSet;
const class TileSheetEditorModel *m_model = nullptr; const class TileSheetEditorModel &m_model;
public: public:
explicit TileSheetPixels(class TileSheetEditorModel *model) noexcept; explicit TileSheetPixels(class TileSheetEditorModel &model) noexcept;
void setPixelSizeMod(float sm) noexcept; void setPixelSizeMod(float sm) noexcept;

View File

@ -10,133 +10,47 @@
namespace nostalgia::core { namespace nostalgia::core {
TileSheet::SubSheet::SubSheet(SubSheet &&other) noexcept: std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept {
id (other.id), return ptToIdx(pt, ss.columns);
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 = {};
} }
TileSheet::SubSheet::SubSheet( uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept {
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 {
if (idx & 1) { if (idx & 1) {
return this->pixels[idx / 2] >> 4; return ss.pixels[idx / 2] >> 4;
} else { } 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 { uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, std::size_t idx) noexcept {
return this->pixels[idx]; 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) { if (pBpp == 4) {
return getPixel4Bpp(idx); return getPixel4Bpp(ss, idx);
} else { } else {
return getPixel8Bpp(idx); return getPixel8Bpp(ss, idx);
} }
} }
uint8_t TileSheet::SubSheet::getPixel4Bpp(ox::Point const&pt) const noexcept { uint8_t getPixel4Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept {
const auto idx = ptToIdx(pt, columns); const auto idx = ptToIdx(pt, ss.columns);
return getPixel4Bpp(idx); return getPixel4Bpp(ss, idx);
} }
uint8_t TileSheet::SubSheet::getPixel8Bpp(ox::Point const&pt) const noexcept { uint8_t getPixel8Bpp(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept {
const auto idx = ptToIdx(pt, columns); const auto idx = ptToIdx(pt, ss.columns);
return getPixel8Bpp(idx); return getPixel8Bpp(ss, idx);
} }
uint8_t TileSheet::SubSheet::getPixel(int8_t pBpp, ox::Point const&pt) const noexcept { uint8_t getPixel(TileSheet::SubSheet const&ss, int8_t pBpp, ox::Point const&pt) noexcept {
const auto idx = ptToIdx(pt, columns); const auto idx = ptToIdx(pt, ss.columns);
return getPixel(pBpp, idx); return getPixel(ss, pBpp, idx);
} }
void TileSheet::SubSheet::setPixel(int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept { void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, uint64_t idx, uint8_t palIdx) noexcept {
auto &pixel = this->pixels[static_cast<std::size_t>(idx / 2)]; auto &pixel = ss.pixels[static_cast<std::size_t>(idx / 2)];
if (pBpp == 4) { if (pBpp == 4) {
if (idx & 1) { if (idx & 1) {
pixel = static_cast<uint8_t>((pixel & 0b0000'1111) | (palIdx << 4)); 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 { void setPixel(TileSheet::SubSheet &ss, int8_t pBpp, ox::Point const&pt, uint8_t palIdx) noexcept {
const auto idx = ptToIdx(pt, columns); const auto idx = ptToIdx(pt, ss.columns);
setPixel(pBpp, idx, palIdx); 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) { switch (pBpp) {
case 4: case 4:
pixels.resize(cnt / 2); ss.pixels.resize(cnt / 2);
return OxError(0); return OxError(0);
case 8: case 8:
pixels.resize(cnt); ss.pixels.resize(cnt);
return OxError(0); return OxError(0);
default: default:
return OxError(1, "Invalid pBpp used for TileSheet::SubSheet::setPixelCount"); return OxError(1, "Invalid pBpp used for TileSheet::SubSheet::setPixelCount");
} }
} }
unsigned TileSheet::SubSheet::pixelCnt(int8_t pBpp) const noexcept { unsigned pixelCnt(TileSheet::SubSheet const&ss, int8_t pBpp) noexcept {
const auto pixelsSize = static_cast<unsigned>(pixels.size()); const auto pixelsSize = static_cast<unsigned>(ss.pixels.size());
return pBpp == 4 ? pixelsSize * 2 : pixelsSize; return pBpp == 4 ? pixelsSize * 2 : pixelsSize;
} }
ox::Result<unsigned> TileSheet::SubSheet::getTileOffset( ox::Result<ox::StringView> getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept {
ox::SpanView<ox::StringView> const&pNamePath, if (ss.id == pId) {
int8_t pBpp, return ox::StringView(ss.name);
std::size_t pIt,
unsigned pCurrentTotal) const noexcept {
// pIt == pNamePath.size() - 1 &&
if (name != pNamePath[pIt]) {
return OxError(2, "Wrong branch");
} }
if (pIt == pNamePath.size() - 1) { for (const auto &sub : ss.subsheets) {
return pCurrentTotal; const auto [name, err] = getNameFor(sub, pId);
}
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);
if (!err) { if (!err) {
return name; return name;
} }
@ -222,138 +99,181 @@ ox::Result<ox::StringView> TileSheet::SubSheet::getNameFor(SubSheetId pId) const
} }
TileSheet &TileSheet::operator=(TileSheet const&other) noexcept { TileSheet::SubSheetIdx validateSubSheetIdx(
if (this != &other) { TileSheet::SubSheetIdx const&pIdx,
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,
std::size_t pIdxIt, std::size_t pIdxIt,
const SubSheet *pSubsheet) noexcept { TileSheet::SubSheet const&pSubsheet) noexcept {
if (pIdxIt == pIdx.size()) { if (pIdxIt == pIdx.size()) {
return pIdx; return pIdx;
} }
const auto currentIdx = pIdx[pIdxIt]; const auto currentIdx = pIdx[pIdxIt];
if (pSubsheet->subsheets.size() <= currentIdx) { if (pSubsheet.subsheets.size() <= currentIdx) {
auto out = pIdx; auto out = pIdx;
if (!pSubsheet->subsheets.empty()) { if (!pSubsheet.subsheets.empty()) {
*out.back().value = pSubsheet->subsheets.size() - 1; *out.back().value = pSubsheet.subsheets.size() - 1;
} else { } else {
out.pop_back(); out.pop_back();
} }
return out; 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 { TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept {
return validateSubSheetIdx(idx, 0, &subsheet); return validateSubSheetIdx(idx, 0, ts.subsheet);
} }
const TileSheet::SubSheet &TileSheet::getSubSheet( const TileSheet::SubSheet &getSubSheet(
TileSheet::SubSheetIdx const&idx, TileSheet::SubSheetIdx const&idx,
std::size_t idxIt, std::size_t idxIt,
SubSheet const*pSubsheet) noexcept { TileSheet::SubSheet const&pSubsheet) noexcept {
if (idxIt == idx.size()) { if (idxIt == idx.size()) {
return *pSubsheet; return pSubsheet;
} }
const auto currentIdx = idx[idxIt]; const auto currentIdx = idx[idxIt];
if (pSubsheet->subsheets.size() < currentIdx) { if (pSubsheet.subsheets.size() < currentIdx) {
return *pSubsheet; 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, TileSheet::SubSheetIdx const&idx,
std::size_t idxIt, std::size_t idxIt,
TileSheet::SubSheet *pSubsheet) noexcept { TileSheet::SubSheet &pSubsheet) noexcept {
if (idxIt == idx.size()) { 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 { TileSheet::SubSheet const&getSubSheet(TileSheet const&ts, TileSheet::SubSheetIdx const&idx) noexcept {
return getSubSheet(idx, 0, &subsheet); return core::getSubSheet(idx, 0, ts.subsheet);
} }
TileSheet::SubSheet &TileSheet::getSubSheet(TileSheet::SubSheetIdx const&idx) noexcept { TileSheet::SubSheet &getSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
return getSubSheet(idx, 0, &subsheet); return core::getSubSheet(idx, 0, ts.subsheet);
} }
ox::Error TileSheet::addSubSheet(TileSheet::SubSheetIdx const&idx) noexcept { ox::Error addSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
auto &parent = getSubSheet(idx); auto &parent = getSubSheet(ts, idx);
if (parent.subsheets.size() < 2) { 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 { } else {
parent.subsheets.emplace_back(idIt++, "Subsheet 0", parent.columns, parent.rows, bpp); parent.subsheets.emplace_back(++ts.idIt, "Subsheet 0", parent.columns, parent.rows, ts.bpp);
parent.subsheets.emplace_back(idIt++, "Subsheet 1", 1, 1, bpp); parent.subsheets.emplace_back(++ts.idIt, "Subsheet 1", 1, 1, ts.bpp);
} }
return OxError(0); return OxError(0);
} }
ox::Error TileSheet::rmSubSheet( ox::Error rmSubSheet(
SubSheetIdx const&idx, TileSheet &ts,
TileSheet::SubSheetIdx const&idx,
std::size_t idxIt, std::size_t idxIt,
SubSheet *pSubsheet) noexcept { TileSheet::SubSheet &pSubsheet) noexcept {
if (idxIt == idx.size() - 1) { 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 { ox::Error rmSubSheet(TileSheet &ts, TileSheet::SubSheetIdx const&idx) noexcept {
return rmSubSheet(idx, 0, &subsheet); return rmSubSheet(ts, idx, 0, ts.subsheet);
} }
uint8_t TileSheet::getPixel4Bpp( uint8_t getPixel4Bpp(
TileSheet const&ts,
ox::Point const&pt, ox::Point const&pt,
TileSheet::SubSheetIdx const&subsheetIdx) const noexcept { TileSheet::SubSheetIdx const&subsheetIdx) noexcept {
oxAssert(bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp"); oxAssert(ts.bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp");
auto &s = this->getSubSheet(subsheetIdx); auto &s = getSubSheet(ts, subsheetIdx);
const auto idx = ptToIdx(pt, s.columns); 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, ox::Point const&pt,
TileSheet::SubSheetIdx const&subsheetIdx) const noexcept { TileSheet::SubSheetIdx const&subsheetIdx) noexcept {
oxAssert(bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp"); oxAssert(ts.bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp");
auto &s = this->getSubSheet(subsheetIdx); auto &s = getSubSheet(ts, subsheetIdx);
const auto idx = ptToIdx(pt, s.columns); 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 { static ox::Result<SubSheetId> getIdFor(
return subsheet.getIdFor(ox::split<8>(path, '.')); 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 { ox::Result<SubSheetId> getIdFor(TileSheet const&ts, ox::CRStringView path) noexcept {
return subsheet.getTileOffset(ox::split<8>(pNamePath, '.'), bpp); 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; ox::Vector<uint8_t> out;
subsheet.readPixelsTo(&out); readPixelsTo(ts.subsheet, out);
return out; return out;
} }

View File

@ -42,7 +42,7 @@ struct TileDoc {
if (subsheetId > -1) { if (subsheetId > -1) {
return subsheetId; return subsheetId;
} }
return ts.getIdFor(subsheetPath); return getIdFor(ts, subsheetPath);
} }
[[nodiscard]] [[nodiscard]]
@ -50,7 +50,7 @@ struct TileDoc {
core::TileSheet const&ts) const noexcept { core::TileSheet const&ts) const noexcept {
// prefer the already present path // prefer the already present path
if (!subsheetPath.len()) { if (!subsheetPath.len()) {
return ts.getNameFor(subsheetId); return core::getNameFor(ts, subsheetId);
} }
return ox::StringView(subsheetPath); return ox::StringView(subsheetPath);
} }

View File

@ -54,7 +54,7 @@ ox::Error SceneDocToSceneStaticConverter::convert(
auto dstTile = dstLayer.tile(tileIdx); auto dstTile = dstLayer.tile(tileIdx);
dstTile.tileType = srcTile.type; dstTile.tileType = srcTile.type;
oxRequire(path, srcTile.getSubsheetPath(*ts)); oxRequire(path, srcTile.getSubsheetPath(*ts));
oxRequire(mapIdx, ts->getTileOffset(path)); oxRequire(mapIdx, getTileOffset(*ts, path));
dstTile.tileMapIdx = static_cast<uint16_t>(mapIdx); dstTile.tileMapIdx = static_cast<uint16_t>(mapIdx);
setLayerAttachments(layerIdx, srcTile, dstTile); setLayerAttachments(layerIdx, srcTile, dstTile);
++tileIdx; ++tileIdx;

View File

@ -18,7 +18,7 @@ ox::Error Scene::setupDisplay(core::Context &ctx) const noexcept {
} }
auto const&palette = m_sceneStatic.palettes[0]; auto const&palette = m_sceneStatic.palettes[0];
oxReturnError(core::loadBgTileSheet(ctx, 0, m_sceneStatic.tilesheet)); oxReturnError(core::loadBgTileSheet(ctx, 0, m_sceneStatic.tilesheet));
oxReturnError(core::loadBgPalette(ctx, palette)); oxReturnError(core::loadBgPalette(ctx, 0, palette));
// disable all backgrounds // disable all backgrounds
core::setBgStatus(ctx, 0); core::setBgStatus(ctx, 0);
for (auto layerNo = 0u; auto const&layer : m_sceneStatic.tileMapIdx) { for (auto layerNo = 0u; auto const&layer : m_sceneStatic.tileMapIdx) {

View File

@ -82,7 +82,8 @@ static ox::Error runTest(turbine::Context &tctx) {
[[maybe_unused]] [[maybe_unused]]
static ox::Error runTileSheetSetTest(turbine::Context &tctx) { static ox::Error runTileSheetSetTest(turbine::Context &tctx) {
// this should make the screen display 'ABCDB' // this should make the screen display 'ABCDB', with the A being upside down
// and the first B being backwards
constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal"); constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal");
oxRequireM(cctx, core::init(tctx)); oxRequireM(cctx, core::init(tctx));
turbine::setApplicationData(tctx, cctx.get()); turbine::setApplicationData(tctx, cctx.get());
@ -96,14 +97,16 @@ static ox::Error runTileSheetSetTest(turbine::Context &tctx) {
{ .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 1, .tiles = 1}} }, { .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 1, .tiles = 1}} },
}, },
}; };
constexpr auto bgPalBank = 1;
oxReturnError(core::loadBgTileSheet(*cctx, 0, set)); oxReturnError(core::loadBgTileSheet(*cctx, 0, set));
oxReturnError(core::loadSpriteTileSheet(*cctx, set)); oxReturnError(core::loadSpriteTileSheet(*cctx, set));
oxReturnError(core::loadBgPalette(*cctx, PaletteAddr)); oxReturnError(core::loadBgPalette(*cctx, bgPalBank, PaletteAddr));
oxReturnError(core::loadBgPalette(*cctx, 0, ox::StringLiteral("/Palettes/Chester.npal")));
oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr)); oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr));
core::setBgStatus(*cctx, 0, true); core::setBgStatus(*cctx, 0, true);
core::setBgTile(*cctx, 0, 10, 9, 1); core::setBgTile(*cctx, 0, 10, 9, { .tileIdx = 1, .palBank = bgPalBank, .flipX = 0, .flipY = 1 });
core::setBgTile(*cctx, 0, 11, 9, 2); core::setBgTile(*cctx, 0, 11, 9, { .tileIdx = 2, .palBank = bgPalBank, .flipX = 1, .flipY = 0 });
core::setBgTile(*cctx, 0, 13, 9, 4); core::setBgTile(*cctx, 0, 13, 9, { .tileIdx = 4, .palBank = bgPalBank, .flipX = 0, .flipY = 0 });
core::setSprite(*cctx, 16, { core::setSprite(*cctx, 16, {
.enabled = true, .enabled = true,
.x = 12 * 8, .x = 12 * 8,

View File

@ -148,7 +148,7 @@ ox::Error convert(keel::Context &ctx, ox::Buffer const&buff, DstType *outObj) no
} }
template<typename DstType> template<typename DstType>
ox::Result<ox::Buffer> convertBuffToBuff(keel::Context &ctx, const ox::Buffer &srcBuffer, ox::ClawFormat fmt) noexcept { ox::Result<ox::Buffer> convertBuffToBuff(keel::Context &ctx, ox::Buffer const&srcBuffer, ox::ClawFormat fmt) noexcept {
static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>(); static constexpr auto DstTypeName = ox::requireModelTypeName<DstType>();
static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>(); static constexpr auto DstTypeVersion = ox::requireModelTypeVersion<DstType>();
oxRequire(out, convert(ctx, srcBuffer, DstTypeName, DstTypeVersion)); oxRequire(out, convert(ctx, srcBuffer, DstTypeName, DstTypeVersion));

View File

@ -5,6 +5,7 @@ add_library(
filedialogmanager.cpp filedialogmanager.cpp
main.cpp main.cpp
newmenu.cpp newmenu.cpp
newproject.cpp
projectexplorer.cpp projectexplorer.cpp
projecttreemodel.cpp projecttreemodel.cpp
studioapp.cpp studioapp.cpp

View File

@ -115,12 +115,12 @@ void NewMenu::drawLastPageButtons(turbine::Context &ctx) noexcept {
} }
void NewMenu::finish(turbine::Context &ctx) noexcept { void NewMenu::finish(turbine::Context &ctx) noexcept {
auto const err = m_types[static_cast<std::size_t>(m_selectedType)]->write(ctx, m_itemName); auto const [path, err] = m_types[static_cast<std::size_t>(m_selectedType)]->write(ctx, m_itemName);
if (err) { if (err) {
oxLogError(err); oxLogError(err);
return; return;
} }
finished.emit(""); finished.emit(path);
m_stage = Stage::Closed; m_stage = Stage::Closed;
} }

View File

@ -0,0 +1,95 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <imgui.h>
#include <studio/imguiuitl.hpp>
#include <utility>
#include "filedialogmanager.hpp"
#include "newproject.hpp"
namespace studio {
NewProject::NewProject(ox::String projectDatadir) noexcept: m_projectDataDir(std::move(projectDatadir)) {
setTitle(ox::String("New Project"));
setSize({230, 140});
}
void NewProject::open() noexcept {
m_stage = Stage::Opening;
m_projectPath = "";
m_projectName = "";
}
void NewProject::close() noexcept {
m_stage = Stage::Closed;
m_open = false;
}
bool NewProject::isOpen() const noexcept {
return m_open;
}
void NewProject::draw(turbine::Context &ctx) noexcept {
switch (m_stage) {
case Stage::Opening:
ImGui::OpenPopup(title().c_str());
m_stage = Stage::NewItemName;
m_open = true;
[[fallthrough]];
case Stage::NewItemName:
drawNewProjectName(ctx);
break;
case Stage::Closed:
m_open = false;
break;
}
}
void NewProject::drawNewProjectName(turbine::Context &ctx) noexcept {
drawWindow(ctx, &m_open, [this, &ctx] {
ImGui::InputText("Name", m_projectName.data(), m_projectName.cap());
ImGui::Text("Path: %s", m_projectPath.c_str());
if (ImGui::Button("Browse")) {
oxLogError(studio::chooseDirectory().moveTo(m_projectPath));
}
drawLastPageButtons(ctx);
});
}
void NewProject::drawFirstPageButtons() noexcept {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
auto const btnSz = ImVec2(60, 20);
if (ImGui::Button("Next", btnSz)) {
m_stage = Stage::NewItemName;
}
ImGui::SameLine();
if (ImGui::Button("Cancel", btnSz)) {
ImGui::CloseCurrentPopup();
m_stage = Stage::Closed;
}
}
void NewProject::drawLastPageButtons(turbine::Context&) noexcept {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 95);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
if (ImGui::Button("Finish")) {
finish();
}
ImGui::SameLine();
if (ImGui::Button("Quit")) {
ImGui::CloseCurrentPopup();
m_stage = Stage::Closed;
}
}
void NewProject::finish() noexcept {
auto projectPath = ox::sfmt("{}/{}", m_projectPath, m_projectName);
finished.emit(projectPath);
m_stage = Stage::Closed;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/claw/claw.hpp>
#include <ox/event/signal.hpp>
#include <ox/std/string.hpp>
#include <studio/itemmaker.hpp>
#include <studio/popup.hpp>
namespace studio {
class NewProject: public studio::Popup {
public:
enum class Stage {
Closed,
Opening,
NewItemName,
};
// emits path parameter
ox::Signal<ox::Error(ox::StringView)> finished;
private:
Stage m_stage = Stage::Closed;
ox::String const m_projectDataDir;
ox::String m_projectPath;
ox::BString<255> m_projectName;
ox::Vector<ox::UniquePtr<studio::ItemMaker>> m_types;
bool m_open = false;
public:
NewProject(ox::String projectDatadir) noexcept;
void open() noexcept override;
void close() noexcept override;
[[nodiscard]]
bool isOpen() const noexcept override;
void draw(turbine::Context &ctx) noexcept override;
private:
void drawNewProjectName(turbine::Context &ctx) noexcept;
void drawFirstPageButtons() noexcept;
void drawLastPageButtons(turbine::Context &ctx) noexcept;
void finish() noexcept;
};
}

View File

@ -2,6 +2,8 @@
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/ */
#include <filesystem>
#include <imgui.h> #include <imgui.h>
#include <keel/media.hpp> #include <keel/media.hpp>
@ -42,15 +44,18 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringView projectDataDir) noexcep
m_ctx(ctx), m_ctx(ctx),
m_projectDataDir(projectDataDir), m_projectDataDir(projectDataDir),
m_projectExplorer(m_ctx), m_projectExplorer(m_ctx),
m_newProject(ox::String(projectDataDir)),
m_aboutPopup(m_ctx) { m_aboutPopup(m_ctx) {
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile); m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
m_newMenu.finished.connect(this, &StudioUI::openFile);
ImGui::GetIO().IniFilename = nullptr; ImGui::GetIO().IniFilename = nullptr;
loadModules(); loadModules();
// open project and files // open project and files
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_ctx)); auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_ctx));
m_showProjectExplorer = config.showProjectExplorer; m_showProjectExplorer = config.showProjectExplorer;
if (!err) { if (!err) {
auto const openProjErr = openProject(config.projectPath); auto const openProjErr = openProjectPath(config.projectPath);
if (!openProjErr) { if (!openProjErr) {
for (auto const&f: config.openFiles) { for (auto const&f: config.openFiles) {
auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f); auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
@ -95,10 +100,14 @@ void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept {
} }
break; break;
case turbine::Key::Alpha_N: case turbine::Key::Alpha_N:
m_newMenu.open(); if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Shift)) {
m_newProject.open();
} else {
m_newMenu.open();
}
break; break;
case turbine::Key::Alpha_O: case turbine::Key::Alpha_O:
m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProject)); m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProjectPath));
break; break;
case turbine::Key::Alpha_Q: case turbine::Key::Alpha_Q:
turbine::requestShutdown(m_ctx); turbine::requestShutdown(m_ctx);
@ -168,8 +177,11 @@ void StudioUI::drawMenu() noexcept {
if (ImGui::MenuItem("New...", "Ctrl+N")) { if (ImGui::MenuItem("New...", "Ctrl+N")) {
m_newMenu.open(); m_newMenu.open();
} }
if (ImGui::MenuItem("New Project...", "Ctrl+Shift+N")) {
m_newProject.open();
}
if (ImGui::MenuItem("Open Project...", "Ctrl+O")) { if (ImGui::MenuItem("Open Project...", "Ctrl+O")) {
m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProject)); m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProjectPath));
} }
if (ImGui::MenuItem("Save", "Ctrl+S", false, m_activeEditor && m_activeEditor->unsavedChanges())) { if (ImGui::MenuItem("Save", "Ctrl+S", false, m_activeEditor && m_activeEditor->unsavedChanges())) {
m_activeEditor->save(); m_activeEditor->save();
@ -317,19 +329,27 @@ void StudioUI::save() noexcept {
} }
} }
ox::Error StudioUI::openProject(ox::CRStringView path) noexcept { ox::Error StudioUI::createOpenProject(ox::CRStringView path) noexcept {
std::error_code ec;
std::filesystem::create_directories(toStdStringView(path), ec);
oxReturnError(OxError(ec.value() != 0, "Could not create project directory"));
oxReturnError(openProjectPath(path));
return m_project->writeAllTypeDescriptors();
}
ox::Error StudioUI::openProjectPath(ox::CRStringView path) noexcept {
oxRequireM(fs, keel::loadRomFs(path)); oxRequireM(fs, keel::loadRomFs(path));
oxReturnError(keel::setRomFs(keelCtx(m_ctx), std::move(fs))); oxReturnError(keel::setRomFs(keelCtx(m_ctx), std::move(fs)));
turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, path));
m_project = ox::make_unique<studio::Project>(keelCtx(m_ctx), ox::String(path), m_projectDataDir); m_project = ox::make_unique<studio::Project>(keelCtx(m_ctx), ox::String(path), m_projectDataDir);
auto sctx = applicationData<studio::StudioContext>(m_ctx); auto const sctx = applicationData<studio::StudioContext>(m_ctx);
sctx->project = m_project.get(); sctx->project = m_project.get();
turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, m_project->projectPath()));
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel); m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
m_openFiles.clear(); m_openFiles.clear();
m_editors.clear(); m_editors.clear();
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig *config) { studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig *config) {
config->projectPath = ox::String(path); config->projectPath = ox::String(m_project->projectPath());
config->openFiles.clear(); config->openFiles.clear();
}); });
return m_projectExplorer.refreshProjectTreeModel(); return m_projectExplorer.refreshProjectTreeModel();

View File

@ -14,6 +14,7 @@
#include <studio/task.hpp> #include <studio/task.hpp>
#include "aboutpopup.hpp" #include "aboutpopup.hpp"
#include "newmenu.hpp" #include "newmenu.hpp"
#include "newproject.hpp"
#include "projectexplorer.hpp" #include "projectexplorer.hpp"
#include "projecttreemodel.hpp" #include "projecttreemodel.hpp"
@ -36,9 +37,11 @@ class StudioUI: public ox::SignalHandler {
studio::BaseEditor *m_activeEditor = nullptr; studio::BaseEditor *m_activeEditor = nullptr;
studio::BaseEditor *m_activeEditorUpdatePending = nullptr; studio::BaseEditor *m_activeEditorUpdatePending = nullptr;
NewMenu m_newMenu; NewMenu m_newMenu;
NewProject m_newProject;
AboutPopup m_aboutPopup; AboutPopup m_aboutPopup;
ox::Array<studio::Popup*, 2> const m_popups = { ox::Array<studio::Popup*, 3> const m_popups = {
&m_newMenu, &m_newMenu,
&m_newProject,
&m_aboutPopup &m_aboutPopup
}; };
bool m_showProjectExplorer = true; bool m_showProjectExplorer = true;
@ -79,7 +82,9 @@ class StudioUI: public ox::SignalHandler {
void save() noexcept; void save() noexcept;
ox::Error openProject(ox::CRStringView path) noexcept; ox::Error createOpenProject(ox::CRStringView path) noexcept;
ox::Error openProjectPath(ox::CRStringView path) noexcept;
ox::Error openFile(ox::CRStringView path) noexcept; ox::Error openFile(ox::CRStringView path) noexcept;

View File

@ -24,7 +24,14 @@ class ItemMaker {
fileExt(pFileExt) { fileExt(pFileExt) {
} }
virtual ~ItemMaker() noexcept = default; virtual ~ItemMaker() noexcept = default;
virtual ox::Error write(turbine::Context &ctx, ox::CRStringView pName) const noexcept = 0;
/**
* Returns path of the file created.
* @param ctx
* @param pName
* @return path of file or error in Result
*/
virtual ox::Result<ox::String> write(turbine::Context &ctx, ox::CRStringView pName) const noexcept = 0;
}; };
template<typename T> template<typename T>
@ -61,11 +68,12 @@ class ItemMakerT: public ItemMaker {
m_item(std::move(pItem)), m_item(std::move(pItem)),
m_fmt(pFmt) { m_fmt(pFmt) {
} }
ox::Error write(turbine::Context &ctx, ox::CRStringView pName) const noexcept override { ox::Result<ox::String> write(turbine::Context &ctx, ox::CRStringView pName) const noexcept override {
auto const path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt); auto const path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
auto sctx = turbine::applicationData<studio::StudioContext>(ctx); auto sctx = turbine::applicationData<studio::StudioContext>(ctx);
keel::createUuidMapping(keelCtx(ctx), path, ox::UUID::generate().unwrap()); keel::createUuidMapping(keelCtx(ctx), path, ox::UUID::generate().unwrap());
return sctx->project->writeObj(path, m_item, m_fmt); oxReturnError(sctx->project->writeObj(path, m_item, m_fmt));
return path;
} }
}; };

View File

@ -36,11 +36,21 @@ constexpr ox::Result<ox::StringView> fileExt(ox::CRStringView path) noexcept {
return substr(path, extStart + 1); return substr(path, extStart + 1);
} }
[[nodiscard]]
constexpr ox::StringView parentDir(ox::StringView path) noexcept {
if (path.len() && path[path.len() - 1] == '/') {
path = substr(path, 0, path.len() - 1);
}
auto const extStart = ox::find(path.crbegin(), path.crend(), '/').offset();
return substr(path, 0, extStart);
}
class Project { class Project {
private: private:
keel::Context &m_ctx; keel::Context &m_ctx;
ox::String m_path; ox::String m_path;
ox::String m_projectDataDir; ox::String m_projectDataDir;
ox::String m_typeDescPath;
mutable keel::TypeStore m_typeStore; mutable keel::TypeStore m_typeStore;
ox::FileSystem &m_fs; ox::FileSystem &m_fs;
ox::HashMap<ox::String, ox::Vector<ox::String>> m_fileExtFileMap; ox::HashMap<ox::String, ox::Vector<ox::String>> m_fileExtFileMap;
@ -50,6 +60,9 @@ class Project {
ox::Error create() noexcept; ox::Error create() noexcept;
[[nodiscard]]
ox::String const&projectPath() const noexcept;
[[nodiscard]] [[nodiscard]]
ox::FileSystem *romFs() noexcept; ox::FileSystem *romFs() noexcept;
@ -78,6 +91,8 @@ class Project {
[[nodiscard]] [[nodiscard]]
ox::Vector<ox::String> const&fileList(ox::CRStringView ext) noexcept; ox::Vector<ox::String> const&fileList(ox::CRStringView ext) noexcept;
ox::Error writeAllTypeDescriptors() noexcept;
private: private:
void buildFileIndex() noexcept; void buildFileIndex() noexcept;
@ -108,21 +123,16 @@ template<typename T>
ox::Error Project::writeObj(ox::CRStringView path, T const&obj, ox::ClawFormat fmt) noexcept { ox::Error Project::writeObj(ox::CRStringView path, T const&obj, ox::ClawFormat fmt) noexcept {
oxRequireM(buff, ox::writeClaw(obj, fmt)); oxRequireM(buff, ox::writeClaw(obj, fmt));
// write to FS // write to FS
oxReturnError(mkdir(parentDir(path)));
oxReturnError(writeBuff(path, buff)); oxReturnError(writeBuff(path, buff));
// write type descriptor // write type descriptor
if (m_typeStore.get<T>().error) { if (m_typeStore.get<T>().error) {
oxReturnError(ox::buildTypeDef(&m_typeStore, &obj)); oxReturnError(ox::buildTypeDef(&m_typeStore, &obj));
} }
// write out type store oxRequire(desc, m_typeStore.get<T>());
auto const descPath = ox::sfmt("/{}/type_descriptors", m_projectDataDir); auto const descExists = m_fs.stat(ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*desc))).error != 0;
oxReturnError(mkdir(descPath)); if (!descExists || ox::defines::Debug) {
for (auto const&t : m_typeStore.typeList()) { oxReturnError(writeAllTypeDescriptors());
oxRequireM(typeOut, ox::writeClaw(*t, ox::ClawFormat::Organic));
// replace garbage last character with new line
*typeOut.back().value = '\n';
// write to FS
auto const typePath = ox::sfmt("/{}/{}", descPath, buildTypeId(*t));
oxReturnError(writeBuff(typePath, typeOut));
} }
oxReturnError(keel::setAsset(m_ctx, path, obj)); oxReturnError(keel::setAsset(m_ctx, path, obj));
fileUpdated.emit(path); fileUpdated.emit(path);

View File

@ -13,6 +13,11 @@
namespace studio { namespace studio {
static_assert(fileExt("main.c").value == "c");
static_assert(fileExt("a.b.c").value == "c");
static_assert(parentDir("/a/b/c") == "/a/b");
static_assert(parentDir("/a/b/c/") == "/a/b");
static void generateTypes(ox::TypeStore &ts) noexcept { static void generateTypes(ox::TypeStore &ts) noexcept {
for (auto const mod : keel::modules()) { for (auto const mod : keel::modules()) {
for (auto gen : mod->types()) { for (auto gen : mod->types()) {
@ -25,6 +30,7 @@ Project::Project(keel::Context &ctx, ox::String path, ox::CRStringView projectDa
m_ctx(ctx), m_ctx(ctx),
m_path(std::move(path)), m_path(std::move(path)),
m_projectDataDir(projectDataDir), m_projectDataDir(projectDataDir),
m_typeDescPath(ox::sfmt("/{}/type_descriptors", m_projectDataDir)),
m_typeStore(*m_ctx.rom, ox::sfmt("/{}/type_descriptors", projectDataDir)), m_typeStore(*m_ctx.rom, ox::sfmt("/{}/type_descriptors", projectDataDir)),
m_fs(*m_ctx.rom) { m_fs(*m_ctx.rom) {
oxTracef("studio", "Project: {}", m_path); oxTracef("studio", "Project: {}", m_path);
@ -38,6 +44,10 @@ ox::Error Project::create() noexcept {
return OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: mkdir failed"); return OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: mkdir failed");
} }
ox::String const&Project::projectPath() const noexcept {
return m_path;
}
ox::FileSystem *Project::romFs() noexcept { ox::FileSystem *Project::romFs() noexcept {
return &m_fs; return &m_fs;
} }
@ -60,6 +70,20 @@ ox::Vector<ox::String> const&Project::fileList(ox::CRStringView ext) noexcept {
return m_fileExtFileMap[ext]; return m_fileExtFileMap[ext];
} }
ox::Error Project::writeAllTypeDescriptors() noexcept {
// write all descriptors because we don't know which types T depends on
oxReturnError(mkdir(m_typeDescPath));
for (auto const &t: m_typeStore.typeList()) {
oxRequireM(typeOut, ox::writeClaw(*t, ox::ClawFormat::Organic));
// replace garbage last character with new line
*typeOut.back().value = '\n';
// write to FS
auto const typePath = ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*t));
oxReturnError(writeBuff(typePath, typeOut));
}
return {};
}
void Project::buildFileIndex() noexcept { void Project::buildFileIndex() noexcept {
auto [files, err] = listFiles(); auto [files, err] = listFiles();
if (err) { if (err) {

View File

@ -54,6 +54,8 @@ static void handleKeyPress(Context &ctx, int key, bool down) noexcept {
map[GLFW_KEY_RIGHT_CONTROL] = Key::Mod_Ctrl; map[GLFW_KEY_RIGHT_CONTROL] = Key::Mod_Ctrl;
map[GLFW_KEY_LEFT_SUPER] = Key::Mod_Super; map[GLFW_KEY_LEFT_SUPER] = Key::Mod_Super;
map[GLFW_KEY_RIGHT_SUPER] = Key::Mod_Super; map[GLFW_KEY_RIGHT_SUPER] = Key::Mod_Super;
map[GLFW_KEY_LEFT_SHIFT] = Key::Mod_Shift;
map[GLFW_KEY_RIGHT_SHIFT] = Key::Mod_Shift;
map[GLFW_KEY_ESCAPE] = Key::Escape; map[GLFW_KEY_ESCAPE] = Key::Escape;
return map; return map;
}(); }();