python-adfs

changeset 52:a9449aa046e1

Added support for character translations in filenames. Defects are now treated like normal files and directories; the number of mapped defects is displayed before the log of verification faults.
author David Boddie <david@boddie.org.uk>
date Sun Jul 20 00:24:28 2003 +0200
parents a8eead849e21
children f8ff54851fbc
files ADFSlib.py
diffstat 1 files changed, 170 insertions(+), 72 deletions(-) [+]
line diff
     1.1 --- a/ADFSlib.py	Sat Jul 19 21:42:57 2003 +0200
     1.2 +++ b/ADFSlib.py	Sun Jul 20 00:24:28 2003 +0200
     1.3 @@ -26,8 +26,8 @@
     1.4  """
     1.5  
     1.6  __author__ = "David Boddie <david@boddie.org.uk>"
     1.7 -__date__ = "Thu 3rd July 2003"
     1.8 -__version__ = "0.14"
     1.9 +__date__ = "Sat 19th July 2003"
    1.10 +__version__ = "0.20"
    1.11  
    1.12  
    1.13  import os, string
    1.14 @@ -127,7 +127,7 @@
    1.15          
    1.16              # Read the disc name and map
    1.17              self.disc_name = self.read_disc_info()
    1.18 -        
    1.19 +            
    1.20              # Find the root directory name and all the files and directories
    1.21              # contained within it
    1.22              self.root_name, self.files = self.read_new_catalogue(0x800)
    1.23 @@ -136,12 +136,13 @@
    1.24          
    1.25              # Read the disc name and map
    1.26              self.disc_name = self.read_disc_info()
    1.27 -        
    1.28 +            
    1.29              # Find the root directory name and all the files and directories
    1.30              # contained within it
    1.31              self.root_name, self.files = self.read_new_catalogue(0xc8800)
    1.32          
    1.33          else:
    1.34 +        
    1.35              # Find the root directory name and all the files and directories
    1.36              # contained within it
    1.37              self.root_name, self.files = self.read_old_catalogue(2*self.sector_size)
    1.38 @@ -152,10 +153,10 @@
    1.39          i = 0
    1.40          n = 0
    1.41          while i < size:
    1.42 -    
    1.43 +        
    1.44              n = n | (ord(s[i]) << (i*8))
    1.45              i = i + 1
    1.46 -    
    1.47 +        
    1.48          return n
    1.49      
    1.50      
    1.51 @@ -168,13 +169,13 @@
    1.52                  new = "1" + new
    1.53              else:
    1.54                  new = "0" + new
    1.55 -    
    1.56 +            
    1.57              n = n >> 1
    1.58              size = size - 1
    1.59 -    
    1.60 +        
    1.61          if size > 0:
    1.62              new = ("0"*size) + new
    1.63 -    
    1.64 +        
    1.65          return new
    1.66      
    1.67      
    1.68 @@ -187,7 +188,7 @@
    1.69          adf.seek(0x801)
    1.70          word2 = adf.read(4)
    1.71          adf.seek(0)
    1.72 -    
    1.73 +        
    1.74          if word1 == 'Hugo':
    1.75              return 'D'
    1.76          elif word2 == 'Nick':
    1.77 @@ -214,7 +215,7 @@
    1.78              density = 'quad'        # Quad density disc
    1.79          else:
    1.80              density = 'unknown'
    1.81 -    
    1.82 +        
    1.83          # LowSector
    1.84          # StartUp
    1.85          # LinkBits
    1.86 @@ -237,7 +238,7 @@
    1.87          disc_id   = self.str2num(2, self.sectors[offset + 20 : offset + 22])
    1.88          # DiscName
    1.89          disc_name = string.strip(self.sectors[offset + 22 : offset + 32])
    1.90 -    
    1.91 +        
    1.92          return {'sectors': nsectors, 'heads': heads, 'density': density,
    1.93              'disc size': disc_size, 'disc ID': disc_id,
    1.94              'disc name': disc_name, 'zones': zones, 'root': root}
    1.95 @@ -263,7 +264,7 @@
    1.96              return self.record['disc name'] #, map
    1.97          
    1.98          if self.disc_type == 'adEbig':
    1.99 -    
   1.100 +        
   1.101              self.record = self.read_disc_record(0xc6804)
   1.102              self.map_header = 0xc6800
   1.103              self.map_start, self.map_end = 0xc6840, 0xc7800
   1.104 @@ -285,7 +286,7 @@
   1.105      def read_new_map(self, header, begin, end):
   1.106      
   1.107          disc_map = {}
   1.108 -    
   1.109 +        
   1.110          a = begin
   1.111          
   1.112          current_piece = None
   1.113 @@ -304,7 +305,8 @@
   1.114              
   1.115              if (a % self.sector_size) < 4:
   1.116              
   1.117 -                # In a zone header.
   1.118 +                # In a zone header. Not the first zone header as this
   1.119 +                # was already skipped when we started reading.
   1.120                  next = a + 4 - (a % self.sector_size)
   1.121                  
   1.122                  # Set the next zone offset.
   1.123 @@ -328,6 +330,8 @@
   1.124              
   1.125              elif current_piece is None and (next_zone - a) >= 2:
   1.126              
   1.127 +                # If there is enough space left in this zone to allow
   1.128 +                # further fragments then read the next two bytes.
   1.129                  value = self.str2num(2, self.sectors[a:a+2])
   1.130                  
   1.131                  entry = value & 0x7fff
   1.132 @@ -336,14 +340,14 @@
   1.133                  # the disc address and hence the file number.
   1.134                  # i.e.the top bit of the file number cannot be set.
   1.135                  
   1.136 -                if entry == 1:
   1.137 +                #if entry == 1:
   1.138 +                #
   1.139 +                #    # File number 1 (defect)
   1.140 +                #    next = a + 2
   1.141 +                #
   1.142 +                if entry >= 1:
   1.143                  
   1.144 -                    # File number 1 (defect)
   1.145 -                    next = a + 2
   1.146 -                
   1.147 -                elif entry > 1:
   1.148 -                
   1.149 -                    # Files or directories (greater than 1)
   1.150 +                    # Defects (1), files or directories (greater than 1)
   1.151                      next = a + 2
   1.152                      
   1.153                      # Define a new entry.
   1.154 @@ -351,6 +355,7 @@
   1.155                      
   1.156                      if not disc_map.has_key(entry):
   1.157                      
   1.158 +                        # Create a new map entry if none exists.
   1.159                          disc_map[entry] = []
   1.160                      
   1.161                      if (value & 0x8000) == 0:
   1.162 @@ -363,7 +368,8 @@
   1.163                      
   1.164                          # For an immediately terminated fragment, add the
   1.165                          # extents of the block to the list of pieces found
   1.166 -                        # and implicitly finish reading this fragment.
   1.167 +                        # and implicitly finish reading this fragment
   1.168 +                        # (current_piece remains None).
   1.169                          
   1.170                          start_addr = self.find_address_from_map(
   1.171                              a, begin, entry
   1.172 @@ -413,6 +419,7 @@
   1.173                              [ start_addr, end_addr]
   1.174                              )
   1.175                      
   1.176 +                    # Look for a new fragment.
   1.177                      current_piece = None
   1.178                  
   1.179                  else:
   1.180 @@ -540,27 +547,28 @@
   1.181      def read_tracks(self, f, inter):
   1.182      
   1.183          t = ""
   1.184 -    
   1.185 +        
   1.186          if inter==0:
   1.187              try:
   1.188                  for i in range(0, self.ntracks):
   1.189 -    
   1.190 +                
   1.191                      t = t + f.read(self.nsectors * self.sector_size)
   1.192 -    
   1.193 +            
   1.194              except IOError:
   1.195                  print 'Less than %i tracks found.' % self.ntracks
   1.196                  f.close()
   1.197                  raise ADFS_exception, \
   1.198                      'Less than %i tracks found.' % self.ntracks
   1.199 -    
   1.200 +        
   1.201          else:
   1.202 -    
   1.203 +        
   1.204              # Tracks are interleaved (0 80 1 81 2 82 ... 79 159) so rearrange
   1.205              # them into the form (0 1 2 3 ... 159)
   1.206 -    
   1.207 +            
   1.208              try:
   1.209 +            
   1.210                  for i in range(0, self.ntracks):
   1.211 -    
   1.212 +                
   1.213                      if i < (self.ntracks >> 1):
   1.214                          f.seek(i*2*self.nsectors*self.sector_size, 0)
   1.215                          t = t + f.read(self.nsectors*self.sector_size)
   1.216 @@ -568,12 +576,14 @@
   1.217                          j = i - (self.ntracks >> 1)
   1.218                          f.seek(((j*2)+1)*self.nsectors*self.sector_size, 0)
   1.219                          t = t + f.read(self.nsectors*self.sector_size)
   1.220 +            
   1.221              except IOError:
   1.222 +            
   1.223                  print 'Less than %i tracks found.' % self.ntracks
   1.224                  f.close()
   1.225                  raise ADFS_exception, \
   1.226                      'Less than %i tracks found.' % self.ntracks
   1.227 -    
   1.228 +        
   1.229          return t
   1.230      
   1.231      
   1.232 @@ -581,22 +591,22 @@
   1.233      
   1.234          s = []
   1.235          try:
   1.236 -    
   1.237 +        
   1.238              for i in range(0, self.ntracks):
   1.239 -    
   1.240 +            
   1.241                  for j in range(0, self.nsectors):
   1.242 -    
   1.243 +                
   1.244                      s.append(adf.read(self.sector_size))
   1.245 -    
   1.246 +        
   1.247          except IOError:
   1.248 -    
   1.249 +        
   1.250              print 'Less than %i tracks x %i sectors found.' % \
   1.251                  (self.ntracks, self.nsectors)
   1.252              adf.close()
   1.253              raise ADFS_exception, \
   1.254                  'Less than %i tracks x %i sectors found.' % \
   1.255                  (self.ntracks, self.nsectors)
   1.256 -    
   1.257 +        
   1.258          return s
   1.259      
   1.260      
   1.261 @@ -609,9 +619,10 @@
   1.262              lower = 32
   1.263          
   1.264          for i in s:
   1.265 +        
   1.266              if ord(i) <= lower:
   1.267                  break
   1.268 -    
   1.269 +            
   1.270              if ord(i) >= 128:
   1.271                  c = ord(i)^128
   1.272                  if c > 32:
   1.273 @@ -623,7 +634,7 @@
   1.274      
   1.275      
   1.276      def read_freespace(self):
   1.277 -
   1.278 +    
   1.279          # Currently unused
   1.280              
   1.281          base = 0
   1.282 @@ -676,13 +687,13 @@
   1.283                  self.verify_log.append('Not a directory: %x' % head)
   1.284              
   1.285              return "", []
   1.286 -    
   1.287 +        
   1.288          p = p + 5
   1.289 -    
   1.290 +        
   1.291          files = []
   1.292 -    
   1.293 +        
   1.294          while ord(self.sectors[head+p]) != 0:
   1.295 -    
   1.296 +        
   1.297              old_name = self.sectors[head+p:head+p+10]
   1.298              top_set = 0
   1.299              counter = 1
   1.300 @@ -690,13 +701,13 @@
   1.301                  if (ord(i) & 128) != 0:
   1.302                      top_set = counter
   1.303                  counter = counter + 1
   1.304 -    
   1.305 +            
   1.306              name = self.safe(self.sectors[head+p:head+p+10])
   1.307 -    
   1.308 +            
   1.309              load = self.str2num(4, self.sectors[head+p+10:head+p+14])
   1.310              exe = self.str2num(4, self.sectors[head+p+14:head+p+18])
   1.311              length = self.str2num(4, self.sectors[head+p+18:head+p+22])
   1.312 -    
   1.313 +            
   1.314              if self.disc_type == 'adD':
   1.315                  inddiscadd = 256 * self.str2num(
   1.316                      3, self.sectors[head+p+22:head+p+25]
   1.317 @@ -705,9 +716,9 @@
   1.318                  inddiscadd = self.sector_size * self.str2num(
   1.319                      3, self.sectors[head+p+22:head+p+25]
   1.320                      )
   1.321 -    
   1.322 +            
   1.323              olddirobseq = self.str2num(1, self.sectors[head+p+25])
   1.324 -    
   1.325 +            
   1.326              #print string.expandtabs(
   1.327              #   "%s\t%s\t%s\t%s" % (
   1.328              #       name, "("+self.binary(8, olddirobseq)+")",
   1.329 @@ -718,7 +729,7 @@
   1.330              #   "%s\t%02x\t%08x\t%08x" % (
   1.331              #       name, olddirobseq, load, exe
   1.332              #   ) )
   1.333 -    
   1.334 +            
   1.335              if self.disc_type == 'adD':
   1.336              
   1.337                  # Old format 800K discs.
   1.338 @@ -738,6 +749,7 @@
   1.339                          [ name, self.sectors[inddiscadd:inddiscadd+length],
   1.340                            load, exe, length ]
   1.341                          )
   1.342 +            
   1.343              else:
   1.344              
   1.345                  # Old format < 800K discs.
   1.346 @@ -763,12 +775,12 @@
   1.347          
   1.348          
   1.349          # Go to tail of directory structure (0x200 -- 0x700)
   1.350 -    
   1.351 +        
   1.352          if self.disc_type == 'adD':
   1.353              tail = head + self.sector_size    # 1024 bytes
   1.354          else:
   1.355              tail = head + (self.sector_size*4)    # 1024 bytes
   1.356 -    
   1.357 +        
   1.358          dir_end = self.sectors[tail+self.sector_size-5:tail+self.sector_size-1]
   1.359          if dir_end != 'Hugo':
   1.360          
   1.361 @@ -780,7 +792,7 @@
   1.362                      )
   1.363                          
   1.364              return '', files
   1.365 -    
   1.366 +        
   1.367          # Read the directory name, its parent and any title given.
   1.368          if self.disc_type == 'adD':
   1.369          
   1.370 @@ -816,10 +828,10 @@
   1.371              
   1.372              # Note that the title may contain spaces.
   1.373              self.disc_name = self.safe(dir_title, with_space = 1)
   1.374 -    
   1.375 -    #    print "Directory title", dir_title
   1.376 -    #    print "Directory name ", dir_name
   1.377 -    
   1.378 +        
   1.379 +        #print "Directory title", dir_title
   1.380 +        #print "Directory name ", dir_name
   1.381 +        
   1.382          endseq = self.sectors[tail+self.sector_size-6]
   1.383          if endseq != dir_seq:
   1.384          
   1.385 @@ -831,7 +843,7 @@
   1.386                      )
   1.387              
   1.388              return dir_name, files
   1.389 -    
   1.390 +        
   1.391          return dir_name, files
   1.392      
   1.393      
   1.394 @@ -966,7 +978,7 @@
   1.395                      #print hex(head+p), hex(head+p+22)
   1.396              
   1.397              else:
   1.398 -        
   1.399 +            
   1.400                  if (newdiratts & 0x8) != 0:
   1.401                  
   1.402                      #print '%s -> %s' % (name, hex(inddiscadd))
   1.403 @@ -1015,7 +1027,7 @@
   1.404                          remaining = remaining - amount
   1.405                      
   1.406                      files.append([name, file, load, exe, length])
   1.407 -    
   1.408 +            
   1.409              p = p + 26
   1.410          
   1.411          
   1.412 @@ -1123,7 +1135,32 @@
   1.413                  self.print_catalogue(i[1], path + "." + name, filetypes)
   1.414      
   1.415      
   1.416 -    def extract_old_files(self, l, path, filetypes = 0, separator = ","):
   1.417 +    def convert_name(self, old_name, convert_dict):
   1.418 +    
   1.419 +        # Use the conversion dictionary to convert any forbidden
   1.420 +        # characters to accepted local substitutes.
   1.421 +        name = ""
   1.422 +        
   1.423 +        for c in old_name:
   1.424 +        
   1.425 +            if c in convert_dict.keys():
   1.426 +            
   1.427 +                name = name + convert_dict[c]
   1.428 +            
   1.429 +            else:
   1.430 +            
   1.431 +                name = name + c
   1.432 +        
   1.433 +        if self.verify and old_name != name:
   1.434 +        
   1.435 +            self.verify_log.append(
   1.436 +                "Changed %s to %s" % (old_name, name)
   1.437 +                )
   1.438 +        
   1.439 +        return name
   1.440 +    
   1.441 +    def extract_old_files(self, l, path, filetypes = 0, separator = ",",
   1.442 +                          convert_dict = {}):
   1.443      
   1.444          new_path = self.create_directory(path)
   1.445          
   1.446 @@ -1137,7 +1174,9 @@
   1.447          
   1.448          for i in l:
   1.449          
   1.450 -            name = i[0]
   1.451 +            old_name = i[0]
   1.452 +            
   1.453 +            name = self.convert_name(old_name, convert_dict)
   1.454              
   1.455              if type(i[1]) != type([]):
   1.456              
   1.457 @@ -1184,10 +1223,13 @@
   1.458              
   1.459                  new_path = os.path.join(path, name)
   1.460                  
   1.461 -                self.extract_old_files(i[1], new_path, filetypes)
   1.462 +                self.extract_old_files(
   1.463 +                    i[1], new_path, filetypes, separator, convert_dict
   1.464 +                    )
   1.465      
   1.466      
   1.467 -    def extract_new_files(self, l, path, filetypes = 0, separator = ","):
   1.468 +    def extract_new_files(self, l, path, filetypes = 0, separator = ",",
   1.469 +                          convert_dict = {}):
   1.470      
   1.471          new_path = self.create_directory(path)
   1.472          
   1.473 @@ -1201,13 +1243,17 @@
   1.474          
   1.475          for i in l:
   1.476          
   1.477 -            name = i[0]
   1.478 +            old_name = i[0]
   1.479 +            
   1.480 +            # Use the conversion dictionary to convert any forbidden
   1.481 +            # characters to accepted local substitutes.
   1.482 +            name = self.convert_name(old_name, convert_dict)
   1.483              
   1.484              if type(i[1]) != type([]):
   1.485              
   1.486                  # A file.
   1.487                  load, exec_addr, length = i[2], i[3], i[4]
   1.488 -    
   1.489 +                
   1.490                  if filetypes == 0:
   1.491                  
   1.492                      # Load and execution addresses assumed to be valid.
   1.493 @@ -1247,11 +1293,13 @@
   1.494              
   1.495                  new_path = os.path.join(path, name)
   1.496                  
   1.497 -                self.extract_new_files(i[1], new_path, filetypes)
   1.498 +                self.extract_new_files(
   1.499 +                    i[1], new_path, filetypes, separator, convert_dict
   1.500 +                    )
   1.501      
   1.502      
   1.503      def extract_files(self, out_path, files = None, filetypes = 0,
   1.504 -                      separator = ","):
   1.505 +                      separator = ",", convert_dict = {}):
   1.506      
   1.507          if files is None:
   1.508          
   1.509 @@ -1259,19 +1307,27 @@
   1.510          
   1.511          if self.disc_type == 'adD':
   1.512          
   1.513 -            self.extract_old_files(files, out_path, filetypes)
   1.514 +            self.extract_old_files(
   1.515 +                files, out_path, filetypes, separator, convert_dict
   1.516 +                )
   1.517          
   1.518          elif self.disc_type == 'adE':
   1.519          
   1.520 -            self.extract_new_files(files, out_path, filetypes)
   1.521 +            self.extract_new_files(
   1.522 +                files, out_path, filetypes, separator, convert_dict
   1.523 +                )
   1.524          
   1.525          elif self.disc_type == 'adEbig':
   1.526          
   1.527 -            self.extract_new_files(files, out_path, filetypes)
   1.528 +            self.extract_new_files(
   1.529 +                files, out_path, filetypes, separator, convert_dict
   1.530 +                )
   1.531          
   1.532          else:
   1.533          
   1.534 -            self.extract_old_files(files, out_path, filetypes)
   1.535 +            self.extract_old_files(
   1.536 +                files, out_path, filetypes, separator, convert_dict
   1.537 +                )
   1.538      
   1.539      def create_directory(self, path, name = None):
   1.540      
   1.541 @@ -1328,6 +1384,41 @@
   1.542          # Success
   1.543          return built
   1.544      
   1.545 +    def plural(self, msg, values, words):
   1.546 +    
   1.547 +        """message = plural(self, msg, values, words)
   1.548 +        
   1.549 +        Return a message which takes into account the plural form of
   1.550 +        words in the original message, assuming that the appropriate
   1.551 +        form for negative numbers of items is the same as that for
   1.552 +        more than one item.
   1.553 +        
   1.554 +        values is a list of numeric values referenced in the message.
   1.555 +        words is a list of sequences of words to substitute into the
   1.556 +        message. This takes the form
   1.557 +        
   1.558 +            [ word_to_use_for_zero_items,
   1.559 +              word_to_use_for_one_item,
   1.560 +              word_to_use_for_two_or_more_items ]
   1.561 +        
   1.562 +        The length of the values and words lists must be equal.
   1.563 +        """
   1.564 +        
   1.565 +        substitutions = []
   1.566 +        
   1.567 +        for i in range(0, len(values)):
   1.568 +        
   1.569 +            n = values[i]
   1.570 +            
   1.571 +            # Each number must be mapped to a value in the range [0, 2].
   1.572 +            if n > 1: n = 2
   1.573 +            elif n < 0: n = 2
   1.574 +            
   1.575 +            substitutions.append(values[i])
   1.576 +            substitutions.append(words[i][n])
   1.577 +        
   1.578 +        return msg % tuple(substitutions)
   1.579 +    
   1.580      def print_log(self):
   1.581      
   1.582          """print_log()
   1.583 @@ -1335,9 +1426,16 @@
   1.584          \rPrint the disc verification log.
   1.585          """
   1.586          
   1.587 +        if hasattr(self, "disc_map") and self.disc_map.has_key(1):
   1.588 +        
   1.589 +            print self.plural(
   1.590 +                "%i mapped %s found.", [len(self.disc_map[1])],
   1.591 +                [("defects", "defect", "defects")]
   1.592 +                )
   1.593 +        
   1.594          if self.verify_log == []:
   1.595          
   1.596 -            print "No problems found."
   1.597 +            print "All objects located."
   1.598              return
   1.599          
   1.600          for line in self.verify_log: