mirror of
https://github.com/robmcmullen/atrcopy.git
synced 2024-11-25 16:32:07 +00:00
Added SegmentData class and updated to version 2.5.0 in preparation for adding comments
This commit is contained in:
parent
3c2f6df828
commit
c8ccd73779
@ -1,4 +1,4 @@
|
||||
__version__ = "2.4.0"
|
||||
__version__ = "2.5.0"
|
||||
|
||||
try:
|
||||
import numpy as np
|
||||
@ -9,7 +9,7 @@ from errors import *
|
||||
from ataridos import AtariDosDiskImage, AtariDosFile
|
||||
from diskimages import AtrHeader, BootDiskImage
|
||||
from kboot import KBootImage
|
||||
from segments import SegmentSaver, DefaultSegment, EmptySegment, ObjSegment, RawSectorsSegment, IndexedByteSegment
|
||||
from segments import SegmentData, SegmentSaver, DefaultSegment, EmptySegment, ObjSegment, RawSectorsSegment, IndexedByteSegment
|
||||
from spartados import SpartaDosDiskImage
|
||||
from utils import to_numpy
|
||||
|
||||
@ -60,12 +60,12 @@ def run():
|
||||
|
||||
for filename in options.files:
|
||||
with open(filename, "rb") as fh:
|
||||
data = fh.read()
|
||||
rawdata = SegmentData(fh.read())
|
||||
data = rawdata.get_data()
|
||||
image = None
|
||||
if options.debug:
|
||||
data = to_numpy(data)
|
||||
header = AtrHeader(data[0:16])
|
||||
image = SpartaDosDiskImage(data, filename)
|
||||
image = SpartaDosDiskImage(rawdata, filename)
|
||||
else:
|
||||
try:
|
||||
data = to_numpy(data)
|
||||
@ -74,7 +74,7 @@ def run():
|
||||
for format in [KBootImage, SpartaDosDiskImage, AtariDosDiskImage]:
|
||||
if options.verbose: print "trying", format.__name__
|
||||
try:
|
||||
image = format(data, filename)
|
||||
image = format(rawdata, filename)
|
||||
print "%s: %s" % (filename, image)
|
||||
break
|
||||
except InvalidDiskImage:
|
||||
@ -82,7 +82,7 @@ def run():
|
||||
except AtrError:
|
||||
for format in [AtariDosDiskImage]:
|
||||
try:
|
||||
image = format(data)
|
||||
image = format(rawdata, filename)
|
||||
print "%s: %s" % (filename, image)
|
||||
break
|
||||
except:
|
||||
@ -91,13 +91,13 @@ def run():
|
||||
except AtrError:
|
||||
if options.verbose: print "%s: Doesn't look like a supported disk image" % filename
|
||||
try:
|
||||
image = AtariDosFile(data)
|
||||
image = AtariDosFile(rawdata)
|
||||
print "%s:\n%s" % (filename, image)
|
||||
except InvalidBinaryFile:
|
||||
if options.verbose: print "%s: Doesn't look like an XEX either" % filename
|
||||
continue
|
||||
if image is None:
|
||||
image = BootDiskImage(data, filename)
|
||||
image = BootDiskImage(rawdata, filename)
|
||||
if options.segments:
|
||||
image.parse_segments()
|
||||
print "\n".join([str(a) for a in image.segments])
|
||||
|
@ -142,13 +142,11 @@ class AtariDosFile(object):
|
||||
|
||||
Ref: http://www.atarimax.com/jindroush.atari.org/afmtexe.html
|
||||
"""
|
||||
def __init__(self, data, style=None):
|
||||
self.bytes = to_numpy(data)
|
||||
self.size = np.alen(self.bytes)
|
||||
if style is None:
|
||||
self.style = np.zeros(self.size, dtype=np.uint8)
|
||||
else:
|
||||
self.style = style
|
||||
def __init__(self, rawdata):
|
||||
self.rawdata = rawdata
|
||||
self.bytes = rawdata.get_data()
|
||||
self.style = rawdata.get_style()
|
||||
self.size = len(rawdata)
|
||||
self.segments = []
|
||||
self.parse_segments()
|
||||
|
||||
@ -156,6 +154,7 @@ class AtariDosFile(object):
|
||||
return "\n".join(str(s) for s in self.segments) + "\n"
|
||||
|
||||
def parse_segments(self):
|
||||
r = self.rawdata
|
||||
b = self.bytes
|
||||
s = self.style
|
||||
pos = 0
|
||||
@ -164,7 +163,7 @@ class AtariDosFile(object):
|
||||
if pos + 1 < self.size:
|
||||
header, = b[pos:pos+2].view(dtype='<u2')
|
||||
else:
|
||||
self.segments.append(ObjSegment(b[pos:pos + 1], s[pos:pos + 1], pos, pos + 1, 0, 1, "Incomplete Data"))
|
||||
self.segments.append(ObjSegment(r[pos:pos + 1], pos, pos + 1, 0, 1, "Incomplete Data"))
|
||||
break
|
||||
if header == 0xffff:
|
||||
# Apparently 0xffff header can appear in any segment, not just
|
||||
@ -174,25 +173,25 @@ class AtariDosFile(object):
|
||||
raise InvalidBinaryFile
|
||||
first = False
|
||||
if len(b[pos:pos + 4]) < 4:
|
||||
self.segments.append(ObjSegment(b[pos:pos + 4], s[pos:pos + 4], 0, 0, "Short Segment Header"))
|
||||
self.segments.append(ObjSegment(r[pos:pos + 4], 0, 0, "Short Segment Header"))
|
||||
break
|
||||
start, end = b[pos:pos + 4].view(dtype='<u2')
|
||||
count = end - start + 1
|
||||
found = len(b[pos + 4:pos + 4 + count])
|
||||
if found < count:
|
||||
self.segments.append(ObjSegment(b[pos + 4:pos + 4 + count], s[pos + 4:pos + 4 + count], pos, pos + 4, start, end, "Incomplete Data"))
|
||||
self.segments.append(ObjSegment(r[pos + 4:pos + 4 + count], pos, pos + 4, start, end, "Incomplete Data"))
|
||||
break
|
||||
self.segments.append(ObjSegment(b[pos + 4:pos + 4 + count], s[pos + 4:pos + 4 + count], pos, pos + 4, start, end))
|
||||
self.segments.append(ObjSegment(r[pos + 4:pos + 4 + count], pos, pos + 4, start, end))
|
||||
pos += 4 + count
|
||||
|
||||
|
||||
class AtariDosDiskImage(DiskImageBase):
|
||||
def __init__(self, bytes, style=None):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.first_vtoc = 360
|
||||
self.num_vtoc = 1
|
||||
self.vtoc2 = 0
|
||||
self.first_data_after_vtoc = 369
|
||||
DiskImageBase.__init__(self, bytes, style)
|
||||
DiskImageBase.__init__(self, *args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return "%s Atari DOS Format: %d usable sectors (%d free), %d files" % (self.header, self.total_sectors, self.unused_sectors, len(self.files))
|
||||
@ -266,34 +265,33 @@ class AtariDosDiskImage(DiskImageBase):
|
||||
if flag == 0:
|
||||
num = int(values[1])
|
||||
addr = int(values[2])
|
||||
bytes, style = self.get_sectors(1, num)
|
||||
header = ObjSegment(bytes[0:20], style[0:20], 0, 0, addr, addr + 20, name="Boot Header")
|
||||
sectors = ObjSegment(bytes, style, 0, 0, addr, addr + len(bytes), name="Boot Sectors")
|
||||
code = ObjSegment(bytes[20:], style[20:], 0, 0, addr + 20, addr + len(bytes), name="Boot Code")
|
||||
s = self.get_sector_slice(1, num)
|
||||
r = self.rawdata[s]
|
||||
header = ObjSegment(r[0:20], 0, 0, addr, addr + 20, name="Boot Header")
|
||||
sectors = ObjSegment(r, 0, 0, addr, addr + len(r), name="Boot Sectors")
|
||||
code = ObjSegment(r[20:], 0, 0, addr + 20, addr + len(r), name="Boot Code")
|
||||
segments = [sectors, header, code]
|
||||
return segments
|
||||
|
||||
def get_vtoc_segments(self):
|
||||
b = self.bytes
|
||||
s = self.style
|
||||
r = self.rawdata
|
||||
segments = []
|
||||
addr = 0
|
||||
start, count = self.get_contiguous_sectors(self.first_vtoc, self.num_vtoc)
|
||||
segment = RawSectorsSegment(b[start:start+count], s[start:start+count], self.first_vtoc, self.num_vtoc, count, 128, 3, self.header.sector_size, name="VTOC")
|
||||
segment = RawSectorsSegment(r[start:start+count], self.first_vtoc, self.num_vtoc, count, 128, 3, self.header.sector_size, name="VTOC")
|
||||
segments.append(segment)
|
||||
if self.vtoc2 > 0:
|
||||
start, count = self.get_contiguous_sectors(self.vtoc2, 1)
|
||||
segment = RawSectorsSegment(b[start:start+count], s[start:start+count], self.vtoc2, 1, count, self.header.sector_size, name="VTOC2")
|
||||
segment = RawSectorsSegment(r[start:start+count], self.vtoc2, 1, count, self.header.sector_size, name="VTOC2")
|
||||
segments.append(segment)
|
||||
return segments
|
||||
|
||||
def get_directory_segments(self):
|
||||
b = self.bytes
|
||||
s = self.style
|
||||
r = self.rawdata
|
||||
segments = []
|
||||
addr = 0
|
||||
start, count = self.get_contiguous_sectors(361, 8)
|
||||
segment = RawSectorsSegment(b[start:start+count], s[start:start+count], 361, 8, count, 128, 3, self.header.sector_size, name="Directory")
|
||||
segment = RawSectorsSegment(r[start:start+count], 361, 8, count, 128, 3, self.header.sector_size, name="Directory")
|
||||
segments.append(segment)
|
||||
return segments
|
||||
|
||||
@ -309,7 +307,7 @@ class AtariDosDiskImage(DiskImageBase):
|
||||
name = "%s %ds@%d" % (dirent.get_filename(), dirent.num_sectors, dirent.starting_sector)
|
||||
verbose_name = "%s (%d sectors, first@%d) %s" % (dirent.get_filename(), dirent.num_sectors, dirent.starting_sector, dirent.verbose_info)
|
||||
print verbose_name
|
||||
segment = IndexedByteSegment(self.bytes, self.style, byte_order, name=name, verbose_name=verbose_name)
|
||||
segment = IndexedByteSegment(self.rawdata, byte_order, name=name, verbose_name=verbose_name)
|
||||
else:
|
||||
segment = EmptySegment(self.bytes, self.style, name=dirent.get_filename())
|
||||
segment = EmptySegment(self.rawdata, name=dirent.get_filename())
|
||||
return segment
|
||||
|
@ -1,7 +1,7 @@
|
||||
import numpy as np
|
||||
|
||||
from errors import *
|
||||
from segments import EmptySegment, ObjSegment, RawSectorsSegment, IndexedByteSegment
|
||||
from segments import SegmentData, EmptySegment, ObjSegment, RawSectorsSegment
|
||||
from utils import to_numpy
|
||||
|
||||
class AtrHeader(object):
|
||||
@ -92,18 +92,11 @@ class XfdHeader(AtrHeader):
|
||||
|
||||
|
||||
class DiskImageBase(object):
|
||||
debug = False
|
||||
|
||||
def __init__(self, bytes, style=None, filename=""):
|
||||
self.bytes = to_numpy(bytes)
|
||||
def __init__(self, rawdata, filename=""):
|
||||
self.rawdata = rawdata
|
||||
self.bytes = self.rawdata.get_data()
|
||||
self.style = self.rawdata.get_style()
|
||||
self.size = np.alen(self.bytes)
|
||||
if style is None:
|
||||
if self.debug:
|
||||
self.style = np.arange(self.size, dtype=np.uint8)
|
||||
else:
|
||||
self.style = np.zeros(self.size, dtype=np.uint8)
|
||||
else:
|
||||
self.style = style
|
||||
self.set_filename(filename)
|
||||
self.header = None
|
||||
self.total_sectors = 0
|
||||
@ -164,7 +157,7 @@ class DiskImageBase(object):
|
||||
pos, size = self.header.get_pos(sector)
|
||||
return self.bytes[pos:pos + size], pos, size
|
||||
|
||||
def get_sectors(self, start, end=None):
|
||||
def get_sector_slice(self, start, end=None):
|
||||
""" Get contiguous sectors
|
||||
|
||||
:param start: first sector number to read (note: numbering starts from 1)
|
||||
@ -178,7 +171,17 @@ class DiskImageBase(object):
|
||||
start += 1
|
||||
_, more = self.header.get_pos(start)
|
||||
size += more
|
||||
return self.bytes[pos:pos + size], self.style[pos:pos + size]
|
||||
return slice(pos, pos + size)
|
||||
|
||||
def get_sectors(self, start, end=None):
|
||||
""" Get contiguous sectors
|
||||
|
||||
:param start: first sector number to read (note: numbering starts from 1)
|
||||
:param end: last sector number to read
|
||||
:returns: bytes
|
||||
"""
|
||||
s = self.get_sector_slice(start, end)
|
||||
return self.bytes[s], self.style[s]
|
||||
|
||||
def get_contiguous_sectors(self, sector, num):
|
||||
start = 0
|
||||
@ -191,12 +194,11 @@ class DiskImageBase(object):
|
||||
return start, count
|
||||
|
||||
def parse_segments(self):
|
||||
b = self.bytes
|
||||
s = self.style
|
||||
r = self.rawdata
|
||||
i = self.header.atr_header_offset
|
||||
if self.header.image_size > 0:
|
||||
self.segments.append(ObjSegment(b[0:i], s[0:i], 0, 0, 0, i, name="%s Header" % self.header.file_format))
|
||||
self.segments.append(RawSectorsSegment(b[i:], s[i:], 1, self.header.max_sectors, self.header.image_size, 128, 3, self.header.sector_size, name="Raw disk sectors"))
|
||||
self.segments.append(ObjSegment(r[0:i], 0, 0, 0, i, name="%s Header" % self.header.file_format))
|
||||
self.segments.append(RawSectorsSegment(r[i:], 1, self.header.max_sectors, self.header.image_size, 128, 3, self.header.sector_size, name="Raw disk sectors"))
|
||||
self.segments.extend(self.get_boot_segments())
|
||||
self.segments.extend(self.get_vtoc_segments())
|
||||
self.segments.extend(self.get_directory_segments())
|
||||
@ -217,10 +219,11 @@ class DiskImageBase(object):
|
||||
if flag == 0:
|
||||
num = int(values[1])
|
||||
addr = int(values[2])
|
||||
bytes, style = self.get_sectors(1, num)
|
||||
header = ObjSegment(bytes[0:6], style[0:6], 0, 0, addr, addr + 6, name="Boot Header")
|
||||
sectors = ObjSegment(bytes, style, 0, 0, addr, addr + len(bytes), name="Boot Sectors")
|
||||
code = ObjSegment(bytes[6:], style[6:], 0, 0, addr + 6, addr + len(bytes), name="Boot Code")
|
||||
s = self.get_sector_slice(1, num)
|
||||
r = self.rawdata[s]
|
||||
header = ObjSegment(r[0:6], 0, 0, addr, addr + 6, name="Boot Header")
|
||||
sectors = ObjSegment(r, 0, 0, addr, addr + len(r), name="Boot Sectors")
|
||||
code = ObjSegment(r[6:], 0, 0, addr + 6, addr + len(r), name="Boot Code")
|
||||
segments = [sectors, header, code]
|
||||
return segments
|
||||
|
||||
@ -249,7 +252,7 @@ class DiskImageBase(object):
|
||||
try:
|
||||
segment = self.get_file_segment(dirent)
|
||||
except InvalidFile, e:
|
||||
segment = EmptySegment(self.bytes, self.style, name=dirent.get_filename(), error=str(e))
|
||||
segment = EmptySegment(self.rawdata, name=dirent.get_filename(), error=str(e))
|
||||
segments.append(segment)
|
||||
return segments
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import numpy as np
|
||||
|
||||
from errors import *
|
||||
from utils import to_numpy_list
|
||||
from utils import to_numpy, to_numpy_list
|
||||
|
||||
|
||||
class SegmentSaver(object):
|
||||
@ -22,13 +22,47 @@ class SegmentSaver(object):
|
||||
return "|".join(wildcards)
|
||||
|
||||
|
||||
class SegmentData(object):
|
||||
def __init__(self, data, style=None, comments=None, debug=False):
|
||||
self.data = to_numpy(data)
|
||||
if style is None:
|
||||
if debug:
|
||||
self.style = np.arange(len(self), dtype=np.uint8)
|
||||
else:
|
||||
self.style = np.zeros(len(self), dtype=np.uint8)
|
||||
else:
|
||||
self.style = style
|
||||
if comments is None:
|
||||
comments = dict()
|
||||
self.comments = comments
|
||||
|
||||
def __len__(self):
|
||||
return np.alen(self.data)
|
||||
|
||||
def get_data(self):
|
||||
return self.data
|
||||
|
||||
def get_style(self):
|
||||
return self.style
|
||||
|
||||
def get_comments(self):
|
||||
return self.comments
|
||||
|
||||
def __getitem__(self, index):
|
||||
d = self.data[index]
|
||||
s = self.style[index]
|
||||
c = self.comments
|
||||
return SegmentData(d, s, c)
|
||||
|
||||
|
||||
class DefaultSegment(object):
|
||||
savers = [SegmentSaver]
|
||||
|
||||
def __init__(self, data, style, start_addr=0, name="All", error=None, verbose_name=None):
|
||||
def __init__(self, rawdata, start_addr=0, name="All", error=None, verbose_name=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.rawdata = rawdata
|
||||
self.data = rawdata.get_data()
|
||||
self.style = rawdata.get_style()
|
||||
self.error = error
|
||||
self.name = name
|
||||
self.verbose_name = verbose_name
|
||||
@ -51,7 +85,7 @@ class DefaultSegment(object):
|
||||
return s
|
||||
|
||||
def __len__(self):
|
||||
return np.alen(self.data)
|
||||
return len(self.rawdata)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.data[index]
|
||||
@ -213,8 +247,8 @@ class DefaultSegment(object):
|
||||
|
||||
|
||||
class EmptySegment(DefaultSegment):
|
||||
def __init__(self, data, style, name="", error=None):
|
||||
DefaultSegment.__init__(self, data, style, 0, name, error)
|
||||
def __init__(self, rawdata, name="", error=None):
|
||||
DefaultSegment.__init__(self, rawdata, 0, name, error)
|
||||
|
||||
def __str__(self):
|
||||
s = "%s (empty file)" % (self.name, )
|
||||
@ -234,8 +268,8 @@ class EmptySegment(DefaultSegment):
|
||||
|
||||
|
||||
class ObjSegment(DefaultSegment):
|
||||
def __init__(self, data, style, metadata_start, data_start, start_addr, end_addr, name="", **kwargs):
|
||||
DefaultSegment.__init__(self, data, style, start_addr, name, **kwargs)
|
||||
def __init__(self, rawdata, metadata_start, data_start, start_addr, end_addr, name="", **kwargs):
|
||||
DefaultSegment.__init__(self, rawdata, start_addr, name, **kwargs)
|
||||
self.metadata_start = metadata_start
|
||||
self.data_start = data_start
|
||||
|
||||
@ -257,8 +291,8 @@ class ObjSegment(DefaultSegment):
|
||||
|
||||
|
||||
class RawSectorsSegment(DefaultSegment):
|
||||
def __init__(self, data, style, first_sector, num_sectors, count, boot_sector_size, num_boot_sectors, sector_size, **kwargs):
|
||||
DefaultSegment.__init__(self, data, style, 0, **kwargs)
|
||||
def __init__(self, rawdata, first_sector, num_sectors, count, boot_sector_size, num_boot_sectors, sector_size, **kwargs):
|
||||
DefaultSegment.__init__(self, rawdata, 0, **kwargs)
|
||||
self.boot_sector_size = boot_sector_size
|
||||
self.num_boot_sectors = num_boot_sectors
|
||||
self.page_size = sector_size
|
||||
@ -324,10 +358,11 @@ class IndexedStyleWrapper(object):
|
||||
|
||||
|
||||
class IndexedByteSegment(DefaultSegment):
|
||||
def __init__(self, data, style, byte_order, **kwargs):
|
||||
def __init__(self, rawdata, byte_order, **kwargs):
|
||||
# Convert to numpy list so fancy indexing works as argument to __getitem__
|
||||
self.order = to_numpy_list(byte_order)
|
||||
DefaultSegment.__init__(self, data, IndexedStyleWrapper(style, byte_order), 0, **kwargs)
|
||||
DefaultSegment.__init__(self, rawdata, **kwargs)
|
||||
self.style = IndexedStyleWrapper(self.style, byte_order)
|
||||
|
||||
def __str__(self):
|
||||
s = "%s ($%x @ $%x)" % (self.name, len(self), self.order[0])
|
||||
|
@ -179,19 +179,19 @@ class SpartaDosDiskImage(DiskImageBase):
|
||||
def get_boot_segments(self):
|
||||
segments = []
|
||||
num = min(self.num_boot, 1)
|
||||
bytes, style = self.get_sectors(1, num)
|
||||
s = self.get_sector_slice(1, num)
|
||||
r = self.rawdata[s]
|
||||
addr = self.boot_addr
|
||||
header = ObjSegment(bytes[0:43], style[0:43], 0, 0, addr, addr + 43, name="Boot Header")
|
||||
header = ObjSegment(r[0:43], 0, 0, addr, addr + 43, name="Boot Header")
|
||||
segments.append(header)
|
||||
if self.num_boot > 0:
|
||||
sectors = ObjSegment(bytes, style, 0, 0, addr, addr + len(bytes), name="Boot Sectors")
|
||||
code = ObjSegment(bytes[43:], style[43:], 0, 0, addr + 43, addr + len(bytes), name="Boot Code")
|
||||
sectors = ObjSegment(r, 0, 0, addr, addr + len(r), name="Boot Sectors")
|
||||
code = ObjSegment(r[43:], 0, 0, addr + 43, addr + len(r), name="Boot Code")
|
||||
segments.extend([header, code])
|
||||
return segments
|
||||
|
||||
def get_vtoc_segments(self):
|
||||
b = self.bytes
|
||||
s = self.style
|
||||
r = self.rawdata
|
||||
segments = []
|
||||
addr = 0
|
||||
start, count = self.get_contiguous_sectors(self.first_bitmap, self.num_bitmap)
|
||||
@ -201,7 +201,7 @@ class SpartaDosDiskImage(DiskImageBase):
|
||||
else:
|
||||
num_boot = 3
|
||||
boot_size = 128
|
||||
segment = RawSectorsSegment(b[start:start+count], s[start:start+count], self.first_bitmap, self.num_bitmap, count, 0, 0, self.sector_size, name="Bitmap")
|
||||
segment = RawSectorsSegment(r[start:start+count], self.first_bitmap, self.num_bitmap, count, 0, 0, self.sector_size, name="Bitmap")
|
||||
segments.append(segment)
|
||||
return segments
|
||||
|
||||
@ -236,7 +236,7 @@ class SpartaDosDiskImage(DiskImageBase):
|
||||
if len(byte_order) > 0:
|
||||
name = "%s %d@%d %s" % (dirent.get_filename(), dirent.length, dirent.starting_sector, dirent.str_timestamp)
|
||||
verbose_name = "%s (%d bytes, sector map@%d) %s %s" % (dirent.get_filename(), dirent.length, dirent.starting_sector, dirent.verbose_info, dirent.str_timestamp)
|
||||
segment = IndexedByteSegment(self.bytes, self.style, byte_order, name=name, verbose_name=verbose_name)
|
||||
segment = IndexedByteSegment(self.rawdata, byte_order, name=name, verbose_name=verbose_name)
|
||||
else:
|
||||
segment = EmptySegment(self.bytes, self.style, name=dirent.get_filename(), error=dirent.str_timestamp)
|
||||
segment = EmptySegment(self.rawdata, name=dirent.get_filename(), error=dirent.str_timestamp)
|
||||
return segment
|
||||
|
Loading…
Reference in New Issue
Block a user