Microprose: X-COM 1, 2 .CAT files
Posted: Fri Jun 18, 2010 7:18 am
The contents of this post was deleted because of possible forum rules violation.
Code: Select all
# script for QuickBMS http://aluigi.org/quickbms
get FILES long
math FILES /= 8
goto 0
for i = 0 < FILES
get OFFSET long
get SIZE long
savepos TMP_OFF
goto OFFSET
get NAMESZ byte
set NAME string ""
if NAMESZ > 0
if NAMESZ < 40
getdstring TMP NAMESZ
savepos OFFSET
math SIZE -= 1
math SIZE -= NAMESZ
if NAMESZ > 5
get NAME basename
string NAME += /
string NAME += i
string NAME += _
string NAME += TMP
endif
endif
endif
goto TMP_OFF
log NAME OFFSET SIZE
next i
Code: Select all
/* *
* gmext.c *
* *
* gm.cat MIDI sequence extractor *
* */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define RUNNING_MODE
inline long read_delta (const unsigned char* *p)
{
long delta = **p & 0x7F;
while ( *(*p)++ & 0x80 )
{
delta <<= 7;
delta += **p & 0x7F;
}
return delta;
}
void write_delta (FILE *f, long delta)
{
unsigned char c [4] = { 0, 0x80, 0x80, 0x80 };
int i = 0;
while ( delta > 0x7F )
{
c[i++] |= delta & 0x7F;
delta >>= 7;
}
c[i++] |= delta;
while ( i )
fwrite (c + --i, 1, 1, f);
}
struct seq
{
uint32_t size;
unsigned char *data;
};
unsigned char volume [0x80] = {
100,100,100,100,100, 90, 100,100,100,100,100, 90, 100,100,100,100,100,100,
85, 100,100,100,100,100,100,100,100,100, 90,90, 110, 80, 100,100,100,
90, 70, 100,100,100,100,100,100,100,100,100,100,100,100,100, 90,
100,100,100,100,100,100, 120, 100,100,100, 120, 100, 127, 100,100, 90,
100,100,100,100,100,100, 95,
100,100,100,100,100,100,100,100,100,100,100,100,100, 115,
100,100,100,100,100,100,100,100,100,100,
100,100,100,100,100,100,100,100,100,100,
100,100,100,100,100,100,100,100,100,100,
100,100,100,100,100,100,100,100,100,100
};
long tseq (FILE *f, unsigned nsubs, const struct seq *subs, char channel,
const struct seq *seq, unsigned char *patch, long delta0)
{
long delta = delta0;
unsigned char output, command = 0x80;
unsigned char data1, data2;
const unsigned char *data = seq->data;
const unsigned char *dataend = seq->data + seq->size;
while ( data < dataend )
{
delta += read_delta (&data);
if ( *data & 0x80 ) command = *data++;
switch ( command )
{
case 0xFF:
case 0xFD: return delta;
case 0xFE: /* insert subsequence */
if ( *data >= nsubs )
{
puts ("invalid subsequence");
return 0;
}
delta = tseq (f, nsubs, subs, channel, &subs[*data++],
patch, delta);
continue;
}
command &= 0xF0;
data1 = *data++;
switch ( command )
{
case 0x80: case 0x90:
data2 = *data++;
write_delta (f, delta);
delta = 0;
#ifdef RUNNING_MODE
if ( output != (command|channel) )
#endif
{
output = command | channel;
fwrite (&output, 1, 1, f);
}
fwrite (&data1, 1, 1, f);
if ( data2 )
data2 = (unsigned) data2 *
(channel==9 ? 80 : volume[*patch]) >> 7;
fwrite (&data2, 1, 1, f);
break;
case 0xC0:
if ( data1 == 0x7E ) return delta;
*patch = data1;
write_delta (f, delta);
delta = 0;
#ifdef RUNNING_MODE
if ( output != (0xC0|channel) )
#endif
{
output = 0xC0 | channel;
fwrite (&output, 1, 1, f);
}
if ( (data1==0x57) || (data1==0x3F) ) data1 = 0x3E;
fwrite (&data1, 1, 1, f);
break;
case 0xB0:
data2 = *data++;
if ( (data1==0x7E) || (!data1 && !data2) ) continue;
write_delta (f, delta);
delta = 0;
if ( !data1 )
{
uint32_t x;
char xdata [3];
output = 0xFF;
fwrite (&output, 1, 1, f);
data1 = 0x51;
fwrite (&data1, 1, 1, f);
data1 = 3;
fwrite (&data1, 1, 1, f);
x = (740*163840) * data2;
xdata[0] = x>>16;
xdata[1] = x>>8;
xdata[2] = x;
fwrite (&xdata, 1, 3, f);
}
#ifdef RUNNING_MODE
if ( output != (0xB0|channel) )
#endif
{
output = 0xB0 | channel;
fwrite (&output, 1, 1, f);
}
fwrite (&data1, 1, 1, f);
if ( data1 == 0x5B ) data2 = 0x1E;
fwrite (&data2, 1, 1, f);
break;
case 0xE0:
data2 = *data++;
write_delta (f, delta);
delta = 0;
#ifdef RUNNING_MODE
if ( output != (0xE0|channel) )
#endif
{
output = 0xE0 | channel;
fwrite (&output, 1, 1, f);
}
fwrite (&data1, 1, 1, f);
fwrite (&data2, 1, 1, f);
break;
}
}
return 0;
}
void midi_header (FILE *f, unsigned ntracks)
{
static const char header [10] = { 'M','T','h','d',0,0,0,6,0,1 };
static const char delta [2] = { 0,24 };
char ntr [2];
fwrite (header, 1, 10, f);
ntr[0] = ntracks>>8;
ntr[1] = ntracks;
fwrite (ntr, 1, 2, f);
fwrite (delta, 1, 2, f);
}
static const char midi_track_start [8] = { 'M','T','r','k',0,0,0,0 };
static const char midi_track_end [4] = { 0, 0xFF, 0x2F, 0 };
void midi_track0 (FILE *f, unsigned tempo, unsigned namelen,
const char *name)
{
static const char set_tempo [4] = { 0, 0xFF, 0x51, 3 };
static const char text_event [3] = { 0, 0xFF, 1 };
char data [4];
unsigned tsize;
fwrite (midi_track_start, 1, 4, f);
tsize = namelen + 15;
data[0] = tsize>>24;
data[1] = tsize>>16;
data[2] = tsize>>8;
data[3] = tsize;
fwrite (data, 1, 4, f);
fwrite (set_tempo, 1, 4, f);
tempo = (370*163840) / tempo;
data[0] = tempo>>16;
data[1] = tempo>>8;
data[2] = tempo;
fwrite (data, 1, 3, f);
fwrite (text_event, 1, 3, f);
fwrite (&namelen, 1, 1, f);
fwrite (name, 1, namelen, f);
fwrite (midi_track_end, 1, 4, f);
}
void midi_track (FILE *f, unsigned nsubs, const struct seq *subs,
char channel, const struct seq *seq)
{
static char midi_track_begin [10] =
{ 0, 0xB0, 0x78, 0, 0, 0x79, 0, 0, 0x7B, 0 };
long foffset, length;
char data [4];
unsigned char patch = 0;
fwrite (midi_track_start, 1, 8, f);
foffset = ftell (f);
midi_track_begin[1] = 0xB0 | channel;
fwrite (midi_track_begin, 1, 10, f);
write_delta (f, tseq (f, nsubs, subs, channel, seq, &patch, 0));
fwrite (midi_track_end+1, 1, 3, f);
length = ftell(f) - foffset;
fseek (f, foffset-4, SEEK_SET);
data[0] = length>>24;
data[1] = length>>16;
data[2] = length>>8;
data[3] = length;
fwrite (data, 1, 4, f);
fseek (f, 0, SEEK_END);
}
int gmext_song (FILE *f, uint32_t n)
{
uint32_t offset, length;
unsigned char namelength, tempo, nsubs, ntracks;
char name [256];
struct seq subs [256];
char channels [256];
struct seq tracks [256];
unsigned namelen, i;
char out [256];
FILE *fout;
/* read in offset and length of song */
if ( fseek (f, 8*n, SEEK_SET) == -1 ) return -1;
if ( fread (&offset, 4, 1, f) != 1 ) return -1;
if ( fread (&length, 4, 1, f) != 1 ) return -1;
if ( fseek (f, offset, SEEK_SET) == -1 ) return -1;
/* read in song name */
if ( fread (&namelength, 1, 1, f) != 1 ) return -1;
if ( fread (name, 1, namelength, f) != namelength ) return -1;
if ( namelength && !name[namelength-1] )
namelen = namelength-1;
else name[namelen=namelength] = 0;
printf ("Extracting song #%u %s\n", n, name);
/* read in tempo */
if ( fread (&tempo, 1, 1, f) != 1 ) return -1;
/* read in subs */
if ( fread (&nsubs, 1, 1, f) != 1 ) return -1;
for (i=0; i<nsubs; i++)
{
/* do not bother free()ing memory on failure, */
/* since we are going to abort */
if ( fread (&subs[i].size, 4, 1, f) != 1 ) return -1;
if ( subs[i].size < 4 )
{
puts ("invalid data length");
return -2;
}
subs[i].size -= 4;
subs[i].data = malloc (subs[i].size);
if ( !subs[i].data )
{
perror (0);
return -2;
}
if ( fread (subs[i].data, 1, subs[i].size, f) != subs[i].size )
return -1;
}
/* read in tracks */
if ( fread (&ntracks, 1, 1, f) != 1 ) return -1;
for (i=0; i<ntracks; i++)
{
if ( fread (&channels[i], 1, 1, f) != 1 ) return -1;
if ( fread (&tracks[i].size, 4, 1, f) != 1 ) return -1;
if ( tracks[i].size < 4 )
{
puts ("invalid data length");
return -2;
}
tracks[i].size -= 4;
tracks[i].data = malloc (tracks[i].size);
if ( !tracks[i].data )
{
perror (0);
return -2;
}
if ( fread (tracks[i].data, 1, tracks[i].size, f) != tracks[i].size )
return -1;
}
/* check current offset */
if ( ftell(f) != (offset+length+namelength+1) )
{
puts ("offset mismatch");
return -2;
}
/* write output file */
snprintf (out, sizeof(out), "%02u.mid", n);
printf ("Output file: %s\n", out);
fout = fopen (out, "wb");
if ( !fout )
{
perror (out);
return -2;
}
midi_header (fout, ntracks+1);
midi_track0 (fout, tempo, namelen, name);
for (i=0; i<ntracks; i++)
midi_track (fout, nsubs, subs, channels[i], &tracks[i]);
/* clean up */
fclose (fout);
for (i=0; i<nsubs; i++) free (subs[i].data);
for (i=0; i<ntracks; i++) free (tracks[i].data);
return 0;
}
int gmext (const char *filename)
{
FILE *f;
uint32_t nsongs, i;
printf ("Reading file %s\n", filename);
f = fopen (filename, "rb");
if ( !f )
{
perror (filename);
return 1;
}
/* compute the number of songs, knowing that the */
/* first song starts right after the index table */
if ( fread (&nsongs, 4, 1, f) != 1 ) /* file format is little-endian */
{
perror (filename);
return 1;
}
nsongs /= 8;
printf ("There are %u songs in %s\n", nsongs, filename);
/* read songs one by one */
for (i=0; i<nsongs; i++)
switch ( gmext_song (f, i) )
{
case -1:
perror (filename);
case -2:
fclose (f);
return 1;
}
fclose (f);
return 0;
}
int main (int nargs, char* *args)
{
return gmext (nargs>1 ? args[1] : "gm.cat");
}