external/libmodplug-0.8.8.4/src/load_ptm.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
//////////////////////////////////////////////
slouken@639
     9
// PTM PolyTracker module loader            //
slouken@639
    10
//////////////////////////////////////////////
slouken@639
    11
#include "stdafx.h"
slouken@639
    12
#include "sndfile.h"
slouken@639
    13
slouken@639
    14
//#pragma warning(disable:4244)
slouken@639
    15
slouken@639
    16
#pragma pack(1)
slouken@639
    17
slouken@639
    18
typedef struct PTMFILEHEADER
slouken@639
    19
{
slouken@639
    20
	CHAR songname[28];		// name of song, asciiz string
slouken@639
    21
	CHAR eof;				// 26
slouken@639
    22
	BYTE version_lo;		// 03 version of file, currently 0203h
slouken@639
    23
	BYTE version_hi;		// 02
slouken@639
    24
	BYTE reserved1;			// reserved, set to 0
slouken@639
    25
	WORD norders;			// number of orders (0..256)
slouken@639
    26
	WORD nsamples;			// number of instruments (1..255)
slouken@639
    27
	WORD npatterns;			// number of patterns (1..128)
slouken@639
    28
	WORD nchannels;			// number of channels (voices) used (1..32)
slouken@639
    29
	WORD fileflags;			// set to 0
slouken@639
    30
	WORD reserved2;			// reserved, set to 0
slouken@639
    31
	DWORD ptmf_id;			// song identification, 'PTMF' or 0x464d5450
slouken@639
    32
	BYTE reserved3[16];		// reserved, set to 0
slouken@639
    33
	BYTE chnpan[32];		// channel panning settings, 0..15, 0 = left, 7 = middle, 15 = right
slouken@639
    34
	BYTE orders[256];		// order list, valid entries 0..nOrders-1
slouken@639
    35
	WORD patseg[128];		// pattern offsets (*16)
slouken@639
    36
} PTMFILEHEADER, *LPPTMFILEHEADER;
slouken@639
    37
slouken@639
    38
#define SIZEOF_PTMFILEHEADER	608
slouken@639
    39
slouken@639
    40
slouken@639
    41
typedef struct PTMSAMPLE
slouken@639
    42
{
slouken@639
    43
	BYTE sampletype;		// sample type (bit array)
slouken@639
    44
	CHAR filename[12];		// name of external sample file
slouken@639
    45
	BYTE volume;			// default volume
slouken@639
    46
	WORD nC4Spd;			// C4 speed
slouken@639
    47
	WORD sampleseg;			// sample segment (used internally)
slouken@639
    48
	WORD fileofs[2];		// offset of sample data
slouken@639
    49
	WORD length[2];			// sample size (in bytes)
slouken@639
    50
	WORD loopbeg[2];		// start of loop
slouken@639
    51
	WORD loopend[2];		// end of loop
slouken@639
    52
	WORD gusdata[8];
slouken@639
    53
	char  samplename[28];	// name of sample, asciiz  // changed from CHAR
slouken@639
    54
	DWORD ptms_id;			// sample identification, 'PTMS' or 0x534d5450
slouken@639
    55
} PTMSAMPLE;
slouken@639
    56
slouken@639
    57
#define SIZEOF_PTMSAMPLE	80
slouken@639
    58
slouken@639
    59
#pragma pack()
slouken@639
    60
slouken@639
    61
slouken@639
    62
static uint32_t BS2WORD(uint16_t w[2]) {
slouken@639
    63
	uint32_t u32 = (w[1] << 16) + w[0];
slouken@639
    64
	return(bswapLE32(u32));
slouken@639
    65
}
slouken@639
    66
slouken@639
    67
BOOL CSoundFile::ReadPTM(const BYTE *lpStream, DWORD dwMemLength)
slouken@639
    68
//---------------------------------------------------------------
slouken@639
    69
{
slouken@639
    70
	DWORD dwMemPos;
slouken@639
    71
	UINT nOrders;
slouken@639
    72
slouken@639
    73
	if ((!lpStream) || (dwMemLength < sizeof(PTMFILEHEADER))) return FALSE;
slouken@639
    74
	PTMFILEHEADER pfh = *(LPPTMFILEHEADER)lpStream;
slouken@639
    75
slouken@639
    76
	pfh.norders = bswapLE16(pfh.norders);
slouken@639
    77
	pfh.nsamples = bswapLE16(pfh.nsamples);
slouken@639
    78
	pfh.npatterns = bswapLE16(pfh.npatterns);
slouken@639
    79
	pfh.nchannels = bswapLE16(pfh.nchannels);
slouken@639
    80
	pfh.fileflags = bswapLE16(pfh.fileflags);
slouken@639
    81
	pfh.reserved2 = bswapLE16(pfh.reserved2);
slouken@639
    82
	pfh.ptmf_id = bswapLE32(pfh.ptmf_id);
slouken@639
    83
	for (UINT j=0; j<128; j++)
slouken@639
    84
        {
slouken@639
    85
	        pfh.patseg[j] = bswapLE16(pfh.patseg[j]);
slouken@639
    86
	}
slouken@639
    87
slouken@639
    88
	if ((pfh.ptmf_id != 0x464d5450) || (!pfh.nchannels)
slouken@639
    89
	 || (pfh.nchannels > 32)
slouken@639
    90
	 || (pfh.norders > 256) || (!pfh.norders)
slouken@639
    91
	 || (!pfh.nsamples) || (pfh.nsamples > 255)
slouken@639
    92
	 || (!pfh.npatterns) || (pfh.npatterns > 128)
slouken@639
    93
	 || (SIZEOF_PTMFILEHEADER+pfh.nsamples*SIZEOF_PTMSAMPLE >= (int)dwMemLength)) return FALSE;
slouken@639
    94
	memcpy(m_szNames[0], pfh.songname, 28);
slouken@639
    95
	m_szNames[0][28] = 0;
slouken@639
    96
	m_nType = MOD_TYPE_PTM;
slouken@639
    97
	m_nChannels = pfh.nchannels;
slouken@639
    98
	m_nSamples = (pfh.nsamples < MAX_SAMPLES) ? pfh.nsamples : MAX_SAMPLES-1;
slouken@639
    99
	dwMemPos = SIZEOF_PTMFILEHEADER;
slouken@639
   100
	nOrders = (pfh.norders < MAX_ORDERS) ? pfh.norders : MAX_ORDERS-1;
slouken@639
   101
	memcpy(Order, pfh.orders, nOrders);
slouken@639
   102
	for (UINT ipan=0; ipan<m_nChannels; ipan++)
slouken@639
   103
	{
slouken@639
   104
		ChnSettings[ipan].nVolume = 64;
slouken@639
   105
		ChnSettings[ipan].nPan = ((pfh.chnpan[ipan] & 0x0F) << 4) + 4;
slouken@639
   106
	}
slouken@639
   107
	for (UINT ismp=0; ismp<m_nSamples; ismp++, dwMemPos += SIZEOF_PTMSAMPLE)
slouken@639
   108
	{
slouken@639
   109
		MODINSTRUMENT *pins = &Ins[ismp+1];
slouken@639
   110
		PTMSAMPLE *psmp = (PTMSAMPLE *)(lpStream+dwMemPos);
slouken@639
   111
slouken@639
   112
		lstrcpyn(m_szNames[ismp+1], psmp->samplename, 28);
slouken@639
   113
		memcpy(pins->name, psmp->filename, 12);
slouken@639
   114
		pins->name[12] = 0;
slouken@639
   115
		pins->nGlobalVol = 64;
slouken@639
   116
		pins->nPan = 128;
slouken@639
   117
		pins->nVolume = psmp->volume << 2;
slouken@639
   118
		pins->nC4Speed = bswapLE16(psmp->nC4Spd) << 1;
slouken@639
   119
		pins->uFlags = 0;
slouken@639
   120
		if ((psmp->sampletype & 3) == 1)
slouken@639
   121
		{
slouken@639
   122
			UINT smpflg = RS_PCM8D;
slouken@639
   123
			pins->nLength = BS2WORD(psmp->length);
slouken@639
   124
			pins->nLoopStart = BS2WORD(psmp->loopbeg);
slouken@639
   125
			pins->nLoopEnd = BS2WORD(psmp->loopend);
slouken@639
   126
			DWORD samplepos = BS2WORD(psmp->fileofs);
slouken@639
   127
			if (psmp->sampletype & 4) pins->uFlags |= CHN_LOOP;
slouken@639
   128
			if (psmp->sampletype & 8) pins->uFlags |= CHN_PINGPONGLOOP;
slouken@639
   129
			if (psmp->sampletype & 16)
slouken@639
   130
			{
slouken@639
   131
				pins->uFlags |= CHN_16BIT;
slouken@639
   132
				pins->nLength >>= 1;
slouken@639
   133
				pins->nLoopStart >>= 1;
slouken@639
   134
				pins->nLoopEnd >>= 1;
slouken@639
   135
				smpflg = RS_PTM8DTO16;
slouken@639
   136
			}
slouken@639
   137
			if ((pins->nLength) && (samplepos) && (samplepos < dwMemLength))
slouken@639
   138
			{
slouken@639
   139
				ReadSample(pins, smpflg, (LPSTR)(lpStream+samplepos), dwMemLength-samplepos);
slouken@639
   140
			}
slouken@639
   141
		}
slouken@639
   142
	}
slouken@639
   143
	// Reading Patterns
slouken@639
   144
	for (UINT ipat=0; ipat<pfh.npatterns; ipat++)
slouken@639
   145
	{
slouken@639
   146
		dwMemPos = ((UINT)pfh.patseg[ipat]) << 4;
slouken@639
   147
		if ((!dwMemPos) || (dwMemPos >= dwMemLength)) continue;
slouken@639
   148
		PatternSize[ipat] = 64;
slouken@639
   149
		if ((Patterns[ipat] = AllocatePattern(64, m_nChannels)) == NULL) break;
slouken@639
   150
		//
slouken@639
   151
		MODCOMMAND *m = Patterns[ipat];
slouken@639
   152
		for (UINT row=0; ((row < 64) && (dwMemPos < dwMemLength)); )
slouken@639
   153
		{
slouken@639
   154
			UINT b = lpStream[dwMemPos++];
slouken@639
   155
slouken@639
   156
			if (dwMemPos >= dwMemLength) break;
slouken@639
   157
			if (b)
slouken@639
   158
			{
slouken@639
   159
				UINT nChn = b & 0x1F;
slouken@639
   160
slouken@639
   161
				if (b & 0x20)
slouken@639
   162
				{
slouken@639
   163
					if (dwMemPos + 2 > dwMemLength) break;
slouken@639
   164
					m[nChn].note = lpStream[dwMemPos++];
slouken@639
   165
					m[nChn].instr = lpStream[dwMemPos++];
slouken@639
   166
				}
slouken@639
   167
				if (b & 0x40)
slouken@639
   168
				{
slouken@639
   169
					if (dwMemPos + 2 > dwMemLength) break;
slouken@639
   170
					m[nChn].command = lpStream[dwMemPos++];
slouken@639
   171
					m[nChn].param = lpStream[dwMemPos++];
slouken@639
   172
					if ((m[nChn].command == 0x0E) && ((m[nChn].param & 0xF0) == 0x80))
slouken@639
   173
					{
slouken@639
   174
						m[nChn].command = CMD_S3MCMDEX;
slouken@639
   175
					} else
slouken@639
   176
					if (m[nChn].command < 0x10)
slouken@639
   177
					{
slouken@639
   178
						ConvertModCommand(&m[nChn]);
slouken@639
   179
					} else
slouken@639
   180
					{
slouken@639
   181
						switch(m[nChn].command)
slouken@639
   182
						{
slouken@639
   183
						case 16:
slouken@639
   184
							m[nChn].command = CMD_GLOBALVOLUME;
slouken@639
   185
							break;
slouken@639
   186
						case 17:
slouken@639
   187
							m[nChn].command = CMD_RETRIG;
slouken@639
   188
							break;
slouken@639
   189
						case 18:
slouken@639
   190
							m[nChn].command = CMD_FINEVIBRATO;
slouken@639
   191
							break;
slouken@639
   192
						default:
slouken@639
   193
							m[nChn].command = 0;
slouken@639
   194
						}
slouken@639
   195
					}
slouken@639
   196
				}
slouken@639
   197
				if (b & 0x80)
slouken@639
   198
				{
slouken@639
   199
					if (dwMemPos >= dwMemLength) break;
slouken@639
   200
					m[nChn].volcmd = VOLCMD_VOLUME;
slouken@639
   201
					m[nChn].vol = lpStream[dwMemPos++];
slouken@639
   202
				}
slouken@639
   203
			} else
slouken@639
   204
			{
slouken@639
   205
				row++;
slouken@639
   206
				m += m_nChannels;
slouken@639
   207
			}
slouken@639
   208
		}
slouken@639
   209
	}
slouken@639
   210
	return TRUE;
slouken@639
   211
}
slouken@639
   212