giga-refactor, part 12

loading works
This commit is contained in:
tildearrow 2025-11-16 07:13:30 -05:00
parent 94044ef210
commit 334d8708e2
3 changed files with 250 additions and 85 deletions

View file

@ -883,11 +883,19 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
ds.chans=(unsigned short)reader.readS();
ds.systemLen=(unsigned short)reader.readS();
if (ds.systemLen<1) {
logE("zero chips!");
lastError="zero chips!";
delete[] file;
return false;
}
// TODO: remove after implementing dynamic stuff
for (int i=0; i<DIV_MAX_CHIPS; i++) {
ds.system[i]=DIV_SYSTEM_NULL;
}
logD("chips: (%d, %d channels)",ds.systemLen,ds.chans);
for (int i=0; i<ds.systemLen; i++) {
unsigned short sysID=reader.readS();
if (sysID>0xff || sysID==0) {
@ -923,6 +931,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
ds.systemChans[i]=(unsigned short)reader.readS();
tchans+=ds.systemChans[i];
logD("- %d: %.2x (%s, %d channels)",i,sysID,getSystemName(ds.system[i]),ds.systemChans[i]);
if (ds.systemChans[i]<1) {
logE("invalid channel count for chip");
lastError=fmt::sprintf("invalid channel count for chip!");
@ -952,13 +962,28 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
// elements
bool hasElement=true;
bool seenElement[DIV_ELEMENT_MAX];
memset(seenElement,0,DIV_ELEMENT_MAX*sizeof(bool));
logD("elements present in this song:");
while (hasElement) {
DivFileElementType elementType=(DivFileElementType)reader.readC();
if (elementType<DIV_ELEMENT_MAX) {
if (seenElement[elementType]) {
logE("duplicate element type!");
lastError="duplicate element type!";
delete[] file;
return false;
} else {
seenElement[elementType]=true;
}
}
switch (elementType) {
case DIV_ELEMENT_SUBSONG:
logD("- sub-songs");
READ_ELEMENT_PTRS(subSongPtr);
break;
case DIV_ELEMENT_CHIP_FLAGS:
logD("- chip flags");
READ_ELEMENT_PTRS(sysFlagsPtr);
if (sysFlagsPtr.size()!=ds.systemLen) {
logE("more chip flag pointers than there should be");
@ -968,9 +993,11 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
}
break;
case DIV_ELEMENT_ASSET_DIR:
logD("- asset dirs");
READ_ELEMENT_PTRS(assetDirPtr);
break;
case DIV_ELEMENT_INSTRUMENT:
logD("- instruments");
READ_ELEMENT_PTRS(insPtr);
if (insPtr.size()>256) {
logE("invalid instrument count!");
@ -981,6 +1008,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
ds.insLen=insPtr.size();
break;
case DIV_ELEMENT_WAVETABLE:
logD("- wavetables");
READ_ELEMENT_PTRS(wavePtr);
if (wavePtr.size()>32768) {
logE("invalid wavetable count!");
@ -991,6 +1019,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
ds.waveLen=wavePtr.size();
break;
case DIV_ELEMENT_SAMPLE:
logD("- samples");
READ_ELEMENT_PTRS(samplePtr);
if (samplePtr.size()>32768) {
logE("invalid sample count!");
@ -1001,22 +1030,42 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
ds.sampleLen=samplePtr.size();
break;
case DIV_ELEMENT_PATTERN:
logD("- patterns");
READ_ELEMENT_PTRS(patPtr);
break;
case DIV_ELEMENT_COMPAT_FLAGS:
logD("- compat flags");
READ_ELEMENT_UNIQUE(compatFlagPtr);
break;
case DIV_ELEMENT_COMMENTS:
logD("- song comments");
READ_ELEMENT_UNIQUE(commentPtr);
break;
case DIV_ELEMENT_GROOVE:
logD("- grooves");
READ_ELEMENT_PTRS(groovePtr);
break;
case DIV_ELEMENT_END:
hasElement=false;
break;
default: {
logD("- UNKNOWN");
// skip element
unsigned int numElements=reader.readI();
for (unsigned int i=0; i<numElements; i++) {
reader.readI();
}
break;
}
}
}
if (subSongPtr.empty()) {
logE("song is empty!");
lastError="song is empty!";
delete[] file;
return false;
}
} else {
// read header (OLD)
reader.read(magic,4);
@ -1030,8 +1079,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
subSong->timeBase=reader.readC();
subSong->speeds.len=2;
subSong->speeds.val[0]=reader.readC();
subSong->speeds.val[1]=reader.readC();
subSong->speeds.val[0]=(unsigned char)reader.readC();
subSong->speeds.val[1]=(unsigned char)reader.readC();
subSong->arpLen=reader.readC();
subSong->hz=reader.readF();
@ -1048,7 +1097,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
if (subSong->patLen<0) {
logE("pattern length is negative!");
lastError="pattern lengrh is negative!";
lastError="pattern length is negative!";
delete[] file;
return false;
}
@ -1182,7 +1231,6 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
}
}
// TODO: don't call this
ds.initDefaultSystemChans();
ds.name=reader.readString();
@ -1576,7 +1624,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
if (ds.version>=139) {
subSong->speeds.len=reader.readC();
for (int i=0; i<16; i++) {
subSong->speeds.val[i]=reader.readC();
subSong->speeds.val[i]=(unsigned char)reader.readC();
}
// grooves
@ -1584,9 +1632,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
ds.grooves.reserve(grooveCount);
for (int i=0; i<grooveCount; i++) {
DivGroovePattern gp;
gp.len=reader.readC();
gp.len=(unsigned char)reader.readC();
for (int j=0; j<16; j++) {
gp.val[j]=reader.readC();
gp.val[j]=(unsigned char)reader.readC();
}
ds.grooves.push_back(gp);
@ -1643,7 +1691,28 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
song.notes=reader.readString();
}
// TODO: read grooves
// read grooves
ds.grooves.reserve(groovePtr.size());
for (size_t i=0; i<groovePtr.size(); i++) {
DivGroovePattern groove;
if (!reader.seek(groovePtr[i],SEEK_SET)) {
logE("couldn't seek to groove %d!",i);
lastError=fmt::sprintf("couldn't seek to groove %d!",i);
ds.unload();
delete[] file;
return false;
}
if (!groove.readData(reader)) {
logE("%d: invalid groove data!",i);
lastError="invalid groove data!";
ds.unload();
delete[] file;
return false;
}
ds.grooves.push_back(groove);
}
// read system flags
if (ds.version>=119) {
@ -1736,7 +1805,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
if (ds.version>=95) {
ds.subsong.reserve(subSongPtr.size());
for (size_t i=0; i<subSongPtr.size(); i++) {
ds.subsong.push_back(new DivSubSong);
subSong=new DivSubSong;
ds.subsong.push_back(subSong);
if (!reader.seek(subSongPtr[i],SEEK_SET)) {
logE("couldn't seek to subsong %d!",i+1);
lastError=fmt::sprintf("couldn't seek to subsong %d!",i+1);
@ -1745,80 +1815,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
return false;
}
reader.read(magic,4);
if (strcmp(magic,"SONG")!=0) {
logE("%d: invalid subsong header!",i);
lastError="invalid subsong header!";
if (!subSong->readData(reader,ds.version,ds.chans)) {
logE("%d: invalid subsong data!",i);
lastError="invalid subsong data!";
ds.unload();
delete[] file;
return false;
}
reader.readI();
subSong=ds.subsong[i+1];
subSong->timeBase=reader.readC();
subSong->speeds.len=2;
subSong->speeds.val[0]=reader.readC();
subSong->speeds.val[1]=reader.readC();
subSong->arpLen=reader.readC();
subSong->hz=reader.readF();
subSong->patLen=reader.readS();
subSong->ordersLen=reader.readS();
subSong->hilightA=reader.readC();
subSong->hilightB=reader.readC();
if (ds.version>=96) {
subSong->virtualTempoN=reader.readS();
subSong->virtualTempoD=reader.readS();
} else {
reader.readI();
}
subSong->name=reader.readString();
subSong->notes=reader.readString();
logD("reading orders of subsong %d (%d)...",i+1,subSong->ordersLen);
for (int j=0; j<tchans; j++) {
for (int k=0; k<subSong->ordersLen; k++) {
subSong->orders.ord[j][k]=reader.readC();
}
}
for (int i=0; i<tchans; i++) {
subSong->pat[i].effectCols=reader.readC();
}
for (int i=0; i<tchans; i++) {
if (ds.version<189) {
subSong->chanShow[i]=reader.readC();
subSong->chanShowChanOsc[i]=subSong->chanShow[i];
} else {
unsigned char tempchar=reader.readC();
subSong->chanShow[i]=tempchar&1;
subSong->chanShowChanOsc[i]=tempchar&2;
}
}
for (int i=0; i<tchans; i++) {
subSong->chanCollapse[i]=reader.readC();
}
for (int i=0; i<tchans; i++) {
subSong->chanName[i]=reader.readString();
}
for (int i=0; i<tchans; i++) {
subSong->chanShortName[i]=reader.readString();
}
if (ds.version>=139) {
subSong->speeds.len=reader.readC();
for (int i=0; i<16; i++) {
subSong->speeds.val[i]=reader.readC();
}
}
}
}

View file

@ -451,6 +451,142 @@ void DivSubSong::calcTimestamps(int chans, std::vector<DivGroovePattern>& groove
logV("song length: %s; %" PRIu64 " ticks",ts.totalTime.toString(6,TA_TIME_FORMAT_AUTO),ts.totalTicks);
}
bool DivSubSong::readData(SafeReader& reader, int version, int chans) {
unsigned char magic[4];
reader.read(magic,4);
if (version>=240) {
if (memcmp(magic,"SNG2",4)!=0) {
logE("invalid subsong header!");
return false;
}
reader.readI();
hz=reader.readF();
arpLen=reader.readC();
timeBase=reader.readC();
patLen=reader.readS();
ordersLen=reader.readS();
hilightA=reader.readC();
hilightB=reader.readC();
virtualTempoN=reader.readS();
virtualTempoD=reader.readS();
speeds.len=reader.readC();
for (int i=0; i<16; i++) {
speeds.val[i]=reader.readS();
}
name=reader.readString();
notes=reader.readString();
logD("reading orders (%d)...",ordersLen);
for (int j=0; j<chans; j++) {
for (int k=0; k<ordersLen; k++) {
orders.ord[j][k]=reader.readC();
}
}
for (int i=0; i<chans; i++) {
pat[i].effectCols=reader.readC();
}
for (int i=0; i<chans; i++) {
unsigned char tempchar=reader.readC();
chanShow[i]=tempchar&1;
chanShowChanOsc[i]=tempchar&2;
}
for (int i=0; i<chans; i++) {
chanCollapse[i]=reader.readC();
}
for (int i=0; i<chans; i++) {
chanName[i]=reader.readString();
}
for (int i=0; i<chans; i++) {
chanShortName[i]=reader.readString();
}
} else {
if (memcmp(magic,"SONG",4)!=0) {
logE("invalid subsong header!");
return false;
}
reader.readI();
timeBase=reader.readC();
speeds.len=2;
speeds.val[0]=(unsigned char)reader.readC();
speeds.val[1]=(unsigned char)reader.readC();
arpLen=reader.readC();
hz=reader.readF();
patLen=reader.readS();
ordersLen=reader.readS();
hilightA=reader.readC();
hilightB=reader.readC();
if (version>=96) {
virtualTempoN=reader.readS();
virtualTempoD=reader.readS();
} else {
reader.readI();
}
name=reader.readString();
notes=reader.readString();
logD("reading orders (%d)...",ordersLen);
for (int j=0; j<chans; j++) {
for (int k=0; k<ordersLen; k++) {
orders.ord[j][k]=reader.readC();
}
}
for (int i=0; i<chans; i++) {
pat[i].effectCols=reader.readC();
}
for (int i=0; i<chans; i++) {
if (version<189) {
chanShow[i]=reader.readC();
chanShowChanOsc[i]=chanShow[i];
} else {
unsigned char tempchar=reader.readC();
chanShow[i]=tempchar&1;
chanShowChanOsc[i]=tempchar&2;
}
}
for (int i=0; i<chans; i++) {
chanCollapse[i]=reader.readC();
}
for (int i=0; i<chans; i++) {
chanName[i]=reader.readString();
}
for (int i=0; i<chans; i++) {
chanShortName[i]=reader.readString();
}
if (version>=139) {
speeds.len=reader.readC();
for (int i=0; i<16; i++) {
speeds.val[i]=(unsigned char)reader.readC();
}
}
}
return true;
}
void DivSubSong::putData(SafeWriter* w, int chans) {
size_t blockStartSeek, blockEndSeek;
w->write("SNG2",4);
@ -468,7 +604,7 @@ void DivSubSong::putData(SafeWriter* w, int chans) {
w->writeS(virtualTempoD);
// speeds
w->writeS(speeds.len);
w->writeC(speeds.len);
for (int i=0; i<16; i++) {
w->writeS(speeds.val[i]);
}
@ -873,13 +1009,29 @@ void DivSong::unload() {
subsong.clear();
}
bool DivGroovePattern::readData(SafeReader& reader) {
unsigned char magic[4];
reader.read(magic,4);
if (memcmp(magic,"GROV",4)!=0) {
logE("invalid groove header!");
return false;
}
reader.readI();
return true;
}
void DivGroovePattern::putData(SafeWriter* w) {
size_t blockStartSeek, blockEndSeek;
w->write("GROV",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeS(len);
w->writeC(len);
for (int i=0; i<16; i++) {
w->writeS(val[i]);
}

View file

@ -171,17 +171,22 @@ enum DivFileElementType: unsigned char {
DIV_ELEMENT_PATTERN,
DIV_ELEMENT_COMPAT_FLAGS,
DIV_ELEMENT_COMMENTS,
DIV_ELEMENT_GROOVE
DIV_ELEMENT_GROOVE,
DIV_ELEMENT_MAX
};
struct DivGroovePattern {
unsigned char val[16];
unsigned char len;
unsigned short val[16];
unsigned short len;
bool readData(SafeReader& reader);
void putData(SafeWriter* w);
DivGroovePattern():
len(1) {
memset(val,6,16);
for (int i=0; i<16; i++) {
val[i]=6;
}
}
};
struct DivSongTimestamps {
@ -244,6 +249,11 @@ struct DivSubSong {
*/
void calcTimestamps(int chans, std::vector<DivGroovePattern>& grooves, int jumpTreatment, int ignoreJumpAtEnd, int brokenSpeedSel, int delayBehavior, int firstPat=0);
/**
* read sub-song data.
*/
bool readData(SafeReader& reader, int version, int chans);
/**
* write sub-song block to a SafeWriter.
*/