Logo Search packages:      
Sourcecode: freecraft version File versions  Download package

load_psm.c

/*
 * This source code is public domain.
 *
 * Authors: Olivier Lapicque <olivierl@jps.net>
 *
 *    $Id: load_psm.c,v 1.1 2001/11/07 23:36:13 johns Exp $
*/


///////////////////////////////////////////////////
//
// PSM module loader
//
///////////////////////////////////////////////////

#include "stdafx.h"
#include "sndfile.h"

//#define PSM_LOG

#define PSM_ID_NEW  0x204d5350
#define PSM_ID_OLD  0xfe4d5350
#define IFFID_FILE  0x454c4946
#define IFFID_TITL  0x4c544954
#define IFFID_SDFT  0x54464453
#define IFFID_PBOD  0x444f4250
#define IFFID_SONG  0x474e4f53
#define IFFID_PATT  0x54544150
#define IFFID_DSMP  0x504d5344
#define IFFID_OPLH  0x484c504f

#pragma pack(1)

00034 typedef struct _PSMCHUNK
{
    uint32_t id;
    uint32_t len;
    uint32_t listid;
} PSMCHUNK;

00041 typedef struct _PSMSONGHDR
{
    int8_t songname[8];             // "MAINSONG"
    uint8_t reserved1;
    uint8_t reserved2;
    uint8_t channels;
} PSMSONGHDR;

00049 typedef struct _PSMPATTERN
{
    uint32_t size;
    uint32_t name;
    uint16_t rows;
    uint16_t reserved1;
    uint8_t data[4];
} PSMPATTERN;

00058 typedef struct _PSMSAMPLE
{
    uint8_t flags;
    int8_t songname[8];
    uint32_t smpid;
    int8_t samplename[34];
    uint32_t reserved1;
    uint8_t reserved2;
    uint8_t insno;
    uint8_t reserved3;
    uint32_t length;
    uint32_t loopstart;
    uint32_t loopend;
    uint16_t reserved4;
    uint8_t defvol;
    uint32_t reserved5;
    uint32_t samplerate;
    uint8_t reserved6[19];
} PSMSAMPLE;

#pragma pack()

int CSoundFile_ReadPSM(CSoundFile * that, const uint8_t * lpStream, uint32_t dwMemLength)
//-----------------------------------------------------------
{
    PSMSONGHDR *pSong;
    PSMCHUNK *pfh;
    uint32_t dwMemPos;
    uint32_t dwSongPos;
    uint32_t smpnames[MAX_SAMPLES];
    uint32_t patptrs[MAX_PATTERNS];
    uint8_t samplemap[MAX_SAMPLES];
    unsigned nPatterns;
    unsigned i;

    // Chunk0: "PSM ",filesize,"FILE"
    if (dwMemLength < 256) {
      return 0;
    }
    pfh = (PSMCHUNK *) lpStream;
    if (pfh->id == PSM_ID_OLD) {
#ifdef PSM_LOG
      Log("Old PSM format not supported\n");
#endif
      return 0;
    }
    if ((pfh->id != PSM_ID_NEW) || (pfh->len + 12 > dwMemLength)
      || (pfh->listid != IFFID_FILE)) {
      return 0;
    }

    that->m_nType = MOD_TYPE_PSM;
    that->m_nChannels = 16;
    that->m_nSamples = 0;
    nPatterns = 0;
    dwMemPos = 12;
    dwSongPos = 0;
    for (i = 0; i < 16; i++) {
      that->ChnSettings[i].nPan =
          (((i & 3) == 1) || ((i & 3) == 2)) ? 0xC0 : 0x40;
    }
    while (dwMemPos + 8 < dwMemLength) {
      PSMCHUNK *pchunk = (PSMCHUNK *) (lpStream + dwMemPos);
      uint8_t *pdata;
      uint32_t len;

      if ((pchunk->len >= dwMemLength - 8)
          || (dwMemPos + pchunk->len + 8 > dwMemLength)) {
          break;
      }
      dwMemPos += 8;
      pdata = (uint8_t *) (lpStream + dwMemPos);
      len = pchunk->len;

      if (len)
          switch (pchunk->id) {
                // "TITL": Song title
            case IFFID_TITL:
                if (!pdata[0]) {
                  pdata++;
                  len--;
                }
                memcpy(that->m_szNames[0], pdata, (len > 31) ? 31 : len);
                that->m_szNames[0][31] = 0;
                break;
                // "PBOD": Pattern
            case IFFID_PBOD:
                if ((len >= 12) && (nPatterns < MAX_PATTERNS)) {
                  patptrs[nPatterns++] = dwMemPos - 8;
                }
                break;
                // "SONG": Song description
            case IFFID_SONG:
                if ((len >= sizeof(PSMSONGHDR) + 8) && (!dwSongPos)) {
                  dwSongPos = dwMemPos - 8;
                }
                break;
                // "DSMP": Sample Data
            case IFFID_DSMP:
                if ((len >= sizeof(PSMSAMPLE))
                  && (that->m_nSamples + 1 < MAX_SAMPLES)) {
                  MODINSTRUMENT *pins;
                  PSMSAMPLE *psmp;

                  that->m_nSamples++;
                  pins = &that->Ins[that->m_nSamples];
                  psmp = (PSMSAMPLE *) pdata;

                  smpnames[that->m_nSamples] = psmp->smpid;
                  memcpy(that->m_szNames[that->m_nSamples],
                      psmp->samplename, 31);
                  that->m_szNames[that->m_nSamples][31] = 0;
                  samplemap[that->m_nSamples - 1] =
                      (uint8_t) that->m_nSamples;
                  // Init sample
                  pins->nGlobalVol = 0x40;
                  pins->nC4Speed = psmp->samplerate;
                  pins->nLength = psmp->length;
                  pins->nLoopStart = psmp->loopstart;
                  pins->nLoopEnd = psmp->loopend;
                  pins->nPan = 128;
                  pins->nVolume = (psmp->defvol + 1) * 2;
                  pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0;
                  if (pins->nLoopStart > 0)
                      pins->nLoopStart--;
                  // Point to sample data
                  pdata += 0x60;
                  len -= 0x60;
                  // Load sample data
                  if ((pins->nLength > 3) && (len > 3)) {
                      CSoundFile_ReadSample(that,pins, RS_PCM8D,
                        (const char *)pdata, len);
                  } else {
                      pins->nLength = 0;
                  }
                }
                break;
#if 0
            default:
            {
                int8_t s[8], s2[64];

                *(uint32_t *) s = pchunk->id;
                s[4] = 0;
                wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len,
                  dwMemPos);
                OutputDebugString(s2);
            }
#endif
          }
      dwMemPos += pchunk->len;
    }

    // Step #1: convert song structure
    pSong = (PSMSONGHDR *) (lpStream + dwSongPos + 8);

    if (!dwSongPos || pSong->channels < 2 || pSong->channels > 32) {
      return 1;
    }
    that->m_nChannels = pSong->channels;
    // Valid song header -> convert attached chunks
    {
      uint32_t dwSongEnd =
          dwSongPos + 8 + *(uint32_t *) (lpStream + dwSongPos + 4);

      dwMemPos = dwSongPos + 8 + 11;      // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR)
      while (dwMemPos + 8 < dwSongEnd) {
          PSMCHUNK *pchunk = (PSMCHUNK *) (lpStream + dwMemPos);
          uint8_t *pdata;
          uint32_t len;

          dwMemPos += 8;
          if ((pchunk->len > dwSongEnd)
            || (dwMemPos + pchunk->len > dwSongEnd))
            break;
          pdata = (uint8_t *) (lpStream + dwMemPos);
          len = pchunk->len;

          switch (pchunk->id) {
            case IFFID_OPLH:
                if (len >= 0x20) {
                  unsigned pos = len - 3;
                  unsigned iOrd;

                  while (pos > 5) {
                      uint32_t dwName;
                      int bFound = 0;

                      pos -= 5;
                      dwName = *(uint32_t *) (pdata + pos);

                      for (i = 0; i < nPatterns; i++) {
                        uint32_t dwPatName =
                            ((PSMPATTERN *) (lpStream + patptrs[i] +
                              8))->name;
                        if (dwName == dwPatName) {
                            bFound = 1;
                            break;
                        }
                      }
                      if ((!bFound) && (pdata[pos + 1] > 0)
                        && (pdata[pos + 1] <= 0x10)
                        && (pdata[pos + 3] > 0x40)
                        && (pdata[pos + 3] < 0xC0)) {
                        that->m_nDefaultSpeed = pdata[pos + 1];
                        that->m_nDefaultTempo = pdata[pos + 3];
                        break;
                      }
                  }
                  iOrd = 0;

                  while ((pos + 5 < len) && (iOrd < MAX_ORDERS)) {
                      uint32_t dwName = *(uint32_t *) (pdata + pos);

                      for (i = 0; i < nPatterns; i++) {
                        uint32_t dwPatName =
                            ((PSMPATTERN *) (lpStream + patptrs[i] +
                              8))->name;
                        if (dwName == dwPatName) {
                            that->Order[iOrd++] = i;
                            break;
                        }
                      }
                      pos += 5;
                  }
                }
                break;
          }
          dwMemPos += pchunk->len;
      }
    }

    // Step #2: convert patterns
    for (i = 0; i < nPatterns; i++) {
      PSMPATTERN *pPsmPat = (PSMPATTERN *) (lpStream + patptrs[i] + 8);
      uint32_t len = *(uint32_t *) (lpStream + patptrs[i] + 4) - 12;
      unsigned nRows = pPsmPat->rows;
      MODCOMMAND *m;
      uint8_t *p;
      unsigned pos;
      unsigned row;
      unsigned oldch;
      int bNewRow;

      if (len > pPsmPat->size) {
          len = pPsmPat->size;
      }
      if (nRows < 64 || nRows > 256) {
          nRows = 64;
      }
      that->PatternSize[i] = nRows;
      if ((that->Patterns[i] =
            CSoundFile_AllocatePattern(nRows, that->m_nChannels)) == NULL) {
          break;
      }
      m = that->Patterns[i];
      p = pPsmPat->data;
      pos = 0;
      row = 0;
      oldch = 0;
      bNewRow = 0;

#ifdef PSM_LOG
      Log("Pattern %d at offset 0x%04X\n", i,
          (uint32_t) (p - (uint8_t *) lpStream));
#endif
      while ((row < nRows) && (pos + 1 < len)) {
          unsigned flags = p[pos++];
          unsigned ch = p[pos++];

#ifdef PSM_LOG
          //Log("flags+ch: %02X.%02X\n", flags, ch);
#endif
          if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow) */ ) {
            if ((pos + 1 < len) && (!(p[pos] & 0x0f))
                && (p[pos + 1] < that->m_nChannels)) {
#ifdef PSM_LOG
                //if (!i) Log("Continuing on new row\n");
#endif
                row++;
                m += that->m_nChannels;
                oldch = ch;
                continue;
            }
          }
          if ((pos >= len) || (row >= nRows))
            break;
          if (!(flags & 0xf0)) {
#ifdef PSM_LOG
            //if (!i) Log("EOR(%d): %02X.%02X\n", row, p[pos], p[pos+1]);
#endif
            row++;
            m += that->m_nChannels;
            bNewRow = 1;
            oldch = ch;
            continue;
          }
          bNewRow = 0;
          if (ch >= that->m_nChannels) {
#ifdef PSM_LOG
            if (!i)
                Log("Invalid channel row=%d (0x%02X.0x%02X)\n", row, flags,
                  ch);
#endif
            ch = 0;
          }
          // Note + Instr
          if ((flags & 0x40) && (pos + 1 < len)) {
            unsigned note = p[pos++];
            unsigned nins = p[pos++];

#ifdef PSM_LOG
            //if (!i) Log("note+ins: %02X.%02X\n", note, nins);
            if ((!i) && (nins >= that->m_nSamples))
                Log("WARNING: invalid instrument number (%d)\n", nins);
#endif
            if ((note) && (note < 0x80))
                note = (note >> 4) * 12 + (note & 0x0f) + 12 + 1;
            m[ch].instr = samplemap[nins];
            m[ch].note = note;
          }
          // Volume
          if ((flags & 0x20) && (pos < len)) {
            m[ch].volcmd = VOLCMD_VOLUME;
            m[ch].vol = p[pos++] / 2;
          }
          // Effect
          if ((flags & 0x10) && (pos + 1 < len)) {
            unsigned command = p[pos++];
            unsigned param = p[pos++];

            // Convert effects
            switch (command) {
                  // 01: fine volslide up
                case 0x01:
                  command = CMD_VOLUMESLIDE;
                  param |= 0x0f;
                  break;
                  // 04: fine volslide down
                case 0x04:
                  command = CMD_VOLUMESLIDE;
                  param >>= 4;
                  param |= 0xf0;
                  break;
                  // 0C: portamento up
                case 0x0C:
                  command = CMD_PORTAMENTOUP;
                  param = (param + 1) / 2;
                  break;
                  // 0E: portamento down
                case 0x0E:
                  command = CMD_PORTAMENTODOWN;
                  param = (param + 1) / 2;
                  break;
                  // 33: Position Jump
                case 0x33:
                  command = CMD_POSITIONJUMP;
                  break;
                  // 34: Pattern break
                case 0x34:
                  command = CMD_PATTERNBREAK;
                  break;
                  // 3D: speed
                case 0x3D:
                  command = CMD_SPEED;
                  break;
                  // 3E: tempo
                case 0x3E:
                  command = CMD_TEMPO;
                  break;
                  // Unknown
                default:
#ifdef PSM_LOG
                  Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", i, row, ch, command, param);
#endif
                  command = param = 0;
            }
            m[ch].command = (uint8_t) command;
            m[ch].param = (uint8_t) param;
          }
          oldch = ch;
      }
#ifdef PSM_LOG
      if (pos < len) {
          Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n",
            i, row, nRows, pPsmPat->rows, pPsmPat->size, len - pos);
      }
#endif
    }

    // Done (finally!)
    return 1;
}

//////////////////////////////////////////////////////////////
//
// PSM Old Format
//

/*

CONST
  c_PSM_MaxOrder   = $FF;
  c_PSM_MaxSample  = $FF;
  c_PSM_MaxChannel = $0F;

 TYPE
  PPSM_Header = ^TPSM_Header;
  TPSM_Header = RECORD
             PSM_Sign             : ARRAY[01..04] OF CHAR; { PSM + #254 }
             PSM_SongName               : ARRAY[01..58] OF CHAR;
             PSM_Byte00           : BYTE;
             PSM_Byte1A           : BYTE;
             PSM_Unknown00              : BYTE;
             PSM_Unknown01              : BYTE;
             PSM_Unknown02              : BYTE;
             PSM_Speed            : BYTE;
             PSM_Tempo            : BYTE;
             PSM_Unknown03              : BYTE;
             PSM_Unknown04              : WORD;
             PSM_OrderLength      : WORD;
             PSM_PatternNumber          : WORD;
             PSM_SampleNumber     : WORD;
             PSM_ChannelNumber          : WORD;
             PSM_ChannelUsed      : WORD;
             PSM_OrderPosition          : LONGINT;
             PSM_ChannelSettingPosition : LONGINT;
             PSM_PatternPosition        : LONGINT;
             PSM_SamplePosition         : LONGINT;
            { *** perhaps there are some more infos in a larger header,
                  but i have not decoded it and so it apears here NOT }
            END;

  PPSM_Sample = ^TPSM_Sample;
  TPSM_Sample = RECORD
             PSM_SampleFileName  : ARRAY[01..12] OF CHAR;
             PSM_SampleByte00    : BYTE;
             PSM_SampleName        : ARRAY[01..22] OF CHAR;
             PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE;
             PSM_SamplePosition  : LONGINT;
             PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE;
             PSM_SampleNumber    : BYTE;
             PSM_SampleFlags     : WORD;
             PSM_SampleLength    : LONGINT;
             PSM_SampleLoopBegin : LONGINT;
             PSM_SampleLoopEnd   : LONGINT;
             PSM_Unknown03         : BYTE;
             PSM_SampleVolume    : BYTE;
             PSM_SampleC5Speed   : WORD;
            END;

  PPSM_SampleList = ^TPSM_SampleList;
  TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample;

  PPSM_Order = ^TPSM_Order;
  TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE;

  PPSM_ChannelSettings = ^TPSM_ChannelSettings;
  TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE;

 CONST
  PSM_NotesInPattern   : BYTE = $00;
  PSM_ChannelInPattern : BYTE = $00;

 CONST
  c_PSM_SetSpeed = 60;

 FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT;
  BEGIN
  END;

 PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD);
  VAR
   Witz : ARRAY[00..04] OF WORD;
   I1,I2    : WORD;
   I3,I4    : WORD;
   TopicalByte    : ^BYTE;
   Pattern  : PUnpackedPattern;
   ChannelP : BYTE;
   NoteP    : BYTE;
   InfoByte : BYTE;
   CodeByte : BYTE;
   InfoWord : WORD;
   Effect   : BYTE;
   Opperand : BYTE;
   Panning  : BYTE;
   Volume   : BYTE;
   PrevInfo : BYTE;
   InfoIndex      : BYTE;
  BEGIN
   Pattern     := @Destination;
   TopicalByte := @Source;
  { *** Initialize patttern }
   FOR I2 := 0 TO c_Maximum_NoteIndex DO
    FOR I3 := 0 TO c_Maximum_ChannelIndex DO
     BEGIN
      Pattern^[I2,I3,c_Pattern_NoteIndex]     := $FF;
      Pattern^[I2,I3,c_Pattern_SampleIndex]   := $00;
      Pattern^[I2,I3,c_Pattern_VolumeIndex]   := $FF;
      Pattern^[I2,I3,c_Pattern_PanningIndex]  := $FF;
      Pattern^[I2,I3,c_Pattern_EffectIndex]   := $00;
      Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00;
     END;
  { *** Byte-pointer on first pattern-entry }
   ChannelP    := $00;
   NoteP       := $00;
   InfoByte    := $00;
   PrevInfo    := $00;
   InfoIndex   := $02;
  { *** read notes in pattern }
   PSM_NotesInPattern   := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
   PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
  { *** unpack pattern }
   WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO
    BEGIN
    { *** Read info-byte }
     InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
     IF InfoByte <> $00 THEN
      BEGIN
       ChannelP := InfoByte AND $0F;
       IF InfoByte AND 128 = 128 THEN { note and sample }
      BEGIN
      { *** read note }
       CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
       DEC(CodeByte);
       CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2;
       Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte;
      { *** read sample }
       CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
       Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte;
      END;
       IF InfoByte AND 64 = 64 THEN { Volume }
      BEGIN
       CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
       Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte;
      END;
       IF InfoByte AND 32 = 32 THEN { effect AND opperand }
      BEGIN
       Effect       := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
       Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
       CASE Effect OF
        c_PSM_SetSpeed:
         BEGIN
          Effect := c_I_Set_Speed;
         END;
        ELSE
         BEGIN
          Effect   := c_I_NoEffect;
          Opperand := $00;
         END;
       END;
       Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex]   := Effect;
       Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand;
      END;
      END ELSE INC(NoteP);
    END;
  END;

 PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD);
 { *** caution : Module has to be inited before!!!! }
  VAR
   Header         : PPSM_Header;
   Sample         : PPSM_SampleList;
   Order          : PPSM_Order;
   ChannelSettings    : PPSM_ChannelSettings;
   MultiPurposeBuffer : PByteArray;
   PatternBuffer      : PUnpackedPattern;
   TopicalParaPointer : WORD;

   InFile : FILE;
   I1,I2  : WORD;
   I3,I4  : WORD;
   TempW  : WORD;
   TempB  : BYTE;
   TempP  : PByteArray;
   TempI  : INTEGER;
  { *** copy-vars for loop-extension }
   CopySource        : LONGINT;
   CopyDestination : LONGINT;
   CopyLength        : LONGINT;
  BEGIN
  { *** try to open file }
   ASSIGN(InFile,FileName);
{$I-}
   RESET(InFile,1);
{$I+}
   IF IORESULT <> $00 THEN
    BEGIN
     EXIT;
    END;
{$I-}
  { *** seek start of module }
   IF FILESIZE(InFile) < FilePosition THEN
    BEGIN
     EXIT;
    END;
   SEEK(InFile,FilePosition);
  { *** look for enough memory for temporary variables }
   IF MEMAVAIL < SIZEOF(TPSM_Header)         + SIZEOF(TPSM_SampleList) +
             SIZEOF(TPSM_Order)        + SIZEOF(TPSM_ChannelSettings) +
             SIZEOF(TByteArray)        + SIZEOF(TUnpackedPattern)
   THEN
    BEGIN
     EXIT;
    END;
  { *** init dynamic variables }
   NEW(Header);
   NEW(Sample);
   NEW(Order);
   NEW(ChannelSettings);
   NEW(MultiPurposeBuffer);
   NEW(PatternBuffer);
  { *** read header }
   BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header));
  { *** test if this is a DSM-file }
   IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S')   AND
         (Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN
    BEGIN
     ErrorCode := c_NoValidFileFormat;
     CLOSE(InFile);
     EXIT;
    END;
  { *** read order }
   SEEK(InFile,FilePosition + Header^.PSM_OrderPosition);
   BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength);
  { *** read channelsettings }
   SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition);
   BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings));
  { *** read samplelist }
   SEEK(InFile,FilePosition + Header^.PSM_SamplePosition);
   BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample));
  { *** copy header to intern NTMIK-structure }
   Module^.Module_Sign               := 'MF';
   Module^.Module_FileFormatVersion    := $0100;
   Module^.Module_SampleNumber             := Header^.PSM_SampleNumber;
   Module^.Module_PatternNumber            := Header^.PSM_PatternNumber;
   Module^.Module_OrderLength        := Header^.PSM_OrderLength;
   Module^.Module_ChannelNumber            := Header^.PSM_ChannelNumber+1;
   Module^.Module_Initial_GlobalVolume := 64;
   Module^.Module_Initial_MasterVolume := $C0;
   Module^.Module_Initial_Speed            := Header^.PSM_Speed;
   Module^.Module_Initial_Tempo            := Header^.PSM_Tempo;
{ *** paragraph 01 start }
   Module^.Module_Flags              := c_Module_Flags_ZeroVolume    * BYTE(1) +
                                c_Module_Flags_Stereo          * BYTE(1) +
                                c_Module_Flags_ForceAmigaLimits  * BYTE(0) +
                                c_Module_Flags_Panning         * BYTE(1) +
                                c_Module_Flags_Surround        * BYTE(1) +
                                c_Module_Flags_QualityMixing         * BYTE(1) +
                                c_Module_Flags_FastVolumeSlides  * BYTE(0) +
                                c_Module_Flags_SpecialCustomData * BYTE(0) +
                                c_Module_Flags_SongName        * BYTE(1);
   I1 := $01;
   WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO
    BEGIN
     Module^.Module_Name[I1] := Header^.PSM_SongName[I1];
     INC(I1);
    END;
   Module^.Module_Name[c_Module_SongNameLength] := #00;
  { *** Init channelsettings }
   FOR I1 := 0 TO c_Maximum_ChannelIndex DO
    BEGIN
     IF I1 < Header^.PSM_ChannelUsed THEN
      BEGIN
      { *** channel enabled }
       Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64;
       Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning      := (ChannelSettings^[I1]) * $08;
       Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code         := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) +
                                   c_ChannelSettings_Code_ChannelEnabled   * BYTE(1) +
                                   c_ChannelSettings_Code_ChannelDigital   * BYTE(1);
       Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls     :=
                                   c_ChannelSettings_Controls_EnhancedMode * BYTE(1) +
                                   c_ChannelSettings_Controls_SurroundMode * BYTE(0);
      END
     ELSE
      BEGIN
      { *** channel disabled }
       Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00;
       Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning      := $00;
       Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code         := $00;
       Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls     := $00;
      END;
    END;
  { *** init and copy order }
   FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF);
   MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength);
  { *** read pattern }
   SEEK(InFile,FilePosition + Header^.PSM_PatternPosition);
   NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1;
   FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO
    BEGIN
     NTMIK_LoadPatternProcedure;
    { *** read length }
     BLOCKREAD(InFile,TempW,2);
    { *** read pattern }
     BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2);
    { *** unpack pattern and set notes per channel to 64 }
     PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW);
     NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern);
     TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00];
     GETMEM(Module^.Module_PatternPointer^[I1],TempW);
     MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW);
    { *** next pattern }
    END;
  { *** read samples }
   NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber;
   FOR I1 := 1 TO Header^.PSM_SampleNumber DO
    BEGIN
     NTMIK_LoadSampleProcedure;
    { *** get index for sample }
     I3 := Sample^[I1].PSM_SampleNumber;
    { *** clip PSM-sample }
     IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength
     THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength;
    { *** init intern sample }
     NEW(Module^.Module_SamplePointer^[I3]);
     FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00);
     FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32);
     FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32);
    { *** copy informations to intern sample }
     I2 := $01;
     WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO
      BEGIN
       Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2];
       INC(I2);
      END;
     Module^.Module_SamplePointer^[I3]^.Sample_Sign          := 'DF';
     Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100;
     Module^.Module_SamplePointer^[I3]^.Sample_Position            := $00000000;
     Module^.Module_SamplePointer^[I3]^.Sample_Selector            := $0000;
     Module^.Module_SamplePointer^[I3]^.Sample_Volume        := Sample^[I1].PSM_SampleVolume;
     Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter   := $00;
     Module^.Module_SamplePointer^[I3]^.Sample_C5Speed             := Sample^[I1].PSM_SampleC5Speed;
     Module^.Module_SamplePointer^[I3]^.Sample_Length        := Sample^[I1].PSM_SampleLength;
     Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin     := Sample^[I1].PSM_SampleLoopBegin;
     Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd             := Sample^[I1].PSM_SampleLoopEnd;
    { *** now it's time for the flags }
     Module^.Module_SamplePointer^[I3]^.Sample_Flags :=
                         c_Sample_Flags_DigitalSample    * BYTE(1) +
                         c_Sample_Flags_8BitSample       * BYTE(1) +
                         c_Sample_Flags_UnsignedSampleData * BYTE(1) +
                         c_Sample_Flags_Packed           * BYTE(0) +
                         c_Sample_Flags_LoopCounter      * BYTE(0) +
                         c_Sample_Flags_SampleName       * BYTE(1) +
                         c_Sample_Flags_LoopActive       *
                       BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15));
    { *** alloc memory for sample-data }
     E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector,
            Module^.Module_SamplePointer^[I3]^.Sample_Position,
            Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize);
    { *** read out data }
     EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector;
     EPT(TempP).p_Offset   := $0000;
     SEEK(InFile,Sample^[I1].PSM_SamplePosition);
     E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length);
    { *** 'coz the samples are signed in a DSM-file -> PC-fy them }
     IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN
      BEGIN
       CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length;
      { *** decode sample }
       ASM
      DB 066h; MOV CX,WORD PTR CopyLength
       { *** load sample selector }
             MOV ES,WORD PTR TempP[00002h]
      DB 066h; XOR SI,SI
      DB 066h; XOR DI,DI
             XOR AH,AH
       { *** conert all bytes }
            @@MainLoop:
      DB 026h; DB 067h; LODSB
             ADD AL,AH
             MOV AH,AL
      DB 067h; STOSB
      DB 066h; LOOP @@MainLoop
       END;
      { *** make samples unsigned }
       ASM
      DB 066h; MOV CX,WORD PTR CopyLength
       { *** load sample selector }
             MOV ES,WORD PTR TempP[00002h]
      DB 066h; XOR SI,SI
      DB 066h; XOR DI,DI
       { *** conert all bytes }
            @@MainLoop:
      DB 026h; DB 067h; LODSB
             SUB AL,080h
      DB 067h; STOSB
      DB 066h; LOOP @@MainLoop
       END;
      { *** Create Loop-Extension }
       IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN
      BEGIN
       CopySource  := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin;
       CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd;
       CopyLength  := CopyDestination - CopySource;
       ASM
       { *** load sample-selector }
               MOV ES,WORD PTR TempP[00002h]
        DB 066h; MOV DI,WORD PTR CopyDestination
       { *** calculate number of full sample-loops to copy }
               XOR DX,DX
               MOV AX,c_LoopExtensionSize
               MOV BX,WORD PTR CopyLength
               DIV BX
               OR AX,AX
               JE @@NoFullLoop
       { *** copy some full-loops (size=bx) }
               MOV CX,AX
              @@InnerLoop:
               PUSH CX
        DB 066h; MOV SI,WORD PTR CopySource
               MOV CX,BX
        DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] }
               POP CX
               LOOP @@InnerLoop
              @@NoFullLoop:
       { *** calculate number of rest-bytes to copy }
        DB 066h; MOV SI,WORD PTR CopySource
               MOV CX,DX
        DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] }
       END;
      END
       ELSE
      BEGIN
       CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length;
       ASM
       { *** load sample-selector }
               MOV ES,WORD PTR TempP[00002h]
        DB 066h; MOV DI,WORD PTR CopyDestination
       { *** clear extension }
               MOV CX,c_LoopExtensionSize
               MOV AL,080h
        DB 0F3h; DB 067h,0AAh       { REP STOS BYTE PTR ES:[EDI] }
       END;
      END;
      END;
    { *** next sample }
    END;
  { *** init period-ranges }
   NTMIK_MaximumPeriod := $0000D600 SHR 1;
   NTMIK_MinimumPeriod := $0000D600 SHR 8;
  { *** close file }
   CLOSE(InFile);
  { *** dispose all dynamic variables }
   DISPOSE(Header);
   DISPOSE(Sample);
   DISPOSE(Order);
   DISPOSE(ChannelSettings);
   DISPOSE(MultiPurposeBuffer);
   DISPOSE(PatternBuffer);
  { *** set errorcode to noerror }
   ErrorCode := c_NoError;
  END;

*/

Generated by  Doxygen 1.6.0   Back to index