Advertisement
Guest User

hex2bin_fixed.py

a guest
May 5th, 2021
481
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.11 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. # Parser for Intel HEX format, intentionally lenient
  4.  
  5. # This can parse .hex files from the Nintendo leaks, since they use a variant of the format
  6. # with a 3-byte payload in the extended segment address records (most other tools reject this).
  7.  
  8. # Usage: ./hex2bin_fixed.py input.hex
  9.  
  10. import binascii, fileinput, os, sys
  11.  
  12. def checksum_of(data):
  13.     # Sum of byte values
  14.     chk = 0
  15.     for b in data:
  16.         chk = (chk + b) & 0xff
  17.  
  18.     # Checksum is two's complement of sum
  19.     return (~chk + 1) & 0xff
  20.  
  21. def parse_record(lineno, line):
  22.     if not line.startswith(":"):
  23.         return None
  24.  
  25.     bs = binascii.unhexlify(line[1:])
  26.  
  27.     count = bs[0]
  28.     addr = bs[1] << 8 | bs[2]
  29.     rtype = bs[3]
  30.     payload = bs[4:-1]
  31.     checksum = bs[-1]
  32.  
  33.     if len(payload) != count:
  34.         print("error: invalid payload length on line {} (expected {:02X} bytes, got {:02X})".format(lineno, count, len(payload)), file=sys.stderr)
  35.    
  36.     actual_checksum = checksum_of(bs[:-1])
  37.     if actual_checksum != checksum:
  38.         print("error: invalid checksum on line {} (expected {:02X}, got {:02X})".format(lineno, checksum, actual_checksum), file=sys.stderr)
  39.         sys.exit(1)
  40.  
  41.     return (rtype, addr, payload)
  42.  
  43. data = bytearray()
  44. offset = 0
  45.  
  46. for (lineno, line) in enumerate(fileinput.input()):
  47.    
  48.     # Skip lines that don't start with a :
  49.     if not line.startswith(":"):
  50.         continue
  51.  
  52.     rtype, addr, payload = parse_record(lineno, line.strip())
  53.     if rtype == 0:
  54.         # Data literal, just write to buffer
  55.         start_addr = offset + addr
  56.         end_addr = offset + addr + len(payload)
  57.        
  58.         # Extend byte array so the data fits
  59.         if len(data) < end_addr:
  60.             new_size = end_addr - len(data)
  61.             #data.extend(bytearray(new_size))
  62.             for i in range(new_size): data.append(255) # pad with ff or 00
  63.        
  64.         # Fill array
  65.         data[start_addr:end_addr] = payload
  66.     elif rtype == 1:
  67.         # End of file
  68.         break
  69.     elif rtype == 2:
  70.         # Extended segment address, set offset for future data
  71.         # NOTE: File format specifies this must be 2 bytes, but Nintendo seems to use a format with 3 bytes... >.>
  72.         offset = int.from_bytes(payload, byteorder="big", signed=False) * 8
  73.     else:
  74.         # We don't support Start Segment Address (03), Extended Linear Address (04) or Start Linear Address (05)
  75.         # those seem to be x86-specific anyway, so meh
  76.         print("error: unsupported record type {:02X}".format(rtype), file=sys.stderr)
  77.         sys.exit(1)
  78. else:
  79.     # We ran out of lines before hitting an end of file record (which would break)
  80.     print("error: hit end of input before EOF record", file=sys.stderr)
  81.     sys.exit(1)
  82.  
  83. # Print output data to stdout
  84. #sys.stdout.buffer.write(bytes(data))
  85. #sys.stdout.buffer.flush()
  86.  
  87. # trim 8000h leading 0's or FF's
  88. if sum(data[:0x8000]) == 0 or sum(data[:0x8000]) == 8355840: data = data[0x8000:]
  89.  
  90. # Print output data to stdout
  91. with open((fileinput.filename()).split(".")[0] + '.bin', "wb") as file: file.write(bytes(data))
  92. # :)
  93. sys.exit(0)
  94.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement