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