summaryrefslogtreecommitdiff
path: root/core/multimedia/opieplayer/modplug/load_psm.cpp
Unidiff
Diffstat (limited to 'core/multimedia/opieplayer/modplug/load_psm.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/multimedia/opieplayer/modplug/load_psm.cpp842
1 files changed, 842 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/modplug/load_psm.cpp b/core/multimedia/opieplayer/modplug/load_psm.cpp
new file mode 100644
index 0000000..e6a6f8e
--- a/dev/null
+++ b/core/multimedia/opieplayer/modplug/load_psm.cpp
@@ -0,0 +1,842 @@
1/*
2 * This program is free software; you can redistribute it and modify it
3 * under the terms of the GNU General Public License as published by the
4 * Free Software Foundation; either version 2 of the license or (at your
5 * option) any later version.
6 *
7 * Authors: Olivier Lapicque <olivierl@jps.net>
8*/
9
10
11///////////////////////////////////////////////////
12//
13// PSM module loader
14//
15///////////////////////////////////////////////////
16#include "stdafx.h"
17#include "sndfile.h"
18
19//#define PSM_LOG
20
21 #define PSM_ID_NEW0x204d5350
22 #define PSM_ID_OLD0xfe4d5350
23 #define IFFID_FILE0x454c4946
24 #define IFFID_TITL0x4c544954
25 #define IFFID_SDFT0x54464453
26 #define IFFID_PBOD0x444f4250
27 #define IFFID_SONG0x474e4f53
28 #define IFFID_PATT0x54544150
29 #define IFFID_DSMP0x504d5344
30 #define IFFID_OPLH0x484c504f
31
32#pragma pack(1)
33
34typedef struct _PSMCHUNK
35{
36 DWORD id;
37 DWORD len;
38 DWORD listid;
39} Q_PACKED PSMCHUNK;
40
41typedef struct _PSMSONGHDR
42{
43 CHAR songname[8];// "MAINSONG"
44 BYTE reserved1;
45 BYTE reserved2;
46 BYTE channels;
47} Q_PACKED PSMSONGHDR;
48
49typedef struct _PSMPATTERN
50{
51 DWORD size;
52 DWORD name;
53 WORD rows;
54 WORD reserved1;
55 BYTE data[4];
56} Q_PACKED PSMPATTERN;
57
58typedef struct _PSMSAMPLE
59{
60 BYTE flags;
61 CHAR songname[8];
62 DWORD smpid;
63 CHAR samplename[34];
64 DWORD reserved1;
65 BYTE reserved2;
66 BYTE insno;
67 BYTE reserved3;
68 DWORD length;
69 DWORD loopstart;
70 DWORD loopend;
71 WORD reserved4;
72 BYTE defvol;
73 DWORD reserved5;
74 DWORD samplerate;
75 BYTE reserved6[19];
76} Q_PACKED PSMSAMPLE;
77
78#pragma pack()
79
80
81BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength)
82//-----------------------------------------------------------
83{
84 PSMCHUNK *pfh = (PSMCHUNK *)lpStream;
85 DWORD dwMemPos, dwSongPos;
86 DWORD smpnames[MAX_SAMPLES];
87 DWORD patptrs[MAX_PATTERNS];
88 BYTE samplemap[MAX_SAMPLES];
89 UINT nPatterns;
90
91 // Chunk0: "PSM ",filesize,"FILE"
92 if (dwMemLength < 256) return FALSE;
93 if (pfh->id == PSM_ID_OLD)
94 {
95 #ifdef PSM_LOG
96 Log("Old PSM format not supported\n");
97 #endif
98 return FALSE;
99 }
100 if ((pfh->id != PSM_ID_NEW) || (pfh->len+12 > dwMemLength) || (pfh->listid != IFFID_FILE)) return FALSE;
101 m_nType = MOD_TYPE_PSM;
102 m_nChannels = 16;
103 m_nSamples = 0;
104 nPatterns = 0;
105 dwMemPos = 12;
106 dwSongPos = 0;
107 for (UINT iChPan=0; iChPan<16; iChPan++)
108 {
109 UINT pan = (((iChPan & 3) == 1) || ((iChPan&3)==2)) ? 0xC0 : 0x40;
110 ChnSettings[iChPan].nPan = pan;
111 }
112 while (dwMemPos+8 < dwMemLength)
113 {
114 PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos);
115 if ((pchunk->len >= dwMemLength - 8) || (dwMemPos + pchunk->len + 8 > dwMemLength)) break;
116 dwMemPos += 8;
117 PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos);
118 ULONG len = pchunk->len;
119 if (len) switch(pchunk->id)
120 {
121 // "TITL": Song title
122 case IFFID_TITL:
123 if (!pdata[0]) { pdata++; len--; }
124 memcpy(m_szNames[0], pdata, (len>31) ? 31 : len);
125 m_szNames[0][31] = 0;
126 break;
127 // "PBOD": Pattern
128 case IFFID_PBOD:
129 if ((len >= 12) && (nPatterns < MAX_PATTERNS))
130 {
131 patptrs[nPatterns++] = dwMemPos-8;
132 }
133 break;
134 // "SONG": Song description
135 case IFFID_SONG:
136 if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos))
137 {
138 dwSongPos = dwMemPos - 8;
139 }
140 break;
141 // "DSMP": Sample Data
142 case IFFID_DSMP:
143 if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES))
144 {
145 m_nSamples++;
146 MODINSTRUMENT *pins = &Ins[m_nSamples];
147 PSMSAMPLE *psmp = (PSMSAMPLE *)pdata;
148 smpnames[m_nSamples] = psmp->smpid;
149 memcpy(m_szNames[m_nSamples], psmp->samplename, 31);
150 m_szNames[m_nSamples][31] = 0;
151 samplemap[m_nSamples-1] = (BYTE)m_nSamples;
152 // Init sample
153 pins->nGlobalVol = 0x40;
154 pins->nC4Speed = psmp->samplerate;
155 pins->nLength = psmp->length;
156 pins->nLoopStart = psmp->loopstart;
157 pins->nLoopEnd = psmp->loopend;
158 pins->nPan = 128;
159 pins->nVolume = (psmp->defvol+1) * 2;
160 pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0;
161 if (pins->nLoopStart > 0) pins->nLoopStart--;
162 // Point to sample data
163 pdata += 0x60;
164 len -= 0x60;
165 // Load sample data
166 if ((pins->nLength > 3) && (len > 3))
167 {
168 ReadSample(pins, RS_PCM8D, (LPCSTR)pdata, len);
169 } else
170 {
171 pins->nLength = 0;
172 }
173 }
174 break;
175 #if 0
176 default:
177 {
178 CHAR s[8], s2[64];
179 *(DWORD *)s = pchunk->id;
180 s[4] = 0;
181 wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len, dwMemPos);
182 OutputDebugString(s2);
183 }
184 #endif
185 }
186 dwMemPos += pchunk->len;
187 }
188 // Step #1: convert song structure
189 PSMSONGHDR *pSong = (PSMSONGHDR *)(lpStream+dwSongPos+8);
190 if ((!dwSongPos) || (pSong->channels < 2) || (pSong->channels > 32)) return TRUE;
191 m_nChannels = pSong->channels;
192 // Valid song header -> convert attached chunks
193 {
194 DWORD dwSongEnd = dwSongPos + 8 + *(DWORD *)(lpStream+dwSongPos+4);
195 dwMemPos = dwSongPos + 8 + 11; // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR)
196 while (dwMemPos + 8 < dwSongEnd)
197 {
198 PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos);
199 dwMemPos += 8;
200 if ((pchunk->len > dwSongEnd) || (dwMemPos + pchunk->len > dwSongEnd)) break;
201 PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos);
202 ULONG len = pchunk->len;
203 switch(pchunk->id)
204 {
205 case IFFID_OPLH:
206 if (len >= 0x20)
207 {
208 UINT pos = len - 3;
209 while (pos > 5)
210 {
211 BOOL bFound = FALSE;
212 pos -= 5;
213 DWORD dwName = *(DWORD *)(pdata+pos);
214 for (UINT i=0; i<nPatterns; i++)
215 {
216 DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name;
217 if (dwName == dwPatName)
218 {
219 bFound = TRUE;
220 break;
221 }
222 }
223 if ((!bFound) && (pdata[pos+1] > 0) && (pdata[pos+1] <= 0x10)
224 && (pdata[pos+3] > 0x40) && (pdata[pos+3] < 0xC0))
225 {
226 m_nDefaultSpeed = pdata[pos+1];
227 m_nDefaultTempo = pdata[pos+3];
228 break;
229 }
230 }
231 UINT iOrd = 0;
232 while ((pos+5<len) && (iOrd < MAX_ORDERS))
233 {
234 DWORD dwName = *(DWORD *)(pdata+pos);
235 for (UINT i=0; i<nPatterns; i++)
236 {
237 DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name;
238 if (dwName == dwPatName)
239 {
240 Order[iOrd++] = i;
241 break;
242 }
243 }
244 pos += 5;
245 }
246 }
247 break;
248 }
249 dwMemPos += pchunk->len;
250 }
251 }
252
253 // Step #2: convert patterns
254 for (UINT nPat=0; nPat<nPatterns; nPat++)
255 {
256 PSMPATTERN *pPsmPat = (PSMPATTERN *)(lpStream+patptrs[nPat]+8);
257 ULONG len = *(DWORD *)(lpStream+patptrs[nPat]+4) - 12;
258 UINT nRows = pPsmPat->rows;
259 if (len > pPsmPat->size) len = pPsmPat->size;
260 if ((nRows < 64) || (nRows > 256)) nRows = 64;
261 PatternSize[nPat] = nRows;
262 if ((Patterns[nPat] = AllocatePattern(nRows, m_nChannels)) == NULL) break;
263 MODCOMMAND *m = Patterns[nPat];
264 BYTE *p = pPsmPat->data;
265 UINT pos = 0;
266 UINT row = 0;
267 UINT oldch = 0;
268 BOOL bNewRow = FALSE;
269 #ifdef PSM_LOG
270 Log("Pattern %d at offset 0x%04X\n", nPat, (DWORD)(p - (BYTE *)lpStream));
271 #endif
272 while ((row < nRows) && (pos+1 < len))
273 {
274 UINT flags = p[pos++];
275 UINT ch = p[pos++];
276
277 #ifdef PSM_LOG
278 //Log("flags+ch: %02X.%02X\n", flags, ch);
279 #endif
280 if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow)*/)
281 {
282 if ((pos+1<len) && (!(p[pos] & 0x0f)) && (p[pos+1] < m_nChannels))
283 {
284 #ifdef PSM_LOG
285 //if (!nPat) Log("Continuing on new row\n");
286 #endif
287 row++;
288 m += m_nChannels;
289 oldch = ch;
290 continue;
291 }
292 }
293 if ((pos >= len) || (row >= nRows)) break;
294 if (!(flags & 0xf0))
295 {
296 #ifdef PSM_LOG
297 //if (!nPat) Log("EOR(%d): %02X.%02X\n", row, p[pos], p[pos+1]);
298 #endif
299 row++;
300 m += m_nChannels;
301 bNewRow = TRUE;
302 oldch = ch;
303 continue;
304 }
305 bNewRow = FALSE;
306 if (ch >= m_nChannels)
307 {
308 #ifdef PSM_LOG
309 if (!nPat) Log("Invalid channel row=%d (0x%02X.0x%02X)\n", row, flags, ch);
310 #endif
311 ch = 0;
312 }
313 // Note + Instr
314 if ((flags & 0x40) && (pos+1 < len))
315 {
316 UINT note = p[pos++];
317 UINT nins = p[pos++];
318 #ifdef PSM_LOG
319 //if (!nPat) Log("note+ins: %02X.%02X\n", note, nins);
320 if ((!nPat) && (nins >= m_nSamples)) Log("WARNING: invalid instrument number (%d)\n", nins);
321 #endif
322 if ((note) && (note < 0x80)) note = (note>>4)*12+(note&0x0f)+12+1;
323 m[ch].instr = samplemap[nins];
324 m[ch].note = note;
325 }
326 // Volume
327 if ((flags & 0x20) && (pos < len))
328 {
329 m[ch].volcmd = VOLCMD_VOLUME;
330 m[ch].vol = p[pos++] / 2;
331 }
332 // Effect
333 if ((flags & 0x10) && (pos+1 < len))
334 {
335 UINT command = p[pos++];
336 UINT param = p[pos++];
337 // Convert effects
338 switch(command)
339 {
340 // 01: fine volslide up
341 case 0x01:command = CMD_VOLUMESLIDE; param |= 0x0f; break;
342 // 04: fine volslide down
343 case 0x04:command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; break;
344 // 0C: portamento up
345 case 0x0C:command = CMD_PORTAMENTOUP; param = (param+1)/2; break;
346 // 0E: portamento down
347 case 0x0E:command = CMD_PORTAMENTODOWN; param = (param+1)/2; break;
348 // 33: Position Jump
349 case 0x33:command = CMD_POSITIONJUMP; break;
350 // 34: Pattern break
351 case 0x34:command = CMD_PATTERNBREAK; break;
352 // 3D: speed
353 case 0x3D:command = CMD_SPEED; break;
354 // 3E: tempo
355 case 0x3E:command = CMD_TEMPO; break;
356 // Unknown
357 default:
358 #ifdef PSM_LOG
359 Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", nPat, row, ch, command, param);
360 #endif
361 command = param = 0;
362 }
363 m[ch].command = (BYTE)command;
364 m[ch].param = (BYTE)param;
365 }
366 oldch = ch;
367 }
368 #ifdef PSM_LOG
369 if (pos < len)
370 {
371 Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n", nPat, row, nRows, pPsmPat->rows, pPsmPat->size, len-pos);
372 }
373 #endif
374 }
375
376 // Done (finally!)
377 return TRUE;
378}
379
380
381//////////////////////////////////////////////////////////////
382//
383// PSM Old Format
384//
385
386/*
387
388CONST
389 c_PSM_MaxOrder = $FF;
390 c_PSM_MaxSample = $FF;
391 c_PSM_MaxChannel = $0F;
392
393 TYPE
394 PPSM_Header = ^TPSM_Header;
395 TPSM_Header = RECORD
396 PSM_Sign : ARRAY[01..04] OF CHAR; { PSM + #254 }
397 PSM_SongName : ARRAY[01..58] OF CHAR;
398 PSM_Byte00 : BYTE;
399 PSM_Byte1A : BYTE;
400 PSM_Unknown00 : BYTE;
401 PSM_Unknown01 : BYTE;
402 PSM_Unknown02 : BYTE;
403 PSM_Speed : BYTE;
404 PSM_Tempo : BYTE;
405 PSM_Unknown03 : BYTE;
406 PSM_Unknown04 : WORD;
407 PSM_OrderLength : WORD;
408 PSM_PatternNumber : WORD;
409 PSM_SampleNumber : WORD;
410 PSM_ChannelNumber : WORD;
411 PSM_ChannelUsed : WORD;
412 PSM_OrderPosition : LONGINT;
413 PSM_ChannelSettingPosition : LONGINT;
414 PSM_PatternPosition : LONGINT;
415 PSM_SamplePosition : LONGINT;
416 { *** perhaps there are some more infos in a larger header,
417 but i have not decoded it and so it apears here NOT }
418 END;
419
420 PPSM_Sample = ^TPSM_Sample;
421 TPSM_Sample = RECORD
422 PSM_SampleFileName : ARRAY[01..12] OF CHAR;
423 PSM_SampleByte00 : BYTE;
424 PSM_SampleName : ARRAY[01..22] OF CHAR;
425 PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE;
426 PSM_SamplePosition : LONGINT;
427 PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE;
428 PSM_SampleNumber : BYTE;
429 PSM_SampleFlags : WORD;
430 PSM_SampleLength : LONGINT;
431 PSM_SampleLoopBegin : LONGINT;
432 PSM_SampleLoopEnd : LONGINT;
433 PSM_Unknown03 : BYTE;
434 PSM_SampleVolume : BYTE;
435 PSM_SampleC5Speed : WORD;
436 END;
437
438 PPSM_SampleList = ^TPSM_SampleList;
439 TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample;
440
441 PPSM_Order = ^TPSM_Order;
442 TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE;
443
444 PPSM_ChannelSettings = ^TPSM_ChannelSettings;
445 TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE;
446
447 CONST
448 PSM_NotesInPattern : BYTE = $00;
449 PSM_ChannelInPattern : BYTE = $00;
450
451 CONST
452 c_PSM_SetSpeed = 60;
453
454 FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT;
455 BEGIN
456 END;
457
458 PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD);
459 VAR
460 Witz : ARRAY[00..04] OF WORD;
461 I1,I2 : WORD;
462 I3,I4 : WORD;
463 TopicalByte : ^BYTE;
464 Pattern : PUnpackedPattern;
465 ChannelP : BYTE;
466 NoteP : BYTE;
467 InfoByte : BYTE;
468 CodeByte : BYTE;
469 InfoWord : WORD;
470 Effect : BYTE;
471 Opperand : BYTE;
472 Panning : BYTE;
473 Volume : BYTE;
474 PrevInfo : BYTE;
475 InfoIndex : BYTE;
476 BEGIN
477 Pattern := @Destination;
478 TopicalByte := @Source;
479 { *** Initialize patttern }
480 FOR I2 := 0 TO c_Maximum_NoteIndex DO
481 FOR I3 := 0 TO c_Maximum_ChannelIndex DO
482 BEGIN
483 Pattern^[I2,I3,c_Pattern_NoteIndex] := $FF;
484 Pattern^[I2,I3,c_Pattern_SampleIndex] := $00;
485 Pattern^[I2,I3,c_Pattern_VolumeIndex] := $FF;
486 Pattern^[I2,I3,c_Pattern_PanningIndex] := $FF;
487 Pattern^[I2,I3,c_Pattern_EffectIndex] := $00;
488 Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00;
489 END;
490 { *** Byte-pointer on first pattern-entry }
491 ChannelP := $00;
492 NoteP := $00;
493 InfoByte := $00;
494 PrevInfo := $00;
495 InfoIndex := $02;
496 { *** read notes in pattern }
497 PSM_NotesInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
498 PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
499 { *** unpack pattern }
500 WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO
501 BEGIN
502 { *** Read info-byte }
503 InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
504 IF InfoByte <> $00 THEN
505 BEGIN
506 ChannelP := InfoByte AND $0F;
507 IF InfoByte AND 128 = 128 THEN { note and sample }
508 BEGIN
509 { *** read note }
510 CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
511 DEC(CodeByte);
512 CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2;
513 Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte;
514 { *** read sample }
515 CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
516 Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte;
517 END;
518 IF InfoByte AND 64 = 64 THEN { Volume }
519 BEGIN
520 CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
521 Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte;
522 END;
523 IF InfoByte AND 32 = 32 THEN { effect AND opperand }
524 BEGIN
525 Effect := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
526 Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
527 CASE Effect OF
528 c_PSM_SetSpeed:
529 BEGIN
530 Effect := c_I_Set_Speed;
531 END;
532 ELSE
533 BEGIN
534 Effect := c_I_NoEffect;
535 Opperand := $00;
536 END;
537 END;
538 Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex] := Effect;
539 Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand;
540 END;
541 END ELSE INC(NoteP);
542 END;
543 END;
544
545 PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD);
546 { *** caution : Module has to be inited before!!!! }
547 VAR
548 Header : PPSM_Header;
549 Sample : PPSM_SampleList;
550 Order : PPSM_Order;
551 ChannelSettings : PPSM_ChannelSettings;
552 MultiPurposeBuffer : PByteArray;
553 PatternBuffer : PUnpackedPattern;
554 TopicalParaPointer : WORD;
555
556 InFile : FILE;
557 I1,I2 : WORD;
558 I3,I4 : WORD;
559 TempW : WORD;
560 TempB : BYTE;
561 TempP : PByteArray;
562 TempI : INTEGER;
563 { *** copy-vars for loop-extension }
564 CopySource : LONGINT;
565 CopyDestination : LONGINT;
566 CopyLength : LONGINT;
567 BEGIN
568 { *** try to open file }
569 ASSIGN(InFile,FileName);
570{$I-}
571 RESET(InFile,1);
572{$I+}
573 IF IORESULT <> $00 THEN
574 BEGIN
575 EXIT;
576 END;
577{$I-}
578 { *** seek start of module }
579 IF FILESIZE(InFile) < FilePosition THEN
580 BEGIN
581 EXIT;
582 END;
583 SEEK(InFile,FilePosition);
584 { *** look for enough memory for temporary variables }
585 IF MEMAVAIL < SIZEOF(TPSM_Header) + SIZEOF(TPSM_SampleList) +
586 SIZEOF(TPSM_Order) + SIZEOF(TPSM_ChannelSettings) +
587 SIZEOF(TByteArray) + SIZEOF(TUnpackedPattern)
588 THEN
589 BEGIN
590 EXIT;
591 END;
592 { *** init dynamic variables }
593 NEW(Header);
594 NEW(Sample);
595 NEW(Order);
596 NEW(ChannelSettings);
597 NEW(MultiPurposeBuffer);
598 NEW(PatternBuffer);
599 { *** read header }
600 BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header));
601 { *** test if this is a DSM-file }
602 IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S') AND
603 (Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN
604 BEGIN
605 ErrorCode := c_NoValidFileFormat;
606 CLOSE(InFile);
607 EXIT;
608 END;
609 { *** read order }
610 SEEK(InFile,FilePosition + Header^.PSM_OrderPosition);
611 BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength);
612 { *** read channelsettings }
613 SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition);
614 BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings));
615 { *** read samplelist }
616 SEEK(InFile,FilePosition + Header^.PSM_SamplePosition);
617 BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample));
618 { *** copy header to intern NTMIK-structure }
619 Module^.Module_Sign := 'MF';
620 Module^.Module_FileFormatVersion := $0100;
621 Module^.Module_SampleNumber := Header^.PSM_SampleNumber;
622 Module^.Module_PatternNumber := Header^.PSM_PatternNumber;
623 Module^.Module_OrderLength := Header^.PSM_OrderLength;
624 Module^.Module_ChannelNumber := Header^.PSM_ChannelNumber+1;
625 Module^.Module_Initial_GlobalVolume := 64;
626 Module^.Module_Initial_MasterVolume := $C0;
627 Module^.Module_Initial_Speed := Header^.PSM_Speed;
628 Module^.Module_Initial_Tempo := Header^.PSM_Tempo;
629{ *** paragraph 01 start }
630 Module^.Module_Flags := c_Module_Flags_ZeroVolume * BYTE(1) +
631 c_Module_Flags_Stereo * BYTE(1) +
632 c_Module_Flags_ForceAmigaLimits * BYTE(0) +
633 c_Module_Flags_Panning * BYTE(1) +
634 c_Module_Flags_Surround * BYTE(1) +
635 c_Module_Flags_QualityMixing * BYTE(1) +
636 c_Module_Flags_FastVolumeSlides * BYTE(0) +
637 c_Module_Flags_SpecialCustomData * BYTE(0) +
638 c_Module_Flags_SongName * BYTE(1);
639 I1 := $01;
640 WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO
641 BEGIN
642 Module^.Module_Name[I1] := Header^.PSM_SongName[I1];
643 INC(I1);
644 END;
645 Module^.Module_Name[c_Module_SongNameLength] := #00;
646 { *** Init channelsettings }
647 FOR I1 := 0 TO c_Maximum_ChannelIndex DO
648 BEGIN
649 IF I1 < Header^.PSM_ChannelUsed THEN
650 BEGIN
651 { *** channel enabled }
652 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64;
653 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := (ChannelSettings^[I1]) * $08;
654 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) +
655 c_ChannelSettings_Code_ChannelEnabled * BYTE(1) +
656 c_ChannelSettings_Code_ChannelDigital * BYTE(1);
657 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls :=
658 c_ChannelSettings_Controls_EnhancedMode * BYTE(1) +
659 c_ChannelSettings_Controls_SurroundMode * BYTE(0);
660 END
661 ELSE
662 BEGIN
663 { *** channel disabled }
664 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00;
665 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := $00;
666 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := $00;
667 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := $00;
668 END;
669 END;
670 { *** init and copy order }
671 FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF);
672 MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength);
673 { *** read pattern }
674 SEEK(InFile,FilePosition + Header^.PSM_PatternPosition);
675 NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1;
676 FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO
677 BEGIN
678 NTMIK_LoadPatternProcedure;
679 { *** read length }
680 BLOCKREAD(InFile,TempW,2);
681 { *** read pattern }
682 BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2);
683 { *** unpack pattern and set notes per channel to 64 }
684 PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW);
685 NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern);
686 TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00];
687 GETMEM(Module^.Module_PatternPointer^[I1],TempW);
688 MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW);
689 { *** next pattern }
690 END;
691 { *** read samples }
692 NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber;
693 FOR I1 := 1 TO Header^.PSM_SampleNumber DO
694 BEGIN
695 NTMIK_LoadSampleProcedure;
696 { *** get index for sample }
697 I3 := Sample^[I1].PSM_SampleNumber;
698 { *** clip PSM-sample }
699 IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength
700 THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength;
701 { *** init intern sample }
702 NEW(Module^.Module_SamplePointer^[I3]);
703 FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00);
704 FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32);
705 FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32);
706 { *** copy informations to intern sample }
707 I2 := $01;
708 WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO
709 BEGIN
710 Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2];
711 INC(I2);
712 END;
713 Module^.Module_SamplePointer^[I3]^.Sample_Sign := 'DF';
714 Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100;
715 Module^.Module_SamplePointer^[I3]^.Sample_Position := $00000000;
716 Module^.Module_SamplePointer^[I3]^.Sample_Selector := $0000;
717 Module^.Module_SamplePointer^[I3]^.Sample_Volume := Sample^[I1].PSM_SampleVolume;
718 Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter := $00;
719 Module^.Module_SamplePointer^[I3]^.Sample_C5Speed := Sample^[I1].PSM_SampleC5Speed;
720 Module^.Module_SamplePointer^[I3]^.Sample_Length := Sample^[I1].PSM_SampleLength;
721 Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin := Sample^[I1].PSM_SampleLoopBegin;
722 Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd := Sample^[I1].PSM_SampleLoopEnd;
723 { *** now it's time for the flags }
724 Module^.Module_SamplePointer^[I3]^.Sample_Flags :=
725 c_Sample_Flags_DigitalSample * BYTE(1) +
726 c_Sample_Flags_8BitSample * BYTE(1) +
727 c_Sample_Flags_UnsignedSampleData * BYTE(1) +
728 c_Sample_Flags_Packed * BYTE(0) +
729 c_Sample_Flags_LoopCounter * BYTE(0) +
730 c_Sample_Flags_SampleName * BYTE(1) +
731 c_Sample_Flags_LoopActive *
732 BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15));
733 { *** alloc memory for sample-data }
734 E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector,
735 Module^.Module_SamplePointer^[I3]^.Sample_Position,
736 Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize);
737 { *** read out data }
738 EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector;
739 EPT(TempP).p_Offset := $0000;
740 SEEK(InFile,Sample^[I1].PSM_SamplePosition);
741 E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length);
742 { *** 'coz the samples are signed in a DSM-file -> PC-fy them }
743 IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN
744 BEGIN
745 CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length;
746 { *** decode sample }
747 ASM
748 DB 066h; MOV CX,WORD PTR CopyLength
749 { *** load sample selector }
750 MOV ES,WORD PTR TempP[00002h]
751 DB 066h; XOR SI,SI
752 DB 066h; XOR DI,DI
753 XOR AH,AH
754 { *** conert all bytes }
755 @@MainLoop:
756 DB 026h; DB 067h; LODSB
757 ADD AL,AH
758 MOV AH,AL
759 DB 067h; STOSB
760 DB 066h; LOOP @@MainLoop
761 END;
762 { *** make samples unsigned }
763 ASM
764 DB 066h; MOV CX,WORD PTR CopyLength
765 { *** load sample selector }
766 MOV ES,WORD PTR TempP[00002h]
767 DB 066h; XOR SI,SI
768 DB 066h; XOR DI,DI
769 { *** conert all bytes }
770 @@MainLoop:
771 DB 026h; DB 067h; LODSB
772 SUB AL,080h
773 DB 067h; STOSB
774 DB 066h; LOOP @@MainLoop
775 END;
776 { *** Create Loop-Extension }
777 IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN
778 BEGIN
779 CopySource := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin;
780 CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd;
781 CopyLength := CopyDestination - CopySource;
782 ASM
783 { *** load sample-selector }
784 MOV ES,WORD PTR TempP[00002h]
785 DB 066h; MOV DI,WORD PTR CopyDestination
786 { *** calculate number of full sample-loops to copy }
787 XOR DX,DX
788 MOV AX,c_LoopExtensionSize
789 MOV BX,WORD PTR CopyLength
790 DIV BX
791 OR AX,AX
792 JE @@NoFullLoop
793 { *** copy some full-loops (size=bx) }
794 MOV CX,AX
795 @@InnerLoop:
796 PUSH CX
797 DB 066h; MOV SI,WORD PTR CopySource
798 MOV CX,BX
799 DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] }
800 POP CX
801 LOOP @@InnerLoop
802 @@NoFullLoop:
803 { *** calculate number of rest-bytes to copy }
804 DB 066h; MOV SI,WORD PTR CopySource
805 MOV CX,DX
806 DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] }
807 END;
808 END
809 ELSE
810 BEGIN
811 CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length;
812 ASM
813 { *** load sample-selector }
814 MOV ES,WORD PTR TempP[00002h]
815 DB 066h; MOV DI,WORD PTR CopyDestination
816 { *** clear extension }
817 MOV CX,c_LoopExtensionSize
818 MOV AL,080h
819 DB 0F3h; DB 067h,0AAh { REP STOS BYTE PTR ES:[EDI] }
820 END;
821 END;
822 END;
823 { *** next sample }
824 END;
825 { *** init period-ranges }
826 NTMIK_MaximumPeriod := $0000D600 SHR 1;
827 NTMIK_MinimumPeriod := $0000D600 SHR 8;
828 { *** close file }
829 CLOSE(InFile);
830 { *** dispose all dynamic variables }
831 DISPOSE(Header);
832 DISPOSE(Sample);
833 DISPOSE(Order);
834 DISPOSE(ChannelSettings);
835 DISPOSE(MultiPurposeBuffer);
836 DISPOSE(PatternBuffer);
837 { *** set errorcode to noerror }
838 ErrorCode := c_NoError;
839 END;
840
841*/
842