|
From: <sag...@us...> - 2012-03-31 23:06:13
|
Revision: 1235
http://modplug.svn.sourceforge.net/modplug/?rev=1235&view=rev
Author: saga-games
Date: 2012-03-31 23:06:04 +0000 (Sat, 31 Mar 2012)
Log Message:
-----------
[Ref] Rewrote MOD loader / saver.
[Ref] Rewrote MO3 loader.
[Ref] Added Order::ReadFromArray to read order items from fixed-size array.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/LOAD_AMF.CPP
trunk/OpenMPT/soundlib/LOAD_DSM.CPP
trunk/OpenMPT/soundlib/Load_669.cpp
trunk/OpenMPT/soundlib/Load_ams.cpp
trunk/OpenMPT/soundlib/Load_far.cpp
trunk/OpenMPT/soundlib/Load_it.cpp
trunk/OpenMPT/soundlib/Load_mdl.cpp
trunk/OpenMPT/soundlib/Load_med.cpp
trunk/OpenMPT/soundlib/Load_mo3.cpp
trunk/OpenMPT/soundlib/Load_mod.cpp
trunk/OpenMPT/soundlib/Load_mt2.cpp
trunk/OpenMPT/soundlib/Load_mtm.cpp
trunk/OpenMPT/soundlib/Load_ptm.cpp
trunk/OpenMPT/soundlib/Load_s3m.cpp
trunk/OpenMPT/soundlib/Load_stm.cpp
trunk/OpenMPT/soundlib/Load_umx.cpp
trunk/OpenMPT/soundlib/Load_xm.cpp
trunk/OpenMPT/soundlib/ModSequence.cpp
trunk/OpenMPT/soundlib/ModSequence.h
trunk/OpenMPT/soundlib/Sampleio.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
trunk/OpenMPT/soundlib/patternContainer.cpp
Modified: trunk/OpenMPT/soundlib/LOAD_AMF.CPP
===================================================================
--- trunk/OpenMPT/soundlib/LOAD_AMF.CPP 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/LOAD_AMF.CPP 2012-03-31 23:06:04 UTC (rev 1235)
@@ -238,7 +238,7 @@
#endif
p->command = 0;
}
- ConvertModCommand(p);
+ ConvertModCommand(*p);
pin += 4;
p++;
}
Modified: trunk/OpenMPT/soundlib/LOAD_DSM.CPP
===================================================================
--- trunk/OpenMPT/soundlib/LOAD_DSM.CPP 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/LOAD_DSM.CPP 2012-03-31 23:06:04 UTC (rev 1235)
@@ -110,7 +110,7 @@
m_nDefaultGlobalVolume = psong->globalvol << 2;
if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME)) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
m_nSamplePreAmp = psong->mastervol & 0x7F;
- Order.ReadAsByte(psong->orders, psong->numord, sizeof(psong->orders));
+ Order.ReadFromArray(psong->orders, psong->numord);
for (UINT iPan=0; iPan<16; iPan++)
{
@@ -199,7 +199,7 @@
}
m[ch].command = (BYTE)command;
m[ch].param = (BYTE)param;
- if (command) ConvertModCommand(&m[ch]);
+ if (command) ConvertModCommand(m[ch]);
}
} else
{
Modified: trunk/OpenMPT/soundlib/Load_669.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -125,7 +125,7 @@
ReadFixedLineLengthMessage(reinterpret_cast<const BYTE *>(fileHeader.songmessage), 108, 36, 0);
// Reading Orders
- Order.ReadAsByte(fileHeader.orders, 128, 128);
+ Order.ReadFromArray(fileHeader.orders);
m_nRestartPos = fileHeader.restartpos;
if(Order[m_nRestartPos] >= fileHeader.patterns) m_nRestartPos = 0;
Modified: trunk/OpenMPT/soundlib/Load_ams.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_ams.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_ams.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -251,7 +251,7 @@
{
m[ch].command = cmd;
m[ch].param = b2;
- ConvertModCommand(&m[ch]);
+ ConvertModCommand(m[ch]);
}
}
}
Modified: trunk/OpenMPT/soundlib/Load_far.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_far.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_far.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -116,7 +116,7 @@
dwMemPos += sizeof(FARHEADER2);
if (dwMemPos >= dwMemLength) return true;
- Order.ReadAsByte(pmh2->orders, pmh2->snglen, sizeof(pmh2->orders));
+ Order.ReadFromArray(pmh2->orders, pmh2->snglen);
m_nRestartPos = pmh2->loopto;
// Reading Patterns
dwMemPos += headerlen - (869 + pmh1->stlen);
Modified: trunk/OpenMPT/soundlib/Load_it.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_it.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_it.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -955,7 +955,7 @@
{
m[ch].command = cmd;
m[ch].param = param;
- S3MConvert(&m[ch], true);
+ S3MConvert(m[ch], true);
lastvalue[ch].command = m[ch].command;
lastvalue[ch].param = m[ch].param;
}
@@ -1382,17 +1382,17 @@
}
BYTE b = 0;
- UINT command = m->command;
- UINT param = m->param;
- UINT vol = 0xFF;
- UINT note = m->note;
- if (note) b |= 1;
- if ((note) && (note < NOTE_MIN_SPECIAL)) note--;
+ uint8 command = m->command;
+ uint8 param = m->param;
+ uint8 vol = 0xFF;
+ uint8 note = m->note;
+ if (note != NOTE_NONE) b |= 1;
+ if (m->IsNote()) note--;
if (note == NOTE_FADE && GetType() != MOD_TYPE_MPT) note = 0xF6;
if (m->instr) b |= 2;
if (m->volcmd)
{
- UINT volcmd = m->volcmd;
+ uint8 volcmd = m->volcmd;
switch(volcmd)
{
case VOLCMD_VOLUME: vol = m->vol; if (vol > 64) vol = 64; break;
@@ -1423,7 +1423,7 @@
if (vol != 0xFF) b |= 4;
if (command)
{
- S3MSaveConvert(&command, ¶m, true, compatibilityExport);
+ S3MSaveConvert(command, param, true, compatibilityExport);
if (command) b |= 8;
}
// Packing information
Modified: trunk/OpenMPT/soundlib/Load_mdl.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mdl.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_mdl.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -339,7 +339,7 @@
else
m_nChannels = i+1;
}
- Order.ReadAsByte(pmib->seq, norders, sizeof(pmib->seq));
+ Order.ReadFromArray(pmib->seq, norders);
break;
// ME: song message
case 0x454D:
Modified: trunk/OpenMPT/soundlib/Load_med.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_med.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_med.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -632,7 +632,7 @@
UINT nbo = BigEndianW(pmsh->songlen);
if (nbo >= MAX_ORDERS) nbo = MAX_ORDERS-1;
if (!nbo) nbo = 1;
- Order.ReadAsByte(pmsh->playseq, nbo, nbo);
+ Order.ReadFromArray(pmsh->playseq, nbo);
playtransp = pmsh->playtransp;
} else
{
Modified: trunk/OpenMPT/soundlib/Load_mo3.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mo3.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_mo3.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -15,26 +15,38 @@
#include "../mptrack/Mptrack.h"
#endif // MODPLUG_TRACKER
-// decode a MO3 file (returns the same "exit codes" as UNMO3.EXE, eg. 0=success)
+#ifndef NO_MO3_SUPPORT
+
+// Decode a MO3 file (returns the same "exit codes" as UNMO3.EXE, eg. 0=success)
// IN: data/len = MO3 data/len
// OUT: data/len = decoded data/len (if successful)
-typedef int (WINAPI * UNMO3_DECODE)(void **data, int *len);
-// free the data returned by UNMO3_Decode
-typedef void (WINAPI * UNMO3_FREE)(void *data);
+typedef int (WINAPI * UNMO3_DECODE)(const void **data, int *len);
+// Free the data returned by UNMO3_Decode
+typedef void (WINAPI * UNMO3_FREE)(const void *data);
+#endif // NO_MO3_SUPPORT
-bool CSoundFile::ReadMO3(LPCBYTE lpStream, const DWORD dwMemLength)
-//-----------------------------------------------------------------
+bool CSoundFile::ReadMO3(FileReader &file)
+//----------------------------------------
{
- // no valid MO3 file (magic bytes: "MO3")
- if(dwMemLength < 4 || lpStream[0] != 'M' || lpStream[1] != 'O' || lpStream[2] != '3')
+ file.Rewind();
+ const void *stream = file.GetRawData();
+ int length = file.GetLength();
+
+ // No valid MO3 file (magic bytes: "MO3")
+ if(file.GetLength() < 8 || !file.ReadMagic("MO3"))
+ {
return false;
+ }
#ifdef NO_MO3_SUPPORT
- /* As of August 2010, 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 log entry with .MOD files that have a song name starting with "MO3" */
- if(lpStream[3] > 31) return false;
+ // As of April 2012, 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 log entry with .MOD files that have a song name starting with "MO3".
+ if(file.ReadUint8() > 31)
+ {
+ return false;
+ }
#ifdef MODPLUG_TRACKER
if(m_pModDoc != nullptr) m_pModDoc->AddToLog(GetStrI18N(_TEXT("The file appears to be a MO3 file, but this OpenMPT build does not support loading MO3 files.")));
@@ -42,10 +54,8 @@
return false;
#else
- bool bResult = false; // result of trying to load the module, false == fail.
- int iLen = static_cast<int>(dwMemLength);
- void **mo3Stream = (void **)&lpStream;
+ bool result = false; // Result of trying to load the module, false == fail.
// try to load unmo3.dll dynamically.
#ifdef MODPLUG_TRACKER
@@ -56,7 +66,7 @@
#else
HMODULE unmo3 = LoadLibrary(_TEXT("unmo3.dll"));
#endif // MODPLUG_TRACKER
- if(unmo3 == NULL) // Didn't succeed.
+ if(unmo3 == nullptr) // Didn't succeed.
{
#ifdef MODPLUG_TRACKER
if(m_pModDoc != nullptr) m_pModDoc->AddToLog(GetStrI18N(_TEXT("Loading MO3 file failed because unmo3.dll could not be loaded.")));
@@ -67,30 +77,28 @@
UNMO3_DECODE UNMO3_Decode = (UNMO3_DECODE)GetProcAddress(unmo3, "UNMO3_Decode");
UNMO3_FREE UNMO3_Free = (UNMO3_FREE)GetProcAddress(unmo3, "UNMO3_Free");
- if(UNMO3_Decode != NULL && UNMO3_Free != NULL)
+ if(UNMO3_Decode != nullptr && UNMO3_Free != nullptr)
{
- if(UNMO3_Decode(mo3Stream, &iLen) == 0)
+ if(UNMO3_Decode(&stream, &length) == 0)
{
- /* if decoding was successful, mo3Stream and iLen will keep the new
- pointers now. */
+ // If decoding was successful, stream and length will keep the new pointers now.
- if(iLen > 0)
+ if(length > 0)
{
- bResult = true;
- if ((!ReadXM((const BYTE *)*mo3Stream, (DWORD)iLen))
- && (!ReadIT((const BYTE *)*mo3Stream, (DWORD)iLen))
- && (!ReadS3M((const BYTE *)*mo3Stream, (DWORD)iLen))
-#ifndef FASTSOUNDLIB
- && (!ReadMTM((const BYTE *)*mo3Stream, (DWORD)iLen))
-#endif // FASTSOUNDLIB
- && (!ReadMod((const BYTE *)*mo3Stream, (DWORD)iLen))) bResult = false;
+ FileReader unpackedFile(static_cast<const char*>(stream), length);
+
+ result = ReadXM(static_cast<const LPCBYTE>(stream), length)
+ || ReadIT(static_cast<const LPCBYTE>(stream), length)
+ || ReadS3M(static_cast<const LPCBYTE>(stream), length)
+ || ReadMTM(static_cast<const LPCBYTE>(stream), length)
+ || ReadMod(unpackedFile);
}
- UNMO3_Free(*mo3Stream);
+ UNMO3_Free(stream);
}
}
FreeLibrary(unmo3);
}
- return bResult;
+ return result;
#endif // NO_MO3_SUPPORT
}
Modified: trunk/OpenMPT/soundlib/Load_mod.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mod.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_mod.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -2,9 +2,8 @@
* Load_mod.cpp
* ------------
* Purpose: MOD / NST (ProTracker / NoiseTracker) module loader / saver
- * Notes : This code is OLD and HORRIBLE!
+ * Notes : (currently none)
* Authors: Olivier Lapicque
- * Adam Goode (endian and char fixes for PPC)
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
@@ -13,68 +12,59 @@
#include "stdafx.h"
#include "Loaders.h"
-#pragma warning(disable:4244) //"conversion from 'type1' to 'type2', possible loss of data"
-
-extern WORD ProTrackerPeriodTable[6*12];
-
-void CSoundFile::ConvertModCommand(ModCommand *m) const
+void CSoundFile::ConvertModCommand(ModCommand &m) const
//-----------------------------------------------------
{
- UINT command = m->command, param = m->param;
+ switch(m.command)
+ {
+ case 0x00: if(m.param) m.command = CMD_ARPEGGIO; break;
+ case 0x01: m.command = CMD_PORTAMENTOUP; break;
+ case 0x02: m.command = CMD_PORTAMENTODOWN; break;
+ case 0x03: m.command = CMD_TONEPORTAMENTO; break;
+ case 0x04: m.command = CMD_VIBRATO; break;
+ case 0x05: m.command = CMD_TONEPORTAVOL; if(m.param & 0xF0) m.param &= 0xF0; break;
+ case 0x06: m.command = CMD_VIBRATOVOL; if(m.param & 0xF0) m.param &= 0xF0; break;
+ case 0x07: m.command = CMD_TREMOLO; break;
+ case 0x08: m.command = CMD_PANNING8; break;
+ case 0x09: m.command = CMD_OFFSET; break;
+ case 0x0A: m.command = CMD_VOLUMESLIDE; if(m.param & 0xF0) m.param &= 0xF0; break;
+ case 0x0B: m.command = CMD_POSITIONJUMP; break;
+ case 0x0C: m.command = CMD_VOLUME; break;
+ case 0x0D: m.command = CMD_PATTERNBREAK; m.param = ((m.param >> 4) * 10) + (m.param & 0x0F); break;
+ case 0x0E: m.command = CMD_MODCMDEX; break;
+ case 0x0F: m.command = (m.param <= ((GetType() & (MOD_TYPE_MOD)) ? 0x20u : 0x1Fu)) ? CMD_SPEED : CMD_TEMPO;
+ if ((m.param == 0xFF) && (GetNumSamples() == 15) && (GetType() & MOD_TYPE_MOD)) m.command = CMD_NONE; break; //<rewbs> what the hell is this?! :) //<jojo> it's the "stop tune" command! :-P
- switch(command)
- {
- case 0x00: if (param) command = CMD_ARPEGGIO; 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; if (param & 0xF0) param &= 0xF0; break;
- case 0x06: command = CMD_VIBRATOVOL; if (param & 0xF0) param &= 0xF0; break;
- case 0x07: command = CMD_TREMOLO; break;
- case 0x08: command = CMD_PANNING8; break;
- case 0x09: command = CMD_OFFSET; break;
- case 0x0A: command = CMD_VOLUMESLIDE; if (param & 0xF0) param &= 0xF0; break;
- case 0x0B: command = CMD_POSITIONJUMP; break;
- case 0x0C: command = CMD_VOLUME; break;
- case 0x0D: command = CMD_PATTERNBREAK; param = ((param >> 4) * 10) + (param & 0x0F); break;
- case 0x0E: command = CMD_MODCMDEX; break;
- case 0x0F: command = (param <= ((m_nType & (MOD_TYPE_MOD)) ? 0x20u : 0x1Fu)) ? CMD_SPEED : CMD_TEMPO;
- if ((param == 0xFF) && (m_nSamples == 15) && (m_nType & MOD_TYPE_MOD)) command = 0; break; //<rewbs> what the hell is this?! :) //<jojo> it's the "stop tune" command! :-P
// Extension for XM extended effects
- case 'G' - 55: command = CMD_GLOBALVOLUME; break; //16
- case 'H' - 55: command = CMD_GLOBALVOLSLIDE; if (param & 0xF0) param &= 0xF0; break;
- case 'K' - 55: command = CMD_KEYOFF; break;
- case 'L' - 55: command = CMD_SETENVPOSITION; break;
- case 'M' - 55: command = CMD_CHANNELVOLUME; break;
- case 'N' - 55: command = CMD_CHANNELVOLSLIDE; break;
- case 'P' - 55: command = CMD_PANNINGSLIDE; if (param & 0xF0) param &= 0xF0; break;
- case 'R' - 55: command = CMD_RETRIG; break;
- case 'T' - 55: command = CMD_TREMOR; break;
- case 'X' - 55: command = CMD_XFINEPORTAUPDOWN; break;
- case 'Y' - 55: command = CMD_PANBRELLO; break; //34
- case 'Z' - 55: command = CMD_MIDI; break; //35
- case '\\' - 56: command = CMD_SMOOTHMIDI; break; //rewbs.smoothVST: 36 - note: this is actually displayed as "-" in FT2, but seems to be doing nothing.
- //case ':' - 21: command = CMD_DELAYCUT; break; //37
- case '#' + 3: command = CMD_XPARAM; break; //rewbs.XMfixes - XParam is 38
- default: command = CMD_NONE;
+ case 'G' - 55: m.command = CMD_GLOBALVOLUME; break; //16
+ case 'H' - 55: m.command = CMD_GLOBALVOLSLIDE; if (m.param & 0xF0) m.param &= 0xF0; break;
+ case 'K' - 55: m.command = CMD_KEYOFF; break;
+ case 'L' - 55: m.command = CMD_SETENVPOSITION; break;
+ case 'M' - 55: m.command = CMD_CHANNELVOLUME; break;
+ case 'N' - 55: m.command = CMD_CHANNELVOLSLIDE; break;
+ case 'P' - 55: m.command = CMD_PANNINGSLIDE; if (m.param & 0xF0) m.param &= 0xF0; break;
+ case 'R' - 55: m.command = CMD_RETRIG; break;
+ case 'T' - 55: m.command = CMD_TREMOR; break;
+ case 'X' - 55: m.command = CMD_XFINEPORTAUPDOWN; break;
+ case 'Y' - 55: m.command = CMD_PANBRELLO; break; //34
+ case 'Z' - 55: m.command = CMD_MIDI; break; //35
+ case '\\' - 56: m.command = CMD_SMOOTHMIDI; break; //rewbs.smoothVST: 36 - note: this is actually displayed as "-" in FT2, but seems to be doing nothing.
+ //case ':' - 21: m.command = CMD_DELAYCUT; break; //37
+ case '#' + 3: m.command = CMD_XPARAM; break; //rewbs.XMfixes - Xm.param is 38
+ default: m.command = CMD_NONE;
}
- m->command = command;
- m->param = param;
}
-WORD CSoundFile::ModSaveCommand(const ModCommand *m, const bool bXM, const bool bCompatibilityExport) const
-//---------------------------------------------------------------------------------------------------------
+void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool compatibilityExport) const
+//------------------------------------------------------------------------------------------------------
{
- UINT command = m->command, param = m->param;
-
switch(command)
{
- case CMD_NONE: command = param = 0; break;
- case CMD_ARPEGGIO: command = 0; break;
+ case CMD_NONE: command = param = 0; break;
+ case CMD_ARPEGGIO: command = 0; break;
case CMD_PORTAMENTOUP:
- if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT))
+ if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT))
{
if ((param & 0xF0) == 0xE0) { command = 0x0E; param = ((param & 0x0F) >> 2) | 0x10; break; }
else if ((param & 0xF0) == 0xF0) { command = 0x0E; param &= 0x0F; param |= 0x10; break; }
@@ -82,7 +72,7 @@
command = 0x01;
break;
case CMD_PORTAMENTODOWN:
- if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT))
+ if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT))
{
if ((param & 0xF0) == 0xE0) { command = 0x0E; param= ((param & 0x0F) >> 2) | 0x20; break; }
else if ((param & 0xF0) == 0xF0) { command = 0x0E; param &= 0x0F; param |= 0x20; break; }
@@ -104,7 +94,7 @@
}
else if(param == 0xA4) // surround
{
- if(bCompatibilityExport || !bXM)
+ if(compatibilityExport || !toXM)
{
command = param = 0;
}
@@ -122,8 +112,8 @@
case CMD_VOLUME: command = 0x0C; break;
case CMD_PATTERNBREAK: command = 0x0D; param = ((param / 10) << 4) | (param % 10); break;
case CMD_MODCMDEX: command = 0x0E; break;
- case CMD_SPEED: command = 0x0F; param = min(param, (bXM) ? 0x1Fu : 0x20u); break;
- case CMD_TEMPO: command = 0x0F; param = max(param, (bXM) ? 0x20u : 0x21u); break;
+ case CMD_SPEED: command = 0x0F; param = min(param, (toXM) ? 0x1Fu : 0x20u); break;
+ case CMD_TEMPO: command = 0x0F; param = max(param, (toXM) ? 0x20u : 0x21u); break;
case CMD_GLOBALVOLUME: command = 'G' - 55; break;
case CMD_GLOBALVOLSLIDE: command = 'H' - 55; break;
case CMD_KEYOFF: command = 'K' - 55; break;
@@ -134,31 +124,31 @@
case CMD_RETRIG: command = 'R' - 55; break;
case CMD_TREMOR: command = 'T' - 55; break;
case CMD_XFINEPORTAUPDOWN:
- if(bCompatibilityExport && (param & 0xF0) > 2) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here.
+ if(compatibilityExport && (param & 0xF0) > 2) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here.
command = param = 0;
else
command = 'X' - 55;
break;
case CMD_PANBRELLO:
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = 'Y' - 55;
break;
case CMD_MIDI:
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = 'Z' - 55;
break;
case CMD_SMOOTHMIDI: //rewbs.smoothVST: 36
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = '\\' - 56;
break;
case CMD_XPARAM: //rewbs.XMfixes - XParam is 38
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = '#' + 3;
@@ -171,7 +161,7 @@
case 0x30: command = 0x0E; param = (param & 0x0F) | 0x40; break;
case 0x40: command = 0x0E; param = (param & 0x0F) | 0x70; break;
case 0x90:
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = 'X' - 55;
@@ -188,61 +178,168 @@
command = param = 0;
}
- // don't even think about saving XM effects in MODs...
- if(command > 0x0F && !bXM)
+ // Don't even think about saving XM effects in MODs...
+ if(command > 0x0F && !toXM)
+ {
command = param = 0;
-
- return (WORD)((command << 8) | (param));
+ }
}
#pragma pack(push, 1)
+// File Header
+struct MODFileHeader
+{
+ uint8 numOrders;
+ uint8 restartPos;
+ uint8 orderList[128];
+ char magic[4];
+
+ // Check if header magic equals a given string.
+ bool IsMagic(const char *otherMagic) const
+ {
+ return (*reinterpret_cast<const uint32 *>(magic) == *reinterpret_cast<const uint32 *>(otherMagic));
+ }
+};
+
+STATIC_ASSERT(sizeof(MODFileHeader) == 134);
+
+
+// Sample Header
struct MODSampleHeader
{
char name[22];
uint16 length;
uint8 finetune;
uint8 volume;
- uint16 loopstart;
- uint16 looplen;
-};
+ uint16 loopStart;
+ uint16 loopLength;
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesBE(length);
+ SwapBytesBE(loopStart);
+ SwapBytesBE(loopLength);
+ }
-struct MODHeader
-{
- uint8 nOrders;
- uint8 nRestartPos;
- uint8 Orders[128];
- char Magic[4];
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.uFlags = 0;
+ mptSmp.nLength = length * 2;
+ mptSmp.RelativeTone = 0;
+ mptSmp.nFineTune = MOD2XMFineTune(finetune & 0x0F);
+ mptSmp.nVolume = 4 * min(volume, 64);
+ mptSmp.nGlobalVol = 64;
+ mptSmp.nPan = 128;
+
+ SmpLength lStart = loopStart * 2;
+ SmpLength lLength = loopLength * 2;
+ // Fix loops
+ if(lLength > 2 && (lStart + lLength > mptSmp.nLength)
+ && (lStart / 2 + lLength <= mptSmp.nLength))
+ {
+ lStart /= 2;
+ }
+ mptSmp.nLoopStart = lStart;
+ mptSmp.nLoopEnd = lStart + lLength;
+
+ if (mptSmp.nLength == 2)
+ {
+ mptSmp.nLength = 0;
+ }
+
+ if (mptSmp.nLength)
+ {
+ if(mptSmp.nLoopStart >= mptSmp.nLength)
+ {
+ mptSmp.nLoopStart = mptSmp.nLength - 1;
+ }
+ if(mptSmp.nLoopEnd > mptSmp.nLength)
+ {
+ mptSmp.nLoopEnd = mptSmp.nLength;
+ }
+ if(mptSmp.nLoopStart > mptSmp.nLoopEnd || mptSmp.nLoopEnd < 4 || mptSmp.nLoopEnd - mptSmp.nLoopStart < 4)
+ {
+ mptSmp.nLoopStart = 0;
+ mptSmp.nLoopEnd = 0;
+ }
+
+ // Fix for most likely broken sample loops. This fixes super_sufm_-_new_life.mod which has a long sample which is looped from 0 to 4.
+ if(mptSmp.nLoopEnd <= 8 && mptSmp.nLoopStart == 0 && mptSmp.nLength > mptSmp.nLoopEnd)
+ {
+ mptSmp.nLoopEnd = 0;
+ }
+ if (mptSmp.nLoopEnd > mptSmp.nLoopStart)
+ {
+ mptSmp.uFlags |= CHN_LOOP;
+ }
+ }
+ }
+
+ // Convert OpenMPT's internal sample header to a MOD sample header.
+ SmpLength ConvertToMOD(const ModSample &mptSmp)
+ {
+ SmpLength writeLength = mptSmp.nLength;
+ // If the sample size is odd, we have to add a padding byte, as all sample sizes in MODs are even.
+ if((writeLength % 2) != 0)
+ {
+ writeLength++;
+ }
+ LimitMax(writeLength, SmpLength(0x1FFF0));
+
+ length = static_cast<uint16>(writeLength / 2);
+
+ if(mptSmp.RelativeTone < 0)
+ {
+ finetune = 0x08;
+ } else if(mptSmp.RelativeTone > 0)
+ {
+ finetune = 0x07;
+ } else
+ {
+ finetune = XM2MODFineTune(mptSmp.nFineTune);
+ }
+ volume = static_cast<uint8>(mptSmp.nVolume / 4);
+
+ loopStart = 0;
+ loopLength = 1;
+ if(mptSmp.uFlags & CHN_LOOP)
+ {
+ loopStart = static_cast<uint16>(mptSmp.nLoopStart / 2);
+ loopLength = static_cast<uint16>(max(1, (mptSmp.nLoopEnd - mptSmp.nLoopStart) / 2));
+ }
+
+ return writeLength;
+ }
};
+STATIC_ASSERT(sizeof(MODSampleHeader) == 30);
+
+
#pragma pack(pop)
-bool IsMagic(const char *s1, const char *s2)
-{
- return (*reinterpret_cast<const uint32 *>(s1) == *reinterpret_cast<const uint32 *>(s2));
-}
// Functor for fixing VBlank MODs and MODs with 7-bit panning
struct FixMODPatterns
//===================
{
- FixMODPatterns(bool bVBlank, bool bPanning)
+ FixMODPatterns(bool fixVBlank, bool fixPanning)
{
- this->bVBlank = bVBlank;
- this->bPanning = bPanning;
+ this->fixVBlank = fixVBlank;
+ this->fixPanning = fixPanning;
}
- void operator()(ModCommand& m)
+ void operator()(ModCommand &m)
{
// Fix VBlank MODs
- if(m.command == CMD_TEMPO && this->bVBlank)
+ if(m.command == CMD_TEMPO && this->fixVBlank)
{
m.command = CMD_SPEED;
}
// Fix MODs with 7-bit + surround panning
- if(m.command == CMD_PANNING8 && this->bPanning)
+ if(m.command == CMD_PANNING8 && this->fixPanning)
{
if(m.param == 0xA4)
{
@@ -255,244 +352,336 @@
}
}
- bool bVBlank, bPanning;
+ bool fixVBlank, fixPanning;
};
-bool CSoundFile::ReadMod(const BYTE *lpStream, DWORD dwMemLength)
-//---------------------------------------------------------------
+bool CSoundFile::ReadMod(FileReader &file)
+//----------------------------------------
{
- char s[1024];
- DWORD dwMemPos, dwTotalSampleLen;
- const MODHeader *header;
- UINT nErr;
+ file.Rewind();
- if ((!lpStream) || (dwMemLength < 0x600)) return false;
- dwMemPos = 20;
+ MODFileHeader fileHeader;
+ if(!file.Seek(20 + sizeof(MODSampleHeader) * 31) || !file.Read(fileHeader))
+ {
+ return false;
+ }
+
m_nSamples = 31;
m_nChannels = 4;
- header = reinterpret_cast<const MODHeader *>(lpStream + dwMemPos + sizeof(MODSampleHeader) * 31);
+ // Check MOD Magic
+ if(fileHeader.IsMagic("M.K.") // ProTracker and compatible
+ || fileHeader.IsMagic("M!K!") // ProTracker (64+ patterns)
+ || fileHeader.IsMagic("M&K!") // NoiseTracker
+ || fileHeader.IsMagic("N.T.") // NoiseTracker
+ || fileHeader.IsMagic("FEST")) // jobbig.mod by Mahoney
+ {
+ m_nChannels = 4;
+ } else if(fileHeader.IsMagic("CD81") // Falcon
+ || fileHeader.IsMagic("OKTA") // Oktalyzer
+ || fileHeader.IsMagic("OCTA")) // Oktalyzer
+ {
+ m_nChannels = 8;
+ } else if((!memcmp(fileHeader.magic, "FLT", 3) || !memcmp(fileHeader.magic, "EXO", 3)) && fileHeader.magic[3] >= '4' && fileHeader.magic[3] <= '9')
+ {
+ // FLTx / EXOx - Startrekker by Exolon / Fairlight
+ m_nChannels = fileHeader.magic[3] - '0';
+ } else if(fileHeader.magic[0] >= '1' && fileHeader.magic[0] <= '9' && !memcmp(fileHeader.magic + 1, "CHN", 3))
+ {
+ // xCHN - Many trackers
+ m_nChannels = fileHeader.magic[0] - '0';
+ } else if(fileHeader.magic[0] >= '1' && fileHeader.magic[0] <= '9' && fileHeader.magic[1]>='0' && fileHeader.magic[1] <= '9'
+ && (!memcmp(fileHeader.magic + 2, "CH", 2) || !memcmp(fileHeader.magic + 2, "CN", 2)))
+ {
+ // xxCN / xxCH - Many trackers
+ m_nChannels = (fileHeader.magic[0] - '0') * 10 + fileHeader.magic[1] - '0';
+ } else if(!memcmp(fileHeader.magic, "TDZ", 3) && fileHeader.magic[3] >= '4' && fileHeader.magic[3] <= '9')
+ {
+ // TDZx - TakeTracker
+ m_nChannels = fileHeader.magic[3] - '0';
+ } else
+ {
+ // Ultimate SoundTracker MODs - no magic bytes
+ m_nSamples = 15;
+ }
- // Check Mod Magic
- memcpy(s, header->Magic, 4);
- if ((IsMagic(s, "M.K.")) || (IsMagic(s, "M!K!"))
- || (IsMagic(s, "M&K!")) || (IsMagic(s, "N.T.")) || (IsMagic(s, "FEST"))) m_nChannels = 4;
- else if ((IsMagic(s, "CD81")) || (IsMagic(s, "OKTA"))) m_nChannels = 8;
-
- else if (!memcmp(s, "FLT", 3) && s[3] >= '4' && s[3] <= '9') m_nChannels = s[3] - '0';
- else if (s[0] >= '1' && s[0] <= '9' && !memcmp(s + 1, "CHN", 3)) m_nChannels = s[0] - '0';
- else if (s[0] >= '1' && s[0] <= '9' && s[1]>='0' && s[1] <= '9' && (!memcmp(s + 2, "CH", 2) || !memcmp(s + 2, "CN", 2))) m_nChannels = (s[0] - '0') * 10 + s[1] - '0';
- else if (!memcmp(s, "TDZ", 3) && s[3] >= '4' && s[3] <= '9') m_nChannels = s[3] - '0';
- else m_nSamples = 15; // Ultimate SoundTracker MODs
-
LimitMax(m_nChannels, MAX_BASECHANNELS);
// Startrekker 8 channel mod (needs special treatment, see below)
- bool isFLT8 = IsMagic(s, "FLT8");
+ bool isFLT8 = fileHeader.IsMagic("FLT8");
// Only apply VBlank tests to M.K. (ProTracker) modules.
- const bool bMdKd = IsMagic(s, "M.K.");
+ const bool isMdKd = fileHeader.IsMagic("M.K.");
// Load Samples
- nErr = 0;
- dwTotalSampleLen = 0;
- for (UINT i=1; i<=m_nSamples; i++)
+ int m15Errors = 0;
+ size_t totalSampleLen = 0;
+
+ file.Seek(20);
+ for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
- const MODSampleHeader *pms = reinterpret_cast<const MODSampleHeader *>(lpStream + dwMemPos);
- ModSample *psmp = &Samples[i];
- UINT loopstart, looplen;
+ MODSampleHeader sampleHeader;
+ if(!file.ReadConvertEndianness(sampleHeader))
+ {
+ return false;
+ }
+ sampleHeader.ConvertToMPT(Samples[smp]);
- StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[i], pms->name);
+ StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[smp], sampleHeader.name);
- psmp->uFlags = 0;
- psmp->nLength = BigEndianW(pms->length)*2;
- dwTotalSampleLen += psmp->nLength;
- psmp->nFineTune = MOD2XMFineTune(pms->finetune & 0x0F);
- psmp->nVolume = 4*pms->volume;
- if (psmp->nVolume > 256) { psmp->nVolume = 256; nErr++; }
- psmp->nGlobalVol = 64;
- psmp->nPan = 128;
- loopstart = BigEndianW(pms->loopstart)*2;
- looplen = BigEndianW(pms->looplen)*2;
- // Fix loops
- if ((looplen > 2) && (loopstart+looplen > psmp->nLength)
- && (loopstart/2+looplen <= psmp->nLength))
+ totalSampleLen += Samples[smp].nLength;
+
+ // M15 Sanity checks
+ if(sampleHeader.volume > 256)
{
- loopstart /= 2;
+ m15Errors++;
}
- psmp->nLoopStart = loopstart;
- psmp->nLoopEnd = loopstart + looplen;
- if (psmp->nLength < 4) psmp->nLength = 0;
- if (psmp->nLength)
+ if(sampleHeader.length > 32768)
{
- UINT derr = 0;
- if (psmp->nLoopStart >= psmp->nLength) { psmp->nLoopStart = psmp->nLength-1; derr|=1; }
- if (psmp->nLoopEnd > psmp->nLength) { psmp->nLoopEnd = psmp->nLength; derr |= 1; }
- if (psmp->nLoopStart > psmp->nLoopEnd) derr |= 1;
- if ((psmp->nLoopStart > psmp->nLoopEnd) || (psmp->nLoopEnd < 4)
- || (psmp->nLoopEnd - psmp->nLoopStart < 4))
- {
- psmp->nLoopStart = 0;
- psmp->nLoopEnd = 0;
- }
- // Fix for most likely broken sample loops. This fixes super_sufm_-_new_life.mod which has a long sample which is looped from 0 to 4.
- if(psmp->nLoopEnd <= 8 && psmp->nLoopStart == 0 && psmp->nLength > psmp->nLoopEnd)
- {
- psmp->nLoopEnd = 0;
- }
- if (psmp->nLoopEnd > psmp->nLoopStart)
- {
- psmp->uFlags |= CHN_LOOP;
- }
+ m15Errors++;
}
- dwMemPos += sizeof(MODSampleHeader);
+ if(sampleHeader.finetune != 0)
+ {
+ m15Errors++;
+ }
}
- if ((m_nSamples == 15) && (dwTotalSampleLen > dwMemLength * 4)) return false;
+ // Sanity checks...
+ if(GetNumSamples() == 15)
+ {
+ if(totalSampleLen > file.GetLength() * 4 || fileHeader.numOrders > 128 || fileHeader.restartPos > 128)
+ {
+ return false;
+ }
+ }
- header = reinterpret_cast<const MODHeader *>(lpStream + dwMemPos);
- dwMemPos += sizeof(MODHeader);
+ // Re-read file header, as it is found at another position in M15 files.
+ file.Read(fileHeader);
- if (m_nSamples == 15) dwMemPos -= 4;
- Order.ReadAsByte(header->Orders, 128, 128);
+ Order.ReadFromArray(fileHeader.orderList);
- UINT nbp, nbpbuggy, nbpbuggy2, norders;
-
- norders = header->nOrders;
- if ((!norders) || (norders > 0x80))
+ ORDERINDEX realOrders = fileHeader.numOrders;
+ if(realOrders > 128)
{
- norders = 0x80;
- while ((norders > 1) && (!Order[norders-1])) norders--;
+ // beatwave.mod by Sidewinder (the version from ModArchive, not ModLand) claims to have 129 orders.
+ realOrders = 128;
+ } else if(realOrders == 0)
+ {
+ // Is this necessary?
+ realOrders = 128;
+ while(realOrders > 1 && Order[realOrders - 1] == 0)
+ {
+ realOrders--;
+ }
}
- nbpbuggy = 0;
- nbpbuggy2 = 0;
- nbp = 0;
- for (UINT iord=0; iord<128; iord++)
+
+ // Do some order sanity checks
+
+ PATTERNINDEX numPatterns = 0; // Total number of patterns in file (determined by going through the whole order list) with pattern number < 128
+ PATTERNINDEX officialPatterns = 0; // Number of patterns only found in the "official" part of the order list (i.e. order positions < claimed order length)
+ PATTERNINDEX numPatternsIllegal = 0; // Total number of patterns in file, also counting in "invalid" pattern indexes >= 128
+
+ for(ORDERINDEX ord = 0; ord < 128; ord++)
{
- UINT i = Order[iord];
- if ((i < 0x80) && (nbp <= i))
+ PATTERNINDEX pat = Order[ord];
+ if(pat < 128 && numPatterns <= pat)
{
- nbp = i+1;
- if (iord<norders) nbpbuggy = nbp;
+ numPatterns = pat + 1;
+ if(ord < realOrders)
+ {
+ officialPatterns = numPatterns;
+ }
}
- if (i >= nbpbuggy2) nbpbuggy2 = i+1;
+ if(pat >= numPatternsIllegal)
+ {
+ numPatternsIllegal = pat + 1;
+ }
- // from mikmod: if the file says FLT8, but the orderlist has odd numbers, it's probably really an FLT4
- if(isFLT8 && (Order[iord] & 1))
+ // From mikmod: if the file says FLT8, but the orderlist has odd numbers, it's probably really an FLT4
+ if(isFLT8 && (Order[ord] % 2) != 0)
{
m_nChannels = 4;
isFLT8 = false;
}
// chances are very high that we're dealing with a non-MOD file here.
- if(m_nSamples == 15 && i >= 0x80)
+ if(GetNumSamples() == 15 && pat >= 64)
+ {
return false;
+ }
}
+ if(!numPatterns)
+ {
+ return false;
+ }
+
if(isFLT8)
{
// FLT8 has only even order items, so divide by two.
- for(ORDERINDEX nOrd = 0; nOrd < Order.GetLength(); nOrd++)
- Order[nOrd] >>= 1;
+ for(ORDERINDEX ord = 0; ord < Order.GetLength(); ord++)
+ {
+ Order[ord] /= 2;
+ }
}
- for(UINT iend = norders; iend < 0x80; iend++) Order[iend] = Order.GetInvalidPatIndex();
+ // Fill order tail with stop patterns
+ for(ORDERINDEX ord = realOrders; ord < 128; ord++)
+ {
+ Order[ord] = Order.GetInvalidPatIndex();
+ }
- norders--;
- m_nRestartPos = header->nRestartPos;
- if (m_nRestartPos >= 0x78) m_nRestartPos = 0;
- if (m_nRestartPos + 1u >= norders) m_nRestartPos = 0;
- if (!nbp) return false;
- DWORD dwWowTest = dwTotalSampleLen+dwMemPos;
- if ((IsMagic(header->Magic, "M.K.")) && (dwWowTest + nbp*8*256 == dwMemLength)) m_nChannels = 8;
- if ((nbp != nbpbuggy) && (dwWowTest + nbp*m_nChannels*256 != dwMemLength))
+ // Restart position sanity checks
+ realOrders--;
+ m_nRestartPos = fileHeader.restartPos;
+
+ // About the weird 0x78 restart pos thing... I also have no idea where it comes from, XMP also has this check but doesn't comment on it, either.
+ // It is said that NoiseTracker writes 0x78, but I've also seen this in M15 files.
+ // Files that have restart pos == 0x78: action's batman by DJ Uno (M.K.), 3ddance.mod (M15), VALLEY.MOD (M.K.), WormsTDC.MOD (M.K.), ZWARTZ.MOD (M.K.)
+ ASSERT(m_nRestartPos != 0x78 || m_nRestartPos + 1u >= realOrders);
+ if(m_nRestartPos >= 128 || m_nRestartPos + 1u >= realOrders || m_nRestartPos == 0x78)
{
- if (dwWowTest + nbpbuggy*m_nChannels*256 == dwMemLength) nbp = nbpbuggy;
- else nErr += 8;
- } else
- if ((nbpbuggy2 > nbp) && (dwWowTest + nbpbuggy2*m_nChannels*256 == dwMemLength))
+ m_nRestartPos = 0;
+ }
+
+ const size_t patternStartOffset = 20 + GetNumSamples() * sizeof(MODSampleHeader) + sizeof(MODFileHeader) - (GetNumSamples() == 15 ? 4 : 0);
+ const size_t sizeWithoutPatterns = totalSampleLen + patternStartOffset;
+
+ if(isMdKd)
{
- nbp = nbpbuggy2;
+ // Check if this is a Mod's Grave WOW file... Never seen one of those, but apparently they *do* exist.
+ // Basically, WOW files seem to use the M.K. extensions, but are actually 8CHN files.
+ if(sizeWithoutPatterns + numPatterns * 8 * 256 == file.GetLength())
+ {
+ m_nChannels = 8;
+ }
}
- if ((dwWowTest < 0x600) || (dwWowTest > dwMemLength)) nErr += 8;
- if ((m_nSamples == 15) && (nErr >= 16)) return false;
- // Default settings
+
+ // Now we have to check if the "hidden" patterns in the order list are actually real, i.e. if they are saved in the file.
+ if(numPatterns != officialPatterns && sizeWithoutPatterns + numPatterns * m_nChannels * 256 != file.GetLength())
+ {
+ // Ok, size does not match the number of patterns including "hidden" patterns. Does it match the number of "official" patterns?
+ if(sizeWithoutPatterns + officialPatterns * m_nChannels * 256 == file.GetLength())
+ {
+ // It does! Forget about that crap.
+ numPatterns = officialPatterns;
+ } else
+ {
+ // Well... Neither fits... So this must be a broken file.
+ m15Errors += 8;
+ }
+ } else if(numPatternsIllegal > numPatterns && sizeWithoutPatterns + numPatternsIllegal * m_nChannels * 256 == file.GetLength())
+ {
+ // Even those illegal pattern indexes (> 128) appear to be valid... What a weird file!
+ numPatterns = numPatternsIllegal;
+ }
+
+ // Some final M15 sanity checks...
+ if(sizeWithoutPatterns < 0x600 || sizeWithoutPatterns > file.GetLength())
+ {
+ m15Errors += 8;
+ }
+
+ if(GetNumSamples() == 15 && m15Errors >= 16)
+ {
+ return false;
+ }
+
+ // Now we can be pretty sure that this is a valid MOD file. Set up default song settings.
m_nType = MOD_TYPE_MOD;
m_nDefaultSpeed = 6;
m_nDefaultTempo = 125;
- m_nMinPeriod = 14 << 2;
- m_nMaxPeriod = 3424 << 2;
+ m_nMinPeriod = 14 * 4;
+ m_nMaxPeriod = 3424 * 4;
- StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[0], reinterpret_cast<const char *>(lpStream), 20);
+ file.Seek(0);
+ file.ReadString<StringFixer::spacePadded>(m_szNames[0], 20);
// Setup channel pan positions and volume
SetupMODPanning();
- const CHANNELINDEX nMaxChn = (isFLT8) ? 4 : m_nChannels; // 4 channels per pattern in FLT8 format.
- if(isFLT8) nbp++; // as one logical pattern consists of two real patterns in FLT8 format, the highest pattern number has to be increased by one.
+ const CHANNELINDEX readChannels = (isFLT8 ? 4 : m_nChannels); // 4 channels per pattern in FLT8 format.
+ if(isFLT8) numPatterns++; // as one logical pattern consists of two real patterns in FLT8 format, the highest pattern number has to be increased by one.
bool hasTempoCommands = false; // for detecting VBlank MODs
bool leftPanning = false, extendedPanning = false; // for detecting 800-880 panning
+ file.Seek(patternStartOffset);
+
// Reading patterns
- for (PATTERNINDEX ipat = 0; ipat < nbp; ipat++)
+ for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
{
- if (ipat < MAX_PATTERNS)
+ if(pat < MAX_PATTERNS)
{
- if (dwMemPos + nMaxChn * 256 > dwMemLength) break;
+ PATTERNINDEX actualPattern = pat;
- ModCommand *m;
if(isFLT8)
{
- if((ipat & 1) == 0)
+ if((pat % 2) == 0)
{
- // only create "even" patterns
- if(Patterns.Insert(ipat >> 1, 64)) break;
+ // Only create "even" patterns for FLT8 files
+ if(Patterns.Insert(pat / 2, 64))
+ {
+ break;
+ }
}
- m = Patterns[ipat >> 1];
+ actualPattern /= 2;
} else
{
- if(Patterns.Insert(ipat, 64)) break;
- m = Patterns[ipat];
+ if(Patterns.Insert(pat, 64))
+ {
+ break;
+ }
}
size_t instrWithoutNoteCount = 0; // For detecting PT1x mode
vector<ModCommand::INSTR> lastInstrument(m_nChannels, 0);
- const BYTE *p = lpStream + dwMemPos;
+ for(ROWINDEX row = 0; row < 64; row++)
+ {
+ // FLT8: either write to channel 1 to 4 (even patterns) or 5 to 8 (odd patterns).
+ ModCommand *rowBase = Patterns[actualPattern].GetpModCommand(row, ((pat % 2) == 0 || !isFLT8) ? 0: 4);
- for(ROWINDEX nRow = 0; nRow < 64; nRow++)
- {
- if(isFLT8)
+ for(CHANNELINDEX chn = 0; chn < readChannels; chn++)
{
- // FLT8: either write to channel 1 to 4 (even patterns) or 5 to 8 (odd patterns).
- m = Patterns[ipat >> 1] + nRow * 8 + ((ipat & 1) ? 4 : 0);
- }
- for(CHANNELINDEX nChn = 0; nChn < nMaxChn; nChn++, m++, p += 4)
- {
- BYTE A0 = p[0], A1 = p[1], A2 = p[2], A3 = p[3];
- UINT n = ((((UINT)A0 & 0x0F) << 8) | (A1));
- if ((n) && (n != 0xFFF)) m->note = GetNoteFromPeriod(n << 2);
- m->instr = ((UINT)A2 >> 4) | (A0 & 0x10);
- m->command = A2 & 0x0F;
- m->param = A3;
- if ((m->command) || (m->param)) ConvertModCommand(m);
+ ModCommand &m = rowBase[chn];
- if (m->command == CMD_TEMPO && m->param < 100)
+ uint8 data[4];
+ file.ReadArray(data);
+
+ // Read Period
+ uint16 period = (((static_cast<uint16>(data[0]) & 0x0F) << 8) | data[1]);
+ if(period > 0 && period != 0xFFF)
+ {
+ m.note = static_cast<ModCommand::NOTE>(GetNoteFromPeriod(period * 4));
+ }
+ // Read Instrument
+ m.instr = (data[2] >> 4) | (data[0] & 0x10);
+ // Read Effect
+ m.command = data[2] & 0x0F;
+ m.param = data[3];
+
+ if(m.command || m.param)
+ {
+ ConvertModCommand(m);
+ }
+
+ // Perform some checks for our heuristics...
+ if (m.command == CMD_TEMPO && m.param < 100)
hasTempoCommands = true;
- if (m->command == CMD_PANNING8 && m->param < 0x80)
+ if (m.command == CMD_PANNING8 && m.param < 0x80)
leftPanning = true;
- if (m->command == CMD_PANNING8 && m->param > 0x80 && m->param != 0xA4)
+ if (m.command == CMD_PANNING8 && m.param > 0x80 && m.param != 0xA4)
extendedPanning = true;
- if (m->note == NOTE_NONE && m->instr > 0 && !isFLT8)
+ if (m.note == NOTE_NONE && m.instr > 0 && !isFLT8)
{
- if(lastInstrument[nChn] > 0 && lastInstrument[nChn] != m->instr)
+ if(lastInstrument[chn] > 0 && lastInstrument[chn] != m.instr)
{
instrWithoutNoteCount++;
}
}
- if (m->instr != 0)
+ if (m.instr != 0)
{
- lastInstrument[nChn] = m->instr;
+ lastInstrument[chn] = m.instr;
}
}
}
@@ -502,23 +691,12 @@
m_dwSongFlags |= SONG_PT1XMODE;
}
}
- dwMemPos += nMaxChn * 256;
}
// Reading samples
- for (UINT ismp = 1; ismp <= m_nSamples; ismp++) if (Samples[ismp].nLength)
+ for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
- UINT flags = RS_PCM8S;
- if (dwMemPos + 5 <= dwMemLength)
- {
- if (!_strnicmp((LPSTR)(lpStream + dwMemPos), "ADPCM", 5))
- {
- // MODPlugin :(
- flags = RS_ADPCM4;
- dwMemPos += 5;
- }
- }
- dwMemPos += ReadSample(&Samples[ismp], flags, (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
+ ReadSample(&Samples[smp], RS_PCM8S, file);
}
// Fix VBlank MODs. Arbitrary threshold: 10 minutes.
@@ -532,11 +710,11 @@
// below 100 BPM are taken into account. Furthermore, only M.K. (ProTracker)
// modules are checked.
// The same check is also applied to original Ultimate Soundtracker 15 sample mods.
- const bool bVBlank = ((bMdKd && hasTempoCommands && GetSongTime() >= 10 * 60) || m_nSamples == 15);
- const bool b7BitPanning = leftPanning && !extendedPanning;
- if(bVBlank || b7BitPanning)
+ const bool fixVBlank = ((isMdKd && hasTempoCommands && GetSongTime() >= 10 * 60) || GetNumSamples()== 15);
+ const bool fix7BitPanning = leftPanning && !extendedPanning;
+ if(fixVBlank || fix7BitPanning)
{
- Patterns.ForEachModCommand(FixMODPatterns(bVBlank, b7BitPanning));
+ Patterns.ForEachModCommand(FixMODPatterns(fixVBlank, fix7BitPanning));
}
return true;
@@ -549,150 +727,165 @@
#include "../mptrack/moddoc.h"
#endif // MODPLUG_TRACKER
+extern WORD ProTrackerPeriodTable[6*12];
+
bool CSoundFile::SaveMod(LPCSTR lpszFileName)
//-------------------------------------------
{
- BYTE insmap[32];
- UINT inslen[32];
- BYTE bTab[32];
- BYTE ord[128];
FILE *f;
if ((!m_nChannels) || (!lpszFileName)) return false;
if ((f = fopen(lpszFileName, "wb")) == NULL) return false;
- MemsetZero(ord);
- MemsetZero(inslen);
- if (m_nInstruments)
+
+ // Write song title
{
- MemsetZero(insmap);
- for (UINT i=1; i<32; i++) if (Instruments[i])
+ char name[20];
+ StringFixer::WriteString<StringFixer::maybeNullTerminated>(name, m_szNames[0]);
+ fwrite(name, 20, 1, f);
+ }
+
+ vector<SmpLength> sampleLength(32, 0);
+ vector<SAMPLEINDEX> sampleSource(32, 0);
+
+ if(m_nInstruments)
+ {
+ for(INSTRUMENTINDEX ins = 1; ins < 32; ins++) if (Instruments[ins])
{
- for (UINT j=0; j<128; j++) if (Instruments[i]->Keyboard[j])
+ // Find some valid sample associated with this instrument.
+ for(size_t i = 0; i < CountOf(Instruments[ins]->Keyboard); i++)
{
- insmap[i] = Instruments[i]->Keyboard[j];
- break;
+ if(Instruments[ins]->Keyboard[i] > 0 && Instruments[ins]->Keyboard[i] <= GetNumSamples())
+ {
+ sampleSource[ins] = Instruments[ins]->Keyboard[i];
+ break;
+ }
}
}
} else
{
- for (UINT i=0; i<32; i++) insmap[i] = (BYTE)i;
- }
- // Writing song name
- fwrite(m_szNames, 20, 1, f);
- // Writing instrument definition
- for (UINT iins=1; iins<=31; iins++)
- {
- ModSample *pSmp = &Samples[insmap[iins]];
- memcpy(bTab, m_szNames[iins],22);
- inslen[iins] = pSmp->nLength;
- // if the sample size is odd, we have to add a padding byte, as all sample sizes in MODs are even.
- if(inslen[iins] & 1)
- inslen[iins]++;
- if (inslen[iins] > 0x1fff0) inslen[iins] = 0x1fff0;
- bTab[22] = inslen[iins] >> 9;
- bTab[23] = inslen[iins] >> 1;
- if (pSmp->RelativeTone < 0) bTab[24] = 0x08; else
- if (pSmp->RelativeTone > 0) bTab[24] = 0x07; else
- bTab[24] = (BYTE)XM2MODFineTune(pSmp->nFineTune);
- bTab[25] = pSmp->nVolume >> 2;
- UINT repstart = 0, replen = 2;
- if(pSmp->uFlags & CHN_LOOP)
+ for(SAMPLEINDEX i = 0; i < 32; i++)
{
- repstart = pSmp->nLoopStart;
- replen = pSmp->nLoopEnd - pSmp->nLoopStart;
+ sampleSource[i] = i;
}
- bTab[26] = repstart >> 9;
- bTab[27] = repstart >> 1;
- if(replen < 2) // ensure PT will load it properly
- replen = 2;
- bTab[28] = replen >> 9;
- bTab[29] = replen >> 1;
- fwrite(bTab, 30, 1, f);
}
- // Writing number of patterns
- UINT nbp = 0, norders = 0;
- for (UINT iord = 0; iord < 128; iord++)
+
+ // Write sample headers
+ for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
{
+ MODSampleHeader sampleHeader;
+ StringFixer::WriteString<StringFixer::maybeNullTerminated>(sampleHeader.name, m_szNames[sampleSource[smp]]);
+ sampleLength[smp] = sampleHeader.ConvertToMOD(Samples[sampleSource[smp]]);
+ sampleHeader.ConvertEndianness();
+ fwrite(&sampleHeader, sizeof(sampleHeader), 1, f);
+ }
+
+ // Write order list
+ MODFileHeader fileHeader;
+ MemsetZero(fileHeader);
+
+ PATTERNINDEX writePatterns = 0;
+ for(ORDERINDEX ord = 0; ord < Order.GetLength() && fileHeader.numOrders < 128; ord++)
+ {
// Ignore +++ and --- patterns in order list, as well as high patterns (MOD officially only supports up to 128 patterns)
- if (Order[iord] < 0x80)
+ if(Order[ord] < 128)
{
- if (nbp <= Order[iord]) nbp = Order[iord] + 1;
- ord[norders++] = Order[iord];
+ fileHeader.orderList[fileHeader.numOrders++] = static_cast<uint8>(Order[ord]);
+ if(writePatterns <= Order[ord])
+ {
+ writePatterns = Order[ord] + 1;
+ }
}
}
- bTab[0] = norders;
- bTab[1] = m_nRestartPos;
- fwrite(bTab, 2, 1, f);
- fwrite(ord, 128, 1, f);
- // Writing signature
- if (m_nChannels == 4)
+
+ if(m_nRestartPos < 128)
{
- if(nbp < 64)
- lstrcpy((LPSTR)&bTab, "M.K.");
- else // more than 64 patterns
- lstrcpy((LPSTR)&bTab, "M!K!");
+ fileHeader.restartPos = static_cast<uint8>(m_nRestartPos);
+ }
+
+ // Write magic bytes
+ CHANNELINDEX writeChannels = min(99, GetNumChannels());
+ if(writeChannels == 4)
+ {
+ if(writePatterns < 64)
+ {
+ memcpy(fileHeader.magic, "M.K.", 4);
+ } else
+ {
+ // More than 64 patterns
+ memcpy(fileHeader.magic, "M!K!", 4);
+ }
} else
{
- sprintf((LPSTR)&bTab, "%luCHN", m_nChannels);
+ char magic[6];
+ sprintf(magic, "%luCHN", writeChannels);
+ memcpy(fileHeader.magic, magic, 4);
}
- fwrite(bTab, 4, 1, f);
- // Writing patterns
- for (UINT ipat=0; ipat<nbp; ipat++) //for all patterns
+
+ fwrite(&fileHeader, sizeof(fileHeader), 1, f);
+
+ // Write patterns
+ vector<uint8> events;
+ for(PATTERNINDEX pat = 0; pat < writePatterns; pat++)
{
- BYTE s[64*4];
- if (Patterns[ipat]) //if pattern exists
+ if(!Patterns.IsValidPat(pat))
{
- ModCommand *m = Patterns[ipat];
- for (UINT i=0; i<64; i++) { //for all rows
- if (i < Patterns[ipat].GetNumRows()) { //if row exists
- LPBYTE p=s;
- for (UINT c=0; c<m_nChannels; c++,p+=4,m++)
- {
- UINT param = ModSaveCommand(m, false, true);
- UINT command = param >> 8;
- param &= 0xFF;
- if (command > 0x0F) command = param = 0;
- if ((m->vol >= 0x10) && (m->vol <= 0x50) && (!command) && (!param)) { command = 0x0C; param = m->vol - 0x10; }
- UINT period = m->note;
- if (period)
- {
- if (period < 37) period = 37;
- period -= 37;
- if (period >= 6*12) period = 6*12-1;
- period = ProTrackerPeriodTable[period];
- }
- UINT instr = (m->instr > 31) ? 0 : m->instr;
- p[0] = ((period >> 8) & 0x0F) | (instr & 0x10);
- p[1] = period & 0xFF;
- p[2] = ((instr & 0x0F) << 4) | (command & 0x0F);
- p[3] = param;
- }
- fwrite(s, m_nChannels, 4, f);
- } else
+ // Invent empty pattern
+ events.assign(writeChannels * 64 * 4, 0);
+ fwrite(&events[0], events.size(), 1, f);
+ continue;
+ }
+
+ for(ROWINDEX row = 0; row < 64; row++)
+ {
+ if(row >= Patterns[pat].GetNumRows())
+ {
+ // Invent empty row
+ events.assign(writeChannels * 4, 0);
+ fwrite(&events[0], events.size(), 1, f);
+ continue;
+ }
+ PatternRow rowBase = Patterns[pat].GetRow(row);
+
+ events.resize(writeChannels * 4);
+ size_t eventByte = 0;
+ for(CHANNELINDEX chn = 0; chn < writeChannels; chn++)
+ {
+ ModCommand &m = rowBase[chn];
+ uint8 command = m.command, param = m.param;
+ ModSaveCommand(command, param, false, true);
+
+ if(m.volcmd == VOLCMD_VOLUME && !command && !param)
{
- // if row does not exist, invent blank row
- memset(s, 0, m_nChannels * 4);
- fwrite(s, m_nChannels, 4, f);
+ // Maybe we can save some volume commands...
+ command = 0x0C;
+ param = min(m.vol, 64);
}
- } //end for all rows
- } else
- {
- // if pattern does not exist, invent blank pattern
- memset(s, 0, m_nChannels * 4);
- for(size_t i = 0; i < 64; i++)
- {
- fwrite(s, m_nChannels, 4, f);
+
+ uint16 period = 0;
+ // Convert note to period
+ if(m.IsNote() && m.note >= 36 + NOTE_MIN && m.note < CountOf(ProTrackerPeriodTable) + 36 + NOTE_MIN)
+ {
+ period = ProTrackerPeriodTable[m.note - 36 - NOTE_MIN];
+ }
+
+ uint8 instr = (m.instr <= 31) ? m.instr : 0;
+
+ events[eventByte++] = ((period >> 8) & 0x0F) | (instr & 0x10);
+ events[eventByte++] = period & 0xFF;
+ events[eventByte++] = ((instr & 0x0F) << 4) | (command & 0x0F);
+ events[eventByte++] = param;
}
+ fwrite(&events[0], eventByte, 1, f);
}
- } //end for all patterns
+ }
//Check for unsaved patterns
#ifdef MODPLUG_TRACKER
if(GetpModDoc() != nullptr)
{
- for(UINT ipat = nbp; ipat < Patterns.Size(); ipat++)
+ for(PATTERNINDEX pat = writePatterns; pat < Patterns.Size(); pat++)
{
- if(Patterns[ipat])
+ if(Patterns.IsValidPat(pat))
{
GetpModDoc()->AddToLog(_T("Warning: This track contains at least one pattern after the highest pattern number referred to in the sequence. Such patterns are not saved in the .mod format.\n"));
break;
@@ -702,25 +895,25 @@
#endif
// Writing instruments
- for (UINT ismpd = 1; ismpd <= 31; ismpd++) if (inslen[ismpd])
+ for(SAMPLEINDEX smp = 1; smp <= 31; smp++) if (sampleLength[smp])
{
- const ModSample *pSmp = &Samples[insmap[ismpd]];
+ const ModSample &sample = Samples[sampleSource[smp]];
const long sampleStart = ftell(f);
- const UINT writtenBytes = WriteSample(f, pSmp, RS_PCM8S, inslen[ismpd]);
+ const UINT writtenBytes = WriteSample(f, &sample, RS_PCM8S, sampleLength[smp]);
- if((pSmp->uFlags & CHN_LOOP) == 0)
+ if((sample.uFlags & CHN_LOOP) == 0)
{
// First two bytes of oneshot samples have to be 0 due to PT's one-shot loop
const long sampleEnd = ftell(f);
fseek(f, sampleStart, SEEK_SET);
- int8 silence[] = { 0, 0 };
- fwrite(&silence, 1, min(writtenBytes, 2), f);
+ int8 silence[] = {0, 0};
+ fwrite(&silence, min(writtenBytes, 2), 1, f);
fseek(f, sampleEnd, SEEK_SET);
}
- // write padding byte if the sample size is odd.
- if((pSmp->nLength % 2) != 0)
+ // Write padding byte if the sample size is odd.
+ if((sample.nLength % 2) != 0)
{
int8 padding = 0;
fwrite(&padding, 1, 1, f);
Modified: trunk/OpenMPT/soundlib/Load_mt2.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mt2.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_mt2.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -2,7 +2,7 @@
* Load_mt2.cpp
* ------------
* Purpose: MT2 (MadTracker 2) module loader
- * Notes : Plugins are currently not imported.
+ * Notes : Plugins are currently not imported. Better rewrite this crap.
* Authors: Olivier Lapicque
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
@@ -182,7 +182,7 @@
{
m->command = p->fxparam2;
m->param = p->fxparam1;
- that->ConvertModCommand(m);
+ that->ConvertModCommand(*m);
m->ExtendedMODtoS3MEffect();
} else
{
@@ -212,7 +212,7 @@
m_nRestartPos = pfh->wRestart;
m_nDefaultSpeed = pfh->bTicksPerLine;
m_nDefaultTempo = 125;
- m_dwSongFlags = SONG_ITCOMPATGXX;
+ m_dwSongFlags = SONG_ITCOMPATGXX | SONG_EXFILTERRANGE;
m_nDefaultRowsPerBeat = pfh->bLinesPerBeat;
m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4;
if ((pfh->wSamplesPerTick > 100) && (pfh->wSamplesPerTick < 5000))
@@ -424,7 +424,7 @@
#ifdef MT2DEBUG
if (iIns <= pfh->wInstruments) Log(" Instrument #%d at offset %04X: %d bytes\n", iIns, dwMemPos, pmi->dwDataLen);
#endif
- if (((LONG)pmi->dwDataLen > 0) && (dwMemPos <= dwMemLength - 40) && (pmi->dwDataLen <= dwMemLength - (dwMemPos + 40)))
+ if (((int)pmi->dwDataLen > 0) && (dwMemPos <= dwMemLength - 40) && (pmi->dwDataLen <= dwMemLength - (dwMemPos + 40)))
{
InstrMap[iIns-1] = pmi;
if (pIns)
@@ -435,6 +435,7 @@
pIns->nDNA = (pmi->wNNA>>12) & 3;
MT2ENVELOPE *pehdr[4];
WORD *pedata[4];
+
if (pfh->wVersion <= 0x201)
{
DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT) - 4;
Modified: trunk/OpenMPT/soundlib/Load_mtm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mtm.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_mtm.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -129,7 +129,7 @@
m->param = param;
if ((cmd) || (param))
{
- ConvertModCommand(m);
+ ConvertModCommand(*m);
m->Convert(MOD_TYPE_MOD, MOD_TYPE_S3M);
}
}
Modified: trunk/OpenMPT/soundlib/Load_ptm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_ptm.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_ptm.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -93,7 +93,7 @@
m_nSamples = min(pfh.nsamples, MAX_SAMPLES - 1);
dwMemPos = sizeof(PTMFILEHEADER);
nOrders = (pfh.norders < MAX_ORDERS) ? pfh.norders : MAX_ORDERS-1;
- Order.ReadAsByte(pfh.orders, nOrders, nOrders);
+ Order.ReadFromArray(pfh.orders, nOrders);
for (CHANNELINDEX ipan = 0; ipan < m_nChannels; ipan++)
{
@@ -168,7 +168,7 @@
m[nChn].param = lpStream[dwMemPos++];
if (m[nChn].command < 0x10)
{
- ConvertModCommand(&m[nChn]);
+ ConvertModCommand(m[nChn]);
m[nChn].ExtendedMODtoS3MEffect();
// Note cut does just mute the sample, not cut it. We have to fix that, if possible.
if(m[nChn].command == CMD_S3MCMDEX && (m[nChn].param & 0xF0) == 0xC0 && m[nChn].volcmd == VOLCMD_NONE)
Modified: trunk/OpenMPT/soundlib/Load_s3m.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_s3m.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_s3m.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -73,60 +73,54 @@
S3I_TYPE_ADMEL = 2,
};
-void CSoundFile::S3MConvert(ModCommand *m, bool bIT) const
+void CSoundFile::S3MConvert(ModCommand &m, bool fromIT) const
//--------------------------------------------------------
{
- UINT command = m->command;
- UINT param = m->param;
- switch (command | 0x40)
+ switch (m.command | 0x40)
{
- case 'A': command = CMD_SPEED; break;
- case 'B': command = CMD_POSITIONJUMP; break;
- case 'C': command = CMD_PATTERNBREAK; if (!bIT) param = (param >> 4) * 10 + (param & 0x0F); break;
- case 'D': command = CMD_VOLUMESLIDE; break;
- case 'E': command = CMD_PORTAMENTODOWN; break;
- case 'F': command = CMD_PORTAMENTOUP; break;
- case 'G': command = CMD_TONEPORTAMENTO; break;
- case 'H': command = CMD_VIBRATO; break;
- case 'I': command = CMD_TREMOR; break;
- case 'J': command = CMD_ARPEGGIO; break;
- case 'K': command = CMD_VIBRATOVOL; break;
- case 'L': command = CMD_TONEPORTAVOL; break;
- case 'M': command = CMD_CHANNELVOLUME; break;
- case 'N': command = CMD_CHANNELVOLSLIDE; break;
- case 'O': command = CMD_OFFSET; break;
- case 'P': command = CMD_PANNINGSLIDE; break;
- case 'Q': command = CMD_RETRIG; break;
- case 'R': command = CMD_TREMOLO; break;
- case 'S': command = CMD_S3MCMDEX; break;
- case 'T': command = CMD_TEMPO; break;
- case 'U': command = CMD_FINEVIBRATO; break;
- case 'V': command = CMD_GLOBALVOLUME; break;
- case 'W': command = CMD_GLOBALVOLSLIDE; break;
- case 'X': command = CMD_PANNING8; break;
- case 'Y': command = CMD_PANBRELLO; break;
- case 'Z': command = CMD_MIDI; break;
- case '\\': command = bIT ? CMD_SMOOTHMIDI : CMD_MIDI; break; //rewbs.smoothVST
+ case 'A': m.command = CMD_SPEED; break;
+ case 'B': m.command = CMD_POSITIONJUMP; break;
+ case 'C': m.command = CMD_PATTERNBREAK; if (!fromIT) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); break;
+ case 'D': m.command = CMD_VOLUMESLIDE; break;
+ case 'E': m.command = CMD_PORTAMENTODOWN; break;
+ case 'F': m.command = CMD_PORTAMENTOUP; break;
+ case 'G': m.command = CMD_TONEPORTAMENTO; break;
+ case 'H': m.command = CMD_VIBRATO; break;
+ case 'I': m.command = CMD_TREMOR; break;
+ case 'J': m.command = CMD_ARPEGGIO; break;
+ case 'K': m.command = CMD_VIBRATOVOL; break;
+ case 'L': m.command = CMD_TONEPORTAVOL; break;
+ case 'M': m.command = CMD_CHANNELVOLUME; break;
+ case 'N': m.command = CMD_CHANNELVOLSLIDE; break;
+ case 'O': m.command = CMD_OFFSET; break;
+ case 'P': m.command = CMD_PANNINGSLIDE; break;
+ case 'Q': m.command = CMD_RETRIG; break;
+ case 'R': m.command = CMD_TREMOLO; break;
+ case 'S': m.command = CMD_S3MCMDEX; break;
+ case 'T': m.command = CMD_TEMPO; break;
+ case 'U': m.command = CMD_FINEVIBRATO; break;
+ case 'V': m.command = CMD_GLOBALVOLUME; break;
+ case 'W': m.command = CMD_GLOBALVOLSLIDE; break;
+ case 'X': m.command = CMD_PANNING8; break;
+ case 'Y': m.command = CMD_PANBRELLO; break;
+ case 'Z': m.command = CMD_MIDI; break;
+ case '\\': m.command = fromIT ? CMD_SMOOTHMIDI : CMD_MIDI; break; //rewbs.smoothVST
// Chars under 0x40 don't save properly, so map : to ] and # to [.
- case ']': command = CMD_DELAYCUT; break;
- case '[': command = CMD_XPARAM; break;
- default: command = 0;
+ case ']': m.command = CMD_DELAYCUT; break;
+ case '[': m.command = CMD_XPARAM; break;
+ default: m.command = 0;
}
- m->command = command;
- m->param = param;
}
-void CSoundFile::S3MSaveConvert(UINT *pcmd, UINT *pprm, bool bIT, bool bCompatibilityExport) const
-//------------------------------------------------------------------------------------------------
+void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool compatibilityExport) const
+//------------------------------------------------------------------------------------------------------
{
- UINT command = *pcmd;
- UINT param = *pprm;
switch(command)
{
case CMD_SPEED: command = 'A'; break;
case CMD_POSITIONJUMP: command = 'B'; break;
- case CMD_PATTERNBREAK: command = 'C'; if (!bIT) param = ((param / 10) << 4) + (param % 10); break;
+ case CMD_PATTERNBREAK: command = 'C'; if (...
[truncated message content] |