1
0
mirror of synced 2025-02-08 22:59:41 +01:00

rewrite recursive song search

This commit is contained in:
Stepland 2023-08-06 01:03:30 +02:00
parent 907a53a5b4
commit c09b5d49c4
19 changed files with 92 additions and 124 deletions

View File

@ -21,7 +21,8 @@ dependencies = [
dependency('gl'),
cc.find_library('atomic'),
dependency('nowide'),
dependency('nlohmann_json')
dependency('nlohmann_json'),
dependency('fmt')
]
if host_machine.system() == 'linux'

View File

@ -1,4 +1,5 @@
#include "Preferences.hpp"
#include "fmt/core.h"
#include <iostream>
#include <fstream>
@ -82,8 +83,8 @@ namespace Data {
prefs_file >> j;
j.get_to(*this);
} catch (const std::exception& e) {
std::cerr << "Error while loading data/preferences.json : " << e.what() << '\n';
std::cerr << "Using fallback preferences instead" << '\n';
fmt::print("Error while loading data/preferences.json : {}\n", e.what());
fmt::print("Using fallback preferences instead\n");
return;
}
key_mapping = Input::KeyMapping{key_mapping.m_button_to_key};

View File

@ -1,17 +1,20 @@
#include "Song.hpp"
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <list>
#include <ranges>
#include <span>
#include <stdexcept>
#include <fmt/core.h>
#include <memon/memon.hpp>
#include "../Toolkit/UTF8Strings.hpp"
#include "../Toolkit/UTF8SFMLRedefinitions.hpp"
namespace fs = std::filesystem;
namespace Data {
bool cmp_dif_name::operator()(const std::string &a, const std::string &b) const {
@ -46,58 +49,30 @@ namespace Data {
}
}
SongList::SongList(const std::filesystem::path& jujube_path) :
songs()
{
std::filesystem::path song_folder = jujube_path/"songs";
SongList load_song_list(const std::filesystem::path& jujube_path) {
SongList songs;
const std::filesystem::path song_folder = jujube_path / "songs";
if (not std::filesystem::is_directory(song_folder)) {
fmt::print(
"Tried searching for songs in {} but the folder does not exist !\n",
path_to_utf8_encoded_string(song_folder)
);
return songs;
}
if (std::filesystem::exists(song_folder) and std::filesystem::is_directory(song_folder)) {
for (const auto& dir_item : std::filesystem::directory_iterator(song_folder)) {
if (dir_item.is_directory()) {
songs.splice(songs.end(), recursiveSongSearch(dir_item.path()));
}
for (const auto& dir_entry : std::filesystem::recursive_directory_iterator(song_folder)) {
if (not dir_entry.is_regular_file() or dir_entry.path().extension() != ".memon") {
continue;
}
}
std::cout << "Loaded Data::SongList, found " << songs.size() << " songs" << '\n';
}
std::list<std::shared_ptr<Song>> recursiveSongSearch(std::filesystem::path song_or_pack) {
std::list<std::shared_ptr<Song>> res;
// First try : any .memo file in the folder ?
std::filesystem::directory_iterator folder_memo{song_or_pack};
if (
std::any_of(
std::filesystem::begin(folder_memo),
std::filesystem::end(folder_memo),
[](const std::filesystem::directory_entry& de) {return de.path().extension() == ".memo";}
)
) {
throw std::invalid_argument("jujube does not support .memo files for now ...");
}
// Second try : any .memon file in the folder ?
// if yes get the first one
std::filesystem::directory_iterator folder_memon{song_or_pack};
auto memon_path = std::find_if(
std::filesystem::begin(folder_memon),
std::filesystem::end(folder_memon),
[](const std::filesystem::directory_entry& de) {return de.path().extension() == ".memon";}
);
if (memon_path != std::filesystem::end(folder_memon)) {
auto song = std::make_shared<MemonSong>(memon_path->path());
auto song = std::make_shared<MemonSong>(dir_entry.path());
if (not song->chart_levels.empty()) {
res.push_back(song);
}
return res;
}
// Nothing found : recurse in subfolders
for (auto& p : std::filesystem::directory_iterator(song_or_pack)) {
if (p.is_directory()) {
res.splice(res.end(), recursiveSongSearch(p));
songs.push_back(song);
}
}
return res;
fmt::print("Loaded song list, found {} songs\n", songs.size());
return songs;
}
TimeBounds SongDifficulty::get_time_bounds() const {

View File

@ -2,13 +2,13 @@
#include <filesystem>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
#include <SFML/Audio.hpp>
@ -80,20 +80,12 @@ namespace Data {
/*
struct MemoSong : public Song {
explicit MemoSong(const std::unordered_map<std::string, fs::path>& memo_paths);
explicit MemoSong(const std::unordered_map<std::string, std::filesystem::path>& memo_paths);
private:
std::unordered_map<std::string, fs::path> memo_paths;
std::unordered_map<std::string, std::filesystem::path> memo_paths;
};
*/
// Class holding all the necessary song data to run the Music Select screen
class SongList {
public:
SongList(const std::filesystem::path& jujube_path);
std::list<std::shared_ptr<Song>> songs;
};
// Returns the folders conscidered to contain a valid song
// classic memo files should have the .memo extension
std::list<std::shared_ptr<Song>> recursiveSongSearch(std::filesystem::path song_or_pack);
using SongList = std::vector<std::shared_ptr<Song>>;
SongList load_song_list(const std::filesystem::path& jujube_path);
}

View File

@ -21,6 +21,7 @@
#include "Screens/Results/Results.hpp"
#include "Toolkit/UTF8Strings.hpp"
#include "fmt/core.h"
#if defined(__unix__) && defined(__linux__)
#include <X11/Xlib.h>
@ -35,7 +36,7 @@ int main() {
// Load prefs, music, markers
const std::filesystem::path jujube_path = utf8_encoded_string_to_path(whereami::executable_dir());
Data::Preferences preferences{jujube_path};
Data::SongList song_list{jujube_path};
auto song_list = Data::load_song_list(jujube_path);
Resources::SharedResources shared_resources{preferences};
MusicSelect::ScreenResources music_select_resources{shared_resources};
if (shared_resources.markers.find(preferences.options.marker) == shared_resources.markers.end()) {
@ -64,9 +65,9 @@ int main() {
while (window.isOpen()) {
auto chart = music_select.select_chart(window);
if (chart) {
std::cout << "Selected Chart : " << chart->song.title << " [" << chart->difficulty << "]" << '\n';
fmt::print("Selected Chart : {} [{}]\n", chart->song.title, chart->difficulty);
} else {
std::cout << "Exited MusicSelect::Screen without selecting a chart" << '\n';
fmt::print("Exited MusicSelect::Screen without selecting a chart\n");
break;
}

View File

@ -5,13 +5,13 @@
#include <stdexcept>
namespace Resources {
LNMarker::LNMarker(const fs::path& t_folder) :
LNMarker::LNMarker(const std::filesystem::path& t_folder) :
folder(t_folder)
{
if (not fs::is_directory(folder)) {
if (not std::filesystem::is_directory(folder)) {
throw std::invalid_argument(folder.string()+" is not a folder");
}
if (not fs::exists(folder/"long.json")) {
if (not std::filesystem::exists(folder/"long.json")) {
throw std::invalid_argument("LNMarker folder ( "+folder.string()+" ) has no long.json file");
}
std::ifstream marker_json{folder/"long.json"};
@ -128,7 +128,7 @@ namespace Resources {
j.at("tip").at("cycle").get_to(m.tip_cycle);
}
LNMarkers::LNMarkers(const fs::path& jujube_path) {
LNMarkers::LNMarkers(const std::filesystem::path& jujube_path) {
load_from_folder(jujube_path/"markers"/"long");
load_from_folder(jujube_path/"assets"/"markers"/"long");
if (empty()) {
@ -136,9 +136,9 @@ namespace Resources {
}
}
void LNMarkers::load_from_folder(const fs::path& lnmarkers_folder) {
if (fs::exists(lnmarkers_folder)) {
for (auto& p : fs::directory_iterator(lnmarkers_folder)) {
void LNMarkers::load_from_folder(const std::filesystem::path& lnmarkers_folder) {
if (std::filesystem::exists(lnmarkers_folder)) {
for (auto& p : std::filesystem::directory_iterator(lnmarkers_folder)) {
if (p.is_directory()) {
try {
LNMarker m{p.path()};

View File

@ -11,11 +11,9 @@
#include "SpriteSheet.hpp"
#include "SplitSpriteSheet.hpp"
namespace fs = std::filesystem;
namespace Resources {
struct LNMarker {
LNMarker(const fs::path& folder);
LNMarker(const std::filesystem::path& folder);
std::optional<sf::Sprite> get_tail_sprite(sf::Time delta) const;
@ -29,7 +27,7 @@ namespace Resources {
std::optional<sf::Sprite> get_highlight_sprite(sf::Time delta) const;
fs::path folder;
std::filesystem::path folder;
std::string name;
std::size_t fps;
std::size_t size;
@ -46,8 +44,8 @@ namespace Resources {
class LNMarkers : public std::map<std::string, LNMarker> {
public:
LNMarkers(const fs::path& jujube_path);
LNMarkers(const std::filesystem::path& jujube_path);
private:
void load_from_folder(const fs::path& lnmarkers_folder);
void load_from_folder(const std::filesystem::path& lnmarkers_folder);
};
}

View File

@ -8,8 +8,6 @@
#include <sstream>
#include <stdexcept>
namespace fs = std::filesystem;
namespace Resources {
void from_json(const nlohmann::json& j, Marker& m) {
@ -24,13 +22,13 @@ namespace Resources {
j.at("perfect").get_to(m.perfect);
}
Marker::Marker(const fs::path& marker_folder) :
Marker::Marker(const std::filesystem::path& marker_folder) :
folder(marker_folder)
{
if (not fs::is_directory(folder)) {
if (not std::filesystem::is_directory(folder)) {
throw std::invalid_argument(folder.string()+" is not a folder");
}
if (not fs::exists(folder/"marker.json")) {
if (not std::filesystem::exists(folder/"marker.json")) {
throw std::invalid_argument("Marker folder ( "+folder.string()+" ) has no marker.json file");
}
std::ifstream marker_json{folder/"marker.json"};
@ -96,7 +94,7 @@ namespace Resources {
return sprite_sheet.get_sprite(frame, size);
}
Markers::Markers(const fs::path& jujube_path) {
Markers::Markers(const std::filesystem::path& jujube_path) {
load_from_folder(jujube_path/"markers"/"tap");
load_from_folder(jujube_path/"assets"/"markers"/"tap");
if (empty()) {
@ -104,9 +102,9 @@ namespace Resources {
}
}
void Markers::load_from_folder(const fs::path& markers_folder) {
if (fs::exists(markers_folder)) {
for (auto& p : fs::directory_iterator(markers_folder)) {
void Markers::load_from_folder(const std::filesystem::path& markers_folder) {
if (std::filesystem::exists(markers_folder)) {
for (auto& p : std::filesystem::directory_iterator(markers_folder)) {
if (p.is_directory()) {
try {
Marker m{p.path()};

View File

@ -10,8 +10,6 @@
#include "SpriteSheet.hpp"
namespace fs = std::filesystem;
namespace Resources {
enum class MarkerAnimation {
APPROACH,
@ -23,13 +21,13 @@ namespace Resources {
};
struct Marker {
explicit Marker(const fs::path& marker_folder);
explicit Marker(const std::filesystem::path& marker_folder);
std::optional<sf::Sprite> get_sprite(const MarkerAnimation& state, const sf::Time seconds) const;
std::optional<sf::Sprite> get_sprite(const MarkerAnimation& state, const float seconds) const;
std::optional<sf::Sprite> get_sprite(const MarkerAnimation& state, const std::size_t frame) const;
const SpriteSheet& get_sprite_sheet_from_enum(const MarkerAnimation& state) const;
fs::path folder;
std::filesystem::path folder;
std::string name;
std::size_t size; // the side length in pixels
std::size_t fps; // classic jubeat markers are 30 fps
@ -45,8 +43,8 @@ namespace Resources {
class Markers : public std::map<std::string, Marker> {
public:
Markers(const fs::path& jujube_path);
Markers(const std::filesystem::path& jujube_path);
private:
void load_from_folder(const fs::path& markers_folder);
void load_from_folder(const std::filesystem::path& markers_folder);
};
}

View File

@ -7,14 +7,14 @@
namespace Resources {
void from_json(const nlohmann::json& j, SplitSpriteSheet& s) {
s.tex_path = fs::path{j.at("sprite_sheet").get<std::string>()};
s.tex_path = std::filesystem::path{j.at("sprite_sheet").get<std::string>()};
j.at("count").get_to(s.count);
j.at("columns").get_to(s.columns);
j.at("rows").get_to(s.rows);
}
void SplitSpriteSheet::load_and_check(
const fs::path& folder,
const std::filesystem::path& folder,
std::size_t size,
std::size_t fps,
const Toolkit::DurationInFrames& max_duration

View File

@ -9,20 +9,18 @@
#include "../Toolkit/DurationInFrames.hpp"
namespace fs = std::filesystem;
namespace Resources {
// SpriteSheet where individual sprites get their own texture
// (when you cant to tile an animated sprite for ex.)
struct SplitSpriteSheet {
std::vector<sf::Texture> textures;
fs::path tex_path;
std::filesystem::path tex_path;
std::size_t count;
std::size_t columns;
std::size_t rows;
void load_and_check(
const fs::path& folder,
const std::filesystem::path& folder,
std::size_t size,
std::size_t fps,
const Toolkit::DurationInFrames& max_duration

View File

@ -5,14 +5,14 @@
namespace Resources {
void from_json(const nlohmann::json& j, SpriteSheet& s) {
s.tex_path = fs::path{j.at("sprite_sheet").get<std::string>()};
s.tex_path = std::filesystem::path{j.at("sprite_sheet").get<std::string>()};
j.at("count").get_to(s.count);
j.at("columns").get_to(s.columns);
j.at("rows").get_to(s.rows);
}
void SpriteSheet::load_and_check(
const fs::path& folder,
const std::filesystem::path& folder,
std::size_t size,
std::size_t fps,
const Toolkit::DurationInFrames& max_duration

View File

@ -9,18 +9,16 @@
#include "../Toolkit/DurationInFrames.hpp"
namespace fs = std::filesystem;
namespace Resources {
struct SpriteSheet {
sf::Texture tex;
fs::path tex_path;
std::filesystem::path tex_path;
std::size_t count;
std::size_t columns;
std::size_t rows;
void load_and_check(
const fs::path& folder,
const std::filesystem::path& folder,
std::size_t size,
std::size_t fps,
const Toolkit::DurationInFrames& max_duration

View File

@ -7,7 +7,7 @@ namespace MusicSelect {
std::mutex openFromFile_mutex;
MusicLoop::MusicLoop(fs::path music_path, std::optional<sf::Music::TimeSpan> t_loop) {
MusicLoop::MusicLoop(std::filesystem::path music_path, std::optional<sf::Music::TimeSpan> t_loop) {
music = std::make_unique<sf::Music>();
{
std::lock_guard<std::mutex> lock{openFromFile_mutex};
@ -33,7 +33,7 @@ namespace MusicSelect {
};
}
void MusicPreview::play_async(std::optional<fs::path> music_path, std::optional<sf::Music::TimeSpan> loop) {
void MusicPreview::play_async(std::optional<std::filesystem::path> music_path, std::optional<sf::Music::TimeSpan> loop) {
if (not music_path.has_value()) {
return;
}
@ -53,7 +53,7 @@ namespace MusicSelect {
}
}
void MusicPreview::play(std::optional<fs::path> music_path, std::optional<sf::Music::TimeSpan> loop) {
void MusicPreview::play(std::optional<std::filesystem::path> music_path, std::optional<sf::Music::TimeSpan> loop) {
std::thread(&MusicPreview::play_async, this, music_path, loop).detach();
}

View File

@ -9,13 +9,11 @@
#include "../../Toolkit/AffineTransform.hpp"
namespace fs = std::filesystem;
// In this file define stuff to try to handle creating sf::Music objects
// and starting their playback asynchronously while trying to keep some thread-safety
namespace MusicSelect {
struct MusicLoop {
MusicLoop(fs::path music_path, std::optional<sf::Music::TimeSpan> t_loop);
MusicLoop(std::filesystem::path music_path, std::optional<sf::Music::TimeSpan> t_loop);
std::unique_ptr<sf::Music> music;
sf::Music::TimeSpan loop;
Toolkit::AffineTransform<float> fade_out = {0.f, 1.f, 0.f, 1.f}; // placeholder value
@ -23,11 +21,11 @@ namespace MusicSelect {
class MusicPreview {
public:
MusicPreview() = default;
void play(std::optional<fs::path> music_path, std::optional<sf::Music::TimeSpan> loop);
void play(std::optional<std::filesystem::path> music_path, std::optional<sf::Music::TimeSpan> loop);
void stop();
void update();
private:
void play_async(std::optional<fs::path> music_path, std::optional<sf::Music::TimeSpan> loop);
void play_async(std::optional<std::filesystem::path> music_path, std::optional<sf::Music::TimeSpan> loop);
std::optional<MusicLoop> m_music_loop;
std::mutex m_music_loop_mutex;
};

View File

@ -13,10 +13,10 @@
#include "Panels/Panel.hpp"
#include "PanelLayout.hpp"
MusicSelect::Screen::Screen(const Data::SongList& t_song_list, ScreenResources& t_resources) :
MusicSelect::Screen::Screen(const Data::SongList& song_list_, ScreenResources& t_resources) :
HoldsResources(t_resources),
song_list(t_song_list),
ribbon(PanelLayout::title_sort(t_song_list, t_resources), t_resources),
song_list(song_list_),
ribbon(PanelLayout::title_sort(song_list_, t_resources), t_resources),
song_info(t_resources),
main_option_page(t_resources),
options_button(t_resources),

View File

@ -27,7 +27,7 @@ namespace MusicSelect {
// it loads a cache of available songs in the song_list attribute
class Screen : public Toolkit::Debuggable, public HoldsResources {
public:
Screen(const Data::SongList& t_song_list, ScreenResources& t_resources);
Screen(const Data::SongList& song_list, ScreenResources& t_resources);
std::optional<Data::SongDifficulty> select_chart(sf::RenderWindow& window);
void draw_debug(sf::RenderWindow& window);
private:

View File

@ -59,10 +59,7 @@ namespace MusicSelect {
}
PanelLayout PanelLayout::title_sort(const Data::SongList& song_list, ScreenResources& t_resources) {
std::vector<std::shared_ptr<const Data::Song>> songs;
for (auto &&song : song_list.songs) {
songs.push_back(song);
}
Data::SongList songs = song_list;
std::sort(
songs.begin(),
songs.end(),

13
subprojects/fmt.wrap Normal file
View File

@ -0,0 +1,13 @@
[wrap-file]
directory = fmt-9.1.0
source_url = https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz
source_filename = fmt-9.1.0.tar.gz
source_hash = 5dea48d1fcddc3ec571ce2058e13910a0d4a6bab4cc09a809d8b1dd1c88ae6f2
patch_filename = fmt_9.1.0-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_9.1.0-2/get_patch
patch_hash = 23e8c4829f3e63f509b5643fe6bb87cbed39eae9594c451b338475d14d051967
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_9.1.0-2/fmt-9.1.0.tar.gz
wrapdb_version = 9.1.0-2
[provide]
fmt = fmt_dep