Skip to content

Commit

Permalink
Merge branch 'refs/heads/dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
JesseTG committed Jul 9, 2024
2 parents 1ad4582 + 1ebd1ab commit f759fd4
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 23 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0),
and this project roughly adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.4] - 2024-07-08

### Changed

- Updated the "DSi NAND Path" core option description to clarify the role of the no$gba footer.

### Fixed

- Fixed some log entries not being output with a newline.
- Fixed a crash when using a hybrid screen layout with a screen ratio of 3:1.
- Fixed DSi NAND images not being recognized if they lacked a no$gba footer
despite having equivalent data at offset 0xFF800. [#195](https://github.com/JesseTG/melonds-ds/issues/195)
- Fixed the screen being rendered when using the OpenGL renderer while the emulated lid is closed,
which caused flickering in some games. [#214](https://github.com/JesseTG/melonds-ds/issues/214)

## [1.1.3] - 2024-06-14

### Fixed
Expand Down
51 changes: 48 additions & 3 deletions src/libretro/config/constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int64_t>(NOCASH_FOOTER_SIZE), RETRO_VFS_SEEK_POSITION_END) < 0) {
filestream_close(stream);
return false;
}

std::array<uint8_t , NOCASH_FOOTER_SIZE> 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<uint8_t , NOCASH_FOOTER_SIZE> 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 {
Expand Down
8 changes: 6 additions & 2 deletions src/libretro/config/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ namespace MelonDsDs::config {
namespace screen {
constexpr unsigned MAX_HYBRID_RATIO = 3;
constexpr unsigned MAX_SCREEN_LAYOUTS = 8; // Chosen arbitrarily; if you need more, open a PR
constexpr unsigned MAX_SCREEN_GAP = 128;
constexpr unsigned MAX_SCREEN_GAP = 126;
static constexpr const char *const CATEGORY = "screen";
static constexpr const char *const CURSOR_TIMEOUT = "melonds_cursor_timeout";
static constexpr const char *const HYBRID_RATIO = "melonds_hybrid_ratio";
Expand Down Expand Up @@ -241,7 +241,11 @@ namespace MelonDsDs::config {
static constexpr const char *const UPSIDE_DOWN = "rotate-180";
}

constexpr std::array<size_t, 2> DSI_NAND_SIZES = { 251658304, 257425472 };
constexpr size_t NOCASH_FOOTER_SIZE = 0x40;
constexpr size_t NOCASH_FOOTER_OFFSET = 0xFF800;
constexpr std::array<size_t, 2> 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<size_t, 3> FIRMWARE_SIZES = { 131072, 262144, 524288 };

bool IsDsiNandImage(const retro::dirent &file) noexcept;
Expand Down
9 changes: 4 additions & 5 deletions src/libretro/config/definitions/system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ namespace MelonDsDs::config::definitions {
nullptr,
"Determines whether melonDS uses native BIOS/firmware dumps "
"or its own built-in replacements. "
"Only applies to DS mode, as DSi mode requires native BIOS and firmware dumps.\n"
"Only applies to DS mode, as DSi mode always requires native BIOS and firmware dumps.\n"
"\n"
"Native mode uses BIOS and firmware files from a real DS. "
"Place your dumps of these in the system directory or its \"melonDS DS\" subdirectory "
Expand All @@ -66,8 +66,7 @@ namespace MelonDsDs::config::definitions {
"Falls back to Built-In if any BIOS/firmware file isn't found.\n"
"\n"
"Built-In mode uses melonDS's built-in BIOS and firmware, "
"and it suitable for most games. "
"Also used as a fallback from Native mode if any required file isn't found.\n"
"and is suitable for most games.\n"
"\n"
"Changes take effect at next restart.",
nullptr,
Expand Down Expand Up @@ -136,8 +135,8 @@ namespace MelonDsDs::config::definitions {
"Files are listed here if they:\n"
"\n"
"- Are inside the frontend's system directory, or a subdirectory named \"melonDS DS\".\n"
"- Are exactly 251,658,304 bytes (240MB) or 257,425,472 bytes (245.5MB) long.\n"
"- Contain valid footer data for DSi NAND images.\n"
"- Are exactly 251,658,304 bytes (240MB) or 257,425,472 bytes (245.5MB) long with valid footer data, OR;\n"
"- Are 64 bytes shorter than these lengths and contain equivalent data at file offset 0xFF800.\n"
"\n"
"Changes take effect at next restart.",
nullptr,
Expand Down
1 change: 0 additions & 1 deletion src/libretro/core/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,6 @@ bool MelonDsDs::CoreState::LoadGame(unsigned type, std::span<const retro_game_in
_optionVisibility.Update();
}
ApplyConfig(Config);
// Must initialize the render state if using OpenGL (so the function pointers can be loaded

_syncClock = Config.StartTimeMode() == StartTimeMode::Sync;
retro_assert(Console == nullptr);
Expand Down
9 changes: 4 additions & 5 deletions src/libretro/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,12 @@ constexpr uint32_t GetLogColor(retro_log_level level) noexcept {
void retro::fmt_log(retro_log_level level, fmt::string_view fmt, fmt::format_args args) noexcept {
fmt::basic_memory_buffer<char, 1024> buffer;
fmt::vformat_to(std::back_inserter(buffer), fmt, args);
// We can't pass the va_list directly to the libretro callback,
// so we have to construct the string and print that

if (buffer[buffer.size() - 1] == '\n')
buffer[buffer.size() - 1] = '\0';
if (buffer[buffer.size() - 1] != '\n')
// If the string doesn't end with a newline...
buffer.push_back('\n');

buffer.push_back('\n');
// vformat_to doesn't append a null terminator, so we have to do it ourselves
buffer.push_back('\0');

if (_log) {
Expand Down
10 changes: 9 additions & 1 deletion src/libretro/render/opengl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,15 @@ void MelonDsDs::OpenGLRenderState::Render(

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
if (nds.IsLidClosed()) [[unlikely]] {
// If the emulated lid is closed, just draw a blank
// so that there's no annoying flickering with some games
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
else {
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
}

glFlush();

Expand Down
8 changes: 7 additions & 1 deletion src/libretro/screenlayout.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,13 @@ namespace MelonDsDs {

constexpr unsigned MaxSoftwareRenderedHeight() noexcept {
using namespace config::screen;
return NDS_SCREEN_HEIGHT * 2 + MAX_SCREEN_GAP;
return std::max({
// Top/Bottom or Bottom/Top layout
NDS_SCREEN_HEIGHT * 2 + MAX_SCREEN_GAP,

// Hybrid layout
NDS_SCREEN_HEIGHT * MAX_HYBRID_RATIO,
});
}

constexpr unsigned MaxOpenGlRenderedWidth() noexcept {
Expand Down
6 changes: 1 addition & 5 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,6 @@ set(GODMODE9I_ROM "${godmode9i_SOURCE_DIR}/GodMode9i.nds")

set(MICRECORD_NDS "${CMAKE_CURRENT_SOURCE_DIR}/nds/micrecord.nds")

# TODO: Write the following test capabilities:
# - Copying a system file to a particular location
# - Select a GBA ROM
# - Select a GBA save file

# TODO: Write the following tests:

# - Core loads BIOS from system
Expand All @@ -293,5 +288,6 @@ include(cmake/Booting.cmake)
include(cmake/Errors.cmake)
include(cmake/Firmware.cmake)
include(cmake/Reset.cmake)
include(cmake/Screen.cmake)
include(cmake/TestHomebrewSDCards.cmake)
include(cmake/Video.cmake)
7 changes: 7 additions & 0 deletions test/cmake/Screen.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
add_python_test(
NAME "Core supports hybrid sceen ratio of 3:1"
TEST_MODULE basics.core_run_frame
CONTENT "${NDS_ROM}"
CORE_OPTION melonds_hybrid_ratio=3
CORE_OPTION melonds_screen_layout1=hybrid-top
)

0 comments on commit f759fd4

Please sign in to comment.