|
def print_block(array, width, size) :
|
|
i = 0
|
|
while i < size :
|
|
j = i + width
|
|
print(array[i:min(j,size)].hex(' '))
|
|
i = j
|
|
|
|
import struct
|
|
|
|
def dump(file, name, width, num, extra=0) :
|
|
pos = file.tell()
|
|
buf = bytearray((width*num)+extra)
|
|
len = file.readinto(buf)
|
|
print("> %s: %04x at %08x" %(name, len, pos))
|
|
if 0 == len : return 0
|
|
print_block(buf, width, len)
|
|
return struct.unpack_from('<H', buf) # 2 byte unsigned short little-endian
|
|
|
|
def skip(file, name, width, num, extra=0) :
|
|
pos = file.tell()
|
|
len = (width*num)+extra
|
|
file.seek(len, 1)
|
|
print("> %s: %04x at %08x" %(name, len, pos))
|
|
return 0
|
|
|
|
def extract(file, num) :
|
|
print("> Memory channel data:")
|
|
file.seek(0x25a3)
|
|
buf = bytearray(119*num)
|
|
len = file.readinto(buf)
|
|
for i in range(0, len, 119) :
|
|
tuple = struct.unpack_from("<3H9s9s8s4H6x3H4xH10x4s4s9H6xH2xH4x5sH2x", buf, i)
|
|
print(tuple)
|
|
|
|
import sys
|
|
|
|
if len(sys.argv) <= 1 : sys.exit("File name not specified.")
|
|
with open(sys.argv[1]+".dat", 'rb', buffering=0) as F :
|
|
dump(F, "Frequency Range", 2, 1) # 0x0000
|
|
skip(F, "DTMF Array", 18, 20) # 0x0002
|
|
skip(F, "DTMF", 27, 1) # 0x016a
|
|
skip(F, "2Tone", 42, 1) # 0x0185
|
|
skip(F, "2Tone Array", 17, 32) # 0x01af
|
|
skip(F, "5Tone", 28, 1) # 0x03cf
|
|
skip(F, "5Tone Info IDs", 22, 8) # 0x03eb
|
|
skip(F, "5Tone Encode IDs", 53, 102) # 0x049b
|
|
skip(F, "Zeros (constant)", 29, 1) # 0x19b9
|
|
skip(F, "Settings", 76, 1) # 0x19d6
|
|
dump(F, "Display", 16, 1) # 0x1a22 + 16 characters (2 lines of 8)
|
|
skip(F, "Scan Info", 14, 1) # 0x1a32
|
|
skip(F, "Emergency Info", 12, 1) # 0x1a40
|
|
count = dump(F, "Channel Count", 2, 1) # at file offset 0x1a4c
|
|
skip(F, "Zeros (constant)", 32, 37, 30) # 0x1a4e
|
|
skip(F, "Comm Notes", 13, 128, 1) # 0x1f0c
|
|
dump(F, "Radio Model", 16, 1) # 0x258d
|
|
dump(F, "CPS Version", 4, 1) # 0x259d
|
|
dump(F, "CPS Zero, Radio Four", 2, 1) # 0x25a1
|
|
skip(F, "Channels", 119, count[0]) # starts at file offset 0x25a3
|
|
# 4th byte from the ends seems to be with scan info...
|
|
dump(F, "Pad (variable)", 6, 1)
|
|
dump(F, "Unparsed", 32, 16)
|
|
print("End parsing at %08x" %(F.tell()))
|
|
extract(F, count[0])
|
|
|
|
# Special channel indices: PL1=0x1F5 PH1=0x1F6 PL2=0x1F7 PH2=0x1F8 VFO1=0x1F9 VFO2=0x1FA
|