[nostalgia] Restore GBA support

This commit is contained in:
Gary Talent 2019-11-03 21:32:40 -06:00
parent deaa293c67
commit 758907ab5a
9 changed files with 152 additions and 108 deletions

View File

@ -17,7 +17,10 @@ add_subdirectory(common)
add_subdirectory(player) add_subdirectory(player)
add_subdirectory(world) add_subdirectory(world)
if(NOSTALGIA_BUILD_STUDIO) if(NOSTALGIA_BUILD_TYPE STREQUAL "Native")
add_subdirectory(tools) add_subdirectory(tools)
endif()
if(NOSTALGIA_BUILD_STUDIO)
add_subdirectory(studio) add_subdirectory(studio)
endif() endif()

View File

@ -7,6 +7,7 @@
*/ */
#include <ox/fs/fs.hpp> #include <ox/fs/fs.hpp>
#include <ox/mc/mc.hpp>
#include <ox/std/std.hpp> #include <ox/std/std.hpp>
#include "../media.hpp" #include "../media.hpp"
@ -24,8 +25,8 @@ using namespace ox;
#define TILE_ADDR ((CharBlock*) 0x06000000) #define TILE_ADDR ((CharBlock*) 0x06000000)
#define TILE8_ADDR ((CharBlock8*) 0x06000000) #define TILE8_ADDR ((CharBlock8*) 0x06000000)
const auto GBA_TILE_COLUMNS = 32; constexpr auto GBA_TILE_COLUMNS = 32;
const auto GBA_TILE_ROWS = 32; constexpr auto GBA_TILE_ROWS = 32;
// map ASCII values to the nostalgia charset // map ASCII values to the nostalgia charset
static char charMap[128] = { static char charMap[128] = {
@ -159,22 +160,22 @@ static char charMap[128] = {
}; };
struct GbaPaletteTarget { struct GbaPaletteTarget {
uint16_t *palette = nullptr; volatile uint16_t *palette = nullptr;
}; };
struct GbaTileMapTarget { struct GbaTileMapTarget {
static constexpr auto Fields = 4; static constexpr auto Fields = 4;
uint8_t bpp = 0; volatile uint32_t *bgCtl = nullptr;
ox::FileAddress defaultPalette; ox::FileAddress defaultPalette;
GbaPaletteTarget pal; GbaPaletteTarget pal;
uint16_t *tileMap = nullptr; volatile uint16_t *tileMap = nullptr;
}; };
template<typename T> template<typename T>
ox::Error modelRead(T *io, GbaPaletteTarget *t) { ox::Error modelRead(T *io, GbaPaletteTarget *t) {
io->setTypeInfo("nostalgia::core::NostalgiaPalette", NostalgiaPalette::Fields); io->setTypeInfo("nostalgia::core::NostalgiaPalette", NostalgiaPalette::Fields);
oxReturnError(io->template field<uint16_t>("colors", [t](auto i, uint16_t c) { oxReturnError(io->template field<Color>("colors", [t](auto i, Color *c) {
t->palette[i] = c; t->palette[i] = *c;
return OxError(0); return OxError(0);
})); }));
return OxError(0); return OxError(0);
@ -183,20 +184,27 @@ ox::Error modelRead(T *io, GbaPaletteTarget *t) {
template<typename T> template<typename T>
ox::Error modelRead(T *io, GbaTileMapTarget *t) { ox::Error modelRead(T *io, GbaTileMapTarget *t) {
io->setTypeInfo("nostalgia::core::NostalgiaGraphic", NostalgiaGraphic::Fields); 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("defaultPalette", &t->defaultPalette));
oxReturnError(io->field("pal", &t->pal)); oxReturnError(io->field("pal", &t->pal));
uint16_t intermediate; uint16_t intermediate = 0;
oxReturnError(io->template field<uint8_t>("tileMap", [t, &intermediate](auto i, uint8_t tile) { auto err = io->template field<uint8_t>("tileMap", [t, &intermediate](auto i, uint8_t *tile) {
if (i & 1) { // i is odd if (i & 1) { // i is odd
intermediate |= tile >> 8; intermediate |= static_cast<uint16_t>(*tile) << 8;
t->tileMap[i / 2] = intermediate; t->tileMap[i / 2] = intermediate;
} else { // i is even } else { // i is even
intermediate = tile & 0xff; intermediate = *tile & 0x00ff;
} }
return OxError(0); return OxError(0);
})); });
return OxError(0); return OxError(err);
} }
ox::Error initGfx(Context*) { ox::Error initGfx(Context*) {
@ -214,75 +222,58 @@ ox::Error shutdownGfx() {
return OxError(0); return OxError(0);
} }
// Do NOT use Context in the GBA version of this function. [[nodiscard]] constexpr volatile uint32_t &bgCtl(int bg) noexcept {
ox::Error initConsole(Context*) { switch (bg) {
const auto PaletteStart = sizeof(GbaImageDataHeader); case 0:
ox::Error err(0); return REG_BG0CNT;
ox::FileStore32 fs(loadRom(), 32 * ox::units::MB); case 1:
ox::FileSystem32 fileSystem(fs); return REG_BG1CNT;
const auto CharsetInode = fileSystem.stat("/TileSheets/Charset.ng").value.inode; case 2:
return REG_BG2CNT;
GbaImageDataHeader imgData; case 3:
return REG_BG3CNT;
REG_BG0CNT = (28 << 8) | 1; default:
if (fs.valid()) { panic("Looking up non-existent register");
// load the header return REG_BG0CNT;
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);
} }
return err;
} }
ox::Error loadTileSheet(Context*, // Do NOT rely on Context in the GBA version of this function.
TileSheetSpace, ox::Error initConsole(Context *ctx) {
int, constexpr auto TilesheetAddr = "/TileSheets/Charset.ng";
ox::FileAddress tilesheetPath, constexpr auto PaletteAddr = "/Palettes/Charset.npal";
ox::FileAddress) { if (!ctx) {
auto inode = tilesheetPath.getInode().value; ctx = new (ox_alloca(sizeof(Context))) Context();
ox::Error err(0); ox::FileStore32 fs(loadRom(), 32 * ox::units::MB);
constexpr auto PaletteStart = sizeof(GbaImageDataHeader); ctx->rom = new (ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs);
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);
} }
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. // Do NOT use Context in the GBA version of this function.

View File

@ -1,4 +1,3 @@
add_executable( add_executable(
nostalgia nostalgia
main.cpp main.cpp

View File

@ -16,11 +16,13 @@ using namespace nostalgia::world;
ox::Error run(ox::FileSystem *fs) { ox::Error run(ox::FileSystem *fs) {
Context ctx; Context ctx;
oxReturnError(init(&ctx));
ctx.rom = fs; ctx.rom = fs;
Zone zone; oxReturnError(init(&ctx));
oxReturnError(zone.init(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/Charset.ng", "/Palettes/Charset.npal")); //Zone zone;
zone.draw(&ctx); //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(run());
oxReturnError(shutdownGfx()); oxReturnError(shutdownGfx());
return OxError(0); return OxError(0);
@ -28,9 +30,14 @@ ox::Error run(ox::FileSystem *fs) {
#ifndef OX_USE_STDLIB #ifndef OX_USE_STDLIB
extern "C" void _start() { int main() {
ox::FileSystem32 fs(ox::FileStore32(loadRom(), 32 * ox::units::MB)); auto rom = loadRom();
if (!rom) {
return 1;
}
ox::FileSystem32 fs(ox::FileStore32(rom, 32 * ox::units::MB));
run(&fs); run(&fs);
return 0;
} }
#else #else

View File

@ -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: _initCpp:
@ disable interrupts // copy data
@ cpsid if 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

View File

@ -1,6 +1,3 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
add_executable(nost-pack pack.cpp) add_executable(nost-pack pack.cpp)
target_link_libraries( target_link_libraries(

View File

@ -66,21 +66,21 @@ using namespace nostalgia::common;
oxReturnError(ox::FileSystem32::format(buff.data(), buff.size())); oxReturnError(ox::FileSystem32::format(buff.data(), buff.size()));
ox::PassThroughFS src(argSrc.c_str()); ox::PassThroughFS src(argSrc.c_str());
ox::FileSystem32 dst(ox::FileStore32(buff.data(), buff.size())); ox::FileSystem32 dst(ox::FileStore32(buff.data(), buff.size()));
auto err = nostalgia::pack(&src, &dst); oxReturnError(nostalgia::pack(&src, &dst));
if (err) {
std::cerr << "pack failed...";
}
oxReturnError(dst.resize()); oxReturnError(dst.resize());
std::cout << "new size: " << dst.size() << '\n'; std::cout << "new size: " << dst.size() << '\n';
buff.resize(dst.size()); buff.resize(dst.size());
oxReturnError(writeFileBuff(argDst, buff)); oxReturnError(writeFileBuff(argDst, buff));
return err; return OxError(0);
} }
int main(int argc, const char **args) { int main(int argc, const char **args) {
auto err = run(ClArgs(argc, args)); auto err = run(ClArgs(argc, args));
oxAssert(err, "pack failed"); oxAssert(err, "pack failed");
if (err) {
std::cerr << "pack failed...\n";
}
return static_cast<int>(err); return static_cast<int>(err);
} }

View File

@ -39,6 +39,7 @@ namespace {
// transformations need to be done after the copy to the new FS is complete // transformations need to be done after the copy to the new FS is complete
[[nodiscard]] ox::Error transformClaw(ox::FileSystem32 *dest, std::string path) { [[nodiscard]] ox::Error transformClaw(ox::FileSystem32 *dest, std::string path) {
// copy // copy
oxTrace("pack::transformClaw") << "path:" << path.c_str();
return dest->ls(path.c_str(), [dest, path](const char *name, ox::InodeId_t) { return dest->ls(path.c_str(), [dest, path](const char *name, ox::InodeId_t) {
auto [stat, err] = dest->stat(path.c_str()); auto [stat, err] = dest->stat(path.c_str());
oxReturnError(err); oxReturnError(err);
@ -68,10 +69,16 @@ namespace {
return OxError(buff == expected ? 0 : 1); 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) { [[nodiscard]] ox::Error copy(ox::PassThroughFS *src, ox::FileSystem32 *dest, std::string path) {
std::cout << "copying directory: " << path << '\n'; std::cout << "copying directory: " << path << '\n';
std::vector<VerificationPair> verficationPairs;
// copy // 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'; std::cout << "reading " << name << '\n';
auto currentFile = path + name; auto currentFile = path + name;
auto [stat, err] = src->stat((currentFile).c_str()); auto [stat, err] = src->stat((currentFile).c_str());
@ -90,16 +97,26 @@ namespace {
std::cout << "writing " << currentFile << '\n'; std::cout << "writing " << currentFile << '\n';
oxReturnError(dest->write(currentFile.c_str(), buff.data(), buff.size())); oxReturnError(dest->write(currentFile.c_str(), buff.data(), buff.size()));
oxReturnError(verifyFile(dest, currentFile, buff)); oxReturnError(verifyFile(dest, currentFile, buff));
verficationPairs.push_back({currentFile, buff});
} }
return OxError(0); 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) { return OxError(0);
oxReturnError(copy(src, dest, path)); }
oxReturnError(transformClaw(dest, path));
}
[[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); return OxError(0);
} }

View File

@ -12,6 +12,6 @@
namespace nostalgia { namespace nostalgia {
ox::Error pack(ox::PassThroughFS *src, ox::FileSystem32 *dest, std::string path = "/"); ox::Error pack(ox::PassThroughFS *src, ox::FileSystem32 *dest);
} }