1 //------------------------------------------------------------------------
2 // Project     : VST SDK
3 //
4 // Category    : Helpers
5 // Filename    : public.sdk/source/vst/vstpresetfile.cpp
6 // Created by  : Steinberg, 03/2006
7 // Description : VST 3 Preset File Format
8 //
9 //-----------------------------------------------------------------------------
10 // LICENSE
11 // (c) 2020, Steinberg Media Technologies GmbH, All Rights Reserved
12 //-----------------------------------------------------------------------------
13 // Redistribution and use in source and binary forms, with or without modification,
14 // are permitted provided that the following conditions are met:
15 //
16 //   * Redistributions of source code must retain the above copyright notice,
17 //     this list of conditions and the following disclaimer.
18 //   * Redistributions in binary form must reproduce the above copyright notice,
19 //     this list of conditions and the following disclaimer in the documentation
20 //     and/or other materials provided with the distribution.
21 //   * Neither the name of the Steinberg Media Technologies nor the names of its
22 //     contributors may be used to endorse or promote products derived from this
23 //     software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33 // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
34 // OF THE POSSIBILITY OF SUCH DAMAGE.
35 //-----------------------------------------------------------------------------
36 
37 #include "vstpresetfile.h"
38 
39 #include <algorithm>
40 
41 
42 namespace Steinberg {
43 namespace Vst {
44 
45 //------------------------------------------------------------------------
46 // Preset Chunk IDs
47 //------------------------------------------------------------------------
48 static const ChunkID commonChunks[kNumPresetChunks] = {
49 	{'V', 'S', 'T', '3'},	// kHeader
50 	{'C', 'o', 'm', 'p'},	// kComponentState
51 	{'C', 'o', 'n', 't'},	// kControllerState
52 	{'P', 'r', 'o', 'g'},	// kProgramData
53 	{'I', 'n', 'f', 'o'},	// kMetaInfo
54 	{'L', 'i', 's', 't'}	// kChunkList
55 };
56 
57 //------------------------------------------------------------------------
58 // Preset Header: header id + version + class id + list offset
59 static const int32 kFormatVersion = 1;
60 static const int32 kClassIDSize = 32; // ASCII-encoded FUID
61 static const int32 kHeaderSize = sizeof (ChunkID) + sizeof (int32) + kClassIDSize + sizeof (TSize);
62 static const int32 kListOffsetPos = kHeaderSize - sizeof (TSize);
63 
64 //------------------------------------------------------------------------
getChunkID(ChunkType type)65 const ChunkID& getChunkID (ChunkType type)
66 {
67 	return commonChunks[type];
68 }
69 
70 #ifdef verify
71 #undef verify
72 #endif
73 
74 //------------------------------------------------------------------------
verify(tresult result)75 inline bool verify (tresult result)
76 {
77 	return result == kResultOk || result == kNotImplemented;
78 }
79 
80 //------------------------------------------------------------------------
copyStream(IBStream * inStream,IBStream * outStream)81 bool copyStream (IBStream* inStream, IBStream* outStream)
82 {
83 	if (!inStream || !outStream)
84 		return false;
85 
86 	int8 buffer[8192];
87 	int32 read = 0;
88 	int32 written = 0;
89 	while (inStream->read (buffer, 8192, &read) == kResultTrue && read > 0)
90 	{
91 		if (outStream->write (buffer, read, &written) != kResultTrue)
92 		{
93 			return false;
94 		}
95 	}
96 	return true;
97 }
98 
99 //------------------------------------------------------------------------
100 // PresetFile
101 //------------------------------------------------------------------------
savePreset(IBStream * stream,const FUID & classID,IComponent * component,IEditController * editController,const char * xmlBuffer,int32 xmlSize)102 bool PresetFile::savePreset (IBStream* stream, const FUID& classID, IComponent* component,
103                              IEditController* editController, const char* xmlBuffer, int32 xmlSize)
104 {
105 	PresetFile pf (stream);
106 	pf.setClassID (classID);
107 	if (!pf.writeHeader ())
108 		return false;
109 
110 	if (!pf.storeComponentState (component))
111 		return false;
112 
113 	if (editController && !pf.storeControllerState (editController))
114 		return false;
115 
116 	if (xmlBuffer && !pf.writeMetaInfo (xmlBuffer, xmlSize))
117 		return false;
118 
119 	return pf.writeChunkList ();
120 }
121 
122 //------------------------------------------------------------------------
savePreset(IBStream * stream,const FUID & classID,IBStream * componentStream,IBStream * editStream,const char * xmlBuffer,int32 xmlSize)123 bool PresetFile::savePreset (IBStream* stream, const FUID& classID, IBStream* componentStream,
124                              IBStream* editStream, const char* xmlBuffer, int32 xmlSize)
125 {
126 	PresetFile pf (stream);
127 	pf.setClassID (classID);
128 	if (!pf.writeHeader ())
129 		return false;
130 
131 	if (!pf.storeComponentState (componentStream))
132 		return false;
133 
134 	if (editStream && !pf.storeControllerState (editStream))
135 		return false;
136 
137 	if (xmlBuffer && !pf.writeMetaInfo (xmlBuffer, xmlSize))
138 		return false;
139 
140 	return pf.writeChunkList ();
141 }
142 
143 //------------------------------------------------------------------------
loadPreset(IBStream * stream,const FUID & classID,IComponent * component,IEditController * editController,std::vector<FUID> * otherClassIDArray)144 bool PresetFile::loadPreset (IBStream* stream, const FUID& classID, IComponent* component,
145                              IEditController* editController, std::vector<FUID>* otherClassIDArray)
146 {
147 	PresetFile pf (stream);
148 	if (!pf.readChunkList ())
149 		return false;
150 
151 	if (pf.getClassID () != classID)
152 	{
153 		if (otherClassIDArray)
154 		{
155 			// continue to load only if found in supported ID else abort load
156 			if (std::find (otherClassIDArray->begin (), otherClassIDArray->end (),
157 			               pf.getClassID ()) == otherClassIDArray->end ())
158 				return false;
159 		}
160 		else
161 			return false;
162 	}
163 
164 	if (!pf.restoreComponentState (component))
165 		return false;
166 
167 	if (editController)
168 	{
169 		// assign component state to controller
170 		if (!pf.restoreComponentState (editController))
171 			return false;
172 
173 		// restore controller-only state (if present)
174 		if (pf.contains (kControllerState) && !pf.restoreControllerState (editController))
175 			return false;
176 	}
177 	return true;
178 }
179 
180 //------------------------------------------------------------------------
PresetFile(IBStream * stream)181 PresetFile::PresetFile (IBStream* stream) : stream (stream), entryCount (0)
182 {
183 	memset (entries, 0, sizeof (entries));
184 
185 	if (stream)
186 		stream->addRef ();
187 }
188 
189 //------------------------------------------------------------------------
~PresetFile()190 PresetFile::~PresetFile ()
191 {
192 	if (stream)
193 		stream->release ();
194 }
195 
196 //------------------------------------------------------------------------
getEntry(ChunkType which) const197 const PresetFile::Entry* PresetFile::getEntry (ChunkType which) const
198 {
199 	const ChunkID& id = getChunkID (which);
200 	for (int32 i = 0; i < entryCount; i++)
201 		if (isEqualID (entries[i].id, id))
202 			return &entries[i];
203 	return nullptr;
204 }
205 
206 //------------------------------------------------------------------------
getLastEntry() const207 const PresetFile::Entry* PresetFile::getLastEntry () const
208 {
209 	return entryCount > 0 ? &entries[entryCount - 1] : nullptr;
210 }
211 
212 //------------------------------------------------------------------------
readID(ChunkID id)213 bool PresetFile::readID (ChunkID id)
214 {
215 	int32 numBytesRead = 0;
216 	stream->read (id, sizeof (ChunkID), &numBytesRead);
217 	return numBytesRead == sizeof (ChunkID);
218 }
219 
220 //------------------------------------------------------------------------
writeID(const ChunkID id)221 bool PresetFile::writeID (const ChunkID id)
222 {
223 	int32 numBytesWritten = 0;
224 	stream->write ((void*)id, sizeof (ChunkID), &numBytesWritten);
225 	return numBytesWritten == sizeof (ChunkID);
226 }
227 
228 //------------------------------------------------------------------------
readEqualID(const ChunkID id)229 bool PresetFile::readEqualID (const ChunkID id)
230 {
231 	ChunkID temp = {0};
232 	return readID (temp) && isEqualID (temp, id);
233 }
234 
235 //------------------------------------------------------------------------
readSize(TSize & size)236 bool PresetFile::readSize (TSize& size)
237 {
238 	int32 numBytesRead = 0;
239 	stream->read (&size, sizeof (TSize), &numBytesRead);
240 #if BYTEORDER == kBigEndian
241 	SWAP_64 (size)
242 #endif
243 	return numBytesRead == sizeof (TSize);
244 }
245 
246 //------------------------------------------------------------------------
writeSize(TSize size)247 bool PresetFile::writeSize (TSize size)
248 {
249 #if BYTEORDER == kBigEndian
250 	SWAP_64 (size)
251 #endif
252 	int32 numBytesWritten = 0;
253 	stream->write (&size, sizeof (TSize), &numBytesWritten);
254 	return numBytesWritten == sizeof (TSize);
255 }
256 
257 //------------------------------------------------------------------------
readInt32(int32 & value)258 bool PresetFile::readInt32 (int32& value)
259 {
260 	int32 numBytesRead = 0;
261 	stream->read (&value, sizeof (int32), &numBytesRead);
262 #if BYTEORDER == kBigEndian
263 	SWAP_32 (value)
264 #endif
265 	return numBytesRead == sizeof (int32);
266 }
267 
268 //------------------------------------------------------------------------
writeInt32(int32 value)269 bool PresetFile::writeInt32 (int32 value)
270 {
271 #if BYTEORDER == kBigEndian
272 	SWAP_32 (value)
273 #endif
274 	int32 numBytesWritten = 0;
275 	stream->write (&value, sizeof (int32), &numBytesWritten);
276 	return numBytesWritten == sizeof (int32);
277 }
278 
279 //------------------------------------------------------------------------
seekTo(TSize offset)280 bool PresetFile::seekTo (TSize offset)
281 {
282 	int64 result = -1;
283 	stream->seek (offset, IBStream::kIBSeekSet, &result);
284 	return result == offset;
285 }
286 
287 //------------------------------------------------------------------------
readChunkList()288 bool PresetFile::readChunkList ()
289 {
290 	seekTo (0);
291 	entryCount = 0;
292 
293 	char8 classString[kClassIDSize + 1] = {0};
294 
295 	// Read header
296 	int32 version = 0;
297 	TSize listOffset = 0;
298 	if (!(readEqualID (getChunkID (kHeader)) && readInt32 (version) &&
299 	      verify (stream->read (classString, kClassIDSize)) && readSize (listOffset) &&
300 	      listOffset > 0 && seekTo (listOffset)))
301 		return false;
302 
303 	classID.fromString (classString);
304 
305 	// Read list
306 	int32 count = 0;
307 	if (!readEqualID (getChunkID (kChunkList)))
308 		return false;
309 	if (!readInt32 (count))
310 		return false;
311 
312 	if (count > kMaxEntries)
313 		count = kMaxEntries;
314 
315 	for (int32 i = 0; i < count; i++)
316 	{
317 		Entry& e = entries[i];
318 		if (!(readID (e.id) && readSize (e.offset) && readSize (e.size)))
319 			break;
320 
321 		entryCount++;
322 	}
323 
324 	return entryCount > 0;
325 }
326 
327 //------------------------------------------------------------------------
writeHeader()328 bool PresetFile::writeHeader ()
329 {
330 	// header id + version + class id + list offset (unknown yet)
331 
332 	char8 classString[kClassIDSize + 1] = {0};
333 	classID.toString (classString);
334 
335 	return seekTo (0) && writeID (getChunkID (kHeader)) && writeInt32 (kFormatVersion) &&
336 	       verify (stream->write (classString, kClassIDSize)) && writeSize (0);
337 }
338 
339 //------------------------------------------------------------------------
writeChunkList()340 bool PresetFile::writeChunkList ()
341 {
342 	// Update list offset
343 	TSize pos = 0;
344 	stream->tell (&pos);
345 	if (!(seekTo (kListOffsetPos) && writeSize (pos) && seekTo (pos)))
346 		return false;
347 
348 	// Write list
349 	if (!writeID (getChunkID (kChunkList)))
350 		return false;
351 	if (!writeInt32 (entryCount))
352 		return false;
353 
354 	for (int32 i = 0; i < entryCount; i++)
355 	{
356 		Entry& e = entries[i];
357 		if (!(writeID (e.id) && writeSize (e.offset) && writeSize (e.size)))
358 			return false;
359 	}
360 	return true;
361 }
362 
363 //------------------------------------------------------------------------
beginChunk(Entry & e,ChunkType which)364 bool PresetFile::beginChunk (Entry& e, ChunkType which)
365 {
366 	if (entryCount >= kMaxEntries)
367 		return false;
368 
369 	const ChunkID& id = getChunkID (which);
370 	memcpy (e.id, &id, sizeof (ChunkID));
371 	stream->tell (&e.offset);
372 	e.size = 0;
373 	return true;
374 }
375 
376 //------------------------------------------------------------------------
endChunk(Entry & e)377 bool PresetFile::endChunk (Entry& e)
378 {
379 	if (entryCount >= kMaxEntries)
380 		return false;
381 
382 	TSize pos = 0;
383 	stream->tell (&pos);
384 	e.size = pos - e.offset;
385 	entries[entryCount++] = e;
386 	return true;
387 }
388 
389 //------------------------------------------------------------------------
readMetaInfo(char * xmlBuffer,int32 & size)390 bool PresetFile::readMetaInfo (char* xmlBuffer, int32& size)
391 {
392 	bool result = false;
393 	const Entry* e = getEntry (kMetaInfo);
394 	if (e)
395 	{
396 		if (xmlBuffer)
397 		{
398 			result = seekTo (e->offset) && verify (stream->read (xmlBuffer, size, &size));
399 		}
400 		else
401 		{
402 			size = (int32)e->size;
403 			result = size > 0;
404 		}
405 	}
406 	return result;
407 }
408 
409 //------------------------------------------------------------------------
writeMetaInfo(const char * xmlBuffer,int32 size,bool forceWriting)410 bool PresetFile::writeMetaInfo (const char* xmlBuffer, int32 size, bool forceWriting)
411 {
412 	if (contains (kMetaInfo)) // already exists!
413 	{
414 		if (!forceWriting)
415 			return false;
416 	}
417 	if (!prepareMetaInfoUpdate ())
418 		return false;
419 
420 	if (size == -1)
421 		size = (int32)strlen (xmlBuffer);
422 
423 	Entry e = {};
424 	return beginChunk (e, kMetaInfo) && verify (stream->write ((void*)xmlBuffer, size)) &&
425 	       endChunk (e);
426 }
427 
428 //------------------------------------------------------------------------
prepareMetaInfoUpdate()429 bool PresetFile::prepareMetaInfoUpdate ()
430 {
431 	TSize writePos = 0;
432 	const Entry* e = getEntry (kMetaInfo);
433 	if (e)
434 	{
435 		// meta info must be the last entry!
436 		if (e != getLastEntry ())
437 			return false;
438 
439 		writePos = e->offset;
440 		entryCount--;
441 	}
442 	else
443 	{
444 		// entries must be sorted ascending by offset!
445 		e = getLastEntry ();
446 		writePos = e ? e->offset + e->size : kHeaderSize;
447 	}
448 
449 	return seekTo (writePos);
450 }
451 
452 //------------------------------------------------------------------------
writeChunk(const void * data,int32 size,ChunkType which)453 bool PresetFile::writeChunk (const void* data, int32 size, ChunkType which)
454 {
455 	if (contains (which)) // already exists!
456 		return false;
457 
458 	Entry e = {};
459 	return beginChunk (e, which) && verify (stream->write ((void*)data, size)) && endChunk (e);
460 }
461 
462 //------------------------------------------------------------------------
seekToComponentState()463 bool PresetFile::seekToComponentState ()
464 {
465 	const Entry* e = getEntry (kComponentState);
466 	return e && seekTo (e->offset);
467 }
468 
469 //------------------------------------------------------------------------
storeComponentState(IComponent * component)470 bool PresetFile::storeComponentState (IComponent* component)
471 {
472 	if (contains (kComponentState)) // already exists!
473 		return false;
474 
475 	Entry e = {};
476 	return beginChunk (e, kComponentState) && verify (component->getState (stream)) && endChunk (e);
477 }
478 
479 //------------------------------------------------------------------------
storeComponentState(IBStream * componentStream)480 bool PresetFile::storeComponentState (IBStream* componentStream)
481 {
482 	if (contains (kComponentState)) // already exists!
483 		return false;
484 
485 	Entry e = {};
486 	return beginChunk (e, kComponentState) && copyStream (componentStream, stream) && endChunk (e);
487 }
488 
489 //------------------------------------------------------------------------
restoreComponentState(IComponent * component)490 bool PresetFile::restoreComponentState (IComponent* component)
491 {
492 	const Entry* e = getEntry (kComponentState);
493 	if (e)
494 	{
495 		auto* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size);
496 		FReleaser readOnlyBStreamReleaser (readOnlyBStream);
497 		return verify (component->setState (readOnlyBStream));
498 	}
499 	return false;
500 }
501 
502 //------------------------------------------------------------------------
restoreComponentState(IEditController * editController)503 bool PresetFile::restoreComponentState (IEditController* editController)
504 {
505 	const Entry* e = getEntry (kComponentState);
506 	if (e)
507 	{
508 		auto* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size);
509 		FReleaser readOnlyBStreamReleaser (readOnlyBStream);
510 		return verify (editController->setComponentState (readOnlyBStream));
511 	}
512 	return false;
513 }
514 
515 //------------------------------------------------------------------------
seekToControllerState()516 bool PresetFile::seekToControllerState ()
517 {
518 	const Entry* e = getEntry (kControllerState);
519 	return e && seekTo (e->offset);
520 }
521 
522 //------------------------------------------------------------------------
storeControllerState(IEditController * editController)523 bool PresetFile::storeControllerState (IEditController* editController)
524 {
525 	if (contains (kControllerState)) // already exists!
526 		return false;
527 
528 	Entry e = {};
529 	return beginChunk (e, kControllerState) && verify (editController->getState (stream)) &&
530 	       endChunk (e);
531 }
532 
533 //------------------------------------------------------------------------
storeControllerState(IBStream * editStream)534 bool PresetFile::storeControllerState (IBStream* editStream)
535 {
536 	if (contains (kControllerState)) // already exists!
537 		return false;
538 
539 	Entry e = {};
540 	return beginChunk (e, kControllerState) && copyStream (editStream, stream) && endChunk (e);
541 }
542 
543 //------------------------------------------------------------------------
restoreControllerState(IEditController * editController)544 bool PresetFile::restoreControllerState (IEditController* editController)
545 {
546 	const Entry* e = getEntry (kControllerState);
547 	if (e)
548 	{
549 		auto* readOnlyBStream = new ReadOnlyBStream (stream, e->offset, e->size);
550 		FReleaser readOnlyBStreamReleaser (readOnlyBStream);
551 		return verify (editController->setState (readOnlyBStream));
552 	}
553 	return false;
554 }
555 
556 //------------------------------------------------------------------------
storeProgramData(IBStream * inStream,ProgramListID listID)557 bool PresetFile::storeProgramData (IBStream* inStream, ProgramListID listID)
558 {
559 	if (contains (kProgramData)) // already exists!
560 		return false;
561 
562 	writeHeader ();
563 
564 	Entry e = {};
565 	if (beginChunk (e, kProgramData))
566 	{
567 		if (writeInt32 (listID))
568 		{
569 			if (!copyStream (inStream, stream))
570 				return false;
571 
572 			return endChunk (e);
573 		}
574 	}
575 	return false;
576 }
577 
578 //------------------------------------------------------------------------
storeProgramData(IProgramListData * programListData,ProgramListID listID,int32 programIndex)579 bool PresetFile::storeProgramData (IProgramListData* programListData, ProgramListID listID,
580                                    int32 programIndex)
581 {
582 	if (contains (kProgramData)) // already exists!
583 		return false;
584 
585 	writeHeader ();
586 
587 	Entry e = {};
588 	return beginChunk (e, kProgramData) && writeInt32 (listID) &&
589 	       verify (programListData->getProgramData (listID, programIndex, stream)) && endChunk (e);
590 }
591 
592 //------------------------------------------------------------------------
restoreProgramData(IProgramListData * programListData,ProgramListID * programListID,int32 programIndex)593 bool PresetFile::restoreProgramData (IProgramListData* programListData,
594                                      ProgramListID* programListID, int32 programIndex)
595 {
596 	const Entry* e = getEntry (kProgramData);
597 	ProgramListID savedProgramListID = -1;
598 	if (e && seekTo (e->offset))
599 	{
600 		if (readInt32 (savedProgramListID))
601 		{
602 			if (programListID && *programListID != savedProgramListID)
603 				return false;
604 
605 			int32 alreadyRead = sizeof (int32);
606 			auto* readOnlyBStream =
607 			    new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead);
608 			FReleaser readOnlyBStreamReleaser (readOnlyBStream);
609 			return programListData && verify (programListData->setProgramData (
610 			                              savedProgramListID, programIndex, readOnlyBStream));
611 		}
612 	}
613 	return false;
614 }
615 
616 //------------------------------------------------------------------------
storeProgramData(IUnitData * unitData,UnitID unitID)617 bool PresetFile::storeProgramData (IUnitData* unitData, UnitID unitID)
618 {
619 	if (contains (kProgramData)) // already exists!
620 		return false;
621 
622 	writeHeader ();
623 
624 	Entry e = {};
625 	return beginChunk (e, kProgramData) && writeInt32 (unitID) &&
626 	       verify (unitData->getUnitData (unitID, stream)) && endChunk (e);
627 }
628 
629 //------------------------------------------------------------------------
restoreProgramData(IUnitData * unitData,UnitID * unitId)630 bool PresetFile::restoreProgramData (IUnitData* unitData, UnitID* unitId)
631 {
632 	const Entry* e = getEntry (kProgramData);
633 	UnitID savedUnitID = -1;
634 	if (e && seekTo (e->offset))
635 	{
636 		if (readInt32 (savedUnitID))
637 		{
638 			if (unitId && *unitId != savedUnitID)
639 				return false;
640 
641 			int32 alreadyRead = sizeof (int32);
642 			auto* readOnlyBStream =
643 			    new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead);
644 			FReleaser readOnlyStreamReleaser (readOnlyBStream);
645 			return (unitData && verify (unitData->setUnitData (savedUnitID, readOnlyBStream)));
646 		}
647 	}
648 	return false;
649 }
650 
651 //------------------------------------------------------------------------
restoreProgramData(IUnitInfo * unitInfo,int32 unitProgramListID,int32 programIndex)652 bool PresetFile::restoreProgramData (IUnitInfo* unitInfo, int32 unitProgramListID,
653                                      int32 programIndex)
654 {
655 	const Entry* e = getEntry (kProgramData);
656 	int32 savedProgramListID = -1;
657 	if (e && seekTo (e->offset))
658 	{
659 		if (readInt32 (savedProgramListID))
660 		{
661 			if (unitProgramListID != savedProgramListID)
662 				return false;
663 
664 			int32 alreadyRead = sizeof (int32);
665 			auto* readOnlyBStream =
666 			    new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead);
667 			FReleaser readOnlyStreamReleaser (readOnlyBStream);
668 			return (unitInfo && unitInfo->setUnitProgramData (unitProgramListID, programIndex,
669 			                                                  readOnlyBStream));
670 		}
671 	}
672 	return false;
673 }
674 
675 //------------------------------------------------------------------------
getUnitProgramListID(int32 & unitProgramListID)676 bool PresetFile::getUnitProgramListID (int32& unitProgramListID)
677 {
678 	const Entry* e = getEntry (kProgramData);
679 	if (e && seekTo (e->offset))
680 	{
681 		if (readInt32 (unitProgramListID))
682 		{
683 			return true;
684 		}
685 	}
686 	return false;
687 }
688 
689 //------------------------------------------------------------------------
690 // FileStream implementation
691 //------------------------------------------------------------------------
open(const char * filename,const char * mode)692 IBStream* FileStream::open (const char* filename, const char* mode)
693 {
694 	FILE* file = fopen (filename, mode);
695 	return file ? new FileStream (file) : nullptr;
696 }
697 
698 //------------------------------------------------------------------------
FileStream(FILE * file)699 FileStream::FileStream (FILE* file)
700 : file (file) {FUNKNOWN_CTOR}
701 
702 //------------------------------------------------------------------------
~FileStream()703 FileStream::~FileStream ()
704 {
705 	fclose (file);
706 	FUNKNOWN_DTOR
707 }
708 
709 //------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS(FileStream,IBStream,IBStream::iid)710 IMPLEMENT_FUNKNOWN_METHODS (FileStream, IBStream, IBStream::iid)
711 
712 //------------------------------------------------------------------------
713 tresult PLUGIN_API FileStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
714 {
715 	size_t result = fread (buffer, 1, static_cast<size_t> (numBytes), file);
716 	if (numBytesRead)
717 		*numBytesRead = (int32)result;
718 	return static_cast<int32> (result) == numBytes ? kResultOk : kResultFalse;
719 }
720 
721 //------------------------------------------------------------------------
write(void * buffer,int32 numBytes,int32 * numBytesWritten)722 tresult PLUGIN_API FileStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
723 {
724 	size_t result = fwrite (buffer, 1, static_cast<size_t> (numBytes), file);
725 	if (numBytesWritten)
726 		*numBytesWritten = (int32)result;
727 	return static_cast<int32> (result) == numBytes ? kResultOk : kResultFalse;
728 }
729 
730 //------------------------------------------------------------------------
seek(int64 pos,int32 mode,int64 * result)731 tresult PLUGIN_API FileStream::seek (int64 pos, int32 mode, int64* result)
732 {
733 	if (fseek (file, (int32)pos, mode) == 0)
734 	{
735 		if (result)
736 			*result = ftell (file);
737 		return kResultOk;
738 	}
739 	return kResultFalse;
740 }
741 
742 //------------------------------------------------------------------------
tell(int64 * pos)743 tresult PLUGIN_API FileStream::tell (int64* pos)
744 {
745 	if (pos)
746 		*pos = ftell (file);
747 	return kResultOk;
748 }
749 
750 //------------------------------------------------------------------------
751 // ReadOnlyBStream implementation
752 //------------------------------------------------------------------------
IMPLEMENT_REFCOUNT(ReadOnlyBStream)753 IMPLEMENT_REFCOUNT (ReadOnlyBStream)
754 
755 //------------------------------------------------------------------------
756 ReadOnlyBStream::ReadOnlyBStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize)
757 : sourceStream (sourceStream)
758 , sourceOffset (sourceOffset)
759 , sectionSize (sectionSize)
760 , seekPosition (0)
761 {
762 	FUNKNOWN_CTOR
763 	if (sourceStream)
764 		sourceStream->addRef ();
765 }
766 
767 //------------------------------------------------------------------------
~ReadOnlyBStream()768 ReadOnlyBStream::~ReadOnlyBStream ()
769 {
770 	if (sourceStream)
771 		sourceStream->release ();
772 
773 	FUNKNOWN_DTOR
774 }
775 
776 //------------------------------------------------------------------------
queryInterface(const TUID _iid,void ** obj)777 tresult PLUGIN_API ReadOnlyBStream::queryInterface (const TUID _iid, void** obj)
778 {
779 	return sourceStream ? sourceStream->queryInterface (_iid, obj) : kResultFalse;
780 }
781 
782 //------------------------------------------------------------------------
read(void * buffer,int32 numBytes,int32 * numBytesRead)783 tresult PLUGIN_API ReadOnlyBStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
784 {
785 	if (numBytesRead)
786 		*numBytesRead = 0;
787 
788 	if (!sourceStream)
789 		return kNotInitialized;
790 
791 	int32 maxBytesToRead = (int32) (sectionSize - seekPosition);
792 	if (numBytes > maxBytesToRead)
793 		numBytes = maxBytesToRead;
794 	if (numBytes <= 0)
795 		return kResultOk;
796 
797 	tresult result = sourceStream->seek (sourceOffset + seekPosition, kIBSeekSet);
798 	if (result != kResultOk)
799 		return result;
800 
801 	int32 numRead = 0;
802 	result = sourceStream->read (buffer, numBytes, &numRead);
803 
804 	if (numRead > 0)
805 		seekPosition += numRead;
806 	if (numBytesRead)
807 		*numBytesRead = numRead;
808 
809 	return result;
810 }
811 
812 //------------------------------------------------------------------------
write(void *,int32,int32 * numBytesWritten)813 tresult PLUGIN_API ReadOnlyBStream::write (void* /*buffer*/, int32 /*numBytes*/,
814                                            int32* numBytesWritten)
815 {
816 	if (numBytesWritten)
817 		*numBytesWritten = 0;
818 
819 	return kNotImplemented;
820 }
821 
822 //------------------------------------------------------------------------
seek(int64 pos,int32 mode,int64 * result)823 tresult PLUGIN_API ReadOnlyBStream::seek (int64 pos, int32 mode, int64* result)
824 {
825 	switch (mode)
826 	{
827 		case kIBSeekSet: seekPosition = pos; break;
828 
829 		case kIBSeekCur: seekPosition += pos; break;
830 
831 		case kIBSeekEnd: seekPosition = sectionSize + pos; break;
832 	}
833 
834 	if (seekPosition < 0)
835 		seekPosition = 0;
836 	if (seekPosition > sectionSize)
837 		seekPosition = sectionSize;
838 
839 	if (result)
840 		*result = seekPosition;
841 	return kResultOk;
842 }
843 
844 //------------------------------------------------------------------------
tell(int64 * pos)845 tresult PLUGIN_API ReadOnlyBStream::tell (int64* pos)
846 {
847 	if (pos)
848 		*pos = seekPosition;
849 	return kResultOk;
850 }
851 
852 //------------------------------------------------------------------------
853 // BufferStream implementation
854 //------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS(BufferStream,IBStream,IBStream::iid)855 IMPLEMENT_FUNKNOWN_METHODS (BufferStream, IBStream, IBStream::iid)
856 
857 //------------------------------------------------------------------------
858 BufferStream::BufferStream () {FUNKNOWN_CTOR}
859 
860 //------------------------------------------------------------------------
~BufferStream()861 BufferStream::~BufferStream () {FUNKNOWN_DTOR}
862 
863 //------------------------------------------------------------------------
read(void * buffer,int32 numBytes,int32 * numBytesRead)864 tresult PLUGIN_API BufferStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
865 {
866 	uint32 size = mBuffer.get (buffer, static_cast<uint32> (numBytes));
867 	if (numBytesRead)
868 		*numBytesRead = static_cast<int32> (size);
869 
870 	return kResultTrue;
871 }
872 
873 //------------------------------------------------------------------------
write(void * buffer,int32 numBytes,int32 * numBytesWritten)874 tresult PLUGIN_API BufferStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
875 {
876 	bool res = mBuffer.put (buffer, static_cast<uint32> (numBytes));
877 	if (numBytesWritten)
878 		*numBytesWritten = res ? numBytes : 0;
879 
880 	return res ? kResultTrue : kResultFalse;
881 }
882 
883 //------------------------------------------------------------------------
seek(int64 pos,int32 mode,int64 * result)884 tresult PLUGIN_API BufferStream::seek (int64 pos, int32 mode, int64* result)
885 {
886 	bool res = false;
887 	switch (mode)
888 	{
889 		//--- -----------------
890 		case IBStream::kIBSeekSet:
891 		{
892 			int64 tmp = pos;
893 			if (tmp < 0)
894 				tmp = 0;
895 			res = mBuffer.setFillSize (static_cast<uint32> (tmp));
896 		}
897 		break;
898 
899 		//--- -----------------
900 		case IBStream::kIBSeekCur:
901 		{
902 			int64 tmp = mBuffer.getFillSize () + pos;
903 			if (tmp < 0)
904 				tmp = 0;
905 			res = mBuffer.setFillSize (static_cast<uint32> (tmp));
906 		}
907 		break;
908 
909 		//--- -----------------
910 		case IBStream::kIBSeekEnd:
911 		{
912 			int64 tmp = mBuffer.getSize () - pos;
913 			if (tmp < 0)
914 				tmp = 0;
915 			res = mBuffer.setFillSize (static_cast<uint32> (tmp));
916 		}
917 		break;
918 	}
919 	if (res && result)
920 		*result = mBuffer.getFillSize ();
921 
922 	return res ? kResultTrue : kResultFalse;
923 }
924 
925 //------------------------------------------------------------------------
tell(int64 * pos)926 tresult PLUGIN_API BufferStream::tell (int64* pos)
927 {
928 	if (pos)
929 		*pos = mBuffer.getFillSize ();
930 	return pos ? kResultTrue : kResultFalse;
931 }
932 
933 //------------------------------------------------------------------------
934 } // namespace Vst
935 } // namespace Steinberg
936