ModSequence.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. /*
  2. * ModSequence.cpp
  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. #include "stdafx.h"
  10. #include "ModSequence.h"
  11. #include "Sndfile.h"
  12. #include "mod_specifications.h"
  13. #include "../common/version.h"
  14. #include "../common/serialization_utils.h"
  15. #include "mpt/io/io.hpp"
  16. #include "mpt/io/io_stdstream.hpp"
  17. OPENMPT_NAMESPACE_BEGIN
  18. ModSequence::ModSequence(CSoundFile &sndFile)
  19. : m_sndFile(sndFile)
  20. {
  21. }
  22. ModSequence& ModSequence::operator=(const ModSequence &other)
  23. {
  24. MPT_ASSERT(&other.m_sndFile == &m_sndFile);
  25. if(&other == this)
  26. return *this;
  27. std::vector<PATTERNINDEX>::assign(other.begin(), other.end());
  28. m_name = other.m_name;
  29. m_restartPos = other.m_restartPos;
  30. return *this;
  31. }
  32. bool ModSequence::operator== (const ModSequence &other) const
  33. {
  34. return static_cast<const std::vector<PATTERNINDEX> &>(*this) == other
  35. && m_name == other.m_name
  36. && m_restartPos == other.m_restartPos;
  37. }
  38. bool ModSequence::NeedsExtraDatafield() const
  39. {
  40. return (m_sndFile.GetType() == MOD_TYPE_MPT && m_sndFile.Patterns.GetNumPatterns() > 0xFD);
  41. }
  42. void ModSequence::AdjustToNewModType(const MODTYPE oldtype)
  43. {
  44. auto &specs = m_sndFile.GetModSpecifications();
  45. if(oldtype != MOD_TYPE_NONE)
  46. {
  47. // If not supported, remove "+++" separator order items.
  48. if(!specs.hasIgnoreIndex)
  49. {
  50. RemovePattern(GetIgnoreIndex());
  51. }
  52. // If not supported, remove "---" items between patterns.
  53. if(!specs.hasStopIndex)
  54. {
  55. RemovePattern(GetInvalidPatIndex());
  56. }
  57. }
  58. //Resize orderlist if needed.
  59. if(specs.ordersMax < size())
  60. {
  61. // Order list too long? Remove "unnecessary" order items first.
  62. if(oldtype != MOD_TYPE_NONE && specs.ordersMax < GetLengthTailTrimmed())
  63. {
  64. erase(std::remove_if(begin(), end(), [&] (PATTERNINDEX pat) { return !m_sndFile.Patterns.IsValidPat(pat); }), end());
  65. if(GetLengthTailTrimmed() > specs.ordersMax)
  66. {
  67. m_sndFile.AddToLog(LogWarning, U_("WARNING: Order list has been trimmed!"));
  68. }
  69. }
  70. resize(specs.ordersMax);
  71. }
  72. }
  73. ORDERINDEX ModSequence::GetLengthTailTrimmed() const
  74. {
  75. if(empty())
  76. return 0;
  77. auto last = std::find_if(rbegin(), rend(), [] (PATTERNINDEX pat) { return pat != GetInvalidPatIndex(); });
  78. return static_cast<ORDERINDEX>(std::distance(begin(), last.base()));
  79. }
  80. ORDERINDEX ModSequence::GetLengthFirstEmpty() const
  81. {
  82. return static_cast<ORDERINDEX>(std::distance(begin(), std::find(begin(), end(), GetInvalidPatIndex())));
  83. }
  84. ORDERINDEX ModSequence::GetNextOrderIgnoringSkips(const ORDERINDEX start) const
  85. {
  86. if(empty())
  87. return 0;
  88. auto length = GetLength();
  89. ORDERINDEX next = std::min(ORDERINDEX(length - 1), ORDERINDEX(start + 1));
  90. while(next + 1 < length && at(next) == GetIgnoreIndex()) next++;
  91. return next;
  92. }
  93. ORDERINDEX ModSequence::GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const
  94. {
  95. const ORDERINDEX last = GetLastIndex();
  96. if(start == 0 || last == 0) return 0;
  97. ORDERINDEX prev = std::min(ORDERINDEX(start - 1), last);
  98. while(prev > 0 && at(prev) == GetIgnoreIndex()) prev--;
  99. return prev;
  100. }
  101. void ModSequence::Remove(ORDERINDEX posBegin, ORDERINDEX posEnd)
  102. {
  103. if(posEnd < posBegin || posEnd >= size())
  104. return;
  105. erase(begin() + posBegin, begin() + posEnd + 1);
  106. }
  107. // Remove all references to a given pattern index from the order list. Jump commands are updated accordingly.
  108. void ModSequence::RemovePattern(PATTERNINDEX pat)
  109. {
  110. // First, calculate the offset that needs to be applied to jump commands
  111. const ORDERINDEX orderLength = GetLengthTailTrimmed();
  112. std::vector<ORDERINDEX> newPosition(orderLength);
  113. ORDERINDEX maxJump = 0;
  114. for(ORDERINDEX i = 0; i < orderLength; i++)
  115. {
  116. newPosition[i] = i - maxJump;
  117. if(at(i) == pat)
  118. {
  119. maxJump++;
  120. }
  121. }
  122. if(!maxJump)
  123. {
  124. return;
  125. }
  126. erase(std::remove(begin(), end(), pat), end());
  127. // Only apply to patterns actually found in this sequence
  128. for(auto p : *this) if(m_sndFile.Patterns.IsValidPat(p))
  129. {
  130. for(auto &m : m_sndFile.Patterns[p])
  131. {
  132. if(m.command == CMD_POSITIONJUMP && m.param < newPosition.size())
  133. {
  134. m.param = static_cast<ModCommand::PARAM>(newPosition[m.param]);
  135. }
  136. }
  137. }
  138. if(m_restartPos < newPosition.size())
  139. {
  140. m_restartPos = newPosition[m_restartPos];
  141. }
  142. }
  143. void ModSequence::assign(ORDERINDEX newSize, PATTERNINDEX pat)
  144. {
  145. LimitMax(newSize, m_sndFile.GetModSpecifications().ordersMax);
  146. std::vector<PATTERNINDEX>::assign(newSize, pat);
  147. }
  148. ORDERINDEX ModSequence::insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill)
  149. {
  150. const auto ordersMax = m_sndFile.GetModSpecifications().ordersMax;
  151. if(pos >= ordersMax || GetLengthTailTrimmed() >= ordersMax || count == 0)
  152. return 0;
  153. // Limit number of orders to be inserted so that we don't exceed the format limit.
  154. LimitMax(count, static_cast<ORDERINDEX>(ordersMax - pos));
  155. reserve(std::max(pos, GetLength()) + count);
  156. // Inserting past the end of the container?
  157. if(pos > size())
  158. resize(pos);
  159. std::vector<PATTERNINDEX>::insert(begin() + pos, count, fill);
  160. // Did we overgrow? Remove patterns at end.
  161. if(size() > ordersMax)
  162. resize(ordersMax);
  163. return count;
  164. }
  165. bool ModSequence::IsValidPat(ORDERINDEX ord) const
  166. {
  167. if(ord < size())
  168. return m_sndFile.Patterns.IsValidPat(at(ord));
  169. return false;
  170. }
  171. CPattern *ModSequence::PatternAt(ORDERINDEX ord) const
  172. {
  173. if(!IsValidPat(ord))
  174. return nullptr;
  175. return &m_sndFile.Patterns[at(ord)];
  176. }
  177. ORDERINDEX ModSequence::FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt, bool searchForward) const
  178. {
  179. const ORDERINDEX length = GetLength();
  180. if(startSearchAt >= length)
  181. return ORDERINDEX_INVALID;
  182. ORDERINDEX ord = startSearchAt;
  183. for(ORDERINDEX p = 0; p < length; p++)
  184. {
  185. if(at(ord) == pat)
  186. {
  187. return ord;
  188. }
  189. if(searchForward)
  190. {
  191. if(++ord >= length)
  192. ord = 0;
  193. } else
  194. {
  195. if(ord-- == 0)
  196. ord = length - 1;
  197. }
  198. }
  199. return ORDERINDEX_INVALID;
  200. }
  201. PATTERNINDEX ModSequence::EnsureUnique(ORDERINDEX ord)
  202. {
  203. PATTERNINDEX pat = at(ord);
  204. if(!IsValidPat(ord))
  205. return pat;
  206. for(const auto &sequence : m_sndFile.Order)
  207. {
  208. ORDERINDEX ords = sequence.GetLength();
  209. for(ORDERINDEX o = 0; o < ords; o++)
  210. {
  211. if(sequence[o] == pat && (o != ord || &sequence != this))
  212. {
  213. // Found duplicate usage.
  214. PATTERNINDEX newPat = m_sndFile.Patterns.Duplicate(pat);
  215. if(newPat != PATTERNINDEX_INVALID)
  216. {
  217. at(ord) = newPat;
  218. return newPat;
  219. }
  220. }
  221. }
  222. }
  223. return pat;
  224. }
  225. /////////////////////////////////////
  226. // ModSequenceSet
  227. /////////////////////////////////////
  228. ModSequenceSet::ModSequenceSet(CSoundFile &sndFile)
  229. : m_sndFile(sndFile)
  230. {
  231. Initialize();
  232. }
  233. void ModSequenceSet::Initialize()
  234. {
  235. m_currentSeq = 0;
  236. m_Sequences.assign(1, ModSequence(m_sndFile));
  237. }
  238. void ModSequenceSet::SetSequence(SEQUENCEINDEX n)
  239. {
  240. if(n < m_Sequences.size())
  241. m_currentSeq = n;
  242. }
  243. SEQUENCEINDEX ModSequenceSet::AddSequence()
  244. {
  245. if(GetNumSequences() >= MAX_SEQUENCES)
  246. return SEQUENCEINDEX_INVALID;
  247. m_Sequences.push_back(ModSequence{m_sndFile});
  248. SetSequence(GetNumSequences() - 1);
  249. return GetNumSequences() - 1;
  250. }
  251. void ModSequenceSet::RemoveSequence(SEQUENCEINDEX i)
  252. {
  253. // Do nothing if index is invalid or if there's only one sequence left.
  254. if(i >= m_Sequences.size() || m_Sequences.size() <= 1)
  255. return;
  256. m_Sequences.erase(m_Sequences.begin() + i);
  257. if(i < m_currentSeq || m_currentSeq >= GetNumSequences())
  258. m_currentSeq--;
  259. }
  260. #ifdef MODPLUG_TRACKER
  261. bool ModSequenceSet::Rearrange(const std::vector<SEQUENCEINDEX> &newOrder)
  262. {
  263. if(newOrder.empty() || newOrder.size() > MAX_SEQUENCES)
  264. return false;
  265. const auto oldSequences = std::move(m_Sequences);
  266. m_Sequences.assign(newOrder.size(), ModSequence{m_sndFile});
  267. for(size_t i = 0; i < newOrder.size(); i++)
  268. {
  269. if(newOrder[i] < oldSequences.size())
  270. m_Sequences[i] = oldSequences[newOrder[i]];
  271. }
  272. if(m_currentSeq > m_Sequences.size())
  273. m_currentSeq = GetNumSequences() - 1u;
  274. return true;
  275. }
  276. void ModSequenceSet::OnModTypeChanged(MODTYPE oldType)
  277. {
  278. for(auto &seq : m_Sequences)
  279. {
  280. seq.AdjustToNewModType(oldType);
  281. }
  282. if(m_sndFile.GetModSpecifications(oldType).sequencesMax > 1 && m_sndFile.GetModSpecifications().sequencesMax <= 1)
  283. MergeSequences();
  284. }
  285. bool ModSequenceSet::CanSplitSubsongs() const
  286. {
  287. return GetNumSequences() == 1 && m_sndFile.GetModSpecifications().sequencesMax > 1 && m_Sequences[0].HasSubsongs();
  288. }
  289. bool ModSequenceSet::SplitSubsongsToMultipleSequences()
  290. {
  291. if(!CanSplitSubsongs())
  292. return false;
  293. bool modified = false;
  294. const ORDERINDEX length = m_Sequences[0].GetLengthTailTrimmed();
  295. for(ORDERINDEX ord = 0; ord < length; ord++)
  296. {
  297. // End of subsong?
  298. if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex())
  299. {
  300. // Remove all separator patterns between current and next subsong first
  301. while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord]))
  302. {
  303. m_Sequences[0][ord] = GetInvalidPatIndex();
  304. ord++;
  305. modified = true;
  306. }
  307. if(ord >= length)
  308. break;
  309. const SEQUENCEINDEX newSeq = AddSequence();
  310. if(newSeq == SEQUENCEINDEX_INVALID)
  311. break;
  312. const ORDERINDEX startOrd = ord;
  313. m_Sequences[newSeq].reserve(length - startOrd);
  314. modified = true;
  315. // Now, move all following orders to the new sequence
  316. while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex())
  317. {
  318. PATTERNINDEX copyPat = m_Sequences[0][ord];
  319. m_Sequences[newSeq].push_back(copyPat);
  320. m_Sequences[0][ord] = GetInvalidPatIndex();
  321. ord++;
  322. // Is this a valid pattern? adjust pattern jump commands, if necessary.
  323. if(m_sndFile.Patterns.IsValidPat(copyPat))
  324. {
  325. for(auto &m : m_sndFile.Patterns[copyPat])
  326. {
  327. if(m.command == CMD_POSITIONJUMP && m.param >= startOrd)
  328. {
  329. m.param = static_cast<ModCommand::PARAM>(m.param - startOrd);
  330. }
  331. }
  332. }
  333. }
  334. ord--;
  335. }
  336. }
  337. SetSequence(0);
  338. return modified;
  339. }
  340. // Convert the sequence's restart position information to a pattern command.
  341. bool ModSequenceSet::RestartPosToPattern(SEQUENCEINDEX seq)
  342. {
  343. bool result = false;
  344. auto length = m_sndFile.GetLength(eNoAdjust, GetLengthTarget(true).StartPos(seq, 0, 0));
  345. ModSequence &order = m_Sequences[seq];
  346. for(const auto &subSong : length)
  347. {
  348. if(subSong.endOrder != ORDERINDEX_INVALID && subSong.endRow != ROWINDEX_INVALID)
  349. {
  350. if(mpt::in_range<ModCommand::PARAM>(order.GetRestartPos()))
  351. {
  352. PATTERNINDEX writePat = order.EnsureUnique(subSong.endOrder);
  353. result = m_sndFile.Patterns[writePat].WriteEffect(
  354. EffectWriter(CMD_POSITIONJUMP, static_cast<ModCommand::PARAM>(order.GetRestartPos())).Row(subSong.endRow).RetryNextRow());
  355. } else
  356. {
  357. result = false;
  358. }
  359. }
  360. }
  361. order.SetRestartPos(0);
  362. return result;
  363. }
  364. bool ModSequenceSet::MergeSequences()
  365. {
  366. if(GetNumSequences() <= 1)
  367. return false;
  368. ModSequence &firstSeq = m_Sequences[0];
  369. firstSeq.resize(firstSeq.GetLengthTailTrimmed());
  370. std::vector<SEQUENCEINDEX> patternsFixed(m_sndFile.Patterns.Size(), SEQUENCEINDEX_INVALID); // pattern fixed by other sequence already?
  371. // Mark patterns handled in first sequence
  372. for(auto pat : firstSeq)
  373. {
  374. if(m_sndFile.Patterns.IsValidPat(pat))
  375. patternsFixed[pat] = 0;
  376. }
  377. for(SEQUENCEINDEX seqNum = 1; seqNum < GetNumSequences(); seqNum++)
  378. {
  379. ModSequence &seq = m_Sequences[seqNum];
  380. const ORDERINDEX firstOrder = firstSeq.GetLength() + 1; // +1 for separator item
  381. const ORDERINDEX lengthTrimmed = seq.GetLengthTailTrimmed();
  382. if(firstOrder + lengthTrimmed > m_sndFile.GetModSpecifications().ordersMax)
  383. {
  384. m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("WARNING: Cannot merge Sequence {} (too long!)")(seqNum + 1));
  385. continue;
  386. }
  387. firstSeq.reserve(firstOrder + lengthTrimmed);
  388. firstSeq.push_back(); // Separator item
  389. RestartPosToPattern(seqNum);
  390. for(ORDERINDEX ord = 0; ord < lengthTrimmed; ord++)
  391. {
  392. PATTERNINDEX pat = seq[ord];
  393. firstSeq.push_back(pat);
  394. // Try to fix pattern jump commands
  395. if(!m_sndFile.Patterns.IsValidPat(pat)) continue;
  396. auto m = m_sndFile.Patterns[pat].begin();
  397. for(size_t len = 0; len < m_sndFile.Patterns[pat].GetNumRows() * m_sndFile.m_nChannels; m++, len++)
  398. {
  399. if(m->command == CMD_POSITIONJUMP)
  400. {
  401. if(patternsFixed[pat] != SEQUENCEINDEX_INVALID && patternsFixed[pat] != seqNum)
  402. {
  403. // Oops, some other sequence uses this pattern already.
  404. const PATTERNINDEX newPat = m_sndFile.Patterns.Duplicate(pat, true);
  405. if(newPat != PATTERNINDEX_INVALID)
  406. {
  407. // Could create new pattern - copy data over and continue from here.
  408. firstSeq[firstOrder + ord] = newPat;
  409. m = m_sndFile.Patterns[newPat].begin() + len;
  410. if(newPat >= patternsFixed.size())
  411. patternsFixed.resize(newPat + 1, SEQUENCEINDEX_INVALID);
  412. pat = newPat;
  413. } else
  414. {
  415. // Cannot create new pattern: notify the user
  416. m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("CONFLICT: Pattern break commands in Pattern {} might be broken since it has been used in several sequences!")(pat));
  417. }
  418. }
  419. m->param = static_cast<ModCommand::PARAM>(m->param + firstOrder);
  420. patternsFixed[pat] = seqNum;
  421. }
  422. }
  423. }
  424. }
  425. m_Sequences.erase(m_Sequences.begin() + 1, m_Sequences.end());
  426. return true;
  427. }
  428. // Check if a playback position is currently locked (inaccessible)
  429. bool ModSequence::IsPositionLocked(ORDERINDEX position) const
  430. {
  431. return(m_sndFile.m_lockOrderStart != ORDERINDEX_INVALID
  432. && (position < m_sndFile.m_lockOrderStart || position > m_sndFile.m_lockOrderEnd));
  433. }
  434. bool ModSequence::HasSubsongs() const
  435. {
  436. const auto endPat = begin() + GetLengthTailTrimmed();
  437. return std::find_if(begin(), endPat,
  438. [&](PATTERNINDEX pat) { return pat != GetIgnoreIndex() && !m_sndFile.Patterns.IsValidPat(pat); }) != endPat;
  439. }
  440. #endif // MODPLUG_TRACKER
  441. /////////////////////////////////////
  442. // Read/Write
  443. /////////////////////////////////////
  444. #ifndef MODPLUG_NO_FILESAVE
  445. size_t ModSequence::WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex, uint8 ignoreIndex) const
  446. {
  447. const size_t limit = std::min(count, GetLength());
  448. for(size_t i = 0; i < limit; i++)
  449. {
  450. const PATTERNINDEX pat = at(i);
  451. uint8 temp = static_cast<uint8>(pat);
  452. if(pat == GetInvalidPatIndex()) temp = stopIndex;
  453. else if(pat == GetIgnoreIndex() || pat > 0xFF) temp = ignoreIndex;
  454. mpt::IO::WriteIntLE<uint8>(f, temp);
  455. }
  456. // Fill non-existing order items with stop indices
  457. for(size_t i = limit; i < count; i++)
  458. {
  459. mpt::IO::WriteIntLE<uint8>(f, stopIndex);
  460. }
  461. return count; //Returns the number of bytes written.
  462. }
  463. #endif // MODPLUG_NO_FILESAVE
  464. void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t)
  465. {
  466. uint16 size;
  467. mpt::IO::ReadIntLE<uint16>(iStrm, size);
  468. if(size > ModSpecs::mptm.ordersMax)
  469. {
  470. seq.m_sndFile.AddToLog(LogWarning, MPT_UFORMAT("Module has sequence of length {}; it will be truncated to maximum supported length, {}.")(size, ModSpecs::mptm.ordersMax));
  471. size = ModSpecs::mptm.ordersMax;
  472. }
  473. seq(0).resize(size);
  474. for(auto &pat : seq(0))
  475. {
  476. uint16 temp;
  477. mpt::IO::ReadIntLE<uint16>(iStrm, temp);
  478. pat = temp;
  479. }
  480. }
  481. #ifndef MODPLUG_NO_FILESAVE
  482. void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq)
  483. {
  484. const uint16 size = seq().GetLength();
  485. mpt::IO::WriteIntLE<uint16>(oStrm, size);
  486. for(auto pat : seq())
  487. {
  488. mpt::IO::WriteIntLE<uint16>(oStrm, static_cast<uint16>(pat));
  489. }
  490. }
  491. #endif // MODPLUG_NO_FILESAVE
  492. #ifndef MODPLUG_NO_FILESAVE
  493. void WriteModSequence(std::ostream& oStrm, const ModSequence& seq)
  494. {
  495. srlztn::SsbWrite ssb(oStrm);
  496. ssb.BeginWrite(FileIdSequence, Version::Current().GetRawVersion());
  497. int8 useUTF8 = 1;
  498. ssb.WriteItem(useUTF8, "u");
  499. ssb.WriteItem(mpt::ToCharset(mpt::Charset::UTF8, seq.GetName()), "n");
  500. const uint16 length = seq.GetLengthTailTrimmed();
  501. ssb.WriteItem<uint16>(length, "l");
  502. ssb.WriteItem(seq, "a", srlztn::VectorWriter<uint16>(length));
  503. if(seq.GetRestartPos() > 0)
  504. ssb.WriteItem<uint16>(seq.GetRestartPos(), "r");
  505. ssb.FinishWrite();
  506. }
  507. #endif // MODPLUG_NO_FILESAVE
  508. void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset)
  509. {
  510. srlztn::SsbRead ssb(iStrm);
  511. ssb.BeginRead(FileIdSequence, Version::Current().GetRawVersion());
  512. if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0)
  513. return;
  514. int8 useUTF8 = 0;
  515. ssb.ReadItem(useUTF8, "u");
  516. std::string str;
  517. ssb.ReadItem(str, "n");
  518. seq.SetName(mpt::ToUnicode(useUTF8 ? mpt::Charset::UTF8 : defaultCharset, str));
  519. ORDERINDEX nSize = 0;
  520. ssb.ReadItem(nSize, "l");
  521. LimitMax(nSize, ModSpecs::mptm.ordersMax);
  522. ssb.ReadItem(seq, "a", srlztn::VectorReader<uint16>(nSize));
  523. ORDERINDEX restartPos = ORDERINDEX_INVALID;
  524. if(ssb.ReadItem(restartPos, "r") != srlztn::SsbRead::EntryNotFound && restartPos < nSize)
  525. seq.SetRestartPos(restartPos);
  526. }
  527. #ifndef MODPLUG_NO_FILESAVE
  528. void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq)
  529. {
  530. srlztn::SsbWrite ssb(oStrm);
  531. ssb.BeginWrite(FileIdSequences, Version::Current().GetRawVersion());
  532. const uint8 nSeqs = seq.GetNumSequences();
  533. const uint8 nCurrent = seq.GetCurrentSequenceIndex();
  534. ssb.WriteItem(nSeqs, "n");
  535. ssb.WriteItem(nCurrent, "c");
  536. for(uint8 i = 0; i < nSeqs; i++)
  537. {
  538. ssb.WriteItem(seq(i), srlztn::ID::FromInt<uint8>(i), &WriteModSequence);
  539. }
  540. ssb.FinishWrite();
  541. }
  542. #endif // MODPLUG_NO_FILESAVE
  543. void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset)
  544. {
  545. srlztn::SsbRead ssb(iStrm);
  546. ssb.BeginRead(FileIdSequences, Version::Current().GetRawVersion());
  547. if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0)
  548. return;
  549. SEQUENCEINDEX seqs = 0;
  550. uint8 currentSeq = 0;
  551. ssb.ReadItem(seqs, "n");
  552. if (seqs == 0)
  553. return;
  554. LimitMax(seqs, MAX_SEQUENCES);
  555. ssb.ReadItem(currentSeq, "c");
  556. if (seq.GetNumSequences() < seqs)
  557. seq.m_Sequences.resize(seqs, ModSequence(seq.m_sndFile));
  558. // There used to be only one restart position for all sequences
  559. ORDERINDEX legacyRestartPos = seq(0).GetRestartPos();
  560. for(SEQUENCEINDEX i = 0; i < seqs; i++)
  561. {
  562. seq(i).SetRestartPos(legacyRestartPos);
  563. 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); });
  564. }
  565. seq.m_currentSeq = (currentSeq < seq.GetNumSequences()) ? currentSeq : 0;
  566. }
  567. OPENMPT_NAMESPACE_END