// instrument_drop — pure implementation. See instrument_drop.h. // No REAPER/SWELL/VST3 SDK/vendor. Reuses sample_map's ComponentState serializer // and the SDK-free UID macros (reasampler_uid.h). #include "core/wire/instrument_drop.h" #include #include "core/wire/reasampler_uid.h" // REASAMPLER_ACTIVE_UID_* — the FROZEN, channel-selected class UID #include "core/instrument/map/component_state_io.h" // ComponentState + serializeComponentState (the SHARED writer) #include "core/wire/bytes.h" // putLE — the ONE LE byte codec namespace reasampler::wire { using instrument::map::ComponentState; using instrument::map::serializeComponentState; namespace { // The .vstpreset container stores its integers little-endian on disk (public.sdk // vstpresetfile.cpp swaps only on big-endian hosts) — putLE is exactly that byte order. void appendFourCC(std::vector& out, const char id[4]) { out.insert(out.end(), id, id + 4); } } // namespace std::string vstClassIdHex() { char buf[33]; std::snprintf(buf, sizeof(buf), "%08X%08X%08X%08X", static_cast(REASAMPLER_ACTIVE_UID_1), static_cast(REASAMPLER_ACTIVE_UID_2), static_cast(REASAMPLER_ACTIVE_UID_3), static_cast(REASAMPLER_ACTIVE_UID_4)); return std::string(buf, 32); } std::vector buildVstPresetBytes( const std::string& classIdHex32, const std::vector& componentState) { std::vector out; if (classIdHex32.size() != 32) return out; // contract violation -> empty, never throws // Header (48 bytes): 'VST3' + int32 version + 32-char class ID + int64 list offset. constexpr std::uint64_t kHeaderSize = 4 + 4 + 32 + 8; const std::uint64_t compSize = componentState.size(); const std::uint64_t listOffset = kHeaderSize + compSize; out.reserve(static_cast(listOffset) + 4 + 4 + (4 + 8 + 8)); appendFourCC(out, "VST3"); putLE(out, 1); // kFormatVersion out.insert(out.end(), classIdHex32.begin(), classIdHex32.end()); putLE(out, listOffset); // Data area: the one 'Comp' chunk's bytes, at offset kHeaderSize. out.insert(out.end(), componentState.begin(), componentState.end()); // Chunk list: 'List' + entry count + one entry {'Comp', offset, size}. appendFourCC(out, "List"); putLE(out, 1); appendFourCC(out, "Comp"); putLE(out, kHeaderSize); putLE(out, compSize); return out; } std::vector instrumentDropStateBytes(const std::string& sampleId) { // Everything but selectionId stays at fresh-instance defaults (no zones, // implicit channel mode, generation 0) — same as a browser click. ComponentState cs; cs.selectionId = sampleId; return serializeComponentState(cs); } std::vector buildInstrumentDropPreset(const std::string& sampleId) { return buildVstPresetBytes(vstClassIdHex(), instrumentDropStateBytes(sampleId)); } DropOutcome decideDropOutcome(const DropAttempt& attempt) { DropOutcome out; if (attempt.addedFxIndex < 0) return out; // add failed — nothing exists to roll back if (!attempt.presetApplied) { out.rollbackFxIndex = attempt.addedFxIndex; return out; } out.loaded = true; return out; } bool infoNamesFxHotspot(const std::string& info) { // See the header contract for the prefix rule and the embed-strip exclusion. auto startsWith = [&info](const char* p) { return info.rfind(p, 0) == 0; }; if (startsWith("tcp.fxembed") || startsWith("mcp.fxembed")) return false; return startsWith("fx_") || startsWith("tcp.fx") || startsWith("mcp.fx"); } } // namespace reasampler::wire