]> icculus.org git repositories - btb/d2x.git/blob - misc/hmp.c
get midi working with sdl_mixer and the hmp2midi from d2x-rebirth
[btb/d2x.git] / misc / hmp.c
1 /*
2  * This code handles HMP files. It can:
3  * - Open/read/close HMP files
4  * - Play HMP via Windows MIDI
5  * - Convert HMP to MIDI for further use
6  * Based on work of Arne de Bruijn and the JFFEE project
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <conf.h>
11 #endif
12
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <physfs.h>
17
18 #include "hmp.h"
19 #include "u_mem.h"
20 #include "cfile.h"
21 #include "byteswap.h"
22
23
24 #ifdef WORDS_BIGENDIAN
25 #define MIDIINT(x) (x)
26 #define MIDISHORT(x) (x)
27 #else
28 #define MIDIINT(x) SWAPINT(x)
29 #define MIDISHORT(x) SWAPSHORT(x)
30 #endif
31
32
33 // READ/OPEN/CLOSE HMP
34
35 void hmp_close(hmp_file *hmp)
36 {
37         int i;
38
39         for (i = 0; i < hmp->num_trks; i++)
40                 if (hmp->trks[i].data)
41                         d_free(hmp->trks[i].data);
42         d_free(hmp);
43 }
44
45 hmp_file *hmp_open(const char *filename) {
46         int i, data, num_tracks, tempo;
47         char buf[256];
48         CFILE *fp;
49         hmp_file *hmp;
50         unsigned char *p;
51
52         if (!(fp = cfopen((char *)filename, "rb")))
53                 return NULL;
54
55         MALLOC(hmp, hmp_file, 1);
56         if (!hmp) {
57                 cfclose(fp);
58                 return NULL;
59         }
60
61         memset(hmp, 0, sizeof(*hmp));
62
63         if ((cfread(buf, 1, 8, fp) != 8) || (memcmp(buf, "HMIMIDIP", 8)))
64         {
65                 cfclose(fp);
66                 hmp_close(hmp);
67                 return NULL;
68         }
69
70         if (cfseek(fp, 0x30, SEEK_SET))
71         {
72                 cfclose(fp);
73                 hmp_close(hmp);
74                 return NULL;
75         }
76
77         if (cfread(&num_tracks, 4, 1, fp) != 1)
78         {
79                 cfclose(fp);
80                 hmp_close(hmp);
81                 return NULL;
82         }
83
84         if ((num_tracks < 1) || (num_tracks > HMP_TRACKS))
85         {
86                 cfclose(fp);
87                 hmp_close(hmp);
88                 return NULL;
89         }
90         hmp->num_trks = num_tracks;
91
92         if (cfseek(fp, 0x38, SEEK_SET))
93         {
94                 cfclose(fp);
95                 hmp_close(hmp);
96                 return NULL;
97         }
98         if (cfread(&tempo, 4, 1, fp) != 1)
99         {
100                 cfclose(fp);
101                 hmp_close(hmp);
102                 return NULL;
103         }
104         hmp->tempo = INTEL_INT(tempo);
105
106         if (cfseek(fp, 0x308, SEEK_SET))
107         {
108                 cfclose(fp);
109                 hmp_close(hmp);
110                 return NULL;
111         }
112
113         for (i = 0; i < num_tracks; i++) {
114                 if ((cfseek(fp, 4, SEEK_CUR)) || (cfread(&data, 4, 1, fp) != 1))
115                 {
116                         cfclose(fp);
117                         hmp_close(hmp);
118                         return NULL;
119                 }
120
121                 data -= 12;
122                 hmp->trks[i].len = data;
123
124                 MALLOC(p, unsigned char, data);
125                 if (!(hmp->trks[i].data = p))
126                 {
127                         cfclose(fp);
128                         hmp_close(hmp);
129                         return NULL;
130                 }
131
132                 /* finally, read track data */
133                 if ((cfseek(fp, 4, SEEK_CUR)) || (cfread(p, data, 1, fp) != 1))
134                 {
135                         cfclose(fp);
136                         hmp_close(hmp);
137                         return NULL;
138                 }
139         }
140         cfclose(fp);
141         return hmp;
142 }
143
144
145 // CONVERSION FROM HMP TO MIDI
146
147 static unsigned int hmptrk2mid(ubyte* data, int size, unsigned char **midbuf, unsigned int *midlen)
148 {
149         ubyte *dptr = data;
150         ubyte lc1 = 0,last_com = 0;
151         uint d;
152         int n1, n2;
153         unsigned int offset = *midlen;
154
155         while (data < dptr + size)
156         {
157                 if (data[0] & 0x80) {
158                         ubyte b = (data[0] & 0x7F);
159                         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 1);
160                         memcpy(&(*midbuf)[*midlen], &b, 1);
161                         *midlen += 1;
162                 }
163                 else {
164                         d = (data[0] & 0x7F);
165                         n1 = 0;
166                         while ((data[n1] & 0x80) == 0) {
167                                 n1++;
168                                 d += (data[n1] & 0x7F) << (n1 * 7);
169                                 }
170                         n1 = 1;
171                         while ((data[n1] & 0x80) == 0) {
172                                 n1++;
173                                 if (n1 == 4)
174                                         return 0;
175                                 }
176                         for(n2 = 0; n2 <= n1; n2++) {
177                                 ubyte b = (data[n1 - n2] & 0x7F);
178
179                                 if (n2 != n1)
180                                         b |= 0x80;
181                                 *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 1);
182                                 memcpy(&(*midbuf)[*midlen], &b, 1);
183                                 *midlen += 1;
184                                 }
185                         data += n1;
186                 }
187                 data++;
188                 if (*data == 0xFF) { //meta?
189                         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 3 + data[2]);
190                         memcpy(&(*midbuf)[*midlen], data, 3 + data[2]);
191                         *midlen += 3 + data[2];
192                         if (data[1] == 0x2F)
193                                 break;
194                 }
195                 else {
196                         lc1=data[0] ;
197                         if ((lc1&0x80) == 0)
198                                 return 0;
199                         switch (lc1 & 0xF0) {
200                                 case 0x80:
201                                 case 0x90:
202                                 case 0xA0:
203                                 case 0xB0:
204                                 case 0xE0:
205                                         if (lc1 != last_com)
206                                         {
207                                                 *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 1);
208                                                 memcpy(&(*midbuf)[*midlen], &lc1, 1);
209                                                 *midlen += 1;
210                                         }
211                                         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 2);
212                                         memcpy(&(*midbuf)[*midlen], data + 1, 2);
213                                         *midlen += 2;
214                                         data += 3;
215                                         break;
216                                 case 0xC0:
217                                 case 0xD0:
218                                         if (lc1 != last_com)
219                                         {
220                                                 *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 1);
221                                                 memcpy(&(*midbuf)[*midlen], &lc1, 1);
222                                                 *midlen += 1;
223                                         }
224                                         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 1);
225                                         memcpy(&(*midbuf)[*midlen], data + 1, 1);
226                                         *midlen += 1;
227                                         data += 2;
228                                         break;
229                                 default:
230                                         return 0;
231                                 }
232                         last_com = lc1;
233                 }
234         }
235         return (*midlen - offset);
236 }
237
238 ubyte tempo [19] = {'M','T','r','k',0,0,0,11,0,0xFF,0x51,0x03,0x18,0x80,0x00,0,0xFF,0x2F,0};
239
240 void hmp2mid(char *hmp_name, unsigned char **midbuf, unsigned int *midlen)
241 {
242         int mi, i;
243         short ms, time_div = 0xC0;
244         hmp_file *hmp=NULL;
245
246         hmp = hmp_open(hmp_name);
247         if (hmp == NULL)
248                 return;
249
250         *midlen = 0;
251         time_div = hmp->tempo*1.6;
252
253         // write MIDI-header
254         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 4);
255         memcpy(&(*midbuf)[*midlen], "MThd", 4);
256         *midlen += 4;
257         mi = MIDIINT(6);
258         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + sizeof(mi));
259         memcpy(&(*midbuf)[*midlen], &mi, sizeof(mi));
260         *midlen += sizeof(mi);
261         ms = MIDISHORT(1);
262         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + sizeof(ms));
263         memcpy(&(*midbuf)[*midlen], &ms, sizeof(ms));
264         *midlen += sizeof(ms);
265         ms = MIDISHORT(hmp->num_trks);
266         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + sizeof(ms));
267         memcpy(&(*midbuf)[*midlen], &ms, sizeof(ms));
268         *midlen += sizeof(ms);
269         ms = MIDISHORT(time_div);
270         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + sizeof(ms));
271         memcpy(&(*midbuf)[*midlen], &ms, sizeof(ms));
272         *midlen += sizeof(ms);
273         *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + sizeof(tempo));
274         memcpy(&(*midbuf)[*midlen], &tempo, sizeof(tempo));
275         *midlen += sizeof(tempo);
276
277         // tracks
278         for (i = 1; i < hmp->num_trks; i++)
279         {
280                 int midtrklenpos = 0;
281
282                 *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + 4);
283                 memcpy(&(*midbuf)[*midlen], "MTrk", 4);
284                 *midlen += 4;
285                 midtrklenpos = *midlen;
286                 mi = 0;
287                 *midbuf = (unsigned char *) d_realloc(*midbuf, *midlen + sizeof(mi));
288                 *midlen += sizeof(mi);
289                 mi = hmptrk2mid(hmp->trks[i].data, hmp->trks[i].len, midbuf, midlen);
290                 mi = MIDIINT(mi);
291                 memcpy(&(*midbuf)[midtrklenpos], &mi, 4);
292         }
293
294         hmp_close(hmp);
295 }