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
