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 "fmt.h"
27 #include "song.h"
28 #include "tables.h"
29 #include "log.h"
30 
31 #include <stdint.h>
32 
33 /* --------------------------------------------------------------------- */
34 
35 #pragma pack(push, 1)
36 typedef struct mtm_header {
37 	char filever[4]; /* M T M \x10 */
38 	char title[20]; /* asciz */
39 	uint16_t ntracks;
40 	uint8_t last_pattern;
41 	uint8_t last_order; /* songlength - 1 */
42 	uint16_t msglen;
43 	uint8_t nsamples;
44 	uint8_t flags; /* always 0 */
45 	uint8_t rows; /* prob. 64 */
46 	uint8_t nchannels;
47 	uint8_t panpos[32];
48 } mtm_header_t;
49 
50 typedef struct mtm_sample {
51 	char name[22];
52 	uint32_t length, loop_start, loop_end;
53 	uint8_t finetune, volume, flags;
54 } mtm_sample_t;
55 #pragma pack(pop)
56 
57 /* --------------------------------------------------------------------- */
58 
59 int fmt_mtm_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
60 {
61 	if (!(length > 24 && memcmp(data, "MTM", 3) == 0))
62 		return 0;
63 
64 	file->description = "MultiTracker Module";
65 	/*file->extension = str_dup("mtm");*/
66 	file->title = strn_dup((const char *)data + 4, 20);
67 	file->type = TYPE_MODULE_MOD;
68 	return 1;
69 }
70 
71 /* --------------------------------------------------------------------------------------------------------- */
72 
73 static void mtm_unpack_track(const uint8_t *b, song_note_t *note, int rows)
74 {
75 	int n;
76 
77 	for (n = 0; n < rows; n++, note++, b += 3) {
78 		note->note = ((b[0] & 0xfc) ? ((b[0] >> 2) + 36 + 1) : NOTE_NONE);
79 		note->instrument = ((b[0] & 0x3) << 4) | (b[1] >> 4);
80 		note->voleffect = VOLFX_NONE;
81 		note->volparam = 0;
82 		note->effect = b[1] & 0xf;
83 		note->param = b[2];
84 		/* From mikmod: volume slide up always overrides slide down */
85 		if (note->effect == 0xa && (note->param & 0xf0))
86 			note->param &= 0xf0;
87 		csf_import_mod_effect(note, 0);
88 	}
89 }
90 
91 int fmt_mtm_load_song(song_t *song, slurp_t *fp, unsigned int lflags)
92 {
93 	uint8_t b[192];
94 	uint8_t nchan, nord, npat, nsmp;
95 	uint16_t ntrk, comment_len;
96 	int n, pat, chan, smp, rows, todo = 0;
97 	song_note_t *note;
98 	uint16_t tmp;
99 	uint32_t tmplong;
100 	song_note_t **trackdata, *tracknote;
101 	song_sample_t *sample;
102 
103 	slurp_read(fp, b, 3);
104 	if (memcmp(b, "MTM", 3) != 0)
105 		return LOAD_UNSUPPORTED;
106 	n = slurp_getc(fp);
107 	sprintf(song->tracker_id, "MultiTracker %d.%d", n >> 4, n & 0xf);
108 	slurp_read(fp, song->title, 20);
109 	song->title[20] = 0;
110 	slurp_read(fp, &ntrk, 2);
111 	ntrk = bswapLE16(ntrk);
112 	npat = slurp_getc(fp);
113 	nord = slurp_getc(fp) + 1;
114 
115 	slurp_read(fp, &comment_len, 2);
116 	comment_len = bswapLE16(comment_len);
117 
118 	nsmp = slurp_getc(fp);
119 	slurp_getc(fp); /* attribute byte (unused) */
120 	rows = slurp_getc(fp); /* beats per track (translation: number of rows in every pattern) */
121 	if (rows != 64)
122 		todo |= 64;
123 	rows = MIN(rows, 64);
124 	nchan = slurp_getc(fp);
125 
126 	if (slurp_eof(fp)) {
127 		return LOAD_FORMAT_ERROR;
128 	}
129 
130 	for (n = 0; n < 32; n++) {
131 		int pan = slurp_getc(fp) & 0xf;
132 		pan = SHORT_PANNING(pan);
133 		pan *= 4; //mphack
134 		song->channels[n].panning = pan;
135 	}
136 	for (n = nchan; n < MAX_CHANNELS; n++)
137 		song->channels[n].flags = CHN_MUTE;
138 
139 	/* samples */
140 	if (nsmp > MAX_SAMPLES) {
141 		log_appendf(4, " Warning: Too many samples");
142 	}
143 	for (n = 1, sample = song->samples + 1; n <= nsmp; n++, sample++) {
144 		if (n > MAX_SAMPLES) {
145 			slurp_seek(fp, 37, SEEK_CUR);
146 			continue;
147 		}
148 
149 		/* IT truncates .mtm sample names at the first \0 rather than the normal behavior
150 		of presenting them as spaces (k-achaet.mtm has some "junk" in the sample text) */
151 		char name[23];
152 		slurp_read(fp, name, 22);
153 		name[22] = '\0';
154 		strcpy(sample->name, name);
155 		slurp_read(fp, &tmplong, 4);
156 		sample->length = bswapLE32(tmplong);
157 		slurp_read(fp, &tmplong, 4);
158 		sample->loop_start = bswapLE32(tmplong);
159 		slurp_read(fp, &tmplong, 4);
160 		sample->loop_end = bswapLE32(tmplong);
161 		if ((sample->loop_end - sample->loop_start) > 2) {
162 			sample->flags |= CHN_LOOP;
163 		} else {
164 			/* Both Impulse Tracker and Modplug do this */
165 			sample->loop_start = 0;
166 			sample->loop_end = 0;
167 		}
168 		song->samples[n].c5speed = MOD_FINETUNE(slurp_getc(fp));
169 		sample->volume = slurp_getc(fp);
170 		sample->volume *= 4; //mphack
171 		sample->global_volume = 64;
172 		if (slurp_getc(fp) & 1) {
173 			todo |= 16;
174 			sample->flags |= CHN_16BIT;
175 			sample->length >>= 1;
176 			sample->loop_start >>= 1;
177 			sample->loop_end >>= 1;
178 		}
179 		song->samples[n].vib_type = 0;
180 		song->samples[n].vib_rate = 0;
181 		song->samples[n].vib_depth = 0;
182 		song->samples[n].vib_speed = 0;
183 	}
184 
185 	/* orderlist */
186 	slurp_read(fp, song->orderlist, 128);
187 	memset(song->orderlist + nord, ORDER_LAST, MAX_ORDERS - nord);
188 
189 	/* tracks */
190 	trackdata = mem_calloc(ntrk, sizeof(song_note_t *));
191 	for (n = 0; n < ntrk; n++) {
192 		slurp_read(fp, b, 3 * rows);
193 		trackdata[n] = mem_calloc(rows, sizeof(song_note_t));
194 		mtm_unpack_track(b, trackdata[n], rows);
195 	}
196 
197 	/* patterns */
198 	if (npat >= MAX_PATTERNS) {
199 		log_appendf(4, " Warning: Too many patterns");
200 	}
201 	for (pat = 0; pat <= npat; pat++) {
202 		// skip ones that can't be loaded
203 		if (pat >= MAX_PATTERNS) {
204 			slurp_seek(fp, 64, SEEK_CUR);
205 			continue;
206 		}
207 
208 		song->patterns[pat] = csf_allocate_pattern(MAX(rows, 32));
209 		song->pattern_size[pat] = song->pattern_alloc_size[pat] = 64;
210 		for (chan = 0; chan < 32; chan++) {
211 			slurp_read(fp, &tmp, 2);
212 			tmp = bswapLE16(tmp);
213 			if (tmp == 0) {
214 				continue;
215 			} else if (tmp > ntrk) {
216 				for (n = 0; n < ntrk; n++)
217 					free(trackdata[n]);
218 				free(trackdata);
219 				return LOAD_FORMAT_ERROR;
220 			}
221 			note = song->patterns[pat] + chan;
222 			tracknote = trackdata[tmp - 1];
223 			for (n = 0; n < rows; n++, tracknote++, note += MAX_CHANNELS)
224 				*note = *tracknote;
225 		}
226 		if (rows < 32) {
227 			/* stick a pattern break on the first channel with an empty effect column
228 			 * (XXX don't do this if there's already one in another column) */
229 			note = song->patterns[pat] + 64 * (rows - 1);
230 			while (note->effect || note->param)
231 				note++;
232 			note->effect = FX_PATTERNBREAK;
233 		}
234 	}
235 
236 	/* free willy */
237 	for (n = 0; n < ntrk; n++)
238 		free(trackdata[n]);
239 	free(trackdata);
240 
241 	read_lined_message(song->message, fp, comment_len, 40);
242 
243 	/* sample data */
244 	if (!(lflags & LOAD_NOSAMPLES)) {
245 		for (smp = 1; smp <= nsmp && smp <= MAX_SAMPLES; smp++) {
246 			uint32_t ssize;
247 
248 			if (song->samples[smp].length == 0)
249 				continue;
250 			ssize = csf_read_sample(song->samples + smp,
251 				(SF_LE | SF_PCMU | SF_M
252 				 | ((song->samples[smp].flags & CHN_16BIT) ? SF_16 : SF_8)),
253 				fp->data + fp->pos, fp->length - fp->pos);
254 			slurp_seek(fp, ssize, SEEK_CUR);
255 		}
256 	}
257 
258 	/* set the rest of the stuff */
259 	song->flags = SONG_ITOLDEFFECTS | SONG_COMPATGXX;
260 
261 //      if (ferror(fp)) {
262 //              return LOAD_FILE_ERROR;
263 //      }
264 
265 	if (todo & 64)
266 		log_appendf(2, " TODO: test this file with other players (beats per track != 64)");
267 	if (todo & 16)
268 		log_appendf(2, " TODO: double check 16 bit sample loading");
269 
270 	/* done! */
271 	return LOAD_SUCCESS;
272 }
273 
274