[nostalgia/core] Get TileSheetSet working
This commit is contained in:
parent
ef6e3af735
commit
fcf6f00797
@ -23,8 +23,9 @@ install(
|
||||
include/nostalgia/modules
|
||||
)
|
||||
|
||||
# Studio
|
||||
if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
||||
if(${OLYMPIC_BUILD_STUDIO})
|
||||
# Studio
|
||||
if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
||||
add_library(
|
||||
NostalgiaStudioModules STATIC
|
||||
studiomodules.cpp
|
||||
@ -41,6 +42,7 @@ if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
||||
DESTINATION
|
||||
include/nostalgia/modules
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(NostalgiaProfile INTERFACE)
|
||||
|
@ -1,4 +1,7 @@
|
||||
add_subdirectory(src)
|
||||
if(NOT TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
install(
|
||||
DIRECTORY
|
||||
|
@ -45,7 +45,11 @@ struct TileSheetSetEntrySection {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
int32_t begin = 0;
|
||||
int32_t size = 0;
|
||||
int32_t tiles = 0;
|
||||
[[nodiscard]]
|
||||
constexpr auto end() const noexcept {
|
||||
return begin + tiles - 1;
|
||||
}
|
||||
};
|
||||
|
||||
oxModelBegin(TileSheetSetEntrySection)
|
||||
@ -68,6 +72,7 @@ oxModelEnd()
|
||||
struct TileSheetSet {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSet";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
int bpp = 0;
|
||||
ox::Vector<TileSheetSetEntry> entries;
|
||||
};
|
||||
@ -101,6 +106,10 @@ ox::Error loadSpriteTileSheet(
|
||||
ox::FileAddress const&tilesheetAddr,
|
||||
bool loadDefaultPalette = false) noexcept;
|
||||
|
||||
ox::Error loadSpriteTileSheet(
|
||||
Context &ctx,
|
||||
TileSheetSet const&set) noexcept;
|
||||
|
||||
void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, uint8_t tile) noexcept;
|
||||
|
||||
void clearBg(Context &ctx, uint_t bgIdx) noexcept;
|
||||
|
@ -41,6 +41,7 @@ struct GbaTileMapTarget {
|
||||
// and only exist to communicate with the loading process
|
||||
size_t tileWriteIdx = 0;
|
||||
unsigned targetBpp = 0;
|
||||
TileSheetSetEntry const*setEntry = nullptr;
|
||||
};
|
||||
|
||||
constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaPaletteTarget> auto *t) noexcept {
|
||||
@ -63,7 +64,6 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t)
|
||||
oxReturnError(io->template setTypeInfo<CompactTileSheet>());
|
||||
oxReturnError(io->field("bpp", &t->bpp));
|
||||
oxReturnError(io->field("defaultPalette", &t->defaultPalette));
|
||||
uint16_t intermediate = 0;
|
||||
if (t->targetBpp == 0) {
|
||||
t->targetBpp = t->bpp;
|
||||
}
|
||||
@ -71,20 +71,51 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t)
|
||||
return OxError(1, "Cannot load an 8 BPP tilesheet into a 4 BPP CBB");
|
||||
}
|
||||
ox::Error out{};
|
||||
uint16_t intermediate = 0;
|
||||
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.
|
||||
// Consider building an array of bitmaps (Array<uint64_t, 4>)
|
||||
// so this can be done with a single bitmap look up.
|
||||
if (t->targetBpp == t->bpp) {
|
||||
const auto handleTileMap = [t, &intermediate](std::size_t i, uint8_t const*tile) {
|
||||
if (i & 1) { // i is odd
|
||||
TileSheetSetEntrySection const*section = &t->setEntry->sections[0];
|
||||
size_t sectionIdx = 1;
|
||||
size_t writeIt = 0;
|
||||
size_t const fourBpp = t->bpp == 4;
|
||||
const auto handleTileMap = [t, &intermediate, §ion, §ionIdx, &writeIt, fourBpp]
|
||||
(std::size_t i, uint8_t const *tile) {
|
||||
if (!section) {
|
||||
return ox::Error{};
|
||||
}
|
||||
auto const tileIdx = static_cast<int>((i * (2 * fourBpp)) / PixelsPerTile);
|
||||
if (tileIdx < section->begin) {
|
||||
return ox::Error{};
|
||||
}
|
||||
if (tileIdx > section->end()) {
|
||||
if (sectionIdx >= t->setEntry->sections.size()) {
|
||||
section = nullptr;
|
||||
return ox::Error{};
|
||||
}
|
||||
section = &t->setEntry->sections[sectionIdx];
|
||||
++sectionIdx;
|
||||
if (tileIdx < section->begin || tileIdx > section->end()) {
|
||||
return ox::Error{};
|
||||
}
|
||||
}
|
||||
if (writeIt & 1) { // writeIt is odd
|
||||
intermediate |= static_cast<uint16_t>(*tile) << 8;
|
||||
t->tileMap[i / 2] = intermediate;
|
||||
t->tileMap[writeIt / 2] = intermediate;
|
||||
++t->tileWriteIdx;
|
||||
} else { // i is even
|
||||
} else { // writeIt is even
|
||||
intermediate = *tile & 0x00ff;
|
||||
}
|
||||
++writeIt;
|
||||
return ox::Error{};
|
||||
};
|
||||
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
|
||||
} else if (t->targetBpp > t->bpp) { // 4 -> 8 bits
|
||||
const auto handleTileMap = [t, &intermediate](std::size_t i, uint8_t const*tile) {
|
||||
const auto handleTileMap = [t, &intermediate](std::size_t i, uint8_t const *tile) {
|
||||
if (i & 1) { // i is odd
|
||||
intermediate |= static_cast<uint16_t>(*tile) << 8;
|
||||
// write 4 pixels from intermediate
|
||||
@ -100,6 +131,18 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t)
|
||||
};
|
||||
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
|
||||
}
|
||||
} else {
|
||||
const auto handleTileMap = [t, &intermediate](std::size_t i, const uint8_t *tile) {
|
||||
if (i & 1) { // i is odd
|
||||
intermediate |= static_cast<uint16_t>(*tile) << 8;
|
||||
t->tileMap[i / 2] = intermediate;
|
||||
} else { // i is even
|
||||
intermediate = *tile & 0x00ff;
|
||||
}
|
||||
return OxError(0);
|
||||
};
|
||||
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -150,6 +193,7 @@ static ox::Error loadTileSheetSet(
|
||||
.bpp = bpp,
|
||||
.defaultPalette = {},
|
||||
.tileMap = tileMapTargetMem + tileWriteIdx,
|
||||
.setEntry = &entry,
|
||||
};
|
||||
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
|
||||
tileWriteIdx += target.tileWriteIdx;
|
||||
|
@ -513,10 +513,14 @@ static ox::Result<TileSheetData> buildSetTsd(
|
||||
for (auto const&entry : set.entries) {
|
||||
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, entry.tilesheet));
|
||||
oxRequire(tsd, normalizeTileSheet(*tilesheet));
|
||||
for (auto const p : tsd.pixels) {
|
||||
setTsd.pixels.push_back(p);
|
||||
for (auto const&s : entry.sections) {
|
||||
auto const size = s.tiles * PixelsPerTile;
|
||||
for (auto i = 0; i < size; ++i) {
|
||||
auto const srcIdx = static_cast<size_t>(i) + static_cast<size_t>(s.begin * PixelsPerTile);
|
||||
setTsd.pixels.push_back(tsd.pixels[srcIdx]);
|
||||
}
|
||||
setTsd.height += TileHeight * s.tiles;
|
||||
}
|
||||
setTsd.height += tsd.height;
|
||||
}
|
||||
return setTsd;
|
||||
}
|
||||
|
11
src/nostalgia/modules/core/test/CMakeLists.txt
Normal file
11
src/nostalgia/modules/core/test/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
add_executable(
|
||||
NostalgiaCoreTest
|
||||
tests.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
NostalgiaCoreTest
|
||||
NostalgiaCore
|
||||
)
|
||||
|
||||
add_test("[NostalgiaCore] readWriteTileSheet" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/NostalgiaCoreTest readWriteTileSheet)
|
38
src/nostalgia/modules/core/test/tests.cpp
Normal file
38
src/nostalgia/modules/core/test/tests.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
|
||||
#include <map>
|
||||
#include <ox/std/error.hpp>
|
||||
#include <ox/mc/mc.hpp>
|
||||
#include <nostalgia/core/core.hpp>
|
||||
|
||||
using namespace nostalgia;
|
||||
|
||||
static std::map<ox::StringView, ox::Error(*)()> tests = {
|
||||
{
|
||||
"readWriteTileSheet",
|
||||
[]() -> ox::Error {
|
||||
core::TileSheet in;
|
||||
oxRequire(buff, ox::writeMC(in));
|
||||
oxRequire(out, ox::readMC<core::TileSheet>(buff));
|
||||
oxAssert(in.subsheet.name == out.subsheet.name, "subsheet.name serialization broken");
|
||||
return {};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
int main(int argc, const char **args) {
|
||||
int retval = -1;
|
||||
if (argc > 0) {
|
||||
auto const testName = ox::StringView(args[1]);
|
||||
if (tests.find(testName) != tests.end()) {
|
||||
retval = static_cast<int>(tests[testName]());
|
||||
} else {
|
||||
retval = 1;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
@ -76,9 +76,47 @@ static ox::Error runTest(turbine::Context &tctx) {
|
||||
core::puts(*cctx, 10, 9, "DOPENESS!!!");
|
||||
turbine::setUpdateHandler(tctx, testUpdateHandler);
|
||||
turbine::setKeyEventHandler(tctx, testKeyEventHandler);
|
||||
//core::setBgStatus(*cctx, 1, true);
|
||||
//core::setBgStatus(*cctx, 2, true);
|
||||
//core::setBgStatus(*cctx, 3, true);
|
||||
return turbine::run(tctx);
|
||||
}
|
||||
|
||||
|
||||
[[maybe_unused]]
|
||||
static ox::Error runTileSheetSetTest(turbine::Context &tctx) {
|
||||
// this should make the screen display 'ABCDB'
|
||||
constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal");
|
||||
oxRequireM(cctx, core::init(tctx));
|
||||
turbine::setApplicationData(tctx, cctx.get());
|
||||
oxRequire(tsStat, turbine::rom(tctx)->stat(PaletteAddr));
|
||||
core::TileSheetSet const set{
|
||||
.bpp = 4,
|
||||
.entries = {
|
||||
{ .tilesheet = ox::StringLiteral("/TileSheets/Chester.ng"), .sections{{.begin = 0, .tiles = 1}} },
|
||||
{ .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 0, .tiles = 2}} },
|
||||
{ .tilesheet = ox::StringLiteral("/TileSheets/CD.ng"), .sections{{.begin = 0, .tiles = 2}} },
|
||||
{ .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 1, .tiles = 1}} },
|
||||
},
|
||||
};
|
||||
oxReturnError(core::loadBgTileSheet(*cctx, 0, set));
|
||||
oxReturnError(core::loadSpriteTileSheet(*cctx, set));
|
||||
oxReturnError(core::loadBgPalette(*cctx, PaletteAddr));
|
||||
oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr));
|
||||
core::setBgStatus(*cctx, 0, true);
|
||||
core::setBgTile(*cctx, 0, 10, 9, 1);
|
||||
core::setBgTile(*cctx, 0, 11, 9, 2);
|
||||
core::setBgTile(*cctx, 0, 13, 9, 4);
|
||||
core::setSprite(*cctx, 16, {
|
||||
.enabled = true,
|
||||
.x = 12 * 8,
|
||||
.y = 9 * 8,
|
||||
.tileIdx = 3,
|
||||
});
|
||||
core::setSprite(*cctx, 17, {
|
||||
.enabled = true,
|
||||
.x = 14 * 8,
|
||||
.y = 9 * 8,
|
||||
.tileIdx = 5,
|
||||
});
|
||||
turbine::setKeyEventHandler(tctx, testKeyEventHandler);
|
||||
return turbine::run(tctx);
|
||||
}
|
||||
|
||||
@ -116,5 +154,5 @@ static ox::Error runScene(turbine::Context &tctx) {
|
||||
|
||||
ox::Error run(ox::UniquePtr<ox::FileSystem> &&fs) noexcept {
|
||||
oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
|
||||
return runTest(*tctx);
|
||||
return runTileSheetSetTest(*tctx);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user