Mercurial > audlegacy-plugins
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 |