examples: Add example of using LibXM7 with files in NitroFS

The only example in the repository was using files included in the ARM9
binary, which isn't a very useful thing to do in games when they start
growing.
This commit is contained in:
Antonio Niño Díaz 2025-04-30 18:50:49 +01:00
parent 4dc870fe85
commit 420d9addf8
11 changed files with 319 additions and 1 deletions

8
examples/libxm7/nitrofs/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
__pycache__/
graph.png
*.nds
*.sav
build
.ninja_deps
.ninja_log
*.ninja

View File

@ -0,0 +1 @@
../../../../architectds/architectds

View File

@ -0,0 +1,29 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileContributor: Antonio Niño Díaz, 2024-2025
from architectds import *
arm9 = Arm9Binary(
sourcedirs=['source'],
libs=['nds9', 'xm79'],
libdirs=['${BLOCKSDS}/libs/libnds', '${BLOCKSDS}/libs/libxm7']
)
arm9.generate_elf()
arm7 = Arm7BinaryDefault(
elf_path='${BLOCKSDS}/sys/arm7/main_core/arm7_libxm7.elf'
)
nds = NdsRom(
binaries=[arm9, arm7],
nitrofsdirs=['nitrofs'],
game_title='NitroFS example',
game_subtitle='LibXM7',
game_author='(sverx, 2009-01-07)'
)
nds.generate_nds()
nds.run_command_line_arguments()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,275 @@
// SPDX-License-Identifier: CC0-1.0
//
// SPDX-FileContributor: Antonio Niño Díaz, 2023-2024
#include <stdbool.h>
#include <stdio.h>
#include <filesystem.h>
#include <libxm7.h>
#include <nds.h>
// List of songs used in this example:
//
// kaos_och_dekadens.mod
// ---------------------
//
// - Title: Kaos och Dekadens
// - Author: Nightbeat
// - Source: https://modarchive.org/index.php?request=view_by_moduleid&query=113319
//
// lasse_haen_pyykit.xm
// --------------------
//
// - Title: haen pyykit
// - Author: Lasse
// - Source: Obtained from the original LibXM7 example by sverx.
//
// nb_essentials.mod
// -----------------
//
// - Title: Essentials Intact
// - Author: Nightbeat
// - Source: https://modarchive.org/index.php?request=view_by_moduleid&query=49587
//
// nb_roots.mod
// ------------
//
// - Title: Roots
// - Author: Nightbeat
// - Source: https://modarchive.org/index.php?request=view_by_moduleid&query=49644
//
// parallax_80599.xm
// -----------------
//
// - Title: Parallax Glacier
// - Author: Raina
// - Source http://modarchive.org/index.php?request=view_by_moduleid&query=163194
//
// secret_message.mod
// ------------------
//
// - Title: Secret Message
// - Author: Frequent/Ephidrena
// - Source: https://modarchive.org/index.php?request=view_by_moduleid&query=96883
void wait_forever(void)
{
while (1)
{
swiWaitForVBlank();
scanKeys();
if (keysHeld() & KEY_START)
exit(1);
}
}
void *file_load(const char *path)
{
FILE *f = fopen(path, "r");
if (f == NULL)
{
perror("fopen");
wait_forever();
}
int ret = fseek(f, 0, SEEK_END);
if (ret != 0)
{
perror("fseek");
wait_forever();
}
long size = ftell(f);
if (size == -1)
{
perror("ftell");
wait_forever();
}
rewind(f);
void *buffer = malloc(size);
if (buffer == NULL)
{
printf("malloc(): Not enough memory (%ld)\n", size);
wait_forever();
}
size_t bytes = fread(buffer, 1, size, f);
if (bytes != size)
{
printf("fread(%ld): %zu bytes read", size, bytes);
wait_forever();
}
int res = fclose(f);
if (res != 0)
{
perror("fclose");
wait_forever();
}
return buffer;
}
// Assign FIFO_USER_07 channel to LibXM7
#define FIFO_LIBXM7 FIFO_USER_07
void song_start(XM7_ModuleManager_Type *module)
{
fifoSendValue32(FIFO_LIBXM7, (u32)module);
}
void song_stop(void)
{
fifoSendValue32(FIFO_LIBXM7, 0);
}
static void *file_buffer = NULL;
static XM7_ModuleManager_Type *module_manager = NULL;
void song_nitrofs_stop_and_free(void)
{
if (file_buffer == NULL)
return;
song_stop();
// Wait until the player has stopped to free the data
swiWaitForVBlank();
XM7_UnloadXM(module_manager);
free(module_manager);
free(file_buffer);
file_buffer = NULL;
}
void song_nitrofs_load_and_play(const char *path)
{
if (file_buffer)
song_nitrofs_stop_and_free();
file_buffer = file_load(path);
if (file_buffer == NULL)
return;
module_manager = calloc(1, sizeof(XM7_ModuleManager_Type));
if (module_manager == NULL)
{
free(file_buffer);
return;
}
u16 res = 0xFFFF;
if (strcmp(path + strlen(path) - 4, ".mod") == 0)
res = XM7_LoadMOD(module_manager, file_buffer);
else if (strcmp(path + strlen(path) - 3, ".xm") == 0)
res = XM7_LoadXM(module_manager, file_buffer);
// Depending on the MOD or XM file you're playing, you may need to adjust
// the replay style. Check the documentation for more details.
//XM7_SetReplayStyle(module_manager, XM7_REPLAY_STYLE_MOD_PLAYER);
if (res != 0)
{
printf("LibXM7 error (module 0): 0x%04x\n", res);
free(file_buffer);
free(module_manager);
return;
}
// Ensure that the ARM7 can see the file and the LibXM7 struct
DC_FlushAll();
song_start(module_manager);
}
int main(int argc, char **argv)
{
consoleDemoInit();
printf("LibXM7 example\n");
printf("==============\n");
printf("\n");
printf("Initializing NitroFS...\n");
printf("\n");
bool init_ok = nitroFSInit(NULL);
if (!init_ok)
{
perror("nitroFSInit()");
wait_forever();
}
soundEnable();
printf("A: Start song\n");
printf("B: Stop song\n");
printf("UP/DOWN: Select song\n");
printf("\n");
printf("START: Return to loader\n");
printf("\n");
printf("\n");
printf("\n");
printf("Available songs:\n");
printf("\n");
#define NUM_SONGS 6
const char *songs[NUM_SONGS] = {
"kaos_och_dekadens.mod",
"lasse_haen_pyykit.xm",
"nb_essentials.mod",
"nb_roots.mod",
"parallax_80599.xm",
"secret_message.mod",
};
int selected_song = 0;
while (1)
{
swiWaitForVBlank();
scanKeys();
uint16_t keys_down = keysDown();
// Print list of songs
consoleSetCursor(NULL, 0, 15);
for (int i = 0; i < NUM_SONGS; i++)
printf("%c %s\n", selected_song == i ? '>' : ' ', songs[i]);
if (keys_down & KEY_B)
song_nitrofs_stop_and_free();
if (keys_down & KEY_A)
song_nitrofs_load_and_play(songs[selected_song]);
if (keys_down & KEY_DOWN)
{
selected_song++;
if (selected_song >= NUM_SONGS)
selected_song = 0;
}
if (keys_down & KEY_UP)
{
selected_song--;
if (selected_song < 0)
selected_song = NUM_SONGS - 1;
}
if (keys_down & KEY_START)
{
song_nitrofs_stop_and_free();
break;
}
}
soundDisable();
return 0;
}

View File

@ -130,7 +130,12 @@ the examples, run ``python3 build.py`` in the folder of the example.
- **libxm7/**:
- ``play_songs``: Example that uses libxm7 instead of Maxmod to play music.
- ``nitrofs``: Example that uses LibXM7 to play XM and MOD songs from NitroFS.
It also shows how to use a different pre-built ARM7 core from BlocksDS and
how to add regular files to NitroFS in a ROM without converting them.
- ``play_songs``: Example that uses LibXM7 to play songs included in the ARM9
binary as data.
- **maxmod/**: