| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 #!/usr/bin/python2.4 | 
|  | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 
|  | 3 # Use of this source code is governed by a BSD-style license that can be | 
|  | 4 # found in the LICENSE file. | 
|  | 5 | 
|  | 6 '''Support for formatting a data pack file used for platform agnostic resource | 
|  | 7 files. | 
|  | 8 ''' | 
|  | 9 | 
|  | 10 import exceptions | 
|  | 11 import os | 
|  | 12 import struct | 
|  | 13 import sys | 
|  | 14 | 
|  | 15 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) | 
|  | 16 from grit.format import interface | 
|  | 17 from grit.node import include | 
|  | 18 from grit.node import message | 
|  | 19 from grit.node import misc | 
|  | 20 | 
|  | 21 | 
|  | 22 PACK_FILE_VERSION = 4 | 
|  | 23 HEADER_LENGTH = 2 * 4 + 1  # Two uint32s. (file version, number of entries) and | 
|  | 24                            # one uint8 (encoding of text resources) | 
|  | 25 BINARY, UTF8, UTF16 = range(3) | 
|  | 26 | 
|  | 27 class WrongFileVersion(Exception): | 
|  | 28   pass | 
|  | 29 | 
|  | 30 class DataPackContents: | 
|  | 31   def __init__(self, resources, encoding): | 
|  | 32     self.resources = resources | 
|  | 33     self.encoding = encoding | 
|  | 34 | 
|  | 35 class DataPack(interface.ItemFormatter): | 
|  | 36   '''Writes out the data pack file format (platform agnostic resource file).''' | 
|  | 37   def Format(self, item, lang='en', begin_item=True, output_dir='.'): | 
|  | 38     if not begin_item: | 
|  | 39       return '' | 
|  | 40 | 
|  | 41     assert isinstance(item, misc.ReleaseNode) | 
|  | 42 | 
|  | 43     nodes = DataPack.GetDataNodes(item) | 
|  | 44     data = {} | 
|  | 45     for node in nodes: | 
|  | 46       id, value = node.GetDataPackPair(lang, UTF8) | 
|  | 47       data[id] = value | 
|  | 48     return DataPack.WriteDataPackToString(data, UTF8) | 
|  | 49 | 
|  | 50   @staticmethod | 
|  | 51   def GetDataNodes(item): | 
|  | 52     '''Returns a list of nodes that can be packed into the data pack file.''' | 
|  | 53     nodes = [] | 
|  | 54     if (isinstance(item, misc.IfNode) and not item.IsConditionSatisfied()): | 
|  | 55       return nodes | 
|  | 56     if (isinstance(item, include.IncludeNode) or | 
|  | 57         isinstance(item, message.MessageNode)): | 
|  | 58       # Include this node if it wasn't marked as skipped by a whitelist. | 
|  | 59       if not item.WhitelistMarkedAsSkip(): | 
|  | 60         return [item] | 
|  | 61       return nodes | 
|  | 62     for child in item.children: | 
|  | 63       nodes.extend(DataPack.GetDataNodes(child)) | 
|  | 64     return nodes | 
|  | 65 | 
|  | 66   @staticmethod | 
|  | 67   def ReadDataPack(input_file): | 
|  | 68     """Reads a data pack file and returns a dictionary.""" | 
|  | 69     data = open(input_file, "rb").read() | 
|  | 70     original_data = data | 
|  | 71 | 
|  | 72     # Read the header. | 
|  | 73     version, num_entries, encoding = struct.unpack("<IIB", | 
|  | 74                                                    data[:HEADER_LENGTH]) | 
|  | 75     if version != PACK_FILE_VERSION: | 
|  | 76       print "Wrong file version in ", input_file | 
|  | 77       raise WrongFileVersion | 
|  | 78 | 
|  | 79     resources = {} | 
|  | 80     if num_entries == 0: | 
|  | 81       return DataPackContents(resources, encoding) | 
|  | 82 | 
|  | 83     # Read the index and data. | 
|  | 84     data = data[HEADER_LENGTH:] | 
|  | 85     kIndexEntrySize = 2 + 4  # Each entry is a uint16 and a uint32. | 
|  | 86     for _ in range(num_entries): | 
|  | 87       id, offset = struct.unpack("<HI", data[:kIndexEntrySize]) | 
|  | 88       data = data[kIndexEntrySize:] | 
|  | 89       next_id, next_offset = struct.unpack("<HI", data[:kIndexEntrySize]) | 
|  | 90       resources[id] = original_data[offset:next_offset] | 
|  | 91 | 
|  | 92     return DataPackContents(resources, encoding) | 
|  | 93 | 
|  | 94   @staticmethod | 
|  | 95   def WriteDataPackToString(resources, encoding): | 
|  | 96     """Write a map of id=>data into a string in the data pack format and return | 
|  | 97     it.""" | 
|  | 98     ids = sorted(resources.keys()) | 
|  | 99     ret = [] | 
|  | 100 | 
|  | 101     # Write file header. | 
|  | 102     ret.append(struct.pack("<IIB", PACK_FILE_VERSION, len(ids), encoding)) | 
|  | 103     HEADER_LENGTH = 2 * 4 + 1            # Two uint32s and one uint8. | 
|  | 104 | 
|  | 105     # Each entry is a uint16 + a uint32s. We have one extra entry for the last | 
|  | 106     # item. | 
|  | 107     index_length = (len(ids) + 1) * (2 + 4) | 
|  | 108 | 
|  | 109     # Write index. | 
|  | 110     data_offset = HEADER_LENGTH + index_length | 
|  | 111     for id in ids: | 
|  | 112       ret.append(struct.pack("<HI", id, data_offset)) | 
|  | 113       data_offset += len(resources[id]) | 
|  | 114 | 
|  | 115     ret.append(struct.pack("<HI", 0, data_offset)) | 
|  | 116 | 
|  | 117     # Write data. | 
|  | 118     for id in ids: | 
|  | 119       ret.append(resources[id]) | 
|  | 120     return ''.join(ret) | 
|  | 121 | 
|  | 122   @staticmethod | 
|  | 123   def WriteDataPack(resources, output_file, encoding): | 
|  | 124     """Write a map of id=>data into output_file as a data pack.""" | 
|  | 125     file = open(output_file, "wb") | 
|  | 126     content = DataPack.WriteDataPackToString(resources, encoding) | 
|  | 127     file.write(content) | 
|  | 128 | 
|  | 129   @staticmethod | 
|  | 130   def RePack(output_file, input_files): | 
|  | 131     """Write a new data pack to |output_file| based on a list of filenames | 
|  | 132     (|input_files|)""" | 
|  | 133     resources = {} | 
|  | 134     encoding = None | 
|  | 135     for filename in input_files: | 
|  | 136       new_content = DataPack.ReadDataPack(filename) | 
|  | 137 | 
|  | 138       # Make sure we have no dups. | 
|  | 139       duplicate_keys = set(new_content.resources.keys()) & set(resources.keys()) | 
|  | 140       if len(duplicate_keys) != 0: | 
|  | 141         raise exceptions.KeyError("Duplicate keys: " + | 
|  | 142                                   str(list(duplicate_keys))) | 
|  | 143 | 
|  | 144       # Make sure encoding is consistent. | 
|  | 145       if encoding in (None, BINARY): | 
|  | 146         encoding = new_content.encoding | 
|  | 147       elif new_content.encoding not in (BINARY, encoding): | 
|  | 148           raise exceptions.KeyError("Inconsistent encodings: " + | 
|  | 149                                     str(encoding) + " vs " + | 
|  | 150                                     str(new_content.encoding)) | 
|  | 151 | 
|  | 152       resources.update(new_content.resources) | 
|  | 153 | 
|  | 154     # Encoding is 0 for BINARY, 1 for UTF8 and 2 for UTF16 | 
|  | 155     if encoding is None: | 
|  | 156       encoding = BINARY | 
|  | 157     DataPack.WriteDataPack(resources, output_file, encoding) | 
|  | 158 | 
|  | 159 def main(): | 
|  | 160   # Just write a simple file. | 
|  | 161   data = { 1: "", 4: "this is id 4", 6: "this is id 6", 10: "" } | 
|  | 162   DataPack.WriteDataPack(data, "datapack1.pak", UTF8) | 
|  | 163   data2 = { 1000: "test", 5: "five" } | 
|  | 164   DataPack.WriteDataPack(data2, "datapack2.pak", UTF8) | 
|  | 165   print "wrote datapack1 and datapack2 to current directory." | 
|  | 166 | 
|  | 167 if __name__ == '__main__': | 
|  | 168   main() | 
| OLD | NEW | 
|---|