diff --git a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp index 37974d61..0e104b25 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp @@ -11,6 +11,7 @@ #include "context.hpp" #include "palette.hpp" +#include "tilesheet.hpp" namespace nostalgia::core { @@ -134,12 +135,23 @@ ox::Error loadBgTileSheet( unsigned cbb, TileSheetSet const&set) noexcept; +ox::Error loadBgTileSheet( + Context &ctx, + unsigned cbb, + CompactTileSheet const&ts, + ox::Optional const&paletteBank) noexcept; + ox::Error loadBgTileSheet( Context &ctx, unsigned cbb, ox::FileAddress const&tilesheetAddr, ox::Optional const&paletteBank = {}) noexcept; +ox::Error loadSpriteTileSheet( + Context &ctx, + CompactTileSheet const&ts, + bool loadDefaultPalette) noexcept; + ox::Error loadSpriteTileSheet( Context &ctx, ox::FileAddress const&tilesheetAddr, diff --git a/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp b/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp index cf7998c7..a1012dc5 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp @@ -296,9 +296,10 @@ using TileSheetV4 = TileSheet; struct CompactTileSheetV1 { static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet"; static constexpr auto TypeVersion = 1; + static constexpr auto Preloadable = true; int8_t bpp = 0; ox::FileAddress defaultPalette; - ox::Vector pixels = {}; + ox::Vector pixels; }; using CompactTileSheet = CompactTileSheetV1; diff --git a/src/nostalgia/modules/core/src/gba/gfx.cpp b/src/nostalgia/modules/core/src/gba/gfx.cpp index fb738660..553cbaba 100644 --- a/src/nostalgia/modules/core/src/gba/gfx.cpp +++ b/src/nostalgia/modules/core/src/gba/gfx.cpp @@ -24,104 +24,6 @@ namespace nostalgia::core { constexpr auto SpriteCount = 128; -struct GbaTileMapTarget { - static constexpr auto TypeName = CompactTileSheet::TypeName; - static constexpr auto TypeVersion = CompactTileSheet::TypeVersion; - unsigned &bpp; - ox::FileAddress defaultPalette; - volatile uint16_t *tileMap = nullptr; - // the following values are not actually in CompactTileSheet, - // and only exist to communicate with the loading process - size_t tileWriteIdx = 0; - unsigned targetBpp = 0; - TileSheetSetEntry const*setEntry = nullptr; -}; - -[[nodiscard]] -static bool loadPixel(TileSheetSetEntry const&setEntry, size_t §ionIdx, int tileIdx) noexcept { - if (setEntry.sections.size() <= sectionIdx) { - return false; - } - auto §ion = setEntry.sections[sectionIdx]; - if (tileIdx < section.begin) { - return false; - } - if (tileIdx > section.end()) { - if (sectionIdx >= setEntry.sections.size()) { - return false; - } - ++sectionIdx; - return tileIdx > section.begin && tileIdx <= section.end(); - } - return true; -} - -constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) noexcept { - oxReturnError(io->template setTypeInfo()); - oxReturnError(io->field("bpp", &t->bpp)); - oxReturnError(io->field("defaultPalette", &t->defaultPalette)); - if (t->targetBpp == 0) { - t->targetBpp = t->bpp; - } - if (t->targetBpp != t->bpp && t->bpp == 8) { - return OxError(1, "Cannot load an 8 BPP tilesheet into a 4 BPP CBB"); - } - ox::Error out{}; - if (t->setEntry) { - // The following code is atrocious, but it works. - // It might be possible to clean it up a little, but it probably - // cannot be seriously optimized without preloading TileSheets. - size_t sectionIdx = 0; - if (t->targetBpp == t->bpp) { - uint16_t intermediate = 0; - size_t const fourBpp = t->bpp == 4; - const auto handleTileMap = [t, &intermediate, §ionIdx, fourBpp] - (std::size_t i, uint8_t const*tile) { - auto const tileIdx = static_cast((i * (2 * fourBpp)) / PixelsPerTile); - if (!loadPixel(*t->setEntry, sectionIdx, tileIdx)) { - return ox::Error{}; - } - if (i & 1) { // i is odd - intermediate |= static_cast(*tile) << 8; - t->tileMap[t->tileWriteIdx] = intermediate; - ++t->tileWriteIdx; - } else { // i is even - intermediate = *tile & 0x00ff; - } - return ox::Error{}; - }; - out = io->template field("tileMap", handleTileMap); - } else if (t->targetBpp > t->bpp) { // 4 -> 8 bits - const auto handleTileMap = [t, §ionIdx](std::size_t i, uint8_t const*tile) { - auto constexpr BytesPerTile4Bpp = 32; - auto const tileIdx = static_cast(i / BytesPerTile4Bpp); - if (!loadPixel(*t->setEntry, sectionIdx, tileIdx)) { - return ox::Error{}; - } - uint16_t const px1 = *tile & 0xf; - uint16_t const px2 = *tile >> 4; - t->tileMap[t->tileWriteIdx] = static_cast(px1 | (px2 << 8)); - ++t->tileWriteIdx; - return ox::Error{}; - }; - out = io->template field("tileMap", handleTileMap); - } - } else { - uint16_t intermediate = 0; - const auto handleTileMap = [t, &intermediate](std::size_t i, const uint8_t*tile) { - if (i & 1) { // i is odd - intermediate |= static_cast(*tile) << 8; - t->tileMap[i / 2] = intermediate; - } else { // i is even - intermediate = *tile & 0x00ff; - } - return ox::Error{}; - }; - out = io->template field("tileMap", handleTileMap); - } - return out; -} - ox::Error initGfx(Context&, InitParams const&) noexcept { for (auto bgCtl = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) { teagba::bgSetSbb(*bgCtl, 28); @@ -181,21 +83,48 @@ static ox::Error loadTileSheetSet( Context &ctx, uint16_t *tileMapTargetMem, TileSheetSet const&set) noexcept { - auto &rom = ctx.rom(); size_t tileWriteIdx = 0; for (auto const&entry : set.entries) { - oxRequire(tsStat, rom.stat(entry.tilesheet)); - oxRequire(ts, rom.directAccess(entry.tilesheet)); - unsigned tilesheetBpp{}; - GbaTileMapTarget target{ - .bpp = tilesheetBpp, - .defaultPalette = {}, - .tileMap = tileMapTargetMem + tileWriteIdx, - .targetBpp = static_cast(set.bpp), - .setEntry = &entry, - }; - oxReturnError(ox::readMC({ts, static_cast(tsStat.size)}, target)); - tileWriteIdx += target.tileWriteIdx; + oxRequire(ts, keel::readObj(keelCtx(ctx), entry.tilesheet)); + if (set.bpp != ts->bpp && ts->bpp == 8) { + return OxError(1, "cannot load an 8 BPP tilesheet into a 4 BPP CBB"); + } + for (auto const&s : entry.sections) { + auto const cnt = (static_cast(s.tiles) * PixelsPerTile) / (1 + (set.bpp == 4)); + for (size_t i = 0; i < cnt; ++i) { + auto const srcIdx = static_cast(s.begin) + i * 2; + auto const v = static_cast( + static_cast(ts->pixels[srcIdx]) | + (static_cast(ts->pixels[srcIdx + 1]) << 8)); + tileMapTargetMem[tileWriteIdx + i] = v; + } + tileWriteIdx += cnt; + } + } + return {}; +} + +ox::Error loadBgTileSheet( + Context &ctx, + unsigned cbb, + CompactTileSheet const&ts, + ox::Optional const&paletteBank) noexcept { + auto const cnt = (ts.pixels.size() * PixelsPerTile) / (1 + (ts.bpp == 4)); + for (size_t i = 0; i < cnt; ++i) { + auto const srcIdx = i * 2; + auto const p1 = static_cast(ts.pixels[srcIdx]); + auto const p2 = static_cast(ts.pixels[srcIdx + 1]); + MEM_BG_TILES[cbb][i] = static_cast(p1 | (p2 << 8)); + } + // update bpp of all bgs with the updated cbb + auto const bpp = ctx.cbbData[cbb].bpp; + teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) { + if (teagba::bgCbb(bgCtl) == cbb) { + teagba::bgSetBpp(bgCtl, bpp); + } + }); + if (paletteBank.has_value() && ts.defaultPalette) { + oxReturnError(loadBgPalette(ctx, *paletteBank, ts.defaultPalette)); } return {}; } @@ -205,26 +134,8 @@ ox::Error loadBgTileSheet( unsigned cbb, ox::FileAddress const&tilesheetAddr, ox::Optional const&paletteBank) noexcept { - auto &rom = ctx.rom(); - oxRequire(tsStat, rom.stat(tilesheetAddr)); - oxRequire(ts, rom.directAccess(tilesheetAddr)); - GbaTileMapTarget target{ - .bpp = ctx.cbbData[cbb].bpp, - .defaultPalette = {}, - .tileMap = MEM_BG_TILES[cbb].data(), - }; - oxReturnError(ox::readMC({ts, static_cast(tsStat.size)}, target)); - // update bpp of all bgs with the updated cbb - const auto bpp = ctx.cbbData[cbb].bpp; - teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) { - if (teagba::bgCbb(bgCtl) == cbb) { - teagba::bgSetBpp(bgCtl, bpp); - } - }); - if (paletteBank.has_value() && target.defaultPalette) { - oxReturnError(loadBgPalette(ctx, *paletteBank, target.defaultPalette)); - } - return {}; + oxRequire(ts, keel::readObj(keelCtx(ctx), tilesheetAddr)); + return loadBgTileSheet(ctx, cbb, *ts, paletteBank); } ox::Error loadBgTileSheet( @@ -252,25 +163,28 @@ static void setSpritesBpp(unsigned const bpp) noexcept { } } +ox::Error loadSpriteTileSheet( + Context &ctx, + CompactTileSheet const&ts, + bool loadDefaultPalette) noexcept { + for (size_t i = 0; i < ts.pixels.size(); i += 2) { + uint16_t v = ts.pixels[i]; + v |= static_cast(ts.pixels[i + 1] << 8); + MEM_SPRITE_TILES[i] = v; + } + if (loadDefaultPalette && ts.defaultPalette) { + oxReturnError(loadSpritePalette(ctx, ts.defaultPalette)); + } + setSpritesBpp(static_cast(ts.bpp)); + return {}; +} + ox::Error loadSpriteTileSheet( Context &ctx, ox::FileAddress const&tilesheetAddr, bool loadDefaultPalette) noexcept { - auto &rom = ctx.rom(); - oxRequire(tsStat, rom.stat(tilesheetAddr)); - oxRequire(ts, rom.directAccess(tilesheetAddr)); - unsigned bpp{}; - GbaTileMapTarget target{ - .bpp = bpp, - .defaultPalette = {}, - .tileMap = MEM_SPRITE_TILES, - }; - oxReturnError(ox::readMC({ts, static_cast(tsStat.size)}, target)); - if (loadDefaultPalette && target.defaultPalette) { - oxReturnError(loadSpritePalette(ctx, target.defaultPalette)); - } - setSpritesBpp(bpp); - return {}; + oxRequire(ts, keel::readObj(keelCtx(ctx), tilesheetAddr)); + return loadSpriteTileSheet(ctx, *ts, loadDefaultPalette); } ox::Error loadSpriteTileSheet( diff --git a/src/nostalgia/modules/core/src/opengl/gfx.cpp b/src/nostalgia/modules/core/src/opengl/gfx.cpp index 19f77a4a..e5dee25c 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.cpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.cpp @@ -572,20 +572,28 @@ static ox::Result buildSetTsd( return setTsd; } +ox::Error loadBgTileSheet( + Context &ctx, + uint_t cbb, + CompactTileSheet const&ts, + ox::Optional const&paletteBank) noexcept { + oxRequire(tsd, normalizeTileSheet(ts)); + oxTracef("nostalgia.core.gfx.gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbb, tsd.width, tsd.height); + ctx.cbbs[cbb].tex = renderer::createTexture(tsd.width, tsd.height, tsd.pixels.data()); + if (paletteBank.has_value() && ts.defaultPalette) { + oxReturnError(loadBgPalette(ctx, *paletteBank, ts.defaultPalette)); + } + return {}; +} + ox::Error loadBgTileSheet( Context &ctx, uint_t cbb, ox::FileAddress const&tilesheetAddr, ox::Optional const&paletteBank) noexcept { auto &kctx = keelCtx(ctx.turbineCtx); - oxRequire(tilesheet, readObj(kctx, tilesheetAddr)); - oxRequire(tsd, normalizeTileSheet(*tilesheet)); - oxTracef("nostalgia.core.gfx.gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbb, tsd.width, tsd.height); - ctx.cbbs[cbb].tex = renderer::createTexture(tsd.width, tsd.height, tsd.pixels.data()); - if (paletteBank.has_value() && tilesheet->defaultPalette) { - oxReturnError(loadBgPalette(ctx, *paletteBank, tilesheet->defaultPalette)); - } - return {}; + oxRequire(ts, readObj(kctx, tilesheetAddr)); + return loadBgTileSheet(ctx, cbb, *ts, paletteBank); } ox::Error loadBgTileSheet( @@ -599,19 +607,26 @@ ox::Error loadBgTileSheet( ox::Error loadSpriteTileSheet( Context &ctx, - ox::FileAddress const&tilesheetAddr, + CompactTileSheet const&ts, bool loadDefaultPalette) noexcept { - auto &kctx = keelCtx(ctx.turbineCtx); - oxRequire(tilesheet, readObj(kctx, tilesheetAddr)); - oxRequire(tsd, normalizeTileSheet(*tilesheet)); + oxRequire(tsd, normalizeTileSheet(ts)); oxTracef("nostalgia.core.gfx.gl", "loadSpriteTexture: { w: {}, h: {} }", tsd.width, tsd.height); ctx.spriteBlocks.tex = renderer::createTexture(tsd.width, tsd.height, tsd.pixels.data()); if (loadDefaultPalette) { - oxReturnError(loadSpritePalette(ctx, tilesheet->defaultPalette)); + oxReturnError(loadSpritePalette(ctx, ts.defaultPalette)); } return {}; } +ox::Error loadSpriteTileSheet( + Context &ctx, + ox::FileAddress const&tilesheetAddr, + bool loadDefaultPalette) noexcept { + auto &kctx = keelCtx(ctx.turbineCtx); + oxRequire(ts, readObj(kctx, tilesheetAddr)); + return loadSpriteTileSheet(ctx, *ts, loadDefaultPalette); +} + ox::Error loadSpriteTileSheet( Context &ctx, TileSheetSet const&set) noexcept {