Change to reader/writer instead of file

This commit is contained in:
Terence Boldt 2022-01-23 16:58:34 -05:00
parent 2a595f0a5c
commit fb9621ff09
13 changed files with 224 additions and 132 deletions

14
main.go
View File

@ -1,3 +1,11 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides a command line utility to read, write and delete
// files and directories on a ProDOS drive image as well as format
// new volumes
package main
import (
@ -101,7 +109,7 @@ func main() {
fmt.Printf("Failed to open input file %s: %s", inFileName, err)
os.Exit(1)
}
err = prodos.WriteFile(file, pathName, fileType, auxType, inFile)
err = prodos.WriteFile(file, file, pathName, fileType, auxType, inFile)
if err != nil {
fmt.Printf("Failed to write file %s: %s", pathName, err)
}
@ -136,7 +144,7 @@ func main() {
return
}
defer file.Close()
prodos.CreateVolume(file, volumeName, volumeSize)
prodos.CreateVolume(file, file, volumeName, volumeSize)
case "rm":
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
if err != nil {
@ -144,7 +152,7 @@ func main() {
os.Exit(1)
}
defer file.Close()
prodos.DeleteFile(file, pathName)
prodos.DeleteFile(file, file, pathName)
default:
fmt.Printf("Invalid command: %s\n\n", command)
flag.PrintDefaults()

View File

@ -1,3 +1,9 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides conversion between BASIC and text
package prodos
import (

View File

@ -1,11 +1,18 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides access to volum bitmap on
// a ProDOS drive image
package prodos
import (
"os"
"io"
)
func ReadVolumeBitmap(file *os.File) []byte {
headerBlock := ReadBlock(file, 2)
func ReadVolumeBitmap(reader io.ReaderAt) []byte {
headerBlock := ReadBlock(reader, 2)
volumeHeader := parseVolumeHeader(headerBlock)
@ -23,7 +30,7 @@ func ReadVolumeBitmap(file *os.File) []byte {
}
for i := 0; i < totalBitmapBlocks; i++ {
bitmapBlock := ReadBlock(file, i+volumeHeader.BitmapStartBlock)
bitmapBlock := ReadBlock(reader, i+volumeHeader.BitmapStartBlock)
for j := 0; j < 512 && i*512+j < totalBitmapBytes; j++ {
bitmap[i*512+j] = bitmapBlock[j]
@ -33,13 +40,13 @@ func ReadVolumeBitmap(file *os.File) []byte {
return bitmap
}
func writeVolumeBitmap(file *os.File, bitmap []byte) {
headerBlock := ReadBlock(file, 2)
func writeVolumeBitmap(writer io.WriterAt, reader io.ReaderAt, bitmap []byte) {
headerBlock := ReadBlock(reader, 2)
volumeHeader := parseVolumeHeader(headerBlock)
for i := 0; i < len(bitmap)/512; i++ {
WriteBlock(file, volumeHeader.BitmapStartBlock+i, bitmap[i*512:i*512+512])
WriteBlock(writer, volumeHeader.BitmapStartBlock+i, bitmap[i*512:i*512+512])
}
}

View File

@ -1,22 +1,24 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides access to read and write
// blocks on a ProDOS drive image
package prodos
import (
"os"
"io"
)
func ReadBlock(file *os.File, block int) []byte {
func ReadBlock(reader io.ReaderAt, block int) []byte {
buffer := make([]byte, 512)
file.ReadAt(buffer, int64(block)*512)
reader.ReadAt(buffer, int64(block)*512)
return buffer
}
func WriteBlock(file *os.File, block int, buffer []byte) {
WriteBlockNoSync(file, block, buffer)
file.Sync()
}
func WriteBlockNoSync(file *os.File, block int, buffer []byte) {
file.WriteAt(buffer, int64(block)*512)
func WriteBlock(writer io.WriterAt, block int, buffer []byte) {
writer.WriteAt(buffer, int64(block)*512)
}

View File

@ -1,9 +1,16 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides access to read, write, delete
// fand parse directories on a ProDOS drive image
package prodos
import (
"errors"
"fmt"
"os"
"io"
"strings"
"time"
)
@ -56,8 +63,8 @@ type FileEntry struct {
DirectoryOffset int
}
func ReadDirectory(file *os.File, path string) (VolumeHeader, DirectoryHeader, []FileEntry) {
buffer := ReadBlock(file, 2)
func ReadDirectory(reader io.ReaderAt, path string) (VolumeHeader, DirectoryHeader, []FileEntry) {
buffer := ReadBlock(reader, 2)
volumeHeader := parseVolumeHeader(buffer)
@ -68,16 +75,16 @@ func ReadDirectory(file *os.File, path string) (VolumeHeader, DirectoryHeader, [
path = strings.ToUpper(path)
paths := strings.Split(path, "/")
directoryHeader, fileEntries := getFileEntriesInDirectory(file, 2, 1, paths)
directoryHeader, fileEntries := getFileEntriesInDirectory(reader, 2, 1, paths)
return volumeHeader, directoryHeader, fileEntries
}
func getFreeFileEntryInDirectory(file *os.File, directory string) (FileEntry, error) {
_, directoryHeader, _ := ReadDirectory(file, directory)
func getFreeFileEntryInDirectory(reader io.ReaderAt, directory string) (FileEntry, error) {
_, directoryHeader, _ := ReadDirectory(reader, directory)
//DumpDirectoryHeader(directoryHeader)
blockNumber := directoryHeader.StartingBlock
buffer := ReadBlock(file, blockNumber)
buffer := ReadBlock(reader, blockNumber)
entryOffset := 43 // start at offset after header
entryNumber := 2 // header is essentially the first entry so start at 2
@ -91,7 +98,7 @@ func getFreeFileEntryInDirectory(file *os.File, directory string) (FileEntry, er
return FileEntry{}, errors.New("No free file entries found")
}
// else read the next block in the directory
buffer = ReadBlock(file, blockNumber)
buffer = ReadBlock(reader, blockNumber)
entryOffset = 4
entryNumber = 1
}
@ -109,8 +116,8 @@ func getFreeFileEntryInDirectory(file *os.File, directory string) (FileEntry, er
}
}
func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int, paths []string) (DirectoryHeader, []FileEntry) {
buffer := ReadBlock(file, blockNumber)
func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath int, paths []string) (DirectoryHeader, []FileEntry) {
buffer := ReadBlock(reader, blockNumber)
directoryHeader := parseDirectoryHeader(buffer, blockNumber)
@ -135,7 +142,7 @@ func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int,
if blockNumber == 0 {
return DirectoryHeader{}, nil
}
buffer = ReadBlock(file, nextBlock)
buffer = ReadBlock(reader, nextBlock)
nextBlock = int(buffer[2]) + int(buffer[3])*256
}
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset)
@ -147,7 +154,7 @@ func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int,
if matchedDirectory {
fileEntries[activeEntries] = fileEntry
} else if !matchedDirectory && fileEntry.FileType == 15 && paths[currentPath+1] == fileEntry.FileName {
return getFileEntriesInDirectory(file, fileEntry.KeyPointer, currentPath+1, paths)
return getFileEntriesInDirectory(reader, fileEntry.KeyPointer, currentPath+1, paths)
}
activeEntries++
}
@ -194,7 +201,7 @@ func parseFileEntry(buffer []byte, blockNumber int, entryOffset int) FileEntry {
return fileEntry
}
func writeFileEntry(file *os.File, fileEntry FileEntry) {
func writeFileEntry(writer io.WriterAt, fileEntry FileEntry) {
buffer := make([]byte, 39)
buffer[0] = byte(fileEntry.StorageType)<<4 + byte(len(fileEntry.FileName))
for i := 0; i < len(fileEntry.FileName); i++ {
@ -224,7 +231,7 @@ func writeFileEntry(file *os.File, fileEntry FileEntry) {
buffer[0x25] = byte(fileEntry.HeaderPointer & 0x00FF)
buffer[0x26] = byte(fileEntry.HeaderPointer >> 8)
_, err := file.WriteAt(buffer, int64(fileEntry.DirectoryBlock*512+fileEntry.DirectoryOffset))
_, err := writer.WriteAt(buffer, int64(fileEntry.DirectoryBlock*512+fileEntry.DirectoryOffset))
if err != nil {
}
@ -280,8 +287,8 @@ func parseDirectoryHeader(buffer []byte, blockNumber int) DirectoryHeader {
return directoryEntry
}
func writeDirectoryHeader(file *os.File, directoryHeader DirectoryHeader) {
buffer := ReadBlock(file, directoryHeader.StartingBlock)
func writeDirectoryHeader(writer io.WriterAt, reader io.ReaderAt, directoryHeader DirectoryHeader) {
buffer := ReadBlock(reader, directoryHeader.StartingBlock)
buffer[0x00] = byte(directoryHeader.PreviousBlock & 0x00FF)
buffer[0x01] = byte(directoryHeader.PreviousBlock >> 8)
buffer[0x02] = byte(directoryHeader.NextBlock & 0x00FF)
@ -292,5 +299,5 @@ func writeDirectoryHeader(file *os.File, directoryHeader DirectoryHeader) {
}
buffer[0x25] = byte(directoryHeader.ActiveFileCount & 0x00FF)
buffer[0x26] = byte(directoryHeader.ActiveFileCount >> 8)
WriteBlock(file, directoryHeader.StartingBlock, buffer)
WriteBlock(writer, directoryHeader.StartingBlock, buffer)
}

11
prodos/doc.go Normal file
View File

@ -0,0 +1,11 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
/*
Package prodos provides access to read/write/delete/list files and
directories on a ProDOS order drive images.
*/
package prodos

View File

@ -1,19 +1,27 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides access to read, write and delete
// files on a ProDOS drive image
package prodos
import (
"errors"
"os"
"io"
"strings"
"time"
)
func LoadFile(file *os.File, path string) ([]byte, error) {
fileEntry, err := getFileEntry(file, path)
// LoadFile loads in a file from a ProDOS volume into a byte array
func LoadFile(reader io.ReaderAt, path string) ([]byte, error) {
fileEntry, err := getFileEntry(reader, path)
if err != nil {
return nil, err
}
blockList, err := getDataBlocklist(file, fileEntry)
blockList, err := getDataBlocklist(reader, fileEntry)
if err != nil {
return nil, err
}
@ -21,7 +29,7 @@ func LoadFile(file *os.File, path string) ([]byte, error) {
buffer := make([]byte, fileEntry.EndOfFile)
for i := 0; i < len(blockList); i++ {
block := ReadBlock(file, blockList[i])
block := ReadBlock(reader, blockList[i])
for j := 0; j < 512 && i*512+j < fileEntry.EndOfFile; j++ {
buffer[i*512+j] = block[j]
}
@ -30,25 +38,25 @@ func LoadFile(file *os.File, path string) ([]byte, error) {
return buffer, nil
}
func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []byte) error {
func WriteFile(writer io.WriterAt, reader io.ReaderAt, path string, fileType int, auxType int, buffer []byte) error {
directory, fileName := GetDirectoryAndFileNameFromPath(path)
existingFileEntry, _ := getFileEntry(file, path)
existingFileEntry, _ := getFileEntry(reader, path)
if existingFileEntry.StorageType != StorageDeleted {
DeleteFile(file, path)
DeleteFile(writer, reader, path)
}
// get list of blocks to write file to
blockList := createBlockList(file, len(buffer))
blockList := createBlockList(reader, len(buffer))
// seedling file
if len(buffer) <= 0x200 {
WriteBlock(file, blockList[0], buffer)
WriteBlock(writer, blockList[0], buffer)
}
// sapling file needs index block
if len(buffer) > 0x200 && len(buffer) <= 0x20000 {
writeSaplingFile(file, buffer, blockList)
writeSaplingFile(writer, buffer, blockList)
}
// TODO: add tree file
@ -56,10 +64,10 @@ func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []b
return errors.New("Files > 128KB not supported yet.")
}
updateVolumeBitmap(file, blockList)
updateVolumeBitmap(writer, reader, blockList)
// add file entry to directory
fileEntry, err := getFreeFileEntryInDirectory(file, directory)
fileEntry, err := getFreeFileEntryInDirectory(reader, directory)
if err != nil {
return err
}
@ -80,59 +88,19 @@ func WriteFile(file *os.File, path string, fileType int, auxType int, buffer []b
fileEntry.StorageType = StorageTree
}
writeFileEntry(file, fileEntry)
writeFileEntry(writer, fileEntry)
// increment file count
directoryHeaderBlock := ReadBlock(file, fileEntry.HeaderPointer)
directoryHeaderBlock := ReadBlock(reader, fileEntry.HeaderPointer)
directoryHeader := parseDirectoryHeader(directoryHeaderBlock, fileEntry.HeaderPointer)
directoryHeader.ActiveFileCount++
writeDirectoryHeader(file, directoryHeader)
writeDirectoryHeader(writer, reader, 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
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)
func DeleteFile(writer io.WriterAt, reader io.ReaderAt, path string) error {
fileEntry, err := getFileEntry(reader, path)
if err != nil {
return errors.New("File not found")
}
@ -144,27 +112,27 @@ func DeleteFile(file *os.File, path string) error {
}
// free the blocks
blocks, err := getBlocklist(file, fileEntry)
blocks, err := getBlocklist(reader, fileEntry)
if err != nil {
return err
}
volumeBitmap := ReadVolumeBitmap(file)
volumeBitmap := ReadVolumeBitmap(reader)
for i := 0; i < len(blocks); i++ {
freeBlockInVolumeBitmap(volumeBitmap, blocks[i])
}
writeVolumeBitmap(file, volumeBitmap)
writeVolumeBitmap(writer, reader, volumeBitmap)
// decrement the directory entry count
directoryBlock := ReadBlock(file, fileEntry.HeaderPointer)
directoryBlock := ReadBlock(reader, fileEntry.HeaderPointer)
directoryHeader := parseDirectoryHeader(directoryBlock, fileEntry.HeaderPointer)
directoryHeader.ActiveFileCount--
writeDirectoryHeader(file, directoryHeader)
writeDirectoryHeader(writer, reader, directoryHeader)
// zero out directory entry
fileEntry.StorageType = 0
fileEntry.FileName = ""
writeFileEntry(file, fileEntry)
writeFileEntry(writer, fileEntry)
return nil
}
@ -186,8 +154,48 @@ func GetDirectoryAndFileNameFromPath(path string) (string, string) {
return directory, fileName
}
func updateVolumeBitmap(writer io.WriterAt, reader io.ReaderAt, blockList []int) {
volumeBitmap := ReadVolumeBitmap(reader)
for i := 0; i < len(blockList); i++ {
markBlockInVolumeBitmap(volumeBitmap, blockList[i])
}
writeVolumeBitmap(writer, reader, volumeBitmap)
}
func writeSaplingFile(writer io.WriterAt, 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(writer, 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(writer, blockList[blockIndexNumber], blockBuffer)
blockPointer = 0
blockIndexNumber++
} else if i == len(buffer)-1 {
for j := blockPointer; j < 512; j++ {
blockBuffer[j] = 0
}
WriteBlock(writer, blockList[blockIndexNumber], blockBuffer)
} else {
blockPointer++
}
}
}
// Returns all blocks, including index blocks
func getBlocklist(file *os.File, fileEntry FileEntry) ([]int, error) {
func getBlocklist(reader io.ReaderAt, fileEntry FileEntry) ([]int, error) {
blocks := make([]int, fileEntry.BlocksUsed)
switch fileEntry.StorageType {
@ -195,17 +203,17 @@ func getBlocklist(file *os.File, fileEntry FileEntry) ([]int, error) {
blocks[0] = fileEntry.KeyPointer
return blocks, nil
case StorageSapling:
index := ReadBlock(file, fileEntry.KeyPointer)
index := ReadBlock(reader, 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)
masterIndex := ReadBlock(reader, fileEntry.KeyPointer)
blocks[0] = fileEntry.KeyPointer
for i := 0; i < 128; i++ {
index := ReadBlock(file, int(masterIndex[i])+int(masterIndex[i+256])*256)
index := ReadBlock(reader, 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
@ -215,10 +223,10 @@ func getBlocklist(file *os.File, fileEntry FileEntry) ([]int, error) {
}
}
return nil, errors.New("Unsupported file storage type")
return nil, errors.New("unsupported file storage type")
}
func getDataBlocklist(file *os.File, fileEntry FileEntry) ([]int, error) {
func getDataBlocklist(reader io.ReaderAt, fileEntry FileEntry) ([]int, error) {
switch fileEntry.StorageType {
case StorageSeedling:
blocks := make([]int, 1)
@ -226,7 +234,7 @@ func getDataBlocklist(file *os.File, fileEntry FileEntry) ([]int, error) {
return blocks, nil
case StorageSapling:
blocks := make([]int, fileEntry.BlocksUsed-1)
index := ReadBlock(file, fileEntry.KeyPointer)
index := ReadBlock(reader, fileEntry.KeyPointer)
for i := 0; i < fileEntry.BlocksUsed-1; i++ {
blocks[i] = int(index[i]) + int(index[i+256])*256
}
@ -236,7 +244,7 @@ func getDataBlocklist(file *os.File, fileEntry FileEntry) ([]int, error) {
return nil, errors.New("Unsupported file storage type")
}
func createBlockList(file *os.File, fileSize int) []int {
func createBlockList(reader io.ReaderAt, fileSize int) []int {
numberOfBlocks := fileSize / 512
if fileSize%512 > 0 {
numberOfBlocks++
@ -254,15 +262,15 @@ func createBlockList(file *os.File, fileSize int) []int {
numberOfBlocks++
}
}
volumeBitmap := ReadVolumeBitmap(file)
volumeBitmap := ReadVolumeBitmap(reader)
blockList := findFreeBlocks(volumeBitmap, numberOfBlocks)
return blockList
}
func getFileEntry(file *os.File, path string) (FileEntry, error) {
func getFileEntry(reader io.ReaderAt, path string) (FileEntry, error) {
directory, fileName := GetDirectoryAndFileNameFromPath(path)
_, _, fileEntries := ReadDirectory(file, directory)
_, _, fileEntries := ReadDirectory(reader, directory)
if fileEntries == nil || len(fileEntries) == 0 {
return FileEntry{}, errors.New("File entry not found")

View File

@ -1,13 +1,21 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides access to format a ProDOS drive image
package prodos
import (
"fmt"
"os"
"io"
"strings"
"time"
)
func CreateVolume(file *os.File, volumeName string, numberOfBlocks int) {
// CreateVolume formats a new ProDOS volume including boot block,
// volume bitmap and empty directory
func CreateVolume(writer io.WriterAt, reader io.ReaderAt, volumeName string, numberOfBlocks int) {
if numberOfBlocks > 65535 || numberOfBlocks < 64 {
return
}
@ -20,7 +28,7 @@ func CreateVolume(file *os.File, volumeName string, numberOfBlocks int) {
blankBlock := make([]byte, 512)
for i := 0; i < numberOfBlocks; i++ {
WriteBlockNoSync(file, i, blankBlock)
WriteBlock(writer, i, blankBlock)
}
volumeHeader := [43]byte{}
@ -55,11 +63,10 @@ func CreateVolume(file *os.File, volumeName string, numberOfBlocks int) {
volumeHeader[0x29] = byte(numberOfBlocks & 0xFF)
volumeHeader[0x2A] = byte(numberOfBlocks >> 8)
file.WriteAt(volumeHeader[:], 1024)
file.Sync()
writer.WriteAt(volumeHeader[:], 1024)
// boot block 0
WriteBlock(file, 0, getBootBlock())
WriteBlock(writer, 0, getBootBlock())
// pointers to volume directory blocks
for i := 2; i < 6; i++ {
@ -76,12 +83,12 @@ func CreateVolume(file *os.File, volumeName string, numberOfBlocks int) {
pointers[2] = byte(i + 1)
}
pointers[3] = 0x00
file.WriteAt(pointers, int64(i*512))
writer.WriteAt(pointers, int64(i*512))
}
// volume bit map starting at block 6
volumeBitmap := createVolumeBitmap(numberOfBlocks)
writeVolumeBitmap(file, volumeBitmap)
writeVolumeBitmap(writer, reader, volumeBitmap)
}
func getBootBlock() []byte {

View File

@ -2,7 +2,6 @@ package prodos
import (
"fmt"
"os"
"testing"
)
@ -20,17 +19,9 @@ func TestCreateVolume(t *testing.T) {
for _, tt := range tests {
testname := fmt.Sprintf("%d", tt.blocks)
t.Run(testname, func(t *testing.T) {
fileName := os.TempDir() + "/test-volume.hdv"
defer os.Remove(fileName)
file, err := os.Create(fileName)
if err != nil {
t.Errorf("failed to create file: %s\n", err)
return
}
file := NewMemoryFile(0x2000000)
defer file.Close()
CreateVolume(file, tt.wantVolumeName, tt.blocks)
CreateVolume(file, file, tt.wantVolumeName, tt.blocks)
volumeHeader, _, fileEntries := ReadDirectory(file, "")
if volumeHeader.VolumeName != tt.wantVolumeName {

26
prodos/memfile.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides a file in memory
package prodos
type MemoryFile struct {
data []byte
size int
}
func NewMemoryFile(size int) *MemoryFile {
return &MemoryFile{make([]byte, size), size}
}
func (memoryFile *MemoryFile) WriteAt(data []byte, offset int64) (int, error) {
copy(memoryFile.data[int(offset):], data)
return len(data), nil
}
func (memoryFile *MemoryFile) ReadAt(data []byte, offset int64) (int, error) {
copy(data, memoryFile.data[int(offset):])
return len(data), nil
}

View File

@ -1,3 +1,10 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides text dumps for directories
// and blocks
package prodos
import (

View File

@ -1,3 +1,9 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides conversion to and from ProDOS time format
package prodos
import (

View File

@ -1,3 +1,9 @@
// Copyright Terence J. Boldt (c)2021-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file provides readme for command line utility
package main
import "fmt"
@ -8,7 +14,7 @@ func printReadme() {
fmt.Println(`
MIT License
Copyright (c) 2021 Terence Boldt
Copyright (c)2021-2022 Terence Boldt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal