#!/usr/bin/env python # Copyright 2017 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """A tool for interacting with .pak files. For details on the pak file format, see: https://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings """ import argparse import hashlib import os import sys from grit.format import data_pack def _RepackMain(args): data_pack.RePack(args.output_pak_file, args.input_pak_files, args.whitelist, args.suppress_removed_key_output) def _ExtractMain(args): pak = data_pack.ReadDataPack(args.pak_file) for resource_id, payload in pak.resources.iteritems(): path = os.path.join(args.output_dir, str(resource_id)) with open(path, 'w') as f: f.write(payload) def _PrintMain(args): pak = data_pack.ReadDataPack(args.pak_file) id_map = {id(v): k for k, v in sorted(pak.resources.items(), reverse=True)} encoding = 'binary' if pak.encoding == 1: encoding = 'utf-8' elif pak.encoding == 2: encoding = 'utf-16' else: encoding = '?' + str(pak.encoding) print 'Encoding:', encoding try_decode = encoding.startswith('utf') # Print IDs in ascending order, since that's the order in which they appear in # the file (order is lost by Python dict). for resource_id in sorted(pak.resources): data = pak.resources[resource_id] desc = '' if try_decode: try: desc = unicode(data, encoding) if len(desc) > 60: desc = desc[:60] + u'...' desc = desc.replace('\n', '\\n') except UnicodeDecodeError: pass sha1 = hashlib.sha1(data).hexdigest()[:10] canonical_id = id_map[id(data)] if resource_id == canonical_id: line = u'Entry(id={}, len={}, sha1={}): {}'.format( resource_id, len(data), sha1, desc) else: line = u'Entry(id={}, alias_of={}): {}'.format( resource_id, canonical_id, desc) print line.encode('utf-8') def _ListMain(args): resources, _ = data_pack.ReadDataPack(args.pak_file) for resource_id in sorted(resources.keys()): args.output.write('%d\n' % resource_id) def main(): parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawTextHelpFormatter) sub_parsers = parser.add_subparsers() sub_parser = sub_parsers.add_parser('repack', help='Combines several .pak files into one.') sub_parser.add_argument('output_pak_file', help='File to create.') sub_parser.add_argument('input_pak_files', nargs='+', help='Input .pak files.') sub_parser.add_argument('--whitelist', help='Path to a whitelist used to filter output pak file resource IDs.') sub_parser.add_argument('--suppress-removed-key-output', action='store_true', help='Do not log which keys were removed by the whitelist.') sub_parser.set_defaults(func=_RepackMain) sub_parser = sub_parsers.add_parser('extract', help='Extracts pak file') sub_parser.add_argument('pak_file') sub_parser.add_argument('-o', '--output-dir', default='.', help='Directory to extract to.') sub_parser.set_defaults(func=_ExtractMain) sub_parser = sub_parsers.add_parser('print', help='Prints all pak IDs and contents. Useful for diffing.') sub_parser.add_argument('pak_file') sub_parser.set_defaults(func=_PrintMain) sub_parser = sub_parsers.add_parser('list-id', help='Outputs all resource IDs to a file.') sub_parser.add_argument('pak_file') sub_parser.add_argument('--output', type=argparse.FileType('w'), default=sys.stdout, help='The resource list path to write (default stdout)') sub_parser.set_defaults(func=_ListMain) if len(sys.argv) == 1: parser.print_help() sys.exit(1) elif len(sys.argv) == 2 and sys.argv[1] in actions: parser.parse_args(sys.argv[1:] + ['-h']) sys.exit(1) args = parser.parse_args() args.func(args) if __name__ == '__main__': main()