fix(instrument_drop_win): UTF-8 temp path, PID collision guard, embed-strip excluded from drop hotspot
This commit is contained in:
+47
-20
@@ -31,27 +31,49 @@ namespace reasampler {
|
||||
|
||||
namespace {
|
||||
|
||||
// Write `bytes` to a fresh uniquely-named .vstpreset in the OS temp dir and return its
|
||||
// absolute path; empty string on any failure. The .vstpreset extension is load-bearing —
|
||||
// Write `bytes` to a fresh uniquely-named .vstpreset in the OS temp dir and return its path;
|
||||
// returns an empty path on any failure. The .vstpreset extension is load-bearing —
|
||||
// TrackFX_SetPreset's full-path form is documented for .vstpreset files (VST3). The file is
|
||||
// transient: the caller deletes it right after the SetPreset call.
|
||||
std::string writeTempPreset(const std::vector<std::uint8_t>& bytes) {
|
||||
static std::atomic<unsigned> counter{0};
|
||||
std::error_code ec;
|
||||
const std::filesystem::path dir = std::filesystem::temp_directory_path(ec);
|
||||
if (ec) return {};
|
||||
const std::filesystem::path path =
|
||||
dir / ("reasampler_drop_" + std::to_string(counter.fetch_add(1)) + ".vstpreset");
|
||||
std::ofstream out(path, std::ios::binary | std::ios::trunc);
|
||||
if (!out) return {};
|
||||
out.write(reinterpret_cast<const char*>(bytes.data()),
|
||||
static_cast<std::streamsize>(bytes.size()));
|
||||
out.close();
|
||||
if (!out) { // short write / flush failure -> don't hand REAPER a truncated preset
|
||||
std::filesystem::remove(path, ec);
|
||||
//
|
||||
// The temp filename embeds the process ID so two concurrent REAPER instances (e.g. stable +
|
||||
// beta) cannot collide in the shared OS temp dir, and one instance's cleanup cannot
|
||||
// accidentally delete another's in-flight file.
|
||||
//
|
||||
// Non-throwing: every std::filesystem call uses the error_code overload. The whole body is
|
||||
// wrapped in try/catch to guarantee no exception crosses the REAPER C callback boundary
|
||||
// (the same discipline persist.cpp uses — see its non-throwing scanPruneOrphans comment).
|
||||
//
|
||||
// Returns the path object (not a narrow string) so the caller can:
|
||||
// (a) pass path.u8string() to TrackFX_SetPreset — UTF-8 on MSVC, not ACP-converted,
|
||||
// so a temp dir with accented or CJK user-name bytes is handled correctly;
|
||||
// (b) delete via the retained path object — not via re-parsing the narrow string —
|
||||
// so the cleanup cannot leak if the conversion above were to round-trip incorrectly.
|
||||
std::filesystem::path writeTempPreset(const std::vector<std::uint8_t>& bytes) {
|
||||
try {
|
||||
static std::atomic<unsigned> counter{0};
|
||||
std::error_code ec;
|
||||
const std::filesystem::path dir = std::filesystem::temp_directory_path(ec);
|
||||
if (ec) return {};
|
||||
// PID in the name keeps files from distinct REAPER instances distinct in the shared
|
||||
// temp dir — prevents cross-instance collisions and spurious post-apply deletions.
|
||||
const std::string name =
|
||||
"reasampler_drop_" + std::to_string(GetCurrentProcessId()) +
|
||||
"_" + std::to_string(counter.fetch_add(1)) + ".vstpreset";
|
||||
const std::filesystem::path path = dir / name;
|
||||
std::ofstream out(path, std::ios::binary | std::ios::trunc);
|
||||
if (!out) return {};
|
||||
out.write(reinterpret_cast<const char*>(bytes.data()),
|
||||
static_cast<std::streamsize>(bytes.size()));
|
||||
out.close();
|
||||
if (!out) { // short write / flush failure -> don't hand REAPER a truncated preset
|
||||
std::filesystem::remove(path, ec);
|
||||
return {};
|
||||
}
|
||||
return path;
|
||||
} catch (...) {
|
||||
return {};
|
||||
}
|
||||
return path.string();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -77,7 +99,7 @@ bool loadInstrumentOntoTrack(MediaTrack* track, const std::vector<std::uint8_t>&
|
||||
|
||||
// Materialize the .vstpreset FIRST so an I/O failure leaves the track untouched (no FX
|
||||
// added yet — nothing to roll back).
|
||||
const std::string presetPath = writeTempPreset(presetBytes);
|
||||
const std::filesystem::path presetPath = writeTempPreset(presetBytes);
|
||||
if (presetPath.empty()) return false;
|
||||
|
||||
// The CHANNEL-correct FX name: "VST3:ReaSampler 9000" on stable, "VST3:ReaSampler 9000
|
||||
@@ -100,9 +122,14 @@ bool loadInstrumentOntoTrack(MediaTrack* track, const std::vector<std::uint8_t>&
|
||||
// serializer produced (instrument_drop::buildInstrumentDropPreset ->
|
||||
// sample_map::serializeComponentState). Unlike the former "vst_chunk" named-config-parm
|
||||
// write, a failure here is REPORTED (false), not silently ignored.
|
||||
if (ok) ok = TrackFX_SetPreset(track, fxIndex, presetPath.c_str());
|
||||
//
|
||||
// u8string() gives UTF-8 bytes on MSVC (not ACP-converted), so a temp dir under an
|
||||
// accented or CJK user-name is handled correctly by REAPER's path APIs.
|
||||
if (ok) ok = TrackFX_SetPreset(track, fxIndex, presetPath.u8string().c_str());
|
||||
|
||||
// The preset file is transient regardless of outcome; best-effort cleanup (temp dir).
|
||||
// The preset file is transient regardless of outcome; delete via the retained path object
|
||||
// (not a re-parsed narrow string) so cleanup cannot leak even if the UTF-8 conversion
|
||||
// round-trip were incorrect.
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(presetPath, ec);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user