Merge commit 'dceeaaa9302b7e9ce85fa773fc187bc593f3c93c'
This commit is contained in:
		
							
								
								
									
										12
									
								
								deps/nostalgia/deps/ox/src/ox/clargs/clargs.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								deps/nostalgia/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/nostalgia/deps/ox/src/ox/std/error.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								deps/nostalgia/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 { | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include "cstringview.hpp" | ||||||
| #include "string.hpp" | #include "string.hpp" | ||||||
|  |  | ||||||
| namespace ox { | namespace ox { | ||||||
|   | |||||||
| @@ -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 { | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								deps/nostalgia/src/nostalgia/modules/gfx/src/studio/subcommands/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								deps/nostalgia/src/nostalgia/modules/gfx/src/studio/subcommands/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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