123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- #include "stdafx.h"
- #include "Loaders.h"
- #include "mod_specifications.h"
- OPENMPT_NAMESPACE_BEGIN
- struct GDMFileHeader
- {
- char magic[4];
- char songTitle[32];
- char songMusician[32];
- char dosEOF[3];
- char magic2[4];
- uint8le formatMajorVer;
- uint8le formatMinorVer;
- uint16le trackerID;
- uint8le trackerMajorVer;
- uint8le trackerMinorVer;
- uint8le panMap[32];
- uint8le masterVol;
- uint8le tempo;
- uint8le bpm;
- uint16le originalFormat;
-
-
-
-
- uint32le orderOffset;
- uint8le lastOrder;
- uint32le patternOffset;
- uint8le lastPattern;
- uint32le sampleHeaderOffset;
- uint32le sampleDataOffset;
- uint8le lastSample;
- uint32le messageTextOffset;
- uint32le messageTextLength;
- uint32le scrollyScriptOffset;
- uint16le scrollyScriptLength;
- uint32le textGraphicOffset;
- uint16le textGraphicLength;
- };
- MPT_BINARY_STRUCT(GDMFileHeader, 157)
- struct GDMSampleHeader
- {
- enum SampleFlags
- {
- smpLoop = 0x01,
- smp16Bit = 0x02,
- smpVolume = 0x04,
- smpPanning = 0x08,
- smpLZW = 0x10,
- smpStereo = 0x20,
- };
- char name[32];
- char fileName[12];
- uint8le emsHandle;
- uint32le length;
- uint32le loopBegin;
- uint32le loopEnd;
- uint8le flags;
- uint16le c4Hertz;
- uint8le volume;
- uint8le panning;
- };
- MPT_BINARY_STRUCT(GDMSampleHeader, 62)
- static constexpr MODTYPE gdmFormatOrigin[] =
- {
- MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM
- };
- static constexpr mpt::uchar gdmFormatOriginType[][4] =
- {
- UL_(""), UL_("mod"), UL_("mtm"), UL_("s3m"), UL_("669"), UL_("far"), UL_("ult"), UL_("stm"), UL_("med"), UL_("psm")
- };
- static constexpr const mpt::uchar * gdmFormatOriginFormat[] =
- {
- UL_(""),
- UL_("Generic MOD"),
- UL_("MultiTracker"),
- UL_("Scream Tracker 3"),
- UL_("Composer 669 / UNIS 669"),
- UL_("Farandole Composer"),
- UL_("UltraTracker"),
- UL_("Scream Tracker 2"),
- UL_("OctaMED"),
- UL_("Epic Megagames MASI")
- };
- static bool ValidateHeader(const GDMFileHeader &fileHeader)
- {
- if(std::memcmp(fileHeader.magic, "GDM\xFE", 4)
- || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26
- || std::memcmp(fileHeader.magic2, "GMFS", 4)
- || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0
- || fileHeader.originalFormat >= std::size(gdmFormatOrigin)
- || fileHeader.originalFormat == 0)
- {
- return false;
- }
- return true;
- }
- CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderGDM(MemoryFileReader file, const uint64 *pfilesize)
- {
- GDMFileHeader fileHeader;
- if(!file.ReadStruct(fileHeader))
- {
- return ProbeWantMoreData;
- }
- if(!ValidateHeader(fileHeader))
- {
- return ProbeFailure;
- }
- MPT_UNREFERENCED_PARAMETER(pfilesize);
- return ProbeSuccess;
- }
- bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags)
- {
- file.Rewind();
- GDMFileHeader fileHeader;
- if(!file.ReadStruct(fileHeader))
- {
- return false;
- }
- if(!ValidateHeader(fileHeader))
- {
- return false;
- }
- if(loadFlags == onlyVerifyHeader)
- {
- return true;
- }
- InitializeGlobals(gdmFormatOrigin[fileHeader.originalFormat]);
- m_SongFlags.set(SONG_IMPORTED);
- m_modFormat.formatName = U_("General Digital Music");
- m_modFormat.type = U_("gdm");
- m_modFormat.madeWithTracker = MPT_UFORMAT("BWSB 2GDM {}.{}")(fileHeader.trackerMajorVer, fileHeader.formatMinorVer);
- m_modFormat.originalType = gdmFormatOriginType[fileHeader.originalFormat];
- m_modFormat.originalFormatName = gdmFormatOriginFormat[fileHeader.originalFormat];
- m_modFormat.charset = mpt::Charset::CP437;
-
- m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songTitle);
-
- {
- std::string artist = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songMusician);
- if(artist != "Unknown")
- {
- m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, artist);
- }
- }
-
- m_nChannels = 32;
- for(CHANNELINDEX i = 0; i < 32; i++)
- {
- ChnSettings[i].Reset();
- if(fileHeader.panMap[i] < 16)
- {
- ChnSettings[i].nPan = static_cast<uint16>(std::min((fileHeader.panMap[i] * 16) + 8, 256));
- } else if(fileHeader.panMap[i] == 16)
- {
- ChnSettings[i].nPan = 128;
- ChnSettings[i].dwFlags = CHN_SURROUND;
- } else if(fileHeader.panMap[i] == 0xFF)
- {
- m_nChannels = i;
- break;
- }
- }
- if(m_nChannels < 1)
- {
- return false;
- }
- m_nDefaultGlobalVolume = std::min(fileHeader.masterVol * 4u, 256u);
- m_nDefaultSpeed = fileHeader.tempo;
- m_nDefaultTempo.Set(fileHeader.bpm);
-
- if(file.Seek(fileHeader.orderOffset))
- {
- ReadOrderFromFile<uint8>(Order(), file, fileHeader.lastOrder + 1, 0xFF, 0xFE);
- }
-
- if(!file.Seek(fileHeader.sampleHeaderOffset))
- {
- return false;
- }
- m_nSamples = fileHeader.lastSample + 1;
-
- for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
- {
- GDMSampleHeader gdmSample;
- if(!file.ReadStruct(gdmSample))
- {
- break;
- }
- ModSample &sample = Samples[smp];
- sample.Initialize();
- m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, gdmSample.name);
- sample.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, gdmSample.fileName);
- sample.nC5Speed = gdmSample.c4Hertz;
- if(UseFinetuneAndTranspose())
- {
-
-
- static constexpr uint16 rate2finetune[] = { 8363, 8424, 8485, 8547, 8608, 8671, 8734, 8797, 7894, 7951, 8009, 8067, 8125, 8184, 8244, 8303 };
- for(uint8 i = 0; i < 16; i++)
- {
- if(sample.nC5Speed == rate2finetune[i])
- {
- sample.nFineTune = MOD2XMFineTune(i);
- break;
- }
- }
- }
- sample.nGlobalVol = 64;
-
- sample.nLength = gdmSample.length;
-
- if(gdmSample.flags & GDMSampleHeader::smp16Bit)
- {
- sample.uFlags.set(CHN_16BIT);
- sample.nLength /= 2;
- }
- sample.nLoopStart = gdmSample.loopBegin;
- sample.nLoopEnd = gdmSample.loopEnd - 1;
- if(gdmSample.flags & GDMSampleHeader::smpLoop)
- sample.uFlags.set(CHN_LOOP);
- if((gdmSample.flags & GDMSampleHeader::smpVolume) && gdmSample.volume != 0xFF)
- sample.nVolume = std::min(static_cast<uint8>(gdmSample.volume), uint8(64)) * 4;
- else
- sample.uFlags.set(SMP_NODEFAULTVOLUME);
- if(gdmSample.flags & GDMSampleHeader::smpPanning)
- {
-
- sample.uFlags.set(CHN_PANNING);
-
- sample.nPan = static_cast<uint16>((gdmSample.panning > 15) ? 128 : std::min((gdmSample.panning * 16) + 8, 256));
- sample.uFlags.set(CHN_SURROUND, gdmSample.panning == 16);
- } else
- {
- sample.nPan = 128;
- }
- }
-
- if((loadFlags & loadSampleData) && file.Seek(fileHeader.sampleDataOffset))
- {
- for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
- {
- SampleIO(
- Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
- SampleIO::mono,
- SampleIO::littleEndian,
- SampleIO::unsignedPCM)
- .ReadSample(Samples[smp], file);
- }
- }
-
- Patterns.ResizeArray(fileHeader.lastPattern + 1);
- const CModSpecifications &modSpecs = GetModSpecifications(GetBestSaveFormat());
- bool onlyAmigaNotes = true;
-
- file.Seek(fileHeader.patternOffset);
- for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPattern; pat++)
- {
-
- uint16 patternLength = file.ReadUint16LE();
- if(patternLength <= 2)
- {
-
- continue;
- }
- FileReader chunk = file.ReadChunk(patternLength - 2);
- if(!(loadFlags & loadPatternData) || !chunk.IsValid() || !Patterns.Insert(pat, 64))
- {
- continue;
- }
- enum
- {
- rowDone = 0x00,
- channelMask = 0x1F,
- noteFlag = 0x20,
- effectFlag = 0x40,
- effectMask = 0x1F,
- effectMore = 0x20,
- };
- for(ROWINDEX row = 0; row < 64; row++)
- {
- PatternRow rowBase = Patterns[pat].GetRow(row);
- uint8 channelByte;
-
- while((channelByte = chunk.ReadUint8()) != rowDone)
- {
- CHANNELINDEX channel = channelByte & channelMask;
- if(channel >= m_nChannels) break;
- ModCommand &m = rowBase[channel];
- if(channelByte & noteFlag)
- {
-
- auto [note, instr] = chunk.ReadArray<uint8, 2>();
- if(note)
- {
- note = (note & 0x7F) - 1;
- m.note = (note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN;
- if(!m.IsAmigaNote())
- {
- onlyAmigaNotes = false;
- }
- }
- m.instr = instr;
- }
- if(channelByte & effectFlag)
- {
-
- m.command = CMD_NONE;
- m.volcmd = VOLCMD_NONE;
- while(chunk.CanRead(2))
- {
-
- const ModCommand oldCmd = m;
- const auto [effByte, param] = chunk.ReadArray<uint8, 2>();
- m.param = param;
-
- static constexpr EffectCommand gdmEffTrans[] =
- {
- CMD_NONE, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO,
- CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO,
- CMD_TREMOR, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP,
- CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_SPEED,
- CMD_ARPEGGIO, CMD_NONE , CMD_RETRIG, CMD_GLOBALVOLUME,
- CMD_FINEVIBRATO, CMD_NONE, CMD_NONE, CMD_NONE,
- CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE,
- CMD_NONE, CMD_NONE, CMD_S3MCMDEX, CMD_TEMPO,
- };
-
- uint8 command = effByte & effectMask;
- if(command < std::size(gdmEffTrans))
- m.command = gdmEffTrans[command];
- else
- m.command = CMD_NONE;
-
- switch(m.command)
- {
- case CMD_PORTAMENTOUP:
- case CMD_PORTAMENTODOWN:
- if(m.param >= 0xE0 && m_nType != MOD_TYPE_MOD)
- m.param = 0xDF;
- break;
- case CMD_TONEPORTAVOL:
- case CMD_VIBRATOVOL:
- if(m.param & 0xF0)
- m.param &= 0xF0;
- break;
- case CMD_VOLUME:
- m.param = std::min(m.param, uint8(64));
- if(modSpecs.HasVolCommand(VOLCMD_VOLUME))
- {
- m.volcmd = VOLCMD_VOLUME;
- m.vol = m.param;
-
- m.command = oldCmd.command;
- m.param = oldCmd.param;
- }
- break;
- case CMD_MODCMDEX:
- switch(m.param >> 4)
- {
- case 0x8:
- m.command = CMD_PORTAMENTOUP;
- m.param = 0xE0 | (m.param & 0x0F);
- break;
- case 0x9:
- m.command = CMD_PORTAMENTODOWN;
- m.param = 0xE0 | (m.param & 0x0F);
- break;
- default:
- if(!modSpecs.HasCommand(CMD_MODCMDEX))
- m.ExtendedMODtoS3MEffect();
- break;
- }
- break;
- case CMD_RETRIG:
- if(!modSpecs.HasCommand(CMD_RETRIG) && modSpecs.HasCommand(CMD_MODCMDEX))
- {
-
- m.command = CMD_MODCMDEX;
- m.param = 0x90 | (m.param & 0x0F);
- }
- break;
- case CMD_S3MCMDEX:
-
- if(m.param == 0x01)
- {
-
- m.param = 0x91;
- } else if((m.param & 0xF0) == 0x80)
- {
-
- if (!modSpecs.HasCommand(CMD_S3MCMDEX))
- m.command = CMD_MODCMDEX;
- } else
- {
-
- m.command = CMD_NONE;
- }
- break;
- }
-
- if(m.command == CMD_S3MCMDEX && ((m.param >> 4) == 0x8) && m.volcmd == VOLCMD_NONE)
- {
- m.volcmd = VOLCMD_PANNING;
- m.vol = ((m.param & 0x0F) * 64 + 8) / 15;
- m.command = oldCmd.command;
- m.param = oldCmd.param;
- }
- if(!(effByte & effectMore))
- break;
- }
- }
- }
- }
- }
- m_SongFlags.set(SONG_AMIGALIMITS | SONG_ISAMIGA, GetType() == MOD_TYPE_MOD && GetNumChannels() == 4 && onlyAmigaNotes);
-
- if(fileHeader.messageTextLength > 0 && file.Seek(fileHeader.messageTextOffset))
- {
- m_songMessage.Read(file, fileHeader.messageTextLength, SongMessage::leAutodetect);
- }
- return true;
- }
- OPENMPT_NAMESPACE_END
|