comparison src/modplug/load_psm.cxx @ 136:6b5a52635b3b trunk

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