python-adfs

changeset 58:62b3240b117e

Added disc map consistency checks for 800K discs, where values from the supposed map are read and compared to expected values. Changed the logging system to support different levels of message.
author David Boddie <david@boddie.org.uk>
date Mon Jul 21 03:24:59 2003 +0200
parents 6a2474543cf7
children b90e29694f28
files ADFSlib.py
diffstat 1 files changed, 234 insertions(+), 50 deletions(-) [+]
line diff
     1.1 --- a/ADFSlib.py	Sun Jul 20 17:30:08 2003 +0200
     1.2 +++ b/ADFSlib.py	Mon Jul 21 03:24:59 2003 +0200
     1.3 @@ -33,6 +33,11 @@
     1.4  import os, string
     1.5  
     1.6  
     1.7 +INFORM = 0
     1.8 +WARNING = 1
     1.9 +ERROR = 2
    1.10 +
    1.11 +
    1.12  class ADFS_exception(Exception):
    1.13  
    1.14      pass
    1.15 @@ -42,6 +47,10 @@
    1.16  
    1.17      def __init__(self, adf, verify = 0):
    1.18      
    1.19 +        # Log problems if the verify flag is set.
    1.20 +        self.verify = verify
    1.21 +        self.verify_log = []
    1.22 +        
    1.23          # Check the properties using the length of the file
    1.24          adf.seek(0,2)
    1.25          length = adf.tell()
    1.26 @@ -72,19 +81,21 @@
    1.27          
    1.28          elif length == 819200:
    1.29          
    1.30 -            if self.identify_format(adf) == 'D':
    1.31 -                self.ntracks = 80
    1.32 -                self.nsectors = 10
    1.33 -                self.sector_size = 1024
    1.34 -                interleave = 0
    1.35 +            self.ntracks = 80
    1.36 +            self.nsectors = 10
    1.37 +            self.sector_size = 1024
    1.38 +            interleave = 0
    1.39 +            
    1.40 +            format = self.identify_format(adf)
    1.41 +            
    1.42 +            if format == 'D':
    1.43 +            
    1.44                  self.disc_type = 'adD'
    1.45              
    1.46 -            elif self.identify_format(adf) == 'E':
    1.47 -                self.ntracks = 80
    1.48 -                self.nsectors = 10
    1.49 -                self.sector_size = 1024
    1.50 -                interleave = 0
    1.51 +            elif format == 'E':
    1.52 +            
    1.53                  self.disc_type = 'adE'
    1.54 +            
    1.55              else:
    1.56                  raise ADFS_exception, \
    1.57                      'Please supply a .adf, .adl or .adD file.'
    1.58 @@ -112,10 +123,7 @@
    1.59          # Set the default disc name.
    1.60          self.disc_name = 'Untitled'
    1.61          
    1.62 -        # Read the files on the disc, logging problems if the verify flag
    1.63 -        # is set.
    1.64 -        self.verify = verify
    1.65 -        self.verify_log = []
    1.66 +        # Read the files on the disc.
    1.67          
    1.68          if self.disc_type == 'adD':
    1.69          
    1.70 @@ -181,6 +189,97 @@
    1.71      
    1.72      def identify_format(self, adf):
    1.73      
    1.74 +        # Look for a valid disc record when determining whether the disc
    1.75 +        # image represents an 800K D or E format floppy disc. First, the
    1.76 +        # disc image needs to be read.
    1.77 +        
    1.78 +        # Read all the data in the image. This will be overwritten
    1.79 +        # when the image is read properly.
    1.80 +        self.sectors = adf.read()
    1.81 +        
    1.82 +        # This will be done again for E format and later discs.
    1.83 +        
    1.84 +        record = self.read_disc_record(4)
    1.85 +        
    1.86 +        # Define a checklist of criteria to satisfy.
    1.87 +        checklist = \
    1.88 +        {
    1.89 +            "Length field matches image length": 0,
    1.90 +            "Expected sector size (1024 bytes)": 0,
    1.91 +            "Expected density (double)": 0,
    1.92 +            "Root directory at location given": 0
    1.93 +        }
    1.94 +        
    1.95 +        # Check the disc image length.
    1.96 +        
    1.97 +        # Seek to the end of the disc image.
    1.98 +        adf.seek(0, 2)
    1.99 +        
   1.100 +        #print hex(record["disc size"]), hex(adf.tell())
   1.101 +        
   1.102 +        if record["disc size"] == adf.tell():
   1.103 +        
   1.104 +            # The record (if is exists) does not provide a consistent value
   1.105 +            # for the length of the image file.
   1.106 +            checklist["Length field matches image length"] = 1
   1.107 +        
   1.108 +        
   1.109 +        # Check the sector size of the disc.
   1.110 +        
   1.111 +        #print hex(record["sector size"])
   1.112 +        
   1.113 +        if record["sector size"] == 1024:
   1.114 +        
   1.115 +            # These should be equal if the disc record is valid.
   1.116 +            checklist["Expected sector size (1024 bytes)"] = 1
   1.117 +        
   1.118 +        
   1.119 +        # Check the density of the disc.
   1.120 +        
   1.121 +        #print record["density"]
   1.122 +        
   1.123 +        if record["density"] == "double":
   1.124 +        
   1.125 +            # This should be a double density disc if the disc record is valid.
   1.126 +            checklist["Expected density (double)"] = 1
   1.127 +        
   1.128 +        
   1.129 +        # Check the data at the root directory location.
   1.130 +        #print hex(record["root dir"])
   1.131 +        
   1.132 +        adf.seek((record["root dir"] * record["sector size"]) + 1, 0)
   1.133 +        word = adf.read(4)
   1.134 +        
   1.135 +        if word == "Hugo" or word == "Nick":
   1.136 +        
   1.137 +            # A valid directory identifier was found.
   1.138 +            checklist["Root directory at location given"] = 1
   1.139 +        
   1.140 +        
   1.141 +        if self.verify:
   1.142 +        
   1.143 +            self.verify_log.append(
   1.144 +                (INFORM, "Checklist for E format discs:")
   1.145 +                )
   1.146 +            
   1.147 +            for key, value in checklist.items():
   1.148 +            
   1.149 +                self.verify_log.append(
   1.150 +                    (INFORM, "%s: %s" % (key, ["no", "yes"][value]))
   1.151 +                    )
   1.152 +        
   1.153 +        # If all the tests pass then the disc is an E format disc.
   1.154 +        if reduce(lambda a, b: a + b, checklist.values(), 0) == \
   1.155 +            len(checklist.keys()):
   1.156 +        
   1.157 +            if self.verify: self.verify_log.append((INFORM, "E format disc"))
   1.158 +            return "E"
   1.159 +        
   1.160 +        # Since there may not be a valid disc record for earlier discs
   1.161 +        # then anything other than full marks can be interpreted as
   1.162 +        # an indication that the disc is a D format disc. However, we
   1.163 +        # can perform a final test to check this.
   1.164 +        
   1.165          # Simple test for D and E formats: look for Hugo at 0x401 for D format
   1.166          # and Nick at 0x801 for E format
   1.167          adf.seek(0x401)
   1.168 @@ -190,32 +289,76 @@
   1.169          adf.seek(0)
   1.170          
   1.171          if word1 == 'Hugo':
   1.172 +        
   1.173 +            if self.verify:
   1.174 +            
   1.175 +                self.verify_log.append(
   1.176 +                    ( INFORM,
   1.177 +                      "Found directory in typical place for the root " + \
   1.178 +                      "directory of a D format disc." )
   1.179 +                    )
   1.180 +            
   1.181              return 'D'
   1.182 +        
   1.183          elif word2 == 'Nick':
   1.184 +        
   1.185 +            if self.verify:
   1.186 +            
   1.187 +                self.verify_log.append(
   1.188 +                    ( INFORM,
   1.189 +                      "Found directory in typical place for the root " + \
   1.190 +                      "directory of an E format disc." )
   1.191 +                    )
   1.192 +            
   1.193              return 'E'
   1.194 +        
   1.195          else:
   1.196 +        
   1.197 +            if self.verify:
   1.198 +            
   1.199 +                self.verify_log.append(
   1.200 +                    ( ERROR,
   1.201 +                      "Failed to find any information which would help " + \
   1.202 +                      "determine the disc format." )
   1.203 +                    )
   1.204 +            
   1.205              return '?'
   1.206      
   1.207      
   1.208      def read_disc_record(self, offset):
   1.209      
   1.210          # Total sectors per track (sectors * heads)
   1.211 -        sector_size = ord(self.sectors[offset])
   1.212 +        log2_sector_size = ord(self.sectors[offset])
   1.213          # Sectors per track
   1.214          nsectors = ord(self.sectors[offset + 1])
   1.215          # Heads per track
   1.216          heads = ord(self.sectors[offset + 2])
   1.217          
   1.218 -        type = ord(self.sectors[offset+3])
   1.219 -        if type == 1:
   1.220 +        density = ord(self.sectors[offset+3])
   1.221 +        
   1.222 +        if density == 1:
   1.223 +        
   1.224              density = 'single'        # Single density disc
   1.225 -        elif type == 2:
   1.226 +            sector_size = 256
   1.227 +        
   1.228 +        elif density == 2:
   1.229 +        
   1.230              density = 'double'        # Double density disc
   1.231 -        elif type == 3:
   1.232 +            sector_size = 512
   1.233 +        
   1.234 +        elif density == 3:
   1.235 +        
   1.236              density = 'quad'        # Quad density disc
   1.237 +            sector_size = 1024
   1.238 +        
   1.239          else:
   1.240 +        
   1.241              density = 'unknown'
   1.242          
   1.243 +        # Length of ID fields in the disc map
   1.244 +        idlen = self.str2num(1, self.sectors[offset + 4])
   1.245 +        # Number of bytes per map bit.
   1.246 +        bytes_per_bit = 2 ** self.str2num(1, self.sectors[offset + 5])
   1.247          # LowSector
   1.248          # StartUp
   1.249          # LinkBits
   1.250 @@ -225,10 +368,10 @@
   1.251          # RASkew
   1.252          # BootOpt
   1.253          # Zones
   1.254 -        zones = ord(self.sectors[offset + 10])
   1.255 +        zones = ord(self.sectors[offset + 9])
   1.256          # ZoneSpare
   1.257          # RootDir
   1.258 -        root = self.sectors[offset + 12 : offset + 15]
   1.259 +        root = self.str2num(3, self.sectors[offset + 13 : offset + 16]) # was 15
   1.260          # Identify
   1.261          # SequenceSides
   1.262          # DoubleStep
   1.263 @@ -239,9 +382,11 @@
   1.264          # DiscName
   1.265          disc_name = string.strip(self.sectors[offset + 22 : offset + 32])
   1.266          
   1.267 -        return {'sectors': nsectors, 'heads': heads, 'density': density,
   1.268 +        return {'sectors': nsectors, 'log2 sector size': log2_sector_size,
   1.269 +            'sector size': 2**log2_sector_size, 'heads': heads,
   1.270 +            'density': density,
   1.271              'disc size': disc_size, 'disc ID': disc_id,
   1.272 -            'disc name': disc_name, 'zones': zones, 'root': root}
   1.273 +            'disc name': disc_name, 'zones': zones, 'root dir': root }
   1.274      
   1.275      
   1.276      def read_disc_info(self):
   1.277 @@ -252,6 +397,9 @@
   1.278          if self.disc_type == 'adE':
   1.279          
   1.280              self.record = self.read_disc_record(4)
   1.281 +            
   1.282 +            self.sector_size = self.record["sector size"]
   1.283 +            
   1.284              self.map_header = 0
   1.285              self.map_start, self.map_end = 0x40, 0x400
   1.286              self.free_space = self.read_free_space(
   1.287 @@ -266,6 +414,9 @@
   1.288          if self.disc_type == 'adEbig':
   1.289          
   1.290              self.record = self.read_disc_record(0xc6804)
   1.291 +            
   1.292 +            self.sector_size = self.record["sector size"]
   1.293 +            
   1.294              self.map_header = 0xc6800
   1.295              self.map_start, self.map_end = 0xc6840, 0xc7800
   1.296              self.free_space = self.read_free_space(
   1.297 @@ -548,6 +699,8 @@
   1.298      
   1.299          t = ""
   1.300          
   1.301 +        f.seek(0, 0)
   1.302 +        
   1.303          if inter==0:
   1.304              try:
   1.305                  for i in range(0, self.ntracks):
   1.306 @@ -684,7 +837,9 @@
   1.307          
   1.308              if self.verify:
   1.309              
   1.310 -                self.verify_log.append('Not a directory: %x' % head)
   1.311 +                self.verify_log.append(
   1.312 +                    (WARNING, 'Not a directory: %x' % head)
   1.313 +                    )
   1.314              
   1.315              return "", []
   1.316          
   1.317 @@ -787,8 +942,9 @@
   1.318              if self.verify:
   1.319              
   1.320                  self.verify_log.append(
   1.321 -                    'Discrepancy in directory structure: [%x, %x] ' % \
   1.322 -                        ( head, tail )
   1.323 +                    ( WARNING,
   1.324 +                      'Discrepancy in directory structure: [%x, %x] ' % \
   1.325 +                      ( head, tail ) )
   1.326                      )
   1.327                          
   1.328              return '', files
   1.329 @@ -838,8 +994,9 @@
   1.330              if self.verify:
   1.331              
   1.332                  self.verify_log.append(
   1.333 -                    'Broken directory: %s at [%x, %x]' % \
   1.334 -                        (dir_title, head, tail)
   1.335 +                    ( WARNING,
   1.336 +                      'Broken directory: %s at [%x, %x]' % \
   1.337 +                      (dir_title, head, tail) )
   1.338                      )
   1.339              
   1.340              return dir_name, files
   1.341 @@ -899,8 +1056,10 @@
   1.342          
   1.343              if self.verify:
   1.344              
   1.345 -                self.verify_log.append('Not a directory: %s' % hex(head))
   1.346 -                print 'Not a directory: %s' % hex(head)
   1.347 +                self.verify_log.append(
   1.348 +                    (WARNING, 'Not a directory: %s' % hex(head))
   1.349 +                    )
   1.350 +                #print 'Not a directory: %s' % hex(head)
   1.351              
   1.352              return '', []
   1.353          
   1.354 @@ -942,32 +1101,37 @@
   1.355                      if self.verify:
   1.356                      
   1.357                          self.verify_log.append(
   1.358 -                            "Couldn't find directory: %s" % name
   1.359 +                            (WARNING, "Couldn't find directory: %s" % name)
   1.360                              )
   1.361                          self.verify_log.append(
   1.362 -                            "    at: %x" % (head+p+22)
   1.363 +                            (WARNING, "    at: %x" % (head+p+22))
   1.364                              )
   1.365 +                        self.verify_log.append( (
   1.366 +                            WARNING, "    file details: %x" % \
   1.367 +                            self.str2num(3, self.sectors[head+p+22:head+p+25])
   1.368 +                            ) )
   1.369                          self.verify_log.append(
   1.370 -                            "    file details: %x" % \
   1.371 -                            self.str2num(3, self.sectors[head+p+22:head+p+25])
   1.372 +                            (WARNING, "    atts: %x" % newdiratts)
   1.373                              )
   1.374 -                        self.verify_log.append("    atts: %x" % newdiratts)
   1.375                  
   1.376                  elif length != 0:
   1.377                  
   1.378                      if self.verify:
   1.379                      
   1.380                          self.verify_log.append(
   1.381 -                            "Couldn't find file: %s" % name
   1.382 +                            (WARNING, "Couldn't find file: %s" % name)
   1.383                              )
   1.384                          self.verify_log.append(
   1.385 -                            "    at: %x" % (head+p+22)
   1.386 +                            (WARNING, "    at: %x" % (head+p+22))
   1.387                              )
   1.388 -                        self.verify_log.append(
   1.389 +                        self.verify_log.append( (
   1.390 +                            WARNING,
   1.391                              "    file details: %x" % \
   1.392                              self.str2num(3, self.sectors[head+p+22:head+p+25])
   1.393 +                            ) )
   1.394 +                        self.verify_log.append(
   1.395 +                            (WARNING, "    atts: %x" % newdiratts)
   1.396                              )
   1.397 -                        self.verify_log.append("    atts: %x" % newdiratts)
   1.398                  
   1.399                  else:
   1.400                  
   1.401 @@ -1042,8 +1206,9 @@
   1.402              if self.verify:
   1.403              
   1.404                  self.verify_log.append(
   1.405 -                    'Discrepancy in directory structure: [%x, %x]' % \
   1.406 -                        ( head, tail )
   1.407 +                    ( WARNING,
   1.408 +                      'Discrepancy in directory structure: [%x, %x]' % \
   1.409 +                      ( head, tail ) )
   1.410                      )
   1.411              
   1.412              return '', files
   1.413 @@ -1078,8 +1243,9 @@
   1.414              if self.verify:
   1.415              
   1.416                  self.verify_log.append(
   1.417 -                    'Broken directory: %s at [%x, %x]' % \
   1.418 -                        (dir_title, head, tail)
   1.419 +                    ( WARNING,
   1.420 +                      'Broken directory: %s at [%x, %x]' % \
   1.421 +                      (dir_title, head, tail) )
   1.422                      )
   1.423              
   1.424              return dir_name, files
   1.425 @@ -1106,6 +1272,10 @@
   1.426          
   1.427              files = self.files
   1.428          
   1.429 +        if files == []:
   1.430 +        
   1.431 +            print path, "(empty)"
   1.432 +        
   1.433          for i in files:
   1.434      
   1.435              name = i[0]
   1.436 @@ -1132,6 +1302,7 @@
   1.437                          )
   1.438              
   1.439              else:
   1.440 +            
   1.441                  self.print_catalogue(i[1], path + "." + name, filetypes)
   1.442      
   1.443      
   1.444 @@ -1154,7 +1325,8 @@
   1.445          if self.verify and old_name != name:
   1.446          
   1.447              self.verify_log.append(
   1.448 -                "Changed %s to %s" % (old_name, name)
   1.449 +                ( WARNING,
   1.450 +                  "Changed %s to %s" % (old_name, name) )
   1.451                  )
   1.452          
   1.453          return name
   1.454 @@ -1419,11 +1591,12 @@
   1.455          
   1.456          return msg % tuple(substitutions)
   1.457      
   1.458 -    def print_log(self):
   1.459 +    def print_log(self, verbose = 0):
   1.460      
   1.461 -        """print_log()
   1.462 +        """print_log(self, verbose = 0)
   1.463          \r
   1.464 -        \rPrint the disc verification log.
   1.465 +        \rPrint the disc verification log. Any purely informational messages
   1.466 +        \rare only printed is verbose is set to 1.
   1.467          """
   1.468          
   1.469          if hasattr(self, "disc_map") and self.disc_map.has_key(1):
   1.470 @@ -1433,12 +1606,23 @@
   1.471                  [("defects", "defect", "defects")]
   1.472                  )
   1.473          
   1.474 -        if self.verify_log == []:
   1.475 +        # Count the information, warning and error messages in the log.
   1.476 +        informs = reduce(lambda a, b: a + (b[0] == INFORM), self.verify_log, 0)
   1.477 +        warnings = reduce(
   1.478 +            lambda a, b: a + (b[0] == WARNING), self.verify_log, 0
   1.479 +            )
   1.480 +        errors = reduce(lambda a, b: a + (b[0] == ERROR), self.verify_log, 0)
   1.481 +        
   1.482 +        if (warnings + errors) == 0:
   1.483          
   1.484              print "All objects located."
   1.485 -            return
   1.486 +            if not verbose: return
   1.487          
   1.488 -        for line in self.verify_log:
   1.489 +        if self.verify_log != []:
   1.490 +        
   1.491 +            print
   1.492 +        
   1.493 +        for msgtype, line in self.verify_log:
   1.494          
   1.495              print line
   1.496