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 
28 #include "sndfile.h"
29 #include "song.h"
30 #include "it_defs.h"
31 
32 /* --------------------------------------------------------------------- */
fmt_its_read_info(dmoz_file_t * file,const uint8_t * data,size_t length)33 int fmt_its_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
34 {
35 	struct it_sample *its;
36 
37 	if (!(length > 80 && memcmp(data, "IMPS", 4) == 0))
38 		return 0;
39 
40 	its = (struct it_sample *)data;
41 	file->smp_length = bswapLE32(its->length);
42 	file->smp_flags = 0;
43 
44 	if (its->flags & 2) {
45 		file->smp_flags |= CHN_16BIT;
46 	}
47 	if (its->flags & 16) {
48 		file->smp_flags |= CHN_LOOP;
49 		if (its->flags & 64)
50 			file->smp_flags |= CHN_PINGPONGLOOP;
51 	}
52 	if (its->flags & 32) {
53 		file->smp_flags |= CHN_SUSTAINLOOP;
54 		if (its->flags & 128)
55 			file->smp_flags |= CHN_PINGPONGSUSTAIN;
56 	}
57 
58 	if (its->dfp & 128) file->smp_flags |= CHN_PANNING;
59 	if (its->flags & 4) file->smp_flags |= CHN_STEREO;
60 
61 	file->smp_defvol = its->vol;
62 	file->smp_gblvol = its->gvl;
63 	file->smp_vibrato_speed = its->vis;
64 	file->smp_vibrato_depth = its->vid & 0x7f;
65 	file->smp_vibrato_rate = its->vir;
66 
67 	file->smp_loop_start = bswapLE32(its->loopbegin);
68 	file->smp_loop_end = bswapLE32(its->loopend);
69 	file->smp_speed = bswapLE32(its->C5Speed);
70 	file->smp_sustain_start = bswapLE32(its->susloopbegin);
71 	file->smp_sustain_end = bswapLE32(its->susloopend);
72 
73 	file->smp_filename = strn_dup((const char *)its->filename, 12);
74 	file->description = "Impulse Tracker Sample";
75 	file->title = strn_dup((const char *)data + 20, 25);
76 	file->type = TYPE_SAMPLE_EXTD;
77 
78 	return 1;
79 }
80 
load_its_sample(const uint8_t * header,const uint8_t * data,size_t length,song_sample_t * smp)81 int load_its_sample(const uint8_t *header, const uint8_t *data, size_t length, song_sample_t *smp)
82 {
83 	struct it_sample *its = (struct it_sample *)header;
84 	uint32_t format;
85 	uint32_t bp;
86 
87 	if (length < 80 || strncmp((const char *) header, "IMPS", 4) != 0)
88 		return 0;
89 	/* alright, let's get started */
90 	smp->length = bswapLE32(its->length);
91 	if ((its->flags & 1) == 0) {
92 		// sample associated with header
93 		return 0;
94 	}
95 
96 	// endianness (always little)
97 	format = SF_LE;
98 	// channels
99 	format |= (its->flags & 4) ? SF_SS : SF_M;
100 	if (its->flags & 8) {
101 		// compression algorithm
102 		format |= (its->cvt & 4) ? SF_IT215 : SF_IT214;
103 	} else {
104 		// signedness (or delta?)
105 		format |= (its->cvt & 4) ? SF_PCMD : (its->cvt & 1) ? SF_PCMS : SF_PCMU;
106 	}
107 	// bit width
108 	format |= (its->flags & 2) ? SF_16 : SF_8;
109 
110 	smp->global_volume = its->gvl;
111 	if (its->flags & 16) {
112 		smp->flags |= CHN_LOOP;
113 		if (its->flags & 64)
114 			smp->flags |= CHN_PINGPONGLOOP;
115 	}
116 	if (its->flags & 32) {
117 		smp->flags |= CHN_SUSTAINLOOP;
118 		if (its->flags & 128)
119 			smp->flags |= CHN_PINGPONGSUSTAIN;
120 	}
121 	smp->volume = its->vol * 4;
122 	strncpy(smp->name, (const char *) its->name, 25);
123 	smp->panning = (its->dfp & 127) * 4;
124 	if (its->dfp & 128)
125 		smp->flags |= CHN_PANNING;
126 	smp->loop_start = bswapLE32(its->loopbegin);
127 	smp->loop_end = bswapLE32(its->loopend);
128 	smp->c5speed = bswapLE32(its->C5Speed);
129 	smp->sustain_start = bswapLE32(its->susloopbegin);
130 	smp->sustain_end = bswapLE32(its->susloopend);
131 
132 	int vibs[] = {VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_RANDOM};
133 	smp->vib_type = vibs[its->vit & 3];
134 	smp->vib_rate = its->vir;
135 	smp->vib_depth = its->vid;
136 	smp->vib_speed = its->vis;
137 
138 	// sanity checks
139 	// (I should probably have more of these in general)
140 	if (smp->loop_start > smp->length) {
141 		smp->loop_start = smp->length;
142 		smp->flags &= ~(CHN_LOOP | CHN_PINGPONGLOOP);
143 	}
144 	if (smp->loop_end > smp->length) {
145 		smp->loop_end = smp->length;
146 		smp->flags &= ~(CHN_LOOP | CHN_PINGPONGLOOP);
147 	}
148 	if (smp->sustain_start > smp->length) {
149 		smp->sustain_start = smp->length;
150 		smp->flags &= ~(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN);
151 	}
152 	if (smp->sustain_end > smp->length) {
153 		smp->sustain_end = smp->length;
154 		smp->flags &= ~(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN);
155 	}
156 
157 	bp = bswapLE32(its->samplepointer);
158 
159 	// dumb casts :P
160 	return csf_read_sample((song_sample_t *) smp, format,
161 			(const char *) (data + bp),
162 			(uint32_t) (length - bp));
163 }
164 
fmt_its_load_sample(const uint8_t * data,size_t length,song_sample_t * smp)165 int fmt_its_load_sample(const uint8_t *data, size_t length, song_sample_t *smp)
166 {
167 	return load_its_sample(data, data, length, smp);
168 }
169 
save_its_header(disko_t * fp,song_sample_t * smp)170 void save_its_header(disko_t *fp, song_sample_t *smp)
171 {
172 	struct it_sample its = {};
173 
174 	its.id = bswapLE32(0x53504D49); // IMPS
175 	strncpy((char *) its.filename, smp->filename, 12);
176 	its.gvl = smp->global_volume;
177 	if (smp->data && smp->length)
178 		its.flags |= 1;
179 	if (smp->flags & CHN_16BIT)
180 		its.flags |= 2;
181 	if (smp->flags & CHN_STEREO)
182 		its.flags |= 4;
183 	if (smp->flags & CHN_LOOP)
184 		its.flags |= 16;
185 	if (smp->flags & CHN_SUSTAINLOOP)
186 		its.flags |= 32;
187 	if (smp->flags & CHN_PINGPONGLOOP)
188 		its.flags |= 64;
189 	if (smp->flags & CHN_PINGPONGSUSTAIN)
190 		its.flags |= 128;
191 	its.vol = smp->volume / 4;
192 	strncpy((char *) its.name, smp->name, 25);
193 	its.name[25] = 0;
194 	its.cvt = 1;                    // signed samples
195 	its.dfp = smp->panning / 4;
196 	if (smp->flags & CHN_PANNING)
197 		its.dfp |= 0x80;
198 	its.length = bswapLE32(smp->length);
199 	its.loopbegin = bswapLE32(smp->loop_start);
200 	its.loopend = bswapLE32(smp->loop_end);
201 	its.C5Speed = bswapLE32(smp->c5speed);
202 	its.susloopbegin = bswapLE32(smp->sustain_start);
203 	its.susloopend = bswapLE32(smp->sustain_end);
204 	//its.samplepointer = 42; - this will be filled in later
205 	its.vis = smp->vib_speed;
206 	its.vir = smp->vib_rate;
207 	its.vid = smp->vib_depth;
208 	switch (smp->vib_type) {
209 		case VIB_RANDOM:    its.vit = 3; break;
210 		case VIB_SQUARE:    its.vit = 2; break;
211 		case VIB_RAMP_DOWN: its.vit = 1; break;
212 		default:
213 		case VIB_SINE:      its.vit = 0; break;
214 	}
215 
216 	disko_write(fp, &its, sizeof(its));
217 }
218 
fmt_its_save_sample(disko_t * fp,song_sample_t * smp)219 int fmt_its_save_sample(disko_t *fp, song_sample_t *smp)
220 {
221 	save_its_header(fp, smp);
222 	csf_write_sample(fp, smp, SF_LE | SF_PCMS
223 			| ((smp->flags & CHN_16BIT) ? SF_16 : SF_8)
224 			| ((smp->flags & CHN_STEREO) ? SF_SS : SF_M),
225 			UINT32_MAX);
226 
227 	/* Write the sample pointer. In an ITS file, the sample data is right after the header,
228 	so its position in the file will be the same as the size of the header. */
229 	unsigned int tmp = bswapLE32(sizeof(struct it_sample));
230 	disko_seek(fp, 0x48, SEEK_SET);
231 	disko_write(fp, &tmp, 4);
232 
233 	return SAVE_SUCCESS;
234 }
235 
236