Squashed 'deps/nostalgia/' changes from 671b8eda..fae1e73e
fae1e73e [nostalgia/gfx] Make getSubSheet check root subsheet name 51f2905c [nostalgia/gfx/studio] Make TileSheetEditor use export-tilesheet functions for export, fix exporting Root subsheet 0c866d1b [studio,nostalgia/gfx] Add system for adding sub-commands in Modules, add export-tilesheet command fdf39d1a [ox/std] Add Result::transformError a523a75e [ox/std] Add missing include to StringParam cdaa64ed [ox/clargs] Fix arg parsing for first '-' 37b5fcc0 [teagba] Put parentheses around all registers f5f2c3be [studio/applib] Make Studio remove files unable to be opened from open file list in config f6ef2b5a [turbine,nostalgia] Make memory regions cast to bounded ox::Arrays bf958a4a [teagba] Make memory regions cast to bounded ox::Arrays 6a70e478 [nostalgia/gfx] Cleanup git-subtree-dir: deps/nostalgia git-subtree-split: fae1e73e54a420d4b93af67e03144d5442825a11
This commit is contained in:
12
deps/ox/src/ox/clargs/clargs.cpp
vendored
12
deps/ox/src/ox/clargs/clargs.cpp
vendored
@ -15,7 +15,7 @@ ClArgs::ClArgs(int argc, const char **args) noexcept: ClArgs({args, static_cast<
|
|||||||
|
|
||||||
ClArgs::ClArgs(ox::SpanView<const char*> args) noexcept {
|
ClArgs::ClArgs(ox::SpanView<const char*> args) noexcept {
|
||||||
for (auto i = 0u; i < args.size(); ++i) {
|
for (auto i = 0u; i < args.size(); ++i) {
|
||||||
auto arg = StringView(args[i]);
|
auto arg = StringView{args[i]};
|
||||||
if (arg[0] == '-') {
|
if (arg[0] == '-') {
|
||||||
while (arg[0] == '-' && arg.len()) {
|
while (arg[0] == '-' && arg.len()) {
|
||||||
arg = substr(arg, 1);
|
arg = substr(arg, 1);
|
||||||
@ -23,8 +23,8 @@ ClArgs::ClArgs(ox::SpanView<const char*> args) noexcept {
|
|||||||
m_bools[arg] = true;
|
m_bools[arg] = true;
|
||||||
// parse additional arguments
|
// parse additional arguments
|
||||||
if (i < args.size() && args[i + 1]) {
|
if (i < args.size() && args[i + 1]) {
|
||||||
auto val = String(args[i + 1]);
|
auto const val = StringView{args[i + 1]};
|
||||||
if (val.len() && val[i] != '-') {
|
if (val.len() && val[0] != '-') {
|
||||||
if (val == "false") {
|
if (val == "false") {
|
||||||
m_bools[arg] = false;
|
m_bools[arg] = false;
|
||||||
}
|
}
|
||||||
@ -40,17 +40,17 @@ ClArgs::ClArgs(ox::SpanView<const char*> args) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ClArgs::getBool(ox::StringViewCR arg, bool defaultValue) const noexcept {
|
bool ClArgs::getBool(ox::StringViewCR arg, bool defaultValue) const noexcept {
|
||||||
auto [value, err] = m_ints.at(arg);
|
auto const [value, err] = m_ints.at(arg);
|
||||||
return !err ? *value : defaultValue;
|
return !err ? *value : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String ClArgs::getString(ox::StringViewCR arg, ox::StringView defaultValue) const noexcept {
|
String ClArgs::getString(ox::StringViewCR arg, ox::StringView defaultValue) const noexcept {
|
||||||
auto [value, err] = m_strings.at(arg);
|
auto const [value, err] = m_strings.at(arg);
|
||||||
return !err ? ox::String(*value) : ox::String(defaultValue);
|
return !err ? ox::String(*value) : ox::String(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClArgs::getInt(ox::StringViewCR arg, int defaultValue) const noexcept {
|
int ClArgs::getInt(ox::StringViewCR arg, int defaultValue) const noexcept {
|
||||||
auto [value, err] = m_ints.at(arg);
|
auto const [value, err] = m_ints.at(arg);
|
||||||
return !err ? *value : defaultValue;
|
return !err ? *value : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
deps/ox/src/ox/std/error.hpp
vendored
7
deps/ox/src/ox/std/error.hpp
vendored
@ -307,6 +307,13 @@ struct [[nodiscard]] Result {
|
|||||||
return std::move(value);
|
return std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr Result transformError(ErrorCode const ec, CString const msg) && {
|
||||||
|
if (error) {
|
||||||
|
error = Error{ec, msg};
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
1
deps/ox/src/ox/std/stringparam.hpp
vendored
1
deps/ox/src/ox/std/stringparam.hpp
vendored
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "cstringview.hpp"
|
||||||
#include "string.hpp"
|
#include "string.hpp"
|
||||||
|
|
||||||
namespace ox {
|
namespace ox {
|
||||||
|
92
deps/teagba/include/teagba/addresses.hpp
vendored
92
deps/teagba/include/teagba/addresses.hpp
vendored
@ -5,103 +5,101 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ox/std/array.hpp>
|
#include <ox/std/array.hpp>
|
||||||
#include <ox/std/types.hpp>
|
#include <ox/std/units.hpp>
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// Interrupt Handler
|
// Interrupt Handler
|
||||||
|
|
||||||
using interrupt_handler = void (*)();
|
using InterruptHandler = void(*)();
|
||||||
#define REG_ISR *reinterpret_cast<interrupt_handler*>(0x0300'7FFC)
|
#define REG_ISR (*reinterpret_cast<InterruptHandler*>(0x0300'7FFC))
|
||||||
#define REG_IE *reinterpret_cast<volatile uint16_t*>(0x0400'0200)
|
#define REG_IE (*reinterpret_cast<volatile uint16_t*>(0x0400'0200))
|
||||||
#define REG_IF *reinterpret_cast<volatile uint16_t*>(0x0400'0202)
|
#define REG_IF (*reinterpret_cast<volatile uint16_t*>(0x0400'0202))
|
||||||
#define REG_IME *reinterpret_cast<volatile uint16_t*>(0x0400'0208)
|
#define REG_IME (*reinterpret_cast<volatile uint16_t*>(0x0400'0208))
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// Display Registers
|
// Display Registers
|
||||||
|
|
||||||
#define REG_DISPCTL *reinterpret_cast<volatile uint32_t*>(0x0400'0000)
|
#define REG_DISPCTL (*reinterpret_cast<volatile uint32_t*>(0x0400'0000))
|
||||||
#define REG_DISPSTAT *reinterpret_cast<volatile uint32_t*>(0x0400'0004)
|
#define REG_DISPSTAT (*reinterpret_cast<volatile uint32_t*>(0x0400'0004))
|
||||||
#define REG_VCOUNT *reinterpret_cast<volatile uint32_t*>(0x0400'0006)
|
#define REG_VCOUNT (*reinterpret_cast<volatile uint32_t*>(0x0400'0006))
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// Timers
|
// Timers
|
||||||
|
|
||||||
#define REG_TIMER0 *reinterpret_cast<volatile uint16_t*>(0x0400'0100)
|
#define REG_TIMER0 (*reinterpret_cast<volatile uint16_t*>(0x0400'0100))
|
||||||
#define REG_TIMER0CTL *reinterpret_cast<volatile uint16_t*>(0x0400'0102)
|
#define REG_TIMER0CTL (*reinterpret_cast<volatile uint16_t*>(0x0400'0102))
|
||||||
|
|
||||||
#define REG_TIMER1 *reinterpret_cast<volatile uint16_t*>(0x0400'0104)
|
#define REG_TIMER1 (*reinterpret_cast<volatile uint16_t*>(0x0400'0104))
|
||||||
#define REG_TIMER1CTL *reinterpret_cast<volatile uint16_t*>(0x0400'0106)
|
#define REG_TIMER1CTL (*reinterpret_cast<volatile uint16_t*>(0x0400'0106))
|
||||||
|
|
||||||
#define REG_TIMER2 *reinterpret_cast<volatile uint16_t*>(0x0400'0108)
|
#define REG_TIMER2 (*reinterpret_cast<volatile uint16_t*>(0x0400'0108))
|
||||||
#define REG_TIMER2CTL *reinterpret_cast<volatile uint16_t*>(0x0400'010a)
|
#define REG_TIMER2CTL (*reinterpret_cast<volatile uint16_t*>(0x0400'010a))
|
||||||
|
|
||||||
#define REG_TIMER3 *reinterpret_cast<volatile uint16_t*>(0x0400'010c)
|
#define REG_TIMER3 (*reinterpret_cast<volatile uint16_t*>(0x0400'010c))
|
||||||
#define REG_TIMER3CTL *reinterpret_cast<volatile uint16_t*>(0x0400'010e)
|
#define REG_TIMER3CTL (*reinterpret_cast<volatile uint16_t*>(0x0400'010e))
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// background registers
|
// background registers
|
||||||
|
|
||||||
// background control registers
|
// background control registers
|
||||||
using BgCtl = uint16_t;
|
using BgCtl = uint16_t;
|
||||||
#define REG_BG0CTL *reinterpret_cast<volatile BgCtl*>(0x0400'0008)
|
#define REG_BG0CTL (*reinterpret_cast<volatile BgCtl*>(0x0400'0008))
|
||||||
#define REG_BG1CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000a)
|
#define REG_BG1CTL (*reinterpret_cast<volatile BgCtl*>(0x0400'000a))
|
||||||
#define REG_BG2CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000c)
|
#define REG_BG2CTL (*reinterpret_cast<volatile BgCtl*>(0x0400'000c))
|
||||||
#define REG_BG3CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000e)
|
#define REG_BG3CTL (*reinterpret_cast<volatile BgCtl*>(0x0400'000e))
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
inline volatile BgCtl ®BgCtl(uintptr_t bgIdx) noexcept {
|
inline volatile BgCtl ®BgCtl(uintptr_t const bgIdx) noexcept {
|
||||||
return *reinterpret_cast<volatile BgCtl*>(0x0400'0008 + 2 * bgIdx);
|
return *reinterpret_cast<volatile BgCtl*>(0x0400'0008 + 2 * bgIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// background horizontal scrolling registers
|
// background horizontal scrolling registers
|
||||||
#define REG_BG0HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0010)
|
#define REG_BG0HOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0010))
|
||||||
#define REG_BG1HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0014)
|
#define REG_BG1HOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0014))
|
||||||
#define REG_BG2HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0018)
|
#define REG_BG2HOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0018))
|
||||||
#define REG_BG3HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001c)
|
#define REG_BG3HOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'001c))
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
inline volatile uint32_t ®BgHofs(auto bgIdx) noexcept {
|
volatile uint32_t ®BgHofs(auto const bgIdx) noexcept {
|
||||||
return *reinterpret_cast<volatile uint32_t*>(0x0400'0010 + 4 * bgIdx);
|
return *reinterpret_cast<volatile uint32_t*>(0x0400'0010 + 4 * bgIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// background vertical scrolling registers
|
// background vertical scrolling registers
|
||||||
#define REG_BG0VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0012)
|
#define REG_BG0VOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0012))
|
||||||
#define REG_BG1VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0016)
|
#define REG_BG1VOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0016))
|
||||||
#define REG_BG2VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001a)
|
#define REG_BG2VOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'001a))
|
||||||
#define REG_BG3VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001e)
|
#define REG_BG3VOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'001e))
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
inline volatile uint32_t ®BgVofs(auto bgIdx) noexcept {
|
volatile uint32_t ®BgVofs(auto const bgIdx) noexcept {
|
||||||
return *reinterpret_cast<volatile uint32_t*>(0x0400'0012 + 4 * bgIdx);
|
return *reinterpret_cast<volatile uint32_t*>(0x0400'0012 + 4 * bgIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// User Input
|
// User Input
|
||||||
|
|
||||||
#define REG_GAMEPAD *reinterpret_cast<volatile uint16_t*>(0x0400'0130)
|
#define REG_GAMEPAD (*reinterpret_cast<volatile uint16_t*>(0x0400'0130))
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// Memory Addresses
|
// Memory Addresses
|
||||||
|
|
||||||
#define MEM_EWRAM_BEGIN reinterpret_cast<uint8_t*>(0x0200'0000)
|
#define MEM_EWRAM (*reinterpret_cast<ox::Array<uint16_t, 0x0203'FFFF - 0x0200'0000>*>(0x0200'0000))
|
||||||
#define MEM_EWRAM_END reinterpret_cast<uint8_t*>(0x0203'FFFF)
|
|
||||||
|
|
||||||
#define MEM_IWRAM_BEGIN reinterpret_cast<uint8_t*>(0x0300'0000)
|
#define MEM_IWRAM (*reinterpret_cast<ox::Array<uint8_t, 0x0300'7FFF - 0x0300'0000>*>(0x0300'0000))
|
||||||
#define MEM_IWRAM_END reinterpret_cast<uint8_t*>(0x0300'7FFF)
|
|
||||||
|
|
||||||
#define REG_BLNDCTL *reinterpret_cast<uint16_t*>(0x0400'0050)
|
#define REG_BLNDCTL (*reinterpret_cast<uint16_t*>(0x0400'0050))
|
||||||
|
|
||||||
#define MEM_BG_PALETTE reinterpret_cast<uint16_t*>(0x0500'0000)
|
using Palette = ox::Array<uint16_t, 128>;
|
||||||
#define MEM_SPRITE_PALETTE reinterpret_cast<uint16_t*>(0x0500'0200)
|
#define MEM_BG_PALETTE (*reinterpret_cast<::Palette*>(0x0500'0000))
|
||||||
|
#define MEM_SPRITE_PALETTE (*reinterpret_cast<::Palette*>(0x0500'0200))
|
||||||
|
|
||||||
using BgMapTile = ox::Array<uint16_t, 8192>;
|
using BgMapTile = ox::Array<uint16_t, 8192>;
|
||||||
#define MEM_BG_TILES reinterpret_cast<BgMapTile*>(0x0600'0000)
|
#define MEM_BG_TILES (*reinterpret_cast<ox::Array<BgMapTile, 4>*>(0x0600'0000))
|
||||||
#define MEM_BG_MAP reinterpret_cast<BgMapTile*>(0x0600'e000)
|
#define MEM_BG_MAP (*reinterpret_cast<ox::Array<BgMapTile, 4>*>(0x0600'e000))
|
||||||
|
|
||||||
#define MEM_SPRITE_TILES reinterpret_cast<uint16_t*>(0x0601'0000)
|
#define MEM_SPRITE_TILES (*reinterpret_cast<ox::Array<uint16_t, 32 * ox::units::KB>*>(0x0601'0000))
|
||||||
#define MEM_OAM reinterpret_cast<uint64_t*>(0x0700'0000)
|
#define MEM_OAM (*reinterpret_cast<ox::Array<uint64_t, 64>*>(0x0700'0000))
|
||||||
|
|
||||||
#define MEM_ROM reinterpret_cast<char*>(0x0800'0000)
|
#define MEM_ROM (*reinterpret_cast<ox::Array<char, 32 * ox::units::MB>*>(0x0700'0000))
|
||||||
|
|
||||||
#define MEM_SRAM reinterpret_cast<char*>(0x0e00'0000)
|
#define MEM_SRAM (*reinterpret_cast<ox::Array<char, 64 * ox::units::KB>*>(0x0e00'0000))
|
||||||
#define MEM_SRAM_SIZE 65535
|
|
||||||
|
@ -428,6 +428,14 @@ TileSheet::SubSheetIdx validateSubSheetIdx(TileSheet const&ts, TileSheet::SubShe
|
|||||||
|
|
||||||
ox::Result<TileSheet::SubSheetIdx> getSubSheetIdx(TileSheet const &ts, SubSheetId pId) noexcept;
|
ox::Result<TileSheet::SubSheetIdx> getSubSheetIdx(TileSheet const &ts, SubSheetId pId) noexcept;
|
||||||
|
|
||||||
|
ox::Result<TileSheet::SubSheet*> getSubSheet(
|
||||||
|
ox::SpanView<ox::StringView> const &idx,
|
||||||
|
TileSheet &ts) noexcept;
|
||||||
|
|
||||||
|
ox::Result<TileSheet::SubSheet const*> getSubSheet(
|
||||||
|
ox::SpanView<ox::StringView> const &idx,
|
||||||
|
TileSheet const &ts) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
TileSheet::SubSheet &getSubSheet(
|
TileSheet::SubSheet &getSubSheet(
|
||||||
ox::SpanView<uint32_t> const&idx,
|
ox::SpanView<uint32_t> const&idx,
|
||||||
|
@ -35,11 +35,6 @@ class Context {
|
|||||||
Context(Context const &&other) noexcept = delete;
|
Context(Context const &&other) noexcept = delete;
|
||||||
virtual ~Context() noexcept = default;
|
virtual ~Context() noexcept = default;
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
ox::MemFS const &rom() const noexcept {
|
|
||||||
return static_cast<ox::MemFS const&>(*turbine::rom(turbineCtx));
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void safeDelete(Context *ctx) noexcept {
|
void safeDelete(Context *ctx) noexcept {
|
||||||
@ -81,7 +76,7 @@ ox::Error loadBgPalette(
|
|||||||
if (palette.pages.empty()) {
|
if (palette.pages.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto const paletteMem = MEM_BG_PALETTE + palBank * 16;
|
auto const paletteMem = ox::Span{MEM_BG_PALETTE} + palBank * 16;
|
||||||
for (auto i = 0u; i < colorCnt(palette, page); ++i) {
|
for (auto i = 0u; i < colorCnt(palette, page); ++i) {
|
||||||
paletteMem[i] = color(palette, page, i);
|
paletteMem[i] = color(palette, page, i);
|
||||||
}
|
}
|
||||||
@ -95,9 +90,8 @@ ox::Error loadSpritePalette(
|
|||||||
if (palette.pages.empty()) {
|
if (palette.pages.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto const paletteMem = MEM_SPRITE_PALETTE;
|
|
||||||
for (auto i = 0u; i < colorCnt(palette, page); ++i) {
|
for (auto i = 0u; i < colorCnt(palette, page); ++i) {
|
||||||
paletteMem[i] = color(palette, page, i);
|
MEM_SPRITE_PALETTE[i] = color(palette, page, i);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -240,7 +234,7 @@ ox::Error loadSpriteTileSheet(
|
|||||||
Context &ctx,
|
Context &ctx,
|
||||||
TileSheetSet const &set) noexcept {
|
TileSheetSet const &set) noexcept {
|
||||||
auto const bpp = static_cast<unsigned>(set.bpp);
|
auto const bpp = static_cast<unsigned>(set.bpp);
|
||||||
OX_RETURN_ERROR(loadTileSheetSet(ctx, {MEM_SPRITE_TILES, 32 * ox::units::KB}, set));
|
OX_RETURN_ERROR(loadTileSheetSet(ctx, MEM_SPRITE_TILES, set));
|
||||||
setSpritesBpp(bpp);
|
setSpritesBpp(bpp);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -349,9 +343,9 @@ void panic(char const *file, int line, char const *panicMsg, ox::Error const &er
|
|||||||
using namespace nostalgia::gfx;
|
using namespace nostalgia::gfx;
|
||||||
// reset heap to make sure we have enough memory to allocate context data
|
// reset heap to make sure we have enough memory to allocate context data
|
||||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||||
auto const heapBegin = reinterpret_cast<char*>(MEM_EWRAM_BEGIN);
|
auto const heapBegin = reinterpret_cast<char*>(MEM_EWRAM.data());
|
||||||
auto const heapSz = (MEM_EWRAM_END - MEM_EWRAM_BEGIN) / 2;
|
auto const heapSz = MEM_EWRAM.size() / 2;
|
||||||
auto const heapEnd = reinterpret_cast<char*>(MEM_EWRAM_BEGIN + heapSz);
|
auto const heapEnd = reinterpret_cast<char*>(MEM_EWRAM.data() + heapSz);
|
||||||
ox::heapmgr::initHeap(heapBegin, heapEnd);
|
ox::heapmgr::initHeap(heapBegin, heapEnd);
|
||||||
OX_ALLOW_UNSAFE_BUFFERS_END
|
OX_ALLOW_UNSAFE_BUFFERS_END
|
||||||
auto tctx = turbine::init(keel::loadRomFs("").unwrap(), "Nostalgia").unwrap();
|
auto tctx = turbine::init(keel::loadRomFs("").unwrap(), "Nostalgia").unwrap();
|
||||||
|
@ -453,7 +453,7 @@ static void drawBackgrounds(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawSprites(Context &ctx, ox::Size const&renderSz) noexcept {
|
static void drawSprites(Context &ctx, ox::Size const &renderSz) noexcept {
|
||||||
glUseProgram(ctx.spriteShader);
|
glUseProgram(ctx.spriteShader);
|
||||||
auto &sb = ctx.spriteBlocks;
|
auto &sb = ctx.spriteBlocks;
|
||||||
auto const uniformXScale = glGetUniformLocation(ctx.bgShader, "vXScale");
|
auto const uniformXScale = glGetUniformLocation(ctx.bgShader, "vXScale");
|
||||||
@ -481,7 +481,7 @@ static void loadPalette(
|
|||||||
ox::Array<GLfloat, 1024> &palette,
|
ox::Array<GLfloat, 1024> &palette,
|
||||||
size_t const palOffset,
|
size_t const palOffset,
|
||||||
GLuint const shaderPgrm,
|
GLuint const shaderPgrm,
|
||||||
CompactPalette const&pal,
|
CompactPalette const &pal,
|
||||||
size_t const page = 0) noexcept {
|
size_t const page = 0) noexcept {
|
||||||
static constexpr std::size_t ColorCnt = 256;
|
static constexpr std::size_t ColorCnt = 256;
|
||||||
for (auto i = palOffset; auto const c : pal.pages[page]) {
|
for (auto i = palOffset; auto const c : pal.pages[page]) {
|
||||||
@ -500,7 +500,7 @@ static void loadPalette(
|
|||||||
static void setSprite(
|
static void setSprite(
|
||||||
Context &ctx,
|
Context &ctx,
|
||||||
uint_t const idx,
|
uint_t const idx,
|
||||||
Sprite const&s) noexcept {
|
Sprite const &s) noexcept {
|
||||||
// Tonc Table 8.4
|
// Tonc Table 8.4
|
||||||
struct Sz { uint_t x{}, y{}; };
|
struct Sz { uint_t x{}, y{}; };
|
||||||
static constexpr ox::Array<Sz, 12> dimensions{
|
static constexpr ox::Array<Sz, 12> dimensions{
|
||||||
@ -574,7 +574,7 @@ static void setSprite(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Result<ox::UPtr<Context>> init(turbine::Context &tctx, InitParams const¶ms) noexcept {
|
ox::Result<ox::UPtr<Context>> init(turbine::Context &tctx, InitParams const ¶ms) noexcept {
|
||||||
auto ctx = ox::make_unique<Context>(tctx, params);
|
auto ctx = ox::make_unique<Context>(tctx, params);
|
||||||
auto const bgVshad = ox::sfmt(renderer::bgvshadTmpl, gl::GlslVersion);
|
auto const bgVshad = ox::sfmt(renderer::bgvshadTmpl, gl::GlslVersion);
|
||||||
auto const bgFshad = ox::sfmt(renderer::bgfshadTmpl, gl::GlslVersion);
|
auto const bgFshad = ox::sfmt(renderer::bgfshadTmpl, gl::GlslVersion);
|
||||||
@ -604,7 +604,7 @@ struct TileSheetData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static ox::Result<TileSheetData> normalizeTileSheet
|
static ox::Result<TileSheetData> normalizeTileSheet
|
||||||
(CompactTileSheet const&ts) noexcept {
|
(CompactTileSheet const &ts) noexcept {
|
||||||
const uint_t bytesPerTile = ts.bpp == 8 ? PixelsPerTile : PixelsPerTile / 2;
|
const uint_t bytesPerTile = ts.bpp == 8 ? PixelsPerTile : PixelsPerTile / 2;
|
||||||
auto const tiles = ts.pixels.size() / bytesPerTile;
|
auto const tiles = ts.pixels.size() / bytesPerTile;
|
||||||
constexpr int width = 8;
|
constexpr int width = 8;
|
||||||
@ -632,7 +632,7 @@ static ox::Result<TileSheetData> normalizeTileSheet
|
|||||||
ox::Error loadBgPalette(
|
ox::Error loadBgPalette(
|
||||||
Context &ctx,
|
Context &ctx,
|
||||||
size_t const palBank,
|
size_t const palBank,
|
||||||
CompactPalette const&palette,
|
CompactPalette const &palette,
|
||||||
size_t const page) noexcept {
|
size_t const page) noexcept {
|
||||||
renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, palette, page);
|
renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, palette, page);
|
||||||
return {};
|
return {};
|
||||||
@ -640,7 +640,7 @@ ox::Error loadBgPalette(
|
|||||||
|
|
||||||
ox::Error loadSpritePalette(
|
ox::Error loadSpritePalette(
|
||||||
Context &ctx,
|
Context &ctx,
|
||||||
CompactPalette const&palette,
|
CompactPalette const &palette,
|
||||||
size_t const page) noexcept {
|
size_t const page) noexcept {
|
||||||
ox::Array<GLfloat, 1024> pal;
|
ox::Array<GLfloat, 1024> pal;
|
||||||
renderer::loadPalette(pal, 0, ctx.spriteShader, palette, page);
|
renderer::loadPalette(pal, 0, ctx.spriteShader, palette, page);
|
||||||
@ -649,14 +649,14 @@ ox::Error loadSpritePalette(
|
|||||||
|
|
||||||
static ox::Result<TileSheetData> buildSetTsd(
|
static ox::Result<TileSheetData> buildSetTsd(
|
||||||
Context const &ctx,
|
Context const &ctx,
|
||||||
TileSheetSet const&set) noexcept {
|
TileSheetSet const &set) noexcept {
|
||||||
auto &kctx = keelCtx(ctx.turbineCtx);
|
auto &kctx = keelCtx(ctx.turbineCtx);
|
||||||
TileSheetData setTsd;
|
TileSheetData setTsd;
|
||||||
setTsd.width = TileWidth;
|
setTsd.width = TileWidth;
|
||||||
for (auto const&entry : set.entries) {
|
for (auto const &entry : set.entries) {
|
||||||
OX_REQUIRE(tilesheet, readObj<CompactTileSheet>(kctx, entry.tilesheet));
|
OX_REQUIRE(tilesheet, readObj<CompactTileSheet>(kctx, entry.tilesheet));
|
||||||
OX_REQUIRE(tsd, normalizeTileSheet(*tilesheet));
|
OX_REQUIRE(tsd, normalizeTileSheet(*tilesheet));
|
||||||
for (auto const&s : entry.sections) {
|
for (auto const &s : entry.sections) {
|
||||||
auto const size = s.tiles * PixelsPerTile;
|
auto const size = s.tiles * PixelsPerTile;
|
||||||
for (auto i = 0; i < size; ++i) {
|
for (auto i = 0; i < size; ++i) {
|
||||||
auto const srcIdx = static_cast<size_t>(i) + static_cast<size_t>(s.begin * PixelsPerTile);
|
auto const srcIdx = static_cast<size_t>(i) + static_cast<size_t>(s.begin * PixelsPerTile);
|
||||||
@ -669,7 +669,7 @@ static ox::Result<TileSheetData> buildSetTsd(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void copyPixels(
|
static void copyPixels(
|
||||||
CompactTileSheet const&ts,
|
CompactTileSheet const &ts,
|
||||||
ox::Span<uint32_t> const dst,
|
ox::Span<uint32_t> const dst,
|
||||||
size_t const srcPxIdx,
|
size_t const srcPxIdx,
|
||||||
size_t const pxlCnt) noexcept {
|
size_t const pxlCnt) noexcept {
|
||||||
@ -704,7 +704,7 @@ void clearCbbs(Context &ctx) noexcept {
|
|||||||
ox::Error loadBgTileSheet(
|
ox::Error loadBgTileSheet(
|
||||||
Context &ctx,
|
Context &ctx,
|
||||||
unsigned const cbb,
|
unsigned const cbb,
|
||||||
CompactTileSheet const&ts,
|
CompactTileSheet const &ts,
|
||||||
size_t const dstTileIdx,
|
size_t const dstTileIdx,
|
||||||
size_t const srcTileIdx,
|
size_t const srcTileIdx,
|
||||||
size_t const tileCnt) noexcept {
|
size_t const tileCnt) noexcept {
|
||||||
@ -728,8 +728,8 @@ ox::Error loadBgTileSheet(
|
|||||||
ox::Error loadBgTileSheet(
|
ox::Error loadBgTileSheet(
|
||||||
Context &ctx,
|
Context &ctx,
|
||||||
uint_t const cbb,
|
uint_t const cbb,
|
||||||
CompactTileSheet const&ts,
|
CompactTileSheet const &ts,
|
||||||
ox::Optional<unsigned> const&paletteBank) noexcept {
|
ox::Optional<unsigned> const &paletteBank) noexcept {
|
||||||
auto const bytesPerTile = static_cast<uint64_t>(PixelsPerTile / (1 + (ts.bpp == 4)));
|
auto const bytesPerTile = static_cast<uint64_t>(PixelsPerTile / (1 + (ts.bpp == 4)));
|
||||||
auto const tiles = ts.pixels.size() / bytesPerTile;
|
auto const tiles = ts.pixels.size() / bytesPerTile;
|
||||||
OX_RETURN_ERROR(loadBgTileSheet(ctx, cbb, ts, 0, 0, tiles));
|
OX_RETURN_ERROR(loadBgTileSheet(ctx, cbb, ts, 0, 0, tiles));
|
||||||
@ -742,7 +742,7 @@ ox::Error loadBgTileSheet(
|
|||||||
ox::Error loadBgTileSheet(
|
ox::Error loadBgTileSheet(
|
||||||
Context &ctx,
|
Context &ctx,
|
||||||
unsigned const cbb,
|
unsigned const cbb,
|
||||||
TileSheetSet const&set) noexcept {
|
TileSheetSet const &set) noexcept {
|
||||||
OX_REQUIRE(setTsd, buildSetTsd(ctx, set));
|
OX_REQUIRE(setTsd, buildSetTsd(ctx, set));
|
||||||
ctx.cbbs[cbb].tex = renderer::createTexture(setTsd.width, setTsd.height, setTsd.pixels.data());
|
ctx.cbbs[cbb].tex = renderer::createTexture(setTsd.width, setTsd.height, setTsd.pixels.data());
|
||||||
return {};
|
return {};
|
||||||
@ -750,7 +750,7 @@ ox::Error loadBgTileSheet(
|
|||||||
|
|
||||||
ox::Error loadSpriteTileSheet(
|
ox::Error loadSpriteTileSheet(
|
||||||
Context &ctx,
|
Context &ctx,
|
||||||
CompactTileSheet const&ts,
|
CompactTileSheet const &ts,
|
||||||
bool const loadDefaultPalette) noexcept {
|
bool const loadDefaultPalette) noexcept {
|
||||||
OX_REQUIRE(tsd, normalizeTileSheet(ts));
|
OX_REQUIRE(tsd, normalizeTileSheet(ts));
|
||||||
oxTracef("nostalgia.gfx.gl", "loadSpriteTexture: { w: {}, h: {} }", tsd.width, tsd.height);
|
oxTracef("nostalgia.gfx.gl", "loadSpriteTexture: { w: {}, h: {} }", tsd.width, tsd.height);
|
||||||
@ -763,7 +763,7 @@ ox::Error loadSpriteTileSheet(
|
|||||||
|
|
||||||
ox::Error loadSpriteTileSheet(
|
ox::Error loadSpriteTileSheet(
|
||||||
Context &ctx,
|
Context &ctx,
|
||||||
TileSheetSet const&set) noexcept {
|
TileSheetSet const &set) noexcept {
|
||||||
OX_REQUIRE(setTsd, buildSetTsd(ctx, set));
|
OX_REQUIRE(setTsd, buildSetTsd(ctx, set));
|
||||||
ctx.spriteBlocks.tex = renderer::createTexture(setTsd.width, setTsd.height, setTsd.pixels.data());
|
ctx.spriteBlocks.tex = renderer::createTexture(setTsd.width, setTsd.height, setTsd.pixels.data());
|
||||||
return {};
|
return {};
|
||||||
@ -774,7 +774,7 @@ void setBgTile(
|
|||||||
uint_t const bgIdx,
|
uint_t const bgIdx,
|
||||||
int const column,
|
int const column,
|
||||||
int const row,
|
int const row,
|
||||||
BgTile const&tile) noexcept {
|
BgTile const &tile) noexcept {
|
||||||
oxTracef(
|
oxTracef(
|
||||||
"nostalgia.gfx.setBgTile",
|
"nostalgia.gfx.setBgTile",
|
||||||
"bgIdx: {}, column: {}, row: {}, tile: {}, palBank: {}",
|
"bgIdx: {}, column: {}, row: {}, tile: {}, palBank: {}",
|
||||||
@ -853,7 +853,7 @@ void showSprite(Context &ctx, uint_t const idx) noexcept {
|
|||||||
renderer::setSprite(ctx, idx, s);
|
renderer::setSprite(ctx, idx, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSprite(Context &ctx, uint_t const idx, Sprite const&sprite) noexcept {
|
void setSprite(Context &ctx, uint_t const idx, Sprite const &sprite) noexcept {
|
||||||
auto &s = ctx.spriteStates[idx];
|
auto &s = ctx.spriteStates[idx];
|
||||||
s = sprite;
|
s = sprite;
|
||||||
renderer::setSprite(ctx, idx, s);
|
renderer::setSprite(ctx, idx, s);
|
||||||
@ -869,7 +869,7 @@ ox::Size drawSize(int const scale) noexcept {
|
|||||||
return {240 * scale, 160 * scale};
|
return {240 * scale, 160 * scale};
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(Context &ctx, ox::Size const&renderSz) noexcept {
|
void draw(Context &ctx, ox::Size const &renderSz) noexcept {
|
||||||
glViewport(0, 0, renderSz.width, renderSz.height);
|
glViewport(0, 0, renderSz.width, renderSz.height);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
|
@ -26,3 +26,4 @@ install(
|
|||||||
|
|
||||||
add_subdirectory(paletteeditor)
|
add_subdirectory(paletteeditor)
|
||||||
add_subdirectory(tilesheeteditor)
|
add_subdirectory(tilesheeteditor)
|
||||||
|
add_subdirectory(subcommands)
|
||||||
|
@ -8,10 +8,25 @@
|
|||||||
|
|
||||||
#include "paletteeditor/paletteeditor-imgui.hpp"
|
#include "paletteeditor/paletteeditor-imgui.hpp"
|
||||||
#include "tilesheeteditor/tilesheeteditor-imgui.hpp"
|
#include "tilesheeteditor/tilesheeteditor-imgui.hpp"
|
||||||
|
#include "subcommands/export-tilesheet/export-tilesheet.hpp"
|
||||||
|
|
||||||
namespace nostalgia::gfx {
|
namespace nostalgia::gfx {
|
||||||
|
|
||||||
static class: public studio::Module {
|
static struct: studio::Module {
|
||||||
|
|
||||||
|
ox::String id() const noexcept final {
|
||||||
|
return ox::String{"net.drinkingtea.nostalgia.gfx"};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Vector<studio::Command> commands() const final {
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
"export-tilesheet",
|
||||||
|
cmdExportTilesheet,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
ox::Vector<studio::EditorMaker> editors(studio::Context &ctx) const noexcept final {
|
ox::Vector<studio::EditorMaker> editors(studio::Context &ctx) const noexcept final {
|
||||||
return {
|
return {
|
||||||
studio::editorMaker<TileSheetEditorImGui>(ctx, {FileExt_ng, FileExt_nts}),
|
studio::editorMaker<TileSheetEditorImGui>(ctx, {FileExt_ng, FileExt_nts}),
|
||||||
@ -28,6 +43,7 @@ static class: public studio::Module {
|
|||||||
}, ox::ClawFormat::Organic));
|
}, ox::ClawFormat::Organic));
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
} const mod;
|
} const mod;
|
||||||
|
|
||||||
studio::Module const *studioModule() noexcept {
|
studio::Module const *studioModule() noexcept {
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
target_sources(
|
||||||
|
NostalgiaGfx-Studio PRIVATE
|
||||||
|
export-tilesheet/export-tilesheet.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
NostalgiaGfx-Studio PUBLIC
|
||||||
|
OxClArgs
|
||||||
|
)
|
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <lodepng.h>
|
||||||
|
|
||||||
|
#include <ox/clargs/clargs.hpp>
|
||||||
|
#include <ox/std/trace.hpp>
|
||||||
|
|
||||||
|
#include <studio/project.hpp>
|
||||||
|
|
||||||
|
#include <nostalgia/gfx/palette.hpp>
|
||||||
|
#include <nostalgia/gfx/tilesheet.hpp>
|
||||||
|
|
||||||
|
#include "export-tilesheet.hpp"
|
||||||
|
|
||||||
|
#include "nostalgia/gfx/tilesheet.hpp"
|
||||||
|
#include "studio/context.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace nostalgia::gfx {
|
||||||
|
|
||||||
|
static ox::Vector<uint32_t> normalizePixelSizes(
|
||||||
|
ox::Vector<uint8_t> const &inPixels) noexcept {
|
||||||
|
ox::Vector<uint32_t> outPixels;
|
||||||
|
outPixels.reserve(inPixels.size());
|
||||||
|
outPixels.resize(inPixels.size());
|
||||||
|
for (size_t i{}; i < inPixels.size(); ++i) {
|
||||||
|
outPixels[i] = inPixels[i];
|
||||||
|
}
|
||||||
|
return outPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ox::Vector<uint32_t> normalizePixelArrangement(
|
||||||
|
ox::Vector<uint32_t> const &inPixels,
|
||||||
|
int const cols,
|
||||||
|
int const scale) {
|
||||||
|
auto const scalePt = ox::Point{scale, scale};
|
||||||
|
auto const width = cols * TileWidth;
|
||||||
|
auto const height = static_cast<int>(inPixels.size()) / width;
|
||||||
|
auto const dstWidth = width * scale;
|
||||||
|
ox::Vector<uint32_t> outPixels(static_cast<size_t>((width * scale) * (height * scale)));
|
||||||
|
for (size_t dstIdx{}; dstIdx < outPixels.size(); ++dstIdx) {
|
||||||
|
auto const dstPt = ox::Point{
|
||||||
|
static_cast<int>(dstIdx) % dstWidth,
|
||||||
|
static_cast<int>(dstIdx) / dstWidth};
|
||||||
|
auto const srcPt = dstPt / scalePt;
|
||||||
|
auto const srcIdx = ptToIdx(srcPt, cols);
|
||||||
|
outPixels[dstIdx] = inPixels[srcIdx];
|
||||||
|
}
|
||||||
|
return outPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ox::Error toPngFile(
|
||||||
|
ox::CStringViewCR path,
|
||||||
|
ox::Vector<uint32_t> &&pixels,
|
||||||
|
Palette const &pal,
|
||||||
|
size_t const page,
|
||||||
|
unsigned const width,
|
||||||
|
unsigned const height) noexcept {
|
||||||
|
for (auto &c : pixels) {
|
||||||
|
c = color32(color(pal, page, c)) | static_cast<Color32>(0XFF << 24);
|
||||||
|
}
|
||||||
|
constexpr auto fmt = LCT_RGBA;
|
||||||
|
return ox::Error(static_cast<ox::ErrorCode>(
|
||||||
|
lodepng_encode_file(
|
||||||
|
path.c_str(),
|
||||||
|
reinterpret_cast<uint8_t const*>(pixels.data()),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
fmt,
|
||||||
|
8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error exportSubsheetToPng(
|
||||||
|
TileSheet::SubSheet const &s,
|
||||||
|
Palette const &pal,
|
||||||
|
size_t const palPage,
|
||||||
|
ox::CStringViewCR dstPath,
|
||||||
|
int const scale) noexcept {
|
||||||
|
// subsheet to png
|
||||||
|
auto const width = s.columns * TileWidth;
|
||||||
|
auto const height = s.rows * TileHeight;
|
||||||
|
auto const err = toPngFile(
|
||||||
|
dstPath,
|
||||||
|
normalizePixelArrangement(
|
||||||
|
normalizePixelSizes(s.pixels),
|
||||||
|
s.columns,
|
||||||
|
scale),
|
||||||
|
pal,
|
||||||
|
palPage,
|
||||||
|
static_cast<unsigned>(width * scale),
|
||||||
|
static_cast<unsigned>(height * scale));
|
||||||
|
if (err) {
|
||||||
|
oxErrorf("TileSheet export failed: {}", toStr(err));
|
||||||
|
return ox::Error{7, "TileSheet export failed"};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error exportSubsheetToPng(
|
||||||
|
TileSheet const &src,
|
||||||
|
Palette const &pal,
|
||||||
|
ox::StringViewCR subsheetPath,
|
||||||
|
size_t const palPage,
|
||||||
|
ox::CStringViewCR dstPath,
|
||||||
|
int const scale) noexcept {
|
||||||
|
// subsheet to png
|
||||||
|
auto const [s, ssErr] = getSubSheet(
|
||||||
|
ox::split(subsheetPath, '.'), src);
|
||||||
|
if (ssErr) {
|
||||||
|
return ox::Error{6, "failed to find SubSheet"};
|
||||||
|
}
|
||||||
|
return exportSubsheetToPng(
|
||||||
|
*s,
|
||||||
|
pal,
|
||||||
|
palPage,
|
||||||
|
dstPath,
|
||||||
|
scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error cmdExportTilesheet(studio::Project &project, ox::SpanView<ox::CString> const args) noexcept {
|
||||||
|
// parse args
|
||||||
|
ox::ClArgs const clargs{args};
|
||||||
|
bool showUsage = true;
|
||||||
|
OX_DEFER [&showUsage] {
|
||||||
|
if (showUsage) {
|
||||||
|
oxErr("usage: export-tilesheet "
|
||||||
|
"-src-path <path> "
|
||||||
|
"-dst-path <path> "
|
||||||
|
"[-pal-path <path>] "
|
||||||
|
"[-subsheet-path <path>] "
|
||||||
|
"[-scale <int>]\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
OX_REQUIRE(srcPath, clargs.getString("src-path").transformError(1, "no src path specified"));
|
||||||
|
OX_REQUIRE(dstPath, clargs.getString("dst-path").transformError(2, "no dst path specified"));
|
||||||
|
auto const palPath = clargs.getString("pal-path", "");
|
||||||
|
auto const subsheetPath = clargs.getString("subsheet-path").or_value(ox::String{"Root"});
|
||||||
|
auto const palPage = static_cast<size_t>(clargs.getInt("pal-page", 0));
|
||||||
|
auto const scale = clargs.getInt("scale", 1);
|
||||||
|
showUsage = false;
|
||||||
|
// load objects
|
||||||
|
auto &kctx = project.kctx();
|
||||||
|
OX_REQUIRE(ts, keel::readObj<TileSheet>(kctx, srcPath).transformError(4, "could not load TileSheet"));
|
||||||
|
OX_REQUIRE(pal, keel::readObj<Palette>(kctx, palPath.len() ? palPath : ts->defaultPalette)
|
||||||
|
.transformError(5, "could not load Palette"));
|
||||||
|
// export to the destination file
|
||||||
|
return exportSubsheetToPng(
|
||||||
|
*ts,
|
||||||
|
*pal,
|
||||||
|
subsheetPath,
|
||||||
|
palPage,
|
||||||
|
dstPath,
|
||||||
|
scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/error.hpp>
|
||||||
|
|
||||||
|
namespace nostalgia::gfx {
|
||||||
|
|
||||||
|
ox::Error exportSubsheetToPng(
|
||||||
|
TileSheet::SubSheet const &s,
|
||||||
|
Palette const &pal,
|
||||||
|
size_t palPage,
|
||||||
|
ox::CStringViewCR dstPath,
|
||||||
|
int scale) noexcept;
|
||||||
|
|
||||||
|
ox::Error exportSubsheetToPng(
|
||||||
|
TileSheet const &src,
|
||||||
|
Palette const &pal,
|
||||||
|
ox::StringViewCR subsheetPath,
|
||||||
|
size_t palPage,
|
||||||
|
ox::CStringViewCR dstPath,
|
||||||
|
int scale) noexcept;
|
||||||
|
|
||||||
|
ox::Error cmdExportTilesheet(studio::Project& project, ox::SpanView<ox::CString> args) noexcept;
|
||||||
|
|
||||||
|
}
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <nostalgia/gfx/studio.hpp>
|
#include <nostalgia/gfx/studio.hpp>
|
||||||
|
|
||||||
|
#include "../subcommands/export-tilesheet/export-tilesheet.hpp"
|
||||||
#include "tilesheeteditor-imgui.hpp"
|
#include "tilesheeteditor-imgui.hpp"
|
||||||
|
|
||||||
namespace nostalgia::gfx {
|
namespace nostalgia::gfx {
|
||||||
@ -37,58 +38,6 @@ OX_MODEL_BEGIN(TileSheetEditorConfig)
|
|||||||
OX_MODEL_FIELD_RENAME(activeSubsheet, active_subsheet)
|
OX_MODEL_FIELD_RENAME(activeSubsheet, active_subsheet)
|
||||||
OX_MODEL_END()
|
OX_MODEL_END()
|
||||||
|
|
||||||
static ox::Vector<uint32_t> normalizePixelSizes(
|
|
||||||
ox::Vector<uint8_t> const &inPixels) noexcept {
|
|
||||||
ox::Vector<uint32_t> outPixels;
|
|
||||||
outPixels.reserve(inPixels.size());
|
|
||||||
outPixels.resize(inPixels.size());
|
|
||||||
for (std::size_t i = 0; i < inPixels.size(); ++i) {
|
|
||||||
outPixels[i] = inPixels[i];
|
|
||||||
}
|
|
||||||
return outPixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ox::Vector<uint32_t> normalizePixelArrangement(
|
|
||||||
ox::Vector<uint32_t> const &inPixels,
|
|
||||||
int const cols,
|
|
||||||
int const scale) {
|
|
||||||
auto const scalePt = ox::Point{scale, scale};
|
|
||||||
auto const width = cols * TileWidth;
|
|
||||||
auto const height = static_cast<int>(inPixels.size()) / width;
|
|
||||||
auto const dstWidth = width * scale;
|
|
||||||
ox::Vector<uint32_t> outPixels(static_cast<size_t>((width * scale) * (height * scale)));
|
|
||||||
for (std::size_t dstIdx = 0; dstIdx < outPixels.size(); ++dstIdx) {
|
|
||||||
auto const dstPt = ox::Point{
|
|
||||||
static_cast<int>(dstIdx) % dstWidth,
|
|
||||||
static_cast<int>(dstIdx) / dstWidth};
|
|
||||||
auto const srcPt = dstPt / scalePt;
|
|
||||||
auto const srcIdx = ptToIdx(srcPt, cols);
|
|
||||||
outPixels[dstIdx] = inPixels[srcIdx];
|
|
||||||
}
|
|
||||||
return outPixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ox::Error toPngFile(
|
|
||||||
ox::CStringViewCR path,
|
|
||||||
ox::Vector<uint32_t> &&pixels,
|
|
||||||
Palette const &pal,
|
|
||||||
size_t const page,
|
|
||||||
unsigned const width,
|
|
||||||
unsigned const height) noexcept {
|
|
||||||
for (auto &c : pixels) {
|
|
||||||
c = color32(color(pal, page, c)) | static_cast<Color32>(0XFF << 24);
|
|
||||||
}
|
|
||||||
constexpr auto fmt = LCT_RGBA;
|
|
||||||
return ox::Error(static_cast<ox::ErrorCode>(
|
|
||||||
lodepng_encode_file(
|
|
||||||
path.c_str(),
|
|
||||||
reinterpret_cast<uint8_t const*>(pixels.data()),
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
fmt,
|
|
||||||
8)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TileSheetEditorImGui::TileSheetEditorImGui(studio::Context &sctx, ox::StringParam path):
|
TileSheetEditorImGui::TileSheetEditorImGui(studio::Context &sctx, ox::StringParam path):
|
||||||
Editor{sctx, std::move(path)},
|
Editor{sctx, std::move(path)},
|
||||||
m_sctx{sctx},
|
m_sctx{sctx},
|
||||||
@ -421,21 +370,7 @@ ox::Error TileSheetEditorImGui::exportSubsheetToPng(int const scale) const noexc
|
|||||||
// subsheet to png
|
// subsheet to png
|
||||||
auto const &s = m_model.activeSubSheet();
|
auto const &s = m_model.activeSubSheet();
|
||||||
auto const &pal = m_model.pal();
|
auto const &pal = m_model.pal();
|
||||||
auto const width = s.columns * TileWidth;
|
return gfx::exportSubsheetToPng(s, pal, m_model.palettePage(), path, scale);
|
||||||
auto const height = s.rows * TileHeight;
|
|
||||||
auto pixels = normalizePixelSizes(s.pixels);
|
|
||||||
pixels = normalizePixelArrangement(pixels, s.columns, scale);
|
|
||||||
auto const err = toPngFile(
|
|
||||||
path,
|
|
||||||
std::move(pixels),
|
|
||||||
pal,
|
|
||||||
m_model.palettePage(),
|
|
||||||
static_cast<unsigned>(width * scale),
|
|
||||||
static_cast<unsigned>(height * scale));
|
|
||||||
if (err) {
|
|
||||||
oxErrorf("TileSheet export failed: {}", toStr(err));
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const &fbSize) noexcept {
|
void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const &fbSize) noexcept {
|
||||||
|
@ -211,6 +211,47 @@ ox::Result<TileSheet::SubSheetIdx> getSubSheetIdx(TileSheet const &ts, SubSheetI
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename SubSheet>
|
||||||
|
static ox::Result<SubSheet*> getSubSheet(
|
||||||
|
ox::SpanView<ox::StringView> const &idx,
|
||||||
|
std::size_t const idxIt,
|
||||||
|
SubSheet &pSubSheet) noexcept {
|
||||||
|
if (idxIt == idx.size()) {
|
||||||
|
return &pSubSheet;
|
||||||
|
}
|
||||||
|
auto const ¤tIdx = idx[idxIt];
|
||||||
|
auto const next = ox::find_if(
|
||||||
|
pSubSheet.subsheets.begin(),
|
||||||
|
pSubSheet.subsheets.end(),
|
||||||
|
[¤tIdx](TileSheet::SubSheet const &ss) {
|
||||||
|
return ss.name == currentIdx;
|
||||||
|
});
|
||||||
|
if (next == pSubSheet.subsheets.end()) {
|
||||||
|
return ox::Error{1, "SubSheet not found"};
|
||||||
|
}
|
||||||
|
return getSubSheet(idx, idxIt + 1, *next);
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Result<TileSheet::SubSheet const*> getSubSheet(
|
||||||
|
ox::SpanView<ox::StringView> const &idx,
|
||||||
|
TileSheet const &ts) noexcept {
|
||||||
|
if (!idx.empty() && idx[0] == ts.subsheet.name) {
|
||||||
|
return getSubSheet<TileSheet::SubSheet const>(idx, 1, ts.subsheet);
|
||||||
|
}
|
||||||
|
return ox::Error{1, "SubSheet not found"};
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Result<TileSheet::SubSheet*> getSubSheet(
|
||||||
|
ox::SpanView<ox::StringView> const &idx,
|
||||||
|
TileSheet &ts) noexcept {
|
||||||
|
if (!idx.empty() && idx[0] == ts.subsheet.name) {
|
||||||
|
return getSubSheet<TileSheet::SubSheet>(idx, 1, ts.subsheet);
|
||||||
|
}
|
||||||
|
return ox::Error{1, "SubSheet not found"};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(__GNUC__) && __GNUC__ >= 13
|
#if defined(__GNUC__) && __GNUC__ >= 13
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wdangling-reference"
|
#pragma GCC diagnostic ignored "-Wdangling-reference"
|
||||||
|
@ -10,6 +10,10 @@ namespace nostalgia::sound {
|
|||||||
|
|
||||||
static struct: studio::Module {
|
static struct: studio::Module {
|
||||||
|
|
||||||
|
ox::String id() const noexcept final {
|
||||||
|
return ox::String{"net.drinkingtea.nostalgia.sound"};
|
||||||
|
}
|
||||||
|
|
||||||
ox::Vector<studio::EditorMaker> editors(studio::Context&) const noexcept final {
|
ox::Vector<studio::EditorMaker> editors(studio::Context&) const noexcept final {
|
||||||
return {
|
return {
|
||||||
};
|
};
|
||||||
@ -22,7 +26,7 @@ static struct: studio::Module {
|
|||||||
|
|
||||||
} const mod;
|
} const mod;
|
||||||
|
|
||||||
const studio::Module *studioModule() noexcept {
|
studio::Module const *studioModule() noexcept {
|
||||||
return &mod;
|
return &mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ox::Vector<Module const*> const &modules() noexcept;
|
||||||
|
|
||||||
void registerModule(Module const*) noexcept;
|
void registerModule(Module const*) noexcept;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,18 @@
|
|||||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <complex>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
#include <ox/logconn/logconn.hpp>
|
#include <ox/logconn/logconn.hpp>
|
||||||
#include <ox/std/trace.hpp>
|
#include <ox/std/trace.hpp>
|
||||||
#include <ox/std/uuid.hpp>
|
#include <ox/std/uuid.hpp>
|
||||||
#include <keel/media.hpp>
|
#include <keel/keel.hpp>
|
||||||
#include <turbine/turbine.hpp>
|
#include <turbine/turbine.hpp>
|
||||||
|
|
||||||
#include <studio/context.hpp>
|
#include <studio/context.hpp>
|
||||||
|
#include <studioapp/studioapp.hpp>
|
||||||
|
|
||||||
#include "studioui.hpp"
|
#include "studioui.hpp"
|
||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
@ -40,7 +43,7 @@ static void mouseButtonEventHandler(turbine::Context &ctx, int const btn, bool c
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::Vector<ox::SpanView<uint8_t>> WindowIcons() noexcept;
|
ox::Vector<ox::SpanView<uint8_t>> WindowIcons() noexcept;
|
||||||
|
|
||||||
static ox::Error runApp(
|
static ox::Error runStudio(
|
||||||
ox::StringViewCR appName,
|
ox::StringViewCR appName,
|
||||||
ox::StringViewCR projectDataDir,
|
ox::StringViewCR projectDataDir,
|
||||||
ox::UPtr<ox::FileSystem> &&fs) noexcept {
|
ox::UPtr<ox::FileSystem> &&fs) noexcept {
|
||||||
@ -61,17 +64,46 @@ static ox::Error runApp(
|
|||||||
static ox::Error run(
|
static ox::Error run(
|
||||||
ox::StringViewCR appName,
|
ox::StringViewCR appName,
|
||||||
ox::StringViewCR projectDataDir,
|
ox::StringViewCR projectDataDir,
|
||||||
ox::SpanView<ox::CString>) {
|
ox::SpanView<ox::CString> const &args) {
|
||||||
// seed UUID generator
|
// seed UUID generator
|
||||||
auto const time = std::time(nullptr);
|
auto const time = std::time(nullptr);
|
||||||
ox::UUID::seedGenerator({
|
ox::UUID::seedGenerator({
|
||||||
static_cast<uint64_t>(time),
|
static_cast<uint64_t>(time),
|
||||||
static_cast<uint64_t>(time << 1)
|
static_cast<uint64_t>(time << 1)
|
||||||
});
|
});
|
||||||
// run app
|
if (args.size() > 1 && ox::StringView{args[1]} == "cmd") {
|
||||||
auto const err = runApp(appName, projectDataDir, ox::UPtr<ox::FileSystem>{});
|
if (args.size() < 5) {
|
||||||
oxAssert(err, "Something went wrong...");
|
return ox::Error{1, "insufficient arguments for sub-command"};
|
||||||
return err;
|
}
|
||||||
|
auto constexpr numCmdArgs = 5;
|
||||||
|
ox::StringView const projectDir = args[2];
|
||||||
|
ox::StringView const moduleId = args[3];
|
||||||
|
ox::StringView const subCmd = args[4];
|
||||||
|
for (auto const m : modules()) {
|
||||||
|
if (m->id() == moduleId) {
|
||||||
|
for (auto const &c : m->commands()) {
|
||||||
|
if (c.name == subCmd) {
|
||||||
|
auto kctx = keel::init(
|
||||||
|
ox::make_unique<ox::PassThroughFS>(projectDir),
|
||||||
|
c.name);
|
||||||
|
if (kctx.error) {
|
||||||
|
return ox::Error{2, "failed to load project directory"};
|
||||||
|
}
|
||||||
|
Project project{*kctx.value, projectDir, projectDataDir};
|
||||||
|
return c.func(project, args + numCmdArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ox::Error{1, "command not found"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ox::Error{1, "module not found"};
|
||||||
|
} else {
|
||||||
|
// run app
|
||||||
|
auto const err = runStudio(appName, projectDataDir, ox::UPtr<ox::FileSystem>{});
|
||||||
|
oxAssert(err, "Something went wrong...");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,11 +37,15 @@ static bool shutdownHandler(turbine::Context &ctx) {
|
|||||||
namespace ig {
|
namespace ig {
|
||||||
extern bool s_mainWinHasFocus;
|
extern bool s_mainWinHasFocus;
|
||||||
}
|
}
|
||||||
static ox::Vector<Module const*> modules;
|
static ox::Vector<Module const*> g_modules;
|
||||||
|
|
||||||
|
ox::Vector<Module const*> const &modules() noexcept {
|
||||||
|
return g_modules;
|
||||||
|
}
|
||||||
|
|
||||||
void registerModule(Module const*mod) noexcept {
|
void registerModule(Module const*mod) noexcept {
|
||||||
if (mod) {
|
if (mod) {
|
||||||
modules.emplace_back(mod);
|
g_modules.emplace_back(mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,7 +447,7 @@ void StudioUI::loadModule(Module const &mod) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StudioUI::loadModules() noexcept {
|
void StudioUI::loadModules() noexcept {
|
||||||
for (auto const mod : modules) {
|
for (auto const mod : g_modules) {
|
||||||
loadModule(*mod);
|
loadModule(*mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -673,14 +677,18 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
|||||||
auto p = std::move(*pcIt);
|
auto p = std::move(*pcIt);
|
||||||
std::ignore = config.projects.erase(pcIt);
|
std::ignore = config.projects.erase(pcIt);
|
||||||
auto &pc = *config.projects.emplace(0, std::move(p));
|
auto &pc = *config.projects.emplace(0, std::move(p));
|
||||||
for (auto const &f: pc.openFiles) {
|
std::ignore = pc.openFiles.erase(
|
||||||
auto const openFileErr = openFileActiveTab(f, pc.activeTabItemName == f);
|
std::remove_if(
|
||||||
if (openFileErr) {
|
pc.openFiles.begin(), pc.openFiles.end(),
|
||||||
oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
|
[&](ox::String const &f) {
|
||||||
continue;
|
auto const openFileErr = openFileActiveTab(f, pc.activeTabItemName == f);
|
||||||
}
|
if (openFileErr) {
|
||||||
m_activeEditor = m_editors.back().value->get();
|
oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr));
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
m_activeEditor = m_editors.back().value->get();
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
config.projects.emplace(0, StudioConfig::ProjectConfig{
|
config.projects.emplace(0, StudioConfig::ProjectConfig{
|
||||||
.projectPath = ox::String{m_project->projectPath()},
|
.projectPath = ox::String{m_project->projectPath()},
|
||||||
|
@ -23,15 +23,34 @@ struct EditorMaker {
|
|||||||
Func make;
|
Func make;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Command {
|
||||||
|
ox::String name;
|
||||||
|
std::function<ox::Error(Project&, ox::SpanView<ox::CString>)> func;
|
||||||
|
Command(
|
||||||
|
ox::StringParam name,
|
||||||
|
std::function<ox::Error(Project&, ox::SpanView<ox::CString>)> func) noexcept:
|
||||||
|
name(std::move(name)),
|
||||||
|
func(std::move(func)) {}
|
||||||
|
};
|
||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
public:
|
public:
|
||||||
virtual ~Module() noexcept = default;
|
virtual ~Module() noexcept = default;
|
||||||
|
|
||||||
virtual ox::Vector<EditorMaker> editors(studio::Context &ctx) const;
|
[[nodiscard]]
|
||||||
|
virtual ox::String id() const noexcept = 0;
|
||||||
|
|
||||||
virtual ox::Vector<ox::UPtr<ItemMaker>> itemMakers(studio::Context&) const;
|
[[nodiscard]]
|
||||||
|
virtual ox::Vector<Command> commands() const;
|
||||||
|
|
||||||
virtual ox::Vector<ox::UPtr<ItemTemplate>> itemTemplates(studio::Context&) const;
|
[[nodiscard]]
|
||||||
|
virtual ox::Vector<EditorMaker> editors(Context &ctx) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual ox::Vector<ox::UPtr<ItemMaker>> itemMakers(Context&) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual ox::Vector<ox::UPtr<ItemTemplate>> itemTemplates(Context&) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ class Project: public ox::SignalHandler {
|
|||||||
ox::HashMap<ox::String, ox::Vector<ox::String>> m_fileExtFileMap;
|
ox::HashMap<ox::String, ox::Vector<ox::String>> m_fileExtFileMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Project(keel::Context &ctx, ox::String path, ox::StringViewCR projectDataDir);
|
explicit Project(keel::Context &ctx, ox::StringParam path, ox::StringViewCR projectDataDir);
|
||||||
|
|
||||||
ox::Error create() noexcept;
|
ox::Error create() noexcept;
|
||||||
|
|
||||||
@ -101,6 +101,16 @@ class Project: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Error deleteItem(ox::StringViewCR path) noexcept;
|
ox::Error deleteItem(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
keel::Context &kctx() noexcept {
|
||||||
|
return m_kctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
keel::Context const &kctx() const noexcept {
|
||||||
|
return m_kctx;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool exists(ox::StringViewCR path) const noexcept;
|
bool exists(ox::StringViewCR path) const noexcept;
|
||||||
|
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
|
ox::Vector<Command> Module::commands() const {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ox::Vector<EditorMaker> Module::editors(Context&) const {
|
ox::Vector<EditorMaker> Module::editors(Context&) const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ static constexpr bool isParentOf(ox::StringViewCR parent, ox::StringViewCR child
|
|||||||
return beginsWith(sfmt("{}/", child), parent);
|
return beginsWith(sfmt("{}/", child), parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
Project::Project(keel::Context &ctx, ox::String path, ox::StringViewCR projectDataDir):
|
Project::Project(keel::Context &ctx, ox::StringParam path, ox::StringViewCR projectDataDir):
|
||||||
m_kctx(ctx),
|
m_kctx(ctx),
|
||||||
m_path(std::move(path)),
|
m_path(std::move(path)),
|
||||||
m_projectDataDir(projectDataDir),
|
m_projectDataDir(projectDataDir),
|
||||||
|
@ -50,7 +50,7 @@ OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
|||||||
constexpr auto headerP1Len = ox::strlen(headerP2);
|
constexpr auto headerP1Len = ox::strlen(headerP2);
|
||||||
constexpr auto headerP2Len = ox::strlen(headerP1);
|
constexpr auto headerP2Len = ox::strlen(headerP1);
|
||||||
constexpr auto headerLen = headerP1Len + headerP2Len;
|
constexpr auto headerLen = headerP1Len + headerP2Len;
|
||||||
for (auto current = MEM_ROM; current < reinterpret_cast<char*>(0x0a000000); current += headerLen) {
|
for (auto current = MEM_ROM.data(); current < reinterpret_cast<char*>(0x0a000000); current += headerLen) {
|
||||||
if (memcmp(current, headerP1, headerP1Len) == 0 &&
|
if (memcmp(current, headerP1, headerP1Len) == 0 &&
|
||||||
memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
|
memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
|
||||||
return reinterpret_cast<std::size_t>(current + headerLen);
|
return reinterpret_cast<std::size_t>(current + headerLen);
|
||||||
|
Reference in New Issue
Block a user