Add file deletion
This commit is contained in:
parent
7e623c530f
commit
a04603f1e2
|
@ -13,3 +13,11 @@
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
|
# Project specific
|
||||||
|
*.hdv
|
||||||
|
*.bin
|
||||||
|
*.xxd
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
ProDOS-Utilities
|
||||||
|
|
16
README.md
16
README.md
|
@ -2,15 +2,19 @@
|
||||||
This project is just starting but is intended to be both a command line tool and library to provide access to ProDOS based hard drive images. It is written in Go to be cross platform (Linux, Windows, macOS etc.). Functionality, naming and parameters are subject to change without notice. This project was started so I would be able to automate writing the firmware file into the drive image for one of my other projects [Apple2-IO-RPi](https://github.com/tjboldt/Apple2-IO-RPi).
|
This project is just starting but is intended to be both a command line tool and library to provide access to ProDOS based hard drive images. It is written in Go to be cross platform (Linux, Windows, macOS etc.). Functionality, naming and parameters are subject to change without notice. This project was started so I would be able to automate writing the firmware file into the drive image for one of my other projects [Apple2-IO-RPi](https://github.com/tjboldt/Apple2-IO-RPi).
|
||||||
|
|
||||||
## Current command line functionality
|
## Current command line functionality
|
||||||
1. Export files up to 128KB (only seedling and sapling files are supported)
|
1. Export files
|
||||||
2. List any directory
|
2. List any directory
|
||||||
3. Display volume bitmap
|
3. Display volume bitmap
|
||||||
|
4. Create new volume
|
||||||
|
5. Delete file
|
||||||
|
|
||||||
## Current library functionality
|
## Current library functionality
|
||||||
1. Read block
|
1. Read block
|
||||||
2. Write block
|
2. Write block
|
||||||
3. Read file (up to 128KB)
|
3. Read file
|
||||||
4. Read volume bitmap
|
4. Delete file
|
||||||
5. Write volume bitmap
|
5. Create new volume
|
||||||
6. Get list of file entries from any path
|
6. Read volume bitmap
|
||||||
7. Get volume header
|
7. Write volume bitmap
|
||||||
|
8. Get list of file entries from any path
|
||||||
|
9. Get volume header
|
||||||
|
|
11
main.go
11
main.go
|
@ -18,7 +18,7 @@ func main() {
|
||||||
var volumeName string
|
var volumeName string
|
||||||
flag.StringVar(&fileName, "driveimage", "", "A ProDOS format drive image")
|
flag.StringVar(&fileName, "driveimage", "", "A ProDOS format drive image")
|
||||||
flag.StringVar(&pathName, "path", "", "Path name in ProDOS drive image")
|
flag.StringVar(&pathName, "path", "", "Path name in ProDOS drive image")
|
||||||
flag.StringVar(&command, "command", "ls", "Command to execute: ls, get, put, volumebitmap, readblock, writeblock, create")
|
flag.StringVar(&command, "command", "ls", "Command to execute: ls, get, put, volumebitmap, readblock, writeblock, createvolume, delete")
|
||||||
flag.StringVar(&outFileName, "outfile", "export.bin", "Name of file to write")
|
flag.StringVar(&outFileName, "outfile", "export.bin", "Name of file to write")
|
||||||
flag.IntVar(&volumeSize, "volumesize", 65535, "Number of blocks to create the volume with")
|
flag.IntVar(&volumeSize, "volumesize", 65535, "Number of blocks to create the volume with")
|
||||||
flag.StringVar(&volumeName, "volumename", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters")
|
flag.StringVar(&volumeName, "volumename", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters")
|
||||||
|
@ -71,9 +71,14 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
outFile.Write(block)
|
outFile.Write(block)
|
||||||
case "create":
|
case "createvolume":
|
||||||
//fmt.Println("Create volume")
|
|
||||||
prodos.CreateVolume(fileName, volumeName, volumeSize)
|
prodos.CreateVolume(fileName, volumeName, volumeSize)
|
||||||
|
case "delete":
|
||||||
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
prodos.DeleteFile(file, pathName)
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Command %s not handle\n", command)
|
fmt.Printf("Command %s not handle\n", command)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -82,27 +82,55 @@ func MarkBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) {
|
||||||
bitToChange := blockNumber % 8
|
bitToChange := blockNumber % 8
|
||||||
byteToChange := blockNumber / 8
|
byteToChange := blockNumber / 8
|
||||||
|
|
||||||
byteToWrite := 0b11111111
|
byteToAnd := 0b11111111
|
||||||
|
|
||||||
switch bitToChange {
|
switch bitToChange {
|
||||||
case 0:
|
case 0:
|
||||||
byteToWrite = 0b01111111
|
byteToAnd = 0b01111111
|
||||||
case 1:
|
case 1:
|
||||||
byteToWrite = 0b10111111
|
byteToAnd = 0b10111111
|
||||||
case 2:
|
case 2:
|
||||||
byteToWrite = 0b11011111
|
byteToAnd = 0b11011111
|
||||||
case 3:
|
case 3:
|
||||||
byteToWrite = 0b11101111
|
byteToAnd = 0b11101111
|
||||||
case 4:
|
case 4:
|
||||||
byteToWrite = 0b11110111
|
byteToAnd = 0b11110111
|
||||||
case 5:
|
case 5:
|
||||||
byteToWrite = 0b11111011
|
byteToAnd = 0b11111011
|
||||||
case 6:
|
case 6:
|
||||||
byteToWrite = 0b11111101
|
byteToAnd = 0b11111101
|
||||||
case 7:
|
case 7:
|
||||||
byteToWrite = 0b11111110
|
byteToAnd = 0b11111110
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Printf("blockNumber: $%04X byteToWrite: 0b%08b volumeBitmap: $%02X byteToChange: $%04X\n", blockNumber, byteToWrite, volumeBitmap[byteToChange], byteToChange)
|
//fmt.Printf("blockNumber: $%04X byteToWrite: 0b%08b volumeBitmap: $%02X byteToChange: $%04X\n", blockNumber, byteToWrite, volumeBitmap[byteToChange], byteToChange)
|
||||||
volumeBitmap[byteToChange] &= byte(byteToWrite)
|
volumeBitmap[byteToChange] &= byte(byteToAnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FreeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) {
|
||||||
|
bitToChange := blockNumber % 8
|
||||||
|
byteToChange := blockNumber / 8
|
||||||
|
|
||||||
|
byteToOr := 0b00000000
|
||||||
|
|
||||||
|
switch bitToChange {
|
||||||
|
case 0:
|
||||||
|
byteToOr = 0b10000000
|
||||||
|
case 1:
|
||||||
|
byteToOr = 0b01000000
|
||||||
|
case 2:
|
||||||
|
byteToOr = 0b00100000
|
||||||
|
case 3:
|
||||||
|
byteToOr = 0b00010000
|
||||||
|
case 4:
|
||||||
|
byteToOr = 0b00001000
|
||||||
|
case 5:
|
||||||
|
byteToOr = 0b00000100
|
||||||
|
case 6:
|
||||||
|
byteToOr = 0b00000010
|
||||||
|
case 7:
|
||||||
|
byteToOr = 0b00000001
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeBitmap[byteToChange] |= byte(byteToOr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ type VolumeHeader struct {
|
||||||
NextBlock int
|
NextBlock int
|
||||||
EntryLength int
|
EntryLength int
|
||||||
EntriesPerBlock int
|
EntriesPerBlock int
|
||||||
|
MinVersion int
|
||||||
|
Version int
|
||||||
}
|
}
|
||||||
|
|
||||||
type DirectoryHeader struct {
|
type DirectoryHeader struct {
|
||||||
|
@ -34,16 +36,21 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileEntry struct {
|
type FileEntry struct {
|
||||||
StorageType int
|
StorageType int
|
||||||
FileName string
|
FileName string
|
||||||
FileType int
|
FileType int
|
||||||
CreationTime time.Time
|
CreationTime time.Time
|
||||||
StartingBlock int
|
KeyPointer int
|
||||||
BlocksUsed int
|
Version int
|
||||||
EndOfFile int
|
MinVersion int
|
||||||
Access int
|
BlocksUsed int
|
||||||
AuxType int
|
EndOfFile int
|
||||||
ModifiedTime time.Time
|
Access int
|
||||||
|
AuxType int
|
||||||
|
ModifiedTime time.Time
|
||||||
|
HeaderPointer int
|
||||||
|
DirectoryBlock int
|
||||||
|
DirectoryOffset int
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadDirectory(file *os.File, path string) (VolumeHeader, []FileEntry) {
|
func ReadDirectory(file *os.File, path string) (VolumeHeader, []FileEntry) {
|
||||||
|
@ -95,14 +102,14 @@ func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int,
|
||||||
buffer = ReadBlock(file, nextBlock)
|
buffer = ReadBlock(file, nextBlock)
|
||||||
nextBlock = int(buffer[2]) + int(buffer[3])*256
|
nextBlock = int(buffer[2]) + int(buffer[3])*256
|
||||||
}
|
}
|
||||||
fileEntry := parseFileEntry(buffer[entryOffset : entryOffset+40])
|
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset)
|
||||||
//DumpFileEntry(fileEntry)
|
//DumpFileEntry(fileEntry)
|
||||||
|
|
||||||
if fileEntry.StorageType != StorageDeleted {
|
if fileEntry.StorageType != StorageDeleted {
|
||||||
if matchedDirectory {
|
if matchedDirectory {
|
||||||
fileEntries[activeEntries] = fileEntry
|
fileEntries[activeEntries] = fileEntry
|
||||||
} else if !matchedDirectory && fileEntry.FileType == 15 && paths[currentPath+1] == fileEntry.FileName {
|
} else if !matchedDirectory && fileEntry.FileType == 15 && paths[currentPath+1] == fileEntry.FileName {
|
||||||
return getFileEntriesInDirectory(file, fileEntry.StartingBlock, currentPath+1, paths)
|
return getFileEntriesInDirectory(file, fileEntry.KeyPointer, currentPath+1, paths)
|
||||||
}
|
}
|
||||||
activeEntries++
|
activeEntries++
|
||||||
if matchedDirectory && activeEntries == directoryHeader.ActiveFileCount {
|
if matchedDirectory && activeEntries == directoryHeader.ActiveFileCount {
|
||||||
|
@ -115,7 +122,7 @@ func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFileEntry(buffer []byte) FileEntry {
|
func parseFileEntry(buffer []byte, blockNumber int, entryOffset int) FileEntry {
|
||||||
storageType := int(buffer[0] >> 4)
|
storageType := int(buffer[0] >> 4)
|
||||||
fileNameLength := int(buffer[0] & 15)
|
fileNameLength := int(buffer[0] & 15)
|
||||||
fileName := string(buffer[1 : fileNameLength+1])
|
fileName := string(buffer[1 : fileNameLength+1])
|
||||||
|
@ -124,26 +131,70 @@ func parseFileEntry(buffer []byte) FileEntry {
|
||||||
blocksUsed := int(buffer[19]) + int(buffer[20])*256
|
blocksUsed := int(buffer[19]) + int(buffer[20])*256
|
||||||
endOfFile := int(buffer[21]) + int(buffer[22])*256 + int(buffer[23])*65536
|
endOfFile := int(buffer[21]) + int(buffer[22])*256 + int(buffer[23])*65536
|
||||||
creationTime := DateTimeFromProDOS(buffer[24:28])
|
creationTime := DateTimeFromProDOS(buffer[24:28])
|
||||||
|
version := int(buffer[28])
|
||||||
|
minVersion := int(buffer[29])
|
||||||
access := int(buffer[30])
|
access := int(buffer[30])
|
||||||
auxType := int(buffer[31]) + int(buffer[32])*256
|
auxType := int(buffer[31]) + int(buffer[32])*256
|
||||||
modifiedTime := DateTimeFromProDOS((buffer[33:37]))
|
modifiedTime := DateTimeFromProDOS((buffer[33:37]))
|
||||||
|
headerPointer := int(buffer[0x25]) + int(buffer[0x26])*256
|
||||||
|
|
||||||
fileEntry := FileEntry{
|
fileEntry := FileEntry{
|
||||||
StorageType: storageType,
|
StorageType: storageType,
|
||||||
FileName: fileName,
|
FileName: fileName,
|
||||||
FileType: fileType,
|
FileType: fileType,
|
||||||
CreationTime: creationTime,
|
CreationTime: creationTime,
|
||||||
StartingBlock: startingBlock,
|
Version: version,
|
||||||
BlocksUsed: blocksUsed,
|
MinVersion: minVersion,
|
||||||
EndOfFile: endOfFile,
|
KeyPointer: startingBlock,
|
||||||
Access: access,
|
BlocksUsed: blocksUsed,
|
||||||
AuxType: auxType,
|
EndOfFile: endOfFile,
|
||||||
ModifiedTime: modifiedTime,
|
Access: access,
|
||||||
|
AuxType: auxType,
|
||||||
|
ModifiedTime: modifiedTime,
|
||||||
|
HeaderPointer: headerPointer,
|
||||||
|
DirectoryBlock: blockNumber,
|
||||||
|
DirectoryOffset: entryOffset,
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileEntry
|
return fileEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeFileEntry(file *os.File, fileEntry FileEntry) {
|
||||||
|
buffer := make([]byte, 39)
|
||||||
|
buffer[0] = byte(fileEntry.StorageType)<<4 + byte(len(fileEntry.FileName))
|
||||||
|
for i := 0; i < len(fileEntry.FileName); i++ {
|
||||||
|
buffer[i+1] = fileEntry.FileName[i]
|
||||||
|
}
|
||||||
|
buffer[0x10] = byte(fileEntry.FileType)
|
||||||
|
buffer[0x11] = byte(fileEntry.KeyPointer & 0xFF)
|
||||||
|
buffer[0x12] = byte(fileEntry.KeyPointer >> 8)
|
||||||
|
buffer[0x13] = byte(fileEntry.BlocksUsed & 0xFF)
|
||||||
|
buffer[0x14] = byte(fileEntry.BlocksUsed >> 8)
|
||||||
|
buffer[0x15] = byte(fileEntry.EndOfFile & 0x0000FF)
|
||||||
|
buffer[0x16] = byte(fileEntry.EndOfFile & 0x00FF00 >> 8)
|
||||||
|
buffer[0x17] = byte(fileEntry.EndOfFile & 0xFF0000 >> 16)
|
||||||
|
creationTime := DateTimeToProDOS(fileEntry.CreationTime)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
buffer[0x18+i] = creationTime[i]
|
||||||
|
}
|
||||||
|
buffer[0x1C] = byte(fileEntry.Version)
|
||||||
|
buffer[0x1D] = byte(fileEntry.MinVersion)
|
||||||
|
buffer[0x1E] = byte(fileEntry.Access)
|
||||||
|
buffer[0x1F] = byte(fileEntry.AuxType & 0x00FF)
|
||||||
|
buffer[0x20] = byte(fileEntry.AuxType >> 8)
|
||||||
|
modifiedTime := DateTimeToProDOS(fileEntry.CreationTime)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
buffer[0x21+i] = modifiedTime[i]
|
||||||
|
}
|
||||||
|
buffer[0x25] = byte(fileEntry.HeaderPointer & 0x00FF)
|
||||||
|
buffer[0x26] = byte(fileEntry.HeaderPointer >> 8)
|
||||||
|
|
||||||
|
_, err := file.WriteAt(buffer, int64(fileEntry.DirectoryBlock*512+fileEntry.DirectoryOffset))
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func parseVolumeHeader(buffer []byte) VolumeHeader {
|
func parseVolumeHeader(buffer []byte) VolumeHeader {
|
||||||
nextBlock := int(buffer[2]) + int(buffer[3])*256
|
nextBlock := int(buffer[2]) + int(buffer[3])*256
|
||||||
filenameLength := buffer[4] & 15
|
filenameLength := buffer[4] & 15
|
||||||
|
@ -170,15 +221,17 @@ func parseVolumeHeader(buffer []byte) VolumeHeader {
|
||||||
NextBlock: nextBlock,
|
NextBlock: nextBlock,
|
||||||
EntriesPerBlock: entriesPerBlock,
|
EntriesPerBlock: entriesPerBlock,
|
||||||
EntryLength: entryLength,
|
EntryLength: entryLength,
|
||||||
|
MinVersion: minVersion,
|
||||||
|
Version: version,
|
||||||
}
|
}
|
||||||
return volumeHeader
|
return volumeHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDirectoryHeader(buffer []byte) DirectoryHeader {
|
func parseDirectoryHeader(buffer []byte) DirectoryHeader {
|
||||||
nextBlock := int(buffer[2]) + int(buffer[3])*256
|
nextBlock := int(buffer[0x02]) + int(buffer[0x03])*256
|
||||||
filenameLength := buffer[4] & 15
|
filenameLength := buffer[0x04] & 15
|
||||||
name := string(buffer[5 : filenameLength+5])
|
name := string(buffer[0x05 : filenameLength+0x05])
|
||||||
fileCount := int(buffer[37]) + int(buffer[38])*256
|
fileCount := int(buffer[0x25]) + int(buffer[0x26])*256
|
||||||
|
|
||||||
directoryEntry := DirectoryHeader{
|
directoryEntry := DirectoryHeader{
|
||||||
NextBlock: nextBlock,
|
NextBlock: nextBlock,
|
||||||
|
@ -188,3 +241,16 @@ func parseDirectoryHeader(buffer []byte) DirectoryHeader {
|
||||||
|
|
||||||
return directoryEntry
|
return directoryEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeDirectoryHeader(file *os.File, directoryHeader DirectoryHeader, blockNumber int) {
|
||||||
|
buffer := ReadBlock(file, blockNumber)
|
||||||
|
buffer[0x02] = byte(directoryHeader.NextBlock & 0x00FF)
|
||||||
|
buffer[0x03] = byte(directoryHeader.NextBlock >> 8)
|
||||||
|
buffer[0x04] = buffer[0x04] | byte(len(directoryHeader.Name))
|
||||||
|
for i := 0; i < len(directoryHeader.Name); i++ {
|
||||||
|
buffer[0x05+i] = directoryHeader.Name[i]
|
||||||
|
}
|
||||||
|
buffer[0x25] = byte(directoryHeader.ActiveFileCount & 0x00FF)
|
||||||
|
buffer[0x26] = byte(directoryHeader.ActiveFileCount >> 8)
|
||||||
|
file.WriteAt(buffer, int64(blockNumber*512))
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,47 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadFile(file *os.File, path string) []byte {
|
func LoadFile(file *os.File, path string) []byte {
|
||||||
|
fileEntry := GetFileEntry(file, path)
|
||||||
|
|
||||||
|
blockList := GetBlocklist(file, fileEntry)
|
||||||
|
|
||||||
|
buffer := make([]byte, fileEntry.EndOfFile)
|
||||||
|
|
||||||
|
for i := 0; i < len(blockList); i++ {
|
||||||
|
block := ReadBlock(file, blockList[i])
|
||||||
|
for j := 0; j < 512 && i*512+j < fileEntry.EndOfFile; j++ {
|
||||||
|
buffer[i*512+j] = block[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBlocklist(file *os.File, fileEntry FileEntry) []int {
|
||||||
|
blocks := make([]int, fileEntry.BlocksUsed)
|
||||||
|
|
||||||
|
switch fileEntry.StorageType {
|
||||||
|
case StorageSeedling:
|
||||||
|
blocks[0] = fileEntry.KeyPointer
|
||||||
|
case StorageSapling:
|
||||||
|
index := ReadBlock(file, fileEntry.KeyPointer)
|
||||||
|
for i := 0; i < fileEntry.BlocksUsed-1; i++ {
|
||||||
|
blocks[i] = int(index[i]) + int(index[i+256])*256
|
||||||
|
}
|
||||||
|
case StorageTree:
|
||||||
|
masterIndex := ReadBlock(file, fileEntry.KeyPointer)
|
||||||
|
for i := 0; i < 128; i++ {
|
||||||
|
index := ReadBlock(file, int(masterIndex[i])+int(masterIndex[i+256])*256)
|
||||||
|
for j := 0; j < 256 && i*256+j < fileEntry.BlocksUsed; j++ {
|
||||||
|
blocks[i*256+j] = int(index[j]) + int(index[j+256])*256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFileEntry(file *os.File, path string) FileEntry {
|
||||||
path = strings.ToUpper(path)
|
path = strings.ToUpper(path)
|
||||||
paths := strings.Split(path, "/")
|
paths := strings.Split(path, "/")
|
||||||
|
|
||||||
|
@ -22,7 +63,7 @@ func LoadFile(file *os.File, path string) []byte {
|
||||||
_, fileEntries := ReadDirectory(file, directory)
|
_, fileEntries := ReadDirectory(file, directory)
|
||||||
|
|
||||||
if fileEntries == nil {
|
if fileEntries == nil {
|
||||||
return nil
|
return FileEntry{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileEntry FileEntry
|
var fileEntry FileEntry
|
||||||
|
@ -33,22 +74,29 @@ func LoadFile(file *os.File, path string) []byte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch fileEntry.StorageType {
|
return fileEntry
|
||||||
case StorageSeedling:
|
}
|
||||||
return ReadBlock(file, fileEntry.StartingBlock)[0:fileEntry.EndOfFile]
|
|
||||||
case StorageSapling:
|
func DeleteFile(file *os.File, path string) {
|
||||||
index := ReadBlock(file, fileEntry.StartingBlock)
|
fileEntry := GetFileEntry(file, path)
|
||||||
buffer := make([]byte, fileEntry.EndOfFile)
|
|
||||||
for i := 0; i < 512 && index[i] > 0; i++ {
|
// free the blocks
|
||||||
chunk := ReadBlock(file, int(index[i])+int(index[i+256])*256)
|
blocks := GetBlocklist(file, fileEntry)
|
||||||
for j := i * 512; j < fileEntry.EndOfFile && j < i*512+512; j++ {
|
volumeBitmap := ReadVolumeBitmap(file)
|
||||||
buffer[j] = chunk[j-i*512]
|
for i := 0; i < len(blocks); i++ {
|
||||||
}
|
FreeBlockInVolumeBitmap(volumeBitmap, blocks[i])
|
||||||
}
|
}
|
||||||
return buffer
|
WriteVolumeBitmap(file, volumeBitmap)
|
||||||
case StorageTree:
|
|
||||||
// add tree file support later
|
// zero out directory entry
|
||||||
return nil
|
fileEntry.StorageType = 0
|
||||||
}
|
fileEntry.FileName = ""
|
||||||
return nil
|
writeFileEntry(file, fileEntry)
|
||||||
|
|
||||||
|
// decrement the directory entry count
|
||||||
|
directoryBlock := ReadBlock(file, fileEntry.HeaderPointer)
|
||||||
|
directoryHeader := parseDirectoryHeader(directoryBlock)
|
||||||
|
|
||||||
|
directoryHeader.ActiveFileCount--
|
||||||
|
writeDirectoryHeader(file, directoryHeader, fileEntry.HeaderPointer)
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,9 +85,10 @@ func DumpFileEntry(fileEntry FileEntry) {
|
||||||
fmt.Printf("AuxType: %04X\n", fileEntry.AuxType)
|
fmt.Printf("AuxType: %04X\n", fileEntry.AuxType)
|
||||||
fmt.Printf("EOF: %06X\n", fileEntry.EndOfFile)
|
fmt.Printf("EOF: %06X\n", fileEntry.EndOfFile)
|
||||||
fmt.Printf("Blocks used: %04X\n", fileEntry.BlocksUsed)
|
fmt.Printf("Blocks used: %04X\n", fileEntry.BlocksUsed)
|
||||||
fmt.Printf("Starting block: %04X\n", fileEntry.StartingBlock)
|
fmt.Printf("Starting block: %04X\n", fileEntry.KeyPointer)
|
||||||
fmt.Printf("File type: %02X\n", fileEntry.FileType)
|
fmt.Printf("File type: %02X\n", fileEntry.FileType)
|
||||||
fmt.Printf("Storage type: %02X\n", fileEntry.StorageType)
|
fmt.Printf("Storage type: %02X\n", fileEntry.StorageType)
|
||||||
|
fmt.Printf("Header pointer: %04X\n", fileEntry.HeaderPointer)
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,8 +96,8 @@ func DumpVolumeHeader(volumeHeader VolumeHeader) {
|
||||||
fmt.Printf("Next block: %d\n", volumeHeader.NextBlock)
|
fmt.Printf("Next block: %d\n", volumeHeader.NextBlock)
|
||||||
fmt.Printf("Volume name: %s\n", volumeHeader.VolumeName)
|
fmt.Printf("Volume name: %s\n", volumeHeader.VolumeName)
|
||||||
fmt.Printf("Creation time: %d-%s-%d %02d:%02d\n", volumeHeader.CreationTime.Year(), volumeHeader.CreationTime.Month(), volumeHeader.CreationTime.Day(), volumeHeader.CreationTime.Hour(), volumeHeader.CreationTime.Minute())
|
fmt.Printf("Creation time: %d-%s-%d %02d:%02d\n", volumeHeader.CreationTime.Year(), volumeHeader.CreationTime.Month(), volumeHeader.CreationTime.Day(), volumeHeader.CreationTime.Hour(), volumeHeader.CreationTime.Minute())
|
||||||
// fmt.Printf("ProDOS version (should be 0): %d\n", volumeHeader.Version)
|
fmt.Printf("ProDOS version (should be 0): %d\n", volumeHeader.Version)
|
||||||
// fmt.Printf("ProDOS mininum version (should be 0): %d\n", minVersion)
|
fmt.Printf("ProDOS mininum version (should be 0): %d\n", volumeHeader.MinVersion)
|
||||||
fmt.Printf("Entry length (should be 39): %d\n", volumeHeader.EntryLength)
|
fmt.Printf("Entry length (should be 39): %d\n", volumeHeader.EntryLength)
|
||||||
fmt.Printf("Entries per block (should be 13): %d\n", volumeHeader.EntriesPerBlock)
|
fmt.Printf("Entries per block (should be 13): %d\n", volumeHeader.EntriesPerBlock)
|
||||||
fmt.Printf("File count: %d\n", volumeHeader.ActiveFileCount)
|
fmt.Printf("File count: %d\n", volumeHeader.ActiveFileCount)
|
||||||
|
|
Loading…
Reference in New Issue