From 8d908a0a55aa8edd3f41991d999e2c2fa20994d6 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 8 Sep 2020 09:21:40 -0400 Subject: [PATCH] Add system::join(). Add asset::module(). --- include/asset.hpp | 33 +++++++++++++++++++++++------ include/plugin/Plugin.hpp | 2 +- include/system.hpp | 9 ++++++++ src/app/ModuleWidget.cpp | 4 ++-- src/asset.cpp | 44 +++++++++++++++++++++------------------ src/engine/Module.cpp | 1 + src/patch.cpp | 6 +++--- src/plugin.cpp | 8 +++---- src/plugin/Model.cpp | 4 ++-- src/plugin/Plugin.cpp | 2 +- src/system.cpp | 25 +++++++++++++--------- src/updater.cpp | 2 +- src/window.cpp | 10 ++++----- 13 files changed, 95 insertions(+), 55 deletions(-) diff --git a/include/asset.hpp b/include/asset.hpp index 9ccdb2bd..92e6aa80 100644 --- a/include/asset.hpp +++ b/include/asset.hpp @@ -10,16 +10,37 @@ struct Plugin; } // namespace plugin +namespace engine { +struct Module; +} // namespace engine + + namespace asset { void init(); -/** Returns the path of a system resource. Should only read files from this location. */ -std::string system(std::string filename); -/** Returns the path of a user resource. Can read and write files to this location. */ -std::string user(std::string filename); -/** Returns the path of a resource in the plugin's folder. Should only read files from this location. */ -std::string plugin(plugin::Plugin* plugin, std::string filename); + +/** Returns the path of a system asset. Should only read files from this location. */ +std::string system(std::string filename = ""); + +/** Returns the path of a user asset. Can read and write files to this location. */ +std::string user(std::string filename = ""); + +/** Returns the path of a asset in the plugin's folder. +Plugin assets should be read-only by plugins. +Examples: + asset::plugin(pluginInstance, "samples/00.wav") // "/path/to/Rack/user/folder/plugins/MyPlugin/samples/00.wav" +*/ +std::string plugin(plugin::Plugin* plugin, std::string filename = ""); + +/** Returns the path to an asset in the module patch folder. +The module patch folder is *not* created automatically. Before creating files at these paths, call + system::createDirectories(asset::module(module)) + +Examples: + asset::module(module, "recordings/00.wav") // "/path/to/Rack/user/folder/autosave/modules/1234/recordings/00.wav" +*/ +std::string module(engine::Module* module, const std::string& filename = ""); // Set these before calling init() to override the default paths diff --git a/include/plugin/Plugin.hpp b/include/plugin/Plugin.hpp index e3596ba5..583b77d4 100644 --- a/include/plugin/Plugin.hpp +++ b/include/plugin/Plugin.hpp @@ -73,7 +73,7 @@ struct Plugin { ~Plugin(); void addModel(Model* model); - Model* getModel(std::string slug); + Model* getModel(const std::string& slug); void fromJson(json_t* rootJ); std::string getBrand(); }; diff --git a/include/system.hpp b/include/system.hpp index 7efd1592..8777e002 100644 --- a/include/system.hpp +++ b/include/system.hpp @@ -14,6 +14,15 @@ namespace system { // Filesystem +/** Joins two paths with a directory separator. +If `path2` is an empty string, returns `path1`. +*/ +std::string join(const std::string& path1, const std::string& path2 = ""); +/** Join an arbitrary number of paths, from left to right. */ +template +std::string join(const std::string& path1, const std::string& path2, Paths... paths) { + return join(join(path1, path2), paths...); +} /** Returns a list of all entries (directories, files, symbolic links, etc) in a directory. `depth` is the number of directories to recurse. 0 depth does not recurse. -1 depth recurses infinitely. */ diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 6fcebd1c..68eed5d0 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -674,7 +674,7 @@ void ModuleWidget::loadAction(std::string filename) { } void ModuleWidget::loadTemplate() { - std::string templatePath = model->getUserPresetDir() + "/" + "template.vcvm"; + std::string templatePath = system::join(model->getUserPresetDir(), "template.vcvm"); try { load(templatePath); } @@ -735,7 +735,7 @@ void ModuleWidget::saveTemplate() { std::string presetDir = model->getUserPresetDir(); system::createDirectories(presetDir); - std::string templatePath = presetDir + "/" + "template.vcvm"; + std::string templatePath = system::join(presetDir, "template.vcvm"); save(templatePath); } diff --git a/src/asset.cpp b/src/asset.cpp index 36be9175..b26955a4 100644 --- a/src/asset.cpp +++ b/src/asset.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -29,7 +30,7 @@ namespace asset { static void initSystemDir() { - if (systemDir != "") + if (!systemDir.empty()) return; if (settings::devMode) { @@ -76,7 +77,7 @@ static void initSystemDir() { static void initUserDir() { - if (userDir != "") + if (!userDir.empty()) return; if (settings::devMode) { @@ -89,15 +90,13 @@ static void initUserDir() { wchar_t documentsBufW[MAX_PATH] = L"."; HRESULT result = SHGetFolderPathW(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, documentsBufW); assert(result == S_OK); - userDir = string::U16toU8(documentsBufW); - userDir += "/Rack"; + userDir = system::join(string::U16toU8(documentsBufW), "Rack"); #endif #if defined ARCH_MAC // Get home directory struct passwd* pw = getpwuid(getuid()); assert(pw); - userDir = pw->pw_dir; - userDir += "/Documents/Rack"; + userDir = system::join(pw->pw_dir, "Documents/Rack"); #endif #if defined ARCH_LIN // Get home directory @@ -107,8 +106,7 @@ static void initUserDir() { assert(pw); homeBuf = pw->pw_dir; } - userDir = homeBuf; - userDir += "/.Rack"; + userDir = system::join(homeBuf, ".Rack"); #endif } @@ -120,34 +118,40 @@ void init() { // Set paths if (settings::devMode) { - pluginsPath = userDir + "/plugins"; - settingsPath = userDir + "/settings.json"; - autosavePath = userDir + "/autosave"; - templatePath = userDir + "/template.vcv"; + pluginsPath = system::join(userDir, "plugins"); + settingsPath = system::join(userDir, "settings.json"); + autosavePath = system::join(userDir, "autosave"); + templatePath = system::join(userDir, "template.vcv"); } else { - logPath = userDir + "/log.txt"; - pluginsPath = userDir + "/plugins-v" + ABI_VERSION; - settingsPath = userDir + "/settings-v" + ABI_VERSION + ".json"; - autosavePath = userDir + "/autosave-v" + ABI_VERSION; - templatePath = userDir + "/template-v" + ABI_VERSION + ".vcv"; + logPath = system::join(userDir, "log.txt"); + pluginsPath = system::join(userDir, "plugins-v" + ABI_VERSION); + settingsPath = system::join(userDir, "settings-v" + ABI_VERSION + ".json"); + autosavePath = system::join(userDir, "autosave-v" + ABI_VERSION); + templatePath = system::join(userDir, "template-v" + ABI_VERSION + ".vcv"); } } std::string system(std::string filename) { - return systemDir + "/" + filename; + return system::join(systemDir, filename); } std::string user(std::string filename) { - return userDir + "/" + filename; + return system::join(userDir, filename); } std::string plugin(plugin::Plugin* plugin, std::string filename) { assert(plugin); - return plugin->path + "/" + filename; + return system::join(plugin->path, filename); +} + + +std::string module(engine::Module* module, const std::string& filename) { + assert(module); + return system::join(autosavePath, "modules", std::to_string(module->id), filename); } diff --git a/src/engine/Module.cpp b/src/engine/Module.cpp index 585e9f10..08c778d7 100644 --- a/src/engine/Module.cpp +++ b/src/engine/Module.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace rack { diff --git a/src/patch.cpp b/src/patch.cpp index d4a4490b..613f0c15 100644 --- a/src/patch.cpp +++ b/src/patch.cpp @@ -153,7 +153,7 @@ void PatchManager::saveAutosave() { // Write to temporary path and then rename it to the correct path system::createDirectories(asset::autosavePath); - std::string patchPath = asset::autosavePath + "/patch.json"; + std::string patchPath = system::join(asset::autosavePath, "patch.json"); std::string tmpPath = patchPath + ".tmp"; FILE* file = std::fopen(tmpPath.c_str(), "w"); if (!file) { @@ -189,7 +189,7 @@ void PatchManager::load(std::string path) { if (isPatchLegacyPre2(path)) { // Copy the .vcv file directly to "patch.json". - system::copy(path, asset::autosavePath + "/patch.json"); + system::copy(path, system::join(asset::autosavePath, "patch.json")); } else { // Extract the .vcv file as a .tar.zst archive. @@ -237,7 +237,7 @@ void PatchManager::loadTemplateDialog() { void PatchManager::loadAutosave() { INFO("Loading autosave"); - std::string patchPath = asset::autosavePath + "/patch.json"; + std::string patchPath = system::join(asset::autosavePath, "patch.json"); FILE* file = std::fopen(patchPath.c_str(), "r"); if (!file) { // Exit silently diff --git a/src/plugin.cpp b/src/plugin.cpp index 18bf2327..fc388fc8 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -79,7 +79,7 @@ static InitCallback loadPluginCallback(Plugin* plugin) { #elif ARCH_MAC libraryExt = "dylib"; #endif - std::string libraryPath = plugin->path + "/plugin." + libraryExt; + std::string libraryPath = system::join(plugin->path, "plugin." + libraryExt); // Check file existence if (!system::isFile(libraryPath)) @@ -124,7 +124,7 @@ static Plugin* loadPlugin(std::string path) { } // Load plugin.json - std::string manifestFilename = (path == "") ? asset::system("Core.json") : (path + "/plugin.json"); + std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json"); FILE* file = std::fopen(manifestFilename.c_str(), "r"); if (!file) throw Exception(string::f("Manifest file %s does not exist", manifestFilename.c_str())); @@ -226,7 +226,7 @@ void init() { #else std::string fundamentalSrc = asset::system("Fundamental.zip"); #endif - std::string fundamentalDir = asset::pluginsPath + "/Fundamental"; + std::string fundamentalDir = system::join(asset::pluginsPath, "Fundamental"); if (!settings::devMode && !getPlugin("Fundamental") && system::isFile(fundamentalSrc)) { INFO("Extracting bundled Fundamental package"); system::unarchiveToFolder(fundamentalSrc.c_str(), asset::pluginsPath.c_str()); @@ -471,7 +471,7 @@ void syncUpdate(Update* update) { INFO("Downloading plugin %s %s %s", update->pluginSlug.c_str(), update->version.c_str(), APP_ARCH.c_str()); // Download zip - std::string pluginDest = asset::pluginsPath + "/" + update->pluginSlug + ".zip"; + std::string pluginDest = system::join(asset::pluginsPath, update->pluginSlug + ".zip"); if (!network::requestDownload(downloadUrl, pluginDest, &update->progress, cookies)) { WARN("Plugin %s download was unsuccessful", update->pluginSlug.c_str()); return; diff --git a/src/plugin/Model.cpp b/src/plugin/Model.cpp index 65d12772..68b0266e 100644 --- a/src/plugin/Model.cpp +++ b/src/plugin/Model.cpp @@ -59,12 +59,12 @@ std::string Model::getFullName() { std::string Model::getFactoryPresetDir() { - return asset::plugin(plugin, "presets/" + slug); + return asset::plugin(plugin, system::join("presets", slug)); } std::string Model::getUserPresetDir() { - return asset::user("presets/" + plugin->slug + "/" + slug); + return asset::user(system::join("presets", plugin->slug, slug)); } diff --git a/src/plugin/Plugin.cpp b/src/plugin/Plugin.cpp index 591fa9ed..26123057 100644 --- a/src/plugin/Plugin.cpp +++ b/src/plugin/Plugin.cpp @@ -22,7 +22,7 @@ void Plugin::addModel(Model* model) { models.push_back(model); } -Model* Plugin::getModel(std::string slug) { +Model* Plugin::getModel(const std::string& slug) { for (Model* model : models) { if (model->slug == slug) { return model; diff --git a/src/system.cpp b/src/system.cpp index 31ad17ad..a1766e2c 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -50,11 +50,16 @@ namespace rack { namespace system { +std::string join(const std::string& path1, const std::string& path2) { + return (fs::u8path(path1) / fs::u8path(path2)).generic_u8string(); +} + + std::list getEntries(const std::string& dirPath, int depth) { try { std::list entries; for (auto& entry : fs::directory_iterator(fs::u8path(dirPath))) { - std::string subEntry = entry.path().u8string(); + std::string subEntry = entry.path().generic_u8string(); entries.push_back(subEntry); // Recurse if depth > 0 (limited recursion) or depth < 0 (infinite recursion). if (depth != 0) { @@ -174,7 +179,7 @@ int removeRecursively(const std::string& path) { std::string getWorkingDirectory() { try { - return fs::current_path().u8string(); + return fs::current_path().generic_u8string(); } catch (fs::filesystem_error& e) { throw Exception(e.what()); @@ -194,7 +199,7 @@ void setWorkingDirectory(const std::string& path) { std::string getTempDir() { try { - return fs::temp_directory_path().u8string(); + return fs::temp_directory_path().generic_u8string(); } catch (fs::filesystem_error& e) { throw Exception(e.what()); @@ -204,7 +209,7 @@ std::string getTempDir() { std::string getAbsolute(const std::string& path) { try { - return fs::absolute(fs::u8path(path)).u8string(); + return fs::absolute(fs::u8path(path)).generic_u8string(); } catch (fs::filesystem_error& e) { throw Exception(e.what()); @@ -214,7 +219,7 @@ std::string getAbsolute(const std::string& path) { std::string getCanonical(const std::string& path) { try { - return fs::canonical(fs::u8path(path)).u8string(); + return fs::canonical(fs::u8path(path)).generic_u8string(); } catch (fs::filesystem_error& e) { throw Exception(e.what()); @@ -224,7 +229,7 @@ std::string getCanonical(const std::string& path) { std::string getDirectory(const std::string& path) { try { - return fs::u8path(path).parent_path().u8string(); + return fs::u8path(path).parent_path().generic_u8string(); } catch (fs::filesystem_error& e) { throw Exception(e.what()); @@ -234,7 +239,7 @@ std::string getDirectory(const std::string& path) { std::string getFilename(const std::string& path) { try { - return fs::u8path(path).filename().u8string(); + return fs::u8path(path).filename().generic_u8string(); } catch (fs::filesystem_error& e) { throw Exception(e.what()); @@ -244,7 +249,7 @@ std::string getFilename(const std::string& path) { std::string getStem(const std::string& path) { try { - return fs::u8path(path).stem().u8string(); + return fs::u8path(path).stem().generic_u8string(); } catch (fs::filesystem_error& e) { throw Exception(e.what()); @@ -254,7 +259,7 @@ std::string getStem(const std::string& path) { std::string getExtension(const std::string& path) { try { - return fs::u8path(path).extension().u8string(); + return fs::u8path(path).extension().generic_u8string(); } catch (fs::filesystem_error& e) { throw Exception(e.what()); @@ -410,7 +415,7 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder std::string entryPath = archive_entry_pathname(entry); if (!fs::u8path(entryPath).is_relative()) throw Exception(string::f("unzipToFolder() does not support absolute paths: %s", entryPath.c_str())); - entryPath = fs::absolute(fs::u8path(entryPath), fs::u8path(folderPath)).u8string(); + entryPath = fs::absolute(fs::u8path(entryPath), fs::u8path(folderPath)).generic_u8string(); #if defined ARCH_WIN archive_entry_copy_pathname_w(entry, string::U8toU16(entryPath).c_str()); #else diff --git a/src/updater.cpp b/src/updater.cpp index ba9ddda8..39a86272 100644 --- a/src/updater.cpp +++ b/src/updater.cpp @@ -76,7 +76,7 @@ void update() { system::runProcessDetached(path); #elif defined ARCH_MAC std::string cmd; - // std::string appPath = asset::userDir + "/Rack.app"; + // std::string appPath = system::join(asset::userDir, "Rack.app"); // cmd = "rm -rf '" + appPath + "'"; // std::system(cmd.c_str()); // // Unzip app using Apple's unzipper, since Rack's unzipper doesn't handle the metadata stuff correctly. diff --git a/src/window.cpp b/src/window.cpp index 047548e8..94b29357 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -434,10 +434,10 @@ void Window::screenshot(float zoom) { std::string screenshotsDir = asset::user("screenshots"); system::createDirectory(screenshotsDir); for (plugin::Plugin* p : plugin::plugins) { - std::string dir = screenshotsDir + "/" + p->slug; + std::string dir = system::join(screenshotsDir, p->slug); system::createDirectory(dir); for (plugin::Model* model : p->models) { - std::string filename = dir + "/" + model->slug + ".png"; + std::string filename = system::join(dir, model->slug + ".png"); // Skip model if screenshot already exists if (system::isFile(filename)) continue; @@ -468,9 +468,9 @@ void Window::screenshot(float zoom) { for (int y = 0; y < height / 2; y++) { int flipY = height - y - 1; uint8_t tmp[width * 4]; - memcpy(tmp, &data[y * width * 4], width * 4); - memcpy(&data[y * width * 4], &data[flipY * width * 4], width * 4); - memcpy(&data[flipY * width * 4], tmp, width * 4); + std::memcpy(tmp, &data[y * width * 4], width * 4); + std::memcpy(&data[y * width * 4], &data[flipY * width * 4], width * 4); + std::memcpy(&data[flipY * width * 4], tmp, width * 4); } // Write pixels to PNG