Created
June 8, 2019 20:19
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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