| #! /bin/env python |
| """ Dump data about a Metrowerks archive file. |
| |
| $Id$ |
| |
| Based on reverse-engineering the library file format. |
| |
| Copyright (C) 1997 Chris Herborth (chrish@qnx.com) |
| """ |
| |
| # ---------------------------------------------------------------------- |
| # Standard modules |
| import sys |
| import getopt |
| import string |
| import time |
| |
| # ---------------------------------------------------------------------- |
| def usage(): |
| """ Display a usage message and exit. |
| """ |
| print "dumpar [-v] library1 [library2 ... libraryn]" |
| print |
| print "Attempt to display some useful information about the contents" |
| print "of the given Metrowerks library file(s)." |
| print |
| print "-v Be verbose (displays offsets along with the data)" |
| raise SystemExit |
| |
| # ---------------------------------------------------------------------- |
| def mk_long( str ): |
| """ convert a 4-byte string into a number |
| |
| Assumes big-endian! |
| """ |
| if len( str ) < 4: |
| raise ValueError, "str must be 4 bytes long" |
| |
| num = ord( str[3] ) |
| num = num + ord( str[2] ) * 0x100 |
| num = num + ord( str[1] ) * 0x10000 |
| num = num + ord( str[0] ) * 0x1000000 |
| |
| return num |
| |
| # ---------------------------------------------------------------------- |
| def str2hex( str ): |
| """ convert a string into a string of hex numbers |
| """ |
| ret = [] |
| for c in str: |
| h = hex( ord( c ) ) |
| ret.append( string.zfill( "%s" % ( h[2:] ), 2 ) ) |
| |
| return string.join( ret ) |
| |
| # ---------------------------------------------------------------------- |
| def print_offset( offset ): |
| """ print the offset nicely |
| """ |
| |
| # Turn the offset into a hex number and strip off the leading "0x". |
| val = "%s" % ( hex( offset ) ) |
| val = val[2:] |
| |
| out = "0x" + string.zfill( val, 8 ) |
| |
| print out, |
| |
| # ---------------------------------------------------------------------- |
| def get_string( data ): |
| """ dig a C string out of a data stream |
| |
| returns the string |
| """ |
| len = 0 |
| while data[len] != '\0': |
| len = len + 1 |
| |
| return data[:len] |
| |
| # ---------------------------------------------------------------------- |
| def dump_lib( file, verbose ): |
| """ dump information about a Metrowerks library file |
| """ |
| offset = 0 |
| |
| print "Dumping library:", file |
| |
| # Attempt to read the data. |
| try: |
| data = open( file ).read() |
| except IOError, retval: |
| print "*** Unable to open file %s: %s" % ( file, retval[1] ) |
| return |
| |
| # Check the magic number. |
| if verbose: |
| print_offset( offset ) |
| print "Magic:", |
| magic = data[offset:offset + 8] |
| print "'%s'" % ( magic ) |
| if magic != "MWOBPPC ": |
| print "*** Invalid magic number!" |
| return |
| |
| offset = offset + 8 |
| |
| # File flags |
| if verbose: |
| print_offset( offset ) |
| print "file flags:", |
| print mk_long( data[offset:offset + 4] ) |
| offset = offset + 4 |
| |
| if verbose: |
| print_offset( offset ) |
| print "file version:", |
| print mk_long( data[offset:offset + 4] ) |
| offset = offset + 4 |
| |
| # code size |
| if verbose: |
| print_offset( offset ) |
| print "code size:", mk_long( data[offset:offset + 4] ) |
| offset = offset + 4 |
| |
| # data size |
| if verbose: |
| print_offset( offset ) |
| print "data size:", mk_long( data[offset:offset + 4] ) |
| offset = offset + 4 |
| |
| # number of objects |
| if verbose: |
| print_offset( offset ) |
| print "number of objects:", |
| num_objs = mk_long( data[offset:offset + 4] ) |
| print num_objs |
| |
| offset = offset + 4 |
| |
| print |
| |
| # Now loop through the objects. |
| obj_sizes = [ 0, ] * num_objs |
| obj_data_offsets = [ 0, ] * num_objs |
| |
| for obj in range( num_objs ): |
| # Magic? |
| if verbose: |
| print_offset( offset ) |
| print "modification time:", |
| modtime = mk_long( data[offset:offset + 4] ) |
| print "[%s]" % ( ( time.localtime( modtime ), ) ) |
| |
| offset = offset + 4 |
| |
| # Offsets? |
| if verbose: |
| print_offset( offset ) |
| print "file name offset 1:", |
| file_offset1 = mk_long( data[offset:offset + 4] ) |
| unknown = "%s" % ( hex( file_offset1 ) ) |
| print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) |
| |
| offset = offset + 4 |
| |
| if verbose: |
| print_offset( offset ) |
| print "file name offset 2:", |
| file_offset2 = mk_long( data[offset:offset + 4] ) |
| unknown = "%s" % ( hex( file_offset2 ) ) |
| print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) |
| |
| offset = offset + 4 |
| |
| # Extra -1 for NUL character. |
| print " >>>> File name should be %s characters." % \ |
| ( file_offset2 - file_offset1 - 1) |
| |
| if verbose: |
| print_offset( offset ) |
| print "object data offset:", |
| file_data_offset = mk_long( data[offset:offset + 4] ) |
| unknown = "%s" % ( hex( file_data_offset ) ) |
| print "%s (%s)" % ( unknown, str2hex( data[offset:offset + 4] ) ) |
| |
| obj_data_offsets[obj] = file_data_offset |
| |
| offset = offset + 4 |
| |
| # object size |
| if verbose: |
| print_offset( offset ) |
| print "object size:", |
| obj_sizes[obj] = mk_long( data[offset:offset + 4] ) |
| print "%s bytes" % ( obj_sizes[obj] ) |
| |
| offset = offset + 4 |
| |
| print |
| |
| # Now loop through the object names. |
| for obj in range( num_objs ): |
| # First name |
| if verbose: |
| print_offset( offset ) |
| print "object", |
| print obj, |
| print "name 1:", |
| name1 = get_string( data[offset:] ) |
| print "[%s] %s chars" % ( name1, len( name1 ) ) |
| |
| offset = offset + len( name1 ) + 1 |
| |
| # Second name |
| if verbose: |
| print_offset( offset ) |
| print "object", |
| print obj, |
| print "name 2:", |
| name2 = get_string( data[offset:] ) |
| print "[%s] %s chars" % ( name2, len( name1 ) ) |
| |
| offset = offset + len( name2 ) + 1 |
| |
| # See if we've got a magic cookie in the object data |
| if verbose: |
| print_offset( obj_data_offsets[obj] ) |
| |
| cookie = data[obj_data_offsets[obj]:obj_data_offsets[obj] + 8] |
| print "object", |
| print obj, |
| print "cookie: '%s'" % ( cookie ) |
| |
| print |
| |
| # Now loop through the data and check for magic numbers there. |
| return |
| |
| # ---------------------------------------------------------------------- |
| def main(): |
| """ mainline |
| """ |
| |
| # Set up some defaults |
| be_verbose = 0 |
| |
| # First, check the command-line arguments |
| try: |
| opt, args = getopt.getopt( sys.argv[1:], "vh?" ) |
| except getopt.error: |
| print "*** Error parsing command-line options!" |
| usage() |
| |
| for o in opt: |
| if o[0] == "-h" or o[0] == "-?": |
| usage() |
| elif o[0] == "-v": |
| be_verbose = 1 |
| else: |
| print "*** Unknown command-line option!" |
| usage() |
| |
| # Now we can attempt to dump info about the arguments. |
| for lib in args: |
| dump_lib( lib, be_verbose ) |
| |
| if __name__ == "__main__": |
| main() |