diff --git a/prodos/directory.go b/prodos/directory.go index 309ab6e..e00049c 100644 --- a/prodos/directory.go +++ b/prodos/directory.go @@ -73,7 +73,7 @@ func ReadDirectory(file *os.File, path string) (VolumeHeader, DirectoryHeader, [ 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) //DumpDirectoryHeader(directoryHeader) blockNumber := directoryHeader.StartingBlock diff --git a/prodos/file.go b/prodos/file.go index b7171d3..37e148c 100644 --- a/prodos/file.go +++ b/prodos/file.go @@ -8,12 +8,15 @@ import ( ) func LoadFile(file *os.File, path string) ([]byte, error) { - fileEntry, err := GetFileEntry(file, path) + fileEntry, err := getFileEntry(file, path) if err != nil { return nil, err } - blockList := getBlocklist(file, fileEntry) + blockList, err := getDataBlocklist(file, fileEntry) + if err != nil { + return nil, err + } 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 { directory, fileName := GetDirectoryAndFileNameFromPath(path) - existingFileEntry, _ := GetFileEntry(file, path) + existingFileEntry, _ := getFileEntry(file, path) if existingFileEntry.StorageType != StorageDeleted { DeleteFile(file, path) } @@ -38,50 +41,14 @@ func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []b // get list of blocks to write file to blockList := createBlockList(file, len(buffer)) - fileEntry, err := GetFreeFileEntryInDirectory(file, directory) - if err != nil { - return err - } - // 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)-1 { - indexBuffer[i] = byte(blockList[i+1] & 0x00FF) - indexBuffer[i+256] = byte(blockList[i+1] >> 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 == 511 { - WriteBlock(file, blockList[blockIndexNumber], blockBuffer) - blockPointer = 0 - blockIndexNumber++ - } else if i == len(buffer)-1 { - for j := blockPointer; j < 512; j++ { - blockBuffer[j] = 0 - } - WriteBlock(file, blockList[blockIndexNumber], blockBuffer) - } else { - blockPointer++ - } - } + writeSaplingFile(file, buffer, blockList) } // TODO: add tree file @@ -89,14 +56,13 @@ func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []b 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) + 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() @@ -125,8 +91,48 @@ func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []b 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 + indexBuffer := make([]byte, 512) + for i := 0; i < 256; i++ { + if i < len(blockList)-1 { + indexBuffer[i] = byte(blockList[i+1] & 0x00FF) + indexBuffer[i+256] = byte(blockList[i+1] >> 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 == 511 { + WriteBlock(file, blockList[blockIndexNumber], blockBuffer) + blockPointer = 0 + blockIndexNumber++ + } else if i == len(buffer)-1 { + for j := blockPointer; j < 512; j++ { + blockBuffer[j] = 0 + } + WriteBlock(file, blockList[blockIndexNumber], blockBuffer) + } else { + blockPointer++ + } + } +} + func DeleteFile(file *os.File, path string) error { - fileEntry, err := GetFileEntry(file, path) + fileEntry, err := getFileEntry(file, path) if err != nil { return errors.New("File not found") } @@ -135,7 +141,10 @@ func DeleteFile(file *os.File, path string) error { } // free the blocks - blocks := getBlocklist(file, fileEntry) + blocks, err := getBlocklist(file, fileEntry) + if err != nil { + return err + } volumeBitmap := ReadVolumeBitmap(file) for i := 0; i < len(blocks); i++ { freeBlockInVolumeBitmap(volumeBitmap, blocks[i]) @@ -157,30 +166,71 @@ func DeleteFile(file *os.File, path string) error { 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) switch fileEntry.StorageType { case StorageSeedling: blocks[0] = fileEntry.KeyPointer + return blocks, nil case StorageSapling: index := ReadBlock(file, fileEntry.KeyPointer) blocks[0] = fileEntry.KeyPointer for i := 0; i < fileEntry.BlocksUsed-1; i++ { blocks[i+1] = int(index[i]) + int(index[i+256])*256 } + return blocks, nil case StorageTree: masterIndex := ReadBlock(file, fileEntry.KeyPointer) blocks[0] = 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++ { + 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 } } } - 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 { @@ -207,7 +257,7 @@ func createBlockList(file *os.File, fileSize int) []int { return blockList } -func GetFileEntry(file *os.File, path string) (FileEntry, error) { +func getFileEntry(file *os.File, path string) (FileEntry, error) { directory, fileName := GetDirectoryAndFileNameFromPath(path) _, _, fileEntries := ReadDirectory(file, directory) @@ -229,20 +279,3 @@ func GetFileEntry(file *os.File, path string) (FileEntry, error) { 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 -} diff --git a/prodos/format_test.go b/prodos/format_test.go index f935d13..37e6fcb 100644 --- a/prodos/format_test.go +++ b/prodos/format_test.go @@ -21,7 +21,7 @@ func TestCreateVolume(t *testing.T) { testname := fmt.Sprintf("%d", tt.blocks) t.Run(testname, func(t *testing.T) { fileName := os.TempDir() + "test-volume.hdv" - os.Remove(fileName) + defer os.Remove(fileName) CreateVolume(fileName, tt.wantVolumeName, tt.blocks) file, err := os.Open(fileName)