diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt index 8f993b8c..9d13eac0 100644 --- a/vst/CMakeLists.txt +++ b/vst/CMakeLists.txt @@ -19,6 +19,7 @@ set(VSTPLUGIN_SOURCES SfizzVstState.cpp SfizzFileScan.cpp SfizzForeignPaths.cpp + SfizzSettings.cpp VstPluginFactory.cpp X11RunLoop.cpp NativeHelpers.cpp @@ -31,6 +32,7 @@ set(VSTPLUGIN_HEADERS SfizzVstState.h SfizzFileScan.h SfizzForeignPaths.h + SfizzSettings.h X11RunLoop.h NativeHelpers.h FileTrie.h) @@ -38,6 +40,7 @@ set(VSTPLUGIN_HEADERS if(APPLE) set(VSTPLUGIN_MAC_SOURCES SfizzForeignPaths.mm + SfizzSettings.mm NativeHelpers.mm) list(APPEND VSTPLUGIN_SOURCES ${VSTPLUGIN_MAC_SOURCES}) set_property(SOURCE ${VSTPLUGIN_MAC_SOURCES} APPEND_STRING @@ -53,7 +56,8 @@ if(WIN32) endif() target_link_libraries(${VSTPLUGIN_PRJ_NAME} PRIVATE ${PROJECT_NAME}::${PROJECT_NAME} - PRIVATE sfizz_editor) + PRIVATE sfizz_editor + PRIVATE sfizz-pugixml) target_include_directories(${VSTPLUGIN_PRJ_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES diff --git a/vst/SfizzForeignPaths.cpp b/vst/SfizzForeignPaths.cpp index 17319997..df7009df 100644 --- a/vst/SfizzForeignPaths.cpp +++ b/vst/SfizzForeignPaths.cpp @@ -15,8 +15,6 @@ fs::path getAriaPathSetting(const char* name) { fs::path path; - HKEY key = 0; - std::unique_ptr nameW; unsigned nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0); if (nameSize == 0) @@ -27,17 +25,22 @@ fs::path getAriaPathSetting(const char* name) const WCHAR ariaKeyPath[] = L"Software\\Plogue Art et Technologie, Inc\\Aria"; - if (RegOpenKeyExW(HKEY_CURRENT_USER, ariaKeyPath, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { - WCHAR valueBuffer[32768 + 1]; - DWORD valueSize = sizeof(valueBuffer) - sizeof(WCHAR); - if (RegQueryValueExW(key, nameW.get(), nullptr, nullptr, reinterpret_cast(valueBuffer), &valueSize) == ERROR_SUCCESS) { - valueBuffer[32768] = L'\0'; - path = fs::path(valueBuffer); - } - RegCloseKey(key); - } + HKEY key = nullptr; + LSTATUS status = RegOpenKeyExW(HKEY_CURRENT_USER, ariaKeyPath, 0, KEY_QUERY_VALUE, &key); + if (status != ERROR_SUCCESS) + return {}; - return path; + WCHAR valueW[32768]; + DWORD valueSize = sizeof(valueW); + DWORD valueType; + status = RegQueryValueExW( + key, nameW.get(), nullptr, + &valueType, reinterpret_cast(valueW), &valueSize); + RegCloseKey(key); + if (status != ERROR_SUCCESS || (valueType != REG_SZ && valueType != REG_EXPAND_SZ)) + return {}; + + return fs::path(valueW); } #elif defined(__APPLE__) // implementation in SfizzForeignPaths.mm diff --git a/vst/SfizzSettings.cpp b/vst/SfizzSettings.cpp new file mode 100644 index 00000000..9560d16d --- /dev/null +++ b/vst/SfizzSettings.cpp @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzSettings.h" +#include + +std::string SfizzSettings::load_or(const char* key, absl::string_view defaultValue) +{ + absl::optional optValue = load(key); + return optValue ? *optValue : std::string(defaultValue); +} + +#if defined(_WIN32) +#include + +static HKEY openRegistryKey() +{ + LSTATUS status; + HKEY root = HKEY_CURRENT_USER; + HKEY parent = root; + HKEY key = nullptr; + for (const WCHAR* component : {L"Software", L"SFZTools", L"sfizz"}) { + status = RegCreateKeyExW( + parent, component, 0, nullptr, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &key, nullptr); + if (parent != root) + RegCloseKey(parent); + if (status != ERROR_SUCCESS) + return nullptr; + parent = key; + } + return key; +} + +static WCHAR* stringToWideChar(const char *str, int strCch = -1) +{ + unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0); + if (strSize == 0) + return {}; + std::unique_ptr strW(new WCHAR[strSize]); + if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0) + return {}; + return strW.release(); +} + +static char* stringToUTF8(const wchar_t *strW, int strWCch = -1) +{ + unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr); + if (strSize == 0) + return {}; + std::unique_ptr str(new char[strSize]); + if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0) + return {}; + return str.release(); +} + +absl::optional SfizzSettings::load(const char* name) +{ + std::unique_ptr nameW { stringToWideChar(name) }; + if (!nameW) + return {}; + + HKEY key = openRegistryKey(); + if (!key) + return {}; + + WCHAR valueW[32768]; + DWORD valueSize = sizeof(valueW); + DWORD valueType; + LSTATUS status = RegQueryValueExW( + key, nameW.get(), nullptr, + &valueType, reinterpret_cast(valueW), &valueSize); + RegCloseKey(key); + if (status != ERROR_SUCCESS || (valueType != REG_SZ && valueType != REG_EXPAND_SZ)) + return {}; + + std::unique_ptr value { stringToUTF8(valueW) }; + if (!value) + return {}; + + return std::string(value.get()); +} + +bool SfizzSettings::store(const char* name, absl::string_view value) +{ + std::unique_ptr nameW { stringToWideChar(name) }; + std::unique_ptr valueW { stringToWideChar(std::string(value).c_str()) }; + if (!nameW || !valueW) + return false; + + HKEY key = openRegistryKey(); + if (!key) + return {}; + + LSTATUS status = RegSetValueExW( + key, nameW.get(), 0, RRF_RT_REG_SZ, + reinterpret_cast(valueW.get()), + (wcslen(valueW.get()) + 1) * sizeof(WCHAR)); + RegCloseKey(key); + + return status == ERROR_SUCCESS; +} +#elif defined(__APPLE__) + // implementation in SfizzSettings.mm +#else +#include +#include + +static const fs::path getSettingsPath() +{ + fs::path dirPath; + const char* env; + if ((env = getenv("XDG_CONFIG_HOME")) && env[0] == '/') + dirPath = fs::path(env); + else if ((env = getenv("HOME")) && env[0] == '/') + dirPath = fs::path(env) / ".config"; + else + return {}; + dirPath /= "SFZTools"; + dirPath /= "sfizz"; + std::error_code ec; + if (!fs::create_directories(dirPath, ec)) + return {}; + return dirPath / "settings.xml"; +} + +absl::optional SfizzSettings::load(const char* key) +{ + const fs::path path = getSettingsPath(); + if (path.empty()) + return {}; + + pugi::xml_document doc; + if (!doc.load_file(path.c_str())) + return {}; + + pugi::xml_node root = doc.child("properties"); + if (!root) + return {}; + + pugi::xml_node entry = root.find_child_by_attribute("entry", "key", key); + if (!entry) + return {}; + + return std::string(entry.text().get()); +} + +bool SfizzSettings::store(const char* key, absl::string_view value) +{ + const fs::path path = getSettingsPath(); + if (path.empty()) + return false; + + pugi::xml_document doc; + doc.load_file(path.c_str()); + + pugi::xml_node root = doc.child("properties"); + if (!root) + root = doc.append_child("properties"); + + pugi::xml_node entry = root.find_child_by_attribute("entry", "key", key); + if (!entry) { + entry = root.append_child("entry"); + entry.append_attribute("key").set_value(key); + } + entry.text().set(std::string(value).c_str()); + + return doc.save_file(path.c_str()); +} +#endif diff --git a/vst/SfizzSettings.h b/vst/SfizzSettings.h new file mode 100644 index 00000000..2fcc2927 --- /dev/null +++ b/vst/SfizzSettings.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include + +class SfizzSettings { +public: + absl::optional load(const char* key); + std::string load_or(const char* key, absl::string_view defaultValue); + bool store(const char* key, absl::string_view value); +}; diff --git a/vst/SfizzSettings.mm b/vst/SfizzSettings.mm new file mode 100644 index 00000000..9ddbaeb4 --- /dev/null +++ b/vst/SfizzSettings.mm @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzSettings.h" + +#if defined(__APPLE__) +#import +#if !__has_feature(objc_arc) +#error This source file requires ARC +#endif + +static NSUserDefaults* getUserDefaults() +{ + return [[NSUserDefaults alloc] initWithSuiteName:@"tools.sfz.sfizz"];; +} + +absl::optional SfizzSettings::load(const char* key) +{ + NSUserDefaults* ud = getUserDefaults(); + NSString* value = [ud stringForKey:[NSString stringWithUTF8String:key]]; + if (!value) + return {}; + return std::string(value.UTF8String); +} + +bool SfizzSettings::store(const char* key, absl::string_view value) +{ + NSUserDefaults* ud = getUserDefaults(); + NSString* object = + [[NSString alloc] initWithBytes:value.data() + length:(NSUInteger)value.size() encoding:NSUTF8StringEncoding]; + [ud setObject:object forKey:[NSString stringWithUTF8String:key]]; + return true; +} +#endif