]> icculus.org git repositories - btb/d2x.git/blob - misc/hmp.c
Did not close hmp after MIDI conversion which caused some unfree memory - fixed
[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 #include <string.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <physfs.h>
12 #include "hmp.h"
13 #include "u_mem.h"
14 #include "cfile.h"
15
16 #ifdef WORDS_BIGENDIAN
17 #define MIDIINT(x) (x)
18 #define MIDISHORT(x) (x)
19 #else
20 #define MIDIINT(x) SWAPINT(x)
21 #define MIDISHORT(x) SWAPSHORT(x)
22 #endif
23
24
25 // READ/OPEN/CLOSE HMP
26
27 void hmp_close(hmp_file *hmp)
28 {
29         int i;
30
31         for (i = 0; i < hmp->num_trks; i++)
32                 if (hmp->trks[i].data)
33                         d_free(hmp->trks[i].data);
34         d_free(hmp);
35 }
36
37 hmp_file *hmp_open(const char *filename) {
38         int i;
39         char buf[256];
40         long data;
41         CFILE *fp;
42         hmp_file *hmp;
43         int num_tracks;
44         unsigned char *p;
45
46         if (!(fp = cfopen((char *)filename, "rb")))
47                 return NULL;
48
49         hmp = d_malloc(sizeof(hmp_file));
50         if (!hmp) {
51                 cfclose(fp);
52                 return NULL;
53         }
54
55         memset(hmp, 0, sizeof(*hmp));
56
57         if ((cfread(buf, 1, 8, fp) != 8) || (memcmp(buf, "HMIMIDIP", 8)))
58         {
59                 cfclose(fp);
60                 hmp_close(hmp);
61                 return NULL;
62         }
63
64         if (cfseek(fp, 0x30, SEEK_SET))
65         {
66                 cfclose(fp);
67                 hmp_close(hmp);
68                 return NULL;
69         }
70
71         if (cfread(&num_tracks, 4, 1, fp) != 1)
72         {
73                 cfclose(fp);
74                 hmp_close(hmp);
75                 return NULL;
76         }
77
78         if ((num_tracks < 1) || (num_tracks > HMP_TRACKS))
79         {
80                 cfclose(fp);
81                 hmp_close(hmp);
82                 return NULL;
83         }
84
85         hmp->num_trks = num_tracks;
86         hmp->tempo = 120;
87
88         if (cfseek(fp, 0x308, SEEK_SET))
89         {
90                 cfclose(fp);
91                 hmp_close(hmp);
92                 return NULL;
93         }
94
95         for (i = 0; i < num_tracks; i++) {
96                 if ((cfseek(fp, 4, SEEK_CUR)) || (cfread(&data, 4, 1, fp) != 1))
97                 {
98                         cfclose(fp);
99                         hmp_close(hmp);
100                         return NULL;
101                 }
102
103                 data -= 12;
104                 hmp->trks[i].len = data;
105
106                 if (!(p = hmp->trks[i].data = d_malloc(data)))
107                 {
108                         cfclose(fp);
109                         hmp_close(hmp);
110                         return NULL;
111                 }
112
113                 /* finally, read track data */
114                 if ((cfseek(fp, 4, SEEK_CUR)) || (cfread(p, data, 1, fp) != 1))
115                 {
116                         cfclose(fp);
117                         hmp_close(hmp);
118                         return NULL;
119                 }
120         }
121         cfclose(fp);
122         return hmp;
123 }
124
125
126 // CONVERSION FROM HMP TO MIDI
127
128 static int hmptrk2mid(ubyte* data, int size, PHYSFS_file *mid)
129 {
130         ubyte *dptr = data;
131         ubyte lc1 = 0,last_com = 0;
132         uint t = 0, d;
133         int n1, n2;
134         int offset = cftell(mid);
135
136         while (data < dptr + size)
137         {
138                 if (data[0] & 0x80) {
139                         ubyte b = (data[0] & 0x7F);
140                         PHYSFS_write(mid, &b, sizeof (b), 1);
141                         t+=b;
142                 }
143                 else {
144                         d = (data[0] & 0x7F);
145                         n1 = 0;
146                         while ((data[n1] & 0x80) == 0) {
147                                 n1++;
148                                 d += (data[n1] & 0x7F) << (n1 * 7);
149                                 }
150                         t += d;
151                         n1 = 1;
152                         while ((data[n1] & 0x80) == 0) {
153                                 n1++;
154                                 if (n1 == 4)
155                                         return 0;
156                                 }
157                         for(n2 = 0; n2 <= n1; n2++) {
158                                 ubyte b = (data[n1 - n2] & 0x7F);
159
160                                 if (n2 != n1)
161                                         b |= 0x80;
162                                 PHYSFS_write(mid, &b, sizeof(b), 1);
163                                 }
164                         data += n1;
165                 }
166                 data++;
167                 if (*data == 0xFF) { //meta?
168                         PHYSFS_write(mid, data, 3 + data [2], 1);
169                         if (data[1] == 0x2F)
170                                 break;
171                 }
172                 else {
173                         lc1=data[0] ;
174                         if ((lc1&0x80) == 0)
175                                 return 0;
176                         switch (lc1 & 0xF0) {
177                                 case 0x80:
178                                 case 0x90:
179                                 case 0xA0:
180                                 case 0xB0:
181                                 case 0xE0:
182                                         if (lc1 != last_com)
183                                                 PHYSFS_write(mid, &lc1, sizeof (lc1), 1);
184                                         PHYSFS_write(mid, data + 1, 2, 1);
185                                         data += 3;
186                                         break;
187                                 case 0xC0:
188                                 case 0xD0:
189                                         if (lc1 != last_com)
190                                                 PHYSFS_write(mid, &lc1, sizeof (lc1), 1);
191                                         PHYSFS_write(mid, data + 1, 1, 1);
192                                         data += 2;
193                                         break;
194                                 default:
195                                         return 0;
196                                 }
197                         last_com = lc1;
198                 }
199         }
200         return (cftell(mid) - offset);
201 }
202
203 ubyte tempo [19] = {'M','T','r','k',0,0,0,11,0,0xFF,0x51,0x03,0x18,0x80,0x00,0,0xFF,0x2F,0};
204
205 void hmp2mid(char *hmp_name, char *mid_name)
206 {
207         PHYSFS_file *mid=NULL;
208         int mi, i, loc;
209         short ms;
210         hmp_file *hmp=NULL;
211
212         hmp = hmp_open(hmp_name);
213         if (hmp == NULL)
214                 return;
215         mid = PHYSFSX_openWriteBuffered(mid_name);
216         if (mid == NULL)
217         {
218                 hmp_close(hmp);
219                 return;
220         }
221         // write MIDI-header
222         PHYSFS_write(mid, "MThd", 4, 1);
223         mi = MIDIINT(6);
224         PHYSFS_write(mid, &mi, sizeof(mi), 1);
225         ms = MIDISHORT(1);
226         PHYSFS_write(mid, &ms, sizeof(mi), 1);
227         ms = MIDISHORT(hmp->num_trks);
228         PHYSFS_write(mid, &ms, sizeof(ms), 1);
229         ms = MIDISHORT((short) 0xC0);
230         PHYSFS_write(mid, &ms, sizeof(ms), 1);
231         PHYSFS_write(mid, tempo, sizeof(tempo), 1);
232
233         // tracks
234         for (i = 1; i < hmp->num_trks; i++)
235         {
236                 PHYSFS_write(mid, "MTrk", 4, 1);
237                 loc = cftell(mid);
238                 mi = 0;
239                 PHYSFS_write(mid, &mi, sizeof(mi), 1);
240                 mi = hmptrk2mid(hmp->trks[i].data, hmp->trks[i].len, mid);
241                 mi = MIDIINT(mi);
242                 cfseek(mid, loc, SEEK_SET);
243                 PHYSFS_write(mid, &mi, sizeof(mi), 1);
244                 cfseek(mid, 0, SEEK_END);
245         }
246
247         hmp_close(hmp);
248         cfclose(mid);
249 }