Initial release
This commit is contained in:
@@ -0,0 +1,907 @@
|
||||
//------------------------------------------------------------------------
|
||||
// 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
|
||||
Reference in New Issue
Block a user