diskii 0.2.0__tar.gz
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.
- diskii-0.2.0/LICENSE +15 -0
- diskii-0.2.0/PKG-INFO +286 -0
- diskii-0.2.0/README.md +261 -0
- diskii-0.2.0/pyproject.toml +100 -0
- diskii-0.2.0/src/diskii/__init__.py +140 -0
- diskii-0.2.0/src/diskii/__main__.py +7 -0
- diskii-0.2.0/src/diskii/addressing.py +129 -0
- diskii-0.2.0/src/diskii/basic/__init__.py +81 -0
- diskii-0.2.0/src/diskii/basic/applesoft/__init__.py +11 -0
- diskii-0.2.0/src/diskii/basic/applesoft/detokenizer.py +176 -0
- diskii-0.2.0/src/diskii/basic/applesoft/tokenizer.py +399 -0
- diskii-0.2.0/src/diskii/basic/applesoft/validator.py +140 -0
- diskii-0.2.0/src/diskii/basic/common.py +359 -0
- diskii-0.2.0/src/diskii/basic/common_validator.py +540 -0
- diskii-0.2.0/src/diskii/basic/integer/__init__.py +11 -0
- diskii-0.2.0/src/diskii/basic/integer/detokenizer.py +400 -0
- diskii-0.2.0/src/diskii/basic/integer/tokenizer.py +442 -0
- diskii-0.2.0/src/diskii/basic/integer/validator.py +394 -0
- diskii-0.2.0/src/diskii/basic/tokens.py +338 -0
- diskii-0.2.0/src/diskii/basic_tokenizer.py +165 -0
- diskii-0.2.0/src/diskii/basic_tokens.py +19 -0
- diskii-0.2.0/src/diskii/cli/__init__.py +5 -0
- diskii-0.2.0/src/diskii/cli/commands/__init__.py +1 -0
- diskii-0.2.0/src/diskii/cli/commands/add.py +203 -0
- diskii-0.2.0/src/diskii/cli/commands/basic.py +212 -0
- diskii-0.2.0/src/diskii/cli/commands/convert.py +151 -0
- diskii-0.2.0/src/diskii/cli/commands/create.py +180 -0
- diskii-0.2.0/src/diskii/cli/commands/extract.py +203 -0
- diskii-0.2.0/src/diskii/cli/commands/info.py +268 -0
- diskii-0.2.0/src/diskii/cli/main.py +262 -0
- diskii-0.2.0/src/diskii/cli/utils.py +160 -0
- diskii-0.2.0/src/diskii/detection.py +422 -0
- diskii-0.2.0/src/diskii/disk_creator.py +370 -0
- diskii-0.2.0/src/diskii/dos32.py +390 -0
- diskii-0.2.0/src/diskii/dos33.py +385 -0
- diskii-0.2.0/src/diskii/dos_ordered_block_reader.py +286 -0
- diskii-0.2.0/src/diskii/dos_writer.py +501 -0
- diskii-0.2.0/src/diskii/exceptions.py +119 -0
- diskii-0.2.0/src/diskii/file_operations.py +242 -0
- diskii-0.2.0/src/diskii/file_reading.py +148 -0
- diskii-0.2.0/src/diskii/file_type_conversion.py +150 -0
- diskii-0.2.0/src/diskii/fileentry.py +599 -0
- diskii-0.2.0/src/diskii/image.py +637 -0
- diskii-0.2.0/src/diskii/prodos.py +605 -0
- diskii-0.2.0/src/diskii/prodos_ordered_sector_reader.py +293 -0
- diskii-0.2.0/src/diskii/prodos_types.py +279 -0
- diskii-0.2.0/src/diskii/prodos_writer.py +653 -0
diskii-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Seth Kushniryk
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
diskii-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: diskii
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: diskii - Apple II Disk Image Tool & Library
|
|
5
|
+
License: ISC
|
|
6
|
+
Keywords: apple2,apple-ii,disk-image,prodos,dos33,retro-computing
|
|
7
|
+
Author: Seth Kushniryk
|
|
8
|
+
Author-email: seth@kushniryk.com
|
|
9
|
+
Requires-Python: >=3.13
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: License :: OSI Approved :: ISC License (ISCL)
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: System :: Archiving
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: System :: Filesystems
|
|
20
|
+
Project-URL: Bug Reports, https://todo.sr.ht/~sethkush/diskii
|
|
21
|
+
Project-URL: Documentation, https://git.sr.ht/~sethkush/diskii/tree/master/item/docs
|
|
22
|
+
Project-URL: Homepage, https://git.sr.ht/~sethkush/diskii
|
|
23
|
+
Project-URL: Source, https://git.sr.ht/~sethkush/diskii
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# diskii - Apple II Disk Image Tool & Library
|
|
27
|
+
|
|
28
|
+
**diskii** is both a command-line utility and Python library for reading, writing, and manipulating Apple II disk images. It supports both DOS 3.3 and ProDOS filesystems across various image formats.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
### ✅ **Disk Image Reading**
|
|
33
|
+
- **Complete DOS 3.3 support**: Read catalogs, extract files, handle all file types (T/I/A/B/S/R)
|
|
34
|
+
- **Full ProDOS support**: Volume directories, subdirectories, all storage types (seedling/sapling/tree)
|
|
35
|
+
- **DOS 3.2 support**: Read 13-sector disk images (.d13 format)
|
|
36
|
+
- **Multiple image formats**: .dsk, .do, .po, .hdv, .d13
|
|
37
|
+
- **Automatic format detection**: Smart detection based on file extension and content analysis
|
|
38
|
+
|
|
39
|
+
### ✅ **File Operations**
|
|
40
|
+
- **File extraction**: Read any file from disk images to host filesystem
|
|
41
|
+
- **File writing**: Create new files on disk images with proper metadata
|
|
42
|
+
- **File type preservation**: Maintain Apple II file types and auxiliary information
|
|
43
|
+
- **Cross-format operations**: Copy files between DOS 3.3 and ProDOS images
|
|
44
|
+
|
|
45
|
+
### ✅ **Disk Image Creation**
|
|
46
|
+
- **Blank disk creation**: Create empty DOS 3.3, DOS 3.2, and ProDOS images
|
|
47
|
+
- **Multiple sizes supported**: Standard 140K disks up to 32MB ProDOS volumes
|
|
48
|
+
- **Proper filesystem initialization**: Correct VTOC, catalogs, and volume bitmaps
|
|
49
|
+
|
|
50
|
+
### ✅ **Advanced Features**
|
|
51
|
+
- **Hierarchical directory support**: Full ProDOS subdirectory navigation
|
|
52
|
+
- **Sparse file handling**: Efficient handling of ProDOS sparse files
|
|
53
|
+
- **Free space tracking**: Volume bitmap and VTOC management
|
|
54
|
+
- **BASIC (de)tokenization**: Convert between tokenized and text BASIC programs
|
|
55
|
+
- **BASIC syntax validation**: ROM-compliant syntax checking for Apple II BASIC programs
|
|
56
|
+
- **Robust error handling**: Graceful handling of corrupted or invalid images
|
|
57
|
+
- **Type-safe**: Full type annotations throughout
|
|
58
|
+
- **Comprehensive testing**: Extensive test suite with real disk images
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install diskii
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Or with Poetry:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
poetry add diskii
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Command Line Usage
|
|
73
|
+
|
|
74
|
+
diskii provides a comprehensive command-line interface for disk image manipulation:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Show disk information
|
|
78
|
+
diskii info mydisk.dsk
|
|
79
|
+
|
|
80
|
+
# Extract all files from a disk
|
|
81
|
+
diskii extract mydisk.po
|
|
82
|
+
|
|
83
|
+
# Extract specific files
|
|
84
|
+
diskii extract mydisk.dsk HELLO.BAS
|
|
85
|
+
|
|
86
|
+
# Add files to a disk
|
|
87
|
+
diskii add mydisk.po myfile.txt
|
|
88
|
+
|
|
89
|
+
# Create blank disk images
|
|
90
|
+
diskii create blank.po --name MYDISK
|
|
91
|
+
|
|
92
|
+
# Convert between sector orderings
|
|
93
|
+
diskii convert mydisk.dsk output.po
|
|
94
|
+
|
|
95
|
+
# BASIC program utilities
|
|
96
|
+
diskii basic detokenize HELLO.BAS
|
|
97
|
+
diskii basic tokenize myprogram.txt --variant applesoft
|
|
98
|
+
diskii basic validate myprogram.txt
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For detailed help on any command:
|
|
102
|
+
```bash
|
|
103
|
+
diskii --help
|
|
104
|
+
diskii <command> --help
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Python Library Usage
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
import diskii
|
|
111
|
+
|
|
112
|
+
# Open any disk image - format detected automatically
|
|
113
|
+
with diskii.open_disk_image("mydisk.dsk") as image:
|
|
114
|
+
# Get volume information
|
|
115
|
+
print(f"Volume: {image.get_volume_name()}")
|
|
116
|
+
print(f"Format: {image.format}")
|
|
117
|
+
|
|
118
|
+
# List all files
|
|
119
|
+
files = image.get_file_list()
|
|
120
|
+
for file_entry in files:
|
|
121
|
+
print(f"{file_entry.filename} ({file_entry.size} bytes)")
|
|
122
|
+
|
|
123
|
+
# Extract file
|
|
124
|
+
data = file_entry.read_data()
|
|
125
|
+
with open(file_entry.filename, 'wb') as f:
|
|
126
|
+
f.write(data)
|
|
127
|
+
|
|
128
|
+
# Create a new blank disk
|
|
129
|
+
diskii.disk_creator.create_blank_prodos_image("new_disk.po", "MY.DISK")
|
|
130
|
+
|
|
131
|
+
# Add files to existing disk (requires read_only=False)
|
|
132
|
+
with diskii.open_disk_image("mydisk.po", read_only=False) as image:
|
|
133
|
+
image.create_file("HELLO.TXT", 0x04, b"Hello from diskii!")
|
|
134
|
+
|
|
135
|
+
# BASIC program tokenization
|
|
136
|
+
import diskii
|
|
137
|
+
|
|
138
|
+
# Tokenize Applesoft BASIC program
|
|
139
|
+
program_text = '''10 HOME
|
|
140
|
+
20 PRINT "HELLO WORLD!"
|
|
141
|
+
30 END'''
|
|
142
|
+
|
|
143
|
+
tokenized = diskii.tokenize_applesoft(program_text)
|
|
144
|
+
detokenized = diskii.detokenize_applesoft(tokenized)
|
|
145
|
+
|
|
146
|
+
# Work with BASIC files on disk images
|
|
147
|
+
with diskii.open_disk_image("mydisk.po", read_only=False) as image:
|
|
148
|
+
# Save as tokenized BASIC program
|
|
149
|
+
image.create_file("HELLO.BAS", 0xFC, tokenized)
|
|
150
|
+
|
|
151
|
+
# Read BASIC file and detokenize automatically
|
|
152
|
+
files = image.get_file_list()
|
|
153
|
+
for file_entry in files:
|
|
154
|
+
if file_entry.is_basic_file():
|
|
155
|
+
plain_text = file_entry.read_as_text() # Auto-detokenizes
|
|
156
|
+
raw_tokens = file_entry.read_as_tokens() # Raw tokenized data
|
|
157
|
+
|
|
158
|
+
# BASIC syntax validation with ROM compliance
|
|
159
|
+
program_text = '''10 HOME
|
|
160
|
+
20 FOR I = 1 TO 10
|
|
161
|
+
30 PRINT "COUNT: "; I
|
|
162
|
+
40 NEXT I
|
|
163
|
+
50 END'''
|
|
164
|
+
|
|
165
|
+
# Validate Applesoft BASIC syntax
|
|
166
|
+
errors = diskii.validate_basic_syntax(program_text, "applesoft")
|
|
167
|
+
if not errors:
|
|
168
|
+
print("✅ Program syntax is valid!")
|
|
169
|
+
else:
|
|
170
|
+
for error in errors:
|
|
171
|
+
print(f"Line {error.line}: {error.message}")
|
|
172
|
+
|
|
173
|
+
# Advanced syntax validation with custom validator
|
|
174
|
+
validator = diskii.BASICSyntaxValidator("applesoft")
|
|
175
|
+
errors = validator.validate_program(program_text)
|
|
176
|
+
|
|
177
|
+
# Validate Integer BASIC programs
|
|
178
|
+
integer_program = '''10 HOME
|
|
179
|
+
20 FOR I = 1 TO 10
|
|
180
|
+
30 PRINT "COUNT: "; I
|
|
181
|
+
40 NEXT I
|
|
182
|
+
50 END'''
|
|
183
|
+
|
|
184
|
+
# Also test Integer BASIC tokenization functions
|
|
185
|
+
tokenized_integer = diskii.tokenize_integer_basic(integer_program)
|
|
186
|
+
detokenized_integer = diskii.detokenize_integer_basic(tokenized_integer)
|
|
187
|
+
|
|
188
|
+
errors = diskii.validate_basic_syntax(integer_program, "integer")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Supported Formats
|
|
192
|
+
|
|
193
|
+
| Extension | Description | Sector Ordering | Supported Filesystems |
|
|
194
|
+
|-----------|-------------|----------------|----------------------|
|
|
195
|
+
| `.dsk` | DOS order disk images (140KB) | DOS order | DOS 3.3, ProDOS |
|
|
196
|
+
| `.do` | DOS order disk images | DOS order | DOS 3.3, ProDOS |
|
|
197
|
+
| `.po` | ProDOS order disk images | ProDOS order | DOS 3.3, ProDOS |
|
|
198
|
+
| `.hdv` | ProDOS hard disk volumes (up to 32MB) | ProDOS order | ProDOS |
|
|
199
|
+
| `.d13` | DOS 3.2 13-sector images | DOS 3.2 order | DOS 3.2, ProDOS* |
|
|
200
|
+
|
|
201
|
+
*ProDOS on 13-sector images is theoretically possible but extremely rare in practice.
|
|
202
|
+
|
|
203
|
+
## Error Handling
|
|
204
|
+
|
|
205
|
+
diskii provides comprehensive error handling:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
try:
|
|
209
|
+
with diskii.open_disk_image("questionable.dsk") as image:
|
|
210
|
+
files = image.get_file_list()
|
|
211
|
+
except diskii.UnrecognizedFormatError:
|
|
212
|
+
print("Not a valid disk image")
|
|
213
|
+
except diskii.CorruptedImageError:
|
|
214
|
+
print("Image appears to be corrupted")
|
|
215
|
+
except diskii.AccessError:
|
|
216
|
+
print("Cannot access the image file")
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Examples
|
|
220
|
+
|
|
221
|
+
The `examples/` directory contains practical usage examples:
|
|
222
|
+
|
|
223
|
+
- **`directory_tree.py`**: Display disk contents in tree format
|
|
224
|
+
- **`file_info.py`**: Show detailed file metadata and type information
|
|
225
|
+
- **`create_files.py`**: Demonstrate creating files on disk images
|
|
226
|
+
- **`cross_format_copy.py`**: Copy files between ProDOS and DOS formats
|
|
227
|
+
- **`basic_tokenization.py`**: BASIC program tokenization and detokenization examples
|
|
228
|
+
|
|
229
|
+
## Requirements
|
|
230
|
+
|
|
231
|
+
- Python 3.13+
|
|
232
|
+
- No external dependencies for core functionality
|
|
233
|
+
|
|
234
|
+
## Development
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Install with development dependencies
|
|
238
|
+
poetry install --with dev,docs
|
|
239
|
+
|
|
240
|
+
# Run tests
|
|
241
|
+
poetry run pytest
|
|
242
|
+
|
|
243
|
+
# Run with coverage
|
|
244
|
+
poetry run pytest --cov=src/diskii
|
|
245
|
+
|
|
246
|
+
# Format code
|
|
247
|
+
poetry run ruff format src/ tests/
|
|
248
|
+
|
|
249
|
+
# Type checking
|
|
250
|
+
poetry run mypy src/
|
|
251
|
+
|
|
252
|
+
# Build documentation
|
|
253
|
+
cd docs && make html
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Planned Features
|
|
257
|
+
|
|
258
|
+
- **Advanced copy operations**: Batch file operations with progress reporting
|
|
259
|
+
- **CLI interface**: Command-line tools for disk manipulation
|
|
260
|
+
|
|
261
|
+
## Not Planned (Pull Requests Welcome)
|
|
262
|
+
|
|
263
|
+
- WOZ image format support
|
|
264
|
+
- 2MG image format support
|
|
265
|
+
- NIB image format support
|
|
266
|
+
- Pascal or CP/M filesystem support
|
|
267
|
+
|
|
268
|
+
## References
|
|
269
|
+
|
|
270
|
+
- [ProDOS Technical Reference](https://prodos8.com/docs/techref/file-organization/)
|
|
271
|
+
- [ProDOS Format Notes](https://ciderpress2.com/formatdoc/ProDOS-notes.html)
|
|
272
|
+
- [DOS 3.3 Format Notes](https://ciderpress2.com/formatdoc/DOS-notes.html)
|
|
273
|
+
- [Disk Image Format Reference](https://ciderpress2.com/formatdoc/Unadorned-notes.html)
|
|
274
|
+
- [Applesoft BASIC Tokenized File Format](http://justsolve.archiveteam.org/wiki/Applesoft_BASIC_tokenized_file)
|
|
275
|
+
- [Applesoft BASIC Programming Reference Manual](https://mirrors.apple2.org.za/ftp.apple.asimov.net/documentation/programming/basic/Applesoft%20BASIC%20Programming%20Reference%20Manual%20-%20Apple%20Computer.pdf)
|
|
276
|
+
- [BASIC Keywords and Tokens Reference](http://mirrors.apple2.org.za/apple.cabi.net/Languages.Programming/BASIC.keywords.tokens.txt)
|
|
277
|
+
- [Integer BASIC ROM Disassembly](https://6502disassembly.com/a2-rom/IntegerBASIC.html)
|
|
278
|
+
- [Applesoft BASIC ROM Disassembly](https://6502disassembly.com/a2-rom/Applesoft.html)
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
ISC License - see [LICENSE](LICENSE) file for details.
|
|
283
|
+
|
|
284
|
+
## Contributing
|
|
285
|
+
|
|
286
|
+
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
|
diskii-0.2.0/README.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# diskii - Apple II Disk Image Tool & Library
|
|
2
|
+
|
|
3
|
+
**diskii** is both a command-line utility and Python library for reading, writing, and manipulating Apple II disk images. It supports both DOS 3.3 and ProDOS filesystems across various image formats.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### ✅ **Disk Image Reading**
|
|
8
|
+
- **Complete DOS 3.3 support**: Read catalogs, extract files, handle all file types (T/I/A/B/S/R)
|
|
9
|
+
- **Full ProDOS support**: Volume directories, subdirectories, all storage types (seedling/sapling/tree)
|
|
10
|
+
- **DOS 3.2 support**: Read 13-sector disk images (.d13 format)
|
|
11
|
+
- **Multiple image formats**: .dsk, .do, .po, .hdv, .d13
|
|
12
|
+
- **Automatic format detection**: Smart detection based on file extension and content analysis
|
|
13
|
+
|
|
14
|
+
### ✅ **File Operations**
|
|
15
|
+
- **File extraction**: Read any file from disk images to host filesystem
|
|
16
|
+
- **File writing**: Create new files on disk images with proper metadata
|
|
17
|
+
- **File type preservation**: Maintain Apple II file types and auxiliary information
|
|
18
|
+
- **Cross-format operations**: Copy files between DOS 3.3 and ProDOS images
|
|
19
|
+
|
|
20
|
+
### ✅ **Disk Image Creation**
|
|
21
|
+
- **Blank disk creation**: Create empty DOS 3.3, DOS 3.2, and ProDOS images
|
|
22
|
+
- **Multiple sizes supported**: Standard 140K disks up to 32MB ProDOS volumes
|
|
23
|
+
- **Proper filesystem initialization**: Correct VTOC, catalogs, and volume bitmaps
|
|
24
|
+
|
|
25
|
+
### ✅ **Advanced Features**
|
|
26
|
+
- **Hierarchical directory support**: Full ProDOS subdirectory navigation
|
|
27
|
+
- **Sparse file handling**: Efficient handling of ProDOS sparse files
|
|
28
|
+
- **Free space tracking**: Volume bitmap and VTOC management
|
|
29
|
+
- **BASIC (de)tokenization**: Convert between tokenized and text BASIC programs
|
|
30
|
+
- **BASIC syntax validation**: ROM-compliant syntax checking for Apple II BASIC programs
|
|
31
|
+
- **Robust error handling**: Graceful handling of corrupted or invalid images
|
|
32
|
+
- **Type-safe**: Full type annotations throughout
|
|
33
|
+
- **Comprehensive testing**: Extensive test suite with real disk images
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install diskii
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or with Poetry:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
poetry add diskii
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Command Line Usage
|
|
48
|
+
|
|
49
|
+
diskii provides a comprehensive command-line interface for disk image manipulation:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Show disk information
|
|
53
|
+
diskii info mydisk.dsk
|
|
54
|
+
|
|
55
|
+
# Extract all files from a disk
|
|
56
|
+
diskii extract mydisk.po
|
|
57
|
+
|
|
58
|
+
# Extract specific files
|
|
59
|
+
diskii extract mydisk.dsk HELLO.BAS
|
|
60
|
+
|
|
61
|
+
# Add files to a disk
|
|
62
|
+
diskii add mydisk.po myfile.txt
|
|
63
|
+
|
|
64
|
+
# Create blank disk images
|
|
65
|
+
diskii create blank.po --name MYDISK
|
|
66
|
+
|
|
67
|
+
# Convert between sector orderings
|
|
68
|
+
diskii convert mydisk.dsk output.po
|
|
69
|
+
|
|
70
|
+
# BASIC program utilities
|
|
71
|
+
diskii basic detokenize HELLO.BAS
|
|
72
|
+
diskii basic tokenize myprogram.txt --variant applesoft
|
|
73
|
+
diskii basic validate myprogram.txt
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For detailed help on any command:
|
|
77
|
+
```bash
|
|
78
|
+
diskii --help
|
|
79
|
+
diskii <command> --help
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Python Library Usage
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
import diskii
|
|
86
|
+
|
|
87
|
+
# Open any disk image - format detected automatically
|
|
88
|
+
with diskii.open_disk_image("mydisk.dsk") as image:
|
|
89
|
+
# Get volume information
|
|
90
|
+
print(f"Volume: {image.get_volume_name()}")
|
|
91
|
+
print(f"Format: {image.format}")
|
|
92
|
+
|
|
93
|
+
# List all files
|
|
94
|
+
files = image.get_file_list()
|
|
95
|
+
for file_entry in files:
|
|
96
|
+
print(f"{file_entry.filename} ({file_entry.size} bytes)")
|
|
97
|
+
|
|
98
|
+
# Extract file
|
|
99
|
+
data = file_entry.read_data()
|
|
100
|
+
with open(file_entry.filename, 'wb') as f:
|
|
101
|
+
f.write(data)
|
|
102
|
+
|
|
103
|
+
# Create a new blank disk
|
|
104
|
+
diskii.disk_creator.create_blank_prodos_image("new_disk.po", "MY.DISK")
|
|
105
|
+
|
|
106
|
+
# Add files to existing disk (requires read_only=False)
|
|
107
|
+
with diskii.open_disk_image("mydisk.po", read_only=False) as image:
|
|
108
|
+
image.create_file("HELLO.TXT", 0x04, b"Hello from diskii!")
|
|
109
|
+
|
|
110
|
+
# BASIC program tokenization
|
|
111
|
+
import diskii
|
|
112
|
+
|
|
113
|
+
# Tokenize Applesoft BASIC program
|
|
114
|
+
program_text = '''10 HOME
|
|
115
|
+
20 PRINT "HELLO WORLD!"
|
|
116
|
+
30 END'''
|
|
117
|
+
|
|
118
|
+
tokenized = diskii.tokenize_applesoft(program_text)
|
|
119
|
+
detokenized = diskii.detokenize_applesoft(tokenized)
|
|
120
|
+
|
|
121
|
+
# Work with BASIC files on disk images
|
|
122
|
+
with diskii.open_disk_image("mydisk.po", read_only=False) as image:
|
|
123
|
+
# Save as tokenized BASIC program
|
|
124
|
+
image.create_file("HELLO.BAS", 0xFC, tokenized)
|
|
125
|
+
|
|
126
|
+
# Read BASIC file and detokenize automatically
|
|
127
|
+
files = image.get_file_list()
|
|
128
|
+
for file_entry in files:
|
|
129
|
+
if file_entry.is_basic_file():
|
|
130
|
+
plain_text = file_entry.read_as_text() # Auto-detokenizes
|
|
131
|
+
raw_tokens = file_entry.read_as_tokens() # Raw tokenized data
|
|
132
|
+
|
|
133
|
+
# BASIC syntax validation with ROM compliance
|
|
134
|
+
program_text = '''10 HOME
|
|
135
|
+
20 FOR I = 1 TO 10
|
|
136
|
+
30 PRINT "COUNT: "; I
|
|
137
|
+
40 NEXT I
|
|
138
|
+
50 END'''
|
|
139
|
+
|
|
140
|
+
# Validate Applesoft BASIC syntax
|
|
141
|
+
errors = diskii.validate_basic_syntax(program_text, "applesoft")
|
|
142
|
+
if not errors:
|
|
143
|
+
print("✅ Program syntax is valid!")
|
|
144
|
+
else:
|
|
145
|
+
for error in errors:
|
|
146
|
+
print(f"Line {error.line}: {error.message}")
|
|
147
|
+
|
|
148
|
+
# Advanced syntax validation with custom validator
|
|
149
|
+
validator = diskii.BASICSyntaxValidator("applesoft")
|
|
150
|
+
errors = validator.validate_program(program_text)
|
|
151
|
+
|
|
152
|
+
# Validate Integer BASIC programs
|
|
153
|
+
integer_program = '''10 HOME
|
|
154
|
+
20 FOR I = 1 TO 10
|
|
155
|
+
30 PRINT "COUNT: "; I
|
|
156
|
+
40 NEXT I
|
|
157
|
+
50 END'''
|
|
158
|
+
|
|
159
|
+
# Also test Integer BASIC tokenization functions
|
|
160
|
+
tokenized_integer = diskii.tokenize_integer_basic(integer_program)
|
|
161
|
+
detokenized_integer = diskii.detokenize_integer_basic(tokenized_integer)
|
|
162
|
+
|
|
163
|
+
errors = diskii.validate_basic_syntax(integer_program, "integer")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Supported Formats
|
|
167
|
+
|
|
168
|
+
| Extension | Description | Sector Ordering | Supported Filesystems |
|
|
169
|
+
|-----------|-------------|----------------|----------------------|
|
|
170
|
+
| `.dsk` | DOS order disk images (140KB) | DOS order | DOS 3.3, ProDOS |
|
|
171
|
+
| `.do` | DOS order disk images | DOS order | DOS 3.3, ProDOS |
|
|
172
|
+
| `.po` | ProDOS order disk images | ProDOS order | DOS 3.3, ProDOS |
|
|
173
|
+
| `.hdv` | ProDOS hard disk volumes (up to 32MB) | ProDOS order | ProDOS |
|
|
174
|
+
| `.d13` | DOS 3.2 13-sector images | DOS 3.2 order | DOS 3.2, ProDOS* |
|
|
175
|
+
|
|
176
|
+
*ProDOS on 13-sector images is theoretically possible but extremely rare in practice.
|
|
177
|
+
|
|
178
|
+
## Error Handling
|
|
179
|
+
|
|
180
|
+
diskii provides comprehensive error handling:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
try:
|
|
184
|
+
with diskii.open_disk_image("questionable.dsk") as image:
|
|
185
|
+
files = image.get_file_list()
|
|
186
|
+
except diskii.UnrecognizedFormatError:
|
|
187
|
+
print("Not a valid disk image")
|
|
188
|
+
except diskii.CorruptedImageError:
|
|
189
|
+
print("Image appears to be corrupted")
|
|
190
|
+
except diskii.AccessError:
|
|
191
|
+
print("Cannot access the image file")
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Examples
|
|
195
|
+
|
|
196
|
+
The `examples/` directory contains practical usage examples:
|
|
197
|
+
|
|
198
|
+
- **`directory_tree.py`**: Display disk contents in tree format
|
|
199
|
+
- **`file_info.py`**: Show detailed file metadata and type information
|
|
200
|
+
- **`create_files.py`**: Demonstrate creating files on disk images
|
|
201
|
+
- **`cross_format_copy.py`**: Copy files between ProDOS and DOS formats
|
|
202
|
+
- **`basic_tokenization.py`**: BASIC program tokenization and detokenization examples
|
|
203
|
+
|
|
204
|
+
## Requirements
|
|
205
|
+
|
|
206
|
+
- Python 3.13+
|
|
207
|
+
- No external dependencies for core functionality
|
|
208
|
+
|
|
209
|
+
## Development
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Install with development dependencies
|
|
213
|
+
poetry install --with dev,docs
|
|
214
|
+
|
|
215
|
+
# Run tests
|
|
216
|
+
poetry run pytest
|
|
217
|
+
|
|
218
|
+
# Run with coverage
|
|
219
|
+
poetry run pytest --cov=src/diskii
|
|
220
|
+
|
|
221
|
+
# Format code
|
|
222
|
+
poetry run ruff format src/ tests/
|
|
223
|
+
|
|
224
|
+
# Type checking
|
|
225
|
+
poetry run mypy src/
|
|
226
|
+
|
|
227
|
+
# Build documentation
|
|
228
|
+
cd docs && make html
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Planned Features
|
|
232
|
+
|
|
233
|
+
- **Advanced copy operations**: Batch file operations with progress reporting
|
|
234
|
+
- **CLI interface**: Command-line tools for disk manipulation
|
|
235
|
+
|
|
236
|
+
## Not Planned (Pull Requests Welcome)
|
|
237
|
+
|
|
238
|
+
- WOZ image format support
|
|
239
|
+
- 2MG image format support
|
|
240
|
+
- NIB image format support
|
|
241
|
+
- Pascal or CP/M filesystem support
|
|
242
|
+
|
|
243
|
+
## References
|
|
244
|
+
|
|
245
|
+
- [ProDOS Technical Reference](https://prodos8.com/docs/techref/file-organization/)
|
|
246
|
+
- [ProDOS Format Notes](https://ciderpress2.com/formatdoc/ProDOS-notes.html)
|
|
247
|
+
- [DOS 3.3 Format Notes](https://ciderpress2.com/formatdoc/DOS-notes.html)
|
|
248
|
+
- [Disk Image Format Reference](https://ciderpress2.com/formatdoc/Unadorned-notes.html)
|
|
249
|
+
- [Applesoft BASIC Tokenized File Format](http://justsolve.archiveteam.org/wiki/Applesoft_BASIC_tokenized_file)
|
|
250
|
+
- [Applesoft BASIC Programming Reference Manual](https://mirrors.apple2.org.za/ftp.apple.asimov.net/documentation/programming/basic/Applesoft%20BASIC%20Programming%20Reference%20Manual%20-%20Apple%20Computer.pdf)
|
|
251
|
+
- [BASIC Keywords and Tokens Reference](http://mirrors.apple2.org.za/apple.cabi.net/Languages.Programming/BASIC.keywords.tokens.txt)
|
|
252
|
+
- [Integer BASIC ROM Disassembly](https://6502disassembly.com/a2-rom/IntegerBASIC.html)
|
|
253
|
+
- [Applesoft BASIC ROM Disassembly](https://6502disassembly.com/a2-rom/Applesoft.html)
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
ISC License - see [LICENSE](LICENSE) file for details.
|
|
258
|
+
|
|
259
|
+
## Contributing
|
|
260
|
+
|
|
261
|
+
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "diskii"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "diskii - Apple II Disk Image Tool & Library"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Seth Kushniryk", email = "seth@kushniryk.com"}
|
|
7
|
+
]
|
|
8
|
+
license = {text = "ISC"}
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
keywords = ["apple2", "apple-ii", "disk-image", "prodos", "dos33", "retro-computing"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Intended Audience :: End Users/Desktop",
|
|
16
|
+
"License :: OSI Approved :: ISC License (ISCL)",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: System :: Archiving",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
"Topic :: System :: Filesystems",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
diskii = "diskii.cli:main"
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
"Homepage" = "https://git.sr.ht/~sethkush/diskii"
|
|
32
|
+
"Bug Reports" = "https://todo.sr.ht/~sethkush/diskii"
|
|
33
|
+
"Source" = "https://git.sr.ht/~sethkush/diskii"
|
|
34
|
+
"Documentation" = "https://git.sr.ht/~sethkush/diskii/tree/master/item/docs"
|
|
35
|
+
|
|
36
|
+
[tool.poetry]
|
|
37
|
+
packages = [{include = "diskii", from = "src"}]
|
|
38
|
+
|
|
39
|
+
[tool.poetry.dependencies]
|
|
40
|
+
python = ">=3.13"
|
|
41
|
+
|
|
42
|
+
[tool.poetry.group.dev.dependencies]
|
|
43
|
+
pytest = ">=7.0.0"
|
|
44
|
+
pytest-cov = ">=4.0.0"
|
|
45
|
+
ruff = ">=0.1.0"
|
|
46
|
+
mypy = ">=1.0.0"
|
|
47
|
+
|
|
48
|
+
[tool.poetry.group.docs.dependencies]
|
|
49
|
+
sphinx = ">=7.0.0"
|
|
50
|
+
sphinx-autodoc-typehints = ">=1.24.0"
|
|
51
|
+
sphinx-rtd-theme = ">=1.3.0"
|
|
52
|
+
myst-parser = ">=2.0.0"
|
|
53
|
+
|
|
54
|
+
[build-system]
|
|
55
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
56
|
+
build-backend = "poetry.core.masonry.api"
|
|
57
|
+
|
|
58
|
+
[tool.pytest.ini_options]
|
|
59
|
+
testpaths = ["tests"]
|
|
60
|
+
python_files = ["test_*.py", "*_test.py"]
|
|
61
|
+
python_classes = ["Test*"]
|
|
62
|
+
python_functions = ["test_*"]
|
|
63
|
+
addopts = [
|
|
64
|
+
"--strict-markers",
|
|
65
|
+
"--strict-config",
|
|
66
|
+
"--cov=src/diskii",
|
|
67
|
+
"--cov-report=term-missing",
|
|
68
|
+
"--cov-report=html:htmlcov",
|
|
69
|
+
"--cov-fail-under=80"
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[tool.ruff]
|
|
73
|
+
line-length = 88
|
|
74
|
+
target-version = "py313"
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint]
|
|
77
|
+
select = [
|
|
78
|
+
"E", # pycodestyle errors
|
|
79
|
+
"W", # pycodestyle warnings
|
|
80
|
+
"F", # pyflakes
|
|
81
|
+
"I", # isort
|
|
82
|
+
"B", # flake8-bugbear
|
|
83
|
+
"C4", # flake8-comprehensions
|
|
84
|
+
"UP", # pyupgrade
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
[tool.mypy]
|
|
88
|
+
python_version = "3.13"
|
|
89
|
+
warn_return_any = true
|
|
90
|
+
warn_unused_configs = true
|
|
91
|
+
disallow_untyped_defs = true
|
|
92
|
+
disallow_incomplete_defs = true
|
|
93
|
+
check_untyped_defs = true
|
|
94
|
+
disallow_untyped_decorators = true
|
|
95
|
+
no_implicit_optional = true
|
|
96
|
+
warn_redundant_casts = true
|
|
97
|
+
warn_unused_ignores = true
|
|
98
|
+
warn_no_return = true
|
|
99
|
+
warn_unreachable = true
|
|
100
|
+
strict_equality = true
|