123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- /*
- * ModSequence.cpp
- * ---------------
- * Purpose: Order and sequence handling.
- * 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 "ModSequence.h"
- #include "Sndfile.h"
- #include "mod_specifications.h"
- #include "../common/version.h"
- #include "../common/serialization_utils.h"
- #include "mpt/io/io.hpp"
- #include "mpt/io/io_stdstream.hpp"
- OPENMPT_NAMESPACE_BEGIN
- ModSequence::ModSequence(CSoundFile &sndFile)
- : m_sndFile(sndFile)
- {
- }
- ModSequence& ModSequence::operator=(const ModSequence &other)
- {
- MPT_ASSERT(&other.m_sndFile == &m_sndFile);
- if(&other == this)
- return *this;
- std::vector<PATTERNINDEX>::assign(other.begin(), other.end());
- m_name = other.m_name;
- m_restartPos = other.m_restartPos;
- return *this;
- }
- bool ModSequence::operator== (const ModSequence &other) const
- {
- return static_cast<const std::vector<PATTERNINDEX> &>(*this) == other
- && m_name == other.m_name
- && m_restartPos == other.m_restartPos;
- }
- bool ModSequence::NeedsExtraDatafield() const
- {
- return (m_sndFile.GetType() == MOD_TYPE_MPT && m_sndFile.Patterns.GetNumPatterns() > 0xFD);
- }
- void ModSequence::AdjustToNewModType(const MODTYPE oldtype)
- {
- auto &specs = m_sndFile.GetModSpecifications();
- if(oldtype != MOD_TYPE_NONE)
- {
- // If not supported, remove "+++" separator order items.
- if(!specs.hasIgnoreIndex)
- {
- RemovePattern(GetIgnoreIndex());
- }
- // If not supported, remove "---" items between patterns.
- if(!specs.hasStopIndex)
- {
- RemovePattern(GetInvalidPatIndex());
- }
- }
- //Resize orderlist if needed.
- if(specs.ordersMax < size())
- {
- // Order list too long? Remove "unnecessary" order items first.
- if(oldtype != MOD_TYPE_NONE && specs.ordersMax < GetLengthTailTrimmed())
- {
- erase(std::remove_if(begin(), end(), [&] (PATTERNINDEX pat) { return !m_sndFile.Patterns.IsValidPat(pat); }), end());
- if(GetLengthTailTrimmed() > specs.ordersMax)
- {
- m_sndFile.AddToLog(LogWarning, U_("WARNING: Order list has been trimmed!"));
- }
- }
- resize(specs.ordersMax);
- }
- }
- ORDERINDEX ModSequence::GetLengthTailTrimmed() const
- {
- if(empty())
- return 0;
- auto last = std::find_if(rbegin(), rend(), [] (PATTERNINDEX pat) { return pat != GetInvalidPatIndex(); });
- return static_cast<ORDERINDEX>(std::distance(begin(), last.base()));
- }
- ORDERINDEX ModSequence::GetLengthFirstEmpty() const
- {
- return static_cast<ORDERINDEX>(std::distance(begin(), std::find(begin(), end(), GetInvalidPatIndex())));
- }
- ORDERINDEX ModSequence::GetNextOrderIgnoringSkips(const ORDERINDEX start) const
- {
- if(empty())
- return 0;
- auto length = GetLength();
- ORDERINDEX next = std::min(ORDERINDEX(length - 1), ORDERINDEX(start + 1));
- while(next + 1 < length && at(next) == GetIgnoreIndex()) next++;
- return next;
- }
- ORDERINDEX ModSequence::GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const
- {
- const ORDERINDEX last = GetLastIndex();
- if(start == 0 || last == 0) return 0;
- ORDERINDEX prev = std::min(ORDERINDEX(start - 1), last);
- while(prev > 0 && at(prev) == GetIgnoreIndex()) prev--;
- return prev;
- }
- void ModSequence::Remove(ORDERINDEX posBegin, ORDERINDEX posEnd)
- {
- if(posEnd < posBegin || posEnd >= size())
- return;
- erase(begin() + posBegin, begin() + posEnd + 1);
- }
- // Remove all references to a given pattern index from the order list. Jump commands are updated accordingly.
- void ModSequence::RemovePattern(PATTERNINDEX pat)
- {
- // First, calculate the offset that needs to be applied to jump commands
- const ORDERINDEX orderLength = GetLengthTailTrimmed();
- std::vector<ORDERINDEX> newPosition(orderLength);
- ORDERINDEX maxJump = 0;
- for(ORDERINDEX i = 0; i < orderLength; i++)
- {
- newPosition[i] = i - maxJump;
- if(at(i) == pat)
- {
- maxJump++;
- }
- }
- if(!maxJump)
- {
- return;
- }
- erase(std::remove(begin(), end(), pat), end());
- // Only apply to patterns actually found in this sequence
- for(auto p : *this) if(m_sndFile.Patterns.IsValidPat(p))
- {
- for(auto &m : m_sndFile.Patterns[p])
- {
- if(m.command == CMD_POSITIONJUMP && m.param < newPosition.size())
- {
- m.param = static_cast<ModCommand::PARAM>(newPosition[m.param]);
- }
- }
- }
- if(m_restartPos < newPosition.size())
- {
- m_restartPos = newPosition[m_restartPos];
- }
- }
- void ModSequence::assign(ORDERINDEX newSize, PATTERNINDEX pat)
- {
- LimitMax(newSize, m_sndFile.GetModSpecifications().ordersMax);
- std::vector<PATTERNINDEX>::assign(newSize, pat);
- }
- ORDERINDEX ModSequence::insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill)
- {
- const auto ordersMax = m_sndFile.GetModSpecifications().ordersMax;
- if(pos >= ordersMax || GetLengthTailTrimmed() >= ordersMax || count == 0)
- return 0;
- // Limit number of orders to be inserted so that we don't exceed the format limit.
- LimitMax(count, static_cast<ORDERINDEX>(ordersMax - pos));
- reserve(std::max(pos, GetLength()) + count);
- // Inserting past the end of the container?
- if(pos > size())
- resize(pos);
- std::vector<PATTERNINDEX>::insert(begin() + pos, count, fill);
- // Did we overgrow? Remove patterns at end.
- if(size() > ordersMax)
- resize(ordersMax);
- return count;
- }
- bool ModSequence::IsValidPat(ORDERINDEX ord) const
- {
- if(ord < size())
- return m_sndFile.Patterns.IsValidPat(at(ord));
- return false;
- }
- CPattern *ModSequence::PatternAt(ORDERINDEX ord) const
- {
- if(!IsValidPat(ord))
- return nullptr;
- return &m_sndFile.Patterns[at(ord)];
- }
- ORDERINDEX ModSequence::FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt, bool searchForward) const
- {
- const ORDERINDEX length = GetLength();
- if(startSearchAt >= length)
- return ORDERINDEX_INVALID;
- ORDERINDEX ord = startSearchAt;
- for(ORDERINDEX p = 0; p < length; p++)
- {
- if(at(ord) == pat)
- {
- return ord;
- }
- if(searchForward)
- {
- if(++ord >= length)
- ord = 0;
- } else
- {
- if(ord-- == 0)
- ord = length - 1;
- }
- }
- return ORDERINDEX_INVALID;
- }
- PATTERNINDEX ModSequence::EnsureUnique(ORDERINDEX ord)
- {
- PATTERNINDEX pat = at(ord);
- if(!IsValidPat(ord))
- return pat;
- for(const auto &sequence : m_sndFile.Order)
- {
- ORDERINDEX ords = sequence.GetLength();
- for(ORDERINDEX o = 0; o < ords; o++)
- {
- if(sequence[o] == pat && (o != ord || &sequence != this))
- {
- // Found duplicate usage.
- PATTERNINDEX newPat = m_sndFile.Patterns.Duplicate(pat);
- if(newPat != PATTERNINDEX_INVALID)
- {
- at(ord) = newPat;
- return newPat;
- }
- }
- }
- }
- return pat;
- }
- /////////////////////////////////////
- // ModSequenceSet
- /////////////////////////////////////
- ModSequenceSet::ModSequenceSet(CSoundFile &sndFile)
- : m_sndFile(sndFile)
- {
- Initialize();
- }
- void ModSequenceSet::Initialize()
- {
- m_currentSeq = 0;
- m_Sequences.assign(1, ModSequence(m_sndFile));
- }
- void ModSequenceSet::SetSequence(SEQUENCEINDEX n)
- {
- if(n < m_Sequences.size())
- m_currentSeq = n;
- }
- SEQUENCEINDEX ModSequenceSet::AddSequence()
- {
- if(GetNumSequences() >= MAX_SEQUENCES)
- return SEQUENCEINDEX_INVALID;
- m_Sequences.push_back(ModSequence{m_sndFile});
- SetSequence(GetNumSequences() - 1);
- return GetNumSequences() - 1;
- }
- void ModSequenceSet::RemoveSequence(SEQUENCEINDEX i)
- {
- // Do nothing if index is invalid or if there's only one sequence left.
- if(i >= m_Sequences.size() || m_Sequences.size() <= 1)
- return;
- m_Sequences.erase(m_Sequences.begin() + i);
- if(i < m_currentSeq || m_currentSeq >= GetNumSequences())
- m_currentSeq--;
- }
- #ifdef MODPLUG_TRACKER
- bool ModSequenceSet::Rearrange(const std::vector<SEQUENCEINDEX> &newOrder)
- {
- if(newOrder.empty() || newOrder.size() > MAX_SEQUENCES)
- return false;
- const auto oldSequences = std::move(m_Sequences);
- m_Sequences.assign(newOrder.size(), ModSequence{m_sndFile});
- for(size_t i = 0; i < newOrder.size(); i++)
- {
- if(newOrder[i] < oldSequences.size())
- m_Sequences[i] = oldSequences[newOrder[i]];
- }
- if(m_currentSeq > m_Sequences.size())
- m_currentSeq = GetNumSequences() - 1u;
- return true;
- }
- void ModSequenceSet::OnModTypeChanged(MODTYPE oldType)
- {
- for(auto &seq : m_Sequences)
- {
- seq.AdjustToNewModType(oldType);
- }
- if(m_sndFile.GetModSpecifications(oldType).sequencesMax > 1 && m_sndFile.GetModSpecifications().sequencesMax <= 1)
- MergeSequences();
- }
- bool ModSequenceSet::CanSplitSubsongs() const
- {
- return GetNumSequences() == 1 && m_sndFile.GetModSpecifications().sequencesMax > 1 && m_Sequences[0].HasSubsongs();
- }
- bool ModSequenceSet::SplitSubsongsToMultipleSequences()
- {
- if(!CanSplitSubsongs())
- return false;
- bool modified = false;
- const ORDERINDEX length = m_Sequences[0].GetLengthTailTrimmed();
- for(ORDERINDEX ord = 0; ord < length; ord++)
- {
- // End of subsong?
- if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex())
- {
- // Remove all separator patterns between current and next subsong first
- while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord]))
- {
- m_Sequences[0][ord] = GetInvalidPatIndex();
- ord++;
- modified = true;
- }
- if(ord >= length)
- break;
- const SEQUENCEINDEX newSeq = AddSequence();
- if(newSeq == SEQUENCEINDEX_INVALID)
- break;
- const ORDERINDEX startOrd = ord;
- m_Sequences[newSeq].reserve(length - startOrd);
- modified = true;
- // Now, move all following orders to the new sequence
- while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex())
- {
- PATTERNINDEX copyPat = m_Sequences[0][ord];
- m_Sequences[newSeq].push_back(copyPat);
- m_Sequences[0][ord] = GetInvalidPatIndex();
- ord++;
- // Is this a valid pattern? adjust pattern jump commands, if necessary.
- if(m_sndFile.Patterns.IsValidPat(copyPat))
- {
- for(auto &m : m_sndFile.Patterns[copyPat])
- {
- if(m.command == CMD_POSITIONJUMP && m.param >= startOrd)
- {
- m.param = static_cast<ModCommand::PARAM>(m.param - startOrd);
- }
- }
- }
- }
- ord--;
- }
- }
- SetSequence(0);
- return modified;
- }
- // Convert the sequence's restart position information to a pattern command.
- bool ModSequenceSet::RestartPosToPattern(SEQUENCEINDEX seq)
- {
- bool result = false;
- auto length = m_sndFile.GetLength(eNoAdjust, GetLengthTarget(true).StartPos(seq, 0, 0));
- ModSequence &order = m_Sequences[seq];
- for(const auto &subSong : length)
- {
- if(subSong.endOrder != ORDERINDEX_INVALID && subSong.endRow != ROWINDEX_INVALID)
- {
- if(mpt::in_range<ModCommand::PARAM>(order.GetRestartPos()))
- {
- PATTERNINDEX writePat = order.EnsureUnique(subSong.endOrder);
- result = m_sndFile.Patterns[writePat].WriteEffect(
- EffectWriter(CMD_POSITIONJUMP, static_cast<ModCommand::PARAM>(order.GetRestartPos())).Row(subSong.endRow).RetryNextRow());
- } else
- {
- result = false;
- }
- }
- }
- order.SetRestartPos(0);
- return result;
- }
- bool ModSequenceSet::MergeSequences()
- {
- if(GetNumSequences() <= 1)
- return false;
- ModSequence &firstSeq = m_Sequences[0];
- firstSeq.resize(firstSeq.GetLengthTailTrimmed());
- std::vector<SEQUENCEINDEX> patternsFixed(m_sndFile.Patterns.Size(), SEQUENCEINDEX_INVALID); // pattern fixed by other sequence already?
- // Mark patterns handled in first sequence
- for(auto pat : firstSeq)
- {
- if(m_sndFile.Patterns.IsValidPat(pat))
- patternsFixed[pat] = 0;
- }
- for(SEQUENCEINDEX seqNum = 1; seqNum < GetNumSequences(); seqNum++)
- {
- ModSequence &seq = m_Sequences[seqNum];
- const ORDERINDEX firstOrder = firstSeq.GetLength() + 1; // +1 for separator item
- const ORDERINDEX lengthTrimmed = seq.GetLengthTailTrimmed();
- if(firstOrder + lengthTrimmed > m_sndFile.GetModSpecifications().ordersMax)
- {
- m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("WARNING: Cannot merge Sequence {} (too long!)")(seqNum + 1));
- continue;
- }
- firstSeq.reserve(firstOrder + lengthTrimmed);
- firstSeq.push_back(); // Separator item
- RestartPosToPattern(seqNum);
- for(ORDERINDEX ord = 0; ord < lengthTrimmed; ord++)
- {
- PATTERNINDEX pat = seq[ord];
- firstSeq.push_back(pat);
- // Try to fix pattern jump commands
- if(!m_sndFile.Patterns.IsValidPat(pat)) continue;
- auto m = m_sndFile.Patterns[pat].begin();
- for(size_t len = 0; len < m_sndFile.Patterns[pat].GetNumRows() * m_sndFile.m_nChannels; m++, len++)
- {
- if(m->command == CMD_POSITIONJUMP)
- {
- if(patternsFixed[pat] != SEQUENCEINDEX_INVALID && patternsFixed[pat] != seqNum)
- {
- // Oops, some other sequence uses this pattern already.
- const PATTERNINDEX newPat = m_sndFile.Patterns.Duplicate(pat, true);
- if(newPat != PATTERNINDEX_INVALID)
- {
- // Could create new pattern - copy data over and continue from here.
- firstSeq[firstOrder + ord] = newPat;
- m = m_sndFile.Patterns[newPat].begin() + len;
- if(newPat >= patternsFixed.size())
- patternsFixed.resize(newPat + 1, SEQUENCEINDEX_INVALID);
- pat = newPat;
- } else
- {
- // Cannot create new pattern: notify the user
- m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("CONFLICT: Pattern break commands in Pattern {} might be broken since it has been used in several sequences!")(pat));
- }
- }
- m->param = static_cast<ModCommand::PARAM>(m->param + firstOrder);
- patternsFixed[pat] = seqNum;
- }
- }
- }
- }
- m_Sequences.erase(m_Sequences.begin() + 1, m_Sequences.end());
- return true;
- }
- // Check if a playback position is currently locked (inaccessible)
- bool ModSequence::IsPositionLocked(ORDERINDEX position) const
- {
- return(m_sndFile.m_lockOrderStart != ORDERINDEX_INVALID
- && (position < m_sndFile.m_lockOrderStart || position > m_sndFile.m_lockOrderEnd));
- }
- bool ModSequence::HasSubsongs() const
- {
- const auto endPat = begin() + GetLengthTailTrimmed();
- return std::find_if(begin(), endPat,
- [&](PATTERNINDEX pat) { return pat != GetIgnoreIndex() && !m_sndFile.Patterns.IsValidPat(pat); }) != endPat;
- }
- #endif // MODPLUG_TRACKER
- /////////////////////////////////////
- // Read/Write
- /////////////////////////////////////
- #ifndef MODPLUG_NO_FILESAVE
- size_t ModSequence::WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex, uint8 ignoreIndex) const
- {
- const size_t limit = std::min(count, GetLength());
- for(size_t i = 0; i < limit; i++)
- {
- const PATTERNINDEX pat = at(i);
- uint8 temp = static_cast<uint8>(pat);
- if(pat == GetInvalidPatIndex()) temp = stopIndex;
- else if(pat == GetIgnoreIndex() || pat > 0xFF) temp = ignoreIndex;
- mpt::IO::WriteIntLE<uint8>(f, temp);
- }
- // Fill non-existing order items with stop indices
- for(size_t i = limit; i < count; i++)
- {
- mpt::IO::WriteIntLE<uint8>(f, stopIndex);
- }
- return count; //Returns the number of bytes written.
- }
- #endif // MODPLUG_NO_FILESAVE
- void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t)
- {
- uint16 size;
- mpt::IO::ReadIntLE<uint16>(iStrm, size);
- if(size > ModSpecs::mptm.ordersMax)
- {
- seq.m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("Module has sequence of length {}; it will be truncated to maximum supported length, {}.")(size, ModSpecs::mptm.ordersMax));
- size = ModSpecs::mptm.ordersMax;
- }
- seq(0).resize(size);
- for(auto &pat : seq(0))
- {
- uint16 temp;
- mpt::IO::ReadIntLE<uint16>(iStrm, temp);
- pat = temp;
- }
- }
- #ifndef MODPLUG_NO_FILESAVE
- void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq)
- {
- const uint16 size = seq().GetLength();
- mpt::IO::WriteIntLE<uint16>(oStrm, size);
- for(auto pat : seq())
- {
- mpt::IO::WriteIntLE<uint16>(oStrm, static_cast<uint16>(pat));
- }
- }
- #endif // MODPLUG_NO_FILESAVE
- #ifndef MODPLUG_NO_FILESAVE
- void WriteModSequence(std::ostream& oStrm, const ModSequence& seq)
- {
- srlztn::SsbWrite ssb(oStrm);
- ssb.BeginWrite(FileIdSequence, Version::Current().GetRawVersion());
- int8 useUTF8 = 1;
- ssb.WriteItem(useUTF8, "u");
- ssb.WriteItem(mpt::ToCharset(mpt::Charset::UTF8, seq.GetName()), "n");
- const uint16 length = seq.GetLengthTailTrimmed();
- ssb.WriteItem<uint16>(length, "l");
- ssb.WriteItem(seq, "a", srlztn::VectorWriter<uint16>(length));
- if(seq.GetRestartPos() > 0)
- ssb.WriteItem<uint16>(seq.GetRestartPos(), "r");
- ssb.FinishWrite();
- }
- #endif // MODPLUG_NO_FILESAVE
- void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset)
- {
- srlztn::SsbRead ssb(iStrm);
- ssb.BeginRead(FileIdSequence, Version::Current().GetRawVersion());
- if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0)
- return;
- int8 useUTF8 = 0;
- ssb.ReadItem(useUTF8, "u");
- std::string str;
- ssb.ReadItem(str, "n");
- seq.SetName(mpt::ToUnicode(useUTF8 ? mpt::Charset::UTF8 : defaultCharset, str));
- ORDERINDEX nSize = 0;
- ssb.ReadItem(nSize, "l");
- LimitMax(nSize, ModSpecs::mptm.ordersMax);
- ssb.ReadItem(seq, "a", srlztn::VectorReader<uint16>(nSize));
- ORDERINDEX restartPos = ORDERINDEX_INVALID;
- if(ssb.ReadItem(restartPos, "r") != srlztn::SsbRead::EntryNotFound && restartPos < nSize)
- seq.SetRestartPos(restartPos);
- }
- #ifndef MODPLUG_NO_FILESAVE
- void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq)
- {
- srlztn::SsbWrite ssb(oStrm);
- ssb.BeginWrite(FileIdSequences, Version::Current().GetRawVersion());
- const uint8 nSeqs = seq.GetNumSequences();
- const uint8 nCurrent = seq.GetCurrentSequenceIndex();
- ssb.WriteItem(nSeqs, "n");
- ssb.WriteItem(nCurrent, "c");
- for(uint8 i = 0; i < nSeqs; i++)
- {
- ssb.WriteItem(seq(i), srlztn::ID::FromInt<uint8>(i), &WriteModSequence);
- }
- ssb.FinishWrite();
- }
- #endif // MODPLUG_NO_FILESAVE
- void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset)
- {
- srlztn::SsbRead ssb(iStrm);
- ssb.BeginRead(FileIdSequences, Version::Current().GetRawVersion());
- if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0)
- return;
- SEQUENCEINDEX seqs = 0;
- uint8 currentSeq = 0;
- ssb.ReadItem(seqs, "n");
- if (seqs == 0)
- return;
- LimitMax(seqs, MAX_SEQUENCES);
- ssb.ReadItem(currentSeq, "c");
- if (seq.GetNumSequences() < seqs)
- seq.m_Sequences.resize(seqs, ModSequence(seq.m_sndFile));
- // There used to be only one restart position for all sequences
- ORDERINDEX legacyRestartPos = seq(0).GetRestartPos();
- for(SEQUENCEINDEX i = 0; i < seqs; i++)
- {
- seq(i).SetRestartPos(legacyRestartPos);
- ssb.ReadItem(seq(i), srlztn::ID::FromInt<uint8>(i), [defaultCharset](std::istream &iStrm, ModSequence &seq, std::size_t dummy) { return ReadModSequence(iStrm, seq, dummy, defaultCharset); });
- }
- seq.m_currentSeq = (currentSeq < seq.GetNumSequences()) ? currentSeq : 0;
- }
- OPENMPT_NAMESPACE_END
|