1 /*
2  * Schism Tracker - a cross-platform Impulse Tracker clone
3  * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4  * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5  * copyright (c) 2009 Storlek & Mrs. Brisby
6  * copyright (c) 2010-2012 Storlek
7  * URL: http://schismtracker.org/
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 #define NEED_BYTESWAP
24 
25 #include "headers.h"
26 #include "fmt.h"
27 
28 #include "it.h"
29 #include "song.h"
30 
31 #include "sndfile.h"
32 
33 #include <string.h>
34 #include <stdlib.h>
35 
36 /* --------------------------------------------------------------------- */
37 
38 #pragma pack(push, 1)
39 
40 struct xm_point {
41 	uint16_t ticks;        // Time in tracker ticks
42 	uint16_t val;        // Value from 0x00 to 0x40.
43 };
44 
45 struct xm_sample_header {
46 	uint32_t samplen;
47 	uint32_t loopstart;
48 	uint32_t looplen;
49 	uint8_t vol;
50 	signed char finetune;
51 	uint8_t type;
52 	uint8_t pan;
53 	signed char relnote;
54 	uint8_t res;
55 	char name[22];
56 };
57 
58 struct xi_sample_header {
59 	uint8_t snum[96];
60 
61 	union {
62 		uint16_t env[48];        // Occupies same mem as venv,penv
63 		struct {
64 			struct xm_point venv[12], penv[12];
65 		};
66 	};
67 
68 	uint8_t vnum, pnum;
69 	uint8_t vsustain, vloops, vloope, psustain, ploops, ploope;
70 	uint8_t vtype, ptype;
71 	uint8_t vibtype, vibsweep, vibdepth, vibrate;
72 	uint16_t volfade;
73 	uint8_t reserved1[0x16];
74 	uint16_t nsamples;
75 };
76 
77 struct xi_file_header {
78 	int8_t header[0x15];    // "Extended Instrument: "
79 	int8_t name[0x16];      // Name of instrument
80 	uint8_t magic;          // 0x1a, DOS EOF char so you can 'type file.xi'
81 	int8_t tracker[0x14];   // Name of tracker
82 	uint16_t version;       // big-endian 0x0102
83 	struct xi_sample_header xish;
84 	struct xm_sample_header sheader[];
85 };
86 #pragma pack(pop)
87 
validate_xi(const struct xi_file_header * xi,size_t length)88 static int validate_xi(const struct xi_file_header *xi, size_t length)
89 {
90 	if (length <= sizeof(struct xi_file_header))
91 		return 0;
92 	if (memcmp(xi->header, "Extended Instrument: ", 21) != 0)
93 		return 0;
94 	if (xi->magic != 0x1a)
95 		return 0;
96 	if (bswapLE16(xi->version) != 0x0102)
97 		return(0);
98 	return(1);
99 }
100 
101 /* --------------------------------------------------------------------- */
fmt_xi_read_info(dmoz_file_t * file,const uint8_t * data,size_t length)102 int fmt_xi_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
103 {
104 	struct xi_file_header *xi = (struct xi_file_header *) data;
105 
106 	if (!validate_xi(xi, length))
107 		return 0;
108 
109 	file->description = "FastTracker Instrument";
110 	file->title = strn_dup((const char *)xi->name, 22);
111 	file->type = TYPE_INST_XI;
112 	return 1;
113 }
114 
fmt_xi_load_instrument(const uint8_t * data,size_t length,int slot)115 int fmt_xi_load_instrument(const uint8_t *data, size_t length, int slot)
116 {
117 	const struct xi_file_header *xi = (const struct xi_file_header *) data;
118 	struct xi_sample_header xmsh;
119 	struct instrumentloader ii;
120 	song_instrument_t *g;
121 	const uint8_t *sampledata, *end;
122 	int k, prevtick;
123 
124 	if (!slot)
125 		return 0;
126 	if (!validate_xi(xi, length))
127 		return 0;
128 
129 	end = data + length;
130 
131 	song_delete_instrument(slot, 0);
132 
133 	g = instrument_loader_init(&ii, slot);
134 	memcpy(g->name, xi->name, 22);
135 	g->name[22] = '\0';
136 
137 	xmsh = xi->xish;
138 
139 	for (k = 0; k < 96; k++) {
140 		if (xmsh.snum[k] > 15)
141 			xmsh.snum[k] = 15;
142 		xmsh.snum[k] = instrument_loader_sample(&ii, xmsh.snum[k] + 1);
143 		g->note_map[k + 12] = k + 1 + 12;
144 		if (xmsh.snum[k])
145 			g->sample_map[k + 12] = xmsh.snum[k];
146 	}
147 
148 	for (k = 0; k < 12; k++) {
149 		g->note_map[k] = 0;
150 		g->sample_map[k] = 0;
151 		g->note_map[k + 108] = 0;
152 		g->sample_map[k + 108] = 0;
153 	}
154 
155 	// bswap all volume and panning envelope points
156 	for (k = 0; k < 48; k++)
157 		xmsh.env[k] = bswapLE16(xmsh.env[k]);
158 
159 	// Set up envelope types in instrument
160 	if (xmsh.vtype & 0x01) g->flags |= ENV_VOLUME;
161 	if (xmsh.vtype & 0x02) g->flags |= ENV_VOLSUSTAIN;
162 	if (xmsh.vtype & 0x04) g->flags |= ENV_VOLLOOP;
163 	if (xmsh.ptype & 0x01) g->flags |= ENV_PANNING;
164 	if (xmsh.ptype & 0x02) g->flags |= ENV_PANSUSTAIN;
165 	if (xmsh.ptype & 0x04) g->flags |= ENV_PANLOOP;
166 
167 	prevtick = -1;
168 	// Copy envelopes into instrument
169 	for (k = 0; k < xmsh.vnum; k++) {
170 		if (xmsh.venv[k].ticks < prevtick)
171 			prevtick++;
172 		else
173 			prevtick = xmsh.venv[k].ticks;
174 		g->vol_env.ticks[k] = prevtick;
175 		g->vol_env.values[k] = xmsh.venv[k].val;
176 	}
177 
178 	prevtick = -1;
179 	for (k = 0; k < xmsh.pnum; k++) {
180 		if (xmsh.penv[k].ticks < prevtick)
181 			prevtick++;
182 		else
183 			prevtick = xmsh.penv[k].ticks;
184 		g->pan_env.ticks[k] = prevtick;
185 		g->pan_env.values[k] = xmsh.penv[k].val;
186 	}
187 
188 	g->vol_env.loop_start = xmsh.vloops;
189 	g->vol_env.loop_end = xmsh.vloope;
190 	g->vol_env.sustain_start = xmsh.vsustain;
191 	g->vol_env.nodes = xmsh.vnum;
192 
193 	g->pan_env.loop_start = xmsh.ploops;
194 	g->pan_env.loop_end = xmsh.ploope;
195 	g->pan_env.sustain_start = xmsh.psustain;
196 	g->pan_env.nodes = xmsh.pnum;
197 
198 	xmsh.volfade = bswapLE16(xmsh.volfade);
199 	xmsh.nsamples = bswapLE16(xmsh.nsamples);
200 
201 	// Sample data begins at the end of the sample headers
202 	sampledata = (const uint8_t *) (xi->sheader + xmsh.nsamples);
203 
204 	for (k = 0; k < xmsh.nsamples; k++) {
205 		struct xm_sample_header xmss = xi->sheader[k];
206 		song_sample_t *smp;
207 		unsigned int rs, samplesize, n;
208 
209 		xmss.samplen = bswapLE32(xmss.samplen);
210 		xmss.loopstart = bswapLE32(xmss.loopstart);
211 		xmss.looplen = bswapLE32(xmss.looplen);
212 
213 		rs = SF_LE | SF_PCMD; // endianness; encoding
214 		rs |= (xmss.type & 0x20) ? SF_SS : SF_M; // channels
215 		rs |= (xmss.type & 0x10) ? SF_16 : SF_8; // bits
216 
217 		if (xmss.type & 0x10) {
218 			xmss.looplen >>= 1;
219 			xmss.loopstart >>= 1;
220 			xmss.samplen >>= 1;
221 		}
222 		if (xmss.type & 0x20) {
223 			xmss.looplen >>= 1;
224 			xmss.loopstart >>= 1;
225 			xmss.samplen >>= 1;
226 		}
227 		if (xmss.loopstart >= xmss.samplen)
228 			xmss.type &= ~3;
229 		xmss.looplen += xmss.loopstart;
230 		if (xmss.looplen > xmss.samplen)
231 			xmss.looplen = xmss.samplen;
232 		if (!xmss.looplen)
233 			xmss.type &= ~3;
234 
235 		n = instrument_loader_sample(&ii, k + 1);
236 		smp = song_get_sample(n);
237 		smp->flags = 0;
238 		memcpy(smp->name, xmss.name, 22);
239 		smp->name[21] = '\0';
240 
241 		samplesize = xmss.samplen;
242 		smp->length = samplesize;
243 		smp->loop_start = xmss.loopstart;
244 		smp->loop_end = xmss.looplen;
245 		if (smp->loop_end < smp->loop_start)
246 			smp->loop_end = smp->length;
247 		if (smp->loop_start >= smp->loop_end)
248 			smp->loop_start = smp->loop_end = 0;
249 		switch (xmss.type & 3) {
250 			case 3: case 2: smp->flags |= CHN_PINGPONGLOOP;
251 			case 1: smp->flags |= CHN_LOOP;
252 		}
253 		smp->volume = xmss.vol << 2;
254 		if (smp->volume > 256)
255 			smp->volume = 256;
256 		smp->global_volume = 64;
257 		smp->panning = xmss.pan;
258 		smp->flags |= CHN_PANNING;
259 		smp->vib_type = xmsh.vibtype;
260 		smp->vib_speed = xmsh.vibsweep;
261 		smp->vib_depth = xmsh.vibdepth;
262 		smp->vib_rate = xmsh.vibrate / 4; // XXX xm.c does not divide here, which is wrong?
263 
264 		smp->c5speed = transpose_to_frequency(xmss.relnote, xmss.finetune);
265 		sampledata += csf_read_sample(current_song->samples + n, rs, sampledata, (end-sampledata));
266 	}
267 
268 	return 1;
269 }
270 
271