author | llornkcor <llornkcor> | 2002-07-20 22:07:31 (UTC) |
---|---|---|
committer | llornkcor <llornkcor> | 2002-07-20 22:07:31 (UTC) |
commit | 2342d48be31847e7ead9d1cc682452e8f0122351 (patch) (unidiff) | |
tree | 8329bb94e9d429c905a0ef6b881cf1c0f775bf14 /core/multimedia/opieplayer/modplug/load_psm.cpp | |
parent | 0f24c1fb86d3bb58d8696358b824c0e01752b10d (diff) | |
download | opie-2342d48be31847e7ead9d1cc682452e8f0122351.zip opie-2342d48be31847e7ead9d1cc682452e8f0122351.tar.gz opie-2342d48be31847e7ead9d1cc682452e8f0122351.tar.bz2 |
initial commit of modplugin
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.cpp | 842 |
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 | |||
34 | typedef struct _PSMCHUNK | ||
35 | { | ||
36 | DWORD id; | ||
37 | DWORD len; | ||
38 | DWORD listid; | ||
39 | } Q_PACKED PSMCHUNK; | ||
40 | |||
41 | typedef struct _PSMSONGHDR | ||
42 | { | ||
43 | CHAR songname[8];// "MAINSONG" | ||
44 | BYTE reserved1; | ||
45 | BYTE reserved2; | ||
46 | BYTE channels; | ||
47 | } Q_PACKED PSMSONGHDR; | ||
48 | |||
49 | typedef struct _PSMPATTERN | ||
50 | { | ||
51 | DWORD size; | ||
52 | DWORD name; | ||
53 | WORD rows; | ||
54 | WORD reserved1; | ||
55 | BYTE data[4]; | ||
56 | } Q_PACKED PSMPATTERN; | ||
57 | |||
58 | typedef 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 | |||
81 | BOOL 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 | |||
388 | CONST | ||
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 | |||