/** * Furnace Tracker - multi-system chiptune tracker * Copyright (C) 2021-2024 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "fileOpsCommon.h" bool DivEngine::loadIT(unsigned char* file, size_t len) { struct InvalidHeaderException {}; bool success=false; char magic[4]; unsigned char chanPan[64]; unsigned char chanVol[64]; unsigned char orders[256]; unsigned int insPtr[256]; unsigned int samplePtr[256]; unsigned int patPtr[256]; SafeReader reader=SafeReader(file,len); warnings=""; try { DivSong ds; ds.version=DIV_VERSION_XM; //ds.linearPitch=0; //ds.pitchMacroIsLinear=false; ds.noSlidesOnFirstTick=true; ds.rowResetsArpPos=true; ds.ignoreJumpAtEnd=false; ds.pitchSlideSpeed=4; ds.system[0]=DIV_SYSTEM_ES5506; ds.systemLen=1; logV("Impulse Tracker module"); // load here if (!reader.seek(0,SEEK_SET)) { logE("premature end of file!"); lastError="incomplete file"; delete[] file; return false; } reader.read(magic,4); if (memcmp(magic,DIV_IT_MAGIC,4)!=0) { logW("the magic isn't complete"); throw EndOfFileException(&reader,reader.tell()); } ds.name=reader.readString(26); unsigned char hilight1=reader.readC(); unsigned char hilight2=reader.readC(); logV("highlights: %d %d",hilight1,hilight2); unsigned short ordersLen=reader.readS(); ds.insLen=reader.readS(); ds.sampleLen=reader.readS(); unsigned short patCount=reader.readS(); unsigned short usedTracker=reader.readS(); unsigned short compatTracker=reader.readS(); unsigned short flags=reader.readS(); unsigned short special=reader.readS(); unsigned char globalVol=reader.readC(); unsigned char masterVol=reader.readC(); unsigned char initSpeed=reader.readC(); unsigned char initTempo=reader.readC(); unsigned char panSep=reader.readC(); reader.readC(); // PWD - unused logV("orders len: %d",ordersLen); logV("pattern count: %d",patCount); logV("used tracker: %x",usedTracker); logV("compatible with: %x",compatTracker); logV("flags: %x",flags); logV("special: %x",special); logV("global vol: %d",globalVol); logV("master vol: %d",masterVol); logV("speed: %d",initSpeed); logV("tempo: %d",initTempo); logV("pan sep: %d",panSep); unsigned short commentLen=reader.readS(); unsigned int commentPtr=reader.readI(); logV("comment len: %d",commentLen); logV("comment ptr: %x",commentPtr); reader.readI(); // reserved reader.read(chanPan,64); reader.read(chanVol,64); reader.read(orders,ordersLen); for (int i=0; iorders.ord[j][i]=orders[i]; } } ds.subsong[0]->ordersLen=ordersLen; for (int i=0; iname=reader.readString(26); if (compatTracker<0x200) { // old format // x reader.readC(); reader.readC(); reader.readC(); reader.readC(); reader.readC(); reader.readC(); } else { // new format // filter params initCut=reader.readC(); initRes=reader.readC(); // MIDI stuff - ignored reader.readI(); } logV("filter: %d/%d",initCut,initRes); // note map ins->amiga.useNoteMap=true; for (int j=0; j<120; j++) { ins->amiga.noteMap[j].freq=(unsigned char)reader.readC(); ins->amiga.noteMap[j].map=reader.readC()-1; } // TODO: envelopes... ds.ins.push_back(ins); } // read samples for (int i=0; iname=reader.readString(26); unsigned char convert=reader.readC(); unsigned char defPan=reader.readC(); logV("defPan: %d",defPan); if (flags&2) { s->depth=DIV_SAMPLE_DEPTH_16BIT; } else { s->depth=DIV_SAMPLE_DEPTH_8BIT; } if (flags&8) { logE("sample decompression not implemented!"); lastError="sample decompression not implemented"; delete s; delete[] file; return false; } s->init((unsigned int)reader.readI()); s->loopStart=reader.readI(); s->loopEnd=reader.readI(); s->centerRate=reader.readI()/2; if (flags&16) { s->loop=true; if (flags&64) { s->loopMode=DIV_SAMPLE_LOOP_PINGPONG; } else { s->loopMode=DIV_SAMPLE_LOOP_FORWARD; } } if (flags&32) { s->loop=true; if (flags&128) { s->loopMode=DIV_SAMPLE_LOOP_PINGPONG; } else { s->loopMode=DIV_SAMPLE_LOOP_FORWARD; } s->loopStart=reader.readI(); s->loopEnd=reader.readI(); } else { reader.readI(); reader.readI(); } unsigned int dataPtr=reader.readI(); unsigned char vibSpeed=reader.readC(); unsigned char vibDepth=reader.readC(); unsigned char vibRate=reader.readC(); unsigned char vibShape=reader.readC(); logV("vibrato: %d %d %d %d",vibSpeed,vibDepth,vibRate,vibShape); if (s->samples>0) { logD("seek to %x...",dataPtr); if (!reader.seek(dataPtr,SEEK_SET)) { logE("premature end of file!"); lastError="incomplete file"; delete s; delete[] file; return false; } } else { logD("seek not needed..."); } if (s->depth==DIV_SAMPLE_DEPTH_16BIT) { if (flags&4) { // downmix stereo for (unsigned int i=0; isamples; i++) { short l; short r; if (convert&2) { l=reader.readS_BE(); r=reader.readS_BE(); } else { l=reader.readS(); r=reader.readS(); } if (!(convert&1)) { l^=0x8000; r^=0x8000; } s->data16[i]=(l+r)>>1; } } else { for (unsigned int i=0; isamples; i++) { if (convert&2) { s->data16[i]=reader.readS_BE()^((convert&1)?0:0x8000); } else { s->data16[i]=reader.readS()^((convert&1)?0:0x8000); } } } } else { if (flags&4) { // downmix stereo for (unsigned int i=0; isamples; i++) { signed char l=reader.readC(); signed char r=reader.readC(); if (!(convert&1)) { l^=0x80; r^=0x80; } s->data8[i]=(l+r)>>1; } } else { for (unsigned int i=0; isamples; i++) { s->data8[i]=reader.readC()^((convert&1)?0:0x80); } } } ds.sample.push_back(s); } // read patterns for (int i=0; i=patRows) break; continue; } if (chan&128) { mask[chan&63]=reader.readC(); } chan&=63; if (mask[chan]&1) { note[chan]=reader.readC(); hasNote=true; } if (mask[chan]&2) { ins[chan]=reader.readC(); hasIns=true; } if (mask[chan]&4) { vol[chan]=reader.readC(); hasVol=true; } if (mask[chan]&8) { effect[chan]=reader.readC(); effectVal[chan]=reader.readC(); hasEffect=true; } if (mask[chan]&16) { hasNote=true; } if (mask[chan]&32) { hasIns=true; } if (mask[chan]&64) { hasVol=true; } if (mask[chan]&128) { hasEffect=true; } DivPattern* p=ds.subsong[0]->pat[chan].getPattern(i,true); if (hasNote) { if (note[chan]==255) { // note off p->data[curRow][0]=100; p->data[curRow][1]=0; } else if (note[chan]==254) { // note release p->data[curRow][0]=101; p->data[curRow][1]=0; } else if (note[chan]<120) { p->data[curRow][0]=note[chan]%12; p->data[curRow][1]=note[chan]/12; if (p->data[curRow][0]==0) { p->data[curRow][0]=12; p->data[curRow][1]--; } } } if (hasIns) { p->data[curRow][2]=ins[chan]-1; } if (hasVol) { p->data[curRow][3]=vol[chan]; } if (hasEffect) { //p->data[curRow][4]=effect[chan]; //p->data[curRow][5]=effectVal[chan]; } } } logV("DAMN IT: %x",insPtr[0]); logV("DAMN IT: %x",samplePtr[0]); if (active) quitDispatch(); BUSY_BEGIN_SOFT; saveLock.lock(); song.unload(); song=ds; changeSong(0); recalcChans(); saveLock.unlock(); BUSY_END; if (active) { initDispatch(); BUSY_BEGIN; renderSamples(); reset(); BUSY_END; } success=true; } catch (EndOfFileException& e) { //logE("premature end of file!"); lastError="incomplete file"; } catch (InvalidHeaderException& e) { //logE("invalid header!"); lastError="invalid header!"; } return success; }