1 /*
2  * Pattern.cpp
3  * -----------
4  * Purpose: Module Pattern header class
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 "pattern.h"
13 #include "patternContainer.h"
14 #include "../common/serialization_utils.h"
15 #include "../common/version.h"
16 #include "ITTools.h"
17 #include "Sndfile.h"
18 #include "mod_specifications.h"
19 #include "mpt/io/io.hpp"
20 #include "mpt/io/io_stdstream.hpp"
21 
22 
23 OPENMPT_NAMESPACE_BEGIN
24 
25 
GetSoundFile()26 CSoundFile& CPattern::GetSoundFile() { return m_rPatternContainer.GetSoundFile(); }
GetSoundFile() const27 const CSoundFile& CPattern::GetSoundFile() const { return m_rPatternContainer.GetSoundFile(); }
28 
29 
GetNumChannels() const30 CHANNELINDEX CPattern::GetNumChannels() const
31 {
32 	return GetSoundFile().GetNumChannels();
33 }
34 
35 
36 // Check if there is any note data on a given row.
IsEmptyRow(ROWINDEX row) const37 bool CPattern::IsEmptyRow(ROWINDEX row) const
38 {
39 	if(m_ModCommands.empty() || !IsValidRow(row))
40 	{
41 		return true;
42 	}
43 
44 	PatternRow data = GetRow(row);
45 	for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, data++)
46 	{
47 		if(!data->IsEmpty())
48 		{
49 			return false;
50 		}
51 	}
52 	return true;
53 }
54 
55 
SetSignature(const ROWINDEX rowsPerBeat,const ROWINDEX rowsPerMeasure)56 bool CPattern::SetSignature(const ROWINDEX rowsPerBeat, const ROWINDEX rowsPerMeasure)
57 {
58 	if(rowsPerBeat < 1
59 		|| rowsPerBeat > GetSoundFile().GetModSpecifications().patternRowsMax
60 		|| rowsPerMeasure < rowsPerBeat
61 		|| rowsPerMeasure > GetSoundFile().GetModSpecifications().patternRowsMax)
62 	{
63 		return false;
64 	}
65 	m_RowsPerBeat = rowsPerBeat;
66 	m_RowsPerMeasure = rowsPerMeasure;
67 	return true;
68 }
69 
70 
71 // Add or remove rows from the pattern.
Resize(const ROWINDEX newRowCount,bool enforceFormatLimits,bool resizeAtEnd)72 bool CPattern::Resize(const ROWINDEX newRowCount, bool enforceFormatLimits, bool resizeAtEnd)
73 {
74 	CSoundFile &sndFile = GetSoundFile();
75 
76 	if(newRowCount == m_Rows || newRowCount < 1 || newRowCount > MAX_PATTERN_ROWS)
77 	{
78 		return false;
79 	}
80 	if(enforceFormatLimits)
81 	{
82 		auto &specs = sndFile.GetModSpecifications();
83 		if(newRowCount > specs.patternRowsMax || newRowCount < specs.patternRowsMin) return false;
84 	}
85 
86 	try
87 	{
88 		size_t count = ((newRowCount > m_Rows) ? (newRowCount - m_Rows) : (m_Rows - newRowCount)) * GetNumChannels();
89 
90 		if(newRowCount > m_Rows)
91 			m_ModCommands.insert(resizeAtEnd ? m_ModCommands.end() : m_ModCommands.begin(), count, ModCommand::Empty());
92 		else if(resizeAtEnd)
93 			m_ModCommands.erase(m_ModCommands.end() - count, m_ModCommands.end());
94 		else
95 			m_ModCommands.erase(m_ModCommands.begin(), m_ModCommands.begin() + count);
96 	} catch(mpt::out_of_memory e)
97 	{
98 		mpt::delete_out_of_memory(e);
99 		return false;
100 	}
101 
102 	m_Rows = newRowCount;
103 	return true;
104 }
105 
106 
ClearCommands()107 void CPattern::ClearCommands()
108 {
109 	std::fill(m_ModCommands.begin(), m_ModCommands.end(), ModCommand::Empty());
110 }
111 
112 
AllocatePattern(ROWINDEX rows)113 bool CPattern::AllocatePattern(ROWINDEX rows)
114 {
115 	size_t newSize = GetNumChannels() * rows;
116 	if(rows == 0)
117 	{
118 		return false;
119 	} else if(rows == GetNumRows() && m_ModCommands.size() == newSize)
120 	{
121 		// Re-use allocated memory
122 		ClearCommands();
123 		return true;
124 	} else
125 	{
126 		// Do this in two steps in order to keep the old pattern data in case of OOM
127 		decltype(m_ModCommands) newPattern(newSize, ModCommand::Empty());
128 		m_ModCommands = std::move(newPattern);
129 	}
130 	m_Rows = rows;
131 	return true;
132 }
133 
134 
Deallocate()135 void CPattern::Deallocate()
136 {
137 	m_Rows = m_RowsPerBeat = m_RowsPerMeasure = 0;
138 	m_ModCommands.clear();
139 	m_PatternName.clear();
140 }
141 
142 
operator =(const CPattern & pat)143 CPattern& CPattern::operator= (const CPattern &pat)
144 {
145 	m_ModCommands = pat.m_ModCommands;
146 	m_Rows = pat.m_Rows;
147 	m_RowsPerBeat = pat.m_RowsPerBeat;
148 	m_RowsPerMeasure = pat.m_RowsPerMeasure;
149 	m_tempoSwing = pat.m_tempoSwing;
150 	m_PatternName = pat.m_PatternName;
151 	return *this;
152 }
153 
154 
155 
operator ==(const CPattern & other) const156 bool CPattern::operator== (const CPattern &other) const
157 {
158 	return GetNumRows() == other.GetNumRows()
159 		&& GetNumChannels() == other.GetNumChannels()
160 		&& GetOverrideSignature() == other.GetOverrideSignature()
161 		&& GetRowsPerBeat() == other.GetRowsPerBeat()
162 		&& GetRowsPerMeasure() == other.GetRowsPerMeasure()
163 		&& GetTempoSwing() == other.GetTempoSwing()
164 		&& m_ModCommands == other.m_ModCommands;
165 }
166 
167 
168 #ifdef MODPLUG_TRACKER
169 
Expand()170 bool CPattern::Expand()
171 {
172 	const ROWINDEX newRows = m_Rows * 2;
173 	const CHANNELINDEX nChns = GetNumChannels();
174 
175 	if(m_ModCommands.empty()
176 		|| newRows > GetSoundFile().GetModSpecifications().patternRowsMax)
177 	{
178 		return false;
179 	}
180 
181 	decltype(m_ModCommands) newPattern;
182 	try
183 	{
184 		newPattern.assign(m_ModCommands.size() * 2, ModCommand::Empty());
185 	} catch(mpt::out_of_memory e)
186 	{
187 		mpt::delete_out_of_memory(e);
188 		return false;
189 	}
190 
191 	for(auto mSrc = m_ModCommands.begin(), mDst = newPattern.begin(); mSrc != m_ModCommands.end(); mSrc += nChns, mDst += 2 * nChns)
192 	{
193 		std::copy(mSrc, mSrc + nChns, mDst);
194 	}
195 
196 	m_ModCommands = std::move(newPattern);
197 	m_Rows = newRows;
198 
199 	return true;
200 }
201 
202 
Shrink()203 bool CPattern::Shrink()
204 {
205 	if (m_ModCommands.empty()
206 		|| m_Rows < GetSoundFile().GetModSpecifications().patternRowsMin * 2)
207 	{
208 		return false;
209 	}
210 
211 	m_Rows /= 2;
212 	const CHANNELINDEX nChns = GetNumChannels();
213 
214 	for(ROWINDEX y = 0; y < m_Rows; y++)
215 	{
216 		const PatternRow srcRow = GetRow(y * 2);
217 		const PatternRow nextSrcRow = GetRow(y * 2 + 1);
218 		PatternRow destRow = GetRow(y);
219 
220 		for(CHANNELINDEX x = 0; x < nChns; x++)
221 		{
222 			const ModCommand &src = srcRow[x];
223 			const ModCommand &srcNext = nextSrcRow[x];
224 			ModCommand &dest = destRow[x];
225 			dest = src;
226 
227 			if(dest.note == NOTE_NONE && !dest.instr)
228 			{
229 				// Fill in data from next row if field is empty
230 				dest.note = srcNext.note;
231 				dest.instr = srcNext.instr;
232 				if(srcNext.volcmd != VOLCMD_NONE)
233 				{
234 					dest.volcmd = srcNext.volcmd;
235 					dest.vol = srcNext.vol;
236 				}
237 				if(dest.command == CMD_NONE)
238 				{
239 					dest.command = srcNext.command;
240 					dest.param = srcNext.param;
241 				}
242 			}
243 		}
244 	}
245 	m_ModCommands.resize(m_Rows * nChns);
246 
247 	return true;
248 }
249 
250 
251 #endif // MODPLUG_TRACKER
252 
253 
SetName(const std::string & newName)254 bool CPattern::SetName(const std::string &newName)
255 {
256 	m_PatternName = newName;
257 	return true;
258 }
259 
260 
SetName(const char * newName,size_t maxChars)261 bool CPattern::SetName(const char *newName, size_t maxChars)
262 {
263 	if(newName == nullptr || maxChars == 0)
264 	{
265 		return false;
266 	}
267 	const auto nameEnd = std::find(newName, newName + maxChars, '\0');
268 	m_PatternName.assign(newName, nameEnd);
269 	return true;
270 }
271 
272 
273 // Write some kind of effect data to the pattern. Exact data to be written and write behaviour can be found in the EffectWriter object.
WriteEffect(EffectWriter & settings)274 bool CPattern::WriteEffect(EffectWriter &settings)
275 {
276 	// First, reject invalid parameters.
277 	if(m_ModCommands.empty()
278 		|| settings.m_row >= GetNumRows()
279 		|| (settings.m_channel >= GetNumChannels() && settings.m_channel != CHANNELINDEX_INVALID))
280 	{
281 		return false;
282 	}
283 
284 	CHANNELINDEX scanChnMin = settings.m_channel, scanChnMax = settings.m_channel;
285 
286 	// Scan all channels
287 	if(settings.m_channel == CHANNELINDEX_INVALID)
288 	{
289 		scanChnMin = 0;
290 		scanChnMax = GetNumChannels() - 1;
291 	}
292 
293 	ModCommand * const baseCommand = GetpModCommand(settings.m_row, scanChnMin);
294 	ModCommand *m;
295 
296 	// Scan channel(s) for same effect type - if an effect of the same type is already present, exit.
297 	if(!settings.m_allowMultiple)
298 	{
299 		m = baseCommand;
300 		for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++)
301 		{
302 			if(!settings.m_isVolEffect && m->command == settings.m_command)
303 				return true;
304 			if(settings.m_isVolEffect && m->volcmd == settings.m_volcmd)
305 				return true;
306 		}
307 	}
308 
309 	// Easy case: check if there's some space left to put the effect somewhere
310 	m = baseCommand;
311 	for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++)
312 	{
313 		if(!settings.m_isVolEffect && m->command == CMD_NONE)
314 		{
315 			m->command = settings.m_command;
316 			m->param = settings.m_param;
317 			return true;
318 		}
319 		if(settings.m_isVolEffect && m->volcmd == VOLCMD_NONE)
320 		{
321 			m->volcmd = settings.m_volcmd;
322 			m->vol = settings.m_vol;
323 			return true;
324 		}
325 	}
326 
327 	// Ok, apparently there's no space. If we haven't tried already, try to map it to the volume column or effect column instead.
328 	if(settings.m_retry)
329 	{
330 		const bool isS3M = (GetSoundFile().GetType() & MOD_TYPE_S3M);
331 
332 		// Move some effects that also work in the volume column, so there's place for our new effect.
333 		if(!settings.m_isVolEffect)
334 		{
335 			m = baseCommand;
336 			for(CHANNELINDEX i = scanChnMin; i <= scanChnMax; i++, m++)
337 			{
338 				switch(m->command)
339 				{
340 				case CMD_VOLUME:
341 					if(!GetSoundFile().GetModSpecifications().HasVolCommand(VOLCMD_VOLUME))
342 					{
343 						break;
344 					}
345 					m->volcmd = VOLCMD_VOLUME;
346 					m->vol = m->param;
347 					m->command = settings.m_command;
348 					m->param = settings.m_param;
349 					return true;
350 
351 				case CMD_PANNING8:
352 					if(isS3M && m->param > 0x80)
353 					{
354 						break;
355 					}
356 
357 					m->volcmd = VOLCMD_PANNING;
358 					m->command = settings.m_command;
359 
360 					if(isS3M)
361 						m->vol = (m->param + 1u) / 2u;
362 					else
363 						m->vol = (m->param + 2u) / 4u;
364 
365 					m->param = settings.m_param;
366 					return true;
367 
368 				default:
369 					break;
370 				}
371 			}
372 		}
373 
374 		// Let's try it again by writing into the "other" effect column.
375 		if(settings.m_isVolEffect)
376 		{
377 			// Convert volume effect to normal effect
378 			ModCommand::COMMAND newCommand = CMD_NONE;
379 			ModCommand::PARAM newParam = settings.m_vol;
380 			switch(settings.m_volcmd)
381 			{
382 			case VOLCMD_PANNING:
383 				newCommand = CMD_PANNING8;
384 				newParam = mpt::saturate_cast<ModCommand::PARAM>(settings.m_vol * (isS3M ? 2u : 4u));
385 				break;
386 			case VOLCMD_VOLUME:
387 				newCommand = CMD_VOLUME;
388 				break;
389 			default:
390 				break;
391 			}
392 
393 			if(newCommand != CMD_NONE)
394 			{
395 				settings.m_command = static_cast<EffectCommand>(newCommand);
396 				settings.m_param = newParam;
397 				settings.m_retry = false;
398 			}
399 		} else
400 		{
401 			// Convert normal effect to volume effect
402 			ModCommand::VOLCMD newVolCmd = VOLCMD_NONE;
403 			ModCommand::VOL newVol = settings.m_param;
404 			if(settings.m_command == CMD_PANNING8 && isS3M)
405 			{
406 				// This needs some manual fixing.
407 				if(settings.m_param <= 0x80)
408 				{
409 					// Can't have surround in volume column, only normal panning
410 					newVolCmd = VOLCMD_PANNING;
411 					newVol /= 2u;
412 				}
413 			} else
414 			{
415 				newVolCmd = settings.m_command;
416 				if(!ModCommand::ConvertVolEffect(newVolCmd, newVol, true))
417 				{
418 					// No Success :(
419 					newVolCmd = VOLCMD_NONE;
420 				}
421 			}
422 
423 			if(newVolCmd != CMD_NONE)
424 			{
425 				settings.m_volcmd = static_cast<VolumeCommand>(newVolCmd);
426 				settings.m_vol = newVol;
427 				settings.m_retry = false;
428 			}
429 		}
430 
431 		if(!settings.m_retry)
432 		{
433 			settings.m_isVolEffect = !settings.m_isVolEffect;
434 			if(WriteEffect(settings))
435 			{
436 				return true;
437 			}
438 		}
439 	}
440 
441 	// Try in the next row if possible (this may also happen if we already retried)
442 	if(settings.m_retryMode == EffectWriter::rmTryNextRow && settings.m_row + 1 < GetNumRows())
443 	{
444 		settings.m_row++;
445 		settings.m_retry = true;
446 		return WriteEffect(settings);
447 	} else if(settings.m_retryMode == EffectWriter::rmTryPreviousRow && settings.m_row > 0)
448 	{
449 		settings.m_row--;
450 		settings.m_retry = true;
451 		return WriteEffect(settings);
452 	}
453 
454 	return false;
455 }
456 
457 
458 ////////////////////////////////////////////////////////////////////////
459 //
460 //	Pattern serialization functions
461 //
462 ////////////////////////////////////////////////////////////////////////
463 
464 
465 enum maskbits
466 {
467 	noteBit			= (1 << 0),
468 	instrBit		= (1 << 1),
469 	volcmdBit		= (1 << 2),
470 	volBit			= (1 << 3),
471 	commandBit		= (1 << 4),
472 	effectParamBit	= (1 << 5),
473 	extraData		= (1 << 6)
474 };
475 
476 void WriteData(std::ostream& oStrm, const CPattern& pat);
477 void ReadData(std::istream& iStrm, CPattern& pat, const size_t nSize = 0);
478 
WriteModPattern(std::ostream & oStrm,const CPattern & pat)479 void WriteModPattern(std::ostream& oStrm, const CPattern& pat)
480 {
481 	srlztn::SsbWrite ssb(oStrm);
482 	ssb.BeginWrite(FileIdPattern, Version::Current().GetRawVersion());
483 	ssb.WriteItem(pat, "data", &WriteData);
484 	// pattern time signature
485 	if(pat.GetOverrideSignature())
486 	{
487 		ssb.WriteItem<uint32>(pat.GetRowsPerBeat(), "RPB.");
488 		ssb.WriteItem<uint32>(pat.GetRowsPerMeasure(), "RPM.");
489 	}
490 	if(pat.HasTempoSwing())
491 	{
492 		ssb.WriteItem<TempoSwing>(pat.GetTempoSwing(), "SWNG", TempoSwing::Serialize);
493 	}
494 	ssb.FinishWrite();
495 }
496 
497 
ReadModPattern(std::istream & iStrm,CPattern & pat,const size_t)498 void ReadModPattern(std::istream& iStrm, CPattern& pat, const size_t)
499 {
500 	srlztn::SsbRead ssb(iStrm);
501 	ssb.BeginRead(FileIdPattern, Version::Current().GetRawVersion());
502 	if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0)
503 		return;
504 	ssb.ReadItem(pat, "data", &ReadData);
505 	// pattern time signature
506 	uint32 rpb = 0, rpm = 0;
507 	ssb.ReadItem<uint32>(rpb, "RPB.");
508 	ssb.ReadItem<uint32>(rpm, "RPM.");
509 	pat.SetSignature(rpb, rpm);
510 	TempoSwing swing;
511 	ssb.ReadItem<TempoSwing>(swing, "SWNG", TempoSwing::Deserialize);
512 	if(!swing.empty())
513 		swing.resize(pat.GetRowsPerBeat());
514 	pat.SetTempoSwing(swing);
515 }
516 
517 
CreateDiffMask(const ModCommand & chnMC,const ModCommand & newMC)518 static uint8 CreateDiffMask(const ModCommand &chnMC, const ModCommand &newMC)
519 {
520 	uint8 mask = 0;
521 	if(chnMC.note != newMC.note)
522 		mask |= noteBit;
523 	if(chnMC.instr != newMC.instr)
524 		mask |= instrBit;
525 	if(chnMC.volcmd != newMC.volcmd)
526 		mask |= volcmdBit;
527 	if(chnMC.vol != newMC.vol)
528 		mask |= volBit;
529 	if(chnMC.command != newMC.command)
530 		mask |= commandBit;
531 	if(chnMC.param != newMC.param)
532 		mask |= effectParamBit;
533 	return mask;
534 }
535 
536 
537 // Writes pattern data. Adapted from SaveIT.
WriteData(std::ostream & oStrm,const CPattern & pat)538 void WriteData(std::ostream& oStrm, const CPattern& pat)
539 {
540 	if(!pat.IsValid())
541 		return;
542 
543 	const ROWINDEX rows = pat.GetNumRows();
544 	const CHANNELINDEX chns = pat.GetNumChannels();
545 	std::vector<ModCommand> lastChnMC(chns);
546 
547 	for(ROWINDEX r = 0; r<rows; r++)
548 	{
549 		for(CHANNELINDEX c = 0; c<chns; c++)
550 		{
551 			const ModCommand m = *pat.GetpModCommand(r, c);
552 			// Writing only commands not written in IT-pattern writing:
553 			// For now this means only NOTE_PC and NOTE_PCS.
554 			if(!m.IsPcNote())
555 				continue;
556 			uint8 diffmask = CreateDiffMask(lastChnMC[c], m);
557 			uint8 chval = static_cast<uint8>(c+1);
558 			if(diffmask != 0)
559 				chval |= IT_bitmask_patternChanEnabled_c;
560 
561 			mpt::IO::WriteIntLE<uint8>(oStrm, chval);
562 
563 			if(diffmask)
564 			{
565 				lastChnMC[c] = m;
566 				mpt::IO::WriteIntLE<uint8>(oStrm, diffmask);
567 				if(diffmask & noteBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.note);
568 				if(diffmask & instrBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.instr);
569 				if(diffmask & volcmdBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.volcmd);
570 				if(diffmask & volBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.vol);
571 				if(diffmask & commandBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.command);
572 				if(diffmask & effectParamBit) mpt::IO::WriteIntLE<uint8>(oStrm, m.param);
573 			}
574 		}
575 		mpt::IO::WriteIntLE<uint8>(oStrm, 0); // Write end of row marker.
576 	}
577 }
578 
579 
580 #define READITEM(itembit,id)		\
581 if(diffmask & itembit)				\
582 {									\
583 	mpt::IO::ReadIntLE<uint8>(iStrm, temp);	\
584 	if(ch < chns)					\
585 		lastChnMC[ch].id = temp;	\
586 }									\
587 if(ch < chns)						\
588 	m.id = lastChnMC[ch].id;
589 
590 
ReadData(std::istream & iStrm,CPattern & pat,const size_t)591 void ReadData(std::istream& iStrm, CPattern& pat, const size_t)
592 {
593 	if (!pat.IsValid()) // Expecting patterns to be allocated and resized properly.
594 		return;
595 
596 	const CHANNELINDEX chns = pat.GetNumChannels();
597 	const ROWINDEX rows = pat.GetNumRows();
598 
599 	std::vector<ModCommand> lastChnMC(chns);
600 
601 	ROWINDEX row = 0;
602 	while(row < rows && iStrm.good())
603 	{
604 		uint8 t = 0;
605 		mpt::IO::ReadIntLE<uint8>(iStrm, t);
606 		if(t == 0)
607 		{
608 			row++;
609 			continue;
610 		}
611 
612 		CHANNELINDEX ch = (t & IT_bitmask_patternChanField_c);
613 		if(ch > 0)
614 			ch--;
615 
616 		uint8 diffmask = 0;
617 		if((t & IT_bitmask_patternChanEnabled_c) != 0)
618 			mpt::IO::ReadIntLE<uint8>(iStrm, diffmask);
619 		uint8 temp = 0;
620 
621 		ModCommand dummy = ModCommand::Empty();
622 		ModCommand& m = (ch < chns) ? *pat.GetpModCommand(row, ch) : dummy;
623 
624 		READITEM(noteBit, note);
625 		READITEM(instrBit, instr);
626 		READITEM(volcmdBit, volcmd);
627 		READITEM(volBit, vol);
628 		READITEM(commandBit, command);
629 		READITEM(effectParamBit, param);
630 		if(diffmask & extraData)
631 		{
632 			//Ignore additional data.
633 			uint8 size;
634 			mpt::IO::ReadIntLE<uint8>(iStrm, size);
635 			iStrm.ignore(size);
636 		}
637 	}
638 }
639 
640 #undef READITEM
641 
642 
643 OPENMPT_NAMESPACE_END
644