diff --git a/atrcopy/ataridos.py b/atrcopy/ataridos.py index 9385bed..288ee74 100644 --- a/atrcopy/ataridos.py +++ b/atrcopy/ataridos.py @@ -154,6 +154,11 @@ class AtariDosDirent(object): def update_sector_info(self, sector_list): self.num_sectors = sector_list.num_sectors self.starting_sector = sector_list.first_sector + + def add_metadata_sectors(self, vtoc, sector_list, header): + # 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: @@ -367,6 +372,7 @@ class AtrHeader(BaseHeader): self.num_directory = 8 self.tracks_per_disk = 40 self.sectors_per_track = 18 + 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 @@ -410,14 +416,6 @@ class AtariDosDiskImage(DiskImageBase): self.vtoc2 = 0 self.first_data_after_vtoc = 369 DiskImageBase.__init__(self, *args, **kwargs) - - @property - def bytes_per_sector(self): - return self.header.sector_size - - @property - def payload_bytes_per_sector(self): - return self.header.sector_size - 3 @property def writeable_sector_class(self): diff --git a/atrcopy/diskimages.py b/atrcopy/diskimages.py index b51b8ce..efd8e0e 100644 --- a/atrcopy/diskimages.py +++ b/atrcopy/diskimages.py @@ -15,6 +15,7 @@ class BaseHeader(object): def __init__(self, sector_size=256, initial_sectors=0, vtoc_sector=0, starting_sector_label=0): self.image_size = 0 self.sector_size = sector_size + self.payload_bytes = sector_size self.initial_sector_size = 0 self.num_initial_sectors = 0 self.crc = 0 @@ -95,14 +96,6 @@ class DiskImageBase(object): def __len__(self): return len(self.rawdata) - @property - def bytes_per_sector(self): - raise NotImplementedError - - @property - def payload_bytes_per_sector(self): - raise NotImplementedError - @property def writeable_sector_class(self): return WriteableSector @@ -118,10 +111,6 @@ class DiskImageBase(object): @property def directory_class(self): return Directory - - @property - def sector_builder_class(self): - return SectorBuilder def set_filename(self, filename): if "." in filename: @@ -334,7 +323,7 @@ class DiskImageBase(object): self.get_directory(directory) dirent = directory.add_dirent(filename, filetype) data = to_numpy(data) - sector_list = self.sector_builder_class(self.header, self.payload_bytes_per_sector, data, self.writeable_sector_class) + sector_list = self.build_sectors(data) vtoc_segments = self.get_vtoc_segments() vtoc = self.vtoc_class(self.header, vtoc_segments) directory.save_dirent(self, dirent, vtoc, sector_list) @@ -348,6 +337,17 @@ class DiskImageBase(object): finally: self.get_metadata() + def build_sectors(self, data): + data = to_numpy(data) + sectors = BaseSectorList(self.header.sector_size) + index = 0 + while index < len(data): + count = min(self.header.payload_bytes, len(data) - index) + sector = self.header.create_sector(data[index:index + count]) + sectors.append(sector) + index += count + return sectors + def write_sector_list(self, sector_list): for sector in sector_list: pos, size = self.header.get_pos(sector.sector_num) diff --git a/atrcopy/dos33.py b/atrcopy/dos33.py index 8ea7e98..d6b1441 100644 --- a/atrcopy/dos33.py +++ b/atrcopy/dos33.py @@ -1,13 +1,42 @@ import numpy as np from errors import * -from diskimages import BaseHeader, DiskImageBase, Directory, VTOC, WriteableSector, BaseSectorList, SectorBuilder +from diskimages import BaseHeader, DiskImageBase, Directory, VTOC, WriteableSector, BaseSectorList from segments import DefaultSegment, EmptySegment, ObjSegment, RawTrackSectorSegment, SegmentSaver import logging log = logging.getLogger(__name__) +class Dos33TSSector(WriteableSector): + def __init__(self, header, sector_list, start, end): + WriteableSector.__init__(self, header.sector_size) + self.header = header + self.used = header.sector_size + self.set_tslist(sector_list, start, end) + + def set_tslist(self, sector_list, start, end): + index = 0xc + for i in range(start, end): + sector = sector_list[i] + t, s = self.header.track_from_sector(sector.sector_num) + self.data[index] = t + self.data[index + 1] = s + log.debug("tslist entry #%d: %d, %d" % (index, t, s)) + index += 2 + + @property + def next_sector_num(self): + return self._next_sector_num + + @next_sector_num.setter + def next_sector_num(self, value): + self._next_sector_num = value + t, s = self.header.track_from_sector(value) + self.data[1] = t + self.data[2] = s + + class Dos33VTOC(VTOC): max_tracks = (256 - 0x38) / 4 # 50, but kept here in case sector size changed max_sectors = max_tracks * 16 @@ -199,6 +228,22 @@ class Dos33Dirent(object): def update_sector_info(self, sector_list): self.num_sectors = sector_list.num_sectors self.starting_sector = sector_list.first_sector + + def add_metadata_sectors(self, vtoc, sector_list, header): + """Add track/sector list + """ + tslist = BaseSectorList(header.sector_size) + for start in range(0, len(sector_list), header.ts_pairs): + end = min(start + header.ts_pairs, len(sector_list)) + log.debug("ts: %d-%d" % (start, end)) + s = Dos33TSSector(header, sector_list, start, end) + s.ts_start, s.ts_end = start, end + tslist.append(s) + self.num_tslists = len(tslist) + vtoc.assign_sector_numbers(self, tslist) + 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: @@ -279,14 +324,6 @@ class Dos33Dirent(object): self.deleted = False -class Dos33SectorBuilder(SectorBuilder): - def calc_extra_sectors(self): - """Add track/sector list - """ - for ts_group_start in range(0, len(self), self.header.ts_pairs): - print ts_group_start - - class Dos33Header(BaseHeader): file_format = "DOS 3.3" @@ -319,14 +356,6 @@ class Dos33DiskImage(DiskImageBase): def read_header(self): self.header = Dos33Header() - @property - def sector_size(self): - return 256 - - @property - def payload_sector_size(self): - return 256 - @property def vtoc_class(self): return Dos33VTOC @@ -339,10 +368,6 @@ class Dos33DiskImage(DiskImageBase): def raw_sector_class(self): return RawTrackSectorSegment - @property - def sector_builder_class(self): - return Dos33SectorBuilder - def get_boot_sector_info(self): # based on logic from a2server data, style = self.get_sectors(0) @@ -406,6 +431,7 @@ class Dos33DiskImage(DiskImageBase): if dirent.flag == 0: break if not dirent.is_sane: + print "not sane: %s" % dirent self.all_sane = False else: files.append(dirent) diff --git a/atrcopy/utils.py b/atrcopy/utils.py index 32971a9..5f51f44 100644 --- a/atrcopy/utils.py +++ b/atrcopy/utils.py @@ -2,6 +2,8 @@ import types import numpy as np +from errors import * + import logging log = logging.getLogger(__name__) @@ -79,6 +81,9 @@ class BaseSectorList(object): def __len__(self): return len(self.sectors) + def __str__(self): + return "\n".join(" %d: %s" % (i, str(s)) for i, s in enumerate(self)) + def __getitem__(self, index): if index < 0 or index >= len(self): raise IndexError @@ -94,9 +99,19 @@ class BaseSectorList(object): return self.sectors[0].sector_num return -1 + @property + def bytes_used(self): + size = 0 + for s in self: + size += s.used + return size + def append(self, sector): self.sectors.append(sector) + def extend(self, sectors): + self.sectors.extend(sectors) + class Directory(BaseSectorList): def __init__(self, header, num_dirents=-1, sector_class=WriteableSector): @@ -138,34 +153,11 @@ class Directory(BaseSectorList): raise FileNotFound("%s not found on disk" % filename) def save_dirent(self, image, dirent, vtoc, sector_list): - self.place_sector_list(dirent, vtoc, sector_list) + vtoc.assign_sector_numbers(dirent, sector_list) + dirent.add_metadata_sectors(vtoc, sector_list, image.header) dirent.update_sector_info(sector_list) self.calc_sectors(image) - def place_sector_list(self, dirent, vtoc, sector_list): - """ Map out the sectors and link the sectors together - - raises NotEnoughSpaceOnDisk if the whole file won't fit. It will not - allow partial writes. - """ - sector_list.calc_extra_sectors() - num = len(sector_list) - order = vtoc.reserve_space(num) - if len(order) != num: - raise InvalidFile("VTOC reserved space for %d sectors. Sectors needed: %d" % (len(order), num)) - file_length = 0 - last_sector = None - for sector, sector_num in zip(sector_list.sectors, order): - sector.sector_num = sector_num - sector.file_num = dirent.file_num - file_length += sector.used - if last_sector is not None: - last_sector.next_sector_num = sector_num - last_sector = sector - if last_sector is not None: - last_sector.next_sector_num = 0 - sector_list.file_length = file_length - def remove_dirent(self, image, dirent, vtoc, sector_list): vtoc.free_sector_list(sector_list) dirent.mark_deleted() @@ -235,6 +227,29 @@ class VTOC(BaseSectorList): def parse_segments(self, segments): raise NotImplementedError + def assign_sector_numbers(self, dirent, sector_list): + """ Map out the sectors and link the sectors together + + raises NotEnoughSpaceOnDisk if the whole file won't fit. It will not + allow partial writes. + """ + num = len(sector_list) + order = self.reserve_space(num) + if len(order) != num: + raise InvalidFile("VTOC reserved space for %d sectors. Sectors needed: %d" % (len(order), num)) + file_length = 0 + last_sector = None + for sector, sector_num in zip(sector_list.sectors, order): + sector.sector_num = sector_num + sector.file_num = dirent.file_num + file_length += sector.used + if last_sector is not None: + last_sector.next_sector_num = sector_num + last_sector = sector + if last_sector is not None: + last_sector.next_sector_num = 0 + sector_list.file_length = file_length + def reserve_space(self, num): order = [] for i in range(num): @@ -258,32 +273,3 @@ class VTOC(BaseSectorList): def free_sector_list(self, sector_list): for sector in sector_list: self.sector_map[sector.sector_num] = 1 - - -class SectorBuilder(BaseSectorList): - def __init__(self, header, usable, data, sector_class): - BaseSectorList.__init__(self, header.sector_size) - self.data = to_numpy(data) - self.usable_bytes = usable - self.split_into_sectors(header) - self.file_length = -1 - - def split_into_sectors(self, header): - index = 0 - while index < len(self.data): - count = min(self.usable_bytes, len(self.data) - index) - sector = header.create_sector(self.data[index:index + count]) - self.sectors.append(sector) - index += count - - - def calc_extra_sectors(self): - """ Add extra sectors to the list. - - For example, DOS 3.3 uses a track/sector list at the beginning - of the file - - Sectors will have their sector assignments when this function is - called. - """ - pass