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 { | ||||
| 	for (auto i = 0u; i < args.size(); ++i) { | ||||
| 		auto arg = StringView(args[i]); | ||||
| 		auto arg = StringView{args[i]}; | ||||
| 		if (arg[0] == '-') { | ||||
| 			while (arg[0] == '-' && arg.len()) { | ||||
| 				arg = substr(arg, 1); | ||||
| @@ -23,8 +23,8 @@ ClArgs::ClArgs(ox::SpanView<const char*> args) noexcept { | ||||
| 			m_bools[arg] = true; | ||||
| 			// parse additional arguments | ||||
| 			if (i < args.size() && args[i + 1]) { | ||||
| 				auto val = String(args[i + 1]); | ||||
| 				if (val.len() && val[i] != '-') { | ||||
| 				auto const val = StringView{args[i + 1]}; | ||||
| 				if (val.len() && val[0] != '-') { | ||||
| 					if (val == "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 { | ||||
| 	auto [value, err] = m_ints.at(arg); | ||||
| 	auto const [value, err] = m_ints.at(arg); | ||||
| 	return !err ? *value : defaultValue; | ||||
| } | ||||
|  | ||||
| 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); | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										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); | ||||
| 	} | ||||
|  | ||||
| 	constexpr Result transformError(ErrorCode const ec, CString const msg) && { | ||||
| 		if (error) { | ||||
| 			error = Error{ec, msg}; | ||||
| 		} | ||||
| 		return *this; | ||||
| 	} | ||||
|  | ||||
| }; | ||||
|  | ||||
| namespace detail { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "cstringview.hpp" | ||||
| #include "string.hpp" | ||||
|  | ||||
| namespace ox { | ||||
|   | ||||
| @@ -5,103 +5,101 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <ox/std/array.hpp> | ||||
| #include <ox/std/types.hpp> | ||||
| #include <ox/std/units.hpp> | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // Interrupt Handler | ||||
|  | ||||
| using interrupt_handler = void (*)(); | ||||
| #define REG_ISR *reinterpret_cast<interrupt_handler*>(0x0300'7FFC) | ||||
| #define REG_IE  *reinterpret_cast<volatile uint16_t*>(0x0400'0200) | ||||
| #define REG_IF  *reinterpret_cast<volatile uint16_t*>(0x0400'0202) | ||||
| #define REG_IME *reinterpret_cast<volatile uint16_t*>(0x0400'0208) | ||||
| using InterruptHandler = void(*)(); | ||||
| #define REG_ISR (*reinterpret_cast<InterruptHandler*>(0x0300'7FFC)) | ||||
| #define REG_IE  (*reinterpret_cast<volatile uint16_t*>(0x0400'0200)) | ||||
| #define REG_IF  (*reinterpret_cast<volatile uint16_t*>(0x0400'0202)) | ||||
| #define REG_IME (*reinterpret_cast<volatile uint16_t*>(0x0400'0208)) | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // Display Registers | ||||
|  | ||||
| #define REG_DISPCTL  *reinterpret_cast<volatile uint32_t*>(0x0400'0000) | ||||
| #define REG_DISPSTAT *reinterpret_cast<volatile uint32_t*>(0x0400'0004) | ||||
| #define REG_VCOUNT   *reinterpret_cast<volatile uint32_t*>(0x0400'0006) | ||||
| #define REG_DISPCTL  (*reinterpret_cast<volatile uint32_t*>(0x0400'0000)) | ||||
| #define REG_DISPSTAT (*reinterpret_cast<volatile uint32_t*>(0x0400'0004)) | ||||
| #define REG_VCOUNT   (*reinterpret_cast<volatile uint32_t*>(0x0400'0006)) | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // Timers | ||||
|  | ||||
| #define REG_TIMER0    *reinterpret_cast<volatile uint16_t*>(0x0400'0100) | ||||
| #define REG_TIMER0CTL *reinterpret_cast<volatile uint16_t*>(0x0400'0102) | ||||
| #define REG_TIMER0    (*reinterpret_cast<volatile uint16_t*>(0x0400'0100)) | ||||
| #define REG_TIMER0CTL (*reinterpret_cast<volatile uint16_t*>(0x0400'0102)) | ||||
|  | ||||
| #define REG_TIMER1    *reinterpret_cast<volatile uint16_t*>(0x0400'0104) | ||||
| #define REG_TIMER1CTL *reinterpret_cast<volatile uint16_t*>(0x0400'0106) | ||||
| #define REG_TIMER1    (*reinterpret_cast<volatile uint16_t*>(0x0400'0104)) | ||||
| #define REG_TIMER1CTL (*reinterpret_cast<volatile uint16_t*>(0x0400'0106)) | ||||
|  | ||||
| #define REG_TIMER2    *reinterpret_cast<volatile uint16_t*>(0x0400'0108) | ||||
| #define REG_TIMER2CTL *reinterpret_cast<volatile uint16_t*>(0x0400'010a) | ||||
| #define REG_TIMER2    (*reinterpret_cast<volatile uint16_t*>(0x0400'0108)) | ||||
| #define REG_TIMER2CTL (*reinterpret_cast<volatile uint16_t*>(0x0400'010a)) | ||||
|  | ||||
| #define REG_TIMER3    *reinterpret_cast<volatile uint16_t*>(0x0400'010c) | ||||
| #define REG_TIMER3CTL *reinterpret_cast<volatile uint16_t*>(0x0400'010e) | ||||
| #define REG_TIMER3    (*reinterpret_cast<volatile uint16_t*>(0x0400'010c)) | ||||
| #define REG_TIMER3CTL (*reinterpret_cast<volatile uint16_t*>(0x0400'010e)) | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // background registers | ||||
|  | ||||
| // background control registers | ||||
| using BgCtl = uint16_t; | ||||
| #define REG_BG0CTL *reinterpret_cast<volatile BgCtl*>(0x0400'0008) | ||||
| #define REG_BG1CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000a) | ||||
| #define REG_BG2CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000c) | ||||
| #define REG_BG3CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000e) | ||||
| #define REG_BG0CTL (*reinterpret_cast<volatile BgCtl*>(0x0400'0008)) | ||||
| #define REG_BG1CTL (*reinterpret_cast<volatile BgCtl*>(0x0400'000a)) | ||||
| #define REG_BG2CTL (*reinterpret_cast<volatile BgCtl*>(0x0400'000c)) | ||||
| #define REG_BG3CTL (*reinterpret_cast<volatile BgCtl*>(0x0400'000e)) | ||||
|  | ||||
| [[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); | ||||
| } | ||||
|  | ||||
| // background horizontal scrolling registers | ||||
| #define REG_BG0HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0010) | ||||
| #define REG_BG1HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0014) | ||||
| #define REG_BG2HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0018) | ||||
| #define REG_BG3HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001c) | ||||
| #define REG_BG0HOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0010)) | ||||
| #define REG_BG1HOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0014)) | ||||
| #define REG_BG2HOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0018)) | ||||
| #define REG_BG3HOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'001c)) | ||||
|  | ||||
| [[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); | ||||
| } | ||||
|  | ||||
| // background vertical scrolling registers | ||||
| #define REG_BG0VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0012) | ||||
| #define REG_BG1VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0016) | ||||
| #define REG_BG2VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001a) | ||||
| #define REG_BG3VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001e) | ||||
| #define REG_BG0VOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0012)) | ||||
| #define REG_BG1VOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'0016)) | ||||
| #define REG_BG2VOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'001a)) | ||||
| #define REG_BG3VOFS (*reinterpret_cast<volatile uint32_t*>(0x0400'001e)) | ||||
|  | ||||
| [[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); | ||||
| } | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // User Input | ||||
|  | ||||
| #define REG_GAMEPAD *reinterpret_cast<volatile uint16_t*>(0x0400'0130) | ||||
| #define REG_GAMEPAD (*reinterpret_cast<volatile uint16_t*>(0x0400'0130)) | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // Memory Addresses | ||||
|  | ||||
| #define MEM_EWRAM_BEGIN reinterpret_cast<uint8_t*>(0x0200'0000) | ||||
| #define MEM_EWRAM_END   reinterpret_cast<uint8_t*>(0x0203'FFFF) | ||||
| #define MEM_EWRAM (*reinterpret_cast<ox::Array<uint16_t, 0x0203'FFFF - 0x0200'0000>*>(0x0200'0000)) | ||||
|  | ||||
| #define MEM_IWRAM_BEGIN reinterpret_cast<uint8_t*>(0x0300'0000) | ||||
| #define MEM_IWRAM_END   reinterpret_cast<uint8_t*>(0x0300'7FFF) | ||||
| #define MEM_IWRAM (*reinterpret_cast<ox::Array<uint8_t, 0x0300'7FFF - 0x0300'0000>*>(0x0300'0000)) | ||||
|  | ||||
| #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) | ||||
| #define MEM_SPRITE_PALETTE reinterpret_cast<uint16_t*>(0x0500'0200) | ||||
| using Palette = ox::Array<uint16_t, 128>; | ||||
| #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>; | ||||
| #define MEM_BG_TILES reinterpret_cast<BgMapTile*>(0x0600'0000) | ||||
| #define MEM_BG_MAP reinterpret_cast<BgMapTile*>(0x0600'e000) | ||||
| #define MEM_BG_TILES (*reinterpret_cast<ox::Array<BgMapTile, 4>*>(0x0600'0000)) | ||||
| #define MEM_BG_MAP (*reinterpret_cast<ox::Array<BgMapTile, 4>*>(0x0600'e000)) | ||||
|  | ||||
| #define MEM_SPRITE_TILES reinterpret_cast<uint16_t*>(0x0601'0000) | ||||
| #define MEM_OAM reinterpret_cast<uint64_t*>(0x0700'0000) | ||||
| #define MEM_SPRITE_TILES (*reinterpret_cast<ox::Array<uint16_t, 32 * ox::units::KB>*>(0x0601'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_SIZE 65535 | ||||
| #define MEM_SRAM (*reinterpret_cast<ox::Array<char, 64 * ox::units::KB>*>(0x0e00'0000)) | ||||
|   | ||||
| @@ -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::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]] | ||||
| TileSheet::SubSheet &getSubSheet( | ||||
| 		ox::SpanView<uint32_t> const&idx, | ||||
|   | ||||
| @@ -35,11 +35,6 @@ class Context { | ||||
| 		Context(Context const &&other) noexcept = delete; | ||||
| 		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 { | ||||
| @@ -81,7 +76,7 @@ ox::Error loadBgPalette( | ||||
| 	if (palette.pages.empty()) { | ||||
| 		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) { | ||||
| 		paletteMem[i] = color(palette, page, i); | ||||
| 	} | ||||
| @@ -95,9 +90,8 @@ ox::Error loadSpritePalette( | ||||
| 	if (palette.pages.empty()) { | ||||
| 		return {}; | ||||
| 	} | ||||
| 	auto const paletteMem = MEM_SPRITE_PALETTE; | ||||
| 	for (auto i = 0u; i < colorCnt(palette, page); ++i) { | ||||
| 		paletteMem[i] = color(palette, page, i); | ||||
| 		MEM_SPRITE_PALETTE[i] = color(palette, page, i); | ||||
| 	} | ||||
| 	return {}; | ||||
| } | ||||
| @@ -240,7 +234,7 @@ ox::Error loadSpriteTileSheet( | ||||
| 		Context &ctx, | ||||
| 		TileSheetSet const &set) noexcept { | ||||
| 	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); | ||||
| 	return {}; | ||||
| } | ||||
| @@ -349,9 +343,9 @@ void panic(char const *file, int line, char const *panicMsg, ox::Error const &er | ||||
| 	using namespace nostalgia::gfx; | ||||
| 	// reset heap to make sure we have enough memory to allocate context data | ||||
| 	OX_ALLOW_UNSAFE_BUFFERS_BEGIN | ||||
| 	auto const heapBegin = reinterpret_cast<char*>(MEM_EWRAM_BEGIN); | ||||
| 	auto const heapSz = (MEM_EWRAM_END - MEM_EWRAM_BEGIN) / 2; | ||||
| 	auto const heapEnd = reinterpret_cast<char*>(MEM_EWRAM_BEGIN + heapSz); | ||||
| 	auto const heapBegin = reinterpret_cast<char*>(MEM_EWRAM.data()); | ||||
| 	auto const heapSz = MEM_EWRAM.size() / 2; | ||||
| 	auto const heapEnd = reinterpret_cast<char*>(MEM_EWRAM.data() + heapSz); | ||||
| 	ox::heapmgr::initHeap(heapBegin, heapEnd); | ||||
| 	OX_ALLOW_UNSAFE_BUFFERS_END | ||||
| 	auto tctx = turbine::init(keel::loadRomFs("").unwrap(), "Nostalgia").unwrap(); | ||||
|   | ||||
| @@ -26,3 +26,4 @@ install( | ||||
|  | ||||
| add_subdirectory(paletteeditor) | ||||
| add_subdirectory(tilesheeteditor) | ||||
| add_subdirectory(subcommands) | ||||
|   | ||||
| @@ -8,10 +8,25 @@ | ||||
|  | ||||
| #include "paletteeditor/paletteeditor-imgui.hpp" | ||||
| #include "tilesheeteditor/tilesheeteditor-imgui.hpp" | ||||
| #include "subcommands/export-tilesheet/export-tilesheet.hpp" | ||||
|  | ||||
| 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 { | ||||
| 		return { | ||||
| 			studio::editorMaker<TileSheetEditorImGui>(ctx, {FileExt_ng, FileExt_nts}), | ||||
| @@ -28,6 +43,7 @@ static class: public studio::Module { | ||||
| 		}, ox::ClawFormat::Organic)); | ||||
| 		return out; | ||||
| 	} | ||||
|  | ||||
| } const mod; | ||||
|  | ||||
| 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 "../subcommands/export-tilesheet/export-tilesheet.hpp" | ||||
| #include "tilesheeteditor-imgui.hpp" | ||||
|  | ||||
| namespace nostalgia::gfx { | ||||
| @@ -37,58 +38,6 @@ OX_MODEL_BEGIN(TileSheetEditorConfig) | ||||
| 	OX_MODEL_FIELD_RENAME(activeSubsheet, active_subsheet) | ||||
| 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): | ||||
| 		Editor{sctx, std::move(path)}, | ||||
| 		m_sctx{sctx}, | ||||
| @@ -421,21 +370,7 @@ ox::Error TileSheetEditorImGui::exportSubsheetToPng(int const scale) const noexc | ||||
| 	// subsheet to png | ||||
| 	auto const &s = m_model.activeSubSheet(); | ||||
| 	auto const &pal = m_model.pal(); | ||||
| 	auto const width = s.columns * TileWidth; | ||||
| 	auto const height = s.rows * TileHeight; | ||||
| 	auto pixels = normalizePixelSizes(s.pixels); | ||||
| 	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; | ||||
| 	return gfx::exportSubsheetToPng(s, pal, m_model.palettePage(), path, scale); | ||||
| } | ||||
|  | ||||
| void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const &fbSize) noexcept { | ||||
|   | ||||
| @@ -211,6 +211,47 @@ ox::Result<TileSheet::SubSheetIdx> getSubSheetIdx(TileSheet const &ts, SubSheetI | ||||
| 	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 | ||||
| #pragma GCC diagnostic push | ||||
| #pragma GCC diagnostic ignored "-Wdangling-reference" | ||||
|   | ||||
| @@ -10,6 +10,10 @@ namespace nostalgia::sound { | ||||
|  | ||||
| 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 { | ||||
| 		return { | ||||
| 		}; | ||||
| @@ -22,7 +26,7 @@ static struct: studio::Module { | ||||
|  | ||||
| } const mod; | ||||
|  | ||||
| const studio::Module *studioModule() noexcept { | ||||
| studio::Module const *studioModule() noexcept { | ||||
| 	return &mod; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,9 @@ | ||||
|  | ||||
| namespace studio { | ||||
|  | ||||
| [[nodiscard]] | ||||
| ox::Vector<Module const*> const &modules() noexcept; | ||||
|  | ||||
| void registerModule(Module const*) noexcept; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,15 +2,18 @@ | ||||
|  * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. | ||||
|  */ | ||||
|  | ||||
| #include <complex> | ||||
| #include <ctime> | ||||
|  | ||||
| #include <ox/logconn/logconn.hpp> | ||||
| #include <ox/std/trace.hpp> | ||||
| #include <ox/std/uuid.hpp> | ||||
| #include <keel/media.hpp> | ||||
| #include <keel/keel.hpp> | ||||
| #include <turbine/turbine.hpp> | ||||
|  | ||||
| #include <studio/context.hpp> | ||||
| #include <studioapp/studioapp.hpp> | ||||
|  | ||||
| #include "studioui.hpp" | ||||
|  | ||||
| namespace studio { | ||||
| @@ -40,7 +43,7 @@ static void mouseButtonEventHandler(turbine::Context &ctx, int const btn, bool c | ||||
| [[nodiscard]] | ||||
| ox::Vector<ox::SpanView<uint8_t>> WindowIcons() noexcept; | ||||
|  | ||||
| static ox::Error runApp( | ||||
| static ox::Error runStudio( | ||||
| 		ox::StringViewCR appName, | ||||
| 		ox::StringViewCR projectDataDir, | ||||
| 		ox::UPtr<ox::FileSystem> &&fs) noexcept { | ||||
| @@ -61,18 +64,47 @@ static ox::Error runApp( | ||||
| static ox::Error run( | ||||
| 		ox::StringViewCR appName, | ||||
| 		ox::StringViewCR projectDataDir, | ||||
| 		ox::SpanView<ox::CString>) { | ||||
| 		ox::SpanView<ox::CString> const &args) { | ||||
| 	// seed UUID generator | ||||
| 	auto const time = std::time(nullptr); | ||||
| 	ox::UUID::seedGenerator({ | ||||
| 		static_cast<uint64_t>(time), | ||||
| 		static_cast<uint64_t>(time << 1) | ||||
| 	}); | ||||
| 	if (args.size() > 1 && ox::StringView{args[1]} == "cmd") { | ||||
| 		if (args.size() < 5) { | ||||
| 			return ox::Error{1, "insufficient arguments for sub-command"}; | ||||
| 		} | ||||
| 		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 = runApp(appName, projectDataDir, ox::UPtr<ox::FileSystem>{}); | ||||
| 		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 { | ||||
| 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 { | ||||
| 	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 { | ||||
| 	for (auto const mod : modules) { | ||||
| 	for (auto const mod : g_modules) { | ||||
| 		loadModule(*mod); | ||||
| 	} | ||||
| } | ||||
| @@ -673,14 +677,18 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept { | ||||
| 			auto p = std::move(*pcIt); | ||||
| 			std::ignore = config.projects.erase(pcIt); | ||||
| 			auto &pc = *config.projects.emplace(0, std::move(p)); | ||||
| 			for (auto const &f: pc.openFiles) { | ||||
| 			std::ignore = pc.openFiles.erase( | ||||
| 				std::remove_if( | ||||
| 					pc.openFiles.begin(), pc.openFiles.end(), | ||||
| 					[&](ox::String const &f) { | ||||
| 						auto const openFileErr = openFileActiveTab(f, pc.activeTabItemName == f); | ||||
| 						if (openFileErr) { | ||||
| 							oxErrorf("\nCould not open editor for file:\n\t{}\nReason:\n\t{}\n", f, toStr(openFileErr)); | ||||
| 					continue; | ||||
| 							return true; | ||||
| 						} | ||||
| 						m_activeEditor = m_editors.back().value->get(); | ||||
| 			} | ||||
| 						return false; | ||||
| 					})); | ||||
| 		} else { | ||||
| 			config.projects.emplace(0, StudioConfig::ProjectConfig{ | ||||
| 				.projectPath = ox::String{m_project->projectPath()}, | ||||
|   | ||||
| @@ -23,15 +23,34 @@ struct EditorMaker { | ||||
| 	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 { | ||||
| 	public: | ||||
| 		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; | ||||
|  | ||||
| 	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; | ||||
|  | ||||
| @@ -101,6 +101,16 @@ class Project: public ox::SignalHandler { | ||||
|  | ||||
| 		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]] | ||||
| 		bool exists(ox::StringViewCR path) const noexcept; | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,10 @@ | ||||
|  | ||||
| namespace studio { | ||||
|  | ||||
| ox::Vector<Command> Module::commands() const { | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| ox::Vector<EditorMaker> Module::editors(Context&) const { | ||||
| 	return {}; | ||||
| } | ||||
|   | ||||
| @@ -54,7 +54,7 @@ static constexpr bool isParentOf(ox::StringViewCR parent, ox::StringViewCR child | ||||
| 	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_path(std::move(path)), | ||||
| 	m_projectDataDir(projectDataDir), | ||||
|   | ||||
| @@ -50,7 +50,7 @@ OX_ALLOW_UNSAFE_BUFFERS_BEGIN | ||||
| 	constexpr auto headerP1Len = ox::strlen(headerP2); | ||||
| 	constexpr auto headerP2Len = ox::strlen(headerP1); | ||||
| 	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 && | ||||
| 		    memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) { | ||||
| 			return reinterpret_cast<std::size_t>(current + headerLen); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user