123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667 |
- #include "stdafx.h"
- #include "Loaders.h"
- OPENMPT_NAMESPACE_BEGIN
- struct IMFChannel
- {
- char name[12];
- uint8 chorus;
- uint8 reverb;
- uint8 panning;
- uint8 status;
- };
- MPT_BINARY_STRUCT(IMFChannel, 16)
- struct IMFFileHeader
- {
- enum SongFlags
- {
- linearSlides = 0x01,
- };
- char title[32];
- uint16le ordNum;
- uint16le patNum;
- uint16le insNum;
- uint16le flags;
- uint8le unused1[8];
- uint8le tempo;
- uint8le bpm;
- uint8le master;
- uint8le amp;
- uint8le unused2[8];
- char im10[4];
- IMFChannel channels[32];
- };
- MPT_BINARY_STRUCT(IMFFileHeader, 576)
- struct IMFEnvelope
- {
- enum EnvFlags
- {
- envEnabled = 0x01,
- envSustain = 0x02,
- envLoop = 0x04,
- };
- uint8 points;
- uint8 sustain;
- uint8 loopStart;
- uint8 loopEnd;
- uint8 flags;
- uint8 unused[3];
- };
- MPT_BINARY_STRUCT(IMFEnvelope, 8)
- struct IMFEnvNode
- {
- uint16le tick;
- uint16le value;
- };
- MPT_BINARY_STRUCT(IMFEnvNode, 4)
- struct IMFInstrument
- {
- enum EnvTypes
- {
- volEnv = 0,
- panEnv = 1,
- filterEnv = 2,
- };
- char name[32];
- uint8le map[120];
- uint8le unused[8];
- IMFEnvNode nodes[3][16];
- IMFEnvelope env[3];
- uint16le fadeout;
- uint16le smpNum;
- char ii10[4];
- void ConvertEnvelope(InstrumentEnvelope &mptEnv, EnvTypes e) const
- {
- const uint8 shift = (e == volEnv) ? 0 : 2;
- const uint8 mirror = (e == filterEnv) ? 0xFF : 0x00;
- mptEnv.dwFlags.set(ENV_ENABLED, (env[e].flags & 1) != 0);
- mptEnv.dwFlags.set(ENV_SUSTAIN, (env[e].flags & 2) != 0);
- mptEnv.dwFlags.set(ENV_LOOP, (env[e].flags & 4) != 0);
- mptEnv.resize(Clamp(env[e].points, uint8(2), uint8(16)));
- mptEnv.nLoopStart = env[e].loopStart;
- mptEnv.nLoopEnd = env[e].loopEnd;
- mptEnv.nSustainStart = mptEnv.nSustainEnd = env[e].sustain;
- uint16 minTick = 0;
- for(uint32 n = 0; n < mptEnv.size(); n++)
- {
- mptEnv[n].tick = minTick = std::max(minTick, nodes[e][n].tick.get());
- minTick++;
- uint8 value = static_cast<uint8>(nodes[e][n].value ^ mirror) >> shift;
- mptEnv[n].value = std::min(value, uint8(ENVELOPE_MAX));
- }
- mptEnv.Convert(MOD_TYPE_XM, MOD_TYPE_IT);
- }
-
- void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX firstSample) const
- {
- mptIns.name = mpt::String::ReadBuf(mpt::String::nullTerminated, name);
- if(smpNum)
- {
- for(size_t note = 0; note < std::min(std::size(map), std::size(mptIns.Keyboard) - 12u); note++)
- {
- mptIns.Keyboard[note + 12] = firstSample + map[note];
- }
- }
- mptIns.nFadeOut = fadeout;
- mptIns.midiPWD = 1;
- ConvertEnvelope(mptIns.VolEnv, volEnv);
- ConvertEnvelope(mptIns.PanEnv, panEnv);
- ConvertEnvelope(mptIns.PitchEnv, filterEnv);
- if(mptIns.PitchEnv.dwFlags[ENV_ENABLED])
- mptIns.PitchEnv.dwFlags.set(ENV_FILTER);
-
- if(!mptIns.VolEnv.dwFlags[ENV_ENABLED] && !mptIns.nFadeOut)
- mptIns.nFadeOut = 32767;
- }
- };
- MPT_BINARY_STRUCT(IMFInstrument, 384)
- struct IMFSample
- {
- enum SampleFlags
- {
- smpLoop = 0x01,
- smpPingPongLoop = 0x02,
- smp16Bit = 0x04,
- smpPanning = 0x08,
- };
- char filename[13];
- uint8le unused1[3];
- uint32le length;
- uint32le loopStart;
- uint32le loopEnd;
- uint32le c5Speed;
- uint8le volume;
- uint8le panning;
- uint8le unused2[14];
- uint8le flags;
- uint8le unused3[5];
- uint16le ems;
- uint32le dram;
- char is10[4];
-
- void ConvertToMPT(ModSample &mptSmp) const
- {
- mptSmp.Initialize();
- mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename);
- mptSmp.nLength = length;
- mptSmp.nLoopStart = loopStart;
- mptSmp.nLoopEnd = loopEnd;
- mptSmp.nC5Speed = c5Speed;
- mptSmp.nVolume = volume * 4;
- mptSmp.nPan = panning;
- if(flags & smpLoop)
- mptSmp.uFlags.set(CHN_LOOP);
- if(flags & smpPingPongLoop)
- mptSmp.uFlags.set(CHN_PINGPONGLOOP);
- if(flags & smp16Bit)
- {
- mptSmp.uFlags.set(CHN_16BIT);
- mptSmp.nLength /= 2;
- mptSmp.nLoopStart /= 2;
- mptSmp.nLoopEnd /= 2;
- }
- if(flags & smpPanning)
- mptSmp.uFlags.set(CHN_PANNING);
- }
- };
- MPT_BINARY_STRUCT(IMFSample, 64)
- static constexpr EffectCommand imfEffects[] =
- {
- CMD_NONE,
- CMD_SPEED,
- CMD_TEMPO,
- CMD_TONEPORTAMENTO,
- CMD_TONEPORTAVOL,
- CMD_VIBRATO,
- CMD_VIBRATOVOL,
- CMD_FINEVIBRATO,
- CMD_TREMOLO,
- CMD_ARPEGGIO,
- CMD_PANNING8,
- CMD_PANNINGSLIDE,
- CMD_VOLUME,
- CMD_VOLUMESLIDE,
- CMD_VOLUMESLIDE,
- CMD_FINETUNE,
- CMD_NOTESLIDEUP,
- CMD_NOTESLIDEDOWN,
- CMD_PORTAMENTOUP,
- CMD_PORTAMENTODOWN,
- CMD_PORTAMENTOUP,
- CMD_PORTAMENTODOWN,
- CMD_MIDI,
- CMD_MIDI,
- CMD_OFFSET,
- CMD_NONE,
- CMD_KEYOFF,
- CMD_RETRIG,
- CMD_TREMOR,
- CMD_POSITIONJUMP,
- CMD_PATTERNBREAK,
- CMD_GLOBALVOLUME,
- CMD_GLOBALVOLSLIDE,
- CMD_S3MCMDEX,
-
-
-
-
-
-
-
-
-
-
- CMD_NONE,
- CMD_NONE,
- };
- static void ImportIMFEffect(ModCommand &m)
- {
- uint8 n;
-
- switch(m.command)
- {
- case 0xE:
-
- if(m.param == 0)
- ;
- else if(m.param == 0xF0)
- m.param = 0xEF;
- else if(m.param == 0x0F)
- m.param = 0xFE;
- else if(m.param & 0xF0)
- m.param |= 0x0F;
- else
- m.param |= 0xF0;
- break;
- case 0xF:
- m.param ^= 0x80;
- break;
- case 0x14:
- case 0x15:
-
- if(m.param >> 4)
- m.param = 0xF0 | (m.param >> 4);
- else
- m.param |= 0xE0;
- break;
- case 0x16:
- m.param = (0xFF - m.param) / 2u;
- break;
- case 0x17:
- m.param = 0x80 | (m.param & 0x0F);
- break;
- case 0x1F:
- m.param = mpt::saturate_cast<uint8>(m.param * 2);
- break;
- case 0x21:
- n = 0;
- switch (m.param >> 4)
- {
- case 0:
-
- break;
- default:
- case 0x1:
- case 0xF:
- m.command = CMD_NONE;
- break;
- case 0x3:
- n = 0x20;
- break;
- case 0x5:
- n = 0x30;
- break;
- case 0x8:
- n = 0x40;
- break;
- case 0xA:
- n = 0xB0;
- break;
- case 0xB:
- n = 0xE0;
- break;
- case 0xC:
- case 0xD:
-
- if(!m.param)
- m.command = CMD_NONE;
- break;
- case 0xE:
- switch(m.param & 0x0F)
- {
-
-
- case 0: m.param = 0x77; break;
-
- case 1: m.param = 0x77; break;
-
- case 2: m.param = 0x79; break;
-
- case 3: m.param = 0x7B; break;
- }
- break;
- case 0x18:
-
- if(!m.param)
- m.command = CMD_NONE;
- break;
- }
- if(n)
- m.param = n | (m.param & 0x0F);
- break;
- }
- m.command = (m.command < std::size(imfEffects)) ? imfEffects[m.command] : CMD_NONE;
- if(m.command == CMD_VOLUME && m.volcmd == VOLCMD_NONE)
- {
- m.volcmd = VOLCMD_VOLUME;
- m.vol = m.param;
- m.command = CMD_NONE;
- m.param = 0;
- }
- }
- static bool ValidateHeader(const IMFFileHeader &fileHeader)
- {
- if(std::memcmp(fileHeader.im10, "IM10", 4)
- || fileHeader.ordNum > 256
- || fileHeader.insNum >= MAX_INSTRUMENTS
- || fileHeader.bpm < 32
- || fileHeader.master > 64
- || fileHeader.amp < 4
- || fileHeader.amp > 127)
- {
- return false;
- }
- bool channelFound = false;
- for(const auto &chn : fileHeader.channels)
- {
- switch(chn.status)
- {
- case 0:
- channelFound = true;
- break;
- case 1:
- channelFound = true;
- break;
- case 2:
-
- break;
- default:
- return false;
- }
- }
- if(!channelFound)
- {
- return false;
- }
- return true;
- }
- static uint64 GetHeaderMinimumAdditionalSize(const IMFFileHeader &fileHeader)
- {
- return 256 + fileHeader.patNum * 4 + fileHeader.insNum * sizeof(IMFInstrument);
- }
- CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIMF(MemoryFileReader file, const uint64 *pfilesize)
- {
- IMFFileHeader fileHeader;
- if(!file.ReadStruct(fileHeader))
- {
- return ProbeWantMoreData;
- }
- if(!ValidateHeader(fileHeader))
- {
- return ProbeFailure;
- }
- return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
- }
- bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags)
- {
- IMFFileHeader fileHeader;
- file.Rewind();
- if(!file.ReadStruct(fileHeader))
- {
- return false;
- }
- if(!ValidateHeader(fileHeader))
- {
- return false;
- }
- if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
- {
- return false;
- }
- if(loadFlags == onlyVerifyHeader)
- {
- return true;
- }
-
- std::bitset<32> ignoreChannels;
- uint8 detectedChannels = 0;
- for(uint8 chn = 0; chn < 32; chn++)
- {
- ChnSettings[chn].Reset();
- ChnSettings[chn].nPan = fileHeader.channels[chn].panning * 256 / 255;
- ChnSettings[chn].szName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.channels[chn].name);
-
- switch(fileHeader.channels[chn].status)
- {
- case 0:
- detectedChannels = chn + 1;
- break;
- case 1:
- ChnSettings[chn].dwFlags = CHN_MUTE;
- detectedChannels = chn + 1;
- break;
- case 2:
- ChnSettings[chn].dwFlags = CHN_MUTE;
- ignoreChannels[chn] = true;
- break;
- default:
- return false;
- }
- }
- InitializeGlobals(MOD_TYPE_IMF);
- m_nChannels = detectedChannels;
- m_modFormat.formatName = U_("Imago Orpheus");
- m_modFormat.type = U_("imf");
- m_modFormat.charset = mpt::Charset::CP437;
-
- if(fileHeader.channels[0].status == 0)
- {
- CHANNELINDEX chn;
- for(chn = 1; chn < 16; chn++)
- if(fileHeader.channels[chn].status != 1)
- break;
- if(chn == 16)
- for(chn = 1; chn < 16; chn++)
- ChnSettings[chn].dwFlags.reset(CHN_MUTE);
- }
-
- m_songName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.title);
- m_SongFlags.set(SONG_LINEARSLIDES, fileHeader.flags & IMFFileHeader::linearSlides);
- m_nDefaultSpeed = fileHeader.tempo;
- m_nDefaultTempo.Set(fileHeader.bpm);
- m_nDefaultGlobalVolume = fileHeader.master * 4u;
- m_nSamplePreAmp = fileHeader.amp;
- m_nInstruments = fileHeader.insNum;
- m_nSamples = 0;
- uint8 orders[256];
- file.ReadArray(orders);
- ReadOrderFromArray(Order(), orders, fileHeader.ordNum, uint16_max, 0xFF);
-
- if(loadFlags & loadPatternData)
- Patterns.ResizeArray(fileHeader.patNum);
- for(PATTERNINDEX pat = 0; pat < fileHeader.patNum; pat++)
- {
- const uint16 length = file.ReadUint16LE(), numRows = file.ReadUint16LE();
- FileReader patternChunk = file.ReadChunk(length - 4);
- if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows))
- {
- continue;
- }
- ModCommand dummy;
- ROWINDEX row = 0;
- while(row < numRows)
- {
- uint8 mask = patternChunk.ReadUint8();
- if(mask == 0)
- {
- row++;
- continue;
- }
- uint8 channel = mask & 0x1F;
- ModCommand &m = (channel < GetNumChannels()) ? *Patterns[pat].GetpModCommand(row, channel) : dummy;
- if(mask & 0x20)
- {
-
- const auto [note, instr] = patternChunk.ReadArray<uint8, 2>();
- m.note = note;
- m.instr = instr;
- if(m.note == 160)
- {
- m.note = NOTE_KEYOFF;
- } else if(m.note == 255)
- {
- m.note = NOTE_NONE;
- } else
- {
- m.note = (m.note >> 4) * 12 + (m.note & 0x0F) + 12 + 1;
- if(!m.IsNoteOrEmpty())
- {
- m.note = NOTE_NONE;
- }
- }
- }
- if((mask & 0xC0) == 0xC0)
- {
-
- const auto [e1c, e1d, e2c, e2d] = patternChunk.ReadArray<uint8, 4>();
- if(e1c == 0x0C)
- {
- m.vol = std::min(e1d, uint8(0x40));
- m.volcmd = VOLCMD_VOLUME;
- m.command = e2c;
- m.param = e2d;
- } else if(e2c == 0x0C)
- {
- m.vol = std::min(e2d, uint8(0x40));
- m.volcmd = VOLCMD_VOLUME;
- m.command = e1c;
- m.param = e1d;
- } else if(e1c == 0x0A)
- {
- m.vol = e1d * 64 / 255;
- m.volcmd = VOLCMD_PANNING;
- m.command = e2c;
- m.param = e2d;
- } else if(e2c == 0x0A)
- {
- m.vol = e2d * 64 / 255;
- m.volcmd = VOLCMD_PANNING;
- m.command = e1c;
- m.param = e1d;
- } else
- {
-
- m.command = e2c;
- m.param = e2d;
- }
- } else if(mask & 0xC0)
- {
-
- const auto [command, param] = patternChunk.ReadArray<uint8, 2>();
- m.command = command;
- m.param = param;
- }
- if(m.command)
- ImportIMFEffect(m);
- if(ignoreChannels[channel] && m.IsGlobalCommand())
- m.command = CMD_NONE;
- }
- }
- SAMPLEINDEX firstSample = 1;
-
- for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++)
- {
- ModInstrument *instr = AllocateInstrument(ins + 1);
- IMFInstrument instrumentHeader;
- if(!file.ReadStruct(instrumentHeader) || instr == nullptr)
- {
- continue;
- }
-
-
-
- instrumentHeader.ConvertToMPT(*instr, firstSample);
-
- for(SAMPLEINDEX smp = 0; smp < instrumentHeader.smpNum; smp++)
- {
- IMFSample sampleHeader;
- file.ReadStruct(sampleHeader);
- const SAMPLEINDEX smpID = firstSample + smp;
- if(memcmp(sampleHeader.is10, "IS10", 4) || smpID >= MAX_SAMPLES)
- {
- continue;
- }
- m_nSamples = smpID;
- ModSample &sample = Samples[smpID];
- sampleHeader.ConvertToMPT(sample);
- m_szNames[smpID] = sample.filename;
- if(sampleHeader.length)
- {
- FileReader sampleChunk = file.ReadChunk(sampleHeader.length);
- if(loadFlags & loadSampleData)
- {
- SampleIO(
- sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
- SampleIO::mono,
- SampleIO::littleEndian,
- SampleIO::signedPCM)
- .ReadSample(sample, sampleChunk);
- }
- }
- }
- firstSample += instrumentHeader.smpNum;
- }
- return true;
- }
- OPENMPT_NAMESPACE_END
|