X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/blobdiff_plain/2728d12b6169a978aa3dc2e35390923d2eecd295..23951f11736687ae7f420db20160a9fd76aa4856:/hotline/flattened_file_object.go?ds=inline diff --git a/hotline/flattened_file_object.go b/hotline/flattened_file_object.go index bd281e0..c57de19 100644 --- a/hotline/flattened_file_object.go +++ b/hotline/flattened_file_object.go @@ -2,15 +2,15 @@ package hotline import ( "encoding/binary" - "os" + "io" ) type flattenedFileObject struct { FlatFileHeader FlatFileHeader - FlatFileInformationForkHeader FlatFileInformationForkHeader + FlatFileInformationForkHeader FlatFileForkHeader FlatFileInformationFork FlatFileInformationFork - FlatFileDataForkHeader FlatFileDataForkHeader - FileData []byte + FlatFileDataForkHeader FlatFileForkHeader + FlatFileResForkHeader FlatFileForkHeader } // FlatFileHeader is the first section of a "Flattened File Object". All fields have static values. @@ -18,25 +18,7 @@ type FlatFileHeader struct { Format [4]byte // Always "FILP" Version [2]byte // Always 1 RSVD [16]byte // Always empty zeros - ForkCount [2]byte // Always 2 -} - -// NewFlatFileHeader returns a FlatFileHeader struct -func NewFlatFileHeader() FlatFileHeader { - return FlatFileHeader{ - Format: [4]byte{0x46, 0x49, 0x4c, 0x50}, // FILP - Version: [2]byte{0, 1}, - RSVD: [16]byte{}, - ForkCount: [2]byte{0, 2}, - } -} - -// FlatFileInformationForkHeader is the second section of a "Flattened File Object" -type FlatFileInformationForkHeader struct { - ForkType []byte // Always "INFO" - CompressionType []byte // Always 0; Compression was never implemented in the Hotline protocol - RSVD []byte // Always zeros - DataSize []byte // Size of the flat file information fork + ForkCount [2]byte // Number of forks, either 2 or 3 if there is a resource fork } type FlatFileInformationFork struct { @@ -48,180 +30,220 @@ type FlatFileInformationFork struct { RSVD []byte CreateDate []byte ModifyDate []byte - NameScript []byte // TODO: what is this? + NameScript []byte NameSize []byte // Length of file name (Maximum 128 characters) Name []byte // File name - CommentSize []byte // Length of file comment + CommentSize []byte // Length of the comment Comment []byte // File comment } -func NewFlatFileInformationFork(fileName string) FlatFileInformationFork { +func NewFlatFileInformationFork(fileName string, modifyTime []byte, typeSignature string, creatorSignature string) FlatFileInformationFork { return FlatFileInformationFork{ - Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?) - TypeSignature: []byte(fileTypeFromFilename(fileName).TypeCode), // TODO: Don't infer types from filename - CreatorSignature: []byte(fileTypeFromFilename(fileName).CreatorCode), // TODO: Don't infer types from filename - Flags: []byte{0, 0, 0, 0}, // TODO: What is this? - PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this? - RSVD: make([]byte, 32), // Unimplemented in Hotline Protocol - CreateDate: []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}, // TODO: implement - ModifyDate: []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}, // TODO: implement - NameScript: make([]byte, 2), // TODO: What is this? + Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?) + TypeSignature: []byte(typeSignature), // TODO: Don't infer types from filename + CreatorSignature: []byte(creatorSignature), // TODO: Don't infer types from filename + Flags: []byte{0, 0, 0, 0}, // TODO: What is this? + PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this? + RSVD: make([]byte, 32), // Unimplemented in Hotline Protocol + CreateDate: modifyTime, // some filesystems don't support createTime + ModifyDate: modifyTime, + NameScript: make([]byte, 2), // TODO: What is this? Name: []byte(fileName), - CommentSize: []byte{0, 4}, - Comment: []byte("TODO"), // TODO: implement (maybe?) + CommentSize: []byte{0, 0}, + Comment: []byte{}, // TODO: implement (maybe?) } } +func (ffif *FlatFileInformationFork) friendlyType() []byte { + if name, ok := friendlyCreatorNames[string(ffif.TypeSignature)]; ok { + return []byte(name) + } + return ffif.TypeSignature +} + +func (ffif *FlatFileInformationFork) friendlyCreator() []byte { + if name, ok := friendlyCreatorNames[string(ffif.CreatorSignature)]; ok { + return []byte(name) + } + return ffif.CreatorSignature +} + +func (ffif *FlatFileInformationFork) setComment(comment []byte) error { + ffif.CommentSize = make([]byte, 2) + ffif.Comment = comment + binary.BigEndian.PutUint16(ffif.CommentSize, uint16(len(comment))) + + // TODO: return err if comment is too long + return nil +} + // DataSize calculates the size of the flat file information fork, which is // 72 bytes for the fixed length fields plus the length of the Name + Comment -func (ffif FlatFileInformationFork) DataSize() []byte { +func (ffif *FlatFileInformationFork) DataSize() []byte { size := make([]byte, 4) - //TODO: Can I do math directly on two byte slices? - dataSize := len(ffif.Name) + len(ffif.Comment) + 74 + dataSize := len(ffif.Name) + len(ffif.Comment) + 74 // 74 = len of fixed size headers binary.BigEndian.PutUint32(size, uint32(dataSize)) return size } -func (ffo flattenedFileObject) TransferSize() []byte { +func (ffif *FlatFileInformationFork) Size() [4]byte { + size := [4]byte{} + + dataSize := len(ffif.Name) + len(ffif.Comment) + 74 // 74 = len of fixed size headers + + binary.BigEndian.PutUint32(size[:], uint32(dataSize)) + + return size +} + +func (ffo *flattenedFileObject) TransferSize(offset int64) []byte { + // get length of the flattenedFileObject, including the info fork payloadSize := len(ffo.BinaryMarshal()) - dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize) - transferSize := make([]byte, 4) - binary.BigEndian.PutUint32(transferSize, dataSize+uint32(payloadSize)) + // length of data fork + dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:]) + + // length of resource fork + resForkSize := binary.BigEndian.Uint32(ffo.FlatFileResForkHeader.DataSize[:]) - return transferSize + size := make([]byte, 4) + binary.BigEndian.PutUint32(size, dataSize+resForkSize+uint32(payloadSize)-uint32(offset)) + + return size } -func (ffif FlatFileInformationFork) ReadNameSize() []byte { +func (ffif *FlatFileInformationFork) ReadNameSize() []byte { size := make([]byte, 2) binary.BigEndian.PutUint16(size, uint16(len(ffif.Name))) return size } -type FlatFileDataForkHeader struct { - ForkType []byte - CompressionType []byte - RSVD []byte - DataSize []byte +type FlatFileForkHeader struct { + ForkType [4]byte // Either INFO, DATA or MACR + CompressionType [4]byte + RSVD [4]byte + DataSize [4]byte } -// ReadFlattenedFileObject parses a byte slice into a flattenedFileObject -func ReadFlattenedFileObject(bytes []byte) flattenedFileObject { - nameSize := bytes[110:112] - bs := binary.BigEndian.Uint16(nameSize) +func (ffif *FlatFileInformationFork) MarshalBinary() []byte { + var b []byte + b = append(b, ffif.Platform...) + b = append(b, ffif.TypeSignature...) + b = append(b, ffif.CreatorSignature...) + b = append(b, ffif.Flags...) + b = append(b, ffif.PlatformFlags...) + b = append(b, ffif.RSVD...) + b = append(b, ffif.CreateDate...) + b = append(b, ffif.ModifyDate...) + b = append(b, ffif.NameScript...) + b = append(b, ffif.ReadNameSize()...) + b = append(b, ffif.Name...) + b = append(b, ffif.CommentSize...) + b = append(b, ffif.Comment...) + + return b +} - nameEnd := 112 + bs - - commentSize := bytes[nameEnd : nameEnd+2] - commentLen := binary.BigEndian.Uint16(commentSize) - - commentStartPos := int(nameEnd) + 2 - commentEndPos := int(nameEnd) + 2 + int(commentLen) - - comment := bytes[commentStartPos:commentEndPos] - - //dataSizeField := bytes[nameEnd+14+commentLen : nameEnd+18+commentLen] - //dataSize := binary.BigEndian.Uint32(dataSizeField) - - ffo := flattenedFileObject{ - FlatFileHeader: NewFlatFileHeader(), - FlatFileInformationForkHeader: FlatFileInformationForkHeader{ - ForkType: bytes[24:28], - CompressionType: bytes[28:32], - RSVD: bytes[32:36], - DataSize: bytes[36:40], - }, - FlatFileInformationFork: FlatFileInformationFork{ - Platform: bytes[40:44], - TypeSignature: bytes[44:48], - CreatorSignature: bytes[48:52], - Flags: bytes[52:56], - PlatformFlags: bytes[56:60], - RSVD: bytes[60:92], - CreateDate: bytes[92:100], - ModifyDate: bytes[100:108], - NameScript: bytes[108:110], - NameSize: bytes[110:112], - Name: bytes[112:nameEnd], - CommentSize: bytes[nameEnd : nameEnd+2], - Comment: comment, - }, - FlatFileDataForkHeader: FlatFileDataForkHeader{ - ForkType: bytes[commentEndPos : commentEndPos+4], - CompressionType: bytes[commentEndPos+4 : commentEndPos+8], - RSVD: bytes[commentEndPos+8 : commentEndPos+12], - DataSize: bytes[commentEndPos+12 : commentEndPos+16], - }, +func (ffif *FlatFileInformationFork) UnmarshalBinary(b []byte) error { + nameSize := b[70:72] + bs := binary.BigEndian.Uint16(nameSize) + nameEnd := 72 + bs + + ffif.Platform = b[0:4] + ffif.TypeSignature = b[4:8] + ffif.CreatorSignature = b[8:12] + ffif.Flags = b[12:16] + ffif.PlatformFlags = b[16:20] + ffif.RSVD = b[20:52] + ffif.CreateDate = b[52:60] + ffif.ModifyDate = b[60:68] + ffif.NameScript = b[68:70] + ffif.NameSize = b[70:72] + ffif.Name = b[72:nameEnd] + + if len(b) > int(nameEnd) { + ffif.CommentSize = b[nameEnd : nameEnd+2] + commentLen := binary.BigEndian.Uint16(ffif.CommentSize) + + commentStartPos := int(nameEnd) + 2 + commentEndPos := int(nameEnd) + 2 + int(commentLen) + + ffif.Comment = b[commentStartPos:commentEndPos] } - return ffo + return nil } -func (f flattenedFileObject) BinaryMarshal() []byte { +func (ffo *flattenedFileObject) BinaryMarshal() []byte { var out []byte - out = append(out, f.FlatFileHeader.Format[:]...) - out = append(out, f.FlatFileHeader.Version[:]...) - out = append(out, f.FlatFileHeader.RSVD[:]...) - out = append(out, f.FlatFileHeader.ForkCount[:]...) + out = append(out, ffo.FlatFileHeader.Format[:]...) + out = append(out, ffo.FlatFileHeader.Version[:]...) + out = append(out, ffo.FlatFileHeader.RSVD[:]...) + out = append(out, ffo.FlatFileHeader.ForkCount[:]...) out = append(out, []byte("INFO")...) out = append(out, []byte{0, 0, 0, 0}...) out = append(out, make([]byte, 4)...) - out = append(out, f.FlatFileInformationFork.DataSize()...) - - out = append(out, f.FlatFileInformationFork.Platform...) - out = append(out, f.FlatFileInformationFork.TypeSignature...) - out = append(out, f.FlatFileInformationFork.CreatorSignature...) - out = append(out, f.FlatFileInformationFork.Flags...) - out = append(out, f.FlatFileInformationFork.PlatformFlags...) - out = append(out, f.FlatFileInformationFork.RSVD...) - out = append(out, f.FlatFileInformationFork.CreateDate...) - out = append(out, f.FlatFileInformationFork.ModifyDate...) - out = append(out, f.FlatFileInformationFork.NameScript...) - out = append(out, f.FlatFileInformationFork.ReadNameSize()...) - out = append(out, f.FlatFileInformationFork.Name...) - out = append(out, f.FlatFileInformationFork.CommentSize...) - out = append(out, f.FlatFileInformationFork.Comment...) - - out = append(out, f.FlatFileDataForkHeader.ForkType...) - out = append(out, f.FlatFileDataForkHeader.CompressionType...) - out = append(out, f.FlatFileDataForkHeader.RSVD...) - out = append(out, f.FlatFileDataForkHeader.DataSize...) + out = append(out, ffo.FlatFileInformationFork.DataSize()...) + + out = append(out, ffo.FlatFileInformationFork.Platform...) + out = append(out, ffo.FlatFileInformationFork.TypeSignature...) + out = append(out, ffo.FlatFileInformationFork.CreatorSignature...) + out = append(out, ffo.FlatFileInformationFork.Flags...) + out = append(out, ffo.FlatFileInformationFork.PlatformFlags...) + out = append(out, ffo.FlatFileInformationFork.RSVD...) + out = append(out, ffo.FlatFileInformationFork.CreateDate...) + out = append(out, ffo.FlatFileInformationFork.ModifyDate...) + out = append(out, ffo.FlatFileInformationFork.NameScript...) + out = append(out, ffo.FlatFileInformationFork.ReadNameSize()...) + out = append(out, ffo.FlatFileInformationFork.Name...) + out = append(out, ffo.FlatFileInformationFork.CommentSize...) + out = append(out, ffo.FlatFileInformationFork.Comment...) + + out = append(out, ffo.FlatFileDataForkHeader.ForkType[:]...) + out = append(out, ffo.FlatFileDataForkHeader.CompressionType[:]...) + out = append(out, ffo.FlatFileDataForkHeader.RSVD[:]...) + out = append(out, ffo.FlatFileDataForkHeader.DataSize[:]...) return out } -func NewFlattenedFileObject(fileRoot string, filePath, fileName []byte) (*flattenedFileObject, error) { - fullFilePath, err := readPath(fileRoot, filePath, fileName) - if err != nil { - return nil, err +func (ffo *flattenedFileObject) ReadFrom(r io.Reader) (int, error) { + var n int + + if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileHeader); err != nil { + return n, err } - file, err := os.Open(fullFilePath) - if err != nil { - return nil, err + + if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileInformationForkHeader); err != nil { + return n, err + } + + dataLen := binary.BigEndian.Uint32(ffo.FlatFileInformationForkHeader.DataSize[:]) + ffifBuf := make([]byte, dataLen) + if _, err := io.ReadFull(r, ffifBuf); err != nil { + return n, err + } + + if err := ffo.FlatFileInformationFork.UnmarshalBinary(ffifBuf); err != nil { + return n, err } - defer file.Close() - fileInfo, err := file.Stat() - if err != nil { - return nil, err + if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileDataForkHeader); err != nil { + return n, err } - dataSize := make([]byte, 4) - binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size())) - - return &flattenedFileObject{ - FlatFileHeader: NewFlatFileHeader(), - FlatFileInformationFork: NewFlatFileInformationFork(string(fileName)), - FlatFileDataForkHeader: FlatFileDataForkHeader{ - ForkType: []byte("DATA"), - CompressionType: []byte{0, 0, 0, 0}, - RSVD: []byte{0, 0, 0, 0}, - DataSize: dataSize, - }, - }, nil + return n, nil +} + +func (ffo *flattenedFileObject) dataSize() int64 { + return int64(binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:])) +} + +func (ffo *flattenedFileObject) rsrcSize() int64 { + return int64(binary.BigEndian.Uint32(ffo.FlatFileResForkHeader.DataSize[:])) }