X-Com Alliance imus

Codecs, formats, encoding/decoding of game audio, video and music
ultradumb
Posts: 7
Joined: Thu Apr 23, 2020 5:57 pm

X-Com Alliance imus

Post by ultradumb »

Here is what I have discovered after examining the X-Com Alliance imus .int format:

Code: Select all

Offset 0x00 has the track count

Offset 0x264 is the start of the track info header block
0x266 is the channel count for track 1
0x268 is the sample rate for track 1
0x270 is the interleave for track 1

Each track block is 54 bytes, so track 2 offset would be at 0x29A, etc.

Then the audio data begins. 4 byte header which is the data size of the chunk. Each chunk is interleaved, so the first chunk is for track 1, the next chunk is for track 2, etc.

I tried to make a bms script, which works but is not optimal, because I haven't played with bms recently. I found that each chunk needs to be decoded to wav with Foobar2000, because if I binary copy the data to 1 file, the audio is distorted.

With the script, you can dechunk the files and then use a txth with foobar2000 to encode to 1 wav for each subsong, with the converter set to output merge.

If a spike in audio is seen at the end, then remove the last vgmstream file.

I recommend listening to the audio in Audacity because the music is often really quiet, and then spikes to max amplitude.

Code: Select all

#X-Com Alliance BMS
get TRACKS byte
goto 0x264
for i = 1 <= TRACKS
   get DUMMY1 short
   get CHANNELS byte
   get DUMMY2 byte
   get FREQUENCY short
   get DUMMY3 long
   get DUMMY4 short
   get INTERLEAVE short

   for x = 0 < 10
      get DUMMY4 long
   next x

   print "Track %i%, Channels %CHANNELS%, Frequency %FREQUENCY%, Interleave %INTERLEAVE|hex%"
next i

# Uncomment next line if you want to see the track info but not extract
# cleanexit

get FSIZE ASIZE
math counter = 1

for i = 1
   get SIZE long
   savePos OFFSET
   
   if counter > TRACKS
      math counter = 1
   endif
   
   get NAME BASENAME
   string NAME += "-"
   string NAME += counter
   string NAME += "_"
   if i < 10
      string NAME += 0
      string NAME += 0
      string NAME += i
   elif i >= 10 && i < 100
      string NAME += 0
      string NAME += i
   else
      string NAME += i
   endif
   
   string NAME += ".vgmstream"
   
   log NAME OFFSET SIZE
   
   math counter += 1
   
   math OFFSET += SIZE
   if OFFSET == FSIZE
      cleanexit
   else
      goto OFFSET
   endif
next i


Code: Select all

# X-Com Alliance PC TXTH
# vgmstream files extracted from imusX.int

codec = MSADPCM

interleave = 0x400
#interleave = 0x200

channels = 2
#channels = 1

sample_rate = 22050
#sample_rate = 11025

num_samples = data_size
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: X-Com Alliance imus

Post by aluigi »

Little off-topic from me: you can build the filename just by using the following command :)

Code: Select all

get NAME basename
string NAME p "%s-%d_%03d.vgmstream" NAME counter i
ultradumb
Posts: 7
Joined: Thu Apr 23, 2020 5:57 pm

Re: X-Com Alliance imus

Post by ultradumb »

Thank you aluigi, that code is much more elegant.
ultradumb
Posts: 7
Joined: Thu Apr 23, 2020 5:57 pm

Re: X-Com Alliance imus

Post by ultradumb »

I improved the bms script by concatenating the chunks and generating txth files for use with vgmstream.

Code: Select all

# X-Com Alliance BMS to deinterleave the imus int files
# and create txth files needed to play the concatenated
# chunks with vgmstream

# Decrease the value of subsong_count in the generated txth files
# if you hear pops at the end of the music

goto 0x00
get TRACKS byte

goto 0x04
get CHUNKS byte

goto 0x264
for i = 0 < TRACKS
   get DUMMY1 short
   get CHANNELS byte
   get DUMMY2 byte
   get FREQUENCY short
   get DUMMY3 long
   get DUMMY4 short
   get INTERLEAVE short

   for x = 0 < 10
      get DUMMY4 long
   next x

   print "Track %i%, Interleave %INTERLEAVE|hex%, Channels %CHANNELS%, Frequency %FREQUENCY%, Chunks %CHUNKS%"
   putarray 0 i INTERLEAVE
   putarray 1 i CHANNELS
   putarray 2 i FREQUENCY
   putarray 3 i CHUNKS
next i

# Uncomment next line if you want to see the track info but not extract
# cleanexit

math COUNTER = 0
set TOTALCHUNKS CHUNKS
math TOTALCHUNKS *= TRACKS

for i = 0 < TOTALCHUNKS
   get DUMMY long

   if COUNTER == TRACKS
      math COUNTER = 0
   endif
   getarray INTERLEAVE 0 COUNTER
   getarray CHANNELS 1 COUNTER
   getarray FREQUENCY 2 COUNTER
   getarray CHUNKS 3 COUNTER

   if INTERLEAVE == 0x400
      set SIZE == 0x2B94
   elif INTERLEAVE == 0x200
      set SIZE == 0x15CA
   elif INTERLEAVE == 0x100
      set SIZE == 0x0B0C
   else
      cleanexit
   endif

   savePos OFFSET

   math COUNTER += 1

   get NAME basename
   string VGMSTREAM p "%s-%d.vgmstream" NAME COUNTER

   if i < TRACKS
      string TXTH p "%s-%d.vgmstream.txth" NAME COUNTER

      log MEMORY_FILE 0 0

      put "codec = MSADPCM" line MEMORY_FILE
      string txthval p "interleave = 0x%x" INTERLEAVE
      put txthval line MEMORY_FILE
      string txthval p "channels = %d" CHANNELS
      put txthval line MEMORY_FILE
      string txthval p "sample_rate = %d" FREQUENCY
      put txthval line MEMORY_FILE
      put "" line MEMORY_FILE
      string txthval p "subsong_count = %d" CHUNKS
      put txthval line MEMORY_FILE
      put "chunk_start = 0" line MEMORY_FILE
      string txthval p "chunk_size = 0x%x" SIZE
      put txthval line MEMORY_FILE
      string txthval p "chunk_count = %d" CHUNKS
      put txthval line MEMORY_FILE
      put "" line MEMORY_FILE
      put "start_offset = 0x00" line MEMORY_FILE
      put "num_samples = data_size" line MEMORY_FILE

      get MEM_SIZE asize MEMORY_FILE

      log TXTH 0 MEM_SIZE MEMORY_FILE
   endif

   if i == TRACKS
      append
   endif
   log VGMSTREAM OFFSET SIZE

   math OFFSET += SIZE
   goto OFFSET
next i