Advertisement
Guest User

unrpa

a guest
Jan 5th, 2014
53
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.05 KB | None | 0 0
  1. #!/usr/bin/env python2
  2.  
  3. '''
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13.  
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. '''
  17.  
  18. ### Copyright Gareth Latty 2009
  19.  
  20. ### Version 1.4 (Current)
  21. ### make_directory_structure improved.
  22.  
  23. ### Version 1.3
  24. ### BUGFIX: Presumed that the RPA archive would use the path seperator of the
  25. ### system. Broke under some circumstances.
  26.  
  27. ### Version 1.2
  28. ### BUGFIX: Presumed a forwards slash in the make_directory_structure function,
  29. ### Broke under windows when using --mkdir.
  30.  
  31. ### Version 1.1
  32. ### BUGFIX: Crash upon listing files when in verbose mode.
  33. ### BUGFIX: A few extra bytes were appended to the end of every file.
  34.  
  35. ### Version 1.0
  36. ### Initial Release
  37.  
  38. import os
  39. import optparse
  40. import sys
  41. import pickle
  42.  
  43. class UnRPA:
  44.  
  45. verbose = None
  46. path = None
  47. mkdir = None
  48. version = None
  49. archive = None
  50.  
  51. def __init__(self, filename, verbosity=1, path=None, mkdir=False, version=None):
  52. self.verbose = verbosity
  53. if path:
  54. self.path = os.path.abspath(path)
  55. else:
  56. self.path = os.getcwd()
  57. self.mkdir = mkdir
  58. self.version = version
  59. self.archive = filename
  60.  
  61. def extract_files(self):
  62. if self.verbose > 0:
  63. print("unrpa: extracting files.")
  64. if self.mkdir:
  65. self.make_directory_structure(self.path)
  66. if not os.path.isdir(self.path):
  67. sys.exit("unrpa: error: path doesn't exist, if you want to create it, use -m.")
  68. index = self.get_index()
  69. for item, data in index.iteritems():
  70. self.make_directory_structure(os.path.join(self.path, os.path.split(item)[0]))
  71. raw_file = self.extract_file(item, data)
  72. with open(os.path.join(self.path, item), "wb") as f:
  73. f.write(raw_file)
  74.  
  75. def list_files(self):
  76. if self.verbose > 1:
  77. print("unrpa: listing files:")
  78. index = self.get_index()
  79. paths = index.keys()
  80. paths.sort()
  81. for path in paths:
  82. print(path)
  83.  
  84. def extract_file(self, name, data):
  85. if self.verbose > 1:
  86. print("unrpa: extracting file: " + name)
  87. if len(data[0]) == 2:
  88. offset, dlen = data[0]
  89. start = ''
  90. else:
  91. offset, dlen, start = data[0]
  92. with open(self.archive, "rb") as f:
  93. f.seek(offset)
  94. raw_file = start + f.read(dlen - len(start))
  95. return raw_file
  96.  
  97. def make_directory_structure(self, name):
  98. if self.verbose > 2:
  99. print("unrpa: creating directory structure:" + name)
  100.  
  101. if not os.path.exists(name) and not os.path.isdir(name):
  102. os.makedirs(name)
  103.  
  104. def get_index(self):
  105. if not self.version:
  106. if os.path.splitext(self.archive)[1].lower() == ".rpa":
  107. with open(self.archive, "rb") as f:
  108. self.version = self.determine_version(f.readline())
  109. elif os.path.splitext(self.archive)[1].lower() == ".rpi":
  110. self.version = 1
  111. if self.version == 3:
  112. with open(self.archive, "rb") as f:
  113. l = f.readline()
  114. offset = int(l[8:24], 16)
  115. key = int(l[25:33], 16)
  116. f.seek(offset)
  117. index = pickle.loads(f.read().decode("zlib"))
  118. index = self.deobfuscate_index(index, key)
  119. elif self.version == 2:
  120. with open(self.archive, "rb") as f:
  121. offset = int(f.readline()[8:], 16)
  122. f.seek(offset)
  123. index = pickle.loads(f.read().decode("zlib"))
  124. elif self.version == 1:
  125. with open(self.archive, "rb") as f:
  126. index = pickle.loads(f.read().decode("zlib"))
  127. else:
  128. sys.exit("unrpa: error: file doesn't look like an archive, if you are sure it is, use -f.")
  129. if not "/" == os.sep:
  130. final = {}
  131. for item, data in index.iteritems():
  132. final[item.replace("/", os.sep)] = data
  133. return final
  134. else:
  135. return index
  136.  
  137. def determine_version(self, line):
  138. if line.startswith("RPA-3.0 "):
  139. return 3
  140. if line.startswith("RPA-2.0 "):
  141. return 2
  142. else:
  143. return None
  144.  
  145. def deobfuscate_index(self, index, key):
  146. for k in index.keys():
  147. if len(index[k][0]) == 2:
  148. index[k] = [ (offset ^ key, dlen ^ key) for offset, dlen in index[k] ]
  149. else:
  150. index[k] = [ (offset ^ key, dlen ^ key, start) for offset, dlen, start in index[k] ]
  151. return index
  152.  
  153. if __name__ == "__main__":
  154. parser = optparse.OptionParser(usage = "usage: %prog [options] pathname", version="%prog 1.1")
  155.  
  156. parser.add_option("-v", "--verbose", action="count", dest="verbose", help="explain what is being done [default]")
  157. parser.add_option("-s", "--silent", action="store_const", const=0, dest="verbose", default=1, help="make no output")
  158. parser.add_option("-l", "--list", action="store_true", dest="list", default=False, help="only list contents, do not extract")
  159. parser.add_option("-p", "--path", action="store", type="string", dest="path", default=None, help="will extract to the given path")
  160. parser.add_option("-m", "--mkdir", action="store_true", dest="mkdir", default=False, help="will make any non-existant directories in extraction path")
  161. parser.add_option("-f", "--force", action="store", type="int", dest="version", default=None, help="forces an archive version. May result in failure.")
  162.  
  163. (options, args) = parser.parse_args()
  164.  
  165. if not len(args) == 1:
  166. if options.verbose:
  167. parser.print_help()
  168. parser.error("incorrect number of arguments.")
  169.  
  170. if options.list and options.path:
  171. parser.error("option -p: only valid when extracting.")
  172.  
  173. if options.mkdir and not options.path:
  174. parser.error("option -m: only valid when --path (-p) is set.")
  175.  
  176. if options.list and options.verbose == 0:
  177. parser.error("option -l: can't be silent while listing data.")
  178.  
  179. filename = args[0]
  180.  
  181. unarchiver = UnRPA(filename, options.verbose, options.path, options.mkdir, options.version)
  182. if options.list:
  183. unarchiver.list_files()
  184. else:
  185. unarchiver.extract_files()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement