From: <sag...@us...> - 2012-03-04 23:39:59
|
Revision: 1203 http://modplug.svn.sourceforge.net/modplug/?rev=1203&view=rev Author: saga-games Date: 2012-03-04 23:39:50 +0000 (Sun, 04 Mar 2012) Log Message: ----------- [Ref] Even more refactoring: Removed a lot of duplicate code regaring IT sample / instrument loading. A lot of IT-related code has been moved to a new file, ITTools.cpp. Also smashed a couple of related bugs while I was at it... Modified Paths: -------------- trunk/OpenMPT/mptrack/mptrack.vcproj trunk/OpenMPT/mptrack/mptrack_08.vcproj trunk/OpenMPT/mptrack/mptrack_10.vcxproj trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters trunk/OpenMPT/soundlib/Load_it.cpp trunk/OpenMPT/soundlib/Load_itp.cpp trunk/OpenMPT/soundlib/ModInstrument.h trunk/OpenMPT/soundlib/Sampleio.cpp trunk/OpenMPT/soundlib/Sndfile.h trunk/OpenMPT/soundlib/pattern.cpp Added Paths: ----------- trunk/OpenMPT/soundlib/ITTools.cpp trunk/OpenMPT/soundlib/ITTools.h Removed Paths: ------------- trunk/OpenMPT/soundlib/IT_DEFS.H Modified: trunk/OpenMPT/mptrack/mptrack.vcproj =================================================================== --- trunk/OpenMPT/mptrack/mptrack.vcproj 2012-03-04 23:19:51 UTC (rev 1202) +++ trunk/OpenMPT/mptrack/mptrack.vcproj 2012-03-04 23:39:50 UTC (rev 1203) @@ -753,9 +753,6 @@ RelativePath="InputHandler.h"> </File> <File - RelativePath="..\soundlib\IT_DEFS.H"> - </File> - <File RelativePath=".\KeyConfigDlg.h"> </File> <File @@ -972,6 +969,12 @@ Name="Module Loaders" Filter=""> <File + RelativePath="..\soundlib\ITTools.h"> + </File> + <File + RelativePath="..\soundlib\ITTools.cpp"> + </File> + <File RelativePath="..\soundlib\Load_669.cpp"> </File> <File Modified: trunk/OpenMPT/mptrack/mptrack_08.vcproj =================================================================== --- trunk/OpenMPT/mptrack/mptrack_08.vcproj 2012-03-04 23:19:51 UTC (rev 1202) +++ trunk/OpenMPT/mptrack/mptrack_08.vcproj 2012-03-04 23:39:50 UTC (rev 1203) @@ -999,10 +999,6 @@ > </File> <File - RelativePath="..\soundlib\IT_DEFS.H" - > - </File> - <File RelativePath=".\KeyConfigDlg.h" > </File> @@ -1291,6 +1287,14 @@ Name="Module Loaders" > <File + RelativePath="..\soundlib\ITTools.h" + > + </File> + <File + RelativePath="..\soundlib\ITTools.cpp" + > + </File> + <File RelativePath="..\soundlib\Load_669.cpp" > </File> Modified: trunk/OpenMPT/mptrack/mptrack_10.vcxproj =================================================================== --- trunk/OpenMPT/mptrack/mptrack_10.vcxproj 2012-03-04 23:19:51 UTC (rev 1202) +++ trunk/OpenMPT/mptrack/mptrack_10.vcxproj 2012-03-04 23:39:50 UTC (rev 1203) @@ -169,6 +169,7 @@ <ItemGroup> <ClCompile Include="..\common\misc_util.cpp" /> <ClCompile Include="..\common\Reporting.cpp" /> + <ClCompile Include="..\soundlib\ITTools.cpp" /> <ClCompile Include="..\soundlib\MIDIMacros.cpp" /> <ClCompile Include="..\soundlib\ModInstrument.cpp" /> <ClCompile Include="..\soundlib\ModSample.cpp" /> @@ -326,6 +327,7 @@ <ClInclude Include="..\common\Reporting.h" /> <ClInclude Include="..\common\StringFixer.h" /> <ClInclude Include="..\common\typedefs.h" /> + <ClInclude Include="..\soundlib\ITTools.h" /> <ClInclude Include="..\soundlib\MIDIMacros.h" /> <ClInclude Include="..\soundlib\ModChannel.h" /> <ClInclude Include="..\soundlib\ModInstrument.h" /> @@ -376,7 +378,6 @@ <ClInclude Include="fxp.h" /> <ClInclude Include="globals.h" /> <ClInclude Include="InputHandler.h" /> - <ClInclude Include="..\soundlib\IT_DEFS.H" /> <ClInclude Include="KeyConfigDlg.h" /> <ClInclude Include="mainbar.h" /> <ClInclude Include="MainFrm.h" /> Modified: trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters =================================================================== --- trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters 2012-03-04 23:19:51 UTC (rev 1202) +++ trunk/OpenMPT/mptrack/mptrack_10.vcxproj.filters 2012-03-04 23:39:50 UTC (rev 1203) @@ -436,6 +436,9 @@ <ClCompile Include="..\soundlib\ModSample.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\soundlib\ITTools.cpp"> + <Filter>Module Loaders</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="AbstractVstEditor.h"> @@ -477,9 +480,6 @@ <ClInclude Include="DefaultVstEditor.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="..\soundlib\IT_DEFS.H"> - <Filter>Module Loaders</Filter> - </ClInclude> <ClInclude Include="..\soundlib\Loaders.h"> <Filter>Module Loaders</Filter> </ClInclude> @@ -771,6 +771,9 @@ <ClInclude Include="PSRatioCalc.h"> <Filter>Header Files\Dialogs</Filter> </ClInclude> + <ClInclude Include="..\soundlib\ITTools.h"> + <Filter>Module Loaders</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <None Include="res\bitmap1.bmp"> Added: trunk/OpenMPT/soundlib/ITTools.cpp =================================================================== --- trunk/OpenMPT/soundlib/ITTools.cpp (rev 0) +++ trunk/OpenMPT/soundlib/ITTools.cpp 2012-03-04 23:39:50 UTC (rev 1203) @@ -0,0 +1,634 @@ +/* + * ITTools.cpp + * ----------- + * Purpose: Definition of IT file structures and helper functions + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Loaders.h" +#include "ITTools.h" +#include "../common/StringFixer.h" + + +// Convert all multi-byte numeric values to current platform's endianness or vice versa. +void ITFileHeader::ConvertEndianness() +//------------------------------------ +{ + id = LittleEndian(id); + ordnum = LittleEndianW(ordnum); + insnum = LittleEndianW(insnum); + smpnum = LittleEndianW(smpnum); + patnum = LittleEndianW(patnum); + cwtv = LittleEndianW(cwtv); + cmwt = LittleEndianW(cmwt); + flags = LittleEndianW(flags); + special = LittleEndianW(special); + msglength = LittleEndianW(msglength); + msgoffset = LittleEndian(msgoffset); + reserved = LittleEndian(reserved); +} + + +// Convert OpenMPT's internal envelope format into an IT/MPTM envelope. +void ITEnvelope::ConvertToIT(const InstrumentEnvelope &mptEnv, BYTE envOffset, BYTE envDefault) +//--------------------------------------------------------------------------------------------- +{ + // Envelope Flags + if(mptEnv.dwFlags & ENV_ENABLED) flags |= ITEnvelope::envEnabled; + if(mptEnv.dwFlags & ENV_LOOP) flags |= ITEnvelope::envLoop; + if(mptEnv.dwFlags & ENV_SUSTAIN) flags |= ITEnvelope::envSustain; + if(mptEnv.dwFlags & ENV_CARRY) flags |= ITEnvelope::envCarry; + + // Nodes and Loops + num = (uint8)min(mptEnv.nNodes, 25); + lpb = (uint8)mptEnv.nLoopStart; + lpe = (uint8)mptEnv.nLoopEnd; + slb = (uint8)mptEnv.nSustainStart; + sle = (uint8)mptEnv.nSustainEnd; + + // Envelope Data + if(mptEnv.nNodes > 0) + { + // Attention: Full MPTM envelope is stored in extended instrument properties + for(size_t ev = 0; ev < 25; ev++) + { + data[ev * 3] = mptEnv.Values[ev] - envOffset; + data[ev * 3 + 1] = mptEnv.Ticks[ev] & 0xFF; + data[ev * 3 + 2] = mptEnv.Ticks[ev] >> 8; + } + } else + { + // Fix non-existing envelopes so that they can still be edited in Impulse Tracker. + num = 2; + MemsetZero(data); + data[0] = data[3] = envDefault - envOffset; + data[4] = 10; + } +} + + +// Convert IT/MPTM envelope data into OpenMPT's internal envelope format - To be used by ITInstrToMPT() +void ITEnvelope::ConvertToMPT(InstrumentEnvelope &mptEnv, BYTE envOffset, int maxNodes) const +//------------------------------------------------------------------------------------------- +{ + // Envelope Flags + if(flags & ITEnvelope::envEnabled) mptEnv.dwFlags |= ENV_ENABLED; + if(flags & ITEnvelope::envLoop) mptEnv.dwFlags |= ENV_LOOP; + if(flags & ITEnvelope::envSustain) mptEnv.dwFlags |= ENV_SUSTAIN; + if(flags & ITEnvelope::envCarry) mptEnv.dwFlags |= ENV_CARRY; + + // Nodes and Loops + mptEnv.nNodes = min(num, maxNodes); + mptEnv.nLoopStart = min(lpb, static_cast<uint8>(maxNodes)); + mptEnv.nLoopEnd = Clamp(lpe, mptEnv.nLoopStart, static_cast<uint8>(maxNodes)); + mptEnv.nSustainStart = min(slb, static_cast<uint8>(maxNodes)); + mptEnv.nSustainEnd = Clamp(sle, mptEnv.nSustainStart, static_cast<uint8>(maxNodes)); + + // Envelope Data + // Attention: Full MPTM envelope is stored in extended instrument properties + for(size_t ev = 0; ev < 25; ev++) + { + mptEnv.Values[ev] = data[ev * 3] + envOffset; + mptEnv.Ticks[ev] = (data[ev * 3 + 2] << 8) | (data[ev * 3 + 1]); + if(ev > 0 && ev < num && mptEnv.Ticks[ev] < mptEnv.Ticks[ev - 1]) + { + // Fix broken envelopes... Instruments 2 and 3 in NoGap.it by Werewolf have envelope points where the high byte of envelope nodes is missing. + // NoGap.it was saved with MPT 1.07 or MPT 1.09, which *normally* doesn't do this in IT files. + // However... It turns out that MPT 1.07 omitted the high byte of envelope nodes when saving an XI instrument file, and it looks like + // Instrument 2 and 3 in NoGap.it were loaded from XI files. + mptEnv.Ticks[ev] &= 0xFF; + mptEnv.Ticks[ev] |= (mptEnv.Ticks[ev] & ~0xFF); + if(mptEnv.Ticks[ev] < mptEnv.Ticks[ev - 1]) + { + mptEnv.Ticks[ev] += 0x100; + } + } + } + + // Sanitize envelope + mptEnv.Ticks[0] = 0; +} + + +// Convert an ITOldInstrument to OpenMPT's internal instrument representation. +void ITOldInstrument::ConvertToMPT(ModInstrument &mptIns) const +//------------------------------------------------------------- +{ + // Header + if(id != LittleEndian(ITOldInstrument::magic)) + { + return; + } + + memcpy(mptIns.name, name, 26); + StringFixer::SpaceToNullStringFixed<25>(mptIns.name); + memcpy(mptIns.filename, filename, 13); + StringFixer::SpaceToNullStringFixed<12>(mptIns.filename); + + // Volume / Panning + mptIns.nFadeOut = LittleEndianW(fadeout) << 6; + mptIns.nGlobalVol = 64; + mptIns.nPan = 128; + + // NNA Stuff + mptIns.nNNA = nna; + mptIns.nDCT = dnc; + + // Sample Map + for(size_t i = 0; i < 120; i++) + { + BYTE note = keyboard[i * 2]; + SAMPLEINDEX ins = keyboard[i * 2 + 1]; + if(ins < MAX_SAMPLES) + { + mptIns.Keyboard[i] = ins; + } + if(note < 120) + { + mptIns.NoteMap[i] = note + 1u; + } + else + { + mptIns.NoteMap[i] = static_cast<BYTE>(i + 1); + } + } + + // Volume Envelope Flags + if(flags & ITOldInstrument::envEnabled) mptIns.VolEnv.dwFlags |= ENV_ENABLED; + if(flags & ITOldInstrument::envLoop) mptIns.VolEnv.dwFlags |= ENV_LOOP; + if(flags & ITOldInstrument::envSustain) mptIns.VolEnv.dwFlags |= ENV_SUSTAIN; + + // Volume Envelope Loops + mptIns.VolEnv.nLoopStart = vls; + mptIns.VolEnv.nLoopEnd = vle; + mptIns.VolEnv.nSustainStart = sls; + mptIns.VolEnv.nSustainEnd = sle; + mptIns.VolEnv.nNodes = 25; + + // Volume Envelope Data + for(size_t i = 0; i < 25; i++) + { + if((mptIns.VolEnv.Ticks[i] = nodes[i * 2]) == 0xFF) + { + mptIns.VolEnv.nNodes = i; + break; + } + mptIns.VolEnv.Values[i] = nodes[i * 2 + 1]; + } + + if(max(mptIns.VolEnv.nLoopStart, mptIns.VolEnv.nLoopEnd) >= mptIns.VolEnv.nNodes) mptIns.VolEnv.dwFlags &= ~ENV_LOOP; + if(max(mptIns.VolEnv.nSustainStart, mptIns.VolEnv.nSustainEnd) >= mptIns.VolEnv.nNodes) mptIns.VolEnv.dwFlags &= ~ENV_SUSTAIN; +} + + +// Convert OpenMPT's internal instrument representation to an ITInstrument. +size_t ITInstrument::ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile) +//--------------------------------------------------------------------------------------------------------- +{ + MemsetZero(*this); + + // Header + id = LittleEndian(ITInstrument::magic); + trkvers = LittleEndianW(0x0214); + + memcpy(filename, mptIns.filename, 13); + StringFixer::FixNullString(filename); + memcpy(name, mptIns.name, 26); + StringFixer::FixNullString(name); + + // Volume / Panning + fadeout = LittleEndianW(static_cast<uint16>(min(mptIns.nFadeOut >> 5, 256))); + gbv = static_cast<uint8>(mptIns.nGlobalVol * 2); + dfp = static_cast<uint8>(mptIns.nPan >> 2); + if(!(mptIns.dwFlags & INS_SETPANNING)) dfp |= ITInstrument::ignorePanning; + + // Random Variation + rv = min(mptIns.nVolSwing, 100); + rp = min(mptIns.nPanSwing, 64); + + // NNA Stuff + nna = mptIns.nNNA; + dct = (mptIns.nDCT < DCT_PLUGIN || !compatExport) ? mptIns.nDCT : DCT_NONE; + dca = mptIns.nDNA; + + // Pitch / Pan Separation + pps = mptIns.nPPS; + ppc = mptIns.nPPC; + + // Filter Stuff + ifc = mptIns.GetCutoff() | (mptIns.IsCutoffEnabled() ? ITInstrument::enableCutoff : 0x00); + ifr = mptIns.GetResonance() | (mptIns.IsResonanceEnabled() ? ITInstrument::enableResonance : 0x00); + + // MIDI Setup + mbank = mptIns.wMidiBank; + mpr = mptIns.nMidiProgram; + if(mptIns.nMidiChannel || mptIns.nMixPlug == 0 || compatExport) + { + // Default. Prefer MIDI channel over mixplug to keep the semantics intact. + mch = mptIns.nMidiChannel; + } else + { + // Keep compatibility with MPT 1.16's instrument format if possible, as XMPlay / BASS also uses this. + mch = mptIns.nMixPlug + 128; + } + + // Sample Map + nos = 0; + vector<bool> smpcount(sndFile.GetNumSamples(), false); + for(size_t i = 0; i < 120; i++) + { + keyboard[i * 2] = (mptIns.NoteMap[i] >= NOTE_MIN && mptIns.NoteMap[i] <= NOTE_MAX) ? (mptIns.NoteMap[i] - NOTE_MIN) : static_cast<uint8>(i); + + const SAMPLEINDEX smp = mptIns.Keyboard[i]; + if(smp < MAX_SAMPLES && smp < 256) + { + keyboard[i * 2 + 1] = static_cast<uint8>(smp); + + if(smp && smp <= sndFile.GetNumSamples() && !smpcount[smp - 1]) + { + // We haven't considered this sample yet. Update number of samples. + smpcount[smp - 1] = true; + nos++; + } + } + } + + // Writing Volume envelope + volenv.ConvertToIT(mptIns.VolEnv, 0, 64); + // Writing Panning envelope + panenv.ConvertToIT(mptIns.PanEnv, 32, 32); + // Writing Pitch Envelope + pitchenv.ConvertToIT(mptIns.PitchEnv, 32, 32); + if(mptIns.PitchEnv.dwFlags & ENV_FILTER) pitchenv.flags |= ITEnvelope::envFilter; + + return sizeof(ITInstrument); +} + + +// Convert an ITInstrument to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read. +size_t ITInstrument::ConvertToMPT(ModInstrument &mptIns, MODTYPE modFormat) const +//------------------------------------------------------------------------------- +{ + if(id != LittleEndian(ITInstrument::magic)) + { + return 0; + } + + memcpy(mptIns.name, name, 26); + StringFixer::SpaceToNullStringFixed<25>(mptIns.name); + memcpy(mptIns.filename, filename, 13); + StringFixer::SpaceToNullStringFixed<12>(mptIns.filename); + + // Volume / Panning + mptIns.nFadeOut = LittleEndianW(fadeout) << 5; + mptIns.nGlobalVol = gbv / 2; + LimitMax(mptIns.nGlobalVol, 64u); + mptIns.nPan = (dfp & 0x7F) << 2; + if(mptIns.nPan > 256) mptIns.nPan = 128; + if(!(dfp & ITInstrument::ignorePanning)) mptIns.dwFlags |= INS_SETPANNING; + + // Random Variation + mptIns.nVolSwing = min(rv, 100); + mptIns.nPanSwing = min(rp, 64); + + // NNA Stuff + mptIns.nNNA = nna; + mptIns.nDCT = dct; + mptIns.nDNA = dca; + + // Pitch / Pan Separation + mptIns.nPPS = pps; + mptIns.nPPC = ppc; + + // Filter Stuff + mptIns.SetCutoff(ifc & 0x7F, (ifc & ITInstrument::enableCutoff) != 0); + mptIns.SetResonance(ifr & 0x7F, (ifr & ITInstrument::enableResonance) != 0); + + // MIDI Setup + if(mpr <= 128) + { + mptIns.nMidiProgram = mpr; + } + mptIns.nMidiChannel = mch; + if(mptIns.nMidiChannel >= 128) + { + // Handle old format where MIDI channel and Plugin index are stored in the same variable + mptIns.nMixPlug = mptIns.nMidiChannel - 128; + mptIns.nMidiChannel = 0; + } + if(mbank <= 128) + { + mptIns.wMidiBank = LittleEndianW(mbank); + } + + // Envelope point count. Limited to 25 in IT format. + const int maxNodes = (modFormat & MOD_TYPE_MPT) ? MAX_ENVPOINTS : 25; + + // Volume Envelope + volenv.ConvertToMPT(mptIns.VolEnv, 0, maxNodes); + // Panning Envelope + panenv.ConvertToMPT(mptIns.PanEnv, 32, maxNodes); + // Pitch Envelope + pitchenv.ConvertToMPT(mptIns.PitchEnv, 32, maxNodes); + if(pitchenv.flags & ITEnvelope::envFilter) mptIns.PitchEnv.dwFlags |= ENV_FILTER; + + // Sample Map + for(size_t i = 0; i < 120; i++) + { + BYTE note = keyboard[i * 2]; + SAMPLEINDEX ins = keyboard[i * 2 + 1]; + if(ins < MAX_SAMPLES) + { + mptIns.Keyboard[i] = ins; + } + if(note < 120) + { + mptIns.NoteMap[i] = note + 1u; + } + else + { + mptIns.NoteMap[i] = static_cast<BYTE>(i + 1); + } + } + + return sizeof(ITInstrument); +} + + +// Convert OpenMPT's internal instrument representation to an ITInstrumentEx. Returns true if instrument extension is actually necessary. +size_t ITInstrumentEx::ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile) +//----------------------------------------------------------------------------------------------------------- +{ + size_t instSize = iti.ConvertToIT(mptIns, compatExport, sndFile); + + if(compatExport) + { + return instSize; + } + + // Sample Map + bool usedExtension = false; + iti.nos = 0; + vector<bool>smpcount(sndFile.GetNumSamples(), false); + for(size_t i = 0; i < 120; i++) + { + const SAMPLEINDEX smp = mptIns.Keyboard[i]; + if(smp < MAX_SAMPLES) + { + if(smp >= 256) + { + // We need to save the upper byte for this sample index. + iti.keyboard[i * 2 + 1] = static_cast<uint8>(smp & 0xFF); + keyboardhi[i] = static_cast<uint8>(smp >> 8); + usedExtension = true; + } + + if(smp && smp <= sndFile.GetNumSamples() && !smpcount[smp - 1]) + { + // We haven't considered this sample yet. Update number of samples. + smpcount[smp - 1] = true; + iti.nos++; + } + } + } + + if(usedExtension) + { + // If we actually had to extend the sample map, update the magic bytes and instrument size. + iti.dummy = LittleEndian(ITInstrumentEx::mptx); + instSize = sizeof(ITInstrumentEx); + } + + return instSize; +} + + +// Convert an ITInstrumentEx to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read. +size_t ITInstrumentEx::ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const +//-------------------------------------------------------------------------------- +{ + size_t insSize = iti.ConvertToMPT(mptIns, fromType); + + // Is this actually an extended instrument? + if(insSize == 0 || iti.dummy != LittleEndian(ITInstrumentEx::mptx)) + { + return insSize; + } + + // Olivier's MPT Instrument Extension + for(size_t i = 0; i < 120; i++) + { + mptIns.Keyboard[i] |= ((SAMPLEINDEX)keyboardhi[i] << 8); + } + + return sizeof(ITInstrumentEx); +} + + +// Convert OpenMPT's internal sample representation to an ITSample. +void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType) +//------------------------------------------------------------------- +{ + MemsetZero(*this); + + // Header + id = LittleEndian(ITSample::magic); + + memcpy(filename, mptSmp.filename, 13); + StringFixer::FixNullString(filename); + //memcpy(name, m_szNames[nsmp], 26); + //StringFixer::FixNullString(name); + + // Volume / Panning + gvl = static_cast<BYTE>(mptSmp.nGlobalVol); + vol = static_cast<BYTE>(mptSmp.nVolume / 4); + dfp = static_cast<BYTE>(mptSmp.nPan / 4); + if(mptSmp.uFlags & CHN_PANNING) dfp |= ITSample::enablePanning; + + // Sample Format / Loop Flags + if(mptSmp.nLength && mptSmp.pSample) + { + flags = ITSample::sampleDataPresent; + if(mptSmp.uFlags & CHN_LOOP) flags |= ITSample::sampleLoop; + if(mptSmp.uFlags & CHN_SUSTAINLOOP) flags |= ITSample::sampleSustain; + if(mptSmp.uFlags & CHN_PINGPONGLOOP) flags |= ITSample::sampleBidiLoop; + if(mptSmp.uFlags & CHN_PINGPONGSUSTAIN) flags |= ITSample::sampleBidiSustain; + + if(mptSmp.uFlags & CHN_STEREO) + { + flags |= ITSample::sampleStereo; + } + if(mptSmp.uFlags & CHN_16BIT) + { + flags |= ITSample::sample16Bit; + } + cvt = ITSample::cvtSignedSample; + } + else + { + flags = 0x00; + } + + // Frequency + C5Speed = LittleEndian(mptSmp.nC5Speed ? mptSmp.nC5Speed : 8363); + + // Size and loops + length = LittleEndian(mptSmp.nLength); + loopbegin = LittleEndian(mptSmp.nLoopStart); + loopend = LittleEndian(mptSmp.nLoopEnd); + susloopbegin = LittleEndian(mptSmp.nSustainStart); + susloopend = LittleEndian(mptSmp.nSustainEnd); + + // Auto Vibrato settings + static const uint8 autovibxm2it[8] = { 0, 2, 4, 1, 3, 0, 0, 0 }; // OpenMPT VibratoType -> IT Vibrato + vit = autovibxm2it[mptSmp.nVibType & 7]; + vis = min(mptSmp.nVibRate, 64); + vid = min(mptSmp.nVibDepth, 32); + vir = min(mptSmp.nVibSweep, 255); + + if((vid | vis) != 0 && (fromType & MOD_TYPE_XM)) + { + // Sweep is upside down in XM + vir = 255 - vir; + } +} + + +// Convert an ITSample to OpenMPT's internal sample representation. +size_t ITSample::ConvertToMPT(ModSample &mptSmp) const +//---------------------------------------------------- +{ + if(id != LittleEndian(ITSample::magic)) + { + return 0; + } + + memcpy(mptSmp.filename, filename, 13); + StringFixer::SpaceToNullStringFixed<12>(mptSmp.filename); + + mptSmp.uFlags = 0; + + // Volume / Panning + mptSmp.nVolume = vol * 4; + LimitMax(mptSmp.nVolume, WORD(256)); + mptSmp.nGlobalVol = gvl; + LimitMax(mptSmp.nGlobalVol, WORD(64)); + mptSmp.nPan = (dfp & 0x7F) * 4; + LimitMax(mptSmp.nPan, WORD(256)); + if(dfp & ITSample::enablePanning) mptSmp.uFlags |= CHN_PANNING; + + // Loop Flags + if(flags & ITSample::sampleLoop) mptSmp.uFlags |= CHN_LOOP; + if(flags & ITSample::sampleSustain) mptSmp.uFlags |= CHN_SUSTAINLOOP; + if(flags & ITSample::sampleBidiLoop) mptSmp.uFlags |= CHN_PINGPONGLOOP; + if(flags & ITSample::sampleBidiSustain) mptSmp.uFlags |= CHN_PINGPONGSUSTAIN; + + // Frequency + mptSmp.nC5Speed = LittleEndian(C5Speed); + if(!mptSmp.nC5Speed) mptSmp.nC5Speed = 8363; + if(mptSmp.nC5Speed < 256) mptSmp.nC5Speed = 256; + + // Size and loops + mptSmp.nLength = min(LittleEndian(length), MAX_SAMPLE_LENGTH); + mptSmp.nLoopStart = LittleEndian(loopbegin); + mptSmp.nLoopEnd = LittleEndian(loopend); + mptSmp.nSustainStart = LittleEndian(susloopbegin); + mptSmp.nSustainEnd = LittleEndian(susloopend); + + // Auto Vibrato settings + static const uint8 autovibit2xm[8] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_RANDOM, VIB_RAMP_UP, 0, 0, 0 }; // IT Vibrato -> OpenMPT VibratoType + mptSmp.nVibType = autovibit2xm[vit & 7]; + mptSmp.nVibRate = vis; + mptSmp.nVibDepth = vid & 0x7F; + mptSmp.nVibSweep = vir; + + return mptSmp.nLength ? LittleEndian(samplepointer) : 0; +} + + +// Retrieve the internal sample format flags for this instrument. +UINT ITSample::GetSampleFormat(uint16 cwtv) const +//----------------------------------------------- +{ + UINT mptFlags = (cvt & ITSample::cvtSignedSample) ? RS_PCM8S : RS_PCM8U; + + if(flags & ITSample::sample16Bit) + { + // 16-Bit sample + mptFlags += 5; + + // Some old version of IT didn't clear the stereo flag when importing samples. Luckily, all other trackers are identifying as IT 2.14+, so let's check for old IT versions. + if((flags & ITSample::sampleStereo) && cwtv >= 0x214) + { + mptFlags |= RSF_STEREO; + } + + if(flags & ITSample::sampleCompressed) + { + // IT 2.14 16-bit packed sample + mptFlags = (cvt & ITSample::cvtIT215Compression) ? RS_IT21516 : RS_IT21416; + } + } else + { + // 8-Bit sample + // Some old version of IT didn't clear the stereo flag when importing samples. Luckily, all other trackers are identifying as IT 2.14+, so let's check for old IT versions. + if((flags & ITSample::sampleStereo) && cwtv >= 0x214) + { + mptFlags |= RSF_STEREO; + } + + if(cvt == ITSample::cvtADPCMSample) + { + mptFlags = RS_ADPCM4; + } else if(flags & ITSample::sampleCompressed) + { + // IT 2.14 8-bit packed sample? + mptFlags = (cvt & ITSample::cvtIT215Compression) ? RS_IT2158 : RS_IT2148; + } + } + + return mptFlags; +} + + +#ifdef MODPLUG_TRACKER + +// Convert an ITHistoryStruct to OpenMPT's internal edit history representation +void ITHistoryStruct::ConvertToMPT(FileHistory &mptHistory) const +//--------------------------------------------------------------- +{ + uint16 date = LittleEndianW(fatdate); + uint16 time = LittleEndianW(fattime); + uint32 run = LittleEndian(runtime); + + // Decode FAT date and time + MemsetZero(mptHistory.loadDate); + mptHistory.loadDate.tm_year = ((date >> 9) & 0x7F) + 80; + mptHistory.loadDate.tm_mon = Clamp((date >> 5) & 0x0F, 1, 12) - 1; + mptHistory.loadDate.tm_mday = Clamp(date & 0x1F, 1, 31); + mptHistory.loadDate.tm_hour = Clamp((time >> 11) & 0x1F, 0, 23); + mptHistory.loadDate.tm_min = Clamp((time >> 5) & 0x3F, 0, 59); + mptHistory.loadDate.tm_sec = Clamp((time & 0x1F) * 2, 0, 59); + mptHistory.openTime = static_cast<uint32>(run * (HISTORY_TIMER_PRECISION / 18.2f)); +} + + +// Convert OpenMPT's internal edit history representation to an ITHistoryStruct +void ITHistoryStruct::ConvertToIT(const FileHistory &mptHistory) +//-------------------------------------------------------------- +{ + // Create FAT file dates + fatdate = static_cast<uint16>(mptHistory.loadDate.tm_mday | ((mptHistory.loadDate.tm_mon + 1) << 5) | ((mptHistory.loadDate.tm_year - 80) << 9)); + fattime = static_cast<uint16>((mptHistory.loadDate.tm_sec / 2) | (mptHistory.loadDate.tm_min << 5) | (mptHistory.loadDate.tm_hour << 11)); + runtime = static_cast<uint32>(mptHistory.openTime * (18.2f / HISTORY_TIMER_PRECISION)); + + fatdate = LittleEndianW(fatdate); + fattime = LittleEndianW(fattime); + runtime = LittleEndian(runtime); +} + +#endif // MODPLUG_TRACKER Copied: trunk/OpenMPT/soundlib/ITTools.h (from rev 1194, trunk/OpenMPT/soundlib/IT_DEFS.H) =================================================================== --- trunk/OpenMPT/soundlib/ITTools.h (rev 0) +++ trunk/OpenMPT/soundlib/ITTools.h 2012-03-04 23:39:50 UTC (rev 1203) @@ -0,0 +1,326 @@ +/* + * ITTools.h + * --------- + * Purpose: Definition of IT file structures and helper functions + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#ifdef MODPLUG_TRACKER +#include "../mptrack/Moddoc.h" +#endif // MODPLUG_TRACKER + +#pragma pack(push, 1) + +struct ITFileHeader +{ + // Magic Bytes + enum Magic + { + itMagic = 0x4D504D49, // "IMPM" IT Header Magic Bytes + mptmMagic = 0x2E6D7074, // "tpm." Old MPTM header magic bytes + omptMagic = 0x54504D4F, // "OMPT" Magic Bytes for non-standard OpenMPT IT files + chibiMagic = 0x49424843, // "CHBI" Magic Bytes in the IT header to identify ChibiTracker + }; + + // Header Flags + enum ITHeaderFlags + { + useStereoPlayback = 0x01, + vol0Optimisations = 0x02, + instrumentMode = 0x04, + linearSlides = 0x08, + itOldEffects = 0x10, + itCompatGxx = 0x20, + useMIDIPitchController = 0x40, + reqEmbeddedMIDIConfig = 0x80, + extendedFilterRange = 0x1000, + }; + + // Special Flags + enum ITHeaderSpecialFlags + { + embedSongMessage = 0x01, + embedEditHistory = 0x02, + embedPatternHighlights = 0x04, + embedMIDIConfiguration = 0x08, + }; + + uint32 id; // Magic Bytes (IMPM) + char songname[26]; // Song Name (duh) + uint8 highlight_minor; // Rows per Beat highlight + uint8 highlight_major; // Rows per Measure highlight + uint16 ordnum; // Number of Orders + uint16 insnum; // Number of Instruments + uint16 smpnum; // Number of Samples + uint16 patnum; // Number of Patterns + uint16 cwtv; // "Made With" Tracker + uint16 cmwt; // "Compatible With" Tracker + uint16 flags; // Header Flags + uint16 special; // Special Flags, for embedding extra information + uint8 globalvol; // Global Volume (0...128) + uint8 mv; // Master Volume (0...128), referred to as Sample Volume in OpenMPT + uint8 speed; // Initial Speed (1...255) + uint8 tempo; // Initial Tempo (31...255) + uint8 sep; // Pan Separation (0...128) + uint8 pwd; // Pitch Wheel Depth + uint16 msglength; // Length of Song Message + uint32 msgoffset; // Offset of Song Message in File + uint32 reserved; // ChibiTracker writes "CHBI" here. OpenMPT writes "OMPT" here in some cases, see Load_it.cpp + uint8 chnpan[64]; // Initial Channel Panning + uint8 chnvol[64]; // Initial Channel Volume + + // Convert all multi-byte numeric values to current platform's endianness or vice versa. + void ConvertEndianness(); +}; + +STATIC_ASSERT(sizeof(ITFileHeader) == 192); + + +struct ITEnvelope +{ + // Envelope Flags + enum ITEnvelopeFlags + { + envEnabled = 0x01, + envLoop = 0x02, + envSustain = 0x04, + envCarry = 0x08, + envFilter = 0x80, + }; + + uint8 flags; // Envelope Flags + uint8 num; // Number of Envelope Nodes + uint8 lpb; // Loop Start + uint8 lpe; // Loop End + uint8 slb; // Sustain Start + uint8 sle; // Sustain End + uint8 data[25 * 3]; // Envelope Node Positions / Values + uint8 reserved; // Reserved + + // Convert OpenMPT's internal envelope format to an IT/MPTM envelope. + void ConvertToIT(const InstrumentEnvelope &mptEnv, BYTE envOffset, BYTE envDefault); + // Convert IT/MPTM envelope data into OpenMPT's internal envelope format - To be used by ITInstrToMPT() + void ConvertToMPT(InstrumentEnvelope &mptEnv, BYTE envOffset, int maxNodes) const; +}; + +STATIC_ASSERT(sizeof(ITEnvelope) == 82); + + +// Old Impulse Instrument Format (cmwt < 0x200) +struct ITOldInstrument +{ + // Magic Bytes + enum Magic + { + magic = 0x49504D49, // "IMPI" IT Instrument Header Magic Bytes + }; + + enum ITOldInstrFlags + { + envEnabled = 0x01, + envLoop = 0x02, + envSustain = 0x04, + }; + + uint32 id; // Magic Bytes (IMPI) + char filename[13]; // DOS Filename + uint8 flags; // Volume Envelope Flags + uint8 vls; // Envelope Loop Start + uint8 vle; // Envelope Loop End + uint8 sls; // Envelope Sustain Start + uint8 sle; // Envelope Sustain End + char reserved1[2]; // Reserved + uint16 fadeout; // Instrument Fadeout (0...128) + uint8 nna; // New Note Action + uint8 dnc; // Duplicate Note Check Type + uint16 trkvers; // Tracker ID + uint8 nos; // Number of embedded samples + char reserved2; // Reserved + char name[26]; // Instrument Name + char reserved3[6]; // Even more reserved bytes + uint8 keyboard[240]; // Sample / Transpose map + uint8 volenv[200]; // This appears to be a pre-computed (interpolated) version of the volume envelope data found below. + uint8 nodes[25 * 2]; // Volume Envelope Node Positions / Values + + // Convert an ITOldInstrument to OpenMPT's internal instrument representation. + void ConvertToMPT(ModInstrument &mptIns) const; +}; + +STATIC_ASSERT(sizeof(ITOldInstrument) == 554); + + +// Impulse Instrument Format +struct ITInstrument +{ + // Magic Bytes + enum Magic + { + magic = 0x49504D49, // "IMPI" IT Instrument Header Magic Bytes + }; + + enum ITInstrumentFlags + { + ignorePanning = 0x80, + enableCutoff = 0x80, + enableResonance = 0x80, + }; + + uint32 id; // Magic Bytes (IMPI) + char filename[13]; // DOS Filename + uint8 nna; // New Note Action + uint8 dct; // Duplicate Note Check Type + uint8 dca; // Duplicate Note Check Action + uint16 fadeout; // Instrument Fadeout (0...128) + int8 pps; // Pitch/Pan Separatation + uint8 ppc; // Pitch/Pan Centre + uint8 gbv; // Global Volume + uint8 dfp; // Panning + uint8 rv; // Vol Swing + uint8 rp; // Pan Swing + uint16 trkvers; // Tracker ID + uint8 nos; // Number of embedded samples + char reserved1; // Reserved + char name[26]; // Instrument Name + uint8 ifc; // Filter Cutoff + uint8 ifr; // Filter Resonance + uint8 mch; // MIDI Channel + uint8 mpr; // MIDI Program + uint16 mbank; // MIDI Bank + uint8 keyboard[240]; // Sample / Transpose map + ITEnvelope volenv; // Volume Envelope + ITEnvelope panenv; // Pan Envelope + ITEnvelope pitchenv; // Pitch / Filter Envelope + uint32 dummy; // IT saves some additional padding bytes to match the size of the old instrument format for simplified loading. We use them for some hacks. + + // Convert OpenMPT's internal instrument representation to an ITInstrument. Returns amount of bytes that need to be written. + size_t ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile); + // Convert an ITInstrument to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read. + size_t ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const; +}; + +STATIC_ASSERT(sizeof(ITInstrument) == 554); + + +// MPT IT Instrument Extension +struct ITInstrumentEx +{ + enum Magic + { + mptx = 0x5854504D, // "MPTX" Extended Instrument Header Magic Bytes + }; + + ITInstrument iti; // Normal IT Instrument + uint8 keyboardhi[120]; // High Byte of Sample map + + // Convert OpenMPT's internal instrument representation to an ITInstrumentEx. Returns amount of bytes that need to be written. + size_t ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile); + // Convert an ITInstrumentEx to OpenMPT's internal instrument representation. Returns size of the instrument data that has been read. + size_t ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const; +}; + +STATIC_ASSERT(sizeof(ITInstrumentEx) == sizeof(ITInstrument) + 120); + + +// IT Sample Format +struct ITSample +{ + // Magic Bytes + enum Magic + { + magic = 0x53504D49, // "IMPS" IT Sample Header Magic Bytes + }; + + enum ITSampleFlags + { + sampleDataPresent = 0x01, + sample16Bit = 0x02, + sampleStereo = 0x04, + sampleCompressed = 0x08, + sampleLoop = 0x10, + sampleSustain = 0x20, + sampleBidiLoop = 0x40, + sampleBidiSustain = 0x80, + + enablePanning = 0x80, + + cvtSignedSample = 0x01, + cvtIT215Compression = 0x04, + cvtADPCMSample = 0xFF + }; + + uint32 id; // Magic Bytes (IMPS) + char filename[13]; // DOS Filename + uint8 gvl; // Global Volume + uint8 flags; // Sample Flags + uint8 vol; // Default Volume + char name[26]; // Sample Name + uint8 cvt; // Sample Import Format + uint8 dfp; // Sample Panning + uint32 length; // Sample Length (in samples) + uint32 loopbegin; // Sample Loop Begin (in samples) + uint32 loopend; // Sample Loop End (in samples) + uint32 C5Speed; // C-5 frequency + uint32 susloopbegin; // Sample Sustain Begin (in samples) + uint32 susloopend; // Sample Sustain End (in samples) + uint32 samplepointer; // Pointer to sample data + uint8 vis; // Auto-Vibrato Rate (called Sweep in IT) + uint8 vid; // Auto-Vibrato Depth + uint8 vir; // Auto-Vibrato Sweep (called Rate in IT) + uint8 vit; // Auto-Vibrato Type + + // Convert OpenMPT's internal sample representation to an ITSample. + void ConvertToIT(const ModSample &mptSmp, MODTYPE fromType); + // Convert an ITSample to OpenMPT's internal sample representation. + size_t ConvertToMPT(ModSample &mptSmp) const; + // Retrieve the internal sample format flags for this instrument. + UINT GetSampleFormat(uint16 cwtv = 0x214) const; +}; + +STATIC_ASSERT(sizeof(ITSample) == 80); + + +// IT Header extension: Save history +struct ITHistoryStruct +{ + uint16 fatdate; // DOS / FAT date when the file was opened / created in the editor. For details, read http://msdn.microsoft.com/en-us/library/ms724247(VS.85).aspx + uint16 fattime; // DOS / FAT time when the file was opened / created in the editor. + uint32 runtime; // The time how long the file was open in the editor, in 1/18.2th seconds. (= ticks of the DOS timer) + +#ifdef MODPLUG_TRACKER + + // Convert an ITHistoryStruct to OpenMPT's internal edit history representation + void ConvertToMPT(FileHistory &mptHistory) const; + // Convert OpenMPT's internal edit history representation to an ITHistoryStruct + void ConvertToIT(const FileHistory &mptHistory); + +#endif // MODPLUG_TRACKER + +}; + +STATIC_ASSERT(sizeof(ITHistoryStruct) == 8); + +#pragma pack(pop) + +// MPT stuff +enum MPTHackMagic +{ + magicPatternNames = 0x4D414E50, // "PNAM" pattern names + magicChannelNames = 0x4D414E43, // "CNAM" channel names +}; + +enum IT_ReaderBitMasks +{ + // pattern row parsing, the channel data is read to obtain + // number of channels active in the pattern. These bit masks are + // to blank out sections of the byte of data being read. + + IT_bitmask_patternChanField_c = 0x7f, + IT_bitmask_patternChanMask_c = 0x3f, + IT_bitmask_patternChanEnabled_c = 0x80, + IT_bitmask_patternChanUsed_c = 0x0f +}; Deleted: trunk/OpenMPT/soundlib/IT_DEFS.H =================================================================== --- trunk/OpenMPT/soundlib/IT_DEFS.H 2012-03-04 23:19:51 UTC (rev 1202) +++ trunk/OpenMPT/soundlib/IT_DEFS.H 2012-03-04 23:39:50 UTC (rev 1203) @@ -1,186 +0,0 @@ -/* - * IT_DEFS.H - * --------- - * Purpose: IT file structures - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#pragma once - -#pragma pack(push, 1) - -struct ITFILEHEADER -{ - DWORD id; // 0x4D504D49 - CHAR songname[26]; - BYTE highlight_minor; - BYTE highlight_major; - WORD ordnum; - WORD insnum; - WORD smpnum; - WORD patnum; - WORD cwtv; // "made with" tracker - WORD cmwt; // "compatible with" tracker - WORD flags; - WORD special; - BYTE globalvol; - BYTE mv; // master volume - BYTE speed; - BYTE tempo; - BYTE sep; // panning separation (0...128) - BYTE pwd; // pitch wheel depth - WORD msglength; - DWORD msgoffset; - DWORD reserved; // ChibiTracker writes "CHBI" here. OpenMPT writes "OMPT" here in some cases, see Load_it.cpp - BYTE chnpan[64]; - BYTE chnvol[64]; -}; - - -struct ITENVELOPE -{ - BYTE flags; - BYTE num; - BYTE lpb; - BYTE lpe; - BYTE slb; - BYTE sle; - BYTE data[25*3]; - BYTE reserved; -}; - -// Old Impulse Instrument Format (cmwt < 0x200) -struct ITOLDINSTRUMENT -{ - DWORD id; // IMPI = 0x49504D49 - CHAR filename[12]; // DOS file name - BYTE zero; - BYTE flags; - BYTE vls; - BYTE vle; - BYTE sls; - BYTE sle; - WORD reserved1; - WORD fadeout; - BYTE nna; - BYTE dnc; - WORD trkvers; - BYTE nos; - BYTE reserved2; - CHAR name[26]; - WORD reserved3[3]; - BYTE keyboard[240]; - BYTE volenv[200]; - BYTE nodes[50]; -}; - - -// Impulse Instrument Format -struct ITINSTRUMENT -{ - DWORD id; - CHAR filename[12]; - BYTE zero; - BYTE nna; - BYTE dct; - BYTE dca; - WORD fadeout; - signed char pps; - BYTE ppc; - BYTE gbv; - BYTE dfp; - BYTE rv; - BYTE rp; - WORD trkvers; - BYTE nos; - BYTE reserved1; - CHAR name[26]; - BYTE ifc; - BYTE ifr; - BYTE mch; - BYTE mpr; - WORD mbank; - BYTE keyboard[240]; - ITENVELOPE volenv; - ITENVELOPE panenv; - ITENVELOPE pitchenv; - BYTE dummy[4]; // was 7, but IT v2.17 saves 554 bytes -}; - - -// MPT IT Instrument Extension -struct ITINSTRUMENTEX -{ - ITINSTRUMENT iti; - BYTE keyboardhi[120]; -}; - - -// IT Sample Format -struct ITSAMPLESTRUCT -{ - DWORD id; // 0x53504D49 - CHAR filename[12]; - BYTE zero; - BYTE gvl; - BYTE flags; - BYTE vol; - CHAR name[26]; - BYTE cvt; - BYTE dfp; - DWORD length; - DWORD loopbegin; - DWORD loopend; - DWORD C5Speed; - DWORD susloopbegin; - DWORD susloopend; - DWORD samplepointer; - BYTE vis; - BYTE vid; - BYTE vir; - BYTE vit; -}; - - -// IT Header extension: Save history -struct ITHISTORYSTRUCT -{ - uint16 fatdate; // DOS / FAT date when the file was opened / created in the editor. For details, read http://msdn.microsoft.com/en-us/library/ms724247(VS.85).aspx - uint16 fattime; // DOS / FAT time when the file was opened / created in the editor. - uint32 runtime; // The time how long the file was open in the editor, in 1/18.2th seconds. (= ticks of the DOS timer) -}; - -#pragma pack(pop) - -extern BYTE autovibit2xm[8]; -extern BYTE autovibxm2it[8]; - -// Impulse Tracker identifcators -#define IT_IMPM 0x4D504D49 // "IMPM" IT header magic bytes -#define IT_IMPS 0x53504D49 // "IMPS" IT sample header magic bytes -#define IT_IMPI 0x49504D49 // "IMPI" IT instrument header magic bytes - -// Identificators by other trackers -#define IT_MPTM 0x2E6D7074 // "tpm." old MPTM header magic bytes -#define IT_OMPT 0x54504D4F // "OMPT" magic bytes for non-standard OpenMPT IT files -#define IT_CHBI 0x49424843 // "CHBI" magic bytes in the IT header to identify ChibiTracker - -// MPT stuff -#define IT_PNAM 0x4D414E50 // "PNAM" pattern names -#define IT_CNAM 0x4D414E43 // "CNAM" channel names - -enum IT_ReaderBitMasks -{ - // pattern row parsing, the channel data is read to obtain - // number of channels active in the pattern. These bit masks are - // to blank out sections of the byte of data being read. - - IT_bitmask_patternChanField_c = 0x7f, - IT_bitmask_patternChanMask_c = 0x3f, - IT_bitmask_patternChanEnabled_c = 0x80, - IT_bitmask_patternChanUsed_c = 0x0f - -}; Modified: trunk/OpenMPT/soundlib/Load_it.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_it.cpp 2012-03-04 23:19:51 UTC (rev 1202) +++ trunk/OpenMPT/soundlib/Load_it.cpp 2012-03-04 23:39:50 UTC (rev 1203) @@ -11,7 +11,6 @@ #include "stdafx.h" #include "Loaders.h" -#include "IT_DEFS.H" #include "tuningcollection.h" #include "../mptrack/moddoc.h" #include "../mptrack/serialization_utils.h" @@ -19,6 +18,7 @@ #include <strstream> #include <list> #include "../mptrack/version.h" +#include "ITTools.h" #define str_tooMuchPatternData (GetStrI18N((_TEXT("Warning: File format limit was reached. Some pattern data may not get written to file.")))) #define str_pattern (GetStrI18N((_TEXT("pattern")))) @@ -223,14 +223,6 @@ #pragma warning(disable:4244) //conversion from 'type1' to 'type2', possible loss of data -// IT Vibrato -> VibratoType -BYTE autovibit2xm[8] = -{ VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_RANDOM, VIB_RAMP_UP, 0, 0, 0 }; - -// VibratoType -> Vibrato -BYTE autovibxm2it[8] = -{ 0, 2, 4, 1, 3, 0, 0, 0 }; - ////////////////////////////////////////////////////////// // Impulse Tracker IT file support @@ -242,235 +234,35 @@ } -// Convert MPT's internal envelope format into an IT/MPTM envelope. -void MPTEnvToIT(const InstrumentEnvelope *mptEnv, ITENVELOPE *itEnv, const BYTE envOffset, const BYTE envDefault) -//--------------------------------------------------------------------------------------------------------------- +size_t CSoundFile::ITInstrToMPT(const void *p, ModInstrument *pIns, UINT trkvers, size_t memLength) +//------------------------------------------------------------------------------------------------- { - if(mptEnv->dwFlags & ENV_ENABLED) itEnv->flags |= 1; - if(mptEnv->dwFlags & ENV_LOOP) itEnv->flags |= 2; - if(mptEnv->dwFlags & ENV_SUSTAIN) itEnv->flags |= 4; - if(mptEnv->dwFlags & ENV_CARRY) itEnv->flags |= 8; - itEnv->num = (BYTE)min(mptEnv->nNodes, 25); - itEnv->lpb = (BYTE)mptEnv->nLoopStart; - itEnv->lpe = (BYTE)mptEnv->nLoopEnd; - itEnv->slb = (BYTE)mptEnv->nSustainStart; - itEnv->sle = (BYTE)mptEnv->nSustainEnd; - - if(mptEnv->nNodes > 0) + if(trkvers < 0x0200) { - // Attention: Full MPTM envelope is stored in extended instrument properties - for(size_t ev = 0; ev < 25; ev++) - { - itEnv->data[ev * 3] = mptEnv->Values[ev] - envOffset; - itEnv->data[ev * 3 + 1] = mptEnv->Ticks[ev] & 0xFF; - itEnv->data[ev * 3 + 2] = mptEnv->Ticks[ev] >> 8; - } + // Load old format (IT 1.xx) instrument + const ITOldInstrument *itIns = reinterpret_cast<const ITOldInstrument *>(p); + itIns->ConvertToMPT(*pIns); + return sizeof(ITOldInstrument); } else { - // Fix non-existing envelopes so that they can still be edited in Impulse Tracker. - itEnv->num = 2; - MemsetZero(itEnv->data); - itEnv->data[0] = itEnv->data[3] = envDefault - envOffset; - itEnv->data[4] = 10; - } -} - - -// Convert IT/MPTM envelope data into MPT's internal envelope format - To be used by ITInstrToMPT() -void ITEnvToMPT(const ITENVELOPE *itEnv, InstrumentEnvelope *mptEnv, const BYTE envOffset, const int iEnvMax) -//----------------------------------------------------------------------------------------------------------- -{ - if(itEnv->flags & 1) mptEnv->dwFlags |= ENV_ENABLED; - if(itEnv->flags & 2) mptEnv->dwFlags |= ENV_LOOP; - if(itEnv->flags & 4) mptEnv->dwFlags |= ENV_SUSTAIN; - if(itEnv->flags & 8) mptEnv->dwFlags |= ENV_CARRY; - mptEnv->nNodes = min(itEnv->num, iEnvMax); - mptEnv->nLoopStart = itEnv->lpb; - mptEnv->nLoopEnd = itEnv->lpe; - mptEnv->nSustainStart = itEnv->slb; - mptEnv->nSustainEnd = itEnv->sle; - - // Attention: Full MPTM envelope is stored in extended instrument properties - for (UINT ev = 0; ev < 25; ev++) - { - mptEnv->Values[ev] = itEnv->data[ev * 3] + envOffset; - mptEnv->Ticks[ev] = (itEnv->data[ev * 3 + 2] << 8) | (itEnv->data[ev * 3 + 1]); - if(ev > 0 && ev < itEnv->num && mptEnv->Ticks[ev] < mptEnv->Ticks[ev - 1]) + size_t instSize = 0; + if(memLength >= sizeof(ITInstrumentEx)) { - // Fix broken envelopes... Instruments 2 and 3 in NoGap.it by Werewolf have envelope points where the high byte of envelope nodes is missing. - // NoGap.it was saved with MPT 1.07 or MPT 1.09, which *normally* doesn't do this in IT files. - // However... It turns out that MPT 1.07 omitted the high byte of envelope nodes when saving an XI instrument file, and it looks like - // Instrument 2 and 3 in NoGap.it were loaded from XI files. - mptEnv->Ticks[ev] &= 0xFF; - mptEnv->Ticks[ev] |= (mptEnv->Ticks[ev] & ~0xFF); - if(mptEnv->Ticks[ev] < mptEnv->Ticks[ev - 1]) - { - mptEnv->Ticks[ev] += 0x100; - } - } - } - // Sanitize envelope - mptEnv->Ticks[0] = 0; -} - - -//BOOL CSoundFile::ITInstrToMPT(const void *p, ModInstrument *pIns, UINT trkvers) -long CSoundFile::ITInstrToMPT(const void *p, ModInstrument *pIns, UINT trkvers) //rewbs.modularInstData -//----------------------------------------------------------------------------- -{ - // Envelope point count. Limited to 25 in IT format. - const int iEnvMax = (m_nType & MOD_TYPE_MPT) ? MAX_ENVPOINTS : 25; - - long returnVal=0; - if (trkvers < 0x0200) - { - const ITOLDINSTRUMENT *pis = (const ITOLDINSTRUMENT *)p; - memcpy(pIns->name, pis->name, 26); - memcpy(pIns->filename, pis->filename, 12); - StringFixer::SpaceToNullStringFixed<26>(pIns->name); - StringFixer::SpaceToNullStringFixed<12>(pIns->filename); - pIns->nFadeOut = pis->fadeout << 6; - pIns->nGlobalVol = 64; - for (UINT j = 0; j < 120; j++) + // Try loading extended instrument + const ITInstrumentEx *itIns = reinterpret_cast<const ITInstrumentEx *>(p); + instSize = itIns->ConvertToMPT(*pIns, GetType()); + } else { - UINT note = pis->keyboard[j*2]; - UINT ins = pis->keyboard[j*2+1]; - if (ins < MAX_SAMPLES) pIns->Keyboard[j] = ins; - if (note < 120) pIns->NoteMap[j] = note + 1; - else pIns->NoteMap[j] = j + 1; + // Load normal instrument + const ITInstrument *itIns = reinterpret_cast<const ITInstrument *>(p); + instSize = itIns->ConvertToMPT(*pIns, GetType()); } - if (pis->flags & 0x01) pIns->VolEnv.dwFlags |= ENV_ENABLED; - if (pis->flags & 0x02) pIns->VolEnv.dwFlags |= ENV_LOOP; - if (pis->flags & 0x04) pIns->VolEnv.dwFlags |= ENV_SUSTAIN; - pIns->VolEnv.nLoopStart = pis->vls; - pIns->VolEnv.nLoopEnd = pis->vle; - pIns->VolEnv.nSustainStart = pis->sls; - pIns->VolEnv.nSustainEnd = pis->sle; - pIns->VolEnv.nNodes = 25; - for (UINT ev=0; ev<25; ev++) - { - if ((pIns->VolEnv.Ticks[ev] = pis->nodes[ev*2]) == 0xFF) - { - pIns->VolEnv.nNodes = ev; - break; - } - pIns->VolEnv.Values[ev] = pis->nodes[ev*2+1]; - } - pIns->nNNA = pis->nna; - pIns->nDCT = pis->dnc; - pIns->nPan = 0x80; - } else - { - const ITINSTRUMENT *pis = (const ITINSTRUMENT *)p; - memcpy(pIns->name, pis->name, 26); - memcpy(pIns->filename, pis->filename, 12); - StringFixer::SpaceToNullStringFixed<26>(pIns->name); - StringFixer::SpaceToNullStringFixed<12>(pIns->filename); - if (pis->mpr<=128) - pIns->nMidiProgram = pis->mpr; - pIns->nMidiChannel = pis->mch; - if (pIns->nMidiChannel >= 128) //rewbs.instroVSTi - { //(handle old format where midichan - // and mixplug are 1 value) - pIns->nMixPlug = pIns->nMidiChannel - 128; - pIns->nMidiChannel = 0; - } - if (pis->mbank<=128) - pIns->wMidiBank = pis->mbank; - pIns->nFadeOut = pis->fadeout << 5; - pIns->nGlobalVol = pis->gbv >> 1; - if (pIns->nGlobalVol > 64) pIns->nGlobalVol = 64; - for (UINT j = 0; j < 120; j++) - { - UINT note = pis->keyboard[j*2]; - UINT ins = pis->keyboard[j*2+1]; - if (ins < MAX_SAMPLES) pIns->Keyboard[j] = ins; - if (note < 120) pIns->NoteMap[j] = note + 1; - else pIns->NoteMap[j] = j + 1; - } - // Olivier's MPT Instrument Extension - if (*((int *)pis->dummy) == 'MPTX') - { - const ITINSTRUMENTEX *pisex = (const ITINSTRUMENTEX *)pis; - for (UINT k = 0; k < 120; k++) - { - pIns->Keyboard[k] |= ((UINT)pisex->keyboardhi[k] << 8); - } - } - //rewbs.modularInstData - //find end of standard header - BYTE* pEndInstHeader; - if (*((int *)pis->dummy) == 'MPTX') - pEndInstHeader=(BYTE*)pis+sizeof(ITINSTRUMENTEX); - else - pEndInstHeader=(BYTE*)pis+sizeof(ITINSTRUMENT); + // Try reading modular instrument data + instSize += LoadModularInstrumentData(LPCBYTE(p) + instSize, memLength - instSize, pIns); - //If the next piece of data is 'INSM' we have modular extensions to our instrument... - if ( *( (UINT*)pEndInstHeader ) == 'INSM' ) - { - //...the next piece of data must be the total size of the modular data - long modularInstSize = *((long *)(pEndInstHeader+4)); - - //handle chunks - BYTE* pModularInst = (BYTE*)(pEndInstHeader+4+sizeof(modularInstSize)); //4 is for 'INSM' - pEndInstHeader+=4+sizeof(modularInstSize)+modularInstSize; - while (pModularInst<pEndInstHeader) //4 is for 'INSM' - { - UINT chunkID = *((int *)pModularInst); - pModularInst+=4; - switch (chunkID) - { - /*case 'DMMY': - MessageBox(NULL, "Dummy chunk identified", NULL, MB_OK|MB_ICONEXCLAMATION); - pModularInst+=1024; - break;*/ - case 'PLUG': - pIns->nMixPlug = *(pModularInst); - pModularInst+=sizeof(pIns->nMixPlug); - break; - /*How to load more chunks? -- see also how to save chunks - case: 'MYID': - // handle chunk data, as pointed to by pModularInst - // move pModularInst as appropriate - break; - */ - - default: pModularInst++; //move forward one byte and try to recognize again. - - } - } - returnVal = 4+sizeof(modularInstSize)+modularInstSize; - } - //end rewbs.modularInstData - - - // Volume Envelope - ITEnvToMPT(&pis->volenv, &pIns->VolEnv, 0, iEnvMax); - // Panning Envelope - ITEnvToMPT(&pis->panenv, &pIns->PanEnv, 32, iEnvMax); - // Pitch Envelope - ITEnvToMPT(&pis->pitchenv, &pIns->PitchEnv, 32, iEnvMax); - if (pis->pitchenv.flags & 0x80) pIns->PitchEnv.dwFlags |= ENV_FILTER; - - pIns->nNNA = pis->nna; - pIns->nDCT = pis->dct; - pIns->nDNA = pis->dca; - pIns->nPPS = pis->pps; - pIns->nPPC = pis->ppc; - pIns->SetCutoff(pis->ifc & 0x7F, (pis->ifc & 0x80) != 0); - pIns->SetResonance(pis->ifr & 0x7F, (pis->ifr & 0x80) != 0); - pIns->nVolSwing = min(pis->rv, 100); - pIns->nPanSwing = min(pis->rp, 64); - pIns->nPan = (pis->dfp & 0x7F) << 2; - if (pIns->nPan > 256) pIns->nPan = 128; - if (pis->dfp < 0x80) pIns->dwFlags |= INS_SETPANNING; + return instSize; } - - if ((pIns->VolEnv.nLoopStart >= iEnvMax) || (pIns->VolEnv.nLoopEnd >= iEnvMax)) pIns->VolEnv.dwFlags &= ~ENV_LOOP; - if ((pIns->VolEnv.nSustainStart >= iEnvMax) || (pIns->VolEnv.nSustainEnd >= iEnvMax)) pIns->VolEnv.dwFlags &= ~ENV_SUSTAIN; - - return returnVal; //return offset } @@ -489,9 +281,12 @@ bool CSoundFile::ReadIT(const LPCBYTE lpStream, const DWORD dwMemLength) //---------------------------------------------------------------------- { - ITFILEHEADER *pifh = (ITFILEHEADER *)lpStream; + if(!lpStream || dwMemLength < sizeof(ITFileHeader)) return false; - DWORD dwMemPos = sizeof(ITFILEHEADER); + ITFileHeader itHeader = *reinterpret_cast<const ITFileHeader *>(lpStream); + itHeader.ConvertEndianness(); + + DWORD dwMemPos = sizeof(ITFileHeader); vector<DWORD> inspos; vector<DWORD> smppos; vector<DWORD> patpos; @@ -499,25 +294,24 @@ bool interpretModPlugMade = false; bool hasModPlugExtensions = false; - if ((!lpStream) || (dwMemLength < 0xC0)) return false; - if ((pifh->id != LittleEndian(IT_IMPM) && pifh->id != LittleEndian(IT_MPTM)) || (pifh->insnum > 0xFF) - || (pifh->smpnum >= MAX_SAMPLES)) return false; - if (dwMemPos + pifh->ordnum + pifh->insnum*4 - + pifh->smpnum*4 + pifh->patnum*4 > dwMemLength) return false; + if((itHeader.id != ITFileHeader::itMagic && itHeader.id != ITFileHeader::mptmMagic) || itHeader.insnum > 0xFF + || itHeader.smpnum >= MAX_SAMPLES) return false; + // Check if we can actually read all parapointers + if(dwMemPos + itHeader.ordnum + itHeader.insnum * 4 + + itHeader.smpnum * 4 + itHeader.patnum * 4 > dwMemLength) return false; - DWORD mptStartPos = dwMemLength; memcpy(&mptStartPos, lpStream + (dwMemLength - sizeof(DWORD)), sizeof(DWORD)); if(mptStartPos >= dwMemLength || mptStartPos < 0x100) mptStartPos = dwMemLength; - if(pifh->id == LittleEndian(IT_MPTM)) + if(itHeader.id == ITFileHeader::mptmMagic) { ChangeModTypeTo(MOD_TYPE_MPT); } else { - if(mptStartPos <= dwMemLength - 3 && pifh->cwtv > 0x888) + if(mptStartPos <= dwMemLength - 3 && itHeader.cwtv > 0x888) { char temp[3]; const char ID[3] = {'2','2','8'}; @@ -530,22 +324,22 @@ if(GetType() == MOD_TYPE_IT) { // Which tracker was used to made this? - if((pifh->cwtv & 0xF000) == 0x5000) + if((itHeader.cwtv & 0xF000) == 0x5000) { // OpenMPT Version number (Major.Minor) // This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used. - m_dwLastSavedWithVersion = (pifh->cwtv & 0x0FFF) << 16; - if(pifh->reserved == LittleEndian(IT_OMPT)) + m_dwLastSavedWithVersion = (itHeader.cwtv & 0x0FFF) << 16; + if(itHeader.reserved == ITFileHeader::omptMagic) interpretModPlugMade = true; - } else if(pifh->cmwt == 0x888 || pifh->cwtv == 0x888) + } else if(itHeader.cmwt == 0x888 || itHeader.cwtv == 0x888) { // OpenMPT 1.17 and 1.18 (raped IT format) // Exact version number will be determined later. interpretModPlugMade = true; m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); - } else if(pifh->cwtv == 0x0217 && pifh->cmwt == 0x0200 && pifh->reserved == 0) + } else if(itHeader.cwtv == 0x0217 && itHeader.cmwt == 0x0200 && itHeader.reserved == 0) { - if(memchr(pifh->chnpan, 0xFF, sizeof(pifh->chnpan)) != NULL) + if(memchr(itHeader.chnpan, 0xFF, sizeof(itHeader.chnpan)) != NULL) { // ModPlug Tracker 1.16 (semi-raped IT format) m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); @@ -556,13 +350,13 @@ m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); } interpretModPlugMade = true; - } else if(pifh->cwtv == 0x0214 && pifh->cmwt == 0x0202 && pifh->reserved == 0) + } else if(itHeader.cwtv == 0x0214 && itHeader.cmwt == 0x0202 && itHeader.reserved == 0) { // ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); interpretModPlugMade = true; } - else if(pifh->cwtv == 0x0214 && pifh->cmwt == 0x0200 && pifh->reserved == 0) + else if(itHeader.cwtv == 0x0214 && itHeader.cmwt == 0x0200 && itHeader.reserved == 0) { // ModPlug Tracker 1.00a5, instruments 560 bytes apart m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5); @@ -571,13 +365,13 @@ } else // case: type == MOD_TYPE_MPT { - if (pifh->cwtv >= verMptFileVerLoadLimit) + if (itHeader.cwtv >= verMptFileVerLoadLimit) { if (GetpModDoc()) GetpModDoc()->AddToLog(str_LoadingIncompatibleVersion); return false; } - else if (pifh->cwtv > verMptFileVer) + else if (itHeader.cwtv > verMptFileVer) { if (GetpModDoc()) GetpModDoc()->AddToLog(str_LoadingMoreRecentVersion); @@ -588,67 +382,59 @@ if(GetType() == MOD_TYPE_IT) mptStartPos = dwMemLength; // Read row highlights - if((pifh->special & 0x04)) + if((itHeader.special & ITFileHeader::embedPatternHighlights)) { // MPT 1.09, 1.07 and most likely other old MPT versions leave this blank (0/0), but have the "special" flag set. // Newer versions of MPT and OpenMPT 1.17 *always* write 4/16 here. // Thus, we will just ignore those old versions. if(m_dwLastSavedWithVersion == 0 || m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02)) { - m_nDefaultRowsPerBeat = pifh->highlight_minor; - m_nDefaultRowsPerMeasure = pifh->highlight_major; + m_nDefaultRowsPerBeat = itHeader.highlight_minor; + m_nDefaultRowsPerMeasure = itHeader.highlight_major; } #ifdef DEBUG - if((pifh->highlight_minor | pifh->highlight_major) == 0) + if((itHeader.highlight_minor | itHeader.highlight_major) == 0) { Log("IT Header: Row highlight is 0"); } #endif } - if (pifh->flags & 0x08) m_dwSongFlags |= SONG_LINEARSLIDES; - if (pifh->flags & 0x10) m_dwSongFlags |= SONG_ITOLDEFFECTS; - if (pifh->flags & 0x20) m_dwSongFlags |= SONG_ITCOMPATGXX; - if ((pifh->flags & 0x80) || (pifh->special & 0x08)) m_dwSongFlags |= SONG_EMBEDMIDICFG; - if (pifh->flags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE; + if (itHeader.flags & ITFileHeader::linearSlides) m_dwSongFlags |= SONG_LINEARSLIDES; + if (itHeader.flags & ITFileHeader::itOldEffects) m_dwSongFlags |= SONG_ITOLDEFFECTS; + if (itHeader.flags & ITFileHeader::itCompatGxx) m_dwSongFlags |= SONG_ITCOMPATGXX; + if ((itHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) || (itHeader.special & ITFileHeader::embedMIDIConfiguration)) m_dwSongFlags |= SONG_EMBEDMIDICFG; + if (itHeader.flags & ITFileHeader::extendedFilterRange) m_dwSongFlags |= SONG_EXFILTERRANGE; - memcpy(m_szNames[0], pifh->songname, 26); + memcpy(m_szNames[0], itHeader.songname, 26); StringFixer::SpaceToNullStringFixed<26>(m_szNames[0]); // Global Volume - m_nDefaultGlobalVolume = pifh->globalvol << 1; + m_nDefaultGlobalVolume = itHeader.globalvol << 1; if (m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; - if (pifh->speed) m_nDefaultSpeed = pifh->speed; - m_nDefaultTempo = max(32, pifh->tempo); // tempo 31 is possible. due to conflicts with the rest of the engine, let's just clamp it to 32. - m_nSamplePreAmp = min(pifh->mv, 128); + if (itHeader.speed) m_nDefaultSpeed = itHeader.speed; + m_nDefaultTempo = max(32, itHeader.tempo); // tempo 31 is possible. due to conflicts with the rest of the engine, let's just clamp it to 32. + m_nSamplePreAmp = min(itHeader.mv, 128); // Reading Channels Pan Positions - for (int ipan=0; ipan< 64; ipan++) if (pifh->chnpan[ipan] != 0xFF) + for (int ipan=0; ipan< 64; ipan++) if (itHeader.chnpan[ipan] != 0xFF) { - ChnSettings[ipan].nVolume = pifh->chnvol[ipan]; + ChnSettings[ipan].nVolume = itHeader.chnvol[ipan]; ChnSettings[ipan].nPan = 128; - if (pifh->chnpan[ipan] & 0x80) ChnSettings[ipan].dwFlags |= CHN_MUTE; - UINT n = pifh->chnpan[ipan] & 0x7F; + if (itHeader.chnpan[ipan] & 0x80) ChnSettings[ipan].dwFlags |= CHN_MUTE; + UINT n = itHeader.chnpan[ipan] & 0x7F; if (n <= 64) ChnSettings[ipan].nPan = n << 2; if (n == 100) ChnSettings[ipan].dwFlags |= CHN_SURROUND; } if (m_nChannels < GetModSpecifications().channelsMin) m_nChannels = GetModSpecifications().channelsMin; - // Reading Song Message - if ((pifh->special & 0x01) && (pifh->msglength) && (pifh->msglength <= dwMemLength) && (pifh->msgoffset < dwMemLength - pifh->msglength)) - { - // Generally, IT files should use CR for line endings. However, ChibiTracker uses LF. One could do... - // if(pifh->cwtv == 0x0214 && pifh->cmwt == 0x0214 && pifh->reserved == LittleEndian(IT_CHBI)) --> Chibi detected. - // But we'll just use autodetection here: - ReadMessage(lpStream + pifh->msgoffset, pifh->msglength, leAutodetect); - } // Reading orders - UINT nordsize = pifh->ordnum; + UINT nordsize = itHeader.ordnum; if(GetType() == MOD_TYPE_IT) { if(nordsize > MAX_ORDERS) nordsize = MAX_ORDERS; Order.ReadAsByte(lpStream + dwMemPos, nordsize, dwMemLength-dwMemPos); - dwMemPos += pifh->ordnum; + dwMemPos += itHeader.ordnum; } else { @@ -662,12 +448,12 @@ nordsize = GetModSpecifications().ordersMax; } - if(pifh->cwtv > 0x88A && pifh->cwtv <= 0x88D) + if(itHeader.cwtv... [truncated message content] |