908 lines
25 KiB
C++
908 lines
25 KiB
C++
//------------------------------------------------------------------------
|
|
// Project : VST SDK
|
|
//
|
|
// Category : Helpers
|
|
// Filename : public.sdk/source/vst/vstpresetfile.cpp
|
|
// Created by : Steinberg, 03/2006
|
|
// Description : VST 3 Preset File Format
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// This file is part of a Steinberg SDK. It is subject to the license terms
|
|
// in the LICENSE file found in the top-level directory of this distribution
|
|
// and at www.steinberg.net/sdklicenses.
|
|
// No part of the SDK, including this file, may be copied, modified, propagated,
|
|
// or distributed except according to the terms contained in the LICENSE file.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "vstpresetfile.h"
|
|
#include <algorithm>
|
|
|
|
namespace Steinberg {
|
|
namespace Vst {
|
|
|
|
//------------------------------------------------------------------------
|
|
// Preset Chunk IDs
|
|
//------------------------------------------------------------------------
|
|
static const ChunkID commonChunks[kNumPresetChunks] = {
|
|
{'V', 'S', 'T', '3'}, // kHeader
|
|
{'C', 'o', 'm', 'p'}, // kComponentState
|
|
{'C', 'o', 'n', 't'}, // kControllerState
|
|
{'P', 'r', 'o', 'g'}, // kProgramData
|
|
{'I', 'n', 'f', 'o'}, // kMetaInfo
|
|
{'L', 'i', 's', 't'} // kChunkList
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
// Preset Header: header id + version + class id + list offset
|
|
static const int32 kFormatVersion = 1;
|
|
static const int32 kClassIDSize = 32; // ASCII-encoded FUID
|
|
static const int32 kHeaderSize = sizeof (ChunkID) + sizeof (int32) + kClassIDSize + sizeof (TSize);
|
|
static const int32 kListOffsetPos = kHeaderSize - sizeof (TSize);
|
|
|
|
//------------------------------------------------------------------------
|
|
const ChunkID& getChunkID (ChunkType type)
|
|
{
|
|
return commonChunks[type];
|
|
}
|
|
|
|
#ifdef verify
|
|
#undef verify
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------
|
|
inline bool verify (tresult result)
|
|
{
|
|
return result == kResultOk || result == kNotImplemented;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool copyStream (IBStream* inStream, IBStream* outStream)
|
|
{
|
|
if (!inStream || !outStream)
|
|
return false;
|
|
|
|
int8 buffer[8192];
|
|
int32 read = 0;
|
|
int32 written = 0;
|
|
while (inStream->read (buffer, 8192, &read) == kResultTrue && read > 0)
|
|
{
|
|
if (outStream->write (buffer, read, &written) != kResultTrue)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// PresetFile
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::savePreset (IBStream* stream, const FUID& classID, IComponent* component,
|
|
IEditController* editController, const char* xmlBuffer, int32 xmlSize)
|
|
{
|
|
PresetFile pf (stream);
|
|
pf.setClassID (classID);
|
|
if (!pf.writeHeader ())
|
|
return false;
|
|
|
|
if (!pf.storeComponentState (component))
|
|
return false;
|
|
|
|
if (editController && !pf.storeControllerState (editController))
|
|
return false;
|
|
|
|
if (xmlBuffer && !pf.writeMetaInfo (xmlBuffer, xmlSize))
|
|
return false;
|
|
|
|
return pf.writeChunkList ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::savePreset (IBStream* stream, const FUID& classID, IBStream* componentStream,
|
|
IBStream* editStream, const char* xmlBuffer, int32 xmlSize)
|
|
{
|
|
PresetFile pf (stream);
|
|
pf.setClassID (classID);
|
|
if (!pf.writeHeader ())
|
|
return false;
|
|
|
|
if (!pf.storeComponentState (componentStream))
|
|
return false;
|
|
|
|
if (editStream && !pf.storeControllerState (editStream))
|
|
return false;
|
|
|
|
if (xmlBuffer && !pf.writeMetaInfo (xmlBuffer, xmlSize))
|
|
return false;
|
|
|
|
return pf.writeChunkList ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::loadPreset (IBStream* stream, const FUID& classID, IComponent* component,
|
|
IEditController* editController, std::vector<FUID>* otherClassIDArray)
|
|
{
|
|
PresetFile pf (stream);
|
|
if (!pf.readChunkList ())
|
|
return false;
|
|
|
|
if (pf.getClassID () != classID)
|
|
{
|
|
if (otherClassIDArray)
|
|
{
|
|
// continue to load only if found in supported ID else abort load
|
|
if (std::find (otherClassIDArray->begin (), otherClassIDArray->end (),
|
|
pf.getClassID ()) == otherClassIDArray->end ())
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
if (!pf.restoreComponentState (component))
|
|
return false;
|
|
|
|
if (editController)
|
|
{
|
|
// assign component state to controller
|
|
if (!pf.restoreComponentState (editController))
|
|
return false;
|
|
|
|
// restore controller-only state (if present)
|
|
if (pf.contains (kControllerState) && !pf.restoreControllerState (editController))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
PresetFile::PresetFile (IBStream* stream) : stream (stream)
|
|
{
|
|
memset (entries, 0, sizeof (entries));
|
|
|
|
if (stream)
|
|
stream->addRef ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
PresetFile::~PresetFile ()
|
|
{
|
|
if (stream)
|
|
stream->release ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
const PresetFile::Entry* PresetFile::getEntry (ChunkType which) const
|
|
{
|
|
const ChunkID& id = getChunkID (which);
|
|
for (int32 i = 0; i < entryCount; i++)
|
|
if (isEqualID (entries[i].id, id))
|
|
return &entries[i];
|
|
return nullptr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
const PresetFile::Entry* PresetFile::getLastEntry () const
|
|
{
|
|
return entryCount > 0 ? &entries[entryCount - 1] : nullptr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::readID (ChunkID id)
|
|
{
|
|
int32 numBytesRead = 0;
|
|
stream->read (id, sizeof (ChunkID), &numBytesRead);
|
|
return numBytesRead == sizeof (ChunkID);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::writeID (const ChunkID id)
|
|
{
|
|
int32 numBytesWritten = 0;
|
|
stream->write ((void*)id, sizeof (ChunkID), &numBytesWritten);
|
|
return numBytesWritten == sizeof (ChunkID);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::readEqualID (const ChunkID id)
|
|
{
|
|
ChunkID temp = {0};
|
|
return readID (temp) && isEqualID (temp, id);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::readSize (TSize& size)
|
|
{
|
|
int32 numBytesRead = 0;
|
|
stream->read (&size, sizeof (TSize), &numBytesRead);
|
|
#if BYTEORDER == kBigEndian
|
|
SWAP_64 (size)
|
|
#endif
|
|
return numBytesRead == sizeof (TSize);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::writeSize (TSize size)
|
|
{
|
|
#if BYTEORDER == kBigEndian
|
|
SWAP_64 (size)
|
|
#endif
|
|
int32 numBytesWritten = 0;
|
|
stream->write (&size, sizeof (TSize), &numBytesWritten);
|
|
return numBytesWritten == sizeof (TSize);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::readInt32 (int32& value)
|
|
{
|
|
int32 numBytesRead = 0;
|
|
stream->read (&value, sizeof (int32), &numBytesRead);
|
|
#if BYTEORDER == kBigEndian
|
|
SWAP_32 (value)
|
|
#endif
|
|
return numBytesRead == sizeof (int32);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::writeInt32 (int32 value)
|
|
{
|
|
#if BYTEORDER == kBigEndian
|
|
SWAP_32 (value)
|
|
#endif
|
|
int32 numBytesWritten = 0;
|
|
stream->write (&value, sizeof (int32), &numBytesWritten);
|
|
return numBytesWritten == sizeof (int32);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::seekTo (TSize offset)
|
|
{
|
|
int64 result = -1;
|
|
stream->seek (offset, IBStream::kIBSeekSet, &result);
|
|
return result == offset;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::readChunkList ()
|
|
{
|
|
seekTo (0);
|
|
entryCount = 0;
|
|
|
|
char8 classString[kClassIDSize + 1] = {0};
|
|
|
|
// Read header
|
|
int32 version = 0;
|
|
TSize listOffset = 0;
|
|
if (!(readEqualID (getChunkID (kHeader)) && readInt32 (version) &&
|
|
verify (stream->read (classString, kClassIDSize)) && readSize (listOffset) &&
|
|
listOffset > 0 && seekTo (listOffset)))
|
|
return false;
|
|
|
|
classID.fromString (classString);
|
|
|
|
// Read list
|
|
int32 count = 0;
|
|
if (!readEqualID (getChunkID (kChunkList)))
|
|
return false;
|
|
if (!readInt32 (count))
|
|
return false;
|
|
|
|
if (count > kMaxEntries)
|
|
count = kMaxEntries;
|
|
|
|
for (int32 i = 0; i < count; i++)
|
|
{
|
|
Entry& e = entries[i];
|
|
if (!(readID (e.id) && readSize (e.offset) && readSize (e.size)))
|
|
break;
|
|
|
|
entryCount++;
|
|
}
|
|
|
|
return entryCount > 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::writeHeader ()
|
|
{
|
|
// header id + version + class id + list offset (unknown yet)
|
|
|
|
char8 classString[kClassIDSize + 1] = {0};
|
|
classID.toString (classString);
|
|
|
|
return seekTo (0) && writeID (getChunkID (kHeader)) && writeInt32 (kFormatVersion) &&
|
|
verify (stream->write (classString, kClassIDSize)) && writeSize (0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::writeChunkList ()
|
|
{
|
|
// Update list offset
|
|
TSize pos = 0;
|
|
stream->tell (&pos);
|
|
if (!(seekTo (kListOffsetPos) && writeSize (pos) && seekTo (pos)))
|
|
return false;
|
|
|
|
// Write list
|
|
if (!writeID (getChunkID (kChunkList)))
|
|
return false;
|
|
if (!writeInt32 (entryCount))
|
|
return false;
|
|
|
|
for (int32 i = 0; i < entryCount; i++)
|
|
{
|
|
Entry& e = entries[i];
|
|
if (!(writeID (e.id) && writeSize (e.offset) && writeSize (e.size)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::beginChunk (Entry& e, ChunkType which)
|
|
{
|
|
if (entryCount >= kMaxEntries)
|
|
return false;
|
|
|
|
const ChunkID& id = getChunkID (which);
|
|
memcpy (e.id, &id, sizeof (ChunkID));
|
|
stream->tell (&e.offset);
|
|
e.size = 0;
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::endChunk (Entry& e)
|
|
{
|
|
if (entryCount >= kMaxEntries)
|
|
return false;
|
|
|
|
TSize pos = 0;
|
|
stream->tell (&pos);
|
|
e.size = pos - e.offset;
|
|
entries[entryCount++] = e;
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::readMetaInfo (char* xmlBuffer, int32& size)
|
|
{
|
|
bool result = false;
|
|
const Entry* e = getEntry (kMetaInfo);
|
|
if (e)
|
|
{
|
|
if (xmlBuffer)
|
|
{
|
|
result = seekTo (e->offset) && verify (stream->read (xmlBuffer, size, &size));
|
|
}
|
|
else
|
|
{
|
|
size = (int32)e->size;
|
|
result = size > 0;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::writeMetaInfo (const char* xmlBuffer, int32 size, bool forceWriting)
|
|
{
|
|
if (contains (kMetaInfo)) // already exists!
|
|
{
|
|
if (!forceWriting)
|
|
return false;
|
|
}
|
|
if (!prepareMetaInfoUpdate ())
|
|
return false;
|
|
|
|
if (size == -1)
|
|
size = (int32)strlen (xmlBuffer);
|
|
|
|
Entry e = {};
|
|
return beginChunk (e, kMetaInfo) && verify (stream->write ((void*)xmlBuffer, size)) &&
|
|
endChunk (e);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::prepareMetaInfoUpdate ()
|
|
{
|
|
TSize writePos = 0;
|
|
const Entry* e = getEntry (kMetaInfo);
|
|
if (e)
|
|
{
|
|
// meta info must be the last entry!
|
|
if (e != getLastEntry ())
|
|
return false;
|
|
|
|
writePos = e->offset;
|
|
entryCount--;
|
|
}
|
|
else
|
|
{
|
|
// entries must be sorted ascending by offset!
|
|
e = getLastEntry ();
|
|
writePos = e ? e->offset + e->size : kHeaderSize;
|
|
}
|
|
|
|
return seekTo (writePos);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::writeChunk (const void* data, int32 size, ChunkType which)
|
|
{
|
|
if (contains (which)) // already exists!
|
|
return false;
|
|
|
|
Entry e = {};
|
|
return beginChunk (e, which) && verify (stream->write ((void*)data, size)) && endChunk (e);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::seekToComponentState ()
|
|
{
|
|
const Entry* e = getEntry (kComponentState);
|
|
return e && seekTo (e->offset);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::storeComponentState (IComponent* component)
|
|
{
|
|
if (contains (kComponentState)) // already exists!
|
|
return false;
|
|
|
|
Entry e = {};
|
|
return beginChunk (e, kComponentState) && verify (component->getState (stream)) && endChunk (e);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::storeComponentState (IBStream* componentStream)
|
|
{
|
|
if (contains (kComponentState)) // already exists!
|
|
return false;
|
|
|
|
Entry e = {};
|
|
return beginChunk (e, kComponentState) && copyStream (componentStream, stream) && endChunk (e);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::restoreComponentState (IComponent* component)
|
|
{
|
|
const Entry* e = getEntry (kComponentState);
|
|
if (!e)
|
|
return false;
|
|
|
|
auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size));
|
|
return verify (component->setState (readOnlyBStream));
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::restoreComponentState (IEditController* editController)
|
|
{
|
|
const Entry* e = getEntry (kComponentState);
|
|
if (e)
|
|
{
|
|
auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size));
|
|
return verify (editController->setComponentState (readOnlyBStream));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::seekToControllerState ()
|
|
{
|
|
const Entry* e = getEntry (kControllerState);
|
|
return e && seekTo (e->offset);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::storeControllerState (IEditController* editController)
|
|
{
|
|
if (contains (kControllerState)) // already exists!
|
|
return false;
|
|
|
|
Entry e = {};
|
|
return beginChunk (e, kControllerState) && verify (editController->getState (stream)) &&
|
|
endChunk (e);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::storeControllerState (IBStream* editStream)
|
|
{
|
|
if (contains (kControllerState)) // already exists!
|
|
return false;
|
|
|
|
Entry e = {};
|
|
return beginChunk (e, kControllerState) && copyStream (editStream, stream) && endChunk (e);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::restoreControllerState (IEditController* editController)
|
|
{
|
|
const Entry* e = getEntry (kControllerState);
|
|
if (e)
|
|
{
|
|
auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size));
|
|
return verify (editController->setState (readOnlyBStream)) || e->size == 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::storeProgramData (IBStream* inStream, ProgramListID listID)
|
|
{
|
|
if (contains (kProgramData)) // already exists!
|
|
return false;
|
|
|
|
writeHeader ();
|
|
|
|
Entry e = {};
|
|
if (beginChunk (e, kProgramData))
|
|
{
|
|
if (writeInt32 (listID))
|
|
{
|
|
if (!copyStream (inStream, stream))
|
|
return false;
|
|
|
|
return endChunk (e);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::storeProgramData (IProgramListData* programListData, ProgramListID listID,
|
|
int32 programIndex)
|
|
{
|
|
if (contains (kProgramData)) // already exists!
|
|
return false;
|
|
|
|
writeHeader ();
|
|
|
|
Entry e = {};
|
|
return beginChunk (e, kProgramData) && writeInt32 (listID) &&
|
|
verify (programListData->getProgramData (listID, programIndex, stream)) && endChunk (e);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::restoreProgramData (IProgramListData* programListData,
|
|
ProgramListID* programListID, int32 programIndex)
|
|
{
|
|
const Entry* e = getEntry (kProgramData);
|
|
ProgramListID savedProgramListID = -1;
|
|
if (e && seekTo (e->offset))
|
|
{
|
|
if (readInt32 (savedProgramListID))
|
|
{
|
|
if (programListID && *programListID != savedProgramListID)
|
|
return false;
|
|
|
|
int32 alreadyRead = sizeof (int32);
|
|
auto readOnlyBStream = owned (
|
|
new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead));
|
|
return programListData && verify (programListData->setProgramData (
|
|
savedProgramListID, programIndex, readOnlyBStream));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::storeProgramData (IUnitData* unitData, UnitID unitID)
|
|
{
|
|
if (contains (kProgramData)) // already exists!
|
|
return false;
|
|
|
|
writeHeader ();
|
|
|
|
Entry e = {};
|
|
return beginChunk (e, kProgramData) && writeInt32 (unitID) &&
|
|
verify (unitData->getUnitData (unitID, stream)) && endChunk (e);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::restoreProgramData (IUnitData* unitData, UnitID* unitId)
|
|
{
|
|
const Entry* e = getEntry (kProgramData);
|
|
UnitID savedUnitID = -1;
|
|
if (e && seekTo (e->offset))
|
|
{
|
|
if (readInt32 (savedUnitID))
|
|
{
|
|
if (unitId && *unitId != savedUnitID)
|
|
return false;
|
|
|
|
int32 alreadyRead = sizeof (int32);
|
|
auto readOnlyBStream = owned (
|
|
new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead));
|
|
return (unitData && verify (unitData->setUnitData (savedUnitID, readOnlyBStream)));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::restoreProgramData (IUnitInfo* unitInfo, int32 unitProgramListID,
|
|
int32 programIndex)
|
|
{
|
|
const Entry* e = getEntry (kProgramData);
|
|
int32 savedProgramListID = -1;
|
|
if (e && seekTo (e->offset))
|
|
{
|
|
if (readInt32 (savedProgramListID))
|
|
{
|
|
if (unitProgramListID != savedProgramListID)
|
|
return false;
|
|
|
|
int32 alreadyRead = sizeof (int32);
|
|
auto readOnlyBStream = owned (
|
|
new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead));
|
|
return (unitInfo && unitInfo->setUnitProgramData (unitProgramListID, programIndex,
|
|
readOnlyBStream));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool PresetFile::getUnitProgramListID (int32& unitProgramListID)
|
|
{
|
|
const Entry* e = getEntry (kProgramData);
|
|
if (e && seekTo (e->offset))
|
|
{
|
|
if (readInt32 (unitProgramListID))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// FileStream implementation
|
|
//------------------------------------------------------------------------
|
|
IBStream* FileStream::open (const char* filename, const char* mode)
|
|
{
|
|
FILE* file = fopen (filename, mode);
|
|
return file ? new FileStream (file) : nullptr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
FileStream::FileStream (FILE* file)
|
|
: file (file) {FUNKNOWN_CTOR}
|
|
|
|
//------------------------------------------------------------------------
|
|
FileStream::~FileStream ()
|
|
{
|
|
fclose (file);
|
|
FUNKNOWN_DTOR
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
IMPLEMENT_FUNKNOWN_METHODS (FileStream, IBStream, IBStream::iid)
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API FileStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
|
|
{
|
|
size_t result = fread (buffer, 1, static_cast<size_t> (numBytes), file);
|
|
if (numBytesRead)
|
|
*numBytesRead = (int32)result;
|
|
return static_cast<int32> (result) == numBytes ? kResultOk : kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API FileStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
|
|
{
|
|
size_t result = fwrite (buffer, 1, static_cast<size_t> (numBytes), file);
|
|
if (numBytesWritten)
|
|
*numBytesWritten = (int32)result;
|
|
return static_cast<int32> (result) == numBytes ? kResultOk : kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API FileStream::seek (int64 pos, int32 mode, int64* result)
|
|
{
|
|
if (fseek (file, (int32)pos, mode) == 0)
|
|
{
|
|
if (result)
|
|
*result = ftell (file);
|
|
return kResultOk;
|
|
}
|
|
return kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API FileStream::tell (int64* pos)
|
|
{
|
|
if (pos)
|
|
*pos = ftell (file);
|
|
return kResultOk;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// ReadOnlyBStream implementation
|
|
//------------------------------------------------------------------------
|
|
IMPLEMENT_REFCOUNT (ReadOnlyBStream)
|
|
|
|
//------------------------------------------------------------------------
|
|
ReadOnlyBStream::ReadOnlyBStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize)
|
|
: sourceStream (sourceStream)
|
|
, sourceOffset (sourceOffset)
|
|
, sectionSize (sectionSize)
|
|
, seekPosition (0)
|
|
{
|
|
FUNKNOWN_CTOR
|
|
if (sourceStream)
|
|
sourceStream->addRef ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
ReadOnlyBStream::~ReadOnlyBStream ()
|
|
{
|
|
if (sourceStream)
|
|
sourceStream->release ();
|
|
|
|
FUNKNOWN_DTOR
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API ReadOnlyBStream::queryInterface (const TUID _iid, void** obj)
|
|
{
|
|
return sourceStream ? sourceStream->queryInterface (_iid, obj) : kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API ReadOnlyBStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
|
|
{
|
|
if (numBytesRead)
|
|
*numBytesRead = 0;
|
|
|
|
if (!sourceStream)
|
|
return kNotInitialized;
|
|
|
|
int32 maxBytesToRead = static_cast<int32> (sectionSize - seekPosition);
|
|
if (numBytes > maxBytesToRead)
|
|
numBytes = maxBytesToRead;
|
|
if (numBytes <= 0)
|
|
return kResultOk;
|
|
|
|
tresult result = sourceStream->seek (sourceOffset + seekPosition, kIBSeekSet);
|
|
if (result != kResultOk)
|
|
return result;
|
|
|
|
int32 numRead = 0;
|
|
result = sourceStream->read (buffer, numBytes, &numRead);
|
|
|
|
if (numRead > 0)
|
|
seekPosition += numRead;
|
|
if (numBytesRead)
|
|
*numBytesRead = numRead;
|
|
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API ReadOnlyBStream::write (void* /*buffer*/, int32 /*numBytes*/,
|
|
int32* numBytesWritten)
|
|
{
|
|
if (numBytesWritten)
|
|
*numBytesWritten = 0;
|
|
|
|
return kNotImplemented;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API ReadOnlyBStream::seek (int64 pos, int32 mode, int64* result)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case kIBSeekSet: seekPosition = pos; break;
|
|
|
|
case kIBSeekCur: seekPosition += pos; break;
|
|
|
|
case kIBSeekEnd: seekPosition = sectionSize + pos; break;
|
|
}
|
|
|
|
if (seekPosition < 0)
|
|
seekPosition = 0;
|
|
if (seekPosition > sectionSize)
|
|
seekPosition = sectionSize;
|
|
|
|
if (result)
|
|
*result = seekPosition;
|
|
return kResultOk;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API ReadOnlyBStream::tell (int64* pos)
|
|
{
|
|
if (pos)
|
|
*pos = seekPosition;
|
|
return kResultOk;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// BufferStream implementation
|
|
//------------------------------------------------------------------------
|
|
IMPLEMENT_FUNKNOWN_METHODS (BufferStream, IBStream, IBStream::iid)
|
|
|
|
//------------------------------------------------------------------------
|
|
BufferStream::BufferStream () {FUNKNOWN_CTOR}
|
|
|
|
//------------------------------------------------------------------------
|
|
BufferStream::~BufferStream () {FUNKNOWN_DTOR}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API BufferStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
|
|
{
|
|
uint32 size = mBuffer.get (buffer, static_cast<uint32> (numBytes));
|
|
if (numBytesRead)
|
|
*numBytesRead = static_cast<int32> (size);
|
|
|
|
return kResultTrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API BufferStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
|
|
{
|
|
bool res = mBuffer.put (buffer, static_cast<uint32> (numBytes));
|
|
if (numBytesWritten)
|
|
*numBytesWritten = res ? numBytes : 0;
|
|
|
|
return res ? kResultTrue : kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API BufferStream::seek (int64 pos, int32 mode, int64* result)
|
|
{
|
|
bool res = false;
|
|
switch (mode)
|
|
{
|
|
//--- -----------------
|
|
case IBStream::kIBSeekSet:
|
|
{
|
|
int64 tmp = pos;
|
|
if (tmp < 0)
|
|
tmp = 0;
|
|
res = mBuffer.setFillSize (static_cast<uint32> (tmp));
|
|
}
|
|
break;
|
|
|
|
//--- -----------------
|
|
case IBStream::kIBSeekCur:
|
|
{
|
|
int64 tmp = mBuffer.getFillSize () + pos;
|
|
if (tmp < 0)
|
|
tmp = 0;
|
|
res = mBuffer.setFillSize (static_cast<uint32> (tmp));
|
|
}
|
|
break;
|
|
|
|
//--- -----------------
|
|
case IBStream::kIBSeekEnd:
|
|
{
|
|
int64 tmp = mBuffer.getSize () - pos;
|
|
if (tmp < 0)
|
|
tmp = 0;
|
|
res = mBuffer.setFillSize (static_cast<uint32> (tmp));
|
|
}
|
|
break;
|
|
}
|
|
if (res && result)
|
|
*result = mBuffer.getFillSize ();
|
|
|
|
return res ? kResultTrue : kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API BufferStream::tell (int64* pos)
|
|
{
|
|
if (pos)
|
|
*pos = mBuffer.getFillSize ();
|
|
return pos ? kResultTrue : kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
} // namespace Vst
|
|
} // namespace Steinberg
|