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 
24 #define NEED_BYTESWAP
25 #include "headers.h"
26 #include "slurp.h"
27 #include "fmt.h"
28 
29 #include "sndfile.h"
30 
31 /* --------------------------------------------------------------------- */
32 
33 struct header_669 {
34 	char sig[2];
35 	char songmessage[108];
36 	uint8_t samples;
37 	uint8_t patterns;
38 	uint8_t restartpos;
39 	uint8_t orders[128];
40 	uint8_t tempolist[128];
41 	uint8_t breaks[128];
42 };
43 
fmt_669_read_info(dmoz_file_t * file,const uint8_t * data,size_t length)44 int fmt_669_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
45 {
46 	struct header_669 *header = (struct header_669 *) data;
47 	unsigned long i;
48 	const char *desc;
49 
50 	if (length < sizeof(struct header_669))
51 		return 0;
52 
53 	/* Impulse Tracker identifies any 669 file as a "Composer 669 Module",
54 	regardless of the signature tag. */
55 	if (memcmp(header->sig, "if", 2) == 0)
56 		desc = "Composer 669 Module";
57 	else if (memcmp(header->sig, "JN", 2) == 0)
58 		desc = "Extended 669 Module";
59 	else
60 		return 0;
61 
62 	if (header->samples == 0 || header->patterns == 0
63 	    || header->samples > 64 || header->patterns > 128
64 	    || header->restartpos > 127)
65 		return 0;
66 	for (i = 0; i < 128; i++)
67 		if (header->breaks[i] > 0x3f)
68 			return 0;
69 
70 	file->title = strn_dup(header->songmessage, 36);
71 	file->description = desc;
72 	/*file->extension = str_dup("669");*/
73 	file->type = TYPE_MODULE_S3M;
74 
75 	return 1;
76 }
77 
78 /* --------------------------------------------------------------------------------------------------------- */
79 
80 /* <opinion humble="false">This is better than IT's and MPT's 669 loaders</opinion> */
81 
fmt_669_load_song(song_t * song,slurp_t * fp,unsigned int lflags)82 int fmt_669_load_song(song_t *song, slurp_t *fp, unsigned int lflags)
83 {
84 	uint8_t b[16];
85 	uint16_t npat, nsmp;
86 	int n, pat, chan, smp, row;
87 	song_note_t *note;
88 	uint16_t tmp;
89 	uint32_t tmplong;
90 	uint8_t patspeed[128], breakpos[128];
91 	uint8_t restartpos;
92 	const char *tid;
93 	char titletmp[37];
94 
95 	slurp_read(fp, &tmp, 2);
96 	switch (bswapLE16(tmp)) {
97 	case 0x6669: // 'if'
98 		tid = "Composer 669";
99 		break;
100 	case 0x4e4a: // 'JN'
101 		tid = "UNIS 669";
102 		break;
103 	default:
104 		return LOAD_UNSUPPORTED;
105 	}
106 
107 	/* The message is 108 bytes, split onto 3 lines of 36 bytes each.
108 	Also copy the first part of the message into the title, because 669 doesn't actually have
109 	a dedicated title field... */
110 	read_lined_message(song->message, fp, 108, 36);
111 	strncpy(titletmp, song->message, 36);
112 	titletmp[36] = '\0';
113 	titletmp[strcspn(titletmp, "\r\n")] = '\0';
114 	trim_string(titletmp);
115 	titletmp[25] = '\0';
116 	strcpy(song->title, titletmp);
117 
118 	nsmp = slurp_getc(fp);
119 	npat = slurp_getc(fp);
120 	restartpos = slurp_getc(fp);
121 
122 	if (nsmp > 64 || npat > 128 || restartpos > 127)
123 		return LOAD_UNSUPPORTED;
124 
125 	strcpy(song->tracker_id, tid);
126 
127 	/* orderlist */
128 	slurp_read(fp, song->orderlist, 128);
129 
130 	/* stupid crap */
131 	slurp_read(fp, patspeed, 128);
132 	slurp_read(fp, breakpos, 128);
133 
134 	/* samples */
135 	for (smp = 1; smp <= nsmp; smp++) {
136 		slurp_read(fp, b, 13);
137 		b[13] = 0; /* the spec says it's supposed to be ASCIIZ, but some 669's use all 13 chars */
138 		strcpy(song->samples[smp].name, (char *) b);
139 		b[12] = 0; /* ... filename field only has room for 12 chars though */
140 		strcpy(song->samples[smp].filename, (char *) b);
141 
142 		slurp_read(fp, &tmplong, 4);
143 		song->samples[smp].length = bswapLE32(tmplong);
144 		slurp_read(fp, &tmplong, 4);
145 		song->samples[smp].loop_start = bswapLE32(tmplong);
146 		slurp_read(fp, &tmplong, 4);
147 		tmplong = bswapLE32(tmplong);
148 		if (tmplong > song->samples[smp].length)
149 			tmplong = 0;
150 		else
151 			song->samples[smp].flags |= CHN_LOOP;
152 		song->samples[smp].loop_end = tmplong;
153 
154 		song->samples[smp].c5speed = 8363;
155 		song->samples[smp].volume = 60;  /* ickypoo */
156 		song->samples[smp].volume *= 4; //mphack
157 		song->samples[smp].global_volume = 64;  /* ickypoo */
158 		song->samples[smp].vib_type = 0;
159 		song->samples[smp].vib_rate = 0;
160 		song->samples[smp].vib_depth = 0;
161 		song->samples[smp].vib_speed = 0;
162 	}
163 
164 	/* patterns */
165 	for (pat = 0; pat < npat; pat++) {
166 		uint8_t effect[8] = {
167 			255, 255, 255, 255, 255, 255, 255, 255
168 		};
169 		uint8_t rows = breakpos[pat] + 1;
170 		if (rows > 64) {
171 			return LOAD_UNSUPPORTED;
172 		}
173 
174 		note = song->patterns[pat] = csf_allocate_pattern(CLAMP(rows, 32, 64));
175 		song->pattern_size[pat] = song->pattern_alloc_size[pat] = CLAMP(rows, 32, 64);
176 
177 		for (row = 0; row < rows; row++, note += 56) {
178 			for (chan = 0; chan < 8; chan++, note++) {
179 				slurp_read(fp, b, 3);
180 
181 				switch (b[0]) {
182 				case 0xfe:     /* no note, only volume */
183 					note->voleffect = VOLFX_VOLUME;
184 					note->volparam = (b[1] & 0xf) << 2;
185 					break;
186 				case 0xff:     /* no note or volume */
187 					break;
188 				default:
189 					note->note = (b[0] >> 2) + 36 + 1;
190 					note->instrument = ((b[0] & 3) << 4 | (b[1] >> 4)) + 1;
191 					note->voleffect = VOLFX_VOLUME;
192 					note->volparam = (b[1] & 0xf) << 2;
193 					break;
194 				}
195 				/* (sloppily) import the stupid effect */
196 				if (b[2] != 0xff)
197 					effect[chan] = b[2];
198 				if (effect[chan] == 0xff)
199 					continue;
200 				note->param = effect[chan] & 0xf;
201 				switch (effect[chan] >> 4) {
202 				default:
203 					/* oops. never mind. */
204 					//printf("ignoring effect %X\n", effect[chan]);
205 					note->param = 0;
206 					break;
207 				case 0: /* A - portamento up */
208 					note->effect = FX_PORTAMENTOUP;
209 					break;
210 				case 1: /* B - portamento down */
211 					note->effect = FX_PORTAMENTODOWN;
212 					break;
213 				case 2: /* C - port to note */
214 					note->effect = FX_TONEPORTAMENTO;
215 					break;
216 				case 3: /* D - frequency adjust (??) */
217 					note->effect = FX_PORTAMENTOUP;
218 					if (note->param)
219 						note->param |= 0xf0;
220 					else
221 						note->param = 0xf1;
222 					effect[chan] = 0xff;
223 					break;
224 				case 4: /* E - frequency vibrato */
225 					note->effect = FX_VIBRATO;
226 					note->param |= 0x80;
227 					break;
228 				case 5: /* F - set tempo */
229 					/* TODO: param 0 is a "super fast tempo" in extended mode (?) */
230 					if (note->param)
231 						note->effect = FX_SPEED;
232 					effect[chan] = 0xff;
233 					break;
234 				case 6: /* G - subcommands (extended) */
235 					switch (note->param) {
236 					case 0: /* balance fine slide left */
237 						//TODO("test pan slide effect (P%dR%dC%d)", pat, row, chan);
238 						note->effect = FX_PANNINGSLIDE;
239 						note->param = 0x8F;
240 						break;
241 					case 1: /* balance fine slide right */
242 						//TODO("test pan slide effect (P%dR%dC%d)", pat, row, chan);
243 						note->effect = FX_PANNINGSLIDE;
244 						note->param = 0xF8;
245 						break;
246 					default:
247 						/* oops, nothing again */
248 						note->param = 0;
249 					}
250 					break;
251 				case 7: /* H - slot retrig */
252 					//TODO("test slot retrig (P%dR%dC%d)", pat, row, chan);
253 					note->effect = FX_RETRIG;
254 					break;
255 				}
256 			}
257 		}
258 		if (rows < 64) {
259 			/* skip the rest of the rows beyond the break position */
260 			slurp_seek(fp, 3 * 8 * (64 - rows), SEEK_CUR);
261 		}
262 
263 		/* handle the stupid pattern speed */
264 		note = song->patterns[pat];
265 		for (chan = 0; chan < 9; chan++, note++) {
266 			if (note->effect == FX_SPEED) {
267 				break;
268 			} else if (!note->effect) {
269 				note->effect = FX_SPEED;
270 				note->param = patspeed[pat];
271 				break;
272 			}
273 		}
274 		/* handle the break position */
275 		if (rows < 32) {
276 			//printf("adding pattern break for pattern %d\n", pat);
277 			note = song->patterns[pat] + MAX_CHANNELS * (rows - 1);
278 			for (chan = 0; chan < 9; chan++, note++) {
279 				if (!note->effect) {
280 					note->effect = FX_PATTERNBREAK;
281 					note->param = 0;
282 					break;
283 				}
284 			}
285 		}
286 	}
287 	csf_insert_restart_pos(song, restartpos);
288 
289 	/* sample data */
290 	if (!(lflags & LOAD_NOSAMPLES)) {
291 		for (smp = 1; smp <= nsmp; smp++) {
292 			uint32_t ssize;
293 
294 			if (song->samples[smp].length == 0)
295 				continue;
296 
297 			ssize = csf_read_sample(song->samples + smp, SF_LE | SF_M | SF_PCMU | SF_8,
298 				fp->data + fp->pos, fp->length - fp->pos);
299 			slurp_seek(fp, ssize, SEEK_CUR);
300 		}
301 	}
302 
303 	/* set the rest of the stuff */
304 	song->initial_speed = 4;
305 	song->initial_tempo = 78;
306 	song->flags = SONG_ITOLDEFFECTS | SONG_LINEARSLIDES;
307 
308 	song->pan_separation = 64;
309 	for (n = 0; n < 8; n++)
310 		song->channels[n].panning = (n & 1) ? 256 : 0; //mphack
311 	for (n = 8; n < 64; n++)
312 		song->channels[n].flags = CHN_MUTE;
313 
314 //      if (ferror(fp)) {
315 //              return LOAD_FILE_ERROR;
316 //      }
317 
318 	/* done! */
319 	return LOAD_SUCCESS;
320 }
321 
322