# ===================================================================================================== # Preliminary MLT--->DSF converter (ver 0.03 2008-10-14) by kingshriek # This script only supports the Manatee sound driver and not necessarily all the different versions of # it. For sound data not in .MLT files, convert to .MLT first before using. # Requires bin2psf & psfpoint (http://www.neillcorlett.com/psf/utilities.html) # Update history located near end of file # ===================================================================================================== # ===================================================================================================== # This script produces .dsf/.minidsf files from a Manatee sound driver image and a multi-unit sound # data file (.MLT extension) and has similar functionality to the ssfmake script (for .ssf/.minissf). # Not all games pack their sound data into .MLT files, and for those that don't, it will require # combining the various sound files into an .MLT file before using this script. The individual sound # formats should have extentions similar to their header strings, only without the leading "S" (e.g. a # sequence data file will have extention .msb and header string SMSB). See comments below inside # dsfmake() for information on the various sound formats and header strings. # # This script in its current form may not work with some versions of the manatee driver. This is the # case since the driver needs to be patched in a few locations so that it doesn't wipe out ripper-provided # sound commands upon initialization and these patch locations may vary with different driver versions. # Also, some of these patches disable loading of various default sound data (the driver contains itself # contains a default DSP program and mixer for instance) so that it uses data in the MLT file instead. # If a track needs any of this default data, you will need to comment out the corresponding patches in # dsfmake() for a proper rip. # # The bulk of the script is defined as a function, which is called at the very bottom. Feel free to customize # this bottom section as you wish. Once good use for it is that you can write some code to loop over a bunch # of files and perform the dsf creation in batch mode. # ===================================================================================================== # ===================================================================================================== # PARAMETERS # ----------------------------------------------------------------------------------------------------- bank = 0x00 # sequence bank number track = None # sequence track number (if None, create minidsfs for all tracks) volume = 0x77 # sequence volume (0xFF is max, but it should probably be <= 0x7F in most cases) mixer = 0x00 # effect-out mixer select (usually 0x00) effect = 0x00 # DSP program select (usually 0x00) # Not sure about any other paramaters yet, especially for sound command 0x87. For others, try editing # the sound commands specified in dsfmake() directly for now. # ----------------------------------------------------------------------------------------------------- use_osb = 0 # 0: use MIDI sequence data, 1: use one-shot data (sound effects) naomi = 0 # 0: assume 2 MB sound RAM, 1: assume 8 MB sound RAM (Naomi/Hikaru boards) # ----------------------------------------------------------------------------------------------------- # filenames ndrv = 'MANATEE.DRV' # sound driver (usually MANATEE.DRV) nmlt = 'FILE.MLT' # sound data archive (.MLT file) # ----------------------------------------------------------------------------------------------------- nout = nmlt.upper().replace('.MLT','.DSF') # output file name # ===================================================================================================== # ===================================================================================================== from struct import * # pack, unpack from array import * import os # system # ===================================================================================================== # ===================================================================================================== # Converts list of bytes into a sound command array (zero-padded out to 16 bytes) def sndcmd(x): if len(x) > 0x10: x = x[:0x10] return array('B',x + [0x00]*(0x10-len(x))) # ===================================================================================================== # ===================================================================================================== # Get base file name with and without extension def fnoext(fname): fnameb = os.path.basename(fname) idot = -fname[::-1].find('.')-1 if idot: fnamex = fnameb[:idot] else: fnamex = fnameb return (fnameb,fnamex) # ===================================================================================================== # ===================================================================================================== # Get sound RAM offset and byte allocation (useful for custom dsflib/minidsf creation) def get_offset(mlt_filename, hdr_str, bank): mlt_hdr_top = array('B', open(mlt_filename, 'rb').read(0x20)) nhdr = unpack('=0): # write area map data if offset is known dsfbin[areamap[fhdr]+8*mbank+0x0:areamap[fhdr]+8*mbank+0x4] = datamlt[offset+0x8:offset+0xC] dsfbin[areamap[fhdr]+8*mbank+0x4:areamap[fhdr]+8*mbank+0x8] = datamlt[offset+0xC:offset+0x10] #if foff >= 0 or fhdr == 'SFPW' or fhdr == 'SPSR': # DEBUG #fcon.write('%s: bank=0x%02X offset=0x%06X size=0x%06X\n' % (fhdr,mbank,moff,msz)) # DEBUG if fhdr != 'SFPW' and fhdr != 'SPSR' and foff >= 0: # if DSP work ram or invalid offset, don't write out anything if fhdr == 'SMPB' and use_osb: offset += 0x20 continue if fhdr == 'SOSB' and not use_osb: offset += 0x20 continue dsfbin[moff:moff+fsz] = datamlt[foff:foff+fsz] # write sound data if fhdr == sbank[use_osb] and mbank == bank: # check specifically for sequence data and get number of tracks ntracks = dsfbin[moff+0xC] print ('%s data contains %d track(s).' % (sbank[use_osb], ntracks)) offset += 0x20 #fcon.close() # DEBUG # ----------------------------------------------------------------------------------------------------- # Set sound commands if track != None: ctrack = track else: ctrack = 0 scbase = {0x01: 0x13200, 0x02: 0x12200}[vdrv] ssbase = scbase + 0x1F0 # SEQUENCE_START track number offset (for minidsf) dsfbin[scbase+0x00:scbase+0x10] = sndcmd([0x87,0x00,0x00,0x00,0x00,0x00]) # not sure, but 3rd and 5th bytes have various effects dsfbin[scbase+0x10:scbase+0x20] = sndcmd([0x82,0x00,mixer]) # MIXER_CHANGE dsfbin[scbase+0x20:scbase+0x30] = sndcmd([0x84,0x00,effect]) # EFFECT_CHANGE dsfbin[scbase+0x30:scbase+0x40] = sndcmd([0x81,0x00,0xFF]) # TOTAL_VOLUME dsfbin[scbase+0x40:scbase+0x50] = sndcmd([0x05+0x10*use_osb,0x00,0x00,volume]) # SEQUENCE_VOLUME (or OSB_VOLUME) dsfbin[ssbase+0x00:ssbase+0x10] = sndcmd([0x01+0x10*use_osb,0x00,0x00,bank,ctrack]) # SEQUENCE_START (or OSB_START) # ----------------------------------------------------------------------------------------------------- # Necessary for sound command processing dsfbin[scbase+0x200] = 0x01 # ----------------------------------------------------------------------------------------------------- # Driver patch for MANATEE.DRV - should cover most versions of the driver # Comment any of these out as needed NOPtable = { 0x01: [0x448,0x470,0x488,0x4A4,0x4AC,0x4C4,0x4DC], 0x02: [0x434,0x4CC,0x4E4,0x500,0x508,0x520,0x538], }[vdrv] NOP = array('B',[0x00,0x00,0xA0,0xE3]) dsfbin[NOPtable[0]:NOPtable[0]+4] = NOP # don't clear command area dsfbin[NOPtable[1]:NOPtable[1]+4] = NOP # don't clear MSB banks dsfbin[NOPtable[2]:NOPtable[2]+4] = NOP # don't clear MPB banks dsfbin[NOPtable[3]:NOPtable[3]+8] = NOP*2 # don't use default MDB bank 0 dsfbin[NOPtable[4]:NOPtable[4]+8] = NOP*2 # don't use default MDB bank 1 dsfbin[NOPtable[5]:NOPtable[5]+8] = NOP*2 # don't use default FPB dsfbin[NOPtable[6]:NOPtable[6]+8] = NOP*2 # don't use default FOB # ----------------------------------------------------------------------------------------------------- # Write output file (bout,xout) = fnoext(nout) if use_osb: xout += '_OSB' fo = open(xout+'.bin','wb') fo.write('\x00'*4) # load address fo.write(dsfbin.tostring()) fo.close() # ----------------------------------------------------------------------------------------------------- # If track number is explicity given, generate a single dsf file if track != None: ext = bout.replace(xout,'')[1:] os.system('bin2psf %s 18 %s.bin' % (ext,xout)) # Otherwise, automatically create minidsfs based on number of tracks found in bank else: # If only 1 track, create dsf file if ntracks == 1: os.system('bin2psf dsf 18 %s.bin' % xout) # Otherwise, create the dsflib file elif ntracks > 1: os.system('bin2psf dsflib 18 %s.bin' % xout) # ----------------------------------------------------------------------------------------------------- # Create minidsfs for itrack in range(0,ntracks): fo = open('temp.bin','wb') fo.write(pack('