Want more features on Pastebin? Sign Up, it's FREE!
Guest

dsfmake

By: a guest on Feb 20th, 2012  |  syntax: None  |  size: 16.00 KB  |  views: 551  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. # =====================================================================================================
  2. # Preliminary MLT--->DSF converter (ver 0.03 2008-10-14) by kingshriek
  3. # This script only supports the Manatee sound driver and not necessarily all the different versions of
  4. # it. For sound data not in .MLT files, convert to .MLT first before using.
  5. # Requires bin2psf & psfpoint (http://www.neillcorlett.com/psf/utilities.html)
  6. # Update history located near end of file
  7. # =====================================================================================================
  8.  
  9. # =====================================================================================================
  10. # This script produces .dsf/.minidsf files from a Manatee sound driver image and a multi-unit sound
  11. # data file (.MLT extension) and has similar functionality to the ssfmake script (for .ssf/.minissf).
  12. # Not all games pack their sound data into .MLT files, and for those that don't, it will require
  13. # combining the various sound files into an .MLT file before using this script. The individual sound
  14. # formats should have extentions similar to their header strings, only without the leading "S" (e.g. a
  15. # sequence data file will have extention .msb and header string SMSB). See comments below inside
  16. # dsfmake() for information on the various sound formats and header strings.
  17. #
  18. # This script in its current form may not work with some versions of the manatee driver. This is the
  19. # case since the driver needs to be patched in a few locations so that it doesn't wipe out ripper-provided
  20. # sound commands upon initialization and these patch locations may vary with different driver versions.
  21. # Also, some of these patches disable loading of various default sound data (the driver contains itself
  22. # contains a default DSP program and mixer for instance) so that it uses data in the MLT file instead.
  23. # If a track needs any of this default data, you will need to comment out the corresponding patches in
  24. # dsfmake() for a proper rip.
  25. #
  26. # The bulk of the script is defined as a function, which is called at the very bottom. Feel free to customize
  27. # this bottom section as you wish. Once good use for it is that you can write some code to loop over a bunch
  28. # of files and perform the dsf creation in batch mode.
  29. # =====================================================================================================
  30.  
  31. # =====================================================================================================
  32. # PARAMETERS
  33. # -----------------------------------------------------------------------------------------------------
  34. bank     = 0x00    # sequence bank number
  35. track    = None    # sequence track number (if None, create minidsfs for all tracks)
  36. volume   = 0x77    # sequence volume (0xFF is max, but it should probably be <= 0x7F in most cases)
  37. mixer    = 0x00    # effect-out mixer select (usually 0x00)
  38. effect   = 0x00    # DSP program select (usually 0x00)
  39. # Not sure about any other paramaters yet, especially for sound command 0x87. For others, try editing
  40. # the sound commands specified in dsfmake() directly for now.
  41. # -----------------------------------------------------------------------------------------------------
  42. use_osb  = 0       # 0: use MIDI sequence data, 1: use one-shot data (sound effects)
  43. naomi    = 0       # 0: assume 2 MB sound RAM, 1: assume 8 MB sound RAM (Naomi/Hikaru boards)
  44. # -----------------------------------------------------------------------------------------------------
  45. # filenames
  46. ndrv = 'MANATEE.DRV'    # sound driver (usually MANATEE.DRV)
  47. nmlt = 'FILE.MLT'    # sound data archive (.MLT file)
  48. # -----------------------------------------------------------------------------------------------------
  49. nout = nmlt.upper().replace('.MLT','.DSF')    # output file name
  50. # =====================================================================================================
  51.  
  52. # =====================================================================================================
  53. from struct import *    # pack, unpack
  54. from array import *
  55. import os    # system
  56. # =====================================================================================================
  57.  
  58. # =====================================================================================================
  59. # Converts list of bytes into a sound command array (zero-padded out to 16 bytes)
  60. def sndcmd(x):
  61.     if len(x) > 0x10:
  62.         x = x[:0x10]
  63.     return array('B',x + [0x00]*(0x10-len(x)))
  64. # =====================================================================================================
  65.  
  66. # =====================================================================================================
  67. # Get base file name with and without extension
  68. def fnoext(fname):
  69.     fnameb = os.path.basename(fname)
  70.     idot  = -fname[::-1].find('.')-1
  71.     if idot:
  72.         fnamex = fnameb[:idot]
  73.     else:
  74.         fnamex = fnameb
  75.     return (fnameb,fnamex)
  76. # =====================================================================================================
  77.  
  78. # =====================================================================================================
  79. # Get sound RAM offset and byte allocation (useful for custom dsflib/minidsf creation)
  80. def get_offset(mlt_filename, hdr_str, bank):
  81.     mlt_hdr_top = array('B', open(mlt_filename, 'rb').read(0x20))    
  82.     nhdr = unpack('<I', mlt_hdr_top[0x8:0xC])[0]    # Number of MLT header entries
  83.     hdr_offset = 0x20    # Skip the MLT top header
  84.     mlt_hdr = array('B', open(mlt_filename, 'rb').read(0x20*(nhdr+1)))    # Get the full MLT header
  85.     for k in range(nhdr):    # Search for header string and bank
  86.         mlt_hdr_str = mlt_hdr[hdr_offset:hdr_offset+4].tostring()    # Get header string from MLT header entry
  87.         mlt_bank = unpack('<I', mlt_hdr[hdr_offset+4:hdr_offset+8])[0]    # Get bank number from MLT header entry
  88.         if mlt_hdr_str == hdr_str and mlt_bank == bank:    # Compare to inputs
  89.             offset = unpack('<I', mlt_hdr[hdr_offset+0x8:hdr_offset+0xC])[0]    # Get sound RAM offset from MLT header entry
  90.             alloc = unpack('<I', mlt_hdr[hdr_offset+0xC:hdr_offset+0x10])[0]    # Get sound RAM allocation from MLT header entry
  91.             return (offset, alloc)
  92.         hdr_offset += 0x20
  93.     return (None, None)    
  94. # =====================================================================================================    
  95.  
  96. # =====================================================================================================
  97. # Creates ssf file from user-specified parameters.
  98. # Inputs are defined in paramter section above.
  99. def dsfmake(nout,ndrv,nmlt,bank,track,volume,mixer,effect):
  100.     # Initialization
  101.     szdrv = 0
  102.     szmlt = 0
  103.     datadrv = array('B',[])
  104.     datamlt = array('B',[])
  105.     ntracks = 0
  106. # -----------------------------------------------------------------------------------------------------
  107.     if ndrv != '':
  108.         fdrv = open(ndrv,'rb')    # sound driver
  109.         szdrv = os.path.getsize(ndrv)
  110.         datadrv = array('B',fdrv.read(szdrv))
  111.         fdrv.close()
  112.     if nmlt != '':
  113.         fmlt = open(nmlt,'rb')    # sound data
  114.         szmlt = os.path.getsize(nmlt)
  115.         datamlt = array('B',fmlt.read(szmlt))
  116.         fmlt.close()
  117. # -----------------------------------------------------------------------------------------------------
  118.     if naomi:
  119.         dsfbin = array('B','\x00'*0x800000)
  120.     else:
  121.         dsfbin = array('B','\x00'*0x200000)
  122. # -----------------------------------------------------------------------------------------------------
  123. # Set driver
  124. # Identified by header string 'SDRV'
  125.         if datadrv[:4].tostring() == 'SDRV':    # header string for sound driver
  126.                 sfbin[:szdrv-0x20] = datadrv[0x20:szdrv]
  127.                 vdrv = datadrv[0x04]    # driver version
  128. # -----------------------------------------------------------------------------------------------------
  129.     # Set sound data
  130.     # MANATEE driver sound data formats identified by header strings:
  131.     #     SMLT - multi-unit archive
  132.     #     SMSB - MIDI sequence bank
  133.     #     SMPB - MIDI program bank
  134.     #     SMDB - MIDI drum bank
  135.     #     SOSB - one-shot bank (sound effects)
  136.     #     SFPB - DSP program bank
  137.     #     SFPW - DSP work RAM
  138.     #     SFOB - DSP output bank
  139.     #     SPSR - PCM stream ring-buffer
  140.     sbank = ('SMSB', 'SOSB')    # DSF can either target MIDI sequences or one-shots
  141.     areamap = {'SMSB':0x14000,'SMPB':0x14080,'SOSB':0x14100,'SFPB':0x14200,'SFOB':0x14280,'SFPW':0x14288,'SMDB':0x14290,'SPSR':0x14180} # canonical area map offsets
  142.     #fcon = open('mlt_contents.txt','w') # DEBUG
  143.     if datamlt[:4].tostring() == 'SMLT':
  144.         nfiles = unpack('<I',datamlt[0x8:0xC].tostring())[0]
  145.         offset = 0x20
  146.         for ifile in range(0,nfiles):
  147.             fhdr = datamlt[offset+0x0:offset+0x4].tostring()
  148.             foff = unpack('<i',datamlt[offset+0x10:offset+0x14].tostring())[0]
  149.             fsz = unpack('<i',datamlt[offset+0x14:offset+0x18].tostring())[0]
  150.             mbank = unpack('<i',datamlt[offset+0x4:offset+0x8].tostring())[0]
  151.             moff = unpack('<i',datamlt[offset+0x8:offset+0xC].tostring())[0]
  152.             msz = unpack('<i',datamlt[offset+0xC:offset+0x10].tostring())[0]
  153.             #if fhdr in areamap and (fhdr == 'SFPW' or foff >=0):    # write area map data if offset is known
  154.             dsfbin[areamap[fhdr]+8*mbank+0x0:areamap[fhdr]+8*mbank+0x4] = datamlt[offset+0x8:offset+0xC]
  155.             dsfbin[areamap[fhdr]+8*mbank+0x4:areamap[fhdr]+8*mbank+0x8] = datamlt[offset+0xC:offset+0x10]
  156.             #if foff >= 0 or fhdr == 'SFPW' or fhdr == 'SPSR':    # DEBUG
  157.                 #fcon.write('%s: bank=0x%02X offset=0x%06X size=0x%06X\n' % (fhdr,mbank,moff,msz))    # DEBUG
  158.             if fhdr != 'SFPW' and fhdr != 'SPSR' and foff >= 0:    # if DSP work ram or invalid offset, don't write out anything
  159.                 if fhdr == 'SMPB' and use_osb:
  160.                     offset += 0x20
  161.                     continue
  162.                 if fhdr == 'SOSB' and not use_osb:
  163.                     offset += 0x20
  164.                     continue
  165.                 dsfbin[moff:moff+fsz] = datamlt[foff:foff+fsz]    # write sound data
  166.                 if fhdr == sbank[use_osb] and mbank == bank:    # check specifically for sequence data and get number of tracks
  167.                     ntracks = dsfbin[moff+0xC]
  168.                     print ('%s data contains %d track(s).' % (sbank[use_osb], ntracks))
  169.             offset += 0x20
  170.     #fcon.close()    # DEBUG
  171. # -----------------------------------------------------------------------------------------------------
  172.     # Set sound commands
  173.     if track != None:
  174.         ctrack = track
  175.     else:
  176.         ctrack = 0
  177.     scbase = {0x01: 0x13200, 0x02: 0x12200}[vdrv]
  178.     ssbase = scbase + 0x1F0   # SEQUENCE_START track number offset (for minidsf)
  179.     dsfbin[scbase+0x00:scbase+0x10] = sndcmd([0x87,0x00,0x00,0x00,0x00,0x00])    # not sure, but 3rd and 5th bytes have various effects
  180.     dsfbin[scbase+0x10:scbase+0x20] = sndcmd([0x82,0x00,mixer])    # MIXER_CHANGE
  181.     dsfbin[scbase+0x20:scbase+0x30] = sndcmd([0x84,0x00,effect])    # EFFECT_CHANGE
  182.     dsfbin[scbase+0x30:scbase+0x40] = sndcmd([0x81,0x00,0xFF])    # TOTAL_VOLUME
  183.     dsfbin[scbase+0x40:scbase+0x50] = sndcmd([0x05+0x10*use_osb,0x00,0x00,volume])    # SEQUENCE_VOLUME (or OSB_VOLUME)
  184.     dsfbin[ssbase+0x00:ssbase+0x10] = sndcmd([0x01+0x10*use_osb,0x00,0x00,bank,ctrack])    # SEQUENCE_START (or OSB_START)
  185. # -----------------------------------------------------------------------------------------------------
  186.     # Necessary for sound command processing
  187.     dsfbin[scbase+0x200] = 0x01
  188. # -----------------------------------------------------------------------------------------------------
  189.     # Driver patch for MANATEE.DRV - should cover most versions of the driver
  190.     # Comment any of these out as needed
  191.     NOPtable = {
  192.     0x01: [0x448,0x470,0x488,0x4A4,0x4AC,0x4C4,0x4DC],
  193.     0x02: [0x434,0x4CC,0x4E4,0x500,0x508,0x520,0x538],
  194.     }[vdrv]
  195.     NOP = array('B',[0x00,0x00,0xA0,0xE3])
  196.     dsfbin[NOPtable[0]:NOPtable[0]+4] = NOP      # don't clear command area
  197.     dsfbin[NOPtable[1]:NOPtable[1]+4] = NOP      # don't clear MSB banks
  198.     dsfbin[NOPtable[2]:NOPtable[2]+4] = NOP      # don't clear MPB banks
  199.     dsfbin[NOPtable[3]:NOPtable[3]+8] = NOP*2    # don't use default MDB bank 0
  200.     dsfbin[NOPtable[4]:NOPtable[4]+8] = NOP*2    # don't use default MDB bank 1
  201.     dsfbin[NOPtable[5]:NOPtable[5]+8] = NOP*2    # don't use default FPB
  202.     dsfbin[NOPtable[6]:NOPtable[6]+8] = NOP*2    # don't use default FOB
  203. # -----------------------------------------------------------------------------------------------------
  204.     # Write output file
  205.     (bout,xout) = fnoext(nout)
  206.     if use_osb:
  207.         xout += '_OSB'
  208.     fo = open(xout+'.bin','wb')
  209.     fo.write('\x00'*4)    # load address
  210.     fo.write(dsfbin.tostring())
  211.     fo.close()
  212. # -----------------------------------------------------------------------------------------------------
  213.     # If track number is explicity given, generate a single dsf file
  214.     if track != None:
  215.         ext = bout.replace(xout,'')[1:]
  216.         os.system('bin2psf %s 18 %s.bin' % (ext,xout))
  217.     # Otherwise, automatically create minidsfs based on number of tracks found in bank
  218.     else:
  219.         # If only 1 track, create dsf file
  220.         if ntracks == 1:
  221.             os.system('bin2psf dsf 18 %s.bin' % xout)
  222.         # Otherwise, create the dsflib file
  223.         elif ntracks > 1:
  224.             os.system('bin2psf dsflib 18 %s.bin' % xout)
  225. # -----------------------------------------------------------------------------------------------------
  226.             # Create minidsfs
  227.             for itrack in range(0,ntracks):
  228.                 fo = open('temp.bin','wb')
  229.                 fo.write(pack('<I',ssbase+3))
  230.                 fo.write(pack('B',bank))
  231.                 fo.write(pack('B',itrack))
  232.                 fo.close()
  233.                 os.system('bin2psf minidsf 18 ' + 'temp.bin')
  234.                 os.system('psfpoint "-_lib='+xout+'.dsflib" '+'temp.minidsf')
  235.                 strfmt = ('_%02d','_%03d')[use_osb]
  236.                 fname = xout +  '_%02d' % bank + strfmt % itrack + '.minidsf'
  237.                 if os.access(fname,os.F_OK):
  238.                     os.remove(fname)
  239.                 os.rename('temp.minidsf',fname)
  240. # -----------------------------------------------------------------------------------------------------
  241.     # Cleanup
  242.     if os.access(xout+'.bin',os.F_OK):
  243.         os.remove(xout+'.bin')
  244.     if os.access('temp.bin',os.F_OK):
  245.         os.remove('temp.bin')
  246. # =====================================================================================================
  247.  
  248. # =====================================================================================================
  249. # Generate dsf file(s). Feel free to customize this section as you see fit (looping through a bunch of files, etc.)
  250. dsfmake(nout,ndrv,nmlt,bank,track,volume,mixer,effect)
  251.    
  252. # =====================================================================================================
  253.  
  254. # =====================================================================================================
  255. # Update history:
  256. # 08-10-14 (0.03) - Changed the naming behavior of the output file when automatic dsflib/minidsf creation
  257. #                   is turned off so that it doesn't force a .dsf extention (useful for custom dsflibs).
  258. #                   Added a simple utility function that gets the sound RAM offset and allocation for
  259. #                   a given sound data type and bank (useful for custom dsflib/minidsf creation).
  260. # 08-10-12 (0.02) - Very minor update. Made it possible to turn off automatic dsflib/minidsf creation.
  261. # 08-02-16 (0.01) - Filled in much of the missing information from the initial version - sound data types,
  262. #                   area map locations, sound commands, driver patches, etc. Added ability to rip one-shot
  263. #                   data in addition to sequence data.
  264. # 08-01-12 (0.00) - Initial version.
  265. # =====================================================================================================
clone this paste RAW Paste Data