Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # =====================================================================================================
- # 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('<I', mlt_hdr_top[0x8:0xC])[0] # Number of MLT header entries
- hdr_offset = 0x20 # Skip the MLT top header
- mlt_hdr = array('B', open(mlt_filename, 'rb').read(0x20*(nhdr+1))) # Get the full MLT header
- for k in range(nhdr): # Search for header string and bank
- mlt_hdr_str = mlt_hdr[hdr_offset:hdr_offset+4].tostring() # Get header string from MLT header entry
- mlt_bank = unpack('<I', mlt_hdr[hdr_offset+4:hdr_offset+8])[0] # Get bank number from MLT header entry
- if mlt_hdr_str == hdr_str and mlt_bank == bank: # Compare to inputs
- offset = unpack('<I', mlt_hdr[hdr_offset+0x8:hdr_offset+0xC])[0] # Get sound RAM offset from MLT header entry
- alloc = unpack('<I', mlt_hdr[hdr_offset+0xC:hdr_offset+0x10])[0] # Get sound RAM allocation from MLT header entry
- return (offset, alloc)
- hdr_offset += 0x20
- return (None, None)
- # =====================================================================================================
- # =====================================================================================================
- # Creates ssf file from user-specified parameters.
- # Inputs are defined in paramter section above.
- def dsfmake(nout,ndrv,nmlt,bank,track,volume,mixer,effect):
- # Initialization
- szdrv = 0
- szmlt = 0
- datadrv = array('B',[])
- datamlt = array('B',[])
- ntracks = 0
- # -----------------------------------------------------------------------------------------------------
- if ndrv != '':
- fdrv = open(ndrv,'rb') # sound driver
- szdrv = os.path.getsize(ndrv)
- datadrv = array('B',fdrv.read(szdrv))
- fdrv.close()
- if nmlt != '':
- fmlt = open(nmlt,'rb') # sound data
- szmlt = os.path.getsize(nmlt)
- datamlt = array('B',fmlt.read(szmlt))
- fmlt.close()
- # -----------------------------------------------------------------------------------------------------
- if naomi:
- dsfbin = array('B','\x00'*0x800000)
- else:
- dsfbin = array('B','\x00'*0x200000)
- # -----------------------------------------------------------------------------------------------------
- # Set driver
- # Identified by header string 'SDRV'
- if datadrv[:4].tostring() == 'SDRV': # header string for sound driver
- sfbin[:szdrv-0x20] = datadrv[0x20:szdrv]
- vdrv = datadrv[0x04] # driver version
- # -----------------------------------------------------------------------------------------------------
- # Set sound data
- # MANATEE driver sound data formats identified by header strings:
- # SMLT - multi-unit archive
- # SMSB - MIDI sequence bank
- # SMPB - MIDI program bank
- # SMDB - MIDI drum bank
- # SOSB - one-shot bank (sound effects)
- # SFPB - DSP program bank
- # SFPW - DSP work RAM
- # SFOB - DSP output bank
- # SPSR - PCM stream ring-buffer
- sbank = ('SMSB', 'SOSB') # DSF can either target MIDI sequences or one-shots
- areamap = {'SMSB':0x14000,'SMPB':0x14080,'SOSB':0x14100,'SFPB':0x14200,'SFOB':0x14280,'SFPW':0x14288,'SMDB':0x14290,'SPSR':0x14180} # canonical area map offsets
- #fcon = open('mlt_contents.txt','w') # DEBUG
- if datamlt[:4].tostring() == 'SMLT':
- nfiles = unpack('<I',datamlt[0x8:0xC].tostring())[0]
- offset = 0x20
- for ifile in range(0,nfiles):
- fhdr = datamlt[offset+0x0:offset+0x4].tostring()
- foff = unpack('<i',datamlt[offset+0x10:offset+0x14].tostring())[0]
- fsz = unpack('<i',datamlt[offset+0x14:offset+0x18].tostring())[0]
- mbank = unpack('<i',datamlt[offset+0x4:offset+0x8].tostring())[0]
- moff = unpack('<i',datamlt[offset+0x8:offset+0xC].tostring())[0]
- msz = unpack('<i',datamlt[offset+0xC:offset+0x10].tostring())[0]
- #if fhdr in areamap and (fhdr == 'SFPW' or foff >=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('<I',ssbase+3))
- fo.write(pack('B',bank))
- fo.write(pack('B',itrack))
- fo.close()
- os.system('bin2psf minidsf 18 ' + 'temp.bin')
- os.system('psfpoint "-_lib='+xout+'.dsflib" '+'temp.minidsf')
- strfmt = ('_%02d','_%03d')[use_osb]
- fname = xout + '_%02d' % bank + strfmt % itrack + '.minidsf'
- if os.access(fname,os.F_OK):
- os.remove(fname)
- os.rename('temp.minidsf',fname)
- # -----------------------------------------------------------------------------------------------------
- # Cleanup
- if os.access(xout+'.bin',os.F_OK):
- os.remove(xout+'.bin')
- if os.access('temp.bin',os.F_OK):
- os.remove('temp.bin')
- # =====================================================================================================
- # =====================================================================================================
- # Generate dsf file(s). Feel free to customize this section as you see fit (looping through a bunch of files, etc.)
- dsfmake(nout,ndrv,nmlt,bank,track,volume,mixer,effect)
- # =====================================================================================================
- # =====================================================================================================
- # Update history:
- # 08-10-14 (0.03) - Changed the naming behavior of the output file when automatic dsflib/minidsf creation
- # is turned off so that it doesn't force a .dsf extention (useful for custom dsflibs).
- # Added a simple utility function that gets the sound RAM offset and allocation for
- # a given sound data type and bank (useful for custom dsflib/minidsf creation).
- # 08-10-12 (0.02) - Very minor update. Made it possible to turn off automatic dsflib/minidsf creation.
- # 08-02-16 (0.01) - Filled in much of the missing information from the initial version - sound data types,
- # area map locations, sound commands, driver patches, etc. Added ability to rip one-shot
- # data in addition to sequence data.
- # 08-01-12 (0.00) - Initial version.
- # =====================================================================================================
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement