ProDOS-Utilities/prodos/directory.go

554 lines
17 KiB
Go
Raw Normal View History

// Copyright Terence J. Boldt (c)2021-2023
// 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
2021-06-06 03:22:30 +00:00
package prodos
import (
2021-06-30 04:04:59 +00:00
"errors"
2021-06-06 03:22:30 +00:00
"fmt"
"io"
2021-06-06 03:22:30 +00:00
"strings"
"time"
)
2022-01-23 22:30:18 +00:00
// VolumeHeader from ProDOS
2021-06-06 03:22:30 +00:00
type VolumeHeader struct {
VolumeName string
CreationTime time.Time
2024-03-11 13:05:23 +00:00
ActiveFileCount uint16
BitmapStartBlock uint16
TotalBlocks uint16
NextBlock uint16
EntryLength uint8
EntriesPerBlock uint8
MinVersion uint8
Version uint8
2021-06-06 03:22:30 +00:00
}
2022-01-23 22:30:18 +00:00
// DirectoryHeader from ProDOS
2021-06-06 03:22:30 +00:00
type DirectoryHeader struct {
2024-03-11 13:05:23 +00:00
PreviousBlock uint16
NextBlock uint16
2023-01-22 15:01:57 +00:00
IsSubDirectory bool
2023-01-19 05:30:17 +00:00
Name string
CreationTime time.Time
2024-03-11 13:05:23 +00:00
Version uint8
MinVersion uint8
Access uint8
EntryLength uint8
EntriesPerBlock uint8
ActiveFileCount uint16
StartingBlock uint16
ParentBlock uint16
ParentEntry uint16
ParentEntryLength uint8
2021-06-06 03:22:30 +00:00
}
const (
2022-01-23 23:57:16 +00:00
// StorageDeleted signifies file is deleted
2022-01-23 23:51:44 +00:00
StorageDeleted = 0
2022-01-23 23:57:16 +00:00
// StorageSeedling signifies file is <= 512 bytes
2022-01-23 23:51:44 +00:00
StorageSeedling = 1
2022-01-23 23:57:16 +00:00
// StorageSapling signifies file is > 512 bytes and <= 128 KB
2022-01-23 23:51:44 +00:00
StorageSapling = 2
2022-01-23 23:57:16 +00:00
// StorageTree signifies file is > 128 KB and <= 16 MB
2022-01-23 23:51:44 +00:00
StorageTree = 3
2022-01-23 23:57:16 +00:00
// StoragePascal signifies pascal storage area
2022-01-23 23:51:44 +00:00
StoragePascal = 4
2022-01-23 23:57:16 +00:00
// StorageDirectory signifies directory
2021-06-06 03:22:30 +00:00
StorageDirectory = 13
)
2022-01-23 22:30:18 +00:00
// FileEntry from ProDOS
2021-06-06 03:22:30 +00:00
type FileEntry struct {
2024-03-11 13:05:23 +00:00
StorageType uint8
2023-01-22 15:01:57 +00:00
FileName string
2024-03-11 13:05:23 +00:00
FileType uint8
2023-01-22 15:01:57 +00:00
CreationTime time.Time
2024-03-11 13:05:23 +00:00
KeyPointer uint16
Version uint8
MinVersion uint8
BlocksUsed uint16
EndOfFile uint32
Access uint8
AuxType uint16
2023-01-22 15:01:57 +00:00
ModifiedTime time.Time
2024-03-11 13:05:23 +00:00
HeaderPointer uint16
2023-01-22 15:01:57 +00:00
2024-03-11 13:05:23 +00:00
DirectoryBlock uint16
DirectoryOffset uint16
2021-06-06 03:22:30 +00:00
}
2022-01-23 22:30:18 +00:00
// ReadDirectory reads the directory information from a specified path
// on a ProDOS image
func ReadDirectory(reader io.ReaderAt, path string) (VolumeHeader, DirectoryHeader, []FileEntry, error) {
buffer, err := ReadBlock(reader, 2)
if err != nil {
return VolumeHeader{}, DirectoryHeader{}, nil, err
}
2021-06-06 03:22:30 +00:00
2021-06-06 12:00:20 +00:00
volumeHeader := parseVolumeHeader(buffer)
2021-06-06 03:22:30 +00:00
if len(path) == 0 {
path = fmt.Sprintf("/%s", volumeHeader.VolumeName)
}
2023-01-22 15:01:57 +00:00
// add volume name if not full path
if !strings.HasPrefix(path, "/") {
path = fmt.Sprintf("/%s/%s", volumeHeader.VolumeName, path)
}
2021-06-06 03:22:30 +00:00
path = strings.ToUpper(path)
paths := strings.Split(path, "/")
directoryHeader, fileEntries, err := getFileEntriesInDirectory(reader, 2, 1, paths)
if err != nil {
return VolumeHeader{}, DirectoryHeader{}, nil, err
}
2021-06-06 03:22:30 +00:00
return volumeHeader, directoryHeader, fileEntries, nil
2021-06-06 03:22:30 +00:00
}
2023-01-22 15:01:57 +00:00
// CreateDirectory creates a directory information of a specified path
// on a ProDOS image
2023-01-19 05:30:17 +00:00
func CreateDirectory(readerWriter ReaderWriterAt, path string) error {
2023-01-22 15:01:57 +00:00
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
}
2023-01-19 05:30:17 +00:00
parentPath, newDirectory := GetDirectoryAndFileNameFromPath(path)
existingFileEntry, _ := GetFileEntry(readerWriter, path)
if existingFileEntry.StorageType != StorageDeleted {
return errors.New("directory already exists")
}
fileEntry, err := getFreeFileEntryInDirectory(readerWriter, parentPath)
if err != nil {
errString := fmt.Sprintf("failed to create directory: %s", err)
return errors.New(errString)
}
// get list of blocks to write file to
blockList, err := createBlockList(readerWriter, 512)
if err != nil {
errString := fmt.Sprintf("failed to create directory: %s", err)
return errors.New(errString)
}
updateVolumeBitmap(readerWriter, blockList)
fileEntry.FileName = newDirectory
fileEntry.BlocksUsed = 1
fileEntry.CreationTime = time.Now()
fileEntry.ModifiedTime = time.Now()
fileEntry.AuxType = 0
fileEntry.EndOfFile = 0x200
fileEntry.FileType = 0x0F
fileEntry.KeyPointer = blockList[0]
fileEntry.Access = 0b11100011
fileEntry.StorageType = StorageDirectory
2023-01-22 15:01:57 +00:00
fileEntry.Version = 0x24
fileEntry.MinVersion = 0x00
2023-01-19 05:30:17 +00:00
writeFileEntry(readerWriter, fileEntry)
err = incrementFileCount(readerWriter, fileEntry)
if err != nil {
errString := fmt.Sprintf("failed to create directory: %s", err)
return errors.New(errString)
}
directoryEntry := DirectoryHeader{
PreviousBlock: 0,
NextBlock: 0,
2023-01-22 15:01:57 +00:00
IsSubDirectory: true,
2023-01-19 05:30:17 +00:00
Name: newDirectory,
CreationTime: time.Now(),
Version: 0x24,
MinVersion: 0,
Access: 0xE3,
EntryLength: 0x27,
EntriesPerBlock: 0x0D,
ActiveFileCount: 0,
StartingBlock: blockList[0],
ParentBlock: fileEntry.DirectoryBlock,
2024-03-11 13:05:23 +00:00
ParentEntry: uint16(fileEntry.DirectoryOffset-0x04) / 0x27,
2023-01-19 05:30:17 +00:00
ParentEntryLength: 0x27,
}
err = writeDirectoryHeader(readerWriter, directoryEntry)
if err != nil {
errString := fmt.Sprintf("failed to create directory: %s", err)
return errors.New(errString)
}
return nil
}
2023-01-22 15:01:57 +00:00
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
}
2021-06-26 01:15:20 +00:00
blockNumber := directoryHeader.StartingBlock
2023-01-22 15:01:57 +00:00
buffer, err := ReadBlock(readerWriter, blockNumber)
if err != nil {
return FileEntry{}, err
}
2024-03-11 13:05:23 +00:00
entryOffset := uint16(43) // start at offset after header
entryNumber := 2 // header is essentially the first entry so start at 2
2021-06-26 01:15:20 +00:00
for {
if entryNumber > 13 {
2024-03-11 13:05:23 +00:00
nextBlockNumber := uint16(buffer[2]) + uint16(buffer[3])*256
2023-01-22 15:01:57 +00:00
// 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")
}
2024-03-11 13:05:23 +00:00
nextBlockNumber, err = expandDirectory(readerWriter, buffer, blockNumber, directoryHeader)
2023-01-22 15:01:57 +00:00
if err != nil {
return FileEntry{}, err
}
2021-06-26 01:15:20 +00:00
}
2023-01-22 15:01:57 +00:00
blockNumber = nextBlockNumber
2021-06-26 01:15:20 +00:00
// else read the next block in the directory
2023-01-22 15:01:57 +00:00
buffer, err = ReadBlock(readerWriter, blockNumber)
if err != nil {
return FileEntry{}, nil
}
2023-01-22 15:01:57 +00:00
2021-06-26 01:15:20 +00:00
entryOffset = 4
entryNumber = 1
}
2023-01-22 15:01:57 +00:00
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+0x28], blockNumber, entryOffset)
2021-06-26 01:15:20 +00:00
if fileEntry.StorageType == StorageDeleted {
2023-01-22 17:02:09 +00:00
fileEntry = FileEntry{}
2021-06-26 01:15:20 +00:00
fileEntry.DirectoryBlock = blockNumber
fileEntry.DirectoryOffset = entryOffset
fileEntry.HeaderPointer = directoryHeader.StartingBlock
2021-06-30 04:04:59 +00:00
return fileEntry, nil
2021-06-26 01:15:20 +00:00
}
entryNumber++
entryOffset += 39
}
}
2024-03-11 13:05:23 +00:00
func expandDirectory(readerWriter ReaderWriterAt, buffer []byte, blockNumber uint16, directoryHeader DirectoryHeader) (uint16, error) {
2023-01-22 15:01:57 +00:00
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")
}
2024-03-11 13:05:23 +00:00
nextBlockNumber := blockList[0]
2023-01-22 15:01:57 +00:00
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)
}
2024-03-11 13:05:23 +00:00
directoryEntryOffset := directoryHeader.ParentEntry*uint16(directoryHeader.EntryLength) + 0x04
directoryFileEntry := parseFileEntry(buffer[directoryEntryOffset:directoryEntryOffset+0x28], directoryHeader.ParentBlock, directoryHeader.ParentEntry*uint16(directoryHeader.EntryLength)+0x04)
2023-01-22 15:01:57 +00:00
directoryFileEntry.BlocksUsed++
directoryFileEntry.EndOfFile += 0x200
writeFileEntry(readerWriter, directoryFileEntry)
return nextBlockNumber, nil
}
2024-03-11 13:05:23 +00:00
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
}
2021-06-06 03:22:30 +00:00
2021-06-26 01:15:20 +00:00
directoryHeader := parseDirectoryHeader(buffer, blockNumber)
2021-06-06 03:22:30 +00:00
fileEntries := make([]FileEntry, directoryHeader.ActiveFileCount)
2024-03-11 13:05:23 +00:00
entryOffset := uint16(43) // start at offset after header
activeEntries := uint16(0)
entryNumber := uint8(2) // header is essentially the first entry so start at 2
2021-06-06 03:22:30 +00:00
nextBlock := directoryHeader.NextBlock
matchedDirectory := (currentPath == len(paths)-1) && (paths[currentPath] == directoryHeader.Name)
if !matchedDirectory && (currentPath == len(paths)-1) {
// path not matched by last path part
return DirectoryHeader{}, nil, errors.New("path not matched")
2021-06-06 03:22:30 +00:00
}
for {
if entryNumber > 13 {
entryOffset = 4
entryNumber = 1
if blockNumber == 0 {
return DirectoryHeader{}, nil, nil
}
buffer, err = ReadBlock(reader, nextBlock)
if err != nil {
return DirectoryHeader{}, nil, err
2021-06-06 03:22:30 +00:00
}
2024-03-11 13:05:23 +00:00
nextBlock = uint16(buffer[2]) + uint16(buffer[3])*256
2021-06-06 03:22:30 +00:00
}
2021-06-12 02:43:35 +00:00
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset)
2021-06-06 03:22:30 +00:00
if fileEntry.StorageType != StorageDeleted {
2021-06-26 01:15:20 +00:00
if matchedDirectory && activeEntries == directoryHeader.ActiveFileCount {
return directoryHeader, fileEntries[0:activeEntries], nil
2021-06-26 01:15:20 +00:00
}
2021-06-06 03:22:30 +00:00
if matchedDirectory {
fileEntries[activeEntries] = fileEntry
} else if !matchedDirectory && fileEntry.FileType == 15 && paths[currentPath+1] == fileEntry.FileName {
return getFileEntriesInDirectory(reader, fileEntry.KeyPointer, currentPath+1, paths)
2021-06-06 03:22:30 +00:00
}
activeEntries++
}
entryNumber++
entryOffset += 39
}
}
2024-03-11 13:05:23 +00:00
func parseFileEntry(buffer []byte, blockNumber uint16, entryOffset uint16) FileEntry {
storageType := buffer[0] >> 4
fileNameLength := buffer[0] & 15
2021-06-06 03:22:30 +00:00
fileName := string(buffer[1 : fileNameLength+1])
2024-03-11 13:05:23 +00:00
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
2021-06-06 03:22:30 +00:00
creationTime := DateTimeFromProDOS(buffer[24:28])
2024-03-11 13:05:23 +00:00
version := buffer[28]
minVersion := buffer[29]
access := buffer[30]
auxType := uint16(buffer[31]) + uint16(buffer[32])*256
2021-06-06 03:22:30 +00:00
modifiedTime := DateTimeFromProDOS((buffer[33:37]))
2024-03-11 13:05:23 +00:00
headerPointer := uint16(buffer[0x25]) + uint16(buffer[0x26])*256
2021-06-06 03:22:30 +00:00
fileEntry := FileEntry{
2021-06-12 02:43:35 +00:00
StorageType: storageType,
FileName: fileName,
FileType: fileType,
CreationTime: creationTime,
Version: version,
MinVersion: minVersion,
KeyPointer: startingBlock,
BlocksUsed: blocksUsed,
EndOfFile: endOfFile,
Access: access,
AuxType: auxType,
ModifiedTime: modifiedTime,
HeaderPointer: headerPointer,
DirectoryBlock: blockNumber,
DirectoryOffset: entryOffset,
2021-06-06 03:22:30 +00:00
}
return fileEntry
}
func writeFileEntry(writer io.WriterAt, fileEntry FileEntry) {
2021-06-12 02:43:35 +00:00
buffer := make([]byte, 39)
buffer[0] = byte(fileEntry.StorageType)<<4 + byte(len(fileEntry.FileName))
for i := 0; i < len(fileEntry.FileName); i++ {
buffer[i+1] = fileEntry.FileName[i]
}
buffer[0x10] = byte(fileEntry.FileType)
buffer[0x11] = byte(fileEntry.KeyPointer & 0xFF)
buffer[0x12] = byte(fileEntry.KeyPointer >> 8)
buffer[0x13] = byte(fileEntry.BlocksUsed & 0xFF)
buffer[0x14] = byte(fileEntry.BlocksUsed >> 8)
buffer[0x15] = byte(fileEntry.EndOfFile & 0x0000FF)
buffer[0x16] = byte(fileEntry.EndOfFile & 0x00FF00 >> 8)
buffer[0x17] = byte(fileEntry.EndOfFile & 0xFF0000 >> 16)
creationTime := DateTimeToProDOS(fileEntry.CreationTime)
for i := 0; i < 4; i++ {
buffer[0x18+i] = creationTime[i]
}
buffer[0x1C] = byte(fileEntry.Version)
buffer[0x1D] = byte(fileEntry.MinVersion)
buffer[0x1E] = byte(fileEntry.Access)
buffer[0x1F] = byte(fileEntry.AuxType & 0x00FF)
buffer[0x20] = byte(fileEntry.AuxType >> 8)
2023-01-22 15:01:57 +00:00
modifiedTime := DateTimeToProDOS(fileEntry.ModifiedTime)
2021-06-12 02:43:35 +00:00
for i := 0; i < 4; i++ {
buffer[0x21+i] = modifiedTime[i]
}
buffer[0x25] = byte(fileEntry.HeaderPointer & 0x00FF)
buffer[0x26] = byte(fileEntry.HeaderPointer >> 8)
2023-01-22 15:01:57 +00:00
//fmt.Printf("Writing file entry at block: %04X offset: %04X\n", fileEntry.DirectoryBlock, fileEntry.DirectoryOffset)
2024-03-11 13:05:23 +00:00
_, err := writer.WriteAt(buffer, int64(fileEntry.DirectoryBlock)*512+int64(fileEntry.DirectoryOffset))
2021-06-12 02:43:35 +00:00
if err != nil {
}
}
2021-06-06 12:00:20 +00:00
func parseVolumeHeader(buffer []byte) VolumeHeader {
2024-03-11 13:05:23 +00:00
nextBlock := uint16(buffer[2]) + uint16(buffer[3])*256
2021-06-06 03:22:30 +00:00
filenameLength := buffer[4] & 15
volumeName := string(buffer[5 : filenameLength+5])
creationTime := DateTimeFromProDOS(buffer[28:32])
2024-03-11 13:05:23 +00:00
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
2021-06-06 03:22:30 +00:00
if minVersion > 0 {
2021-06-06 03:22:30 +00:00
panic("Unsupported ProDOS version")
}
volumeHeader := VolumeHeader{
VolumeName: volumeName,
CreationTime: creationTime,
ActiveFileCount: fileCount,
BitmapStartBlock: bitmapBlock,
TotalBlocks: totalBlocks,
NextBlock: nextBlock,
EntriesPerBlock: entriesPerBlock,
EntryLength: entryLength,
2021-06-12 02:43:35 +00:00
MinVersion: minVersion,
Version: version,
2021-06-06 03:22:30 +00:00
}
return volumeHeader
}
2024-03-11 13:05:23 +00:00
func parseDirectoryHeader(buffer []byte, blockNumber uint16) DirectoryHeader {
previousBlock := uint16(buffer[0x00]) + uint16(buffer[0x01])*256
nextBlock := uint16(buffer[0x02]) + uint16(buffer[0x03])*256
2023-01-22 15:01:57 +00:00
isSubDirectory := (buffer[0x04] & 0xF0) == 0xE0
filenameLength := buffer[0x04] & 0x0F
2021-06-12 02:43:35 +00:00
name := string(buffer[0x05 : filenameLength+0x05])
2023-01-19 05:30:17 +00:00
creationTime := DateTimeFromProDOS(buffer[0x1C:0x20])
2024-03-11 13:05:23 +00:00
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]
2021-06-06 03:22:30 +00:00
directoryEntry := DirectoryHeader{
2023-01-19 05:30:17 +00:00
PreviousBlock: previousBlock,
NextBlock: nextBlock,
StartingBlock: blockNumber,
2023-01-22 15:01:57 +00:00
IsSubDirectory: isSubDirectory,
2023-01-19 05:30:17 +00:00
Name: name,
CreationTime: creationTime,
Version: version,
MinVersion: minVersion,
Access: access,
EntryLength: entryLength,
EntriesPerBlock: entriesPerBlock,
ActiveFileCount: fileCount,
ParentBlock: parentBlock,
ParentEntry: parentEntry,
ParentEntryLength: parentEntryLength,
2021-06-06 03:22:30 +00:00
}
return directoryEntry
}
2021-06-12 02:43:35 +00:00
func writeDirectoryHeader(readerWriter ReaderWriterAt, directoryHeader DirectoryHeader) error {
2023-01-22 15:01:57 +00:00
// Reading back the block preserves values including reserved fields
buffer, err := ReadBlock(readerWriter, directoryHeader.StartingBlock)
if err != nil {
return err
}
2021-06-30 12:22:08 +00:00
buffer[0x00] = byte(directoryHeader.PreviousBlock & 0x00FF)
buffer[0x01] = byte(directoryHeader.PreviousBlock >> 8)
2021-06-12 02:43:35 +00:00
buffer[0x02] = byte(directoryHeader.NextBlock & 0x00FF)
buffer[0x03] = byte(directoryHeader.NextBlock >> 8)
2023-01-22 15:01:57 +00:00
if directoryHeader.IsSubDirectory {
buffer[0x04] = 0xE0
} else {
buffer[0x04] = 0xF0
}
2021-06-12 02:43:35 +00:00
buffer[0x04] = buffer[0x04] | byte(len(directoryHeader.Name))
for i := 0; i < len(directoryHeader.Name); i++ {
buffer[0x05+i] = directoryHeader.Name[i]
}
2023-01-19 05:30:17 +00:00
creationTime := DateTimeToProDOS(directoryHeader.CreationTime)
for i := 0; i < 4; i++ {
buffer[0x1C+i] = creationTime[i]
}
2023-01-22 15:01:57 +00:00
// 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
2023-01-19 05:30:17 +00:00
buffer[0x20] = byte(directoryHeader.Version)
buffer[0x21] = byte(directoryHeader.MinVersion)
buffer[0x22] = byte(directoryHeader.Access)
buffer[0x23] = byte(directoryHeader.EntryLength)
buffer[0x24] = byte(directoryHeader.EntriesPerBlock)
2021-06-12 02:43:35 +00:00
buffer[0x25] = byte(directoryHeader.ActiveFileCount & 0x00FF)
buffer[0x26] = byte(directoryHeader.ActiveFileCount >> 8)
2023-01-19 05:30:17 +00:00
buffer[0x27] = byte(directoryHeader.ParentBlock & 0x00FF)
buffer[0x28] = byte(directoryHeader.ParentBlock >> 8)
buffer[0x29] = byte(directoryHeader.ParentEntry)
buffer[0x2A] = byte(directoryHeader.ParentEntryLength)
WriteBlock(readerWriter, directoryHeader.StartingBlock, buffer)
return nil
2021-06-12 02:43:35 +00:00
}