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,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: diskstack
3
+ Version: 1.0.0
4
+ Summary: Merge several dumps of one floppy disk into the best possible image, and say what is still missing
5
+ Author-email: Christian Bosch <cbosch101@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Booyaka101/diskstack
8
+ Project-URL: Repository, https://github.com/Booyaka101/diskstack
9
+ Project-URL: Issues, https://github.com/Booyaka101/diskstack/issues
10
+ Project-URL: Changelog, https://github.com/Booyaka101/diskstack/blob/main/CHANGELOG.md
11
+ Keywords: floppy,flux,greaseweazle,supercard-pro,scp,preservation,disk-image,data-recovery,retrocomputing,amiga,mfm
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: System :: Archiving
20
+ Classifier: Topic :: System :: Recovery Tools
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ License-File: NOTICE
25
+ License-File: diskstack/_vendor/greaseweazle/COPYING
26
+ Requires-Dist: click>=8.1
27
+ Requires-Dist: bitarray>=2.9
28
+ Requires-Dist: crcmod>=1.7
29
+ Provides-Extra: test
30
+ Requires-Dist: pytest>=7; extra == "test"
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=7; extra == "dev"
33
+ Requires-Dist: ruff>=0.16; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # diskstack
37
+
38
+ Merge several dumps of one floppy disk into the best possible image, and get
39
+ told exactly what is still missing.
40
+
41
+ ## The loop
42
+
43
+ Read the disk twice, stack the dumps, and diskstack hands you back a
44
+ Greaseweazle command that re-reads only the tracks that are still bad:
45
+
46
+ ```
47
+ $ diskstack disk_a.scp disk_b.scp -o merged.img
48
+
49
+ 720 sectors: 714 clean (from 2 files), 6 unresolved
50
+
51
+ Tracks needing attention
52
+ cyl head clean voted unresolved missing sector ids still bad
53
+ 17 0 7 0 2 0 4,7
54
+ 18 0 7 0 2 0 4,7
55
+ 19 0 7 0 2 0 4,7
56
+
57
+ Re-read just these tracks, then run diskstack again with the new capture added:
58
+ gw read retry.scp --tracks=c=17-19:h=0
59
+ ```
60
+
61
+ Three tracks instead of eighty, so the drive spends seconds on the media
62
+ rather than minutes. Feed the new capture back in:
63
+
64
+ ```
65
+ $ gw read disk_c.scp --tracks=c=17-19:h=0
66
+ $ diskstack disk_a.scp disk_b.scp disk_c.scp -o merged.img
67
+
68
+ 720 sectors: 714 clean (from 3 files), 6 recovered by vote
69
+ Since the last report: 6 recovered.
70
+
71
+ Every sector confirmed by CRC. Nothing left to re-read.
72
+ ```
73
+
74
+ That run produced a file byte-identical to the original disk image. Repeat
75
+ until the unresolved count reaches zero or stops moving. diskstack reads the
76
+ report it is about to overwrite, so it tells you which of the two happened:
77
+ either how many sectors the new capture recovered, or that nothing changed and
78
+ another pass at the same settings is unlikely to. It also keeps the decoded
79
+ flux, so the second run only decodes the capture you just added: re-running
80
+ the three-capture merge above takes 0.4 seconds against 6.5 the first time.
81
+ The full session is in
82
+ [examples/console-session.txt](examples/console-session.txt) and the report it
83
+ wrote is in [examples/diskstack-report.json](examples/diskstack-report.json).
84
+
85
+ ## Install
86
+
87
+ ```
88
+ pip install diskstack
89
+ ```
90
+
91
+ Python 3.11 or newer. No hardware, no network, no configuration.
92
+
93
+ Until the PyPI upload lands, `pip install
94
+ git+https://github.com/Booyaka101/diskstack` gets you the same thing.
95
+
96
+ ## Use
97
+
98
+ ```
99
+ diskstack disk_a.scp disk_b.scp disk_c.img -o merged.img
100
+ ```
101
+
102
+ Inputs are two or more dumps of the same disk, in any mix of:
103
+
104
+ | what | extensions |
105
+ | --- | --- |
106
+ | flux | `.scp` (SuperCard Pro), `.raw` (KryoFlux streams), `.hfe` (HxC) |
107
+ | sector images | `.img`, `.ima`, `.st`, `.adf`, `.imd` |
108
+
109
+ For KryoFlux, name any one of the per-track stream files and the rest of the
110
+ set is picked up from the directory.
111
+
112
+ Output is a sector image: `.img`, `.ima`, `.st`, `.adf` or `.imd`. Flux goes in
113
+ but never comes out. Along with it you get `diskstack-report.json` next to the
114
+ image and the table above on stdout.
115
+
116
+ Formats understood are IBM FM and MFM (PC 160K through 2.88M, Atari ST) and
117
+ AmigaDOS MFM. `diskstack --list-formats` prints the list. Detection is
118
+ automatic; `--format ibm.720` overrides it.
119
+
120
+ The exit code is 0 when every sector is confirmed and 2 when something is still
121
+ unresolved, so a retry loop can be scripted.
122
+
123
+ ## How it decides
124
+
125
+ Every sector of every revolution of every input is a separate read attempt.
126
+ Attempts are grouped by the `(cylinder, head, sector id)` in their ID address
127
+ mark, never by where they sit in the flux, because two captures of the same
128
+ disk do not line up: the index phase differs and so does the motor speed.
129
+
130
+ Per sector, in order:
131
+
132
+ 1. **clean** - an attempt whose own CRC or checksum passes. Taken as is.
133
+ 2. **recovered by vote** - no attempt passed, so the attempts get recombined
134
+ into candidate payloads and the first one that satisfies a check value off
135
+ the disk wins. Three routes are tried, best evidence first:
136
+ `majority` votes byte by byte across every attempt, `per_source_majority`
137
+ collapses each input file to one payload first so a capture with more
138
+ revolutions cannot outvote the files that read the sector correctly, and
139
+ `cross_check` tries each individual read as it stands, which is what
140
+ recovers a sector whose payload was fine and whose own CRC bytes were the
141
+ damaged part. The CRC bytes came off the same damaged track, so they get
142
+ voted on too and the voted check value is tried alongside the ones read.
143
+ Ties go to the dump that read the rest of the disk best. The report says
144
+ which route it was.
145
+ 3. **unresolved** - the vote still failed. The largest cluster of attempts that
146
+ agree exactly is written out, the sector is listed in the table, and its
147
+ track goes into the re-read command.
148
+
149
+ A sector that is good in any input is never worse in the output. There is a
150
+ test for that.
151
+
152
+ Only a handful of reconstructions are tried, deliberately. A CRC16 accepts the
153
+ wrong payload once in 65536 tries, so every extra candidate thrown at the check
154
+ value buys recovery at the price of a small chance of confidently writing out
155
+ garbage. Three routes that each mean something beats a brute-force sweep over
156
+ the contested bytes.
157
+
158
+ The check value has to come off the disk, which means a stack of only sector
159
+ images can never reach step 2. `.img` and `.adf` files carry data and nothing
160
+ to verify it with, so identical bytes in two images prove only that both dumps
161
+ read the same thing. Mixing in one flux capture is what gives the vote
162
+ something to check against.
163
+
164
+ That distinction runs through the whole report. A sector is `verified` when
165
+ some read of it satisfied a check value off the disk, and a merge of sector
166
+ images alone never gets that, so the closing line says the sectors came from
167
+ inputs that carry no check value instead of claiming a CRC confirmed them. A
168
+ sector is `contested` when two dumps both looked good and held different
169
+ bytes. Nothing in a sector image can arbitrate that, so diskstack keeps the
170
+ best-ranked dump's copy, prints which sectors it happened to, and leaves the
171
+ judgement to you. It is what deliberate weak bits look like, and also what two
172
+ dumps of two different disks look like.
173
+
174
+ A sector the merge could not confirm is marked `unstable` if one capture read
175
+ it two different ways across its own revolutions. That is what weak bits look
176
+ like, and they are usually deliberate, so more passes over the disk will not
177
+ settle them. The stdout note says so next to the re-read command. Two `--pll`
178
+ settings reading one revolution two ways is a different thing, a decode that
179
+ went marginal rather than a disk that moved, and does not count.
180
+
181
+ Sectors Greaseweazle filled with `-=[BAD SECTOR]=-` are treated as failed
182
+ reads, not as data, so a `.img` from an earlier bad session still contributes
183
+ its good sectors. `--keep-filler` turns that off.
184
+
185
+ A truncated `.img` or `.adf` contributes only the sectors the file actually
186
+ holds bytes for. The readers pad a short image out to the format's length and
187
+ mark every sector they invented as CRC-clean, so without that check a dump that
188
+ stopped halfway would win the merge with 512 bytes of nothing per sector.
189
+
190
+ ## Options
191
+
192
+ ```
193
+ -o, --output PATH Merged sector image to write (.img, .ima,
194
+ .st, .adf, .imd). [required]
195
+ -r, --report PATH JSON report path [default: diskstack-
196
+ report.json beside the output]
197
+ --no-report Do not write the JSON report.
198
+ -f, --format TEXT Disk format, e.g. ibm.720 or amiga.amigados.
199
+ Detected from the inputs when not given.
200
+ --list-formats Print the disk formats diskstack understands
201
+ and exit.
202
+ --pll SPEC Flux PLL settings, e.g.
203
+ period=5:phase=60:lowpass=1.5. Repeat to
204
+ decode each capture several ways and let the
205
+ results compete.
206
+ --revs N Use only the first N revolutions of each
207
+ flux capture [default: all of them] [x>=1]
208
+ --keep-filler Trust sectors written as '-=[BAD SECTOR]=-'
209
+ filler instead of treating them as failed
210
+ reads.
211
+ --fill-unresolved [best|filler|zero]
212
+ What to write for a sector no input
213
+ confirmed: its best guess, bad-sector
214
+ filler, or zeroes. [default: best]
215
+ --no-vote Skip the byte-wise majority vote; keep only
216
+ sectors whose own CRC passes.
217
+ -j, --jobs N Worker processes for decoding flux [default:
218
+ one per core, up to 8] [x>=0]
219
+ --cache DIR Where to keep decoded flux between runs
220
+ [default: .diskstack-cache beside the
221
+ output]
222
+ --no-cache Decode every flux capture again instead of
223
+ reusing the decode a previous run stored.
224
+ --retry-name NAME Filename used in the printed gw read
225
+ command. [default: retry.scp]
226
+ -q, --quiet Print the summary and the re-read command,
227
+ no tables.
228
+ -V, --version Show the version and exit.
229
+ -h, --help Show this message and exit.
230
+ ```
231
+
232
+ Decoding flux is the slow part, so it runs one track per core by default. A
233
+ three-capture 40-cylinder merge takes about six seconds on eight cores against
234
+ half a minute on one. `--jobs 1` forces the single-process path, which is also
235
+ what happens automatically if the worker pool cannot start.
236
+
237
+ `--pll` is worth knowing about. Repeating it decodes each flux capture several
238
+ ways, and since two PLL settings disagree about different marginal bitcells,
239
+ they become independent attempts that the vote can use:
240
+
241
+ ```
242
+ diskstack a.scp b.scp -o merged.img --pll period=5:phase=60 --pll lowpass=1.5
243
+ ```
244
+
245
+ It applies to every flux container, `.scp`, `.raw` and `.hfe` alike.
246
+
247
+ Decoded flux is kept in `.diskstack-cache` beside the output, along with the
248
+ detected disk format, so a re-run only pays for the captures it has not seen.
249
+ An entry is named for a hash of the input's bytes and of every setting that
250
+ changes the decode, which means a fresh dump written over the same filename is
251
+ a new entry rather than a stale hit, and a capture that moved or was renamed
252
+ still counts as one it has seen. `--cache DIR` puts it somewhere else,
253
+ `--no-cache` turns it off, and deleting the directory costs nothing but the
254
+ next decode. It drops its least recently used entries once it passes 512 MB;
255
+ three captures of a 360K disk take 377 KB.
256
+
257
+ ## The report
258
+
259
+ `diskstack-report.json` carries one entry per sector of the disk: its status,
260
+ how many attempts it got, how many agreed, and which file and revolution each
261
+ contributing read came from. Plus per-input totals and the re-read trackspecs.
262
+ `schema` is versioned and starts at 1.
263
+
264
+ ```json
265
+ {
266
+ "cyl": 17, "head": 0, "sec_id": 4, "size": 512,
267
+ "status": "unresolved", "attempts": 6, "good": 0, "agreement": 3,
268
+ "discarded": 0, "unstable": false, "contested": false, "verified": false,
269
+ "method": null,
270
+ "sources": [{"path": "capture_a.scp", "rev": 0}]
271
+ }
272
+ ```
273
+
274
+ `method` names the route that rebuilt a recovered sector and is null for any
275
+ other status. `discarded` counts attempts thrown away because they decoded to
276
+ the wrong length for the sector. `verified` and `contested` are the two above,
277
+ and `totals` counts both across the disk.
278
+
279
+ `since_last_report` holds the `recovered`, `lost` and `still_bad` counts
280
+ against the report this run replaced, or null on the first run and whenever
281
+ the old report covers a different set of sectors. Each input carries `cached`,
282
+ saying whether its sectors were decoded on this run or reused from an earlier
283
+ one.
284
+
285
+ ## What it does not do
286
+
287
+ v1 is deliberately narrow. No GCR, so no Apple II and no Commodore. No writing
288
+ flux back out. No hardware access, so nothing here talks to a Greaseweazle
289
+ or a KryoFlux. No copy-protection preservation: this produces sector images,
290
+ which is the wrong container for weak bits and long tracks. No network calls.
291
+
292
+ ## Validation, honestly
293
+
294
+ The three-capture test builds genuine SuperCard Pro flux from a real PC floppy
295
+ image, damages each capture a different way, gives each its own index phase and
296
+ its own motor speed within 1.5 percent, and asserts the merged output is
297
+ byte-identical to the original. The AmigaDOS codec gets the same treatment one
298
+ track at a time, since the fixture disk is a PC one. That is a real decode of
299
+ real flux through Greaseweazle's own decoders, but the damage is synthetic. It
300
+ has not yet been run against dumps of a genuinely deteriorated disk.
301
+
302
+ If you have a disk that needs several passes, I would like to see the report.
303
+ Open an issue with the `diskstack-report.json` and what the disk is.
304
+
305
+ ## Tests
306
+
307
+ ```
308
+ pip install -e ".[dev]"
309
+ python tests/fixtures/make_fixtures.py # downloads 360 KB, writes 55 MB
310
+ python -m pytest tests -q
311
+ ruff check .
312
+ ```
313
+
314
+ The fixture builder needs the network once. Everything else runs offline.
315
+
316
+ ## Built on Greaseweazle
317
+
318
+ The flux and sector decoders are [Greaseweazle's](
319
+ https://github.com/keirf/greaseweazle), vendored under
320
+ `diskstack/_vendor/greaseweazle` because Greaseweazle is not published on PyPI.
321
+ It is public domain under the Unlicense. `tools/vendor.py` regenerates the tree
322
+ from a pinned commit and applies every local change; `NOTICE` lists them.
323
+
324
+ Runtime dependencies are `click`, `bitarray` and `crcmod`. The last two are
325
+ what the vendored decoders need.
326
+
327
+ ## License
328
+
329
+ MIT. See `LICENSE`, and `NOTICE` for the vendored code.
@@ -0,0 +1,119 @@
1
+ diskstack/__init__.py,sha256=ZU5oaRHH7jE5eYXj0ZG0asmB-1Z5rC4dRbNpNxYBVm8,125
2
+ diskstack/cache.py,sha256=h-HGf7IA26PlETg6O7MHmGkmJPfGV_UQ2fkIFkpKZvM,7956
3
+ diskstack/candidates.py,sha256=JWSgyGJk29jk9iGta_jtAiBkuZwYrJpzEcu3WhoDp5U,15987
4
+ diskstack/cli.py,sha256=Aqr9QfhIrmzDiawbxHewGEEs--y1ePBJ1aEGQ3N-KdU,14766
5
+ diskstack/errors.py,sha256=2cBdBva5nbkEZyouny4HEC1hyHyzsdnjPkmU5mdmPpQ,187
6
+ diskstack/filler.py,sha256=h7QUXDOCptWkqSffv6XgOsOBgBBNATTOt34OPttBvvg,1109
7
+ diskstack/formats.py,sha256=A8CVQOKhV6p5KQy5gZszp1fIL5eb341651crPHPB0wo,15645
8
+ diskstack/parallel.py,sha256=TwISTrTf5CIoANWX7WFapNUGdUvi0PpqSAIHVkD-VUA,2690
9
+ diskstack/report.py,sha256=969R5HjjTdOgt8fjvmPap8cbCYUXqZ2Y059BfHDeS9w,14022
10
+ diskstack/stack.py,sha256=qNI6zL-AC5bR3DNItXhIAXto0wcPCN7TROcZO72zu6U,11953
11
+ diskstack/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ diskstack/_vendor/greaseweazle/COPYING,sha256=fhLl30uuEsshWBuhV87SDhmGoFCN0Q0Oikq5pM-U6Fw,1211
13
+ diskstack/_vendor/greaseweazle/__init__.py,sha256=WQwc7i6Az4lXTHq87mu07KGYA4q8I86DhEvIPa6Dl9c,107
14
+ diskstack/_vendor/greaseweazle/error.py,sha256=iX0IepFCSzR1pTO9DVVZYA0mBk4U4DovPTspvH76wKo,420
15
+ diskstack/_vendor/greaseweazle/flux.py,sha256=x5YUkf-lZSt-rdpGRtEZE-6eFq0uu0eHg7FpAVWiae8,10940
16
+ diskstack/_vendor/greaseweazle/track.py,sha256=60ArZiY8RwxTtxamnMi7c3FyTzNYOKXRtB7FdRwOiw4,19948
17
+ diskstack/_vendor/greaseweazle/codec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ diskstack/_vendor/greaseweazle/codec/bitcell.py,sha256=9c2rlwGFK0Z44biRTKSylsc8I3wA0OJEQ9MLQZ2rPg4,3601
19
+ diskstack/_vendor/greaseweazle/codec/codec.py,sha256=aw_KbW3OWGvxv1mZJlrfx0bktv9tfdnywbYZCN15ZuE,13105
20
+ diskstack/_vendor/greaseweazle/codec/amiga/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ diskstack/_vendor/greaseweazle/codec/amiga/amigados.py,sha256=RLsBMw5Jb-lEukC1eByWQojojIAtvsT5tAUCauJsGq0,6725
22
+ diskstack/_vendor/greaseweazle/codec/apple2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ diskstack/_vendor/greaseweazle/codec/apple2/apple2_gcr.py,sha256=0eDsEi8-f2_rur63EFybdS9KbO7ASAVzhMANzpTyU_8,7053
24
+ diskstack/_vendor/greaseweazle/codec/commodore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ diskstack/_vendor/greaseweazle/codec/commodore/c64_gcr.py,sha256=bPMf_pAx5fLxTaLcxiMj521W1P8SF8ylls4YyNCuyjg,7345
26
+ diskstack/_vendor/greaseweazle/codec/datageneral/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ diskstack/_vendor/greaseweazle/codec/datageneral/datageneral.py,sha256=RvKUrcUF5QK0JhRGwGtFMoCcBDRIDYGeq9vUXCCz69Q,6962
28
+ diskstack/_vendor/greaseweazle/codec/hp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ diskstack/_vendor/greaseweazle/codec/hp/hp_mmfm.py,sha256=p8jqwqiLpwEfdKu3EEewiyjWB-HXjFU004JqiBCvC1k,8107
30
+ diskstack/_vendor/greaseweazle/codec/ibm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ diskstack/_vendor/greaseweazle/codec/ibm/ibm.py,sha256=w_il3GnAZW26LId9X-MSl4DSMIilNNmrV89nUq_RAkw,34932
32
+ diskstack/_vendor/greaseweazle/codec/macintosh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ diskstack/_vendor/greaseweazle/codec/macintosh/mac_gcr.py,sha256=2Jf9eZhEPhKC-7Wrp6OGmn89hMJGrDuAaEoP1A--h-c,7752
34
+ diskstack/_vendor/greaseweazle/codec/micropolis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ diskstack/_vendor/greaseweazle/codec/micropolis/micropolis.py,sha256=tnDAiJ5KBaP5FXT5Ef1jf5ySYDbhG7YP8VKubbe1F4I,7707
36
+ diskstack/_vendor/greaseweazle/codec/northstar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ diskstack/_vendor/greaseweazle/codec/northstar/northstar.py,sha256=M-nldnwo0B-jRveJX1Kgay5Hc29N0OBfRbP6NiAlxgA,6651
38
+ diskstack/_vendor/greaseweazle/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ diskstack/_vendor/greaseweazle/data/diskdefs.cfg,sha256=eequJOURVsKk-X6fmvfzFfj8yGIyLtIGtIN6wAtewgs,3448
40
+ diskstack/_vendor/greaseweazle/data/diskdefs_acorn.cfg,sha256=-o9Eu6_vjxhI0CZjGOn5ujLUaFfUFlZyg0brtWHYf-Q,1657
41
+ diskstack/_vendor/greaseweazle/data/diskdefs_akai.cfg,sha256=96wq0X2t3lSDWLG5fOh4yrj2sGypMUGKggyxRKQIjw8,374
42
+ diskstack/_vendor/greaseweazle/data/diskdefs_amiga.cfg,sha256=83IYH8_JwC_hYXkfdlejw3xgOwqCxOXrGLPTo6o5_6I,222
43
+ diskstack/_vendor/greaseweazle/data/diskdefs_apple2.cfg,sha256=JWOklXhZzE8W1mxHpCmDVus0e552mJ1fVE57SpdTJNM,690
44
+ diskstack/_vendor/greaseweazle/data/diskdefs_apricot.cfg,sha256=LYkiw7mmeSZ1GqiJwVh4wQEGWizGmqLlcL9E_Ivvh1I,347
45
+ diskstack/_vendor/greaseweazle/data/diskdefs_atari.cfg,sha256=YwMHbbhWTSD0O17Z1hXsVH2evunvQR3avqUC3DR9YZk,442
46
+ diskstack/_vendor/greaseweazle/data/diskdefs_atarist.cfg,sha256=B1bwX8GASwY5S4N_fHQUBQ8cC_ujP06lMw-F4IKMvC0,1035
47
+ diskstack/_vendor/greaseweazle/data/diskdefs_coco.cfg,sha256=eNwkP7lPNGUZMBrpLXNOeNwCCp-A3rYgCayuuJUh6kk,1474
48
+ diskstack/_vendor/greaseweazle/data/diskdefs_commodore.cfg,sha256=2eX6fgesLyAClkS2UV2naR1NjPw9_kCjUh9qFRxSPvo,1986
49
+ diskstack/_vendor/greaseweazle/data/diskdefs_datageneral.cfg,sha256=O_nbKIbKYK7WZTPKYN5r7m9ZkIryRC79KlLqRxDKn-g,101
50
+ diskstack/_vendor/greaseweazle/data/diskdefs_dec.cfg,sha256=JN084FacPBXyPeKr8ke04bf_veHsMWtLn_2pK1LXDj4,575
51
+ diskstack/_vendor/greaseweazle/data/diskdefs_dragon.cfg,sha256=M5Cu5_RfRhdCQ_tWlfDVLXJt1JEThWjt4JadBuLMXQo,890
52
+ diskstack/_vendor/greaseweazle/data/diskdefs_eagle.cfg,sha256=6ZB0BRYLULkv2LIVVUeHN1vPII92mIOPYKwO_ZMbtds,331
53
+ diskstack/_vendor/greaseweazle/data/diskdefs_ensoniq.cfg,sha256=Fsa41MjiTJeDmzY4syIdnd8Gh8RXIjPwJ4tRAfD3hfo,603
54
+ diskstack/_vendor/greaseweazle/data/diskdefs_epson.cfg,sha256=a8w_diUD8N246PUWj3EPMPf6Codh12qIEcuj8DtrCP0,1735
55
+ diskstack/_vendor/greaseweazle/data/diskdefs_gem.cfg,sha256=MgEnLPouLpnD9NooBUIZePh7OjZpjmgxCLw-a5KQpzk,227
56
+ diskstack/_vendor/greaseweazle/data/diskdefs_hp.cfg,sha256=lyhIrAmN4BFsGc0OFOWyjmYGmE0FGFKk35ylrAs0sgU,393
57
+ diskstack/_vendor/greaseweazle/data/diskdefs_ibm.cfg,sha256=PvUVaLNs5fFEa7z4lWMCqIoRBOePAZX0WmInT5KiZWg,2038
58
+ diskstack/_vendor/greaseweazle/data/diskdefs_kaypro.cfg,sha256=osHT7EyIjyZ1Frd_Zh3KnkpPqD_NMQjbr8VqdDt8g8k,762
59
+ diskstack/_vendor/greaseweazle/data/diskdefs_luxor.cfg,sha256=TUZIGdiIe46JilQmp-44iFL12K0TPYph1lvb2zQ51Jc,1348
60
+ diskstack/_vendor/greaseweazle/data/diskdefs_mac.cfg,sha256=nhFqsAkWCm8Cgvkbq4SvFQykEuwYXsdni-JsW2G0VeE,1255
61
+ diskstack/_vendor/greaseweazle/data/diskdefs_micropolis.cfg,sha256=PeHM3pUxYGwMVc5NWVOT2-lsYUGXeC6NXdr7T6AqgFM,1042
62
+ diskstack/_vendor/greaseweazle/data/diskdefs_mm1.cfg,sha256=lDwnY4yJHRuR-OrqEoiC1bsN-ccroava0d5j4O07mvk,1143
63
+ diskstack/_vendor/greaseweazle/data/diskdefs_msx.cfg,sha256=_msxB-vcSGUl_j94zqeGvgkgKXKLvSA6w7dVh1iHBqU,585
64
+ diskstack/_vendor/greaseweazle/data/diskdefs_northstar.cfg,sha256=P09c66m2mHs87Y-v95VEAju6UuQcYrAMeFQTz8POiUQ,493
65
+ diskstack/_vendor/greaseweazle/data/diskdefs_occ1.cfg,sha256=WfjsOiQWXVrrnybOhn3017pAF40csnkeTtHcNiWpaMM,305
66
+ diskstack/_vendor/greaseweazle/data/diskdefs_olivetti.cfg,sha256=CU61ghIg89DyyGNn0_wC6r-cf21s_xJ6CKKe99wzCU4,286
67
+ diskstack/_vendor/greaseweazle/data/diskdefs_pc98.cfg,sha256=_y1XZCOUBloEmgKF5dm-VO3Qzqw1sxkspOxjZDBeLxA,994
68
+ diskstack/_vendor/greaseweazle/data/diskdefs_raw.cfg,sha256=qgFZJzZqqGDwKU9c9DjckNtJpGHfhOuuDOrxzSo6vFo,374
69
+ diskstack/_vendor/greaseweazle/data/diskdefs_rm.cfg,sha256=rA0OjCacR3Wps5MzSBY723nPObMYEEMkqvrAr9NAbe0,1810
70
+ diskstack/_vendor/greaseweazle/data/diskdefs_sci.cfg,sha256=R4LKnz1N_f80bYJoqicvNqnP355fFCXxiv7hMB20Q0E,290
71
+ diskstack/_vendor/greaseweazle/data/diskdefs_sega.cfg,sha256=Oz0OBtobWxn1kE4fvFi4QvKaKZKb7Jp-ery0hJ4cxhc,178
72
+ diskstack/_vendor/greaseweazle/data/diskdefs_sharp.cfg,sha256=jK5c50KVX0sfF0SGnX-hvivyWQvIyS3qS-mcF8w7kRU,178
73
+ diskstack/_vendor/greaseweazle/data/diskdefs_thomson.cfg,sha256=MUEcT99wrVif5IXq0gNQC9dr15SB3cQ8y4sThKus5f0,1476
74
+ diskstack/_vendor/greaseweazle/data/diskdefs_tsc.cfg,sha256=AIKsFqNYC-p2fpNkkcOBD34C_syVCtqX7KXJABjT1Lo,1529
75
+ diskstack/_vendor/greaseweazle/data/diskdefs_xerox.cfg,sha256=KPfA1yzymL29zuDTpfkmtBKglaqJ9peErbkZyxbPZc4,951
76
+ diskstack/_vendor/greaseweazle/data/diskdefs_zx.cfg,sha256=BtgWO6IZNgq6HxeWxepESAojv9HNXyUxrS_FBErl2_Y,3295
77
+ diskstack/_vendor/greaseweazle/image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
+ diskstack/_vendor/greaseweazle/image/a2r.py,sha256=0jLiLgpTdD8iaeTMjxIlTnmDCnTZS4MSXC3c1FLlW7M,3536
79
+ diskstack/_vendor/greaseweazle/image/acorn.py,sha256=Qd-UltEDDQokjuoNmaglPutFFZnEgmjeM4k8zCsp9Ac,644
80
+ diskstack/_vendor/greaseweazle/image/adf.py,sha256=BoOZpsQqm3JP8O-7S6ECeFioQ2oB0NCtwPpk0K57mkw,396
81
+ diskstack/_vendor/greaseweazle/image/apple2.py,sha256=toCQz5-_7N-AhtksmKuwQ7yzujwdvzBdWw51gg7cY8I,460
82
+ diskstack/_vendor/greaseweazle/image/apridisk.py,sha256=edJsZrSQgFlTz9vbvLxArBsv35ebcabcUfLbC2rIK9g,3903
83
+ diskstack/_vendor/greaseweazle/image/caps.py,sha256=mUYxDSnY49vXlTmY_gS4tJbSGYj5aqU3xPA3hP4bFOU,15932
84
+ diskstack/_vendor/greaseweazle/image/d64.py,sha256=ikyCHGI7-wEyLJg842N4Cgu7ZWn9Ax20m4zJ2OQ52ak,1656
85
+ diskstack/_vendor/greaseweazle/image/d81.py,sha256=OGQ3oi9RslS5GCQueM4rDRPLoY3tBCld6u3uumpHYEE,688
86
+ diskstack/_vendor/greaseweazle/image/d88.py,sha256=TElUF3uYLVgfnR1PU9RSbooEqO3_Uuhuxe4jinhWBsc,7277
87
+ diskstack/_vendor/greaseweazle/image/dcp.py,sha256=yIoRhzMmT2mKKQZPOF_X0aR4MPOq7MLgRXSeM3ZojEU,1253
88
+ diskstack/_vendor/greaseweazle/image/dim.py,sha256=87B_TkqAMNuQRHyeY8UMoLE_BAZfR-SmA8_m142a6ao,1463
89
+ diskstack/_vendor/greaseweazle/image/dmk.py,sha256=oBI5XtDmVYWtQeYgBxOJGPqlDdt22spPRjZehOYpfbY,6214
90
+ diskstack/_vendor/greaseweazle/image/dsk.py,sha256=iKrdp3F8F9R2M9FlZ_bIUAq5kKXjCSgl0-OtjNgDZdg,985
91
+ diskstack/_vendor/greaseweazle/image/edsk.py,sha256=sYFhZcyuraIudTZkx6zQWlwQxhgBGbpzHqoH8gDjY24,23782
92
+ diskstack/_vendor/greaseweazle/image/fd.py,sha256=KhJ71Atxg39jJ9ifdqTyvvctfHBQ2lZyL3MMPxA6DXA,838
93
+ diskstack/_vendor/greaseweazle/image/fdi.py,sha256=IPTAM1zAAuEt6Gfp5Z6lxJyVxJsLzMTLyBnC5-mMqOo,1450
94
+ diskstack/_vendor/greaseweazle/image/hdm.py,sha256=EhtOGL-_xTTGwEp2DAou-xsL3MjeUReQDxYCNppCfY0,390
95
+ diskstack/_vendor/greaseweazle/image/hfe.py,sha256=CGEUSkbONCvLP6TbgPplWYdKpbbe1aUlFMqbbXK2WiM,27175
96
+ diskstack/_vendor/greaseweazle/image/image.py,sha256=bzNrSc8ddIQbDTNvgXmp0Dgl4AMKTxYci1D-UxJmkds,4292
97
+ diskstack/_vendor/greaseweazle/image/imd.py,sha256=URLenkW6fh84vxbSNZg9Yd8bX86O2o7rOdlHo_aEHOA,6761
98
+ diskstack/_vendor/greaseweazle/image/img.py,sha256=BVCRpN0su55z4OiISpbAlClaVb-ghCgjpsc0P0An2rA,3340
99
+ diskstack/_vendor/greaseweazle/image/kryoflux.py,sha256=vkr2NB0jXrlTlFN0xBJZg58ZWL6UvQvcE2tilhchYUk,9826
100
+ diskstack/_vendor/greaseweazle/image/mgt.py,sha256=EqoUOpMiQ9AmAgY24HUTxdV1nLFj1FnIBRQIdtTwLNk,389
101
+ diskstack/_vendor/greaseweazle/image/msa.py,sha256=ixEIBlMu_7lwOn-4ssxnCkzF-HCceaMw9NcnzRdPgSY,5464
102
+ diskstack/_vendor/greaseweazle/image/nfd.py,sha256=Il2-ZcZj1kBD7WVxEkX7M3x6XrFPVd2F7180nN1w7HI,4660
103
+ diskstack/_vendor/greaseweazle/image/nsi.py,sha256=LPQ3XDhWIptAUypLNuvDL1TjqDq2qQNrAzTjml7Kahk,1274
104
+ diskstack/_vendor/greaseweazle/image/scp.py,sha256=UO7MyqOm4huXJKa6Fwrc2qFZLrJ-xVk0WWTJJofYHmU,16891
105
+ diskstack/_vendor/greaseweazle/image/sf7.py,sha256=gOR1D0BBmiFALJ-zkEihNIpZEZzd4RxLqwhK-BrCeXw,393
106
+ diskstack/_vendor/greaseweazle/image/sharp2d.py,sha256=VkAVCtzAVj5liDv34lKfKzQPdD4gnz-ftE80CNmx4O4,398
107
+ diskstack/_vendor/greaseweazle/image/td0.py,sha256=DogtiUJgmK-b5z5foTv6wVnQj0xQ63GtT-4tifKzGtE,5031
108
+ diskstack/_vendor/greaseweazle/image/xdf.py,sha256=tvtKBBB5qwVWBxoGX0KA1C6wtoWM8u2159g7vqKXQ3E,390
109
+ diskstack/_vendor/greaseweazle/optimised/__init__.py,sha256=fixsy0ziIuGEx9cTHs0ubxe0IBW90C6tFZLP8uTE0jA,522
110
+ diskstack/_vendor/greaseweazle/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
+ diskstack/_vendor/greaseweazle/tools/util.py,sha256=mbvAf1ySO68pbgz_nQqDG6I8WQsLt96P-7x8UgrcZRU,4714
112
+ diskstack-1.0.0.dist-info/licenses/LICENSE,sha256=uf44leV1GCW7pqMmbDtnSEARGNLN6k_er7NOzwP1zZA,1072
113
+ diskstack-1.0.0.dist-info/licenses/NOTICE,sha256=SXxJ2QAHgRlt7dOP2m3edrWoRovcxOp8-YUjw-E4FII,2664
114
+ diskstack-1.0.0.dist-info/licenses/diskstack/_vendor/greaseweazle/COPYING,sha256=fhLl30uuEsshWBuhV87SDhmGoFCN0Q0Oikq5pM-U6Fw,1211
115
+ diskstack-1.0.0.dist-info/METADATA,sha256=3b8b-g09OORQFFKQ0rS94qE5OnrbEMKm0Wd33pDVcPw,15164
116
+ diskstack-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
117
+ diskstack-1.0.0.dist-info/entry_points.txt,sha256=yXB56_Aa3eeNz3oumReSQEV-XBR3kmDJ7B2gvKFE2o0,49
118
+ diskstack-1.0.0.dist-info/top_level.txt,sha256=x2MhHA534oHwVVdaqAhJpImQmD61VsNjbDqHiIYEKEc,10
119
+ diskstack-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ diskstack = diskstack.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christian Bosch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,55 @@
1
+ diskstack bundles part of Greaseweazle.
2
+
3
+ https://github.com/keirf/greaseweazle
4
+ commit 26690f89967d519e0106ab9566019a026b920bb4 (master, post-v1.23)
5
+
6
+ Greaseweazle is released into the public domain under the Unlicense. The full
7
+ text is in diskstack/_vendor/greaseweazle/COPYING.
8
+
9
+ Greaseweazle is not published on PyPI, so its flux and sector decoders are
10
+ vendored rather than depended on. tools/vendor.py regenerates the whole tree
11
+ from that commit; nothing under diskstack/_vendor/ is edited by hand.
12
+
13
+ What is vendored: src/greaseweazle/ minus usb.py, cli.py and tools/. TrackSet,
14
+ range_str and columnify are extracted from tools/util.py into a standalone
15
+ module because the rest of that file drives the USB hardware and needs
16
+ pyserial.
17
+
18
+ Local changes, all applied automatically by tools/vendor.py:
19
+
20
+ * imports rewritten from `greaseweazle.*` to
21
+ `diskstack._vendor.greaseweazle.*`
22
+
23
+ * greaseweazle/__init__.py is generated. It carries the vendored commit as
24
+ __version__ and imports codec.codec, because codec/codec.py imports every
25
+ codec at module scope and those codecs import back into codec.ibm.ibm, so
26
+ importing e.g. image.imd first lands on a partially-initialised module.
27
+ Upstream never hits this because its CLI always reaches codec.codec first.
28
+
29
+ * codec/ibm/ibm.py: the two `print("Unknown mark %02x")` warnings are
30
+ dropped. Damaged tracks produce them constantly and they would corrupt the
31
+ table diskstack prints on stdout.
32
+
33
+ * optimised/__init__.py: the two warnings about the missing C extension are
34
+ dropped, for the same reason. The pure-Python fallback is used and is fast
35
+ enough (about 75 ms per three-revolution track).
36
+
37
+ * image/imd.py: the reader now records the per-sector data-error flag
38
+ (rec & 4) and the data-unavailable code (rec == 0) as a failed CRC instead
39
+ of discarding both. An IMD full of error sectors is exactly the input
40
+ diskstack exists to merge.
41
+
42
+ * tools/util.py: TrackSet.TrackIter gets an __iter__ returning itself. An
43
+ iterator has to be iterable, and Python 3.13 enforces it, so without this
44
+ every `for track in diskdef.tracks` raises TypeError there.
45
+
46
+ * codec/codec.py: importlib.resources.open_text is replaced with
47
+ importlib.resources.files(...).joinpath(...).open(), which is the form
48
+ that still works once the package is installed as a wheel.
49
+
50
+ The test fixtures download Transylvania.img from the fluxfox test corpus:
51
+
52
+ https://github.com/dbalsom/fluxfox/tree/main/tests/images/transylvania
53
+
54
+ See the LICENSE.txt in that directory for its terms. Nothing from it is
55
+ redistributed here; tests/fixtures/make_fixtures.py fetches it on demand.
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
@@ -0,0 +1 @@
1
+ diskstack