@@ -27,7 +27,7 @@ E.g. | |||||
DEPRECATED void foo(); | DEPRECATED void foo(); | ||||
*/ | */ | ||||
#if defined(__GNUC__) || defined(__clang__) | #if defined(__GNUC__) || defined(__clang__) | ||||
#define DEPRECATED __attribute__ ((deprecated)) | |||||
#define DEPRECATED __attribute__((deprecated)) | |||||
#elif defined(_MSC_VER) | #elif defined(_MSC_VER) | ||||
#define DEPRECATED __declspec(deprecated) | #define DEPRECATED __declspec(deprecated) | ||||
#endif | #endif | ||||
@@ -168,6 +168,10 @@ Can be subclassed to throw/catch specific custom exceptions. | |||||
*/ | */ | ||||
struct Exception : std::exception { | struct Exception : std::exception { | ||||
std::string msg; | std::string msg; | ||||
// Attribute index 1 refers to `Exception*` argument so use 2. | |||||
__attribute__((format(printf, 2, 3))) | |||||
Exception(const char* format, ...); | |||||
Exception(const std::string& msg) : msg(msg) {} | Exception(const std::string& msg) : msg(msg) {} | ||||
const char* what() const noexcept override { | const char* what() const noexcept override { | ||||
return msg.c_str(); | return msg.c_str(); | ||||
@@ -35,7 +35,7 @@ void destroy(); | |||||
/** Do not use this function directly. Use the macros above. | /** Do not use this function directly. Use the macros above. | ||||
Thread-safe, meaning messages cannot overlap each other in the log. | Thread-safe, meaning messages cannot overlap each other in the log. | ||||
*/ | */ | ||||
__attribute__ ((format(printf, 5, 6))) | |||||
__attribute__((format(printf, 5, 6))) | |||||
void log(Level level, const char* filename, int line, const char* func, const char* format, ...); | void log(Level level, const char* filename, int line, const char* func, const char* format, ...); | ||||
/** Returns whether the current log file failed to end properly, due to a possible crash. | /** Returns whether the current log file failed to end properly, due to a possible crash. | ||||
Must be called *before* init(). | Must be called *before* init(). | ||||
@@ -100,7 +100,7 @@ | |||||
#undef PRIVATE | #undef PRIVATE | ||||
#define PRIVATE __attribute__ ((warning ("Using private function or symbol"))) | |||||
#define PRIVATE __attribute__((warning ("Using private function or symbol"))) | |||||
namespace rack { | namespace rack { | ||||
@@ -1,4 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include <stdarg.h> | |||||
#include <vector> | #include <vector> | ||||
#include <common.hpp> | #include <common.hpp> | ||||
@@ -15,7 +16,9 @@ namespace string { | |||||
/** Converts a `printf()` format string and optional arguments into a std::string. | /** Converts a `printf()` format string and optional arguments into a std::string. | ||||
Remember that "%s" must reference a `char *`, so use `.c_str()` for `std::string`s, otherwise you might get binary garbage. | Remember that "%s" must reference a `char *`, so use `.c_str()` for `std::string`s, otherwise you might get binary garbage. | ||||
*/ | */ | ||||
__attribute__((format(printf, 1, 2))) | |||||
std::string f(const char* format, ...); | std::string f(const char* format, ...); | ||||
std::string fV(const char* format, va_list args); | |||||
/** Replaces all characters to lowercase letters */ | /** Replaces all characters to lowercase letters */ | ||||
std::string lowercase(const std::string& s); | std::string lowercase(const std::string& s); | ||||
/** Replaces all characters to uppercase letters */ | /** Replaces all characters to uppercase letters */ | ||||
@@ -737,7 +737,7 @@ void ModuleWidget::pasteClipboardAction() { | |||||
void ModuleWidget::load(std::string filename) { | void ModuleWidget::load(std::string filename) { | ||||
FILE* file = std::fopen(filename.c_str(), "r"); | FILE* file = std::fopen(filename.c_str(), "r"); | ||||
if (!file) | if (!file) | ||||
throw Exception(string::f("Could not load patch file %s", filename.c_str())); | |||||
throw Exception("Could not load patch file %s", filename.c_str()); | |||||
DEFER({std::fclose(file);}); | DEFER({std::fclose(file);}); | ||||
INFO("Loading preset %s", filename.c_str()); | INFO("Loading preset %s", filename.c_str()); | ||||
@@ -745,7 +745,7 @@ void ModuleWidget::load(std::string filename) { | |||||
json_error_t error; | json_error_t error; | ||||
json_t* moduleJ = json_loadf(file, 0, &error); | json_t* moduleJ = json_loadf(file, 0, &error); | ||||
if (!moduleJ) | if (!moduleJ) | ||||
throw Exception(string::f("File is not a valid patch file. JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text)); | |||||
throw Exception("File is not a valid patch file. JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||||
DEFER({json_decref(moduleJ);}); | DEFER({json_decref(moduleJ);}); | ||||
// Don't use IDs from JSON | // Don't use IDs from JSON | ||||
@@ -21,6 +21,14 @@ const std::string API_URL = "https://api.vcvrack.com"; | |||||
const std::string API_VERSION = "2"; | const std::string API_VERSION = "2"; | ||||
Exception::Exception(const char* format, ...) { | |||||
va_list args; | |||||
va_start(args, format); | |||||
msg = string::fV(format, args); | |||||
va_end(args); | |||||
} | |||||
} // namespace rack | } // namespace rack | ||||
@@ -48,7 +48,7 @@ static void* loadLibrary(std::string libraryPath) { | |||||
SetErrorMode(0); | SetErrorMode(0); | ||||
if (!handle) { | if (!handle) { | ||||
int error = GetLastError(); | int error = GetLastError(); | ||||
throw Exception(string::f("Failed to load library %s: code %d", libraryPath.c_str(), error)); | |||||
throw Exception("Failed to load library %s: code %d", libraryPath.c_str(), error); | |||||
} | } | ||||
#else | #else | ||||
// As of Rack v2.0, plugins are linked with `-rpath=.` so change current directory so it can find libRack. | // As of Rack v2.0, plugins are linked with `-rpath=.` so change current directory so it can find libRack. | ||||
@@ -59,7 +59,7 @@ static void* loadLibrary(std::string libraryPath) { | |||||
// Load library with dlopen | // Load library with dlopen | ||||
void* handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL); | void* handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL); | ||||
if (!handle) | if (!handle) | ||||
throw Exception(string::f("Failed to load library %s: %s", libraryPath.c_str(), dlerror())); | |||||
throw Exception("Failed to load library %s: %s", libraryPath.c_str(), dlerror()); | |||||
#endif | #endif | ||||
return handle; | return handle; | ||||
} | } | ||||
@@ -80,7 +80,7 @@ static InitCallback loadPluginCallback(Plugin* plugin) { | |||||
// Check file existence | // Check file existence | ||||
if (!system::isFile(libraryPath)) | if (!system::isFile(libraryPath)) | ||||
throw Exception(string::f("Plugin binary not found at %s", libraryPath.c_str())); | |||||
throw Exception("Plugin binary not found at %s", libraryPath.c_str()); | |||||
// Load dynamic/shared library | // Load dynamic/shared library | ||||
plugin->handle = loadLibrary(libraryPath); | plugin->handle = loadLibrary(libraryPath); | ||||
@@ -93,7 +93,7 @@ static InitCallback loadPluginCallback(Plugin* plugin) { | |||||
initCallback = (InitCallback) dlsym(plugin->handle, "init"); | initCallback = (InitCallback) dlsym(plugin->handle, "init"); | ||||
#endif | #endif | ||||
if (!initCallback) | if (!initCallback) | ||||
throw Exception(string::f("Failed to read init() symbol in %s", libraryPath.c_str())); | |||||
throw Exception("Failed to read init() symbol in %s", libraryPath.c_str()); | |||||
return initCallback; | return initCallback; | ||||
} | } | ||||
@@ -124,13 +124,13 @@ static Plugin* loadPlugin(std::string path) { | |||||
std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json"); | std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json"); | ||||
FILE* file = std::fopen(manifestFilename.c_str(), "r"); | FILE* file = std::fopen(manifestFilename.c_str(), "r"); | ||||
if (!file) | if (!file) | ||||
throw Exception(string::f("Manifest file %s does not exist", manifestFilename.c_str())); | |||||
throw Exception("Manifest file %s does not exist", manifestFilename.c_str()); | |||||
DEFER({std::fclose(file);}); | DEFER({std::fclose(file);}); | ||||
json_error_t error; | json_error_t error; | ||||
json_t* rootJ = json_loadf(file, 0, &error); | json_t* rootJ = json_loadf(file, 0, &error); | ||||
if (!rootJ) | if (!rootJ) | ||||
throw Exception(string::f("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text)); | |||||
throw Exception("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text); | |||||
DEFER({json_decref(rootJ);}); | DEFER({json_decref(rootJ);}); | ||||
// Call init callback | // Call init callback | ||||
@@ -149,7 +149,7 @@ static Plugin* loadPlugin(std::string path) { | |||||
// Reject plugin if slug already exists | // Reject plugin if slug already exists | ||||
Plugin* oldPlugin = getPlugin(plugin->slug); | Plugin* oldPlugin = getPlugin(plugin->slug); | ||||
if (oldPlugin) | if (oldPlugin) | ||||
throw Exception(string::f("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str())); | |||||
throw Exception("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str()); | |||||
plugins.push_back(plugin); | plugins.push_back(plugin); | ||||
INFO("Loaded plugin %s v%s from %s", plugin->slug.c_str(), plugin->version.c_str(), plugin->path.c_str()); | INFO("Loaded plugin %s v%s from %s", plugin->slug.c_str(), plugin->version.c_str(), plugin->path.c_str()); | ||||
@@ -288,7 +288,7 @@ Model* modelFromJson(json_t* moduleJ) { | |||||
// Get Model | // Get Model | ||||
Model* model = getModel(pluginSlug, modelSlug); | Model* model = getModel(pluginSlug, modelSlug); | ||||
if (!model) | if (!model) | ||||
throw Exception(string::f("Could not find module \"%s\" of plugin \"%s\"", modelSlug.c_str(), pluginSlug.c_str())); | |||||
throw Exception("Could not find module \"%s\" of plugin \"%s\"", modelSlug.c_str(), pluginSlug.c_str()); | |||||
return model; | return model; | ||||
} | } | ||||
@@ -19,7 +19,7 @@ void Model::fromJson(json_t* rootJ) { | |||||
if (nameJ) | if (nameJ) | ||||
name = json_string_value(nameJ); | name = json_string_value(nameJ); | ||||
if (name == "") | if (name == "") | ||||
throw Exception(string::f("No module name for slug %s", slug.c_str())); | |||||
throw Exception("No module name for slug %s", slug.c_str()); | |||||
json_t* descriptionJ = json_object_get(rootJ, "description"); | json_t* descriptionJ = json_object_get(rootJ, "description"); | ||||
if (descriptionJ) | if (descriptionJ) | ||||
@@ -39,14 +39,14 @@ void Plugin::fromJson(json_t* rootJ) { | |||||
if (slug == "") | if (slug == "") | ||||
throw Exception("No plugin slug"); | throw Exception("No plugin slug"); | ||||
if (!isSlugValid(slug)) | if (!isSlugValid(slug)) | ||||
throw Exception(string::f("Plugin slug \"%s\" is invalid", slug.c_str())); | |||||
throw Exception("Plugin slug \"%s\" is invalid", slug.c_str()); | |||||
// version | // version | ||||
json_t* versionJ = json_object_get(rootJ, "version"); | json_t* versionJ = json_object_get(rootJ, "version"); | ||||
if (versionJ) | if (versionJ) | ||||
version = json_string_value(versionJ); | version = json_string_value(versionJ); | ||||
if (!string::startsWith(version, ABI_VERSION + ".")) | if (!string::startsWith(version, ABI_VERSION + ".")) | ||||
throw Exception(string::f("Plugin version %s does not match Rack ABI version %s", version.c_str(), ABI_VERSION.c_str())); | |||||
throw Exception("Plugin version %s does not match Rack ABI version %s", version.c_str(), ABI_VERSION.c_str()); | |||||
if (version == "") | if (version == "") | ||||
throw Exception("No plugin version"); | throw Exception("No plugin version"); | ||||
@@ -120,26 +120,26 @@ void Plugin::fromJson(json_t* rootJ) { | |||||
// Get model slug | // Get model slug | ||||
json_t* modelSlugJ = json_object_get(moduleJ, "slug"); | json_t* modelSlugJ = json_object_get(moduleJ, "slug"); | ||||
if (!modelSlugJ) { | if (!modelSlugJ) { | ||||
throw Exception(string::f("No slug found for module entry %d", moduleId)); | |||||
throw Exception("No slug found for module entry %" PRId64, moduleId); | |||||
} | } | ||||
std::string modelSlug = json_string_value(modelSlugJ); | std::string modelSlug = json_string_value(modelSlugJ); | ||||
// Check model slug | // Check model slug | ||||
if (!isSlugValid(modelSlug)) { | if (!isSlugValid(modelSlug)) { | ||||
throw Exception(string::f("Module slug \"%s\" is invalid", modelSlug.c_str())); | |||||
throw Exception("Module slug \"%s\" is invalid", modelSlug.c_str()); | |||||
} | } | ||||
// Get model | // Get model | ||||
Model* model = getModel(modelSlug); | Model* model = getModel(modelSlug); | ||||
if (!model) { | if (!model) { | ||||
throw Exception(string::f("Manifest contains module %s but it is not defined in the plugin", modelSlug.c_str())); | |||||
throw Exception("Manifest contains module %s but it is not defined in the plugin", modelSlug.c_str()); | |||||
} | } | ||||
model->fromJson(moduleJ); | model->fromJson(moduleJ); | ||||
} | } | ||||
} | } | ||||
else { | else { | ||||
WARN("No modules in plugin %s", slug.c_str()); | |||||
WARN("No modules in plugin manifest %s", slug.c_str()); | |||||
} | } | ||||
// Remove models without names | // Remove models without names | ||||
@@ -32,7 +32,7 @@ struct RtAudioDevice : audio::Device { | |||||
rtAudio = new RtAudio(api); | rtAudio = new RtAudio(api); | ||||
rtAudio->showWarnings(false); | rtAudio->showWarnings(false); | ||||
if (!rtAudio) { | if (!rtAudio) { | ||||
throw Exception(string::f("Failed to create RtAudio driver %d", api)); | |||||
throw Exception("Failed to create RtAudio driver %d", api); | |||||
} | } | ||||
rtAudio->showWarnings(false); | rtAudio->showWarnings(false); | ||||
@@ -40,7 +40,7 @@ struct RtAudioDevice : audio::Device { | |||||
deviceInfo = rtAudio->getDeviceInfo(deviceId); | deviceInfo = rtAudio->getDeviceInfo(deviceId); | ||||
} | } | ||||
catch (RtAudioError& e) { | catch (RtAudioError& e) { | ||||
throw Exception(string::f("Failed to query RtAudio device: %s", e.what())); | |||||
throw Exception("Failed to query RtAudio device: %s", e.what()); | |||||
} | } | ||||
this->deviceId = deviceId; | this->deviceId = deviceId; | ||||
@@ -61,7 +61,7 @@ struct RtAudioDevice : audio::Device { | |||||
void openStream() { | void openStream() { | ||||
// Open new device | // Open new device | ||||
if (deviceInfo.outputChannels == 0 && deviceInfo.inputChannels == 0) { | if (deviceInfo.outputChannels == 0 && deviceInfo.inputChannels == 0) { | ||||
throw Exception(string::f("RtAudio device %d has 0 inputs and 0 outputs", deviceId)); | |||||
throw Exception("RtAudio device %d has 0 inputs and 0 outputs", deviceId); | |||||
} | } | ||||
inputParameters = RtAudio::StreamParameters(); | inputParameters = RtAudio::StreamParameters(); | ||||
@@ -103,7 +103,7 @@ struct RtAudioDevice : audio::Device { | |||||
&rtAudioCallback, this, &options, NULL); | &rtAudioCallback, this, &options, NULL); | ||||
} | } | ||||
catch (RtAudioError& e) { | catch (RtAudioError& e) { | ||||
throw Exception(string::f("Failed to open RtAudio stream: %s", e.what())); | |||||
throw Exception("Failed to open RtAudio stream: %s", e.what()); | |||||
} | } | ||||
INFO("Starting RtAudio stream %d", deviceId); | INFO("Starting RtAudio stream %d", deviceId); | ||||
@@ -111,7 +111,7 @@ struct RtAudioDevice : audio::Device { | |||||
rtAudio->startStream(); | rtAudio->startStream(); | ||||
} | } | ||||
catch (RtAudioError& e) { | catch (RtAudioError& e) { | ||||
throw Exception(string::f("Failed to start RtAudio stream: %s", e.what())); | |||||
throw Exception("Failed to start RtAudio stream: %s", e.what()); | |||||
} | } | ||||
// Update sample rate to actual value | // Update sample rate to actual value | ||||
@@ -127,7 +127,7 @@ struct RtAudioDevice : audio::Device { | |||||
rtAudio->stopStream(); | rtAudio->stopStream(); | ||||
} | } | ||||
catch (RtAudioError& e) { | catch (RtAudioError& e) { | ||||
throw Exception(string::f("Failed to stop RtAudio stream %s", e.what())); | |||||
throw Exception("Failed to stop RtAudio stream %s", e.what()); | |||||
} | } | ||||
} | } | ||||
if (rtAudio->isStreamOpen()) { | if (rtAudio->isStreamOpen()) { | ||||
@@ -136,7 +136,7 @@ struct RtAudioDevice : audio::Device { | |||||
rtAudio->closeStream(); | rtAudio->closeStream(); | ||||
} | } | ||||
catch (RtAudioError& e) { | catch (RtAudioError& e) { | ||||
throw Exception(string::f("Failed to close RtAudio stream %s", e.what())); | |||||
throw Exception("Failed to close RtAudio stream %s", e.what()); | |||||
} | } | ||||
} | } | ||||
INFO("Closed RtAudio stream"); | INFO("Closed RtAudio stream"); | ||||
@@ -35,7 +35,7 @@ struct RtMidiInputDevice : midi::InputDevice { | |||||
rtMidiIn = new RtMidiIn((RtMidi::Api) driverId, "VCV Rack"); | rtMidiIn = new RtMidiIn((RtMidi::Api) driverId, "VCV Rack"); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to create RtMidi input driver %d: %s", driverId, e.what())); | |||||
throw Exception("Failed to create RtMidi input driver %d: %s", driverId, e.what()); | |||||
} | } | ||||
rtMidiIn->setErrorCallback(rtMidiErrorCallback); | rtMidiIn->setErrorCallback(rtMidiErrorCallback); | ||||
rtMidiIn->ignoreTypes(false, false, false); | rtMidiIn->ignoreTypes(false, false, false); | ||||
@@ -45,14 +45,14 @@ struct RtMidiInputDevice : midi::InputDevice { | |||||
name = rtMidiIn->getPortName(deviceId); | name = rtMidiIn->getPortName(deviceId); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to get RtMidi input device name: %s", e.what())); | |||||
throw Exception("Failed to get RtMidi input device name: %s", e.what()); | |||||
} | } | ||||
try { | try { | ||||
rtMidiIn->openPort(deviceId, "VCV Rack input"); | rtMidiIn->openPort(deviceId, "VCV Rack input"); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to open RtMidi input device: %s", e.what())); | |||||
throw Exception("Failed to open RtMidi input device: %s", e.what()); | |||||
} | } | ||||
} | } | ||||
@@ -105,7 +105,7 @@ struct RtMidiOutputDevice : midi::OutputDevice { | |||||
rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack"); | rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack"); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to create RtMidi output driver %d: %s", driverId, e.what())); | |||||
throw Exception("Failed to create RtMidi output driver %d: %s", driverId, e.what()); | |||||
} | } | ||||
rtMidiOut->setErrorCallback(rtMidiErrorCallback); | rtMidiOut->setErrorCallback(rtMidiErrorCallback); | ||||
@@ -113,14 +113,14 @@ struct RtMidiOutputDevice : midi::OutputDevice { | |||||
name = rtMidiOut->getPortName(deviceId); | name = rtMidiOut->getPortName(deviceId); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to get RtMidi output device name: %s", e.what())); | |||||
throw Exception("Failed to get RtMidi output device name: %s", e.what()); | |||||
} | } | ||||
try { | try { | ||||
rtMidiOut->openPort(deviceId, "VCV Rack output"); | rtMidiOut->openPort(deviceId, "VCV Rack output"); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to get RtMidi output device name: %s", e.what())); | |||||
throw Exception("Failed to get RtMidi output device name: %s", e.what()); | |||||
} | } | ||||
startThread(); | startThread(); | ||||
@@ -211,7 +211,7 @@ struct RtMidiDriver : midi::Driver { | |||||
rtMidiIn = new RtMidiIn((RtMidi::Api) driverId); | rtMidiIn = new RtMidiIn((RtMidi::Api) driverId); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to create RtMidi input driver %d: %s", driverId, e.what())); | |||||
throw Exception("Failed to create RtMidi input driver %d: %s", driverId, e.what()); | |||||
} | } | ||||
rtMidiIn->setErrorCallback(rtMidiErrorCallback); | rtMidiIn->setErrorCallback(rtMidiErrorCallback); | ||||
@@ -219,7 +219,7 @@ struct RtMidiDriver : midi::Driver { | |||||
rtMidiOut = new RtMidiOut((RtMidi::Api) driverId); | rtMidiOut = new RtMidiOut((RtMidi::Api) driverId); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to create RtMidi output driver %d: %s", driverId, e.what())); | |||||
throw Exception("Failed to create RtMidi output driver %d: %s", driverId, e.what()); | |||||
} | } | ||||
rtMidiOut->setErrorCallback(rtMidiErrorCallback); | rtMidiOut->setErrorCallback(rtMidiErrorCallback); | ||||
} | } | ||||
@@ -250,7 +250,7 @@ struct RtMidiDriver : midi::Driver { | |||||
count = rtMidiIn->getPortCount(); | count = rtMidiIn->getPortCount(); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to get RtMidi input device count: %s", e.what())); | |||||
throw Exception("Failed to get RtMidi input device count: %s", e.what()); | |||||
} | } | ||||
std::vector<int> deviceIds; | std::vector<int> deviceIds; | ||||
@@ -266,7 +266,7 @@ struct RtMidiDriver : midi::Driver { | |||||
return rtMidiIn->getPortName(deviceId); | return rtMidiIn->getPortName(deviceId); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to get RtMidi input device name: %s", e.what())); | |||||
throw Exception("Failed to get RtMidi input device name: %s", e.what()); | |||||
} | } | ||||
} | } | ||||
@@ -279,7 +279,7 @@ struct RtMidiDriver : midi::Driver { | |||||
inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId); | inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to create RtMidi input device: %s", e.what())); | |||||
throw Exception("Failed to create RtMidi input device: %s", e.what()); | |||||
} | } | ||||
} | } | ||||
@@ -301,7 +301,7 @@ struct RtMidiDriver : midi::Driver { | |||||
delete device; | delete device; | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to delete RtMidi input device: %s", e.what())); | |||||
throw Exception("Failed to delete RtMidi input device: %s", e.what()); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -313,7 +313,7 @@ struct RtMidiDriver : midi::Driver { | |||||
count = rtMidiOut->getPortCount(); | count = rtMidiOut->getPortCount(); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to get RtMidi output device count: %s", e.what())); | |||||
throw Exception("Failed to get RtMidi output device count: %s", e.what()); | |||||
} | } | ||||
std::vector<int> deviceIds; | std::vector<int> deviceIds; | ||||
@@ -329,7 +329,7 @@ struct RtMidiDriver : midi::Driver { | |||||
return rtMidiOut->getPortName(deviceId); | return rtMidiOut->getPortName(deviceId); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to get RtMidi output device count: %s", e.what())); | |||||
throw Exception("Failed to get RtMidi output device count: %s", e.what()); | |||||
} | } | ||||
} | } | ||||
@@ -342,7 +342,7 @@ struct RtMidiDriver : midi::Driver { | |||||
outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId); | outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId); | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to create RtMidi output device: %s", e.what())); | |||||
throw Exception("Failed to create RtMidi output device: %s", e.what()); | |||||
} | } | ||||
} | } | ||||
@@ -364,7 +364,7 @@ struct RtMidiDriver : midi::Driver { | |||||
delete device; | delete device; | ||||
} | } | ||||
catch (RtMidiError& e) { | catch (RtMidiError& e) { | ||||
throw Exception(string::f("Failed to delete RtMidi output device: %s", e.what())); | |||||
throw Exception("Failed to delete RtMidi output device: %s", e.what()); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -356,7 +356,7 @@ void load(const std::string& path) { | |||||
json_error_t error; | json_error_t error; | ||||
json_t* rootJ = json_loadf(file, 0, &error); | json_t* rootJ = json_loadf(file, 0, &error); | ||||
if (!rootJ) | if (!rootJ) | ||||
throw Exception(string::f("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text)); | |||||
throw Exception("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text); | |||||
fromJson(rootJ); | fromJson(rootJ); | ||||
json_decref(rootJ); | json_decref(rootJ); | ||||
@@ -1,6 +1,7 @@ | |||||
#include <cctype> // for tolower and toupper | #include <cctype> // for tolower and toupper | ||||
#include <algorithm> // for transform and equal | #include <algorithm> // for transform and equal | ||||
#include <libgen.h> // for dirname and basename | #include <libgen.h> // for dirname and basename | ||||
#include <stdarg.h> | |||||
#if defined ARCH_WIN | #if defined ARCH_WIN | ||||
#include <windows.h> // for MultiByteToWideChar | #include <windows.h> // for MultiByteToWideChar | ||||
@@ -16,17 +17,24 @@ namespace string { | |||||
std::string f(const char* format, ...) { | std::string f(const char* format, ...) { | ||||
va_list args; | va_list args; | ||||
va_start(args, format); | va_start(args, format); | ||||
std::string s = fV(format, args); | |||||
va_end(args); | |||||
return s; | |||||
} | |||||
std::string fV(const char* format, va_list args) { | |||||
// va_lists cannot be reused but we need it twice, so clone args. | |||||
va_list args2; | |||||
va_copy(args2, args); | |||||
// Compute size of required buffer | // Compute size of required buffer | ||||
int size = vsnprintf(NULL, 0, format, args); | int size = vsnprintf(NULL, 0, format, args); | ||||
va_end(args); | |||||
if (size < 0) | if (size < 0) | ||||
return ""; | return ""; | ||||
// Create buffer | // Create buffer | ||||
std::string s; | std::string s; | ||||
s.resize(size); | s.resize(size); | ||||
va_start(args, format); | |||||
vsnprintf(&s[0], size + 1, format, args); | |||||
va_end(args); | |||||
vsnprintf(&s[0], size + 1, format, args2); | |||||
return s; | return s; | ||||
} | } | ||||
@@ -303,7 +303,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||||
assert(0 <= compressionLevel && compressionLevel <= 19); | assert(0 <= compressionLevel && compressionLevel <= 19); | ||||
r = archive_write_set_filter_option(a, NULL, "compression-level", std::to_string(compressionLevel).c_str()); | r = archive_write_set_filter_option(a, NULL, "compression-level", std::to_string(compressionLevel).c_str()); | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Archiver could not set filter option: %s", archive_error_string(a))); | |||||
throw Exception("Archiver could not set filter option: %s", archive_error_string(a)); | |||||
#if defined ARCH_WIN | #if defined ARCH_WIN | ||||
r = archive_write_open_filename_w(a, string::U8toU16(archivePath).c_str()); | r = archive_write_open_filename_w(a, string::U8toU16(archivePath).c_str()); | ||||
@@ -311,7 +311,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||||
r = archive_write_open_filename(a, archivePath.c_str()); | r = archive_write_open_filename(a, archivePath.c_str()); | ||||
#endif | #endif | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Archiver could not open archive %s for writing: %s", archivePath.c_str(), archive_error_string(a))); | |||||
throw Exception("Archiver could not open archive %s for writing: %s", archivePath.c_str(), archive_error_string(a)); | |||||
DEFER({archive_write_close(a);}); | DEFER({archive_write_close(a);}); | ||||
// Open folder for reading | // Open folder for reading | ||||
@@ -323,7 +323,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||||
r = archive_read_disk_open(disk, folderPath.c_str()); | r = archive_read_disk_open(disk, folderPath.c_str()); | ||||
#endif | #endif | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Archiver could not open folder %s for reading: %s", folderPath.c_str(), archive_error_string(a))); | |||||
throw Exception("Archiver could not open folder %s for reading: %s", folderPath.c_str(), archive_error_string(a)); | |||||
DEFER({archive_read_close(a);}); | DEFER({archive_read_close(a);}); | ||||
// Iterate folder | // Iterate folder | ||||
@@ -335,7 +335,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||||
if (r == ARCHIVE_EOF) | if (r == ARCHIVE_EOF) | ||||
break; | break; | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Archiver could not get next entry from archive: %s", archive_error_string(disk))); | |||||
throw Exception("Archiver could not get next entry from archive: %s", archive_error_string(disk)); | |||||
// Recurse dirs | // Recurse dirs | ||||
archive_read_disk_descend(disk); | archive_read_disk_descend(disk); | ||||
@@ -358,7 +358,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||||
// Write file to archive | // Write file to archive | ||||
r = archive_write_header(a, entry); | r = archive_write_header(a, entry); | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Archiver could not write entry to archive: %s", archive_error_string(a))); | |||||
throw Exception("Archiver could not write entry to archive: %s", archive_error_string(a)); | |||||
// Manually copy data | // Manually copy data | ||||
#if defined ARCH_WIN | #if defined ARCH_WIN | ||||
@@ -394,7 +394,7 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder | |||||
r = archive_read_open_filename(a, archivePath.c_str(), 1 << 14); | r = archive_read_open_filename(a, archivePath.c_str(), 1 << 14); | ||||
#endif | #endif | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Unarchiver could not open archive %s: %s", archivePath.c_str(), archive_error_string(a))); | |||||
throw Exception("Unarchiver could not open archive %s: %s", archivePath.c_str(), archive_error_string(a)); | |||||
DEFER({archive_read_close(a);}); | DEFER({archive_read_close(a);}); | ||||
// Open folder for writing | // Open folder for writing | ||||
@@ -412,12 +412,12 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder | |||||
if (r == ARCHIVE_EOF) | if (r == ARCHIVE_EOF) | ||||
break; | break; | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Unarchiver could not read entry from archive: %s", archive_error_string(a))); | |||||
throw Exception("Unarchiver could not read entry from archive: %s", archive_error_string(a)); | |||||
// Convert relative pathname to absolute based on folderPath | // Convert relative pathname to absolute based on folderPath | ||||
std::string entryPath = archive_entry_pathname(entry); | std::string entryPath = archive_entry_pathname(entry); | ||||
if (!fs::u8path(entryPath).is_relative()) | if (!fs::u8path(entryPath).is_relative()) | ||||
throw Exception(string::f("Unarchiver does not support absolute tar paths: %s", entryPath.c_str())); | |||||
throw Exception("Unarchiver does not support absolute tar paths: %s", entryPath.c_str()); | |||||
entryPath = (fs::u8path(folderPath) / fs::u8path(entryPath)).generic_u8string(); | entryPath = (fs::u8path(folderPath) / fs::u8path(entryPath)).generic_u8string(); | ||||
#if defined ARCH_WIN | #if defined ARCH_WIN | ||||
archive_entry_copy_pathname_w(entry, string::U8toU16(entryPath).c_str()); | archive_entry_copy_pathname_w(entry, string::U8toU16(entryPath).c_str()); | ||||
@@ -428,7 +428,7 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder | |||||
// Write entry to disk | // Write entry to disk | ||||
r = archive_write_header(disk, entry); | r = archive_write_header(disk, entry); | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Unarchiver could not write file to folder: %s", archive_error_string(disk))); | |||||
throw Exception("Unarchiver could not write file to folder: %s", archive_error_string(disk)); | |||||
// Copy data to file | // Copy data to file | ||||
for (;;) { | for (;;) { | ||||
@@ -440,18 +440,18 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder | |||||
if (r == ARCHIVE_EOF) | if (r == ARCHIVE_EOF) | ||||
break; | break; | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Unarchiver could not read data from archive", archive_error_string(a))); | |||||
throw Exception("Unarchiver could not read data from archive: %s", archive_error_string(a)); | |||||
// Write data to file | // Write data to file | ||||
r = archive_write_data_block(disk, buf, size, offset); | r = archive_write_data_block(disk, buf, size, offset); | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Unarchiver could not write data to file", archive_error_string(disk))); | |||||
throw Exception("Unarchiver could not write data to file: %s", archive_error_string(disk)); | |||||
} | } | ||||
// Close file | // Close file | ||||
r = archive_write_finish_entry(disk); | r = archive_write_finish_entry(disk); | ||||
if (r < ARCHIVE_OK) | if (r < ARCHIVE_OK) | ||||
throw Exception(string::f("Unarchiver could not close file", archive_error_string(disk))); | |||||
throw Exception("Unarchiver could not close file: %s", archive_error_string(disk)); | |||||
} | } | ||||
} | } | ||||