1 /*
2  * PatternContainer.cpp
3  * --------------------
4  * Purpose: Container class for managing patterns.
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 
10 
11 #include "stdafx.h"
12 #include "patternContainer.h"
13 #include "Sndfile.h"
14 #include "mod_specifications.h"
15 #include "../common/serialization_utils.h"
16 #include "../common/version.h"
17 
18 
19 OPENMPT_NAMESPACE_BEGIN
20 
21 
ClearPatterns()22 void CPatternContainer::ClearPatterns()
23 {
24 	DestroyPatterns();
25 	m_Patterns.assign(m_Patterns.size(), CPattern(*this));
26 }
27 
28 
DestroyPatterns()29 void CPatternContainer::DestroyPatterns()
30 {
31 	for(PATTERNINDEX i = 0; i < m_Patterns.size(); i++)
32 	{
33 		Remove(i);
34 	}
35 }
36 
37 
Duplicate(PATTERNINDEX from,bool respectQtyLimits)38 PATTERNINDEX CPatternContainer::Duplicate(PATTERNINDEX from, bool respectQtyLimits)
39 {
40 	if(!IsValidPat(from))
41 	{
42 		return PATTERNINDEX_INVALID;
43 	}
44 
45 	PATTERNINDEX newPatIndex = InsertAny(m_Patterns[from].GetNumRows(), respectQtyLimits);
46 
47 	if(newPatIndex != PATTERNINDEX_INVALID)
48 	{
49 		m_Patterns[newPatIndex] = m_Patterns[from];
50 	}
51 	return newPatIndex;
52 }
53 
54 
InsertAny(const ROWINDEX rows,bool respectQtyLimits)55 PATTERNINDEX CPatternContainer::InsertAny(const ROWINDEX rows, bool respectQtyLimits)
56 {
57 	PATTERNINDEX i = 0;
58 	for(i = 0; i < m_Patterns.size(); i++)
59 		if(!m_Patterns[i].IsValid()) break;
60 	if(respectQtyLimits && i >= m_rSndFile.GetModSpecifications().patternsMax)
61 		return PATTERNINDEX_INVALID;
62 	if(!Insert(i, rows))
63 		return PATTERNINDEX_INVALID;
64 	else return i;
65 }
66 
67 
Insert(const PATTERNINDEX index,const ROWINDEX rows)68 bool CPatternContainer::Insert(const PATTERNINDEX index, const ROWINDEX rows)
69 {
70 	if(rows > MAX_PATTERN_ROWS || rows == 0)
71 		return false;
72 	if(IsValidPat(index))
73 		return false;
74 
75 	try
76 	{
77 		if(index >= m_Patterns.size())
78 		{
79 			m_Patterns.resize(index + 1, CPattern(*this));
80 		}
81 		m_Patterns[index].AllocatePattern(rows);
82 		m_Patterns[index].RemoveSignature();
83 		m_Patterns[index].SetName("");
84 	} catch(mpt::out_of_memory e)
85 	{
86 		mpt::delete_out_of_memory(e);
87 		return false;
88 	}
89 	return m_Patterns[index].IsValid();
90 }
91 
92 
Remove(const PATTERNINDEX ipat)93 void CPatternContainer::Remove(const PATTERNINDEX ipat)
94 {
95 	if(ipat < m_Patterns.size()) m_Patterns[ipat].Deallocate();
96 }
97 
98 
IsPatternEmpty(const PATTERNINDEX nPat) const99 bool CPatternContainer::IsPatternEmpty(const PATTERNINDEX nPat) const
100 {
101 	if(!IsValidPat(nPat))
102 		return false;
103 
104 	for(const auto &m : m_Patterns[nPat].m_ModCommands)
105 	{
106 		if(!m.IsEmpty())
107 			return false;
108 	}
109 	return true;
110 }
111 
112 
ResizeArray(const PATTERNINDEX newSize)113 void CPatternContainer::ResizeArray(const PATTERNINDEX newSize)
114 {
115 	m_Patterns.resize(newSize, CPattern(*this));
116 }
117 
118 
OnModTypeChanged(const MODTYPE)119 void CPatternContainer::OnModTypeChanged(const MODTYPE /*oldtype*/)
120 {
121 	const CModSpecifications specs = m_rSndFile.GetModSpecifications();
122 	//if(specs.patternsMax < Size())
123 	//	ResizeArray(specs.patternsMax);
124 
125 	// remove pattern time signatures
126 	if(!specs.hasPatternSignatures)
127 	{
128 		for(PATTERNINDEX nPat = 0; nPat < m_Patterns.size(); nPat++)
129 		{
130 			m_Patterns[nPat].RemoveSignature();
131 			m_Patterns[nPat].RemoveTempoSwing();
132 		}
133 	}
134 }
135 
136 
GetNumPatterns() const137 PATTERNINDEX CPatternContainer::GetNumPatterns() const
138 {
139 	for(PATTERNINDEX pat = Size(); pat > 0; pat--)
140 	{
141 		if(IsValidPat(pat - 1))
142 		{
143 			return pat;
144 		}
145 	}
146 	return 0;
147 }
148 
149 
GetNumNamedPatterns() const150 PATTERNINDEX CPatternContainer::GetNumNamedPatterns() const
151 {
152 	if(Size() == 0)
153 	{
154 		return 0;
155 	}
156 	for(PATTERNINDEX nPat = Size(); nPat > 0; nPat--)
157 	{
158 		if(!m_Patterns[nPat - 1].GetName().empty())
159 		{
160 			return nPat;
161 		}
162 	}
163 	return 0;
164 }
165 
166 
167 
WriteModPatterns(std::ostream & oStrm,const CPatternContainer & patc)168 void WriteModPatterns(std::ostream& oStrm, const CPatternContainer& patc)
169 {
170 	srlztn::SsbWrite ssb(oStrm);
171 	ssb.BeginWrite(FileIdPatterns, Version::Current().GetRawVersion());
172 	const PATTERNINDEX nPatterns = patc.Size();
173 	uint16 nCount = 0;
174 	for(uint16 i = 0; i < nPatterns; i++) if (patc[i].IsValid())
175 	{
176 		ssb.WriteItem(patc[i], srlztn::ID::FromInt<uint16>(i), &WriteModPattern);
177 		nCount = i + 1;
178 	}
179 	ssb.WriteItem<uint16>(nCount, "num"); // Index of last pattern + 1.
180 	ssb.FinishWrite();
181 }
182 
183 
ReadModPatterns(std::istream & iStrm,CPatternContainer & patc,const size_t)184 void ReadModPatterns(std::istream& iStrm, CPatternContainer& patc, const size_t)
185 {
186 	srlztn::SsbRead ssb(iStrm);
187 	ssb.BeginRead(FileIdPatterns, Version::Current().GetRawVersion());
188 	if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0)
189 		return;
190 	PATTERNINDEX nPatterns = patc.Size();
191 	uint16 nCount = uint16_max;
192 	if (ssb.ReadItem(nCount, "num") != srlztn::SsbRead::EntryNotFound)
193 		nPatterns = nCount;
194 	LimitMax(nPatterns, ModSpecs::mptm.patternsMax);
195 	if (nPatterns > patc.Size())
196 		patc.ResizeArray(nPatterns);
197 	for(uint16 i = 0; i < nPatterns; i++)
198 	{
199 		ssb.ReadItem(patc[i], srlztn::ID::FromInt<uint16>(i), &ReadModPattern);
200 	}
201 }
202 
203 
204 OPENMPT_NAMESPACE_END
205