id_utils.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. # a collection of utility functions to manipulate pak files
  2. import os, zipfile, md5, pdb
  3. # sorts in reverse alphabetical order like doom does for searching
  4. def list_paks( path ):
  5. files = os.listdir( path )
  6. for i in files:
  7. if ( i[-4:] != '.pk4' ):
  8. files.remove( i )
  9. files.sort()
  10. files.reverse()
  11. return files
  12. def list_files_in_pak( pak ):
  13. files = []
  14. zippy = zipfile.ZipFile( pak )
  15. files += zippy.namelist()
  16. files.sort()
  17. return files
  18. # no sorting, blunt list of everything
  19. def list_files_in_paks( path ):
  20. files = []
  21. zippies = list_paks( path )
  22. for fname in zippies:
  23. print fname
  24. zippy = zipfile.ZipFile( os.path.join( path, fname ) )
  25. files += zippy.namelist()
  26. # sort and remove dupes
  27. dico = {}
  28. for f in files:
  29. dico[ f ] = 1
  30. files = dico.keys()
  31. files.sort()
  32. return files
  33. # build a dictionary of names -> ( pak name, md5 ) from a path of pk4s
  34. def md5_in_paks( path ):
  35. ret = {}
  36. zippies = list_paks( path )
  37. for fname in zippies:
  38. print fname
  39. zippy = zipfile.ZipFile( os.path.join( path, fname ) )
  40. for file in zippy.namelist():
  41. if ( ret.has_key( file ) ):
  42. continue
  43. data = zippy.read( file )
  44. m = md5.new()
  45. m.update( data )
  46. ret[ file ] = ( fname, m.hexdigest() )
  47. return ret
  48. # find which files need to be updated in a set of paks from an expanded list
  49. # returns ( updated, not_found, {} )
  50. # ignores directories
  51. # by default, no case match is done
  52. # if case match is set, return ( updated, not_found, { zip case -> FS case } )
  53. # updated will contain the zip case name
  54. def list_updated_files( pak_path, base_path, case_match = False ):
  55. not_found = []
  56. updated = []
  57. case_table = {}
  58. pak_md5 = md5_in_paks( pak_path )
  59. for file in pak_md5.keys():
  60. if ( file[-1] == '/' ):
  61. continue
  62. path = os.path.join( base_path, file )
  63. if ( case_match ):
  64. ret = ifind( base_path, file )
  65. if ( not ret[ 0 ] ):
  66. not_found.append( file )
  67. continue
  68. else:
  69. case_table[ path ] = ret[ 1 ]
  70. path = os.path.join( base_path, ret[ 1 ] )
  71. try:
  72. f = open( path )
  73. data = f.read()
  74. f.close()
  75. except:
  76. if ( case_match ):
  77. raise "internal error: ifind success but later read failed"
  78. not_found.append( file )
  79. else:
  80. m = md5.new()
  81. m.update( data )
  82. if ( m.hexdigest() != pak_md5[ file ][ 1 ] ):
  83. print file
  84. updated.append( file )
  85. return ( updated, not_found, case_table )
  86. # find which files are missing in the expanded path, and extract the directories
  87. # returns ( files, dirs, missing )
  88. def status_files_for_path( path, infiles ):
  89. files = []
  90. dirs = []
  91. missing = []
  92. for i in infiles:
  93. test_path = os.path.join( path, i )
  94. if ( os.path.isfile( test_path ) ):
  95. files.append( i )
  96. elif ( os.path.isdir( test_path ) ):
  97. dirs.append( i )
  98. else:
  99. missing.append( i )
  100. return ( files, dirs, missing )
  101. # build a pak from a base path and a list of files
  102. def build_pak( pak, path, files ):
  103. zippy = zipfile.ZipFile( pak, 'w', zipfile.ZIP_DEFLATED )
  104. for i in files:
  105. source_path = os.path.join( path, i )
  106. print source_path
  107. zippy.write( source_path, i )
  108. zippy.close()
  109. # process the list of files after a run to update media
  110. # dds/ -> verify all the .dds are present in zip ( case insensitive )
  111. # .wav -> verify that all .wav have a .ogg version in zip ( case insensitive )
  112. # .tga not in dds/ -> try to find a .dds for them
  113. # work from a list of files, and a path to the base pak files
  114. # files: text files with files line by line
  115. # pak_path: the path to the pak files to compare against
  116. # returns: ( [ missing ], [ bad ] )
  117. # bad are files the function didn't know what to do about ( bug )
  118. # missing are lowercased of all the files that where not matched in build
  119. # the dds/ ones are all forced to .dds extension
  120. # missing .wav are returned in the missing list both as .wav and .ogg
  121. # ( that's handy when you need to fetch next )
  122. def check_files_against_build( files, pak_path ):
  123. pak_list = list_files_in_paks( pak_path )
  124. # make it lowercase
  125. tmp = []
  126. for i in pak_list:
  127. tmp.append( i.lower() )
  128. pak_list = tmp
  129. # read the files and make them lowercase
  130. f = open( files )
  131. check_files = f.readlines()
  132. f.close()
  133. tmp = []
  134. for i in check_files:
  135. s = i.lower()
  136. s = s.replace( '\n', '' )
  137. s = s.replace( '\r', '' )
  138. tmp.append( s )
  139. check_files = tmp
  140. # start processing
  141. bad = []
  142. missing = []
  143. for i in check_files:
  144. if ( i[ :4 ] == 'dds/' ):
  145. if ( i[ len(i)-4: ] == '.tga' ):
  146. i = i[ :-4 ] + '.dds'
  147. elif ( i[ len(i)-4: ] != '.dds' ):
  148. print 'File not understood: ' + i
  149. bad.append( i )
  150. continue
  151. try:
  152. pak_list.index( i )
  153. except:
  154. print 'Not found: ' + i
  155. missing.append( i )
  156. elif ( i[ len(i)-4: ] == '.wav' ):
  157. i = i[ :-4 ] + '.ogg'
  158. try:
  159. pak_list.index( i )
  160. except:
  161. print 'Not found: ' + i
  162. missing.append( i )
  163. missing.append( i[ :-4 ] + '.wav' )
  164. elif ( i[ len(i)-4: ] == '.tga' ):
  165. # tga, not from dds/
  166. try:
  167. pak_list.index( i )
  168. except:
  169. print 'Not found: ' + i
  170. missing.append( i )
  171. i = 'dds/' + i[ :-4 ] + '.dds'
  172. print 'Add dds : ' + i
  173. missing.append( i )
  174. else:
  175. try:
  176. pak_list.index( i )
  177. except:
  178. print 'Not found: ' + i
  179. missing.append( i )
  180. return ( missing, bad )
  181. # match a path to a file in a case insensitive way
  182. # return ( True/False, 'walked up to' )
  183. def ifind( base, path ):
  184. refpath = path
  185. path = os.path.normpath( path )
  186. path = os.path.normcase( path )
  187. # early out just in case
  188. if ( os.path.exists( path ) ):
  189. return ( True, path )
  190. head = path
  191. components = []
  192. while ( len( head ) ):
  193. ( head, chunk ) = os.path.split( head )
  194. components.append( chunk )
  195. #print 'head: %s - components: %s' % ( head, repr( components ) )
  196. components.reverse()
  197. level = 0
  198. for root, dirs, files in os.walk( base, topdown = True ):
  199. if ( level < len( components ) - 1 ):
  200. #print 'filter dirs: %s' % repr( dirs )
  201. dirs_del = []
  202. for i in dirs:
  203. if ( not i.lower() == components[ level ].lower() ):
  204. dirs_del.append( i )
  205. for i in dirs_del:
  206. dirs.remove( i )
  207. level += 1
  208. # we assume there is never going to be 2 dirs with only case difference
  209. if ( len( dirs ) != 1 ):
  210. #print '%s: ifind failed dirs matching at %s - dirs: %s' % ( refpath, root, repr( dirs ) )
  211. return ( False, root[ len( base ) + 1: ] )
  212. else:
  213. # must find the file here
  214. for i in files:
  215. if ( i.lower() == components[-1].lower() ):
  216. return ( True, os.path.join( root, i )[ len( base ) + 1: ] )
  217. return ( False, root[ len( base ) + 1: ] )
  218. # do case insensitive FS search on files list
  219. # return [ cased files, not found (unmodified ) ]
  220. def ifind_list( base, files ):
  221. cased = []
  222. notfound = []
  223. for i in files:
  224. ret = ifind( base, i )
  225. if ( ret[ 0 ] ):
  226. cased.append( ret[ 1 ] )
  227. else:
  228. notfound.append( i )
  229. return [ cased, notfound ]