1 /*
2  * S3MTools.cpp
3  * ------------
4  * Purpose: Definition of S3M file structures and helper functions
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 "Loaders.h"
13 #include "S3MTools.h"
14 #include "../common/mptStringBuffer.h"
15 
16 
17 OPENMPT_NAMESPACE_BEGIN
18 
19 // Convert an S3M sample header to OpenMPT's internal sample header.
ConvertToMPT(ModSample & mptSmp,bool isST3) const20 void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp, bool isST3) const
21 {
22 	mptSmp.Initialize(MOD_TYPE_S3M);
23 	mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename);
24 
25 	if(sampleType == typePCM || sampleType == typeNone)
26 	{
27 		// Sample Length and Loops
28 		if(sampleType == typePCM)
29 		{
30 			mptSmp.nLength = length;
31 			mptSmp.nLoopStart = std::min(static_cast<SmpLength>(loopStart), mptSmp.nLength - 1);
32 			mptSmp.nLoopEnd = std::min(static_cast<SmpLength>(loopEnd), mptSmp.nLength);
33 			mptSmp.uFlags.set(CHN_LOOP, (flags & smpLoop) != 0);
34 		}
35 
36 		if(mptSmp.nLoopEnd < 2 || mptSmp.nLoopStart >= mptSmp.nLoopEnd || mptSmp.nLoopEnd - mptSmp.nLoopStart < 1)
37 		{
38 			mptSmp.nLoopStart = mptSmp.nLoopEnd = 0;
39 			mptSmp.uFlags.reset();
40 		}
41 	} else if(sampleType == typeAdMel)
42 	{
43 		OPLPatch patch;
44 		std::memcpy(patch.data() + 0, mpt::as_raw_memory(length).data(), 4);
45 		std::memcpy(patch.data() + 4, mpt::as_raw_memory(loopStart).data(), 4);
46 		std::memcpy(patch.data() + 8, mpt::as_raw_memory(loopEnd).data(), 4);
47 		mptSmp.SetAdlib(true, patch);
48 	}
49 
50 	// Volume / Panning
51 	mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4;
52 
53 	// C-5 frequency
54 	mptSmp.nC5Speed = c5speed;
55 	if(isST3)
56 	{
57 		// ST3 ignores or clamps the high 16 bits depending on the instrument type
58 		if(sampleType == typeAdMel)
59 			mptSmp.nC5Speed &= 0xFFFF;
60 		else
61 			LimitMax(mptSmp.nC5Speed, uint16_max);
62 	}
63 
64 	if(mptSmp.nC5Speed == 0)
65 		mptSmp.nC5Speed = 8363;
66 	else if(mptSmp.nC5Speed < 1024)
67 		mptSmp.nC5Speed = 1024;
68 
69 }
70 
71 
72 // Convert OpenMPT's internal sample header to an S3M sample header.
ConvertToS3M(const ModSample & mptSmp)73 SmpLength S3MSampleHeader::ConvertToS3M(const ModSample &mptSmp)
74 {
75 	SmpLength smpLength = 0;
76 	mpt::String::WriteBuf(mpt::String::maybeNullTerminated, filename) = mptSmp.filename;
77 	memcpy(magic, "SCRS", 4);
78 
79 	if(mptSmp.uFlags[CHN_ADLIB])
80 	{
81 		memcpy(magic, "SCRI", 4);
82 		sampleType = typeAdMel;
83 		std::memcpy(mpt::as_raw_memory(length   ).data(), mptSmp.adlib.data() + 0, 4);
84 		std::memcpy(mpt::as_raw_memory(loopStart).data(), mptSmp.adlib.data() + 4, 4);
85 		std::memcpy(mpt::as_raw_memory(loopEnd  ).data(), mptSmp.adlib.data() + 8, 4);
86 	} else if(mptSmp.HasSampleData())
87 	{
88 		sampleType = typePCM;
89 		length = mpt::saturate_cast<uint32>(mptSmp.nLength);
90 		loopStart = mpt::saturate_cast<uint32>(mptSmp.nLoopStart);
91 		loopEnd = mpt::saturate_cast<uint32>(mptSmp.nLoopEnd);
92 
93 		smpLength = length;
94 
95 		flags = (mptSmp.uFlags[CHN_LOOP] ? smpLoop : 0);
96 		if(mptSmp.uFlags[CHN_16BIT])
97 		{
98 			flags |= smp16Bit;
99 		}
100 		if(mptSmp.uFlags[CHN_STEREO])
101 		{
102 			flags |= smpStereo;
103 		}
104 	} else
105 	{
106 		sampleType = typeNone;
107 	}
108 
109 	defaultVolume = static_cast<uint8>(std::min(static_cast<uint16>(mptSmp.nVolume / 4), uint16(64)));
110 	if(mptSmp.nC5Speed != 0)
111 	{
112 		c5speed = mptSmp.nC5Speed;
113 	} else
114 	{
115 		c5speed = ModSample::TransposeToFrequency(mptSmp.RelativeTone, mptSmp.nFineTune);
116 	}
117 
118 	return smpLength;
119 }
120 
121 
122 // Retrieve the internal sample format flags for this sample.
GetSampleFormat(bool signedSamples) const123 SampleIO S3MSampleHeader::GetSampleFormat(bool signedSamples) const
124 {
125 	if(pack == S3MSampleHeader::pADPCM && !(flags & S3MSampleHeader::smp16Bit) && !(flags & S3MSampleHeader::smpStereo))
126 	{
127 		// MODPlugin :(
128 		return SampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::ADPCM);
129 	} else
130 	{
131 		return SampleIO(
132 			(flags & S3MSampleHeader::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit,
133 			(flags & S3MSampleHeader::smpStereo) ?  SampleIO::stereoSplit : SampleIO::mono,
134 			SampleIO::littleEndian,
135 			signedSamples ? SampleIO::signedPCM : SampleIO::unsignedPCM);
136 	}
137 }
138 
139 
140 // Calculate the sample position in file
GetSampleOffset() const141 uint32 S3MSampleHeader::GetSampleOffset() const
142 {
143 	return (dataPointer[1] << 4) | (dataPointer[2] << 12) | (dataPointer[0] << 20);
144 }
145 
146 
147 OPENMPT_NAMESPACE_END
148