python-adfs

changeset 85:bab9a6eaf74c

Started to refactor the disc map and catalogue handling into separate classes.
author David Boddie <david@boddie.org.uk>
date Fri Oct 14 00:01:08 2011 +0200
parents 9f1ea30aa03c
children 71640463533b
files ADFSlib.py
diffstat 1 files changed, 614 insertions(+), 614 deletions(-) [+]
line diff
     1.1 --- a/ADFSlib.py	Fri Oct 14 00:00:13 2011 +0200
     1.2 +++ b/ADFSlib.py	Fri Oct 14 00:01:08 2011 +0200
     1.3 @@ -36,6 +36,85 @@
     1.4  between_epochs = ((365 * 70) + 17) * 24 * 360000L
     1.5  
     1.6  
     1.7 +class Utilities:
     1.8 +
     1.9 +    # Little endian reading
    1.10 +    
    1.11 +    def _read_signed_word(self, s):
    1.12 +    
    1.13 +        return struct.unpack("<i", s)[0]
    1.14 +    
    1.15 +    def _read_unsigned_word(self, s):
    1.16 +    
    1.17 +        return struct.unpack("<I", s)[0]
    1.18 +    
    1.19 +    def _read_signed_byte(self, s):
    1.20 +    
    1.21 +        return struct.unpack("<b", s)[0]
    1.22 +    
    1.23 +    def _read_unsigned_byte(self, s):
    1.24 +    
    1.25 +        return struct.unpack("<B", s)[0]
    1.26 +    
    1.27 +    def _read_unsigned_half_word(self, s):
    1.28 +    
    1.29 +        return struct.unpack("<H", s)[0]
    1.30 +    
    1.31 +    def _read_signed_half_word(self, s):
    1.32 +    
    1.33 +        return struct.unpack("<h", s)[0]
    1.34 +    
    1.35 +    def _str2num(self, size, s):
    1.36 +    
    1.37 +        i = 0
    1.38 +        n = 0
    1.39 +        while i < size:
    1.40 +        
    1.41 +            n = n | (ord(s[i]) << (i*8))
    1.42 +            i = i + 1
    1.43 +        
    1.44 +        return n
    1.45 +    
    1.46 +    def _binary(self, size, n):
    1.47 +    
    1.48 +        new = ""
    1.49 +        while (n != 0) & (size > 0):
    1.50 +        
    1.51 +            if (n & 1)==1:
    1.52 +                new = "1" + new
    1.53 +            else:
    1.54 +                new = "0" + new
    1.55 +            
    1.56 +            n = n >> 1
    1.57 +            size = size - 1
    1.58 +        
    1.59 +        if size > 0:
    1.60 +            new = ("0"*size) + new
    1.61 +        
    1.62 +        return new
    1.63 +    
    1.64 +    def _safe(self, s, with_space = 0):
    1.65 +    
    1.66 +        new = ""
    1.67 +        if with_space == 1:
    1.68 +            lower = 31
    1.69 +        else:
    1.70 +            lower = 32
    1.71 +        
    1.72 +        for i in s:
    1.73 +        
    1.74 +            if ord(i) <= lower:
    1.75 +                break
    1.76 +            
    1.77 +            if ord(i) >= 128:
    1.78 +                c = ord(i)^128
    1.79 +                if c > 32:
    1.80 +                    new = new + chr(c)
    1.81 +            else:
    1.82 +                new = new + i
    1.83 +        
    1.84 +        return new
    1.85 +
    1.86  class ADFS_exception(Exception):
    1.87  
    1.88      pass
    1.89 @@ -113,7 +192,531 @@
    1.90              return ()
    1.91  
    1.92  
    1.93 -class ADFSdisc:
    1.94 +class ADFSmap(Utilities):
    1.95 +
    1.96 +    def __getitem__(self, index):
    1.97 +    
    1.98 +        return self.disc_map[index]
    1.99 +    
   1.100 +    def _read_freespace(self):
   1.101 +    
   1.102 +        # Currently unused
   1.103 +            
   1.104 +        base = 0
   1.105 +    
   1.106 +        free = []
   1.107 +        p = 0
   1.108 +        while self.sectors[base+p] != 0:
   1.109 +    
   1.110 +            free.append(self._str2num(3, self.sectors[base+p:base+p+3]))
   1.111 +    
   1.112 +        name = self.sectors[self.sector_size-9:self.sector_size-4]
   1.113 +    
   1.114 +        disc_size = self._str2num(
   1.115 +            3, self.sectors[self.sector_size-4:self.sector_size-1]
   1.116 +            )
   1.117 +    
   1.118 +        checksum0 = self._read_unsigned_byte(self.sectors[self.sector_size-1])
   1.119 +    
   1.120 +        base = self.sector_size
   1.121 +    
   1.122 +        p = 0
   1.123 +        while self.sectors[base+p] != 0:
   1.124 +    
   1.125 +            free.append(self._str2num(3, self.sectors[base+p:base+p+3]))
   1.126 +    
   1.127 +        name = name + \
   1.128 +            self.sectors[base+self.sector_size-10:base+self.sector_size-5]
   1.129 +    
   1.130 +        disc_id = self._str2num(
   1.131 +            2, self.sectors[base+self.sector_size-5:base+self.sector_size-3]
   1.132 +            )
   1.133 +    
   1.134 +        boot = self._read_unsigned_byte(self.sectors[base+self.sector_size-3])
   1.135 +    
   1.136 +        checksum1 = self._read_unsigned_byte(self.sectors[base+self.sector_size-1])
   1.137 +
   1.138 +class ADFSnewMap(ADFSmap):
   1.139 +
   1.140 +    dir_markers = ('Hugo', 'Nick')
   1.141 +    root_dir_address = 0x800
   1.142 +    
   1.143 +    def __init__(self, header, begin, end, sectors, sector_size):
   1.144 +    
   1.145 +        self.header = header
   1.146 +        self.begin = begin
   1.147 +        self.end = end
   1.148 +        self.sectors = sectors
   1.149 +        self.sector_size = sector_size
   1.150 +        
   1.151 +        self.free_space = self._read_free_space()
   1.152 +        self.disc_map = self._read_disc_map()
   1.153 +    
   1.154 +    def _read_disc_map(self):
   1.155 +    
   1.156 +        disc_map = {}
   1.157 +        
   1.158 +        a = self.begin
   1.159 +        
   1.160 +        current_piece = None
   1.161 +        current_start = 0
   1.162 +        
   1.163 +        next_zone = self.header + self.sector_size
   1.164 +        
   1.165 +        # Copy the free space map.
   1.166 +        free_space = self.free_space[:]
   1.167 +        
   1.168 +        while a < self.end:
   1.169 +        
   1.170 +            # The next entry to be read will occur one byte after this one
   1.171 +            # unless one of the following checks override this behaviour.
   1.172 +            next = a + 1
   1.173 +            
   1.174 +            if (a % self.sector_size) < 4:
   1.175 +            
   1.176 +                # In a zone header. Not the first zone header as this
   1.177 +                # was already skipped when we started reading.
   1.178 +                next = a + 4 - (a % self.sector_size)
   1.179 +                
   1.180 +                # Set the next zone offset.
   1.181 +                next_zone = next_zone + self.sector_size
   1.182 +                
   1.183 +                # Reset the current piece and starting offset.
   1.184 +                current_piece = None
   1.185 +                current_start = 0
   1.186 +            
   1.187 +            elif free_space != [] and a >= free_space[0][0]:
   1.188 +            
   1.189 +                # In the next free space entry. Go to the entry following
   1.190 +                # it and discard this free space entry.
   1.191 +                next = free_space[0][1]
   1.192 +                
   1.193 +                free_space.pop(0)
   1.194 +                
   1.195 +                # Reset the current piece and starting offset.
   1.196 +                current_piece = None
   1.197 +                current_start = 0
   1.198 +            
   1.199 +            elif current_piece is None and (next_zone - a) >= 2:
   1.200 +            
   1.201 +                # If there is enough space left in this zone to allow
   1.202 +                # further fragments then read the next two bytes.
   1.203 +                value = self._read_unsigned_half_word(self.sectors[a:a+2])
   1.204 +                
   1.205 +                entry = value & 0x7fff
   1.206 +                
   1.207 +                # See ADFS/EAddrs.htm document for restriction on
   1.208 +                # the disc address and hence the file number.
   1.209 +                # i.e.the top bit of the file number cannot be set.
   1.210 +                
   1.211 +                if entry >= 1:
   1.212 +                
   1.213 +                    # Defects (1), files or directories (greater than 1)
   1.214 +                    next = a + 2
   1.215 +                    
   1.216 +                    # Define a new entry.
   1.217 +                    #print "Begin:", hex(entry), hex(a)
   1.218 +                    
   1.219 +                    if not disc_map.has_key(entry):
   1.220 +                    
   1.221 +                        # Create a new map entry if none exists.
   1.222 +                        disc_map[entry] = []
   1.223 +                    
   1.224 +                    if (value & 0x8000) == 0:
   1.225 +                    
   1.226 +                        # Record the file number and start of this fragment.
   1.227 +                        current_piece = entry
   1.228 +                        current_start = a
   1.229 +                    
   1.230 +                    else:
   1.231 +                    
   1.232 +                        # For an immediately terminated fragment, add the
   1.233 +                        # extents of the block to the list of pieces found
   1.234 +                        # and implicitly finish reading this fragment
   1.235 +                        # (current_piece remains None).
   1.236 +                        
   1.237 +                        start_addr = self.find_address_from_map(
   1.238 +                            a, self.begin, entry
   1.239 +                            )
   1.240 +                        end_addr = self.find_address_from_map(
   1.241 +                            next, self.begin, entry
   1.242 +                            )
   1.243 +                        
   1.244 +                        if [start_addr, end_addr] not in disc_map[entry]:
   1.245 +                        
   1.246 +                            disc_map[entry].append( [start_addr, end_addr] )
   1.247 +                
   1.248 +                else:
   1.249 +                
   1.250 +                    # Search for a valid file number.
   1.251 +                    # Should probably stop looking in this zone.
   1.252 +                    next = a + 1
   1.253 +            
   1.254 +            elif current_piece is not None:
   1.255 +            
   1.256 +                # In a piece being read.
   1.257 +                
   1.258 +                value = ord(self.sectors[a])
   1.259 +                
   1.260 +                if value == 0:
   1.261 +                
   1.262 +                    # Still in the block.
   1.263 +                    next = a + 1
   1.264 +                
   1.265 +                elif value == 0x80:
   1.266 +                
   1.267 +                    # At the end of the block.
   1.268 +                    next = a + 1
   1.269 +                    
   1.270 +                    # For relevant entries add the block to the list of
   1.271 +                    # pieces found.
   1.272 +                    start_addr = self.find_address_from_map(
   1.273 +                        current_start, self.begin, current_piece
   1.274 +                        )
   1.275 +                    end_addr = self.find_address_from_map(
   1.276 +                        next, self.begin, current_piece
   1.277 +                        )
   1.278 +                    
   1.279 +                    if [start_addr, end_addr] not in disc_map[current_piece]:
   1.280 +                    
   1.281 +                        disc_map[current_piece].append(
   1.282 +                            [ start_addr, end_addr]
   1.283 +                            )
   1.284 +                    
   1.285 +                    # Look for a new fragment.
   1.286 +                    current_piece = None
   1.287 +                
   1.288 +                else:
   1.289 +                
   1.290 +                    # The byte found was unexpected - backtrack to the
   1.291 +                    # byte after the start of this block and try again.
   1.292 +                    #print "Backtrack from %s to %s" % (hex(a), hex(current_start+1))
   1.293 +                    
   1.294 +                    next = current_start + 1
   1.295 +                    current_piece = None
   1.296 +            
   1.297 +            # Move to the next relevant byte.
   1.298 +            a = next
   1.299 +        
   1.300 +        return disc_map
   1.301 +    
   1.302 +    def _read_free_space(self):
   1.303 +    
   1.304 +        free_space = []
   1.305 +        
   1.306 +        a = self.header
   1.307 +        
   1.308 +        while a < self.end:
   1.309 +        
   1.310 +            # The next zone starts a sector after this one.
   1.311 +            next_zone = a + self.sector_size
   1.312 +            
   1.313 +            a = a + 1
   1.314 +            
   1.315 +            # Start by reading the offset in bits from the start of the header
   1.316 +            # of the first item of free space in the map.
   1.317 +            offset = self._read_unsigned_half_word(self.sectors[a:a+2])
   1.318 +            
   1.319 +            # The top bit is apparently always set, so mask it off and convert
   1.320 +            # the result into bytes. * Not sure if this is the case for
   1.321 +            # entries in the map. *
   1.322 +            next = ((offset & 0x7fff) >> 3)
   1.323 +            
   1.324 +            if next == 0:
   1.325 +            
   1.326 +                # No more free space in this zone. Look at the free
   1.327 +                # space field in the next zone.
   1.328 +                a = next_zone
   1.329 +                continue
   1.330 +            
   1.331 +            # Update the offset to point to the free space in this zone.
   1.332 +            a = a + next
   1.333 +            
   1.334 +            while a < next_zone:
   1.335 +            
   1.336 +                # Read the offset to the next free fragment in this zone.
   1.337 +                offset = self._read_unsigned_half_word(self.sectors[a:a+2])
   1.338 +                
   1.339 +                # Convert this to a byte offset.
   1.340 +                next = ((offset & 0x7fff) >> 3)
   1.341 +                
   1.342 +                # Find the end of the free space.
   1.343 +                b = a + 1
   1.344 +                
   1.345 +                while b < next_zone:
   1.346 +                
   1.347 +                    c = b + 1
   1.348 +                    
   1.349 +                    value = self._read_unsigned_byte(self.sectors[b])
   1.350 +                    
   1.351 +                    if (value & 0x80) != 0:
   1.352 +                    
   1.353 +                        break
   1.354 +                    
   1.355 +                    b = c
   1.356 +                
   1.357 +                # Record the offset into the map of this item of free space
   1.358 +                # and the offset of the byte after it ends.
   1.359 +                free_space.append( (a, c) )
   1.360 +                
   1.361 +                if next == 0:
   1.362 +                
   1.363 +                    break
   1.364 +                
   1.365 +                # Move to the next free space entry.
   1.366 +                a = a + next
   1.367 +            
   1.368 +            # Whether we are at the end of the zone or not, move to the
   1.369 +            # beginning of the next zone.
   1.370 +            a = next_zone
   1.371 +        
   1.372 +        # Return the free space list.
   1.373 +        return free_space
   1.374 +    
   1.375 +    def read_catalogue(self, base):
   1.376 +    
   1.377 +        head = base
   1.378 +        p = 0
   1.379 +        
   1.380 +        dir_seq = self.sectors[head + p]
   1.381 +        dir_start = self.sectors[head+p+1:head+p+5]
   1.382 +        if dir_start not in self.dir_markers:
   1.383 +        
   1.384 +            if self.verify:
   1.385 +            
   1.386 +                self.verify_log.append(
   1.387 +                    (WARNING, 'Not a directory: %s' % hex(head))
   1.388 +                    )
   1.389 +            
   1.390 +            return '', []
   1.391 +        
   1.392 +        p = p + 5
   1.393 +        
   1.394 +        files = []
   1.395 +        
   1.396 +        while ord(self.sectors[head+p]) != 0:
   1.397 +        
   1.398 +            old_name = self.sectors[head+p:head+p+10]
   1.399 +            top_set = 0
   1.400 +            counter = 1
   1.401 +            for i in old_name:
   1.402 +                if (ord(i) & 128) != 0:
   1.403 +                    top_set = counter
   1.404 +                counter = counter + 1
   1.405 +            
   1.406 +            name = self._safe(self.sectors[head+p:head+p+10])
   1.407 +            
   1.408 +            load = self._read_unsigned_word(self.sectors[head+p+10:head+p+14])
   1.409 +            exe = self._read_unsigned_word(self.sectors[head+p+14:head+p+18])
   1.410 +            length = self._read_unsigned_word(self.sectors[head+p+18:head+p+22])
   1.411 +            
   1.412 +            inddiscadd = self._read_new_address(
   1.413 +                self.sectors[head+p+22:head+p+25]
   1.414 +                )
   1.415 +            newdiratts = self._read_unsigned_byte(self.sectors[head+p+25])
   1.416 +            
   1.417 +            if inddiscadd == -1:
   1.418 +            
   1.419 +                if (newdiratts & 0x8) != 0:
   1.420 +                
   1.421 +                    if self.verify:
   1.422 +                    
   1.423 +                        self.verify_log.append(
   1.424 +                            (WARNING, "Couldn't find directory: %s" % name)
   1.425 +                            )
   1.426 +                        self.verify_log.append(
   1.427 +                            (WARNING, "    at: %x" % (head+p+22))
   1.428 +                            )
   1.429 +                        self.verify_log.append( (
   1.430 +                            WARNING, "    file details: %x" % \
   1.431 +                            self._str2num(3, self.sectors[head+p+22:head+p+25])
   1.432 +                            ) )
   1.433 +                        self.verify_log.append(
   1.434 +                            (WARNING, "    atts: %x" % newdiratts)
   1.435 +                            )
   1.436 +                
   1.437 +                elif length != 0:
   1.438 +                
   1.439 +                    if self.verify:
   1.440 +                    
   1.441 +                        self.verify_log.append(
   1.442 +                            (WARNING, "Couldn't find file: %s" % name)
   1.443 +                            )
   1.444 +                        self.verify_log.append(
   1.445 +                            (WARNING, "    at: %x" % (head+p+22))
   1.446 +                            )
   1.447 +                        self.verify_log.append( (
   1.448 +                            WARNING,
   1.449 +                            "    file details: %x" % \
   1.450 +                            self._str2num(3, self.sectors[head+p+22:head+p+25])
   1.451 +                            ) )
   1.452 +                        self.verify_log.append(
   1.453 +                            (WARNING, "    atts: %x" % newdiratts)
   1.454 +                            )
   1.455 +                
   1.456 +                else:
   1.457 +                
   1.458 +                    # Store a zero length file. This appears to be the
   1.459 +                    # standard behaviour for storing empty files.
   1.460 +                    files.append(ADFSfile(name, "", load, exe, length))
   1.461 +            
   1.462 +            else:
   1.463 +            
   1.464 +                if (newdiratts & 0x8) != 0:
   1.465 +                
   1.466 +                    # Remember that inddiscadd will be a sequence of
   1.467 +                    # pairs of addresses.
   1.468 +                    
   1.469 +                    for start, end in inddiscadd:
   1.470 +                    
   1.471 +                        # Try to interpret the data at the referenced address
   1.472 +                        # as a directory.
   1.473 +                        
   1.474 +                        lower_dir_name, lower_files = \
   1.475 +                            self.read_catalogue(start)
   1.476 +                        
   1.477 +                        # Store the directory name and file found therein.
   1.478 +                        files.append(ADFSdirectory(name, lower_files))
   1.479 +                
   1.480 +                else:
   1.481 +                
   1.482 +                    # Remember that inddiscadd will be a sequence of
   1.483 +                    # pairs of addresses.
   1.484 +                    
   1.485 +                    file = ""
   1.486 +                    remaining = length
   1.487 +                    
   1.488 +                    for start, end in inddiscadd:
   1.489 +                    
   1.490 +                        amount = min(remaining, end - start)
   1.491 +                        file = file + self.sectors[start : (start + amount)]
   1.492 +                        remaining = remaining - amount
   1.493 +                    
   1.494 +                    files.append(ADFSfile(name, file, load, exe, length))
   1.495 +            
   1.496 +            p = p + 26
   1.497 +        
   1.498 +        
   1.499 +        # Go to tail of directory structure (0x800 -- 0xc00)
   1.500 +        
   1.501 +        tail = head + self.sector_size
   1.502 +        
   1.503 +        dir_end = self.sectors[tail+self.sector_size-5:tail+self.sector_size-1]
   1.504 +        
   1.505 +        if dir_end not in self.dir_markers:
   1.506 +        
   1.507 +            if self.verify:
   1.508 +            
   1.509 +                self.verify_log.append(
   1.510 +                    ( WARNING,
   1.511 +                      'Discrepancy in directory structure: [%x, %x]' % \
   1.512 +                      ( head, tail ) )
   1.513 +                    )
   1.514 +            
   1.515 +            return '', files
   1.516 +        
   1.517 +        dir_name = self._safe(
   1.518 +            self.sectors[tail+self.sector_size-16:tail+self.sector_size-6]
   1.519 +            )
   1.520 +        
   1.521 +        parent = \
   1.522 +            self.sectors[tail+self.sector_size-38:tail+self.sector_size-35]
   1.523 +        
   1.524 +        dir_title = \
   1.525 +            self.sectors[tail+self.sector_size-35:tail+self.sector_size-16]
   1.526 +        
   1.527 +        if head == self.root_dir_address:
   1.528 +            dir_name = '$'
   1.529 +        
   1.530 +        endseq = self.sectors[tail+self.sector_size-6]
   1.531 +        if endseq != dir_seq:
   1.532 +        
   1.533 +            if self.verify:
   1.534 +            
   1.535 +                self.verify_log.append(
   1.536 +                    ( WARNING,
   1.537 +                      'Broken directory: %s at [%x, %x]' % \
   1.538 +                      (dir_title, head, tail) )
   1.539 +                    )
   1.540 +            
   1.541 +            return dir_name, files
   1.542 +        
   1.543 +        return dir_name, files
   1.544 +    
   1.545 +    def _read_new_address(self, s):
   1.546 +    
   1.547 +        # From the three character string passed, determine the address on the
   1.548 +        # disc.
   1.549 +        value = self._str2num(3, s)
   1.550 +        
   1.551 +        # This is a SIN (System Internal Number)
   1.552 +        # The bottom 8 bits are the sector offset + 1
   1.553 +        offset = value & 0xff
   1.554 +        if offset != 0:
   1.555 +            address = (offset - 1) * self.sector_size
   1.556 +        else:
   1.557 +            address = 0
   1.558 +        
   1.559 +        # The top 16 bits are the file number
   1.560 +        file_no = value >> 8
   1.561 +        
   1.562 +        # The pieces of the object are returned as a list of pairs of
   1.563 +        # addresses.
   1.564 +        pieces = self._find_in_new_map(file_no)
   1.565 +        
   1.566 +        if pieces == []:
   1.567 +        
   1.568 +            return -1
   1.569 +        
   1.570 +        # Ensure that the first piece of data is read from the appropriate
   1.571 +        # point in the relevant sector.
   1.572 +        
   1.573 +        pieces[0][0] = pieces[0][0] + address
   1.574 +        
   1.575 +        return pieces
   1.576 +    
   1.577 +    def _find_in_new_map(self, file_no):
   1.578 +    
   1.579 +        try:
   1.580 +        
   1.581 +            return self.disc_map[file_no]
   1.582 +        
   1.583 +        except KeyError:
   1.584 +        
   1.585 +            return []
   1.586 +    
   1.587 +    def find_address_from_map(self, addr, begin, entry):
   1.588 +    
   1.589 +        return ((addr - begin) * self.sector_size)
   1.590 +
   1.591 +class ADFSbigNewMap(ADFSnewMap):
   1.592 +
   1.593 +    dir_markers = ('Nick',)
   1.594 +    root_dir_address = 0xc8800
   1.595 +    
   1.596 +    def find_address_from_map(self, addr, begin, entry):
   1.597 +    
   1.598 +        # I can't remember where the rationale for this calculation
   1.599 +        # came from or where the necessary information was obtained.
   1.600 +        # It probably came from one of the WSS files, such as
   1.601 +        # Formats.htm or Formats2.htm which imply that the F format
   1.602 +        # uses 512 byte sectors (see the 0x200 value below) and
   1.603 +        # indicate that F format uses 4 zones rather than 1.
   1.604 +        
   1.605 +        upper = (entry & 0x7f00) >> 8
   1.606 +        
   1.607 +        if upper > 1:
   1.608 +            upper = upper - 1
   1.609 +        if upper > 3:
   1.610 +            upper = 3
   1.611 +        
   1.612 +        return ((addr - begin) - (upper * 0xc8)) * 0x200
   1.613 +
   1.614 +class ADFSoldMap(ADFSmap):
   1.615 +
   1.616 +    pass
   1.617 +
   1.618 +class ADFSdisc(Utilities):
   1.619  
   1.620      """disc = ADFSdisc(file_handle, verify = 0)
   1.621      
   1.622 @@ -244,7 +847,7 @@
   1.623              
   1.624              # Find the root directory name and all the files and directories
   1.625              # contained within it.
   1.626 -            self.root_name, self.files = self._read_new_catalogue(2*self.sector_size)
   1.627 +            self.root_name, self.files = self.disc_map.read_catalogue(2*self.sector_size)
   1.628          
   1.629          elif self.disc_type == 'adEbig':
   1.630          
   1.631 @@ -253,7 +856,7 @@
   1.632              
   1.633              # Find the root directory name and all the files and directories
   1.634              # contained within it. The 
   1.635 -            self.root_name, self.files = self._read_new_catalogue((self.ntracks * self.nsectors/2 + 2) * self.sector_size)
   1.636 +            self.root_name, self.files = self.disc_map.read_catalogue((self.ntracks * self.nsectors/2 + 2) * self.sector_size)
   1.637          
   1.638          else:
   1.639          
   1.640 @@ -261,64 +864,6 @@
   1.641              # contained within it.
   1.642              self.root_name, self.files = self._read_old_catalogue(2*self.sector_size)
   1.643      
   1.644 -    
   1.645 -    # Little endian reading
   1.646 -    
   1.647 -    def _read_signed_word(self, s):
   1.648 -    
   1.649 -        return struct.unpack("<i", s)[0]
   1.650 -    
   1.651 -    def _read_unsigned_word(self, s):
   1.652 -    
   1.653 -        return struct.unpack("<I", s)[0]
   1.654 -    
   1.655 -    def _read_signed_byte(self, s):
   1.656 -    
   1.657 -        return struct.unpack("<b", s)[0]
   1.658 -    
   1.659 -    def _read_unsigned_byte(self, s):
   1.660 -    
   1.661 -        return struct.unpack("<B", s)[0]
   1.662 -    
   1.663 -    def _read_unsigned_half_word(self, s):
   1.664 -    
   1.665 -        return struct.unpack("<H", s)[0]
   1.666 -    
   1.667 -    def _read_signed_half_word(self, s):
   1.668 -    
   1.669 -        return struct.unpack("<h", s)[0]
   1.670 -    
   1.671 -    def _str2num(self, size, s):
   1.672 -    
   1.673 -        i = 0
   1.674 -        n = 0
   1.675 -        while i < size:
   1.676 -        
   1.677 -            n = n | (ord(s[i]) << (i*8))
   1.678 -            i = i + 1
   1.679 -        
   1.680 -        return n
   1.681 -    
   1.682 -    
   1.683 -    def _binary(self, size, n):
   1.684 -    
   1.685 -        new = ""
   1.686 -        while (n != 0) & (size > 0):
   1.687 -        
   1.688 -            if (n & 1)==1:
   1.689 -                new = "1" + new
   1.690 -            else:
   1.691 -                new = "0" + new
   1.692 -            
   1.693 -            n = n >> 1
   1.694 -            size = size - 1
   1.695 -        
   1.696 -        if size > 0:
   1.697 -            new = ("0"*size) + new
   1.698 -        
   1.699 -        return new
   1.700 -    
   1.701 -    
   1.702      def _identify_format(self, adf):
   1.703      
   1.704          # Look for a valid disc record when determining whether the disc
   1.705 @@ -353,7 +898,6 @@
   1.706              # for the length of the image file.
   1.707              checklist["Length field matches image length"] = 1
   1.708          
   1.709 -        
   1.710          # Check the sector size of the disc.
   1.711          
   1.712          if record["sector size"] == 1024:
   1.713 @@ -361,7 +905,6 @@
   1.714              # These should be equal if the disc record is valid.
   1.715              checklist["Expected sector size (1024 bytes)"] = 1
   1.716          
   1.717 -        
   1.718          # Check the density of the disc.
   1.719          
   1.720          if record["density"] == "double":
   1.721 @@ -369,7 +912,6 @@
   1.722              # This should be a double density disc if the disc record is valid.
   1.723              checklist["Expected density (double)"] = 1
   1.724          
   1.725 -        
   1.726          # Check the data at the root directory location.
   1.727          
   1.728          adf.seek((record["root dir"] * record["sector size"]) + 1, 0)
   1.729 @@ -460,7 +1002,6 @@
   1.730              
   1.731              return '?'
   1.732      
   1.733 -    
   1.734      def _read_disc_record(self, offset):
   1.735      
   1.736          # Total sectors per track (sectors * heads)
   1.737 @@ -524,7 +1065,6 @@
   1.738              'disc size': disc_size, 'disc ID': disc_id,
   1.739              'disc name': disc_name, 'zones': zones, 'root dir': root }
   1.740      
   1.741 -    
   1.742      def _read_disc_info(self):
   1.743      
   1.744          checksum = ord(self.sectors[0])
   1.745 @@ -538,14 +1078,11 @@
   1.746              
   1.747              self.map_header = 0
   1.748              self.map_start, self.map_end = 0x40, 0x400
   1.749 -            self.free_space = self._read_free_space(
   1.750 -                self.map_header, self.map_start, self.map_end
   1.751 -                )
   1.752 -            self.disc_map = self._read_new_map(
   1.753 -                self.map_header, self.map_start, self.map_end
   1.754 -                )
   1.755 +            self.disc_map = ADFSnewMap(self.map_header, self.map_start,
   1.756 +                                       self.map_end, self.sectors,
   1.757 +                                       self.sector_size)
   1.758              
   1.759 -            return self.record['disc name'] #, map
   1.760 +            return self.record['disc name']
   1.761          
   1.762          elif self.disc_type == 'adEbig':
   1.763          
   1.764 @@ -555,278 +1092,15 @@
   1.765              
   1.766              self.map_header = 0xc6800
   1.767              self.map_start, self.map_end = 0xc6840, 0xc7800
   1.768 -            self.free_space = self._read_free_space(
   1.769 -                self.map_header, self.map_start, self.map_end
   1.770 -                )
   1.771 -            self.disc_map = self._read_new_map(
   1.772 -                self.map_header, self.map_start, self.map_end
   1.773 -                )
   1.774 +            self.disc_map = ADFSbigNewMap(self.map_header, self.map_start,
   1.775 +                                          self.map_end, self.sectors,
   1.776 +                                          self.sector_size)
   1.777              
   1.778 -            return self.record['disc name'] #, map
   1.779 +            return self.record['disc name']
   1.780          
   1.781          else:
   1.782              return 'Unknown'
   1.783      
   1.784 -    #    zone_size = 819200 / record['zones']
   1.785 -    #    ids_per_zone = zone_size /
   1.786 -    
   1.787 -    def _read_new_map(self, header, begin, end):
   1.788 -    
   1.789 -        disc_map = {}
   1.790 -        
   1.791 -        a = begin
   1.792 -        
   1.793 -        current_piece = None
   1.794 -        current_start = 0
   1.795 -        
   1.796 -        next_zone = header + self.sector_size
   1.797 -        
   1.798 -        # Copy the free space map.
   1.799 -        free_space = self.free_space[:]
   1.800 -        
   1.801 -        while a < end:
   1.802 -        
   1.803 -            # The next entry to be read will occur one byte after this one
   1.804 -            # unless one of the following checks override this behaviour.
   1.805 -            next = a + 1
   1.806 -            
   1.807 -            if (a % self.sector_size) < 4:
   1.808 -            
   1.809 -                # In a zone header. Not the first zone header as this
   1.810 -                # was already skipped when we started reading.
   1.811 -                next = a + 4 - (a % self.sector_size)
   1.812 -                
   1.813 -                # Set the next zone offset.
   1.814 -                next_zone = next_zone + self.sector_size
   1.815 -                
   1.816 -                # Reset the current piece and starting offset.
   1.817 -                current_piece = None
   1.818 -                current_start = 0
   1.819 -            
   1.820 -            elif free_space != [] and a >= free_space[0][0]:
   1.821 -            
   1.822 -                # In the next free space entry. Go to the entry following
   1.823 -                # it and discard this free space entry.
   1.824 -                next = free_space[0][1]
   1.825 -                
   1.826 -                free_space.pop(0)
   1.827 -                
   1.828 -                # Reset the current piece and starting offset.
   1.829 -                current_piece = None
   1.830 -                current_start = 0
   1.831 -            
   1.832 -            elif current_piece is None and (next_zone - a) >= 2:
   1.833 -            
   1.834 -                # If there is enough space left in this zone to allow
   1.835 -                # further fragments then read the next two bytes.
   1.836 -                value = self._read_unsigned_half_word(self.sectors[a:a+2])
   1.837 -                
   1.838 -                entry = value & 0x7fff
   1.839 -                
   1.840 -                # See ADFS/EAddrs.htm document for restriction on
   1.841 -                # the disc address and hence the file number.
   1.842 -                # i.e.the top bit of the file number cannot be set.
   1.843 -                
   1.844 -                if entry >= 1:
   1.845 -                
   1.846 -                    # Defects (1), files or directories (greater than 1)
   1.847 -                    next = a + 2
   1.848 -                    
   1.849 -                    # Define a new entry.
   1.850 -                    #print "Begin:", hex(entry), hex(a)
   1.851 -                    
   1.852 -                    if not disc_map.has_key(entry):
   1.853 -                    
   1.854 -                        # Create a new map entry if none exists.
   1.855 -                        disc_map[entry] = []
   1.856 -                    
   1.857 -                    if (value & 0x8000) == 0:
   1.858 -                    
   1.859 -                        # Record the file number and start of this fragment.
   1.860 -                        current_piece = entry
   1.861 -                        current_start = a
   1.862 -                    
   1.863 -                    else:
   1.864 -                    
   1.865 -                        # For an immediately terminated fragment, add the
   1.866 -                        # extents of the block to the list of pieces found
   1.867 -                        # and implicitly finish reading this fragment
   1.868 -                        # (current_piece remains None).
   1.869 -                        
   1.870 -                        start_addr = self._find_address_from_map(
   1.871 -                            a, begin, entry
   1.872 -                            )
   1.873 -                        end_addr = self._find_address_from_map(
   1.874 -                            next, begin, entry
   1.875 -                            )
   1.876 -                        
   1.877 -                        if [start_addr, end_addr] not in disc_map[entry]:
   1.878 -                        
   1.879 -                            disc_map[entry].append( [start_addr, end_addr] )
   1.880 -                
   1.881 -                else:
   1.882 -                
   1.883 -                    # Search for a valid file number.
   1.884 -                    # Should probably stop looking in this zone.
   1.885 -                    next = a + 1
   1.886 -            
   1.887 -            elif current_piece is not None:
   1.888 -            
   1.889 -                # In a piece being read.
   1.890 -                
   1.891 -                value = ord(self.sectors[a])
   1.892 -                
   1.893 -                if value == 0:
   1.894 -                
   1.895 -                    # Still in the block.
   1.896 -                    next = a + 1
   1.897 -                
   1.898 -                elif value == 0x80:
   1.899 -                
   1.900 -                    # At the end of the block.
   1.901 -                    next = a + 1
   1.902 -                    
   1.903 -                    # For relevant entries add the block to the list of
   1.904 -                    # pieces found.
   1.905 -                    start_addr = self._find_address_from_map(
   1.906 -                        current_start, begin, current_piece
   1.907 -                        )
   1.908 -                    end_addr = self._find_address_from_map(
   1.909 -                        next, begin, current_piece
   1.910 -                        )
   1.911 -                    
   1.912 -                    if [start_addr, end_addr] not in disc_map[current_piece]:
   1.913 -                    
   1.914 -                        disc_map[current_piece].append(
   1.915 -                            [ start_addr, end_addr]
   1.916 -                            )
   1.917 -                    
   1.918 -                    # Look for a new fragment.
   1.919 -                    current_piece = None
   1.920 -                
   1.921 -                else:
   1.922 -                
   1.923 -                    # The byte found was unexpected - backtrack to the
   1.924 -                    # byte after the start of this block and try again.
   1.925 -                    #print "Backtrack from %s to %s" % (hex(a), hex(current_start+1))
   1.926 -                    
   1.927 -                    next = current_start + 1
   1.928 -                    current_piece = None
   1.929 -            
   1.930 -            # Move to the next relevant byte.
   1.931 -            a = next
   1.932 -        
   1.933 -        return disc_map
   1.934 -    
   1.935 -    def _read_free_space(self, header, begin, end):
   1.936 -    
   1.937 -        free_space = []
   1.938 -        
   1.939 -        a = header
   1.940 -        
   1.941 -        while a < end:
   1.942 -        
   1.943 -            # The next zone starts a sector after this one.
   1.944 -            next_zone = a + self.sector_size
   1.945 -            
   1.946 -            a = a + 1
   1.947 -            
   1.948 -            # Start by reading the offset in bits from the start of the header
   1.949 -            # of the first item of free space in the map.
   1.950 -            offset = self._read_unsigned_half_word(self.sectors[a:a+2])
   1.951 -            
   1.952 -            # The top bit is apparently always set, so mask it off and convert
   1.953 -            # the result into bytes. * Not sure if this is the case for
   1.954 -            # entries in the map. *
   1.955 -            next = ((offset & 0x7fff) >> 3)
   1.956 -            
   1.957 -            if next == 0:
   1.958 -            
   1.959 -                # No more free space in this zone. Look at the free
   1.960 -                # space field in the next zone.
   1.961 -                a = next_zone
   1.962 -                continue
   1.963 -            
   1.964 -            # Update the offset to point to the free space in this zone.
   1.965 -            a = a + next
   1.966 -            
   1.967 -            while a < next_zone:
   1.968 -            
   1.969 -                # Read the offset to the next free fragment in this zone.
   1.970 -                offset = self._read_unsigned_half_word(self.sectors[a:a+2])
   1.971 -                
   1.972 -                # Convert this to a byte offset.
   1.973 -                next = ((offset & 0x7fff) >> 3)
   1.974 -                
   1.975 -                # Find the end of the free space.
   1.976 -                b = a + 1
   1.977 -                
   1.978 -                while b < next_zone:
   1.979 -                
   1.980 -                    c = b + 1
   1.981 -                    
   1.982 -                    value = self._read_unsigned_byte(self.sectors[b])
   1.983 -                    
   1.984 -                    if (value & 0x80) != 0:
   1.985 -                    
   1.986 -                        break
   1.987 -                    
   1.988 -                    b = c
   1.989 -                
   1.990 -                # Record the offset into the map of this item of free space
   1.991 -                # and the offset of the byte after it ends.
   1.992 -                free_space.append( (a, c) )
   1.993 -                
   1.994 -                if next == 0:
   1.995 -                
   1.996 -                    break
   1.997 -                
   1.998 -                # Move to the next free space entry.
   1.999 -                a = a + next
  1.1000 -            
  1.1001 -            # Whether we are at the end of the zone or not, move to the
  1.1002 -            # beginning of the next zone.
  1.1003 -            a = next_zone
  1.1004 -        
  1.1005 -        # Return the free space list.
  1.1006 -        return free_space
  1.1007 -    
  1.1008 -    def _find_address_from_map(self, addr, begin, entry):
  1.1009 -    
  1.1010 -        if self.disc_type == 'adE':
  1.1011 -        
  1.1012 -            address = ((addr - begin) * self.sector_size)
  1.1013 -        
  1.1014 -        elif self.disc_type == 'adEbig':
  1.1015 -        
  1.1016 -            # I can't remember where the rationale for this calculation
  1.1017 -            # came from or where the necessary information was obtained.
  1.1018 -            # It probably came from one of the WSS files, such as
  1.1019 -            # Formats.htm or Formats2.htm which imply that the F format
  1.1020 -            # uses 512 byte sectors (see the 0x200 value below) and
  1.1021 -            # indicate that F format uses 4 zones rather than 1.
  1.1022 -            
  1.1023 -            upper = (entry & 0x7f00) >> 8
  1.1024 -            
  1.1025 -            if upper > 1:
  1.1026 -                upper = upper - 1
  1.1027 -            if upper > 3:
  1.1028 -                upper = 3
  1.1029 -            
  1.1030 -            address = ((addr - begin) - (upper * 0xc8)) * 0x200
  1.1031 -        
  1.1032 -        return address
  1.1033 -    
  1.1034 -    def _find_in_new_map(self, file_no):
  1.1035 -    
  1.1036 -        try:
  1.1037 -        
  1.1038 -            return self.disc_map[file_no]
  1.1039 -        
  1.1040 -        except KeyError:
  1.1041 -        
  1.1042 -            return []
  1.1043 -    
  1.1044      def _read_tracks(self, f, inter):
  1.1045      
  1.1046          t = ""
  1.1047 @@ -871,7 +1145,6 @@
  1.1048          
  1.1049          return t
  1.1050      
  1.1051 -    
  1.1052      def _read_sectors(self, adf):
  1.1053      
  1.1054          s = []
  1.1055 @@ -894,69 +1167,6 @@
  1.1056          
  1.1057          return s
  1.1058      
  1.1059 -    
  1.1060 -    def _safe(self, s, with_space = 0):
  1.1061 -    
  1.1062 -        new = ""
  1.1063 -        if with_space == 1:
  1.1064 -            lower = 31
  1.1065 -        else:
  1.1066 -            lower = 32
  1.1067 -        
  1.1068 -        for i in s:
  1.1069 -        
  1.1070 -            if ord(i) <= lower:
  1.1071 -                break
  1.1072 -            
  1.1073 -            if ord(i) >= 128:
  1.1074 -                c = ord(i)^128
  1.1075 -                if c > 32:
  1.1076 -                    new = new + chr(c)
  1.1077 -            else:
  1.1078 -                new = new + i
  1.1079 -        
  1.1080 -        return new
  1.1081 -    
  1.1082 -    
  1.1083 -    def _read_freespace(self):
  1.1084 -    
  1.1085 -        # Currently unused
  1.1086 -            
  1.1087 -        base = 0
  1.1088 -    
  1.1089 -        free = []
  1.1090 -        p = 0
  1.1091 -        while self.sectors[base+p] != 0:
  1.1092 -    
  1.1093 -            free.append(self._str2num(3, self.sectors[base+p:base+p+3]))
  1.1094 -    
  1.1095 -        name = self.sectors[self.sector_size-9:self.sector_size-4]
  1.1096 -    
  1.1097 -        disc_size = self._str2num(
  1.1098 -            3, self.sectors[self.sector_size-4:self.sector_size-1]
  1.1099 -            )
  1.1100 -    
  1.1101 -        checksum0 = self._read_unsigned_byte(self.sectors[self.sector_size-1])
  1.1102 -    
  1.1103 -        base = self.sector_size
  1.1104 -    
  1.1105 -        p = 0
  1.1106 -        while self.sectors[base+p] != 0:
  1.1107 -    
  1.1108 -            free.append(self._str2num(3, self.sectors[base+p:base+p+3]))
  1.1109 -    
  1.1110 -        name = name + \
  1.1111 -            self.sectors[base+self.sector_size-10:base+self.sector_size-5]
  1.1112 -    
  1.1113 -        disc_id = self._str2num(
  1.1114 -            2, self.sectors[base+self.sector_size-5:base+self.sector_size-3]
  1.1115 -            )
  1.1116 -    
  1.1117 -        boot = self._read_unsigned_byte(self.sectors[base+self.sector_size-3])
  1.1118 -    
  1.1119 -        checksum1 = self._read_unsigned_byte(self.sectors[base+self.sector_size-1])
  1.1120 -    
  1.1121 -    
  1.1122      def _read_old_catalogue(self, base):
  1.1123      
  1.1124          head = base
  1.1125 @@ -1115,213 +1325,6 @@
  1.1126          
  1.1127          return dir_name, files
  1.1128      
  1.1129 -    
  1.1130 -    def _read_new_address(self, s):
  1.1131 -    
  1.1132 -        # From the three character string passed, determine the address on the
  1.1133 -        # disc.
  1.1134 -        value = self._str2num(3, s)
  1.1135 -        
  1.1136 -        # This is a SIN (System Internal Number)
  1.1137 -        # The bottom 8 bits are the sector offset + 1
  1.1138 -        offset = value & 0xff
  1.1139 -        if offset != 0:
  1.1140 -            address = (offset - 1) * self.sector_size
  1.1141 -        else:
  1.1142 -            address = 0
  1.1143 -        
  1.1144 -        # The top 16 bits are the file number
  1.1145 -        file_no = value >> 8
  1.1146 -        
  1.1147 -        # The pieces of the object are returned as a list of pairs of
  1.1148 -        # addresses.
  1.1149 -        pieces = self._find_in_new_map(file_no)
  1.1150 -        
  1.1151 -        if pieces == []:
  1.1152 -        
  1.1153 -            return -1
  1.1154 -        
  1.1155 -        # Ensure that the first piece of data is read from the appropriate
  1.1156 -        # point in the relevant sector.
  1.1157 -        
  1.1158 -        pieces[0][0] = pieces[0][0] + address
  1.1159 -        
  1.1160 -        return pieces
  1.1161 -    
  1.1162 -    
  1.1163 -    def _read_new_catalogue(self, base):
  1.1164 -    
  1.1165 -        head = base
  1.1166 -        p = 0
  1.1167 -        
  1.1168 -        dir_seq = self.sectors[head + p]
  1.1169 -        dir_start = self.sectors[head+p+1:head+p+5]
  1.1170 -        if dir_start not in self.dir_markers:
  1.1171 -        
  1.1172 -            if self.verify:
  1.1173 -            
  1.1174 -                self.verify_log.append(
  1.1175 -                    (WARNING, 'Not a directory: %s' % hex(head))
  1.1176 -                    )
  1.1177 -            
  1.1178 -            return '', []
  1.1179 -        
  1.1180 -        p = p + 5
  1.1181 -        
  1.1182 -        files = []
  1.1183 -        
  1.1184 -        while ord(self.sectors[head+p]) != 0:
  1.1185 -        
  1.1186 -            old_name = self.sectors[head+p:head+p+10]
  1.1187 -            top_set = 0
  1.1188 -            counter = 1
  1.1189 -            for i in old_name:
  1.1190 -                if (ord(i) & 128) != 0:
  1.1191 -                    top_set = counter
  1.1192 -                counter = counter + 1
  1.1193 -            
  1.1194 -            name = self._safe(self.sectors[head+p:head+p+10])
  1.1195 -            
  1.1196 -            load = self._read_unsigned_word(self.sectors[head+p+10:head+p+14])
  1.1197 -            exe = self._read_unsigned_word(self.sectors[head+p+14:head+p+18])
  1.1198 -            length = self._read_unsigned_word(self.sectors[head+p+18:head+p+22])
  1.1199 -            
  1.1200 -            inddiscadd = self._read_new_address(
  1.1201 -                self.sectors[head+p+22:head+p+25]
  1.1202 -                )
  1.1203 -            newdiratts = self._read_unsigned_byte(self.sectors[head+p+25])
  1.1204 -            
  1.1205 -            if inddiscadd == -1:
  1.1206 -            
  1.1207 -                if (newdiratts & 0x8) != 0:
  1.1208 -                
  1.1209 -                    if self.verify:
  1.1210 -                    
  1.1211 -                        self.verify_log.append(
  1.1212 -                            (WARNING, "Couldn't find directory: %s" % name)
  1.1213 -                            )
  1.1214 -                        self.verify_log.append(
  1.1215 -                            (WARNING, "    at: %x" % (head+p+22))
  1.1216 -                            )
  1.1217 -                        self.verify_log.append( (
  1.1218 -                            WARNING, "    file details: %x" % \
  1.1219 -                            self._str2num(3, self.sectors[head+p+22:head+p+25])
  1.1220 -                            ) )
  1.1221 -                        self.verify_log.append(
  1.1222 -                            (WARNING, "    atts: %x" % newdiratts)
  1.1223 -                            )
  1.1224 -                
  1.1225 -                elif length != 0:
  1.1226 -                
  1.1227 -                    if self.verify:
  1.1228 -                    
  1.1229 -                        self.verify_log.append(
  1.1230 -                            (WARNING, "Couldn't find file: %s" % name)
  1.1231 -                            )
  1.1232 -                        self.verify_log.append(
  1.1233 -                            (WARNING, "    at: %x" % (head+p+22))
  1.1234 -                            )
  1.1235 -                        self.verify_log.append( (
  1.1236 -                            WARNING,
  1.1237 -                            "    file details: %x" % \
  1.1238 -                            self._str2num(3, self.sectors[head+p+22:head+p+25])
  1.1239 -                            ) )
  1.1240 -                        self.verify_log.append(
  1.1241 -                            (WARNING, "    atts: %x" % newdiratts)
  1.1242 -                            )
  1.1243 -                
  1.1244 -                else:
  1.1245 -                
  1.1246 -                    # Store a zero length file. This appears to be the
  1.1247 -                    # standard behaviour for storing empty files.
  1.1248 -                    files.append(ADFSfile(name, "", load, exe, length))
  1.1249 -            
  1.1250 -            else:
  1.1251 -            
  1.1252 -                if (newdiratts & 0x8) != 0:
  1.1253 -                
  1.1254 -                    # Remember that inddiscadd will be a sequence of
  1.1255 -                    # pairs of addresses.
  1.1256 -                    
  1.1257 -                    for start, end in inddiscadd:
  1.1258 -                    
  1.1259 -                        # Try to interpret the data at the referenced address
  1.1260 -                        # as a directory.
  1.1261 -                        
  1.1262 -                        lower_dir_name, lower_files = \
  1.1263 -                            self._read_new_catalogue(start)
  1.1264 -                        
  1.1265 -                        # Store the directory name and file found therein.
  1.1266 -                        files.append(ADFSdirectory(name, lower_files))
  1.1267 -                
  1.1268 -                else:
  1.1269 -                
  1.1270 -                    # Remember that inddiscadd will be a sequence of
  1.1271 -                    # pairs of addresses.
  1.1272 -                    
  1.1273 -                    file = ""
  1.1274 -                    remaining = length
  1.1275 -                    
  1.1276 -                    for start, end in inddiscadd:
  1.1277 -                    
  1.1278 -                        amount = min(remaining, end - start)
  1.1279 -                        file = file + self.sectors[start : (start + amount)]
  1.1280 -                        remaining = remaining - amount
  1.1281 -                    
  1.1282 -                    files.append(ADFSfile(name, file, load, exe, length))
  1.1283 -            
  1.1284 -            p = p + 26
  1.1285 -        
  1.1286 -        
  1.1287 -        # Go to tail of directory structure (0x800 -- 0xc00)
  1.1288 -        
  1.1289 -        tail = head + self.sector_size
  1.1290 -        
  1.1291 -        dir_end = self.sectors[tail+self.sector_size-5:tail+self.sector_size-1]
  1.1292 -        
  1.1293 -        if dir_end not in self.dir_markers:
  1.1294 -        
  1.1295 -            if self.verify:
  1.1296 -            
  1.1297 -                self.verify_log.append(
  1.1298 -                    ( WARNING,
  1.1299 -                      'Discrepancy in directory structure: [%x, %x]' % \
  1.1300 -                      ( head, tail ) )
  1.1301 -                    )
  1.1302 -            
  1.1303 -            return '', files
  1.1304 -        
  1.1305 -        dir_name = self._safe(
  1.1306 -            self.sectors[tail+self.sector_size-16:tail+self.sector_size-6]
  1.1307 -            )
  1.1308 -        
  1.1309 -        parent = \
  1.1310 -            self.sectors[tail+self.sector_size-38:tail+self.sector_size-35]
  1.1311 -        
  1.1312 -        dir_title = \
  1.1313 -            self.sectors[tail+self.sector_size-35:tail+self.sector_size-16]
  1.1314 -        
  1.1315 -        if head == 0x800 and self.disc_type == 'adE':
  1.1316 -            dir_name = '$'
  1.1317 -        if head == 0xc8800 and self.disc_type == 'adEbig':
  1.1318 -            dir_name = '$'
  1.1319 -        
  1.1320 -        endseq = self.sectors[tail+self.sector_size-6]
  1.1321 -        if endseq != dir_seq:
  1.1322 -        
  1.1323 -            if self.verify:
  1.1324 -            
  1.1325 -                self.verify_log.append(
  1.1326 -                    ( WARNING,
  1.1327 -                      'Broken directory: %s at [%x, %x]' % \
  1.1328 -                      (dir_title, head, tail) )
  1.1329 -                    )
  1.1330 -            
  1.1331 -            return dir_name, files
  1.1332 -        
  1.1333 -        return dir_name, files
  1.1334 -    
  1.1335 -    
  1.1336      def print_catalogue(self, files = None, path = "$", filetypes = 0):
  1.1337      
  1.1338          """Prints the contents of the disc catalogue to standard output.
  1.1339 @@ -1393,7 +1396,6 @@
  1.1340              
  1.1341                  self.print_catalogue(obj.files, path + "." + name, filetypes)
  1.1342      
  1.1343 -    
  1.1344      def _convert_name(self, old_name, convert_dict):
  1.1345      
  1.1346          # Use the conversion dictionary to convert any forbidden
  1.1347 @@ -1486,7 +1488,6 @@
  1.1348                      obj.files, new_path, filetypes, separator, convert_dict
  1.1349                      )
  1.1350      
  1.1351 -    
  1.1352      def _extract_new_files(self, objects, path, filetypes = 0, separator = ",",
  1.1353                             convert_dict = {}, with_time_stamps = False):
  1.1354      
  1.1355 @@ -1555,7 +1556,6 @@
  1.1356                      obj.files, new_path, filetypes, separator, convert_dict
  1.1357                      )
  1.1358      
  1.1359 -    
  1.1360      def extract_files(self, out_path, files = None, filetypes = 0,
  1.1361                        separator = ",", convert_dict = {},
  1.1362                        with_time_stamps = False):