diff --git a/papers/format.md b/papers/format.md index 1b2e30c8f..2c98ecb99 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 156: Furnace dev156 - 155: Furnace dev155 - 154: Furnace dev154 - 153: Furnace dev153 @@ -435,6 +436,10 @@ size | description ??? | groove entries. the format is: | - 1 byte: length of groove | - 16 bytes: groove pattern + --- | **pointers to asset directories** (>=156) + 4 | instrument directories + 4 | wavetable directories + 4 | sample directories ``` # patchbay @@ -526,6 +531,22 @@ clock=4000000 stereo=true ``` +# asset directories (>=156) + +also known as "folder" in the user interface. + +``` +size | description +-----|------------------------------------ + 4 | "ADIR" block ID + 4 | size of this block + 4 | number of directories + --- | **asset directory** (×numberOfDirs) + STR | name (if empty, this is the uncategorized directory) + 2 | number of assets + 1?? | assets in this directory +``` + # instrument (>=127) Furnace dev127 and higher use the new instrument format. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index edf153fba..5a7ec1db2 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1601,14 +1601,14 @@ void DivEngine::checkAssetDir(std::vector& dir, size_t entries) { } // erase duplicate entry - if (inAssetDir[j]) { + if (inAssetDir[i.entries[j]]) { i.entries.erase(i.entries.begin()+j); j--; continue; } // mark entry as present - inAssetDir[j]=true; + inAssetDir[i.entries[j]]=true; } } diff --git a/src/engine/engine.h b/src/engine/engine.h index 20cec077c..07fd53507 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -53,8 +53,8 @@ #define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock(); #define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false; -#define DIV_VERSION "dev155" -#define DIV_ENGINE_VERSION 155 +#define DIV_VERSION "dev156" +#define DIV_ENGINE_VERSION 156 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -523,6 +523,10 @@ class DivEngine { // check whether an asset directory is complete void checkAssetDir(std::vector& dir, size_t entries); + // read/write asset dir + void putAssetDirData(SafeWriter* w, std::vector& dir); + DivDataErrors readAssetDirData(SafeReader& reader, std::vector& dir); + // add every export method here friend class DivROMExport; friend class DivExportAmigaValidation; diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 6ea0c59b7..dacaa14cc 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "dataErrors.h" #include "engine.h" #include "../ta-log.h" #include "instrument.h" @@ -1655,6 +1656,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { unsigned int samplePtr[256]; unsigned int subSongPtr[256]; unsigned int sysFlagsPtr[DIV_MAX_CHIPS]; + unsigned int assetDirPtr[3]; std::vector patPtr; int numberOfSubSongs=0; char magic[5]; @@ -2332,6 +2334,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } + if (ds.version>=156) { + assetDirPtr[0]=reader.readI(); + assetDirPtr[1]=reader.readI(); + assetDirPtr[2]=reader.readI(); + } + // read system flags if (ds.version>=119) { logD("reading chip flags..."); @@ -2366,6 +2374,53 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } + // read asset directories + if (ds.version>=156) { + logD("reading asset directories..."); + + if (!reader.seek(assetDirPtr[0],SEEK_SET)) { + logE("couldn't seek to ins dir!"); + lastError=fmt::sprintf("couldn't read instrument directory"); + ds.unload(); + delete[] file; + return false; + } + if (readAssetDirData(reader,ds.insDir)!=DIV_DATA_SUCCESS) { + lastError="invalid instrument directory data!"; + ds.unload(); + delete[] file; + return false; + } + + if (!reader.seek(assetDirPtr[1],SEEK_SET)) { + logE("couldn't seek to wave dir!"); + lastError=fmt::sprintf("couldn't read wavetable directory"); + ds.unload(); + delete[] file; + return false; + } + if (readAssetDirData(reader,ds.waveDir)!=DIV_DATA_SUCCESS) { + lastError="invalid wavetable directory data!"; + ds.unload(); + delete[] file; + return false; + } + + if (!reader.seek(assetDirPtr[2],SEEK_SET)) { + logE("couldn't seek to sample dir!"); + lastError=fmt::sprintf("couldn't read sample directory"); + ds.unload(); + delete[] file; + return false; + } + if (readAssetDirData(reader,ds.sampleDir)!=DIV_DATA_SUCCESS) { + lastError="invalid sample directory data!"; + ds.unload(); + delete[] file; + return false; + } + } + // read subsongs if (ds.version>=95) { for (int i=0; i& dir) { + size_t blockStartSeek, blockEndSeek; + + w->write("ADIR",4); + blockStartSeek=w->tell(); + w->writeI(0); + + w->writeI(dir.size()); + + for (DivAssetDir& i: dir) { + w->writeString(i.name,false); + w->writeS(i.entries.size()); + for (int j: i.entries) { + w->writeC(j); + } + } + + blockEndSeek=w->tell(); + w->seek(blockStartSeek,SEEK_SET); + w->writeI(blockEndSeek-blockStartSeek-4); + w->seek(0,SEEK_END); +} + +DivDataErrors DivEngine::readAssetDirData(SafeReader& reader, std::vector& dir) { + char magic[4]; + reader.read(magic,4); + if (memcmp(magic,"ADIR",4)!=0) { + logV("header is invalid: %c%c%c%c",magic[0],magic[1],magic[2],magic[3]); + return DIV_DATA_INVALID_HEADER; + } + + logV("reading"); + + reader.readI(); // reserved + + unsigned int numDirs=reader.readI(); + + for (unsigned int i=0; i subSongPtr; @@ -4851,7 +4961,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { std::vector wavePtr; std::vector samplePtr; std::vector patPtr; - size_t ptrSeek, subSongPtrSeek, sysFlagsPtrSeek, blockStartSeek, blockEndSeek; + int assetDirPtr[3]; + size_t ptrSeek, subSongPtrSeek, sysFlagsPtrSeek, blockStartSeek, blockEndSeek, assetDirPtrSeek; size_t subSongIndex=0; DivSubSong* subSong=song.subsong[subSongIndex]; warnings=""; @@ -5150,6 +5261,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { } } + // asset dir pointers (we'll seek here later) + assetDirPtrSeek=w->tell(); + w->writeI(0); + w->writeI(0); + w->writeI(0); + blockEndSeek=w->tell(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); @@ -5237,6 +5354,14 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->seek(0,SEEK_END); } + /// ASSET DIRECTORIES + assetDirPtr[0]=w->tell(); + putAssetDirData(w,song.insDir); + assetDirPtr[1]=w->tell(); + putAssetDirData(w,song.waveDir); + assetDirPtr[2]=w->tell(); + putAssetDirData(w,song.sampleDir); + /// INSTRUMENT for (int i=0; iwriteI(sysFlagsPtr[i]); } + // asset dir pointers + w->seek(assetDirPtrSeek,SEEK_SET); + for (size_t i=0; i<3; i++) { + w->writeI(assetDirPtr[i]); + } + saveLock.unlock(); return w; }