Advertisement
Guest User

dsfmake

a guest
Feb 20th, 2012
989
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.00 KB | None | 0 0
  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. # =====================================================================================================
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement