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
fmt_far_read_info(dmoz_file_t * file,const uint8_t * data,size_t length)33 int fmt_far_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
34 {
35 /* The magic for this format is truly weird (which I suppose is good, as the chance of it
36 being "accidentally" correct is pretty low) */
37 if (!(length > 47 && memcmp(data + 44, "\x0d\x0a\x1a", 3) == 0 && memcmp(data, "FAR\xfe", 4) == 0))
38 return 0;
39
40 file->description = "Farandole Module";
41 /*file->extension = str_dup("far");*/
42 file->title = strn_dup((const char *)data + 4, 40);
43 file->type = TYPE_MODULE_S3M;
44 return 1;
45 }
46
47 /* --------------------------------------------------------------------------------------------------------- */
48 /* This loader sucks. Mostly it was implemented based on what Modplug does, which is
49 kind of counterproductive, but I can't get Farandole to run in Dosbox to test stuff */
50
51 #pragma pack(push, 1)
52 struct far_header {
53 uint8_t magic[4];
54 char title[40];
55 uint8_t eof[3];
56 uint16_t header_len;
57 uint8_t version;
58 uint8_t onoff[16];
59 uint8_t editing_state[9]; // stuff we don't care about
60 uint8_t default_speed;
61 uint8_t chn_panning[16];
62 uint8_t pattern_state[4]; // more stuff we don't care about
63 uint16_t message_len;
64 };
65
66 struct far_sample {
67 char name[32];
68 uint32_t length;
69 uint8_t finetune;
70 uint8_t volume;
71 uint32_t loopstart;
72 uint32_t loopend;
73 uint8_t type;
74 uint8_t loop;
75 };
76 #pragma pack(pop)
77
78 static uint8_t far_effects[] = {
79 FX_NONE,
80 FX_PORTAMENTOUP,
81 FX_PORTAMENTODOWN,
82 FX_TONEPORTAMENTO,
83 FX_RETRIG,
84 FX_VIBRATO, // depth
85 FX_VIBRATO, // speed
86 FX_VOLUMESLIDE, // up
87 FX_VOLUMESLIDE, // down
88 FX_VIBRATO, // sustained (?)
89 FX_NONE, // actually slide-to-volume
90 FX_PANNING,
91 FX_SPECIAL, // note offset => note delay?
92 FX_NONE, // fine tempo down
93 FX_NONE, // fine tempo up
94 FX_SPEED,
95 };
96
far_import_note(song_note_t * note,const uint8_t data[4])97 static void far_import_note(song_note_t *note, const uint8_t data[4])
98 {
99 if (data[0] > 0 && data[0] < 85) {
100 note->note = data[0] + 36;
101 note->instrument = data[1] + 1;
102 }
103 if (data[2] & 0x0F) {
104 note->voleffect = VOLFX_VOLUME;
105 note->volparam = (data[2] & 0x0F) << 2; // askjdfjasdkfjasdf
106 }
107 note->param = data[3] & 0xf;
108 switch (data[3] >> 4) {
109 case 3: // porta to note
110 note->param <<= 2;
111 break;
112 case 4: // retrig
113 note->param = 6 / (1 + (note->param & 0xf)) + 1; // ugh?
114 break;
115 case 6: // vibrato speed
116 case 7: // volume slide up
117 case 0xb: // panning
118 note->param <<= 4;
119 break;
120 case 0xa: // volume-portamento (what!)
121 note->voleffect = VOLFX_VOLUME;
122 note->volparam = (note->param << 2) + 4;
123 break;
124 case 0xc: // note offset
125 note->param = 6 / (1 + (note->param & 0xf)) + 1;
126 note->param |= 0xd;
127 }
128 note->effect = far_effects[data[3] >> 4];
129 }
130
131
fmt_far_load_song(song_t * song,slurp_t * fp,unsigned int lflags)132 int fmt_far_load_song(song_t *song, slurp_t *fp, unsigned int lflags)
133 {
134 struct far_header fhdr;
135 struct far_sample fsmp;
136 song_sample_t *smp;
137 int nord, restartpos;
138 int n, pat, row, chn;
139 uint8_t orderlist[256];
140 uint16_t pattern_size[256];
141 uint8_t data[8];
142
143 slurp_read(fp, &fhdr, sizeof(fhdr));
144 if (memcmp(fhdr.magic, "FAR\xfe", 4) != 0 || memcmp(fhdr.eof, "\x0d\x0a\x1a", 3) != 0)
145 return LOAD_UNSUPPORTED;
146
147 fhdr.title[25] = '\0';
148 strcpy(song->title, fhdr.title);
149 fhdr.header_len = bswapLE16(fhdr.header_len);
150 fhdr.message_len = bswapLE16(fhdr.message_len);
151
152 for (n = 0; n < 16; n++) {
153 /* WHAT A GREAT WAY TO STORE THIS INFORMATION */
154 song->channels[n].panning = SHORT_PANNING(fhdr.chn_panning[n] & 0xf);
155 song->channels[n].panning *= 4; //mphack
156 if (!fhdr.onoff[n])
157 song->channels[n].flags |= CHN_MUTE;
158 }
159 for (; n < 64; n++)
160 song->channels[n].flags |= CHN_MUTE;
161
162 song->initial_speed = fhdr.default_speed;
163 song->initial_tempo = 80;
164
165 // to my knowledge, no other program is insane enough to save in this format
166 strcpy(song->tracker_id, "Farandole Composer");
167
168 /* Farandole's song message doesn't have line breaks, and the tracker runs in
169 some screwy ultra-wide text mode, so this displays more or less like crap. */
170 read_lined_message(song->message, fp, fhdr.message_len, 132);
171
172
173 slurp_seek(fp, sizeof(fhdr) + fhdr.message_len, SEEK_SET);
174
175 if ((lflags & (LOAD_NOSAMPLES | LOAD_NOPATTERNS)) == (LOAD_NOSAMPLES | LOAD_NOPATTERNS))
176 return LOAD_SUCCESS;
177
178 slurp_read(fp, orderlist, 256);
179 slurp_getc(fp); // supposed to be "number of patterns stored in the file"; apparently that's wrong
180 nord = slurp_getc(fp);
181 restartpos = slurp_getc(fp);
182
183 nord = MIN(nord, MAX_ORDERS);
184 memcpy(song->orderlist, orderlist, nord);
185 memset(song->orderlist + nord, ORDER_LAST, MAX_ORDERS - nord);
186
187 slurp_read(fp, pattern_size, 256 * 2); // byteswapped later
188 slurp_seek(fp, fhdr.header_len - (869 + fhdr.message_len), SEEK_CUR);
189
190 for (pat = 0; pat < 256; pat++) {
191 int breakpos, rows;
192 song_note_t *note;
193
194 pattern_size[pat] = bswapLE16(pattern_size[pat]);
195
196 if (pat >= MAX_PATTERNS || pattern_size[pat] < (2 + 16 * 4)) {
197 slurp_seek(fp, pattern_size[pat], SEEK_CUR);
198 continue;
199 }
200 breakpos = slurp_getc(fp);
201 slurp_getc(fp); // apparently, this value is *not* used anymore!!! I will not support it!!
202
203 rows = (pattern_size[pat] - 2) / (16 * 4);
204 if (!rows)
205 continue;
206 note = song->patterns[pat] = csf_allocate_pattern(rows);
207 song->pattern_size[pat] = song->pattern_alloc_size[pat] = rows;
208 breakpos = breakpos && breakpos < rows - 2 ? breakpos + 1 : -1;
209 for (row = 0; row < rows; row++, note += 48) {
210 for (chn = 0; chn < 16; chn++, note++) {
211 slurp_read(fp, data, 4);
212 far_import_note(note, data);
213 }
214 if (row == breakpos)
215 note->effect = FX_PATTERNBREAK;
216 }
217 }
218 csf_insert_restart_pos(song, restartpos);
219
220 if (lflags & LOAD_NOSAMPLES)
221 return LOAD_SUCCESS;
222
223 slurp_read(fp, data, 8);
224 smp = song->samples + 1;
225 for (n = 0; n < 64; n++, smp++) {
226 if (!(data[n / 8] & (1 << (n % 8)))) /* LOLWHAT */
227 continue;
228 slurp_read(fp, &fsmp, sizeof(fsmp));
229 fsmp.name[25] = '\0';
230 strcpy(smp->name, fsmp.name);
231 smp->length = fsmp.length = bswapLE32(fsmp.length);
232 smp->loop_start = bswapLE32(fsmp.loopstart);
233 smp->loop_end = bswapLE32(fsmp.loopend);
234 smp->volume = fsmp.volume << 4; // "not supported", but seems to exist anyway
235 if (fsmp.type & 1) {
236 smp->length >>= 1;
237 smp->loop_start >>= 1;
238 smp->loop_end >>= 1;
239 }
240 if (smp->loop_end > smp->loop_start && (fsmp.loop & 8))
241 smp->flags |= CHN_LOOP;
242 smp->c5speed = 16726;
243 smp->global_volume = 64;
244 csf_read_sample(smp, SF_LE | SF_M | SF_PCMS | ((fsmp.type & 1) ? SF_16 : SF_8),
245 fp->data + fp->pos, fp->length - fp->pos);
246 slurp_seek(fp, fsmp.length, SEEK_CUR);
247 }
248
249 return LOAD_SUCCESS;
250 }
251
252