Skip to content

Commit f759fd4

Browse files
committed
Merge branch 'refs/heads/dev'
2 parents 1ad4582 + 1ebd1ab commit f759fd4

File tree

10 files changed

+101
-23
lines changed

10 files changed

+101
-23
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0),
66
and this project roughly adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.1.4] - 2024-07-08
9+
10+
### Changed
11+
12+
- Updated the "DSi NAND Path" core option description to clarify the role of the no$gba footer.
13+
14+
### Fixed
15+
16+
- Fixed some log entries not being output with a newline.
17+
- Fixed a crash when using a hybrid screen layout with a screen ratio of 3:1.
18+
- Fixed DSi NAND images not being recognized if they lacked a no$gba footer
19+
despite having equivalent data at offset 0xFF800. [#195](https://github.com/JesseTG/melonds-ds/issues/195)
20+
- Fixed the screen being rendered when using the OpenGL renderer while the emulated lid is closed,
21+
which caused flickering in some games. [#214](https://github.com/JesseTG/melonds-ds/issues/214)
22+
823
## [1.1.3] - 2024-06-14
924

1025
### Fixed

src/libretro/config/constants.cpp

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,63 @@ using std::nullopt;
3535
using std::string;
3636
using namespace melonDS;
3737

38+
// We verify the filesize of the NAND image and the presence of the no$gba footer (since melonDS needs it)
3839
bool MelonDsDs::config::IsDsiNandImage(const retro::dirent &file) noexcept {
3940
ZoneScopedN(TracyFunction);
4041
ZoneText(file.path, strnlen(file.path, sizeof(file.path)));
4142

42-
// TODO: Validate the NoCash footer
4343
if (!file.is_regular_file())
4444
return false;
4545

46-
if (find(DSI_NAND_SIZES.begin(), DSI_NAND_SIZES.end(), file.size) == DSI_NAND_SIZES.end())
46+
switch (file.size) {
47+
case DSI_NAND_SIZES_NOFOOTER[0] + NOCASH_FOOTER_SIZE: // 240MB + no$gba footer
48+
case DSI_NAND_SIZES_NOFOOTER[1] + NOCASH_FOOTER_SIZE: // 245.5MB + no$gba footer
49+
case DSI_NAND_SIZES_NOFOOTER[0]: // 240MB
50+
case DSI_NAND_SIZES_NOFOOTER[1]: // 245.5MB
51+
break; // the size is good, let's look for the footer!
52+
default:
53+
return false;
54+
}
55+
56+
RFILE* stream = filestream_open(file.path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
57+
if (!stream)
4758
return false;
4859

49-
return true;
60+
if (filestream_seek(stream, -static_cast<int64_t>(NOCASH_FOOTER_SIZE), RETRO_VFS_SEEK_POSITION_END) < 0) {
61+
filestream_close(stream);
62+
return false;
63+
}
64+
65+
std::array<uint8_t , NOCASH_FOOTER_SIZE> footer;
66+
if (filestream_read(stream, footer.data(), footer.size()) != NOCASH_FOOTER_SIZE) {
67+
filestream_close(stream);
68+
return false;
69+
}
70+
71+
if (filestream_seek(stream, NOCASH_FOOTER_OFFSET, RETRO_VFS_SEEK_POSITION_START) < 0) {
72+
filestream_close(stream);
73+
return false;
74+
}
75+
76+
std::array<uint8_t , NOCASH_FOOTER_SIZE> unusedArea;
77+
if (filestream_read(stream, unusedArea.data(), unusedArea.size()) != NOCASH_FOOTER_SIZE) {
78+
filestream_close(stream);
79+
return false;
80+
}
81+
82+
filestream_close(stream);
83+
84+
if (memcmp(footer.data(), NOCASH_FOOTER_MAGIC, NOCASH_FOOTER_MAGIC_SIZE) == 0) {
85+
// If the no$gba footer is present at the end of the file and correctly starts with the magic bytes...
86+
return true;
87+
}
88+
89+
if (memcmp(unusedArea.data(), NOCASH_FOOTER_MAGIC, NOCASH_FOOTER_MAGIC_SIZE) == 0) {
90+
// If the no$gba footer is present in a normally-unused section of the DSi NAND, and it starts with the magic bytes...
91+
return true;
92+
}
93+
94+
return false;
5095
}
5196

5297
bool MelonDsDs::config::IsFirmwareImage(const retro::dirent& file, Firmware::FirmwareHeader& header) noexcept {

src/libretro/config/constants.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ namespace MelonDsDs::config {
9090
namespace screen {
9191
constexpr unsigned MAX_HYBRID_RATIO = 3;
9292
constexpr unsigned MAX_SCREEN_LAYOUTS = 8; // Chosen arbitrarily; if you need more, open a PR
93-
constexpr unsigned MAX_SCREEN_GAP = 128;
93+
constexpr unsigned MAX_SCREEN_GAP = 126;
9494
static constexpr const char *const CATEGORY = "screen";
9595
static constexpr const char *const CURSOR_TIMEOUT = "melonds_cursor_timeout";
9696
static constexpr const char *const HYBRID_RATIO = "melonds_hybrid_ratio";
@@ -241,7 +241,11 @@ namespace MelonDsDs::config {
241241
static constexpr const char *const UPSIDE_DOWN = "rotate-180";
242242
}
243243

244-
constexpr std::array<size_t, 2> DSI_NAND_SIZES = { 251658304, 257425472 };
244+
constexpr size_t NOCASH_FOOTER_SIZE = 0x40;
245+
constexpr size_t NOCASH_FOOTER_OFFSET = 0xFF800;
246+
constexpr std::array<size_t, 2> DSI_NAND_SIZES_NOFOOTER = { 0xF000000, 0xF580000 }; // Taken from GBATek
247+
constexpr const char *const NOCASH_FOOTER_MAGIC = "DSi eMMC CID/CPU";
248+
constexpr size_t NOCASH_FOOTER_MAGIC_SIZE = 16;
245249
constexpr std::array<size_t, 3> FIRMWARE_SIZES = { 131072, 262144, 524288 };
246250

247251
bool IsDsiNandImage(const retro::dirent &file) noexcept;

src/libretro/config/definitions/system.hpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ namespace MelonDsDs::config::definitions {
5252
nullptr,
5353
"Determines whether melonDS uses native BIOS/firmware dumps "
5454
"or its own built-in replacements. "
55-
"Only applies to DS mode, as DSi mode requires native BIOS and firmware dumps.\n"
55+
"Only applies to DS mode, as DSi mode always requires native BIOS and firmware dumps.\n"
5656
"\n"
5757
"Native mode uses BIOS and firmware files from a real DS. "
5858
"Place your dumps of these in the system directory or its \"melonDS DS\" subdirectory "
@@ -66,8 +66,7 @@ namespace MelonDsDs::config::definitions {
6666
"Falls back to Built-In if any BIOS/firmware file isn't found.\n"
6767
"\n"
6868
"Built-In mode uses melonDS's built-in BIOS and firmware, "
69-
"and it suitable for most games. "
70-
"Also used as a fallback from Native mode if any required file isn't found.\n"
69+
"and is suitable for most games.\n"
7170
"\n"
7271
"Changes take effect at next restart.",
7372
nullptr,
@@ -136,8 +135,8 @@ namespace MelonDsDs::config::definitions {
136135
"Files are listed here if they:\n"
137136
"\n"
138137
"- Are inside the frontend's system directory, or a subdirectory named \"melonDS DS\".\n"
139-
"- Are exactly 251,658,304 bytes (240MB) or 257,425,472 bytes (245.5MB) long.\n"
140-
"- Contain valid footer data for DSi NAND images.\n"
138+
"- Are exactly 251,658,304 bytes (240MB) or 257,425,472 bytes (245.5MB) long with valid footer data, OR;\n"
139+
"- Are 64 bytes shorter than these lengths and contain equivalent data at file offset 0xFF800.\n"
141140
"\n"
142141
"Changes take effect at next restart.",
143142
nullptr,

src/libretro/core/core.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,6 @@ bool MelonDsDs::CoreState::LoadGame(unsigned type, std::span<const retro_game_in
490490
_optionVisibility.Update();
491491
}
492492
ApplyConfig(Config);
493-
// Must initialize the render state if using OpenGL (so the function pointers can be loaded
494493

495494
_syncClock = Config.StartTimeMode() == StartTimeMode::Sync;
496495
retro_assert(Console == nullptr);

src/libretro/environment.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,13 +300,12 @@ constexpr uint32_t GetLogColor(retro_log_level level) noexcept {
300300
void retro::fmt_log(retro_log_level level, fmt::string_view fmt, fmt::format_args args) noexcept {
301301
fmt::basic_memory_buffer<char, 1024> buffer;
302302
fmt::vformat_to(std::back_inserter(buffer), fmt, args);
303-
// We can't pass the va_list directly to the libretro callback,
304-
// so we have to construct the string and print that
305303

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

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

312311
if (_log) {

src/libretro/render/opengl.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,15 @@ void MelonDsDs::OpenGLRenderState::Render(
407407

408408
glBindBuffer(GL_ARRAY_BUFFER, vbo);
409409
glBindVertexArray(vao);
410-
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
410+
if (nds.IsLidClosed()) [[unlikely]] {
411+
// If the emulated lid is closed, just draw a blank
412+
// so that there's no annoying flickering with some games
413+
glClearColor(0, 0, 0, 0);
414+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
415+
}
416+
else {
417+
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
418+
}
411419

412420
glFlush();
413421

src/libretro/screenlayout.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,13 @@ namespace MelonDsDs {
318318

319319
constexpr unsigned MaxSoftwareRenderedHeight() noexcept {
320320
using namespace config::screen;
321-
return NDS_SCREEN_HEIGHT * 2 + MAX_SCREEN_GAP;
321+
return std::max({
322+
// Top/Bottom or Bottom/Top layout
323+
NDS_SCREEN_HEIGHT * 2 + MAX_SCREEN_GAP,
324+
325+
// Hybrid layout
326+
NDS_SCREEN_HEIGHT * MAX_HYBRID_RATIO,
327+
});
322328
}
323329

324330
constexpr unsigned MaxOpenGlRenderedWidth() noexcept {

test/CMakeLists.txt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,6 @@ set(GODMODE9I_ROM "${godmode9i_SOURCE_DIR}/GodMode9i.nds")
274274

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

277-
# TODO: Write the following test capabilities:
278-
# - Copying a system file to a particular location
279-
# - Select a GBA ROM
280-
# - Select a GBA save file
281-
282277
# TODO: Write the following tests:
283278

284279
# - Core loads BIOS from system
@@ -293,5 +288,6 @@ include(cmake/Booting.cmake)
293288
include(cmake/Errors.cmake)
294289
include(cmake/Firmware.cmake)
295290
include(cmake/Reset.cmake)
291+
include(cmake/Screen.cmake)
296292
include(cmake/TestHomebrewSDCards.cmake)
297293
include(cmake/Video.cmake)

test/cmake/Screen.cmake

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
add_python_test(
2+
NAME "Core supports hybrid sceen ratio of 3:1"
3+
TEST_MODULE basics.core_run_frame
4+
CONTENT "${NDS_ROM}"
5+
CORE_OPTION melonds_hybrid_ratio=3
6+
CORE_OPTION melonds_screen_layout1=hybrid-top
7+
)

0 commit comments

Comments
 (0)