| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 | /* * Load_digi.cpp * ------------- * Purpose: Digi Booster module loader * Notes  : Basically these are like ProTracker MODs with a few extra features such as more channels, longer samples and a few more effects. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */#include "stdafx.h"#include "Loaders.h"OPENMPT_NAMESPACE_BEGIN// DIGI File Headerstruct DIGIFileHeader{	char     signature[20];	char     versionStr[4];	// Supposed to be "V1.6" or similar, but other values like "TAP!" have been found as well.	uint8be  versionInt;	// e.g. 0x16 = 1.6	uint8be  numChannels;	uint8be  packEnable;	char     unknown[19];	uint8be  lastPatIndex;	uint8be  lastOrdIndex;	uint8be  orders[128];	uint32be smpLength[31];	uint32be smpLoopStart[31];	uint32be smpLoopLength[31];	uint8be  smpVolume[31];	uint8be  smpFinetune[31];};MPT_BINARY_STRUCT(DIGIFileHeader, 610)static void ReadDIGIPatternEntry(FileReader &file, ModCommand &m){	CSoundFile::ReadMODPatternEntry(file, m);	CSoundFile::ConvertModCommand(m);	if(m.command == CMD_MODCMDEX)	{		switch(m.param & 0xF0)		{		case 0x30:			// E3x: Play sample backwards (E30 stops sample when it reaches the beginning, any other value plays it from the beginning including regular loop)			// The play direction is also reset if a new note is played on the other channel linked to this channel.			// The behaviour is rather broken when there is no note next to the ommand.			m.command = CMD_DIGIREVERSESAMPLE;			m.param &= 0x0F;			break;		case 0x40:			// E40: Stop playing sample			if(m.param == 0x40)			{				m.note = NOTE_NOTECUT;				m.command = CMD_NONE;			}			break;		case 0x80:			// E8x: High sample offset			m.command = CMD_S3MCMDEX;			m.param = 0xA0 | (m.param & 0x0F);		}	} else if(m.command == CMD_PANNING8)	{		// 8xx "Robot" effect (not supported)		m.command = CMD_NONE;	}}static bool ValidateHeader(const DIGIFileHeader &fileHeader){	if(std::memcmp(fileHeader.signature, "DIGI Booster module\0", 20)		|| !fileHeader.numChannels		|| fileHeader.numChannels > 8		|| fileHeader.lastOrdIndex > 127)	{		return false;	}	return true;}CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize){	DIGIFileHeader fileHeader;	if(!file.ReadStruct(fileHeader))	{		return ProbeWantMoreData;	}	if(!ValidateHeader(fileHeader))	{		return ProbeFailure;	}	MPT_UNREFERENCED_PARAMETER(pfilesize);	return ProbeSuccess;}bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags){	file.Rewind();	DIGIFileHeader fileHeader;	if(!file.ReadStruct(fileHeader))	{		return false;	}	if(!ValidateHeader(fileHeader))	{		return false;	}	if(loadFlags == onlyVerifyHeader)	{		return true;	}	// Globals	InitializeGlobals(MOD_TYPE_DIGI);	InitializeChannels();	m_nChannels = fileHeader.numChannels;	m_nSamples = 31;	m_nSamplePreAmp = 256 / m_nChannels;	m_modFormat.formatName = U_("DigiBooster");	m_modFormat.type = U_("digi");	m_modFormat.madeWithTracker = MPT_UFORMAT("Digi Booster {}.{}")(fileHeader.versionInt >> 4, fileHeader.versionInt & 0x0F);	m_modFormat.charset = mpt::Charset::Amiga_no_C1;	ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.lastOrdIndex + 1);	// Read sample headers	for(SAMPLEINDEX smp = 0; smp < 31; smp++)	{		ModSample &sample = Samples[smp + 1];		sample.Initialize(MOD_TYPE_MOD);		sample.nLength = fileHeader.smpLength[smp];		sample.nLoopStart = fileHeader.smpLoopStart[smp];		sample.nLoopEnd = sample.nLoopStart + fileHeader.smpLoopLength[smp];		if(fileHeader.smpLoopLength[smp])		{			sample.uFlags.set(CHN_LOOP);		}		sample.SanitizeLoops();			sample.nVolume = std::min(fileHeader.smpVolume[smp].get(), uint8(64)) * 4;		sample.nFineTune = MOD2XMFineTune(fileHeader.smpFinetune[smp]);	}	// Read song + sample names	file.ReadString<mpt::String::maybeNullTerminated>(m_songName, 32);	for(SAMPLEINDEX smp = 1; smp <= 31; smp++)	{		file.ReadString<mpt::String::maybeNullTerminated>(m_szNames[smp], 30);	}	if(loadFlags & loadPatternData)		Patterns.ResizeArray(fileHeader.lastPatIndex + 1);	for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPatIndex; pat++)	{		FileReader patternChunk;		if(fileHeader.packEnable)		{			patternChunk = file.ReadChunk(file.ReadUint16BE());		} else		{			patternChunk = file.ReadChunk(4 * 64 * GetNumChannels());		}		if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64))		{			continue;		}		if(fileHeader.packEnable)		{			uint8 eventMask[64];			patternChunk.ReadArray(eventMask);			// Compressed patterns are stored in row-major order...			for(ROWINDEX row = 0; row < 64; row++)			{				PatternRow patRow = Patterns[pat].GetRow(row);				uint8 bit = 0x80;				for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, bit >>= 1)				{					if(eventMask[row] & bit)					{						ModCommand &m = patRow[chn];						ReadDIGIPatternEntry(patternChunk, m);					}				}			}		} else		{			// ...but uncompressed patterns are stored in column-major order. WTF!			for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)			{				for(ROWINDEX row = 0; row < 64; row++)				{					ReadDIGIPatternEntry(patternChunk, *Patterns[pat].GetpModCommand(row, chn));				}			}		}	}	if(loadFlags & loadSampleData)	{		// Reading Samples		const SampleIO sampleIO(			SampleIO::_8bit,			SampleIO::mono,			SampleIO::bigEndian,			SampleIO::signedPCM);		for(SAMPLEINDEX smp = 1; smp <= 31; smp++)		{			sampleIO.ReadSample(Samples[smp], file);		}	}	return true;}OPENMPT_NAMESPACE_END
 |