/[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 2450 by persson, Wed May 8 17:53:07 2013 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-2013 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 281  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 751  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 1191  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 1209  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 1419  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          //HACK: see _GET_RESIZED_CHUNKS() comment
1458          ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));          ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1459          #if defined(WIN32)          #if defined(WIN32)
# Line 1441  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            : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard)
1480        {
1481         #if DEBUG         #if DEBUG
1482         std::cout << "File::File("<<path<<")" << std::endl;         std::cout << "File::File("<<path<<")" << std::endl;
1483         #endif // DEBUG         #endif // DEBUG
1484            bEndianNative = true;
1485          try {          try {
1486              bEndianNative = true;              __openExistingFile(path);
             //HACK: see _GET_RESIZED_CHUNKS() comment  
             ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));  
             #if POSIX  
             hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);  
             if (hFileRead <= 0) {  
                 hFileRead = hFileWrite = 0;  
                 throw RIFF::Exception("Can't open \"" + path + "\"");  
             }  
             #elif defined(WIN32)  
             hFileRead = hFileWrite = CreateFile(  
                                          path.c_str(), GENERIC_READ,  
                                          FILE_SHARE_READ | FILE_SHARE_WRITE,  
                                          NULL, OPEN_EXISTING,  
                                          FILE_ATTRIBUTE_NORMAL |  
                                          FILE_FLAG_RANDOM_ACCESS, NULL  
                                      );  
             if (hFileRead == INVALID_HANDLE_VALUE) {  
                 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;  
                 throw RIFF::Exception("Can't open \"" + path + "\"");  
             }  
             #else  
             hFileRead = hFileWrite = fopen(path.c_str(), "rb");  
             if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");  
             #endif // POSIX  
             Mode = stream_mode_read;  
             ulStartPos = RIFF_HEADER_SIZE;  
             ReadHeader(0);  
1487              if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {              if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1488                  throw RIFF::Exception("Not a RIFF file");                  throw RIFF::Exception("Not a RIFF file");
1489              }              }
# Line 1484  namespace RIFF { Line 1494  namespace RIFF {
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
1549            hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1550            if (hFileRead <= 0) {
1551                hFileRead = hFileWrite = 0;
1552                throw RIFF::Exception("Can't open \"" + path + "\"");
1553            }
1554            #elif defined(WIN32)
1555            hFileRead = hFileWrite = CreateFile(
1556                                         path.c_str(), GENERIC_READ,
1557                                         FILE_SHARE_READ | FILE_SHARE_WRITE,
1558                                         NULL, OPEN_EXISTING,
1559                                         FILE_ATTRIBUTE_NORMAL |
1560                                         FILE_FLAG_RANDOM_ACCESS, NULL
1561                                     );
1562            if (hFileRead == INVALID_HANDLE_VALUE) {
1563                hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1564                throw RIFF::Exception("Can't open \"" + path + "\"");
1565            }
1566            #else
1567            hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1568            if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1569            #endif // POSIX
1570            Mode = stream_mode_read;
1571            switch (Layout) {
1572                case layout_standard: // this is a normal RIFF file
1573                    ulStartPos = RIFF_HEADER_SIZE;
1574                    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 1624  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 1715  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 1766  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 1791  namespace RIFF { Line 1917  namespace RIFF {
1917         #endif // DEBUG         #endif // DEBUG
1918          Cleanup();          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() {      void File::Cleanup() {
1930          #if POSIX          #if POSIX

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

  ViewVC Help
Powered by ViewVC