PEP8 whitespace fixes

This commit is contained in:
Rob McMullen 2017-03-23 10:06:37 -07:00
parent 6f29e6053a
commit c897460df0
12 changed files with 219 additions and 186 deletions

View File

@ -32,7 +32,7 @@ def process(image, dirent, options):
outfilename = "%s%s.XEX" % (dirent.filename, dirent.ext)
if options.lower:
outfilename = outfilename.lower()
if options.dry_run:
action = "DRY_RUN: %s" % action
skip = True
@ -45,6 +45,7 @@ def process(image, dirent, options):
else:
print dirent
def find_diskimage(filename):
try:
with open(filename, "rb") as fh:
@ -72,6 +73,7 @@ def find_diskimage(filename):
parser.image.ext = ""
return parser
def extract_files(image, files):
for name in files:
try:
@ -85,6 +87,7 @@ def extract_files(image, files):
with open(dirent.filename, "wb") as fh:
fh.write(data)
def save_file(image, name, filetype, data):
try:
dirent = image.find_dirent(name)
@ -114,6 +117,7 @@ def add_files(image, files):
if changed:
image.save()
def remove_files(image, files):
changed = False
for name in files:
@ -129,6 +133,7 @@ def remove_files(image, files):
if changed:
image.save()
def list_files(image, files):
files = set(files)
for dirent in image.files:
@ -137,6 +142,7 @@ def list_files(image, files):
if options.metadata:
print dirent.extra_metadata(image)
def assemble(image, source_files, data_files):
if source_files:
try:
@ -170,6 +176,7 @@ def assemble(image, source_files, data_files):
if changed:
image.save()
def shred_image(image, value=0):
print "shredding: free sectors from %s filled with %d" % (image, value)
if not options.dry_run:
@ -182,7 +189,7 @@ def run():
import argparse
global options
parser = argparse.ArgumentParser(description="Manipulate files on several types of 8-bit computer disk images")
parser.add_argument("-v", "--verbose", default=0, action="count")
parser.add_argument("--dry-run", action="store_true", default=False, help="don't perform operation, just show what would have happened")
@ -214,7 +221,7 @@ def run():
log.setLevel(logging.DEBUG)
else:
log.setLevel(logging.INFO)
file_list = []
if options.add or options.extract or options.delete:
image = options.files.pop()

View File

@ -91,7 +91,7 @@ class AtariDosDirent(Dirent):
self.current_read = 0
self.sectors_seen = None
self.parse_raw_dirent(image, bytes)
def __str__(self):
return "File #%-2d (%s) %03d %-8s%-3s %03d" % (self.file_num, self.summary, self.starting_sector, self.basename, self.ext, self.num_sectors)
@ -102,7 +102,7 @@ class AtariDosDirent(Dirent):
def filename(self):
ext = ("." + self.ext) if self.ext else ""
return self.basename + ext
@property
def summary(self):
output = "o" if self.opened_output else "."
@ -113,7 +113,7 @@ class AtariDosDirent(Dirent):
locked = "*" if self.locked else " "
flags = "%s%s%s%s%s%s" % (output, dos2, mydos, in_use, deleted, locked)
return flags
@property
def verbose_info(self):
flags = []
@ -131,7 +131,7 @@ class AtariDosDirent(Dirent):
def parse_raw_dirent(self, image, bytes):
if bytes is None:
return
values = bytes.view(dtype=self.format)[0]
values = bytes.view(dtype=self.format)[0]
flag = values[0]
self.flag = flag
self.opened_output = (flag&0x01) > 0
@ -170,7 +170,7 @@ class AtariDosDirent(Dirent):
# no extra sectors are needed for an Atari DOS file; the links to the
# next sector is contained in the sector.
pass
def sanity_check(self, image):
if not self.in_use:
return True
@ -179,7 +179,7 @@ class AtariDosDirent(Dirent):
if self.num_sectors < 0 or self.num_sectors > image.header.max_sectors:
return False
return True
def get_sectors_in_vtoc(self, image):
sector_list = BaseSectorList(image.header)
self.start_read(image)
@ -197,7 +197,7 @@ class AtariDosDirent(Dirent):
self.current_sector = self.starting_sector
self.current_read = self.num_sectors
self.sectors_seen = set()
def read_sector(self, image):
raw, pos, size = image.get_raw_bytes(self.current_sector)
bytes, num_data_bytes = self.process_raw_sector(image, raw)
@ -256,12 +256,13 @@ class AtariDosFile(object):
Ref: http://www.atarimax.com/jindroush.atari.org/afmtexe.html
"""
def __init__(self, rawdata):
self.rawdata = rawdata
self.size = len(rawdata)
self.segments = []
self.files = []
def __str__(self):
return "\n".join(str(s) for s in self.segments) + "\n"
@ -270,7 +271,7 @@ class AtariDosFile(object):
def relaxed_check(self):
pass
def parse_segments(self):
r = self.rawdata
b = r.get_data()
@ -325,7 +326,7 @@ class AtrHeader(BaseHeader):
('btFlags','u1'),
])
file_format = "ATR"
def __init__(self, bytes=None, sector_size=128, initial_sectors=3, create=False):
BaseHeader.__init__(self, sector_size, initial_sectors, 360, 1)
if create:
@ -333,7 +334,7 @@ class AtrHeader(BaseHeader):
self.check_size(0)
if bytes is None:
return
if len(bytes) == 16:
values = bytes.view(dtype=self.format)[0]
if values[0] != 0x296:
@ -346,10 +347,10 @@ class AtrHeader(BaseHeader):
self.header_offset = 16
else:
raise InvalidAtrHeader
def __str__(self):
return "%s Disk Image (size=%d (%dx%db), crc=%d flags=%d unused=%d)" % (self.file_format, self.image_size, self.max_sectors, self.sector_size, self.crc, self.flags, self.unused)
def encode(self, raw):
values = raw.view(dtype=self.format)[0]
values[0] = 0x296
@ -390,7 +391,7 @@ class AtrHeader(BaseHeader):
self.payload_bytes = self.sector_size - 3
initial_bytes = self.initial_sector_size * self.num_initial_sectors
self.max_sectors = ((self.image_size - initial_bytes) / self.sector_size) + self.num_initial_sectors
def get_pos(self, sector):
if not self.sector_is_valid(sector):
raise ByteNotInFile166("Sector %d out of range" % sector)
@ -406,13 +407,13 @@ class AtrHeader(BaseHeader):
class XfdHeader(AtrHeader):
file_format = "XFD"
def __str__(self):
return "%s Disk Image (size=%d (%dx%db)" % (self.file_format, self.image_size, self.max_sectors, self.sector_size)
def __len__(self):
return 0
def to_array(self):
raw = np.zeros([0], dtype=np.uint8)
return raw
@ -446,7 +447,7 @@ class AtariDosDiskImage(DiskImageBase):
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))
@classmethod
def new_header(cls, diskimage, format="ATR"):
if format.lower() == "atr":
@ -455,7 +456,7 @@ class AtariDosDiskImage(DiskImageBase):
else:
raise RuntimeError("Unknown header type %s" % format)
return header
def as_new_format(self, format="ATR"):
""" Create a new disk image in the specified format
"""
@ -465,13 +466,13 @@ class AtariDosDiskImage(DiskImageBase):
newraw = SegmentData(data)
image = self.__class__(newraw)
return image
vtoc_type = np.dtype([
('code', 'u1'),
('total','<u2'),
('unused','<u2'),
])
def read_header(self):
bytes = self.bytes[0:16]
try:
@ -509,7 +510,7 @@ class AtariDosDiskImage(DiskImageBase):
self.num_vtoc = num
if num < 0 or num > self.calc_vtoc_code():
raise InvalidDiskImage("Invalid number of VTOC sectors: %d" % num)
self.total_sectors = values[1]
self.unused_sectors = values[2]
if self.header.image_size == 133120:
@ -518,7 +519,7 @@ class AtariDosDiskImage(DiskImageBase):
data, style = self.get_sectors(self.vtoc2)
extra_free = data[122:124].view(dtype='<u2')[0]
self.unused_sectors += extra_free
def get_directory(self, directory=None):
dir_bytes, style = self.get_sectors(361, 368)
i = 0
@ -528,7 +529,7 @@ class AtariDosDiskImage(DiskImageBase):
dirent = AtariDosDirent(self, num, dir_bytes[i:i+16])
if dirent.mydos:
dirent = MydosDirent(self, num, dir_bytes[i:i+16])
if dirent.in_use:
files.append(dirent)
if not dirent.is_sane:
@ -541,7 +542,7 @@ class AtariDosDiskImage(DiskImageBase):
i += 16
num += 1
self.files = files
boot_record_type = np.dtype([
('BFLAG', 'u1'),
('BRCNT', 'u1'),
@ -561,7 +562,7 @@ class AtariDosDiskImage(DiskImageBase):
def get_boot_segments(self):
data, style = self.get_sectors(360)
values = data[0:20].view(dtype=self.boot_record_type)[0]
values = data[0:20].view(dtype=self.boot_record_type)[0]
flag = int(values[0])
segments = []
if flag == 0:
@ -574,7 +575,7 @@ class AtariDosDiskImage(DiskImageBase):
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):
r = self.rawdata
segments = []
@ -600,7 +601,7 @@ class AtariDosDiskImage(DiskImageBase):
segment.set_comment_at(0x7c, "unused")
segments.append(segment)
return segments
def get_directory_segments(self):
r = self.rawdata
segments = []
@ -618,7 +619,7 @@ class AtariDosDiskImage(DiskImageBase):
index += 16
segments.append(segment)
return segments
def get_file_segment(self, dirent):
byte_order = []
dirent.start_read(self)
@ -635,7 +636,7 @@ class AtariDosDiskImage(DiskImageBase):
else:
segment = EmptySegment(self.rawdata, name=dirent.filename)
return segment
def get_file_segments(self):
segments_in = DiskImageBase.get_file_segments(self)
segments_out = []
@ -655,7 +656,7 @@ class AtariDosDiskImage(DiskImageBase):
class BootDiskImage(AtariDosDiskImage):
def __str__(self):
return "%s Boot Disk" % (self.header)
def check_size(self):
if self.header is None:
return
@ -667,7 +668,7 @@ class BootDiskImage(AtariDosDiskImage):
raise InvalidDiskImage("Appears to be an executable")
nsec = b[i + 1]
bload = b[i + 2:i + 4].view(dtype='<u2')[0]
# Sanity check: number of sectors to be loaded can't be more than the
# lower 48k of ram because there's no way to bank switch or anything
# before the boot sectors are finished loading
@ -687,17 +688,17 @@ class BootDiskImage(AtariDosDiskImage):
def get_directory(self, directory=None):
pass
boot_record_type = np.dtype([
('BFLAG', 'u1'),
('BRCNT', 'u1'),
('BLDADR', '<u2'),
('BWTARR', '<u2'),
])
def get_boot_segments(self):
data, style = self.get_sectors(1)
values = data[0:6].view(dtype=self.boot_record_type)[0]
values = data[0:6].view(dtype=self.boot_record_type)[0]
flag = int(values[0])
segments = []
if flag == 0:
@ -746,6 +747,7 @@ def get_xex(segments, runaddr=None):
i += len(s)
return bytes
def add_atr_header(bytes):
header = AtrHeader(create=True)
header.check_size(len(bytes))

View File

@ -95,6 +95,7 @@ known_cart_types = [
known_cart_type_map = {c[0]:i for i, c in enumerate(known_cart_types)}
def get_known_carts():
grouped = defaultdict(list)
for c in known_cart_types[1:]:
@ -102,6 +103,7 @@ def get_known_carts():
grouped[size].append(c)
return grouped
def get_cart(cart_type):
try:
return known_cart_types[known_cart_type_map[cart_type]]
@ -118,7 +120,7 @@ class A8CartHeader(object):
('unused','>u4')
])
file_format = "Cart"
def __init__(self, bytes=None, create=False):
self.image_size = 0
self.cart_type = -1
@ -140,7 +142,7 @@ class A8CartHeader(object):
self.check_size(0)
if bytes is None:
return
if len(bytes) == 16:
values = bytes.view(dtype=self.format)[0]
if values[0] != 'CART':
@ -151,13 +153,13 @@ class A8CartHeader(object):
self.set_type(self.cart_type)
else:
raise InvalidCartHeader
def __str__(self):
return "%s Cartridge (atari800 type=%d size=%d, %d banks, crc=%d)" % (self.cart_name, self.cart_type, self.cart_size, self.bank_size, self.crc)
def __len__(self):
return self.header_offset
def to_array(self):
raw = np.zeros([16], dtype=np.uint8)
values = raw.view(dtype=self.format)[0]
@ -201,7 +203,7 @@ class AtariCartImage(DiskImageBase):
def __str__(self):
return str(self.header)
def read_header(self):
bytes = self.bytes[0:16]
try:
@ -219,7 +221,7 @@ class AtariCartImage(DiskImageBase):
# force the header to be the specified cart type
self.header = A8CartHeader()
self.header.set_type(self.cart_type)
def check_size(self):
if self.header is None:
return
@ -230,7 +232,7 @@ class AtariCartImage(DiskImageBase):
raise InvalidDiskImage("Cart not multiple of 1K")
if k != c[2]:
raise InvalidDiskImage("Image size %d doesn't match cart type %d size %d" % (k, self.cart_type, c[2]))
def parse_segments(self):
r = self.rawdata
i = self.header.header_offset

View File

@ -31,10 +31,10 @@ class BaseHeader(object):
self.extra_vtoc = []
self.first_directory = 0
self.num_directory = 0
def __len__(self):
return self.header_offset
def to_array(self):
header_bytes = np.zeros([self.header_offset], dtype=np.uint8)
self.encode(header_bytes)
@ -55,7 +55,7 @@ class BaseHeader(object):
pos, size = self.get_pos(i)
yield i, pos, size
i += 1
def get_pos(self, sector):
"""Get index (into the raw data of the disk image) of start of sector
@ -119,13 +119,13 @@ class DiskImageBase(object):
@property
def directory_class(self):
return Directory
def set_filename(self, filename):
if "." in filename:
self.filename, self.ext = filename.rsplit(".", 1)
else:
self.filename, self.ext = filename, ""
def dir(self):
lines = []
lines.append(str(self))
@ -156,16 +156,16 @@ class DiskImageBase(object):
format.
"""
pass
@classmethod
def new_header(cls, diskimage, format="ATR"):
raise NotImplementedError
def as_new_format(self, format="ATR"):
""" Create a new disk image in the specified format
"""
raise NotImplementedError
def save(self, filename=""):
if not filename:
filename = self.filename
@ -176,35 +176,35 @@ class DiskImageBase(object):
bytes = self.bytes[:]
with open(filename, "wb") as fh:
bytes.tofile(fh)
def assert_valid_sector(self, sector):
if not self.header.sector_is_valid(sector):
raise ByteNotInFile166("Sector %d out of range" % sector)
def check_sane(self):
if not self.all_sane:
raise InvalidDiskImage("Invalid directory entries; may be boot disk")
def read_header(self):
return BaseHeader()
def check_size(self):
pass
def get_boot_sector_info(self):
pass
def get_vtoc(self):
"""Get information from VTOC and populate the VTOC object"""
pass
def get_directory(self, directory=None):
pass
def get_raw_bytes(self, sector):
pos, size = self.header.get_pos(sector)
return self.bytes[pos:pos + size], pos, size
def get_sector_slice(self, start, end=None):
""" Get contiguous sectors
@ -220,7 +220,7 @@ class DiskImageBase(object):
_, more = self.header.get_pos(start)
size += more
return slice(pos, pos + size)
def get_sectors(self, start, end=None):
""" Get contiguous sectors
@ -230,7 +230,7 @@ class DiskImageBase(object):
"""
s = self.get_sector_slice(start, end)
return self.bytes[s], self.style[s]
def get_contiguous_sectors(self, sector, num):
start = 0
count = 0
@ -240,7 +240,7 @@ class DiskImageBase(object):
start = pos
count += size
return start, count
def parse_segments(self):
r = self.rawdata
i = self.header.header_offset
@ -251,16 +251,16 @@ class DiskImageBase(object):
self.segments.extend(self.get_vtoc_segments())
self.segments.extend(self.get_directory_segments())
self.segments.extend(self.get_file_segments())
def get_boot_segments(self):
return []
def get_vtoc_segments(self):
return []
def get_directory_segments(self):
return []
def find_dirent(self, filename):
# check if we've been passed a dirent instead of a filename
if hasattr(filename, "filename"):
@ -269,18 +269,18 @@ class DiskImageBase(object):
if filename == dirent.filename:
return dirent
raise FileNotFound("%s not found on disk" % filename)
def find_file(self, filename):
dirent = self.find_dirent(filename)
return self.get_file(dirent)
def get_file(self, dirent):
segment = self.get_file_segment(dirent)
return segment.tostring()
def get_file_segment(self, dirent):
pass
def get_file_segments(self):
segments = []
for dirent in self.files:

View File

@ -168,13 +168,13 @@ class Dos33Dirent(Dirent):
self.sectors_seen = None
self.sector_map = None
self.parse_raw_dirent(image, bytes)
def __str__(self):
return "File #%-2d (%s) %03d %-30s %03d %03d" % (self.file_num, self.summary, self.num_sectors, self.filename, self.track, self.sector)
def __eq__(self, other):
return self.__class__ == other.__class__ and self.filename == other.filename and self.track == other.track and self.sector == other.sector and self.num_sectors == other.num_sectors
type_to_text = {
0x0: "T", # text
0x1: "I", # integer basic
@ -202,7 +202,7 @@ class Dos33Dirent(Dirent):
file_type = self.file_type
flag = "%s%s" % (locked, file_type)
return flag
@property
def verbose_info(self):
return self.summary
@ -221,7 +221,7 @@ class Dos33Dirent(Dirent):
lines.append("track/sector list at: " + str(ts))
lines.append("sector map: " + str(self.sector_map))
return "\n".join(lines)
def parse_raw_dirent(self, image, bytes):
if bytes is None:
return
@ -275,7 +275,7 @@ class Dos33Dirent(Dirent):
sector_list.extend(tslist)
self.track, self.sector = header.track_from_sector(tslist[0].sector_num)
log.debug("track/sector lists:\n%s" % str(tslist))
def sanity_check(self, image):
if self.deleted:
return True
@ -304,7 +304,7 @@ class Dos33Dirent(Dirent):
self.sector_map = sector_map[0:self.num_sectors - len(tslist)]
self.track_sector_list = tslist
return tslist
def get_sectors_in_vtoc(self, image):
self.get_track_sector_list(image)
sectors = BaseSectorList(image.header)
@ -321,7 +321,7 @@ class Dos33Dirent(Dirent):
log.debug("start_read: %s, t/s list: %s" % (str(self), str(self.sector_map)))
self.current_sector_index = 0
self.current_read = self.num_sectors
def read_sector(self, image):
try:
sector = self.sector_map[self.current_sector_index]
@ -337,7 +337,7 @@ class Dos33Dirent(Dirent):
self.current_sector_index += 1
num_bytes = len(raw)
return raw[0:num_bytes], num_bytes
def get_filename(self):
return self.filename
@ -359,7 +359,7 @@ class Dos33Header(BaseHeader):
def __init__(self):
BaseHeader.__init__(self, 256)
def __str__(self):
return "%s Disk Image (size=%d (%dx%db)" % (self.file_format, self.image_size, self.max_sectors, self.sector_size)
@ -383,7 +383,7 @@ class Dos33DiskImage(DiskImageBase):
def __str__(self):
return str(self.header)
def read_header(self):
self.header = Dos33Header()
@ -394,7 +394,7 @@ class Dos33DiskImage(DiskImageBase):
@property
def directory_class(self):
return Dos33Directory
@property
def raw_sector_class(self):
return RawTrackSectorSegment
@ -416,7 +416,7 @@ class Dos33DiskImage(DiskImageBase):
raise InvalidDiskImage("Invalid VTOC location for DOS 3.3")
log.debug("DOS 3.3 byte swap: %s" % swap_order)
vtoc_type = np.dtype([
('unused1', 'S1'),
('cat_track','u1'),
@ -446,7 +446,7 @@ class Dos33DiskImage(DiskImageBase):
self.header.last_track_num = values['last_track']
self.header.track_alloc_dir = values['track_dir']
self.assert_valid_sector(self.header.first_directory)
def get_directory(self, directory=None):
sector = self.header.first_directory
num = 0
@ -488,7 +488,7 @@ class Dos33DiskImage(DiskImageBase):
r = self.rawdata[s]
boot3 = ObjSegment(r, 0, 0, 0x1d00, name="Boot 3")
return [boot1, boot2, relocator, boot3]
def get_vtoc_segments(self):
r = self.rawdata
segments = []
@ -517,7 +517,7 @@ class Dos33DiskImage(DiskImageBase):
index += 4
segments.append(segment)
return segments
def get_directory_segments(self):
byte_order = []
r = self.rawdata
@ -561,7 +561,7 @@ class Dos33DiskImage(DiskImageBase):
if next_sector == 0:
raise NoSpaceInDirectory("No space left in catalog")
return sector_num, next_sector
def get_file_segment(self, dirent):
byte_order = []
dirent.start_read(self)
@ -605,10 +605,6 @@ class Dos33DiskImage(DiskImageBase):
return image, 'B'
class ProdosHeader(Dos33Header):
file_format = "ProDOS"

View File

@ -1,12 +1,15 @@
class AtrError(RuntimeError):
pass
class InvalidAtrHeader(AtrError):
pass
class InvalidCartHeader(AtrError):
pass
class InvalidDiskImage(AtrError):
""" Disk image is not recognized by a parser.
@ -15,6 +18,7 @@ class InvalidDiskImage(AtrError):
"""
pass
class UnsupportedDiskImage(AtrError):
""" Disk image is recognized by a parser but it isn't supported yet.
@ -22,32 +26,42 @@ class UnsupportedDiskImage(AtrError):
"""
pass
class InvalidDirent(AtrError):
pass
class LastDirent(AtrError):
pass
class InvalidFile(AtrError):
pass
class FileNumberMismatchError164(InvalidFile):
pass
class ByteNotInFile166(InvalidFile):
pass
class InvalidBinaryFile(InvalidFile):
pass
class InvalidSegmentParser(AtrError):
pass
class NoSpaceInDirectory(AtrError):
pass
class NotEnoughSpaceOnDisk(AtrError):
pass
class FileNotFound(AtrError):
pass

View File

@ -25,7 +25,7 @@ class KBootDirent(AtariDosDirent):
self.exe_size = count
self.exe_start = start
self.num_sectors = count / 128 + 1
def parse_raw_dirent(self, image, bytes):
pass
@ -37,14 +37,14 @@ class KBootDirent(AtariDosDirent):
class KBootImage(AtariDosDiskImage):
def __str__(self):
return "%s KBoot Format: %d byte executable" % (self.header, self.files[0].exe_size)
def check_sane(self):
if not self.all_sane:
raise InvalidDiskImage("Doesn't seem to be KBoot header")
def get_vtoc(self):
pass
def get_directory(self):
dirent = KBootDirent(self)
if not dirent.is_sane:
@ -57,8 +57,10 @@ class KBootImage(AtariDosDiskImage):
raw = self.rawdata[start:end]
return XexSegment(raw, 0, 0, start, end, name="KBoot Executable")
xexboot_header = '\x00\x03\x00\x07\r\x07L\r\x07\x1c[\x00\x00\xa0\x00\x8c\t\x03\x8c\x04\x03\x8cD\x02\x8c\xe2\x02\x8c\xe3\x02\xc8\x84\t\x8c\x01\x03\xce\x06\x03\xa91\x8d\x00\x03\xa9R\x8d\x02\x03\xa9\x80\x8d\x08\x03\xa9\x01\x8d\x05\x03\xa9\xe3\x8d0\x02\x8d\x02\xd4\xa9\x07\x8d1\x02\x8d\x03\xd4\xa9\x00\xaa\x8d\x0b\x03\xa9\x04\x8d\n\x03 \xbc\x07\xca \xa5\x07\x85C \xa5\x07\x85D%C\xc9\xff\xf0\xf0 \xa5\x07\x85E \xa5\x07\x85F \xa5\x07\x91C\xe6C\xd0\x02\xe6D\xa5E\xc5C\xa5F\xe5D\xb0\xeb\xad\xe2\x02\r\xe3\x02\xf0\xc9\x86\x19 \xa2\x07\xa6\x19\xa0\x00\x8c\xe2\x02\x8c\xe3\x02\xf0\xb8l\xe2\x02\xad\t\x07\xd0\x0b\xad\n\x07\xd0\x03l\xe0\x02\xce\n\x07\xce\t\x07\xe0\x80\x90"\xa9@\x8d\x03\x03 Y\xe4\x10\x06\xce\x01\x07\xd0\xf1\x00\xee\n\x03\xd0\x03\xee\x0b\x03\xad\n\x03\x8d\x19\xd0\xa0\x00\xa2\x00\xbd\x00\x01\xe8`pppppF\xf8\x07p\x07ppp\x06p\x06p\x06A\xe3\x07\x00\x00\x00\x00\x00,/!$).\'\x0e\x0e\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&2/-'
def insert_string(data, offset, string, color):
s = np.fromstring(string.upper(), dtype=np.uint8) - 32 # convert to internal
s = s | color
@ -66,6 +68,7 @@ def insert_string(data, offset, string, color):
tx = offset + (20 - count)/ 2
data[tx:tx+count] = s
def add_xexboot_header(bytes, bootcode=None, title="DEMO", author="an atari user"):
sec_size = 128
xex_size = len(bytes)
@ -74,7 +77,7 @@ def add_xexboot_header(bytes, bootcode=None, title="DEMO", author="an atari user
if xex_size < padded_size:
bytes = np.append(bytes, np.zeros([padded_size - xex_size], dtype=np.uint8))
paragraphs = padded_size / 16
if bootcode is None:
bootcode = np.fromstring(xexboot_header, dtype=np.uint8)
else:
@ -85,7 +88,7 @@ def add_xexboot_header(bytes, bootcode=None, title="DEMO", author="an atari user
bootsize = np.alen(bootcode)
v = bootcode[9:11].view(dtype="<u2")
v[0] = xex_size
bootsectors = np.zeros([384], dtype=np.uint8)
bootsectors[0:bootsize] = bootcode

View File

@ -34,7 +34,7 @@ class MameZipImage(DiskImageBase):
def relaxed_check(self):
pass
def check_zip_size(self, zf):
for item in zf.infolist():
_, r = divmod(item.file_size, 16)
@ -56,7 +56,7 @@ class MameZipImage(DiskImageBase):
def check_size(self):
pass
def parse_segments(self):
r = self.rawdata
self.segments = []

View File

@ -17,7 +17,7 @@ class SegmentParser(object):
menu_name = ""
image_type = None
container_segment = DefaultSegment
def __init__(self, segment_data, strict=False):
self.image = None
self.segments = []
@ -53,7 +53,7 @@ class SegmentParser(object):
class DefaultSegmentParser(SegmentParser):
menu_name = "Raw Data"
def parse(self):
self.segments = [DefaultSegment(self.segment_data, 0)]
@ -122,6 +122,7 @@ def guess_parser_for_mime(mime, r, verbose=False):
pass
return found
def guess_parser_for_system(mime_base, r):
for mime in mime_parse_order:
if mime.startswith(mime_base):
@ -130,6 +131,7 @@ def guess_parser_for_system(mime_base, r):
return mime, p
return None, None
def iter_parsers(r):
for mime in mime_parse_order:
p = guess_parser_for_mime(mime, r)
@ -197,6 +199,7 @@ known_segment_parsers = [DefaultSegmentParser]
for mime in mime_parse_order:
known_segment_parsers.extend(mime_parsers[mime])
def iter_known_segment_parsers():
yield "application/octet-stream", "", [DefaultSegmentParser]
for mime in mime_parse_order:

View File

@ -16,6 +16,7 @@ match_bit_mask = 0x20
comment_bit_mask = 0x40
selected_bit_mask = 0x80
def get_style_bits(match=False, comment=False, selected=False, data=False, diff=False, user=0):
""" Return an int value that contains the specified style bits set.
@ -41,6 +42,7 @@ def get_style_bits(match=False, comment=False, selected=False, data=False, diff=
style_bits |= selected_bit_mask
return style_bits
def get_style_mask(**kwargs):
"""Get the bit mask that, when anded with data, will turn off the
selected bits
@ -51,12 +53,12 @@ def get_style_mask(**kwargs):
else:
bits &= (0xff ^ user_bit_mask)
return 0xff ^ bits
class SegmentSaver(object):
export_data_name = "Raw Data"
export_extensions = [".dat"]
@classmethod
def encode_data(cls, segment, ui_control):
return segment.tostring()
@ -70,27 +72,28 @@ class OrderWrapper(object):
intermediate layer is needed that defines the __setitem__ method that
explicitly references the byte ordering in the data array.
"""
def __init__(self, data, byte_order):
self.np_data = data
self.base = data.base # base array for numpy bounds determination
self.order = byte_order
def __len__(self):
return np.alen(self.order)
def __and__(self, other):
return self.np_data[self.order] & other
def __iand__(self, other):
self.np_data[self.order] &= other
return self
def __getitem__(self, index):
return self.np_data[self.order[index]]
def __setitem__(self, index, value):
self.np_data[self.order[index]] = value
def sub_index(self, index):
"""Return index of index so it can be used directly in a new
SegmentData object, rather than propagating multiple index lookups by
@ -109,6 +112,7 @@ class OrderWrapper(object):
def tostring(self):
return self.np_data[self.order].tostring()
class UserExtraData(object):
def __init__(self):
self.comments = dict()
@ -146,10 +150,10 @@ class SegmentData(object):
extra = UserExtraData()
self.extra = extra
self.reverse_index_mapping = None
def __str__(self):
return "SegmentData id=%x indexed=%s data=%s" % (id(self), self.is_indexed, type(self.data))
def __len__(self):
return len(self.data)
@ -199,10 +203,10 @@ class SegmentData(object):
@property
def style_base(self):
return self.style.np_data if self.is_indexed else self.style.base if self.style.base is not None else self.style
def get_data(self):
return self.data
def get_style(self):
return self.style
@ -217,7 +221,7 @@ class SegmentData(object):
if self.is_indexed:
return self.style.unindexed
return self.style
def byte_bounds_offset(self):
"""Return start and end offsets of this segment's data into the
base array's data.
@ -234,7 +238,7 @@ class SegmentData(object):
data_start, data_end = np.byte_bounds(self.data)
base_start, base_end = np.byte_bounds(self.data.base)
return int(data_start - base_start), int(data_end - base_start)
def get_raw_index(self, i):
"""Get index into base array's raw data, given the index into this
segment
@ -246,7 +250,7 @@ class SegmentData(object):
data_start, data_end = np.byte_bounds(self.data)
base_start, base_end = np.byte_bounds(self.data.base)
return int(data_start - base_start + i)
def get_indexes_from_base(self):
"""Get array of indexes from the base array, as if this raw data were
indexed.
@ -258,7 +262,7 @@ class SegmentData(object):
else:
i = self.get_raw_index(0)
return np.arange(i, i + len(self), dtype=np.uint32)
def __getitem__(self, index):
if self.is_indexed:
order = self.data.sub_index(index)
@ -289,7 +293,7 @@ class SegmentData(object):
print "copy: start, end =", start, end
copy = SegmentData(d[start:end], s[start:end])
return copy
def get_bases(self):
if self.data.base is None:
data_base = self.data
@ -303,15 +307,15 @@ class SegmentData(object):
index = to_numpy_list(index)
if self.is_indexed:
return self[index]
# check to make sure all indexes are valid, raises IndexError if not
check = self.data[index]
# index needs to be relative to the base array
base_index = index + self.get_raw_index(0)
data_base, style_base = self.get_bases()
return SegmentData(data_base, style_base, self.extra, order=base_index)
def get_reverse_index(self, base_index):
"""Get index into this segment's data given the index into the base data
@ -337,7 +341,7 @@ class DefaultSegment(object):
savers = [SegmentSaver]
use_origin_default = False
can_resize_default = False
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.set_raw(rawdata)
@ -355,13 +359,13 @@ class DefaultSegment(object):
# Some segments may be resized to contain additional segments not
# present when the segment was created.
self.can_resize = self.__class__.can_resize_default
def set_raw(self, rawdata):
self.rawdata = rawdata
self.data = rawdata.get_data()
self.style = rawdata.get_style()
self._search_copy = None
def get_raw(self):
return self.rawdata
@ -397,7 +401,7 @@ class DefaultSegment(object):
def replace_data(self, container):
self.rawdata.replace_arrays(container.rawdata)
def __getstate__(self):
state = dict()
for key in ['start_addr', 'error', 'name', 'verbose_name', 'page_size', 'map_width', 'uuid', 'use_origin', 'can_resize']:
@ -424,7 +428,7 @@ class DefaultSegment(object):
self.use_origin = self.__class__.use_origin_default
if not hasattr(self, 'can_resize'):
self.can_resize = self.__class__.can_resize_default
def reconstruct_raw(self, rawdata):
start, end = self._rawdata_bounds
r = rawdata[start:end]
@ -438,7 +442,7 @@ class DefaultSegment(object):
pass
self.set_raw(r)
self.reconstruct_missing()
def get_parallel_raw_data(self, other):
""" Get the raw data that is similar to the specified other segment
"""
@ -463,7 +467,7 @@ class DefaultSegment(object):
if r:
slot = "user style %d" % i
mdict[slot] = r
# json serialization doesn't allow int keys, so convert to list of
# pairs
mdict["comments"] = self.get_sorted_comments()
@ -492,7 +496,6 @@ class DefaultSegment(object):
if slot in e:
self.set_style_ranges(e[slot], user=i)
def __str__(self):
if self.start_addr > 0:
origin = " @ %04x" % (self.start_addr)
@ -502,7 +505,7 @@ class DefaultSegment(object):
if self.error:
s += " " + self.error
return s
@property
def verbose_info(self):
name = self.verbose_name or self.name
@ -513,32 +516,32 @@ class DefaultSegment(object):
if self.error:
s += " error='%s'" % self.error
return s
def __len__(self):
return len(self.rawdata)
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 start and end offsets of this segment's data into the
base array's data
"""
return self.rawdata.byte_bounds_offset()
def is_valid_index(self, i):
return i >= 0 and i < len(self)
def get_raw_index(self, i):
"""Get index into base array's raw data, given the index into this
segment
"""
return self.rawdata.get_raw_index(i)
def get_index_from_base_index(self, base_index):
"""Get index into this array's data given the index into the base array
"""
@ -553,13 +556,13 @@ class DefaultSegment(object):
def tostring(self):
return self.data.tostring()
def get_style_bits(self, **kwargs):
return get_style_bits(**kwargs)
def get_style_mask(self, **kwargs):
return get_style_mask(**kwargs)
def set_style_ranges(self, ranges, **kwargs):
style_bits = self.get_style_bits(**kwargs)
s = self.style
@ -567,7 +570,7 @@ class DefaultSegment(object):
if end < start:
start, end = end, start
s[start:end] |= style_bits
def clear_style_ranges(self, ranges, **kwargs):
style_mask = self.get_style_mask(**kwargs)
s = self.style
@ -575,14 +578,14 @@ class DefaultSegment(object):
if end < start:
start, end = end, start
s[start:end] &= style_mask
def get_style_ranges(self, **kwargs):
"""Return a list of start, end pairs that match the specified style
"""
style_bits = self.get_style_bits(**kwargs)
matches = (self.style & style_bits) == style_bits
return self.bool_to_ranges(matches)
def get_comment_locations(self, **kwargs):
style_bits = self.get_style_bits(**kwargs)
r = self.rawdata.copy()
@ -655,7 +658,7 @@ class DefaultSegment(object):
if np.alen(group) > 0:
ranges.append((int(group[0]), int(group[-1]) + 1))
return ranges
def find_next(self, index, **kwargs):
ranges = self.get_style_ranges(**kwargs)
if len(ranges) > 0:
@ -665,7 +668,7 @@ class DefaultSegment(object):
match_index = 0
return ranges[match_index][0]
return None
def find_previous(self, index, **kwargs):
ranges = self.get_style_ranges(**kwargs)
if len(ranges) > 0:
@ -676,7 +679,7 @@ class DefaultSegment(object):
match_index = len(ranges) - 1
return ranges[match_index][0]
return None
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)
@ -714,7 +717,7 @@ class DefaultSegment(object):
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
@ -741,7 +744,7 @@ class DefaultSegment(object):
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
@ -750,25 +753,25 @@ class DefaultSegment(object):
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 set_user_data(self, ranges, user_index, user_data):
for start, end in ranges:
# FIXME: this is slow
for i in range(start, end):
rawindex = self.get_raw_index(i)
self.rawdata.extra.user_data[user_index][rawindex] = user_data
def get_user_data(self, index, user_index):
rawindex = self.get_raw_index(index)
try:
return self.rawdata.extra.user_data[user_index][rawindex]
except KeyError:
return 0
def get_sorted_user_data(self, user_index):
d = self.rawdata.extra.user_data[user_index]
indexes = sorted(d.keys())
@ -787,7 +790,7 @@ class DefaultSegment(object):
if start is not None:
ranges.append([[start, end], current])
return ranges
def get_style_at_indexes(self, indexes):
return self.style[indexes]
@ -893,7 +896,7 @@ class DefaultSegment(object):
self.set_style_ranges(ranges, comment=True)
for start, end in ranges:
self.set_comment_at(start, text)
def get_comment(self, index):
rawindex = self.get_raw_index(index)
return self.rawdata.extra.comments.get(rawindex, "")
@ -904,19 +907,19 @@ class DefaultSegment(object):
del self.rawdata.extra.comments[rawindex]
except KeyError:
pass
def get_first_comment(self, ranges):
start = reduce(min, [r[0] for r in ranges])
rawindex = self.get_raw_index(start)
return self.rawdata.extra.comments.get(rawindex, "")
def clear_comment(self, ranges):
self.clear_style_ranges(ranges, comment=True)
for start, end in ranges:
rawindex = self.get_raw_index(start)
if rawindex in self.rawdata.extra.comments:
del self.rawdata.extra.comments[rawindex]
def get_sorted_comments(self):
return sorted([[k, v] for k, v in self.rawdata.extra.comments.iteritems()])
@ -927,19 +930,19 @@ class DefaultSegment(object):
for k, v in self.rawdata.extra.comments.iteritems():
if k >= start_index and k < end_index:
yield self.rawdata.get_reverse_index(k), v
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
def compare_segment(self, other_segment):
self.clear_style_bits(diff=True)
diff = self.rawdata.data != other_segment.rawdata.data
@ -953,20 +956,20 @@ class DefaultSegment(object):
class EmptySegment(DefaultSegment):
def __init__(self, rawdata, name="", error=None):
DefaultSegment.__init__(self, rawdata, 0, name, error)
def __str__(self):
s = "%s (empty file)" % (self.name, )
if self.error:
s += " " + self.error
return s
@property
def verbose_info(self):
s = "%s (empty file)" % (self.name, )
if self.error:
s += " error='%s'" % self.error
return s
def __len__(self):
return 0
@ -977,14 +980,14 @@ class ObjSegment(DefaultSegment):
self.metadata_start = metadata_start
self.data_start = data_start
self.use_origin = True
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
@property
def verbose_info(self):
count = len(self)
@ -1007,7 +1010,7 @@ class RawSectorsSegment(DefaultSegment):
self.page_size = sector_size
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)
@ -1016,7 +1019,7 @@ class RawSectorsSegment(DefaultSegment):
if self.error:
s += " " + self.error
return s
@property
def verbose_info(self):
name = self.verbose_name or self.name
@ -1028,7 +1031,7 @@ class RawSectorsSegment(DefaultSegment):
if self.error:
s += " error='%s'" % self.error
return s
def label(self, index, lower_case=True):
boot_size = self.num_boot_sectors * self.boot_sector_size
if index >= boot_size:
@ -1055,6 +1058,7 @@ class RawTrackSectorSegment(RawSectorsSegment):
return "t%02ds%02d:%02x" % (t, s, byte)
return "t%02ds%02d:%02X" % (t, s, byte)
def interleave_indexes(segments, num_bytes):
num_segments = len(segments)
size = len(segments[0])
@ -1074,6 +1078,7 @@ def interleave_indexes(segments, num_bytes):
start += 1
return interleave
def interleave_segments(segments, num_bytes):
new_index = interleave_indexes(segments, num_bytes)
data_base, style_base = segments[0].rawdata.get_bases()

View File

@ -30,7 +30,7 @@ class SpartaDosDirent(AtariDosDirent):
# rather the boot sector so it must be specified here.
self.starting_sector = starting_sector
self.is_sane = self.sanity_check(image)
def __str__(self):
output = "o" if self.opened_output else "."
subdir = "D" if self.is_dir else "."
@ -39,7 +39,7 @@ class SpartaDosDirent(AtariDosDirent):
locked = "*" if self.locked else " "
flags = "%s%s%s%s%s %03d" % (output, subdir, in_use, deleted, locked, self.starting_sector)
return "File #%-2d (%s) %-8s%-3s %8d %s" % (self.file_num, flags, self.filename, self.ext, self.length, self.str_timestamp)
@property
def verbose_info(self):
flags = []
@ -49,11 +49,11 @@ class SpartaDosDirent(AtariDosDirent):
if self.deleted: flags.append("DEL")
if self.locked: flags.append("LOCK")
return "flags=[%s]" % ", ".join(flags)
def parse_raw_dirent(self, image, bytes):
if bytes is None:
return
values = bytes.view(dtype=self.format)[0]
values = bytes.view(dtype=self.format)[0]
flag = values['status']
self.flag = flag
self.locked = (flag&0x1) > 0
@ -73,14 +73,14 @@ class SpartaDosDirent(AtariDosDirent):
self.date_array = tuple(bytes[17:20])
self.time_array = tuple(bytes[20:23])
self.is_sane = self.sanity_check(image)
def sanity_check(self, image):
if not self.in_use:
return True
if not image.header.sector_is_valid(self.starting_sector):
return False
return True
@property
def str_timestamp(self):
str_date = "%d/%d/%d" % self.date_array
@ -94,7 +94,7 @@ class SpartaDosDirent(AtariDosDirent):
self.sector_map = image.get_sector_map(self.starting_sector)
self.sector_map_index = 0
self.length_remaining = self.length
def read_sector(self, image):
sector = self.sector_map[self.sector_map_index]
if sector == 0:
@ -114,10 +114,10 @@ class SpartaDosDiskImage(AtariDosDiskImage):
self.root_dir_dirent = None
self.fs_version = 0
AtariDosDiskImage.__init__(self, *args, **kwargs)
def __str__(self):
return "%s Sparta DOS Format: %d usable sectors (%d free), %d files" % (self.header, self.total_sectors, self.unused_sectors, len(self.files))
boot_record_type = np.dtype([
('unused', 'u1'),
('num_boot', 'u1'),
@ -137,12 +137,12 @@ class SpartaDosDiskImage(AtariDosDiskImage):
('sector_size','u1'),
('fs_version','u1'),
])
sector_size_map = {0: 256,
1: 512,
0x80: 128,
}
def get_boot_sector_info(self):
data, style = self.get_sectors(1)
values = data[0:33].view(dtype=self.boot_record_type)[0]
@ -163,7 +163,7 @@ class SpartaDosDiskImage(AtariDosDiskImage):
def get_vtoc(self):
pass
def get_directory(self):
self.files = []
dir_map = self.get_sector_map(self.root_dir)
@ -177,7 +177,7 @@ class SpartaDosDiskImage(AtariDosDiskImage):
dirent = SpartaDosDirent(self, filenum + 1, s[i:i + 23])
self.files.append(dirent)
self.root_dir_dirent = d
def get_boot_segments(self):
segments = []
num = min(self.num_boot, 1)
@ -191,7 +191,7 @@ class SpartaDosDiskImage(AtariDosDiskImage):
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):
r = self.rawdata
segments = []
@ -206,7 +206,7 @@ class SpartaDosDiskImage(AtariDosDiskImage):
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
def get_sector_map(self, sector):
m = None
while sector > 0:
@ -217,7 +217,7 @@ class SpartaDosDiskImage(AtariDosDiskImage):
else:
m = np.hstack((m, b[4:].view(dtype='<u2')))
return m
def get_directory_segments(self):
dirent = self.root_dir_dirent
segment = self.get_file_segment(dirent)
@ -225,7 +225,7 @@ class SpartaDosDiskImage(AtariDosDiskImage):
segment.map_width = 23
segments = [segment]
return segments
def get_file_segment(self, dirent):
byte_order = []
dirent.start_read(self)

View File

@ -137,6 +137,7 @@ class Dirent(object):
"""Abstract base class for a directory entry
"""
def __init__(self, file_num=0):
self.file_num = file_num