diff --git a/src/nostalgia/core/core.hpp b/src/nostalgia/core/core.hpp index e6fd94ae..7eb2ee2f 100644 --- a/src/nostalgia/core/core.hpp +++ b/src/nostalgia/core/core.hpp @@ -17,8 +17,8 @@ namespace nostalgia::core { -ox::Error init(Context *ctx); +[[nodiscard]] ox::Error init(Context *ctx); -ox::Error run(); +[[nodiscard]] ox::Error run(); } diff --git a/src/nostalgia/core/gfx.hpp b/src/nostalgia/core/gfx.hpp index c9d99b14..4075e17b 100644 --- a/src/nostalgia/core/gfx.hpp +++ b/src/nostalgia/core/gfx.hpp @@ -15,6 +15,11 @@ namespace nostalgia::core { +enum class TileSheetSpace { + Background, + Sprite +}; + using Color = uint16_t; struct NostalgiaPalette { @@ -26,7 +31,7 @@ struct NostalgiaGraphic { static constexpr auto Fields = 4; uint8_t bpp = 0; ox::FileAddress defaultPalette; - ox::Vector pal; + NostalgiaPalette pal; ox::Vector tiles; }; @@ -47,9 +52,16 @@ ox::Error model(T *io, NostalgiaPalette *pal) { return OxError(0); } -ox::Error initGfx(Context *ctx); +[[nodiscard]] ox::Error initGfx(Context *ctx); -ox::Error initConsole(Context *ctx); +[[nodiscard]] ox::Error shutdownGfx(); + +[[nodiscard]] ox::Error initConsole(Context *ctx); + +/** + * @param section describes which section of the selected TileSheetSpace to use (e.g. MEM_PALLETE_BG[section]) + */ +[[nodiscard]] ox::Error loadTileSheet(Context *ctx, TileSheetSpace tss, int section, ox::FileAddress tilesheet, ox::FileAddress palette = nullptr); ox::Error loadTileSheet(Context *ctx, ox::FileAddress file); diff --git a/src/nostalgia/core/sdl/core.cpp b/src/nostalgia/core/sdl/core.cpp index ccbe48a6..46f247ec 100644 --- a/src/nostalgia/core/sdl/core.cpp +++ b/src/nostalgia/core/sdl/core.cpp @@ -12,6 +12,8 @@ namespace nostalgia::core { +void draw(); + ox::Error run() { for (auto running = true; running;) { SDL_Event event; @@ -28,7 +30,7 @@ ox::Error run() { } } } - SDL_Delay(1); + draw(); } return OxError(0); } diff --git a/src/nostalgia/core/sdl/gfx.cpp b/src/nostalgia/core/sdl/gfx.cpp index 1650b565..b5ad8a23 100644 --- a/src/nostalgia/core/sdl/gfx.cpp +++ b/src/nostalgia/core/sdl/gfx.cpp @@ -6,25 +6,153 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include + #include -#include "../gfx.hpp" +#include + +#include namespace nostalgia::core { static SDL_Window *window = nullptr; +static SDL_Renderer *renderer = nullptr; + +static std::array bgTextures; +static std::vector> bgTileMaps(128, std::vector(128, 0)); + +[[nodiscard]] static ox::ValErr> readFile(Context *ctx, const ox::FileAddress &file) { + auto [stat, err] = ctx->rom->stat(file); + oxReturnError(err); + ox::Vector buff(stat.size); + oxReturnError(ctx->rom->read(file, buff.data(), buff.size())); + return buff; +} + +template +[[nodiscard]] ox::ValErr readMC(Context *ctx, const ox::FileAddress &file) { + auto [buff, err] = readFile(ctx, file); + oxReturnError(err); + T t; + oxReturnError(ox::readMC(buff.data(), buff.size(), &t)); + return t; +} ox::Error initGfx(Context*) { window = SDL_CreateWindow("nostalgia", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 768, SDL_WINDOW_SHOWN); + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); return OxError(window == nullptr); } +ox::Error shutdownGfx() { + for (auto tex : bgTextures) { + SDL_DestroyTexture(tex); + } + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + return OxError(0); +} + ox::Error initConsole(Context*) { return OxError(1); } -ox::Error loadTileSheet(Context*, ox::FileAddress) { - return OxError(1); +SDL_Color createSDL_Color(Color nc) { + SDL_Color c; + // extract the color chanels and scale them up for a 24 bit color + c.r = ((nc & 0b0000000000011111) >> 0) * 8; + c.g = ((nc & 0b0000001111100000) >> 5) * 8; + c.b = ((nc & 0b0111110000000000) >> 10) * 8; + c.a = 1; + return c; +} + +SDL_Palette *createSDL_Palette(const NostalgiaPalette &npal) { + auto pal = SDL_AllocPalette(npal.colors.size()); + for (std::size_t i = 0; i < npal.colors.size(); ++i) { + pal->colors[i] = createSDL_Color(npal.colors[i]); + } + return pal; +} + +ox::Error loadTileSheet(Context *ctx, + TileSheetSpace tss, + int section, + ox::FileAddress tilesheetPath, + ox::FileAddress palettePath) { + auto [tilesheet, tserr] = readMC(ctx, tilesheetPath); + oxReturnError(tserr); + NostalgiaPalette palette; + if (palettePath) { + oxReturnError(readMC(ctx, palettePath).get(&palette)); + } else { + palette = tilesheet.pal; + } + + const auto bytesPerTile = tilesheet.bpp == 8 ? 64 : 32; + const auto tiles = tilesheet.tiles.size() / bytesPerTile; + const int width = 8; + const int height = 8 * tiles; + const auto format = SDL_PIXELFORMAT_INDEX8; + auto surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 16, format); + auto sdlPalette = createSDL_Palette(palette); + SDL_SetSurfacePalette(surface, sdlPalette); + if (bytesPerTile == 1) { + SDL_memcpy(surface->pixels, tilesheet.tiles.data(), bytesPerTile * tiles); + } else { + for (std::size_t i = 0; i < tilesheet.tiles.size(); ++i) { + static_cast(surface->pixels)[i * 2 + 0] = tilesheet.tiles[i] & 0xF; + static_cast(surface->pixels)[i * 2 + 1] = tilesheet.tiles[i] >> 4; + } + } + + auto texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_FreeSurface(surface); + SDL_FreePalette(sdlPalette); + + if (tss == TileSheetSpace::Background) { + if (bgTextures[section]) { + SDL_DestroyTexture(bgTextures[section]); + } + bgTextures[section] = texture; + } + + return OxError(0); +} + +void drawBackground(std::size_t bg) { + //oxTrace("nostalgia::core::drawBackground") << "Drawing background"; + SDL_Rect src = {}, dst = {}; + src.x = 0; + src.w = 8; + src.h = 8; + dst.x = 0; + dst.y = 0; + dst.w = 64; + dst.h = 64; + const auto tex = bgTextures[bg]; + if (tex) { + for (auto &m : bgTileMaps) { + for (auto t : m) { + src.y = t * 8; + SDL_RenderCopy(renderer, tex, &src, &dst); + dst.y += 64; + } + dst.x += 64; + dst.y = 0; + } + } +} + +void draw() { + SDL_RenderClear(renderer); + drawBackground(0); + drawBackground(1); + drawBackground(2); + drawBackground(3); + SDL_RenderPresent(renderer); } void puts(Context*, int, const char*) { diff --git a/src/nostalgia/core/studio/imgconv.cpp b/src/nostalgia/core/studio/imgconv.cpp index 86636bb7..2bcec80b 100644 --- a/src/nostalgia/core/studio/imgconv.cpp +++ b/src/nostalgia/core/studio/imgconv.cpp @@ -75,7 +75,7 @@ namespace { QMap colors; auto ng = std::make_unique(); - ng->pal.resize(countColors(src, argTiles)); + ng->pal.colors.resize(countColors(src, argTiles)); if (argBpp == 4) { ng->tiles.resize(Pixels / 2); } else { @@ -112,7 +112,7 @@ namespace { // store colors in palette with the corresponding color id for (auto key : colors.keys()) { auto colorId = colors[key]; - ng->pal[colorId] = toGbaColor(key); + ng->pal.colors[colorId] = toGbaColor(key); } return ng; diff --git a/src/nostalgia/core/studio/import_tilesheet_wizard.cpp b/src/nostalgia/core/studio/import_tilesheet_wizard.cpp index bac5f724..20228429 100644 --- a/src/nostalgia/core/studio/import_tilesheet_wizard.cpp +++ b/src/nostalgia/core/studio/import_tilesheet_wizard.cpp @@ -73,7 +73,7 @@ int ImportTilesheetWizardPalettePage::accept() { if (palette != PaletteOptions.indexOf(PaletteOption_Bundle)) { const auto outPath = PaletteDir + paletteName + FileExt_npal; core::NostalgiaPalette pal; - pal.colors = std::move(ng->pal); + pal = std::move(ng->pal); auto [buff, err] = toBuffer(&pal); oxReturnError(err); oxReturnError(m_ctx->project->write(outPath, buff.data(), buff.size())); diff --git a/src/nostalgia/player/main.cpp b/src/nostalgia/player/main.cpp index dc10ff1d..5d864446 100644 --- a/src/nostalgia/player/main.cpp +++ b/src/nostalgia/player/main.cpp @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include #include #include @@ -13,14 +14,16 @@ using namespace nostalgia::common; using namespace nostalgia::core; using namespace nostalgia::world; -int run(ox::FileSystem *fs) { +ox::Error run(ox::FileSystem *fs) { Context ctx; - init(&ctx); + oxReturnError(init(&ctx)); ctx.rom = fs; - Zone zone(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/GeneralWorld"); + Zone zone; + oxReturnError(zone.init(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/Charset.ng", "/Palettes/Charset.npal")); zone.draw(&ctx); - run(); - return 0; + oxReturnError(run()); + oxReturnError(shutdownGfx()); + return OxError(0); } #ifndef OX_USE_STDLIB @@ -32,8 +35,14 @@ extern "C" void _start() { #else -int main() { - return run(nullptr); +int main(int argc, const char **argv) { + if (argc > 1) { + ox::PassThroughFS fs(argv[1]); + auto err = run(&fs); + oxAssert(err, "Something went wrong..."); + return err; + } + return 2; } #endif diff --git a/src/nostalgia/world/world.cpp b/src/nostalgia/world/world.cpp index d278123f..93bdea81 100644 --- a/src/nostalgia/world/world.cpp +++ b/src/nostalgia/world/world.cpp @@ -13,11 +13,15 @@ namespace nostalgia::world { using namespace common; using namespace core; -Zone::Zone(Context *ctx, Bounds bnds, ox::FileAddress tileSheet) { +ox::Error Zone::init(Context *ctx, Bounds bnds, ox::FileAddress tileSheet, ox::FileAddress palette) { const auto size = bnds.width * bnds.height; m_tiles = new Tile[size]; m_bounds = bnds; - core::loadTileSheet(ctx, tileSheet); + return core::loadTileSheet(ctx, core::TileSheetSpace::Background, 0, tileSheet, palette); +} + +Zone::~Zone() { + delete[] m_tiles; } void Zone::draw(Context *ctx) { diff --git a/src/nostalgia/world/world.hpp b/src/nostalgia/world/world.hpp index 0cdc7792..646efa37 100644 --- a/src/nostalgia/world/world.hpp +++ b/src/nostalgia/world/world.hpp @@ -47,7 +47,11 @@ struct Zone { Tile *m_tiles = nullptr; public: - Zone(core::Context *ctx, common::Bounds bnds, ox::FileAddress tileSheet); + Zone() = default; + + ~Zone(); + + [[nodiscard]] ox::Error init(core::Context *ctx, common::Bounds bnds, ox::FileAddress tileSheet, ox::FileAddress palette = {}); void draw(core::Context *ctx);