diff --git a/README.rst b/README.rst index 2ff3985..cfc9dfa 100644 --- a/README.rst +++ b/README.rst @@ -134,3 +134,5 @@ help`` argument:: files) -x, --extract extract files --xex add .xex extension + -f, --force force operation on disk images that have bad directory + entries or look like boot disks diff --git a/atrcopy.py b/atrcopy.py index c2e3de9..467fcab 100755 --- a/atrcopy.py +++ b/atrcopy.py @@ -15,6 +15,9 @@ class LastDirent(AtrError): class FileNumberMismatchError164(AtrError): pass +class ByteNotInFile166(AtrError): + pass + class AtrHeader(object): format = "= 0 and sector < self.max_sectors + + def get_pos(self, sector): + if not self.sector_is_valid(sector): + raise ByteNotInFile166("Sector %d out of range" % sector) + if sector <= self.num_initial_sectors: + pos = self.num_initial_sectors * (sector - 1) + size = self.initial_sector_size + else: + pos = self.num_initial_sectors * self.initial_sector_size + (sector - 1 - self.num_initial_sectors) * self.sector_size + size = self.sector_size + pos += self.atr_header_offset + return pos, size class XfdHeader(AtrHeader): file_format = "XFD" @@ -63,7 +87,7 @@ class XfdHeader(AtrHeader): class AtrDirent(object): format = " disk.header.max_sectors: + return False + return True + def start_read(self): self.current_sector = self.starting_sector self.current_read = self.num_sectors @@ -151,10 +185,14 @@ class AtrDiskImage(object): self.total_sectors = 0 self.unused_sectors = 0 self.files = [] + self.all_sane = True self.setup() def __str__(self): - return "%s %d total sectors (%d free), %d files" % (self.header, self.total_sectors, self.unused_sectors, len(self.files)) + if self.all_sane: + return "%s %d usable sectors (%d free), %d files" % (self.header, self.total_sectors, self.unused_sectors, len(self.files)) + else: + return "%s bad directory entries; possible boot disk? Use -f option to try to extract anyway" % self.header def dir(self): lines = [] @@ -183,21 +221,9 @@ class AtrDiskImage(object): def check_size(self): self.header.check_size(self.size) - self.initial_sector_size = self.header.sector_size - self.num_initial_sectors = 0 - - def get_pos(self, sector): - if sector <= self.num_initial_sectors: - pos = self.num_initial_sectors * (sector - 1) - size = self.initial_sector_size - else: - pos = self.num_initial_sectors * self.initial_sector_size + (sector - 1 - self.num_initial_sectors) * self.header.sector_size - size = self.header.sector_size - pos += self.header.atr_header_offset - return pos, size def get_raw_bytes(self, sector): - pos, size = self.get_pos(sector) + pos, size = self.header.get_pos(sector) self.fh.seek(pos) raw = self.fh.read(size) return raw @@ -210,7 +236,7 @@ class AtrDiskImage(object): :returns: bytes """ output = StringIO() - pos, size = self.get_pos(start) + pos, size = self.header.get_pos(start) self.fh.seek(pos) if end is None: end = start @@ -218,12 +244,12 @@ class AtrDiskImage(object): bytes = self.fh.read(size) output.write(bytes) start += 1 - pos, size = self.get_pos(start) + pos, size = self.header.get_pos(start) return output.getvalue() def get_vtoc(self): - bytes = self.get_sectors(360) - values = struct.unpack("