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,28 @@
1
+ # greaseweazle/image/d81.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 diskstack._vendor.greaseweazle.image.img import IMG
9
+
10
+ class D81(IMG):
11
+ default_format = 'commodore.1581'
12
+ sides_swapped = True
13
+
14
+ class D1M(IMG):
15
+ default_format = 'commodore.cmd.fd2000.dd'
16
+ sides_swapped = True
17
+
18
+ class D2M(IMG):
19
+ default_format = 'commodore.cmd.fd2000.hd'
20
+ sides_swapped = True
21
+
22
+ class D4M(IMG):
23
+ default_format = 'commodore.cmd.fd4000.ed'
24
+ sides_swapped = True
25
+
26
+ # Local variables:
27
+ # python-indent: 4
28
+ # End:
@@ -0,0 +1,211 @@
1
+ # greaseweazle/image/d88.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, List
9
+
10
+ import struct
11
+ import os
12
+
13
+ from diskstack._vendor.greaseweazle import error
14
+ from diskstack._vendor.greaseweazle.codec.ibm import ibm
15
+ from .image import Image, ImageOpts, OptDict
16
+
17
+ TrackDict = Dict[Tuple[int,int],ibm.IBMTrack_Fixed]
18
+
19
+ class D88Opts(ImageOpts):
20
+ """index: Index into multi-disk image."""
21
+
22
+ r_settings = [ 'index' ]
23
+
24
+ def __init__(self) -> None:
25
+ self._index = 0
26
+
27
+ @property
28
+ def index(self) -> int:
29
+ return self._index
30
+ @index.setter
31
+ def index(self, index: float):
32
+ try:
33
+ self._index = int(index)
34
+ if self._index < 0:
35
+ raise ValueError
36
+ except ValueError:
37
+ raise error.Fatal("D88: Invalid index: '%s'" % index)
38
+
39
+
40
+ class D88(Image):
41
+
42
+ opts: D88Opts
43
+
44
+ read_only = True
45
+
46
+ def __init__(self, name: str, _fmt):
47
+ self.opts = D88Opts()
48
+ self.to_track: TrackDict = dict()
49
+ self.filename = name
50
+
51
+ @staticmethod
52
+ def remove_duplicate_sectors(secs) -> List[Tuple[int,int,int,int,bytes]]:
53
+ new_secs: List[Tuple[int,int,int,int,bytes]] = []
54
+ for s in secs:
55
+ dup = False
56
+ for t in new_secs:
57
+ if s == t:
58
+ dup = True
59
+ break
60
+ if not dup:
61
+ new_secs.append(s)
62
+ return new_secs
63
+
64
+ @staticmethod
65
+ def track_from_file(f, cyl: int, head: int,
66
+ media_flag: int) -> Optional[ibm.IBMTrack_Fixed]:
67
+ track = None
68
+ track_mfm_flag = None
69
+ pos = None
70
+ secs: List[Tuple[int,int,int,int,bytes]] = []
71
+ num_sectors_track = 255
72
+ while len(secs) < num_sectors_track:
73
+ (c, h, r, n, num_sectors, mfm_flag,
74
+ deleted, status, data_size) = \
75
+ struct.unpack('<BBBBHBBB5xH', f.read(16))
76
+ error.check(status == 0x00,
77
+ 'D88: FDC error codes are unsupported.')
78
+ error.check(deleted == 0x00,
79
+ 'D88: Deleted data is unsupported.')
80
+ if track is None:
81
+ if media_flag == 0x00:
82
+
83
+ if mfm_flag == 0x40:
84
+ track = ibm.IBMTrack_FixedDef('ibm.fm')
85
+ track.rate = 125
86
+ else:
87
+ track = ibm.IBMTrack_FixedDef('ibm.mfm')
88
+ track.rate = 250
89
+ track.rpm = 300
90
+ else:
91
+ if mfm_flag == 0x40:
92
+ track = ibm.IBMTrack_FixedDef('ibm.fm')
93
+ track.rate = 250
94
+ else:
95
+ track = ibm.IBMTrack_FixedDef('ibm.mfm')
96
+ track.rate = 500
97
+ track.rpm = 360
98
+ track_mfm_flag = mfm_flag
99
+ num_sectors_track = num_sectors
100
+ error.check(mfm_flag == track_mfm_flag,
101
+ 'D88: Mixed FM and MFM sectors in one track '
102
+ 'are unsupported.')
103
+ error.check(num_sectors_track == num_sectors,
104
+ 'D88: Corrupt number of sectors per track '
105
+ 'in sector header.')
106
+ data = f.read(data_size)
107
+ size = 128 << n
108
+ error.check(size == data_size,
109
+ 'D88: Extra sector data is unsupported.')
110
+ secs.append((c,h,r,n,data))
111
+
112
+ if track is None:
113
+ return None
114
+
115
+ track.secs = len(secs)
116
+ track.sz = [x[3] for x in secs]
117
+ track.finalise()
118
+ t = ibm.IBMTrack_Fixed.from_config(
119
+ track, cyl, head, warn_on_oversize = False)
120
+
121
+ # If the track is oversized, remove duplicate sectors, and
122
+ # re-generate the track layout with oversize warning enabled.
123
+ if t.oversized:
124
+ new_secs = D88.remove_duplicate_sectors(secs)
125
+ ndups = len(secs) - len(new_secs)
126
+ if ndups != 0:
127
+ print('T%d.%d: D88: Removed %d duplicate sectors '
128
+ 'from oversized track' % (cyl, head, ndups))
129
+ secs = new_secs
130
+ track.secs = len(secs)
131
+ track.sz = [x[3] for x in secs]
132
+ t = ibm.IBMTrack_Fixed.from_config(
133
+ track, cyl, head, warn_on_oversize = True)
134
+
135
+ for nr,s in enumerate(t.sectors):
136
+ c,h,r,n,data = secs[nr]
137
+ s.crc = s.idam.crc = s.dam.crc = 0
138
+ s.idam.c, s.idam.h, s.idam.r, s.idam.n = c,h,r,n
139
+ s.dam.data = data
140
+
141
+ return t
142
+
143
+ @staticmethod
144
+ def disk_from_file(f, disk_offset: int) -> TrackDict:
145
+ f.seek(disk_offset)
146
+ to_track: TrackDict = dict()
147
+
148
+ header = struct.unpack('<16sB9xBBL', f.read(32))
149
+ disk_name, terminator, write_prot, media_flag, disk_size = header
150
+ track_table = list(struct.unpack('<160L', f.read(640)))
151
+ s_off = min(filter(lambda x: x != 0, track_table), default=672)
152
+ if s_off == 688:
153
+ track_table.extend(list(struct.unpack('<4L', f.read(16))))
154
+ elif s_off != 672:
155
+ raise error.Fatal("D88: Unsupported track table length.")
156
+
157
+ for track_index, track_offset in enumerate(track_table):
158
+ if track_offset == 0:
159
+ continue
160
+ f.seek(disk_offset + track_offset)
161
+ if f.tell() >= disk_offset + disk_size:
162
+ continue
163
+ cyl = track_index // 2
164
+ head = track_index % 2
165
+ t = D88.track_from_file(f, cyl, head, media_flag)
166
+ if t is not None:
167
+ to_track[cyl, head] = t
168
+
169
+ return to_track
170
+
171
+ @classmethod
172
+ def from_file(cls, name: str, _fmt, opts: OptDict) -> Image:
173
+
174
+ d88 = cls(name, _fmt)
175
+ d88.apply_r_opts(opts)
176
+
177
+ with open(name, "rb") as f:
178
+ f.seek(0, os.SEEK_END)
179
+ file_size = f.tell()
180
+ f.seek(0)
181
+ disk_offset, disk_index = 0, 0
182
+ while disk_offset < file_size:
183
+ if disk_index == d88.opts.index:
184
+ d88.to_track = D88.disk_from_file(f, disk_offset)
185
+ f.seek(disk_offset)
186
+ header = struct.unpack('<16sB9xBBL', f.read(32))
187
+ _, _, _, _, disk_size = header
188
+ disk_offset += disk_size
189
+ disk_index += 1
190
+
191
+ error.check(disk_index > 0,
192
+ f'D88: {d88.filename}: No valid disk found')
193
+
194
+ if disk_index > 1:
195
+ print(f'D88: {d88.filename}: {disk_index} disks in file; '
196
+ f'reading disk with index {d88.opts.index}')
197
+
198
+ error.check(d88.opts.index < disk_index,
199
+ f'D88: {d88.filename}: No disk with index '
200
+ f'{d88.opts.index} (valid indexes 0-{disk_index-1})')
201
+
202
+ return d88
203
+
204
+ def get_track(self, cyl: int, side: int) -> Optional[ibm.IBMTrack_Fixed]:
205
+ if (cyl,side) not in self.to_track:
206
+ return None
207
+ return self.to_track[cyl,side]
208
+
209
+ # Local variables:
210
+ # python-indent: 4
211
+ # End:
@@ -0,0 +1,45 @@
1
+ # greaseweazle/image/dcp.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
+ import struct
9
+
10
+ from diskstack._vendor.greaseweazle import error
11
+ from diskstack._vendor.greaseweazle.image.img import IMG_AutoFormat
12
+ from diskstack._vendor.greaseweazle.codec import codec
13
+ from .image import Image
14
+
15
+ class DCP(IMG_AutoFormat):
16
+
17
+ read_only = True
18
+
19
+ @staticmethod
20
+ def format_from_file(name: str) -> str:
21
+ return 'pc98.2hd'
22
+
23
+ def from_bytes(self, dat: bytes) -> None:
24
+
25
+ header = dat[:162]
26
+ pos = 162
27
+ fmt = self.fmt
28
+
29
+ for cyl, head in self.track_list():
30
+ if self.sides_swapped:
31
+ head ^= 1
32
+ track = fmt.mk_track(cyl, head)
33
+ if track is None:
34
+ continue
35
+ if cyl > 80:
36
+ break
37
+ if header[cyl * 2 + head] == 1:
38
+ pos += track.set_img_track(dat[pos:])
39
+ self.to_track[cyl,head] = track
40
+ elif header[cyl * 2 + head] != 0:
41
+ raise error.Fatal("DCP: Corrupt header.")
42
+
43
+ # Local variables:
44
+ # python-indent: 4
45
+ # End:
@@ -0,0 +1,53 @@
1
+ # greaseweazle/image/dim.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
+ import struct
9
+
10
+ from diskstack._vendor.greaseweazle import error
11
+ from diskstack._vendor.greaseweazle.image.img import IMG_AutoFormat
12
+ from diskstack._vendor.greaseweazle.codec import codec
13
+ from .image import Image
14
+
15
+ class DIM(IMG_AutoFormat):
16
+
17
+ read_only = True
18
+
19
+ @staticmethod
20
+ def format_from_file(name: str) -> str:
21
+
22
+ with open(name, "rb") as f:
23
+ header = f.read(256)
24
+
25
+ error.check(header[0xAB:0xB8] == b"DIFC HEADER ",
26
+ "DIM: Not a DIM file.")
27
+ media_byte, = struct.unpack('B255x', header)
28
+ if media_byte == 0:
29
+ format_str = 'pc98.2hd'
30
+ elif media_byte == 1:
31
+ format_str = 'pc98.2hs'
32
+ else:
33
+ raise error.Fatal("DIM: Unsupported format.")
34
+
35
+ return format_str
36
+
37
+ def from_bytes(self, dat: bytes) -> None:
38
+
39
+ pos = 256
40
+ fmt = self.fmt
41
+
42
+ for t in fmt.tracks:
43
+ cyl, head = t.cyl, t.head
44
+ if self.sides_swapped:
45
+ head ^= 1
46
+ track = fmt.mk_track(cyl, head)
47
+ if track is not None:
48
+ pos += track.set_img_track(dat[pos:])
49
+ self.to_track[cyl,head] = track
50
+
51
+ # Local variables:
52
+ # python-indent: 4
53
+ # End:
@@ -0,0 +1,166 @@
1
+ # greaseweazle/image/dmk.py
2
+ #
3
+ # Some of the code here is heavily inspired by Simon Owen's SAMdisk:
4
+ # https://simonowen.com/samdisk/
5
+ #
6
+ # Written & released by Keir Fraser <keir.xen@gmail.com>
7
+ #
8
+ # This is free and unencumbered software released into the public domain.
9
+ # See the file COPYING for more details, or visit <http://unlicense.org>.
10
+
11
+ from typing import Dict, Tuple, Optional, List, Union
12
+
13
+ import struct
14
+ import itertools as it
15
+ from bitarray import bitarray
16
+
17
+ from diskstack._vendor.greaseweazle import __version__
18
+ from diskstack._vendor.greaseweazle import error
19
+ from diskstack._vendor.greaseweazle.codec.ibm import ibm
20
+ from diskstack._vendor.greaseweazle.track import MasterTrack, PLLTrack
21
+ from .image import Image
22
+
23
+ class Encoding:
24
+
25
+ def __init__(self, len: int, mfm: bool, fm_step: int) -> None:
26
+ self.fm_step = fm_step
27
+ self.mfm = [False] * len
28
+ self.clock = bytearray(len)
29
+ self.cursor = 0
30
+ self.prev_mfm = mfm
31
+
32
+ def move_cursor(self, off: int) -> None:
33
+ while self.cursor < off:
34
+ self.mfm[self.cursor] = self.prev_mfm
35
+ self.cursor += 1
36
+
37
+ def _off(self, areas, data: bytes, off: int, step: int) -> None:
38
+ for i, v, c in areas:
39
+ min_off = max(off - i*step, 0)
40
+ while off > min_off and data[off-step] == v:
41
+ off -= step
42
+ self.clock[off] = c
43
+ self.move_cursor(off)
44
+
45
+ def mfm_off(self, data: bytes, off: int) -> None:
46
+ areas = [( 3, 0xa1, 0x0a), # IDAM A1, Clock 0A -> 4489
47
+ ( 12, 0x00, 0),
48
+ (512, 0x4e, 0),
49
+ ( 1, 0xfc, 0),
50
+ ( 3, 0xc2, 0x14), # IAM C2, Clock 14 -> 5224
51
+ ( 12, 0x00, 0),
52
+ (512, 0x4e, 0)]
53
+ self._off(areas, data, off, 1)
54
+ self.prev_mfm = True
55
+
56
+ def fm_off(self, data: bytes, off: int) -> None:
57
+ areas = [( 6, 0x00, 0),
58
+ (256, 0xff, 0),
59
+ ( 1, 0xfc, 0xd7), # IAM FC, Clock D7
60
+ ( 6, 0x00, 0)]
61
+ self._off(areas, data, off, self.fm_step)
62
+ self.prev_mfm = False
63
+
64
+
65
+ class DMKTrack:
66
+
67
+ def __init__(self, data: bytes) -> None:
68
+ time_per_rev = 0.2
69
+ dlen = (len(data) * 8) // 1000
70
+ if (80 <= dlen <= 85) or (160 <= dlen <= 170):
71
+ time_per_rev *= 5/6
72
+ self.track = MasterTrack(
73
+ bits = bytes(data),
74
+ time_per_rev = time_per_rev)
75
+
76
+ def master_track(self) -> MasterTrack:
77
+ return self.track
78
+
79
+
80
+ class DMK(Image):
81
+
82
+ read_only = True
83
+
84
+ def __init__(self, name: str, _fmt) -> None:
85
+ self.to_track: Dict[Tuple[int,int], DMKTrack] = dict()
86
+ self.filename = name
87
+
88
+ def from_bytes(self, dat: bytes) -> None:
89
+
90
+ ro, ncyl, tlen, flags = struct.unpack('<2BHB', dat[:5])
91
+ error.check(flags & 0x2f == 0,
92
+ 'DMK: Unrecognised flags value 0x%02x' % flags)
93
+ nside = 1 if flags & 0x10 else 2
94
+ fm_step = 1 if flags & 0xc0 else 2
95
+
96
+ o = 16 # skip disk header
97
+ for cyl in range(ncyl):
98
+ for head in range(nside):
99
+ offs = list(map(lambda x: ((x & 0x8000) == 0x8000,
100
+ (x & 0x3fff) - 128),
101
+ filter(lambda x: x != 0,
102
+ struct.unpack("<64H", dat[o:o+128]))))
103
+ # DMK Spec: "The IDAM offsets MUST be in ascending order with
104
+ # no unused or bad pointers." We clip at the first
105
+ # non-ascending offset, fixing some buggy DMK image imports
106
+ # (for example, which have 0x80 added to empty offset entries).
107
+ prev = -1
108
+ for i, (_, off) in enumerate(offs):
109
+ if off <= prev:
110
+ offs = offs[:i]
111
+ break
112
+ prev = off
113
+ data = dat[o+128:o+tlen]
114
+ o += tlen
115
+ if not offs:
116
+ continue
117
+ encoding = Encoding(len(data), offs[0][0], fm_step)
118
+ for mfm, off in offs:
119
+ if mfm:
120
+ encoding.mfm_off(data, off)
121
+ dam = data[off+8:off+64].find(b'\xa1' * 3)
122
+ error.check(dam != -1, 'DMK: No MFM DAM sync found')
123
+ dam += off+8
124
+ encoding.move_cursor(dam+8)
125
+ encoding.clock[dam+0] = 0x0a
126
+ encoding.clock[dam+1] = 0x0a
127
+ encoding.clock[dam+2] = 0x0a
128
+ else:
129
+ # Single density: Step-align the offset
130
+ step = encoding.fm_step
131
+ off &= ~(step-1)
132
+ if data[off] == 0:
133
+ off += step
134
+ encoding.fm_off(data, off)
135
+ encoding.clock[off] = 0xc7
136
+ for dam in range(off+8*step, off+64*step, step):
137
+ if (data[dam-step] == 0 and
138
+ data[dam] & 0xf0 == 0xf0):
139
+ break
140
+ encoding.move_cursor(dam+8*step)
141
+ encoding.clock[dam] = 0xc7
142
+
143
+ encoding.move_cursor(len(data))
144
+
145
+ enc_data, fm_mask = bytearray(), encoding.fm_step-1
146
+ for i in range(len(data)):
147
+ if not (mfm := encoding.mfm[i]) and (i & fm_mask) != 0:
148
+ continue
149
+ d = ibm.encode_list[data[i]]
150
+ c = ibm.encode_list[encoding.clock[i]]
151
+ x = struct.pack('>H', (c<<1)|d)
152
+ if mfm:
153
+ enc_data += ibm.mfm_encode(x)
154
+ else:
155
+ enc_data += ibm.doubler(ibm.fm_encode(x))
156
+
157
+ self.to_track[cyl,head] = DMKTrack(enc_data)
158
+
159
+ def get_track(self, cyl: int, side: int) -> Optional[MasterTrack]:
160
+ if (cyl,side) not in self.to_track:
161
+ return None
162
+ return self.to_track[cyl,side].master_track()
163
+
164
+ # Local variables:
165
+ # python-indent: 4
166
+ # End:
@@ -0,0 +1,31 @@
1
+ # greaseweazle/image/dsk.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 diskstack._vendor.greaseweazle.image.image import Image, OptDict
9
+ from diskstack._vendor.greaseweazle.image.img import IMG
10
+ from diskstack._vendor.greaseweazle.image.edsk import EDSK
11
+ from diskstack._vendor.greaseweazle.image.apridisk import Apridisk
12
+
13
+ class DSK(IMG):
14
+
15
+ @classmethod
16
+ def from_file(cls, name: str, fmt, opts: OptDict) -> Image:
17
+
18
+ with open(name, "rb") as f:
19
+ sig = f.read(24)
20
+
21
+ if sig[:8] == b'MV - CPC' or sig[:16] == b'EXTENDED CPC DSK':
22
+ return EDSK.from_file(name, fmt, opts)
23
+
24
+ if sig[:24] == b"ACT Apricot disk image\032\004":
25
+ return Apridisk.from_file(name, fmt, opts)
26
+
27
+ return IMG.from_file(name, fmt, opts)
28
+
29
+ # Local variables:
30
+ # python-indent: 4
31
+ # End: