Skip to content

Instantly share code, notes, and snippets.

@emoon
Created June 8, 2019 20:19
Show Gist options
  • Save emoon/159051b444bac8259bdc2dd503dd9125 to your computer and use it in GitHub Desktop.
Save emoon/159051b444bac8259bdc2dd503dd9125 to your computer and use it in GitHub Desktop.
Extract music from iXalance files (only seems to work for Astral blur right now)
// Code mostly extracted from https://www.libsdl.org/projects/ixalance/ but fixed a bunch of 64-bit issues with the code
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define INDEX_BIT_COUNT 10 // This is a total window of 4Kb
#define LENGTH_BIT_COUNT 4
#define WINDOW_SIZE ( 1 << INDEX_BIT_COUNT )
#define BREAK_EVEN ( ( 1 + INDEX_BIT_COUNT + LENGTH_BIT_COUNT ) / 9 )
#define RAW_LOOK_AHEAD_SIZE ( 1 << LENGTH_BIT_COUNT )
#define LOOK_AHEAD_SIZE ( RAW_LOOK_AHEAD_SIZE + BREAK_EVEN )
#define END_OF_STREAM 0
/*
* Function prototypes for ANSI C compilers
*/
char swindow[1024];
void Expand (int32_t *input, char *output);
void InitBitstring (int32_t *bitstring);
int InputBit();
uint32_t InputBits(int bit_count);
// read one bit out of the bitstream
#define InputBit(value) \
{ \
if (Bitstring_Count == 0) \
{ \
Bitstring_Rack = *Bitstring++; \
Bitstring_Count = 31; \
} \
else \
Bitstring_Count--; \
\
value = (Bitstring_Rack>>31); \
Bitstring_Rack <<= 1; \
\
}
// read a bitstring out of the bitstream
#define InputBits(bit_count,return_value) \
{ \
\
if (Bitstring_Count < bit_count) \
{ \
int32_t second_pass = bit_count-Bitstring_Count; \
\
return_value = Bitstring_Rack >> (32-bit_count); \
Bitstring_Rack = *Bitstring++; \
return_value |= Bitstring_Rack >> (32-second_pass); \
Bitstring_Rack <<= second_pass; \
Bitstring_Count = 32-second_pass; \
} \
else \
{ \
return_value = Bitstring_Rack >> (32-bit_count); \
Bitstring_Count -= bit_count; \
Bitstring_Rack <<= bit_count; \
} \
}
/*
* This is the expansion routine for the LZSS algorithm. All it has
* to do is read in flag bits, decide whether to read in a character or
* a index/length pair, and take the appropriate action.
*/
void Expand(int32_t *Bitstring, char *output)
{
int window_ptr, window_ptr2;
int i;
int c;
uint32_t Bitstring_Count = 0;
uint32_t Bitstring_Rack;
int match_length;
int match_position;
window_ptr=1;
while (1)
{
InputBit(i);
if (i)
{
InputBits(8,c);
*output++ = c;
swindow[window_ptr]= c;
window_ptr++;
window_ptr&=1023;
}
else
{
InputBits(INDEX_BIT_COUNT,match_position);
if ( match_position == END_OF_STREAM )
break;
InputBits(LENGTH_BIT_COUNT,match_length);
match_length += BREAK_EVEN;
window_ptr2 = match_position;
for (i=0;i<=match_length;i++ )
{
c = swindow[window_ptr2++];
window_ptr2&=1023;
swindow[window_ptr++]= c;
*output=c;
output++;
window_ptr&=1023;
}
}
}
}
// rle decode
void decode_rle(unsigned char* input,unsigned char* output)
{
int len;
int pos=0;
int ch, i;
len=*(int*)input;
pos+=4;
while (1) {
ch=*(input+pos);
pos++;
if (pos>len)
break;
if (ch > 127)
{
i = ch - 127; /* i is the number of repetitions */
/* get the byte to be repeated */
ch=*(input+pos);
pos++;
/* uncompress a chunk */
for ( ; i ; i--)
{
*output=ch;
output++;
}
}
else
{
/* copy out some uncompressed bytes */
i = ch + 1; /* i is the no. of bytes */
/* uncompress a chunk */
for ( ; i ; i--)
{
ch=*(input+pos);
pos++;
*output=ch;
output++;
}
}
} /* end while */
}
struct IXAheaderstruct {
char name[8];
char demoname[32];
int32_t dirstart;
int32_t type;
} IXAheader;
struct {
int32_t pos;
int32_t size;
int32_t fullsize;
} IXAdir[256];
int IXAnumfiles = 0;
char IXAscript[1024];
int IXAscriptpos = 0;
int escaped = 0;
char IXA_Name[256]="iXalance";
char IXA_Filename[256];
FILE* ixafp = 0;
void PlayMusic(int filenum)
{
char tempname[1024];
char *lzssbuf, *lzssbuf1, *lzssbuf2;
FILE *tempfp;
printf("Loading music file: %i", filenum);
printf("\n");
// Allocate memory for possible decompression
lzssbuf2 = (char *) malloc(IXAdir[filenum].fullsize * 4);
lzssbuf1 = (char *) malloc(IXAdir[filenum].size * 4);
fseek(ixafp, IXAdir[filenum].pos, SEEK_SET);
fread(lzssbuf1, IXAdir[filenum].size, 1, ixafp);
printf("data size %d\n", IXAdir[filenum].size);
printf("full size %d\n", IXAdir[filenum].fullsize);
printf("Decompressing...\n");
Expand((int32_t *) lzssbuf1, lzssbuf2);
if (!(lzssbuf = (char *) malloc(IXAdir[filenum].fullsize * 4)))
printf("Couldn't allocate memory for decompression!\n");
decode_rle((unsigned char *) lzssbuf2, (unsigned char *) lzssbuf);
sprintf(tempname, "music_%d.bin", filenum);
// What I do is a bit nasty. I save the XM to a temporary file, and make
// Midas open it. You might have some better way to do this :)
//tempname = tempnam(NULL, "IXA");
tempfp = fopen(tempname, "wb");
fwrite(lzssbuf, IXAdir[filenum].fullsize, 1, tempfp);
fclose(tempfp);
}
int main() {
ixafp = fopen("astral.ixa", "rb");
if (!ixafp) {
printf("Can't open IXA file!\n");
fprintf(stderr, "Can't open IXA File!\n");
return 0;
}
// Load directory
fseek(ixafp, 0, SEEK_SET);
fread(&IXAheader, sizeof(IXAheader), 1, ixafp);
fseek(ixafp, IXAheader.dirstart, SEEK_SET);
fread(&IXAnumfiles, 4, 1, ixafp);
fread(IXAdir, IXAnumfiles, 12, ixafp);
strcpy(IXA_Name, IXAheader.demoname);
// Load scriptfile
fseek(ixafp, IXAdir[0].pos, SEEK_SET);
fread(IXAscript, IXAdir[0].size, 1, ixafp);
IXAscriptpos = 0;
printf("size %d\n", IXAdir[0].size);
while ((IXAscriptpos < IXAdir[0].size) && (!escaped)) {
if (IXAscript[IXAscriptpos] == 2) {
//PopPart();
IXAscriptpos++;
} else if (IXAscript[IXAscriptpos] == 1) {
//PushExe(IXAscript[IXAscriptpos + 1]);
IXAscriptpos += 2;
} else if (IXAscript[IXAscriptpos] == 3) {
PlayMusic(IXAscript[IXAscriptpos + 1]);
IXAscriptpos += 2;
} else if (IXAscript[IXAscriptpos] == 4) {
//PushPicture(IXAscript[IXAscriptpos + 1]);
IXAscriptpos += 2;
} else if (IXAscript[IXAscriptpos] == 5) {
//WaitMusic(IXAscript[IXAscriptpos + 1], IXAscript[IXAscriptpos + 2]);
IXAscriptpos += 3;
}
if (!escaped) {
//IXDRV_CheckMessages();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment