diff --git a/src/nostalgia/CMakeLists.txt b/src/nostalgia/CMakeLists.txt
index 708e6e23..23d11ad3 100644
--- a/src/nostalgia/CMakeLists.txt
+++ b/src/nostalgia/CMakeLists.txt
@@ -17,7 +17,10 @@ add_subdirectory(common)
 add_subdirectory(player)
 add_subdirectory(world)
 
-if(NOSTALGIA_BUILD_STUDIO)
+if(NOSTALGIA_BUILD_TYPE STREQUAL "Native")
 	add_subdirectory(tools)
+endif()
+
+if(NOSTALGIA_BUILD_STUDIO)
 	add_subdirectory(studio)
 endif()
diff --git a/src/nostalgia/core/gba/gfx.cpp b/src/nostalgia/core/gba/gfx.cpp
index 27dbff09..02ccab73 100644
--- a/src/nostalgia/core/gba/gfx.cpp
+++ b/src/nostalgia/core/gba/gfx.cpp
@@ -7,6 +7,7 @@
  */
 
 #include <ox/fs/fs.hpp>
+#include <ox/mc/mc.hpp>
 #include <ox/std/std.hpp>
 
 #include "../media.hpp"
@@ -24,8 +25,8 @@ using namespace ox;
 #define TILE_ADDR  ((CharBlock*) 0x06000000)
 #define TILE8_ADDR ((CharBlock8*) 0x06000000)
 
-const auto GBA_TILE_COLUMNS = 32;
-const auto GBA_TILE_ROWS = 32;
+constexpr auto GBA_TILE_COLUMNS = 32;
+constexpr auto GBA_TILE_ROWS = 32;
 
 // map ASCII values to the nostalgia charset
 static char charMap[128] = {
@@ -159,22 +160,22 @@ static char charMap[128] = {
 };
 
 struct GbaPaletteTarget {
-	uint16_t *palette = nullptr;
+	volatile uint16_t *palette = nullptr;
 };
 
 struct GbaTileMapTarget {
 	static constexpr auto Fields = 4;
-	uint8_t bpp = 0;
+	volatile uint32_t *bgCtl = nullptr;
 	ox::FileAddress defaultPalette;
 	GbaPaletteTarget pal;
-	uint16_t *tileMap = nullptr;
+	volatile uint16_t *tileMap = nullptr;
 };
 
 template<typename T>
 ox::Error modelRead(T *io, GbaPaletteTarget *t) {
 	io->setTypeInfo("nostalgia::core::NostalgiaPalette", NostalgiaPalette::Fields);
-	oxReturnError(io->template field<uint16_t>("colors", [t](auto i, uint16_t c) {
-		t->palette[i] = c;
+	oxReturnError(io->template field<Color>("colors", [t](auto i, Color *c) {
+		t->palette[i] = *c;
 		return OxError(0);
 	}));
 	return OxError(0);
@@ -183,20 +184,27 @@ ox::Error modelRead(T *io, GbaPaletteTarget *t) {
 template<typename T>
 ox::Error modelRead(T *io, GbaTileMapTarget *t) {
 	io->setTypeInfo("nostalgia::core::NostalgiaGraphic", NostalgiaGraphic::Fields);
-	oxReturnError(io->field("bpp", &t->bpp));
+
+	uint8_t bpp;
+	oxReturnError(io->field("bpp", &bpp));
+	constexpr auto Bpp8 = 1 << 7;
+	*t->bgCtl = (*t->bgCtl | Bpp8) ^ Bpp8; // set to use 4 bits per pixel
+	*t->bgCtl |= Bpp8; // set to use 8 bits per pixel
+	*t->bgCtl = (28 << 8) | 1;
+
 	oxReturnError(io->field("defaultPalette", &t->defaultPalette));
 	oxReturnError(io->field("pal", &t->pal));
-	uint16_t intermediate;
-	oxReturnError(io->template field<uint8_t>("tileMap", [t, &intermediate](auto i, uint8_t tile) {
+	uint16_t intermediate = 0;
+	auto err = io->template field<uint8_t>("tileMap", [t, &intermediate](auto i, uint8_t *tile) {
 		if (i & 1) { // i is odd
-			intermediate |= tile >> 8;
+			intermediate |= static_cast<uint16_t>(*tile) << 8;
 			t->tileMap[i / 2] = intermediate;
 		} else { // i is even
-			intermediate = tile & 0xff;
+			intermediate = *tile & 0x00ff;
 		}
 		return OxError(0);
-	}));
-	return OxError(0);
+	});
+	return OxError(err);
 }
 
 ox::Error initGfx(Context*) {
@@ -214,75 +222,58 @@ ox::Error shutdownGfx() {
 	return OxError(0);
 }
 
-// Do NOT use Context in the GBA version of this function.
-ox::Error initConsole(Context*) {
-	const auto PaletteStart = sizeof(GbaImageDataHeader);
-	ox::Error err(0);
-	ox::FileStore32 fs(loadRom(), 32 * ox::units::MB);
-	ox::FileSystem32 fileSystem(fs);
-	const auto CharsetInode = fileSystem.stat("/TileSheets/Charset.ng").value.inode;
-
-	GbaImageDataHeader imgData;
-
-	REG_BG0CNT = (28 << 8) | 1;
-	if (fs.valid()) {
-		// load the header
-		err |= fs.read(CharsetInode, 0, sizeof(imgData), &imgData, nullptr);
-
-		// load palette
-		err |= fs.read(CharsetInode, PaletteStart,
-		               512, (uint16_t*) &MEM_PALLETE_BG[0], nullptr);
-
-		if (imgData.bpp == 4) {
-			err |= fs.read(CharsetInode, __builtin_offsetof(GbaImageData, tiles),
-			               sizeof(Tile) * imgData.tileCount, (uint16_t*) &TILE_ADDR[0][1], nullptr);
-		} else if (imgData.bpp == 8) {
-			REG_BG0CNT |= (1 << 7); // set to use 8 bits per pixel
-			err |= fs.read(CharsetInode, __builtin_offsetof(GbaImageData, tiles),
-			               sizeof(Tile8) * imgData.tileCount, (uint16_t*) &TILE8_ADDR[0][1], nullptr);
-		} else {
-			err = OxError(1);
-		}
-	} else {
-		err = OxError(1);
+[[nodiscard]] constexpr volatile uint32_t &bgCtl(int bg) noexcept {
+	switch (bg) {
+		case 0:
+			return REG_BG0CNT;
+		case 1:
+			return REG_BG1CNT;
+		case 2:
+			return REG_BG2CNT;
+		case 3:
+			return REG_BG3CNT;
+		default:
+			panic("Looking up non-existent register");
+			return REG_BG0CNT;
 	}
-	return err;
 }
 
-ox::Error loadTileSheet(Context*,
-                        TileSheetSpace,
-                        int,
-                        ox::FileAddress tilesheetPath,
-                        ox::FileAddress) {
-	auto inode = tilesheetPath.getInode().value;
-	ox::Error err(0);
-	constexpr auto PaletteStart = sizeof(GbaImageDataHeader);
-	GbaImageDataHeader imgData;
-
-	ox::FileStore32 fs(loadRom(), 32 * 1024 * 1024);
-	REG_BG0CNT = (28 << 8) | 1;
-	if (fs.valid()) {
-		// load the header
-		err |= fs.read(inode, 0, sizeof(imgData), &imgData, nullptr);
-
-		// load palette
-		err |= fs.read(inode, PaletteStart,
-		               512, (uint16_t*) &MEM_PALLETE_BG[0], nullptr);
-
-		if (imgData.bpp == 4) {
-			err |= fs.read(inode, __builtin_offsetof(GbaImageData, tiles),
-			               sizeof(Tile) * imgData.tileCount, (uint16_t*) &TILE_ADDR[0][1], nullptr);
-		} else if (imgData.bpp == 8) {
-			REG_BG0CNT |= (1 << 7); // set to use 8 bits per pixel
-			err |= fs.read(inode, __builtin_offsetof(GbaImageData, tiles),
-			               sizeof(Tile8) * imgData.tileCount, (uint16_t*) &TILE8_ADDR[0][1], nullptr);
-		} else {
-			err = OxError(1);
-		}
-	} else {
-		err = OxError(1);
+// Do NOT rely on Context in the GBA version of this function.
+ox::Error initConsole(Context *ctx) {
+	constexpr auto TilesheetAddr = "/TileSheets/Charset.ng";
+	constexpr auto PaletteAddr = "/Palettes/Charset.npal";
+	if (!ctx) {
+		ctx = new (ox_alloca(sizeof(Context))) Context();
+		ox::FileStore32 fs(loadRom(), 32 * ox::units::MB);
+		ctx->rom = new (ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs);
 	}
-	return err;
+	return loadTileSheet(ctx, TileSheetSpace::Background, 0, TilesheetAddr, PaletteAddr);
+}
+
+ox::Error loadTileSheet(Context *ctx,
+                        TileSheetSpace,
+                        int section,
+                        ox::FileAddress tilesheetAddr,
+                        ox::FileAddress paletteAddr) {
+	auto [tsStat, tsStatErr] = ctx->rom->stat(tilesheetAddr);
+	oxReturnError(tsStatErr);
+	auto [ts, tserr] = ctx->rom->read(tilesheetAddr);
+	oxReturnError(tserr);
+	GbaTileMapTarget target;
+	target.pal.palette = &MEM_PALLETE_BG[section];
+	target.bgCtl = &bgCtl(section);
+	target.tileMap = reinterpret_cast<uint16_t*>(&TILE_ADDR[section][0]);
+	oxReturnError(ox::readMC(ts, tsStat.size, &target));
+	// load external palette if available
+	if (paletteAddr) {
+		auto [palStat, palStatErr] = ctx->rom->stat(paletteAddr);
+		oxReturnError(palStatErr);
+		auto [pal, palErr] = ctx->rom->read(paletteAddr);
+		oxReturnError(palErr);
+		oxReturnError(ox::readMC(pal, palStat.size, &target.pal));
+	}
+
+	return OxError(0);
 }
 
 // Do NOT use Context in the GBA version of this function.
diff --git a/src/nostalgia/player/CMakeLists.txt b/src/nostalgia/player/CMakeLists.txt
index f381bfa3..9676bb7d 100644
--- a/src/nostalgia/player/CMakeLists.txt
+++ b/src/nostalgia/player/CMakeLists.txt
@@ -1,4 +1,3 @@
-
 add_executable(
 	nostalgia
 		main.cpp
diff --git a/src/nostalgia/player/main.cpp b/src/nostalgia/player/main.cpp
index 5d864446..7ad0da2b 100644
--- a/src/nostalgia/player/main.cpp
+++ b/src/nostalgia/player/main.cpp
@@ -16,11 +16,13 @@ using namespace nostalgia::world;
 
 ox::Error run(ox::FileSystem *fs) {
 	Context ctx;
-	oxReturnError(init(&ctx));
 	ctx.rom = fs;
-	Zone zone;
-	oxReturnError(zone.init(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/Charset.ng", "/Palettes/Charset.npal"));
-	zone.draw(&ctx);
+	oxReturnError(init(&ctx));
+	//Zone zone;
+	//oxReturnError(zone.init(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/Charset.ng", "/Palettes/Charset.npal"));
+	//zone.draw(&ctx);
+	oxReturnError(initConsole(&ctx));
+	puts(nullptr, 9 * 32 + 10, "DOPENESS!!!");
 	oxReturnError(run());
 	oxReturnError(shutdownGfx());
 	return OxError(0);
@@ -28,9 +30,14 @@ ox::Error run(ox::FileSystem *fs) {
 
 #ifndef OX_USE_STDLIB
 
-extern "C" void _start() {
-	ox::FileSystem32 fs(ox::FileStore32(loadRom(), 32 * ox::units::MB));
+int main() {
+	auto rom = loadRom();
+	if (!rom) {
+		return 1;
+	}
+	ox::FileSystem32 fs(ox::FileStore32(rom, 32 * ox::units::MB));
 	run(&fs);
+	return 0;
 }
 
 #else
diff --git a/src/nostalgia/player/startup.s b/src/nostalgia/player/startup.s
index f26cfb3c..cf91eb47 100644
--- a/src/nostalgia/player/startup.s
+++ b/src/nostalgia/player/startup.s
@@ -1,9 +1,39 @@
+	.section ".crt0","ax"
+	.global _start
+	.align
 
-_entry:
+	.arm
+	.cpu arm7tdmi
+
+_start:
+	// disable interrupts
+	//cpsid if
+	b _reset
+	.fill 156,1,0 // make room for Nintendo logo
+	.fill 16,1,0
+	.byte 0x30,0x31
 
 
-reset:
-	@ disable interrupts
-	@ cpsid if
+_initCpp:
+	// copy data
+	ldr r0,=__data_lma
+	ldr r1,=__data_start__
+	ldr r2,=__data_end__
+	// loop over data until copied
+data_copy_loop:
+	cmp r1,r2
+	ldmltia r0!,{r3}
+	stmltia r1!,{r3}
+	blt data_copy_loop
 
-@ vim:ft=arm
+_reset:
+	ldr r0,=_reset
+	ldr r1,=_initCpp
+	mov lr,r1
+	ldr sp,=__ewram_end
+	b _initCpp
+
+	.size _start, . - _start
+	.end
+
+// vim:ft=arm
diff --git a/src/nostalgia/tools/CMakeLists.txt b/src/nostalgia/tools/CMakeLists.txt
index ac4eddd2..f3f080dd 100644
--- a/src/nostalgia/tools/CMakeLists.txt
+++ b/src/nostalgia/tools/CMakeLists.txt
@@ -1,6 +1,3 @@
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-set(CMAKE_AUTOMOC ON)
-
 add_executable(nost-pack pack.cpp)
 
 target_link_libraries(
diff --git a/src/nostalgia/tools/pack.cpp b/src/nostalgia/tools/pack.cpp
index d5a614c6..2e0cf1db 100644
--- a/src/nostalgia/tools/pack.cpp
+++ b/src/nostalgia/tools/pack.cpp
@@ -66,21 +66,21 @@ using namespace nostalgia::common;
 	oxReturnError(ox::FileSystem32::format(buff.data(), buff.size()));
 	ox::PassThroughFS src(argSrc.c_str());
 	ox::FileSystem32 dst(ox::FileStore32(buff.data(), buff.size()));
-	auto err = nostalgia::pack(&src, &dst);
-	if (err) {
-		std::cerr << "pack failed...";
-	}
+	oxReturnError(nostalgia::pack(&src, &dst));
 
 	oxReturnError(dst.resize());
 	std::cout << "new size: " << dst.size() << '\n';
 	buff.resize(dst.size());
 
 	oxReturnError(writeFileBuff(argDst, buff));
-	return err;
+	return OxError(0);
 }
 
 int main(int argc, const char **args) {
 	auto err = run(ClArgs(argc, args));
 	oxAssert(err, "pack failed");
+	if (err) {
+		std::cerr << "pack failed...\n";
+	}
 	return static_cast<int>(err);
 }
diff --git a/src/nostalgia/tools/pack/pack.cpp b/src/nostalgia/tools/pack/pack.cpp
index fa4f8245..ea66242e 100644
--- a/src/nostalgia/tools/pack/pack.cpp
+++ b/src/nostalgia/tools/pack/pack.cpp
@@ -39,6 +39,7 @@ namespace {
 // transformations need to be done after the copy to the new FS is complete
 [[nodiscard]] ox::Error transformClaw(ox::FileSystem32 *dest, std::string path) {
 	// copy
+	oxTrace("pack::transformClaw") << "path:" << path.c_str();
 	return dest->ls(path.c_str(), [dest, path](const char *name, ox::InodeId_t) {
 		auto [stat, err] = dest->stat(path.c_str());
 		oxReturnError(err);
@@ -68,10 +69,16 @@ namespace {
 	return OxError(buff == expected ? 0 : 1);
 }
 
+struct VerificationPair {
+	std::string path;
+	std::vector<uint8_t> buff;
+};
+
 [[nodiscard]] ox::Error copy(ox::PassThroughFS *src, ox::FileSystem32 *dest, std::string path) {
 	std::cout << "copying directory: " << path << '\n';
+	std::vector<VerificationPair> verficationPairs;
 	// copy
-	return src->ls(path.c_str(), [src, dest, path](const char *name, ox::InodeId_t) {
+	oxReturnError(src->ls(path.c_str(), [&verficationPairs, src, dest, path](const char *name, ox::InodeId_t) {
 		std::cout << "reading " << name << '\n';
 		auto currentFile = path + name;
 		auto [stat, err] = src->stat((currentFile).c_str());
@@ -90,16 +97,26 @@ namespace {
 			std::cout << "writing " << currentFile << '\n';
 			oxReturnError(dest->write(currentFile.c_str(), buff.data(), buff.size()));
 			oxReturnError(verifyFile(dest, currentFile, buff));
+			verficationPairs.push_back({currentFile, buff});
 		}
 		return OxError(0);
-	});
-}
+	}));
 
-}
+	// verify all at once in addition to right after the files are written
+	for (auto v : verficationPairs) {
+		oxReturnError(verifyFile(dest, v.path, v.buff));
+	}
 
-[[nodiscard]] ox::Error pack(ox::PassThroughFS *src, ox::FileSystem32 *dest, std::string path) {
-	oxReturnError(copy(src, dest, path));
-	oxReturnError(transformClaw(dest, path));
+	return OxError(0);
+}
+
+}
+
+[[nodiscard]] ox::Error pack(ox::PassThroughFS *src, ox::FileSystem32 *dest) {
+	oxReturnError(copy(src, dest, "/"));
+	oxReturnError(dest->stat("/TileSheets/Charset.ng").error);
+	oxReturnError(dest->stat("/Palettes/Charset.npal").error);
+	oxReturnError(transformClaw(dest, "/"));
 	return OxError(0);
 }
 
diff --git a/src/nostalgia/tools/pack/pack.hpp b/src/nostalgia/tools/pack/pack.hpp
index d81cee6a..7ead6b1a 100644
--- a/src/nostalgia/tools/pack/pack.hpp
+++ b/src/nostalgia/tools/pack/pack.hpp
@@ -12,6 +12,6 @@
 
 namespace nostalgia {
 
-ox::Error pack(ox::PassThroughFS *src, ox::FileSystem32 *dest, std::string path = "/");
+ox::Error pack(ox::PassThroughFS *src, ox::FileSystem32 *dest);
 
 }