mirror of
https://github.com/tjboldt/ProDOS-Utilities.git
synced 2024-06-08 21:29:28 +00:00
Fix file read and refactor
This commit is contained in:
parent
07e0bacab3
commit
48121c279f
|
@ -73,7 +73,7 @@ func ReadDirectory(file *os.File, path string) (VolumeHeader, DirectoryHeader, [
|
||||||
return volumeHeader, directoryHeader, fileEntries
|
return volumeHeader, directoryHeader, fileEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFreeFileEntryInDirectory(file *os.File, directory string) (FileEntry, error) {
|
func getFreeFileEntryInDirectory(file *os.File, directory string) (FileEntry, error) {
|
||||||
_, directoryHeader, _ := ReadDirectory(file, directory)
|
_, directoryHeader, _ := ReadDirectory(file, directory)
|
||||||
//DumpDirectoryHeader(directoryHeader)
|
//DumpDirectoryHeader(directoryHeader)
|
||||||
blockNumber := directoryHeader.StartingBlock
|
blockNumber := directoryHeader.StartingBlock
|
||||||
|
|
179
prodos/file.go
179
prodos/file.go
|
@ -8,12 +8,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadFile(file *os.File, path string) ([]byte, error) {
|
func LoadFile(file *os.File, path string) ([]byte, error) {
|
||||||
fileEntry, err := GetFileEntry(file, path)
|
fileEntry, err := getFileEntry(file, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
blockList := getBlocklist(file, fileEntry)
|
blockList, err := getDataBlocklist(file, fileEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
buffer := make([]byte, fileEntry.EndOfFile)
|
buffer := make([]byte, fileEntry.EndOfFile)
|
||||||
|
|
||||||
|
@ -30,7 +33,7 @@ func LoadFile(file *os.File, path string) ([]byte, error) {
|
||||||
func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []byte) error {
|
func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []byte) error {
|
||||||
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
||||||
|
|
||||||
existingFileEntry, _ := GetFileEntry(file, path)
|
existingFileEntry, _ := getFileEntry(file, path)
|
||||||
if existingFileEntry.StorageType != StorageDeleted {
|
if existingFileEntry.StorageType != StorageDeleted {
|
||||||
DeleteFile(file, path)
|
DeleteFile(file, path)
|
||||||
}
|
}
|
||||||
|
@ -38,21 +41,65 @@ func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []b
|
||||||
// get list of blocks to write file to
|
// get list of blocks to write file to
|
||||||
blockList := createBlockList(file, len(buffer))
|
blockList := createBlockList(file, len(buffer))
|
||||||
|
|
||||||
fileEntry, err := GetFreeFileEntryInDirectory(file, directory)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// seedling file
|
// seedling file
|
||||||
if len(buffer) <= 0x200 {
|
if len(buffer) <= 0x200 {
|
||||||
WriteBlock(file, blockList[0], buffer)
|
WriteBlock(file, blockList[0], buffer)
|
||||||
fileEntry.StorageType = StorageSeedling
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sapling file needs index block
|
// sapling file needs index block
|
||||||
if len(buffer) > 0x200 && len(buffer) <= 0x20000 {
|
if len(buffer) > 0x200 && len(buffer) <= 0x20000 {
|
||||||
fileEntry.StorageType = StorageSapling
|
writeSaplingFile(file, buffer, blockList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add tree file
|
||||||
|
if len(buffer) > 0x20000 {
|
||||||
|
return errors.New("Files > 128KB not supported yet.")
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVolumeBitmap(file, blockList)
|
||||||
|
|
||||||
|
// add file entry to directory
|
||||||
|
fileEntry, err := getFreeFileEntryInDirectory(file, directory)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fileEntry.FileName = fileName
|
||||||
|
fileEntry.BlocksUsed = len(blockList)
|
||||||
|
fileEntry.CreationTime = time.Now()
|
||||||
|
fileEntry.ModifiedTime = time.Now()
|
||||||
|
fileEntry.AuxType = auxType
|
||||||
|
fileEntry.EndOfFile = len(buffer)
|
||||||
|
fileEntry.FileType = fileType
|
||||||
|
fileEntry.KeyPointer = blockList[0]
|
||||||
|
fileEntry.Access = 0b11100011
|
||||||
|
if len(blockList) == 1 {
|
||||||
|
fileEntry.StorageType = StorageSeedling
|
||||||
|
} else if len(blockList) <= 257 {
|
||||||
|
fileEntry.StorageType = StorageSapling
|
||||||
|
} else {
|
||||||
|
fileEntry.StorageType = StorageTree
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileEntry(file, fileEntry)
|
||||||
|
|
||||||
|
// increment file count
|
||||||
|
directoryHeaderBlock := ReadBlock(file, fileEntry.HeaderPointer)
|
||||||
|
directoryHeader := parseDirectoryHeader(directoryHeaderBlock, fileEntry.HeaderPointer)
|
||||||
|
directoryHeader.ActiveFileCount++
|
||||||
|
writeDirectoryHeader(file, directoryHeader)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateVolumeBitmap(file *os.File, blockList []int) {
|
||||||
|
volumeBitmap := ReadVolumeBitmap(file)
|
||||||
|
for i := 0; i < len(blockList); i++ {
|
||||||
|
markBlockInVolumeBitmap(volumeBitmap, blockList[i])
|
||||||
|
}
|
||||||
|
writeVolumeBitmap(file, volumeBitmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSaplingFile(file *os.File, buffer []byte, blockList []int) {
|
||||||
// write index block with pointers to data blocks
|
// write index block with pointers to data blocks
|
||||||
indexBuffer := make([]byte, 512)
|
indexBuffer := make([]byte, 512)
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
|
@ -84,49 +131,8 @@ func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add tree file
|
|
||||||
if len(buffer) > 0x20000 {
|
|
||||||
return errors.New("Files > 128KB not supported yet.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// update volume bitmap
|
|
||||||
volumeBitmap := ReadVolumeBitmap(file)
|
|
||||||
for i := 0; i < len(blockList); i++ {
|
|
||||||
markBlockInVolumeBitmap(volumeBitmap, blockList[i])
|
|
||||||
}
|
|
||||||
writeVolumeBitmap(file, volumeBitmap)
|
|
||||||
|
|
||||||
// add file entry to directory
|
|
||||||
fileEntry.FileName = fileName
|
|
||||||
fileEntry.BlocksUsed = len(blockList)
|
|
||||||
fileEntry.CreationTime = time.Now()
|
|
||||||
fileEntry.ModifiedTime = time.Now()
|
|
||||||
fileEntry.AuxType = auxType
|
|
||||||
fileEntry.EndOfFile = len(buffer)
|
|
||||||
fileEntry.FileType = fileType
|
|
||||||
fileEntry.KeyPointer = blockList[0]
|
|
||||||
fileEntry.Access = 0b11100011
|
|
||||||
if len(blockList) == 1 {
|
|
||||||
fileEntry.StorageType = StorageSeedling
|
|
||||||
} else if len(blockList) <= 257 {
|
|
||||||
fileEntry.StorageType = StorageSapling
|
|
||||||
} else {
|
|
||||||
fileEntry.StorageType = StorageTree
|
|
||||||
}
|
|
||||||
|
|
||||||
writeFileEntry(file, fileEntry)
|
|
||||||
|
|
||||||
// increment file count
|
|
||||||
directoryHeaderBlock := ReadBlock(file, fileEntry.HeaderPointer)
|
|
||||||
directoryHeader := parseDirectoryHeader(directoryHeaderBlock, fileEntry.HeaderPointer)
|
|
||||||
directoryHeader.ActiveFileCount++
|
|
||||||
writeDirectoryHeader(file, directoryHeader)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteFile(file *os.File, path string) error {
|
func DeleteFile(file *os.File, path string) error {
|
||||||
fileEntry, err := GetFileEntry(file, path)
|
fileEntry, err := getFileEntry(file, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("File not found")
|
return errors.New("File not found")
|
||||||
}
|
}
|
||||||
|
@ -135,7 +141,10 @@ func DeleteFile(file *os.File, path string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// free the blocks
|
// free the blocks
|
||||||
blocks := getBlocklist(file, fileEntry)
|
blocks, err := getBlocklist(file, fileEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
volumeBitmap := ReadVolumeBitmap(file)
|
volumeBitmap := ReadVolumeBitmap(file)
|
||||||
for i := 0; i < len(blocks); i++ {
|
for i := 0; i < len(blocks); i++ {
|
||||||
freeBlockInVolumeBitmap(volumeBitmap, blocks[i])
|
freeBlockInVolumeBitmap(volumeBitmap, blocks[i])
|
||||||
|
@ -157,30 +166,71 @@ func DeleteFile(file *os.File, path string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBlocklist(file *os.File, fileEntry FileEntry) []int {
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all blocks, including index blocks
|
||||||
|
func getBlocklist(file *os.File, fileEntry FileEntry) ([]int, error) {
|
||||||
blocks := make([]int, fileEntry.BlocksUsed)
|
blocks := make([]int, fileEntry.BlocksUsed)
|
||||||
|
|
||||||
switch fileEntry.StorageType {
|
switch fileEntry.StorageType {
|
||||||
case StorageSeedling:
|
case StorageSeedling:
|
||||||
blocks[0] = fileEntry.KeyPointer
|
blocks[0] = fileEntry.KeyPointer
|
||||||
|
return blocks, nil
|
||||||
case StorageSapling:
|
case StorageSapling:
|
||||||
index := ReadBlock(file, fileEntry.KeyPointer)
|
index := ReadBlock(file, fileEntry.KeyPointer)
|
||||||
blocks[0] = fileEntry.KeyPointer
|
blocks[0] = fileEntry.KeyPointer
|
||||||
for i := 0; i < fileEntry.BlocksUsed-1; i++ {
|
for i := 0; i < fileEntry.BlocksUsed-1; i++ {
|
||||||
blocks[i+1] = int(index[i]) + int(index[i+256])*256
|
blocks[i+1] = int(index[i]) + int(index[i+256])*256
|
||||||
}
|
}
|
||||||
|
return blocks, nil
|
||||||
case StorageTree:
|
case StorageTree:
|
||||||
masterIndex := ReadBlock(file, fileEntry.KeyPointer)
|
masterIndex := ReadBlock(file, fileEntry.KeyPointer)
|
||||||
blocks[0] = fileEntry.KeyPointer
|
blocks[0] = fileEntry.KeyPointer
|
||||||
for i := 0; i < 128; i++ {
|
for i := 0; i < 128; i++ {
|
||||||
index := ReadBlock(file, int(masterIndex[i])+int(masterIndex[i+256])*256)
|
index := ReadBlock(file, int(masterIndex[i])+int(masterIndex[i+256])*256)
|
||||||
for j := 0; j < 256 && i*256+j < fileEntry.BlocksUsed; j++ {
|
for j := 0; j < 256 && i*256+j < fileEntry.BlocksUsed; j++ {
|
||||||
|
if (int(index[j]) + int(index[j+256])*256) == 0 {
|
||||||
|
return blocks, nil
|
||||||
|
}
|
||||||
blocks[i*256+j] = int(index[j]) + int(index[j+256])*256
|
blocks[i*256+j] = int(index[j]) + int(index[j+256])*256
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocks
|
return nil, errors.New("Unsupported file storage type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDataBlocklist(file *os.File, fileEntry FileEntry) ([]int, error) {
|
||||||
|
switch fileEntry.StorageType {
|
||||||
|
case StorageSeedling:
|
||||||
|
blocks := make([]int, 1)
|
||||||
|
blocks[0] = fileEntry.KeyPointer
|
||||||
|
return blocks, nil
|
||||||
|
case StorageSapling:
|
||||||
|
blocks := make([]int, fileEntry.BlocksUsed-1)
|
||||||
|
index := ReadBlock(file, fileEntry.KeyPointer)
|
||||||
|
for i := 0; i < fileEntry.BlocksUsed-1; i++ {
|
||||||
|
blocks[i] = int(index[i]) + int(index[i+256])*256
|
||||||
|
}
|
||||||
|
return blocks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("Unsupported file storage type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func createBlockList(file *os.File, fileSize int) []int {
|
func createBlockList(file *os.File, fileSize int) []int {
|
||||||
|
@ -207,7 +257,7 @@ func createBlockList(file *os.File, fileSize int) []int {
|
||||||
return blockList
|
return blockList
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFileEntry(file *os.File, path string) (FileEntry, error) {
|
func getFileEntry(file *os.File, path string) (FileEntry, error) {
|
||||||
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
||||||
_, _, fileEntries := ReadDirectory(file, directory)
|
_, _, fileEntries := ReadDirectory(file, directory)
|
||||||
|
|
||||||
|
@ -229,20 +279,3 @@ func GetFileEntry(file *os.File, path string) (FileEntry, error) {
|
||||||
|
|
||||||
return fileEntry, nil
|
return fileEntry, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ func TestCreateVolume(t *testing.T) {
|
||||||
testname := fmt.Sprintf("%d", tt.blocks)
|
testname := fmt.Sprintf("%d", tt.blocks)
|
||||||
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"
|
||||||
os.Remove(fileName)
|
defer os.Remove(fileName)
|
||||||
CreateVolume(fileName, tt.wantVolumeName, tt.blocks)
|
CreateVolume(fileName, tt.wantVolumeName, tt.blocks)
|
||||||
|
|
||||||
file, err := os.Open(fileName)
|
file, err := os.Open(fileName)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user