2006-11-13 20:23:44 +00:00
|
|
|
/* hmp 2 midi file conversion. The conversion algorithm is based on the
|
|
|
|
conversion algorithms found in d1x, d2x-xl and jjffe. The code is mine.
|
|
|
|
|
|
|
|
Copyright 2006 Hans de Goede
|
|
|
|
|
|
|
|
This file and the acompanying hmp2mid.h file are free software;
|
|
|
|
you can redistribute them and/or modify them under the terms of the GNU
|
|
|
|
Library General Public License as published by the Free Software Foundation;
|
|
|
|
either version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
These files are distributed in the hope that they will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with these files; see the file COPYING.LIB. If not,
|
|
|
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2006-12-30 05:29:21 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <physfs.h>
|
2006-11-13 20:23:44 +00:00
|
|
|
#include "hmp2mid.h"
|
|
|
|
|
|
|
|
/* Some convience macros to keep the code below more readable */
|
|
|
|
|
|
|
|
#define HMP_READ(buf, count) \
|
|
|
|
{\
|
2006-12-30 05:29:21 +00:00
|
|
|
if (PHYSFS_read(hmp_in, buf, 1, count) != (count)) \
|
2006-11-13 20:23:44 +00:00
|
|
|
{ \
|
|
|
|
free(mid_track_buf); \
|
|
|
|
return hmp_read_error; \
|
|
|
|
} \
|
|
|
|
/* notice that when we are not actually in the track reading code this \
|
|
|
|
is bogus, but does no harm. And in the track code it helps :) */ \
|
|
|
|
track_length -= count; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define HMP_READ_DWORD(dest) \
|
|
|
|
{ \
|
|
|
|
unsigned char _buf[4]; \
|
|
|
|
HMP_READ(_buf, 4); \
|
|
|
|
*(dest) = (_buf[3] << 24) | (_buf[2] << 16) | (_buf[1] << 8) | _buf[0]; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MID_WRITE(buf, count) \
|
2006-12-30 05:29:21 +00:00
|
|
|
if (PHYSFS_write(mid_out, buf, 1, count) != (count)) \
|
2006-11-13 20:23:44 +00:00
|
|
|
{ \
|
|
|
|
free(mid_track_buf); \
|
|
|
|
snprintf(hmp2mid_error, sizeof(hmp2mid_error), mid_write_error_templ, \
|
2006-12-30 05:29:21 +00:00
|
|
|
PHYSFS_getLastError); \
|
2006-11-13 20:23:44 +00:00
|
|
|
return hmp2mid_error; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MID_TRACK_BUF(buf, count) \
|
|
|
|
{ \
|
|
|
|
if ((mid_track_buf_used + count) > mid_track_buf_size) \
|
|
|
|
{ \
|
|
|
|
void *tmp = mid_track_buf; \
|
|
|
|
mid_track_buf = realloc(mid_track_buf, mid_track_buf_size + 65536); \
|
|
|
|
if (!mid_track_buf) \
|
|
|
|
{ \
|
|
|
|
free(tmp); \
|
|
|
|
return "Error could not allocate midi track memory"; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
memcpy(mid_track_buf + mid_track_buf_used, buf, count); \
|
|
|
|
mid_track_buf_used += count; \
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const char *hmp_read_error = "Error reading from hmp file";
|
|
|
|
static const char *mid_write_error_templ = "Error writing to mid file: %s";
|
|
|
|
static char hmp2mid_error[512];
|
|
|
|
/* The beginning of the midi header */
|
|
|
|
static const char midi_header1[10] = { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1 };
|
|
|
|
/* The last 2 bytes of the midi header and track0 */
|
|
|
|
static const char midi_header2[21] = { 0, 0xC0, 'M', 'T', 'r', 'k', 0, 0, 0,
|
|
|
|
0x0B, 0, 0xFF, 0x51, 0x03, 0x18, 0x80, 0, 0, 0xFF, 0x2F, 0 };
|
|
|
|
|
2006-12-30 05:29:21 +00:00
|
|
|
const char *hmp2mid(PHYSFS_File *hmp_in, PHYSFS_File *mid_out)
|
2006-11-13 20:23:44 +00:00
|
|
|
{
|
|
|
|
unsigned char last_com = 0, buf[0x300];
|
|
|
|
unsigned int num_tracks, track_length = 0, i, t;
|
|
|
|
unsigned char *mid_track_buf = NULL;
|
|
|
|
int n1, n2, mid_track_buf_used, mid_track_buf_size = 0;
|
|
|
|
|
|
|
|
/* Read the header and verify that this is a hmp file */
|
|
|
|
HMP_READ(buf, 8);
|
|
|
|
buf[8] = 0;
|
|
|
|
if (strcmp((char *)buf, "HMIMIDIP"))
|
|
|
|
return "Error not a hmp file";
|
|
|
|
|
|
|
|
/* Discard the next 0x28 bytes, taking us to location 0x30 and read the
|
|
|
|
number of tracks there */
|
|
|
|
HMP_READ(buf, 0x30 - 8);
|
|
|
|
HMP_READ_DWORD(&num_tracks);
|
|
|
|
|
|
|
|
/* Discard bytes taking us to location 0x300 around which track 1 starts */
|
|
|
|
HMP_READ(buf, 0x300 - 0x34);
|
|
|
|
|
|
|
|
/* Now search for the end of track marker of track 0 */
|
|
|
|
HMP_READ(buf, 3);
|
|
|
|
while ((buf[0] != 0xFF) || (buf[1] != 0x2F) || (buf[2] != 0x00))
|
|
|
|
{
|
|
|
|
buf[0] = buf[1];
|
|
|
|
buf[1] = buf[2];
|
|
|
|
HMP_READ(buf + 2, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sofar so good, write out the midi header */
|
|
|
|
MID_WRITE(midi_header1, sizeof(midi_header1));
|
|
|
|
/* number of tracks is a 16 bit MSB word, better not have more then 65535
|
|
|
|
tracks :) */
|
|
|
|
buf[0] = (num_tracks >> 8) & 0xFF;
|
|
|
|
buf[1] = num_tracks & 0xFF;
|
|
|
|
MID_WRITE(buf, 2);
|
|
|
|
/* Write the last word of the header and track0 */
|
|
|
|
MID_WRITE(midi_header2, sizeof(midi_header2));
|
|
|
|
|
|
|
|
/* Read and convert all the tracks */
|
|
|
|
for (i = 1; i < num_tracks; i++)
|
|
|
|
{
|
|
|
|
/* Read and verify hmp track number */
|
|
|
|
HMP_READ_DWORD(&t);
|
|
|
|
if (t != i)
|
|
|
|
{
|
|
|
|
free(mid_track_buf);
|
|
|
|
return "Error invalid hmp track number";
|
|
|
|
}
|
|
|
|
/* Read number of bytes in this track */
|
|
|
|
HMP_READ_DWORD(&track_length);
|
|
|
|
/* Above we've already read the first 8 bytes of this track, adjust
|
|
|
|
track_lenght accordingly. */
|
|
|
|
track_length -= 8;
|
|
|
|
/* Read and skip the next 4 unidentified header bytes */
|
|
|
|
HMP_READ(buf, 4);
|
|
|
|
|
|
|
|
/* Sofar so good write midi track header */
|
|
|
|
MID_WRITE("MTrk", 4);
|
|
|
|
|
|
|
|
/* Now we need to write the track length, but we don't know that yet, so
|
|
|
|
the track gets written to a dynamicly growing buffer (which is hidden
|
|
|
|
in the MID_TRACK_BUF macro) */
|
|
|
|
mid_track_buf_used = 0;
|
|
|
|
|
|
|
|
/* And finally start the real conversion */
|
|
|
|
while (track_length > 0)
|
|
|
|
{
|
|
|
|
n1 = -1; /* -1 to "counter" the intital n1++ */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
n1++;
|
|
|
|
HMP_READ(buf + n1, 1);
|
|
|
|
} while (!(buf[n1] & 0x80));
|
|
|
|
|
|
|
|
if (n1 >= 4)
|
|
|
|
{
|
|
|
|
free(mid_track_buf);
|
|
|
|
return "Error parsing hmp track";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (n2 = 0; n2 <= n1; n2++)
|
|
|
|
{
|
|
|
|
unsigned char u = buf[n1-n2] & 0x7F;
|
|
|
|
|
|
|
|
if (n2 != n1)
|
|
|
|
u |= 0x80;
|
|
|
|
MID_TRACK_BUF(&u, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
HMP_READ(buf, 1);
|
|
|
|
|
|
|
|
if (buf[0] == 0xFF) /* meta? */
|
|
|
|
{
|
|
|
|
HMP_READ(buf + 1, 2);
|
|
|
|
HMP_READ(buf + 3, buf[2]);
|
|
|
|
MID_TRACK_BUF(buf, 3 + buf[2]);
|
|
|
|
if (buf[1] == 0x2F) /* end of track? */
|
|
|
|
{
|
|
|
|
if (buf[2] != 0x00)
|
|
|
|
{
|
|
|
|
free(mid_track_buf);
|
|
|
|
return "Error hmp meta end of track with non zero size";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (buf[0] & 0xF0)
|
|
|
|
{
|
|
|
|
case 0x80:
|
|
|
|
case 0x90:
|
|
|
|
case 0xA0:
|
|
|
|
case 0xB0:
|
|
|
|
case 0xE0:
|
|
|
|
t = 2;
|
|
|
|
break;
|
|
|
|
case 0xC0:
|
|
|
|
case 0xD0:
|
|
|
|
t = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
free(mid_track_buf);
|
|
|
|
return "Error invalid hmp command";
|
|
|
|
}
|
|
|
|
if (buf[0] != last_com)
|
|
|
|
MID_TRACK_BUF(buf, 1);
|
|
|
|
HMP_READ(buf + 1, t);
|
|
|
|
MID_TRACK_BUF(buf + 1, t);
|
|
|
|
last_com = buf[0];
|
|
|
|
}
|
|
|
|
if (track_length != 0)
|
|
|
|
{
|
|
|
|
free(mid_track_buf);
|
|
|
|
return "Error invalid track length";
|
|
|
|
}
|
|
|
|
/* write the midi track length */
|
|
|
|
buf [0] = mid_track_buf_used >> 24;
|
|
|
|
buf [1] = (mid_track_buf_used >> 16) & 0xFF;
|
|
|
|
buf [2] = (mid_track_buf_used >> 8) & 0xFF;
|
|
|
|
buf [3] = mid_track_buf_used & 0xFF;
|
|
|
|
MID_WRITE(buf, 4);
|
|
|
|
/* and the track itself */
|
|
|
|
MID_WRITE(mid_track_buf, mid_track_buf_used);
|
|
|
|
}
|
|
|
|
free (mid_track_buf);
|
|
|
|
return NULL;
|
|
|
|
}
|