1 // Copyright 2021 Jean Pierre Cimalando
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 // SPDX-License-Identifier: Apache-2.0
19 #include "ysfx_preset.hpp"
20 #include "ysfx_utils.hpp"
25 #include "WDL/lineparse.h"
27 static void ysfx_preset_clear(ysfx_preset_t
*preset
);
28 static ysfx_bank_t
*ysfx_load_bank_from_rpl_text(const std::string
&text
);
29 static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t
*preset
, const char *name
, const std::vector
<uint8_t> &data
);
31 ysfx_bank_t
*ysfx_load_bank(const char *path
)
34 std::wstring wpath
= ysfx::widen(path
);
35 ysfx::FILE_u stream
{_wfopen(wpath
.c_str(), L
"rb")};
37 ysfx::FILE_u stream
{fopen(path
, "rb")};
44 constexpr uint32_t max_input
= 1u << 24;
45 input
.reserve(1u << 16);
47 for (int ch
; input
.size() < max_input
&& (ch
= fgetc(stream
.get())) != EOF
; ) {
48 ch
= (ch
== '\r' || ch
== '\n') ? ' ' : ch
;
49 input
.push_back((unsigned char)ch
);
52 if (ferror(stream
.get()))
56 return ysfx_load_bank_from_rpl_text(input
);
59 void ysfx_bank_free(ysfx_bank_t
*bank
)
66 if (ysfx_preset_t
*presets
= bank
->presets
) {
67 uint32_t count
= bank
->preset_count
;
68 for (uint32_t i
= 0; i
< count
; ++i
)
69 ysfx_preset_clear(&presets
[i
]);
76 static void ysfx_preset_clear(ysfx_preset_t
*preset
)
78 delete[] preset
->name
;
79 preset
->name
= nullptr;
81 ysfx_state_free(preset
->state
);
82 preset
->state
= nullptr;
85 static ysfx_bank_t
*ysfx_load_bank_from_rpl_text(const std::string
&text
)
88 if (parser
.parse(text
.c_str()) < 0)
92 std::vector
<ysfx_preset_t
> preset_list
;
93 preset_list
.reserve(256);
95 auto list_cleanup
= ysfx::defer([&preset_list
]() {
96 for (ysfx_preset_t
&pst
: preset_list
)
97 ysfx_preset_clear(&pst
);
101 int ntok
= parser
.getnumtokens();
104 if (strcmp("<REAPER_PRESET_LIBRARY", parser
.gettoken_str(itok
++)) != 0)
107 const char *bank_name
= parser
.gettoken_str(itok
++);
109 while (itok
< ntok
) {
110 if (strcmp("<PRESET", parser
.gettoken_str(itok
++)) == 0) {
111 const char *preset_name
= parser
.gettoken_str(itok
++);
113 std::vector
<uint8_t> blob
;
114 blob
.reserve(64 * 1024);
116 for (const char *part
; itok
< ntok
&&
117 strcmp(">", (part
= parser
.gettoken_str(itok
++))) != 0; )
119 std::vector
<uint8_t> blobChunk
= ysfx::decode_base64(part
);
120 blob
.insert(blob
.end(), blobChunk
.begin(), blobChunk
.end());
123 preset_list
.emplace_back();
124 ysfx_preset_t
&preset
= preset_list
.back();
126 ysfx_parse_preset_from_rpl_blob(&preset
, preset_name
, blob
);
131 ysfx_bank_u bank
{new ysfx_bank_t
{}};
132 bank
->name
= ysfx::strdup_using_new(bank_name
);
133 bank
->presets
= new ysfx_preset_t
[(uint32_t)preset_list
.size()]{};
134 bank
->preset_count
= (uint32_t)preset_list
.size();
136 for (uint32_t i
= (uint32_t)preset_list
.size(); i
-- > 0; ) {
137 bank
->presets
[i
] = preset_list
[i
];
138 preset_list
.pop_back();
141 return bank
.release();
144 static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t
*preset
, const char *name
, const std::vector
<uint8_t> &data
)
146 ysfx_state_t state
{};
147 std::vector
<ysfx_state_slider_t
> sliders
;
149 const char *text
= (const char *)data
.data();
150 size_t len
= data
.size();
152 // find the null terminator
154 while (pos
< len
&& data
[pos
] != 0)
157 // skip null terminator if there was one
158 // otherwise null-terminate the text
163 textbuf
.assign(text
, len
);
164 text
= textbuf
.c_str();
167 // whatever follows null is the raw serialization
168 state
.data
= const_cast<uint8_t *>(&data
[pos
]);
169 state
.data_size
= len
- pos
;
171 // parse a line of 64 slider floats (or '-' if missing)
173 if (parser
.parse(text
) >= 0) {
174 sliders
.reserve(ysfx_max_sliders
);
176 for (uint32_t i
= 0; i
< 64; ++i
) {
177 const char *str
= parser
.gettoken_str(i
);
178 bool skip
= str
[0] == '-' && str
[1] == '\0';
180 ysfx_state_slider_t slider
{};
182 slider
.value
= (ysfx_real
)ysfx::dot_atof(str
);
183 sliders
.push_back(slider
);
187 state
.sliders
= sliders
.data();
188 state
.slider_count
= (uint32_t)sliders
.size();
191 preset
->name
= ysfx::strdup_using_new(name
);
192 preset
->state
= ysfx_state_dup(&state
);