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