@@ -5,13 +5,15 @@ unit TBMImport; |
| 5 |
interface |
5 |
interface |
| 6 |
|
6 |
|
| 7 |
uses |
7 |
uses |
| 8 |
Classes, SysUtils, song, LazUTF8, HugeDatatypes, Constants, Utils; |
8 |
Classes, SysUtils, song, LazUTF8, HugeDatatypes, Constants, Utils, fgl; |
| 9 |
|
9 |
|
| 10 |
function LoadSongFromTbmStream(Stream: TStream): TSong; |
10 |
function LoadSongFromTbmStream(Stream: TStream): TSong; |
| 11 |
|
11 |
|
| 12 |
implementation |
12 |
implementation |
| 13 |
|
13 |
|
| 14 |
type |
14 |
type |
|
|
15 |
ETBMException = class(Exception); |
|
|
16 |
|
| 15 |
TTBMHeader = packed record |
17 |
TTBMHeader = packed record |
| 16 |
Signature: array[0..11] of Char; // ' TRACKERBOY ' |
18 |
Signature: array[0..11] of Char; // ' TRACKERBOY ' |
| 17 |
VersionMajor: DWord; |
19 |
VersionMajor: DWord; |
@@ -86,6 +88,8 @@ type |
| 86 |
function ReadLString: String; |
88 |
function ReadLString: String; |
| 87 |
end; |
89 |
end; |
| 88 |
|
90 |
|
|
|
91 |
TTBMInstrumentMap = specialize TFPGMap<Integer, Integer>; |
|
|
92 |
|
| 89 |
//https://github.com/stoneface86/libtrackerboy/blob/bf4993d53bc34691ca75819a96d3d00b3b699dea/src/trackerboy/data.nim#L111 |
93 |
//https://github.com/stoneface86/libtrackerboy/blob/bf4993d53bc34691ca75819a96d3d00b3b699dea/src/trackerboy/data.nim#L111 |
| 90 |
TTBMEffectType = ( |
94 |
TTBMEffectType = ( |
| 91 |
etNoEffect = 0, // No effect, this effect column is unset. |
95 |
etNoEffect = 0, // No effect, this effect column is unset. |
@@ -272,12 +276,15 @@ begin |
| 272 |
end; |
276 |
end; |
| 273 |
end; |
277 |
end; |
| 274 |
|
278 |
|
| 275 |
procedure CleanupPattern(Pat: PPattern); |
279 |
procedure CleanupPattern(Pat: PPattern; InstMap: TTBMInstrumentMap); |
| 276 |
var |
280 |
var |
| 277 |
I: Integer; |
281 |
I: Integer; |
| 278 |
CurNote: Integer = NO_NOTE; |
282 |
CurNote: Integer = NO_NOTE; |
| 279 |
begin |
283 |
begin |
| 280 |
for I := Low(TPattern) to High(TPattern) do begin |
284 |
for I := Low(TPattern) to High(TPattern) do begin |
|
|
285 |
if not InstMap.TryGetData(Pat^[I].Instrument, Pat^[I].Instrument) then |
|
|
286 |
Pat^[I].Instrument := 0; |
|
|
287 |
|
| 281 |
if Pat^[I].Note <> NO_NOTE then |
288 |
if Pat^[I].Note <> NO_NOTE then |
| 282 |
CurNote := Pat^[I].Note; |
289 |
CurNote := Pat^[I].Note; |
| 283 |
|
290 |
|
@@ -311,11 +318,18 @@ var |
| 311 |
WaveId: Byte; |
318 |
WaveId: Byte; |
| 312 |
WaveName: String; |
319 |
WaveName: String; |
| 313 |
FourBitWave: T4bitWave; |
320 |
FourBitWave: T4bitWave; |
|
|
321 |
|
|
|
322 |
SquareMap, WaveMap, NoiseMap: TTBMInstrumentMap; |
|
|
323 |
SeenSquare, SeenWave, SeenNoise: Integer; |
|
|
324 |
NewInstId: Integer; |
| 314 |
begin |
325 |
begin |
| 315 |
InitializeSong(Result); |
326 |
InitializeSong(Result); |
| 316 |
|
327 |
|
| 317 |
Stream.ReadBuffer(Header, SizeOf(TTBMHeader)); |
328 |
Stream.ReadBuffer(Header, SizeOf(TTBMHeader)); |
| 318 |
|
329 |
|
|
|
330 |
if Header.SCount > 1 then |
|
|
331 |
raise ETBMException.Create('hUGETracker only supports loading TBM modules with one song.'); |
|
|
332 |
|
| 319 |
// COMM block |
333 |
// COMM block |
| 320 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
334 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
| 321 |
Stream.Seek(BlockHeader.Length, soCurrent); // Skip the comment for now |
335 |
Stream.Seek(BlockHeader.Length, soCurrent); // Skip the comment for now |
@@ -352,10 +366,17 @@ begin |
| 352 |
Stream.ReadBuffer(RowFormat, SizeOf(TTBMRowFormat)); |
366 |
Stream.ReadBuffer(RowFormat, SizeOf(TTBMRowFormat)); |
| 353 |
Pat^[RowFormat.RowNo] := ConverTBMRow(RowFormat.RowData, InsType); |
367 |
Pat^[RowFormat.RowNo] := ConverTBMRow(RowFormat.RowData, InsType); |
| 354 |
end; |
368 |
end; |
| 355 |
CleanupPattern(Pat); |
|
|
| 356 |
end; |
369 |
end; |
| 357 |
|
370 |
|
| 358 |
// INST block |
371 |
// INST block |
|
|
372 |
|
|
|
373 |
SquareMap := TTBMInstrumentMap.Create; |
|
|
374 |
WaveMap := TTBMInstrumentMap.Create; |
|
|
375 |
NoiseMap := TTBMInstrumentMap.Create; |
|
|
376 |
SeenSquare := 1; |
|
|
377 |
SeenWave := 1; |
|
|
378 |
SeenNoise := 1; |
|
|
379 |
|
| 359 |
for I := 0 to Header.ICount-1 do begin |
380 |
for I := 0 to Header.ICount-1 do begin |
| 360 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
381 |
Stream.ReadBuffer(BlockHeader, SizeOf(TTBMBlockHeader)); |
| 361 |
|
382 |
|
@@ -363,13 +384,29 @@ begin |
| 363 |
InstName := Stream.ReadLString; |
384 |
InstName := Stream.ReadLString; |
| 364 |
|
385 |
|
| 365 |
Stream.ReadBuffer(InstFormat, SizeOf(TTBMInstrumentFormat)); |
386 |
Stream.ReadBuffer(InstFormat, SizeOf(TTBMInstrumentFormat)); |
|
|
387 |
|
| 366 |
case InstFormat.Channel of |
388 |
case InstFormat.Channel of |
| 367 |
0, 1: InsType := itSquare; |
389 |
0, 1: begin |
| 368 |
2: InsType := itWave; |
390 |
InsType := itSquare; |
| 369 |
3: InsType := itNoise; |
391 |
SquareMap.Add(InstId, SeenSquare); |
|
|
392 |
NewInstId := SeenSquare; |
|
|
393 |
Inc(SeenSquare); |
|
|
394 |
end; |
|
|
395 |
2: begin |
|
|
396 |
InsType := itWave; |
|
|
397 |
WaveMap.Add(InstId, SeenWave); |
|
|
398 |
NewInstId := SeenWave; |
|
|
399 |
Inc(SeenWave); |
|
|
400 |
end; |
|
|
401 |
3: begin |
|
|
402 |
InsType := itNoise; |
|
|
403 |
NoiseMap.Add(InstId, SeenNoise); |
|
|
404 |
NewInstId := SeenNoise; |
|
|
405 |
Inc(SeenNoise); |
|
|
406 |
end; |
| 370 |
end; |
407 |
end; |
| 371 |
|
408 |
|
| 372 |
Ins := @Result.Instruments.All[UnmodInst(InsType, InstId+1)]; |
409 |
Ins := @Result.Instruments.All[UnmodInst(InsType, NewInstId)]; |
| 373 |
Ins^.Name := InstName; |
410 |
Ins^.Name := InstName; |
| 374 |
|
411 |
|
| 375 |
EnvReg.ByteValue := InstFormat.Envelope; |
412 |
EnvReg.ByteValue := InstFormat.Envelope; |
@@ -426,6 +463,15 @@ begin |
| 426 |
Result.Waves[WaveId] := UnconvertWaveform(FourBitWave); |
463 |
Result.Waves[WaveId] := UnconvertWaveform(FourBitWave); |
| 427 |
end; |
464 |
end; |
| 428 |
|
465 |
|
|
|
466 |
// Cleanup patterns |
|
|
467 |
for I := Low(Result.OrderMatrix[0]) to High(Result.OrderMatrix[0])-1 do |
|
|
468 |
CleanupPattern(Result.Patterns.KeyData[Result.OrderMatrix[0, I]], SquareMap); |
|
|
469 |
for I := Low(Result.OrderMatrix[1]) to High(Result.OrderMatrix[1])-1 do |
|
|
470 |
CleanupPattern(Result.Patterns.KeyData[Result.OrderMatrix[1, I]], SquareMap); |
|
|
471 |
for I := Low(Result.OrderMatrix[2]) to High(Result.OrderMatrix[2])-1 do |
|
|
472 |
CleanupPattern(Result.Patterns.KeyData[Result.OrderMatrix[2, I]], WaveMap); |
|
|
473 |
for I := Low(Result.OrderMatrix[3]) to High(Result.OrderMatrix[3])-1 do |
|
|
474 |
CleanupPattern(Result.Patterns.KeyData[Result.OrderMatrix[3, I]], NoiseMap); |
| 429 |
end; |
475 |
end; |
| 430 |
|
476 |
|
| 431 |
end. |
477 |
end. |