Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a7f8b73
replace audrec with audout, use sdl2 resampler in place of audren, re…
ITotalJustice Dec 17, 2024
f134fe2
add option to clear playlist queue, increase max playlist size to 512
ITotalJustice Dec 17, 2024
5e5c1cf
bump all dr_libs to latest commit.
ITotalJustice Dec 17, 2024
c996336
remove audout hack due to being fixed in libnx, reduce latency betwee…
ITotalJustice Jul 12, 2025
8a48304
update dr libs to latest commit (85019a9), fix dr_mp3 seek crash due …
ITotalJustice Jul 12, 2025
3a244df
slightly more optimised version of sdmc::FileExists()
ITotalJustice Jul 12, 2025
b2ed905
re-write the playlist code.
ITotalJustice Jul 13, 2025
061ba97
silence gcc lto warning
ITotalJustice Jul 13, 2025
aa50d81
add support for loading a song / folder on startup.
ITotalJustice Jul 13, 2025
d4ceec6
improve overlay dir scan speed by reducing calls to fsDirRead
ITotalJustice Jul 13, 2025
d8f8038
fix overlay browser not focusing first element
ITotalJustice Jul 13, 2025
a26ded1
ovl fix addAll, ovl add setting/removing startup file, ovl fix playli…
ITotalJustice Jul 13, 2025
e71b8a5
update makefile to set boot2.flag, update workflow to build on pr but…
ITotalJustice Jul 13, 2025
63a25cd
allocate large drmp3/drflac structs on the heap rather that making th…
ITotalJustice Jul 13, 2025
bf14412
increase heap to 300k, set NDEBUG, add guards against including dr_x …
ITotalJustice Jul 13, 2025
a2cc0c6
ovl set file scan and playlist add limit.
ITotalJustice Jul 13, 2025
9e6f4ce
reserve max amount of entries in a vector to avoid allocs/moves in th…
ITotalJustice Jul 13, 2025
69096dd
fix wav seek crash, impl buffered io, reduce latency between songs, i…
ITotalJustice Jul 14, 2025
c01ea11
added support for re-shuffling the playlist when shuffle mode is togg…
ITotalJustice Jul 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Build sys-tune and overlay

on: [push]
on: [push, pull_request]
jobs:
build:

Expand All @@ -23,7 +23,9 @@ jobs:
cp sys-tune/toolbox.json dist/atmosphere/contents/4200000000000000/
cp overlay/sys-tune-overlay.ovl dist/switch/.overlays/

- uses: actions/upload-artifact@master
- name: Deploy
if: ${{ github.event_name != 'pull_request' && github.event.action != 'unassigned' }}
uses: actions/upload-artifact@master
with:
name: sys-tune
path: dist
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ module:

dist: all
mkdir -p dist/switch/.overlays
mkdir -p dist/atmosphere/contents/4200000000000000
mkdir -p dist/atmosphere/contents/4200000000000000/flags
touch dist/atmosphere/contents/4200000000000000/flags/boot2.flag
cp sys-tune/sys-tune.nsp dist/atmosphere/contents/4200000000000000/exefs.nsp
cp overlay/sys-tune-overlay.ovl dist/switch/.overlays/
cp sys-tune/toolbox.json dist/atmosphere/contents/4200000000000000/
Expand Down
14 changes: 4 additions & 10 deletions common/config/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ namespace config {
namespace {

const char CONFIG_PATH[]{"/config/sys-tune/config.ini"};
// blacklist uses it's own config file because eventually a database
// may be setup and users can easily update their blacklist by downloading
// an updated blacklist.ini.
// Also, the blacklist lookup needs to be as fast as possible
// (literally a race until the title opens audren), so a seperate, smaller file is ideal.
const char BLACKLIST_PATH[]{"/config/sys-tune/blacklist.ini"};

void create_config_dir() {
/* Creating directory on every set call looks sus, but the user may delete the dir */
Expand Down Expand Up @@ -102,13 +96,13 @@ void set_default_title_volume(float value) {
ini_putf("config", "global_volume", value, CONFIG_PATH);
}

auto get_title_blacklist(u64 tid) -> bool {
return ini_getbool("blacklist", get_tid_str(tid), false, BLACKLIST_PATH);
auto get_load_path(char* out, int max_len) -> int {
return ini_gets("config", "load_path", "", out, max_len, CONFIG_PATH);
}

void set_title_blacklist(u64 tid, bool value) {
void set_load_path(const char* path) {
create_config_dir();
ini_putl("blacklist", get_tid_str(tid), value, BLACKLIST_PATH);
ini_puts("config", "load_path", path, CONFIG_PATH);
}

}
6 changes: 3 additions & 3 deletions common/config/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ void set_title_volume(u64 tid, float value);
auto get_default_title_volume() -> float;
void set_default_title_volume(float value);

// returns true if title causes a fatal on launch
auto get_title_blacklist(u64 tid) -> bool;
void set_title_blacklist(u64 tid, bool value);
// returns the length of the string
auto get_load_path(char* out, int max_len) -> int;
void set_load_path(const char* path);

}
1 change: 1 addition & 0 deletions common/minIni/minIni.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define MININI_IMPLEMENTATION
#include "minIni.h"
#if defined NDEBUG
#undef assert
#define assert(e)
#else
#include <assert.h>
Expand Down
15 changes: 12 additions & 3 deletions common/sdmc/sdmc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@ namespace sdmc {
return fsFsOpenFile(&sdmc, path_buffer, open_mode, file);
}

bool FileExists(const char* path) {
Result OpenDir(FsDir *dir, const char *path, int open_mode) {
std::strcpy(path_buffer, path);
return fsFsOpenDirectory(&sdmc, path_buffer, open_mode, dir);
}

Result GetType(const char* path, FsDirEntryType* type) {
std::strcpy(path_buffer, path);
FsTimeStampRaw ts;
return R_SUCCEEDED(fsFsGetFileTimeStampRaw(&sdmc, path_buffer, &ts));
return fsFsGetEntryType(&sdmc, path_buffer, type);;
}

bool FileExists(const char* path) {
FsDirEntryType type;
return R_SUCCEEDED(GetType(path, &type)) && type == FsDirEntryType_File;
}

Result CreateFolder(const char* path) {
Expand Down
3 changes: 3 additions & 0 deletions common/sdmc/sdmc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ namespace sdmc {
void Close();

Result OpenFile(FsFile *file, const char* path, int open_mode = FsOpenMode_Read);
Result OpenDir(FsDir *dir, const char *path, int open_mode);

Result GetType(const char* path, FsDirEntryType* type);
bool FileExists(const char* path);

Result CreateFolder(const char* path);
Expand Down
2 changes: 1 addition & 1 deletion overlay/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -Wno-format-truncation -O2 -ffunction-sections \
$(ARCH) $(DEFINES)

CFLAGS += $(INCLUDE) -DVERSION=\"v$(APP_VERSION)\" -DTUNE_API_VERSION=$(API_VERSION) \
CFLAGS += $(INCLUDE) -DVERSION=\"v$(APP_VERSION)\" -DTUNE_API_VERSION=$(API_VERSION) -DNDEBUG=1 \
$(WANT_FLAGS)

CXXFLAGS := $(CFLAGS) -fno-exceptions -std=c++23
Expand Down
136 changes: 97 additions & 39 deletions overlay/source/gui_browser.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "gui_browser.hpp"

#include "config/config.hpp"
#include "tune.h"

namespace {
Expand All @@ -8,7 +9,7 @@ namespace {
return strcasecmp(_lhs->getText().c_str(), _rhs->getText().c_str()) < 0;
};

bool StringTextCompare(std::string _lhs, std::string _rhs) {
bool StringTextCompare(const std::string& _lhs, const std::string& _rhs) {
return strcasecmp(_lhs.c_str(), _rhs.c_str()) < 0;
};

Expand Down Expand Up @@ -97,12 +98,14 @@ void BrowserGui::scanCwd() {
tsl::Gui::removeFocus();
this->m_list->clear();

this->m_list->addItem(new tsl::elm::CategoryHeader("\uE0E7 Play selected path on start up", true));

/* Show absolute folder path. */
this->m_list->addItem(new tsl::elm::CategoryHeader(this->cwd, true));

/* Open directory. */
FsDir dir;
Result rc = fsFsOpenDirectory(&this->m_fs, this->cwd, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &dir);
Result rc = fsFsOpenDirectory(&this->m_fs, this->cwd, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &dir);
if (R_FAILED(rc)) {
char result_buffer[0x10];
std::snprintf(result_buffer, 0x10, "2%03X-%04X", R_MODULE(rc), R_DESCRIPTION(rc));
Expand All @@ -116,56 +119,96 @@ void BrowserGui::scanCwd() {

/* Iternate over directory. */
s64 count = 0;
FsDirectoryEntry entry;
while (R_SUCCEEDED(fsDirRead(&dir, &count, 1, &entry)) && count) {
if (entry.type == FsDirEntryType_Dir) {
/* Add directory entries. */
auto item = new tsl::elm::ListItem(entry.name);
item->setClickListener([this, item](u64 down) -> bool {
if (down & HidNpadButton_A) {
std::strncat(this->cwd, item->getText().c_str(), sizeof(this->cwd) - 1);
std::strncat(this->cwd, "/", sizeof(this->cwd) - 1);
this->scanCwd();
return true;
}
return false;
});
folders.push_back(item);
} else if (SupportsType(entry.name)) {
/* Add file entry. */
auto item = new tsl::elm::ListItem(entry.name);
item->setClickListener([this, item](u64 down) -> bool {
if (down & HidNpadButton_A) {
std::snprintf(path_buffer, sizeof(path_buffer), "%s%s", this->cwd, item->getText().c_str());
Result rc = tuneEnqueue(path_buffer, TuneEnqueueType_Back);
if (R_SUCCEEDED(rc)) {
m_frame->setToast("Playlist updated", "Added 1 song to Playlist.");
} else {
m_frame->setToast("Failed to add Track.", "Does the name contain umlauts?");
const u64 max = 2048; // max items to be added to the array.
std::vector<FsDirectoryEntry> entries(64);

// avoid vector allocs / resize in the loop.
folders.reserve(max);
files.reserve(max);

while (R_SUCCEEDED(fsDirRead(&dir, &count, entries.size(), entries.data())) && count) {
for (s64 i = 0; i < count; i++) {
if (folders.size() + files.size() >= max) {
break;
}

const auto& entry = entries[i];
if (entry.type == FsDirEntryType_Dir) {
/* Add directory entries. */
auto item = new tsl::elm::ListItem(entry.name);
item->setClickListener([this, item](u64 down) -> bool {
if (down & HidNpadButton_A) {
std::strncat(this->cwd, item->getText().c_str(), sizeof(this->cwd) - 1);
std::strncat(this->cwd, "/", sizeof(this->cwd) - 1);
this->scanCwd();
return true;
} else if (down & HidNpadButton_ZR) {
std::snprintf(path_buffer, sizeof(path_buffer), "%s%s", this->cwd, item->getText().c_str());
config::set_load_path(path_buffer);
m_frame->setToast("Set start up file", item->getText().c_str());
return true;
}
return true;
}
return false;
});
files.push_back(item);
return false;
});
folders.push_back(item);
} else if (SupportsType(entry.name)) {
/* Add file entry. */
auto item = new tsl::elm::ListItem(entry.name);
item->setClickListener([this, item](u64 down) -> bool {
if (down & HidNpadButton_A) {
std::snprintf(path_buffer, sizeof(path_buffer), "%s%s", this->cwd, item->getText().c_str());
Result rc = tuneEnqueue(path_buffer, TuneEnqueueType_Back);
if (R_SUCCEEDED(rc)) {
m_frame->setToast("Playlist updated", "Added 1 song to Playlist.");
} else {
m_frame->setToast("Failed to add Track.", "Does the name contain umlauts?");
}
return true;
} else if (down & HidNpadButton_ZR) {
std::snprintf(path_buffer, sizeof(path_buffer), "%s%s", this->cwd, item->getText().c_str());
config::set_load_path(path_buffer);
m_frame->setToast("Set start up file", path_buffer);
return true;
}
return false;
});
files.push_back(item);
}
}

if (folders.size() + files.size() >= max) {
m_frame->setToast("Stopped scanning folder", "maximum of " + std::to_string(max) + " hit");
break;
}
}
if (folders.size() == 0 && files.size() == 0) {
this->m_list->addItem(new tsl::elm::CategoryHeader("Empty..."));
return;
}

tsl::elm::ListItem* focus_elm = nullptr;

if (folders.size() > 0) {
std::sort(folders.begin(), folders.end(), ListItemTextCompare);

focus_elm = folders[0];

for (auto element : folders)
this->m_list->addItem(element);
}
if (files.size() > 0) {
this->m_list->addItem(new tsl::elm::CategoryHeader("Files"));
std::sort(files.begin(), files.end(), ListItemTextCompare);

if (!focus_elm)
focus_elm = files[0];

for (auto element : files)
this->m_list->addItem(element);
}

if (focus_elm)
tsl::Gui::requestFocus(focus_elm, tsl::FocusDirection::None);
}

void BrowserGui::upCwd() {
Expand All @@ -184,7 +227,7 @@ void BrowserGui::upCwd() {

void BrowserGui::addAllToPlaylist() {
FsDir dir;
Result rc = fsFsOpenDirectory(&this->m_fs, this->cwd, FsDirOpenMode_ReadFiles, &dir);
Result rc = fsFsOpenDirectory(&this->m_fs, this->cwd, FsDirOpenMode_ReadFiles|FsDirOpenMode_NoFileSize, &dir);
if (R_FAILED(rc)) {
char result_buffer[0x10];
std::snprintf(result_buffer, 0x10, "2%03X-%04X", R_MODULE(rc), R_DESCRIPTION(rc));
Expand All @@ -197,11 +240,26 @@ void BrowserGui::addAllToPlaylist() {
std::vector<std::string> file_list;
s64 songs_added = 0;
s64 count = 0;
FsDirectoryEntry entry;
while (R_SUCCEEDED(fsDirRead(&dir, &count, 1, &entry)) && count){
if (entry.type == FsDirEntryType_File && SupportsType(entry.name)){
file_list.push_back(std::string(entry.name));
count++;
const u64 max = 300; // max set by PLAYLIST_ENTRY_MAX in music_player.cpp
std::vector<FsDirectoryEntry> entries(64);

// avoid vector allocs / resize in the loop.
file_list.reserve(max);

while (R_SUCCEEDED(fsDirRead(&dir, &count, entries.size(), entries.data())) && count) {
for (s64 i = 0; i < count; i++) {
const auto& entry = entries[i];
if (entry.type == FsDirEntryType_File && SupportsType(entry.name)){
file_list.emplace_back(entry.name);

if (file_list.size() >= max) {
break;
}
}
}

if (file_list.size() >= max) {
break;
}
}

Expand Down
21 changes: 21 additions & 0 deletions overlay/source/gui_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,27 @@ tsl::elm::Element *MainGui::createUI() {

list->addItem(new tsl::elm::CategoryHeader("Misc"));

auto startup_button = new tsl::elm::ListItem("Remove start up file");
startup_button->setClickListener([frame](u64 keys) {
if (keys & HidNpadButton_A) {
char path[512];
if (config::get_load_path(path, sizeof(path))) {
config::set_load_path("");
const auto* p = path;
if (auto ext = std::strrchr(path, '/')) {
p = ext + 1;
}

frame->setToast("Removed start up file", p);
} else {
frame->setToast("Failed to remove start up file", "No start up file set in config");
}
return true;
}
return false;
});
list->addItem(startup_button);

auto exit_button = new tsl::elm::ListItem("Close sys-tune");
exit_button->setClickListener([](u64 keys) {
if (keys & HidNpadButton_A) {
Expand Down
Loading