python-adfs
changeset 64:2e2bd0a863db
Tidied up the API and added some classes for files and directories.
| author | David Boddie <david@boddie.org.uk> |
|---|---|
| date | Sun Jun 08 20:17:05 2008 +0200 |
| parents | 1a92043cd7bb |
| children | 828d28f47e66 |
| files | ADF2INF.py ADFSlib.py |
| diffstat | 2 files changed, 264 insertions(+), 173 deletions(-) [+] |
line diff
1.1 --- a/ADF2INF.py Sun Jun 08 15:22:16 2008 +0200 1.2 +++ b/ADF2INF.py Sun Jun 08 20:17:05 2008 +0200 1.3 @@ -3,13 +3,13 @@ 1.4 Name : ADF2INF.py 1.5 Author : David Boddie 1.6 Created : Wed 18th October 2000 1.7 -Updated : Mon 21st July 2003 1.8 +Updated : Sun 8th June 2008 1.9 Purpose : Convert ADFS disc images (ADF) to INF files 1.10 WWW : http://david.boddie.org.uk/Projects/Python/ADFSlib 1.11 1.12 License: 1.13 1.14 -Copyright (c) 2000-2003, David Boddie 1.15 +Copyright (c) 2000-2008, David Boddie 1.16 1.17 This software is free software; you can redistribute it and/or 1.18 modify it under the terms of the GNU General Public License as 1.19 @@ -172,9 +172,13 @@ 1.20 print 'name into which the contents of the disc will be written.' 1.21 print 1.22 print "The -t flag causes the load and execution addresses of files to be" 1.23 - print "interpreted as filetype information for files created on RISC OS." 1.24 + print "interpreted as file type information for files created on RISC OS." 1.25 print "A separator used to append a suffix onto the file is optional and" 1.26 - print "defaults to the standard period character. e.g. myfile.fff" 1.27 + print "defaults to the standard period character; e.g. myfile.fff" 1.28 + print 1.29 + print "The -s flag is used to specify the character which joins the file" 1.30 + print "name to the file type. This can only be specified when extracting" 1.31 + print "files from a disc image." 1.32 print 1.33 print "The -v flag causes the disc image to be checked for simple defects and" 1.34 print "determines whether there are files and directories which cannot be" 1.35 @@ -285,9 +289,7 @@ 1.36 print 'Contents of', adfsdisc.disc_name,':' 1.37 print 1.38 1.39 - adfsdisc.print_catalogue( 1.40 - adfsdisc.files, adfsdisc.root_name, filetypes, separator 1.41 - ) 1.42 + adfsdisc.print_catalogue(adfsdisc.files, adfsdisc.root_name, filetypes) 1.43 1.44 print 1.45
2.1 --- a/ADFSlib.py Sun Jun 08 15:22:16 2008 +0200 2.2 +++ b/ADFSlib.py Sun Jun 08 20:17:05 2008 +0200 2.3 @@ -4,7 +4,7 @@ 2.4 2.5 A library for reading ADFS disc images. 2.6 2.7 -Copyright (c) 2003-2005, David Boddie 2.8 +Copyright (c) 2003-2008, David Boddie 2.9 2.10 This software is free software; you can redistribute it and/or 2.11 modify it under the terms of the GNU General Public License as 2.12 @@ -24,8 +24,8 @@ 2.13 """ 2.14 2.15 __author__ = "David Boddie <david@boddie.org.uk>" 2.16 -__date__ = "Sun 10th April 2005" 2.17 -__version__ = "0.22" 2.18 +__date__ = "Sun 8th June 2008" 2.19 +__version__ = "0.30" 2.20 2.21 2.22 import os, string, struct 2.23 @@ -41,8 +41,64 @@ 2.24 pass 2.25 2.26 2.27 +class ADFSdirectory: 2.28 + 2.29 + """ADFSdirectory 2.30 + 2.31 + directory = ADFSdirectory(name, files) 2.32 + """ 2.33 + 2.34 + def __init__(self, name, files): 2.35 + 2.36 + self.name = name 2.37 + self.files = files 2.38 + 2.39 + def __repr__(self): 2.40 + 2.41 + return '<%s instance, "%s", at %x>' % (self.__class__, self.name, id(self)) 2.42 + 2.43 + 2.44 +class ADFSfile: 2.45 + 2.46 + """ADFSfile 2.47 + 2.48 + file = ADFSfile(name, data, load_address, execution_address, length) 2.49 + """ 2.50 + 2.51 + def __init__(self, name, data, load_address, execution_address, length): 2.52 + 2.53 + self.name = name 2.54 + self.data = data 2.55 + self.load_address = load_address 2.56 + self.execution_address = execution_address 2.57 + self.length = length 2.58 + 2.59 + def __repr__(self): 2.60 + 2.61 + return '<%s instance, "%s", at %x>' % (self.__class__, self.name, id(self)) 2.62 + 2.63 + def filetype(self): 2.64 + 2.65 + return "%x" % ((self.load_address >> 8) & 0xfff) 2.66 + 2.67 + 2.68 class ADFSdisc: 2.69 2.70 + """ADFSdisc 2.71 + 2.72 + disc = ADFSdisc(file_handle, verify = 0) 2.73 + 2.74 + Represents an ADFS disc image stored in the file with the specified file 2.75 + handle. The image is not verified by default; pass True or another 2.76 + non-False value to request automatic verification of the disc format. 2.77 + 2.78 + If the disc image specified cannot be read successfully, an ADFS_exception 2.79 + is raised. 2.80 + 2.81 + Once an ADFSdisc instance has been created, it can be used to access the 2.82 + contents of the disc image. The files attribute contains 2.83 + """ 2.84 + 2.85 def __init__(self, adf, verify = 0): 2.86 2.87 # Log problems if the verify flag is set. 2.88 @@ -86,7 +142,7 @@ 2.89 self.sector_size = 1024 2.90 interleave = 0 2.91 2.92 - format = self.identify_format(adf) 2.93 + format = self._identify_format(adf) 2.94 2.95 if format == 'D': 2.96 2.97 @@ -112,7 +168,7 @@ 2.98 raise ADFS_exception, 'Please supply a .adf, .adl or .adD file.' 2.99 2.100 # Read tracks 2.101 - self.sectors = self.read_tracks(adf, interleave) 2.102 + self.sectors = self._read_tracks(adf, interleave) 2.103 2.104 # Close the ADF file 2.105 adf.close() 2.106 @@ -129,60 +185,60 @@ 2.107 2.108 # Find the root directory name and all the files and directories 2.109 # contained within it 2.110 - self.root_name, self.files = self.read_old_catalogue(0x400) 2.111 + self.root_name, self.files = self._read_old_catalogue(0x400) 2.112 2.113 elif self.disc_type == 'adE': 2.114 2.115 # Read the disc name and map 2.116 - self.disc_name = self.safe(self.read_disc_info(), with_space = 1) 2.117 + self.disc_name = self._safe(self._read_disc_info(), with_space = 1) 2.118 2.119 # Find the root directory name and all the files and directories 2.120 # contained within it 2.121 - self.root_name, self.files = self.read_new_catalogue(0x800) 2.122 + self.root_name, self.files = self._read_new_catalogue(0x800) 2.123 2.124 elif self.disc_type == 'adEbig': 2.125 2.126 # Read the disc name and map 2.127 - self.disc_name = self.safe(self.read_disc_info(), with_space = 1) 2.128 + self.disc_name = self._safe(self._read_disc_info(), with_space = 1) 2.129 2.130 # Find the root directory name and all the files and directories 2.131 # contained within it 2.132 - self.root_name, self.files = self.read_new_catalogue(0xc8800) 2.133 + self.root_name, self.files = self._read_new_catalogue(0xc8800) 2.134 2.135 else: 2.136 2.137 # Find the root directory name and all the files and directories 2.138 # contained within it 2.139 - self.root_name, self.files = self.read_old_catalogue(2*self.sector_size) 2.140 + self.root_name, self.files = self._read_old_catalogue(2*self.sector_size) 2.141 2.142 2.143 # Little endian reading 2.144 2.145 - def read_signed_word(self, s): 2.146 + def _read_signed_word(self, s): 2.147 2.148 return struct.unpack("<i", s)[0] 2.149 2.150 - def read_unsigned_word(self, s): 2.151 + def _read_unsigned_word(self, s): 2.152 2.153 return struct.unpack("<I", s)[0] 2.154 2.155 - def read_signed_byte(self, s): 2.156 + def _read_signed_byte(self, s): 2.157 2.158 return struct.unpack("<b", s)[0] 2.159 2.160 - def read_unsigned_byte(self, s): 2.161 + def _read_unsigned_byte(self, s): 2.162 2.163 return struct.unpack("<B", s)[0] 2.164 2.165 - def read_unsigned_half_word(self, s): 2.166 + def _read_unsigned_half_word(self, s): 2.167 2.168 return struct.unpack("<H", s)[0] 2.169 2.170 - def read_signed_half_word(self, s): 2.171 + def _read_signed_half_word(self, s): 2.172 2.173 return struct.unpack("<h", s)[0] 2.174 2.175 - def str2num(self, size, s): 2.176 + def _str2num(self, size, s): 2.177 2.178 i = 0 2.179 n = 0 2.180 @@ -194,7 +250,7 @@ 2.181 return n 2.182 2.183 2.184 - def binary(self, size, n): 2.185 + def _binary(self, size, n): 2.186 2.187 new = "" 2.188 while (n != 0) & (size > 0): 2.189 @@ -213,7 +269,7 @@ 2.190 return new 2.191 2.192 2.193 - def identify_format(self, adf): 2.194 + def _identify_format(self, adf): 2.195 2.196 # Look for a valid disc record when determining whether the disc 2.197 # image represents an 800K D or E format floppy disc. First, the 2.198 @@ -225,7 +281,7 @@ 2.199 2.200 # This will be done again for E format and later discs. 2.201 2.202 - record = self.read_disc_record(4) 2.203 + record = self._read_disc_record(4) 2.204 2.205 # Define a checklist of criteria to satisfy. 2.206 checklist = \ 2.207 @@ -351,7 +407,7 @@ 2.208 return '?' 2.209 2.210 2.211 - def read_disc_record(self, offset): 2.212 + def _read_disc_record(self, offset): 2.213 2.214 # Total sectors per track (sectors * heads) 2.215 log2_sector_size = ord(self.sectors[offset]) 2.216 @@ -382,14 +438,14 @@ 2.217 density = 'unknown' 2.218 2.219 # Length of ID fields in the disc map 2.220 - idlen = self.read_unsigned_byte(self.sectors[offset + 4]) 2.221 + idlen = self._read_unsigned_byte(self.sectors[offset + 4]) 2.222 # Number of bytes per map bit. 2.223 - bytes_per_bit = 2 ** self.read_unsigned_byte(self.sectors[offset + 5]) 2.224 + bytes_per_bit = 2 ** self._read_unsigned_byte(self.sectors[offset + 5]) 2.225 # LowSector 2.226 # StartUp 2.227 # LinkBits 2.228 # BitSize (size of ID field?) 2.229 - bit_size = self.read_unsigned_byte(self.sectors[offset + 6 : offset + 7]) 2.230 + bit_size = self._read_unsigned_byte(self.sectors[offset + 6 : offset + 7]) 2.231 #print "Bit size: %s" % hex(bit_size) 2.232 # RASkew 2.233 # BootOpt 2.234 @@ -397,14 +453,14 @@ 2.235 zones = ord(self.sectors[offset + 9]) 2.236 # ZoneSpare 2.237 # RootDir 2.238 - root = self.str2num(3, self.sectors[offset + 13 : offset + 16]) # was 15 2.239 + root = self._str2num(3, self.sectors[offset + 13 : offset + 16]) # was 15 2.240 # Identify 2.241 # SequenceSides 2.242 # DoubleStep 2.243 # DiscSize 2.244 - disc_size = self.read_unsigned_word(self.sectors[offset + 16 : offset + 20]) 2.245 + disc_size = self._read_unsigned_word(self.sectors[offset + 16 : offset + 20]) 2.246 # DiscId 2.247 - disc_id = self.read_unsigned_half_word(self.sectors[offset + 20 : offset + 22]) 2.248 + disc_id = self._read_unsigned_half_word(self.sectors[offset + 20 : offset + 22]) 2.249 # DiscName 2.250 disc_name = string.strip(self.sectors[offset + 22 : offset + 32]) 2.251 2.252 @@ -415,23 +471,23 @@ 2.253 'disc name': disc_name, 'zones': zones, 'root dir': root } 2.254 2.255 2.256 - def read_disc_info(self): 2.257 + def _read_disc_info(self): 2.258 2.259 checksum = ord(self.sectors[0]) 2.260 - first_free = self.read_unsigned_half_word(self.sectors[1:3]) 2.261 + first_free = self._read_unsigned_half_word(self.sectors[1:3]) 2.262 2.263 if self.disc_type == 'adE': 2.264 2.265 - self.record = self.read_disc_record(4) 2.266 + self.record = self._read_disc_record(4) 2.267 2.268 self.sector_size = self.record["sector size"] 2.269 2.270 self.map_header = 0 2.271 self.map_start, self.map_end = 0x40, 0x400 2.272 - self.free_space = self.read_free_space( 2.273 + self.free_space = self._read_free_space( 2.274 self.map_header, self.map_start, self.map_end 2.275 ) 2.276 - self.disc_map = self.read_new_map( 2.277 + self.disc_map = self._read_new_map( 2.278 self.map_header, self.map_start, self.map_end 2.279 ) 2.280 2.281 @@ -439,16 +495,16 @@ 2.282 2.283 if self.disc_type == 'adEbig': 2.284 2.285 - self.record = self.read_disc_record(0xc6804) 2.286 + self.record = self._read_disc_record(0xc6804) 2.287 2.288 self.sector_size = self.record["sector size"] 2.289 2.290 self.map_header = 0xc6800 2.291 self.map_start, self.map_end = 0xc6840, 0xc7800 2.292 - self.free_space = self.read_free_space( 2.293 + self.free_space = self._read_free_space( 2.294 self.map_header, self.map_start, self.map_end 2.295 ) 2.296 - self.disc_map = self.read_new_map( 2.297 + self.disc_map = self._read_new_map( 2.298 self.map_header, self.map_start, self.map_end 2.299 ) 2.300 2.301 @@ -460,7 +516,7 @@ 2.302 # zone_size = 819200 / record['zones'] 2.303 # ids_per_zone = zone_size / 2.304 2.305 - def read_new_map(self, header, begin, end): 2.306 + def _read_new_map(self, header, begin, end): 2.307 2.308 disc_map = {} 2.309 2.310 @@ -509,7 +565,7 @@ 2.311 2.312 # If there is enough space left in this zone to allow 2.313 # further fragments then read the next two bytes. 2.314 - value = self.read_unsigned_half_word(self.sectors[a:a+2]) 2.315 + value = self._read_unsigned_half_word(self.sectors[a:a+2]) 2.316 2.317 entry = value & 0x7fff 2.318 2.319 @@ -548,10 +604,10 @@ 2.320 # and implicitly finish reading this fragment 2.321 # (current_piece remains None). 2.322 2.323 - start_addr = self.find_address_from_map( 2.324 + start_addr = self._find_address_from_map( 2.325 a, begin, entry 2.326 ) 2.327 - end_addr = self.find_address_from_map( 2.328 + end_addr = self._find_address_from_map( 2.329 next, begin, entry 2.330 ) 2.331 2.332 @@ -583,10 +639,10 @@ 2.333 2.334 # For relevant entries add the block to the list of 2.335 # pieces found. 2.336 - start_addr = self.find_address_from_map( 2.337 + start_addr = self._find_address_from_map( 2.338 current_start, begin, current_piece 2.339 ) 2.340 - end_addr = self.find_address_from_map( 2.341 + end_addr = self._find_address_from_map( 2.342 next, begin, current_piece 2.343 ) 2.344 2.345 @@ -617,7 +673,7 @@ 2.346 2.347 return disc_map 2.348 2.349 - def read_free_space(self, header, begin, end): 2.350 + def _read_free_space(self, header, begin, end): 2.351 2.352 free_space = [] 2.353 2.354 @@ -632,7 +688,7 @@ 2.355 2.356 # Start by reading the offset in bits from the start of the header 2.357 # of the first item of free space in the map. 2.358 - offset = self.read_unsigned_half_word(self.sectors[a:a+2]) 2.359 + offset = self._read_unsigned_half_word(self.sectors[a:a+2]) 2.360 2.361 # The top bit is apparently always set, so mask it off and convert 2.362 # the result into bytes. * Not sure if this is the case for 2.363 @@ -652,7 +708,7 @@ 2.364 while a < next_zone: 2.365 2.366 # Read the offset to the next free fragment in this zone. 2.367 - offset = self.read_unsigned_half_word(self.sectors[a:a+2]) 2.368 + offset = self._read_unsigned_half_word(self.sectors[a:a+2]) 2.369 2.370 # Convert this to a byte offset. 2.371 next = ((offset & 0x7fff) >> 3) 2.372 @@ -664,7 +720,7 @@ 2.373 2.374 c = b + 1 2.375 2.376 - value = self.read_unsigned_byte(self.sectors[b]) 2.377 + value = self._read_unsigned_byte(self.sectors[b]) 2.378 2.379 if (value & 0x80) != 0: 2.380 2.381 @@ -692,7 +748,7 @@ 2.382 # Return the free space list. 2.383 return free_space 2.384 2.385 - def find_address_from_map(self, addr, begin, entry): 2.386 + def _find_address_from_map(self, addr, begin, entry): 2.387 2.388 if self.disc_type == 'adE': 2.389 2.390 @@ -718,7 +774,7 @@ 2.391 2.392 return address 2.393 2.394 - def find_in_new_map(self, file_no): 2.395 + def _find_in_new_map(self, file_no): 2.396 2.397 try: 2.398 2.399 @@ -728,7 +784,7 @@ 2.400 2.401 return [] 2.402 2.403 - def read_tracks(self, f, inter): 2.404 + def _read_tracks(self, f, inter): 2.405 2.406 t = "" 2.407 2.408 @@ -773,7 +829,7 @@ 2.409 return t 2.410 2.411 2.412 - def read_sectors(self, adf): 2.413 + def _read_sectors(self, adf): 2.414 2.415 s = [] 2.416 try: 2.417 @@ -796,7 +852,7 @@ 2.418 return s 2.419 2.420 2.421 - def safe(self, s, with_space = 0): 2.422 + def _safe(self, s, with_space = 0): 2.423 2.424 new = "" 2.425 if with_space == 1: 2.426 @@ -819,7 +875,7 @@ 2.427 return new 2.428 2.429 2.430 - def read_freespace(self): 2.431 + def _read_freespace(self): 2.432 2.433 # Currently unused 2.434 2.435 @@ -829,36 +885,36 @@ 2.436 p = 0 2.437 while self.sectors[base+p] != 0: 2.438 2.439 - free.append(self.str2num(3, self.sectors[base+p:base_p+3])) 2.440 + free.append(self._str2num(3, self.sectors[base+p:base+p+3])) 2.441 2.442 name = self.sectors[self.sector_size-9:self.sector_size-4] 2.443 2.444 - disc_size = self.str2num( 2.445 + disc_size = self._str2num( 2.446 3, self.sectors[self.sector_size-4:self.sector_size-1] 2.447 ) 2.448 2.449 - checksum0 = self.read_unsigned_byte(self.sectors[self.sector_size-1]) 2.450 + checksum0 = self._read_unsigned_byte(self.sectors[self.sector_size-1]) 2.451 2.452 base = self.sector_size 2.453 2.454 p = 0 2.455 while self.sectors[base+p] != 0: 2.456 2.457 - free.append(self.str2num(3, self.sectors[base+p:base_p+3])) 2.458 + free.append(self._str2num(3, self.sectors[base+p:base+p+3])) 2.459 2.460 name = name + \ 2.461 self.sectors[base+self.sector_size-10:base+self.sector_size-5] 2.462 2.463 - disc_id = self.str2num( 2.464 + disc_id = self._str2num( 2.465 2, self.sectors[base+self.sector_size-5:base+self.sector_size-3] 2.466 ) 2.467 2.468 - boot = self.read_unsigned_byte(self.sectors[base+self.sector_size-3]) 2.469 + boot = self._read_unsigned_byte(self.sectors[base+self.sector_size-3]) 2.470 2.471 - checksum1 = self.read_unsigned_byte(self.sectors[base+self.sector_size-1]) 2.472 + checksum1 = self._read_unsigned_byte(self.sectors[base+self.sector_size-1]) 2.473 2.474 2.475 - def read_old_catalogue(self, base): 2.476 + def _read_old_catalogue(self, base): 2.477 2.478 head = base 2.479 # base = sector_size*2 2.480 @@ -890,28 +946,28 @@ 2.481 top_set = counter 2.482 counter = counter + 1 2.483 2.484 - name = self.safe(self.sectors[head+p:head+p+10]) 2.485 + name = self._safe(self.sectors[head+p:head+p+10]) 2.486 2.487 - load = self.read_unsigned_word(self.sectors[head+p+10:head+p+14]) 2.488 - exe = self.read_unsigned_word(self.sectors[head+p+14:head+p+18]) 2.489 - length = self.read_unsigned_word(self.sectors[head+p+18:head+p+22]) 2.490 + load = self._read_unsigned_word(self.sectors[head+p+10:head+p+14]) 2.491 + exe = self._read_unsigned_word(self.sectors[head+p+14:head+p+18]) 2.492 + length = self._read_unsigned_word(self.sectors[head+p+18:head+p+22]) 2.493 2.494 if self.disc_type == 'adD': 2.495 - inddiscadd = 256 * self.str2num( 2.496 + inddiscadd = 256 * self._str2num( 2.497 3, self.sectors[head+p+22:head+p+25] 2.498 ) 2.499 else: 2.500 - inddiscadd = self.sector_size * self.str2num( 2.501 + inddiscadd = self.sector_size * self._str2num( 2.502 3, self.sectors[head+p+22:head+p+25] 2.503 ) 2.504 2.505 - olddirobseq = self.read_unsigned_byte(self.sectors[head+p+25]) 2.506 + olddirobseq = self._read_unsigned_byte(self.sectors[head+p+25]) 2.507 2.508 #print string.expandtabs( 2.509 # "%s\t%s\t%s\t%s" % ( 2.510 - # name, "("+self.binary(8, olddirobseq)+")", 2.511 - # "("+self.binary(8, load)+")", 2.512 - # "("+self.binary(8, exe)+")" 2.513 + # name, "("+self._binary(8, olddirobseq)+")", 2.514 + # "("+self._binary(8, load)+")", 2.515 + # "("+self._binary(8, exe)+")" 2.516 # ) ) 2.517 #print string.expandtabs( 2.518 # "%s\t%02x\t%08x\t%08x" % ( 2.519 @@ -926,17 +982,15 @@ 2.520 2.521 # A directory has been found. 2.522 lower_dir_name, lower_files = \ 2.523 - self.read_old_catalogue(inddiscadd) 2.524 + self._read_old_catalogue(inddiscadd) 2.525 2.526 - files.append([name, lower_files]) 2.527 + files.append(ADFSdirectory(name, lower_files)) 2.528 2.529 else: 2.530 2.531 # A file has been found. 2.532 - files.append( 2.533 - [ name, self.sectors[inddiscadd:inddiscadd+length], 2.534 - load, exe, length ] 2.535 - ) 2.536 + data = self.sectors[inddiscadd:inddiscadd+length] 2.537 + files.append(ADFSfile(name, data, load, exe, length)) 2.538 2.539 else: 2.540 2.541 @@ -947,17 +1001,15 @@ 2.542 2.543 # A directory has been found. 2.544 lower_dir_name, lower_files = \ 2.545 - self.read_old_catalogue(inddiscadd) 2.546 + self._read_old_catalogue(inddiscadd) 2.547 2.548 - files.append([name, lower_files]) 2.549 + files.append(ADFSdirectory(name, lower_files)) 2.550 2.551 else: 2.552 2.553 # A file has been found. 2.554 - files.append( 2.555 - [ name, self.sectors[inddiscadd:inddiscadd+length], 2.556 - load, exe, length ] 2.557 - ) 2.558 + data = self.sectors[inddiscadd:inddiscadd+length] 2.559 + files.append(ADFSfile(name, data, load, exe, length)) 2.560 2.561 p = p + 26 2.562 2.563 @@ -985,11 +1037,11 @@ 2.564 # Read the directory name, its parent and any title given. 2.565 if self.disc_type == 'adD': 2.566 2.567 - dir_name = self.safe( 2.568 + dir_name = self._safe( 2.569 self.sectors[tail+self.sector_size-16:tail+self.sector_size-6] 2.570 ) 2.571 2.572 - parent = 256*self.str2num( 2.573 + parent = 256*self._str2num( 2.574 3, 2.575 self.sectors[tail+self.sector_size-38:tail+self.sector_size-35] 2.576 ) 2.577 @@ -998,16 +1050,16 @@ 2.578 self.sectors[tail+self.sector_size-35:tail+self.sector_size-16] 2.579 else: 2.580 2.581 - dir_name = self.safe( 2.582 + dir_name = self._safe( 2.583 self.sectors[tail+self.sector_size-52:tail+self.sector_size-42] 2.584 ) 2.585 2.586 - parent = self.sector_size*self.str2num( 2.587 + parent = self.sector_size*self._str2num( 2.588 3, 2.589 self.sectors[tail+self.sector_size-42:tail+self.sector_size-39] 2.590 ) 2.591 2.592 - dir_title = self.safe( 2.593 + dir_title = self._safe( 2.594 self.sectors[tail+self.sector_size-39:tail+self.sector_size-20] 2.595 ) 2.596 2.597 @@ -1016,7 +1068,7 @@ 2.598 # Use the directory title as the disc name. 2.599 2.600 # Note that the title may contain spaces. 2.601 - self.disc_name = self.safe(dir_title, with_space = 1) 2.602 + self.disc_name = self._safe(dir_title, with_space = 1) 2.603 2.604 #print "Directory title", dir_title 2.605 #print "Directory name ", dir_name 2.606 @@ -1037,11 +1089,11 @@ 2.607 return dir_name, files 2.608 2.609 2.610 - def read_new_address(self, s): 2.611 + def _read_new_address(self, s): 2.612 2.613 # From the three character string passed, determine the address on the 2.614 # disc. 2.615 - value = self.str2num(3, s) 2.616 + value = self._str2num(3, s) 2.617 2.618 # This is a SIN (System Internal Number) 2.619 # The bottom 8 bits are the sector offset + 1 2.620 @@ -1059,8 +1111,8 @@ 2.621 2.622 # The pieces of the object are returned as a list of pairs of 2.623 # addresses. 2.624 - #pieces = self.find_in_new_map(self.map_start, self.map_end, file_no) 2.625 - pieces = self.find_in_new_map(file_no) 2.626 + #pieces = self._find_in_new_map(self.map_start, self.map_end, file_no) 2.627 + pieces = self._find_in_new_map(file_no) 2.628 2.629 #print map(lambda x: map(hex, x), pieces) 2.630 2.631 @@ -1076,7 +1128,7 @@ 2.632 return pieces 2.633 2.634 2.635 - def read_new_catalogue(self, base): 2.636 + def _read_new_catalogue(self, base): 2.637 2.638 head = base 2.639 p = 0 2.640 @@ -1110,22 +1162,22 @@ 2.641 top_set = counter 2.642 counter = counter + 1 2.643 2.644 - name = self.safe(self.sectors[head+p:head+p+10]) 2.645 + name = self._safe(self.sectors[head+p:head+p+10]) 2.646 2.647 #print hex(head+p), name 2.648 2.649 - load = self.read_unsigned_word(self.sectors[head+p+10:head+p+14]) 2.650 - exe = self.read_unsigned_word(self.sectors[head+p+14:head+p+18]) 2.651 - length = self.read_unsigned_word(self.sectors[head+p+18:head+p+22]) 2.652 + load = self._read_unsigned_word(self.sectors[head+p+10:head+p+14]) 2.653 + exe = self._read_unsigned_word(self.sectors[head+p+14:head+p+18]) 2.654 + length = self._read_unsigned_word(self.sectors[head+p+18:head+p+22]) 2.655 2.656 #print hex(ord(self.sectors[head+p+22])), \ 2.657 # hex(ord(self.sectors[head+p+23])), \ 2.658 # hex(ord(self.sectors[head+p+24])) 2.659 2.660 - inddiscadd = self.read_new_address( 2.661 + inddiscadd = self._read_new_address( 2.662 self.sectors[head+p+22:head+p+25] 2.663 ) 2.664 - newdiratts = self.read_unsigned_byte(self.sectors[head+p+25]) 2.665 + newdiratts = self._read_unsigned_byte(self.sectors[head+p+25]) 2.666 2.667 if inddiscadd == -1: 2.668 2.669 @@ -1141,7 +1193,7 @@ 2.670 ) 2.671 self.verify_log.append( ( 2.672 WARNING, " file details: %x" % \ 2.673 - self.str2num(3, self.sectors[head+p+22:head+p+25]) 2.674 + self._str2num(3, self.sectors[head+p+22:head+p+25]) 2.675 ) ) 2.676 self.verify_log.append( 2.677 (WARNING, " atts: %x" % newdiratts) 2.678 @@ -1160,7 +1212,7 @@ 2.679 self.verify_log.append( ( 2.680 WARNING, 2.681 " file details: %x" % \ 2.682 - self.str2num(3, self.sectors[head+p+22:head+p+25]) 2.683 + self._str2num(3, self.sectors[head+p+22:head+p+25]) 2.684 ) ) 2.685 self.verify_log.append( 2.686 (WARNING, " atts: %x" % newdiratts) 2.687 @@ -1170,7 +1222,7 @@ 2.688 2.689 # Store a zero length file. This appears to be the 2.690 # standard behaviour for storing empty files. 2.691 - files.append([name, "", load, exe, length]) 2.692 + files.append(ADFSfile(name, "", load, exe, length)) 2.693 2.694 #print hex(head+p), hex(head+p+22) 2.695 2.696 @@ -1200,10 +1252,10 @@ 2.697 # as a directory. 2.698 2.699 lower_dir_name, lower_files = \ 2.700 - self.read_new_catalogue(start) 2.701 + self._read_new_catalogue(start) 2.702 2.703 # Store the directory name and file found therein. 2.704 - files.append([name, lower_files]) 2.705 + files.append(ADFSdirectory(name, lower_files)) 2.706 2.707 else: 2.708 2.709 @@ -1223,7 +1275,7 @@ 2.710 file = file + self.sectors[start : (start + amount)] 2.711 remaining = remaining - amount 2.712 2.713 - files.append([name, file, load, exe, length]) 2.714 + files.append(ADFSfile(name, file, load, exe, length)) 2.715 2.716 p = p + 26 2.717 2.718 @@ -1246,11 +1298,11 @@ 2.719 2.720 return '', files 2.721 2.722 - dir_name = self.safe( 2.723 + dir_name = self._safe( 2.724 self.sectors[tail+self.sector_size-16:tail+self.sector_size-6] 2.725 ) 2.726 2.727 - #parent = self.read_new_address( 2.728 + #parent = self._read_new_address( 2.729 # self.sectors[tail+self.sector_size-38:tail+self.sector_size-35], dir = 1 2.730 # ) 2.731 #print "This directory:", hex(head), "Parent:", hex(parent) 2.732 @@ -1258,7 +1310,7 @@ 2.733 parent = \ 2.734 self.sectors[tail+self.sector_size-38:tail+self.sector_size-35] 2.735 2.736 - #256*self.str2num( 2.737 + #256*self._str2num( 2.738 # 3, self.sectors[tail+self.sector_size-38:tail+self.sector_size-35] 2.739 #) 2.740 2.741 @@ -1289,8 +1341,10 @@ 2.742 return dir_name, files 2.743 2.744 2.745 - def read_leafname(self, path): 2.746 + def _read_leafname(self, path): 2.747 2.748 + # Unused 2.749 + 2.750 pos = string.rfind(path, os.sep) 2.751 if pos != -1: 2.752 return path[pos+1:] 2.753 @@ -1298,9 +1352,28 @@ 2.754 return path 2.755 2.756 2.757 - def print_catalogue(self, files = None, path = "$", filetypes = 0, 2.758 - separator = ","): 2.759 + def print_catalogue(self, files = None, path = "$", filetypes = 0): 2.760 2.761 + """print_catalogue(self, files = None, path = "$", filetypes = 0) 2.762 + 2.763 + Prints the contents of the disc catalogue to standard output. 2.764 + Usually, this method is called without specifying any of the keyword 2.765 + arguments, but these can be used to customise the output. 2.766 + 2.767 + If files is None, the contents of the entire disc will be shown. 2.768 + A subset of the list of files obtained from the instance's files 2.769 + attribute can be passed if only a subset of the catalogue needs to 2.770 + be displayed. 2.771 + 2.772 + The path parameter specifies the representation of the root directory 2.773 + in the output. By default, root directories are represented by the 2.774 + familiar "$" symbol. 2.775 + 2.776 + If filetypes is set to True or a non-False value, the file types of 2.777 + each file will be displayed; otherwise, load and execution addresses 2.778 + will be displayed instead. 2.779 + """ 2.780 + 2.781 if files is None: 2.782 2.783 files = self.files 2.784 @@ -1309,19 +1382,18 @@ 2.785 2.786 print path, "(empty)" 2.787 2.788 - for i in files: 2.789 + for obj in files: 2.790 2.791 - name = i[0] 2.792 - if type(i[1]) != type([]): 2.793 + name = obj.name 2.794 + if isinstance(obj, ADFSfile): 2.795 2.796 - load, exec_addr, length = i[2], i[3], i[4] 2.797 - 2.798 - if filetypes == 0: 2.799 + if not filetypes: 2.800 2.801 # Load and execution addresses treated as valid. 2.802 print string.expandtabs( 2.803 "%s.%s\t%X\t%X\t%X" % ( 2.804 - path, name, load, exec_addr, length 2.805 + path, name, obj.load_address, 2.806 + obj.execution_address, obj.length 2.807 ), 16 2.808 ) 2.809 2.810 @@ -1329,17 +1401,17 @@ 2.811 2.812 # Load address treated as a filetype. 2.813 print string.expandtabs( 2.814 - "%s.%s\t%X\t%X" % ( 2.815 - path, name, ((load >> 8) & 0xfff), length 2.816 + "%s.%s\t%s\t%X" % ( 2.817 + path, name, obj.filetype().upper(), obj.length 2.818 ), 16 2.819 ) 2.820 2.821 else: 2.822 2.823 - self.print_catalogue(i[1], path + "." + name, filetypes) 2.824 + self.print_catalogue(obj.files, path + "." + name, filetypes) 2.825 2.826 2.827 - def convert_name(self, old_name, convert_dict): 2.828 + def _convert_name(self, old_name, convert_dict): 2.829 2.830 # Use the conversion dictionary to convert any forbidden 2.831 # characters to accepted local substitutes. 2.832 @@ -1364,10 +1436,10 @@ 2.833 2.834 return name 2.835 2.836 - def extract_old_files(self, l, path, filetypes = 0, separator = ",", 2.837 - convert_dict = {}): 2.838 + def _extract_old_files(self, objects, path, filetypes = 0, separator = ",", 2.839 + convert_dict = {}): 2.840 2.841 - new_path = self.create_directory(path) 2.842 + new_path = self._create_directory(path) 2.843 2.844 if new_path != "": 2.845 2.846 @@ -1377,18 +1449,17 @@ 2.847 2.848 return 2.849 2.850 - for i in l: 2.851 + for obj in objects: 2.852 2.853 - old_name = i[0] 2.854 + old_name = obj.name 2.855 2.856 - name = self.convert_name(old_name, convert_dict) 2.857 + name = self._convert_name(old_name, convert_dict) 2.858 2.859 - if type(i[1]) != type([]): 2.860 + if isinstance(obj, ADFSfile): 2.861 2.862 # A file. 2.863 - load, exec_addr, length = i[2], i[3], i[4] 2.864 2.865 - if filetypes == 0: 2.866 + if not filetypes: 2.867 2.868 # Load and execution addresses assumed to be valid. 2.869 2.870 @@ -1398,16 +1469,17 @@ 2.871 2.872 try: 2.873 out = open(out_file, "wb") 2.874 - out.write(i[1]) 2.875 + out.write(obj.data) 2.876 out.close() 2.877 except IOError: 2.878 print "Couldn't open the file: %s" % out_file 2.879 2.880 try: 2.881 inf = open(inf_file, "w") 2.882 - load, exec_addr, length = i[2], i[3], i[4] 2.883 - inf.write( "$.%s\t%X\t%X\t%X" % \ 2.884 - ( name, load, exec_addr, length ) ) 2.885 + inf.write("$.%s\t%X\t%X\t%X" % ( 2.886 + name, obj.load_address, obj.execution_address, 2.887 + obj.length 2.888 + )) 2.889 inf.close() 2.890 except IOError: 2.891 print "Couldn't open the file: %s" % inf_file 2.892 @@ -1415,12 +1487,11 @@ 2.893 else: 2.894 2.895 # Interpret the load address as a filetype. 2.896 - out_file = os.path.join(path, name) + separator + "%x" % \ 2.897 - ((load >> 8) & 0xfff) 2.898 + out_file = os.path.join(path, name) + separator + obj.filetype() 2.899 2.900 try: 2.901 out = open(out_file, "wb") 2.902 - out.write(i[1]) 2.903 + out.write(obj.data) 2.904 out.close() 2.905 except IOError: 2.906 print "Couldn't open the file: %s" % out_file 2.907 @@ -1428,15 +1499,15 @@ 2.908 2.909 new_path = os.path.join(path, name) 2.910 2.911 - self.extract_old_files( 2.912 - i[1], new_path, filetypes, separator, convert_dict 2.913 + self._extract_old_files( 2.914 + obj.files, new_path, filetypes, separator, convert_dict 2.915 ) 2.916 2.917 2.918 - def extract_new_files(self, l, path, filetypes = 0, separator = ",", 2.919 - convert_dict = {}): 2.920 + def _extract_new_files(self, objects, path, filetypes = 0, separator = ",", 2.921 + convert_dict = {}): 2.922 2.923 - new_path = self.create_directory(path) 2.924 + new_path = self._create_directory(path) 2.925 2.926 if new_path != "": 2.927 2.928 @@ -1446,20 +1517,19 @@ 2.929 2.930 return 2.931 2.932 - for i in l: 2.933 + for obj in objects: 2.934 2.935 - old_name = i[0] 2.936 + old_name = obj.name 2.937 2.938 # Use the conversion dictionary to convert any forbidden 2.939 # characters to accepted local substitutes. 2.940 - name = self.convert_name(old_name, convert_dict) 2.941 + name = self._convert_name(old_name, convert_dict) 2.942 2.943 - if type(i[1]) != type([]): 2.944 + if isinstance(obj, ADFSfile): 2.945 2.946 # A file. 2.947 - load, exec_addr, length = i[2], i[3], i[4] 2.948 2.949 - if filetypes == 0: 2.950 + if not filetypes: 2.951 2.952 # Load and execution addresses assumed to be valid. 2.953 2.954 @@ -1469,28 +1539,28 @@ 2.955 2.956 try: 2.957 out = open(out_file, "wb") 2.958 - out.write(i[1]) 2.959 + out.write(obj.data) 2.960 out.close() 2.961 except IOError: 2.962 print "Couldn't open the file: %s" % out_file 2.963 2.964 try: 2.965 inf = open(inf_file, "w") 2.966 - load, exec_addr, length = i[2], i[3], i[4] 2.967 - inf.write( "$.%s\t%X\t%X\t%X" % \ 2.968 - ( name, load, exec_addr, length ) ) 2.969 + inf.write("$.%s\t%X\t%X\t%X" % ( 2.970 + name, obj.load_address, obj.execution_address, 2.971 + obj.length 2.972 + )) 2.973 inf.close() 2.974 except IOError: 2.975 print "Couldn't open the file: %s" % inf_file 2.976 else: 2.977 2.978 # Interpret the load address as a filetype. 2.979 - out_file = path + os.sep + name + separator + "%x" % \ 2.980 - ((load >> 8) & 0xfff) 2.981 + out_file = path + os.sep + name + separator + obj.filetype() 2.982 2.983 try: 2.984 out = open(out_file, "wb") 2.985 - out.write(i[1]) 2.986 + out.write(obj.data) 2.987 out.close() 2.988 except IOError: 2.989 print "Couldn't open the file: %s" % out_file 2.990 @@ -1498,43 +1568,62 @@ 2.991 2.992 new_path = os.path.join(path, name) 2.993 2.994 - self.extract_new_files( 2.995 - i[1], new_path, filetypes, separator, convert_dict 2.996 + self._extract_new_files( 2.997 + obj.files, new_path, filetypes, separator, convert_dict 2.998 ) 2.999 2.1000 2.1001 def extract_files(self, out_path, files = None, filetypes = 0, 2.1002 separator = ",", convert_dict = {}): 2.1003 2.1004 + """extract_files(self, out_path, files = None, filetypes = 0, 2.1005 + separator = ",", convert_dict = {}) 2.1006 + 2.1007 + Extracts the files stored in the disc image into a directory structure 2.1008 + stored on the path specified by out_path. 2.1009 + 2.1010 + The files parameter specified a list of ADFSfile or ADFSdirectory 2.1011 + instances to extract to the target file system. This keyword argument 2.1012 + can be omitted if all files and directories in the disc image are to 2.1013 + be extracted. 2.1014 + 2.1015 + If the filetypes keyword argument is set to True, or another non-False 2.1016 + value, file type suffixes are appended to each file created using the 2.1017 + separator string supplied to join the file name to the file type. 2.1018 + 2.1019 + The convert_dict parameter can be used to specify a mapping between 2.1020 + characters used in ADFS file names and those on the target file system. 2.1021 + """ 2.1022 + 2.1023 if files is None: 2.1024 2.1025 files = self.files 2.1026 2.1027 if self.disc_type == 'adD': 2.1028 2.1029 - self.extract_old_files( 2.1030 + self._extract_old_files( 2.1031 files, out_path, filetypes, separator, convert_dict 2.1032 ) 2.1033 2.1034 elif self.disc_type == 'adE': 2.1035 2.1036 - self.extract_new_files( 2.1037 + self._extract_new_files( 2.1038 files, out_path, filetypes, separator, convert_dict 2.1039 ) 2.1040 2.1041 elif self.disc_type == 'adEbig': 2.1042 2.1043 - self.extract_new_files( 2.1044 + self._extract_new_files( 2.1045 files, out_path, filetypes, separator, convert_dict 2.1046 ) 2.1047 2.1048 else: 2.1049 2.1050 - self.extract_old_files( 2.1051 + self._extract_old_files( 2.1052 files, out_path, filetypes, separator, convert_dict 2.1053 ) 2.1054 2.1055 - def create_directory(self, path, name = None): 2.1056 + def _create_directory(self, path, name = None): 2.1057 2.1058 elements = [] 2.1059 2.1060 @@ -1589,9 +1678,9 @@ 2.1061 # Success 2.1062 return built 2.1063 2.1064 - def plural(self, msg, values, words): 2.1065 + def _plural(self, msg, values, words): 2.1066 2.1067 - """message = plural(self, msg, values, words) 2.1068 + """message = _plural(self, msg, values, words) 2.1069 2.1070 Return a message which takes into account the plural form of 2.1071 words in the original message, assuming that the appropriate 2.1072 @@ -1634,7 +1723,7 @@ 2.1073 2.1074 if hasattr(self, "disc_map") and self.disc_map.has_key(1): 2.1075 2.1076 - print self.plural( 2.1077 + print self._plural( 2.1078 "%i mapped %s found.", [len(self.disc_map[1])], 2.1079 [("defects", "defect", "defects")] 2.1080 )
