1 /*
2 * This source code is public domain.
3 *
4 * Authors: Olivier Lapicque <olivierl@jps.net>,
5 * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
6 */
7
8 ////////////////////////////////////////////////////////////
9 // 669 Composer / UNIS 669 module loader
10 ////////////////////////////////////////////////////////////
11
12 #include "stdafx.h"
13 #include "sndfile.h"
14
15 //#pragma warning(disable:4244)
16
17 typedef struct tagFILEHEADER669
18 {
19 WORD sig; // 'if' or 'JN'
20 signed char songmessage[108]; // Song Message
21 BYTE samples; // number of samples (1-64)
22 BYTE patterns; // number of patterns (1-128)
23 BYTE restartpos;
24 BYTE orders[128];
25 BYTE tempolist[128];
26 BYTE breaks[128];
27 } FILEHEADER669;
28
29
30 typedef struct tagSAMPLE669
31 {
32 BYTE filename[13];
33 BYTE length[4]; // when will somebody think about DWORD align ???
34 BYTE loopstart[4];
35 BYTE loopend[4];
36 } SAMPLE669;
37
lengthArrayToDWORD(const BYTE length[4])38 static DWORD lengthArrayToDWORD(const BYTE length[4]) {
39 DWORD len = (length[3] << 24) +
40 (length[2] << 16) +
41 (length[1] << 8) +
42 (length[0]);
43
44 return(len);
45 }
46
47
Read669(const BYTE * lpStream,DWORD dwMemLength)48 BOOL CSoundFile::Read669(const BYTE *lpStream, DWORD dwMemLength)
49 //---------------------------------------------------------------
50 {
51 BOOL b669Ext;
52 const FILEHEADER669 *pfh = (const FILEHEADER669 *)lpStream;
53 const SAMPLE669 *psmp = (const SAMPLE669 *)(lpStream + 0x1F1);
54 DWORD dwMemPos = 0;
55
56 if ((!lpStream) || (dwMemLength < sizeof(FILEHEADER669))) return FALSE;
57 if ((bswapLE16(pfh->sig) != 0x6669) && (bswapLE16(pfh->sig) != 0x4E4A)) return FALSE;
58 b669Ext = (bswapLE16(pfh->sig) == 0x4E4A) ? TRUE : FALSE;
59 if ((!pfh->samples) || (pfh->samples > 64) || (pfh->restartpos >= 128)
60 || (!pfh->patterns) || (pfh->patterns > 128)) return FALSE;
61 DWORD dontfuckwithme = 0x1F1 + pfh->samples * sizeof(SAMPLE669) + pfh->patterns * 0x600;
62 if (dontfuckwithme > dwMemLength) return FALSE;
63 for (UINT ichk=0; ichk<pfh->samples; ichk++)
64 {
65 DWORD len = lengthArrayToDWORD(psmp[ichk].length);
66 dontfuckwithme += len;
67 }
68 if (dontfuckwithme > dwMemLength) return FALSE;
69 // That should be enough checking: this must be a 669 module.
70 m_nType = MOD_TYPE_669;
71 m_dwSongFlags |= SONG_LINEARSLIDES;
72 m_nMinPeriod = 28 << 2;
73 m_nMaxPeriod = 1712 << 3;
74 m_nDefaultTempo = 125;
75 m_nDefaultSpeed = 6;
76 m_nChannels = 8;
77 memcpy(m_szNames[0], pfh->songmessage, 16);
78 m_nSamples = pfh->samples;
79 for (UINT nins=1; nins<=m_nSamples; nins++, psmp++)
80 {
81 DWORD len = lengthArrayToDWORD(psmp->length);
82 DWORD loopstart = lengthArrayToDWORD(psmp->loopstart);
83 DWORD loopend = lengthArrayToDWORD(psmp->loopend);
84 if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
85 if ((loopend > len) && (!loopstart)) loopend = 0;
86 if (loopend > len) loopend = len;
87 if (loopstart + 4 >= loopend) loopstart = loopend = 0;
88 Ins[nins].nLength = len;
89 Ins[nins].nLoopStart = loopstart;
90 Ins[nins].nLoopEnd = loopend;
91 if (loopend) Ins[nins].uFlags |= CHN_LOOP;
92 memcpy(m_szNames[nins], psmp->filename, 13);
93 Ins[nins].nVolume = 256;
94 Ins[nins].nGlobalVol = 64;
95 Ins[nins].nPan = 128;
96 }
97 // Song Message
98 m_lpszSongComments = new char[109];
99 memcpy(m_lpszSongComments, pfh->songmessage, 108);
100 m_lpszSongComments[108] = 0;
101 // Reading Orders
102 memcpy(Order, pfh->orders, 128);
103 m_nRestartPos = pfh->restartpos;
104 if (Order[m_nRestartPos] >= pfh->patterns) m_nRestartPos = 0;
105 // Reading Pattern Break Locations
106 for (UINT npan=0; npan<8; npan++)
107 {
108 ChnSettings[npan].nPan = (npan & 1) ? 0x30 : 0xD0;
109 ChnSettings[npan].nVolume = 64;
110 }
111 // Reading Patterns
112 dwMemPos = 0x1F1 + pfh->samples * 25;
113 for (UINT npat=0; npat<pfh->patterns; npat++)
114 {
115 Patterns[npat] = AllocatePattern(64, m_nChannels);
116 if (!Patterns[npat]) break;
117 PatternSize[npat] = 64;
118 MODCOMMAND *m = Patterns[npat];
119 const BYTE *p = lpStream + dwMemPos;
120 for (UINT row=0; row<64; row++)
121 {
122 MODCOMMAND *mspeed = m;
123 if ((row == pfh->breaks[npat]) && (row != 63))
124 {
125 for (UINT i=0; i<8; i++)
126 {
127 m[i].command = CMD_PATTERNBREAK;
128 m[i].param = 0;
129 }
130 }
131 for (UINT n=0; n<8; n++, m++, p+=3)
132 {
133 UINT note = p[0] >> 2;
134 UINT instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
135 UINT vol = p[1] & 0x0F;
136 if (p[0] < 0xFE)
137 {
138 m->note = note + 37;
139 m->instr = instr + 1;
140 }
141 if (p[0] <= 0xFE)
142 {
143 m->volcmd = VOLCMD_VOLUME;
144 m->vol = (vol << 2) + 2;
145 }
146 if (p[2] != 0xFF)
147 {
148 UINT command = p[2] >> 4;
149 UINT param = p[2] & 0x0F;
150 switch(command)
151 {
152 case 0x00: command = CMD_PORTAMENTOUP; break;
153 case 0x01: command = CMD_PORTAMENTODOWN; break;
154 case 0x02: command = CMD_TONEPORTAMENTO; break;
155 case 0x03: command = CMD_MODCMDEX; param |= 0x50; break;
156 case 0x04: command = CMD_VIBRATO; param |= 0x40; break;
157 case 0x05: if (param) command = CMD_SPEED; else command = 0; param += 2; break;
158 case 0x06: if (param == 0) { command = CMD_PANNINGSLIDE; param = 0xFE; }
159 else if (param == 1) { command = CMD_PANNINGSLIDE; param = 0xEF; }
160 else command = 0;
161 break;
162 default: command = 0;
163 }
164 if (command)
165 {
166 if (command == CMD_SPEED) mspeed = NULL;
167 m->command = command;
168 m->param = param;
169 }
170 }
171 }
172 if ((!row) && (mspeed))
173 {
174 for (UINT i=0; i<8; i++) if (!mspeed[i].command)
175 {
176 mspeed[i].command = CMD_SPEED;
177 mspeed[i].param = pfh->tempolist[npat] + 2;
178 break;
179 }
180 }
181 }
182 dwMemPos += 0x600;
183 }
184 // Reading Samples
185 for (UINT n=1; n<=m_nSamples; n++)
186 {
187 UINT len = Ins[n].nLength;
188 if (dwMemPos >= dwMemLength) break;
189 if (len > 4) ReadSample(&Ins[n], RS_PCM8U, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
190 dwMemPos += len;
191 }
192 return TRUE;
193 }
194