diff --git a/src/greaseweazle/codec/codec.py b/src/greaseweazle/codec/codec.py index fe4934a5..5e2f6f00 100644 --- a/src/greaseweazle/codec/codec.py +++ b/src/greaseweazle/codec/codec.py @@ -5,6 +5,7 @@ # This is free and unencumbered software released into the public domain. # See the file COPYING for more details, or visit . +from __future__ import annotations from typing import Dict, List, Tuple, Optional import os.path, re @@ -128,16 +129,37 @@ def default_revs(self) -> float: return max([x.default_revs for x in self.track_map.values()]) - -def read_diskdef_file_lines(filename: Optional[str]) -> Tuple[List[str], str]: - if filename is None: - filename = 'diskdefs.cfg' - with importlib.resources.open_text('greaseweazle.data', filename) as f: - lines = f.readlines() - else: - with open(os.path.expanduser(filename), 'r') as f: - lines = f.readlines() - return (lines, filename) +class DiskDef_File: + def __init__(self, name: Optional[str], + parent: Optional[DiskDef_File] = None, + prefix: Optional[str] = None) -> None: + self.linenr = 0 + self.parent = parent + self.prefix = prefix + self.path: Optional[str] = None + self.name: str = 'diskdefs.cfg' if name is None else name + if name is None or (self.parent and not self.parent.path): + with importlib.resources.open_text('greaseweazle.data', + self.name) as f: + self.lines = f.readlines() + else: + if self.parent: + assert self.parent.path # mypy + self.path = os.path.join(os.path.dirname(self.parent.path), + self.name) + else: + self.path = os.path.expanduser(self.name) + with open(self.path, 'r') as f: + self.lines = f.readlines() + + def full_prefix(self) -> str: + s: List[str] = [] + p: Optional[DiskDef_File] = self + while p is not None: + if p.prefix: + s.insert(0, p.prefix) + p = p.parent + return '.'.join(s) + '.' if s else '' # Import the TrackDef subclasses @@ -177,18 +199,18 @@ class ParseMode: Disk = 1 Track = 2 -def get_diskdef( +def _get_diskdef( format_name: str, - diskdef_filename: Optional[str] = None + diskdef_file: DiskDef_File ) -> Optional[DiskDef]: parse_mode = ParseMode.Outer active = False + prefix = diskdef_file.full_prefix() disk: Optional[DiskDef] = None track: Optional[TrackDef] = None - lines, diskdef_filename = read_diskdef_file_lines(diskdef_filename) - for linenr, l in enumerate(lines, start=1): + for diskdef_file.linenr, l in enumerate(diskdef_file.lines, start=1): try: # Strip comments and whitespace. match = re.match(r'\s*([^#]*)', l) @@ -201,17 +223,28 @@ def get_diskdef( if parse_mode == ParseMode.Outer: disk_match = re.match(r'disk\s+([\w,.-]+)', t) - error.check(disk_match is not None, 'syntax error') - assert disk_match is not None # mypy - parse_mode = ParseMode.Disk - active = disk_match.group(1) == format_name - if active: - disk = DiskDef() + if disk_match: + parse_mode = ParseMode.Disk + active = prefix + disk_match.group(1) == format_name + if active: + disk = DiskDef() + else: + prefix_match = re.match(r'prefix\s+([\w,.-]+)\s+(\S+)', t) + error.check(prefix_match is not None, 'syntax error') + assert prefix_match is not None # mypy + disk = _get_diskdef(format_name, DiskDef_File( + name = prefix_match.group(2), + prefix = prefix_match.group(1), + parent = diskdef_file)) + if disk: + break elif parse_mode == ParseMode.Disk: if t == 'end': parse_mode = ParseMode.Outer active = False + if disk: + break continue tracks_match = re.match(r'tracks\s+([0-9,.*-]+)' '\s+([\w,.-]+)', t) @@ -287,24 +320,44 @@ def get_diskdef( keyval_match.group(2)) except Exception as err: - ctxt = "%s, line %d: " % (diskdef_filename, linenr) - err.args = (ctxt + err.args[0],) + err.args[1:] + if err.args and isinstance(x := err.args[0], str): + ctxt = f'At {diskdef_file.name}, line {diskdef_file.linenr}:' + ctxt += '\n' if x.startswith('At') else ' ' + err.args = (ctxt + x,) + err.args[1:] raise + return disk + +def get_diskdef( + format_name: str, + diskdef_filename: Optional[str] = None +) -> Optional[DiskDef]: + diskdef_file = DiskDef_File(name = diskdef_filename) + disk = _get_diskdef(format_name, diskdef_file) if disk is None: return None disk.finalise() - return disk - -def print_formats(diskdef_filename: Optional[str] = None) -> str: - columns, sep, formats = 80, 2, [] - lines, _ = read_diskdef_file_lines(diskdef_filename) - for l in lines: +def get_all_formats(diskdef_file: DiskDef_File) -> List[str]: + formats = [] + prefix = diskdef_file.full_prefix() + for diskdef_file.linenr, l in enumerate(diskdef_file.lines, start=1): disk_match = re.match(r'\s*disk\s+([\w,.-]+)', l) if disk_match: - formats.append(disk_match.group(1)) + formats.append(prefix + disk_match.group(1)) + prefix_match = re.match(r'\s*prefix\s+([\w,.-]+)\s+(\S+)', l) + if prefix_match: + formats += get_all_formats(DiskDef_File( + name = prefix_match.group(2), + prefix = prefix_match.group(1), + parent = diskdef_file)) + return formats + +def print_formats(diskdef_filename: Optional[str] = None) -> str: + columns, sep = 80, 2 + diskdef_file = DiskDef_File(name = diskdef_filename) + formats = get_all_formats(diskdef_file) formats.sort() return util.columnify(formats) diff --git a/src/greaseweazle/data/diskdefs.cfg b/src/greaseweazle/data/diskdefs.cfg index 20c274f1..7cd4eb7d 100644 --- a/src/greaseweazle/data/diskdefs.cfg +++ b/src/greaseweazle/data/diskdefs.cfg @@ -794,144 +794,7 @@ disk hp.mmfm.9895 end end -disk ibm.160 - cyls = 40 - heads = 1 - tracks * ibm.mfm - secs = 8 - bps = 512 - gap3 = 84 - rate = 250 - end -end - -disk ibm.180 - cyls = 40 - heads = 1 - tracks * ibm.mfm - secs = 9 - bps = 512 - gap3 = 84 - rate = 250 - end -end - -disk ibm.320 - cyls = 40 - heads = 2 - tracks * ibm.mfm - secs = 8 - bps = 512 - gap3 = 84 - rate = 250 - end -end - -disk ibm.360 - cyls = 40 - heads = 2 - tracks * ibm.mfm - secs = 9 - bps = 512 - gap3 = 84 - rate = 250 - end -end - -disk ibm.720 - cyls = 80 - heads = 2 - tracks * ibm.mfm - secs = 9 - bps = 512 - gap3 = 84 - rate = 250 - end -end - -disk ibm.800 - cyls = 80 - heads = 2 - tracks * ibm.mfm - secs = 10 - bps = 512 - gap3 = 30 - rate = 250 - end -end - -disk ibm.1200 - cyls = 80 - heads = 2 - tracks * ibm.mfm - secs = 15 - bps = 512 - gap3 = 84 - rate = 500 - rpm = 360 - end -end - -disk ibm.1440 - cyls = 80 - heads = 2 - tracks * ibm.mfm - secs = 18 - bps = 512 - gap3 = 84 - rate = 500 - end -end - -disk ibm.1680 - cyls = 80 - heads = 2 - tracks * ibm.mfm - secs = 21 - bps = 512 - gap3 = 12 - cskew = 3 - interleave = 2 - rate = 500 - end -end - -# alias for ibm.1680 -disk ibm.dmf - cyls = 80 - heads = 2 - tracks * ibm.mfm - secs = 21 - bps = 512 - gap3 = 12 - cskew = 3 - interleave = 2 - rate = 500 - end -end - -disk ibm.2880 - cyls = 80 - heads = 2 - tracks * ibm.mfm - secs = 36 - bps = 512 - gap2 = 41 - rate = 1000 - end -end - -# Generic IBM format codec which will scan a track for any valid FM/MFM -# sectors at standard rates (125kbps, 250kbps, 500kbps) and RPMs (300, 360). -disk ibm.scan - cyls = 80 - heads = 2 - tracks * ibm.scan - # Following options restrict the scanner's search - # rate = 250 - # rpm = 300 - end -end +prefix ibm diskdefs_ibm.cfg disk mac.400 cyls = 80 diff --git a/src/greaseweazle/data/diskdefs_ibm.cfg b/src/greaseweazle/data/diskdefs_ibm.cfg new file mode 100644 index 00000000..41174680 --- /dev/null +++ b/src/greaseweazle/data/diskdefs_ibm.cfg @@ -0,0 +1,138 @@ +disk 160 + cyls = 40 + heads = 1 + tracks * ibm.mfm + secs = 8 + bps = 512 + gap3 = 84 + rate = 250 + end +end + +disk 180 + cyls = 40 + heads = 1 + tracks * ibm.mfm + secs = 9 + bps = 512 + gap3 = 84 + rate = 250 + end +end + +disk 320 + cyls = 40 + heads = 2 + tracks * ibm.mfm + secs = 8 + bps = 512 + gap3 = 84 + rate = 250 + end +end + +disk 360 + cyls = 40 + heads = 2 + tracks * ibm.mfm + secs = 9 + bps = 512 + gap3 = 84 + rate = 250 + end +end + +disk 720 + cyls = 80 + heads = 2 + tracks * ibm.mfm + secs = 9 + bps = 512 + gap3 = 84 + rate = 250 + end +end + +disk 800 + cyls = 80 + heads = 2 + tracks * ibm.mfm + secs = 10 + bps = 512 + gap3 = 30 + rate = 250 + end +end + +disk 1200 + cyls = 80 + heads = 2 + tracks * ibm.mfm + secs = 15 + bps = 512 + gap3 = 84 + rate = 500 + rpm = 360 + end +end + +disk 1440 + cyls = 80 + heads = 2 + tracks * ibm.mfm + secs = 18 + bps = 512 + gap3 = 84 + rate = 500 + end +end + +disk 1680 + cyls = 80 + heads = 2 + tracks * ibm.mfm + secs = 21 + bps = 512 + gap3 = 12 + cskew = 3 + interleave = 2 + rate = 500 + end +end + +# alias for 1680 +disk dmf + cyls = 80 + heads = 2 + tracks * ibm.mfm + secs = 21 + bps = 512 + gap3 = 12 + cskew = 3 + interleave = 2 + rate = 500 + end +end + +disk 2880 + cyls = 80 + heads = 2 + tracks * ibm.mfm + secs = 36 + bps = 512 + gap2 = 41 + rate = 1000 + end +end + +# Generic IBM format codec which will scan a track for any valid FM/MFM +# sectors at standard rates (125kbps, 250kbps, 500kbps) and RPMs (300, 360). +disk scan + cyls = 80 + heads = 2 + tracks * ibm.scan + # Following options restrict the scanner's search + # rate = 250 + # rpm = 300 + end +end