aboutsummaryrefslogtreecommitdiff
path: root/hotline/flattened_file_object.go
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2021-07-28 18:21:52 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2021-07-28 14:21:52 -0700
commit22c599abc18895f73e96095f35b71cf3357d41b4 (patch)
tree482ef57d386c955692ea43c43e4655b3c3763499 /hotline/flattened_file_object.go
parent71c56068adca18f76ebee86355f000a3e51d3127 (diff)
Move code to hotline dir
Diffstat (limited to 'hotline/flattened_file_object.go')
-rw-r--r--hotline/flattened_file_object.go239
1 files changed, 239 insertions, 0 deletions
diff --git a/hotline/flattened_file_object.go b/hotline/flattened_file_object.go
new file mode 100644
index 0000000..dfdd8a8
--- /dev/null
+++ b/hotline/flattened_file_object.go
@@ -0,0 +1,239 @@
+package hotline
+
+import (
+ "encoding/binary"
+ "fmt"
+ "os"
+)
+
+type flattenedFileObject struct {
+ FlatFileHeader FlatFileHeader
+ FlatFileInformationForkHeader FlatFileInformationForkHeader
+ FlatFileInformationFork FlatFileInformationFork
+ FlatFileDataForkHeader FlatFileDataForkHeader
+ FileData []byte
+}
+
+// FlatFileHeader is the first section of a "Flattened File Object". All fields have static values.
+type FlatFileHeader struct {
+ Format []byte // Always "FILP"
+ Version []byte // Always 1
+ RSVD []byte // Always empty zeros
+ ForkCount []byte // Always 2
+}
+
+// NewFlatFileHeader returns a FlatFileHeader struct
+func NewFlatFileHeader() FlatFileHeader {
+ return FlatFileHeader{
+ Format: []byte("FILP"),
+ Version: []byte{0, 1},
+ RSVD: make([]byte, 16),
+ ForkCount: []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
+}
+
+type FlatFileInformationFork struct {
+ Platform []byte // Operating System used. ("AMAC" or "MWIN")
+ TypeSignature []byte // File type signature
+ CreatorSignature []byte // File creator signature
+ Flags []byte
+ PlatformFlags []byte
+ RSVD []byte
+ CreateDate []byte
+ ModifyDate []byte
+ NameScript []byte // TODO: what is this?
+ NameSize []byte // Length of file name (Maximum 128 characters)
+ Name []byte // File name
+ CommentSize []byte // Length of file comment
+ Comment []byte // File comment
+}
+
+func NewFlatFileInformationFork(fileName string) FlatFileInformationFork {
+ return FlatFileInformationFork{
+ Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?)
+ TypeSignature: []byte(fileTypeFromFilename(fileName)), // TODO: Don't infer types from filename
+ CreatorSignature: []byte(fileCreatorFromFilename(fileName)), // 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?
+ Name: []byte(fileName),
+ Comment: []byte("TODO"), // TODO: implement (maybe?)
+ }
+}
+
+// Size of the flat file information fork, which is the fixed size of 72 bytes
+// plus the number of bytes in the FileName
+// TODO: plus the size of the Comment!
+func (ffif FlatFileInformationFork) DataSize() []byte {
+ size := make([]byte, 4)
+ nameLen := len(ffif.Name)
+ //TODO: Can I do math directly on two byte slices?
+ dataSize := nameLen + 74
+
+ binary.BigEndian.PutUint32(size, uint32(dataSize))
+
+ return size
+}
+
+func (ffo flattenedFileObject) TransferSize() []byte {
+ payloadSize := len(ffo.Payload())
+ dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize)
+
+ transferSize := make([]byte, 4)
+ binary.BigEndian.PutUint32(transferSize, dataSize+uint32(payloadSize))
+
+ return transferSize
+}
+
+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
+}
+
+func NewFlatFileDataForkHeader() FlatFileDataForkHeader {
+ return FlatFileDataForkHeader{
+ ForkType: []byte("DATA"),
+ CompressionType: []byte{0, 0, 0, 0},
+ RSVD: []byte{0, 0, 0, 0},
+ // DataSize: []byte{0, 0, 0x03, 0xc3},
+ }
+}
+
+// ReadFlattenedFileObject parses a byte slice into a flattenedFileObject
+func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
+ nameSize := bytes[110:112]
+ bs := binary.BigEndian.Uint16(nameSize)
+
+ 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: FlatFileHeader{
+ Format: bytes[0:4],
+ Version: bytes[4:6],
+ RSVD: bytes[6:22],
+ ForkCount: bytes[22:24],
+ },
+ 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],
+ },
+ }
+
+ return ffo
+}
+
+func (f flattenedFileObject) Payload() []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, []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...)
+
+ // TODO: Implement commentlen and comment field
+ out = append(out, []byte{0, 0}...)
+
+ out = append(out, f.FlatFileDataForkHeader.ForkType...)
+ out = append(out, f.FlatFileDataForkHeader.CompressionType...)
+ out = append(out, f.FlatFileDataForkHeader.RSVD...)
+ out = append(out, f.FlatFileDataForkHeader.DataSize...)
+
+ return out
+}
+
+func NewFlattenedFileObject(filePath string, fileName string) (flattenedFileObject, error) {
+ file, err := os.Open(fmt.Sprintf("%v/%v", filePath, fileName))
+ if err != nil {
+ return flattenedFileObject{}, err
+ }
+ defer file.Close()
+
+ fileInfo, err := file.Stat()
+ if err != nil {
+ return flattenedFileObject{}, err
+ }
+
+ dataSize := make([]byte, 4)
+ binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size()))
+
+ return flattenedFileObject{
+ FlatFileHeader: NewFlatFileHeader(),
+ FlatFileInformationFork: NewFlatFileInformationFork(fileName),
+ FlatFileDataForkHeader: FlatFileDataForkHeader{
+ ForkType: []byte("DATA"),
+ CompressionType: []byte{0, 0, 0, 0},
+ RSVD: []byte{0, 0, 0, 0},
+ DataSize: dataSize,
+ },
+ }, nil
+}