00001 /* 00002 * This program is free software; you can redistribute it and modify it 00003 * under the terms of the GNU General Public License as published by the 00004 * Free Software Foundation; either version 2 of the license or (at your 00005 * option) any later version. 00006 * 00007 * Authors: Olivier Lapicque <olivierl@jps.net> 00008 */ 00009 00010 00012 // 00013 // PSM module loader 00014 // 00016 #include "stdafx.h" 00017 #include "sndfile.h" 00018 00019 //#define PSM_LOG 00020 00021 #define PSM_ID_NEW 0x204d5350 00022 #define PSM_ID_OLD 0xfe4d5350 00023 #define IFFID_FILE 0x454c4946 00024 #define IFFID_TITL 0x4c544954 00025 #define IFFID_SDFT 0x54464453 00026 #define IFFID_PBOD 0x444f4250 00027 #define IFFID_SONG 0x474e4f53 00028 #define IFFID_PATT 0x54544150 00029 #define IFFID_DSMP 0x504d5344 00030 #define IFFID_OPLH 0x484c504f 00031 00032 #pragma pack(1) 00033 00034 typedef struct _PSMCHUNK 00035 { 00036 DWORD id; 00037 DWORD len; 00038 DWORD listid; 00039 } Q_PACKED PSMCHUNK; 00040 00041 typedef struct _PSMSONGHDR 00042 { 00043 CHAR songname[8]; // "MAINSONG" 00044 BYTE reserved1; 00045 BYTE reserved2; 00046 BYTE channels; 00047 } Q_PACKED PSMSONGHDR; 00048 00049 typedef struct _PSMPATTERN 00050 { 00051 DWORD size; 00052 DWORD name; 00053 WORD rows; 00054 WORD reserved1; 00055 BYTE data[4]; 00056 } Q_PACKED PSMPATTERN; 00057 00058 typedef struct _PSMSAMPLE 00059 { 00060 BYTE flags; 00061 CHAR songname[8]; 00062 DWORD smpid; 00063 CHAR samplename[34]; 00064 DWORD reserved1; 00065 BYTE reserved2; 00066 BYTE insno; 00067 BYTE reserved3; 00068 DWORD length; 00069 DWORD loopstart; 00070 DWORD loopend; 00071 WORD reserved4; 00072 BYTE defvol; 00073 DWORD reserved5; 00074 DWORD samplerate; 00075 BYTE reserved6[19]; 00076 } Q_PACKED PSMSAMPLE; 00077 00078 #pragma pack() 00079 00080 00081 BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) 00082 //----------------------------------------------------------- 00083 { 00084 PSMCHUNK *pfh = (PSMCHUNK *)lpStream; 00085 DWORD dwMemPos, dwSongPos; 00086 DWORD smpnames[MAX_SAMPLES]; 00087 DWORD patptrs[MAX_PATTERNS]; 00088 BYTE samplemap[MAX_SAMPLES]; 00089 UINT nPatterns; 00090 00091 // Chunk0: "PSM ",filesize,"FILE" 00092 if (dwMemLength < 256) return FALSE; 00093 if (pfh->id == PSM_ID_OLD) 00094 { 00095 #ifdef PSM_LOG 00096 Log("Old PSM format not supported\n"); 00097 #endif 00098 return FALSE; 00099 } 00100 if ((pfh->id != PSM_ID_NEW) || (pfh->len+12 > dwMemLength) || (pfh->listid != IFFID_FILE)) return FALSE; 00101 m_nType = MOD_TYPE_PSM; 00102 m_nChannels = 16; 00103 m_nSamples = 0; 00104 nPatterns = 0; 00105 dwMemPos = 12; 00106 dwSongPos = 0; 00107 for (UINT iChPan=0; iChPan<16; iChPan++) 00108 { 00109 UINT pan = (((iChPan & 3) == 1) || ((iChPan&3)==2)) ? 0xC0 : 0x40; 00110 ChnSettings[iChPan].nPan = pan; 00111 } 00112 while (dwMemPos+8 < dwMemLength) 00113 { 00114 PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); 00115 if ((pchunk->len >= dwMemLength - 8) || (dwMemPos + pchunk->len + 8 > dwMemLength)) break; 00116 dwMemPos += 8; 00117 PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); 00118 ULONG len = pchunk->len; 00119 if (len) switch(pchunk->id) 00120 { 00121 // "TITL": Song title 00122 case IFFID_TITL: 00123 if (!pdata[0]) { pdata++; len--; } 00124 memcpy(m_szNames[0], pdata, (len>31) ? 31 : len); 00125 m_szNames[0][31] = 0; 00126 break; 00127 // "PBOD": Pattern 00128 case IFFID_PBOD: 00129 if ((len >= 12) && (nPatterns < MAX_PATTERNS)) 00130 { 00131 patptrs[nPatterns++] = dwMemPos-8; 00132 } 00133 break; 00134 // "SONG": Song description 00135 case IFFID_SONG: 00136 if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos)) 00137 { 00138 dwSongPos = dwMemPos - 8; 00139 } 00140 break; 00141 // "DSMP": Sample Data 00142 case IFFID_DSMP: 00143 if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES)) 00144 { 00145 m_nSamples++; 00146 MODINSTRUMENT *pins = &Ins[m_nSamples]; 00147 PSMSAMPLE *psmp = (PSMSAMPLE *)pdata; 00148 smpnames[m_nSamples] = psmp->smpid; 00149 memcpy(m_szNames[m_nSamples], psmp->samplename, 31); 00150 m_szNames[m_nSamples][31] = 0; 00151 samplemap[m_nSamples-1] = (BYTE)m_nSamples; 00152 // Init sample 00153 pins->nGlobalVol = 0x40; 00154 pins->nC4Speed = psmp->samplerate; 00155 pins->nLength = psmp->length; 00156 pins->nLoopStart = psmp->loopstart; 00157 pins->nLoopEnd = psmp->loopend; 00158 pins->nPan = 128; 00159 pins->nVolume = (psmp->defvol+1) * 2; 00160 pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0; 00161 if (pins->nLoopStart > 0) pins->nLoopStart--; 00162 // Point to sample data 00163 pdata += 0x60; 00164 len -= 0x60; 00165 // Load sample data 00166 if ((pins->nLength > 3) && (len > 3)) 00167 { 00168 ReadSample(pins, RS_PCM8D, (LPCSTR)pdata, len); 00169 } else 00170 { 00171 pins->nLength = 0; 00172 } 00173 } 00174 break; 00175 #if 0 00176 default: 00177 { 00178 CHAR s[8], s2[64]; 00179 *(DWORD *)s = pchunk->id; 00180 s[4] = 0; 00181 wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len, dwMemPos); 00182 OutputDebugString(s2); 00183 } 00184 #endif 00185 } 00186 dwMemPos += pchunk->len; 00187 } 00188 // Step #1: convert song structure 00189 PSMSONGHDR *pSong = (PSMSONGHDR *)(lpStream+dwSongPos+8); 00190 if ((!dwSongPos) || (pSong->channels < 2) || (pSong->channels > 32)) return TRUE; 00191 m_nChannels = pSong->channels; 00192 // Valid song header -> convert attached chunks 00193 { 00194 DWORD dwSongEnd = dwSongPos + 8 + *(DWORD *)(lpStream+dwSongPos+4); 00195 dwMemPos = dwSongPos + 8 + 11; // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR) 00196 while (dwMemPos + 8 < dwSongEnd) 00197 { 00198 PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); 00199 dwMemPos += 8; 00200 if ((pchunk->len > dwSongEnd) || (dwMemPos + pchunk->len > dwSongEnd)) break; 00201 PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); 00202 ULONG len = pchunk->len; 00203 switch(pchunk->id) 00204 { 00205 case IFFID_OPLH: 00206 if (len >= 0x20) 00207 { 00208 UINT pos = len - 3; 00209 while (pos > 5) 00210 { 00211 BOOL bFound = FALSE; 00212 pos -= 5; 00213 DWORD dwName = *(DWORD *)(pdata+pos); 00214 for (UINT i=0; i<nPatterns; i++) 00215 { 00216 DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name; 00217 if (dwName == dwPatName) 00218 { 00219 bFound = TRUE; 00220 break; 00221 } 00222 } 00223 if ((!bFound) && (pdata[pos+1] > 0) && (pdata[pos+1] <= 0x10) 00224 && (pdata[pos+3] > 0x40) && (pdata[pos+3] < 0xC0)) 00225 { 00226 m_nDefaultSpeed = pdata[pos+1]; 00227 m_nDefaultTempo = pdata[pos+3]; 00228 break; 00229 } 00230 } 00231 UINT iOrd = 0; 00232 while ((pos+5<len) && (iOrd < MAX_ORDERS)) 00233 { 00234 DWORD dwName = *(DWORD *)(pdata+pos); 00235 for (UINT i=0; i<nPatterns; i++) 00236 { 00237 DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name; 00238 if (dwName == dwPatName) 00239 { 00240 Order[iOrd++] = i; 00241 break; 00242 } 00243 } 00244 pos += 5; 00245 } 00246 } 00247 break; 00248 } 00249 dwMemPos += pchunk->len; 00250 } 00251 } 00252 00253 // Step #2: convert patterns 00254 for (UINT nPat=0; nPat<nPatterns; nPat++) 00255 { 00256 PSMPATTERN *pPsmPat = (PSMPATTERN *)(lpStream+patptrs[nPat]+8); 00257 ULONG len = *(DWORD *)(lpStream+patptrs[nPat]+4) - 12; 00258 UINT nRows = pPsmPat->rows; 00259 if (len > pPsmPat->size) len = pPsmPat->size; 00260 if ((nRows < 64) || (nRows > 256)) nRows = 64; 00261 PatternSize[nPat] = nRows; 00262 if ((Patterns[nPat] = AllocatePattern(nRows, m_nChannels)) == NULL) break; 00263 MODCOMMAND *m = Patterns[nPat]; 00264 BYTE *p = pPsmPat->data; 00265 UINT pos = 0; 00266 UINT row = 0; 00267 UINT oldch = 0; 00268 BOOL bNewRow = FALSE; 00269 #ifdef PSM_LOG 00270 Log("Pattern %d at offset 0x%04X\n", nPat, (DWORD)(p - (BYTE *)lpStream)); 00271 #endif 00272 while ((row < nRows) && (pos+1 < len)) 00273 { 00274 UINT flags = p[pos++]; 00275 UINT ch = p[pos++]; 00276 00277 #ifdef PSM_LOG 00278 //Log("flags+ch: %02X.%02X\n", flags, ch); 00279 #endif 00280 if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow)*/) 00281 { 00282 if ((pos+1<len) && (!(p[pos] & 0x0f)) && (p[pos+1] < m_nChannels)) 00283 { 00284 #ifdef PSM_LOG 00285 //if (!nPat) Log("Continuing on new row\n"); 00286 #endif 00287 row++; 00288 m += m_nChannels; 00289 oldch = ch; 00290 continue; 00291 } 00292 } 00293 if ((pos >= len) || (row >= nRows)) break; 00294 if (!(flags & 0xf0)) 00295 { 00296 #ifdef PSM_LOG 00297 //if (!nPat) Log("EOR(%d): %02X.%02X\n", row, p[pos], p[pos+1]); 00298 #endif 00299 row++; 00300 m += m_nChannels; 00301 bNewRow = TRUE; 00302 oldch = ch; 00303 continue; 00304 } 00305 bNewRow = FALSE; 00306 if (ch >= m_nChannels) 00307 { 00308 #ifdef PSM_LOG 00309 if (!nPat) Log("Invalid channel row=%d (0x%02X.0x%02X)\n", row, flags, ch); 00310 #endif 00311 ch = 0; 00312 } 00313 // Note + Instr 00314 if ((flags & 0x40) && (pos+1 < len)) 00315 { 00316 UINT note = p[pos++]; 00317 UINT nins = p[pos++]; 00318 #ifdef PSM_LOG 00319 //if (!nPat) Log("note+ins: %02X.%02X\n", note, nins); 00320 if ((!nPat) && (nins >= m_nSamples)) Log("WARNING: invalid instrument number (%d)\n", nins); 00321 #endif 00322 if ((note) && (note < 0x80)) note = (note>>4)*12+(note&0x0f)+12+1; 00323 m[ch].instr = samplemap[nins]; 00324 m[ch].note = note; 00325 } 00326 // Volume 00327 if ((flags & 0x20) && (pos < len)) 00328 { 00329 m[ch].volcmd = VOLCMD_VOLUME; 00330 m[ch].vol = p[pos++] / 2; 00331 } 00332 // Effect 00333 if ((flags & 0x10) && (pos+1 < len)) 00334 { 00335 UINT command = p[pos++]; 00336 UINT param = p[pos++]; 00337 // Convert effects 00338 switch(command) 00339 { 00340 // 01: fine volslide up 00341 case 0x01: command = CMD_VOLUMESLIDE; param |= 0x0f; break; 00342 // 04: fine volslide down 00343 case 0x04: command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; break; 00344 // 0C: portamento up 00345 case 0x0C: command = CMD_PORTAMENTOUP; param = (param+1)/2; break; 00346 // 0E: portamento down 00347 case 0x0E: command = CMD_PORTAMENTODOWN; param = (param+1)/2; break; 00348 // 33: Position Jump 00349 case 0x33: command = CMD_POSITIONJUMP; break; 00350 // 34: Pattern break 00351 case 0x34: command = CMD_PATTERNBREAK; break; 00352 // 3D: speed 00353 case 0x3D: command = CMD_SPEED; break; 00354 // 3E: tempo 00355 case 0x3E: command = CMD_TEMPO; break; 00356 // Unknown 00357 default: 00358 #ifdef PSM_LOG 00359 Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", nPat, row, ch, command, param); 00360 #endif 00361 command = param = 0; 00362 } 00363 m[ch].command = (BYTE)command; 00364 m[ch].param = (BYTE)param; 00365 } 00366 oldch = ch; 00367 } 00368 #ifdef PSM_LOG 00369 if (pos < len) 00370 { 00371 Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n", nPat, row, nRows, pPsmPat->rows, pPsmPat->size, len-pos); 00372 } 00373 #endif 00374 } 00375 00376 // Done (finally!) 00377 return TRUE; 00378 } 00379 00380 00382 // 00383 // PSM Old Format 00384 // 00385 00386 /* 00387 00388 CONST 00389 c_PSM_MaxOrder = $FF; 00390 c_PSM_MaxSample = $FF; 00391 c_PSM_MaxChannel = $0F; 00392 00393 TYPE 00394 PPSM_Header = ^TPSM_Header; 00395 TPSM_Header = RECORD 00396 PSM_Sign : ARRAY[01..04] OF CHAR; { PSM + #254 } 00397 PSM_SongName : ARRAY[01..58] OF CHAR; 00398 PSM_Byte00 : BYTE; 00399 PSM_Byte1A : BYTE; 00400 PSM_Unknown00 : BYTE; 00401 PSM_Unknown01 : BYTE; 00402 PSM_Unknown02 : BYTE; 00403 PSM_Speed : BYTE; 00404 PSM_Tempo : BYTE; 00405 PSM_Unknown03 : BYTE; 00406 PSM_Unknown04 : WORD; 00407 PSM_OrderLength : WORD; 00408 PSM_PatternNumber : WORD; 00409 PSM_SampleNumber : WORD; 00410 PSM_ChannelNumber : WORD; 00411 PSM_ChannelUsed : WORD; 00412 PSM_OrderPosition : LONGINT; 00413 PSM_ChannelSettingPosition : LONGINT; 00414 PSM_PatternPosition : LONGINT; 00415 PSM_SamplePosition : LONGINT; 00416 { *** perhaps there are some more infos in a larger header, 00417 but i have not decoded it and so it apears here NOT } 00418 END; 00419 00420 PPSM_Sample = ^TPSM_Sample; 00421 TPSM_Sample = RECORD 00422 PSM_SampleFileName : ARRAY[01..12] OF CHAR; 00423 PSM_SampleByte00 : BYTE; 00424 PSM_SampleName : ARRAY[01..22] OF CHAR; 00425 PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE; 00426 PSM_SamplePosition : LONGINT; 00427 PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE; 00428 PSM_SampleNumber : BYTE; 00429 PSM_SampleFlags : WORD; 00430 PSM_SampleLength : LONGINT; 00431 PSM_SampleLoopBegin : LONGINT; 00432 PSM_SampleLoopEnd : LONGINT; 00433 PSM_Unknown03 : BYTE; 00434 PSM_SampleVolume : BYTE; 00435 PSM_SampleC5Speed : WORD; 00436 END; 00437 00438 PPSM_SampleList = ^TPSM_SampleList; 00439 TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample; 00440 00441 PPSM_Order = ^TPSM_Order; 00442 TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE; 00443 00444 PPSM_ChannelSettings = ^TPSM_ChannelSettings; 00445 TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE; 00446 00447 CONST 00448 PSM_NotesInPattern : BYTE = $00; 00449 PSM_ChannelInPattern : BYTE = $00; 00450 00451 CONST 00452 c_PSM_SetSpeed = 60; 00453 00454 FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT; 00455 BEGIN 00456 END; 00457 00458 PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD); 00459 VAR 00460 Witz : ARRAY[00..04] OF WORD; 00461 I1,I2 : WORD; 00462 I3,I4 : WORD; 00463 TopicalByte : ^BYTE; 00464 Pattern : PUnpackedPattern; 00465 ChannelP : BYTE; 00466 NoteP : BYTE; 00467 InfoByte : BYTE; 00468 CodeByte : BYTE; 00469 InfoWord : WORD; 00470 Effect : BYTE; 00471 Opperand : BYTE; 00472 Panning : BYTE; 00473 Volume : BYTE; 00474 PrevInfo : BYTE; 00475 InfoIndex : BYTE; 00476 BEGIN 00477 Pattern := @Destination; 00478 TopicalByte := @Source; 00479 { *** Initialize patttern } 00480 FOR I2 := 0 TO c_Maximum_NoteIndex DO 00481 FOR I3 := 0 TO c_Maximum_ChannelIndex DO 00482 BEGIN 00483 Pattern^[I2,I3,c_Pattern_NoteIndex] := $FF; 00484 Pattern^[I2,I3,c_Pattern_SampleIndex] := $00; 00485 Pattern^[I2,I3,c_Pattern_VolumeIndex] := $FF; 00486 Pattern^[I2,I3,c_Pattern_PanningIndex] := $FF; 00487 Pattern^[I2,I3,c_Pattern_EffectIndex] := $00; 00488 Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00; 00489 END; 00490 { *** Byte-pointer on first pattern-entry } 00491 ChannelP := $00; 00492 NoteP := $00; 00493 InfoByte := $00; 00494 PrevInfo := $00; 00495 InfoIndex := $02; 00496 { *** read notes in pattern } 00497 PSM_NotesInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); 00498 PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); 00499 { *** unpack pattern } 00500 WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO 00501 BEGIN 00502 { *** Read info-byte } 00503 InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); 00504 IF InfoByte <> $00 THEN 00505 BEGIN 00506 ChannelP := InfoByte AND $0F; 00507 IF InfoByte AND 128 = 128 THEN { note and sample } 00508 BEGIN 00509 { *** read note } 00510 CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); 00511 DEC(CodeByte); 00512 CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2; 00513 Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte; 00514 { *** read sample } 00515 CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); 00516 Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte; 00517 END; 00518 IF InfoByte AND 64 = 64 THEN { Volume } 00519 BEGIN 00520 CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); 00521 Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte; 00522 END; 00523 IF InfoByte AND 32 = 32 THEN { effect AND opperand } 00524 BEGIN 00525 Effect := TopicalByte^; INC(TopicalByte); DEC(PatternLength); 00526 Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength); 00527 CASE Effect OF 00528 c_PSM_SetSpeed: 00529 BEGIN 00530 Effect := c_I_Set_Speed; 00531 END; 00532 ELSE 00533 BEGIN 00534 Effect := c_I_NoEffect; 00535 Opperand := $00; 00536 END; 00537 END; 00538 Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex] := Effect; 00539 Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand; 00540 END; 00541 END ELSE INC(NoteP); 00542 END; 00543 END; 00544 00545 PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD); 00546 { *** caution : Module has to be inited before!!!! } 00547 VAR 00548 Header : PPSM_Header; 00549 Sample : PPSM_SampleList; 00550 Order : PPSM_Order; 00551 ChannelSettings : PPSM_ChannelSettings; 00552 MultiPurposeBuffer : PByteArray; 00553 PatternBuffer : PUnpackedPattern; 00554 TopicalParaPointer : WORD; 00555 00556 InFile : FILE; 00557 I1,I2 : WORD; 00558 I3,I4 : WORD; 00559 TempW : WORD; 00560 TempB : BYTE; 00561 TempP : PByteArray; 00562 TempI : INTEGER; 00563 { *** copy-vars for loop-extension } 00564 CopySource : LONGINT; 00565 CopyDestination : LONGINT; 00566 CopyLength : LONGINT; 00567 BEGIN 00568 { *** try to open file } 00569 ASSIGN(InFile,FileName); 00570 {$I-} 00571 RESET(InFile,1); 00572 {$I+} 00573 IF IORESULT <> $00 THEN 00574 BEGIN 00575 EXIT; 00576 END; 00577 {$I-} 00578 { *** seek start of module } 00579 IF FILESIZE(InFile) < FilePosition THEN 00580 BEGIN 00581 EXIT; 00582 END; 00583 SEEK(InFile,FilePosition); 00584 { *** look for enough memory for temporary variables } 00585 IF MEMAVAIL < SIZEOF(TPSM_Header) + SIZEOF(TPSM_SampleList) + 00586 SIZEOF(TPSM_Order) + SIZEOF(TPSM_ChannelSettings) + 00587 SIZEOF(TByteArray) + SIZEOF(TUnpackedPattern) 00588 THEN 00589 BEGIN 00590 EXIT; 00591 END; 00592 { *** init dynamic variables } 00593 NEW(Header); 00594 NEW(Sample); 00595 NEW(Order); 00596 NEW(ChannelSettings); 00597 NEW(MultiPurposeBuffer); 00598 NEW(PatternBuffer); 00599 { *** read header } 00600 BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header)); 00601 { *** test if this is a DSM-file } 00602 IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S') AND 00603 (Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN 00604 BEGIN 00605 ErrorCode := c_NoValidFileFormat; 00606 CLOSE(InFile); 00607 EXIT; 00608 END; 00609 { *** read order } 00610 SEEK(InFile,FilePosition + Header^.PSM_OrderPosition); 00611 BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength); 00612 { *** read channelsettings } 00613 SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition); 00614 BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings)); 00615 { *** read samplelist } 00616 SEEK(InFile,FilePosition + Header^.PSM_SamplePosition); 00617 BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample)); 00618 { *** copy header to intern NTMIK-structure } 00619 Module^.Module_Sign := 'MF'; 00620 Module^.Module_FileFormatVersion := $0100; 00621 Module^.Module_SampleNumber := Header^.PSM_SampleNumber; 00622 Module^.Module_PatternNumber := Header^.PSM_PatternNumber; 00623 Module^.Module_OrderLength := Header^.PSM_OrderLength; 00624 Module^.Module_ChannelNumber := Header^.PSM_ChannelNumber+1; 00625 Module^.Module_Initial_GlobalVolume := 64; 00626 Module^.Module_Initial_MasterVolume := $C0; 00627 Module^.Module_Initial_Speed := Header^.PSM_Speed; 00628 Module^.Module_Initial_Tempo := Header^.PSM_Tempo; 00629 { *** paragraph 01 start } 00630 Module^.Module_Flags := c_Module_Flags_ZeroVolume * BYTE(1) + 00631 c_Module_Flags_Stereo * BYTE(1) + 00632 c_Module_Flags_ForceAmigaLimits * BYTE(0) + 00633 c_Module_Flags_Panning * BYTE(1) + 00634 c_Module_Flags_Surround * BYTE(1) + 00635 c_Module_Flags_QualityMixing * BYTE(1) + 00636 c_Module_Flags_FastVolumeSlides * BYTE(0) + 00637 c_Module_Flags_SpecialCustomData * BYTE(0) + 00638 c_Module_Flags_SongName * BYTE(1); 00639 I1 := $01; 00640 WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO 00641 BEGIN 00642 Module^.Module_Name[I1] := Header^.PSM_SongName[I1]; 00643 INC(I1); 00644 END; 00645 Module^.Module_Name[c_Module_SongNameLength] := #00; 00646 { *** Init channelsettings } 00647 FOR I1 := 0 TO c_Maximum_ChannelIndex DO 00648 BEGIN 00649 IF I1 < Header^.PSM_ChannelUsed THEN 00650 BEGIN 00651 { *** channel enabled } 00652 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64; 00653 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := (ChannelSettings^[I1]) * $08; 00654 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) + 00655 c_ChannelSettings_Code_ChannelEnabled * BYTE(1) + 00656 c_ChannelSettings_Code_ChannelDigital * BYTE(1); 00657 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := 00658 c_ChannelSettings_Controls_EnhancedMode * BYTE(1) + 00659 c_ChannelSettings_Controls_SurroundMode * BYTE(0); 00660 END 00661 ELSE 00662 BEGIN 00663 { *** channel disabled } 00664 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00; 00665 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := $00; 00666 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := $00; 00667 Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := $00; 00668 END; 00669 END; 00670 { *** init and copy order } 00671 FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF); 00672 MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength); 00673 { *** read pattern } 00674 SEEK(InFile,FilePosition + Header^.PSM_PatternPosition); 00675 NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1; 00676 FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO 00677 BEGIN 00678 NTMIK_LoadPatternProcedure; 00679 { *** read length } 00680 BLOCKREAD(InFile,TempW,2); 00681 { *** read pattern } 00682 BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2); 00683 { *** unpack pattern and set notes per channel to 64 } 00684 PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW); 00685 NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern); 00686 TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00]; 00687 GETMEM(Module^.Module_PatternPointer^[I1],TempW); 00688 MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW); 00689 { *** next pattern } 00690 END; 00691 { *** read samples } 00692 NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber; 00693 FOR I1 := 1 TO Header^.PSM_SampleNumber DO 00694 BEGIN 00695 NTMIK_LoadSampleProcedure; 00696 { *** get index for sample } 00697 I3 := Sample^[I1].PSM_SampleNumber; 00698 { *** clip PSM-sample } 00699 IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength 00700 THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength; 00701 { *** init intern sample } 00702 NEW(Module^.Module_SamplePointer^[I3]); 00703 FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00); 00704 FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32); 00705 FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32); 00706 { *** copy informations to intern sample } 00707 I2 := $01; 00708 WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO 00709 BEGIN 00710 Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2]; 00711 INC(I2); 00712 END; 00713 Module^.Module_SamplePointer^[I3]^.Sample_Sign := 'DF'; 00714 Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100; 00715 Module^.Module_SamplePointer^[I3]^.Sample_Position := $00000000; 00716 Module^.Module_SamplePointer^[I3]^.Sample_Selector := $0000; 00717 Module^.Module_SamplePointer^[I3]^.Sample_Volume := Sample^[I1].PSM_SampleVolume; 00718 Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter := $00; 00719 Module^.Module_SamplePointer^[I3]^.Sample_C5Speed := Sample^[I1].PSM_SampleC5Speed; 00720 Module^.Module_SamplePointer^[I3]^.Sample_Length := Sample^[I1].PSM_SampleLength; 00721 Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin := Sample^[I1].PSM_SampleLoopBegin; 00722 Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd := Sample^[I1].PSM_SampleLoopEnd; 00723 { *** now it's time for the flags } 00724 Module^.Module_SamplePointer^[I3]^.Sample_Flags := 00725 c_Sample_Flags_DigitalSample * BYTE(1) + 00726 c_Sample_Flags_8BitSample * BYTE(1) + 00727 c_Sample_Flags_UnsignedSampleData * BYTE(1) + 00728 c_Sample_Flags_Packed * BYTE(0) + 00729 c_Sample_Flags_LoopCounter * BYTE(0) + 00730 c_Sample_Flags_SampleName * BYTE(1) + 00731 c_Sample_Flags_LoopActive * 00732 BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15)); 00733 { *** alloc memory for sample-data } 00734 E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector, 00735 Module^.Module_SamplePointer^[I3]^.Sample_Position, 00736 Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize); 00737 { *** read out data } 00738 EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector; 00739 EPT(TempP).p_Offset := $0000; 00740 SEEK(InFile,Sample^[I1].PSM_SamplePosition); 00741 E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length); 00742 { *** 'coz the samples are signed in a DSM-file -> PC-fy them } 00743 IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN 00744 BEGIN 00745 CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length; 00746 { *** decode sample } 00747 ASM 00748 DB 066h; MOV CX,WORD PTR CopyLength 00749 { *** load sample selector } 00750 MOV ES,WORD PTR TempP[00002h] 00751 DB 066h; XOR SI,SI 00752 DB 066h; XOR DI,DI 00753 XOR AH,AH 00754 { *** conert all bytes } 00755 @@MainLoop: 00756 DB 026h; DB 067h; LODSB 00757 ADD AL,AH 00758 MOV AH,AL 00759 DB 067h; STOSB 00760 DB 066h; LOOP @@MainLoop 00761 END; 00762 { *** make samples unsigned } 00763 ASM 00764 DB 066h; MOV CX,WORD PTR CopyLength 00765 { *** load sample selector } 00766 MOV ES,WORD PTR TempP[00002h] 00767 DB 066h; XOR SI,SI 00768 DB 066h; XOR DI,DI 00769 { *** conert all bytes } 00770 @@MainLoop: 00771 DB 026h; DB 067h; LODSB 00772 SUB AL,080h 00773 DB 067h; STOSB 00774 DB 066h; LOOP @@MainLoop 00775 END; 00776 { *** Create Loop-Extension } 00777 IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN 00778 BEGIN 00779 CopySource := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin; 00780 CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd; 00781 CopyLength := CopyDestination - CopySource; 00782 ASM 00783 { *** load sample-selector } 00784 MOV ES,WORD PTR TempP[00002h] 00785 DB 066h; MOV DI,WORD PTR CopyDestination 00786 { *** calculate number of full sample-loops to copy } 00787 XOR DX,DX 00788 MOV AX,c_LoopExtensionSize 00789 MOV BX,WORD PTR CopyLength 00790 DIV BX 00791 OR AX,AX 00792 JE @@NoFullLoop 00793 { *** copy some full-loops (size=bx) } 00794 MOV CX,AX 00795 @@InnerLoop: 00796 PUSH CX 00797 DB 066h; MOV SI,WORD PTR CopySource 00798 MOV CX,BX 00799 DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } 00800 POP CX 00801 LOOP @@InnerLoop 00802 @@NoFullLoop: 00803 { *** calculate number of rest-bytes to copy } 00804 DB 066h; MOV SI,WORD PTR CopySource 00805 MOV CX,DX 00806 DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } 00807 END; 00808 END 00809 ELSE 00810 BEGIN 00811 CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length; 00812 ASM 00813 { *** load sample-selector } 00814 MOV ES,WORD PTR TempP[00002h] 00815 DB 066h; MOV DI,WORD PTR CopyDestination 00816 { *** clear extension } 00817 MOV CX,c_LoopExtensionSize 00818 MOV AL,080h 00819 DB 0F3h; DB 067h,0AAh { REP STOS BYTE PTR ES:[EDI] } 00820 END; 00821 END; 00822 END; 00823 { *** next sample } 00824 END; 00825 { *** init period-ranges } 00826 NTMIK_MaximumPeriod := $0000D600 SHR 1; 00827 NTMIK_MinimumPeriod := $0000D600 SHR 8; 00828 { *** close file } 00829 CLOSE(InFile); 00830 { *** dispose all dynamic variables } 00831 DISPOSE(Header); 00832 DISPOSE(Sample); 00833 DISPOSE(Order); 00834 DISPOSE(ChannelSettings); 00835 DISPOSE(MultiPurposeBuffer); 00836 DISPOSE(PatternBuffer); 00837 { *** set errorcode to noerror } 00838 ErrorCode := c_NoError; 00839 END; 00840 00841 */ 00842
1.4.2