diff --git a/main.go b/main.go index ec3bb76..631b871 100644 --- a/main.go +++ b/main.go @@ -25,21 +25,21 @@ func main() { var command string var outFileName string var inFileName string - var blockNumber int - var volumeSize int + var blockNumber uint + var volumeSize uint var volumeName string - var fileType int - var auxType int + var fileType uint + var auxType uint 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, 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)") + flag.UintVar(&volumeSize, "s", 65535, "Number of blocks to create the volume with (default 65535, 64 to 65535, 0x0040 to 0xFFFF hex input accepted)") flag.StringVar(&volumeName, "v", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters") - flag.IntVar(&blockNumber, "b", 0, "A block number to read/write from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)") - flag.IntVar(&fileType, "t", 0, "ProDOS FileType: 0x04 for TXT, 0x06 for BIN, 0xFC for BAS, 0xFF for SYS etc., omit to autodetect") - flag.IntVar(&auxType, "a", 0, "ProDOS AuxType from 0 to 65535 (0x0000 to 0xFFFF hex input accepted), omit to autodetect") + flag.UintVar(&blockNumber, "b", 0, "A block number to read/write from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)") + flag.UintVar(&fileType, "t", 0, "ProDOS FileType: 0x04 for TXT, 0x06 for BIN, 0xFC for BAS, 0xFF for SYS etc., omit to autodetect") + flag.UintVar(&auxType, "a", 0, "ProDOS AuxType from 0 to 65535 (0x0000 to 0xFFFF hex input accepted), omit to autodetect") flag.Parse() if len(fileName) == 0 { @@ -54,13 +54,13 @@ func main() { case "get": get(fileName, pathName, outFileName) case "put": - put(fileName, pathName, fileType, auxType, inFileName) + put(fileName, pathName, uint8(fileType), uint16(auxType), inFileName) case "readblock": - readBlock(blockNumber, fileName) + readBlock(uint16(blockNumber), fileName) case "writeblock": - writeBlock(blockNumber, fileName, inFileName) + writeBlock(uint16(blockNumber), fileName, inFileName) case "create": - create(fileName, volumeName, volumeSize) + create(fileName, volumeName, uint16(volumeSize)) case "putall": putall(fileName, inFileName, pathName, false) case "putallrecursive": @@ -147,7 +147,7 @@ func putall(fileName string, inFileName string, pathName string, recursive bool) } } -func create(fileName string, volumeName string, volumeSize int) { +func create(fileName string, volumeName string, volumeSize uint16) { file, err := os.Create(fileName) if err != nil { fmt.Printf("failed to create file: %s\n", err) @@ -157,7 +157,7 @@ func create(fileName string, volumeName string, volumeSize int) { prodos.CreateVolume(file, volumeName, volumeSize) } -func writeBlock(blockNumber int, fileName string, inFileName string) { +func writeBlock(blockNumber uint16, fileName string, inFileName string) { checkInFileName(inFileName) fmt.Printf("Writing block 0x%04X (%d):\n\n", blockNumber, blockNumber) file, err := os.OpenFile(fileName, os.O_RDWR, 0755) @@ -174,7 +174,7 @@ func writeBlock(blockNumber int, fileName string, inFileName string) { prodos.WriteBlock(file, blockNumber, inFile) } -func readBlock(blockNumber int, fileName string) { +func readBlock(blockNumber uint16, fileName string) { fmt.Printf("Reading block 0x%04X (%d):\n\n", blockNumber, blockNumber) file, err := os.OpenFile(fileName, os.O_RDONLY, 0755) if err != nil { @@ -190,7 +190,7 @@ func readBlock(blockNumber int, fileName string) { prodos.DumpBlock(block) } -func put(fileName string, pathName string, fileType int, auxType int, inFileName string) { +func put(fileName string, pathName string, fileType uint8, auxType uint16, inFileName string) { checkPathName(pathName) checkInFileName(inFileName) file, err := os.OpenFile(fileName, os.O_RDWR, 0755) diff --git a/prodos/bitmap.go b/prodos/bitmap.go index 6794360..b612e93 100644 --- a/prodos/bitmap.go +++ b/prodos/bitmap.go @@ -33,13 +33,13 @@ func ReadVolumeBitmap(reader io.ReaderAt) ([]byte, error) { totalBitmapBlocks++ } - for i := 0; i < totalBitmapBlocks; i++ { + for i := uint16(0); i < totalBitmapBlocks; i++ { bitmapBlock, err := ReadBlock(reader, i+volumeHeader.BitmapStartBlock) if err != nil { return nil, err } - for j := 0; j < 512 && i*512+j < totalBitmapBytes; j++ { + for j := uint16(0); j < 512 && i*512+j < totalBitmapBytes; j++ { bitmap[i*512+j] = bitmapBlock[j] } } @@ -48,10 +48,10 @@ func ReadVolumeBitmap(reader io.ReaderAt) ([]byte, error) { } // GetFreeBlockCount gets the number of free blocks on a ProDOS image -func GetFreeBlockCount(volumeBitmap []byte, totalBlocks int) int { - freeBlockCount := 0 +func GetFreeBlockCount(volumeBitmap []byte, totalBlocks uint16) uint16 { + freeBlockCount := uint16(0) - for i := 0; i < totalBlocks; i++ { + for i := uint16(0); i < totalBlocks; i++ { if checkFreeBlockInVolumeBitmap(volumeBitmap, i) { freeBlockCount++ } @@ -77,13 +77,13 @@ func writeVolumeBitmap(readerWriter ReaderWriterAt, bitmap []byte) error { totalBitmapBlocks++ } - for i := 0; i < totalBitmapBlocks; i++ { + for i := uint16(0); i < totalBitmapBlocks; i++ { bitmapBlock, err := ReadBlock(readerWriter, i+volumeHeader.BitmapStartBlock) if err != nil { return err } - for j := 0; j < 512 && i*512+j < totalBitmapBytes; j++ { + for j := uint16(0); j < 512 && i*512+j < totalBitmapBytes; j++ { bitmapBlock[j] = bitmap[i*512+j] } @@ -96,9 +96,10 @@ func writeVolumeBitmap(readerWriter ReaderWriterAt, bitmap []byte) error { return nil } -func createVolumeBitmap(numberOfBlocks int) []byte { - volumeBitmapBlocks := numberOfBlocks / 512 / 8 - if volumeBitmapBlocks*8*512 < numberOfBlocks { +func createVolumeBitmap(numberOfBlocks uint16) []byte { + // needs to be > uint16 because it's multiplying by a uint16 + volumeBitmapBlocks := uint32(numberOfBlocks / 512 / 8) + if volumeBitmapBlocks*8*512 < uint32(numberOfBlocks) { volumeBitmapBlocks++ } @@ -119,30 +120,31 @@ func createVolumeBitmap(numberOfBlocks int) []byte { markBlockInVolumeBitmap(volumeBitmap, 5) // volume bitmap blocks - for i := 0; i < volumeBitmapBlocks; i++ { - markBlockInVolumeBitmap(volumeBitmap, 6+i) + for i := uint32(0); i < volumeBitmapBlocks; i++ { + markBlockInVolumeBitmap(volumeBitmap, uint16(6+i)) } // blocks beyond the volume totalBlocksInBitmap := volumeBitmapBlocks * 512 * 8 - blocksBeyondEnd := totalBlocksInBitmap - numberOfBlocks + blocksBeyondEnd := totalBlocksInBitmap - uint32(numberOfBlocks) if blocksBeyondEnd > 0 { for i := totalBlocksInBitmap - blocksBeyondEnd; i < totalBlocksInBitmap; i++ { - markBlockInVolumeBitmap(volumeBitmap, i) + markBlockInVolumeBitmap(volumeBitmap, uint16(i)) } } return volumeBitmap } -func findFreeBlocks(volumeBitmap []byte, numberOfBlocks int) []int { - blocks := make([]int, numberOfBlocks) +func findFreeBlocks(volumeBitmap []byte, numberOfBlocks uint16) []uint16 { + blocks := make([]uint16, numberOfBlocks) - blocksFound := 0 + blocksFound := uint16(0) - for i := 0; i < len(volumeBitmap)*8; i++ { - if checkFreeBlockInVolumeBitmap(volumeBitmap, i) { - blocks[blocksFound] = i + // needs to be > uint16 because it's multiplying by a uint16 + for i := uint32(0); i < uint32(len(volumeBitmap))*8; i++ { + if checkFreeBlockInVolumeBitmap(volumeBitmap, uint16(i)) { + blocks[blocksFound] = uint16(i) blocksFound++ if blocksFound == numberOfBlocks { return blocks @@ -153,87 +155,29 @@ func findFreeBlocks(volumeBitmap []byte, numberOfBlocks int) []int { return nil } -func markBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) { +func markBlockInVolumeBitmap(volumeBitmap []byte, blockNumber uint16) { bitToChange := blockNumber % 8 byteToChange := blockNumber / 8 - byteToAnd := 0b11111111 + byteToAnd := (uint8(0b10000000) >> uint8(bitToChange)) ^ 0b11111111 - switch bitToChange { - case 0: - byteToAnd = 0b01111111 - case 1: - byteToAnd = 0b10111111 - case 2: - byteToAnd = 0b11011111 - case 3: - byteToAnd = 0b11101111 - case 4: - byteToAnd = 0b11110111 - case 5: - byteToAnd = 0b11111011 - case 6: - byteToAnd = 0b11111101 - case 7: - byteToAnd = 0b11111110 - } - - //fmt.Printf("blockNumber: $%04X byteToWrite: 0b%08b volumeBitmap: $%02X byteToChange: $%04X\n", blockNumber, byteToWrite, volumeBitmap[byteToChange], byteToChange) volumeBitmap[byteToChange] &= byte(byteToAnd) } -func freeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) { +func freeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber uint16) { bitToChange := blockNumber % 8 byteToChange := blockNumber / 8 - byteToOr := 0b00000000 - - switch bitToChange { - case 0: - byteToOr = 0b10000000 - case 1: - byteToOr = 0b01000000 - case 2: - byteToOr = 0b00100000 - case 3: - byteToOr = 0b00010000 - case 4: - byteToOr = 0b00001000 - case 5: - byteToOr = 0b00000100 - case 6: - byteToOr = 0b00000010 - case 7: - byteToOr = 0b00000001 - } + byteToOr := uint8(0b10000000) >> uint8(bitToChange) volumeBitmap[byteToChange] |= byte(byteToOr) } -func checkFreeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) bool { +func checkFreeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber uint16) bool { bitToCheck := blockNumber % 8 byteToCheck := blockNumber / 8 - byteToAnd := 0b00000000 - - switch bitToCheck { - case 0: - byteToAnd = 0b10000000 - case 1: - byteToAnd = 0b01000000 - case 2: - byteToAnd = 0b00100000 - case 3: - byteToAnd = 0b00010000 - case 4: - byteToAnd = 0b00001000 - case 5: - byteToAnd = 0b00000100 - case 6: - byteToAnd = 0b00000010 - case 7: - byteToAnd = 0b00000001 - } + byteToAnd := uint8(0b10000000) >> uint8(bitToCheck) return (volumeBitmap[byteToCheck] & byte(byteToAnd)) > 0 } diff --git a/prodos/bitmap_test.go b/prodos/bitmap_test.go index 2ea6a40..c4dc85b 100644 --- a/prodos/bitmap_test.go +++ b/prodos/bitmap_test.go @@ -14,10 +14,10 @@ import ( func TestCreateVolumeBitmap(t *testing.T) { var tests = []struct { - blocks int - want int + blocks uint16 + want uint16 }{ - {65536, 8192}, + {65535, 8192}, {65533, 8192}, {140, 512}, } @@ -26,7 +26,7 @@ func TestCreateVolumeBitmap(t *testing.T) { testname := fmt.Sprintf("%d", tt.blocks) t.Run(testname, func(t *testing.T) { volumeBitMap := createVolumeBitmap(tt.blocks) - ans := len(volumeBitMap) + ans := uint16(len(volumeBitMap)) if ans != tt.want { t.Errorf("got %d, want %d", ans, tt.want) } @@ -36,7 +36,7 @@ func TestCreateVolumeBitmap(t *testing.T) { func TestCheckFreeBlockInVolumeBitmap(t *testing.T) { var tests = []struct { - blocks int + blocks uint16 want bool }{ {0, false}, // boot block @@ -63,7 +63,7 @@ func TestCheckFreeBlockInVolumeBitmap(t *testing.T) { func TestMarkBlockInVolumeBitmap(t *testing.T) { var tests = []struct { - blocks int + blocks uint16 want bool }{ {0, false}, // boot block diff --git a/prodos/block.go b/prodos/block.go index 92cffa4..0730d8b 100644 --- a/prodos/block.go +++ b/prodos/block.go @@ -14,7 +14,7 @@ import ( ) // ReadBlock reads a block from a ProDOS volume into a byte array -func ReadBlock(reader io.ReaderAt, block int) ([]byte, error) { +func ReadBlock(reader io.ReaderAt, block uint16) ([]byte, error) { buffer := make([]byte, 512) _, err := reader.ReadAt(buffer, int64(block)*512) @@ -27,7 +27,7 @@ func ReadBlock(reader io.ReaderAt, block int) ([]byte, error) { } // WriteBlock writes a block to a ProDOS volume from a byte array -func WriteBlock(writer io.WriterAt, block int, buffer []byte) error { +func WriteBlock(writer io.WriterAt, block uint16, buffer []byte) error { _, err := writer.WriteAt(buffer, int64(block)*512) if err != nil { errString := fmt.Sprintf("failed to write block %04X: %s", block, err.Error()) diff --git a/prodos/directory.go b/prodos/directory.go index d06b757..35d4948 100644 --- a/prodos/directory.go +++ b/prodos/directory.go @@ -19,33 +19,33 @@ import ( type VolumeHeader struct { VolumeName string CreationTime time.Time - ActiveFileCount int - BitmapStartBlock int - TotalBlocks int - NextBlock int - EntryLength int - EntriesPerBlock int - MinVersion int - Version int + ActiveFileCount uint16 + BitmapStartBlock uint16 + TotalBlocks uint16 + NextBlock uint16 + EntryLength uint8 + EntriesPerBlock uint8 + MinVersion uint8 + Version uint8 } // DirectoryHeader from ProDOS type DirectoryHeader struct { - PreviousBlock int - NextBlock int + PreviousBlock uint16 + NextBlock uint16 IsSubDirectory bool Name string CreationTime time.Time - Version int - MinVersion int - Access int - EntryLength int - EntriesPerBlock int - ActiveFileCount int - StartingBlock int - ParentBlock int - ParentEntry int - ParentEntryLength int + Version uint8 + MinVersion uint8 + Access uint8 + EntryLength uint8 + EntriesPerBlock uint8 + ActiveFileCount uint16 + StartingBlock uint16 + ParentBlock uint16 + ParentEntry uint16 + ParentEntryLength uint8 } const ( @@ -65,22 +65,22 @@ const ( // FileEntry from ProDOS type FileEntry struct { - StorageType int + StorageType uint8 FileName string - FileType int + FileType uint8 CreationTime time.Time - KeyPointer int - Version int - MinVersion int - BlocksUsed int - EndOfFile int - Access int - AuxType int + KeyPointer uint16 + Version uint8 + MinVersion uint8 + BlocksUsed uint16 + EndOfFile uint32 + Access uint8 + AuxType uint16 ModifiedTime time.Time - HeaderPointer int + HeaderPointer uint16 - DirectoryBlock int - DirectoryOffset int + DirectoryBlock uint16 + DirectoryOffset uint16 } // ReadDirectory reads the directory information from a specified path @@ -183,7 +183,7 @@ func CreateDirectory(readerWriter ReaderWriterAt, path string) error { ActiveFileCount: 0, StartingBlock: blockList[0], ParentBlock: fileEntry.DirectoryBlock, - ParentEntry: (fileEntry.DirectoryOffset - 0x04) / 0x27, + ParentEntry: uint16(fileEntry.DirectoryOffset-0x04) / 0x27, ParentEntryLength: 0x27, } @@ -219,18 +219,18 @@ func getFreeFileEntryInDirectory(readerWriter ReaderWriterAt, directory string) if err != nil { return FileEntry{}, err } - entryOffset := 43 // start at offset after header - entryNumber := 2 // header is essentially the first entry so start at 2 + entryOffset := uint16(43) // start at offset after header + entryNumber := 2 // header is essentially the first entry so start at 2 for { if entryNumber > 13 { - nextBlockNumber := int(buffer[2]) + int(buffer[3])*256 + nextBlockNumber := uint16(buffer[2]) + uint16(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) + nextBlockNumber, err = expandDirectory(readerWriter, buffer, blockNumber, directoryHeader) if err != nil { return FileEntry{}, err } @@ -260,7 +260,7 @@ func getFreeFileEntryInDirectory(readerWriter ReaderWriterAt, directory string) } } -func expandDirectory(readerWriter ReaderWriterAt, nextBlockNumber int, buffer []byte, blockNumber int, directoryHeader DirectoryHeader) (int, error) { +func expandDirectory(readerWriter ReaderWriterAt, buffer []byte, blockNumber uint16, directoryHeader DirectoryHeader) (uint16, error) { volumeBitMap, err := ReadVolumeBitmap(readerWriter) if err != nil { errString := fmt.Sprintf("failed to get volume bitmap to expand directory: %s", err) @@ -271,7 +271,7 @@ func expandDirectory(readerWriter ReaderWriterAt, nextBlockNumber int, buffer [] return 0, errors.New("failed to get free block to expand directory") } - nextBlockNumber = blockList[0] + nextBlockNumber := blockList[0] buffer[0x02] = byte(nextBlockNumber & 0x00FF) buffer[0x03] = byte(nextBlockNumber >> 8) WriteBlock(readerWriter, blockNumber, buffer) @@ -296,8 +296,8 @@ func expandDirectory(readerWriter ReaderWriterAt, nextBlockNumber int, buffer [] 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) + directoryEntryOffset := directoryHeader.ParentEntry*uint16(directoryHeader.EntryLength) + 0x04 + directoryFileEntry := parseFileEntry(buffer[directoryEntryOffset:directoryEntryOffset+0x28], directoryHeader.ParentBlock, directoryHeader.ParentEntry*uint16(directoryHeader.EntryLength)+0x04) directoryFileEntry.BlocksUsed++ directoryFileEntry.EndOfFile += 0x200 writeFileEntry(readerWriter, directoryFileEntry) @@ -305,7 +305,7 @@ func expandDirectory(readerWriter ReaderWriterAt, nextBlockNumber int, buffer [] return nextBlockNumber, nil } -func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath int, paths []string) (DirectoryHeader, []FileEntry, error) { +func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber uint16, currentPath int, paths []string) (DirectoryHeader, []FileEntry, error) { buffer, err := ReadBlock(reader, blockNumber) if err != nil { return DirectoryHeader{}, nil, err @@ -314,9 +314,9 @@ func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath directoryHeader := parseDirectoryHeader(buffer, blockNumber) fileEntries := make([]FileEntry, directoryHeader.ActiveFileCount) - entryOffset := 43 // start at offset after header - activeEntries := 0 - entryNumber := 2 // header is essentially the first entry so start at 2 + entryOffset := uint16(43) // start at offset after header + activeEntries := uint16(0) + entryNumber := uint8(2) // header is essentially the first entry so start at 2 nextBlock := directoryHeader.NextBlock @@ -338,7 +338,7 @@ func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath if err != nil { return DirectoryHeader{}, nil, err } - nextBlock = int(buffer[2]) + int(buffer[3])*256 + nextBlock = uint16(buffer[2]) + uint16(buffer[3])*256 } fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset) @@ -359,21 +359,21 @@ func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath } } -func parseFileEntry(buffer []byte, blockNumber int, entryOffset int) FileEntry { - storageType := int(buffer[0] >> 4) - fileNameLength := int(buffer[0] & 15) +func parseFileEntry(buffer []byte, blockNumber uint16, entryOffset uint16) FileEntry { + storageType := buffer[0] >> 4 + fileNameLength := buffer[0] & 15 fileName := string(buffer[1 : fileNameLength+1]) - fileType := int(buffer[16]) - startingBlock := int(buffer[17]) + int(buffer[18])*256 - blocksUsed := int(buffer[19]) + int(buffer[20])*256 - endOfFile := int(buffer[21]) + int(buffer[22])*256 + int(buffer[23])*65536 + fileType := buffer[16] + startingBlock := uint16(buffer[17]) + uint16(buffer[18])*256 + blocksUsed := uint16(buffer[19]) + uint16(buffer[20])*256 + endOfFile := uint32(buffer[21]) + uint32(buffer[22])*256 + uint32(buffer[23])*65536 creationTime := DateTimeFromProDOS(buffer[24:28]) - version := int(buffer[28]) - minVersion := int(buffer[29]) - access := int(buffer[30]) - auxType := int(buffer[31]) + int(buffer[32])*256 + version := buffer[28] + minVersion := buffer[29] + access := buffer[30] + auxType := uint16(buffer[31]) + uint16(buffer[32])*256 modifiedTime := DateTimeFromProDOS((buffer[33:37])) - headerPointer := int(buffer[0x25]) + int(buffer[0x26])*256 + headerPointer := uint16(buffer[0x25]) + uint16(buffer[0x26])*256 fileEntry := FileEntry{ StorageType: storageType, @@ -427,24 +427,24 @@ func writeFileEntry(writer io.WriterAt, fileEntry FileEntry) { 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)) + _, err := writer.WriteAt(buffer, int64(fileEntry.DirectoryBlock)*512+int64(fileEntry.DirectoryOffset)) if err != nil { } } func parseVolumeHeader(buffer []byte) VolumeHeader { - nextBlock := int(buffer[2]) + int(buffer[3])*256 + nextBlock := uint16(buffer[2]) + uint16(buffer[3])*256 filenameLength := buffer[4] & 15 volumeName := string(buffer[5 : filenameLength+5]) creationTime := DateTimeFromProDOS(buffer[28:32]) - version := int(buffer[32]) - minVersion := int(buffer[33]) - entryLength := int(buffer[35]) - entriesPerBlock := int(buffer[36]) - fileCount := int(buffer[37]) + int(buffer[38])*256 - bitmapBlock := int(buffer[39]) + int(buffer[40])*256 - totalBlocks := int(buffer[41]) + int(buffer[42])*256 + version := buffer[32] + minVersion := buffer[33] + entryLength := buffer[35] + entriesPerBlock := buffer[36] + fileCount := uint16(buffer[37]) + uint16(buffer[38])*256 + bitmapBlock := uint16(buffer[39]) + uint16(buffer[40])*256 + totalBlocks := uint16(buffer[41]) + uint16(buffer[42])*256 if minVersion > 0 { panic("Unsupported ProDOS version") @@ -465,22 +465,22 @@ func parseVolumeHeader(buffer []byte) VolumeHeader { return volumeHeader } -func parseDirectoryHeader(buffer []byte, blockNumber int) DirectoryHeader { - previousBlock := int(buffer[0x00]) + int(buffer[0x01])*256 - nextBlock := int(buffer[0x02]) + int(buffer[0x03])*256 +func parseDirectoryHeader(buffer []byte, blockNumber uint16) DirectoryHeader { + previousBlock := uint16(buffer[0x00]) + uint16(buffer[0x01])*256 + nextBlock := uint16(buffer[0x02]) + uint16(buffer[0x03])*256 isSubDirectory := (buffer[0x04] & 0xF0) == 0xE0 filenameLength := buffer[0x04] & 0x0F name := string(buffer[0x05 : filenameLength+0x05]) creationTime := DateTimeFromProDOS(buffer[0x1C:0x20]) - version := int(buffer[0x20]) - minVersion := int(buffer[0x21]) - access := int(buffer[0x22]) - entryLength := int(buffer[0x23]) - entriesPerBlock := int(buffer[0x24]) - fileCount := int(buffer[0x25]) + int(buffer[0x26])*256 - parentBlock := int(buffer[0x27]) + int(buffer[0x28])*256 - parentEntry := int(buffer[0x29]) - parentEntryLength := int(buffer[0x2A]) + version := buffer[0x20] + minVersion := buffer[0x21] + access := buffer[0x22] + entryLength := buffer[0x23] + entriesPerBlock := buffer[0x24] + fileCount := uint16(buffer[0x25]) + uint16(buffer[0x26])*256 + parentBlock := uint16(buffer[0x27]) + uint16(buffer[0x28])*256 + parentEntry := uint16(buffer[0x29]) + parentEntryLength := buffer[0x2A] directoryEntry := DirectoryHeader{ PreviousBlock: previousBlock, diff --git a/prodos/file.go b/prodos/file.go index e9d3637..c642b2c 100644 --- a/prodos/file.go +++ b/prodos/file.go @@ -29,12 +29,12 @@ func LoadFile(reader io.ReaderAt, path string) ([]byte, error) { buffer := make([]byte, fileEntry.EndOfFile) - for i := 0; i < len(blockList); i++ { + for i := uint16(0); i < uint16(len(blockList)); i++ { block, err := ReadBlock(reader, blockList[i]) if err != nil { return nil, err } - for j := 0; j < 512 && i*512+j < fileEntry.EndOfFile; j++ { + for j := uint16(0); j < 512 && uint32(i)*512+uint32(j) < fileEntry.EndOfFile; j++ { buffer[i*512+j] = block[j] } } @@ -43,7 +43,7 @@ func LoadFile(reader io.ReaderAt, path string) ([]byte, error) { } // WriteFile writes a file to a ProDOS volume from a byte array -func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType int, createdTime time.Time, modifiedTime time.Time, buffer []byte) error { +func WriteFile(readerWriter ReaderWriterAt, path string, fileType uint8, auxType uint16, createdTime time.Time, modifiedTime time.Time, buffer []byte) error { directory, fileName := GetDirectoryAndFileNameFromPath(path) if len(fileName) > 15 { @@ -56,7 +56,7 @@ func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType i } // get list of blocks to write file to - blockList, err := createBlockList(readerWriter, len(buffer)) + blockList, err := createBlockList(readerWriter, uint32(len(buffer))) if err != nil { return err } @@ -88,11 +88,11 @@ func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType i return err } fileEntry.FileName = fileName - fileEntry.BlocksUsed = len(blockList) + fileEntry.BlocksUsed = uint16(len(blockList)) fileEntry.CreationTime = createdTime fileEntry.ModifiedTime = modifiedTime fileEntry.AuxType = auxType - fileEntry.EndOfFile = len(buffer) + fileEntry.EndOfFile = uint32(len(buffer)) fileEntry.FileType = fileType fileEntry.KeyPointer = blockList[0] fileEntry.Version = 0x24 @@ -197,24 +197,24 @@ func GetDirectoryAndFileNameFromPath(path string) (string, string) { return directory, fileName } -func updateVolumeBitmap(readerWriter ReaderWriterAt, blockList []int) error { +func updateVolumeBitmap(readerWriter ReaderWriterAt, blockList []uint16) error { volumeBitmap, err := ReadVolumeBitmap(readerWriter) if err != nil { fmt.Printf("%s", err) return err } - for i := 0; i < len(blockList); i++ { + for i := uint16(0); i < uint16(len(blockList)); i++ { markBlockInVolumeBitmap(volumeBitmap, blockList[i]) } return writeVolumeBitmap(readerWriter, volumeBitmap) } -func writeSeedlingFile(writer io.WriterAt, buffer []byte, blockList []int) { +func writeSeedlingFile(writer io.WriterAt, buffer []byte, blockList []uint16) { WriteBlock(writer, blockList[0], buffer) } -func writeSaplingFile(writer io.WriterAt, buffer []byte, blockList []int) { +func writeSaplingFile(writer io.WriterAt, buffer []byte, blockList []uint16) { // write index block with pointers to data blocks indexBuffer := make([]byte, 512) for i := 0; i < 256; i++ { @@ -249,7 +249,7 @@ func writeSaplingFile(writer io.WriterAt, buffer []byte, blockList []int) { } } -func writeTreeFile(writer io.WriterAt, buffer []byte, blockList []int) { +func writeTreeFile(writer io.WriterAt, buffer []byte, blockList []uint16) { // write master index block with pointers to index blocks indexBuffer := make([]byte, 512) numberOfIndexBlocks := len(blockList)/256 + 1 @@ -303,17 +303,17 @@ func writeTreeFile(writer io.WriterAt, buffer []byte, blockList []int) { } } -func getDataBlocklist(reader io.ReaderAt, fileEntry FileEntry) ([]int, error) { +func getDataBlocklist(reader io.ReaderAt, fileEntry FileEntry) ([]uint16, error) { return getBlocklist(reader, fileEntry, true) } -func getAllBlockList(reader io.ReaderAt, fileEntry FileEntry) ([]int, error) { +func getAllBlockList(reader io.ReaderAt, fileEntry FileEntry) ([]uint16, error) { return getBlocklist(reader, fileEntry, false) } // Returns all blocks, including index blocks -func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int, error) { - blocks := make([]int, fileEntry.BlocksUsed) +func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]uint16, error) { + blocks := make([]uint16, fileEntry.BlocksUsed) switch fileEntry.StorageType { case StorageSeedling: @@ -324,10 +324,10 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int if err != nil { return nil, err } - blockOffset := 1 + blockOffset := uint16(1) blocks[0] = fileEntry.KeyPointer - for i := 0; i < fileEntry.BlocksUsed-1; i++ { - blocks[i+blockOffset] = int(index[i]) + int(index[i+256])*256 + for i := uint16(0); i < fileEntry.BlocksUsed-1; i++ { + blocks[i+blockOffset] = uint16(index[i]) + uint16(index[i+256])*256 } if dataOnly { return blocks[1:], nil @@ -335,10 +335,10 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int return blocks, nil case StorageTree: // this is actually too large - dataBlocks := make([]int, fileEntry.BlocksUsed) + dataBlocks := make([]uint16, fileEntry.BlocksUsed) // this is also actually too large numberOfIndexBlocks := fileEntry.BlocksUsed/256 + 2 - indexBlocks := make([]int, numberOfIndexBlocks) + indexBlocks := make([]uint16, numberOfIndexBlocks) masterIndex, err := ReadBlock(reader, fileEntry.KeyPointer) if err != nil { return nil, err @@ -348,8 +348,8 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int indexBlocks[0] = fileEntry.KeyPointer indexBlockCount := 1 - for i := 0; i < 128; i++ { - indexBlock := int(masterIndex[i]) + int(masterIndex[i+256])*256 + for i := uint16(0); i < 128; i++ { + indexBlock := uint16(masterIndex[i]) + uint16(masterIndex[i+256])*256 if indexBlock == 0 { break } @@ -359,12 +359,12 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int if err != nil { return nil, err } - for j := 0; j < 256 && i*256+j < fileEntry.BlocksUsed; j++ { - if (int(index[j]) + int(index[j+256])*256) == 0 { + for j := uint16(0); j < 256 && i*256+j < fileEntry.BlocksUsed; j++ { + if (uint16(index[j]) + uint16(index[j+256])*256) == 0 { break } numberOfDataBlocks++ - dataBlocks[i*256+j] = int(index[j]) + int(index[j+256])*256 + dataBlocks[i*256+j] = uint16(index[j]) + uint16(index[j+256])*256 } } @@ -379,8 +379,8 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry, dataOnly bool) ([]int return nil, errors.New("unsupported file storage type") } -func createBlockList(reader io.ReaderAt, fileSize int) ([]int, error) { - numberOfBlocks := fileSize / 512 +func createBlockList(reader io.ReaderAt, fileSize uint32) ([]uint16, error) { + numberOfBlocks := uint16(fileSize / 512) if fileSize%512 > 0 { numberOfBlocks++ diff --git a/prodos/file_test.go b/prodos/file_test.go index 0d6ad96..84db15b 100644 --- a/prodos/file_test.go +++ b/prodos/file_test.go @@ -11,8 +11,8 @@ import ( func TestCreateBlocklist(t *testing.T) { var tests = []struct { - fileSize int - wantBlocks int + fileSize uint32 + wantBlocks uint16 }{ {1, 1}, {512, 1}, @@ -33,7 +33,7 @@ func TestCreateBlocklist(t *testing.T) { if err != nil { t.Error("got error, want nil") } - if len(blockList) != tt.wantBlocks { + if uint16(len(blockList)) != tt.wantBlocks { t.Errorf("got %d blocks, want %d", len(blockList), tt.wantBlocks) } }) @@ -41,7 +41,7 @@ func TestCreateBlocklist(t *testing.T) { } func TestUpdateVolumeBitmap(t *testing.T) { - blockList := []int{10, 11, 12, 100, 120} + blockList := []uint16{10, 11, 12, 100, 120} virtualDisk := NewMemoryFile(0x2000000) CreateVolume(virtualDisk, "VIRTUAL.DISK", 0xFFFE) diff --git a/prodos/format.go b/prodos/format.go index 6c51e81..d276019 100644 --- a/prodos/format.go +++ b/prodos/format.go @@ -14,7 +14,7 @@ import ( // CreateVolume formats a new ProDOS volume including boot block, // volume bitmap and empty directory -func CreateVolume(readerWriter ReaderWriterAt, volumeName string, numberOfBlocks int) { +func CreateVolume(readerWriter ReaderWriterAt, volumeName string, numberOfBlocks uint16) { if numberOfBlocks > 65535 || numberOfBlocks < 64 { return } @@ -26,7 +26,7 @@ func CreateVolume(readerWriter ReaderWriterAt, volumeName string, numberOfBlocks } blankBlock := make([]byte, 512) - for i := 0; i < numberOfBlocks; i++ { + for i := uint16(0); i < numberOfBlocks; i++ { WriteBlock(readerWriter, i, blankBlock) } diff --git a/prodos/format_test.go b/prodos/format_test.go index a2a42ed..3770da5 100644 --- a/prodos/format_test.go +++ b/prodos/format_test.go @@ -13,9 +13,9 @@ import ( func TestCreateVolume(t *testing.T) { var tests = []struct { - blocks int + blocks uint16 wantVolumeName string - wantFreeBlocks int + wantFreeBlocks uint16 }{ {65535, "MAX", 65513}, {65500, "ALMOST.MAX", 65478}, diff --git a/prodos/host.go b/prodos/host.go index 9276cab..099befa 100644 --- a/prodos/host.go +++ b/prodos/host.go @@ -12,6 +12,8 @@ import ( "fmt" "os" "path/filepath" + "regexp" + "strconv" "strings" "time" ) @@ -75,8 +77,8 @@ func AddFilesFromHostDirectory( func WriteFileFromFile( readerWriter ReaderWriterAt, pathName string, - fileType int, - auxType int, + fileType uint8, + auxType uint16, modifiedTime time.Time, inFileName string, ignoreDuplicates bool, @@ -117,6 +119,11 @@ func WriteFileFromFile( case ".SYS", ".TXT", ".BAS", ".BIN": pathName = strings.TrimSuffix(pathName, ext) } + match, err := regexp.MatchString("^\\.(BIN|SYS|TXT|BAS)\\$[0-9]{4}", ext) + + if err == nil && match { + pathName = strings.TrimSuffix(pathName, ext) + } } } @@ -140,9 +147,12 @@ func WriteFileFromFile( return WriteFile(readerWriter, pathName, fileType, auxType, time.Now(), modifiedTime, inFile) } -func convertFileByType(inFileName string, inFile []byte) (int, int, []byte, error) { - fileType := 0x06 // default to BIN - auxType := 0x2000 // default to $2000 +func convertFileByType(inFileName string, inFile []byte) (uint16, uint8, []byte, error) { + var auxType uint16 + var fileType uint8 + + fileType = 0x06 // default to BIN + auxType = 0x2000 // default to $2000 var err error @@ -166,36 +176,57 @@ func convertFileByType(inFileName string, inFile []byte) (int, int, []byte, erro // Length binary.BigEndian.Uint32(inFile[0x2E:]) == 0x00000008 { - fileType = int(binary.BigEndian.Uint16(inFile[0x34:])) - auxType = int(binary.BigEndian.Uint32(inFile[0x36:])) + fileType = uint8(binary.BigEndian.Uint16(inFile[0x34:])) + auxType = uint16(binary.BigEndian.Uint32(inFile[0x36:])) inFile = inFile[0x3A:] } else { // use extension to determine file type ext := strings.ToUpper(filepath.Ext(inFileName)) - switch ext { - case ".BAS": - inFile, err = ConvertTextToBasic(string(inFile)) - fileType = 0xFC - auxType = 0x0801 + match, err := regexp.MatchString("^\\.(BIN|SYS|TXT|BAS)\\$[0-9]{4}", ext) + if err == nil && match { + parts := strings.Split(ext, "$") + extAuxType, err := strconv.ParseUint(parts[1], 16, 16) if err != nil { return 0, 0, nil, err } - case ".SYS": - fileType = 0xFF - auxType = 0x2000 - case ".BIN": - fileType = 0x06 - auxType = 0x2000 - case ".TXT": - inFile = []byte(strings.ReplaceAll(strings.ReplaceAll(string(inFile), "\r\n", "r"), "\n", "\r")) - fileType = 0x04 - auxType = 0x0000 - case ".JPG", ".PNG": - inFile = ConvertImageToHiResMonochrome(inFile) - fileType = 0x06 - auxType = 0x2000 + auxType = uint16(extAuxType) + switch parts[0] { + case ".BAS": + fileType = 0xFC + case ".SYS": + fileType = 0xFF + case ".BIN": + fileType = 0x06 + case ".TXT": + fileType = 0x04 + } + } else { + switch ext { + case ".BAS": + inFile, err = ConvertTextToBasic(string(inFile)) + fileType = 0xFC + auxType = 0x0801 + + if err != nil { + return 0, 0, nil, err + } + case ".SYS": + fileType = 0xFF + auxType = 0x2000 + case ".BIN": + fileType = 0x06 + auxType = 0x2000 + case ".TXT": + inFile = []byte(strings.ReplaceAll(strings.ReplaceAll(string(inFile), "\r\n", "r"), "\n", "\r")) + fileType = 0x04 + auxType = 0x0000 + case ".JPG", ".PNG": + inFile = ConvertImageToHiResMonochrome(inFile) + fileType = 0x06 + auxType = 0x2000 + } } } diff --git a/prodos/text.go b/prodos/text.go index 15b1a1e..1e5ecd4 100644 --- a/prodos/text.go +++ b/prodos/text.go @@ -25,7 +25,7 @@ func TimeToString(printTime time.Time) string { } // FileTypeToString display the file type as a string -func FileTypeToString(fileType int) string { +func FileTypeToString(fileType uint8) string { switch fileType { case 1: return "BAD" @@ -159,7 +159,7 @@ func DumpBlock(buffer []byte) { } // DumpDirectory displays the directory similar to ProDOS catalog -func DumpDirectory(blocksFree int, totalBlocks int, path string, fileEntries []FileEntry) { +func DumpDirectory(blocksFree uint16, totalBlocks uint16, path string, fileEntries []FileEntry) { fmt.Printf("%s\n\n", path) fmt.Printf("NAME TYPE BLOCKS MODIFIED CREATED ENDFILE SUBTYPE\n\n")