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

load_s3m.cpp

/*
 * This source code is public domain.
 *
 * Authors: Olivier Lapicque <olivierl@jps.net>,
 *          Adam Goode       <adam@evdebs.org> (endian and char fixes for PPC)
*/

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

//#pragma warning(disable:4244)

extern WORD S3MFineTuneTable[16];

//////////////////////////////////////////////////////
// ScreamTracker S3M file support

typedef struct tagS3MSAMPLESTRUCT
{
      BYTE type;
      CHAR dosname[12];
      BYTE hmem;
      WORD memseg;
      DWORD length;
      DWORD loopbegin;
      DWORD loopend;
      BYTE vol;
      BYTE bReserved;
      BYTE pack;
      BYTE flags;
      DWORD finetune;
      DWORD dwReserved;
      WORD intgp;
      WORD int512;
      DWORD lastused;
      CHAR name[28];
      CHAR scrs[4];
} S3MSAMPLESTRUCT;


typedef struct tagS3MFILEHEADER
{
      CHAR name[28];
      BYTE b1A;
      BYTE type;
      WORD reserved1;
      WORD ordnum;
      WORD insnum;
      WORD patnum;
      WORD flags;
      WORD cwtv;
      WORD version;
      DWORD scrm; // "SCRM" = 0x4D524353
      BYTE globalvol;
      BYTE speed;
      BYTE tempo;
      BYTE mastervol;
      BYTE ultraclicks;
      BYTE panning_present;
      BYTE reserved2[8];
      WORD special;
      BYTE channels[32];
} S3MFILEHEADER;


void CSoundFile::S3MConvert(MODCOMMAND *m, BOOL bIT) const
//--------------------------------------------------------
{
      UINT command = m->command;
      UINT param = m->param;
      switch (command + 0x40)
      {
      case 'A':   command = CMD_SPEED; break;
      case 'B':   command = CMD_POSITIONJUMP; break;
      case 'C':   command = CMD_PATTERNBREAK; if (!bIT) param = (param >> 4) * 10 + (param & 0x0F); break;
      case 'D':   command = CMD_VOLUMESLIDE; break;
      case 'E':   command = CMD_PORTAMENTODOWN; break;
      case 'F':   command = CMD_PORTAMENTOUP; break;
      case 'G':   command = CMD_TONEPORTAMENTO; break;
      case 'H':   command = CMD_VIBRATO; break;
      case 'I':   command = CMD_TREMOR; break;
      case 'J':   command = CMD_ARPEGGIO; break;
      case 'K':   command = CMD_VIBRATOVOL; break;
      case 'L':   command = CMD_TONEPORTAVOL; break;
      case 'M':   command = CMD_CHANNELVOLUME; break;
      case 'N':   command = CMD_CHANNELVOLSLIDE; break;
      case 'O':   command = CMD_OFFSET; break;
      case 'P':   command = CMD_PANNINGSLIDE; break;
      case 'Q':   command = CMD_RETRIG; break;
      case 'R':   command = CMD_TREMOLO; break;
      case 'S':   command = CMD_S3MCMDEX; break;
      case 'T':   command = CMD_TEMPO; break;
      case 'U':   command = CMD_FINEVIBRATO; break;
      case 'V':   command = CMD_GLOBALVOLUME; break;
      case 'W':   command = CMD_GLOBALVOLSLIDE; break;
      case 'X':   command = CMD_PANNING8; break;
      case 'Y':   command = CMD_PANBRELLO; break;
      case 'Z':   command = CMD_MIDI; break;
      default:    command = 0;
      }
      m->command = command;
      m->param = param;
}


void CSoundFile::S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const
//---------------------------------------------------------------------
{
      UINT command = *pcmd;
      UINT param = *pprm;
      switch(command)
      {
      case CMD_SPEED:                     command = 'A'; break;
      case CMD_POSITIONJUMP:        command = 'B'; break;
      case CMD_PATTERNBREAK:        command = 'C'; if (!bIT) param = ((param / 10) << 4) + (param % 10); break;
      case CMD_VOLUMESLIDE:         command = 'D'; break;
      case CMD_PORTAMENTODOWN:      command = 'E'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break;
      case CMD_PORTAMENTOUP:        command = 'F'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break;
      case CMD_TONEPORTAMENTO:      command = 'G'; break;
      case CMD_VIBRATO:             command = 'H'; break;
      case CMD_TREMOR:              command = 'I'; break;
      case CMD_ARPEGGIO:                  command = 'J'; break;
      case CMD_VIBRATOVOL:          command = 'K'; break;
      case CMD_TONEPORTAVOL:        command = 'L'; break;
      case CMD_CHANNELVOLUME:       command = 'M'; break;
      case CMD_CHANNELVOLSLIDE:     command = 'N'; break;
      case CMD_OFFSET:              command = 'O'; break;
      case CMD_PANNINGSLIDE:        command = 'P'; break;
      case CMD_RETRIG:              command = 'Q'; break;
      case CMD_TREMOLO:             command = 'R'; break;
      case CMD_S3MCMDEX:                  command = 'S'; break;
      case CMD_TEMPO:                     command = 'T'; break;
      case CMD_FINEVIBRATO:         command = 'U'; break;
      case CMD_GLOBALVOLUME:        command = 'V'; break;
      case CMD_GLOBALVOLSLIDE:      command = 'W'; break;
      case CMD_PANNING8:                  
            command = 'X';
            if ((bIT) && (m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM))
            {
                  if (param == 0xA4) { command = 'S'; param = 0x91; }   else
                  if (param <= 0x80) { param <<= 1; if (param > 255) param = 255; } else
                  command = param = 0;
            } else
            if ((!bIT) && ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM)))
            {
                  param >>= 1;
            }
            break;
      case CMD_PANBRELLO:                 command = 'Y'; break;
      case CMD_MIDI:                      command = 'Z'; break;
      case CMD_XFINEPORTAUPDOWN:
            if (param & 0x0F) switch(param & 0xF0)
            {
            case 0x10:  command = 'F'; param = (param & 0x0F) | 0xE0; break;
            case 0x20:  command = 'E'; param = (param & 0x0F) | 0xE0; break;
            case 0x90:  command = 'S'; break;
            default:    command = param = 0;
            } else command = param = 0;
            break;
      case CMD_MODCMDEX:
            command = 'S';
            switch(param & 0xF0)
            {
            case 0x00:  command = param = 0; break;
            case 0x10:  command = 'F'; param |= 0xF0; break;
            case 0x20:  command = 'E'; param |= 0xF0; break;
            case 0x30:  param = (param & 0x0F) | 0x10; break;
            case 0x40:  param = (param & 0x0F) | 0x30; break;
            case 0x50:  param = (param & 0x0F) | 0x20; break;
            case 0x60:  param = (param & 0x0F) | 0xB0; break;
            case 0x70:  param = (param & 0x0F) | 0x40; break;
            case 0x90:  command = 'Q'; param &= 0x0F; break;
            case 0xA0:  if (param & 0x0F) { command = 'D'; param = (param << 4) | 0x0F; } else command=param=0; break;
            case 0xB0:  if (param & 0x0F) { command = 'D'; param |= 0xF0; } else command=param=0; break;
            }
            break;
      default:    command = param = 0;
      }
      command &= ~0x40;
      *pcmd = command;
      *pprm = param;
}


BOOL CSoundFile::ReadS3M(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
      UINT insnum,patnum,nins,npat;
      DWORD insfile[128];
      WORD ptr[256];
      BYTE s[1024];
      DWORD dwMemPos;
      BYTE insflags[128], inspack[128];
      S3MFILEHEADER psfh = *(S3MFILEHEADER *)lpStream;

      psfh.reserved1 = bswapLE16(psfh.reserved1);
      psfh.ordnum = bswapLE16(psfh.ordnum);
      psfh.insnum = bswapLE16(psfh.insnum);
      psfh.patnum = bswapLE16(psfh.patnum);
      psfh.flags = bswapLE16(psfh.flags);
      psfh.cwtv = bswapLE16(psfh.cwtv);
      psfh.version = bswapLE16(psfh.version);
      psfh.scrm = bswapLE32(psfh.scrm);
      psfh.special = bswapLE16(psfh.special);

      if ((!lpStream) || (dwMemLength <= sizeof(S3MFILEHEADER)+sizeof(S3MSAMPLESTRUCT)+64)) return FALSE;
      if (psfh.scrm != 0x4D524353) return FALSE;
      dwMemPos = 0x60;
      m_nType = MOD_TYPE_S3M;
      memset(m_szNames,0,sizeof(m_szNames));
      memcpy(m_szNames[0], psfh.name, 28);
      // Speed
      m_nDefaultSpeed = psfh.speed;
      if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 6;
      if (m_nDefaultSpeed > 0x1F) m_nDefaultSpeed = 0x1F;
      // Tempo
      m_nDefaultTempo = psfh.tempo;
      if (m_nDefaultTempo < 40) m_nDefaultTempo = 40;
      if (m_nDefaultTempo > 240) m_nDefaultTempo = 240;
      // Global Volume
      m_nDefaultGlobalVolume = psfh.globalvol << 2;
      if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256;
      m_nSongPreAmp = psfh.mastervol & 0x7F;
      // Channels
      m_nChannels = 4;
      for (UINT ich=0; ich<32; ich++)
      {
            ChnSettings[ich].nPan = 128;
            ChnSettings[ich].nVolume = 64;

            ChnSettings[ich].dwFlags = CHN_MUTE;
            if (psfh.channels[ich] != 0xFF)
            {
                  m_nChannels = ich+1;
                  UINT b = psfh.channels[ich] & 0x0F;
                  ChnSettings[ich].nPan = (b & 8) ? 0xC0 : 0x40;
                  ChnSettings[ich].dwFlags = 0;
            }
      }
      if (m_nChannels < 4) m_nChannels = 4;
      if ((psfh.cwtv < 0x1320) || (psfh.flags & 0x40)) m_dwSongFlags |= SONG_FASTVOLSLIDES;
      // Reading pattern order
      UINT iord = psfh.ordnum;
      if (iord<1) iord = 1;
      if (iord > MAX_ORDERS) iord = MAX_ORDERS;
      if (iord)
      {
            memcpy(Order, lpStream+dwMemPos, iord);
            dwMemPos += iord;
      }
      if ((iord & 1) && (lpStream[dwMemPos] == 0xFF)) dwMemPos++;
      // Reading file pointers
      insnum = nins = psfh.insnum;
      if (insnum >= MAX_SAMPLES) insnum = MAX_SAMPLES-1;
      m_nSamples = insnum;
      patnum = npat = psfh.patnum;
      if (patnum > MAX_PATTERNS) patnum = MAX_PATTERNS;
      memset(ptr, 0, sizeof(ptr));
      if (nins+npat)
      {
            memcpy(ptr, lpStream+dwMemPos, 2*(nins+npat));
            dwMemPos += 2*(nins+npat);
            for (UINT j = 0; j < (nins+npat); ++j) {
                    ptr[j] = bswapLE16(ptr[j]);
            }
            if (psfh.panning_present == 252)
            {
                  const BYTE *chnpan = lpStream+dwMemPos;
                  for (UINT i=0; i<32; i++) if (chnpan[i] & 0x20)
                  {
                        ChnSettings[i].nPan = ((chnpan[i] & 0x0F) << 4) + 8;
                  }
            }
      }
      if (!m_nChannels) return TRUE;
      // Reading instrument headers
      memset(insfile, 0, sizeof(insfile));
      for (UINT iSmp=1; iSmp<=insnum; iSmp++)
      {
            UINT nInd = ((DWORD)ptr[iSmp-1])*16;
            if ((!nInd) || (nInd + 0x50 > dwMemLength)) continue;
            memcpy(s, lpStream+nInd, 0x50);
            memcpy(Ins[iSmp].name, s+1, 12);
            insflags[iSmp-1] = s[0x1F];
            inspack[iSmp-1] = s[0x1E];
            s[0x4C] = 0;
            lstrcpy(m_szNames[iSmp], (LPCSTR)&s[0x30]);
            if ((s[0]==1) && (s[0x4E]=='R') && (s[0x4F]=='S'))
            {
                  UINT j = bswapLE32(*((LPDWORD)(s+0x10)));
                  if (j > MAX_SAMPLE_LENGTH) j = MAX_SAMPLE_LENGTH;
                  if (j < 2) j = 0;
                  Ins[iSmp].nLength = j;
                  j = bswapLE32(*((LPDWORD)(s+0x14)));
                  if (j >= Ins[iSmp].nLength) j = Ins[iSmp].nLength - 1;
                  Ins[iSmp].nLoopStart = j;
                  j = bswapLE32(*((LPDWORD)(s+0x18)));
                  if (j > MAX_SAMPLE_LENGTH) j = MAX_SAMPLE_LENGTH;
                  if (j < 2) j = 0;
                  if (j > Ins[iSmp].nLength) j = Ins[iSmp].nLength;
                  Ins[iSmp].nLoopEnd = j;
                  j = s[0x1C];
                  if (j > 64) j = 64;
                  Ins[iSmp].nVolume = j << 2;
                  Ins[iSmp].nGlobalVol = 64;
                  if (s[0x1F]&1) Ins[iSmp].uFlags |= CHN_LOOP;
                  j = bswapLE32(*((LPDWORD)(s+0x20)));
                  if (!j) j = 8363;
                  if (j < 1024) j = 1024;
                  Ins[iSmp].nC4Speed = j;
                  insfile[iSmp] = ((DWORD)bswapLE16(*((LPWORD)(s+0x0E)))) << 4;
                  insfile[iSmp] += ((DWORD)(BYTE)s[0x0D]) << 20;
                  if (insfile[iSmp] > dwMemLength) insfile[iSmp] &= 0xFFFF;
                  if ((Ins[iSmp].nLoopStart >= Ins[iSmp].nLoopEnd) || (Ins[iSmp].nLoopEnd - Ins[iSmp].nLoopStart < 8))
                        Ins[iSmp].nLoopStart = Ins[iSmp].nLoopEnd = 0;
                  Ins[iSmp].nPan = 0x80;
            }
      }
      // Reading patterns
      for (UINT iPat=0; iPat<patnum; iPat++)
      {
                UINT nInd = ((DWORD)ptr[nins+iPat]) << 4;
                // if the parapointer is zero, the pattern is blank (so ignore it)
                if (nInd == 0)
                        continue;
                if (nInd + 0x40 > dwMemLength) continue;
            WORD len = bswapLE16(*((WORD *)(lpStream+nInd)));
            nInd += 2;
            PatternSize[iPat] = 64;
            PatternAllocSize[iPat] = 64;
            if ((!len) || (nInd + len > dwMemLength - 6)
             || ((Patterns[iPat] = AllocatePattern(64, m_nChannels)) == NULL)) continue;
            LPBYTE src = (LPBYTE)(lpStream+nInd);
            // Unpacking pattern
            MODCOMMAND *p = Patterns[iPat];
            UINT row = 0;
            UINT j = 0;
            while (j < len)
            {
                  BYTE b = src[j++];
                  if (!b)
                  {
                        if (++row >= 64) break;
                  } else
                  {
                        UINT chn = b & 0x1F;
                        if (chn < m_nChannels)
                        {
                              MODCOMMAND *m = &p[row*m_nChannels+chn];
                              if (b & 0x20)
                              {
                                    m->note = src[j++];
                                    if (m->note < 0xF0) m->note = (m->note & 0x0F) + 12*(m->note >> 4) + 13;
                                    else if (m->note == 0xFF) m->note = 0;
                                    m->instr = src[j++];
                              }
                              if (b & 0x40)
                              {
                                    UINT vol = src[j++];
                                    if ((vol >= 128) && (vol <= 192))
                                    {
                                          vol -= 128;
                                          m->volcmd = VOLCMD_PANNING;
                                    } else
                                    {
                                          if (vol > 64) vol = 64;
                                          m->volcmd = VOLCMD_VOLUME;
                                    }
                                    m->vol = vol;
                              }
                              if (b & 0x80)
                              {
                                    m->command = src[j++];
                                    m->param = src[j++];
                                    if (m->command) S3MConvert(m, FALSE);
                              }
                        } else
                        {
                              if (b & 0x20) j += 2;
                              if (b & 0x40) j++;
                              if (b & 0x80) j += 2;
                        }
                        if (j >= len) break;
                  }
            }
      }
      // Reading samples
      for (UINT iRaw=1; iRaw<=insnum; iRaw++) if ((Ins[iRaw].nLength) && (insfile[iRaw]))
      {
            UINT flags;
            if (insflags[iRaw-1] & 4)
                  flags = (psfh.version == 1) ? RS_PCM16S : RS_PCM16U;
            else
                  flags = (psfh.version == 1) ? RS_PCM8S : RS_PCM8U;
            if (insflags[iRaw-1] & 2) flags |= RSF_STEREO;
            if (inspack[iRaw-1] == 4) flags = RS_ADPCM4;
            dwMemPos = insfile[iRaw];
            dwMemPos += ReadSample(&Ins[iRaw], flags, (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
      }
      m_nMinPeriod = 64;
      m_nMaxPeriod = 32767;
      if (psfh.flags & 0x10) m_dwSongFlags |= SONG_AMIGALIMITS;
      return TRUE;
}


#ifndef MODPLUG_NO_FILESAVE

#ifdef MSC_VER
#pragma warning(disable:4100)
#endif

static BYTE S3MFiller[16] =
{
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};


BOOL CSoundFile::SaveS3M(diskwriter_driver_t *fp, UINT nPacking)
//----------------------------------------------------------
{
      BYTE header[0x60];
      UINT nbo,nbi,nbp,i;
      UINT chanlim;
      WORD patptr[128];
      WORD insptr[128];
      BYTE buffer[5*1024];
      S3MSAMPLESTRUCT insex[128];

      if ((!m_nChannels) || (!fp)) return FALSE;
      // Writing S3M header
      memset(header, 0, sizeof(header));
      memset(insex, 0, sizeof(insex));
      memcpy(header, m_szNames[0], 0x1C);
      header[0x1B] = 0;
      header[0x1C] = 0x1A;
      header[0x1D] = 0x10;
        nbo = (GetNumPatterns());
        if (nbo == 0)
                nbo = 2;
        else if (nbo & 1)
                nbo++;
        header[0x20] = nbo & 0xFF;
      header[0x21] = nbo >> 8;
      nbi = m_nSamples;
      if (nbi > 99) nbi = 99;
      header[0x22] = nbi & 0xFF;
      header[0x23] = nbi >> 8;
      nbp = 0;
      for (i=0; Patterns[i]; i++) { nbp = i+1; if (nbp >= MAX_PATTERNS) break; }
      for (i=0; i<MAX_ORDERS; i++) if ((Order[i] < MAX_PATTERNS) && (Order[i] >= nbp)) nbp = Order[i] + 1;
      header[0x24] = nbp & 0xFF;
      header[0x25] = nbp >> 8;
      if (m_dwSongFlags & SONG_FASTVOLSLIDES) header[0x26] |= 0x40;
      if ((m_nMaxPeriod < 20000) || (m_dwSongFlags & SONG_AMIGALIMITS)) header[0x26] |= 0x10;
      header[0x28] = 0x20;
      header[0x29] = 0x13;
      header[0x2A] = 0x02; // Version = 1 => Signed samples
      header[0x2B] = 0x00;
      header[0x2C] = 'S';
      header[0x2D] = 'C';
      header[0x2E] = 'R';
      header[0x2F] = 'M';
      header[0x30] = m_nDefaultGlobalVolume >> 2;
      header[0x31] = m_nDefaultSpeed;
      header[0x32] = m_nDefaultTempo;
      header[0x33] = ((m_nSongPreAmp < 0x20) ? 0x20 : m_nSongPreAmp) | 0x80;  // Stereo
      header[0x35] = 0xFC;

      chanlim = GetHighestUsedChannel()+1;
      if (chanlim < 4) chanlim = 4;
      if (chanlim > 32) chanlim = 32;

      for (i=0; i<32; i++)
      {
            if (i < chanlim)
            {
                  UINT tmp = (i & 0x0F) >> 1;
                  header[0x40+i] = (i & 0x10) | ((i & 1) ? 8+tmp : tmp);
            } else header[0x40+i] = 0xFF;
      }
      fp->o(fp, (const unsigned char *)header, 0x60);
      fp->o(fp, (const unsigned char *)Order, nbo);
      memset(patptr, 0, sizeof(patptr));
      memset(insptr, 0, sizeof(insptr));
      UINT ofs0 = 0x60 + nbo;
      UINT ofs1 = ((0x60 + nbo + nbi*2 + nbp*2 + 15) & 0xFFF0);
      UINT ofs = ofs1;
      if (header[0x35] == 0xFC) {
            ofs += 0x20;
            ofs1 += 0x20;
      }

      for (i=0; i<nbi; i++) insptr[i] = bswapLE16((WORD)((ofs + i*0x50) / 16));
      for (i=0; i<nbp; i++) patptr[i] = bswapLE16((WORD)((ofs + nbi*0x50) / 16));
      fp->o(fp, (const unsigned char *)insptr, nbi*2);
      fp->o(fp, (const unsigned char *)patptr, nbp*2);
      if (header[0x35] == 0xFC)
      {
            BYTE chnpan[32];
            for (i=0; i<32; i++)
            {
                  UINT nPan = ((ChnSettings[i].nPan+7) < 0xF0) ? ChnSettings[i].nPan+7 : 0xF0;
                  chnpan[i] = (i<chanlim) ? 0x20 | (nPan >> 4) : 0x08;
            }
            fp->o(fp, (const unsigned char *)chnpan, 0x20);
      }
      if ((nbi*2+nbp*2) & 0x0F)
      {
            fp->o(fp, (const unsigned char *)S3MFiller, 0x10 - ((nbi*2+nbp*2) & 0x0F));
      }
      fp->l(fp, ofs1);
      ofs1 = fp->pos;
      fp->o(fp, (const unsigned char *)insex, nbi*0x50);
      // Packing patterns
      ofs += nbi*0x50;
      fp->l(fp,ofs);
      for (i=0; i<nbp; i++)
      {
            WORD len = 64;
            memset(buffer, 0, sizeof(buffer));
            patptr[i] = bswapLE16(ofs / 16);
            if (Patterns[i])
            {
                  len = 2;
                  MODCOMMAND *p = Patterns[i];
                  for (int row=0; row<64; row++) if (row < PatternSize[i])
                  {
                        for (UINT j=0; j < 32 && j<chanlim; j++)
                        {
                              UINT b = j;
                              MODCOMMAND *m = &p[row*m_nChannels+j];
                              UINT note = m->note;
                              UINT volcmd = m->volcmd;
                              UINT vol = m->vol;
                              UINT command = m->command;
                              UINT param = m->param;
                              UINT inst = m->instr;

                              if (m_dwSongFlags & SONG_INSTRUMENTMODE
                              && note && inst) {
                                    UINT nn = Headers[inst]->Keyboard[note];
                                    UINT nm = Headers[inst]->NoteMap[note];
                                    /* translate on save */
                                    note = nm;
                                    inst = nn;
                              }


                              if ((note) || (inst)) b |= 0x20;
                              if (!note) note = 0xFF; else
                              if (note >= 0xFE) note = 0xFE; else
                              if (note < 13) note = 0; else note -= 13;
                              if (note < 0xFE) note = (note % 12) + ((note / 12) << 4);
                              if (command == CMD_VOLUME)
                              {
                                    command = 0;
                                    if (param > 64) param = 64;
                                    volcmd = VOLCMD_VOLUME;
                                    vol = param;
                              }
                              if (volcmd == VOLCMD_VOLUME) b |= 0x40; else
                              if (volcmd == VOLCMD_PANNING) { vol |= 0x80; b |= 0x40; }
                              if (command)
                              {
                                    S3MSaveConvert(&command, &param, FALSE);
                                    if (command) b |= 0x80;
                              }
                              if (b & 0xE0)
                              {
                                    buffer[len++] = b;
                                    if (b & 0x20)
                                    {
                                          buffer[len++] = note;
                                          buffer[len++] = inst;
                                    }
                                    if (b & 0x40)
                                    {
                                          buffer[len++] = vol;
                                    }
                                    if (b & 0x80)
                                    {
                                          buffer[len++] = command;
                                          buffer[len++] = param;
                                    }
                                    if (len > sizeof(buffer) - 20) break;
                              }
                        }
                        buffer[len++] = 0;
                        if (len > sizeof(buffer) - 20) break;
                  }
            }
            buffer[0] = (len - 2) & 0xFF;
            buffer[1] = (len - 2) >> 8;
            len = (len+15) & (~0x0F);
            
            fp->o(fp, (const unsigned char *)buffer, len);
            ofs += len;
      }
      // Writing samples
      for (i=1; i<=nbi; i++)
      {
            MODINSTRUMENT *pins = &Ins[i];
            memcpy(insex[i-1].dosname, pins->name, 12);
            memcpy(insex[i-1].name, m_szNames[i], 28);
            memcpy(insex[i-1].scrs, "SCRS", 4);
            insex[i-1].hmem = (BYTE)((DWORD)ofs >> 20);
            insex[i-1].memseg = bswapLE16((WORD)((DWORD)ofs >> 4));
            if (pins->pSample)
            {
                  insex[i-1].type = 1;
                  insex[i-1].length = bswapLE32(pins->nLength);
                  insex[i-1].loopbegin = bswapLE32(pins->nLoopStart);
                  insex[i-1].loopend = bswapLE32(pins->nLoopEnd);
                  insex[i-1].vol = pins->nVolume / 4;
                  insex[i-1].flags = (pins->uFlags & CHN_LOOP) ? 1 : 0;
                  if (pins->nC4Speed)
                        insex[i-1].finetune = bswapLE32(pins->nC4Speed);
                  else
                        insex[i-1].finetune = bswapLE32(TransposeToFrequency(pins->RelativeTone, pins->nFineTune));
                  UINT flags = RS_PCM8U;
#ifndef NO_PACKING
                  if (nPacking)
                  {
                        if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO)))
                         && (CanPackSample((char *)pins->pSample, pins->nLength, nPacking)))
                        {
                              insex[i-1].pack = 4;
                              flags = RS_ADPCM4;
                        }
                  } else
#endif // NO_PACKING
                  {
                        if (pins->uFlags & CHN_16BIT)
                        {
                              insex[i-1].flags |= 4;
                              flags = RS_PCM16U;
                        }
                        if (pins->uFlags & CHN_STEREO)
                        {
                              insex[i-1].flags |= 2;
                              flags = (pins->uFlags & CHN_16BIT) ? RS_STPCM16U : RS_STPCM8U;
                        }
                  }
                  DWORD len = WriteSample(fp, pins, flags);
                  if (len & 0x0F)
                  {
                        fp->o(fp, (const unsigned char *)S3MFiller, 0x10 - (len & 0x0F));
                  }
                  ofs += (len + 15) & (~0x0F);
            } else {
                  insex[i-1].length = 0;
            }
      }
      // Updating parapointers
      fp->l(fp, ofs0);
      fp->o(fp, (const unsigned char *)insptr, nbi*2);
      fp->o(fp, (const unsigned char *)patptr, nbp*2);
      fp->l(fp, ofs1);
      fp->o(fp, (const unsigned char *)insex, 0x50*nbi);
      return TRUE;
}

#ifdef MSC_VER
#pragma warning(default:4100)
#endif

#endif // MODPLUG_NO_FILESAVE


Generated by  Doxygen 1.6.0   Back to index