Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
643
Src/external_dependencies/openmpt-trunk/soundlib/pattern.cpp
Normal file
643
Src/external_dependencies/openmpt-trunk/soundlib/pattern.cpp
Normal file
|
@ -0,0 +1,643 @@
|
|||
/*
|
||||
* Pattern.cpp
|
||||
* -----------
|
||||
* Purpose: Module Pattern header class
|
||||
* 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 "pattern.h"
|
||||
#include "patternContainer.h"
|
||||
#include "../common/serialization_utils.h"
|
||||
#include "../common/version.h"
|
||||
#include "ITTools.h"
|
||||
#include "Sndfile.h"
|
||||
#include "mod_specifications.h"
|
||||
#include "mpt/io/io.hpp"
|
||||
#include "mpt/io/io_stdstream.hpp"
|
||||
|
||||
|
||||
OPENMPT_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
CSoundFile& CPattern::GetSoundFile() { return m_rPatternContainer.GetSoundFile(); }
|
||||
const CSoundFile& CPattern::GetSoundFile() const { return m_rPatternContainer.GetSoundFile(); }
|
||||
|
||||
|
||||
CHANNELINDEX CPattern::GetNumChannels() const
|
||||
{
|
||||
return GetSoundFile().GetNumChannels();
|
||||
}
|
||||
|
||||
|
||||
// Check if there is any note data on a given row.
|
||||
bool CPattern::IsEmptyRow(ROWINDEX row) const
|
||||
{
|
||||
if(m_ModCommands.empty() || !IsValidRow(row))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
PatternRow data = GetRow(row);
|
||||
for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, data++)
|
||||
{
|
||||
if(!data->IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPattern::SetSignature(const ROWINDEX rowsPerBeat, const ROWINDEX rowsPerMeasure)
|
||||
{
|
||||
if(rowsPerBeat < 1
|
||||
|| rowsPerBeat > GetSoundFile().GetModSpecifications().patternRowsMax
|
||||
|| rowsPerMeasure < rowsPerBeat
|
||||
|| rowsPerMeasure > GetSoundFile().GetModSpecifications().patternRowsMax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_RowsPerBeat = rowsPerBeat;
|
||||
m_RowsPerMeasure = rowsPerMeasure;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Add or remove rows from the pattern.
|
||||
bool CPattern::Resize(const ROWINDEX newRowCount, bool enforceFormatLimits, bool resizeAtEnd)
|
||||
{
|
||||
CSoundFile &sndFile = GetSoundFile();
|
||||
|
||||
if(newRowCount == m_Rows || newRowCount < 1 || newRowCount > MAX_PATTERN_ROWS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(enforceFormatLimits)
|
||||
{
|
||||
auto &specs = sndFile.GetModSpecifications();
|
||||
if(newRowCount > specs.patternRowsMax || newRowCount < specs.patternRowsMin) return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
size_t count = ((newRowCount > m_Rows) ? (newRowCount - m_Rows) : (m_Rows - newRowCount)) * GetNumChannels();
|
||||
|
||||
if(newRowCount > m_Rows)
|
||||
m_ModCommands.insert(resizeAtEnd ? m_ModCommands.end() : m_ModCommands.begin(), count, ModCommand::Empty());
|
||||
else if(resizeAtEnd)
|
||||
m_ModCommands.erase(m_ModCommands.end() - count, m_ModCommands.end());
|
||||
else
|
||||
m_ModCommands.erase(m_ModCommands.begin(), m_ModCommands.begin() + count);
|
||||
} catch(mpt::out_of_memory e)
|
||||
{
|
||||
mpt::delete_out_of_memory(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Rows = newRowCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CPattern::ClearCommands()
|
||||
{
|
||||
std::fill(m_ModCommands.begin(), m_ModCommands.end(), ModCommand::Empty());
|
||||
}
|
||||
|
||||
|
||||
bool CPattern::AllocatePattern(ROWINDEX rows)
|
||||
{
|
||||
size_t newSize = GetNumChannels() * rows;
|
||||
if(rows == 0)
|
||||
{
|
||||
return false;
|
||||
} else if(rows == GetNumRows() && m_ModCommands.size() == newSize)
|
||||
{
|
||||
// Re-use allocated memory
|
||||
ClearCommands();
|
||||
return true;
|
||||
} else
|
||||
{
|
||||
// Do this in two steps in order to keep the old pattern data in case of OOM
|
||||
decltype(m_ModCommands) newPattern(newSize, ModCommand::Empty());
|
||||
m_ModCommands = std::move(newPattern);
|
||||
}
|
||||
m_Rows = rows;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CPattern::Deallocate()
|
||||
{
|
||||
m_Rows = m_RowsPerBeat = m_RowsPerMeasure = 0;
|
||||
m_ModCommands.clear();
|
||||
m_PatternName.clear();
|
||||
}
|
||||
|
||||
|
||||
CPattern& CPattern::operator= (const CPattern &pat)
|
||||
{
|
||||
m_ModCommands = pat.m_ModCommands;
|
||||
m_Rows = pat.m_Rows;
|
||||
m_RowsPerBeat = pat.m_RowsPerBeat;
|
||||
m_RowsPerMeasure = pat.m_RowsPerMeasure;
|
||||
m_tempoSwing = pat.m_tempoSwing;
|
||||
m_PatternName = pat.m_PatternName;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CPattern::operator== (const CPattern &other) const
|
||||
{
|
||||
return GetNumRows() == other.GetNumRows()
|
||||
&& GetNumChannels() == other.GetNumChannels()
|
||||
&& GetOverrideSignature() == other.GetOverrideSignature()
|
||||
&& GetRowsPerBeat() == other.GetRowsPerBeat()
|
||||
&& GetRowsPerMeasure() == other.GetRowsPerMeasure()
|
||||
&& GetTempoSwing() == other.GetTempoSwing()
|
||||
&& m_ModCommands == other.m_ModCommands;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODPLUG_TRACKER
|
||||
|
||||
bool CPattern::Expand()
|
||||
{
|
||||
const ROWINDEX newRows = m_Rows * 2;
|
||||
const CHANNELINDEX nChns = GetNumChannels();
|
||||
|
||||
if(m_ModCommands.empty()
|
||||
|| newRows > GetSoundFile().GetModSpecifications().patternRowsMax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
decltype(m_ModCommands) newPattern;
|
||||
try
|
||||
{
|
||||
newPattern.assign(m_ModCommands.size() * 2, ModCommand::Empty());
|
||||
} catch(mpt::out_of_memory e)
|
||||
{
|
||||
mpt::delete_out_of_memory(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto mSrc = m_ModCommands.begin(), mDst = newPattern.begin(); mSrc != m_ModCommands.end(); mSrc += nChns, mDst += 2 * nChns)
|
||||
{
|
||||
std::copy(mSrc, mSrc + nChns, mDst);
|
||||
}
|
||||
|
||||
m_ModCommands = std::move(newPattern);
|
||||
m_Rows = newRows;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPattern::Shrink()
|
||||
{
|
||||
if (m_ModCommands.empty()
|
||||
|| m_Rows < GetSoundFile().GetModSpecifications().patternRowsMin * 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Rows /= 2;
|
||||
const CHANNELINDEX nChns = GetNumChannels();
|
||||
|
||||
for(ROWINDEX y = 0; y < m_Rows; y++)
|
||||
{
|
||||
const PatternRow srcRow = GetRow(y * 2);
|
||||
const PatternRow nextSrcRow = GetRow(y * 2 + 1);
|
||||
PatternRow destRow = GetRow(y);
|
||||
|
||||
for(CHANNELINDEX x = 0; x < nChns; x++)
|
||||
{
|
||||
const ModCommand &src = srcRow[x];
|
||||
const ModCommand &srcNext = nextSrcRow[x];
|
||||
ModCommand &dest = destRow[x];
|
||||
dest = src;
|
||||
|
||||
if(dest.note == NOTE_NONE && !dest.instr)
|
||||
{
|
||||
// Fill in data from next row if field is empty
|
||||
dest.note = srcNext.note;
|
||||
dest.instr = srcNext.instr;
|
||||
if(srcNext.volcmd != VOLCMD_NONE)
|
||||
{
|
||||
dest.volcmd = srcNext.volcmd;
|
||||
dest.vol = srcNext.vol;
|
||||
}
|
||||
if(dest.command == CMD_NONE)
|
||||
{
|
||||
dest.command = srcNext.command;
|
||||
dest.param = srcNext.param;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_ModCommands.resize(m_Rows * nChns);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif // MODPLUG_TRACKER
|
||||
|
||||
|
||||
bool CPattern::SetName(const std::string &newName)
|
||||
{
|
||||
m_PatternName = newName;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPattern::SetName(const char *newName, size_t maxChars)
|
||||
{
|
||||
if(newName == nullptr || maxChars == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto nameEnd = std::find(newName, newName + maxChars, '\0');
|
||||
m_PatternName.assign(newName, nameEnd);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Write some kind of effect data to the pattern. Exact data to be written and write behaviour can be found in the EffectWriter object.
|
||||
bool CPattern::WriteEffect(EffectWriter &settings)
|
||||
{
|
||||
// First, reject invalid parameters.
|
||||
if(m_ModCommands.empty()
|
||||
|| settings.m_row >= GetNumRows()
|
||||
|| (settings.m_channel >= GetNumChannels() && settings.m_channel != CHANNELINDEX_INVALID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CHANNELINDEX scanChnMin = settings.m_channel, scanChnMax = settings.m_channel;
|
||||
|
||||
// Scan all channels
|
||||
if(settings.m_channel == CHANNELINDEX_INVALID)
|
||||
{
|
||||
scanChnMin = 0;
|
||||
scanChnMax = GetNumChannels() - 1;
|
||||
}
|
||||
|
||||
ModCommand * const baseCommand = GetpModCommand(settings.m_row, scanChnMin);
|
||||
ModCommand *m;
|
||||
|
||||
// Scan channel(s) for same effect type - if an effect of the same type is already present, exit.
|
||||
if(!settings.m_allowMultiple)
|
||||
{
|
||||
m = baseCommand;
|
||||
for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++)
|
||||
{
|
||||
if(!settings.m_isVolEffect && m->command == settings.m_command)
|
||||
return true;
|
||||
if(settings.m_isVolEffect && m->volcmd == settings.m_volcmd)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Easy case: check if there's some space left to put the effect somewhere
|
||||
m = baseCommand;
|
||||
for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++)
|
||||
{
|
||||
if(!settings.m_isVolEffect && m->command == CMD_NONE)
|
||||
{
|
||||
m->command = settings.m_command;
|
||||
m->param = settings.m_param;
|
||||
return true;
|
||||
}
|
||||
if(settings.m_isVolEffect && m->volcmd == VOLCMD_NONE)
|
||||
{
|
||||
m->volcmd = settings.m_volcmd;
|
||||
m->vol = settings.m_vol;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, apparently there's no space. If we haven't tried already, try to map it to the volume column or effect column instead.
|
||||
if(settings.m_retry)
|
||||
{
|
||||
const bool isS3M = (GetSoundFile().GetType() & MOD_TYPE_S3M);
|
||||
|
||||
// Move some effects that also work in the volume column, so there's place for our new effect.
|
||||
if(!settings.m_isVolEffect)
|
||||
{
|
||||
m = baseCommand;
|
||||
for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++)
|
||||
{
|
||||
switch(m->command)
|
||||
{
|
||||
case CMD_VOLUME:
|
||||
if(!GetSoundFile().GetModSpecifications().HasVolCommand(VOLCMD_VOLUME))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m->volcmd = VOLCMD_VOLUME;
|
||||
m->vol = m->param;
|
||||
m->command = settings.m_command;
|
||||
m->param = settings.m_param;
|
||||
return true;
|
||||
|
||||
case CMD_PANNING8:
|
||||
if(isS3M && m->param > 0x80)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
m->volcmd = VOLCMD_PANNING;
|
||||
m->command = settings.m_command;
|
||||
|
||||
if(isS3M)
|
||||
m->vol = (m->param + 1u) / 2u;
|
||||
else
|
||||
m->vol = (m->param + 2u) / 4u;
|
||||
|
||||
m->param = settings.m_param;
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Let's try it again by writing into the "other" effect column.
|
||||
if(settings.m_isVolEffect)
|
||||
{
|
||||
// Convert volume effect to normal effect
|
||||
ModCommand::COMMAND newCommand = CMD_NONE;
|
||||
ModCommand::PARAM newParam = settings.m_vol;
|
||||
switch(settings.m_volcmd)
|
||||
{
|
||||
case VOLCMD_PANNING:
|
||||
newCommand = CMD_PANNING8;
|
||||
newParam = mpt::saturate_cast<ModCommand::PARAM>(settings.m_vol * (isS3M ? 2u : 4u));
|
||||
break;
|
||||
case VOLCMD_VOLUME:
|
||||
newCommand = CMD_VOLUME;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(newCommand != CMD_NONE)
|
||||
{
|
||||
settings.m_command = static_cast<EffectCommand>(newCommand);
|
||||
settings.m_param = newParam;
|
||||
settings.m_retry = false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
// Convert normal effect to volume effect
|
||||
ModCommand::VOLCMD newVolCmd = VOLCMD_NONE;
|
||||
ModCommand::VOL newVol = settings.m_param;
|
||||
if(settings.m_command == CMD_PANNING8 && isS3M)
|
||||
{
|
||||
// This needs some manual fixing.
|
||||
if(settings.m_param <= 0x80)
|
||||
{
|
||||
// Can't have surround in volume column, only normal panning
|
||||
newVolCmd = VOLCMD_PANNING;
|
||||
newVol /= 2u;
|
||||
}
|
||||
} else
|
||||
{
|
||||
newVolCmd = settings.m_command;
|
||||
if(!ModCommand::ConvertVolEffect(newVolCmd, newVol, true))
|
||||
{
|
||||
// No Success :(
|
||||
newVolCmd = VOLCMD_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if(newVolCmd != CMD_NONE)
|
||||
{
|
||||
settings.m_volcmd = static_cast<VolumeCommand>(newVolCmd);
|
||||
settings.m_vol = newVol;
|
||||
settings.m_retry = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!settings.m_retry)
|
||||
{
|
||||
settings.m_isVolEffect = !settings.m_isVolEffect;
|
||||
if(WriteEffect(settings))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try in the next row if possible (this may also happen if we already retried)
|
||||
if(settings.m_retryMode == EffectWriter::rmTryNextRow && settings.m_row + 1 < GetNumRows())
|
||||
{
|
||||
settings.m_row++;
|
||||
settings.m_retry = true;
|
||||
return WriteEffect(settings);
|
||||
} else if(settings.m_retryMode == EffectWriter::rmTryPreviousRow && settings.m_row > 0)
|
||||
{
|
||||
settings.m_row--;
|
||||
settings.m_retry = true;
|
||||
return WriteEffect(settings);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Pattern serialization functions
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
enum maskbits
|
||||
{
|
||||
noteBit = (1 << 0),
|
||||
instrBit = (1 << 1),
|
||||
volcmdBit = (1 << 2),
|
||||
volBit = (1 << 3),
|
||||
commandBit = (1 << 4),
|
||||
effectParamBit = (1 << 5),
|
||||
extraData = (1 << 6)
|
||||
};
|
||||
|
||||
void WriteData(std::ostream& oStrm, const CPattern& pat);
|
||||
void ReadData(std::istream& iStrm, CPattern& pat, const size_t nSize = 0);
|
||||
|
||||
void WriteModPattern(std::ostream& oStrm, const CPattern& pat)
|
||||
{
|
||||
srlztn::SsbWrite ssb(oStrm);
|
||||
ssb.BeginWrite(FileIdPattern, Version::Current().GetRawVersion());
|
||||
ssb.WriteItem(pat, "data", &WriteData);
|
||||
// pattern time signature
|
||||
if(pat.GetOverrideSignature())
|
||||
{
|
||||
ssb.WriteItem<uint32>(pat.GetRowsPerBeat(), "RPB.");
|
||||
ssb.WriteItem<uint32>(pat.GetRowsPerMeasure(), "RPM.");
|
||||
}
|
||||
if(pat.HasTempoSwing())
|
||||
{
|
||||
ssb.WriteItem<TempoSwing>(pat.GetTempoSwing(), "SWNG", TempoSwing::Serialize);
|
||||
}
|
||||
ssb.FinishWrite();
|
||||
}
|
||||
|
||||
|
||||
void ReadModPattern(std::istream& iStrm, CPattern& pat, const size_t)
|
||||
{
|
||||
srlztn::SsbRead ssb(iStrm);
|
||||
ssb.BeginRead(FileIdPattern, Version::Current().GetRawVersion());
|
||||
if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0)
|
||||
return;
|
||||
ssb.ReadItem(pat, "data", &ReadData);
|
||||
// pattern time signature
|
||||
uint32 rpb = 0, rpm = 0;
|
||||
ssb.ReadItem<uint32>(rpb, "RPB.");
|
||||
ssb.ReadItem<uint32>(rpm, "RPM.");
|
||||
pat.SetSignature(rpb, rpm);
|
||||
TempoSwing swing;
|
||||
ssb.ReadItem<TempoSwing>(swing, "SWNG", TempoSwing::Deserialize);
|
||||
if(!swing.empty())
|
||||
swing.resize(pat.GetRowsPerBeat());
|
||||
pat.SetTempoSwing(swing);
|
||||
}
|
||||
|
||||
|
||||
static uint8 CreateDiffMask(const ModCommand &chnMC, const ModCommand &newMC)
|
||||
{
|
||||
uint8 mask = 0;
|
||||
if(chnMC.note != newMC.note)
|
||||
mask |= noteBit;
|
||||
if(chnMC.instr != newMC.instr)
|
||||
mask |= instrBit;
|
||||
if(chnMC.volcmd != newMC.volcmd)
|
||||
mask |= volcmdBit;
|
||||
if(chnMC.vol != newMC.vol)
|
||||
mask |= volBit;
|
||||
if(chnMC.command != newMC.command)
|
||||
mask |= commandBit;
|
||||
if(chnMC.param != newMC.param)
|
||||
mask |= effectParamBit;
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
// Writes pattern data. Adapted from SaveIT.
|
||||
void WriteData(std::ostream& oStrm, const CPattern& pat)
|
||||
{
|
||||
if(!pat.IsValid())
|
||||
return;
|
||||
|
||||
const ROWINDEX rows = pat.GetNumRows();
|
||||
const CHANNELINDEX chns = pat.GetNumChannels();
|
||||
std::vector<ModCommand> lastChnMC(chns);
|
||||
|
||||
for(ROWINDEX r = 0; r<rows; r++)
|
||||
{
|
||||
for(CHANNELINDEX c = 0; c<chns; c++)
|
||||
{
|
||||
const ModCommand m = *pat.GetpModCommand(r, c);
|
||||
// Writing only commands not written in IT-pattern writing:
|
||||
// For now this means only NOTE_PC and NOTE_PCS.
|
||||
if(!m.IsPcNote())
|
||||
continue;
|
||||
uint8 diffmask = CreateDiffMask(lastChnMC[c], m);
|
||||
uint8 chval = static_cast<uint8>(c+1);
|
||||
if(diffmask != 0)
|
||||
chval |= IT_bitmask_patternChanEnabled_c;
|
||||
|
||||
mpt::IO::WriteIntLE<uint8>(oStrm, chval);
|
||||
|
||||
if(diffmask)
|
||||
{
|
||||
lastChnMC[c] = m;
|
||||
mpt::IO::WriteIntLE<uint8>(oStrm, diffmask);
|
||||
if(diffmask & noteBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.note);
|
||||
if(diffmask & instrBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.instr);
|
||||
if(diffmask & volcmdBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.volcmd);
|
||||
if(diffmask & volBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.vol);
|
||||
if(diffmask & commandBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.command);
|
||||
if(diffmask & effectParamBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.param);
|
||||
}
|
||||
}
|
||||
mpt::IO::WriteIntLE<uint8>(oStrm, 0); // Write end of row marker.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define READITEM(itembit,id) \
|
||||
if(diffmask & itembit) \
|
||||
{ \
|
||||
mpt::IO::ReadIntLE<uint8>(iStrm, temp); \
|
||||
if(ch < chns) \
|
||||
lastChnMC[ch].id = temp; \
|
||||
} \
|
||||
if(ch < chns) \
|
||||
m.id = lastChnMC[ch].id;
|
||||
|
||||
|
||||
void ReadData(std::istream& iStrm, CPattern& pat, const size_t)
|
||||
{
|
||||
if (!pat.IsValid()) // Expecting patterns to be allocated and resized properly.
|
||||
return;
|
||||
|
||||
const CHANNELINDEX chns = pat.GetNumChannels();
|
||||
const ROWINDEX rows = pat.GetNumRows();
|
||||
|
||||
std::vector<ModCommand> lastChnMC(chns);
|
||||
|
||||
ROWINDEX row = 0;
|
||||
while(row < rows && iStrm.good())
|
||||
{
|
||||
uint8 t = 0;
|
||||
mpt::IO::ReadIntLE<uint8>(iStrm, t);
|
||||
if(t == 0)
|
||||
{
|
||||
row++;
|
||||
continue;
|
||||
}
|
||||
|
||||
CHANNELINDEX ch = (t & IT_bitmask_patternChanField_c);
|
||||
if(ch > 0)
|
||||
ch--;
|
||||
|
||||
uint8 diffmask = 0;
|
||||
if((t & IT_bitmask_patternChanEnabled_c) != 0)
|
||||
mpt::IO::ReadIntLE<uint8>(iStrm, diffmask);
|
||||
uint8 temp = 0;
|
||||
|
||||
ModCommand dummy = ModCommand::Empty();
|
||||
ModCommand& m = (ch < chns) ? *pat.GetpModCommand(row, ch) : dummy;
|
||||
|
||||
READITEM(noteBit, note);
|
||||
READITEM(instrBit, instr);
|
||||
READITEM(volcmdBit, volcmd);
|
||||
READITEM(volBit, vol);
|
||||
READITEM(commandBit, command);
|
||||
READITEM(effectParamBit, param);
|
||||
if(diffmask & extraData)
|
||||
{
|
||||
//Ignore additional data.
|
||||
uint8 size;
|
||||
mpt::IO::ReadIntLE<uint8>(iStrm, size);
|
||||
iStrm.ignore(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef READITEM
|
||||
|
||||
|
||||
OPENMPT_NAMESPACE_END
|
Loading…
Add table
Add a link
Reference in a new issue