mirror of
https://github.com/tjboldt/ProDOS-Utilities.git
synced 2024-11-28 12:51:35 +00:00
Prepare for initial alpha release 0.1.0
This commit is contained in:
parent
035d20033e
commit
c17bfa0b4d
140
README.md
140
README.md
@ -3,3 +3,143 @@ This project is just starting but is intended to be both a command line tool and
|
|||||||
|
|
||||||
## DISCLAIMER
|
## DISCLAIMER
|
||||||
Being a work in progress, be warned that this code is likely to corrupt drive images so be sure to have backups. Also, command line parameters are likely to change significantly in the future.
|
Being a work in progress, be warned that this code is likely to corrupt drive images so be sure to have backups. Also, command line parameters are likely to change significantly in the future.
|
||||||
|
|
||||||
|
## Current TODO list
|
||||||
|
1. Allow > 128 KB file support
|
||||||
|
2. Create/Delete directories
|
||||||
|
3. Add file/directory/basic tests
|
||||||
|
4. Add rename
|
||||||
|
5. Add in-place file/directory moves
|
||||||
|
|
||||||
|
## Example commands and output
|
||||||
|
|
||||||
|
### Specify drive image with -d DRIVEIMAGE (default command lists directory, default path is root of volume)
|
||||||
|
```
|
||||||
|
ProDOS-Utilities -d ~/Downloads/Total\ Replay\ v4.01.hdv
|
||||||
|
|
||||||
|
/TOTAL.REPLAY
|
||||||
|
|
||||||
|
NAME TYPE BLOCKS MODIFIED CREATED ENDFILE SUBTYPE
|
||||||
|
|
||||||
|
LAUNCHER.SYSTEM SYS 24 2021-FEB-18 01:25 2021-FEB-18 01:25 11416 8192
|
||||||
|
TITLE BIN 17 2019-JUN-29 13:04 2019-JUN-29 13:04 8184 8192
|
||||||
|
COVER BIN 17 2018-MAY-20 15:03 2018-MAY-20 15:03 8184 8192
|
||||||
|
HELP BIN 17 2019-MAY-20 10:24 2019-MAY-20 10:24 8184 8192
|
||||||
|
GAMES.CONF TXT 16 2021-FEB-18 01:12 2021-FEB-18 01:12 7344 32768
|
||||||
|
ATTRACT.CONF TXT 13 2021-FEB-18 01:22 2021-FEB-18 01:22 5748 32768
|
||||||
|
FX.CONF TXT 6 2021-JAN-03 23:00 2021-JAN-03 23:00 2443 32768
|
||||||
|
DFX.CONF TXT 5 2020-MAY-10 15:29 2020-MAY-10 15:29 1981 32768
|
||||||
|
PREFS.CONF TXT 1 2021-FEB-18 01:25 2021-FEB-18 01:25 512 32768
|
||||||
|
CREDITS TXT 1 2021-FEB-18 01:15 2021-FEB-18 01:15 449 32768
|
||||||
|
HELPTEXT TXT 1 2020-JUN-15 11:56 2020-JUN-15 11:56 206 32768
|
||||||
|
DECRUNCH BIN 1 2020-MAR-14 20:35 2020-MAR-14 20:35 303 512
|
||||||
|
JOYSTICK BIN 6 2020-APR-24 23:07 2020-APR-24 23:07 2370 2048
|
||||||
|
FINDER.DATA $C9 3 2020-JUN-27 17:40 2020-JUN-27 17:40 541 0
|
||||||
|
FINDER.ROOT $C9 1 2020-MAY-24 10:56 2020-MAY-24 10:56 9 0
|
||||||
|
TITLE.HGR DIR 23 2021-FEB-18 01:25 2021-FEB-18 01:25 11776 0
|
||||||
|
TITLE.DHGR DIR 4 2021-FEB-18 01:25 2021-FEB-18 01:25 2048 0
|
||||||
|
ACTION.HGR DIR 58 2021-FEB-18 01:25 2021-FEB-18 01:25 29696 0
|
||||||
|
ACTION.DHGR DIR 9 2021-FEB-18 01:25 2021-FEB-18 01:25 4608 0
|
||||||
|
ACTION.GR DIR 1 2021-FEB-18 01:25 2021-FEB-18 01:25 512 0
|
||||||
|
ARTWORK.SHR DIR 17 2021-FEB-18 01:25 2021-FEB-18 01:25 8704 0
|
||||||
|
ATTRACT DIR 26 2021-FEB-18 01:25 2021-FEB-18 01:25 13312 0
|
||||||
|
SS DIR 16 2021-FEB-18 01:25 2021-FEB-18 01:25 8192 0
|
||||||
|
DEMO DIR 15 2021-FEB-18 01:25 2021-FEB-18 01:25 7680 0
|
||||||
|
TITLE.ANIMATED DIR 1 2021-FEB-18 01:25 2021-FEB-18 01:25 512 0
|
||||||
|
ICONS DIR 1 2021-FEB-18 01:25 2021-FEB-18 01:25 512 0
|
||||||
|
FX DIR 16 2021-FEB-18 01:25 2021-FEB-18 01:25 8192 0
|
||||||
|
X DIR 26 2021-FEB-18 01:25 2021-FEB-18 01:25 13312 0
|
||||||
|
PRELAUNCH DIR 27 2021-FEB-18 01:25 2021-FEB-18 01:25 13824 0
|
||||||
|
GAMEHELP DIR 26 2021-FEB-18 01:25 2021-FEB-18 01:25 13312 0
|
||||||
|
|
||||||
|
BLOCKS FREE: 15 BLOCKS USED: 65520 TOTAL BLOCKS: 65535
|
||||||
|
```
|
||||||
|
|
||||||
|
### Specifying -p PATHNAME and -c COMMAND
|
||||||
|
|
||||||
|
```
|
||||||
|
ProDOS-Utilities -d ~/Downloads/Total\ Replay\ v4.01.hdv -c ls -p /total.replay/icons
|
||||||
|
|
||||||
|
/TOTAL.REPLAY/ICONS
|
||||||
|
|
||||||
|
NAME TYPE BLOCKS MODIFIED CREATED ENDFILE SUBTYPE
|
||||||
|
|
||||||
|
TR.ICONS $CA 8 2020-MAY-24 10:56 2020-MAY-24 10:56 3524 0
|
||||||
|
|
||||||
|
BLOCKS FREE: 15 BLOCKS USED: 65520 TOTAL BLOCKS: 65535
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a new ProDOS hard drive image
|
||||||
|
```
|
||||||
|
ProDOS-Utilities -d new.hdv -c create -b 65535
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hex dump a block with command readblock and block number (both decimal and hexadecimal input work)
|
||||||
|
```
|
||||||
|
ProDOS-Utilities -d new.hdv -c readblock -b 0
|
||||||
|
Block 0x0000 (0):
|
||||||
|
|
||||||
|
0000: 01 38 B0 03 4C 1C 09 78 86 43 C9 03 08 8A 29 70 .80.L..x.CI...)p
|
||||||
|
0010: 4A 4A 4A 4A 09 C0 85 49 A0 FF 84 48 28 C8 B1 48 JJJJ.@.I ..H(H1H
|
||||||
|
0020: D0 3A B0 0E A9 03 8D 00 08 E6 3D A5 49 48 A9 5B P:0.)....f=%IH)[
|
||||||
|
0030: 48 60 85 40 85 48 A0 5E B1 48 99 94 09 C8 C0 EB H`.@.H ^1H...H@k
|
||||||
|
0040: D0 F6 A2 06 BC 32 09 BD 39 09 99 F2 09 BD 40 09 Pv".<2.=9..r.=@.
|
||||||
|
0050: 9D 7F 0A CA 10 EE A9 09 85 49 A9 86 A0 00 C9 F9 ...J.n)..I). .Iy
|
||||||
|
0060: B0 2F 85 48 84 60 84 4A 84 4C 84 4E 84 47 C8 84 0/.H.`.J.L.N.GH.
|
||||||
|
0070: 42 C8 84 46 A9 0C 85 61 85 4B 20 27 09 B0 66 E6 BH.F)..a.K '.0ff
|
||||||
|
0080: 61 E6 61 E6 46 A5 46 C9 06 90 EF AD 00 0C 0D 01 afafF%FI..o-....
|
||||||
|
0090: 0C D0 52 A9 04 D0 02 A5 4A 18 6D 23 0C A8 90 0D .PR).P.%J.m#.(..
|
||||||
|
00A0: E6 4B A5 4B 4A B0 06 C9 0A F0 71 A0 04 84 4A AD fK%KJ0.I.pq ..J-
|
||||||
|
00B0: 20 09 29 0F A8 B1 4A D9 20 09 D0 DB 88 10 F6 A0 .).(1JY .P[..v
|
||||||
|
00C0: 16 B1 4A 4A 6D 1F 09 8D 1F 09 A0 11 B1 4A 85 46 .1JJm..... .1J.F
|
||||||
|
00D0: C8 B1 4A 85 47 A9 00 85 4A A0 1E 84 4B 84 61 C8 H1J.G)..J ..K.aH
|
||||||
|
00E0: 84 4D 20 27 09 B0 35 E6 61 E6 61 A4 4E E6 4E B1 .M '.05fafa$NfN1
|
||||||
|
00F0: 4A 85 46 B1 4C 85 47 11 4A D0 18 A2 01 A9 00 A8 J.F1L.G.JP.".).(
|
||||||
|
0100: 91 60 C8 D0 FB E6 61 EA EA CA 10 F4 CE 1F 09 F0 .`HP{fajjJ.tN..p
|
||||||
|
0110: 07 D0 D8 CE 1F 09 D0 CA 58 4C 00 20 4C 47 09 02 .PXN..PJXL. LG..
|
||||||
|
0120: 26 50 52 4F 44 4F 53 A5 60 85 44 A5 61 85 45 6C &PRODOS%`.D%a.El
|
||||||
|
0130: 48 00 08 1E 24 3F 45 47 76 F4 D7 D1 B6 4B B4 AC H...$?EGvtWQ6K4,
|
||||||
|
0140: A6 2B 18 60 4C BC 09 20 58 FC A0 14 B9 58 09 99 &+.`L<. X| .9X..
|
||||||
|
0150: B1 05 88 10 F7 4C 55 09 D5 CE C1 C2 CC C5 A0 D4 1...wLU.UNABLE T
|
||||||
|
0160: CF A0 CC CF C1 C4 A0 D0 D2 CF C4 CF D3 A5 53 29 O LOAD PRODOS%S)
|
||||||
|
0170: 03 2A 05 2B AA BD 80 C0 A9 2C A2 11 CA D0 FD E9 .*.+*=.@),".JP}i
|
||||||
|
0180: 01 D0 F7 A6 2B 60 A5 46 29 07 C9 04 29 03 08 0A .Pw&+`%F).I.)...
|
||||||
|
0190: 28 2A 85 3D A5 47 4A A5 46 6A 4A 4A 85 41 0A 85 (*.=%GJ%FjJJ.A..
|
||||||
|
01A0: 51 A5 45 85 27 A6 2B BD 89 C0 20 BC 09 E6 27 E6 Q%E.'&+=.@ <.f'f
|
||||||
|
01B0: 3D E6 3D B0 03 20 BC 09 BC 88 C0 60 A5 40 0A 85 =f=0. <.<.@`%@..
|
||||||
|
01C0: 53 A9 00 85 54 A5 53 85 50 38 E5 51 F0 14 B0 04 S)..T%S.P8eQp.0.
|
||||||
|
01D0: E6 53 90 02 C6 53 38 20 6D 09 A5 50 18 20 6F 09 fS..FS8 m.%P. o.
|
||||||
|
01E0: D0 E3 A0 7F 84 52 08 28 38 C6 52 F0 CE 18 08 88 Pc ..R.(8FRpN...
|
||||||
|
01F0: F0 F5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 pu..............
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export files (using .bas file extension coverts Applesoft to text file)
|
||||||
|
```
|
||||||
|
ProDOS-Utilities -d ../Apple2-IO-RPi/RaspberryPi/Apple2-IO-RPi.hdv -c get -o Update.Firmware.bas -p /APPLE2.IO.RPI/UPDATE.FIRMWARE; cat Update.Firmware.bas
|
||||||
|
10 HOME
|
||||||
|
100 PRINT CHR$ (4)"BLOAD AT28C64B.BIN,A$2000"
|
||||||
|
200 PRINT "Program the firmare in slot #"
|
||||||
|
300 INPUT SL
|
||||||
|
400 FW = 8192 + 256 * SL: REM Firmware source
|
||||||
|
500 PS = 49287 + SL * 16: REM Firmware page selection
|
||||||
|
600 EP = 49152 + SL * 256: REM EPROM location
|
||||||
|
900 HOME
|
||||||
|
1000 FOR PG = 0 TO 3
|
||||||
|
1004 PRINT : PRINT
|
||||||
|
1005 PRINT "Writing page "PG" to slot "SL"
|
||||||
|
1006 PRINT "_______________________________________"
|
||||||
|
1007 PRINT "_______________________________________";
|
||||||
|
1008 HTAB 1
|
||||||
|
1010 POKE PS,PG * 16 + 15: REM Set firmware page
|
||||||
|
1020 FOR X = 0 TO 255
|
||||||
|
1030 A = PEEK (FW + PG * 2048 + X)
|
||||||
|
1040 POKE EP + X,A
|
||||||
|
1041 B = PEEK (EP + X)
|
||||||
|
1042 IF (B < > A) THEN GOTO 1041
|
||||||
|
1045 HTAB (X / 256) * 39 + 1
|
||||||
|
1046 INVERSE : PRINT " ";: NORMAL
|
||||||
|
1050 NEXT X
|
||||||
|
1060 NEXT PG
|
||||||
|
1900 PRINT
|
||||||
|
2000 PRINT "Firmware Update Complete"
|
||||||
|
```
|
65
main.go
65
main.go
@ -9,6 +9,8 @@ import (
|
|||||||
"github.com/tjboldt/ProDOS-Utilities/prodos"
|
"github.com/tjboldt/ProDOS-Utilities/prodos"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const version = "0.1.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var fileName string
|
var fileName string
|
||||||
var pathName string
|
var pathName string
|
||||||
@ -20,20 +22,21 @@ func main() {
|
|||||||
var volumeName string
|
var volumeName string
|
||||||
var fileType int
|
var fileType int
|
||||||
var auxType int
|
var auxType int
|
||||||
flag.StringVar(&fileName, "driveimage", "", "A ProDOS format drive image")
|
flag.StringVar(&fileName, "d", "", "A ProDOS format drive image")
|
||||||
flag.StringVar(&pathName, "path", "", "Path name in ProDOS drive image")
|
flag.StringVar(&pathName, "p", "", "Path name in ProDOS drive image (default is root of volume)")
|
||||||
flag.StringVar(&command, "command", "ls", "Command to execute: ls, get, put, volumebitmap, readblock, writeblock, createvolume, delete")
|
flag.StringVar(&command, "c", "ls", "Command to execute: ls, get, put, rm, mkdir, readblock, writeblock, create")
|
||||||
flag.StringVar(&outFileName, "outfile", "", "Name of file to write")
|
flag.StringVar(&outFileName, "o", "", "Name of file to write")
|
||||||
flag.StringVar(&inFileName, "infile", "", "Name of file to read")
|
flag.StringVar(&inFileName, "i", "", "Name of file to read")
|
||||||
flag.IntVar(&volumeSize, "volumesize", 65535, "Number of blocks to create the volume with")
|
flag.IntVar(&volumeSize, "s", 65535, "Number of blocks to create the volume with (default 65535, 64 to 65535, 0x0040 to 0xFFFF hex input accepted)")
|
||||||
flag.StringVar(&volumeName, "volumename", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters")
|
flag.StringVar(&volumeName, "v", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters")
|
||||||
flag.IntVar(&blockNumber, "block", 0, "A block number to read/write from 0 to 65535")
|
flag.IntVar(&blockNumber, "b", 0, "A block number to read/write from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
||||||
flag.IntVar(&fileType, "type", 6, "ProDOS FileType: 4=txt, 6=bin, 252=bas, 255=sys etc.")
|
flag.IntVar(&fileType, "t", 6, "ProDOS FileType: 0x04 for TXT, 0x06 for BIN, 0xFA for BAS, 0xFF for SYS etc.")
|
||||||
flag.IntVar(&auxType, "aux", 0x2000, "ProDOS AuxType from 0 to 65535 (usually load address)")
|
flag.IntVar(&auxType, "a", 0x2000, "ProDOS AuxType from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if len(fileName) == 0 {
|
if len(fileName) == 0 {
|
||||||
fmt.Printf("Missing driveimage. Run with --help for more info.\n")
|
printReadme()
|
||||||
|
flag.PrintDefaults()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,22 +44,25 @@ func main() {
|
|||||||
case "ls":
|
case "ls":
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to open drive image %s:\n %s", fileName, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
|
pathName = strings.ToUpper(pathName)
|
||||||
volumeHeader, _, fileEntries := prodos.ReadDirectory(file, pathName)
|
volumeHeader, _, fileEntries := prodos.ReadDirectory(file, pathName)
|
||||||
prodos.DumpDirectory(volumeHeader, fileEntries)
|
if len(pathName) == 0 {
|
||||||
case "volumebitmap":
|
pathName = "/" + volumeHeader.VolumeName
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
|
||||||
if err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
volumeBitmap := prodos.ReadVolumeBitmap(file)
|
volumeBitmap := prodos.ReadVolumeBitmap(file)
|
||||||
prodos.DumpBlock(volumeBitmap)
|
freeBlocks := prodos.GetFreeBlockCount(volumeBitmap, volumeHeader.TotalBlocks)
|
||||||
|
prodos.DumpDirectory(freeBlocks, volumeHeader.TotalBlocks, pathName, fileEntries)
|
||||||
case "get":
|
case "get":
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to open drive image %s:\n %s", fileName, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
if len(pathName) == 0 {
|
if len(pathName) == 0 {
|
||||||
fmt.Println("Missing pathname")
|
fmt.Println("Missing pathname")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -82,14 +88,17 @@ func main() {
|
|||||||
case "put":
|
case "put":
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to open drive image %s:\n %s", fileName, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
if len(pathName) == 0 {
|
if len(pathName) == 0 {
|
||||||
fmt.Println("Missing pathname")
|
fmt.Println("Missing pathname")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
inFile, err := os.ReadFile(inFileName)
|
inFile, err := os.ReadFile(inFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to open input file %s: %s", inFileName, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err = prodos.WriteFile(file, pathName, fileType, auxType, inFile)
|
err = prodos.WriteFile(file, pathName, fileType, auxType, inFile)
|
||||||
@ -97,26 +106,34 @@ func main() {
|
|||||||
fmt.Printf("Failed to write file %s: %s", pathName, err)
|
fmt.Printf("Failed to write file %s: %s", pathName, err)
|
||||||
}
|
}
|
||||||
case "readblock":
|
case "readblock":
|
||||||
|
fmt.Printf("Block 0x%04X (%d):\n\n", blockNumber, blockNumber)
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to open drive image %s:\n %s", fileName, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
block := prodos.ReadBlock(file, blockNumber)
|
block := prodos.ReadBlock(file, blockNumber)
|
||||||
outFile, err := os.Create(outFileName)
|
prodos.DumpBlock(block)
|
||||||
|
case "create":
|
||||||
|
file, err := os.Create(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
fmt.Printf("failed to create file: %s\n", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
outFile.Write(block)
|
defer file.Close()
|
||||||
case "createvolume":
|
prodos.CreateVolume(file, volumeName, volumeSize)
|
||||||
prodos.CreateVolume(fileName, volumeName, volumeSize)
|
case "rm":
|
||||||
case "delete":
|
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to open drive image %s:\n %s", fileName, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
prodos.DeleteFile(file, pathName)
|
prodos.DeleteFile(file, pathName)
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Command %s not handle\n", command)
|
fmt.Printf("Invalid command: %s\n\n", command)
|
||||||
|
flag.PrintDefaults()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ func findFreeBlocks(volumeBitmap []byte, numberOfBlocks int) []int {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFreeBlockCount(volumeBitmap []byte, totalBlocks int) int {
|
func GetFreeBlockCount(volumeBitmap []byte, totalBlocks int) int {
|
||||||
freeBlockCount := 0
|
freeBlockCount := 0
|
||||||
|
|
||||||
for i := 0; i < totalBlocks; i++ {
|
for i := 0; i < totalBlocks; i++ {
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateVolume(fileName string, volumeName string, numberOfBlocks int) {
|
func CreateVolume(file *os.File, volumeName string, numberOfBlocks int) {
|
||||||
if numberOfBlocks > 65535 || numberOfBlocks < 64 {
|
if numberOfBlocks > 65535 || numberOfBlocks < 64 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -17,11 +17,6 @@ func CreateVolume(fileName string, volumeName string, numberOfBlocks int) {
|
|||||||
fmt.Println("bad name length")
|
fmt.Println("bad name length")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
file, err := os.Create(fileName)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("failed to create file: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
blankBlock := make([]byte, 512)
|
blankBlock := make([]byte, 512)
|
||||||
for i := 0; i < numberOfBlocks; i++ {
|
for i := 0; i < numberOfBlocks; i++ {
|
||||||
@ -87,8 +82,6 @@ func CreateVolume(fileName string, volumeName string, numberOfBlocks int) {
|
|||||||
// volume bit map starting at block 6
|
// volume bit map starting at block 6
|
||||||
volumeBitmap := createVolumeBitmap(numberOfBlocks)
|
volumeBitmap := createVolumeBitmap(numberOfBlocks)
|
||||||
writeVolumeBitmap(file, volumeBitmap)
|
writeVolumeBitmap(file, volumeBitmap)
|
||||||
|
|
||||||
file.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBootBlock() []byte {
|
func getBootBlock() []byte {
|
||||||
|
@ -22,12 +22,16 @@ func TestCreateVolume(t *testing.T) {
|
|||||||
t.Run(testname, func(t *testing.T) {
|
t.Run(testname, func(t *testing.T) {
|
||||||
fileName := os.TempDir() + "test-volume.hdv"
|
fileName := os.TempDir() + "test-volume.hdv"
|
||||||
defer os.Remove(fileName)
|
defer os.Remove(fileName)
|
||||||
CreateVolume(fileName, tt.wantVolumeName, tt.blocks)
|
file, err := os.Create(fileName)
|
||||||
|
|
||||||
file, err := os.Open(fileName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Errorf("failed to create file: %s\n", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
CreateVolume(file, tt.wantVolumeName, tt.blocks)
|
||||||
|
|
||||||
volumeHeader, _, fileEntries := ReadDirectory(file, "")
|
volumeHeader, _, fileEntries := ReadDirectory(file, "")
|
||||||
if volumeHeader.VolumeName != tt.wantVolumeName {
|
if volumeHeader.VolumeName != tt.wantVolumeName {
|
||||||
t.Errorf("got volume name %s, want %s", volumeHeader.VolumeName, tt.wantVolumeName)
|
t.Errorf("got volume name %s, want %s", volumeHeader.VolumeName, tt.wantVolumeName)
|
||||||
@ -40,7 +44,7 @@ func TestCreateVolume(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
volumeBitmap := ReadVolumeBitmap(file)
|
volumeBitmap := ReadVolumeBitmap(file)
|
||||||
freeBlockCount := getFreeBlockCount(volumeBitmap, tt.blocks)
|
freeBlockCount := GetFreeBlockCount(volumeBitmap, tt.blocks)
|
||||||
if freeBlockCount != tt.wantFreeBlocks {
|
if freeBlockCount != tt.wantFreeBlocks {
|
||||||
t.Errorf("got free blocks: %d, want %d", freeBlockCount, tt.wantFreeBlocks)
|
t.Errorf("got free blocks: %d, want %d", freeBlockCount, tt.wantFreeBlocks)
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,7 @@ func DumpDirectoryHeader(directoryHeader DirectoryHeader) {
|
|||||||
|
|
||||||
func DumpBlock(buffer []byte) {
|
func DumpBlock(buffer []byte) {
|
||||||
for i := 0; i < len(buffer); i += 16 {
|
for i := 0; i < len(buffer); i += 16 {
|
||||||
|
fmt.Printf("%04X: ", i)
|
||||||
for j := i; j < i+16; j++ {
|
for j := i; j < i+16; j++ {
|
||||||
fmt.Printf("%02X ", buffer[j])
|
fmt.Printf("%02X ", buffer[j])
|
||||||
}
|
}
|
||||||
@ -130,20 +131,33 @@ func DumpBlock(buffer []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DumpDirectory(volumeHeader VolumeHeader, fileEntries []FileEntry) {
|
func DumpDirectory(blocksFree int, totalBlocks int, path string, fileEntries []FileEntry) {
|
||||||
fmt.Printf("VOLUME: %s\n\n", volumeHeader.VolumeName)
|
fmt.Printf("%s\n\n", path)
|
||||||
fmt.Printf("NAME TYPE BLOCKS MODIFIED CREATED ENDFILE SUBTYPE\n\n")
|
fmt.Printf(" NAME TYPE BLOCKS MODIFIED CREATED ENDFILE SUBTYPE\n\n")
|
||||||
|
|
||||||
for i := 0; i < len(fileEntries); i++ {
|
for i := 0; i < len(fileEntries); i++ {
|
||||||
fmt.Printf("%-15s %s %7d %s %s %8d %8d\n",
|
var zeroTime = time.Time{}
|
||||||
|
var modifiedTime, createdTime string
|
||||||
|
if fileEntries[i].ModifiedTime == zeroTime {
|
||||||
|
modifiedTime = "<NO DATE> "
|
||||||
|
} else {
|
||||||
|
modifiedTime = TimeToString(fileEntries[i].ModifiedTime)
|
||||||
|
}
|
||||||
|
if fileEntries[i].CreationTime == zeroTime {
|
||||||
|
createdTime = "<NO DATE> "
|
||||||
|
} else {
|
||||||
|
createdTime = TimeToString(fileEntries[i].CreationTime)
|
||||||
|
}
|
||||||
|
fmt.Printf(" %-15s %s %7d %s %s %8d %8d\n",
|
||||||
fileEntries[i].FileName,
|
fileEntries[i].FileName,
|
||||||
FileTypeToString(fileEntries[i].FileType),
|
FileTypeToString(fileEntries[i].FileType),
|
||||||
fileEntries[i].BlocksUsed,
|
fileEntries[i].BlocksUsed,
|
||||||
TimeToString(fileEntries[i].ModifiedTime),
|
modifiedTime,
|
||||||
TimeToString(fileEntries[i].CreationTime),
|
createdTime,
|
||||||
fileEntries[i].EndOfFile,
|
fileEntries[i].EndOfFile,
|
||||||
fileEntries[i].AuxType,
|
fileEntries[i].AuxType,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
fmt.Printf("BLOCKS FREE: %5d BLOCKS USED: %5d TOTAL BLOCKS: %5d\n", blocksFree, totalBlocks-blocksFree, totalBlocks)
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,12 @@ func DateTimeToProDOS(dateTime time.Time) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DateTimeFromProDOS(buffer []byte) time.Time {
|
func DateTimeFromProDOS(buffer []byte) time.Time {
|
||||||
|
if buffer[0] == 0 &&
|
||||||
|
buffer[1] == 0 &&
|
||||||
|
buffer[2] == 0 &&
|
||||||
|
buffer[3] == 0 {
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
twoDigitYear := buffer[1] >> 1
|
twoDigitYear := buffer[1] >> 1
|
||||||
var year int
|
var year int
|
||||||
if twoDigitYear < 76 {
|
if twoDigitYear < 76 {
|
||||||
|
31
readme.go
Normal file
31
readme.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func printReadme() {
|
||||||
|
fmt.Printf("ProDOS-Utilities %s\n", version)
|
||||||
|
fmt.Println("https://github.com/tjboldt/ProDOS-Utilities")
|
||||||
|
fmt.Println(`
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Terence Boldt
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.`)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user