ProDOS-Utilities/prodos/file.go

202 lines
4.9 KiB
Go
Raw Normal View History

2021-06-06 12:00:20 +00:00
package prodos
import (
"os"
"strings"
2021-06-26 01:15:20 +00:00
"time"
2021-06-06 12:00:20 +00:00
)
func LoadFile(file *os.File, path string) []byte {
2021-06-12 02:43:35 +00:00
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
}
2021-06-26 01:15:20 +00:00
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)
}
2021-06-12 02:43:35 +00:00
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
}
2021-06-26 01:15:20 +00:00
func CreateBlockList(file *os.File, fileSize int) []int {
numberOfBlocks := fileSize / 512
if fileSize%512 > 0 {
numberOfBlocks++
}
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++
}
2021-06-06 12:00:20 +00:00
}
2021-06-26 01:15:20 +00:00
volumeBitmap := ReadVolumeBitmap(file)
blockList := FindFreeBlocks(volumeBitmap, numberOfBlocks)
2021-06-06 12:00:20 +00:00
2021-06-26 01:15:20 +00:00
return blockList
}
func GetFileEntry(file *os.File, path string) FileEntry {
directory, fileName := GetDirectoryAndFileNameFromPath(path)
2021-06-06 12:00:20 +00:00
2021-06-26 01:15:20 +00:00
_, _, fileEntries := ReadDirectory(file, directory)
2021-06-06 12:00:20 +00:00
if fileEntries == nil {
2021-06-12 02:43:35 +00:00
return FileEntry{}
2021-06-06 12:00:20 +00:00
}
var fileEntry FileEntry
for i := 0; i < len(fileEntries); i++ {
if fileEntries[i].FileName == fileName {
fileEntry = fileEntries[i]
}
}
2021-06-12 02:43:35 +00:00
return fileEntry
}
2021-06-26 01:15:20 +00:00
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
}
2021-06-12 02:43:35 +00:00
func DeleteFile(file *os.File, path string) {
fileEntry := GetFileEntry(file, path)
2021-06-26 01:15:20 +00:00
if fileEntry.StorageType == StorageDeleted {
return
}
2021-06-12 02:43:35 +00:00
// free the blocks
blocks := GetBlocklist(file, fileEntry)
volumeBitmap := ReadVolumeBitmap(file)
for i := 0; i < len(blocks); i++ {
FreeBlockInVolumeBitmap(volumeBitmap, blocks[i])
2021-06-06 12:00:20 +00:00
}
2021-06-12 02:43:35 +00:00
WriteVolumeBitmap(file, volumeBitmap)
// zero out directory entry
fileEntry.StorageType = 0
fileEntry.FileName = ""
writeFileEntry(file, fileEntry)
// decrement the directory entry count
directoryBlock := ReadBlock(file, fileEntry.HeaderPointer)
2021-06-26 01:15:20 +00:00
directoryHeader := parseDirectoryHeader(directoryBlock, fileEntry.HeaderPointer)
2021-06-12 02:43:35 +00:00
directoryHeader.ActiveFileCount--
writeDirectoryHeader(file, directoryHeader, fileEntry.HeaderPointer)
2021-06-06 12:00:20 +00:00
}