Guido van Rossum | d8eb211 | 1998-08-04 17:57:28 +0000 | [diff] [blame] | 1 | #! /bin/env python |
| 2 | """ Dump data about a Metrowerks archive file. |
| 3 | |
| 4 | $Id$ |
| 5 | |
| 6 | Based on reverse-engineering the library file format. |
| 7 | |
| 8 | Copyright (C) 1997 Chris Herborth (chrish@qnx.com) |
| 9 | """ |
| 10 | |
| 11 | # ---------------------------------------------------------------------- |
| 12 | # Standard modules |
| 13 | import sys |
| 14 | import getopt |
| 15 | import string |
| 16 | import time |
| 17 | |
| 18 | # ---------------------------------------------------------------------- |
| 19 | def usage(): |
| 20 | """ Display a usage message and exit. |
| 21 | """ |
| 22 | print "dumpar [-v] library1 [library2 ... libraryn]" |
| 23 | print |
| 24 | print "Attempt to display some useful information about the contents" |
| 25 | print "of the given Metrowerks library file(s)." |
| 26 | print |
| 27 | print "-v Be verbose (displays offsets along with the data)" |
| 28 | raise SystemExit |
| 29 | |
| 30 | # ---------------------------------------------------------------------- |
| 31 | def mk_long( str ): |
| 32 | """ convert a 4-byte string into a number |
| 33 | |
| 34 | Assumes big-endian! |
| 35 | """ |
| 36 | if len( str ) < 4: |
| 37 | raise ValueError, "str must be 4 bytes long" |
| 38 | |
| 39 | num = ord( str[3] ) |
| 40 | num = num + ord( str[2] ) * 0x100 |
| 41 | num = num + ord( str[1] ) * 0x10000 |
| 42 | num = num + ord( str[0] ) * 0x1000000 |
| 43 | |
| 44 | return num |
| 45 | |
| 46 | # ---------------------------------------------------------------------- |
| 47 | def str2hex( str ): |
| 48 | """ convert a string into a string of hex numbers |
| 49 | """ |
| 50 | ret = [] |
| 51 | for c in str: |
| 52 | h = hex( ord( c ) ) |
| 53 | ret.append( string.zfill( "%s" % ( h[2:] ), 2 ) ) |
| 54 | |
| 55 | return string.join( ret ) |
| 56 | |
| 57 | # ---------------------------------------------------------------------- |
| 58 | def print_offset( offset ): |
| 59 | """ print the offset nicely |
| 60 | """ |
| 61 | |
| 62 | # Turn the offset into a hex number and strip off the leading "0x". |
| 63 | val = "%s" % ( hex( offset ) ) |
| 64 | val = val[2:] |
| 65 | |
| 66 | out = "0x" + string.zfill( val, 8 ) |
| 67 | |
| 68 | print out, |
| 69 | |
| 70 | # ---------------------------------------------------------------------- |
| 71 | def get_string( data ): |
| 72 | """ dig a C string out of a data stream |
| 73 | |
| 74 | returns the string |
| 75 | """ |
| 76 | len = 0 |
| 77 | while data[len] != '\0': |
| 78 | len = len + 1 |
| 79 | |
| 80 | return data[:len] |
| 81 | |
| 82 | # ---------------------------------------------------------------------- |
| 83 | def dump_lib( file, verbose ): |
| 84 | """ dump information about a Metrowerks library file |
| 85 | """ |
| 86 | offset = 0 |
| 87 | |
| 88 | print "Dumping library:", file |
| 89 | |
| 90 | # Attempt to read the data. |
| 91 | try: |
| 92 | data = open( file ).read() |
| 93 | except IOError, retval: |
| 94 | print "*** Unable to open file %s: %s" % ( file, retval[1] ) |
| 95 | return |
| 96 | |
| 97 | # Check the magic number. |
| 98 | if verbose: |
| 99 | print_offset( offset ) |
| 100 | print "Magic:", |
| 101 | magic = data[offset:offset + 8] |
| 102 | print "'%s'" % ( magic ) |
| 103 | if magic != "MWOBPPC ": |
| 104 | print "*** Invalid magic number!" |
| 105 | return |
| 106 | |
| 107 | offset = offset + 8 |
| 108 | |
| 109 | # File flags |
| 110 | if verbose: |
| 111 | print_offset( offset ) |
| 112 | print "file flags:", |
| 113 | print mk_long( data[offset:offset + 4] ) |
| 114 | offset = offset + 4 |
| 115 | |
| 116 | if verbose: |
| 117 | print_offset( offset ) |
| 118 | print "file version:", |
| 119 | print mk_long( data[offset:offset + 4] ) |
| 120 | offset = offset + 4 |
| 121 | |
| 122 | # code size |
| 123 | if verbose: |
| 124 | print_offset( offset ) |
| 125 | print "code size:", mk_long( data[offset:offset + 4] ) |
| 126 | offset = offset + 4 |
| 127 | |
| 128 | # data size |
| 129 | if verbose: |
| 130 | print_offset( offset ) |
| 131 | print "data size:", mk_long( data[offset:offset + 4] ) |
| 132 | offset = offset + 4 |
| 133 | |
| 134 | # number of objects |
| 135 | if verbose: |
| 136 | print_offset( offset ) |
| 137 | print "number of objects:", |
| 138 | num_objs = mk_long( data[offset:offset + 4] ) |
| 139 | print num_objs |
| 140 | |
| 141 | offset = offset + 4 |
| 142 | |
| 143 | print |
| 144 | |
| 145 | # Now loop through the objects. |
| 146 | obj_sizes = [ 0, ] * num_objs |
| 147 | obj_data_offsets = [ 0, ] * num_objs |
| 148 | |
| 149 | for obj in range( num_objs ): |
| 150 | # Magic? |
| 151 | if verbose: |
| 152 | print_offset( offset ) |
| 153 | print "modification time:", |
| 154 | modtime = mk_long( data[offset:offset + 4] ) |
| 155 | print "[%s]" % ( ( time.localtime( modtime ), ) ) |
| 156 | |
| 157 | offset = offset + 4 |
| 158 | |
| 159 | # Offsets? |
| 160 | if verbose: |
| 161 | print_offset( offset ) |
| 162 | print "file name offset 1:", |
| 163 | file_offset1 = mk_long( data[offset:offset + 4] ) |
| 164 | unknown = "%s" % ( hex( file_offset1 ) ) |
| 165 | print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) |
| 166 | |
| 167 | offset = offset + 4 |
| 168 | |
| 169 | if verbose: |
| 170 | print_offset( offset ) |
| 171 | print "file name offset 2:", |
| 172 | file_offset2 = mk_long( data[offset:offset + 4] ) |
| 173 | unknown = "%s" % ( hex( file_offset2 ) ) |
| 174 | print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) |
| 175 | |
| 176 | offset = offset + 4 |
| 177 | |
| 178 | # Extra -1 for NUL character. |
| 179 | print " >>>> File name should be %s characters." % \ |
| 180 | ( file_offset2 - file_offset1 - 1) |
| 181 | |
| 182 | if verbose: |
| 183 | print_offset( offset ) |
| 184 | print "object data offset:", |
| 185 | file_data_offset = mk_long( data[offset:offset + 4] ) |
| 186 | unknown = "%s" % ( hex( file_data_offset ) ) |
| 187 | print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) |
| 188 | |
| 189 | obj_data_offsets[obj] = file_data_offset |
| 190 | |
| 191 | offset = offset + 4 |
| 192 | |
| 193 | # object size |
| 194 | if verbose: |
| 195 | print_offset( offset ) |
| 196 | print "object size:", |
| 197 | obj_sizes[obj] = mk_long( data[offset:offset + 4] ) |
| 198 | print "%s bytes" % ( obj_sizes[obj] ) |
| 199 | |
| 200 | offset = offset + 4 |
| 201 | |
| 202 | print |
| 203 | |
| 204 | # Now loop through the object names. |
| 205 | for obj in range( num_objs ): |
| 206 | # First name |
| 207 | if verbose: |
| 208 | print_offset( offset ) |
| 209 | print "object", |
| 210 | print obj, |
| 211 | print "name 1:", |
| 212 | name1 = get_string( data[offset:] ) |
| 213 | print "[%s] %s chars" % ( name1, len( name1 ) ) |
| 214 | |
| 215 | offset = offset + len( name1 ) + 1 |
| 216 | |
| 217 | # Second name |
| 218 | if verbose: |
| 219 | print_offset( offset ) |
| 220 | print "object", |
| 221 | print obj, |
| 222 | print "name 2:", |
| 223 | name2 = get_string( data[offset:] ) |
| 224 | print "[%s] %s chars" % ( name2, len( name1 ) ) |
| 225 | |
| 226 | offset = offset + len( name2 ) + 1 |
| 227 | |
| 228 | # See if we've got a magic cookie in the object data |
| 229 | if verbose: |
| 230 | print_offset( obj_data_offsets[obj] ) |
| 231 | |
| 232 | cookie = data[obj_data_offsets[obj]:obj_data_offsets[obj] + 8] |
| 233 | print "object", |
| 234 | print obj, |
| 235 | print "cookie: '%s'" % ( cookie ) |
| 236 | |
| 237 | print |
| 238 | |
| 239 | # Now loop through the data and check for magic numbers there. |
| 240 | return |
| 241 | |
| 242 | # ---------------------------------------------------------------------- |
| 243 | def main(): |
| 244 | """ mainline |
| 245 | """ |
| 246 | |
| 247 | # Set up some defaults |
| 248 | be_verbose = 0 |
| 249 | |
| 250 | # First, check the command-line arguments |
| 251 | try: |
| 252 | opt, args = getopt.getopt( sys.argv[1:], "vh?" ) |
| 253 | except getopt.error: |
| 254 | print "*** Error parsing command-line options!" |
| 255 | usage() |
| 256 | |
| 257 | for o in opt: |
| 258 | if o[0] == "-h" or o[0] == "-?": |
| 259 | usage() |
| 260 | elif o[0] == "-v": |
| 261 | be_verbose = 1 |
| 262 | else: |
| 263 | print "*** Unknown command-line option!" |
| 264 | usage() |
| 265 | |
| 266 | # Now we can attempt to dump info about the arguments. |
| 267 | for lib in args: |
| 268 | dump_lib( lib, be_verbose ) |
| 269 | |
| 270 | if __name__ == "__main__": |
| 271 | main() |