1 /*
2 * Load_ams.cpp
3 * ------------
4 * Purpose: AMS (Extreme's Tracker / Velvet Studio) module loader
5 * Notes : Extreme was renamed to Velvet Development at some point,
6 * and thus they also renamed their tracker from
7 * "Extreme's Tracker" to "Velvet Studio".
8 * While the two programs look rather similiar, the structure of both
9 * programs' "AMS" format is significantly different in some places -
10 * Velvet Studio is a rather advanced tracker in comparison to Extreme's Tracker.
11 * The source code of Velvet Studio has been released into the
12 * public domain in 2013: https://github.com/Patosc/VelvetStudio/commits/master
13 * Authors: OpenMPT Devs
14 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
15 */
16
17
18 #include "stdafx.h"
19 #include "Loaders.h"
20
21
22 OPENMPT_NAMESPACE_BEGIN
23
24
25 // Read AMS or AMS2 (newVersion = true) pattern. At least this part of the format is more or less identical between the two trackers...
ReadAMSPattern(CPattern & pattern,bool newVersion,FileReader & patternChunk)26 static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patternChunk)
27 {
28 enum
29 {
30 emptyRow = 0xFF, // No commands on row
31 endOfRowMask = 0x80, // If set, no more commands on this row
32 noteMask = 0x40, // If set, no note+instr in this command
33 channelMask = 0x1F, // Mask for extracting channel
34
35 // Note flags
36 readNextCmd = 0x80, // One more command follows
37 noteDataMask = 0x7F, // Extract note
38
39 // Command flags
40 volCommand = 0x40, // Effect is compressed volume command
41 commandMask = 0x3F, // Command or volume mask
42 };
43
44 // Effect translation table for extended (non-Protracker) effects
45 static constexpr ModCommand::COMMAND effTrans[] =
46 {
47 CMD_S3MCMDEX, // Forward / Backward
48 CMD_PORTAMENTOUP, // Extra fine slide up
49 CMD_PORTAMENTODOWN, // Extra fine slide up
50 CMD_RETRIG, // Retrigger
51 CMD_NONE,
52 CMD_TONEPORTAVOL, // Toneporta with fine volume slide
53 CMD_VIBRATOVOL, // Vibrato with fine volume slide
54 CMD_NONE,
55 CMD_PANNINGSLIDE,
56 CMD_NONE,
57 CMD_VOLUMESLIDE, // Two times finder volume slide than Axx
58 CMD_NONE,
59 CMD_CHANNELVOLUME, // Channel volume (0...127)
60 CMD_PATTERNBREAK, // Long pattern break (in hex)
61 CMD_S3MCMDEX, // Fine slide commands
62 CMD_NONE, // Fractional BPM
63 CMD_KEYOFF, // Key off at tick xx
64 CMD_PORTAMENTOUP, // Porta up, but uses all octaves (?)
65 CMD_PORTAMENTODOWN, // Porta down, but uses all octaves (?)
66 CMD_NONE,
67 CMD_NONE,
68 CMD_NONE,
69 CMD_NONE,
70 CMD_NONE,
71 CMD_NONE,
72 CMD_NONE,
73 CMD_GLOBALVOLSLIDE, // Global volume slide
74 CMD_NONE,
75 CMD_GLOBALVOLUME, // Global volume (0... 127)
76 };
77
78 ModCommand dummy;
79 for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
80 {
81 PatternRow baseRow = pattern.GetRow(row);
82 while(patternChunk.CanRead(1))
83 {
84 const uint8 flags = patternChunk.ReadUint8();
85 if(flags == emptyRow)
86 {
87 break;
88 }
89
90 const CHANNELINDEX chn = (flags & channelMask);
91 ModCommand &m = chn < pattern.GetNumChannels() ? baseRow[chn] : dummy;
92 bool moreCommands = true;
93 if(!(flags & noteMask))
94 {
95 // Read note + instr
96 uint8 note = patternChunk.ReadUint8();
97 moreCommands = (note & readNextCmd) != 0;
98 note &= noteDataMask;
99
100 if(note == 1)
101 {
102 m.note = NOTE_KEYOFF;
103 } else if(note >= 2 && note <= 121 && newVersion)
104 {
105 m.note = note - 2 + NOTE_MIN;
106 } else if(note >= 12 && note <= 108 && !newVersion)
107 {
108 m.note = note + 12 + NOTE_MIN;
109 }
110 m.instr = patternChunk.ReadUint8();
111 }
112
113 while(moreCommands)
114 {
115 // Read one more effect command
116 ModCommand origCmd = m;
117 const uint8 command = patternChunk.ReadUint8(), effect = (command & commandMask);
118 moreCommands = (command & readNextCmd) != 0;
119
120 if(command & volCommand)
121 {
122 m.volcmd = VOLCMD_VOLUME;
123 m.vol = effect;
124 } else
125 {
126 m.param = patternChunk.ReadUint8();
127
128 if(effect < 0x10)
129 {
130 // PT commands
131 m.command = effect;
132 CSoundFile::ConvertModCommand(m);
133
134 // Post-fix some commands
135 switch(m.command)
136 {
137 case CMD_PANNING8:
138 // 4-Bit panning
139 m.command = CMD_PANNING8;
140 m.param = (m.param & 0x0F) * 0x11;
141 break;
142
143 case CMD_VOLUME:
144 m.command = CMD_NONE;
145 m.volcmd = VOLCMD_VOLUME;
146 m.vol = static_cast<ModCommand::VOL>(std::min((m.param + 1) / 2, 64));
147 break;
148
149 case CMD_MODCMDEX:
150 if(m.param == 0x80)
151 {
152 // Break sample loop (cut after loop)
153 m.command = CMD_NONE;
154 } else
155 {
156 m.ExtendedMODtoS3MEffect();
157 }
158 break;
159 }
160 } else if(effect < 0x10 + mpt::array_size<decltype(effTrans)>::size)
161 {
162 // Extended commands
163 m.command = effTrans[effect - 0x10];
164
165 // Post-fix some commands
166 switch(effect)
167 {
168 case 0x10:
169 // Play sample forwards / backwards
170 if(m.param <= 0x01)
171 {
172 m.param |= 0x9E;
173 } else
174 {
175 m.command = CMD_NONE;
176 }
177 break;
178
179 case 0x11:
180 case 0x12:
181 // Extra fine slides
182 m.param = static_cast<ModCommand::PARAM>(std::min(uint8(0x0F), m.param) | 0xE0);
183 break;
184
185 case 0x15:
186 case 0x16:
187 // Fine slides
188 m.param = static_cast<ModCommand::PARAM>((std::min(0x10, m.param + 1) / 2) | 0xF0);
189 break;
190
191 case 0x1E:
192 // More fine slides
193 switch(m.param >> 4)
194 {
195 case 0x1:
196 // Fine porta up
197 m.command = CMD_PORTAMENTOUP;
198 m.param |= 0xF0;
199 break;
200 case 0x2:
201 // Fine porta down
202 m.command = CMD_PORTAMENTODOWN;
203 m.param |= 0xF0;
204 break;
205 case 0xA:
206 // Extra fine volume slide up
207 m.command = CMD_VOLUMESLIDE;
208 m.param = ((((m.param & 0x0F) + 1) / 2) << 4) | 0x0F;
209 break;
210 case 0xB:
211 // Extra fine volume slide down
212 m.command = CMD_VOLUMESLIDE;
213 m.param = (((m.param & 0x0F) + 1) / 2) | 0xF0;
214 break;
215 default:
216 m.command = CMD_NONE;
217 break;
218 }
219 break;
220
221 case 0x1C:
222 // Adjust channel volume range
223 m.param = static_cast<ModCommand::PARAM>(std::min((m.param + 1) / 2, 64));
224 break;
225 }
226 }
227
228 // Try merging commands first
229 ModCommand::CombineEffects(m.command, m.param, origCmd.command, origCmd.param);
230
231 if(ModCommand::GetEffectWeight(origCmd.command) > ModCommand::GetEffectWeight(m.command))
232 {
233 if(m.volcmd == VOLCMD_NONE && ModCommand::ConvertVolEffect(m.command, m.param, true))
234 {
235 // Volume column to the rescue!
236 m.volcmd = m.command;
237 m.vol = m.param;
238 }
239
240 m.command = origCmd.command;
241 m.param = origCmd.param;
242 }
243 }
244 }
245
246 if(flags & endOfRowMask)
247 {
248 // End of row
249 break;
250 }
251 }
252 }
253 }
254
255
256 /////////////////////////////////////////////////////////////////////
257 // AMS (Extreme's Tracker) 1.x loader
258
259 // AMS File Header
260 struct AMSFileHeader
261 {
262 uint8le versionLow;
263 uint8le versionHigh;
264 uint8le channelConfig;
265 uint8le numSamps;
266 uint16le numPats;
267 uint16le numOrds;
268 uint8le midiChannels;
269 uint16le extraSize;
270 };
271
272 MPT_BINARY_STRUCT(AMSFileHeader, 11)
273
274
275 // AMS Sample Header
276 struct AMSSampleHeader
277 {
278 enum SampleFlags
279 {
280 smp16BitOld = 0x04, // AMS 1.0 (at least according to docs, I yet have to find such a file)
281 smp16Bit = 0x80, // AMS 1.1+
282 smpPacked = 0x03,
283 };
284
285 uint32le length;
286 uint32le loopStart;
287 uint32le loopEnd;
288 uint8le panFinetune; // High nibble = pan position, low nibble = finetune value
289 uint16le sampleRate;
290 uint8le volume; // 0...127
291 uint8le flags; // See SampleFlags
292
293 // Convert sample header to OpenMPT's internal format.
ConvertToMPTAMSSampleHeader294 void ConvertToMPT(ModSample &mptSmp) const
295 {
296 mptSmp.Initialize();
297
298 mptSmp.nLength = length;
299 mptSmp.nLoopStart = std::min(loopStart, length);
300 mptSmp.nLoopEnd = std::min(loopEnd, length);
301
302 mptSmp.nVolume = (std::min(uint8(127), volume.get()) * 256 + 64) / 127;
303 if(panFinetune & 0xF0)
304 {
305 mptSmp.nPan = (panFinetune & 0xF0);
306 mptSmp.uFlags = CHN_PANNING;
307 }
308
309 mptSmp.nC5Speed = 2 * sampleRate;
310 if(sampleRate == 0)
311 {
312 mptSmp.nC5Speed = 2 * 8363;
313 }
314
315 uint32 newC4speed = ModSample::TransposeToFrequency(0, MOD2XMFineTune(panFinetune & 0x0F));
316 mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363;
317
318 if(mptSmp.nLoopStart < mptSmp.nLoopEnd)
319 {
320 mptSmp.uFlags.set(CHN_LOOP);
321 }
322
323 if(flags & (smp16Bit | smp16BitOld))
324 {
325 mptSmp.uFlags.set(CHN_16BIT);
326 }
327 }
328 };
329
330 MPT_BINARY_STRUCT(AMSSampleHeader, 17)
331
332
ValidateHeader(const AMSFileHeader & fileHeader)333 static bool ValidateHeader(const AMSFileHeader &fileHeader)
334 {
335 if(fileHeader.versionHigh != 0x01)
336 {
337 return false;
338 }
339 return true;
340 }
341
342
GetHeaderMinimumAdditionalSize(const AMSFileHeader & fileHeader)343 static uint64 GetHeaderMinimumAdditionalSize(const AMSFileHeader &fileHeader)
344 {
345 return fileHeader.extraSize + 3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u;
346 }
347
348
ProbeFileHeaderAMS(MemoryFileReader file,const uint64 * pfilesize)349 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize)
350 {
351 if(!file.CanRead(7))
352 {
353 return ProbeWantMoreData;
354 }
355 if(!file.ReadMagic("Extreme"))
356 {
357 return ProbeFailure;
358 }
359 AMSFileHeader fileHeader;
360 if(!file.ReadStruct(fileHeader))
361 {
362 return ProbeWantMoreData;
363 }
364 if(!ValidateHeader(fileHeader))
365 {
366 return ProbeFailure;
367 }
368 return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
369 }
370
371
ReadAMS(FileReader & file,ModLoadingFlags loadFlags)372 bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags)
373 {
374 file.Rewind();
375
376 if(!file.ReadMagic("Extreme"))
377 {
378 return false;
379 }
380 AMSFileHeader fileHeader;
381 if(!file.ReadStruct(fileHeader))
382 {
383 return false;
384 }
385 if(!ValidateHeader(fileHeader))
386 {
387 return false;
388 }
389 if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
390 {
391 return false;
392 }
393 if(!file.Skip(fileHeader.extraSize))
394 {
395 return false;
396 }
397 if(loadFlags == onlyVerifyHeader)
398 {
399 return true;
400 }
401
402 InitializeGlobals(MOD_TYPE_AMS);
403
404 m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
405 m_nChannels = (fileHeader.channelConfig & 0x1F) + 1;
406 m_nSamples = fileHeader.numSamps;
407 SetupMODPanning(true);
408
409 m_modFormat.formatName = U_("Extreme's Tracker");
410 m_modFormat.type = U_("ams");
411 m_modFormat.madeWithTracker = MPT_UFORMAT("Extreme's Tracker {}.{}")(fileHeader.versionHigh, fileHeader.versionLow);
412 m_modFormat.charset = mpt::Charset::CP437;
413
414 std::vector<bool> packSample(fileHeader.numSamps);
415
416 static_assert(MAX_SAMPLES > 255);
417 for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
418 {
419 AMSSampleHeader sampleHeader;
420 file.ReadStruct(sampleHeader);
421 sampleHeader.ConvertToMPT(Samples[smp]);
422 packSample[smp - 1] = (sampleHeader.flags & AMSSampleHeader::smpPacked) != 0;
423 }
424
425 // Texts
426 file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName);
427
428 // Read sample names
429 for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
430 {
431 file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[smp]);
432 }
433
434 // Read channel names
435 for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
436 {
437 ChnSettings[chn].Reset();
438 file.ReadSizedString<uint8le, mpt::String::spacePadded>(ChnSettings[chn].szName);
439 }
440
441 // Read pattern names and create patterns
442 Patterns.ResizeArray(fileHeader.numPats);
443 for(PATTERNINDEX pat = 0; pat < fileHeader.numPats; pat++)
444 {
445 char name[11];
446 const bool ok = file.ReadSizedString<uint8le, mpt::String::spacePadded>(name);
447 // Create pattern now, so name won't be reset later.
448 if(Patterns.Insert(pat, 64) && ok)
449 {
450 Patterns[pat].SetName(name);
451 }
452 }
453
454 // Read packed song message
455 const uint16 packedLength = file.ReadUint16LE();
456 if(packedLength && file.CanRead(packedLength))
457 {
458 std::vector<uint8> textIn;
459 file.ReadVector(textIn, packedLength);
460 std::string textOut;
461 textOut.reserve(packedLength);
462
463 for(auto c : textIn)
464 {
465 if(c & 0x80)
466 {
467 textOut.insert(textOut.end(), (c & 0x7F), ' ');
468 } else
469 {
470 textOut.push_back(c);
471 }
472 }
473
474 textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS, textOut);
475
476 // Packed text doesn't include any line breaks!
477 m_songMessage.ReadFixedLineLength(mpt::byte_cast<const std::byte*>(textOut.c_str()), textOut.length(), 76, 0);
478 }
479
480 // Read Order List
481 ReadOrderFromFile<uint16le>(Order(), file, fileHeader.numOrds);
482
483 // Read patterns
484 for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++)
485 {
486 uint32 patLength = file.ReadUint32LE();
487 FileReader patternChunk = file.ReadChunk(patLength);
488
489 if((loadFlags & loadPatternData) && Patterns.IsValidPat(pat))
490 {
491 ReadAMSPattern(Patterns[pat], false, patternChunk);
492 }
493 }
494
495 if(loadFlags & loadSampleData)
496 {
497 // Read Samples
498 for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
499 {
500 SampleIO(
501 Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
502 SampleIO::mono,
503 SampleIO::littleEndian,
504 packSample[smp - 1] ? SampleIO::AMS : SampleIO::signedPCM)
505 .ReadSample(Samples[smp], file);
506 }
507 }
508
509 return true;
510 }
511
512
513 /////////////////////////////////////////////////////////////////////
514 // AMS (Velvet Studio) 2.0 - 2.02 loader
515
516 // AMS2 File Header
517 struct AMS2FileHeader
518 {
519 enum FileFlags
520 {
521 linearSlides = 0x40,
522 };
523
524 uint8le versionLow; // Version of format (Hi = MainVer, Low = SubVer e.g. 0202 = 2.02)
525 uint8le versionHigh; // ditto
526 uint8le numIns; // Nr of Instruments (0-255)
527 uint16le numPats; // Nr of Patterns (1-1024)
528 uint16le numOrds; // Nr of Positions (1-65535)
529 // Rest of header differs between format revision 2.01 and 2.02
530 };
531
532 MPT_BINARY_STRUCT(AMS2FileHeader, 7)
533
534
535 // AMS2 Instument Envelope
536 struct AMS2Envelope
537 {
538 uint8 speed; // Envelope speed (currently not supported, always the same as current BPM)
539 uint8 sustainPoint; // Envelope sustain point
540 uint8 loopStart; // Envelope loop Start
541 uint8 loopEnd; // Envelope loop End
542 uint8 numPoints; // Envelope length
543
544 // Read envelope and do partial conversion.
ConvertToMPTAMS2Envelope545 void ConvertToMPT(InstrumentEnvelope &mptEnv, FileReader &file)
546 {
547 file.ReadStruct(*this);
548
549 // Read envelope points
550 uint8 data[64][3];
551 file.ReadStructPartial(data, numPoints * 3);
552
553 if(numPoints <= 1)
554 {
555 // This is not an envelope.
556 return;
557 }
558
559 static_assert(MAX_ENVPOINTS >= std::size(data));
560 mptEnv.resize(std::min(numPoints, mpt::saturate_cast<uint8>(std::size(data))));
561 mptEnv.nLoopStart = loopStart;
562 mptEnv.nLoopEnd = loopEnd;
563 mptEnv.nSustainStart = mptEnv.nSustainEnd = sustainPoint;
564
565 for(uint32 i = 0; i < mptEnv.size(); i++)
566 {
567 if(i != 0)
568 {
569 mptEnv[i].tick = mptEnv[i - 1].tick + static_cast<uint16>(std::max(1, data[i][0] | ((data[i][1] & 0x01) << 8)));
570 }
571 mptEnv[i].value = data[i][2];
572 }
573 }
574 };
575
576 MPT_BINARY_STRUCT(AMS2Envelope, 5)
577
578
579 // AMS2 Instrument Data
580 struct AMS2Instrument
581 {
582 enum EnvelopeFlags
583 {
584 envLoop = 0x01,
585 envSustain = 0x02,
586 envEnabled = 0x04,
587
588 // Flag shift amounts
589 volEnvShift = 0,
590 panEnvShift = 1,
591 vibEnvShift = 2,
592
593 vibAmpMask = 0x3000,
594 vibAmpShift = 12,
595 fadeOutMask = 0xFFF,
596 };
597
598 uint8le shadowInstr; // Shadow Instrument. If non-zero, the value=the shadowed inst.
599 uint16le vibampFadeout; // Vib.Amplify + Volume fadeout in one variable!
600 uint16le envFlags; // See EnvelopeFlags
601
ApplyFlagsAMS2Instrument602 void ApplyFlags(InstrumentEnvelope &mptEnv, EnvelopeFlags shift) const
603 {
604 const int flags = envFlags >> (shift * 3);
605 mptEnv.dwFlags.set(ENV_ENABLED, (flags & envEnabled) != 0);
606 mptEnv.dwFlags.set(ENV_LOOP, (flags & envLoop) != 0);
607 mptEnv.dwFlags.set(ENV_SUSTAIN, (flags & envSustain) != 0);
608
609 // "Break envelope" should stop the envelope loop when encountering a note-off... We can only use the sustain loop to emulate this behaviour.
610 if(!(flags & envSustain) && (flags & envLoop) != 0 && (flags & (1 << (9 - shift * 2))) != 0)
611 {
612 mptEnv.nSustainStart = mptEnv.nLoopStart;
613 mptEnv.nSustainEnd = mptEnv.nLoopEnd;
614 mptEnv.dwFlags.set(ENV_SUSTAIN);
615 mptEnv.dwFlags.reset(ENV_LOOP);
616 }
617 }
618
619 };
620
621 MPT_BINARY_STRUCT(AMS2Instrument, 5)
622
623
624 // AMS2 Sample Header
625 struct AMS2SampleHeader
626 {
627 enum SampleFlags
628 {
629 smpPacked = 0x03,
630 smp16Bit = 0x04,
631 smpLoop = 0x08,
632 smpBidiLoop = 0x10,
633 smpReverse = 0x40,
634 };
635
636 uint32le length;
637 uint32le loopStart;
638 uint32le loopEnd;
639 uint16le sampledRate; // Whyyyy?
640 uint8le panFinetune; // High nibble = pan position, low nibble = finetune value
641 uint16le c4speed; // Why is all of this so redundant?
642 int8le relativeTone; // q.e.d.
643 uint8le volume; // 0...127
644 uint8le flags; // See SampleFlags
645
646 // Convert sample header to OpenMPT's internal format.
ConvertToMPTAMS2SampleHeader647 void ConvertToMPT(ModSample &mptSmp) const
648 {
649 mptSmp.Initialize();
650
651 mptSmp.nLength = length;
652 mptSmp.nLoopStart = std::min(loopStart, length);
653 mptSmp.nLoopEnd = std::min(loopEnd, length);
654
655 mptSmp.nC5Speed = c4speed * 2;
656 if(c4speed == 0)
657 {
658 mptSmp.nC5Speed = 8363 * 2;
659 }
660 // Why, oh why, does this format need a c5speed and transpose/finetune at the same time...
661 uint32 newC4speed = ModSample::TransposeToFrequency(relativeTone, MOD2XMFineTune(panFinetune & 0x0F));
662 mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363;
663
664 mptSmp.nVolume = (std::min(volume.get(), uint8(127)) * 256 + 64) / 127;
665 if(panFinetune & 0xF0)
666 {
667 mptSmp.nPan = (panFinetune & 0xF0);
668 mptSmp.uFlags = CHN_PANNING;
669 }
670
671 if(flags & smp16Bit) mptSmp.uFlags.set(CHN_16BIT);
672 if((flags & smpLoop) && mptSmp.nLoopStart < mptSmp.nLoopEnd)
673 {
674 mptSmp.uFlags.set(CHN_LOOP);
675 if(flags & smpBidiLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP);
676 if(flags & smpReverse) mptSmp.uFlags.set(CHN_REVERSE);
677 }
678 }
679 };
680
681 MPT_BINARY_STRUCT(AMS2SampleHeader, 20)
682
683
684 // AMS2 Song Description Header
685 struct AMS2Description
686 {
687 uint32le packedLen; // Including header
688 uint32le unpackedLen;
689 uint8le packRoutine; // 01
690 uint8le preProcessing; // None!
691 uint8le packingMethod; // RLE
692 };
693
694 MPT_BINARY_STRUCT(AMS2Description, 11)
695
696
ValidateHeader(const AMS2FileHeader & fileHeader)697 static bool ValidateHeader(const AMS2FileHeader &fileHeader)
698 {
699 if(fileHeader.versionHigh != 2 || fileHeader.versionLow > 2)
700 {
701 return false;
702 }
703 return true;
704 }
705
706
GetHeaderMinimumAdditionalSize(const AMS2FileHeader & fileHeader)707 static uint64 GetHeaderMinimumAdditionalSize(const AMS2FileHeader &fileHeader)
708 {
709 return 36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u;
710 }
711
712
ProbeFileHeaderAMS2(MemoryFileReader file,const uint64 * pfilesize)713 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize)
714 {
715 if(!file.CanRead(7))
716 {
717 return ProbeWantMoreData;
718 }
719 if(!file.ReadMagic("AMShdr\x1A"))
720 {
721 return ProbeFailure;
722 }
723 if(!file.CanRead(1))
724 {
725 return ProbeWantMoreData;
726 }
727 const uint8 songNameLength = file.ReadUint8();
728 if(!file.Skip(songNameLength))
729 {
730 return ProbeWantMoreData;
731 }
732 AMS2FileHeader fileHeader;
733 if(!file.ReadStruct(fileHeader))
734 {
735 return ProbeWantMoreData;
736 }
737 if(!ValidateHeader(fileHeader))
738 {
739 return ProbeFailure;
740 }
741 return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
742 }
743
744
ReadAMS2(FileReader & file,ModLoadingFlags loadFlags)745 bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
746 {
747 file.Rewind();
748
749 if(!file.ReadMagic("AMShdr\x1A"))
750 {
751 return false;
752 }
753 std::string songName;
754 if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(songName))
755 {
756 return false;
757 }
758 AMS2FileHeader fileHeader;
759 if(!file.ReadStruct(fileHeader))
760 {
761 return false;
762 }
763 if(!ValidateHeader(fileHeader))
764 {
765 return false;
766 }
767 if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
768 {
769 return false;
770 }
771 if(loadFlags == onlyVerifyHeader)
772 {
773 return true;
774 }
775
776 InitializeGlobals(MOD_TYPE_AMS);
777
778 m_songName = songName;
779
780 m_nInstruments = fileHeader.numIns;
781 m_nChannels = 32;
782 SetupMODPanning(true);
783
784 m_modFormat.formatName = U_("Velvet Studio");
785 m_modFormat.type = U_("ams");
786 m_modFormat.madeWithTracker = MPT_UFORMAT("Velvet Studio {}.{}")(fileHeader.versionHigh.get(), mpt::ufmt::dec0<2>(fileHeader.versionLow.get()));
787 m_modFormat.charset = mpt::Charset::CP437;
788
789 uint16 headerFlags;
790 if(fileHeader.versionLow >= 2)
791 {
792 uint16 tempo = std::max(uint16(32 << 8), file.ReadUint16LE()); // 8.8 tempo
793 m_nDefaultTempo.SetRaw((tempo * TEMPO::fractFact) >> 8);
794 m_nDefaultSpeed = std::max(uint8(1), file.ReadUint8());
795 file.Skip(3); // Default values for pattern editor
796 headerFlags = file.ReadUint16LE();
797 } else
798 {
799 m_nDefaultTempo.Set(std::max(uint8(32), file.ReadUint8()));
800 m_nDefaultSpeed = std::max(uint8(1), file.ReadUint8());
801 headerFlags = file.ReadUint8();
802 }
803
804 m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS | ((headerFlags & AMS2FileHeader::linearSlides) ? SONG_LINEARSLIDES : SongFlags(0));
805
806 // Instruments
807 std::vector<SAMPLEINDEX> firstSample; // First sample of instrument
808 std::vector<uint16> sampleSettings; // Shadow sample map... Lo byte = Instrument, Hi byte, lo nibble = Sample index in instrument, Hi byte, hi nibble = Sample pack status
809 enum
810 {
811 instrIndexMask = 0xFF, // Shadow instrument
812 sampleIndexMask = 0x7F00, // Sample index in instrument
813 sampleIndexShift = 8,
814 packStatusMask = 0x8000, // If bit is set, sample is packed
815 };
816
817 static_assert(MAX_INSTRUMENTS > 255);
818 for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++)
819 {
820 ModInstrument *instrument = AllocateInstrument(ins);
821 if(instrument == nullptr
822 || !file.ReadSizedString<uint8le, mpt::String::spacePadded>(instrument->name))
823 {
824 break;
825 }
826
827 uint8 numSamples = file.ReadUint8();
828 uint8 sampleAssignment[120];
829 MemsetZero(sampleAssignment); // Only really needed for v2.0, where the lowest and highest octave aren't cleared.
830
831 if(numSamples == 0
832 || (fileHeader.versionLow > 0 && !file.ReadArray(sampleAssignment)) // v2.01+: 120 Notes
833 || (fileHeader.versionLow == 0 && !file.ReadRaw(mpt::span(sampleAssignment + 12, 96)).size())) // v2.0: 96 Notes
834 {
835 continue;
836 }
837
838 static_assert(mpt::array_size<decltype(instrument->Keyboard)>::size >= std::size(sampleAssignment));
839 for(size_t i = 0; i < 120; i++)
840 {
841 instrument->Keyboard[i] = sampleAssignment[i] + GetNumSamples() + 1;
842 }
843
844 AMS2Envelope volEnv, panEnv, vibratoEnv;
845 volEnv.ConvertToMPT(instrument->VolEnv, file);
846 panEnv.ConvertToMPT(instrument->PanEnv, file);
847 vibratoEnv.ConvertToMPT(instrument->PitchEnv, file);
848
849 AMS2Instrument instrHeader;
850 file.ReadStruct(instrHeader);
851 instrument->nFadeOut = (instrHeader.vibampFadeout & AMS2Instrument::fadeOutMask);
852 const int16 vibAmp = 1 << ((instrHeader.vibampFadeout & AMS2Instrument::vibAmpMask) >> AMS2Instrument::vibAmpShift);
853
854 instrHeader.ApplyFlags(instrument->VolEnv, AMS2Instrument::volEnvShift);
855 instrHeader.ApplyFlags(instrument->PanEnv, AMS2Instrument::panEnvShift);
856 instrHeader.ApplyFlags(instrument->PitchEnv, AMS2Instrument::vibEnvShift);
857
858 // Scale envelopes to correct range
859 for(auto &p : instrument->VolEnv)
860 {
861 p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>((p.value * ENVELOPE_MAX + 64u) / 127u));
862 }
863 for(auto &p : instrument->PanEnv)
864 {
865 p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>((p.value * ENVELOPE_MAX + 128u) / 255u));
866 }
867 for(auto &p : instrument->PitchEnv)
868 {
869 #ifdef MODPLUG_TRACKER
870 p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>(32 + Util::muldivrfloor(static_cast<int8>(p.value - 128), vibAmp, 255)));
871 #else
872 // Try to keep as much precision as possible... divide by 8 since that's the highest possible vibAmp factor.
873 p.value = static_cast<uint8>(128 + Util::muldivrfloor(static_cast<int8>(p.value - 128), vibAmp, 8));
874 #endif
875 }
876
877 // Sample headers - we will have to read them even for shadow samples, and we will have to load them several times,
878 // as it is possible that shadow samples use different sample settings like base frequency or panning.
879 const SAMPLEINDEX firstSmp = GetNumSamples() + 1;
880 for(SAMPLEINDEX smp = 0; smp < numSamples; smp++)
881 {
882 if(firstSmp + smp >= MAX_SAMPLES)
883 {
884 file.Skip(sizeof(AMS2SampleHeader));
885 break;
886 }
887 file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[firstSmp + smp]);
888
889 AMS2SampleHeader sampleHeader;
890 file.ReadStruct(sampleHeader);
891 sampleHeader.ConvertToMPT(Samples[firstSmp + smp]);
892
893 uint16 settings = (instrHeader.shadowInstr & instrIndexMask)
894 | ((smp << sampleIndexShift) & sampleIndexMask)
895 | ((sampleHeader.flags & AMS2SampleHeader::smpPacked) ? packStatusMask : 0);
896 sampleSettings.push_back(settings);
897 }
898
899 firstSample.push_back(firstSmp);
900 m_nSamples = static_cast<SAMPLEINDEX>(std::min(MAX_SAMPLES - 1, GetNumSamples() + numSamples));
901 }
902
903 // Text
904
905 // Read composer name
906 if(std::string composer; file.ReadSizedString<uint8le, mpt::String::spacePadded>(composer))
907 {
908 m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, composer);
909 }
910
911 // Channel names
912 for(CHANNELINDEX chn = 0; chn < 32; chn++)
913 {
914 ChnSettings[chn].Reset();
915 file.ReadSizedString<uint8le, mpt::String::spacePadded>(ChnSettings[chn].szName);
916 }
917
918 // RLE-Packed description text
919 AMS2Description descriptionHeader;
920 if(!file.ReadStruct(descriptionHeader))
921 {
922 return true;
923 }
924 if(descriptionHeader.packedLen > sizeof(descriptionHeader) && file.CanRead(descriptionHeader.packedLen - sizeof(descriptionHeader)))
925 {
926 const uint32 textLength = descriptionHeader.packedLen - static_cast<uint32>(sizeof(descriptionHeader));
927 std::vector<uint8> textIn;
928 file.ReadVector(textIn, textLength);
929 // In the best case, every byte triplet can decode to 255 bytes, which is a ratio of exactly 1:85
930 const uint32 maxLength = std::min(textLength, Util::MaxValueOfType(textLength) / 85u) * 85u;
931 std::string textOut;
932 textOut.reserve(std::min(maxLength, descriptionHeader.unpackedLen.get()));
933
934 size_t readLen = 0;
935 while(readLen < textLength)
936 {
937 uint8 c = textIn[readLen++];
938 if(c == 0xFF && textLength - readLen >= 2)
939 {
940 c = textIn[readLen++];
941 uint32 count = textIn[readLen++];
942 textOut.insert(textOut.end(), count, c);
943 } else
944 {
945 textOut.push_back(c);
946 }
947 }
948 textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS2, textOut);
949 // Packed text doesn't include any line breaks!
950 m_songMessage.ReadFixedLineLength(mpt::byte_cast<const std::byte*>(textOut.c_str()), textOut.length(), 74, 0);
951 }
952
953 // Read Order List
954 ReadOrderFromFile<uint16le>(Order(), file, fileHeader.numOrds);
955
956 // Read Patterns
957 if(loadFlags & loadPatternData)
958 Patterns.ResizeArray(fileHeader.numPats);
959 for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++)
960 {
961 uint32 patLength = file.ReadUint32LE();
962 FileReader patternChunk = file.ReadChunk(patLength);
963
964 if(loadFlags & loadPatternData)
965 {
966 const ROWINDEX numRows = patternChunk.ReadUint8() + 1;
967 // We don't need to know the number of channels or commands.
968 patternChunk.Skip(1);
969
970 if(!Patterns.Insert(pat, numRows))
971 {
972 continue;
973 }
974
975 char patternName[11];
976 if(patternChunk.ReadSizedString<uint8le, mpt::String::spacePadded>(patternName))
977 Patterns[pat].SetName(patternName);
978
979 ReadAMSPattern(Patterns[pat], true, patternChunk);
980 }
981 }
982
983 if(!(loadFlags & loadSampleData))
984 {
985 return true;
986 }
987
988 // Read Samples
989 for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++)
990 {
991 if((sampleSettings[smp] & instrIndexMask) == 0)
992 {
993 // Only load samples that aren't part of a shadow instrument
994 SampleIO(
995 (Samples[smp + 1].uFlags & CHN_16BIT) ? SampleIO::_16bit : SampleIO::_8bit,
996 SampleIO::mono,
997 SampleIO::littleEndian,
998 (sampleSettings[smp] & packStatusMask) ? SampleIO::AMS : SampleIO::signedPCM)
999 .ReadSample(Samples[smp + 1], file);
1000 }
1001 }
1002
1003 // Copy shadow samples
1004 for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++)
1005 {
1006 INSTRUMENTINDEX sourceInstr = (sampleSettings[smp] & instrIndexMask);
1007 if(sourceInstr == 0
1008 || --sourceInstr >= firstSample.size())
1009 {
1010 continue;
1011 }
1012
1013 SAMPLEINDEX sourceSample = ((sampleSettings[smp] & sampleIndexMask) >> sampleIndexShift) + firstSample[sourceInstr];
1014 if(sourceSample > GetNumSamples() || !Samples[sourceSample].HasSampleData())
1015 {
1016 continue;
1017 }
1018
1019 // Copy over original sample
1020 ModSample &sample = Samples[smp + 1];
1021 ModSample &source = Samples[sourceSample];
1022 sample.uFlags.set(CHN_16BIT, source.uFlags[CHN_16BIT]);
1023 sample.nLength = source.nLength;
1024 if(sample.AllocateSample())
1025 {
1026 memcpy(sample.sampleb(), source.sampleb(), source.GetSampleSizeInBytes());
1027 }
1028 }
1029
1030 return true;
1031 }
1032
1033
1034 /////////////////////////////////////////////////////////////////////
1035 // AMS Sample unpacking
1036
AMSUnpack(const int8 * const source,size_t sourceSize,void * const dest,const size_t destSize,char packCharacter)1037 void AMSUnpack(const int8 * const source, size_t sourceSize, void * const dest, const size_t destSize, char packCharacter)
1038 {
1039 std::vector<int8> tempBuf(destSize, 0);
1040 size_t depackSize = destSize;
1041
1042 // Unpack Loop
1043 {
1044 const int8 *in = source;
1045 int8 *out = tempBuf.data();
1046
1047 size_t i = sourceSize, j = destSize;
1048 while(i != 0 && j != 0)
1049 {
1050 int8 ch = *(in++);
1051 if(--i != 0 && ch == packCharacter)
1052 {
1053 uint8 repCount = *(in++);
1054 repCount = static_cast<uint8>(std::min(static_cast<size_t>(repCount), j));
1055 if(--i != 0 && repCount)
1056 {
1057 ch = *(in++);
1058 i--;
1059 while(repCount-- != 0)
1060 {
1061 *(out++) = ch;
1062 j--;
1063 }
1064 } else
1065 {
1066 *(out++) = packCharacter;
1067 j--;
1068 }
1069 } else
1070 {
1071 *(out++) = ch;
1072 j--;
1073 }
1074 }
1075 // j should only be non-zero for truncated samples
1076 depackSize -= j;
1077 }
1078
1079 // Bit Unpack Loop
1080 {
1081 int8 *out = tempBuf.data();
1082 uint16 bitcount = 0x80;
1083 size_t k = 0;
1084 uint8 *dst = static_cast<uint8 *>(dest);
1085 for(size_t i = 0; i < depackSize; i++)
1086 {
1087 uint8 al = *out++;
1088 uint16 dh = 0;
1089 for(uint16 count = 0; count < 8; count++)
1090 {
1091 uint16 bl = al & bitcount;
1092 bl = ((bl | (bl << 8)) >> ((dh + 8 - count) & 7)) & 0xFF;
1093 bitcount = ((bitcount | (bitcount << 8)) >> 1) & 0xFF;
1094 dst[k++] |= bl;
1095 if(k >= destSize)
1096 {
1097 k = 0;
1098 dh++;
1099 }
1100 }
1101 bitcount = ((bitcount | (bitcount << 8)) >> dh) & 0xFF;
1102 }
1103 }
1104
1105 // Delta Unpack
1106 {
1107 int8 old = 0;
1108 int8 *out = static_cast<int8 *>(dest);
1109 for(size_t i = depackSize; i != 0; i--)
1110 {
1111 int pos = *reinterpret_cast<uint8 *>(out);
1112 if(pos != 128 && (pos & 0x80) != 0)
1113 {
1114 pos = -(pos & 0x7F);
1115 }
1116 old -= static_cast<int8>(pos);
1117 *(out++) = old;
1118 }
1119 }
1120 }
1121
1122
1123 OPENMPT_NAMESPACE_END
1124