mirror of
https://github.com/tjboldt/ProDOS-Utilities.git
synced 2024-11-24 16:31:28 +00:00
Added file write support
This commit is contained in:
parent
a04603f1e2
commit
aa6fb993ee
22
README.md
22
README.md
@ -3,18 +3,20 @@ This project is just starting but is intended to be both a command line tool and
|
|||||||
|
|
||||||
## Current command line functionality
|
## Current command line functionality
|
||||||
1. Export files
|
1. Export files
|
||||||
2. List any directory
|
2. Write files (currently only works with < 128K files)
|
||||||
3. Display volume bitmap
|
3. List any directory
|
||||||
4. Create new volume
|
4. Display volume bitmap
|
||||||
5. Delete file
|
5. Create new volume
|
||||||
|
6. Delete file
|
||||||
|
|
||||||
## Current library functionality
|
## Current library functionality
|
||||||
1. Read block
|
1. Read block
|
||||||
2. Write block
|
2. Write block
|
||||||
3. Read file
|
3. Read file
|
||||||
4. Delete file
|
4. Write file
|
||||||
5. Create new volume
|
5. Delete file
|
||||||
6. Read volume bitmap
|
6. Create new volume
|
||||||
7. Write volume bitmap
|
7. Read volume bitmap
|
||||||
8. Get list of file entries from any path
|
8. Write volume bitmap
|
||||||
9. Get volume header
|
9. Get list of file entries from any path
|
||||||
|
10. Get volume header
|
||||||
|
23
main.go
23
main.go
@ -13,13 +13,15 @@ func main() {
|
|||||||
var pathName string
|
var pathName string
|
||||||
var command string
|
var command string
|
||||||
var outFileName string
|
var outFileName string
|
||||||
|
var inFileName string
|
||||||
var blockNumber int
|
var blockNumber int
|
||||||
var volumeSize int
|
var volumeSize int
|
||||||
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, createvolume, delete")
|
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", "", "Name of file to write")
|
||||||
|
flag.StringVar(&inFileName, "infile", "", "Name of file to read")
|
||||||
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")
|
||||||
flag.IntVar(&blockNumber, "block", 0, "A block number to read/write from 0 to 65535")
|
flag.IntVar(&blockNumber, "block", 0, "A block number to read/write from 0 to 65535")
|
||||||
@ -36,7 +38,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
volumeHeader, fileEntries := prodos.ReadDirectory(file, pathName)
|
volumeHeader, _, fileEntries := prodos.ReadDirectory(file, pathName)
|
||||||
prodos.DumpDirectory(volumeHeader, fileEntries)
|
prodos.DumpDirectory(volumeHeader, fileEntries)
|
||||||
case "volumebitmap":
|
case "volumebitmap":
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
@ -55,11 +57,28 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
getFile := prodos.LoadFile(file, pathName)
|
getFile := prodos.LoadFile(file, pathName)
|
||||||
|
if len(outFileName) == 0 {
|
||||||
|
_, outFileName = prodos.GetDirectoryAndFileNameFromPath(pathName)
|
||||||
|
}
|
||||||
outFile, err := os.Create(outFileName)
|
outFile, err := os.Create(outFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
outFile.Write(getFile)
|
outFile.Write(getFile)
|
||||||
|
case "put":
|
||||||
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if len(pathName) == 0 {
|
||||||
|
fmt.Println("Missing pathname")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
inFile, err := os.ReadFile(inFileName)
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
prodos.WriteFile(file, pathName, inFile)
|
||||||
case "readblock":
|
case "readblock":
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -74,7 +74,21 @@ func CreateVolumeBitmap(numberOfBlocks int) []byte {
|
|||||||
return volumeBitmap
|
return volumeBitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindFreeBlocks(numberOfBlocks int) []int {
|
func FindFreeBlocks(volumeBitmap []byte, numberOfBlocks int) []int {
|
||||||
|
blocks := make([]int, numberOfBlocks)
|
||||||
|
|
||||||
|
blocksFound := 0
|
||||||
|
|
||||||
|
for i := 0; i < len(volumeBitmap)*8; i++ {
|
||||||
|
if CheckFreeBlockInVolumeBitmap(volumeBitmap, i) {
|
||||||
|
blocks[blocksFound] = i
|
||||||
|
blocksFound++
|
||||||
|
if blocksFound == numberOfBlocks {
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,3 +148,31 @@ func FreeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) {
|
|||||||
|
|
||||||
volumeBitmap[byteToChange] |= byte(byteToOr)
|
volumeBitmap[byteToChange] |= byte(byteToOr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CheckFreeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) bool {
|
||||||
|
bitToCheck := blockNumber % 8
|
||||||
|
byteToCheck := blockNumber / 8
|
||||||
|
|
||||||
|
byteToAnd := 0b00000000
|
||||||
|
|
||||||
|
switch bitToCheck {
|
||||||
|
case 0:
|
||||||
|
byteToAnd = 0b10000000
|
||||||
|
case 1:
|
||||||
|
byteToAnd = 0b01000000
|
||||||
|
case 2:
|
||||||
|
byteToAnd = 0b00100000
|
||||||
|
case 3:
|
||||||
|
byteToAnd = 0b00010000
|
||||||
|
case 4:
|
||||||
|
byteToAnd = 0b00001000
|
||||||
|
case 5:
|
||||||
|
byteToAnd = 0b00000100
|
||||||
|
case 6:
|
||||||
|
byteToAnd = 0b00000010
|
||||||
|
case 7:
|
||||||
|
byteToAnd = 0b00000001
|
||||||
|
}
|
||||||
|
|
||||||
|
return (volumeBitmap[byteToCheck] & byte(byteToAnd)) > 0
|
||||||
|
}
|
||||||
|
@ -26,3 +26,30 @@ func TestCreateVolumeBitmap(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckFreeBlockInVolumeBitmap(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
blocks int
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{0, false}, // boot block
|
||||||
|
{1, false}, // SOS boot block
|
||||||
|
{2, false}, // volume root
|
||||||
|
{21, false}, // end of volume bitmap
|
||||||
|
{22, true}, // beginning of free space
|
||||||
|
{8192, true}, // more free space
|
||||||
|
{65534, true}, // last free block
|
||||||
|
{65535, false}, // can't use last block because volume size is 0xFFFF, not 0x10000
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
testname := fmt.Sprintf("%d", tt.blocks)
|
||||||
|
t.Run(testname, func(t *testing.T) {
|
||||||
|
volumeBitMap := CreateVolumeBitmap(65535)
|
||||||
|
ans := CheckFreeBlockInVolumeBitmap(volumeBitMap, tt.blocks)
|
||||||
|
if ans != tt.want {
|
||||||
|
t.Errorf("got %t, want %t", ans, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,6 +23,8 @@ type VolumeHeader struct {
|
|||||||
type DirectoryHeader struct {
|
type DirectoryHeader struct {
|
||||||
Name string
|
Name string
|
||||||
ActiveFileCount int
|
ActiveFileCount int
|
||||||
|
StartingBlock int
|
||||||
|
PreviousBlock int
|
||||||
NextBlock int
|
NextBlock int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,11 +55,10 @@ type FileEntry struct {
|
|||||||
DirectoryOffset int
|
DirectoryOffset int
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadDirectory(file *os.File, path string) (VolumeHeader, []FileEntry) {
|
func ReadDirectory(file *os.File, path string) (VolumeHeader, DirectoryHeader, []FileEntry) {
|
||||||
buffer := ReadBlock(file, 2)
|
buffer := ReadBlock(file, 2)
|
||||||
|
|
||||||
volumeHeader := parseVolumeHeader(buffer)
|
volumeHeader := parseVolumeHeader(buffer)
|
||||||
//dumpVolumeHeader(volumeHeader)
|
|
||||||
|
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
path = fmt.Sprintf("/%s", volumeHeader.VolumeName)
|
path = fmt.Sprintf("/%s", volumeHeader.VolumeName)
|
||||||
@ -66,17 +67,52 @@ func ReadDirectory(file *os.File, path string) (VolumeHeader, []FileEntry) {
|
|||||||
path = strings.ToUpper(path)
|
path = strings.ToUpper(path)
|
||||||
paths := strings.Split(path, "/")
|
paths := strings.Split(path, "/")
|
||||||
|
|
||||||
fileEntries := getFileEntriesInDirectory(file, 2, 1, paths)
|
directoryHeader, fileEntries := getFileEntriesInDirectory(file, 2, 1, paths)
|
||||||
|
|
||||||
return volumeHeader, fileEntries
|
return volumeHeader, directoryHeader, fileEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int, paths []string) []FileEntry {
|
func GetFreeFileEntryInDirectory(file *os.File, directory string) FileEntry {
|
||||||
//fmt.Printf("Parsing '%s'...\n", paths[currentPath])
|
_, directoryHeader, _ := ReadDirectory(file, directory)
|
||||||
|
DumpDirectoryHeader(directoryHeader)
|
||||||
|
blockNumber := directoryHeader.StartingBlock
|
||||||
|
buffer := ReadBlock(file, blockNumber)
|
||||||
|
|
||||||
|
entryOffset := 43 // start at offset after header
|
||||||
|
entryNumber := 2 // header is essentially the first entry so start at 2
|
||||||
|
|
||||||
|
for {
|
||||||
|
if entryNumber > 13 {
|
||||||
|
blockNumber = int(buffer[2]) + int(buffer[3])*256
|
||||||
|
// if we ran out of blocks in the directory, return empty
|
||||||
|
// TODO: expand the directory to add more entries
|
||||||
|
if blockNumber == 0 {
|
||||||
|
return FileEntry{}
|
||||||
|
}
|
||||||
|
// else read the next block in the directory
|
||||||
|
buffer = ReadBlock(file, blockNumber)
|
||||||
|
entryOffset = 4
|
||||||
|
entryNumber = 1
|
||||||
|
}
|
||||||
|
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset)
|
||||||
|
|
||||||
|
if fileEntry.StorageType == StorageDeleted {
|
||||||
|
fileEntry.DirectoryBlock = blockNumber
|
||||||
|
fileEntry.DirectoryOffset = entryOffset
|
||||||
|
fileEntry.HeaderPointer = directoryHeader.StartingBlock
|
||||||
|
return fileEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
entryNumber++
|
||||||
|
entryOffset += 39
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int, paths []string) (DirectoryHeader, []FileEntry) {
|
||||||
|
|
||||||
buffer := ReadBlock(file, blockNumber)
|
buffer := ReadBlock(file, blockNumber)
|
||||||
|
|
||||||
directoryHeader := parseDirectoryHeader(buffer)
|
directoryHeader := parseDirectoryHeader(buffer, blockNumber)
|
||||||
|
|
||||||
fileEntries := make([]FileEntry, directoryHeader.ActiveFileCount)
|
fileEntries := make([]FileEntry, directoryHeader.ActiveFileCount)
|
||||||
entryOffset := 43 // start at offset after header
|
entryOffset := 43 // start at offset after header
|
||||||
@ -89,7 +125,7 @@ func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int,
|
|||||||
|
|
||||||
if !matchedDirectory && (currentPath == len(paths)-1) {
|
if !matchedDirectory && (currentPath == len(paths)-1) {
|
||||||
// path not matched by last path part
|
// path not matched by last path part
|
||||||
return nil
|
return DirectoryHeader{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -97,24 +133,23 @@ func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int,
|
|||||||
entryOffset = 4
|
entryOffset = 4
|
||||||
entryNumber = 1
|
entryNumber = 1
|
||||||
if blockNumber == 0 {
|
if blockNumber == 0 {
|
||||||
return nil
|
return DirectoryHeader{}, nil
|
||||||
}
|
}
|
||||||
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], blockNumber, entryOffset)
|
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset)
|
||||||
//DumpFileEntry(fileEntry)
|
|
||||||
|
|
||||||
if fileEntry.StorageType != StorageDeleted {
|
if fileEntry.StorageType != StorageDeleted {
|
||||||
|
if matchedDirectory && activeEntries == directoryHeader.ActiveFileCount {
|
||||||
|
return directoryHeader, fileEntries[0:activeEntries]
|
||||||
|
}
|
||||||
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.KeyPointer, currentPath+1, paths)
|
return getFileEntriesInDirectory(file, fileEntry.KeyPointer, currentPath+1, paths)
|
||||||
}
|
}
|
||||||
activeEntries++
|
activeEntries++
|
||||||
if matchedDirectory && activeEntries == directoryHeader.ActiveFileCount {
|
|
||||||
return fileEntries[0:activeEntries]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entryNumber++
|
entryNumber++
|
||||||
@ -227,14 +262,17 @@ func parseVolumeHeader(buffer []byte) VolumeHeader {
|
|||||||
return volumeHeader
|
return volumeHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDirectoryHeader(buffer []byte) DirectoryHeader {
|
func parseDirectoryHeader(buffer []byte, blockNumber int) DirectoryHeader {
|
||||||
|
previousBlock := int(buffer[0x00]) + int(buffer[0x01])*256
|
||||||
nextBlock := int(buffer[0x02]) + int(buffer[0x03])*256
|
nextBlock := int(buffer[0x02]) + int(buffer[0x03])*256
|
||||||
filenameLength := buffer[0x04] & 15
|
filenameLength := buffer[0x04] & 15
|
||||||
name := string(buffer[0x05 : filenameLength+0x05])
|
name := string(buffer[0x05 : filenameLength+0x05])
|
||||||
fileCount := int(buffer[0x25]) + int(buffer[0x26])*256
|
fileCount := int(buffer[0x25]) + int(buffer[0x26])*256
|
||||||
|
|
||||||
directoryEntry := DirectoryHeader{
|
directoryEntry := DirectoryHeader{
|
||||||
|
PreviousBlock: previousBlock,
|
||||||
NextBlock: nextBlock,
|
NextBlock: nextBlock,
|
||||||
|
StartingBlock: blockNumber,
|
||||||
Name: name,
|
Name: name,
|
||||||
ActiveFileCount: fileCount,
|
ActiveFileCount: fileCount,
|
||||||
}
|
}
|
||||||
|
125
prodos/file.go
125
prodos/file.go
@ -3,6 +3,7 @@ package prodos
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadFile(file *os.File, path string) []byte {
|
func LoadFile(file *os.File, path string) []byte {
|
||||||
@ -22,6 +23,71 @@ func LoadFile(file *os.File, path string) []byte {
|
|||||||
return buffer
|
return buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WriteFile(file *os.File, path string, buffer []byte) {
|
||||||
|
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
||||||
|
|
||||||
|
DeleteFile(file, path)
|
||||||
|
|
||||||
|
// get list of blocks to write file to
|
||||||
|
blockList := CreateBlockList(file, len(buffer))
|
||||||
|
|
||||||
|
fileEntry := GetFreeFileEntryInDirectory(file, directory)
|
||||||
|
|
||||||
|
// seedling file
|
||||||
|
if len(buffer) <= 0x200 {
|
||||||
|
WriteBlock(file, blockList[0], buffer)
|
||||||
|
fileEntry.StorageType = StorageSeedling
|
||||||
|
}
|
||||||
|
|
||||||
|
// sapling file needs index block
|
||||||
|
if len(buffer) > 0x200 && len(buffer) <= 0x20000 {
|
||||||
|
fileEntry.StorageType = StorageSapling
|
||||||
|
|
||||||
|
// write index block with pointers to data blocks
|
||||||
|
indexBuffer := make([]byte, 512)
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
if i < len(blockList) {
|
||||||
|
indexBuffer[i] = byte(blockList[i] & 0x00FF)
|
||||||
|
indexBuffer[i+256] = byte(blockList[i] >> 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WriteBlock(file, blockList[0], indexBuffer)
|
||||||
|
|
||||||
|
// write all data blocks
|
||||||
|
blockBuffer := make([]byte, 512)
|
||||||
|
blockPointer := 0
|
||||||
|
blockIndexNumber := 1
|
||||||
|
for i := 0; i < len(buffer); i++ {
|
||||||
|
blockBuffer[blockPointer] = buffer[i]
|
||||||
|
if blockPointer == 512 {
|
||||||
|
WriteBlock(file, blockList[blockIndexNumber], blockBuffer)
|
||||||
|
blockPointer = 0
|
||||||
|
blockIndexNumber++
|
||||||
|
}
|
||||||
|
if i == len(buffer)-1 {
|
||||||
|
for j := blockPointer; j < 512; j++ {
|
||||||
|
blockBuffer[j] = 0
|
||||||
|
}
|
||||||
|
WriteBlock(file, blockList[blockIndexNumber], blockBuffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add tree file
|
||||||
|
|
||||||
|
// add file entry to directory
|
||||||
|
fileEntry.FileName = fileName
|
||||||
|
fileEntry.BlocksUsed = len(blockList)
|
||||||
|
fileEntry.CreationTime = time.Now()
|
||||||
|
fileEntry.ModifiedTime = time.Now()
|
||||||
|
fileEntry.AuxType = 0x2000
|
||||||
|
fileEntry.EndOfFile = len(buffer)
|
||||||
|
fileEntry.FileType = 0x06
|
||||||
|
fileEntry.KeyPointer = blockList[0]
|
||||||
|
|
||||||
|
writeFileEntry(file, fileEntry)
|
||||||
|
}
|
||||||
|
|
||||||
func GetBlocklist(file *os.File, fileEntry FileEntry) []int {
|
func GetBlocklist(file *os.File, fileEntry FileEntry) []int {
|
||||||
blocks := make([]int, fileEntry.BlocksUsed)
|
blocks := make([]int, fileEntry.BlocksUsed)
|
||||||
|
|
||||||
@ -46,21 +112,34 @@ func GetBlocklist(file *os.File, fileEntry FileEntry) []int {
|
|||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFileEntry(file *os.File, path string) FileEntry {
|
func CreateBlockList(file *os.File, fileSize int) []int {
|
||||||
path = strings.ToUpper(path)
|
numberOfBlocks := fileSize / 512
|
||||||
paths := strings.Split(path, "/")
|
if fileSize%512 > 0 {
|
||||||
|
numberOfBlocks++
|
||||||
var directoryBuilder strings.Builder
|
|
||||||
|
|
||||||
for i := 1; i < len(paths)-1; i++ {
|
|
||||||
directoryBuilder.WriteString("/")
|
|
||||||
directoryBuilder.WriteString(paths[i])
|
|
||||||
}
|
}
|
||||||
|
if fileSize > 0x200 && fileSize <= 0x20000 {
|
||||||
|
numberOfBlocks++ // add index block
|
||||||
|
}
|
||||||
|
if fileSize > 0x20000 {
|
||||||
|
// add master index block
|
||||||
|
numberOfBlocks++
|
||||||
|
// add index blocks for each 128 blocks
|
||||||
|
numberOfBlocks += numberOfBlocks / 128
|
||||||
|
// add index block for any remaining blocks
|
||||||
|
if numberOfBlocks%128 > 0 {
|
||||||
|
numberOfBlocks++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
volumeBitmap := ReadVolumeBitmap(file)
|
||||||
|
blockList := FindFreeBlocks(volumeBitmap, numberOfBlocks)
|
||||||
|
|
||||||
directory := directoryBuilder.String()
|
return blockList
|
||||||
fileName := paths[len(paths)-1]
|
}
|
||||||
|
|
||||||
_, fileEntries := ReadDirectory(file, directory)
|
func GetFileEntry(file *os.File, path string) FileEntry {
|
||||||
|
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
||||||
|
|
||||||
|
_, _, fileEntries := ReadDirectory(file, directory)
|
||||||
|
|
||||||
if fileEntries == nil {
|
if fileEntries == nil {
|
||||||
return FileEntry{}
|
return FileEntry{}
|
||||||
@ -77,8 +156,28 @@ func GetFileEntry(file *os.File, path string) FileEntry {
|
|||||||
return fileEntry
|
return fileEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDirectoryAndFileNameFromPath(path string) (string, string) {
|
||||||
|
path = strings.ToUpper(path)
|
||||||
|
paths := strings.Split(path, "/")
|
||||||
|
|
||||||
|
var directoryBuilder strings.Builder
|
||||||
|
|
||||||
|
for i := 1; i < len(paths)-1; i++ {
|
||||||
|
directoryBuilder.WriteString("/")
|
||||||
|
directoryBuilder.WriteString(paths[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
directory := directoryBuilder.String()
|
||||||
|
fileName := paths[len(paths)-1]
|
||||||
|
|
||||||
|
return directory, fileName
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteFile(file *os.File, path string) {
|
func DeleteFile(file *os.File, path string) {
|
||||||
fileEntry := GetFileEntry(file, path)
|
fileEntry := GetFileEntry(file, path)
|
||||||
|
if fileEntry.StorageType == StorageDeleted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// free the blocks
|
// free the blocks
|
||||||
blocks := GetBlocklist(file, fileEntry)
|
blocks := GetBlocklist(file, fileEntry)
|
||||||
@ -95,7 +194,7 @@ func DeleteFile(file *os.File, path string) {
|
|||||||
|
|
||||||
// decrement the directory entry count
|
// decrement the directory entry count
|
||||||
directoryBlock := ReadBlock(file, fileEntry.HeaderPointer)
|
directoryBlock := ReadBlock(file, fileEntry.HeaderPointer)
|
||||||
directoryHeader := parseDirectoryHeader(directoryBlock)
|
directoryHeader := parseDirectoryHeader(directoryBlock, fileEntry.HeaderPointer)
|
||||||
|
|
||||||
directoryHeader.ActiveFileCount--
|
directoryHeader.ActiveFileCount--
|
||||||
writeDirectoryHeader(file, directoryHeader, fileEntry.HeaderPointer)
|
writeDirectoryHeader(file, directoryHeader, fileEntry.HeaderPointer)
|
||||||
|
@ -105,6 +105,14 @@ func DumpVolumeHeader(volumeHeader VolumeHeader) {
|
|||||||
fmt.Printf("Total blocks: %d\n", volumeHeader.TotalBlocks)
|
fmt.Printf("Total blocks: %d\n", volumeHeader.TotalBlocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DumpDirectoryHeader(directoryHeader DirectoryHeader) {
|
||||||
|
fmt.Printf("Name: %s\n", directoryHeader.Name)
|
||||||
|
fmt.Printf("File count: %d\n", directoryHeader.ActiveFileCount)
|
||||||
|
fmt.Printf("Starting block: %04X\n", directoryHeader.StartingBlock)
|
||||||
|
fmt.Printf("Previous block: %04X\n", directoryHeader.PreviousBlock)
|
||||||
|
fmt.Printf("Next block: %04X\n", directoryHeader.NextBlock)
|
||||||
|
}
|
||||||
|
|
||||||
func DumpBlock(buffer []byte) {
|
func DumpBlock(buffer []byte) {
|
||||||
for i := 0; i < len(buffer); i += 16 {
|
for i := 0; i < len(buffer); i += 16 {
|
||||||
for j := i; j < i+16; j++ {
|
for j := i; j < i+16; j++ {
|
||||||
|
Loading…
Reference in New Issue
Block a user