G2.1: save full instrument state to disk (path/type/preset/transport)

This commit is contained in:
2026-08-16 07:28:17 +07:00
parent 8d9011222a
commit 28441566c2
9 changed files with 212 additions and 2 deletions
+27
View File
@@ -6,6 +6,8 @@
#include <cstring>
#include <fstream>
#include <sstream>
#include <cerrno>
#include <sys/stat.h>
#ifdef _WIN32
#include <windows.h>
@@ -37,6 +39,30 @@ std::string jsonEscape(const std::string& s) {
return out;
}
// Create the parent directory of `path` (all missing levels). Returns false
// only on a hard error (permission, invalid path).
bool ensureParentDir(const std::string& path) {
const size_t slash = path.find_last_of("/\\");
if (slash == std::string::npos) return true;
const std::string dir = path.substr(0, slash);
if (dir.empty()) return true;
#ifdef _WIN32
// CreateDirectoryA creates one level at a time - walk the chain so
// %APPDATA%\SonicForgeDAW\state (or a custom SF_STATE_FILE dir) is
// created even when none of it exists yet.
for (size_t i = 0; i < dir.size(); ++i) {
if ((dir[i] == '/' || dir[i] == '\\') && i > 0)
CreateDirectoryA(dir.substr(0, i).c_str(), nullptr);
}
if (CreateDirectoryA(dir.c_str(), nullptr)) return true;
return GetLastError() == ERROR_ALREADY_EXISTS;
#else
for (size_t i = 1; i < dir.size(); ++i)
if (dir[i] == '/') mkdir(dir.substr(0, i).c_str(), 0755);
return mkdir(dir.c_str(), 0755) == 0 || errno == EEXIST;
#endif
}
// Skip whitespace; returns pointer at first non-ws char.
const char* skipWs(const char* p) {
while (p && *p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) ++p;
@@ -144,6 +170,7 @@ std::string StateStore::defaultPath() {
}
bool StateStore::save(const BridgeStateSnapshot& snap, const std::string& path) {
if (!ensureParentDir(path)) return false;
std::string json;
json.reserve(256 + snap.instruments.size() * 192);
json += "{\n";