Advertisement
Guest User

unrpa.py

a guest
Jan 5th, 2014
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 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