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.
- diskstack/__init__.py +5 -0
- diskstack/_vendor/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/COPYING +24 -0
- diskstack/_vendor/greaseweazle/__init__.py +2 -0
- diskstack/_vendor/greaseweazle/codec/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/amiga/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/amiga/amigados.py +221 -0
- diskstack/_vendor/greaseweazle/codec/apple2/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/apple2/apple2_gcr.py +223 -0
- diskstack/_vendor/greaseweazle/codec/bitcell.py +117 -0
- diskstack/_vendor/greaseweazle/codec/codec.py +362 -0
- diskstack/_vendor/greaseweazle/codec/commodore/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/commodore/c64_gcr.py +229 -0
- diskstack/_vendor/greaseweazle/codec/datageneral/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/datageneral/datageneral.py +211 -0
- diskstack/_vendor/greaseweazle/codec/hp/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/hp/hp_mmfm.py +258 -0
- diskstack/_vendor/greaseweazle/codec/ibm/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/ibm/ibm.py +1005 -0
- diskstack/_vendor/greaseweazle/codec/macintosh/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/macintosh/mac_gcr.py +238 -0
- diskstack/_vendor/greaseweazle/codec/micropolis/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/micropolis/micropolis.py +233 -0
- diskstack/_vendor/greaseweazle/codec/northstar/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/northstar/northstar.py +210 -0
- diskstack/_vendor/greaseweazle/data/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/data/diskdefs.cfg +81 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_acorn.cfg +119 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_akai.cfg +27 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_amiga.cfg +17 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_apple2.cfg +31 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_apricot.cfg +25 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_atari.cfg +33 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_atarist.cfg +76 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_coco.cfg +98 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_commodore.cfg +127 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_datageneral.cfg +8 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_dec.cfg +37 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_dragon.cfg +61 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_eagle.cfg +25 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_ensoniq.cfg +39 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_epson.cfg +95 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_gem.cfg +15 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_hp.cfg +25 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_ibm.cfg +140 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_kaypro.cfg +58 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_luxor.cfg +96 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_mac.cfg +71 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_micropolis.cfg +73 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_mm1.cfg +69 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_msx.cfg +45 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_northstar.cfg +37 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_occ1.cfg +23 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_olivetti.cfg +18 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_pc98.cfg +69 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_raw.cfg +25 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_rm.cfg +98 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_sci.cfg +15 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_sega.cfg +13 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_sharp.cfg +13 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_thomson.cfg +78 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_tsc.cfg +81 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_xerox.cfg +59 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_zx.cfg +245 -0
- diskstack/_vendor/greaseweazle/error.py +19 -0
- diskstack/_vendor/greaseweazle/flux.py +315 -0
- diskstack/_vendor/greaseweazle/image/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/image/a2r.py +133 -0
- diskstack/_vendor/greaseweazle/image/acorn.py +28 -0
- diskstack/_vendor/greaseweazle/image/adf.py +15 -0
- diskstack/_vendor/greaseweazle/image/apple2.py +18 -0
- diskstack/_vendor/greaseweazle/image/apridisk.py +107 -0
- diskstack/_vendor/greaseweazle/image/caps.py +464 -0
- diskstack/_vendor/greaseweazle/image/d64.py +51 -0
- diskstack/_vendor/greaseweazle/image/d81.py +28 -0
- diskstack/_vendor/greaseweazle/image/d88.py +211 -0
- diskstack/_vendor/greaseweazle/image/dcp.py +45 -0
- diskstack/_vendor/greaseweazle/image/dim.py +53 -0
- diskstack/_vendor/greaseweazle/image/dmk.py +166 -0
- diskstack/_vendor/greaseweazle/image/dsk.py +31 -0
- diskstack/_vendor/greaseweazle/image/edsk.py +591 -0
- diskstack/_vendor/greaseweazle/image/fd.py +23 -0
- diskstack/_vendor/greaseweazle/image/fdi.py +41 -0
- diskstack/_vendor/greaseweazle/image/hdm.py +15 -0
- diskstack/_vendor/greaseweazle/image/hfe.py +742 -0
- diskstack/_vendor/greaseweazle/image/image.py +130 -0
- diskstack/_vendor/greaseweazle/image/imd.py +201 -0
- diskstack/_vendor/greaseweazle/image/img.py +111 -0
- diskstack/_vendor/greaseweazle/image/kryoflux.py +304 -0
- diskstack/_vendor/greaseweazle/image/mgt.py +15 -0
- diskstack/_vendor/greaseweazle/image/msa.py +153 -0
- diskstack/_vendor/greaseweazle/image/nfd.py +113 -0
- diskstack/_vendor/greaseweazle/image/nsi.py +47 -0
- diskstack/_vendor/greaseweazle/image/scp.py +471 -0
- diskstack/_vendor/greaseweazle/image/sf7.py +15 -0
- diskstack/_vendor/greaseweazle/image/sharp2d.py +15 -0
- diskstack/_vendor/greaseweazle/image/td0.py +141 -0
- diskstack/_vendor/greaseweazle/image/xdf.py +15 -0
- diskstack/_vendor/greaseweazle/optimised/__init__.py +22 -0
- diskstack/_vendor/greaseweazle/tools/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/tools/util.py +133 -0
- diskstack/_vendor/greaseweazle/track.py +494 -0
- diskstack/cache.py +210 -0
- diskstack/candidates.py +396 -0
- diskstack/cli.py +348 -0
- diskstack/errors.py +5 -0
- diskstack/filler.py +29 -0
- diskstack/formats.py +381 -0
- diskstack/parallel.py +73 -0
- diskstack/report.py +353 -0
- diskstack/stack.py +317 -0
- diskstack-1.0.0.dist-info/METADATA +329 -0
- diskstack-1.0.0.dist-info/RECORD +119 -0
- diskstack-1.0.0.dist-info/WHEEL +5 -0
- diskstack-1.0.0.dist-info/entry_points.txt +2 -0
- diskstack-1.0.0.dist-info/licenses/LICENSE +21 -0
- diskstack-1.0.0.dist-info/licenses/NOTICE +55 -0
- diskstack-1.0.0.dist-info/licenses/diskstack/_vendor/greaseweazle/COPYING +24 -0
- 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:
|