Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #279 - Fail to render when non-VST fx are in fx chains #280

Merged
merged 4 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
UNRELEASED
Note: This release bumps the minimum required REAPER version from v6.11 to v6.37

* Fix occasional crash bug in start up of Binaural Monitoring plugin [#277](https://github.com/ebu/ear-production-suite/issues/277) [#278](https://github.com/ebu/ear-production-suite/pull/278)
* Fix fail to render when non-VST plugins are in FX chains [#279](https://github.com/ebu/ear-production-suite/issues/279) [#280](https://github.com/ebu/ear-production-suite/pull/280)

Version 1.1.0b

Expand Down
558 changes: 362 additions & 196 deletions reaper-adm-extension/src/reaper_adm/reaper_plugin_functions.h

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions reaper-adm-extension/src/reaper_adm/reaperapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,6 @@ class ReaperAPI {
virtual void CleanFXName(std::string& name) const = 0;
virtual int TrackFX_PositionByActualName(MediaTrack* track, const std::string& fxName) const = 0;
virtual int TrackFX_AddByActualName(MediaTrack* track, const char* fxname, bool recFX, int instantiate) const = 0;
virtual std::vector<std::pair<int, std::string>> GetVSTElementsFromTrackStateChunk(const std::string& fullChunk) const = 0;
virtual std::vector<std::string> SplitVSTElement(const std::string& elm, bool stripBoundingQuotes, bool includeSeperators) const = 0;
virtual std::string GetTrackStateChunkStr(MediaTrack* track) const = 0;
virtual int GetDawChannelCount() const = 0;

};
Expand Down
181 changes: 16 additions & 165 deletions reaper-adm-extension/src/reaper_adm/reaperapiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -824,186 +824,37 @@ std::optional<std::pair<double, double>> admplug::ReaperAPIImpl::getTrackAudioBo
return std::optional<std::pair<double, double>>();
}

std::vector<std::pair<int, std::string>> admplug::ReaperAPIImpl::GetVSTElementsFromTrackStateChunk(const std::string& fullChunk) const
{
std::vector<std::pair<int, std::string>> vst3Elements;

const std::vector<char> quoteMarks{ '\'', '`', '"' };
int elmStart = -1;

for (int pos = 0; pos < fullChunk.length(); ++pos) {

bool escapedQuote = false;
for (auto quote : quoteMarks) {
if (fullChunk[pos] == quote) {
// Entered a quote mark
if (pos == fullChunk.length() - 1) {
// Already at EOF - don't attempt to find
escapedQuote = true;
break;
}
// Find end quote mark
auto newPos = fullChunk.find(quote, pos + 1);
if (newPos == std::string::npos) {
// End not found to EOF - abort
escapedQuote = true;
break;
}
pos = newPos;
}
}
if (escapedQuote) {
break;
}

if (elmStart == -1) {
// Not in an element - check if at start of one
if (fullChunk.substr(pos, 5) == "<VST ") {
elmStart = pos;
}
}
else {
// In an element - check if at end
if (fullChunk.substr(pos, 1) == ">") {
vst3Elements.push_back(std::make_pair(
elmStart,
fullChunk.substr(elmStart, pos - elmStart + 1))
);
elmStart = -1;
}
}
}

return vst3Elements;
}

std::vector<std::string> admplug::ReaperAPIImpl::SplitVSTElement(const std::string& elm, bool stripBoundingQuotes, bool includeSeperators) const
{
std::vector<std::string> sec;

const std::vector<char> quoteMarks{ '\'', '`', '"' };
const std::vector<char> splitMarks{ ' ', '\r', '\n' };
int secStart = 5;

for (int pos = 5; pos < elm.length(); ++pos) {

bool escapedQuote = false;
for (auto quote : quoteMarks) {
if (elm[pos] == quote) {
// Entered a quote mark
if (pos == elm.length() - 1) {
// Already at EOF - don't attempt to find
escapedQuote = true;
break;
}
// Find end quote mark
auto newPos = elm.find(quote, pos + 1);
if (newPos == std::string::npos) {
// End not found to EOF - abort
escapedQuote = true;
break;
}
pos = newPos;
}
}
if (escapedQuote) {
break;
}

for (auto splitMark : splitMarks) {
if (elm[pos] == splitMark) {
// Close this section and start a new one
sec.push_back(elm.substr(secStart, pos - secStart));
if (includeSeperators) {
sec.push_back(std::string{ splitMark });
}
secStart = pos + 1;
break;
}
}
}

if (stripBoundingQuotes) {
for (auto& s : sec) {
if (s.length() >= 2) {
for (auto quote : quoteMarks) {
if (s[0] == quote && s[s.length() - 1] == quote) {
s = s.substr(1, s.length() - 2);
break;
}
}
}
}
}

return sec;
}

std::string admplug::ReaperAPIImpl::GetTrackStateChunkStr(MediaTrack* track) const
{
const size_t chunkMaxLen = 65535; // Should be plenty
char chunk[chunkMaxLen];
auto res = GetTrackStateChunk(track, chunk, chunkMaxLen, false);
if (!res) return std::string();
std::string fullChunk{ chunk, strnlen(chunk, chunkMaxLen) };
return fullChunk;
}

int admplug::ReaperAPIImpl::GetDawChannelCount() const
{
return GetReaperChannelCount(GetAppVersion());
}

bool admplug::ReaperAPIImpl::TrackFX_GetActualFXName(MediaTrack* track, int fx, std::string& name) const
bool admplug::ReaperAPIImpl::TrackFX_GetActualFXName(MediaTrack* track, int fxNum, std::string& name) const
{
// Note that;
// TrackFX_GetNamedConfigParm( track, 0, "fx_name" )
// can get the pre-aliased name but is only supported from v6.37
// Also does not support FX renamed in FX selection window
// (although neither does this)

auto chunk = GetTrackStateChunkStr(track);

auto vst3Elements = GetVSTElementsFromTrackStateChunk(chunk);
if (fx >= vst3Elements.size()) {
return false;
}

const int nameSectionNum = 0;

auto vst3Sections = SplitVSTElement(vst3Elements[fx].second, true, false);
if (vst3Sections.size() <= nameSectionNum) {
return false;
}

name = vst3Sections[nameSectionNum];;
const size_t fxNameMaxLen = 1024; // Should be plenty
char fxName[fxNameMaxLen];
Comment on lines +834 to +835
Copy link
Contributor

@rsjbailey rsjbailey Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just on 'stackwatch' because of the previous issue - do we call this from anywhere that will be troubled by pushing 1k onto the stack? If so:

const std::size_t fxNameMaxLen = 1023;
// I think the '\0' is superfluous, but it shouldn't hurt.
std::vector<char> fxName(fxNameMaxLen + 1, '\0'); 

if(auto fxNameRes = TrackFX_GetNamedConfigParm(track, fxNum, "fx_name", fxName.data(), fxNameMaxLen)) {
     fxName[fxNameMaxLen] = '\0'; // just in case REAPER is naughty
     name = std::string(fxName.data());
   } else {
     return false;
}

Or if we're doing it a few times

template<typename F>
std::optional<std::string> stringFromAPICall(F&& f, std::size_t maxLength = 1023) 
{
    std::vector<char> buffer(maxLength + 1, '\0');
    if(auto result = f(buffer.data(), maxLength)) {
        buffer[maxLength] = '\0'; 
        return std::string(buffer.data());
    } else { 
        return {}; 
    } 
}

and then

if(auto name = stringFromAPICall([track, fxNum](char* buffer, std::size_t size) {
  return TrackFX_GetNamedConfigParm(track, fxNum, "fx_name", buffer, size);
}) {
    // do something with *name
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're safe as the old code used to allocate 64k on the stack for the entire state chunk before parsing it. That said, if you think we should go with the vector for safety anyway, happy to go ahead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah, good point. No worries then.

auto fxNameRes = TrackFX_GetNamedConfigParm(track, fxNum, "fx_name", fxName, fxNameMaxLen);
if (!fxNameRes) return false;
name = std::string{ fxName, strnlen(fxName, fxNameMaxLen) };
return true;
}

std::vector<std::string> admplug::ReaperAPIImpl::TrackFX_GetActualFXNames(MediaTrack* track) const
{
// Only gets and parses state chunk once
// More efficient when you want to query every plugin on the track

std::vector<std::string> names;

auto chunk = GetTrackStateChunkStr(track);

auto vst3Elements = GetVSTElementsFromTrackStateChunk(chunk);

const int nameSectionNum = 0;

for (auto const& elmPair : vst3Elements) {
auto vst3Sections = SplitVSTElement(elmPair.second, true, false);
if (vst3Sections.size() <= nameSectionNum) {
names.push_back("");
std::vector<std::string> fxNames;
auto numFx = TrackFX_GetCount(track);
for (int fxNum = 0; fxNum < numFx; fxNum++) {
const size_t fxNameMaxLen = 1024; // Should be plenty
char fxName[fxNameMaxLen];
auto fxNameRes = TrackFX_GetNamedConfigParm(track, fxNum, "fx_name", fxName, fxNameMaxLen);
if (fxNameRes) {
fxNames.push_back(std::string{ fxName, strnlen(fxName, fxNameMaxLen) });
}
else {
names.push_back(vst3Sections[nameSectionNum]);
fxNames.push_back("");
}
}

return names;
return fxNames;
}

void admplug::ReaperAPIImpl::CleanFXName(std::string& fxName) const
Expand Down
3 changes: 0 additions & 3 deletions reaper-adm-extension/src/reaper_adm/reaperapiimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,6 @@ class ReaperAPIImpl : public ReaperAPI
void CleanFXName(std::string& name) const override;
int TrackFX_PositionByActualName(MediaTrack* track, const std::string& fxName) const override;
int TrackFX_AddByActualName(MediaTrack* track, const char* fxname, bool recFX, int instantiate) const override;
std::vector<std::pair<int, std::string>> GetVSTElementsFromTrackStateChunk(const std::string& fullChunk) const override;
std::vector<std::string> SplitVSTElement(const std::string& elm, bool stripBoundingQuotes, bool includeSeperators) const override;
std::string GetTrackStateChunkStr(MediaTrack* track) const override;
int GetDawChannelCount() const override;

private:
Expand Down
3 changes: 0 additions & 3 deletions reaper-adm-extension/test/reaper_adm/mocks/reaperapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,6 @@ class MockReaperAPI : public ReaperAPI {
MOCK_CONST_METHOD1(CleanFXName, void(std::string& name));
MOCK_CONST_METHOD2(TrackFX_PositionByActualName, int(MediaTrack* track, const std::string& fxName));
MOCK_CONST_METHOD4(TrackFX_AddByActualName, int(MediaTrack* track, const char* fxname, bool recFX, int instantiate));
MOCK_CONST_METHOD1(GetVSTElementsFromTrackStateChunk, std::vector<std::pair<int, std::string>>(const std::string& fullChunk));
MOCK_CONST_METHOD3(SplitVSTElement, std::vector<std::string>(const std::string& elm, bool stripBoundingQuotes, bool includeSeperators));
MOCK_CONST_METHOD1(GetTrackStateChunkStr, std::string(MediaTrack* track));
MOCK_CONST_METHOD0(GetDawChannelCount, int());
};

Expand Down
121 changes: 0 additions & 121 deletions reaper-adm-extension/test/reaper_adm/pluginrenametests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,129 +2,8 @@
#include <catch2/catch_all.hpp>
#include <reaperapiimpl.h>

const std::string trackState =
R"STATESTRING(<TRACK
NAME ""
PEAKCOL 16576
BEAT -1
AUTOMODE 0
VOLPAN 1 0 -1 -1 1
MUTESOLO 0 0 0
IPHASE 0
PLAYOFFS 0 1
ISBUS 0 0
BUSCOMP 0 0 0 0 0
SHOWINMIX 1 0.6667 0.5 1 0.5 0 -1 0
REC 0 0 0 0 0 0 0
VU 2
TRACKHEIGHT 0 0 0 0 0 0
INQ 0 0 0 0.5 100 0 0 100
NCHAN 64
FX 1
TRACKID {BD8FCB16-184B-4852-83A2-A916009600D0}
PERF 0
MIDIOUT -1
MAINSEND 1 0
<FXCHAIN
SHOW 0
LASTSEL 0
DOCKED 0
BYPASS 0 0 0
<VST "VST3: EAR Object (EBU)" "EAR Object.vst3" 0 "Custom Name" 1933719658{ABCDEF019182FAEB4542552045505311} ""
ajhCc+5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAEAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAA
QAAAAAAAAACAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAA
AAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgJ0BAAABAAAA//8QgA==
jQEAAAEAAABWQzIhSAEAADw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+IDxPYmplY3RzUGx1Z2luIGNvbm5lY3Rpb25faWQ9IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIgcm91dGluZz0iLTEiIGdhaW49IjAuMCIgYXppbXV0aD0iMC4wIiBlbGV2YXRpb249IjAuMCIgZGlzdGFuY2U9IjEuMCIgbGlua19zaXplPSIwIiBzaXpl
PSIwLjAiIHdpZHRoPSIwLjAiIGhlaWdodD0iMC4wIiBkZXB0aD0iMC4wIiBkaWZmdXNlPSIwLjAiIGRpdmVyZ2VuY2U9IjAiIGZhY3Rvcj0iMC4wIiByYW5nZT0iNDUuMCIgdXNlX3RyYWNrX25hbWU9IjEiIG5hbWU9IlRyYWNrIDEiLz4AAAAAAAAAAABKVUNFUHJpdmF0ZURhdGEAAQFCeXBhc3MAAQEDAB0AAAAAAAAASlVDRVByaXZhdGVEYXRhAAAAAAAAAAA=
AFByb2dyYW0gMQAQAAAA
>
PRESETNAME "Program 1"
FLOATPOS 1944 -62 616 512
FXID {159FD704-E101-42BC-A4D2-8F3F263F5160}
WAK 0 0
BYPASS 0 0 0
<VST "VST3: EAR Object (EBU)" "EAR Object.vst3" 0 SingleWord 1933719658{ABCDEF019182FAEB4542552045505311} ""
ajhCc+5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAEAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAA
QAAAAAAAAACAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAA
AAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgJ0BAAABAAAA//8QAA==
jQEAAAEAAABWQzIhSAEAADw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+IDxPYmplY3RzUGx1Z2luIGNvbm5lY3Rpb25faWQ9IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIgcm91dGluZz0iLTEiIGdhaW49IjAuMCIgYXppbXV0aD0iMC4wIiBlbGV2YXRpb249IjAuMCIgZGlzdGFuY2U9IjEuMCIgbGlua19zaXplPSIwIiBzaXpl
PSIwLjAiIHdpZHRoPSIwLjAiIGhlaWdodD0iMC4wIiBkZXB0aD0iMC4wIiBkaWZmdXNlPSIwLjAiIGRpdmVyZ2VuY2U9IjAiIGZhY3Rvcj0iMC4wIiByYW5nZT0iNDUuMCIgdXNlX3RyYWNrX25hbWU9IjEiIG5hbWU9IlRyYWNrIDEiLz4AAAAAAAAAAABKVUNFUHJpdmF0ZURhdGEAAQFCeXBhc3MAAQEDAB0AAAAAAAAASlVDRVByaXZhdGVEYXRhAAAAAAAAAAA=
AFByb2dyYW0gMQAQAAAA
>
PRESETNAME "Program 1"
FLOATPOS 1944 -62 742 738
FXID {66EFE191-C8CE-4786-8C38-4BE42D751146}
WAK 0 0
BYPASS 0 0 0
<VST "VST3: EAR Object (EBU)" "EAR Object.vst3" 0 `'quote' "Marks" 'lol'` 1933719658{ABCDEF019182FAEB4542552045505311} ""
ajhCc+5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAEAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAA
QAAAAAAAAACAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAA
AAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgJ0BAAABAAAA//8QAA==
jQEAAAEAAABWQzIhSAEAADw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+IDxPYmplY3RzUGx1Z2luIGNvbm5lY3Rpb25faWQ9IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIgcm91dGluZz0iLTEiIGdhaW49IjAuMCIgYXppbXV0aD0iMC4wIiBlbGV2YXRpb249IjAuMCIgZGlzdGFuY2U9IjEuMCIgbGlua19zaXplPSIwIiBzaXpl
PSIwLjAiIHdpZHRoPSIwLjAiIGhlaWdodD0iMC4wIiBkZXB0aD0iMC4wIiBkaWZmdXNlPSIwLjAiIGRpdmVyZ2VuY2U9IjAiIGZhY3Rvcj0iMC4wIiByYW5nZT0iNDUuMCIgdXNlX3RyYWNrX25hbWU9IjEiIG5hbWU9IlRyYWNrIDEiLz4AAAAAAAAAAABKVUNFUHJpdmF0ZURhdGEAAQFCeXBhc3MAAQEDAB0AAAAAAAAASlVDRVByaXZhdGVEYXRhAAAAAAAAAAA=
AFByb2dyYW0gMQAQAAAA
>
PRESETNAME "Program 1"
FLOATPOS 1944 -62 742 738
FXID {24D3CD65-1A9A-44D6-B8A1-5FBC65C39DD5}
WAK 0 0
BYPASS 0 0 0
<VST "VST3: EAR Object (EBU)" "EAR Object.vst3" 0 '"just in quotes"' 1933719658{ABCDEF019182FAEB4542552045505311} ""
ajhCc+5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAEAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAA
QAAAAAAAAACAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAA
AAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgJ0BAAABAAAA//8QAA==
jQEAAAEAAABWQzIhSAEAADw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+IDxPYmplY3RzUGx1Z2luIGNvbm5lY3Rpb25faWQ9IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIgcm91dGluZz0iLTEiIGdhaW49IjAuMCIgYXppbXV0aD0iMC4wIiBlbGV2YXRpb249IjAuMCIgZGlzdGFuY2U9IjEuMCIgbGlua19zaXplPSIwIiBzaXpl
PSIwLjAiIHdpZHRoPSIwLjAiIGhlaWdodD0iMC4wIiBkZXB0aD0iMC4wIiBkaWZmdXNlPSIwLjAiIGRpdmVyZ2VuY2U9IjAiIGZhY3Rvcj0iMC4wIiByYW5nZT0iNDUuMCIgdXNlX3RyYWNrX25hbWU9IjEiIG5hbWU9IlRyYWNrIDEiLz4AAAAAAAAAAABKVUNFUHJpdmF0ZURhdGEAAQFCeXBhc3MAAQEDAB0AAAAAAAAASlVDRVByaXZhdGVEYXRhAAAAAAAAAAA=
AFByb2dyYW0gMQAQAAAA
>
PRESETNAME "Program 1"
FLOATPOS 1944 -62 742 738
FXID {493829B3-8224-4E57-AC50-ED2C5E652E31}
WAK 0 0
BYPASS 0 0 0
<VST "VST3: EAR Object (EBU)" "EAR Object.vst3" 0 "" 1933719658{ABCDEF019182FAEB4542552045505311} ""
ajhCc+5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAEAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAA
QAAAAAAAAACAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAA
AAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAgJ0BAAABAAAA//8QAA==
jQEAAAEAAABWQzIhSAEAADw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+IDxPYmplY3RzUGx1Z2luIGNvbm5lY3Rpb25faWQ9IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIgcm91dGluZz0iLTEiIGdhaW49IjAuMCIgYXppbXV0aD0iMC4wIiBlbGV2YXRpb249IjAuMCIgZGlzdGFuY2U9IjEuMCIgbGlua19zaXplPSIwIiBzaXpl
PSIwLjAiIHdpZHRoPSIwLjAiIGhlaWdodD0iMC4wIiBkZXB0aD0iMC4wIiBkaWZmdXNlPSIwLjAiIGRpdmVyZ2VuY2U9IjAiIGZhY3Rvcj0iMC4wIiByYW5nZT0iNDUuMCIgdXNlX3RyYWNrX25hbWU9IjEiIG5hbWU9IlRyYWNrIDEiLz4AAAAAAAAAAABKVUNFUHJpdmF0ZURhdGEAAQFCeXBhc3MAAQEDAB0AAAAAAAAASlVDRVByaXZhdGVEYXRhAAAAAAAAAAA=
AFByb2dyYW0gMQAQAAAA
>
PRESETNAME "Program 1"
FLOATPOS 1944 -62 742 738
FXID {17FB49CB-DDA3-41B4-BAF3-9365DF7AC29C}
WAK 0 0
>
>)STATESTRING";

using namespace admplug;

TEST_CASE("Track State Chunk Parsing") {

reaper_plugin_info_t info{};
admplug::ReaperAPIImpl api(info);

auto vst3Elements = api.GetVSTElementsFromTrackStateChunk(trackState);

REQUIRE(vst3Elements.size() == 5);

for (int i = 0; i < vst3Elements.size(); ++i) {
auto el = vst3Elements[i];
auto s = api.SplitVSTElement(el.second, true, false);

REQUIRE(s.size() >= 6);
REQUIRE(s[0] == "VST3: EAR Object (EBU)");
REQUIRE(s[1] == "EAR Object.vst3");

if (i == 0) REQUIRE(s[3] == "Custom Name");
if (i == 1) REQUIRE(s[3] == "SingleWord");
if (i == 2) REQUIRE(s[3] == "'quote' \"Marks\" 'lol'");
if (i == 3) REQUIRE(s[3] == "\"just in quotes\"");
if (i == 4) REQUIRE(s[3] == "");
}

}

TEST_CASE("Clean Plugin Name") {

reaper_plugin_info_t info{};
Expand Down
Loading