1 /*
2  * Copyright 2010-2014 OpenXcom Developers.
3  *
4  * This file is part of OpenXcom.
5  *
6  * OpenXcom is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * OpenXcom is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "GMCat.h"
21 #include <vector>
22 #include "Music.h"
23 
24 namespace OpenXcom
25 {
26 
read_uint32_le(const unsigned char * p)27 static inline unsigned read_uint32_le (const unsigned char *p)
28 {
29 return ((unsigned) p[0]) + (((unsigned) p[1]) << 8)
30 	+ (((unsigned) p[2]) << 16) + (((unsigned) p[3]) << 24);
31 }
32 
33 /// MIDI sequence.
34 struct seq {
35 	unsigned size;
36 	const unsigned char *data;
37 };
38 
39 /// MIDI track.
40 struct track {
41 	struct seq seq;
42 	unsigned channel;
43 };
44 
45 /// MIDI stream.
46 struct gmstream {
47 	int tempo, nsubs, ntracks;
48 	struct seq subs [256];
49 	struct track tracks [256];
50 };
51 
gmext_read_stream(struct gmstream * p,unsigned int n,const unsigned char * data)52 static int gmext_read_stream (struct gmstream *p,
53 	unsigned int n, const unsigned char *data)
54 {
55 	if (!n--)
56 		return -1;
57 	p->tempo = *data++;
58 
59 	// subsequences
60 	if (!n--)
61 		return -1;
62 	p->nsubs = *data++;
63 
64 	for (int i=0; i<p->nsubs; ++i) {
65 		if (n < 4)
66 			return -1;
67 		unsigned int s = read_uint32_le(data);
68 		if (s < 4)
69 			return -1;
70 		p->subs[i].size = s - 4;
71 		p->subs[i].data = data + 4;
72 		n -= s;
73 		data += s;
74 	}
75 
76 	// tracks
77 	if (!n--)
78 		return -1;
79 	p->ntracks = *data++;
80 
81 	for (int i=0; i<p->ntracks; ++i) {
82 		if (n-- < 5)
83 			return -1;
84 		p->tracks[i].channel = *data++;
85 		unsigned int s = read_uint32_le(data);
86 		if (s < 4)
87 			return -1;
88 		p->tracks[i].seq.size = s - 4;
89 		p->tracks[i].seq.data = data + 4;
90 		n -= s;
91 		data += s;
92 	}
93 
94 	return n ? -1 : 0;
95 }
96 
gmext_write_int16(std::vector<unsigned char> & midi,unsigned int n)97 static inline void gmext_write_int16 (std::vector<unsigned char> &midi,
98 	unsigned int n)
99 {
100 	midi.push_back(n >> 8);
101 	midi.push_back(n);
102 }
103 
gmext_write_delta(std::vector<unsigned char> & midi,unsigned int delta)104 static inline void gmext_write_delta (std::vector<unsigned char> &midi,
105 	unsigned int delta)
106 {
107 	unsigned char data[4];
108 	unsigned int i = 0;
109 
110 	delta &= ((1<<28)-1);
111 	do {
112 		data[i++] = delta & 0x7F;
113 		delta >>= 7;
114 	} while (delta > 0);
115 
116 	while (--i)
117 		midi.push_back(data[i] | 0x80);
118 
119 	midi.push_back(data[0]);
120 }
121 
gmext_write_tempo_ev(std::vector<unsigned char> & midi,unsigned int tempo)122 static inline void gmext_write_tempo_ev (std::vector<unsigned char> &midi,
123 	unsigned int tempo)
124 {
125 	midi.push_back(0xFF);
126 	midi.push_back(0x51);
127 	midi.push_back(3);
128 	tempo = 60000000 / tempo;
129 	midi.push_back(tempo >> 16);
130 	midi.push_back(tempo >> 8);
131 	midi.push_back(tempo);
132 }
133 
gmext_write_end_ev(std::vector<unsigned char> & midi)134 static inline void gmext_write_end_ev (std::vector<unsigned char> &midi)
135 {
136 	midi.push_back(0xFF);
137 	midi.push_back(0x2F);
138 	midi.push_back(0);
139 }
140 
141 static const unsigned int volume [0x80] = {
142 	100,100,100,100,100, 90,100,100,100,100,100, 90,100,100,100,100,
143 	100,100, 85,100,100,100,100,100,100,100,100,100, 90,90, 110, 80,
144 	100,100,100, 90, 70,100,100,100,100,100,100,100,100,100,100,100,
145 	100,100, 90,100,100,100,100,100,100,120,100,100,100,120,100,127,
146 	100,100, 90,100,100,100,100,100,100, 95,100,100,100,100,100,100,
147 	100,100,100,100,100,100,100,115,100,100,100,100,100,100,100,100,
148 	100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
149 	100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
150 };
151 
152 /// Output status.
153 struct output_status {
154 	unsigned int delta;
155 	unsigned int patch;
156 	unsigned char prevcmd;
157 };
158 
gmext_write_sequence(std::vector<unsigned char> & midi,const struct gmstream * stream,unsigned int channel,const struct seq * seq,struct output_status * status)159 static int gmext_write_sequence (std::vector<unsigned char> &midi,
160 	const struct gmstream *stream, unsigned int channel,
161 	const struct seq *seq, struct output_status *status)
162 {
163 	const unsigned char *data = seq->data;
164 	unsigned int left = seq->size;
165 
166 	unsigned char cmd = -1;
167 
168 	while (left) {
169 
170 		// read delta
171 		unsigned int ndelta = 0;
172 
173 		for (int i=0; ; ) {
174 			unsigned char c = *data++;
175 			left--;
176 			ndelta += c & 0x7F;
177 			if (!(c & 0x80))
178 				break;
179 			if ((++i == 4) || !left)
180 				return -1;
181 			ndelta <<= 7;
182 		}
183 
184 		status->delta += ndelta;
185 
186 		// read cmd byte
187 		if (!left)
188 			return -1;
189 
190 		if (*data & 0x80) {
191 			// actual cmd byte
192 			cmd = *data++;
193 			left--;
194 			switch (cmd) {
195 				case 0xFF:	  // end track
196 				case 0xFD:	  // end subsequence
197 					return 0;
198 				case 0xFE:	  // insert subsequence
199 					if (!left--)
200 						return -1;
201 					if (*data >= stream->nsubs)
202 						// invalid subsequence
203 						return -1;
204 					if (gmext_write_sequence(midi,
205 						stream, channel,
206 						&stream->subs[*data++], status)
207 							== -1)
208 						return -1;
209 					cmd = 0;
210 					continue;
211 				default:
212 					cmd &= 0xF0;
213 			}
214 		} else if (cmd == 0)
215 			return -1;	  // invalid running mode
216 
217 		if (!left--)
218 			return -1;
219 		unsigned char data1 = *data++;
220 
221 		switch (cmd) {
222 
223 			case 0x80:
224 			case 0x90: {
225 				if (!left--)
226 					return -1;
227 				unsigned char data2 = *data++;
228 				if (data2)
229 					data2 = (unsigned int) data2 *
230 						(channel==9 ? 80 : volume[status->patch]) >> 7;
231 				gmext_write_delta(midi, status->delta);
232 				midi.push_back(cmd | channel);
233 				midi.push_back(data1);
234 				midi.push_back(data2);
235 				} break;
236 
237 			case 0xC0:
238 				if (data1 == 0x7E)
239 					return 0;	   // restart stream
240 				status->patch = data1;
241 				if ((data1 == 0x57) || (data1 == 0x3F))
242 					data1 = 0x3E;
243 				gmext_write_delta(midi, status->delta);
244 				midi.push_back(cmd | channel);
245 				midi.push_back(data1);
246 				break;
247 
248 			case 0xB0: {
249 				if (!left--)
250 					return -1;
251 				unsigned char data2 = *data++;
252 				if (data1 == 0x7E)
253 					continue;
254 				if (!data1) {
255 					if (!data2)
256 						continue;
257 					gmext_write_delta(midi, status->delta);
258 					gmext_write_tempo_ev(midi, 2*data2);
259 					break;
260 				}
261 				if (data1 == 0x5B)
262 					data2 = 0x1E;
263 				gmext_write_delta(midi, status->delta);
264 				midi.push_back(cmd | channel);
265 				midi.push_back(data1);
266 				midi.push_back(data2);
267 				} break;
268 
269 			case 0xE0: {
270 				if (!left--)
271 					return -1;
272 				unsigned char data2 = *data++;
273 				gmext_write_delta(midi, status->delta);
274 				midi.push_back(cmd | channel);
275 				midi.push_back(data1);
276 				midi.push_back(data2);
277 				} break;
278 
279 			default:		// unhandled cmd byte
280 				return -1;
281 		}
282 
283 		status->delta = 0;
284 	}
285 
286 	return 0;
287 }
288 
gmext_write_midi(const struct gmstream * stream,std::vector<unsigned char> & midi)289 static int gmext_write_midi (const struct gmstream *stream,
290 	std::vector<unsigned char> &midi)
291 {
292 	// write MIDI file header
293 	static const unsigned char midi_file_signature[8] =
294 		{ 'M','T','h','d',0,0,0,6 };
295 	for (int i=0; i<8; ++i)
296 		midi.push_back(midi_file_signature[i]);
297 	gmext_write_int16(midi, 1);
298 	gmext_write_int16(midi, stream->ntracks + 1);
299 	gmext_write_int16(midi, 24);
300 
301 	// write global tempo track
302 	static const unsigned char midi_track_header[8] =
303 		{ 'M','T','r','k',0,0,0,11 };
304 	for (int i=0; i<8; ++i)
305 		midi.push_back(midi_track_header[i]);
306 	gmext_write_delta(midi, 0);
307 	gmext_write_tempo_ev(midi, stream->tempo);
308 	gmext_write_delta(midi, 0);
309 	gmext_write_end_ev(midi);
310 
311 	// write tracks
312 	for (int j=0; j<stream->ntracks; ++j) {
313 
314 		// header
315 		for (int i=0; i<4; ++i)
316 			midi.push_back(midi_track_header[i]);
317 
318 		size_t loffset = midi.size();
319 		for (int i=0; i<4; ++i)
320 			midi.push_back(0);
321 
322 		// initial data
323 		static const unsigned char midi_track_init[8] =
324 			{ /* 0, 0xB0, */ 0x78, 0, 0, 0x79, 0, 0, 0x7B, 0 };
325 		midi.push_back(0);
326 		midi.push_back(0xB0 | stream->tracks[j].channel);
327 		for (int i=0; i<8; ++i)
328 			midi.push_back(midi_track_init[i]);
329 
330 		// body
331 		struct output_status status = { 0, 0, 0 };
332 		if (gmext_write_sequence(midi, stream,
333 				stream->tracks[j].channel,
334 				&stream->tracks[j].seq, &status) == -1)
335 			return -1;
336 
337 		// end of track
338 		gmext_write_delta(midi, status.delta);
339 		gmext_write_end_ev(midi);
340 
341 		// rewrite track length
342 		unsigned char *p = &midi[loffset];
343 		size_t length = midi.size() - loffset - 4;
344 		p[0] = length >> 24;
345 		p[1] = length >> 16;
346 		p[2] = length >> 8;
347 		p[3] = length;
348 	}
349 
350 	return 0;
351 }
352 
353 /**
354  * Loads a MIDI object into memory.
355  * @param i Music number to load.
356  * @return Pointer to the loaded music.
357  */
loadMIDI(unsigned int i)358 Music *GMCatFile::loadMIDI(unsigned int i)
359 {
360 	Music *music = new Music;
361 
362 	unsigned char *raw = static_cast<unsigned char*> ((void*)load(i));
363 
364 	if (!raw)
365 		return music;
366 
367 	// stream info
368 	struct gmstream stream;
369 	if (gmext_read_stream(&stream, getObjectSize(i), raw) == -1) {
370 		delete[] raw;
371 		return music;
372 	}
373 
374 	std::vector<unsigned char> midi;
375 	midi.reserve(65536);
376 
377 	// fields in stream still point into raw
378 	if (gmext_write_midi(&stream, midi) == -1) {
379 		delete[] raw;
380 		return music;
381 	}
382 
383 	delete[] raw;
384 
385 	music->load(&midi[0], midi.size());
386 
387 	return music;
388 }
389 
390 }
391