From: <sag...@us...> - 2009-08-16 16:59:06
|
Revision: 329 http://modplug.svn.sourceforge.net/modplug/?rev=329&view=rev Author: saga-games Date: 2009-08-16 16:58:51 +0000 (Sun, 16 Aug 2009) Log Message: ----------- [Imp] Mod Conversion: Try to use fix commands that don't have cache (00 value) in XM (Arpeggio) and MOD format (Arpeggio and a few others) by using the previous value [New] OpenMPT can now import IMF (Imago Orpheus) modules [Ref] Minor modifications in the GDM and MO3 loaders Modified Paths: -------------- trunk/OpenMPT/mptrack/Modedit.cpp trunk/OpenMPT/mptrack/Mptrack.cpp trunk/OpenMPT/soundlib/Load_gdm.cpp trunk/OpenMPT/soundlib/Load_imf.cpp trunk/OpenMPT/soundlib/Load_mo3.cpp trunk/OpenMPT/soundlib/Sndfile.cpp Modified: trunk/OpenMPT/mptrack/Modedit.cpp =================================================================== --- trunk/OpenMPT/mptrack/Modedit.cpp 2009-08-16 11:55:06 UTC (rev 328) +++ trunk/OpenMPT/mptrack/Modedit.cpp 2009-08-16 16:58:51 UTC (rev 329) @@ -162,8 +162,11 @@ for (UINT nPat=0; nPat<m_SndFile.Patterns.Size(); nPat++) if (m_SndFile.Patterns[nPat]) { MODCOMMAND *m = m_SndFile.Patterns[nPat]; + BYTE cEffectMemory[MAX_CHANNELS][MAX_EFFECTS] = {0}; // for -> MOD/XM conversion + UINT nChannel = m_SndFile.m_nChannels - 1; for (UINT len = m_SndFile.PatternSize[nPat] * m_SndFile.m_nChannels; len; m++, len--) { + nChannel = (nChannel + 1) % m_SndFile.m_nChannels; // 0...Channels - 1 ////////////////////////// // Convert 8-bit Panning @@ -286,6 +289,13 @@ switch(m->command) { + case CMD_ARPEGGIO: + // No effect memory in XM / MOD + if(m->param == 0) + m->param = cEffectMemory[nChannel][CMD_ARPEGGIO]; + else + cEffectMemory[nChannel][CMD_ARPEGGIO] = m->param; + break; case CMD_S3MCMDEX: m->command = CMD_MODCMDEX; switch(m->param & 0xF0) @@ -391,8 +401,8 @@ } - ////////////////////////////////////////////////////////////////// - // Convert anything to MOD - remove volume column, adjust retrig + ///////////////////////////////////////////////////////////////////////////////// + // Convert anything to MOD - remove volume column, adjust retrig, effect memory if (newTypeIsMOD) { if(m->command) switch(m->command) @@ -401,6 +411,17 @@ m->command = CMD_MODCMDEX; m->param = 0x90 | (m->param & 0x0F); break; + case CMD_PORTAMENTOUP: + case CMD_PORTAMENTODOWN: + case CMD_TONEPORTAVOL: + case CMD_VIBRATOVOL: + case CMD_VOLUMESLIDE: + // ProTracker doesn't have effect memory for these commands, so let's try to fix them + if(m->param == 0) + m->param = cEffectMemory[nChannel][m->command]; + else + cEffectMemory[nChannel][m->command] = m->param; + break; } else switch(m->volcmd) Modified: trunk/OpenMPT/mptrack/Mptrack.cpp =================================================================== --- trunk/OpenMPT/mptrack/Mptrack.cpp 2009-08-16 11:55:06 UTC (rev 328) +++ trunk/OpenMPT/mptrack/Mptrack.cpp 2009-08-16 16:58:51 UTC (rev 329) @@ -1252,13 +1252,11 @@ OFN_HIDEREADONLY | OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_FORCESHOWHIDDEN | OFN_ALLOWMULTISELECT, // -> CODE#0023 // -> DESC="IT project files (.itp)" -// "All Modules|*.mod;*.nst;*.wow;*.s3m;*.stm;*.669;*.mtm;*.xm;*.it;*.ult;*.mdz;*.s3z;*.xmz;*.itz;mod.*;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.mdr;*.med;*.ams;*.dbm;*.dsm;*.mid;*.rmi;*.smf;*.bak;*.umx;*.amf;*.psm;*.mt2|" -// "All Modules|*.mod;*.nst;*.wow;*.s3m;*.stm;*.669;*.mtm;*.xm;*.it;*.ult;*.mdz;*.s3z;*.xmz;*.itz;mod.*;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.mdr;*.med;*.ams;*.dbm;*.dsm;*.mid;*.rmi;*.smf;*.bak;*.umx;*.amf;*.psm;*.mt2;*.gdm|" #ifndef NO_MO3_SUPPORT - "All Modules|*.mod;*.nst;*.wow;*.s3m;*.stm;*.669;*.mtm;*.xm;*.it;*.itp;*.mptm;*.ult;*.mdz;*.s3z;*.xmz;*.itz;mod.*;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.mdr;*.med;*.ams;*.dbm;*.dsm;*.mid;*.rmi;*.smf;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.mo3|" + "All Modules|*.mod;*.nst;*.wow;*.s3m;*.stm;*.669;*.mtm;*.xm;*.it;*.itp;*.mptm;*.ult;*.mdz;*.s3z;*.xmz;*.itz;mod.*;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.mdr;*.med;*.ams;*.dbm;*.dsm;*.mid;*.rmi;*.smf;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf;*.mo3|" "Compressed Modules (*.mdz;*.s3z;*.xmz;*.itz;*.mo3)|*.mdz;*.s3z;*.xmz;*.itz;*.mdr;*.zip;*.rar;*.lha;*.mo3|" #else - "All Modules|*.mod;*.nst;*.wow;*.s3m;*.stm;*.669;*.mtm;*.xm;*.it;*.itp;*.mptm;*.ult;*.mdz;*.s3z;*.xmz;*.itz;mod.*;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.mdr;*.med;*.ams;*.dbm;*.dsm;*.mid;*.rmi;*.smf;*.umx;*.amf;*.psm;*.mt2;*.gdm|" + "All Modules|*.mod;*.nst;*.wow;*.s3m;*.stm;*.669;*.mtm;*.xm;*.it;*.itp;*.mptm;*.ult;*.mdz;*.s3z;*.xmz;*.itz;mod.*;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.mdr;*.med;*.ams;*.dbm;*.dsm;*.mid;*.rmi;*.smf;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf|" "Compressed Modules (*.mdz;*.s3z;*.xmz;*.itz)|*.mdz;*.s3z;*.xmz;*.itz;*.mdr;*.zip;*.rar;*.lha|" #endif // -! NEW_FEATURE#0023 @@ -1271,7 +1269,7 @@ "Impulse Tracker Projects (*.itp)|*.itp;*.itpz|" // -! NEW_FEATURE#0023 "OpenMPT Modules (*.mptm)|*.mptm;*.mptmz|" - "Other Modules (mtm,okt,mdl,669,far,...)|*.mtm;*.669;*.ult;*.wow;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.med;*.ams;*.dbm;*.dsm;*.umx;*.amf;*.psm;*.mt2;*.gdm|" + "Other Modules (mtm,okt,mdl,669,far,...)|*.mtm;*.669;*.ult;*.wow;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.med;*.ams;*.dbm;*.dsm;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf|" "Wave Files (*.wav)|*.wav|" "Midi Files (*.mid,*.rmi)|*.mid;*.rmi;*.smf|" "All Files (*.*)|*.*||", Modified: trunk/OpenMPT/soundlib/Load_gdm.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_gdm.cpp 2009-08-16 11:55:06 UTC (rev 328) +++ trunk/OpenMPT/soundlib/Load_gdm.cpp 2009-08-16 16:58:51 UTC (rev 329) @@ -66,6 +66,8 @@ BYTE Pan; // default pan } GDMSAMPLEHEADER, *PGDMSAMPLEHEADER; +#pragma pack() + bool CSoundFile::ReadGDM(const LPCBYTE lpStream, const DWORD dwMemLength) //----------------------------------------------------------- { Modified: trunk/OpenMPT/soundlib/Load_imf.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_imf.cpp 2009-08-16 11:55:06 UTC (rev 328) +++ trunk/OpenMPT/soundlib/Load_imf.cpp 2009-08-16 16:58:51 UTC (rev 329) @@ -1,6 +1,6 @@ /* * Purpose: Load IMF (Imago Orpheus) modules - * Authors: Storlek (http://schismtracker.org/) + * Authors: Storlek (Original author - http://schismtracker.org/) * Johannes Schultz (OpenMPT Port) * * Thanks to Storlek for allowing me to use this code! @@ -12,7 +12,7 @@ #pragma pack(1) struct IMFCHANNEL { - char name[12]; // Channelname (ASCIIZ-String, max 11 chars) + char name[12]; // Channel name (ASCIIZ-String, max 11 chars) BYTE chorus; // Default chorus BYTE reverb; // Default reverb BYTE panning; // Pan positions 00-FF @@ -20,20 +20,20 @@ }; struct IMFHEADER { - char title[32]; // Songname (ASCIIZ-String, max. 31 chars) - UINT16 ordnum; // Number of orders saved - UINT16 patnum; // Number of patterns saved - UINT16 insnum; // Number of instruments saved - UINT16 flags; // Module flags (&1 => linear) + char title[32]; // Songname (ASCIIZ-String, max. 31 chars) + UINT16 ordnum; // Number of orders saved + UINT16 patnum; // Number of patterns saved + UINT16 insnum; // Number of instruments saved + UINT16 flags; // Module flags (&1 => linear) BYTE unused1[8]; - BYTE tempo; // Default tempo (Axx, 1..255) - BYTE bpm; // Default beats per minute (BPM) (Txx, 32..255) - BYTE master; // Default mastervolume (Vxx, 0..64) - BYTE amp; // Amplification factor (mixing volume, 4..127) + BYTE tempo; // Default tempo (Axx, 1..255) + BYTE bpm; // Default beats per minute (BPM) (Txx, 32..255) + BYTE master; // Default mastervolume (Vxx, 0..64) + BYTE amp; // Amplification factor (mixing volume, 4..127) BYTE unused2[8]; - char im10[4]; // 'IM10' - struct IMFCHANNEL channels[32]; // Channel settings - BYTE orderlist[256]; // Order list (0xff = +++; blank out anything beyond ordnum) + char im10[4]; // 'IM10' + IMFCHANNEL channels[32]; // Channel settings + BYTE orderlist[256]; // Order list (0xff = +++; blank out anything beyond ordnum) }; enum { @@ -60,22 +60,22 @@ char name[32]; // Inst. name (ASCIIZ-String, max. 31 chars) BYTE map[120]; // Multisample settings BYTE unused[8]; - struct IMFENVNODES nodes[3][16]; - struct IMFENVELOPE env[3]; + IMFENVNODES nodes[3][16]; + IMFENVELOPE env[3]; UINT16 fadeout; // Fadeout rate (0...0FFFH) UINT16 smpnum; // Number of samples in instrument char ii10[4]; // 'II10' }; struct IMFSAMPLE { - char filename[13]; // Sample filename (12345678.ABC) */ + char filename[13]; // Sample filename (12345678.ABC) */ BYTE unused1[3]; UINT32 length; // Length UINT32 loop_start; // Loop start UINT32 loop_end; // Loop end UINT32 C5Speed; // Samplerate - BYTE volume; // Default volume (0..64) - BYTE panning; // Default pan (00h = Left / 80h = Middle) + BYTE volume; // Default volume (0...64) + BYTE panning; // Default pan (0...255) BYTE unused2[14]; BYTE flags; // Sample flags BYTE unused3[5]; @@ -85,54 +85,53 @@ }; #pragma pack() - static BYTE imf_efftrans[] = { CMD_NONE, - CMD_SPEED, // 0x01 1xx Set Tempo - CMD_TEMPO, // 0x02 2xx Set BPM + CMD_SPEED, // 0x01 1xx Set Tempo + CMD_TEMPO, // 0x02 2xx Set BPM CMD_TONEPORTAMENTO, // 0x03 3xx Tone Portamento - CMD_TONEPORTAVOL, // 0x04 4xy Tone Portamento + Volume Slide - CMD_VIBRATO, // 0x05 5xy Vibrato - CMD_VIBRATOVOL, // 0x06 6xy Vibrato + Volume Slide - CMD_FINEVIBRATO, // 0x07 7xy Fine Vibrato - CMD_TREMOLO, // 0x08 8xy Tremolo - CMD_ARPEGGIO, // 0x09 9xy Arpeggio - CMD_PANNING8, // 0x0A Axx Set Pan Position - CMD_PANNINGSLIDE, // 0x0B Bxy Pan Slide - CMD_VOLUME, // 0x0C Cxx Set Volume - CMD_VOLUMESLIDE, // 0x0D Dxy Volume Slide - CMD_VOLUMESLIDE, // 0x0E Exy Fine Volume Slide - CMD_S3MCMDEX, // 0x0F Fxx Set Finetune - CMD_NOTESLIDEUP, // 0x10 Gxy Note Slide Up - CMD_NOTESLIDEDOWN, // 0x11 Hxy Note Slide Down - CMD_PORTAMENTOUP, // 0x12 Ixx Slide Up - CMD_PORTAMENTODOWN, // 0x13 Jxx Slide Down - CMD_PORTAMENTOUP, // 0x14 Kxx Fine Slide Up - CMD_PORTAMENTODOWN, // 0x15 Lxx Fine Slide Down - CMD_MIDI, // 0x16 Mxx Set Filter Cutoff - XXX - CMD_NONE, // 0x17 Nxy Filter Slide + Resonance - XXX - CMD_OFFSET, // 0x18 Oxx Set Sample Offset - CMD_NONE, // 0x19 Pxx Set Fine Sample Offset - XXX - CMD_KEYOFF, // 0x1A Qxx Key Off - CMD_RETRIG, // 0x1B Rxy Retrig - CMD_TREMOR, // 0x1C Sxy Tremor - CMD_POSITIONJUMP, // 0x1D Txx Position Jump - CMD_PATTERNBREAK, // 0x1E Uxx Pattern Break - CMD_GLOBALVOLUME, // 0x1F Vxx Set Mastervolume - CMD_GLOBALVOLSLIDE, // 0x20 Wxy Mastervolume Slide - CMD_S3MCMDEX, // 0x21 Xxx Extended Effect - // X1x Set Filter - // X3x Glissando - // X5x Vibrato Waveform - // X8x Tremolo Waveform - // XAx Pattern Loop - // XBx Pattern Delay - // XCx Note Cut - // XDx Note Delay - // XEx Ignore Envelope - // XFx Invert Loop - CMD_NONE, // 0x22 Yxx Chorus - XXX - CMD_NONE, // 0x23 Zxx Reverb - XXX + CMD_TONEPORTAVOL, // 0x04 4xy Tone Portamento + Volume Slide + CMD_VIBRATO, // 0x05 5xy Vibrato + CMD_VIBRATOVOL, // 0x06 6xy Vibrato + Volume Slide + CMD_FINEVIBRATO, // 0x07 7xy Fine Vibrato + CMD_TREMOLO, // 0x08 8xy Tremolo + CMD_ARPEGGIO, // 0x09 9xy Arpeggio + CMD_PANNING8, // 0x0A Axx Set Pan Position + CMD_PANNINGSLIDE, // 0x0B Bxy Pan Slide + CMD_VOLUME, // 0x0C Cxx Set Volume + CMD_VOLUMESLIDE, // 0x0D Dxy Volume Slide + CMD_VOLUMESLIDE, // 0x0E Exy Fine Volume Slide + CMD_S3MCMDEX, // 0x0F Fxx Set Finetune + CMD_NOTESLIDEUP, // 0x10 Gxy Note Slide Up + CMD_NOTESLIDEDOWN, // 0x11 Hxy Note Slide Down + CMD_PORTAMENTOUP, // 0x12 Ixx Slide Up + CMD_PORTAMENTODOWN, // 0x13 Jxx Slide Down + CMD_PORTAMENTOUP, // 0x14 Kxx Fine Slide Up + CMD_PORTAMENTODOWN, // 0x15 Lxx Fine Slide Down + CMD_MIDI, // 0x16 Mxx Set Filter Cutoff - XXX + CMD_NONE, // 0x17 Nxy Filter Slide + Resonance - XXX + CMD_OFFSET, // 0x18 Oxx Set Sample Offset + CMD_NONE, // 0x19 Pxx Set Fine Sample Offset - XXX + CMD_KEYOFF, // 0x1A Qxx Key Off + CMD_RETRIG, // 0x1B Rxy Retrig + CMD_TREMOR, // 0x1C Sxy Tremor + CMD_POSITIONJUMP, // 0x1D Txx Position Jump + CMD_PATTERNBREAK, // 0x1E Uxx Pattern Break + CMD_GLOBALVOLUME, // 0x1F Vxx Set Mastervolume + CMD_GLOBALVOLSLIDE, // 0x20 Wxy Mastervolume Slide + CMD_S3MCMDEX, // 0x21 Xxx Extended Effect + // X1x Set Filter + // X3x Glissando + // X5x Vibrato Waveform + // X8x Tremolo Waveform + // XAx Pattern Loop + // XBx Pattern Delay + // XCx Note Cut + // XDx Note Delay + // XEx Ignore Envelope + // XFx Invert Loop + CMD_NONE, // 0x22 Yxx Chorus - XXX + CMD_NONE, // 0x23 Zxx Reverb - XXX }; static void import_imf_effect(MODCOMMAND *note) @@ -225,115 +224,6 @@ } } -#ifdef _IMF_SUPPORT_FINISHED_ - -static void load_imf_pattern(CSoundFile *csf, int pat, UINT32 ignore_channels, slurp_t *fp) -{ - UINT16 length, nrows; - BYTE mask, channel; - int row, startpos; - unsigned int lostfx = 0; - MODCOMMAND *row_data, *note, junk_note; - - startpos = slurp_tell(fp); - - slurp_read(fp, &length, 2); - length = LittleEndianW(length); - slurp_read(fp, &nrows, 2); - nrows = LittleEndianW(nrows); - - csf->Patterns.Insert(pat, nrows); - //row_data = Patterns[pat] = csf_allocate_pattern(nrows, 64); - //PatternSize[pat] = PatternAllocSize[pat] = nrows; - row_data = csf->Patterns[pat]; - - row = 0; - while (row < nrows) { - mask = slurp_getc(fp); - if (mask == 0) { - row++; - row_data += MAX_CHANNELS; - continue; - } - - channel = mask & 0x1f; - - if (ignore_channels & (1 << channel)) { - /* should do this better, i.e. not go through the whole process of deciding - what to do with the effects since they're just being thrown out */ - //printf("disabled channel %d contains data\n", channel + 1); - note = &junk_note; - } else { - note = row_data + channel; - } - - if (mask & 0x20) { - /* read note/instrument */ - note->note = slurp_getc(fp); - note->instr = slurp_getc(fp); - if (note->note == 160) { - note->note = NOTE_KEYOFF; /* ??? */ - } else if (note->note == 255) { - note->note = NOTE_NONE; /* ??? */ - } else { - note->note = (note->note >> 4) * 12 + (note->note & 0xf) + 12 + 1; - if (note->note > NOTE_MAX) { - //printf("%d.%d.%d: funny note 0x%02x\n", - // pat, row, channel, fp->data[fp->pos - 1]); - note->note = NOTE_NONE; - } - } - } - if ((mask & 0xc0) == 0xc0) { - BYTE e1c, e1d, e2c, e2d; - - /* read both effects and figure out what to do with them */ - e1c = slurp_getc(fp); - e1d = slurp_getc(fp); - e2c = slurp_getc(fp); - e2d = slurp_getc(fp); - if (e1c == 0xc) { - note->vol = min(e1d, 0x40); - note->volcmd = VOLCMD_VOLUME; - note->command = e2c; - note->param = e2d; - } else if (e2c == 0xc) { - note->vol = min(e2d, 0x40); - note->volcmd = VOLCMD_VOLUME; - note->command = e1c; - note->param = e1d; - } else if (e1c == 0xa) { - note->vol = e1d * 64 / 255; - note->volcmd = VOLCMD_PANNING; - note->command = e2c; - note->param = e2d; - } else if (e2c == 0xa) { - note->vol = e2d * 64 / 255; - note->volcmd = VOLCMD_PANNING; - note->command = e1c; - note->param = e1d; - } else { - /* check if one of the effects is a 'global' effect - -- if so, put it in some unused channel instead. - otherwise pick the most important effect. */ - lostfx++; - note->command = e2c; - note->param = e2d; - } - } else if (mask & 0xc0) { - /* there's one effect, just stick it in the effect column */ - note->command = slurp_getc(fp); - note->param = slurp_getc(fp); - } - if (note->command) - import_imf_effect(note); - } - - /*if (lostfx) - log_appendf(2, "Pattern %d: %d effect%s skipped!\n", pat, lostfx, lostfx == 1 ? "" : "s");*/ -} - - static unsigned int envflags[3][3] = { {ENV_VOLUME, ENV_VOLSUSTAIN, ENV_VOLLOOP}, {ENV_PANNING, ENV_PANSUSTAIN, ENV_PANLOOP}, @@ -342,21 +232,23 @@ static void load_imf_envelope(MODINSTRUMENT *ins, INSTRUMENTENVELOPE *env, IMFINSTRUMENT *imfins, int e) { - int n, t, v; - int min = 0; // minimum tick value for next node - int shift = (e == IMF_ENV_VOL ? 0 : 2); + UINT n; + UINT min = 0; // minimum tick value for next node + int shift = (e == IMF_ENV_VOL) ? 0 : 2; env->nNodes = CLAMP(imfins->env[e].points, 2, 25); env->nLoopStart = imfins->env[e].loop_start; env->nLoopEnd = imfins->env[e].loop_end; env->nSustainStart = env->nSustainEnd = imfins->env[e].sustain; + env->nReleaseNode = ENV_RELEASE_NODE_UNSET; for (n = 0; n < env->nNodes; n++) { - t = LittleEndianW(imfins->nodes[e][n].tick); - v = LittleEndianW(imfins->nodes[e][n].value) >> shift; - env->Ticks[n] = max(min, t); - env->Values[n] = v = min(v, 64); - min = t + 1; + UINT16 nTick, nValue; + nTick = LittleEndianW(imfins->nodes[e][n].tick); + nValue = LittleEndianW(imfins->nodes[e][n].value) >> shift; + env->Ticks[n] = (WORD)max(min, nTick); + env->Values[n] = (BYTE)min(nValue, 64); + min = nTick + 1; } // this would be less retarded if the envelopes all had their own flags... if (imfins->env[e].flags & 1) @@ -367,20 +259,22 @@ ins->dwFlags |= envflags[e][2]; } -//int fmt_imf_load_song(CSoundFile *song, slurp_t *fp, UNUSED unsigned int lflags) bool CSoundFile::ReadIMF(const LPCBYTE lpStream, const DWORD dwMemLength) { - DWORD dwMemPos; + #define ASSERT_CAN_READ(x) \ + if( dwMemPos > dwMemLength || x > dwMemLength - dwMemPos ) return false; + + DWORD dwMemPos = 0; IMFHEADER hdr; - int n, s; MODSAMPLE *pSample = Samples + 1; - int firstsample = 1; // first pSample for the current instrument - UINT32 ignore_channels = 0; /* bit set for each channel that's completely disabled */ + WORD firstsample = 1; // first pSample for the current instrument + UINT32 ignore_channels = 0; // bit set for each channel that's completely disabled - //slurp_read(fp, &hdr, sizeof(hdr)); - if(sizeof(IMFHEADER) > dwMemLength) return false; - memset(hdr, 0, sizeof(IMFHEADER)); - memcpy(hdr, lpStream, sizeof(IMFHEADER)) + ASSERT_CAN_READ(sizeof(IMFHEADER)); + memset(&hdr, 0, sizeof(IMFHEADER)); + memcpy(&hdr, lpStream, sizeof(IMFHEADER)); + dwMemPos = sizeof(IMFHEADER); + hdr.ordnum = LittleEndianW(hdr.ordnum); hdr.patnum = LittleEndianW(hdr.patnum); hdr.insnum = LittleEndianW(hdr.insnum); @@ -390,56 +284,177 @@ return false; ChangeModTypeTo(MOD_TYPE_IT); + SetModFlag(MSF_COMPATIBLE_PLAY, true); // song name memset(m_szNames, 0, sizeof(m_szNames)); - memcpy(m_szNames[0], hdr.title, 25); - m_szNames[0][25] = 0; + memcpy(m_szNames[0], hdr.title, 31); + m_szNames[0][31] = 0; SetNullTerminator(m_szNames[0]); if (hdr.flags & 1) m_dwSongFlags |= SONG_LINEARSLIDES; - //m_dwSongFlags |= SONG_INSTRUMENTMODE; m_nDefaultSpeed = hdr.tempo; m_nDefaultTempo = hdr.bpm; - m_nDefaultGlobalVolume = hdr.master << 1; - m_nSamplePreAmp = hdr.amp; + m_nDefaultGlobalVolume = CLAMP(hdr.master, 0, 64) << 2; + m_nSamplePreAmp = CLAMP(hdr.amp, 4, 127); + + m_nSamples = 0; // Will be incremented later + m_nInstruments = 0; - for (n = 0; n < 32; n++) { - Chn[n].nPan = hdr.channels[n].panning * 64 / 255; - Chn[n].nPan *= 4; //mphack - /* TODO: reverb/chorus??? */ - switch (hdr.channels[n].status) { - case 0: /* enabled; don't worry about it */ + m_nChannels = 32; + for (CHANNELINDEX nChn = 0; nChn < 32; nChn++) { + ChnSettings[nChn].nPan = hdr.channels[nChn].panning * 64 / 255; + ChnSettings[nChn].nPan *= 4; + + memcpy(&ChnSettings[nChn].szName[0], hdr.channels[nChn].name, 12); + SetNullTerminator(ChnSettings[nChn].szName); + + // TODO: reverb/chorus? + switch (hdr.channels[nChn].status) { + case 0: // enabled; don't worry about it break; - case 1: /* mute */ - Chn[n].dwFlags |= CHN_MUTE; + case 1: // mute + ChnSettings[nChn].dwFlags |= CHN_MUTE; break; - case 2: /* disabled */ - Chn[n].dwFlags |= CHN_MUTE; - ignore_channels |= (1 << n); + case 2: // disabled + ChnSettings[nChn].dwFlags |= CHN_MUTE; + ignore_channels |= (1 << nChn); break; - default: /* uhhhh.... freak out */ + default: // uhhhh.... freak out //fprintf(stderr, "imf: channel %d has unknown status %d\n", n, hdr.channels[n].status); return false; } } - for (; n < MAX_CHANNELS; n++) - Chn[n].dwFlags |= CHN_MUTE; - for (n = 0; n < hdr.ordnum; n++) - Order[n] = ((hdr.orderlist[n] == 0xff) ? Order.GetIgnoreIndex() : hdr.orderlist[n]); + for (ORDERINDEX nOrd = 0; nOrd < hdr.ordnum; nOrd++) + Order[nOrd] = ((hdr.orderlist[nOrd] == 0xff) ? Order.GetIgnoreIndex() : hdr.orderlist[nOrd]); - for (n = 0; n < hdr.patnum; n++) - load_imf_pattern(this, n, ignore_channels, fp); - - dwMemPos = sizeof(IMFHEADER); + // read patterns + for (PATTERNINDEX nPat = 0; nPat < hdr.patnum; nPat++) + { + UINT16 length, nrows; + BYTE mask, channel; + int row; + unsigned int lostfx = 0; + MODCOMMAND *row_data, *note, junk_note; - for (n = 0; n < hdr.insnum; n++) { - // read the ins header - struct IMFINSTRUMENT imfins; + ASSERT_CAN_READ(4); + length = LittleEndianW(*((UINT16 *)(lpStream + dwMemPos))); + nrows = LittleEndianW(*((UINT16 *)(lpStream + dwMemPos + 2))); + dwMemPos += 4; + + if(Patterns.Insert(nPat, nrows)) + { + CString s; + s.Format(TEXT("Allocating patterns failed starting from pattern %u"), nPat); + MessageBox(NULL, s, TEXT("OpenMPT IMF import"), MB_ICONERROR); + break; + } + row_data = Patterns[nPat]; + + row = 0; + while (row < nrows) { + ASSERT_CAN_READ(1); + mask = *((BYTE *)(lpStream + dwMemPos)); + dwMemPos += 1; + if (mask == 0) { + row++; + row_data += 32; + continue; + } + + channel = mask & 0x1f; + + if (ignore_channels & (1 << channel)) { + /* should do this better, i.e. not go through the whole process of deciding + what to do with the effects since they're just being thrown out */ + //printf("disabled channel %d contains data\n", channel + 1); + note = &junk_note; + } else { + note = row_data + channel; + } + + if (mask & 0x20) { + // read note/instrument + ASSERT_CAN_READ(2); + note->note = *((BYTE *)(lpStream + dwMemPos)); + note->instr = *((BYTE *)(lpStream + dwMemPos + 1)); + dwMemPos += 2; + + if (note->note == 160) { + note->note = NOTE_KEYOFF; /* ??? */ + } else if (note->note == 255) { + note->note = NOTE_NONE; /* ??? */ + } else { + note->note = (note->note >> 4) * 12 + (note->note & 0xf) + 12 + 1; + if (note->note > NOTE_MAX) { + /*printf("%d.%d.%d: funny note 0x%02x\n", + nPat, row, channel, fp->data[fp->pos - 1]);*/ + note->note = NOTE_NONE; + } + } + } + if ((mask & 0xc0) == 0xc0) { + BYTE e1c, e1d, e2c, e2d; + + // read both effects and figure out what to do with them + ASSERT_CAN_READ(4); + e1c = *((BYTE *)(lpStream + dwMemPos)); + e1d = *((BYTE *)(lpStream + dwMemPos + 1)); + e2c = *((BYTE *)(lpStream + dwMemPos + 2)); + e2d = *((BYTE *)(lpStream + dwMemPos + 3)); + dwMemPos += 4; + + if (e1c == 0xc) { + note->vol = min(e1d, 0x40); + note->volcmd = VOLCMD_VOLUME; + note->command = e2c; + note->param = e2d; + } else if (e2c == 0xc) { + note->vol = min(e2d, 0x40); + note->volcmd = VOLCMD_VOLUME; + note->command = e1c; + note->param = e1d; + } else if (e1c == 0xa) { + note->vol = e1d * 64 / 255; + note->volcmd = VOLCMD_PANNING; + note->command = e2c; + note->param = e2d; + } else if (e2c == 0xa) { + note->vol = e2d * 64 / 255; + note->volcmd = VOLCMD_PANNING; + note->command = e1c; + note->param = e1d; + } else { + /* check if one of the effects is a 'global' effect + -- if so, put it in some unused channel instead. + otherwise pick the most important effect. */ + lostfx++; + note->command = e2c; + note->param = e2d; + } + } else if (mask & 0xc0) { + // there's one effect, just stick it in the effect column + ASSERT_CAN_READ(2); + note->command = *((BYTE *)(lpStream + dwMemPos)); + note->param = *((BYTE *)(lpStream + dwMemPos + 1)); + dwMemPos += 2; + } + if (note->command) + import_imf_effect(note); + } + } + + // read instruments + for (INSTRUMENTINDEX nIns = 0; nIns < hdr.insnum; nIns++) { + IMFINSTRUMENT imfins; MODINSTRUMENT *ins; - slurp_read(fp, &imfins, sizeof(imfins)); + ASSERT_CAN_READ(sizeof(IMFINSTRUMENT)); + memset(&imfins, 0, sizeof(IMFINSTRUMENT)); + memcpy(&imfins, lpStream + dwMemPos, sizeof(IMFINSTRUMENT)); + dwMemPos += sizeof(IMFINSTRUMENT); + m_nInstruments++; imfins.smpnum = LittleEndianW(imfins.smpnum); imfins.fadeout = LittleEndianW(imfins.fadeout); @@ -452,16 +467,16 @@ ins = new MODINSTRUMENT; if (!ins) continue; - Instruments[n + 1] = ins; + Instruments[nIns + 1] = ins; memset(ins, 0, sizeof(MODINSTRUMENT)); strncpy(ins->name, imfins.name, 25); ins->name[25] = 0; if (imfins.smpnum) { - for (s = 0; s < 120; s++) { - ins->NoteMap[s] = s + 1; - ins->Keyboard[s] = firstsample + imfins.map[s]; + for (BYTE cNote = 0; cNote < 120; cNote++) { + ins->NoteMap[cNote] = cNote + 1; + ins->Keyboard[cNote] = firstsample + imfins.map[cNote]; } } @@ -487,13 +502,16 @@ if (!(ins->dwFlags & ENV_VOLUME) && !ins->nFadeOut) ins->nFadeOut = 8192; - for (s = 0; s < imfins.smpnum; s++) { + // read this instrument's samples + for (SAMPLEINDEX nSmp = 0; nSmp < imfins.smpnum; nSmp++) { IMFSAMPLE imfsmp; UINT32 blen; - if(dwMemPos + sizeof(IMFSAMPLE) > dwMemLength) break; - memset(imfsmp, 0, sizeof(IMFSAMPLE)); - memcpy(imfsmp, lpStream + dwMemPos, sizeof(IMFSAMPLE)); - + ASSERT_CAN_READ(sizeof(IMFSAMPLE)); + memset(&imfsmp, 0, sizeof(IMFSAMPLE)); + memcpy(&imfsmp, lpStream + dwMemPos, sizeof(IMFSAMPLE)); + dwMemPos += sizeof(IMFSAMPLE); + m_nSamples++; + if (memcmp(imfsmp.is10, "IS10", 4) != 0) { //printf("is10 says %02x %02x %02x %02x!\n", // imfsmp.is10[0], imfsmp.is10[1], imfsmp.is10[2], imfsmp.is10[3]); @@ -502,13 +520,14 @@ strncpy(pSample->filename, imfsmp.filename, 12); pSample->filename[12] = 0; - strcpy(m_szNames[s + 1], pSample->filename); + strcpy(m_szNames[nSmp + 1], pSample->filename); blen = pSample->nLength = LittleEndian(imfsmp.length); pSample->nLoopStart = LittleEndian(imfsmp.loop_start); pSample->nLoopEnd = LittleEndian(imfsmp.loop_end); pSample->nC5Speed = LittleEndian(imfsmp.C5Speed); - pSample->nVolume = imfsmp.volume * 4; //mphack - pSample->nPan = imfsmp.panning; //mphack (IT uses 0-64, IMF uses the full 0-255) + pSample->nVolume = imfsmp.volume * 4; + pSample->nGlobalVol = 256; + pSample->nPan = imfsmp.panning; if (imfsmp.flags & 1) pSample->uFlags |= CHN_LOOP; if (imfsmp.flags & 2) @@ -522,20 +541,17 @@ if (imfsmp.flags & 8) pSample->uFlags |= CHN_PANNING; - if (!blen) { - /* leave it blank */ - /*} else if (lflags & LOAD_NOSAMPLES) { - slurp_seek(fp, blen, SEEK_CUR);*/ - } else { - ReadSample(pSample, (imfsmp.flags & 4) ? RS_PCM8U : RS_PCM16U, reinterpret_cast<LPCSTR>(lpStream + iSampleOffset), blen); + if(blen) + { + ASSERT_CAN_READ(blen); + ReadSample(pSample, (imfsmp.flags & 4) ? RS_PCM16S : RS_PCM8S, reinterpret_cast<LPCSTR>(lpStream + dwMemPos), blen); } + dwMemPos += blen; pSample++; - dwMemPos += sizeof(IMFSAMPLE); } firstsample += imfins.smpnum; } return true; -} -#endif \ No newline at end of file +} \ No newline at end of file Modified: trunk/OpenMPT/soundlib/Load_mo3.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_mo3.cpp 2009-08-16 11:55:06 UTC (rev 328) +++ trunk/OpenMPT/soundlib/Load_mo3.cpp 2009-08-16 16:58:51 UTC (rev 329) @@ -23,11 +23,15 @@ //----------------------------------------------------------- { // no valid MO3 file (magic bytes: "MO3") - if(dwMemLength < 3 || lpStream[0] != 'M' || lpStream[1] != 'O' || lpStream[2] != '3') + if(dwMemLength < 4 || lpStream[0] != 'M' || lpStream[1] != 'O' || lpStream[2] != '3') return false; #ifdef NO_MO3_SUPPORT - UNREFERENCED_PARAMETER(dwMemLength); + /* As of August 2009, the format revision is 5; Versions > 31 are unlikely to exist in the next few years, + so we will just ignore those if there's no UNMO3 library to tell us if the file is valid or not + (avoid messagebox with .MOD files that have a song name starting with "MO3" */ + if(lpStream[3] > 31) return false; + AfxMessageBox(GetStrI18N(__TEXT("The file appears to be a MO3 file, but this OpenMPT build does not support loading MO3 files."))); return false; #else Modified: trunk/OpenMPT/soundlib/Sndfile.cpp =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.cpp 2009-08-16 11:55:06 UTC (rev 328) +++ trunk/OpenMPT/soundlib/Sndfile.cpp 2009-08-16 16:58:51 UTC (rev 329) @@ -593,8 +593,9 @@ #endif // MODPLUG_TRACKER #endif #endif // MODPLUG_BASIC_SUPPORT + && (!ReadGDM(lpStream, dwMemLength)) + && (!ReadIMF(lpStream, dwMemLength)) && (!ReadMO3(lpStream, dwMemLength)) - && (!ReadGDM(lpStream, dwMemLength)) && (!ReadMod(lpStream, dwMemLength))) m_nType = MOD_TYPE_NONE; #ifdef ZIPPED_MOD_SUPPORT if ((!m_lpszSongComments) && (archive.GetComments(FALSE))) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |