mirror of
https://github.com/tjboldt/ProDOS-Utilities.git
synced 2024-11-24 16:31:28 +00:00
Add putallrecursive
This commit is contained in:
parent
1722e7b23a
commit
286964d3ee
24
main.go
24
main.go
@ -32,7 +32,7 @@ func main() {
|
||||
var auxType int
|
||||
flag.StringVar(&fileName, "d", "", "A ProDOS format drive image")
|
||||
flag.StringVar(&pathName, "p", "", "Path name in ProDOS drive image (default is root of volume)")
|
||||
flag.StringVar(&command, "c", "ls", "Command to execute: ls, get, put, rm, mkdir, readblock, writeblock, create, putall")
|
||||
flag.StringVar(&command, "c", "ls", "Command to execute: ls, get, put, rm, mkdir, readblock, writeblock, create, putall, putallrecursive")
|
||||
flag.StringVar(&outFileName, "o", "", "Name of file to write")
|
||||
flag.StringVar(&inFileName, "i", "", "Name of file to read")
|
||||
flag.IntVar(&volumeSize, "s", 65535, "Number of blocks to create the volume with (default 65535, 64 to 65535, 0x0040 to 0xFFFF hex input accepted)")
|
||||
@ -62,13 +62,17 @@ func main() {
|
||||
case "create":
|
||||
create(fileName, volumeName, volumeSize)
|
||||
case "putall":
|
||||
putall(fileName, inFileName)
|
||||
putall(fileName, inFileName, pathName, false)
|
||||
case "putallrecursive":
|
||||
putall(fileName, inFileName, pathName, true)
|
||||
case "rm":
|
||||
rm(fileName, pathName)
|
||||
case "mkdir":
|
||||
mkdir(fileName, pathName)
|
||||
case "dumpfile":
|
||||
dumpFile(fileName, pathName)
|
||||
case "dumpdirectory":
|
||||
dumpDirectory(fileName, pathName)
|
||||
default:
|
||||
fmt.Printf("Invalid command: %s\n\n", command)
|
||||
flag.PrintDefaults()
|
||||
@ -88,6 +92,18 @@ func dumpFile(fileName string, pathName string) {
|
||||
prodos.DumpFileEntry(fileEntry)
|
||||
}
|
||||
|
||||
func dumpDirectory(fileName string, pathName string) {
|
||||
checkPathName(pathName)
|
||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to open drive image %s:\n %s", fileName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
_, directoryheader, _, err := prodos.ReadDirectory(file, pathName)
|
||||
prodos.DumpDirectoryHeader(directoryheader)
|
||||
}
|
||||
|
||||
func mkdir(fileName string, pathName string) {
|
||||
checkPathName(pathName)
|
||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||
@ -114,7 +130,7 @@ func rm(fileName string, pathName string) {
|
||||
prodos.DeleteFile(file, pathName)
|
||||
}
|
||||
|
||||
func putall(fileName string, inFileName string) {
|
||||
func putall(fileName string, inFileName string, pathName string, recursive bool) {
|
||||
if len(inFileName) == 0 {
|
||||
inFileName = "."
|
||||
}
|
||||
@ -124,7 +140,7 @@ func putall(fileName string, inFileName string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
err = prodos.AddFilesFromHostDirectory(file, inFileName)
|
||||
err = prodos.AddFilesFromHostDirectory(file, inFileName, pathName, recursive)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to add host files: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
@ -131,7 +131,6 @@ func createVolumeBitmap(numberOfBlocks int) []byte {
|
||||
markBlockInVolumeBitmap(volumeBitmap, i)
|
||||
}
|
||||
}
|
||||
//DumpBlock(volumeBitmap)
|
||||
|
||||
return volumeBitmap
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ type VolumeHeader struct {
|
||||
type DirectoryHeader struct {
|
||||
PreviousBlock int
|
||||
NextBlock int
|
||||
IsSubDirectory bool
|
||||
Name string
|
||||
CreationTime time.Time
|
||||
Version int
|
||||
@ -64,19 +65,20 @@ const (
|
||||
|
||||
// FileEntry from ProDOS
|
||||
type FileEntry struct {
|
||||
StorageType int
|
||||
FileName string
|
||||
FileType int
|
||||
CreationTime time.Time
|
||||
KeyPointer int
|
||||
Version int
|
||||
MinVersion int
|
||||
BlocksUsed int
|
||||
EndOfFile int
|
||||
Access int
|
||||
AuxType int
|
||||
ModifiedTime time.Time
|
||||
HeaderPointer int
|
||||
StorageType int
|
||||
FileName string
|
||||
FileType int
|
||||
CreationTime time.Time
|
||||
KeyPointer int
|
||||
Version int
|
||||
MinVersion int
|
||||
BlocksUsed int
|
||||
EndOfFile int
|
||||
Access int
|
||||
AuxType int
|
||||
ModifiedTime time.Time
|
||||
HeaderPointer int
|
||||
|
||||
DirectoryBlock int
|
||||
DirectoryOffset int
|
||||
}
|
||||
@ -95,6 +97,11 @@ func ReadDirectory(reader io.ReaderAt, path string) (VolumeHeader, DirectoryHead
|
||||
path = fmt.Sprintf("/%s", volumeHeader.VolumeName)
|
||||
}
|
||||
|
||||
// add volume name if not full path
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = fmt.Sprintf("/%s/%s", volumeHeader.VolumeName, path)
|
||||
}
|
||||
|
||||
path = strings.ToUpper(path)
|
||||
paths := strings.Split(path, "/")
|
||||
|
||||
@ -106,12 +113,23 @@ func ReadDirectory(reader io.ReaderAt, path string) (VolumeHeader, DirectoryHead
|
||||
return volumeHeader, directoryHeader, fileEntries, nil
|
||||
}
|
||||
|
||||
// CreateDirectory creates a directory information of a specified path
|
||||
// on a ProDOS image
|
||||
func CreateDirectory(readerWriter ReaderWriterAt, path string) error {
|
||||
if len(path) == 0 {
|
||||
return errors.New("cannot create directory with path")
|
||||
}
|
||||
|
||||
// add volume name if not full path
|
||||
path, err := makeFullPath(path, readerWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parentPath, newDirectory := GetDirectoryAndFileNameFromPath(path)
|
||||
|
||||
existingFileEntry, _ := GetFileEntry(readerWriter, path)
|
||||
if existingFileEntry.StorageType != StorageDeleted {
|
||||
//DeleteFile(readerWriter, path)
|
||||
return errors.New("directory already exists")
|
||||
}
|
||||
|
||||
@ -140,6 +158,8 @@ func CreateDirectory(readerWriter ReaderWriterAt, path string) error {
|
||||
fileEntry.KeyPointer = blockList[0]
|
||||
fileEntry.Access = 0b11100011
|
||||
fileEntry.StorageType = StorageDirectory
|
||||
fileEntry.Version = 0x24
|
||||
fileEntry.MinVersion = 0x00
|
||||
|
||||
writeFileEntry(readerWriter, fileEntry)
|
||||
|
||||
@ -152,6 +172,7 @@ func CreateDirectory(readerWriter ReaderWriterAt, path string) error {
|
||||
directoryEntry := DirectoryHeader{
|
||||
PreviousBlock: 0,
|
||||
NextBlock: 0,
|
||||
IsSubDirectory: true,
|
||||
Name: newDirectory,
|
||||
CreationTime: time.Now(),
|
||||
Version: 0x24,
|
||||
@ -162,7 +183,7 @@ func CreateDirectory(readerWriter ReaderWriterAt, path string) error {
|
||||
ActiveFileCount: 0,
|
||||
StartingBlock: blockList[0],
|
||||
ParentBlock: fileEntry.DirectoryBlock,
|
||||
ParentEntry: fileEntry.DirectoryOffset,
|
||||
ParentEntry: (fileEntry.DirectoryOffset - 0x04) / 0x27,
|
||||
ParentEntryLength: 0x27,
|
||||
}
|
||||
|
||||
@ -175,14 +196,26 @@ func CreateDirectory(readerWriter ReaderWriterAt, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFreeFileEntryInDirectory(reader io.ReaderAt, directory string) (FileEntry, error) {
|
||||
_, directoryHeader, _, err := ReadDirectory(reader, directory)
|
||||
func makeFullPath(path string, reader io.ReaderAt) (string, error) {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
buffer, err := ReadBlock(reader, 0x0002)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
volumeHeader := parseVolumeHeader(buffer)
|
||||
path = fmt.Sprintf("/%s/%s", volumeHeader.VolumeName, path)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func getFreeFileEntryInDirectory(readerWriter ReaderWriterAt, directory string) (FileEntry, error) {
|
||||
_, directoryHeader, _, err := ReadDirectory(readerWriter, directory)
|
||||
if err != nil {
|
||||
return FileEntry{}, err
|
||||
}
|
||||
//DumpDirectoryHeader(directoryHeader)
|
||||
blockNumber := directoryHeader.StartingBlock
|
||||
buffer, err := ReadBlock(reader, blockNumber)
|
||||
buffer, err := ReadBlock(readerWriter, blockNumber)
|
||||
if err != nil {
|
||||
return FileEntry{}, err
|
||||
}
|
||||
@ -191,21 +224,28 @@ func getFreeFileEntryInDirectory(reader io.ReaderAt, directory string) (FileEntr
|
||||
|
||||
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{}, errors.New("no free file entries found")
|
||||
nextBlockNumber := int(buffer[2]) + int(buffer[3])*256
|
||||
// if we ran out of blocks in the directory, expand directory or fail
|
||||
if nextBlockNumber == 0 {
|
||||
if !directoryHeader.IsSubDirectory {
|
||||
return FileEntry{}, errors.New("no free file entries found")
|
||||
}
|
||||
nextBlockNumber, err = expandDirectory(readerWriter, nextBlockNumber, buffer, blockNumber, directoryHeader)
|
||||
if err != nil {
|
||||
return FileEntry{}, err
|
||||
}
|
||||
}
|
||||
blockNumber = nextBlockNumber
|
||||
// else read the next block in the directory
|
||||
buffer, err = ReadBlock(reader, blockNumber)
|
||||
buffer, err = ReadBlock(readerWriter, blockNumber)
|
||||
if err != nil {
|
||||
return FileEntry{}, nil
|
||||
}
|
||||
|
||||
entryOffset = 4
|
||||
entryNumber = 1
|
||||
}
|
||||
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset)
|
||||
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+0x28], blockNumber, entryOffset)
|
||||
|
||||
if fileEntry.StorageType == StorageDeleted {
|
||||
fileEntry.DirectoryBlock = blockNumber
|
||||
@ -219,6 +259,51 @@ func getFreeFileEntryInDirectory(reader io.ReaderAt, directory string) (FileEntr
|
||||
}
|
||||
}
|
||||
|
||||
func expandDirectory(readerWriter ReaderWriterAt, nextBlockNumber int, buffer []byte, blockNumber int, directoryHeader DirectoryHeader) (int, error) {
|
||||
volumeBitMap, err := ReadVolumeBitmap(readerWriter)
|
||||
if err != nil {
|
||||
errString := fmt.Sprintf("failed to get volume bitmap to expand directory: %s", err)
|
||||
return 0, errors.New(errString)
|
||||
}
|
||||
blockList := findFreeBlocks(volumeBitMap, 1)
|
||||
if len(blockList) != 1 {
|
||||
return 0, errors.New("failed to get free block to expand directory")
|
||||
}
|
||||
|
||||
nextBlockNumber = blockList[0]
|
||||
buffer[0x02] = byte(nextBlockNumber & 0x00FF)
|
||||
buffer[0x03] = byte(nextBlockNumber >> 8)
|
||||
WriteBlock(readerWriter, blockNumber, buffer)
|
||||
if err != nil {
|
||||
errString := fmt.Sprintf("failed to write block to expand directory: %s", err)
|
||||
return 0, errors.New(errString)
|
||||
}
|
||||
|
||||
buffer = make([]byte, 0x200)
|
||||
buffer[0x00] = byte(blockNumber & 0x00FF)
|
||||
buffer[0x01] = byte(blockNumber >> 8)
|
||||
err = WriteBlock(readerWriter, nextBlockNumber, buffer)
|
||||
if err != nil {
|
||||
errString := fmt.Sprintf("failed to write new block to expand directory: %s", err)
|
||||
return 0, errors.New(errString)
|
||||
}
|
||||
|
||||
updateVolumeBitmap(readerWriter, blockList)
|
||||
|
||||
buffer, err = ReadBlock(readerWriter, directoryHeader.ParentBlock)
|
||||
if err != nil {
|
||||
errString := fmt.Sprintf("failed to read parent block to expand directory: %s", err)
|
||||
return 0, errors.New(errString)
|
||||
}
|
||||
directoryEntryOffset := directoryHeader.ParentEntry*directoryHeader.EntryLength + 0x04
|
||||
directoryFileEntry := parseFileEntry(buffer[directoryEntryOffset:directoryEntryOffset+0x28], directoryHeader.ParentBlock, directoryHeader.ParentEntry*directoryHeader.EntryLength+0x04)
|
||||
directoryFileEntry.BlocksUsed++
|
||||
directoryFileEntry.EndOfFile += 0x200
|
||||
writeFileEntry(readerWriter, directoryFileEntry)
|
||||
|
||||
return nextBlockNumber, nil
|
||||
}
|
||||
|
||||
func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath int, paths []string) (DirectoryHeader, []FileEntry, error) {
|
||||
buffer, err := ReadBlock(reader, blockNumber)
|
||||
if err != nil {
|
||||
@ -333,13 +418,14 @@ func writeFileEntry(writer io.WriterAt, fileEntry FileEntry) {
|
||||
buffer[0x1E] = byte(fileEntry.Access)
|
||||
buffer[0x1F] = byte(fileEntry.AuxType & 0x00FF)
|
||||
buffer[0x20] = byte(fileEntry.AuxType >> 8)
|
||||
modifiedTime := DateTimeToProDOS(fileEntry.CreationTime)
|
||||
modifiedTime := DateTimeToProDOS(fileEntry.ModifiedTime)
|
||||
for i := 0; i < 4; i++ {
|
||||
buffer[0x21+i] = modifiedTime[i]
|
||||
}
|
||||
buffer[0x25] = byte(fileEntry.HeaderPointer & 0x00FF)
|
||||
buffer[0x26] = byte(fileEntry.HeaderPointer >> 8)
|
||||
|
||||
//fmt.Printf("Writing file entry at block: %04X offset: %04X\n", fileEntry.DirectoryBlock, fileEntry.DirectoryOffset)
|
||||
_, err := writer.WriteAt(buffer, int64(fileEntry.DirectoryBlock*512+fileEntry.DirectoryOffset))
|
||||
if err != nil {
|
||||
|
||||
@ -381,7 +467,8 @@ func parseVolumeHeader(buffer []byte) VolumeHeader {
|
||||
func parseDirectoryHeader(buffer []byte, blockNumber int) DirectoryHeader {
|
||||
previousBlock := int(buffer[0x00]) + int(buffer[0x01])*256
|
||||
nextBlock := int(buffer[0x02]) + int(buffer[0x03])*256
|
||||
filenameLength := buffer[0x04] & 15
|
||||
isSubDirectory := (buffer[0x04] & 0xF0) == 0xE0
|
||||
filenameLength := buffer[0x04] & 0x0F
|
||||
name := string(buffer[0x05 : filenameLength+0x05])
|
||||
creationTime := DateTimeFromProDOS(buffer[0x1C:0x20])
|
||||
version := int(buffer[0x20])
|
||||
@ -398,6 +485,7 @@ func parseDirectoryHeader(buffer []byte, blockNumber int) DirectoryHeader {
|
||||
PreviousBlock: previousBlock,
|
||||
NextBlock: nextBlock,
|
||||
StartingBlock: blockNumber,
|
||||
IsSubDirectory: isSubDirectory,
|
||||
Name: name,
|
||||
CreationTime: creationTime,
|
||||
Version: version,
|
||||
@ -415,6 +503,7 @@ func parseDirectoryHeader(buffer []byte, blockNumber int) DirectoryHeader {
|
||||
}
|
||||
|
||||
func writeDirectoryHeader(readerWriter ReaderWriterAt, directoryHeader DirectoryHeader) error {
|
||||
// Reading back the block preserves values including reserved fields
|
||||
buffer, err := ReadBlock(readerWriter, directoryHeader.StartingBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -423,6 +512,11 @@ func writeDirectoryHeader(readerWriter ReaderWriterAt, directoryHeader Directory
|
||||
buffer[0x01] = byte(directoryHeader.PreviousBlock >> 8)
|
||||
buffer[0x02] = byte(directoryHeader.NextBlock & 0x00FF)
|
||||
buffer[0x03] = byte(directoryHeader.NextBlock >> 8)
|
||||
if directoryHeader.IsSubDirectory {
|
||||
buffer[0x04] = 0xE0
|
||||
} else {
|
||||
buffer[0x04] = 0xF0
|
||||
}
|
||||
buffer[0x04] = buffer[0x04] | byte(len(directoryHeader.Name))
|
||||
for i := 0; i < len(directoryHeader.Name); i++ {
|
||||
buffer[0x05+i] = directoryHeader.Name[i]
|
||||
@ -431,6 +525,16 @@ func writeDirectoryHeader(readerWriter ReaderWriterAt, directoryHeader Directory
|
||||
for i := 0; i < 4; i++ {
|
||||
buffer[0x1C+i] = creationTime[i]
|
||||
}
|
||||
// Without these reserved bytes, reading the directory causes I/O ERROR
|
||||
buffer[0x14] = 0x75
|
||||
buffer[0x15] = byte(directoryHeader.Version)
|
||||
buffer[0x16] = byte(directoryHeader.MinVersion)
|
||||
buffer[0x17] = 0xC3
|
||||
buffer[0x18] = 0x0D
|
||||
buffer[0x19] = 0x27
|
||||
buffer[0x1A] = 0x00
|
||||
buffer[0x1B] = 0x00
|
||||
|
||||
buffer[0x20] = byte(directoryHeader.Version)
|
||||
buffer[0x21] = byte(directoryHeader.MinVersion)
|
||||
buffer[0x22] = byte(directoryHeader.Access)
|
||||
|
@ -46,6 +46,10 @@ func LoadFile(reader io.ReaderAt, path string) ([]byte, error) {
|
||||
func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType int, createdTime time.Time, modifiedTime time.Time, buffer []byte) error {
|
||||
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
||||
|
||||
if len(fileName) > 15 {
|
||||
return errors.New("filename too long")
|
||||
}
|
||||
|
||||
existingFileEntry, _ := GetFileEntry(readerWriter, path)
|
||||
if existingFileEntry.StorageType != StorageDeleted {
|
||||
DeleteFile(readerWriter, path)
|
||||
@ -91,6 +95,8 @@ func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType i
|
||||
fileEntry.EndOfFile = len(buffer)
|
||||
fileEntry.FileType = fileType
|
||||
fileEntry.KeyPointer = blockList[0]
|
||||
fileEntry.Version = 0x24
|
||||
fileEntry.MinVersion = 0x00
|
||||
fileEntry.Access = 0b11100011
|
||||
if len(blockList) == 1 {
|
||||
fileEntry.StorageType = StorageSeedling
|
||||
@ -135,6 +141,7 @@ func DeleteFile(readerWriter ReaderWriterAt, path string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
volumeBitmap, err := ReadVolumeBitmap(readerWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -307,26 +314,27 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockOffset := 0
|
||||
if !dataOnly {
|
||||
blocks[0] = fileEntry.KeyPointer
|
||||
blockOffset = 1
|
||||
}
|
||||
blockOffset := 1
|
||||
blocks[0] = fileEntry.KeyPointer
|
||||
for i := 0; i < fileEntry.BlocksUsed-1; i++ {
|
||||
blocks[i+blockOffset] = int(index[i]) + int(index[i+256])*256
|
||||
}
|
||||
if dataOnly {
|
||||
return blocks[1:], nil
|
||||
}
|
||||
return blocks, nil
|
||||
case StorageTree:
|
||||
// this is actually too large
|
||||
dataBlocks := make([]int, fileEntry.BlocksUsed)
|
||||
numberOfIndexBlocks := fileEntry.BlocksUsed/256 + 1
|
||||
if fileEntry.BlocksUsed%256 != 0 {
|
||||
numberOfIndexBlocks++
|
||||
}
|
||||
// this is also actually too large
|
||||
numberOfIndexBlocks := fileEntry.BlocksUsed/256 + 2
|
||||
indexBlocks := make([]int, numberOfIndexBlocks)
|
||||
masterIndex, err := ReadBlock(reader, fileEntry.KeyPointer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numberOfDataBlocks := 0
|
||||
|
||||
indexBlocks[0] = fileEntry.KeyPointer
|
||||
indexBlockCount := 1
|
||||
|
||||
@ -345,6 +353,7 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int
|
||||
if (int(index[j]) + int(index[j+256])*256) == 0 {
|
||||
break
|
||||
}
|
||||
numberOfDataBlocks++
|
||||
dataBlocks[i*256+j] = int(index[j]) + int(index[j+256])*256
|
||||
}
|
||||
}
|
||||
@ -353,7 +362,7 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int
|
||||
return dataBlocks, nil
|
||||
}
|
||||
|
||||
blocks = append(indexBlocks, dataBlocks...)
|
||||
blocks = append(indexBlocks[0:numberOfIndexBlocks], dataBlocks[0:numberOfDataBlocks]...)
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
@ -392,7 +401,7 @@ func createBlockList(reader io.ReaderAt, fileSize int) ([]int, error) {
|
||||
|
||||
blockList := findFreeBlocks(volumeBitmap, numberOfBlocks)
|
||||
|
||||
return blockList, nil
|
||||
return blockList[0:numberOfBlocks], nil
|
||||
}
|
||||
|
||||
// GetFileEntry returns a file entry for the given path
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreatBlocklist(t *testing.T) {
|
||||
func TestCreateBlocklist(t *testing.T) {
|
||||
var tests = []struct {
|
||||
fileSize int
|
||||
wantBlocks int
|
||||
|
@ -8,6 +8,7 @@ package prodos
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -19,7 +20,15 @@ import (
|
||||
// from the specified host directory
|
||||
func AddFilesFromHostDirectory(
|
||||
readerWriter ReaderWriterAt,
|
||||
directory string) error {
|
||||
directory string,
|
||||
path string,
|
||||
recursive bool) error {
|
||||
|
||||
path, err := makeFullPath(path, readerWriter)
|
||||
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path = path + "/"
|
||||
}
|
||||
|
||||
files, err := os.ReadDir(directory)
|
||||
if err != nil {
|
||||
@ -33,37 +42,67 @@ func AddFilesFromHostDirectory(
|
||||
}
|
||||
|
||||
if file.Name()[0] != '.' && !file.IsDir() && info.Size() > 0 && info.Size() <= 0x1000000 {
|
||||
err = WriteFileFromFile(readerWriter, "", 0, 0, info.ModTime(), filepath.Join(directory, file.Name()))
|
||||
err = WriteFileFromFile(readerWriter, path, 0, 0, info.ModTime(), filepath.Join(directory, file.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if file.Name()[0] != '.' && recursive && file.IsDir() {
|
||||
newPath := file.Name()
|
||||
if len(newPath) > 15 {
|
||||
newPath = newPath[0:15]
|
||||
}
|
||||
newFullPath := strings.ToUpper(path + newPath)
|
||||
|
||||
newHostDirectory := filepath.Join(directory, file.Name())
|
||||
CreateDirectory(readerWriter, newFullPath)
|
||||
AddFilesFromHostDirectory(readerWriter, newHostDirectory, newFullPath+"/", recursive)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteFileFromFile writes a file to a ProDOS volume from a host file
|
||||
func WriteFileFromFile(readerWriter ReaderWriterAt, pathName string, fileType int, auxType int, modifiedTime time.Time, inFileName string) error {
|
||||
fmt.Printf("WriteFileFromFile: %s\n", inFileName)
|
||||
func WriteFileFromFile(
|
||||
readerWriter ReaderWriterAt,
|
||||
pathName string,
|
||||
fileType int,
|
||||
auxType int,
|
||||
modifiedTime time.Time,
|
||||
inFileName string) error {
|
||||
|
||||
inFile, err := os.ReadFile(inFileName)
|
||||
if err != nil {
|
||||
fmt.Println("failed to read file")
|
||||
return err
|
||||
errString := fmt.Sprintf("write from file failed: %s", err)
|
||||
return errors.New(errString)
|
||||
}
|
||||
|
||||
if auxType == 0 && fileType == 0 {
|
||||
auxType, fileType, inFile, err = convertFileByType(inFileName, inFile)
|
||||
if err != nil {
|
||||
fmt.Println("failed to convert file")
|
||||
return err
|
||||
errString := fmt.Sprintf("failed to convert file: %s", err)
|
||||
return errors.New(errString)
|
||||
}
|
||||
}
|
||||
|
||||
trimExtensions := false
|
||||
if len(pathName) == 0 {
|
||||
_, pathName = filepath.Split(inFileName)
|
||||
pathName = strings.ToUpper(pathName)
|
||||
trimExtensions = true
|
||||
}
|
||||
|
||||
if strings.HasSuffix(pathName, "/") {
|
||||
trimExtensions = true
|
||||
_, fileName := filepath.Split(inFileName)
|
||||
pathName = strings.ToUpper(pathName + fileName)
|
||||
}
|
||||
|
||||
if trimExtensions {
|
||||
ext := filepath.Ext(pathName)
|
||||
|
||||
if len(ext) > 0 {
|
||||
switch ext {
|
||||
case ".SYS", ".TXT", ".BAS", ".BIN":
|
||||
@ -72,6 +111,13 @@ func WriteFileFromFile(readerWriter ReaderWriterAt, pathName string, fileType in
|
||||
}
|
||||
}
|
||||
|
||||
paths := strings.SplitAfter(pathName, "/")
|
||||
if len(paths[len(paths)-1]) > 15 {
|
||||
paths[len(paths)-1] = paths[len(paths)-1][0:15]
|
||||
pathName = strings.Join(paths, "")
|
||||
}
|
||||
|
||||
fmt.Printf("Source: %s Destination: %s\n", inFileName, pathName)
|
||||
return WriteFile(readerWriter, pathName, fileType, auxType, time.Now(), modifiedTime, inFile)
|
||||
}
|
||||
|
||||
|
@ -100,6 +100,8 @@ func DumpFileEntry(fileEntry FileEntry) {
|
||||
fmt.Printf("Storage type: %02X\n", fileEntry.StorageType)
|
||||
fmt.Printf("Header pointer: %04X\n", fileEntry.HeaderPointer)
|
||||
fmt.Printf("Access: %04X\n", fileEntry.Access)
|
||||
fmt.Printf("Directory block: %04X\n", fileEntry.DirectoryBlock)
|
||||
fmt.Printf("Directory offset: %04X\n", fileEntry.DirectoryOffset)
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
@ -119,11 +121,22 @@ func DumpVolumeHeader(volumeHeader VolumeHeader) {
|
||||
|
||||
// DumpDirectoryHeader dumps the directory header as text
|
||||
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)
|
||||
fmt.Printf("Is subdirectory: %t\n", directoryHeader.IsSubDirectory)
|
||||
fmt.Printf("Name: %s\n", directoryHeader.Name)
|
||||
fmt.Printf("Creation time: %s\n", TimeToString(directoryHeader.CreationTime))
|
||||
fmt.Printf("Version: %02X\n", directoryHeader.Version)
|
||||
fmt.Printf("MinVersion: %02X\n", directoryHeader.MinVersion)
|
||||
fmt.Printf("Access: %02X\n", directoryHeader.Access)
|
||||
fmt.Printf("Entry length: %02X\n", directoryHeader.EntryLength)
|
||||
fmt.Printf("Entries per block: %02X\n", directoryHeader.EntriesPerBlock)
|
||||
fmt.Printf("File count: %d\n", directoryHeader.ActiveFileCount)
|
||||
fmt.Printf("Active file count: %04X\n", directoryHeader.ActiveFileCount)
|
||||
fmt.Printf("Parent block: %04X\n", directoryHeader.ParentBlock)
|
||||
fmt.Printf("Parent entry: %02X\n", directoryHeader.ParentEntry)
|
||||
fmt.Printf("Parent entry length: %02X\n", directoryHeader.ParentEntryLength)
|
||||
}
|
||||
|
||||
// DumpBlock dumps the block as hexadecimal and text
|
||||
|
Loading…
Reference in New Issue
Block a user