From d714a143a2df3d68bd20c734fd82a8e81f39a754 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Mon, 17 Jun 2024 19:19:40 -0400 Subject: [PATCH] Validate the DSi NAND's no$gba footer - Fix #63 --- src/libretro/config/constants.cpp | 51 +++++++++++++++++++++++++++++-- src/libretro/config/constants.hpp | 6 +++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/libretro/config/constants.cpp b/src/libretro/config/constants.cpp index 04308be..459d62c 100644 --- a/src/libretro/config/constants.cpp +++ b/src/libretro/config/constants.cpp @@ -35,18 +35,63 @@ using std::nullopt; using std::string; using namespace melonDS; +// We verify the filesize of the NAND image and the presence of the no$gba footer (since melonDS needs it) bool MelonDsDs::config::IsDsiNandImage(const retro::dirent &file) noexcept { ZoneScopedN(TracyFunction); ZoneText(file.path, strnlen(file.path, sizeof(file.path))); - // TODO: Validate the NoCash footer if (!file.is_regular_file()) return false; - if (find(DSI_NAND_SIZES.begin(), DSI_NAND_SIZES.end(), file.size) == DSI_NAND_SIZES.end()) + switch (file.size) { + case DSI_NAND_SIZES_NOFOOTER[0] + NOCASH_FOOTER_SIZE: // 240MB + no$gba footer + case DSI_NAND_SIZES_NOFOOTER[1] + NOCASH_FOOTER_SIZE: // 245.5MB + no$gba footer + case DSI_NAND_SIZES_NOFOOTER[0]: // 240MB + case DSI_NAND_SIZES_NOFOOTER[1]: // 245.5MB + break; // the size is good, let's look for the footer! + default: + return false; + } + + RFILE* stream = filestream_open(file.path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + if (!stream) return false; - return true; + if (filestream_seek(stream, -static_cast(NOCASH_FOOTER_SIZE), RETRO_VFS_SEEK_POSITION_END) < 0) { + filestream_close(stream); + return false; + } + + std::array footer; + if (filestream_read(stream, footer.data(), footer.size()) != NOCASH_FOOTER_SIZE) { + filestream_close(stream); + return false; + } + + if (filestream_seek(stream, NOCASH_FOOTER_OFFSET, RETRO_VFS_SEEK_POSITION_START) < 0) { + filestream_close(stream); + return false; + } + + std::array unusedArea; + if (filestream_read(stream, unusedArea.data(), unusedArea.size()) != NOCASH_FOOTER_SIZE) { + filestream_close(stream); + return false; + } + + filestream_close(stream); + + if (memcmp(footer.data(), NOCASH_FOOTER_MAGIC, NOCASH_FOOTER_MAGIC_SIZE) == 0) { + // If the no$gba footer is present at the end of the file and correctly starts with the magic bytes... + return true; + } + + if (memcmp(unusedArea.data(), NOCASH_FOOTER_MAGIC, NOCASH_FOOTER_MAGIC_SIZE) == 0) { + // If the no$gba footer is present in a normally-unused section of the DSi NAND, and it starts with the magic bytes... + return true; + } + + return false; } bool MelonDsDs::config::IsFirmwareImage(const retro::dirent& file, Firmware::FirmwareHeader& header) noexcept { diff --git a/src/libretro/config/constants.hpp b/src/libretro/config/constants.hpp index 0b57610..3507861 100644 --- a/src/libretro/config/constants.hpp +++ b/src/libretro/config/constants.hpp @@ -241,7 +241,11 @@ namespace MelonDsDs::config { static constexpr const char *const UPSIDE_DOWN = "rotate-180"; } - constexpr std::array DSI_NAND_SIZES = { 251658304, 257425472 }; + constexpr size_t NOCASH_FOOTER_SIZE = 0x40; + constexpr size_t NOCASH_FOOTER_OFFSET = 0xFF800; + constexpr std::array DSI_NAND_SIZES_NOFOOTER = { 0xF000000, 0xF580000 }; // Taken from GBATek + constexpr const char *const NOCASH_FOOTER_MAGIC = "DSi eMMC CID/CPU"; + constexpr size_t NOCASH_FOOTER_MAGIC_SIZE = 16; constexpr std::array FIRMWARE_SIZES = { 131072, 262144, 524288 }; bool IsDsiNandImage(const retro::dirent &file) noexcept;