ModSequence.h 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*
  2. * ModSequence.h
  3. * -------------
  4. * Purpose: Order and sequence handling.
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #pragma once
  10. #include "openmpt/all/BuildSettings.hpp"
  11. #include "Snd_defs.h"
  12. #include <algorithm>
  13. #include <vector>
  14. OPENMPT_NAMESPACE_BEGIN
  15. class CPattern;
  16. class CSoundFile;
  17. class ModSequenceSet;
  18. class ModSequence: public std::vector<PATTERNINDEX>
  19. {
  20. friend class ModSequenceSet;
  21. protected:
  22. mpt::ustring m_name; // Sequence name
  23. CSoundFile &m_sndFile; // Associated CSoundFile
  24. ORDERINDEX m_restartPos = 0; // Restart position when playback of this order ended
  25. public:
  26. ModSequence(CSoundFile &sndFile);
  27. ModSequence(ModSequence &&) noexcept = default;
  28. ModSequence(const ModSequence &) = default;
  29. ModSequence& operator=(const ModSequence &other);
  30. bool operator==(const ModSequence &other) const;
  31. bool operator!=(const ModSequence &other) const { return !(*this == other); }
  32. ORDERINDEX GetLength() const { return mpt::saturate_cast<ORDERINDEX>(size()); }
  33. // Returns last accessible index, i.e. GetLength() - 1, or 0 if the order list is empty.
  34. ORDERINDEX GetLastIndex() const { return std::max(ORDERINDEX(1), GetLength()) - 1u; }
  35. // Returns length of sequence without counting trailing '---' items.
  36. ORDERINDEX GetLengthTailTrimmed() const;
  37. // Returns length of sequence stopping counting on first '---' (or at the end of sequence).
  38. ORDERINDEX GetLengthFirstEmpty() const;
  39. // Replaces order list with 'newSize' copies of 'pat'.
  40. void assign(ORDERINDEX newSize, PATTERNINDEX pat);
  41. // Inserts 'count' orders starting from 'pos' using 'fill' as the pattern index for all inserted orders.
  42. // Sequence will automatically grow if needed and if it can't grow enough, some tail orders will be discarded.
  43. // Return: Number of orders inserted (up to 'count' many).
  44. ORDERINDEX insert(ORDERINDEX pos, ORDERINDEX count) { return insert(pos, count, GetInvalidPatIndex()); }
  45. ORDERINDEX insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill);
  46. void push_back() { push_back(GetInvalidPatIndex()); }
  47. void push_back(PATTERNINDEX pat) { if(GetLength() < MAX_ORDERS) std::vector<PATTERNINDEX>::push_back(pat); }
  48. void resize(ORDERINDEX newSize) { resize(newSize, GetInvalidPatIndex()); }
  49. void resize(ORDERINDEX newSize, PATTERNINDEX pat) { std::vector<PATTERNINDEX>::resize(std::min(MAX_ORDERS, newSize), pat); }
  50. // Removes orders from range [posBegin, posEnd].
  51. void Remove(ORDERINDEX posBegin, ORDERINDEX posEnd);
  52. // Remove all references to a given pattern index from the order list. Jump commands are updated accordingly.
  53. void RemovePattern(PATTERNINDEX pat);
  54. // Replaces all occurences of oldPat with newPat.
  55. void Replace(PATTERNINDEX oldPat, PATTERNINDEX newPat) { if(oldPat != newPat) std::replace(begin(), end(), oldPat, newPat); }
  56. // Removes any "---" patterns at the end of the list.
  57. void Shrink() { resize(GetLengthTailTrimmed()); }
  58. // Check if pattern at sequence position ord is valid.
  59. bool IsValidPat(ORDERINDEX ord) const;
  60. CPattern *PatternAt(ORDERINDEX ord) const;
  61. void AdjustToNewModType(const MODTYPE oldtype);
  62. // Returns the internal representation of a stop '---' index
  63. static constexpr PATTERNINDEX GetInvalidPatIndex() { return uint16_max; }
  64. // Returns the internal representation of an ignore '+++' index
  65. static constexpr PATTERNINDEX GetIgnoreIndex() { return uint16_max - 1; }
  66. // Returns the previous/next order ignoring skip indices (+++).
  67. // If no previous/next order exists, return first/last order, and zero
  68. // when orderlist is empty.
  69. ORDERINDEX GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const;
  70. ORDERINDEX GetNextOrderIgnoringSkips(const ORDERINDEX start) const;
  71. // Find an order item that contains a given pattern number.
  72. ORDERINDEX FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt = 0, bool searchForward = true) const;
  73. // Ensures that the pattern at the specified order position is used only once (across all sequences).
  74. // If another usage is found, the pattern is replaced by a copy and the new index is returned.
  75. PATTERNINDEX EnsureUnique(ORDERINDEX ord);
  76. #ifndef MODPLUG_NO_FILESAVE
  77. // Write order items as bytes. '---' is written as stopIndex, '+++' is written as ignoreIndex
  78. size_t WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex = 0xFF, uint8 ignoreIndex = 0xFE) const;
  79. #endif // MODPLUG_NO_FILESAVE
  80. // Returns true if the IT orderlist datafield is not sufficient to store orderlist information.
  81. bool NeedsExtraDatafield() const;
  82. #ifdef MODPLUG_TRACKER
  83. // Check if a playback position is currently locked (inaccessible)
  84. bool IsPositionLocked(ORDERINDEX position) const;
  85. // Check if this sequence has subsongs separated by invalid ("---" or non-existing) patterns
  86. bool HasSubsongs() const;
  87. #endif // MODPLUG_TRACKER
  88. // Sequence name setter / getter
  89. inline void SetName(const mpt::ustring &newName) { m_name = newName;}
  90. inline mpt::ustring GetName() const { return m_name; }
  91. // Restart position setter / getter
  92. inline void SetRestartPos(ORDERINDEX restartPos) noexcept { m_restartPos = restartPos; }
  93. inline ORDERINDEX GetRestartPos() const noexcept { return m_restartPos; }
  94. };
  95. class ModSequenceSet
  96. {
  97. friend void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t);
  98. friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset);
  99. protected:
  100. std::vector<ModSequence> m_Sequences; // Array of sequences
  101. CSoundFile &m_sndFile;
  102. SEQUENCEINDEX m_currentSeq = 0; // Index of current sequence.
  103. public:
  104. ModSequenceSet(CSoundFile &sndFile);
  105. ModSequenceSet(ModSequenceSet &&) noexcept = default;
  106. // Remove all sequences and initialize default sequence
  107. void Initialize();
  108. // Get the working sequence
  109. ModSequence& operator() () { return m_Sequences[m_currentSeq]; }
  110. const ModSequence& operator() () const { return m_Sequences[m_currentSeq]; }
  111. // Get an arbitrary sequence
  112. ModSequence& operator() (SEQUENCEINDEX seq) { return m_Sequences[seq]; }
  113. const ModSequence& operator() (SEQUENCEINDEX seq) const { return m_Sequences[seq]; }
  114. SEQUENCEINDEX GetNumSequences() const { return static_cast<SEQUENCEINDEX>(m_Sequences.size()); }
  115. SEQUENCEINDEX GetCurrentSequenceIndex() const { return m_currentSeq; }
  116. // Sets working sequence.
  117. void SetSequence(SEQUENCEINDEX);
  118. // Add new empty sequence.
  119. // Returns the ID of the new sequence, or SEQUENCEINDEX_INVALID on failure.
  120. SEQUENCEINDEX AddSequence();
  121. // Removes given sequence.
  122. void RemoveSequence(SEQUENCEINDEX);
  123. // Returns the internal representation of a stop '---' index
  124. static constexpr PATTERNINDEX GetInvalidPatIndex() { return ModSequence::GetInvalidPatIndex(); }
  125. // Returns the internal representation of an ignore '+++' index
  126. static constexpr PATTERNINDEX GetIgnoreIndex() { return ModSequence::GetIgnoreIndex(); }
  127. #ifdef MODPLUG_TRACKER
  128. // Assigns a new set of sequences. The vector contents indicate which existing sequences to keep / duplicate or if a new sequences should be inserted (SEQUENCEINDEX_INVALID)
  129. // The function fails if the vector is empty or contains too many sequences.
  130. bool Rearrange(const std::vector<SEQUENCEINDEX> &newOrder);
  131. // Adjust sequence when converting between module formats
  132. void OnModTypeChanged(MODTYPE oldType);
  133. // Check if there is a single sequences that qualifies for subsong splitting
  134. bool CanSplitSubsongs() const;
  135. // If there are subsongs (separated by "---" patterns) in the module,
  136. // asks user whether to convert these into multiple sequences (given that the
  137. // modformat supports multiple sequences).
  138. // Returns true if sequences were modified, false otherwise.
  139. bool SplitSubsongsToMultipleSequences();
  140. // Convert the sequence's restart position information to a pattern command.
  141. bool RestartPosToPattern(SEQUENCEINDEX seq);
  142. // Merges multiple sequences into one and destroys all other sequences.
  143. // Returns false if there were no sequences to merge, true otherwise.
  144. bool MergeSequences();
  145. #endif // MODPLUG_TRACKER
  146. std::vector<ModSequence>::iterator begin() { return m_Sequences.begin(); }
  147. std::vector<ModSequence>::const_iterator begin() const { return m_Sequences.begin(); }
  148. std::vector<ModSequence>::const_iterator cbegin() const { return m_Sequences.cbegin(); }
  149. std::vector<ModSequence>::iterator end() { return m_Sequences.end(); }
  150. std::vector<ModSequence>::const_iterator end() const { return m_Sequences.end(); }
  151. std::vector<ModSequence>::const_iterator cend() const { return m_Sequences.cend(); }
  152. };
  153. const char FileIdSequences[] = "mptSeqC";
  154. const char FileIdSequence[] = "mptSeq";
  155. #ifndef MODPLUG_NO_FILESAVE
  156. void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq);
  157. #endif // MODPLUG_NO_FILESAVE
  158. void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize, mpt::Charset defaultCharset);
  159. #ifndef MODPLUG_NO_FILESAVE
  160. void WriteModSequence(std::ostream& oStrm, const ModSequence& seq);
  161. #endif // MODPLUG_NO_FILESAVE
  162. void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset);
  163. #ifndef MODPLUG_NO_FILESAVE
  164. void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq);
  165. #endif // MODPLUG_NO_FILESAVE
  166. void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t);
  167. OPENMPT_NAMESPACE_END