/[svn]/libgig/trunk/src/RIFF.cpp
ViewVC logotype

Diff of /libgig/trunk/src/RIFF.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1885 by persson, Thu Apr 16 18:25:31 2009 UTC revision 2584 by schoenebeck, Sat May 31 20:54:39 2014 UTC
# Line 2  Line 2 
2   *                                                                         *   *                                                                         *
3   *   libgig - C++ cross-platform Gigasampler format file access library    *   *   libgig - C++ cross-platform Gigasampler format file access library    *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003-2009 by Christian Schoenebeck                      *   *   Copyright (C) 2003-2014 by Christian Schoenebeck                      *
6   *                              <cuse@users.sourceforge.net>               *   *                              <cuse@users.sourceforge.net>               *
7   *                                                                         *   *                                                                         *
8   *   This library is free software; you can redistribute it and/or modify  *   *   This library is free software; you can redistribute it and/or modify  *
# Line 22  Line 22 
22   ***************************************************************************/   ***************************************************************************/
23    
24  #include <algorithm>  #include <algorithm>
25    #include <set>
26  #include <string.h>  #include <string.h>
27    
28  #include "RIFF.h"  #include "RIFF.h"
# Line 280  namespace RIFF { Line 281  namespace RIFF {
281         #if DEBUG         #if DEBUG
282         std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;         std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
283         #endif // DEBUG         #endif // DEBUG
284          if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)          //if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
285          if (ulPos >= CurrentChunkSize) return 0;          if (ulPos >= CurrentChunkSize) return 0;
286          if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;          if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
287          #if POSIX          #if POSIX
# Line 594  namespace RIFF { Line 595  namespace RIFF {
595      }      }
596    
597      /**      /**
598         * Reads a null-padded string of size characters and copies it
599         * into the string \a s. The position within the chunk will
600         * automatically be incremented.
601         *
602         * @param s                 destination string
603         * @param size              number of characters to read
604         * @throws RIFF::Exception  if an error occured or less than
605         *                          \a size characters could be read!
606         */
607        void Chunk::ReadString(String& s, int size) {
608            char* buf = new char[size];
609            ReadSceptical(buf, 1, size);
610            s.assign(buf, std::find(buf, buf + size, '\0'));
611            delete[] buf;
612        }
613    
614        /**
615       * Writes \a WordCount number of 32 Bit unsigned integer words from the       * Writes \a WordCount number of 32 Bit unsigned integer words from the
616       * buffer pointed by \a pData to the chunk's body, directly to the       * buffer pointed by \a pData to the chunk's body, directly to the
617       * actual "physical" file. The position within the chunk will       * actual "physical" file. The position within the chunk will
# Line 733  namespace RIFF { Line 751  namespace RIFF {
751       * @see ReleaseChunkData()       * @see ReleaseChunkData()
752       */       */
753      void* Chunk::LoadChunkData() {      void* Chunk::LoadChunkData() {
754          if (!pChunkData && pFile->Filename != "" && ulStartPos != 0) {          if (!pChunkData && pFile->Filename != "" /*&& ulStartPos != 0*/) {
755              #if POSIX              #if POSIX
756              if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;              if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
757              #elif defined(WIN32)              #elif defined(WIN32)
# Line 860  namespace RIFF { Line 878  namespace RIFF {
878              #else              #else
879              int iBytesMoved = 1;              int iBytesMoved = 1;
880              #endif              #endif
881              for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {              for (unsigned long ulOffset = 0; ulToMove > 0 && iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
882                  iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;                  iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
883                  #if POSIX                  #if POSIX
884                  lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);                  lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
# Line 1173  namespace RIFF { Line 1191  namespace RIFF {
1191          return pNewChunk;          return pNewChunk;
1192      }      }
1193    
1194      /** @brief Moves a sub chunk.      /** @brief Moves a sub chunk witin this list.
1195       *       *
1196       * Moves a sub chunk from one position in a list to another       * Moves a sub chunk from one position in this list to another
1197       * position in the same list. The pSrc chunk is placed before the       * position in the same list. The pSrc chunk is placed before the
1198       * pDst chunk.       * pDst chunk.
1199       *       *
# Line 1191  namespace RIFF { Line 1209  namespace RIFF {
1209          pSubChunks->insert(iter, pSrc);          pSubChunks->insert(iter, pSrc);
1210      }      }
1211    
1212        /** @brief Moves a sub chunk from this list to another list.
1213         *
1214         * Moves a sub chunk from this list list to the end of another
1215         * list.
1216         *
1217         * @param pSrc - sub chunk to be moved
1218         * @param pDst - destination list where the chunk shall be moved to
1219         */
1220        void List::MoveSubChunk(Chunk* pSrc, List* pNewParent) {
1221            if (pNewParent == this || !pNewParent) return;
1222            if (!pSubChunks) LoadSubChunks();
1223            if (!pNewParent->pSubChunks) pNewParent->LoadSubChunks();
1224            pSubChunks->remove(pSrc);
1225            pNewParent->pSubChunks->push_back(pSrc);
1226            // update chunk id map of this List
1227            if ((*pSubChunksMap)[pSrc->GetChunkID()] == pSrc) {
1228                pSubChunksMap->erase(pSrc->GetChunkID());
1229                // try to find another chunk of the same chunk ID
1230                ChunkList::iterator iter = pSubChunks->begin();
1231                ChunkList::iterator end  = pSubChunks->end();
1232                for (; iter != end; ++iter) {
1233                    if ((*iter)->GetChunkID() == pSrc->GetChunkID()) {
1234                        (*pSubChunksMap)[pSrc->GetChunkID()] = *iter;
1235                        break; // we're done, stop search
1236                    }
1237                }
1238            }
1239            // update chunk id map of other list
1240            if (!(*pNewParent->pSubChunksMap)[pSrc->GetChunkID()])
1241                (*pNewParent->pSubChunksMap)[pSrc->GetChunkID()] = pSrc;
1242        }
1243    
1244      /** @brief Creates a new list sub chunk.      /** @brief Creates a new list sub chunk.
1245       *       *
1246       * Creates and adds a new list sub chunk to this list chunk. Note that       * Creates and adds a new list sub chunk to this list chunk. Note that
# Line 1383  namespace RIFF { Line 1433  namespace RIFF {
1433  // *************** File ***************  // *************** File ***************
1434  // *  // *
1435    
1436    //HACK: to avoid breaking DLL compatibility to older versions of libgig we roll the new std::set<Chunk*> into the old std::list<Chunk*> container, should be replaced on member variable level soon though
1437    #define _GET_RESIZED_CHUNKS() \
1438            (reinterpret_cast<std::set<Chunk*>*>(ResizedChunks.front()))
1439    
1440      /** @brief Create new RIFF file.      /** @brief Create new RIFF file.
1441       *       *
1442       * Use this constructor if you want to create a new RIFF file completely       * Use this constructor if you want to create a new RIFF file completely
# Line 1397  namespace RIFF { Line 1451  namespace RIFF {
1451       * @param FileType - four-byte identifier of the RIFF file type       * @param FileType - four-byte identifier of the RIFF file type
1452       * @see AddSubChunk(), AddSubList(), SetByteOrder()       * @see AddSubChunk(), AddSubList(), SetByteOrder()
1453       */       */
1454      File::File(uint32_t FileType) : List(this) {      File::File(uint32_t FileType)
1455            : List(this), bIsNewFile(true), Layout(layout_standard)
1456        {
1457            //HACK: see _GET_RESIZED_CHUNKS() comment
1458            ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1459          #if defined(WIN32)          #if defined(WIN32)
1460          hFileRead = hFileWrite = INVALID_HANDLE_VALUE;          hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1461          #else          #else
# Line 1417  namespace RIFF { Line 1475  namespace RIFF {
1475       * @throws RIFF::Exception if error occured while trying to load the       * @throws RIFF::Exception if error occured while trying to load the
1476       *                         given RIFF file       *                         given RIFF file
1477       */       */
1478      File::File(const String& path) : List(this), Filename(path) {      File::File(const String& path)
1479        #if DEBUG          : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard)
1480        std::cout << "File::File("<<path<<")" << std::endl;      {
1481        #endif // DEBUG         #if DEBUG
1482           std::cout << "File::File("<<path<<")" << std::endl;
1483           #endif // DEBUG
1484          bEndianNative = true;          bEndianNative = true;
1485            try {
1486                __openExistingFile(path);
1487                if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1488                    throw RIFF::Exception("Not a RIFF file");
1489                }
1490            }
1491            catch (...) {
1492                Cleanup();
1493                throw;
1494            }
1495        }
1496    
1497        /** @brief Load existing RIFF-like file.
1498         *
1499         * Loads an existing file, which is not a "real" RIFF file, but similar to
1500         * an ordinary RIFF file.
1501         *
1502         * A "real" RIFF file contains at top level a List chunk either with chunk
1503         * ID "RIFF" or "RIFX". The simple constructor above expects this to be
1504         * case, and if it finds the toplevel List chunk to have another chunk ID
1505         * than one of those two expected ones, it would throw an Exception and
1506         * would refuse to load the file accordingly.
1507         *
1508         * Since there are however a lot of file formats which use the same simple
1509         * principles of the RIFF format, with another toplevel List chunk ID
1510         * though, you can use this alternative constructor here to be able to load
1511         * and handle those files in the same way as you would do with "real" RIFF
1512         * files.
1513         *
1514         * @param path - path and file name of the RIFF-alike file to be opened
1515         * @param FileType - expected toplevel List chunk ID (this is the very
1516         *                   first chunk found in the file)
1517         * @param Endian - whether the file uses little endian or big endian layout
1518         * @param layout - general file structure type
1519         * @throws RIFF::Exception if error occured while trying to load the
1520         *                         given RIFF-alike file
1521         */
1522        File::File(const String& path, uint32_t FileType, endian_t Endian, layout_t layout)
1523            : List(this), Filename(path), bIsNewFile(false), Layout(layout)
1524        {
1525            SetByteOrder(Endian);
1526            try {
1527                __openExistingFile(path, &FileType);
1528            }
1529            catch (...) {
1530                Cleanup();
1531                throw;
1532            }
1533        }
1534    
1535        /**
1536         * Opens an already existing RIFF file or RIFF-alike file. This method
1537         * shall only be called once (in a File class constructor).
1538         *
1539         * @param path - path and file name of the RIFF file or RIFF-alike file to
1540         *               be opened
1541         * @param FileType - (optional) expected chunk ID of first chunk in file
1542         * @throws RIFF::Exception if error occured while trying to load the
1543         *                         given RIFF file or RIFF-alike file
1544         */
1545        void File::__openExistingFile(const String& path, uint32_t* FileType) {
1546            //HACK: see _GET_RESIZED_CHUNKS() comment
1547            ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1548          #if POSIX          #if POSIX
1549          hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);          hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1550          if (hFileRead <= 0) {          if (hFileRead <= 0) {
# Line 1445  namespace RIFF { Line 1568  namespace RIFF {
1568          if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");          if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1569          #endif // POSIX          #endif // POSIX
1570          Mode = stream_mode_read;          Mode = stream_mode_read;
1571          ulStartPos = RIFF_HEADER_SIZE;          switch (Layout) {
1572          ReadHeader(0);              case layout_standard: // this is a normal RIFF file
1573          if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {                  ulStartPos = RIFF_HEADER_SIZE;
1574              throw RIFF::Exception("Not a RIFF file");                  ReadHeader(0);
1575                    if (FileType && ChunkID != *FileType)
1576                        throw RIFF::Exception("Invalid file container ID");
1577                    break;
1578                case layout_flat: // non-standard RIFF-alike file
1579                    ulStartPos = 0;
1580                    NewChunkSize = CurrentChunkSize = GetFileSize();
1581                    if (FileType) {
1582                        uint32_t ckid;
1583                        if (Read(&ckid, 4, 1) != 4) {
1584                            throw RIFF::Exception("Invalid file header ID (premature end of header)");
1585                        } else if (ckid != *FileType) {
1586                            String s = " (expected '" + convertToString(*FileType) + "' but got '" + convertToString(ckid) + "')";
1587                            throw RIFF::Exception("Invalid file header ID" + s);
1588                        }
1589                        SetPos(0); // reset to first byte of file
1590                    }
1591                    LoadSubChunks();
1592                    break;
1593          }          }
1594      }      }
1595    
1596      String File::GetFileName() {      String File::GetFileName() {
1597          return Filename;          return Filename;
1598      }      }
1599        
1600        void File::SetFileName(const String& path) {
1601            Filename = path;
1602        }
1603    
1604      stream_mode_t File::GetMode() {      stream_mode_t File::GetMode() {
1605          return Mode;          return Mode;
1606      }      }
1607    
1608        layout_t File::GetLayout() const {
1609            return Layout;
1610        }
1611    
1612      /** @brief Change file access mode.      /** @brief Change file access mode.
1613       *       *
1614       * Changes files access mode either to read-only mode or to read/write       * Changes files access mode either to read-only mode or to read/write
# Line 1592  namespace RIFF { Line 1741  namespace RIFF {
1741       *                         chunk or any kind of IO error occured       *                         chunk or any kind of IO error occured
1742       */       */
1743      void File::Save() {      void File::Save() {
1744            //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1745            if (Layout == layout_flat)
1746                throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1747    
1748          // make sure the RIFF tree is built (from the original file)          // make sure the RIFF tree is built (from the original file)
1749          LoadSubChunksRecursively();          LoadSubChunksRecursively();
1750    
# Line 1607  namespace RIFF { Line 1760  namespace RIFF {
1760    
1761          // first we sum up all positive chunk size changes (and skip all negative ones)          // first we sum up all positive chunk size changes (and skip all negative ones)
1762          unsigned long ulPositiveSizeDiff = 0;          unsigned long ulPositiveSizeDiff = 0;
1763          for (std::set<Chunk*>::const_iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {          std::set<Chunk*>* resizedChunks = _GET_RESIZED_CHUNKS();
1764            for (std::set<Chunk*>::const_iterator iter = resizedChunks->begin(), end = resizedChunks->end(); iter != end; ++iter) {
1765              if ((*iter)->GetNewSize() == 0) {              if ((*iter)->GetNewSize() == 0) {
1766                  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));                  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
1767              }              }
# Line 1663  namespace RIFF { Line 1817  namespace RIFF {
1817          if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);          if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1818    
1819          // forget all resized chunks          // forget all resized chunks
1820          ResizedChunks.clear();          resizedChunks->clear();
1821      }      }
1822    
1823      /** @brief Save changes to another file.      /** @brief Save changes to another file.
# Line 1682  namespace RIFF { Line 1836  namespace RIFF {
1836      void File::Save(const String& path) {      void File::Save(const String& path) {
1837          //TODO: we should make a check here if somebody tries to write to the same file and automatically call the other Save() method in that case          //TODO: we should make a check here if somebody tries to write to the same file and automatically call the other Save() method in that case
1838    
1839            //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1840            if (Layout == layout_flat)
1841                throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1842    
1843          // make sure the RIFF tree is built (from the original file)          // make sure the RIFF tree is built (from the original file)
1844          LoadSubChunksRecursively();          LoadSubChunksRecursively();
1845    
1846          if (Filename.length() > 0) SetMode(stream_mode_read);          if (!bIsNewFile) SetMode(stream_mode_read);
1847          // open the other (new) file for writing and truncate it to zero size          // open the other (new) file for writing and truncate it to zero size
1848          #if POSIX          #if POSIX
1849          hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);          hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
# Line 1720  namespace RIFF { Line 1878  namespace RIFF {
1878          if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);          if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1879    
1880          // forget all resized chunks          // forget all resized chunks
1881          ResizedChunks.clear();          _GET_RESIZED_CHUNKS()->clear();
1882    
1883          #if POSIX          #if POSIX
1884          if (hFileWrite) close(hFileWrite);          if (hFileWrite) close(hFileWrite);
# Line 1733  namespace RIFF { Line 1891  namespace RIFF {
1891    
1892          // associate new file with this File object from now on          // associate new file with this File object from now on
1893          Filename = path;          Filename = path;
1894            bIsNewFile = false;
1895          Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...          Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...
1896          SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.          SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1897      }      }
# Line 1756  namespace RIFF { Line 1915  namespace RIFF {
1915         #if DEBUG         #if DEBUG
1916         std::cout << "File::~File()" << std::endl;         std::cout << "File::~File()" << std::endl;
1917         #endif // DEBUG         #endif // DEBUG
1918            Cleanup();
1919        }
1920        
1921        /**
1922         * Returns @c true if this file has been created new from scratch and
1923         * has not been stored to disk yet.
1924         */
1925        bool File::IsNew() const {
1926            return bIsNewFile;
1927        }
1928    
1929        void File::Cleanup() {
1930          #if POSIX          #if POSIX
1931          if (hFileRead) close(hFileRead);          if (hFileRead) close(hFileRead);
1932          #elif defined(WIN32)          #elif defined(WIN32)
# Line 1765  namespace RIFF { Line 1936  namespace RIFF {
1936          #endif // POSIX          #endif // POSIX
1937          DeleteChunkList();          DeleteChunkList();
1938          pFile = NULL;          pFile = NULL;
1939            //HACK: see _GET_RESIZED_CHUNKS() comment
1940            delete _GET_RESIZED_CHUNKS();
1941      }      }
1942    
1943      void File::LogAsResized(Chunk* pResizedChunk) {      void File::LogAsResized(Chunk* pResizedChunk) {
1944          ResizedChunks.insert(pResizedChunk);          _GET_RESIZED_CHUNKS()->insert(pResizedChunk);
1945      }      }
1946    
1947      void File::UnlogResized(Chunk* pResizedChunk) {      void File::UnlogResized(Chunk* pResizedChunk) {
1948          ResizedChunks.erase(pResizedChunk);          _GET_RESIZED_CHUNKS()->erase(pResizedChunk);
1949      }      }
1950    
1951      unsigned long File::GetFileSize() {      unsigned long File::GetFileSize() {

Legend:
Removed from v.1885  
changed lines
  Added in v.2584

  ViewVC Help
Powered by ViewVC