vstpresetfile.cpp

Go to the documentation of this file.
00001 //------------------------------------------------------------------------
00002 // Project     : VST SDK
00003 // Version     : 3.0
00004 //
00005 // Category    : Helpers
00006 // Filename    : vstpresetfile.cpp
00007 // Created by  : Steinberg, 03/2006
00008 // Modified    : $Date: 2008/01/09 12:51:07 $
00009 // Description : VST3 Preset File Format
00010 //
00011 //-----------------------------------------------------------------------------
00012 // LICENSE
00013 // © 2008, Steinberg Media Technologies GmbH, All Rights Reserved
00014 //-----------------------------------------------------------------------------
00015 // This Software Development Kit may not be distributed in parts or its entirety  
00016 // without prior written agreement by Steinberg Media Technologies GmbH. 
00017 // This SDK must not be used to re-engineer or manipulate any technology used  
00018 // in any Steinberg or Third-party application or software module, 
00019 // unless permitted by law.
00020 // Neither the name of the Steinberg Media Technologies nor the names of its
00021 // contributors may be used to endorse or promote products derived from this
00022 // software without specific prior written permission.
00023 // 
00024 // THIS SDK IS PROVIDED BY STEINBERG MEDIA TECHNOLOGIES GMBH "AS IS" AND
00025 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
00026 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
00027 // IN NO EVENT SHALL STEINBERG MEDIA TECHNOLOGIES GMBH BE LIABLE FOR ANY DIRECT, 
00028 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
00029 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
00030 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
00031 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
00032 // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
00033 // OF THE POSSIBILITY OF SUCH DAMAGE.
00034 //-----------------------------------------------------------------------------
00035 
00036 #include "vstpresetfile.h"
00037 
00038 namespace Steinberg {
00039 namespace Vst {
00040 
00041 //------------------------------------------------------------------------
00042 // Preset Chunk IDs
00043 //------------------------------------------------------------------------
00044 static const ChunkID commonChunks[kNumPresetChunks] =
00045 {
00046         {'V', 'S', 'T', '3'},   // kHeader
00047         {'C', 'o', 'm', 'p'},   // kComponentState
00048         {'C', 'o', 'n', 't'},   // kControllerState
00049         {'P', 'r', 'o', 'g'},   // kProgramData
00050         {'I', 'n', 'f', 'o'},   // kMetaInfo
00051         {'L', 'i', 's', 't'}    // kChunkList
00052 };
00053 
00054 //------------------------------------------------------------------------
00055 // Preset Header: header id + version + class id + list offset
00056 static const int32 kFormatVersion = 1;
00057 static const int32 kClassIDSize = 32; // ASCII-encoded FUID
00058 static const int32 kHeaderSize = sizeof (ChunkID) + sizeof (int32) + kClassIDSize + sizeof (TSize);
00059 static const int32 kListOffsetPos = kHeaderSize - sizeof (TSize);
00060 
00061 //------------------------------------------------------------------------
00062 const ChunkID& getChunkID (ChunkType type)
00063 {
00064         return commonChunks[type];
00065 }
00066 
00067 #ifdef verify
00068 #undef verify
00069 #endif
00070 
00071 //------------------------------------------------------------------------
00072 inline bool verify (tresult result)
00073 {
00074         return result == kResultOk || result == kNotImplemented;
00075 }
00076 
00077 //------------------------------------------------------------------------
00078 // PresetFile
00079 //------------------------------------------------------------------------
00080 bool PresetFile::savePreset (IBStream* stream, const FUID& classID, 
00081                                                          IComponent* component, IEditController* editController,
00082                                                          const char* xmlBuffer, int32 xmlSize)
00083 {
00084         PresetFile pf (stream);
00085         pf.setClassID (classID);
00086         if (!pf.writeHeader ())
00087                 return false;
00088 
00089         if (!pf.storeComponentState (component))
00090                 return false;
00091         
00092         if (editController && !pf.storeControllerState (editController))
00093                 return false;
00094 
00095         if (xmlBuffer && !pf.writeMetaInfo (xmlBuffer, xmlSize))
00096                 return false;
00097 
00098         return pf.writeChunkList ();
00099 }
00100 
00101 //------------------------------------------------------------------------
00102 bool PresetFile::loadPreset (IBStream* stream, const FUID& classID, IComponent* component, IEditController* editController)
00103 {
00104         PresetFile pf (stream);
00105         if (!pf.readChunkList ())
00106                 return false;
00107 
00108         if (pf.getClassID () != classID)
00109                 return false;
00110 
00111         if (!pf.restoreComponentState (component))
00112                 return false;
00113 
00114         if (editController)
00115         {
00116                 // assign component state to controller
00117                 if (!pf.restoreComponentState (editController)) 
00118                         return false;
00119 
00120                 // restore controller-only state (if present)
00121                 if (pf.contains (kControllerState) && !pf.restoreControllerState (editController))
00122                         return false;
00123         }
00124         return true;
00125 }
00126 
00127 //------------------------------------------------------------------------
00128 PresetFile::PresetFile (IBStream* stream)
00129 : stream (stream)
00130 , entryCount (0)
00131 {
00132         memset (entries, 0, sizeof (entries));
00133 
00134         if (stream)
00135                 stream->addRef ();
00136 }
00137 
00138 //------------------------------------------------------------------------
00139 PresetFile::~PresetFile ()
00140 {
00141         if (stream)
00142                 stream->release ();
00143 }
00144 
00145 //------------------------------------------------------------------------
00146 const PresetFile::Entry* PresetFile::getEntry (ChunkType which) const
00147 {
00148         const ChunkID& id = getChunkID (which);
00149         for (int32 i = 0; i < entryCount; i++)
00150                 if (isEqualID (entries[i].id, id))
00151                         return &entries[i];
00152         return 0;
00153 }
00154 
00155 //------------------------------------------------------------------------
00156 const PresetFile::Entry* PresetFile::getLastEntry () const 
00157 { 
00158         return entryCount > 0 ? &entries[entryCount-1] : 0; 
00159 }
00160 
00161 //------------------------------------------------------------------------
00162 bool PresetFile::readID (ChunkID id)
00163 {
00164         int32 numBytesRead = 0;
00165         stream->read (id, sizeof (ChunkID), &numBytesRead);
00166         return numBytesRead == sizeof (ChunkID);
00167 }
00168 
00169 //------------------------------------------------------------------------
00170 bool PresetFile::writeID (const ChunkID id)
00171 {
00172         int32 numBytesWritten = 0;
00173         stream->write ((void*)id, sizeof (ChunkID), &numBytesWritten);
00174         return numBytesWritten == sizeof (ChunkID);
00175 }
00176 
00177 //------------------------------------------------------------------------
00178 bool PresetFile::readEqualID (const ChunkID id)
00179 {
00180         ChunkID temp = {0};
00181         return readID (temp) && isEqualID (temp, id);
00182 }
00183 
00184 //------------------------------------------------------------------------
00185 bool PresetFile::readSize (TSize& size)
00186 {
00187         int32 numBytesRead = 0;
00188         stream->read (&size, sizeof (TSize), &numBytesRead);
00189 #if BYTEORDER == kBigEndian
00190         SWAP_64 (size)
00191 #endif  
00192         return numBytesRead == sizeof (TSize);
00193 }
00194 
00195 //------------------------------------------------------------------------
00196 bool PresetFile::writeSize (TSize size)
00197 {
00198 #if BYTEORDER == kBigEndian
00199         SWAP_64 (size)
00200 #endif  
00201         int32 numBytesWritten = 0;
00202         stream->write (&size, sizeof (TSize), &numBytesWritten);
00203         return numBytesWritten == sizeof (TSize);
00204 }
00205 
00206 //------------------------------------------------------------------------
00207 bool PresetFile::readInt32 (int32& value)
00208 {
00209         int32 numBytesRead = 0;
00210         stream->read (&value, sizeof (int32), &numBytesRead);
00211 #if BYTEORDER == kBigEndian
00212         SWAP_32 (value)
00213 #endif
00214         return numBytesRead == sizeof (int32);
00215 }
00216 
00217 //------------------------------------------------------------------------
00218 bool PresetFile::writeInt32 (int32 value)
00219 {
00220 #if BYTEORDER == kBigEndian
00221         SWAP_32 (value)
00222 #endif  
00223         int32 numBytesWritten = 0;
00224         stream->write (&value, sizeof (int32), &numBytesWritten);
00225         return numBytesWritten == sizeof (int32);
00226 }
00227 
00228 //------------------------------------------------------------------------
00229 bool PresetFile::seekTo (TSize offset)
00230 {
00231         int64 result = -1;
00232         stream->seek (offset, IBStream::kIBSeekSet, &result);
00233         return result == offset;
00234 }
00235 
00236 //------------------------------------------------------------------------
00237 bool PresetFile::readChunkList ()
00238 {
00239         seekTo (0);
00240         entryCount = 0;
00241 
00242         char classString[kClassIDSize + 1] = {0};
00243 
00244         // Read header
00245         int32 version = 0;
00246         TSize listOffset = 0;
00247         if (!(readEqualID (getChunkID (kHeader)) &&
00248                  readInt32 (version) &&
00249                  verify (stream->read (classString, kClassIDSize)) &&
00250                  readSize (listOffset) &&
00251                  listOffset > 0 &&
00252                  seekTo (listOffset)))
00253                 return false;
00254 
00255         classID.fromString (classString);
00256 
00257         // Read list
00258         int32 count = 0;
00259         if (!readEqualID (getChunkID (kChunkList)))
00260                 return false;
00261         if (!readInt32 (count))
00262                 return false;
00263 
00264         if (count > kMaxEntries)
00265                 count = kMaxEntries;
00266 
00267         for (int32 i = 0; i < count; i++)
00268         {
00269                 Entry& e = entries[i];
00270                 if (!(readID (e.id) &&
00271                          readSize (e.offset) &&
00272                          readSize (e.size)))
00273                         break;
00274 
00275                 entryCount++;
00276         }
00277 
00278         return entryCount > 0;
00279 }
00280 
00281 //------------------------------------------------------------------------
00282 bool PresetFile::writeHeader ()
00283 {
00284         // header id + version + class id + list offset (unknown yet)
00285 
00286         char classString[kClassIDSize + 1] = {0};
00287         classID.toString (classString);
00288 
00289         return  seekTo (0) &&
00290                         writeID (getChunkID (kHeader)) && 
00291                         writeInt32 (kFormatVersion) && 
00292                         verify (stream->write (classString, kClassIDSize)) &&
00293                         writeSize (0);
00294 }
00295 
00296 //------------------------------------------------------------------------
00297 bool PresetFile::writeChunkList ()
00298 {
00299         // Update list offset
00300         TSize pos = 0;
00301         stream->tell (&pos);
00302         if (!(seekTo (kListOffsetPos) &&
00303                  writeSize (pos) &&
00304                  seekTo (pos)))
00305                 return false;
00306 
00307         // Write list
00308         if (!writeID (getChunkID (kChunkList)))
00309                 return false;
00310         if (!writeInt32 (entryCount))
00311                 return false;
00312 
00313         for (int32 i = 0; i < entryCount; i++)
00314         {
00315                 Entry& e = entries[i];
00316                 if (!(writeID (e.id) &&
00317                          writeSize (e.offset) &&
00318                          writeSize (e.size)))
00319                          return false;
00320         }
00321         return true;
00322 }
00323 
00324 //------------------------------------------------------------------------
00325 bool PresetFile::beginChunk (Entry& e, ChunkType which)
00326 {
00327         if (entryCount >= kMaxEntries)
00328                 return false;
00329 
00330         const ChunkID& id = getChunkID (which);
00331         memcpy (e.id, &id, sizeof (ChunkID));
00332         stream->tell (&e.offset);
00333         e.size = 0;
00334         return true;
00335 }
00336 
00337 //------------------------------------------------------------------------
00338 bool PresetFile::endChunk (Entry& e)
00339 {
00340         if (entryCount >= kMaxEntries)
00341                 return false;
00342 
00343         TSize pos = 0;
00344         stream->tell (&pos);
00345         e.size = pos - e.offset;
00346         entries[entryCount++] = e;
00347         return true;
00348 }
00349 
00350 //------------------------------------------------------------------------
00351 bool PresetFile::readMetaInfo (char* xmlBuffer, int32& size)
00352 {
00353         bool result = false;
00354         const Entry* e = getEntry (kMetaInfo);
00355         if (e)
00356         {
00357                 if (xmlBuffer)
00358                 {
00359                         result = seekTo (e->offset) && 
00360                                          verify (stream->read (xmlBuffer, size, &size));
00361                 }
00362                 else
00363                 {
00364                         size = (int32)e->size;
00365                         result = size > 0;
00366                 }
00367         }
00368         return result;
00369 }
00370 
00371 //------------------------------------------------------------------------
00372 bool PresetFile::writeMetaInfo (const char* xmlBuffer, int32 size)
00373 {
00374         if (contains (kMetaInfo)) // already exists!
00375                 return false;
00376 
00377         if (size == -1)
00378                 size = (int32)strlen (xmlBuffer);
00379 
00380         Entry e = {0};
00381         return  beginChunk (e, kMetaInfo) && 
00382                         verify (stream->write ((void*)xmlBuffer, size)) && 
00383                         endChunk (e);
00384 }
00385 
00386 //------------------------------------------------------------------------
00387 bool PresetFile::prepareMetaInfoUpdate ()
00388 {
00389         TSize writePos = 0;
00390         const Entry* e = getEntry (kMetaInfo);
00391         if (e)
00392         {
00393                 // meta info must be the last entry!    
00394                 if (e != getLastEntry ())
00395                         return false;
00396 
00397                 writePos = e->offset;
00398                 entryCount--;
00399         }
00400         else
00401         {
00402                 // entries must be sorted ascending by offset!
00403                 e = getLastEntry ();
00404                 writePos = e ? e->offset + e->size : kHeaderSize;
00405         }
00406 
00407         return seekTo (writePos);
00408 }
00409 
00410 //------------------------------------------------------------------------
00411 bool PresetFile::writeChunk (const void* data, int32 size, ChunkType which)
00412 {
00413         if (contains (which)) // already exists!
00414                 return false;
00415 
00416         Entry e = {0};
00417         return  beginChunk (e, which) && 
00418                         verify (stream->write ((void*)data, size)) && 
00419                         endChunk (e);
00420 }
00421 
00422 //------------------------------------------------------------------------
00423 bool PresetFile::storeComponentState (IComponent* component)
00424 {
00425         if (contains (kComponentState)) // already exists!
00426                 return false;
00427 
00428         Entry e = {0};
00429         return  beginChunk (e, kComponentState) && 
00430                         verify (component->getState (stream)) && 
00431                         endChunk (e);
00432 }
00433 
00434 //------------------------------------------------------------------------
00435 bool PresetFile::restoreComponentState (IComponent* component)
00436 {
00437         const Entry* e = getEntry (kComponentState);
00438         return  e && seekTo (e->offset) && 
00439                         verify (component->setState (stream));
00440 }
00441 
00442 //------------------------------------------------------------------------
00443 bool PresetFile::restoreComponentState (IEditController* editController)
00444 {
00445         const Entry* e = getEntry (kComponentState);
00446         return  e && seekTo (e->offset) && 
00447                         verify (editController->setComponentState (stream));
00448 }
00449 
00450 //------------------------------------------------------------------------
00451 bool PresetFile::storeControllerState (IEditController* editController)
00452 {
00453         if (contains (kControllerState)) // already exists!
00454                 return false;
00455 
00456         Entry e = {0};
00457         return  beginChunk (e, kControllerState) && 
00458                         verify (editController->getState (stream)) && 
00459                         endChunk (e);
00460 }
00461 
00462 //------------------------------------------------------------------------
00463 bool PresetFile::restoreControllerState (IEditController* editController)
00464 {
00465         const Entry* e = getEntry (kControllerState);
00466         return  e && seekTo (e->offset) &&
00467                         verify (editController->setState (stream));
00468 }
00469 
00470 //------------------------------------------------------------------------
00471 bool PresetFile::storeProgramData (IBStream* inStream, ProgramListID listID, int32 programIndex)
00472 {
00473         if (contains (kProgramData)) // already exists!
00474                 return false;
00475 
00476         Entry e = {0};
00477         if (beginChunk (e, kProgramData))
00478         {
00479                 if (writeInt32 (listID))
00480                 {
00481                         int8 buffer[128];
00482                         int32 read = 0;
00483                         int32 written = 0;
00484                         while (inStream->read (buffer, 128, &read) == kResultTrue && read > 0)
00485                         {
00486                                 if (stream->write (buffer, read, &written) != kResultTrue)
00487                                 {
00488                                         return false;
00489                                 }
00490                         }
00491                         return endChunk (e);
00492                 }
00493         }
00494         return false;
00495 }
00496 
00497 //------------------------------------------------------------------------
00498 bool PresetFile::storeProgramData (IUnitData* unitData, ProgramListID listID, int32 programIndex)
00499 {
00500         if (contains (kProgramData)) // already exists!
00501                 return false;
00502 
00503         Entry e = {0};
00504         return  beginChunk (e, kProgramData) && 
00505                         writeInt32 (listID) &&
00506                         verify (unitData->getProgramData (listID, programIndex, stream)) && 
00507                         endChunk (e);
00508 }
00509 
00510 //------------------------------------------------------------------------
00511 bool PresetFile::restoreProgramData (IUnitData* unitData, ProgramListID* listID, int32 programIndex)
00512 {
00513         const Entry* e = getEntry (kProgramData);
00514         ProgramListID savedListID = -1;
00515         if (e && seekTo (e->offset))
00516         {
00517                 if (readInt32 (savedListID))
00518                 {
00519                         if (listID && *listID != savedListID)
00520                                 return false;
00521                         return verify (unitData->setProgramData (savedListID, programIndex, stream));
00522                 }
00523         }
00524         return false;
00525 }
00526 
00527 //------------------------------------------------------------------------
00528 bool PresetFile::restoreProgramData (IUnitInfo* unitInfo, ProgramListID listID, int32 programIndex)
00529 {
00530         const Entry* e = getEntry (kProgramData);
00531         ProgramListID savedListID = -1;
00532         return  e && seekTo (e->offset) && 
00533                         readInt32 (savedListID) &&
00534                         listID == savedListID &&
00535                         verify (unitInfo->setUnitProgramData (listID, programIndex, stream));
00536 }
00537 
00538 //------------------------------------------------------------------------
00539 // FileStream
00540 //------------------------------------------------------------------------
00541 IBStream* FileStream::open (const char* filename, const char* mode)
00542 {
00543         FILE* file = fopen (filename, mode);
00544         return file ? new FileStream (file) : 0;
00545 }
00546 
00547 //------------------------------------------------------------------------
00548 FileStream::FileStream (FILE* file)
00549 : file (file)
00550 {
00551         FUNKNOWN_CTOR
00552 }
00553 
00554 //------------------------------------------------------------------------
00555 FileStream::~FileStream ()
00556 {
00557         fclose (file);
00558         FUNKNOWN_DTOR
00559 }
00560 
00561 //------------------------------------------------------------------------
00562 IMPLEMENT_FUNKNOWN_METHODS (FileStream, IBStream, IBStream::iid)
00563 
00564 //------------------------------------------------------------------------
00565 tresult PLUGIN_API FileStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
00566 {
00567         size_t result = fread (buffer, 1, numBytes, file);
00568         if (numBytesRead)
00569                 *numBytesRead = (int32)result;
00570         return result == numBytes ? kResultOk : kResultFalse;
00571 }
00572 
00573 //------------------------------------------------------------------------
00574 tresult PLUGIN_API FileStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
00575 {
00576         size_t result = fwrite (buffer, 1, numBytes, file);
00577         if (numBytesWritten)
00578                 *numBytesWritten = (int32)result;
00579         return result == numBytes ? kResultOk : kResultFalse;
00580 }
00581 
00582 //------------------------------------------------------------------------
00583 tresult PLUGIN_API FileStream::seek (int64 pos, int32 mode, int64* result)
00584 {
00585         if (fseek (file, (int32)pos, mode) == 0)
00586         {
00587                 if (result)
00588                         *result = ftell (file);
00589                 return kResultOk;
00590         }
00591         return kResultFalse;
00592 }
00593 
00594 //------------------------------------------------------------------------
00595 tresult PLUGIN_API FileStream::tell (int64* pos)
00596 {
00597         if (pos)
00598                 *pos = ftell (file);
00599         return kResultOk;
00600 }
00601 
00602 //------------------------------------------------------------------------
00603 }} // namespace Vst
Empty

Copyright ©2008 Steinberg Media Technologies. All Rights Reserved.