Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

load_psm.cpp

Go to the documentation of this file.
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 

Generated on Sat Nov 5 16:15:40 2005 for OPIE by  doxygen 1.4.2