#include "stdafx.h"
#include "Loaders.h"
#include "../common/mptStringBuffer.h"
OPENMPT_NAMESPACE_BEGIN
#ifdef MPT_ALL_LOGGING
#define MED_LOG
#endif
#define MED_MAX_COMMENT_LENGTH 5*1024
#define MMD_FLAG_FILTERON 0x1
#define MMD_FLAG_JUMPINGON 0x2
#define MMD_FLAG_JUMP8TH 0x4
#define MMD_FLAG_INSTRSATT 0x8
#define MMD_FLAG_VOLHEX 0x10
#define MMD_FLAG_STSLIDE 0x20
#define MMD_FLAG_8CHANNEL 0x40
#define MMD_FLAG_SLOWHQ 0x80
#define MMD_FLAG2_BMASK 0x1F
#define MMD_FLAG2_BPM 0x20
#define MMD_FLAG2_MIX 0x80
#define MMD_FLAG3_STEREO 0x1
#define MMD_FLAG3_FREEPAN 0x2
#define MMD_FLAG3_GM 0x4
#define MMDTAG_END 0
#define MMDTAG_PTR 0x80000000
#define MMDTAG_MUSTKNOW 0x40000000
#define MMDTAG_MUSTWARN 0x20000000
#define MMDTAG_EXP_NUMFXGROUPS 1
#define MMDTAG_TRK_NAME (MMDTAG_PTR|1)
#define MMDTAG_TRK_NAMELEN 2
#define MMDTAG_TRK_FXGROUP 3
#define MMDTAG_FX_ECHOTYPE 1
#define MMDTAG_FX_ECHOLEN 2
#define MMDTAG_FX_ECHODEPTH 3
#define MMDTAG_FX_STEREOSEP 4
#define MMDTAG_FX_GROUPNAME (MMDTAG_PTR|5)
#define MMDTAG_FX_GRPNAMELEN 6
struct MEDMODULEHEADER
{
char id[4]; uint32be modlen; uint32be song; uint16be psecnum;
uint16be pseq;
uint32be blockarr; uint32be mmdflags;
uint32be smplarr; uint32be reserved;
uint32be expdata; uint32be reserved2;
uint16be pstate;
uint16be pblock;
uint16be pline;
uint16be pseqnum;
uint16be actplayline;
uint8be counter;
uint8be extra_songs; };
MPT_BINARY_STRUCT(MEDMODULEHEADER, 52)
struct MMD0SAMPLE
{
uint16be rep, replen;
uint8be midich;
uint8be midipreset;
uint8be svol;
int8be strans;
};
MPT_BINARY_STRUCT(MMD0SAMPLE, 8)
struct MMDSAMPLEHEADER
{
uint32be length; uint16be type;
uint16be packtype; uint16be subtype; uint8be commonflags; uint8be packerflags; uint32be leftchlen; uint32be rightchlen; uint8be SampleData[1]; };
MPT_BINARY_STRUCT(MMDSAMPLEHEADER, 21)
struct MMD0SONGHEADER
{
MMD0SAMPLE sample[63];
uint16be numblocks; uint16be songlen; uint8be playseq[256]; uint16be deftempo; int8be playtransp; uint8be flags; uint8be flags2; uint8be tempo2; uint8be trkvol[16]; uint8be mastervol; uint8be numsamples; };
MPT_BINARY_STRUCT(MMD0SONGHEADER, 788)
struct MMD2SONGHEADER
{
MMD0SAMPLE sample[63];
uint16be numblocks; uint16be numsections; uint32be playseqtable; uint32be sectiontable; uint32be trackvols; uint16be numtracks; uint16be numpseqs; uint32be trackpans; int32be flags3; uint16be voladj; uint16be channels; uint8be mix_echotype; uint8be mix_echodepth; uint16be mix_echolen; int8be mix_stereosep; uint8be pad0[223];
uint16be deftempo; int8be playtransp; uint8be flags; uint8be flags2; uint8be tempo2; uint8be pad1[16];
uint8be mastervol; uint8be numsamples; };
MPT_BINARY_STRUCT(MMD2SONGHEADER, 788)
struct MMD0BLOCK
{
uint8be numtracks;
uint8be lines; };
MPT_BINARY_STRUCT(MMD0BLOCK, 2)
struct MMD1BLOCK
{
uint16be numtracks; uint16be lines; uint32be info; };
MPT_BINARY_STRUCT(MMD1BLOCK, 8)
struct MMD1BLOCKINFO
{
uint32be hlmask; uint32be blockname; uint32be blocknamelen; uint32be pagetable; uint32be cmdexttable; uint32be reserved[4]; };
MPT_BINARY_STRUCT(MMD1BLOCKINFO, 36)
struct MMD2PLAYSEQ
{
char name[32];
uint32be command_offs; uint32be reserved;
uint16be length;
uint16be seq[512]; };
MPT_BINARY_STRUCT(MMD2PLAYSEQ, 1066)
struct MMDCOMMAND
{
uint16be offset; uint8be cmdnumber; uint8be extra_count;
uint8be extra_bytes[4]; };
MPT_BINARY_STRUCT(MMDCOMMAND, 8)
struct MMD0EXP
{
uint32be nextmod; uint32be exp_smp; uint16be s_ext_entries; uint16be s_ext_entrsz; uint32be annotxt;
uint32be annolen;
uint32be iinfo; uint16be i_ext_entries;
uint16be i_ext_entrsz;
uint32be jumpmask;
uint32be rgbtable;
uint8be channelsplit[4]; uint32be n_info;
uint32be songname; uint32be songnamelen;
uint32be dumps;
uint32be mmdinfo;
uint32be mmdrexx;
uint32be mmdcmd3x;
uint32be trackinfo_ofs; uint32be effectinfo_ofs; uint32be tag_end;
};
MPT_BINARY_STRUCT(MMD0EXP, 80)
static const uint8 bpmvals[9] = { 179,164,152,141,131,123,116,110,104};
static void MedConvert(ModCommand &p, const MMD0SONGHEADER *pmsh)
{
ModCommand::COMMAND command = p.command;
uint32 param = p.param;
switch(command)
{
case 0x00: if (param) command = CMD_ARPEGGIO; else command = CMD_NONE; break;
case 0x01: command = CMD_PORTAMENTOUP; break;
case 0x02: command = CMD_PORTAMENTODOWN; break;
case 0x03: command = CMD_TONEPORTAMENTO; break;
case 0x04: command = CMD_VIBRATO; break;
case 0x05: command = CMD_TONEPORTAVOL; break;
case 0x06: command = CMD_VIBRATOVOL; break;
case 0x07: command = CMD_TREMOLO; break;
case 0x0A: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = CMD_NONE; break;
case 0x0B: command = CMD_POSITIONJUMP; break;
case 0x0C: command = CMD_VOLUME;
if (pmsh->flags & MMD_FLAG_VOLHEX)
{
if (param < 0x80)
{
param = (param+1) / 2;
} else command = CMD_NONE;
} else
{
if (param <= 0x99)
{
param = (param >> 4)*10+((param & 0x0F) % 10);
if (param > 64) param = 64;
} else command = CMD_NONE;
}
break;
case 0x09: command = static_cast<ModCommand::COMMAND>((param <= 0x20) ? CMD_SPEED : CMD_TEMPO); break;
case 0x0D: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = CMD_NONE; break;
case 0x0F: if (!param) command = CMD_PATTERNBREAK; else
if (param <= 0xF0)
{
if (pmsh->flags & MMD_FLAG_8CHANNEL)
{
param = (param == 0 || param >= 10) ? 99 : bpmvals[param-1];
} else
if (param <= 0x0A)
{
command = CMD_SPEED;
} else
if (!(pmsh->flags2 & MMD_FLAG2_BPM))
{
param = Util::muldiv(param, 5*715909, 2*474326);
}
if (param > 0x0A)
{
command = CMD_TEMPO;
if (param < 0x21) param = 0x21;
if (param > 240) param = 240;
}
} else
switch(param)
{
case 0xF1:
command = CMD_MODCMDEX;
param = 0x93;
break;
case 0xF2:
command = CMD_MODCMDEX;
param = 0xD3;
break;
case 0xF3:
command = CMD_MODCMDEX;
param = 0x92;
break;
case 0xF4:
command = CMD_MODCMDEX;
param = 0xD2;
break;
case 0xF5:
command = CMD_MODCMDEX;
param = 0xD4;
break;
case 0xF8:
command = CMD_MODCMDEX;
param = 0x00;
break;
case 0xF9:
command = CMD_MODCMDEX;
param = 0x01;
break;
case 0xFD:
command = CMD_TONEPORTAMENTO;
param = 0xFF;
break;
case 0xFE:
command = CMD_SPEED;
param = 0;
break;
case 0xFF:
command = CMD_MODCMDEX;
param = 0xC0;
break;
default:
#ifdef MED_LOG
MPT_LOG(LogDebug, "MED", mpt::format(U_("Unknown Fxx command: cmd=0x%1 param=0x%2"))(mpt::ufmt::HEX0<2>(command), mpt::ufmt::HEX0<2>(param)));
#endif
command = CMD_NONE;
param = 0;
}
break;
case 0x11:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0x10;
break;
case 0x12:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0x20;
break;
case 0x14:
command = CMD_VIBRATO;
break;
case 0x15:
command = CMD_MODCMDEX;
param &= 0x0F;
param |= 0x50;
break;
case 0x16:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0x60;
break;
case 0x18:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0xC0;
break;
case 0x19:
command = CMD_OFFSET;
break;
case 0x1A:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0xA0;
break;
case 0x1B:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0xB0;
break;
case 0x1D:
command = CMD_PATTERNBREAK;
break;
case 0x1E:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0xE0;
break;
case 0x1F:
command = CMD_RETRIG;
param &= 0x0F;
break;
case 0x2E:
command = CMD_MODCMDEX;
param = ((param + 0x10) & 0xFF) >> 1;
if (param > 0x0F) param = 0x0F;
param |= 0x80;
break;
default:
#ifdef MED_LOG
MPT_LOG(LogDebug, "MED", mpt::format(U_("Unknown command: cmd=0x%1 param=0x%2"))(mpt::ufmt::HEX0<2>(command), mpt::ufmt::HEX0<2>(param)));
#endif
command = CMD_NONE;
param = 0;
}
p.command = command;
p.param = static_cast<ModCommand::PARAM>(param);
}
static bool ValidateHeader(const MEDMODULEHEADER &pmmh)
{
if(std::memcmp(pmmh.id, "MMD", 3)
|| pmmh.id[3] < '0' || pmmh.id[3] > '3'
|| pmmh.song == 0
)
{
return false;
}
return true;
}
static uint64 GetHeaderMinimumAdditionalSize(const MEDMODULEHEADER &pmmh)
{
MPT_UNREFERENCED_PARAMETER(pmmh);
return sizeof(MMD0SONGHEADER);
}
CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize)
{
MEDMODULEHEADER pmmh;
if(!file.ReadStruct(pmmh))
{
return ProbeWantMoreData;
}
if(!ValidateHeader(pmmh))
{
return ProbeFailure;
}
return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(pmmh));
}
bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
{
file.Rewind();
MEDMODULEHEADER pmmh;
if(!file.ReadStruct(pmmh))
{
return false;
}
if(!ValidateHeader(pmmh))
{
return false;
}
if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(pmmh))))
{
return false;
}
const uint32 dwSong = pmmh.song;
if(!file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER)))
{
return false;
}
if(loadFlags == onlyVerifyHeader)
{
return true;
}
file.Rewind();
const FileReader::off_t dwMemLength = file.GetLength();
const uint8 *lpStream = file.GetRawData<uint8>();
const MMD0SONGHEADER *pmsh;
const MMD2SONGHEADER *pmsh2;
const MMD0EXP *pmex;
uint32 dwBlockArr, dwSmplArr, dwExpData;
const_unaligned_ptr_be<uint32> pdwTable;
int8 version = pmmh.id[3];
uint32 deftempo;
int playtransp = 0;
InitializeGlobals(MOD_TYPE_MED);
InitializeChannels();
SetupMODPanning(true);
const MPT_UCHAR_TYPE *madeWithTracker = UL_("");
switch(version)
{
case '0': madeWithTracker = m_nChannels > 4 ? UL_("OctaMED v2.10") : UL_("MED v2"); break;
case '1': madeWithTracker = UL_("OctaMED v4"); break;
case '2': madeWithTracker = UL_("OctaMED v5"); break;
case '3': madeWithTracker = UL_("OctaMED Soundstudio"); break;
}
m_modFormat.formatName = mpt::format(U_("OctaMED (MMD%1)"))(version - '0');
m_modFormat.type = U_("med");
m_modFormat.madeWithTracker = madeWithTracker;
m_modFormat.charset = mpt::CharsetISO8859_1;
m_nSamplePreAmp = 32;
dwBlockArr = pmmh.blockarr;
dwSmplArr = pmmh.smplarr;
dwExpData = pmmh.expdata;
if ((dwExpData) && (dwExpData < dwMemLength - sizeof(MMD0EXP)))
pmex = (const MMD0EXP *)(lpStream+dwExpData);
else
pmex = NULL;
pmsh = (const MMD0SONGHEADER *)(lpStream + dwSong);
pmsh2 = (const MMD2SONGHEADER *)pmsh;
uint16 wNumBlocks = pmsh->numblocks;
m_nChannels = 4;
m_nSamples = pmsh->numsamples;
if (m_nSamples > 63) m_nSamples = 63;
m_nDefaultTempo.Set(125);
deftempo = pmsh->deftempo;
if (!deftempo) deftempo = 125;
if (pmsh->flags2 & MMD_FLAG2_BPM)
{
uint32 tempo_tpl = (pmsh->flags2 & MMD_FLAG2_BMASK) + 1;
if (!tempo_tpl) tempo_tpl = 4;
deftempo *= tempo_tpl;
deftempo /= 4;
#ifdef MED_LOG
MPT_LOG(LogDebug, "MED", mpt::format(U_("newtempo: %1 bpm (bpm=%2 lpb=%3)"))(deftempo, static_cast<uint16>(pmsh->deftempo), (pmsh->flags2 & MMD_FLAG2_BMASK)+1));
#endif
} else
{
if((pmsh->flags & MMD_FLAG_8CHANNEL) && deftempo > 0 && deftempo <= 9)
deftempo = bpmvals[deftempo-1];
else
deftempo = Util::muldiv(deftempo, 5 * 715909, 2 * 474326);
#ifdef MED_LOG
MPT_LOG(LogDebug, "MED", mpt::format(U_("oldtempo: %1 bpm (bpm=%2)\n"))(deftempo, static_cast<uint16>(pmsh->deftempo)));
#endif
}
m_nDefaultSpeed = pmsh->tempo2;
if (!m_nDefaultSpeed) m_nDefaultSpeed = 6;
if (deftempo < 0x21) deftempo = 0x21;
m_nDefaultTempo.Set(deftempo);
for (uint32 iSHdr=0; iSHdr<m_nSamples; iSHdr++)
{
ModSample &sample = Samples[iSHdr + 1];
sample.Initialize(MOD_TYPE_MED);
sample.nLoopStart = pmsh->sample[iSHdr].rep * 2u;
sample.nLoopEnd = sample.nLoopStart + (pmsh->sample[iSHdr].replen * 2u);
sample.nVolume = (pmsh->sample[iSHdr].svol << 2);
sample.nGlobalVol = 64;
if (sample.nVolume > 256) sample.nVolume = 256;
sample.RelativeTone = pmsh->sample[iSHdr].strans;
sample.nPan = 128;
if (sample.nLoopEnd <= 2) sample.nLoopEnd = 0;
if (sample.nLoopEnd) sample.uFlags.set(CHN_LOOP);
}
m_SongFlags.set(SONG_FASTVOLSLIDES, !(pmsh->flags & 0x20));
if (version < '2')
{
uint32 nbo = pmsh->songlen;
if (!nbo) nbo = 1;
ReadOrderFromArray(Order(), pmsh->playseq, nbo);
playtransp = pmsh->playtransp;
} else
{
uint32 nSections;
ORDERINDEX nOrders = 0;
uint16 nTrks = pmsh2->numtracks;
if ((nTrks >= 4) && (nTrks <= 32)) m_nChannels = nTrks;
uint32 playseqtable = pmsh2->playseqtable;
uint32 numplayseqs = pmsh2->numpseqs;
if (!numplayseqs) numplayseqs = 1;
nSections = pmsh2->numsections;
uint32 sectiontable = pmsh2->sectiontable;
if ((!nSections) || (!sectiontable) || (sectiontable >= dwMemLength-2)) nSections = 1;
nOrders = 0;
Order().clear();
for (uint32 iSection=0; iSection<nSections; iSection++)
{
uint32 nplayseq = 0;
if (sectiontable && sectiontable < dwMemLength && 2 >= dwMemLength - sectiontable)
{
nplayseq = *const_unaligned_ptr_be<uint16>(lpStream + sectiontable);
sectiontable += 2;
} else
{
nSections = 0;
}
uint32 pseq = 0;
if ((playseqtable) && (playseqtable < dwMemLength) && (nplayseq * 4 <= dwMemLength - playseqtable))
{
pseq = (const_unaligned_ptr_be<uint32>(lpStream+playseqtable))[nplayseq];
}
if (pseq && pseq < dwMemLength && sizeof(MMD2PLAYSEQ) <= dwMemLength - pseq)
{
const MMD2PLAYSEQ *pmps = (const MMD2PLAYSEQ *)(lpStream + pseq);
if(m_songName.empty()) mpt::String::Read<mpt::String::maybeNullTerminated>(m_songName, pmps->name);
ORDERINDEX n = std::min<ORDERINDEX>(pmps->length, MAX_ORDERS - nOrders);
if (n <= (dwMemLength - pseq + 42) / 2u && n < MPT_ARRAY_COUNT(pmps->seq))
{
Order().resize(nOrders + n);
for (uint32 i=0; i<n; i++)
{
uint16 seqval = pmps->seq[i];
if (seqval < wNumBlocks)
{
Order()[nOrders++] = static_cast<ORDERINDEX>(seqval);
}
}
}
}
}
playtransp = pmsh2->playtransp;
}
if (pmex)
{
if ((m_nChannels == 4) && (pmsh->flags & MMD_FLAG_8CHANNEL))
{
for (uint32 i8ch=0; i8ch<4; i8ch++)
{
if (pmex->channelsplit[i8ch]) m_nChannels++;
}
}
uint32 annotxt = pmex->annotxt;
uint32 annolen = pmex->annolen;
annolen = std::min<uint32>(annolen, MED_MAX_COMMENT_LENGTH); if ((annotxt) && (annolen) && (annolen <= dwMemLength) && (annotxt <= dwMemLength - annolen) )
{
m_songMessage.Read(mpt::byte_cast<const mpt::byte*>(lpStream) + annotxt, annolen - 1, SongMessage::leAutodetect);
}
uint32 songname = pmex->songname;
uint32 songnamelen = pmex->songnamelen;
if ((songname) && (songnamelen) && (songname <= dwMemLength) && (songnamelen <= dwMemLength-songname))
{
mpt::String::Read<mpt::String::maybeNullTerminated>(m_songName, lpStream + songname, songnamelen);
}
uint32 smpinfoex = pmex->iinfo;
if (smpinfoex)
{
uint32 iinfoptr = pmex->iinfo;
uint32 ientries = pmex->i_ext_entries;
uint32 ientrysz = pmex->i_ext_entrsz;
if ((iinfoptr) && (ientrysz < 256) && (ientries*ientrysz < dwMemLength) && (iinfoptr < dwMemLength - ientries*ientrysz))
{
const char *psznames = (const char *)(lpStream + iinfoptr);
for (uint32 i=0; i<ientries; i++) if (i < m_nSamples)
{
mpt::String::Read<mpt::String::maybeNullTerminated>(m_szNames[i + 1], (psznames + i * ientrysz), ientrysz);
}
}
}
uint32 trackinfo_ofs = pmex->trackinfo_ofs;
if ((trackinfo_ofs) && (trackinfo_ofs < dwMemLength) && (m_nChannels * 4u < dwMemLength - trackinfo_ofs))
{
const_unaligned_ptr_be<uint32> ptrktags = const_unaligned_ptr_be<uint32>(lpStream + trackinfo_ofs);
for (uint32 i=0; i<m_nChannels; i++)
{
uint32 trknameofs = 0, trknamelen = 0;
uint32 trktagofs = ptrktags[i];
if (trktagofs && (trktagofs <= dwMemLength - 8) )
{
while (trktagofs < dwMemLength - 8)
{
uint32 ntag = *const_unaligned_ptr_be<uint32>(lpStream + trktagofs);
if (ntag == MMDTAG_END) break;
uint32 tagdata = *const_unaligned_ptr_be<uint32>(lpStream + trktagofs + 4);
switch(ntag)
{
case MMDTAG_TRK_NAMELEN: trknamelen = tagdata; break;
case MMDTAG_TRK_NAME: trknameofs = tagdata; break;
}
trktagofs += 8;
}
if ((trknameofs) && (trknameofs < dwMemLength - trknamelen) && trknamelen < dwMemLength)
{
mpt::String::Read<mpt::String::maybeNullTerminated>(ChnSettings[i].szName, lpStream + trknameofs, trknamelen);
}
}
}
}
}
if (dwSmplArr > dwMemLength - 4*m_nSamples) return true;
pdwTable = const_unaligned_ptr_be<uint32>(lpStream + dwSmplArr);
for (uint32 iSmp=0; iSmp<m_nSamples; iSmp++) if (pdwTable[iSmp])
{
const uint32 dwPos = pdwTable[iSmp];
if ((dwPos >= dwMemLength) || (dwPos + sizeof(MMDSAMPLEHEADER) >= dwMemLength) || !(loadFlags & loadSampleData)) continue;
const MMDSAMPLEHEADER *psdh = (const MMDSAMPLEHEADER *)(lpStream + dwPos);
uint32 len = psdh->length;
#ifdef MED_LOG
MPT_LOG(LogDebug, "MED", mpt::format(U_("SampleData %1: stype=0x%2 len=%3"))(iSmp, mpt::ufmt::HEX0<2>(static_cast<uint16>(psdh->type)), len));
#endif
if(dwPos + len + 6 > dwMemLength) len = 0;
uint32 stype = psdh->type;
FileReader chunk(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(lpStream + dwPos + 6, dwMemLength - dwPos - 6)));
SampleIO sampleIO(
SampleIO::_8bit,
SampleIO::mono,
SampleIO::bigEndian,
SampleIO::signedPCM);
if (stype & 0x80)
{
chunk.Skip((stype & 0x20) ? 14 : 6);
} else
{
if(stype & 0x10)
{
sampleIO |= SampleIO::_16bit;
len /= 2;
}
if(stype & 0x20)
{
sampleIO |= SampleIO::stereoSplit;
len /= 2;
}
}
Samples[iSmp + 1].nLength = len;
sampleIO.ReadSample(Samples[iSmp + 1], chunk);
}
if(!(loadFlags & loadPatternData))
{
return true;
}
if (wNumBlocks > MAX_PATTERNS) wNumBlocks = MAX_PATTERNS;
if ((!dwBlockArr) || (dwBlockArr > dwMemLength - 4u*wNumBlocks) || (4u*wNumBlocks > dwMemLength)) return true;
pdwTable = const_unaligned_ptr_be<uint32>(lpStream + dwBlockArr);
playtransp += (version == '3') ? 24 : 48;
Patterns.ResizeArray(wNumBlocks);
for (PATTERNINDEX iBlk=0; iBlk<wNumBlocks; iBlk++)
{
uint32 dwPos = pdwTable[iBlk];
if ((!dwPos) || (dwPos >= dwMemLength) || (dwPos >= dwMemLength - 8)) continue;
uint32 lines = 64, tracks = 4;
if (version == '0')
{
const MMD0BLOCK *pmb = (const MMD0BLOCK *)(lpStream + dwPos);
lines = pmb->lines + 1;
tracks = pmb->numtracks;
if (!tracks) tracks = m_nChannels;
if(!Patterns.Insert(iBlk, lines)) continue;
const uint8 * s = (const uint8 *)(lpStream + dwPos + 2);
uint32 maxlen = tracks*lines*3;
if (maxlen + dwPos > dwMemLength - 2) break;
for (uint32 y=0; y<lines; y++)
{
ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0);
for (uint32 x=0; x<tracks; x++, s+=3) if (x < m_nChannels)
{
uint8 note = s[0] & 0x3F;
uint8 instr = s[1] >> 4;
if (s[0] & 0x80) instr |= 0x10;
if (s[0] & 0x40) instr |= 0x20;
if ((note) && (note <= 132)) p->note = static_cast<uint8>(note + playtransp);
p->instr = instr;
p->command = s[1] & 0x0F;
p->param = s[2];
MedConvert(*p, pmsh);
p++;
}
}
} else
{
const MMD1BLOCK *pmb = (const MMD1BLOCK *)(lpStream + dwPos);
#ifdef MED_LOG
MPT_LOG(LogDebug, "MED", mpt::format(U_("MMD1BLOCK: lines=%1, tracks=%2, offset=0x%3"))(
static_cast<uint16>(pmb->lines), static_cast<uint16>(pmb->numtracks), mpt::ufmt::HEX0<4>(static_cast<uint32>(pmb->info))));
#endif
const MMD1BLOCKINFO *pbi = NULL;
const uint8 *pcmdext = NULL;
lines = pmb->lines + 1;
tracks = pmb->numtracks;
if (!tracks) tracks = m_nChannels;
Patterns.Insert(iBlk, lines);
uint32 dwBlockInfo = pmb->info;
if ((dwBlockInfo) && (dwBlockInfo < dwMemLength - sizeof(MMD1BLOCKINFO)))
{
pbi = (const MMD1BLOCKINFO *)(lpStream + dwBlockInfo);
#ifdef MED_LOG
MPT_LOG(LogDebug, "MED", mpt::format(U_(" BLOCKINFO: blockname=0x%1 namelen=%2 pagetable=0x%3 &cmdexttable=0x%4"))(
mpt::ufmt::HEX0<4>(static_cast<uint32>(pbi->blockname)), static_cast<uint32>(pbi->blocknamelen), mpt::ufmt::HEX0<4>(static_cast<uint32>(pbi->pagetable)), mpt::ufmt::HEX0<4>(static_cast<uint32>(pbi->cmdexttable))));
#endif
if ((pbi->blockname) && (pbi->blocknamelen))
{
uint32 nameofs = pbi->blockname;
uint32 namelen = pbi->blocknamelen;
if ((nameofs < dwMemLength) && (namelen < dwMemLength - nameofs))
{
Patterns[iBlk].SetName((const char *)(lpStream + nameofs), namelen);
}
}
if (pbi->cmdexttable)
{
uint32 cmdexttable = pbi->cmdexttable;
if (cmdexttable < dwMemLength - 4)
{
cmdexttable = *const_unaligned_ptr_be<uint32>(lpStream + cmdexttable);
if ((cmdexttable) && (cmdexttable <= dwMemLength - lines*tracks))
{
pcmdext = (const uint8 *)(lpStream + cmdexttable);
}
}
}
}
const uint8 * s = (const uint8 *)(lpStream + dwPos + 8);
uint32 maxlen = tracks*lines*4;
if (maxlen + dwPos > dwMemLength - 8 || !Patterns.IsValidPat(iBlk)) break;
for (uint32 y=0; y<lines; y++)
{
ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0);
for (uint32 x=0; x<tracks; x++, s+=4) if (x < m_nChannels)
{
uint8 note = s[0];
if(note & 0x7F)
{
int rnote = note + playtransp;
if (rnote < 1) rnote = 1;
if (rnote > NOTE_MAX) rnote = NOTE_MAX;
p->note = (uint8)rnote;
} else if(note == 0x80)
{
p->note = NOTE_NOTECUT;
}
p->instr = s[1];
p->command = s[2];
p->param = s[3];
if (pcmdext) p->vol = pcmdext[x];
MedConvert(*p, pmsh);
p++;
}
if (pcmdext) pcmdext += tracks;
}
}
}
return true;
}
OPENMPT_NAMESPACE_END