/* * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include "media.hpp" #ifndef OX_BARE_METAL #include #include namespace keel { ox::Result loadRom(ox::CRStringView path) noexcept { std::ifstream file(std::string(toStdStringView(path)), std::ios::binary | std::ios::ate); if (!file.good()) { oxErrorf("Could not find ROM file: {}", path); return OxError(1, "Could not find ROM file"); } try { const auto size = file.tellg(); file.seekg(0, std::ios::beg); auto buff = new char[static_cast(size)]; file.read(buff, size); return buff; } catch (const std::ios_base::failure &e) { oxErrorf("Could not read ROM file: {}", e.what()); return OxError(2, "Could not read ROM file"); } } void unloadRom(char *rom) noexcept { ox::safeDelete(rom); } ox::Result findPreloadSection() noexcept { return OxError(1, "findPreloadSection is unsupported on this platform"); } static void clearUuidMap(Context *ctx) noexcept { ctx->uuidToPath.clear(); ctx->pathToUuid.clear(); } void createUuidMapping(Context *ctx, const ox::String &filePath, const ox::UUID &uuid) noexcept { ctx->pathToUuid[filePath] = uuid; ctx->uuidToPath[uuid.toString()] = filePath; } static ox::Error buildUuidMap(Context *ctx, ox::CRStringView path) noexcept { oxRequire(files, ctx->rom->ls(path)); for (const auto &f : files) { oxRequire(filePath, ox::join("/", ox::Array{path, f})); oxRequire(stat, ctx->rom->stat(filePath)); if (stat.fileType == ox::FileType::NormalFile) { oxRequire(data, ctx->rom->read(filePath)); const auto [hdr, err] = readAssetHeader(data); if (!err) { createUuidMapping(ctx, filePath, hdr.uuid); } } else if (stat.fileType == ox::FileType::Directory) { if (!beginsWith(f, ".")) { oxReturnError(buildUuidMap(ctx, filePath)); } } } return {}; } ox::Error buildUuidMap(Context *ctx) noexcept { if (!ctx->rom) { return OxError(1, "No ROM FS"); } return buildUuidMap(ctx, ""); } } #else #include "context.hpp" #define MEM_ROM reinterpret_cast(0x0800'0000) namespace keel { static void clearUuidMap(Context*) noexcept { } ox::Error buildUuidMap(Context*) noexcept { return {}; } ox::Result loadRom(ox::CRStringView) noexcept { // put the header in the wrong order to prevent mistaking this code for the // media section constexpr auto headerP2 = "HEADER__________"; constexpr auto headerP1 = "NOSTALGIA_MEDIA_"; constexpr auto headerP1Len = ox_strlen(headerP2); constexpr auto headerP2Len = ox_strlen(headerP1); constexpr auto headerLen = headerP1Len + headerP2Len; for (auto current = MEM_ROM; current < reinterpret_cast(0x0a000000); current += headerLen) { if (ox_memcmp(current, headerP1, headerP1Len) == 0 && ox_memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) { return current + headerLen; } } return OxError(1); } void unloadRom(char*) noexcept { } ox::Result getPreloadAddr(keel::Context *ctx, ox::CRStringView path) noexcept { oxRequire(stat, ctx->rom->stat(path)); oxRequire(buff, static_cast(ctx->rom.get())->directAccess(path)); PreloadPtr p; oxReturnError(ox::readMC(buff, stat.size, &p)); return static_cast(p.preloadAddr) + ctx->preloadSectionOffset; } ox::Result getPreloadAddr(keel::Context *ctx, const ox::FileAddress &file) noexcept { oxRequire(stat, ctx->rom->stat(file)); oxRequire(buff, static_cast(ctx->rom.get())->directAccess(file)); PreloadPtr p; oxReturnError(ox::readMC(buff, stat.size, &p)); return static_cast(p.preloadAddr) + ctx->preloadSectionOffset; } } #endif namespace keel { ox::Error setRomFs(Context *ctx, ox::UPtr fs) noexcept { ctx->rom = std::move(fs); clearUuidMap(ctx); return buildUuidMap(ctx); } ox::Result> loadRomFs(ox::CRStringView path) noexcept { const auto lastDot = ox_lastIndexOf(path, '.'); const auto fsExt = lastDot != -1 ? path.substr(static_cast(lastDot)) : ""; if (ox_strcmp(fsExt, ".oxfs") == 0) { oxRequire(rom, loadRom(path)); return {ox::make_unique(rom, 32 * ox::units::MB, unloadRom)}; } else { #ifdef OX_HAS_PASSTHROUGHFS return {ox::make_unique(path)}; #else return OxError(2); #endif } } }