]> git.r.bdr.sh - rbdr/mobius/blame - hotline/flattened_file_object.go
patch: v0.0.12
[rbdr/mobius] / hotline / flattened_file_object.go
CommitLineData
6988a057
JH
1package hotline
2
3import (
4 "encoding/binary"
5 "fmt"
6 "os"
7)
8
9type flattenedFileObject struct {
10 FlatFileHeader FlatFileHeader
11 FlatFileInformationForkHeader FlatFileInformationForkHeader
12 FlatFileInformationFork FlatFileInformationFork
13 FlatFileDataForkHeader FlatFileDataForkHeader
14 FileData []byte
15}
16
17// FlatFileHeader is the first section of a "Flattened File Object". All fields have static values.
18type FlatFileHeader struct {
72dd37f1
JH
19 Format [4]byte // Always "FILP"
20 Version [2]byte // Always 1
21 RSVD [16]byte // Always empty zeros
22 ForkCount [2]byte // Always 2
6988a057
JH
23}
24
25// NewFlatFileHeader returns a FlatFileHeader struct
26func NewFlatFileHeader() FlatFileHeader {
27 return FlatFileHeader{
72dd37f1
JH
28 Format: [4]byte{0x46, 0x49, 0x4c, 0x50}, // FILP
29 Version: [2]byte{0, 1},
30 RSVD: [16]byte{},
31 ForkCount: [2]byte{0, 2},
6988a057
JH
32 }
33}
34
35// FlatFileInformationForkHeader is the second section of a "Flattened File Object"
36type FlatFileInformationForkHeader struct {
37 ForkType []byte // Always "INFO"
38 CompressionType []byte // Always 0; Compression was never implemented in the Hotline protocol
39 RSVD []byte // Always zeros
40 DataSize []byte // Size of the flat file information fork
41}
42
43type FlatFileInformationFork struct {
44 Platform []byte // Operating System used. ("AMAC" or "MWIN")
45 TypeSignature []byte // File type signature
46 CreatorSignature []byte // File creator signature
47 Flags []byte
48 PlatformFlags []byte
49 RSVD []byte
50 CreateDate []byte
51 ModifyDate []byte
52 NameScript []byte // TODO: what is this?
53 NameSize []byte // Length of file name (Maximum 128 characters)
54 Name []byte // File name
55 CommentSize []byte // Length of file comment
56 Comment []byte // File comment
57}
58
59func NewFlatFileInformationFork(fileName string) FlatFileInformationFork {
60 return FlatFileInformationFork{
61 Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?)
62 TypeSignature: []byte(fileTypeFromFilename(fileName)), // TODO: Don't infer types from filename
63 CreatorSignature: []byte(fileCreatorFromFilename(fileName)), // TODO: Don't infer types from filename
64 Flags: []byte{0, 0, 0, 0}, // TODO: What is this?
65 PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this?
66 RSVD: make([]byte, 32), // Unimplemented in Hotline Protocol
67 CreateDate: []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}, // TODO: implement
68 ModifyDate: []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}, // TODO: implement
69 NameScript: make([]byte, 2), // TODO: What is this?
70 Name: []byte(fileName),
71 Comment: []byte("TODO"), // TODO: implement (maybe?)
72 }
73}
74
75// Size of the flat file information fork, which is the fixed size of 72 bytes
76// plus the number of bytes in the FileName
77// TODO: plus the size of the Comment!
78func (ffif FlatFileInformationFork) DataSize() []byte {
79 size := make([]byte, 4)
80 nameLen := len(ffif.Name)
81 //TODO: Can I do math directly on two byte slices?
82 dataSize := nameLen + 74
83
84 binary.BigEndian.PutUint32(size, uint32(dataSize))
85
86 return size
87}
88
89func (ffo flattenedFileObject) TransferSize() []byte {
c5d9af5a 90 payloadSize := len(ffo.BinaryMarshal())
6988a057
JH
91 dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize)
92
93 transferSize := make([]byte, 4)
94 binary.BigEndian.PutUint32(transferSize, dataSize+uint32(payloadSize))
95
96 return transferSize
97}
98
99func (ffif FlatFileInformationFork) ReadNameSize() []byte {
100 size := make([]byte, 2)
101 binary.BigEndian.PutUint16(size, uint16(len(ffif.Name)))
102
103 return size
104}
105
106type FlatFileDataForkHeader struct {
107 ForkType []byte
108 CompressionType []byte
109 RSVD []byte
110 DataSize []byte
111}
112
6988a057
JH
113// ReadFlattenedFileObject parses a byte slice into a flattenedFileObject
114func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
115 nameSize := bytes[110:112]
116 bs := binary.BigEndian.Uint16(nameSize)
117
118 nameEnd := 112 + bs
119
120 commentSize := bytes[nameEnd : nameEnd+2]
121 commentLen := binary.BigEndian.Uint16(commentSize)
122
123 commentStartPos := int(nameEnd) + 2
124 commentEndPos := int(nameEnd) + 2 + int(commentLen)
125
126 comment := bytes[commentStartPos:commentEndPos]
127
128 //dataSizeField := bytes[nameEnd+14+commentLen : nameEnd+18+commentLen]
129 //dataSize := binary.BigEndian.Uint32(dataSizeField)
130
131 ffo := flattenedFileObject{
72dd37f1 132 FlatFileHeader: NewFlatFileHeader(),
6988a057
JH
133 FlatFileInformationForkHeader: FlatFileInformationForkHeader{
134 ForkType: bytes[24:28],
135 CompressionType: bytes[28:32],
136 RSVD: bytes[32:36],
137 DataSize: bytes[36:40],
138 },
139 FlatFileInformationFork: FlatFileInformationFork{
140 Platform: bytes[40:44],
141 TypeSignature: bytes[44:48],
142 CreatorSignature: bytes[48:52],
143 Flags: bytes[52:56],
144 PlatformFlags: bytes[56:60],
145 RSVD: bytes[60:92],
146 CreateDate: bytes[92:100],
147 ModifyDate: bytes[100:108],
148 NameScript: bytes[108:110],
149 NameSize: bytes[110:112],
150 Name: bytes[112:nameEnd],
151 CommentSize: bytes[nameEnd : nameEnd+2],
152 Comment: comment,
153 },
154 FlatFileDataForkHeader: FlatFileDataForkHeader{
155 ForkType: bytes[commentEndPos : commentEndPos+4],
156 CompressionType: bytes[commentEndPos+4 : commentEndPos+8],
157 RSVD: bytes[commentEndPos+8 : commentEndPos+12],
158 DataSize: bytes[commentEndPos+12 : commentEndPos+16],
159 },
160 }
161
162 return ffo
163}
164
c5d9af5a 165func (f flattenedFileObject) BinaryMarshal() []byte {
6988a057 166 var out []byte
72dd37f1
JH
167 out = append(out, f.FlatFileHeader.Format[:]...)
168 out = append(out, f.FlatFileHeader.Version[:]...)
169 out = append(out, f.FlatFileHeader.RSVD[:]...)
170 out = append(out, f.FlatFileHeader.ForkCount[:]...)
6988a057
JH
171
172 out = append(out, []byte("INFO")...)
173 out = append(out, []byte{0, 0, 0, 0}...)
174 out = append(out, make([]byte, 4)...)
175 out = append(out, f.FlatFileInformationFork.DataSize()...)
176
177 out = append(out, f.FlatFileInformationFork.Platform...)
178 out = append(out, f.FlatFileInformationFork.TypeSignature...)
179 out = append(out, f.FlatFileInformationFork.CreatorSignature...)
180 out = append(out, f.FlatFileInformationFork.Flags...)
181 out = append(out, f.FlatFileInformationFork.PlatformFlags...)
182 out = append(out, f.FlatFileInformationFork.RSVD...)
183 out = append(out, f.FlatFileInformationFork.CreateDate...)
184 out = append(out, f.FlatFileInformationFork.ModifyDate...)
185 out = append(out, f.FlatFileInformationFork.NameScript...)
186 out = append(out, f.FlatFileInformationFork.ReadNameSize()...)
187 out = append(out, f.FlatFileInformationFork.Name...)
188
189 // TODO: Implement commentlen and comment field
190 out = append(out, []byte{0, 0}...)
191
192 out = append(out, f.FlatFileDataForkHeader.ForkType...)
193 out = append(out, f.FlatFileDataForkHeader.CompressionType...)
194 out = append(out, f.FlatFileDataForkHeader.RSVD...)
195 out = append(out, f.FlatFileDataForkHeader.DataSize...)
196
197 return out
198}
199
72dd37f1 200func NewFlattenedFileObject(filePath, fileName string) (*flattenedFileObject, error) {
6988a057
JH
201 file, err := os.Open(fmt.Sprintf("%v/%v", filePath, fileName))
202 if err != nil {
72dd37f1 203 return nil, err
6988a057
JH
204 }
205 defer file.Close()
206
207 fileInfo, err := file.Stat()
208 if err != nil {
72dd37f1 209 return nil, err
6988a057
JH
210 }
211
212 dataSize := make([]byte, 4)
213 binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size()))
214
72dd37f1 215 return &flattenedFileObject{
6988a057
JH
216 FlatFileHeader: NewFlatFileHeader(),
217 FlatFileInformationFork: NewFlatFileInformationFork(fileName),
218 FlatFileDataForkHeader: FlatFileDataForkHeader{
219 ForkType: []byte("DATA"),
220 CompressionType: []byte{0, 0, 0, 0},
221 RSVD: []byte{0, 0, 0, 0},
222 DataSize: dataSize,
223 },
224 }, nil
225}