[Script] GENH DSP layer splitter

Codecs, formats, encoding/decoding of game audio, video and music
AlphaTwentyThree
Posts: 909
Joined: Sat Aug 09, 2014 11:21 am

[Script] GENH DSP layer splitter

Post by AlphaTwentyThree »

Hello fellow rippers and coders!

I have just encountered multichannel DSP files in the game "de Blob 2" for Wii. They are packed into FSB containers and were played correctly but the streams are actually layers, so a multichannel playback isn't what I was after. So I extracted the containers with Luigi's fsbext and wrote a script that automatically splits the files by layers. For this, I simply tweaked my deinterleave script a little bit to meet the requirements. Took me about 20 mins. ;)
Have fun with the script! :)
Don't forget to post a little thanks if you like this. ;)

Code: Select all

# splits multichannel GENH dsp files into layers
# - will leave 1 and 2 channels (do nothing)
# - will split into stereo pairs if ch count is even
# - will split into mono files if ch count is odd

# script 1.01

# written by AlphaTwentyThree of XeNTaX
# script for QuickBMS http://quickbms.aluigi.org

idstring "GENH"
get CH long
if CH == 2 # stays
   cleanexit
elif CH == 1
   cleanexit
else
   xmath TEST "CH % 2"
   if TEST == 0
      set INTSIZE 4
      xmath LAYERS "CH / 2"
      set CH 2
   else
      set LAYERS CH
      set CH 1
      set INTSIZE 2
   endif
endif
goto 0x24
get COEFF_START long

goto 0x1c
get HEADER long
set PRESERVE 1
set ADJUST 1
set SPLIT_LAST_BLOCK 0
set BLOCKSIZE 0 # size of one complete block
set SKIP_START 0 # area at start of each block to skip
set SKIP_END 0 # area at end of each block to skip

if BLOCKSIZE == 0
   xmath BLOCKSIZE "(INTSIZE * LAYERS) + SKIP_START + SKIP_END"
endif
if INTSIZE == 0
   xmath INTSIZE "BLOCKSIZE / LAYERS"
endif

xmath WSIZE "(BLOCKSIZE - SKIP_START - SKIP_END) / LAYERS" # data to write: (BLOCKSIZE - SKIP_START - SKIP_END)/LAYERS
get CYCLES asize
xmath CYCLES "(CYCLES - HEADER) / BLOCKSIZE"

get LASTBLOCK asize
xmath LASTBLOCK "(LASTBLOCK - HEADER) % BLOCKSIZE"
xmath LASTINT "LASTBLOCK / LAYERS"

callfunction testparameters 1
get EXT extension
callfunction deinterleave

startfunction testparameters
   # test 1: (BLOCKSIZE-SKIP_START-SKIP_END)/LAYERS must be integer (one block must be dividable into skip and layers)
   xmath TEST "BLOCKSIZE - SKIP_START - SKIP_END"
   math TEST %= LAYERS # single layer -> always ok
   if TEST != 0
      print "Error: blocksize minus skip isn't dividable by layer count! Aborting..."
      cleanexit
   endif
   
   # test 2: test for incomplete last block
   get TEST asize
   math TEST -= HEADER
   math TEST %= BLOCKSIZE
   if TEST != 0
      if SPLIT_LAST_BLOCK == 1
         print "Warning: file size minus header isn't dividable by blocksize! Last block will be equally split between layers!"
         print "Last chunks will have size of %LASTINT% bytes each."
      else
         print "ERROR: file size minus header isn't dividable by blocksize! Aborting..."
         cleanexit
      endif
   endif
   
   # test 3: ASIZE-HEADER-BLOCKSIZE must be greater null (at least one deinterleave cycle)
   get TEST asize
   xmath TEST "TEST - HEADER - BLOCKSIZE"
   if FSIZE <= 0
      print "Error: file too small to deinterleave! Aborting..."
      cleanexit
   endif
endfunction
   
startfunction deinterleave
   xmath PSIZE "CYCLES * WSIZE + LASTINT"
   if PRESERVE == 1
      math PSIZE += HEADER
      goto 0
      getDstring HDATA HEADER
   endif
   
   
   for i = 1 <= LAYERS
      putVarChr MEMORY_FILE PSIZE 0
      log MEMORY_FILE 0 0
      get NAME basename
      string NAME += "_"
      string NAME += i
      if PRESERVE == 1
         putDstring HDATA HEADER MEMORY_FILE
         string NAME += "."
         string NAME += EXT
      endif
      xmath BIAS_A "(i - 1) * WSIZE + SKIP_START"# bias at start
      xmath BIAS_B "BLOCKSIZE - BIAS_A - WSIZE" # bias at end
   
      xmath RES_BIAS_A "(i - 1) * LASTINT + SKIP_START"
      xmath RES_BIAS_B "LASTBLOCK - RES_BIAS_A - LASTINT"
      goto HEADER
      for k = 1 <= CYCLES
         getDstring DUMMY BIAS_A
         getDstring WDATA WSIZE
         putDstring WDATA WSIZE MEMORY_FILE
         getDstring DUMMY BIAS_B
      next k
      if LASTBLOCK != 0
         getDstring DUMMY RES_BIAS_A
         getDstring WDATA LASTINT
         putDstring WDATA LASTINT MEMORY_FILE
         getDstring DUMMY RES_BIAS_B
      endif
      get SIZE asize MEMORY_FILE
      if ADJUST == 1
         xmath COEFF1 "COEFF_START + (i - 1) * CH * 0x20"
         xmath COEFF2 "COEFF1 + 0x20"
         callfunction adjustheader 1
      endif
      log NAME 0 SIZE MEMORY_FILE
   next i
endfunction

startfunction adjustheader
   putVarChr MEMORY_FILE 4 CH long
   putVarChr MEMORY_FILE 0x24 COEFF1 long
   putVarChr MEMORY_FILE 0x34 COEFF1 long
   putVarChr MEMORY_FILE 0x28 COEFF2 long
   putVarChr MEMORY_FILE 0x38 COEFF2 long
endfunction
AlphaTwentyThree
Posts: 909
Joined: Sat Aug 09, 2014 11:21 am

Re: [Script] GENH DSP layer splitter

Post by AlphaTwentyThree »

Just updated to script 1.01.
Apparently, multichannel DSP files with an odd number of channels are interleaved with 2 instead of 4.
brendan19
Posts: 144
Joined: Fri Aug 08, 2014 11:25 am

Re: [Script] GENH DSP layer splitter

Post by brendan19 »

Thanks Alpha :)
Mygoshi
Posts: 654
Joined: Mon Oct 27, 2014 1:49 pm

Re: [Script] GENH DSP layer splitter

Post by Mygoshi »

Nice script, thanks.