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