diskstack 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. diskstack/__init__.py +5 -0
  2. diskstack/_vendor/__init__.py +0 -0
  3. diskstack/_vendor/greaseweazle/COPYING +24 -0
  4. diskstack/_vendor/greaseweazle/__init__.py +2 -0
  5. diskstack/_vendor/greaseweazle/codec/__init__.py +0 -0
  6. diskstack/_vendor/greaseweazle/codec/amiga/__init__.py +0 -0
  7. diskstack/_vendor/greaseweazle/codec/amiga/amigados.py +221 -0
  8. diskstack/_vendor/greaseweazle/codec/apple2/__init__.py +0 -0
  9. diskstack/_vendor/greaseweazle/codec/apple2/apple2_gcr.py +223 -0
  10. diskstack/_vendor/greaseweazle/codec/bitcell.py +117 -0
  11. diskstack/_vendor/greaseweazle/codec/codec.py +362 -0
  12. diskstack/_vendor/greaseweazle/codec/commodore/__init__.py +0 -0
  13. diskstack/_vendor/greaseweazle/codec/commodore/c64_gcr.py +229 -0
  14. diskstack/_vendor/greaseweazle/codec/datageneral/__init__.py +0 -0
  15. diskstack/_vendor/greaseweazle/codec/datageneral/datageneral.py +211 -0
  16. diskstack/_vendor/greaseweazle/codec/hp/__init__.py +0 -0
  17. diskstack/_vendor/greaseweazle/codec/hp/hp_mmfm.py +258 -0
  18. diskstack/_vendor/greaseweazle/codec/ibm/__init__.py +0 -0
  19. diskstack/_vendor/greaseweazle/codec/ibm/ibm.py +1005 -0
  20. diskstack/_vendor/greaseweazle/codec/macintosh/__init__.py +0 -0
  21. diskstack/_vendor/greaseweazle/codec/macintosh/mac_gcr.py +238 -0
  22. diskstack/_vendor/greaseweazle/codec/micropolis/__init__.py +0 -0
  23. diskstack/_vendor/greaseweazle/codec/micropolis/micropolis.py +233 -0
  24. diskstack/_vendor/greaseweazle/codec/northstar/__init__.py +0 -0
  25. diskstack/_vendor/greaseweazle/codec/northstar/northstar.py +210 -0
  26. diskstack/_vendor/greaseweazle/data/__init__.py +0 -0
  27. diskstack/_vendor/greaseweazle/data/diskdefs.cfg +81 -0
  28. diskstack/_vendor/greaseweazle/data/diskdefs_acorn.cfg +119 -0
  29. diskstack/_vendor/greaseweazle/data/diskdefs_akai.cfg +27 -0
  30. diskstack/_vendor/greaseweazle/data/diskdefs_amiga.cfg +17 -0
  31. diskstack/_vendor/greaseweazle/data/diskdefs_apple2.cfg +31 -0
  32. diskstack/_vendor/greaseweazle/data/diskdefs_apricot.cfg +25 -0
  33. diskstack/_vendor/greaseweazle/data/diskdefs_atari.cfg +33 -0
  34. diskstack/_vendor/greaseweazle/data/diskdefs_atarist.cfg +76 -0
  35. diskstack/_vendor/greaseweazle/data/diskdefs_coco.cfg +98 -0
  36. diskstack/_vendor/greaseweazle/data/diskdefs_commodore.cfg +127 -0
  37. diskstack/_vendor/greaseweazle/data/diskdefs_datageneral.cfg +8 -0
  38. diskstack/_vendor/greaseweazle/data/diskdefs_dec.cfg +37 -0
  39. diskstack/_vendor/greaseweazle/data/diskdefs_dragon.cfg +61 -0
  40. diskstack/_vendor/greaseweazle/data/diskdefs_eagle.cfg +25 -0
  41. diskstack/_vendor/greaseweazle/data/diskdefs_ensoniq.cfg +39 -0
  42. diskstack/_vendor/greaseweazle/data/diskdefs_epson.cfg +95 -0
  43. diskstack/_vendor/greaseweazle/data/diskdefs_gem.cfg +15 -0
  44. diskstack/_vendor/greaseweazle/data/diskdefs_hp.cfg +25 -0
  45. diskstack/_vendor/greaseweazle/data/diskdefs_ibm.cfg +140 -0
  46. diskstack/_vendor/greaseweazle/data/diskdefs_kaypro.cfg +58 -0
  47. diskstack/_vendor/greaseweazle/data/diskdefs_luxor.cfg +96 -0
  48. diskstack/_vendor/greaseweazle/data/diskdefs_mac.cfg +71 -0
  49. diskstack/_vendor/greaseweazle/data/diskdefs_micropolis.cfg +73 -0
  50. diskstack/_vendor/greaseweazle/data/diskdefs_mm1.cfg +69 -0
  51. diskstack/_vendor/greaseweazle/data/diskdefs_msx.cfg +45 -0
  52. diskstack/_vendor/greaseweazle/data/diskdefs_northstar.cfg +37 -0
  53. diskstack/_vendor/greaseweazle/data/diskdefs_occ1.cfg +23 -0
  54. diskstack/_vendor/greaseweazle/data/diskdefs_olivetti.cfg +18 -0
  55. diskstack/_vendor/greaseweazle/data/diskdefs_pc98.cfg +69 -0
  56. diskstack/_vendor/greaseweazle/data/diskdefs_raw.cfg +25 -0
  57. diskstack/_vendor/greaseweazle/data/diskdefs_rm.cfg +98 -0
  58. diskstack/_vendor/greaseweazle/data/diskdefs_sci.cfg +15 -0
  59. diskstack/_vendor/greaseweazle/data/diskdefs_sega.cfg +13 -0
  60. diskstack/_vendor/greaseweazle/data/diskdefs_sharp.cfg +13 -0
  61. diskstack/_vendor/greaseweazle/data/diskdefs_thomson.cfg +78 -0
  62. diskstack/_vendor/greaseweazle/data/diskdefs_tsc.cfg +81 -0
  63. diskstack/_vendor/greaseweazle/data/diskdefs_xerox.cfg +59 -0
  64. diskstack/_vendor/greaseweazle/data/diskdefs_zx.cfg +245 -0
  65. diskstack/_vendor/greaseweazle/error.py +19 -0
  66. diskstack/_vendor/greaseweazle/flux.py +315 -0
  67. diskstack/_vendor/greaseweazle/image/__init__.py +0 -0
  68. diskstack/_vendor/greaseweazle/image/a2r.py +133 -0
  69. diskstack/_vendor/greaseweazle/image/acorn.py +28 -0
  70. diskstack/_vendor/greaseweazle/image/adf.py +15 -0
  71. diskstack/_vendor/greaseweazle/image/apple2.py +18 -0
  72. diskstack/_vendor/greaseweazle/image/apridisk.py +107 -0
  73. diskstack/_vendor/greaseweazle/image/caps.py +464 -0
  74. diskstack/_vendor/greaseweazle/image/d64.py +51 -0
  75. diskstack/_vendor/greaseweazle/image/d81.py +28 -0
  76. diskstack/_vendor/greaseweazle/image/d88.py +211 -0
  77. diskstack/_vendor/greaseweazle/image/dcp.py +45 -0
  78. diskstack/_vendor/greaseweazle/image/dim.py +53 -0
  79. diskstack/_vendor/greaseweazle/image/dmk.py +166 -0
  80. diskstack/_vendor/greaseweazle/image/dsk.py +31 -0
  81. diskstack/_vendor/greaseweazle/image/edsk.py +591 -0
  82. diskstack/_vendor/greaseweazle/image/fd.py +23 -0
  83. diskstack/_vendor/greaseweazle/image/fdi.py +41 -0
  84. diskstack/_vendor/greaseweazle/image/hdm.py +15 -0
  85. diskstack/_vendor/greaseweazle/image/hfe.py +742 -0
  86. diskstack/_vendor/greaseweazle/image/image.py +130 -0
  87. diskstack/_vendor/greaseweazle/image/imd.py +201 -0
  88. diskstack/_vendor/greaseweazle/image/img.py +111 -0
  89. diskstack/_vendor/greaseweazle/image/kryoflux.py +304 -0
  90. diskstack/_vendor/greaseweazle/image/mgt.py +15 -0
  91. diskstack/_vendor/greaseweazle/image/msa.py +153 -0
  92. diskstack/_vendor/greaseweazle/image/nfd.py +113 -0
  93. diskstack/_vendor/greaseweazle/image/nsi.py +47 -0
  94. diskstack/_vendor/greaseweazle/image/scp.py +471 -0
  95. diskstack/_vendor/greaseweazle/image/sf7.py +15 -0
  96. diskstack/_vendor/greaseweazle/image/sharp2d.py +15 -0
  97. diskstack/_vendor/greaseweazle/image/td0.py +141 -0
  98. diskstack/_vendor/greaseweazle/image/xdf.py +15 -0
  99. diskstack/_vendor/greaseweazle/optimised/__init__.py +22 -0
  100. diskstack/_vendor/greaseweazle/tools/__init__.py +0 -0
  101. diskstack/_vendor/greaseweazle/tools/util.py +133 -0
  102. diskstack/_vendor/greaseweazle/track.py +494 -0
  103. diskstack/cache.py +210 -0
  104. diskstack/candidates.py +396 -0
  105. diskstack/cli.py +348 -0
  106. diskstack/errors.py +5 -0
  107. diskstack/filler.py +29 -0
  108. diskstack/formats.py +381 -0
  109. diskstack/parallel.py +73 -0
  110. diskstack/report.py +353 -0
  111. diskstack/stack.py +317 -0
  112. diskstack-1.0.0.dist-info/METADATA +329 -0
  113. diskstack-1.0.0.dist-info/RECORD +119 -0
  114. diskstack-1.0.0.dist-info/WHEEL +5 -0
  115. diskstack-1.0.0.dist-info/entry_points.txt +2 -0
  116. diskstack-1.0.0.dist-info/licenses/LICENSE +21 -0
  117. diskstack-1.0.0.dist-info/licenses/NOTICE +55 -0
  118. diskstack-1.0.0.dist-info/licenses/diskstack/_vendor/greaseweazle/COPYING +24 -0
  119. diskstack-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,130 @@
1
+ # greaseweazle/image/image.py
2
+ #
3
+ # Written & released by Keir Fraser <keir.xen@gmail.com>
4
+ #
5
+ # This is free and unencumbered software released into the public domain.
6
+ # See the file COPYING for more details, or visit <http://unlicense.org>.
7
+
8
+ from __future__ import annotations
9
+ from typing import Optional, List, Dict
10
+
11
+ import os
12
+
13
+ from diskstack._vendor.greaseweazle import error
14
+ from diskstack._vendor.greaseweazle.codec import codec
15
+ from diskstack._vendor.greaseweazle.flux import HasFlux
16
+
17
+ OptDict = Dict[str,str]
18
+
19
+ class ImageOpts:
20
+ r_settings: List[str] = [] # r_set()
21
+ w_settings: List[str] = [] # w_set()
22
+ a_settings: List[str] = [] # r_set(), w_set()
23
+
24
+ def _set(self, filename: str, opt: str, val: str,
25
+ settings: List[str]) -> None:
26
+ error.check(opt in settings,
27
+ "%s: Invalid file option: %s\n" % (filename, opt)
28
+ + 'Valid options: '
29
+ + (', '.join(settings) if settings else '<none>'))
30
+ setattr(self, opt, val)
31
+
32
+ def r_set(self, filename: str, opt: str, val: str) -> None:
33
+ self._set(filename, opt, val, self.a_settings + self.r_settings)
34
+
35
+ def w_set(self, filename: str, opt: str, val: str) -> None:
36
+ self._set(filename, opt, val, self.a_settings + self.w_settings)
37
+
38
+ class Image:
39
+
40
+ filename: str
41
+ noclobber = False
42
+ default_format: Optional[str] = None
43
+ read_only = False
44
+ write_on_ctrl_c = False
45
+ opts = ImageOpts() # empty
46
+
47
+ def __init__(self, name: str, fmt) -> None:
48
+ raise NotImplementedError
49
+
50
+ def apply_r_opts(self, opts: OptDict) -> None:
51
+ for opt, val in opts.items():
52
+ self.opts.r_set(self.filename, opt, val)
53
+
54
+ def apply_w_opts(self, opts: OptDict) -> None:
55
+ for opt, val in opts.items():
56
+ self.opts.w_set(self.filename, opt, val)
57
+
58
+ ## Context manager for image objects created using .to_file()
59
+
60
+ def __enter__(self) -> Image:
61
+ self.file = open(self.filename, ('wb','xb')[self.noclobber])
62
+ return self
63
+
64
+ def __exit__(self, type, value, tb):
65
+ save = (type is None or
66
+ (type is KeyboardInterrupt and self.write_on_ctrl_c))
67
+ try:
68
+ if save:
69
+ # No error: Normal writeout.
70
+ self.file.write(self.get_image())
71
+ finally:
72
+ # Always close the file.
73
+ self.file.close()
74
+ if not save:
75
+ # An error occurred: We remove the target file.
76
+ os.remove(self.filename)
77
+
78
+ ## Default .to_file() constructor
79
+ @classmethod
80
+ def to_file(cls, name: str, fmt: Optional[codec.DiskDef],
81
+ noclobber: bool, opts: OptDict) -> Image:
82
+ error.check(not cls.read_only,
83
+ "%s: Cannot create %s image files" % (name, cls.__name__))
84
+ obj = cls(name, fmt)
85
+ obj.noclobber = noclobber
86
+ obj.apply_w_opts(opts)
87
+ return obj
88
+
89
+ ## Default .from_file() constructor
90
+ @classmethod
91
+ def from_file(cls, name: str, fmt: Optional[codec.DiskDef],
92
+ opts: OptDict) -> Image:
93
+ obj = cls(name, fmt)
94
+ obj.apply_r_opts(opts)
95
+ with open(name, "rb") as f:
96
+ obj.from_bytes(f.read())
97
+ return obj
98
+
99
+ ## Used by default .from_file constructor
100
+ def from_bytes(self, dat: bytes) -> None:
101
+ raise NotImplementedError
102
+
103
+ # Maximum non-empty cylinder on each head, or -1 if no cylinders exist.
104
+ # Returns a list of integers, indexed by head.
105
+ def max_cylinder(self):
106
+ r = list()
107
+ for h in range(2):
108
+ for c in range(100, -2, -1):
109
+ if c < 0 or self.get_track(c,h) is not None:
110
+ r.append(c)
111
+ break
112
+ return r
113
+
114
+ ## Above methods and class variables can be overridden by subclasses.
115
+ ## Additionally, subclasses must provide following public interfaces:
116
+
117
+ def get_track(self, cyl: int, side: int) -> Optional[HasFlux]:
118
+ raise NotImplementedError
119
+
120
+ ## Write support (if not cls.read_only):
121
+ def emit_track(self, cyl: int, side: int, track: HasFlux):
122
+ raise NotImplementedError
123
+ ## Plus get_image, or __enter__ / __exit__
124
+ def get_image(self) -> bytes:
125
+ raise NotImplementedError
126
+
127
+
128
+ # Local variables:
129
+ # python-indent: 4
130
+ # End:
@@ -0,0 +1,201 @@
1
+ # greaseweazle/image/imd.py
2
+ #
3
+ # Written & released by Keir Fraser <keir.xen@gmail.com>
4
+ #
5
+ # This is free and unencumbered software released into the public domain.
6
+ # See the file COPYING for more details, or visit <http://unlicense.org>.
7
+
8
+ from typing import Dict, Tuple, Optional
9
+
10
+ import datetime, struct
11
+
12
+ from diskstack._vendor.greaseweazle import __version__
13
+ from diskstack._vendor.greaseweazle import error
14
+ from diskstack._vendor.greaseweazle.codec.ibm import ibm
15
+ from .image import Image
16
+
17
+ class IMDMode:
18
+ FM_500kbps = 0
19
+ FM_300kbps = 1
20
+ FM_250kbps = 2
21
+ MFM_500kbps = 3
22
+ MFM_300kbps = 4
23
+ MFM_250kbps = 5
24
+
25
+ class IMD(Image):
26
+
27
+ def __init__(self, name: str, _fmt):
28
+ self.to_track: Dict[Tuple[int,int],ibm.IBMTrack_Fixed] = dict()
29
+ self.filename = name
30
+
31
+
32
+ def from_bytes(self, dat: bytes) -> None:
33
+
34
+ # Check and strip the header
35
+ sig, = struct.unpack('4s', dat[:4])
36
+ error.check(sig == b'IMD ', 'Unrecognised IMD file: bad signature')
37
+ for i,x in enumerate(dat):
38
+ if x == 0x1a:
39
+ break
40
+ error.check(x == 0x1a, 'IMD: No comment terminator found')
41
+
42
+ # We will adjust this as we go
43
+ rpm = 300
44
+
45
+ i += 1
46
+ while i < len(dat)-5:
47
+ mode, cyl, head, nsec, sec_n = struct.unpack('5B', dat[i:i+5])
48
+ i += 5
49
+ error.check(0 <= sec_n <= 6, 'IMD: Bad sector size %x' % sec_n)
50
+ secsz = 128 << sec_n
51
+
52
+ has_cyl_map = (head & 0x80) != 0
53
+ has_head_map = (head & 0x40) != 0
54
+ head &= 0x3f
55
+ error.check(0 <= head <= 1, 'IMD: Bad head value %x' % head)
56
+
57
+ if mode == IMDMode.FM_250kbps or mode == IMDMode.FM_300kbps:
58
+ fmt = ibm.IBMTrack_FixedDef('ibm.fm')
59
+ fmt.rate = 125
60
+ elif mode == IMDMode.FM_500kbps:
61
+ fmt = ibm.IBMTrack_FixedDef('ibm.fm')
62
+ if nsec == 26: rpm = 360 # 8-inch disk
63
+ fmt.rate = 250
64
+ elif mode == IMDMode.MFM_250kbps or mode == IMDMode.MFM_300kbps:
65
+ fmt = ibm.IBMTrack_FixedDef('ibm.mfm')
66
+ fmt.rate = 250
67
+ elif mode == IMDMode.MFM_500kbps:
68
+ fmt = ibm.IBMTrack_FixedDef('ibm.mfm')
69
+ if nsec == 26: rpm = 360 # 8-inch disk
70
+ fmt.rate = 500
71
+ else:
72
+ raise error.Fatal('IMD: Unrecognised track mode %x' % mode)
73
+
74
+ fmt.rpm = rpm
75
+ fmt.secs, fmt.sz = nsec, [sec_n]
76
+ fmt.finalise()
77
+ t = fmt.mk_track(cyl, head)
78
+
79
+ rmap = dat[i:i+nsec]
80
+ i += nsec
81
+ if has_cyl_map:
82
+ cmap = dat[i:i+nsec]
83
+ i += nsec
84
+ if has_head_map:
85
+ hmap = dat[i:i+nsec]
86
+ i += nsec
87
+
88
+ for nr,s in enumerate(t.sectors):
89
+ s.crc = s.idam.crc = s.dam.crc = 0
90
+ s.idam.r = rmap[nr]
91
+ if has_cyl_map:
92
+ s.idam.c = cmap[nr]
93
+ if has_head_map:
94
+ s.idam.h = hmap[nr]
95
+ rec = dat[i]
96
+ i += 1
97
+ error.check(0 <= rec <= 8,
98
+ 'IMD: Unexpected sector code %x' % rec)
99
+ if rec == 0:
100
+ s.crc = s.dam.crc = 0xffff
101
+ continue # Data unavailable
102
+ rec -= 1
103
+ if rec&1:
104
+ s.dam.data = bytes([dat[i]] * secsz)
105
+ i += 1
106
+ else:
107
+ s.dam.data = dat[i:i+secsz]
108
+ i += secsz
109
+ if rec&2:
110
+ s.dam.mark = ibm.Mark.DDAM
111
+ if rec&4:
112
+ s.crc = s.dam.crc = 0xffff
113
+
114
+ self.to_track[cyl,head] = t
115
+
116
+
117
+ def get_track(self, cyl: int, side: int) -> Optional[ibm.IBMTrack_Fixed]:
118
+ if (cyl,side) not in self.to_track:
119
+ return None
120
+ return self.to_track[cyl,side]
121
+
122
+
123
+ def emit_track(self, cyl: int, side: int, track) -> None:
124
+ if isinstance(track, ibm.IBMTrack_Scan):
125
+ track = track.track
126
+ error.check(isinstance(track, ibm.IBMTrack),
127
+ 'IMD: Cannot create T%d.%d: Not IBM.FM nor IBM.MFM'
128
+ % (cyl, side))
129
+ if not isinstance(track, ibm.IBMTrack_Empty):
130
+ self.to_track[cyl,side] = track
131
+
132
+
133
+ def get_image(self) -> bytes:
134
+
135
+ tdat = bytearray()
136
+
137
+ now = datetime.datetime.now()
138
+ sig = ('IMD 1.17: %s\r\nGreaseweazle %s\r\n\x1a'
139
+ % (now.strftime('%d/%m/%Y %H:%M:%S'), __version__))
140
+ tdat += sig.encode()
141
+
142
+ for (c,h),t in sorted(self.to_track.items()):
143
+ if t.mode is ibm.Mode.FM:
144
+ if t.clock < 3.0e-6:
145
+ mode = IMDMode.FM_500kbps # High Rate
146
+ else:
147
+ mode = IMDMode.FM_250kbps # 300 RPM
148
+ else:
149
+ assert t.mode is ibm.Mode.MFM
150
+ if t.clock < 1.5e-6:
151
+ mode = IMDMode.MFM_500kbps # High Rate
152
+ else:
153
+ mode = IMDMode.MFM_250kbps # 300 RPM
154
+ cyl, head, nsec, sec_n = c, h, 0, None
155
+ rmap, cmap, hmap = [], [], []
156
+ for s in t.sectors:
157
+ if not isinstance(s, ibm.Sector):
158
+ continue
159
+ nsec += 1
160
+ error.check(s.idam.n == sec_n or sec_n is None,
161
+ 'IMD: Cannot create T%d.%d: Sectors vary in size'
162
+ % (c,h))
163
+ sec_n = s.idam.n
164
+ rmap.append(s.idam.r)
165
+ cmap.append(s.idam.c)
166
+ hmap.append(s.idam.h)
167
+ if s.idam.c != c:
168
+ head |= 0x80
169
+ if s.idam.h != h:
170
+ head |= 0x40
171
+ if sec_n is None:
172
+ sec_n = 0
173
+ secsz = 128 << sec_n
174
+ tdat += struct.pack('5B', mode, cyl, head, nsec, sec_n)
175
+ tdat += bytes(rmap)
176
+ if head & 0x80:
177
+ tdat += bytes(cmap)
178
+ if head & 0x40:
179
+ tdat += bytes(hmap)
180
+ for s in t.sectors:
181
+ if not isinstance(s, ibm.Sector):
182
+ continue
183
+ rec = 0
184
+ if s.dam.data.count(s.dam.data[0]) == secsz:
185
+ rec |= 1
186
+ if s.dam.mark == ibm.Mark.DDAM:
187
+ rec |= 2
188
+ if s.dam.crc != 0:
189
+ rec |= 4
190
+ tdat += bytes([rec+1])
191
+ if rec & 1:
192
+ tdat += s.dam.data[:1]
193
+ else:
194
+ tdat += s.dam.data[:]
195
+
196
+ return tdat
197
+
198
+
199
+ # Local variables:
200
+ # python-indent: 4
201
+ # End:
@@ -0,0 +1,111 @@
1
+ # greaseweazle/image/img.py
2
+ #
3
+ # Written & released by Keir Fraser <keir.xen@gmail.com>
4
+ #
5
+ # This is free and unencumbered software released into the public domain.
6
+ # See the file COPYING for more details, or visit <http://unlicense.org>.
7
+
8
+ from typing import Dict, Tuple, Optional, Any
9
+
10
+ from diskstack._vendor.greaseweazle import error
11
+ from diskstack._vendor.greaseweazle.codec import codec
12
+ from diskstack._vendor.greaseweazle.flux import HasFlux
13
+ from .image import Image, OptDict
14
+
15
+ class IMG(Image):
16
+
17
+ sides_swapped = False
18
+ sequential = False
19
+ min_cyls: Optional[int] = None
20
+
21
+ def __init__(self, name: str, fmt):
22
+ self.to_track: Dict[Tuple[int,int],codec.Codec] = dict()
23
+ error.check(fmt is not None, """\
24
+ Sector image requires a disk format to be specified""")
25
+ self.filename = name
26
+ self.fmt: codec.DiskDef = fmt
27
+
28
+
29
+ def track_list(self):
30
+ t, l = self.fmt.tracks, []
31
+ if self.sequential:
32
+ for h in t.heads:
33
+ for c in t.cyls:
34
+ l.append((c,h))
35
+ else:
36
+ for c in t.cyls:
37
+ for h in t.heads:
38
+ l.append((c,h))
39
+ return l
40
+
41
+
42
+ def from_bytes(self, dat: bytes) -> None:
43
+ pos = 0
44
+ for (cyl, head) in self.track_list():
45
+ if self.sides_swapped:
46
+ head ^= 1
47
+ track = self.fmt.mk_track(cyl, head)
48
+ if track is not None:
49
+ pos += track.set_img_track(dat[pos:])
50
+ self.to_track[cyl,head] = track
51
+
52
+
53
+ def get_track(self, cyl: int, side: int) -> Optional[codec.Codec]:
54
+ if (cyl,side) not in self.to_track:
55
+ return None
56
+ return self.to_track[cyl,side]
57
+
58
+
59
+ def emit_track(self, cyl: int, side: int, track) -> None:
60
+ self.to_track[cyl,side] = track
61
+
62
+
63
+ def get_image(self) -> bytes:
64
+
65
+ tdat = bytearray()
66
+
67
+ # If min_cyls is specified, only emit extra cylinders if there is
68
+ # valid data.
69
+ max_cyl = None
70
+ if self.min_cyls is not None:
71
+ max_cyl = self.min_cyls - 1
72
+ for (cyl, head) in self.track_list():
73
+ if cyl > max_cyl and (cyl,head) in self.to_track:
74
+ track = self.to_track[cyl,head]
75
+ if track.nr_missing() < track.nsec:
76
+ max_cyl = cyl
77
+
78
+ for (cyl, head) in self.track_list():
79
+ if max_cyl is not None and cyl > max_cyl:
80
+ break
81
+ if self.sides_swapped:
82
+ head ^= 1
83
+ if (cyl,head) in self.to_track:
84
+ t = self.to_track[cyl,head]
85
+ else:
86
+ _t = self.fmt.mk_track(cyl, head)
87
+ assert _t is not None # mypy
88
+ t = _t
89
+ tdat += t.get_img_track()
90
+
91
+ return tdat
92
+
93
+
94
+ class IMG_AutoFormat(IMG):
95
+
96
+ @staticmethod
97
+ def format_from_file(name: str) -> str:
98
+ raise NotImplementedError
99
+
100
+ @classmethod
101
+ def from_file(cls, name: str, fmt: Optional[codec.DiskDef],
102
+ opts: OptDict) -> Image:
103
+ if fmt is None:
104
+ format_str = cls.format_from_file(name)
105
+ fmt = codec.get_diskdef(format_str)
106
+ print(f'{cls.__name__}: Image format {format_str}')
107
+ return super().from_file(name, fmt, opts)
108
+
109
+ # Local variables:
110
+ # python-indent: 4
111
+ # End: