248 lines
8.4 KiB
Python
Executable File
248 lines
8.4 KiB
Python
Executable File
import numpy as np
|
|
|
|
from errors import *
|
|
|
|
|
|
class SegmentSaver(object):
|
|
name = "Raw Data"
|
|
extensions = [".dat"]
|
|
|
|
@classmethod
|
|
def encode_data(cls, segment):
|
|
return segment.tostring()
|
|
|
|
@classmethod
|
|
def get_file_dialog_wildcard(cls):
|
|
# Using only the first extension
|
|
wildcards = []
|
|
if cls.extensions:
|
|
ext = cls.extensions[0]
|
|
wildcards.append("%s (*%s)|*%s" % (cls.name, ext, ext))
|
|
return "|".join(wildcards)
|
|
|
|
|
|
class DefaultSegment(object):
|
|
savers = [SegmentSaver]
|
|
|
|
def __init__(self, data, style, start_addr=0, name="All", error=None):
|
|
self.start_addr = int(start_addr) # force python int to decouple from possibly being a numpy datatype
|
|
self.data = data
|
|
self.style = style
|
|
self.error = error
|
|
self.name = name
|
|
self.page_size = -1
|
|
self.map_width = 40
|
|
self._search_copy = None
|
|
|
|
def __str__(self):
|
|
s = "%s (%d bytes)" % (self.name, len(self))
|
|
if self.error:
|
|
s += " " + self.error
|
|
return s
|
|
|
|
def __len__(self):
|
|
return np.alen(self.data)
|
|
|
|
def __getitem__(self, index):
|
|
return self.data[index]
|
|
|
|
def __setitem__(self, index, value):
|
|
self.data[index] = value
|
|
self._search_copy = None
|
|
|
|
def byte_bounds_offset(self):
|
|
return np.byte_bounds(self.data)[0]
|
|
|
|
def tostring(self):
|
|
return self.data.tostring()
|
|
|
|
def get_style_bits(self, match=False, comment=False, selected=False):
|
|
style_bits = 0
|
|
if match:
|
|
style_bits |= 1
|
|
if comment:
|
|
style_bits |= 2
|
|
if selected:
|
|
style_bits |= 0x80
|
|
return style_bits
|
|
|
|
def get_style_mask(self, **kwargs):
|
|
return 0xff ^ self.get_style_bits(**kwargs)
|
|
|
|
def set_style_ranges(self, ranges, **kwargs):
|
|
style_bits = self.get_style_bits(**kwargs)
|
|
s = self.style
|
|
for start, end in ranges:
|
|
if end < start:
|
|
start, end = end, start
|
|
s[start:end] |= style_bits
|
|
|
|
def get_rect_indexes(self, anchor_start, anchor_end):
|
|
# determine row,col of upper left and lower right of selected
|
|
# rectangle. The values are inclusive, so ul=(0,0) and lr=(1,2)
|
|
# is 2 rows and 3 columns. Columns need to be adjusted slightly
|
|
# depending on quadrant of selection because anchor indexes are
|
|
# measured as cursor positions, that is: positions between the
|
|
# bytes where as rect select needs to think of the selections as
|
|
# on the byte positions themselves, not in between.
|
|
bpr = self.map_width
|
|
r1, c1 = divmod(anchor_start, bpr)
|
|
r2, c2 = divmod(anchor_end, bpr)
|
|
if c1 >= c2:
|
|
# start column is to the right of the end column so columns
|
|
# need to be swapped
|
|
if r1 >= r2:
|
|
# start row is below end row, so rows swapped as well
|
|
c1, c2 = c2, c1 + 1
|
|
r1, r2 = r2, r1
|
|
elif c2 == 0:
|
|
# When the cursor is at the end of a line, anchor_end points
|
|
# to the first character of the next line. Handle this
|
|
# special case by pointing to end of the previous line.
|
|
c2 = bpr
|
|
r2 -= 1
|
|
else:
|
|
c1, c2 = c2 - 1, c1 + 1
|
|
else:
|
|
# start column is to the left of the end column, so don't need
|
|
# to swap columns
|
|
if r1 > r2:
|
|
# start row is below end row
|
|
r1, r2 = r2, r1
|
|
c2 += 1
|
|
anchor_start = r1 * bpr + c1
|
|
anchor_end = r2 * bpr + c2
|
|
r2 += 1
|
|
return anchor_start, anchor_end, (r1, c1), (r2, c2)
|
|
|
|
def set_style_ranges_rect(self, ranges, **kwargs):
|
|
style_bits = self.get_style_bits(**kwargs)
|
|
s = self.style
|
|
for start, end in ranges:
|
|
start, end, (r1, c1), (r2, c2) = self.get_rect_indexes(start, end)
|
|
# Numpy tricks!
|
|
# >>> c1 = 15
|
|
# >>> r = 4 # r2 - r1
|
|
# >>> c = 10 # c2 - c1
|
|
# >>> width = 40
|
|
# >>> np.arange(c)
|
|
#array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
# >>> np.arange(r) * width
|
|
#array([ 0, 40, 80, 120])
|
|
# >>> np.tile(np.arange(c), r) + np.repeat(np.arange(r)*width, c)
|
|
#array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 40, 41, 42,
|
|
# 43, 44, 45, 46, 47, 48, 49, 80, 81, 82, 83, 84, 85,
|
|
# 86, 87, 88, 89, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129])
|
|
# >>> np.tile(np.arange(c), r) + np.repeat(np.arange(r)*width, c) + c1
|
|
#array([ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 55, 56, 57,
|
|
# 58, 59, 60, 61, 62, 63, 64, 95, 96, 97, 98, 99, 100,
|
|
# 101, 102, 103, 104, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144])
|
|
r = r2 - r1
|
|
c = c2 - c1
|
|
indexes = np.tile(np.arange(c), r) + np.repeat(np.arange(r) * self.map_width, c) + start
|
|
s[indexes] |= style_bits
|
|
|
|
def rects_to_ranges(self, rects):
|
|
ranges = []
|
|
bpr = self.map_width
|
|
for (r1, c1), (r2, c2) in rects:
|
|
start = r1 * bpr + c1
|
|
end = (r2 - 1) * bpr + c2
|
|
ranges.append((start, end))
|
|
return ranges
|
|
|
|
def clear_style_bits(self, **kwargs):
|
|
style_mask = self.get_style_mask(**kwargs)
|
|
self.style &= style_mask
|
|
|
|
def label(self, index, lower_case=True):
|
|
if lower_case:
|
|
return "%04x" % (index + self.start_addr)
|
|
else:
|
|
return "%04X" % (index + self.start_addr)
|
|
|
|
@property
|
|
def search_copy(self):
|
|
if self._search_copy is None:
|
|
self._search_copy = self.data.tostring()
|
|
return self._search_copy
|
|
|
|
|
|
class EmptySegment(DefaultSegment):
|
|
def __init__(self, data, style, name="", error=None):
|
|
DefaultSegment.__init__(self, data, style, 0, name, error)
|
|
|
|
def __str__(self):
|
|
s = "%s (empty file)" % (self.name, )
|
|
if self.error:
|
|
s += " " + self.error
|
|
return s
|
|
|
|
def __len__(self):
|
|
return 0
|
|
|
|
|
|
class ObjSegment(DefaultSegment):
|
|
def __init__(self, data, style, metadata_start, data_start, start_addr, end_addr, name="", error=None):
|
|
DefaultSegment.__init__(self, data, style, start_addr, name, error)
|
|
self.metadata_start = metadata_start
|
|
self.data_start = data_start
|
|
|
|
def __str__(self):
|
|
count = len(self)
|
|
s = "%s $%04x-$%04x ($%04x @ $%04x)" % (self.name, self.start_addr, self.start_addr + count, count, self.data_start)
|
|
if self.error:
|
|
s += " " + self.error
|
|
return s
|
|
|
|
|
|
class RawSectorsSegment(DefaultSegment):
|
|
def __init__(self, data, style, first_sector, num_sectors, count, **kwargs):
|
|
DefaultSegment.__init__(self, data, style, 0, **kwargs)
|
|
self.page_size = 128
|
|
self.first_sector = first_sector
|
|
self.num_sectors = num_sectors
|
|
|
|
def __str__(self):
|
|
if self.num_sectors > 1:
|
|
s = "%s (sectors %d-%d)" % (self.name, self.first_sector, self.first_sector + self.num_sectors - 1)
|
|
else:
|
|
s = "%s (sector %d)" % (self.name, self.first_sector)
|
|
if self.error:
|
|
s += " " + self.error
|
|
return s
|
|
|
|
def label(self, index, lower_case=True):
|
|
sector, byte = divmod(index, self.page_size)
|
|
if lower_case:
|
|
return "s%03d:%02x" % (sector + self.first_sector, byte)
|
|
return "s%03d:%02X" % (sector + self.first_sector, byte)
|
|
|
|
|
|
class IndexedByteSegment(DefaultSegment):
|
|
def __init__(self, data, style, byte_order, **kwargs):
|
|
self.order = byte_order
|
|
DefaultSegment.__init__(self, data, style, 0, **kwargs)
|
|
|
|
def __str__(self):
|
|
s = "%s ($%x @ $%x)" % (self.name, len(self), self.order[0])
|
|
if self.error:
|
|
s += " " + self.error
|
|
return s
|
|
|
|
def __len__(self):
|
|
return np.alen(self.order)
|
|
|
|
def __getitem__(self, index):
|
|
return self.data[self.order[index]]
|
|
|
|
def __setitem__(self, index, value):
|
|
self.data[self.order[index]] = value
|
|
self._search_copy = None
|
|
|
|
def byte_bounds_offset(self):
|
|
return np.byte_bounds(self.data)[0] + self.order[0]
|
|
|
|
def tostring(self):
|
|
return self.data[self.order[:]].tostring()
|