From 87e9fdd0ac832b58ddf92837f3a4e431eb3b21f6 Mon Sep 17 00:00:00 2001 From: flyinghead Date: Tue, 19 Jan 2021 16:58:26 +0100 Subject: [PATCH] allow subfolders in custom texture folders make file iterator to be used by custom tex and game scanner added some missing nowide::getenv --- core/log/LogManager.cpp | 2 +- core/oslib/directory.h | 130 ++++++++++++++++++++++++++++++++++++ core/rend/CustomTexture.cpp | 81 ++++++++-------------- core/rend/CustomTexture.h | 1 + core/rend/game_scanner.h | 85 ++++++++--------------- core/rend/gui_util.cpp | 2 +- 6 files changed, 191 insertions(+), 110 deletions(-) diff --git a/core/log/LogManager.cpp b/core/log/LogManager.cpp index 2f6e6977c..f45e17ef9 100644 --- a/core/log/LogManager.cpp +++ b/core/log/LogManager.cpp @@ -138,7 +138,7 @@ LogManager::LogManager() FileLogListener *listener = new FileLogListener(logPath); if (!listener->IsValid()) { - const char *home = getenv("HOME"); + const char *home = nowide::getenv("HOME"); if (home != nullptr) { delete listener; diff --git a/core/oslib/directory.h b/core/oslib/directory.h index 6640a52dd..3924b9bf7 100644 --- a/core/oslib/directory.h +++ b/core/oslib/directory.h @@ -125,3 +125,133 @@ inline int mkdir(const char *path, mode_t mode) { #endif } +// iterate depth-first over the files contained in a folder hierarchy +class DirectoryTree +{ +public: + struct item { + std::string name; + std::string parentPath; + }; + + class iterator + { + private: + iterator(DIR *dir, std::string pathname) { + if (dir != nullptr) + { + dirs.push_back(dir); + pathnames.push_back(pathname); + advance(); + } + } + + public: + ~iterator() { + for (DIR *dir : dirs) + flycast::closedir(dir); + } + + const item *operator->() { + if (direntry == nullptr) + throw std::runtime_error("null iterator"); + return ¤tItem; + } + + const item& operator*() const { + if (direntry == nullptr) + throw std::runtime_error("null iterator"); + return currentItem; + } + + // Prefix increment + iterator& operator++() { + advance(); + return *this; + } + + // Basic (in)equality implementations, just intended to work when comparing with end() or this + friend bool operator==(const iterator& a, const iterator& b) { + return a.direntry == b.direntry; + } + + friend bool operator!=(const iterator& a, const iterator& b) { + return a.direntry != b.direntry; + } + + private: + void advance() + { + while (!dirs.empty()) + { + direntry = flycast::readdir(dirs.back()); + if (direntry == nullptr) + { + flycast::closedir(dirs.back()); + dirs.pop_back(); + pathnames.pop_back(); + continue; + } + currentItem.name = direntry->d_name; + if (currentItem.name == "." || currentItem.name == "..") + continue; + std::string childPath = pathnames.back() + "/" + currentItem.name; + bool isDir = false; +#ifndef _WIN32 + if (entry->d_type == DT_DIR) + isDir = true; + else if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) +#endif + { + struct stat st; + if (flycast::stat(childPath.c_str(), &st) != 0) + continue; + if (S_ISDIR(st.st_mode)) + isDir = true; + } + if (!isDir) + { + currentItem.parentPath = pathnames.back(); + break; + } + + DIR *childDir = flycast::opendir(childPath.c_str()); + if (childDir == nullptr) + { + INFO_LOG(COMMON, "Cannot read directory '%s'", childPath.c_str()); + } + else + { + dirs.push_back(childDir); + pathnames.push_back(childPath); + } + } + } + + std::vector dirs; + std::vector pathnames; + dirent *direntry = nullptr; + item currentItem; + + friend class DirectoryTree; + }; + + DirectoryTree(const std::string& root) : root(root) { + } + + iterator begin() + { + DIR *dir = flycast::opendir(root.c_str()); + if (dir == nullptr) + INFO_LOG(COMMON, "Cannot read directory '%s'", root.c_str()); + + return iterator(dir, root); + } + iterator end() + { + return iterator(nullptr, root); + } + +private: + const std::string& root; +}; diff --git a/core/rend/CustomTexture.cpp b/core/rend/CustomTexture.cpp index 8109b8d00..e782e99ec 100644 --- a/core/rend/CustomTexture.cpp +++ b/core/rend/CustomTexture.cpp @@ -20,10 +20,7 @@ #include "cfg/cfg.h" #include "oslib/directory.h" -#include -#include #include -#include #define STB_IMAGE_IMPLEMENTATION #define STBI_ONLY_JPEG #define STBI_ONLY_PNG @@ -41,33 +38,33 @@ void CustomTexture::LoaderThread() BaseTextureCacheData *texture; do { - texture = NULL; - - work_queue_mutex.lock(); - if (!work_queue.empty()) + texture = nullptr; { - texture = work_queue.back(); - work_queue.pop_back(); + std::unique_lock lock(work_queue_mutex); + if (!work_queue.empty()) + { + texture = work_queue.back(); + work_queue.pop_back(); + } } - work_queue_mutex.unlock(); - if (texture != NULL) + if (texture != nullptr) { texture->ComputeHash(); - if (texture->custom_image_data != NULL) + if (texture->custom_image_data != nullptr) { free(texture->custom_image_data); - texture->custom_image_data = NULL; + texture->custom_image_data = nullptr; } if (!texture->dirty) { int width, height; u8 *image_data = LoadCustomTexture(texture->texture_hash, width, height); - if (image_data == NULL) + if (image_data == nullptr) { image_data = LoadCustomTexture(texture->old_texture_hash, width, height); } - if (image_data != NULL) + if (image_data != nullptr) { texture->custom_width = width; texture->custom_height = height; @@ -77,7 +74,7 @@ void CustomTexture::LoaderThread() texture->custom_load_in_progress--; } - } while (texture != NULL); + } while (texture != nullptr); wakeup_thread.Wait(); } @@ -105,8 +102,8 @@ bool CustomTexture::Init() { textures_path = get_readonly_data_path("textures/" + game_id) + "/"; - DIR *dir = opendir(textures_path.c_str()); - if (dir != NULL) + DIR *dir = flycast::opendir(textures_path.c_str()); + if (dir != nullptr) { INFO_LOG(RENDERER, "Found custom textures directory: %s", textures_path.c_str()); custom_textures_available = true; @@ -123,9 +120,10 @@ void CustomTexture::Terminate() if (initialized) { initialized = false; - work_queue_mutex.lock(); - work_queue.clear(); - work_queue_mutex.unlock(); + { + std::unique_lock lock(work_queue_mutex); + work_queue.clear(); + } wakeup_thread.Set(); loader_thread.WaitToEnd(); texture_map.clear(); @@ -154,9 +152,10 @@ void CustomTexture::LoadCustomTextureAsync(BaseTextureCacheData *texture_data) return; texture_data->custom_load_in_progress++; - work_queue_mutex.lock(); - work_queue.insert(work_queue.begin(), texture_data); - work_queue_mutex.unlock(); + { + std::unique_lock lock(work_queue_mutex); + work_queue.insert(work_queue.begin(), texture_data); + } wakeup_thread.Set(); } @@ -238,35 +237,14 @@ void CustomTexture::DumpTexture(u32 hash, int w, int h, TextureType textype, voi void CustomTexture::LoadMap() { texture_map.clear(); - DIR *dir = flycast::opendir(textures_path.c_str()); - if (dir == nullptr) - return; - while (true) + DirectoryTree tree(textures_path); + for (const DirectoryTree::item& item : tree) { - struct dirent *entry = flycast::readdir(dir); - if (entry == nullptr) - break; - std::string name(entry->d_name); - if (name == "." || name == "..") - continue; - std::string child_path = textures_path + name; -#ifndef _WIN32 - if (entry->d_type == DT_DIR) - continue; - if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) -#endif - { - struct stat st; - if (flycast::stat(child_path.c_str(), &st) != 0) - continue; - if (S_ISDIR(st.st_mode)) - continue; - } - std::string extension = get_file_extension(name); + std::string extension = get_file_extension(item.name); if (extension != "jpg" && extension != "jpeg" && extension != "png") continue; - std::string::size_type dotpos = name.find_last_of('.'); - std::string basename = name.substr(0, dotpos); + std::string::size_type dotpos = item.name.find_last_of('.'); + std::string basename = item.name.substr(0, dotpos); char *endptr; u32 hash = (u32)strtoll(basename.c_str(), &endptr, 16); if (endptr - basename.c_str() < (ptrdiff_t)basename.length()) @@ -274,8 +252,7 @@ void CustomTexture::LoadMap() INFO_LOG(RENDERER, "Invalid hash %s", basename.c_str()); continue; } - texture_map[hash] = child_path; + texture_map[hash] = item.parentPath + "/" + item.name; } - flycast::closedir(dir); custom_textures_available = !texture_map.empty(); } diff --git a/core/rend/CustomTexture.h b/core/rend/CustomTexture.h index e27f49e31..c005e78c7 100644 --- a/core/rend/CustomTexture.h +++ b/core/rend/CustomTexture.h @@ -24,6 +24,7 @@ #include #include #include +#include class CustomTexture { public: diff --git a/core/rend/game_scanner.h b/core/rend/game_scanner.h index 93ca51e82..c806c7aa3 100644 --- a/core/rend/game_scanner.h +++ b/core/rend/game_scanner.h @@ -55,7 +55,7 @@ class GameScanner void add_game_directory(const std::string& path) { - //printf("Exploring %s\n", path.c_str()); + // FIXME this won't work anymore if (game_list.size() == 0) { ++empty_folders_scanned; @@ -67,63 +67,36 @@ class GameScanner content_path_looks_incorrect = false; } - DIR *dir = flycast::opendir(path.c_str()); - if (dir == NULL) - return; - while (running) - { - dirent *entry = flycast::readdir(dir); - if (entry == NULL) - break; - std::string name(entry->d_name); - if (name == "." || name == "..") + DirectoryTree tree(path); + for (const DirectoryTree::item& item : tree) + { + std::string name(item.name); + std::string child_path = item.parentPath + "/" + name; + + std::string extension = get_file_extension(name); + if (extension == "zip" || extension == "7z") + { + std::string basename = get_file_basename(name); + string_tolower(basename); + auto it = arcade_games.find(basename); + if (it == arcade_games.end()) + continue; + name = name + " (" + std::string(it->second->description) + ")"; + } + else if (extension == "chd" || extension == "gdi") + { + // Hide arcade gdroms + std::string basename = get_file_basename(name); + string_tolower(basename); + if (arcade_gdroms.count(basename) != 0) + continue; + } + else if ((settings.dreamcast.HideLegacyNaomiRoms + || (extension != "bin" && extension != "lst" && extension != "dat")) + && extension != "cdi" && extension != "cue") continue; - std::string child_path = path + "/" + name; - bool is_dir = false; -#ifndef _WIN32 - if (entry->d_type == DT_DIR) - is_dir = true; - if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) -#endif - { - struct stat st; - if (flycast::stat(child_path.c_str(), &st) != 0) - continue; - if (S_ISDIR(st.st_mode)) - is_dir = true; - } - if (is_dir) - { - add_game_directory(child_path); - } - else - { - std::string extension = get_file_extension(name); - if (extension == "zip" || extension == "7z") - { - std::string basename = get_file_basename(name); - string_tolower(basename); - auto it = arcade_games.find(basename); - if (it == arcade_games.end()) - continue; - name = name + " (" + std::string(it->second->description) + ")"; - } - else if (extension == "chd" || extension == "gdi") - { - // Hide arcade gdroms - std::string basename = get_file_basename(name); - string_tolower(basename); - if (arcade_gdroms.count(basename) != 0) - continue; - } - else if ((settings.dreamcast.HideLegacyNaomiRoms - || (extension != "bin" && extension != "lst" && extension != "dat")) - && extension != "cdi" && extension != "cue") - continue; - insert_game(GameMedia{ name, child_path }); - } + insert_game(GameMedia{ name, child_path }); } - flycast::closedir(dir); } public: diff --git a/core/rend/gui_util.cpp b/core/rend/gui_util.cpp index a5b5d3442..c161772ea 100644 --- a/core/rend/gui_util.cpp +++ b/core/rend/gui_util.cpp @@ -111,7 +111,7 @@ void select_directory_popup(const char *prompt, float scaling, StringCallback ca if (select_current_directory == PSEUDO_ROOT) { error_message = "Storage Locations"; - const char *home = getenv("REICAST_HOME"); + const char *home = nowide::getenv("REICAST_HOME"); while (home != NULL) { const char *pcolon = strchr(home, ':');