external/libmodplug-0.8.8.4/src/load_s3m.cpp
author Sam Lantinga <slouken@libsdl.org>
Sun, 09 Jun 2013 16:22:42 -0700
changeset 639 f8901a7ff3f1
permissions -rw-r--r--
Switched from mikmod to libmodplug, which is now in the public domain.
This allows us to add MOD support for iOS and Android, yay!
slouken@639
     1
/*
slouken@639
     2
 * This source code is public domain.
slouken@639
     3
 *
slouken@639
     4
 * Authors: Olivier Lapicque <olivierl@jps.net>,
slouken@639
     5
 *          Adam Goode       <adam@evdebs.org> (endian and char fixes for PPC)
slouken@639
     6
*/
slouken@639
     7
slouken@639
     8
#include "stdafx.h"
slouken@639
     9
#include "sndfile.h"
slouken@639
    10
#include "tables.h"
slouken@639
    11
slouken@639
    12
#ifdef _MSC_VER
slouken@639
    13
//#pragma warning(disable:4244)
slouken@639
    14
#endif
slouken@639
    15
slouken@639
    16
//////////////////////////////////////////////////////
slouken@639
    17
// ScreamTracker S3M file support
slouken@639
    18
slouken@639
    19
#pragma pack(1)
slouken@639
    20
typedef struct tagS3MSAMPLESTRUCT
slouken@639
    21
{
slouken@639
    22
	BYTE type;
slouken@639
    23
	CHAR dosname[12];
slouken@639
    24
	BYTE hmem;
slouken@639
    25
	WORD memseg;
slouken@639
    26
	DWORD length;
slouken@639
    27
	DWORD loopbegin;
slouken@639
    28
	DWORD loopend;
slouken@639
    29
	BYTE vol;
slouken@639
    30
	BYTE bReserved;
slouken@639
    31
	BYTE pack;
slouken@639
    32
	BYTE flags;
slouken@639
    33
	DWORD finetune;
slouken@639
    34
	DWORD dwReserved;
slouken@639
    35
	WORD intgp;
slouken@639
    36
	WORD int512;
slouken@639
    37
	DWORD lastused;
slouken@639
    38
	CHAR name[28];
slouken@639
    39
	CHAR scrs[4];
slouken@639
    40
} S3MSAMPLESTRUCT;
slouken@639
    41
slouken@639
    42
slouken@639
    43
typedef struct tagS3MFILEHEADER
slouken@639
    44
{
slouken@639
    45
	CHAR name[28];
slouken@639
    46
	BYTE b1A;
slouken@639
    47
	BYTE type;
slouken@639
    48
	WORD reserved1;
slouken@639
    49
	WORD ordnum;
slouken@639
    50
	WORD insnum;
slouken@639
    51
	WORD patnum;
slouken@639
    52
	WORD flags;
slouken@639
    53
	WORD cwtv;
slouken@639
    54
	WORD version;
slouken@639
    55
	DWORD scrm;	// "SCRM" = 0x4D524353
slouken@639
    56
	BYTE globalvol;
slouken@639
    57
	BYTE speed;
slouken@639
    58
	BYTE tempo;
slouken@639
    59
	BYTE mastervol;
slouken@639
    60
	BYTE ultraclicks;
slouken@639
    61
	BYTE panning_present;
slouken@639
    62
	BYTE reserved2[8];
slouken@639
    63
	WORD special;
slouken@639
    64
	BYTE channels[32];
slouken@639
    65
} S3MFILEHEADER;
slouken@639
    66
slouken@639
    67
slouken@639
    68
void CSoundFile::S3MConvert(MODCOMMAND *m, BOOL bIT) const
slouken@639
    69
//--------------------------------------------------------
slouken@639
    70
{
slouken@639
    71
	UINT command = m->command;
slouken@639
    72
	UINT param = m->param;
slouken@639
    73
	switch (command + 0x40)
slouken@639
    74
	{
slouken@639
    75
	case 'A':	command = CMD_SPEED; break;
slouken@639
    76
	case 'B':	command = CMD_POSITIONJUMP; break;
slouken@639
    77
	case 'C':	command = CMD_PATTERNBREAK; if (!bIT) param = (param >> 4) * 10 + (param & 0x0F); break;
slouken@639
    78
	case 'D':	command = CMD_VOLUMESLIDE; break;
slouken@639
    79
	case 'E':	command = CMD_PORTAMENTODOWN; break;
slouken@639
    80
	case 'F':	command = CMD_PORTAMENTOUP; break;
slouken@639
    81
	case 'G':	command = CMD_TONEPORTAMENTO; break;
slouken@639
    82
	case 'H':	command = CMD_VIBRATO; break;
slouken@639
    83
	case 'I':	command = CMD_TREMOR; break;
slouken@639
    84
	case 'J':	command = CMD_ARPEGGIO; break;
slouken@639
    85
	case 'K':	command = CMD_VIBRATOVOL; break;
slouken@639
    86
	case 'L':	command = CMD_TONEPORTAVOL; break;
slouken@639
    87
	case 'M':	command = CMD_CHANNELVOLUME; break;
slouken@639
    88
	case 'N':	command = CMD_CHANNELVOLSLIDE; break;
slouken@639
    89
	case 'O':	command = CMD_OFFSET; break;
slouken@639
    90
	case 'P':	command = CMD_PANNINGSLIDE; break;
slouken@639
    91
	case 'Q':	command = CMD_RETRIG; break;
slouken@639
    92
	case 'R':	command = CMD_TREMOLO; break;
slouken@639
    93
	case 'S':	command = CMD_S3MCMDEX; break;
slouken@639
    94
	case 'T':	command = CMD_TEMPO; break;
slouken@639
    95
	case 'U':	command = CMD_FINEVIBRATO; break;
slouken@639
    96
	case 'V':	command = CMD_GLOBALVOLUME; break;
slouken@639
    97
	case 'W':	command = CMD_GLOBALVOLSLIDE; break;
slouken@639
    98
	case 'X':	command = CMD_PANNING8; break;
slouken@639
    99
	case 'Y':	command = CMD_PANBRELLO; break;
slouken@639
   100
	case 'Z':	command = CMD_MIDI; break;
slouken@639
   101
	default:	command = 0;
slouken@639
   102
	}
slouken@639
   103
	m->command = command;
slouken@639
   104
	m->param = param;
slouken@639
   105
}
slouken@639
   106
slouken@639
   107
slouken@639
   108
void CSoundFile::S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const
slouken@639
   109
//---------------------------------------------------------------------
slouken@639
   110
{
slouken@639
   111
	UINT command = *pcmd;
slouken@639
   112
	UINT param = *pprm;
slouken@639
   113
	switch(command)
slouken@639
   114
	{
slouken@639
   115
	case CMD_SPEED:				command = 'A'; break;
slouken@639
   116
	case CMD_POSITIONJUMP:		command = 'B'; break;
slouken@639
   117
	case CMD_PATTERNBREAK:		command = 'C'; if (!bIT) param = ((param / 10) << 4) + (param % 10); break;
slouken@639
   118
	case CMD_VOLUMESLIDE:		command = 'D'; break;
slouken@639
   119
	case CMD_PORTAMENTODOWN:	command = 'E'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break;
slouken@639
   120
	case CMD_PORTAMENTOUP:		command = 'F'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break;
slouken@639
   121
	case CMD_TONEPORTAMENTO:	command = 'G'; break;
slouken@639
   122
	case CMD_VIBRATO:			command = 'H'; break;
slouken@639
   123
	case CMD_TREMOR:			command = 'I'; break;
slouken@639
   124
	case CMD_ARPEGGIO:			command = 'J'; break;
slouken@639
   125
	case CMD_VIBRATOVOL:		command = 'K'; break;
slouken@639
   126
	case CMD_TONEPORTAVOL:		command = 'L'; break;
slouken@639
   127
	case CMD_CHANNELVOLUME:		command = 'M'; break;
slouken@639
   128
	case CMD_CHANNELVOLSLIDE:	command = 'N'; break;
slouken@639
   129
	case CMD_OFFSET:			command = 'O'; break;
slouken@639
   130
	case CMD_PANNINGSLIDE:		command = 'P'; break;
slouken@639
   131
	case CMD_RETRIG:			command = 'Q'; break;
slouken@639
   132
	case CMD_TREMOLO:			command = 'R'; break;
slouken@639
   133
	case CMD_S3MCMDEX:			command = 'S'; break;
slouken@639
   134
	case CMD_TEMPO:				command = 'T'; break;
slouken@639
   135
	case CMD_FINEVIBRATO:		command = 'U'; break;
slouken@639
   136
	case CMD_GLOBALVOLUME:		command = 'V'; break;
slouken@639
   137
	case CMD_GLOBALVOLSLIDE:	command = 'W'; break;
slouken@639
   138
	case CMD_PANNING8:			
slouken@639
   139
		command = 'X';
slouken@639
   140
		if ((bIT) && (m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM))
slouken@639
   141
		{
slouken@639
   142
			if (param == 0xA4) { command = 'S'; param = 0x91; }	else
slouken@639
   143
			if (param <= 0x80) { param <<= 1; if (param > 255) param = 255; } else
slouken@639
   144
			command = param = 0;
slouken@639
   145
		} else
slouken@639
   146
		if ((!bIT) && ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM)))
slouken@639
   147
		{
slouken@639
   148
			param >>= 1;
slouken@639
   149
		}
slouken@639
   150
		break;
slouken@639
   151
	case CMD_PANBRELLO:			command = 'Y'; break;
slouken@639
   152
	case CMD_MIDI:				command = 'Z'; break;
slouken@639
   153
	case CMD_XFINEPORTAUPDOWN:
slouken@639
   154
		if (param & 0x0F) switch(param & 0xF0)
slouken@639
   155
		{
slouken@639
   156
		case 0x10:	command = 'F'; param = (param & 0x0F) | 0xE0; break;
slouken@639
   157
		case 0x20:	command = 'E'; param = (param & 0x0F) | 0xE0; break;
slouken@639
   158
		case 0x90:	command = 'S'; break;
slouken@639
   159
		default:	command = param = 0;
slouken@639
   160
		} else command = param = 0;
slouken@639
   161
		break;
slouken@639
   162
	case CMD_MODCMDEX:
slouken@639
   163
		command = 'S';
slouken@639
   164
		switch(param & 0xF0)
slouken@639
   165
		{
slouken@639
   166
		case 0x00:	command = param = 0; break;
slouken@639
   167
		case 0x10:	command = 'F'; param |= 0xF0; break;
slouken@639
   168
		case 0x20:	command = 'E'; param |= 0xF0; break;
slouken@639
   169
		case 0x30:	param = (param & 0x0F) | 0x10; break;
slouken@639
   170
		case 0x40:	param = (param & 0x0F) | 0x30; break;
slouken@639
   171
		case 0x50:	param = (param & 0x0F) | 0x20; break;
slouken@639
   172
		case 0x60:	param = (param & 0x0F) | 0xB0; break;
slouken@639
   173
		case 0x70:	param = (param & 0x0F) | 0x40; break;
slouken@639
   174
		case 0x90:	command = 'Q'; param &= 0x0F; break;
slouken@639
   175
		case 0xA0:	if (param & 0x0F) { command = 'D'; param = (param << 4) | 0x0F; } else command=param=0; break;
slouken@639
   176
		case 0xB0:	if (param & 0x0F) { command = 'D'; param |= 0xF0; } else command=param=0; break;
slouken@639
   177
		}
slouken@639
   178
		break;
slouken@639
   179
	default:	command = param = 0;
slouken@639
   180
	}
slouken@639
   181
	command &= ~0x40;
slouken@639
   182
	*pcmd = command;
slouken@639
   183
	*pprm = param;
slouken@639
   184
}
slouken@639
   185
slouken@639
   186
static DWORD boundInput(DWORD input, DWORD smin, DWORD smax)
slouken@639
   187
{
slouken@639
   188
	if (input > smax) input = smax;
slouken@639
   189
	else if (input < smin) input = 0;
slouken@639
   190
	return(input);
slouken@639
   191
}
slouken@639
   192
slouken@639
   193
slouken@639
   194
BOOL CSoundFile::ReadS3M(const BYTE *lpStream, DWORD dwMemLength)
slouken@639
   195
//---------------------------------------------------------------
slouken@639
   196
{
slouken@639
   197
	UINT insnum,patnum,nins,npat;
slouken@639
   198
	DWORD insfile[MAX_SAMPLES];
slouken@639
   199
	WORD ptr[256];
slouken@639
   200
	DWORD dwMemPos;
slouken@639
   201
	BYTE insflags[MAX_SAMPLES], inspack[MAX_SAMPLES];
slouken@639
   202
slouken@639
   203
	if ((!lpStream) || (dwMemLength <= sizeof(S3MFILEHEADER)+sizeof(S3MSAMPLESTRUCT)+64)) return FALSE;
slouken@639
   204
	S3MFILEHEADER psfh = *(S3MFILEHEADER *)lpStream;
slouken@639
   205
slouken@639
   206
	psfh.reserved1 = bswapLE16(psfh.reserved1);
slouken@639
   207
	psfh.ordnum = bswapLE16(psfh.ordnum);
slouken@639
   208
	psfh.insnum = bswapLE16(psfh.insnum);
slouken@639
   209
	psfh.patnum = bswapLE16(psfh.patnum);
slouken@639
   210
	psfh.flags = bswapLE16(psfh.flags);
slouken@639
   211
	psfh.cwtv = bswapLE16(psfh.cwtv);
slouken@639
   212
	psfh.version = bswapLE16(psfh.version);
slouken@639
   213
	psfh.scrm = bswapLE32(psfh.scrm);
slouken@639
   214
	psfh.special = bswapLE16(psfh.special);
slouken@639
   215
slouken@639
   216
	if (psfh.scrm != 0x4D524353) return FALSE;
slouken@639
   217
	dwMemPos = 0x60;
slouken@639
   218
	m_nType = MOD_TYPE_S3M;
slouken@639
   219
	memset(m_szNames,0,sizeof(m_szNames));
slouken@639
   220
	memcpy(m_szNames[0], psfh.name, 28);
slouken@639
   221
	// Speed
slouken@639
   222
	m_nDefaultSpeed = psfh.speed;
slouken@639
   223
	if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 6;
slouken@639
   224
	if (m_nDefaultSpeed > 0x1F) m_nDefaultSpeed = 0x1F;
slouken@639
   225
	// Tempo
slouken@639
   226
	m_nDefaultTempo = psfh.tempo;
slouken@639
   227
	if (m_nDefaultTempo < 40) m_nDefaultTempo = 40;
slouken@639
   228
	if (m_nDefaultTempo > 240) m_nDefaultTempo = 240;
slouken@639
   229
	// Global Volume
slouken@639
   230
	m_nDefaultGlobalVolume = psfh.globalvol << 2;
slouken@639
   231
	if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256;
slouken@639
   232
	m_nSongPreAmp = psfh.mastervol & 0x7F;
slouken@639
   233
	// Channels
slouken@639
   234
	m_nChannels = 4;
slouken@639
   235
	for (UINT ich=0; ich<32; ich++)
slouken@639
   236
	{
slouken@639
   237
		ChnSettings[ich].nPan = 128;
slouken@639
   238
		ChnSettings[ich].nVolume = 64;
slouken@639
   239
slouken@639
   240
		ChnSettings[ich].dwFlags = CHN_MUTE;
slouken@639
   241
		if (psfh.channels[ich] != 0xFF)
slouken@639
   242
		{
slouken@639
   243
			m_nChannels = ich+1;
slouken@639
   244
			UINT b = psfh.channels[ich] & 0x0F;
slouken@639
   245
			ChnSettings[ich].nPan = (b & 8) ? 0xC0 : 0x40;
slouken@639
   246
			ChnSettings[ich].dwFlags = 0;
slouken@639
   247
		}
slouken@639
   248
	}
slouken@639
   249
	if (m_nChannels < 4) m_nChannels = 4;
slouken@639
   250
	if ((psfh.cwtv < 0x1320) || (psfh.flags & 0x40)) m_dwSongFlags |= SONG_FASTVOLSLIDES;
slouken@639
   251
	// Reading pattern order
slouken@639
   252
	UINT iord = psfh.ordnum;
slouken@639
   253
	if (iord<1) iord = 1;
slouken@639
   254
	if (iord > MAX_ORDERS) iord = MAX_ORDERS;
slouken@639
   255
	if (iord)
slouken@639
   256
	{
slouken@639
   257
		memcpy(Order, lpStream+dwMemPos, iord);
slouken@639
   258
		dwMemPos += iord;
slouken@639
   259
	}
slouken@639
   260
	if ((iord & 1) && (lpStream[dwMemPos] == 0xFF)) dwMemPos++;
slouken@639
   261
	// Reading file pointers
slouken@639
   262
	insnum = nins = psfh.insnum;
slouken@639
   263
	if (insnum >= MAX_SAMPLES) insnum = MAX_SAMPLES-1;
slouken@639
   264
	m_nSamples = insnum;
slouken@639
   265
	patnum = npat = psfh.patnum;
slouken@639
   266
	if (patnum > MAX_PATTERNS) patnum = MAX_PATTERNS;
slouken@639
   267
	memset(ptr, 0, sizeof(ptr));
slouken@639
   268
slouken@639
   269
	// Ignore file if it has a corrupted header.
slouken@639
   270
	if (nins+npat > 256) return FALSE;
slouken@639
   271
slouken@639
   272
	if (nins+npat)
slouken@639
   273
	{
slouken@639
   274
		memcpy(ptr, lpStream+dwMemPos, 2*(nins+npat));
slouken@639
   275
		dwMemPos += 2*(nins+npat);
slouken@639
   276
		for (UINT j = 0; j < (nins+npat); ++j) {
slouken@639
   277
		        ptr[j] = bswapLE16(ptr[j]);
slouken@639
   278
		}
slouken@639
   279
		if (psfh.panning_present == 252)
slouken@639
   280
		{
slouken@639
   281
			const BYTE *chnpan = lpStream+dwMemPos;
slouken@639
   282
			for (UINT i=0; i<32; i++) if (chnpan[i] & 0x20)
slouken@639
   283
			{
slouken@639
   284
				ChnSettings[i].nPan = ((chnpan[i] & 0x0F) << 4) + 8;
slouken@639
   285
			}
slouken@639
   286
		}
slouken@639
   287
	}
slouken@639
   288
	if (!m_nChannels) return TRUE;
slouken@639
   289
	// Reading instrument headers
slouken@639
   290
	memset(insfile, 0, sizeof(insfile));
slouken@639
   291
	for (UINT iSmp=1; iSmp<=insnum; iSmp++)
slouken@639
   292
	{
slouken@639
   293
		UINT nInd = ((DWORD)ptr[iSmp-1])*16;
slouken@639
   294
		if ((!nInd) || (nInd + 0x50 > dwMemLength)) continue;
slouken@639
   295
		S3MSAMPLESTRUCT pSmp;
slouken@639
   296
		memcpy(&pSmp, lpStream+nInd, 0x50);
slouken@639
   297
		memcpy(Ins[iSmp].name, &pSmp.dosname, 12);
slouken@639
   298
		insflags[iSmp-1] = pSmp.flags;
slouken@639
   299
		inspack[iSmp-1] = pSmp.pack;
slouken@639
   300
		memcpy(m_szNames[iSmp], pSmp.name, 28);
slouken@639
   301
		m_szNames[iSmp][28] = 0;
slouken@639
   302
		if ((pSmp.type==1) && (pSmp.scrs[2]=='R') && (pSmp.scrs[3]=='S'))
slouken@639
   303
		{
slouken@639
   304
			Ins[iSmp].nLength = boundInput(bswapLE32(pSmp.length), 4, MAX_SAMPLE_LENGTH);
slouken@639
   305
			Ins[iSmp].nLoopStart = boundInput(bswapLE32(pSmp.loopbegin), 4, Ins[iSmp].nLength - 1);
slouken@639
   306
			Ins[iSmp].nLoopEnd = boundInput(bswapLE32(pSmp.loopend), 4, Ins[iSmp].nLength);
slouken@639
   307
			Ins[iSmp].nVolume = boundInput(pSmp.vol, 0, 64) << 2;
slouken@639
   308
			Ins[iSmp].nGlobalVol = 64;
slouken@639
   309
			if (pSmp.flags&1) Ins[iSmp].uFlags |= CHN_LOOP;
slouken@639
   310
			UINT j = bswapLE32(pSmp.finetune);
slouken@639
   311
			if (!j) j = 8363;
slouken@639
   312
			if (j < 1024) j = 1024;
slouken@639
   313
			Ins[iSmp].nC4Speed = j;
slouken@639
   314
			insfile[iSmp] = (pSmp.hmem << 20) + (bswapLE16(pSmp.memseg) << 4);
slouken@639
   315
			// offset is invalid - ignore this sample.
slouken@639
   316
			if (insfile[iSmp] > dwMemLength) insfile[iSmp] = 0;
slouken@639
   317
			else if (insfile[iSmp]) {
slouken@639
   318
				// ignore duplicate samples.
slouken@639
   319
				for (int z=iSmp-1; z>=0; z--)
slouken@639
   320
					if (insfile[iSmp] == insfile[z])
slouken@639
   321
						insfile[iSmp] = 0;
slouken@639
   322
			}
slouken@639
   323
			if ((Ins[iSmp].nLoopStart >= Ins[iSmp].nLoopEnd) || (Ins[iSmp].nLoopEnd - Ins[iSmp].nLoopStart < 8))
slouken@639
   324
				Ins[iSmp].nLoopStart = Ins[iSmp].nLoopEnd = 0;
slouken@639
   325
			Ins[iSmp].nPan = 0x80;
slouken@639
   326
		}
slouken@639
   327
	}
slouken@639
   328
	// Reading patterns
slouken@639
   329
	for (UINT iPat=0; iPat<patnum; iPat++)
slouken@639
   330
	{
slouken@639
   331
		UINT nInd = ((DWORD)ptr[nins+iPat]) << 4;
slouken@639
   332
		if (nInd + 0x40 > dwMemLength) continue;
slouken@639
   333
		WORD len = bswapLE16(*((WORD *)(lpStream+nInd)));
slouken@639
   334
		nInd += 2;
slouken@639
   335
		PatternSize[iPat] = 64;
slouken@639
   336
		if ((!len) || (nInd + len > dwMemLength - 6)
slouken@639
   337
		 || ((Patterns[iPat] = AllocatePattern(64, m_nChannels)) == NULL)) continue;
slouken@639
   338
		LPBYTE src = (LPBYTE)(lpStream+nInd);
slouken@639
   339
		// Unpacking pattern
slouken@639
   340
		MODCOMMAND *p = Patterns[iPat];
slouken@639
   341
		UINT row = 0;
slouken@639
   342
		UINT j = 0;
slouken@639
   343
		while (j < len)
slouken@639
   344
		{
slouken@639
   345
			BYTE b = src[j++];
slouken@639
   346
			if (!b)
slouken@639
   347
			{
slouken@639
   348
				if (++row >= 64) break;
slouken@639
   349
			} else
slouken@639
   350
			{
slouken@639
   351
				UINT chn = b & 0x1F;
slouken@639
   352
				if (chn < m_nChannels)
slouken@639
   353
				{
slouken@639
   354
					MODCOMMAND *m = &p[row*m_nChannels+chn];
slouken@639
   355
					if (b & 0x20)
slouken@639
   356
					{
slouken@639
   357
						m->note = src[j++];
slouken@639
   358
						if (m->note < 0xF0) m->note = (m->note & 0x0F) + 12*(m->note >> 4) + 13;
slouken@639
   359
						else if (m->note == 0xFF) m->note = 0;
slouken@639
   360
						m->instr = src[j++];
slouken@639
   361
					}
slouken@639
   362
					if (b & 0x40)
slouken@639
   363
					{
slouken@639
   364
						UINT vol = src[j++];
slouken@639
   365
						if ((vol >= 128) && (vol <= 192))
slouken@639
   366
						{
slouken@639
   367
							vol -= 128;
slouken@639
   368
							m->volcmd = VOLCMD_PANNING;
slouken@639
   369
						} else
slouken@639
   370
						{
slouken@639
   371
							if (vol > 64) vol = 64;
slouken@639
   372
							m->volcmd = VOLCMD_VOLUME;
slouken@639
   373
						}
slouken@639
   374
						m->vol = vol;
slouken@639
   375
					}
slouken@639
   376
					if (b & 0x80)
slouken@639
   377
					{
slouken@639
   378
						m->command = src[j++];
slouken@639
   379
						m->param = src[j++];
slouken@639
   380
						if (m->command) S3MConvert(m, FALSE);
slouken@639
   381
					}
slouken@639
   382
				} else
slouken@639
   383
				{
slouken@639
   384
					if (b & 0x20) j += 2;
slouken@639
   385
					if (b & 0x40) j++;
slouken@639
   386
					if (b & 0x80) j += 2;
slouken@639
   387
				}
slouken@639
   388
				if (j >= len) break;
slouken@639
   389
			}
slouken@639
   390
		}
slouken@639
   391
	}
slouken@639
   392
	// Reading samples
slouken@639
   393
	for (UINT iRaw=1; iRaw<=insnum; iRaw++) if ((Ins[iRaw].nLength) && (insfile[iRaw]))
slouken@639
   394
	{
slouken@639
   395
		UINT flags = (psfh.version == 1) ? RS_PCM8S : RS_PCM8U;
slouken@639
   396
		if (insflags[iRaw-1] & 4) flags += 5;
slouken@639
   397
		if (insflags[iRaw-1] & 2) flags |= RSF_STEREO;
slouken@639
   398
		if (inspack[iRaw-1] == 4) flags = RS_ADPCM4;
slouken@639
   399
		dwMemPos = insfile[iRaw];
slouken@639
   400
		if (dwMemPos < dwMemLength)
slouken@639
   401
			dwMemPos += ReadSample(&Ins[iRaw], flags, (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
slouken@639
   402
	}
slouken@639
   403
	m_nMinPeriod = 64;
slouken@639
   404
	m_nMaxPeriod = 32767;
slouken@639
   405
	if (psfh.flags & 0x10) m_dwSongFlags |= SONG_AMIGALIMITS;
slouken@639
   406
	return TRUE;
slouken@639
   407
}
slouken@639
   408
slouken@639
   409
slouken@639
   410
#ifndef MODPLUG_NO_FILESAVE
slouken@639
   411
slouken@639
   412
#ifdef _MSC_VER
slouken@639
   413
#pragma warning(disable:4100)
slouken@639
   414
#endif
slouken@639
   415
slouken@639
   416
static BYTE S3MFiller[16] =
slouken@639
   417
{
slouken@639
   418
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
slouken@639
   419
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
slouken@639
   420
};
slouken@639
   421
slouken@639
   422
slouken@639
   423
BOOL CSoundFile::SaveS3M(LPCSTR lpszFileName, UINT nPacking)
slouken@639
   424
//----------------------------------------------------------
slouken@639
   425
{
slouken@639
   426
	FILE *f;
slouken@639
   427
	BYTE header[0x60];
slouken@639
   428
	UINT nbo,nbi,nbp,i;
slouken@639
   429
	WORD patptr[128];
slouken@639
   430
	WORD insptr[128];
slouken@639
   431
	BYTE buffer[5*1024];
slouken@639
   432
	S3MSAMPLESTRUCT insex[128];
slouken@639
   433
slouken@639
   434
	if ((!m_nChannels) || (!lpszFileName)) return FALSE;
slouken@639
   435
	if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE;
slouken@639
   436
	// Writing S3M header
slouken@639
   437
	memset(header, 0, sizeof(header));
slouken@639
   438
	memset(insex, 0, sizeof(insex));
slouken@639
   439
	memcpy(header, m_szNames[0], 0x1C);
slouken@639
   440
	header[0x1B] = 0;
slouken@639
   441
	header[0x1C] = 0x1A;
slouken@639
   442
	header[0x1D] = 0x10;
slouken@639
   443
	nbo = (GetNumPatterns() + 15) & 0xF0;
slouken@639
   444
	if (!nbo) nbo = 16;
slouken@639
   445
	header[0x20] = nbo & 0xFF;
slouken@639
   446
	header[0x21] = nbo >> 8;
slouken@639
   447
	nbi = m_nInstruments;
slouken@639
   448
	if (!nbi) nbi = m_nSamples;
slouken@639
   449
	if (nbi > 99) nbi = 99;
slouken@639
   450
	header[0x22] = nbi & 0xFF;
slouken@639
   451
	header[0x23] = nbi >> 8;
slouken@639
   452
	nbp = 0;
slouken@639
   453
	for (i=0; Patterns[i]; i++) { nbp = i+1; if (nbp >= MAX_PATTERNS) break; }
slouken@639
   454
	for (i=0; i<MAX_ORDERS; i++) if ((Order[i] < MAX_PATTERNS) && (Order[i] >= nbp)) nbp = Order[i] + 1;
slouken@639
   455
	header[0x24] = nbp & 0xFF;
slouken@639
   456
	header[0x25] = nbp >> 8;
slouken@639
   457
	if (m_dwSongFlags & SONG_FASTVOLSLIDES) header[0x26] |= 0x40;
slouken@639
   458
	if ((m_nMaxPeriod < 20000) || (m_dwSongFlags & SONG_AMIGALIMITS)) header[0x26] |= 0x10;
slouken@639
   459
	header[0x28] = 0x20;
slouken@639
   460
	header[0x29] = 0x13;
slouken@639
   461
	header[0x2A] = 0x02; // Version = 1 => Signed samples
slouken@639
   462
	header[0x2B] = 0x00;
slouken@639
   463
	header[0x2C] = 'S';
slouken@639
   464
	header[0x2D] = 'C';
slouken@639
   465
	header[0x2E] = 'R';
slouken@639
   466
	header[0x2F] = 'M';
slouken@639
   467
	header[0x30] = m_nDefaultGlobalVolume >> 2;
slouken@639
   468
	header[0x31] = m_nDefaultSpeed;
slouken@639
   469
	header[0x32] = m_nDefaultTempo;
slouken@639
   470
	header[0x33] = ((m_nSongPreAmp < 0x20) ? 0x20 : m_nSongPreAmp) | 0x80;	// Stereo
slouken@639
   471
	header[0x35] = 0xFC;
slouken@639
   472
	for (i=0; i<32; i++)
slouken@639
   473
	{
slouken@639
   474
		if (i < m_nChannels)
slouken@639
   475
		{
slouken@639
   476
			UINT tmp = (i & 0x0F) >> 1;
slouken@639
   477
			header[0x40+i] = (i & 0x10) | ((i & 1) ? 8+tmp : tmp);
slouken@639
   478
		} else header[0x40+i] = 0xFF;
slouken@639
   479
	}
slouken@639
   480
	fwrite(header, 0x60, 1, f);
slouken@639
   481
	fwrite(Order, nbo, 1, f);
slouken@639
   482
	memset(patptr, 0, sizeof(patptr));
slouken@639
   483
	memset(insptr, 0, sizeof(insptr));
slouken@639
   484
	UINT ofs0 = 0x60 + nbo;
slouken@639
   485
	UINT ofs1 = ((0x60 + nbo + nbi*2 + nbp*2 + 15) & 0xFFF0) + 0x20;
slouken@639
   486
	UINT ofs = ofs1;
slouken@639
   487
slouken@639
   488
	for (i=0; i<nbi; i++) insptr[i] = (WORD)((ofs + i*0x50) / 16);
slouken@639
   489
	for (i=0; i<nbp; i++) patptr[i] = (WORD)((ofs + nbi*0x50) / 16);
slouken@639
   490
	fwrite(insptr, nbi, 2, f);
slouken@639
   491
	fwrite(patptr, nbp, 2, f);
slouken@639
   492
	if (header[0x35] == 0xFC)
slouken@639
   493
	{
slouken@639
   494
		BYTE chnpan[32];
slouken@639
   495
		for (i=0; i<32; i++)
slouken@639
   496
		{
slouken@639
   497
			chnpan[i] = 0x20 | (ChnSettings[i].nPan >> 4);
slouken@639
   498
		}
slouken@639
   499
		fwrite(chnpan, 0x20, 1, f);
slouken@639
   500
	}
slouken@639
   501
	if ((nbi*2+nbp*2) & 0x0F)
slouken@639
   502
	{
slouken@639
   503
		fwrite(S3MFiller, 0x10 - ((nbi*2+nbp*2) & 0x0F), 1, f);
slouken@639
   504
	}
slouken@639
   505
	ofs1 = ftell(f);
slouken@639
   506
	fwrite(insex, nbi, 0x50, f);
slouken@639
   507
	// Packing patterns
slouken@639
   508
	ofs += nbi*0x50;
slouken@639
   509
	for (i=0; i<nbp; i++)
slouken@639
   510
	{
slouken@639
   511
		WORD len = 64;
slouken@639
   512
		memset(buffer, 0, sizeof(buffer));
slouken@639
   513
		patptr[i] = ofs / 16;
slouken@639
   514
		if (Patterns[i])
slouken@639
   515
		{
slouken@639
   516
			len = 2;
slouken@639
   517
			MODCOMMAND *p = Patterns[i];
slouken@639
   518
			for (int row=0; row<64; row++) if (row < PatternSize[i])
slouken@639
   519
			{
slouken@639
   520
				for (UINT j=0; j<m_nChannels; j++)
slouken@639
   521
				{
slouken@639
   522
					UINT b = j;
slouken@639
   523
					MODCOMMAND *m = &p[row*m_nChannels+j];
slouken@639
   524
					UINT note = m->note;
slouken@639
   525
					UINT volcmd = m->volcmd;
slouken@639
   526
					UINT vol = m->vol;
slouken@639
   527
					UINT command = m->command;
slouken@639
   528
					UINT param = m->param;
slouken@639
   529
slouken@639
   530
					if ((note) || (m->instr)) b |= 0x20;
slouken@639
   531
					if (!note) note = 0xFF; else
slouken@639
   532
					if (note >= 0xFE) note = 0xFE; else
slouken@639
   533
					if (note < 13) note = 0; else note -= 13;
slouken@639
   534
					if (note < 0xFE) note = (note % 12) + ((note / 12) << 4);
slouken@639
   535
					if (command == CMD_VOLUME)
slouken@639
   536
					{
slouken@639
   537
						command = 0;
slouken@639
   538
						if (param > 64) param = 64;
slouken@639
   539
						volcmd = VOLCMD_VOLUME;
slouken@639
   540
						vol = param;
slouken@639
   541
					}
slouken@639
   542
					if (volcmd == VOLCMD_VOLUME) b |= 0x40; else
slouken@639
   543
					if (volcmd == VOLCMD_PANNING) { vol |= 0x80; b |= 0x40; }
slouken@639
   544
					if (command)
slouken@639
   545
					{
slouken@639
   546
						S3MSaveConvert(&command, &param, FALSE);
slouken@639
   547
						if (command) b |= 0x80;
slouken@639
   548
					}
slouken@639
   549
					if (b & 0xE0)
slouken@639
   550
					{
slouken@639
   551
						buffer[len++] = b;
slouken@639
   552
						if (b & 0x20)
slouken@639
   553
						{
slouken@639
   554
							buffer[len++] = note;
slouken@639
   555
							buffer[len++] = m->instr;
slouken@639
   556
						}
slouken@639
   557
						if (b & 0x40)
slouken@639
   558
						{
slouken@639
   559
							buffer[len++] = vol;
slouken@639
   560
						}
slouken@639
   561
						if (b & 0x80)
slouken@639
   562
						{
slouken@639
   563
							buffer[len++] = command;
slouken@639
   564
							buffer[len++] = param;
slouken@639
   565
						}
slouken@639
   566
						if (len > sizeof(buffer) - 20) break;
slouken@639
   567
					}
slouken@639
   568
				}
slouken@639
   569
				buffer[len++] = 0;
slouken@639
   570
				if (len > sizeof(buffer) - 20) break;
slouken@639
   571
			}
slouken@639
   572
		}
slouken@639
   573
		buffer[0] = (len - 2) & 0xFF;
slouken@639
   574
		buffer[1] = (len - 2) >> 8;
slouken@639
   575
		len = (len+15) & (~0x0F);
slouken@639
   576
		fwrite(buffer, len, 1, f);
slouken@639
   577
		ofs += len;
slouken@639
   578
	}
slouken@639
   579
	// Writing samples
slouken@639
   580
	for (i=1; i<=nbi; i++)
slouken@639
   581
	{
slouken@639
   582
		MODINSTRUMENT *pins = &Ins[i];
slouken@639
   583
		if (m_nInstruments)
slouken@639
   584
		{
slouken@639
   585
			pins = Ins;
slouken@639
   586
			if (Headers[i])
slouken@639
   587
			{
slouken@639
   588
				for (UINT j=0; j<128; j++)
slouken@639
   589
				{
slouken@639
   590
					UINT n = Headers[i]->Keyboard[j];
slouken@639
   591
					if ((n) && (n < MAX_INSTRUMENTS))
slouken@639
   592
					{
slouken@639
   593
						pins = &Ins[n];
slouken@639
   594
						break;
slouken@639
   595
					}
slouken@639
   596
				}
slouken@639
   597
			}
slouken@639
   598
		}
slouken@639
   599
		memcpy(insex[i-1].dosname, pins->name, 12);
slouken@639
   600
		memcpy(insex[i-1].name, m_szNames[i], 28);
slouken@639
   601
		memcpy(insex[i-1].scrs, "SCRS", 4);
slouken@639
   602
		insex[i-1].hmem = (BYTE)((DWORD)ofs >> 20);
slouken@639
   603
		insex[i-1].memseg = (WORD)((DWORD)ofs >> 4);
slouken@639
   604
		if (pins->pSample)
slouken@639
   605
		{
slouken@639
   606
			insex[i-1].type = 1;
slouken@639
   607
			insex[i-1].length = pins->nLength;
slouken@639
   608
			insex[i-1].loopbegin = pins->nLoopStart;
slouken@639
   609
			insex[i-1].loopend = pins->nLoopEnd;
slouken@639
   610
			insex[i-1].vol = pins->nVolume / 4;
slouken@639
   611
			insex[i-1].flags = (pins->uFlags & CHN_LOOP) ? 1 : 0;
slouken@639
   612
			if (pins->nC4Speed)
slouken@639
   613
				insex[i-1].finetune = pins->nC4Speed;
slouken@639
   614
			else
slouken@639
   615
				insex[i-1].finetune = TransposeToFrequency(pins->RelativeTone, pins->nFineTune);
slouken@639
   616
			UINT flags = RS_PCM8U;
slouken@639
   617
#ifndef NO_PACKING
slouken@639
   618
			if (nPacking)
slouken@639
   619
			{
slouken@639
   620
				if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO)))
slouken@639
   621
				 && (CanPackSample((char *)pins->pSample, pins->nLength, nPacking)))
slouken@639
   622
				{
slouken@639
   623
					insex[i-1].pack = 4;
slouken@639
   624
					flags = RS_ADPCM4;
slouken@639
   625
				}
slouken@639
   626
			} else
slouken@639
   627
#endif // NO_PACKING
slouken@639
   628
			{
slouken@639
   629
				if (pins->uFlags & CHN_16BIT)
slouken@639
   630
				{
slouken@639
   631
					insex[i-1].flags |= 4;
slouken@639
   632
					flags = RS_PCM16U;
slouken@639
   633
				}
slouken@639
   634
				if (pins->uFlags & CHN_STEREO)
slouken@639
   635
				{
slouken@639
   636
					insex[i-1].flags |= 2;
slouken@639
   637
					flags = (pins->uFlags & CHN_16BIT) ? RS_STPCM16U : RS_STPCM8U;
slouken@639
   638
				}
slouken@639
   639
			}
slouken@639
   640
			DWORD len = WriteSample(f, pins, flags);
slouken@639
   641
			if (len & 0x0F)
slouken@639
   642
			{
slouken@639
   643
				fwrite(S3MFiller, 0x10 - (len & 0x0F), 1, f);
slouken@639
   644
			}
slouken@639
   645
			ofs += (len + 15) & (~0x0F);
slouken@639
   646
		} else
slouken@639
   647
		{
slouken@639
   648
			insex[i-1].length = 0;
slouken@639
   649
		}
slouken@639
   650
	}
slouken@639
   651
	// Updating parapointers
slouken@639
   652
	fseek(f, ofs0, SEEK_SET);
slouken@639
   653
	fwrite(insptr, nbi, 2, f);
slouken@639
   654
	fwrite(patptr, nbp, 2, f);
slouken@639
   655
	fseek(f, ofs1, SEEK_SET);
slouken@639
   656
	fwrite(insex, 0x50, nbi, f);
slouken@639
   657
	fclose(f);
slouken@639
   658
	return TRUE;
slouken@639
   659
}
slouken@639
   660
slouken@639
   661
#ifdef _MSC_VER
slouken@639
   662
#pragma warning(default:4100)
slouken@639
   663
#endif
slouken@639
   664
slouken@639
   665
#endif // MODPLUG_NO_FILESAVE
slouken@639
   666