diff --git a/main.go b/main.go index 37aa4c9..f1c6436 100644 --- a/main.go +++ b/main.go @@ -9,9 +9,10 @@ import ( func main() { if len(os.Args) < 2 || len(os.Args) > 3 { - fmt.Printf("Usage:") - fmt.Printf(" ProDOS-Utilities DRIVE_IMAGE") - fmt.Printf(" ProDOS-Utilities DRIVE_IMAGE /FULL_PATH") + fmt.Printf("Usage:\n") + fmt.Printf(" ProDOS-Utilities DRIVE_IMAGE\n") + fmt.Printf(" ProDOS-Utilities DRIVE_IMAGE /FULL_PATH\n") + os.Exit(1) } fileName := os.Args[1] @@ -21,8 +22,13 @@ func main() { pathName = os.Args[2] } + file, err := os.OpenFile(fileName, os.O_RDWR, 0755) + if err != nil { + os.Exit(1) + } + // empty path or volume name means read root directory - volumeHeader, fileEntries := prodos.ReadDirectory(fileName, pathName) + volumeHeader, fileEntries := prodos.ReadDirectory(file, pathName) fmt.Printf("VOLUME: %s\n\n", volumeHeader.VolumeName) fmt.Printf("NAME TYPE BLOCKS MODIFIED CREATED ENDFILE SUBTYPE\n\n") diff --git a/prodos/directory.go b/prodos/directory.go index 017698b..2b4f6a0 100644 --- a/prodos/directory.go +++ b/prodos/directory.go @@ -46,15 +46,10 @@ type FileEntry struct { ModifiedTime time.Time } -func ReadDirectory(driveFileName string, path string) (VolumeHeader, []FileEntry) { - file, err := os.OpenFile(driveFileName, os.O_RDWR, 0755) - if err != nil { - return VolumeHeader{}, nil - } - +func ReadDirectory(file *os.File, path string) (VolumeHeader, []FileEntry) { buffer := ReadBlock(file, 2) - volumeHeader := ParseVolumeHeader(buffer) + volumeHeader := parseVolumeHeader(buffer) //dumpVolumeHeader(volumeHeader) if len(path) == 0 { @@ -74,7 +69,7 @@ func getFileEntriesInDirectory(file *os.File, blockNumber int, currentPath int, buffer := ReadBlock(file, blockNumber) - directoryHeader := ParseDirectoryHeader(buffer) + directoryHeader := parseDirectoryHeader(buffer) fileEntries := make([]FileEntry, directoryHeader.ActiveFileCount) entryOffset := 43 // start at offset after header @@ -149,7 +144,7 @@ func parseFileEntry(buffer []byte) FileEntry { return fileEntry } -func ParseVolumeHeader(buffer []byte) VolumeHeader { +func parseVolumeHeader(buffer []byte) VolumeHeader { nextBlock := int(buffer[2]) + int(buffer[3])*256 filenameLength := buffer[4] & 15 volumeName := string(buffer[5 : filenameLength+5]) @@ -179,7 +174,7 @@ func ParseVolumeHeader(buffer []byte) VolumeHeader { return volumeHeader } -func ParseDirectoryHeader(buffer []byte) DirectoryHeader { +func parseDirectoryHeader(buffer []byte) DirectoryHeader { nextBlock := int(buffer[2]) + int(buffer[3])*256 filenameLength := buffer[4] & 15 name := string(buffer[5 : filenameLength+5]) diff --git a/prodos/dumpBlock.go b/prodos/dumpBlock.go deleted file mode 100644 index ea9ed1a..0000000 --- a/prodos/dumpBlock.go +++ /dev/null @@ -1,20 +0,0 @@ -package prodos - -import "fmt" - -func DumpBlock(buffer []byte) { - for i := 0; i < len(buffer); i += 16 { - for j := i; j < i+16; j++ { - fmt.Printf("%02X ", buffer[j]) - } - for j := i; j < i+16; j++ { - c := buffer[j] & 127 - if c >= 32 { - fmt.Printf("%c", c) - } else { - fmt.Printf(".") - } - } - fmt.Printf("\n") - } -} diff --git a/prodos/file.go b/prodos/file.go new file mode 100644 index 0000000..a7d92e3 --- /dev/null +++ b/prodos/file.go @@ -0,0 +1,61 @@ +package prodos + +import ( + "fmt" + "os" + "strings" +) + +func LoadFile(file *os.File, path string) []byte { + 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] + + volumeHeader, fileEntries := ReadDirectory(file, directory) + + if fileEntries == nil { + return nil + } + + var fileEntry FileEntry + + for i := 0; i < len(fileEntries); i++ { + if fileEntries[i].FileName == fileName { + fileEntry = fileEntries[i] + } + } + + DumpVolumeHeader(volumeHeader) + + fmt.Println() + + DumpFileEntry(fileEntry) + + switch fileEntry.StorageType { + case StorageSeedling: + return ReadBlock(file, fileEntry.StartingBlock)[0:fileEntry.EndOfFile] + case StorageSapling: + index := ReadBlock(file, fileEntry.StartingBlock) + buffer := make([]byte, fileEntry.EndOfFile) + for i := 0; i < 512 && index[i] > 0; i++ { + chunk := ReadBlock(file, int(index[i])+int(index[i+256])*256) + for j := i * 512; j < fileEntry.EndOfFile && j < i*512+512; j++ { + buffer[j] = chunk[j-i*512] + } + } + return buffer + case StorageTree: + // add tree file support later + return nil + } + return nil +} diff --git a/prodos/readblock.go b/prodos/readblock.go index 3397c28..034dda7 100644 --- a/prodos/readblock.go +++ b/prodos/readblock.go @@ -7,8 +7,6 @@ import ( func ReadBlock(file *os.File, block int) []byte { buffer := make([]byte, 512) - //fmt.Printf("Read block %d\n", block) - file.ReadAt(buffer, int64(block)*512) return buffer diff --git a/prodos/text.go b/prodos/text.go index 6ffc818..af61205 100644 --- a/prodos/text.go +++ b/prodos/text.go @@ -91,7 +91,7 @@ func DumpFileEntry(fileEntry FileEntry) { fmt.Printf("\n") } -func dumpVolumeHeader(volumeHeader VolumeHeader) { +func DumpVolumeHeader(volumeHeader VolumeHeader) { fmt.Printf("Next block: %d\n", volumeHeader.NextBlock) fmt.Printf("Volume name: %s\n", volumeHeader.VolumeName) fmt.Printf("Creation time: %d-%s-%d %02d:%02d\n", volumeHeader.CreationTime.Year(), volumeHeader.CreationTime.Month(), volumeHeader.CreationTime.Day(), volumeHeader.CreationTime.Hour(), volumeHeader.CreationTime.Minute()) @@ -103,3 +103,20 @@ func dumpVolumeHeader(volumeHeader VolumeHeader) { fmt.Printf("Bitmap starting block: %d\n", volumeHeader.BitmapStartBlock) fmt.Printf("Total blocks: %d\n", volumeHeader.TotalBlocks) } + +func DumpBlock(buffer []byte) { + for i := 0; i < len(buffer); i += 16 { + for j := i; j < i+16; j++ { + fmt.Printf("%02X ", buffer[j]) + } + for j := i; j < i+16; j++ { + c := buffer[j] & 127 + if c >= 32 { + fmt.Printf("%c", c) + } else { + fmt.Printf(".") + } + } + fmt.Printf("\n") + } +}