]> git.r.bdr.sh - rbdr/mobius/blame - hotline/flattened_file_object.go
Add tests and clean up
[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 {
90 payloadSize := len(ffo.Payload())
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
113func NewFlatFileDataForkHeader() FlatFileDataForkHeader {
114 return FlatFileDataForkHeader{
115 ForkType: []byte("DATA"),
116 CompressionType: []byte{0, 0, 0, 0},
117 RSVD: []byte{0, 0, 0, 0},
118 // DataSize: []byte{0, 0, 0x03, 0xc3},
119 }
120}
121
122// ReadFlattenedFileObject parses a byte slice into a flattenedFileObject
123func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
124 nameSize := bytes[110:112]
125 bs := binary.BigEndian.Uint16(nameSize)
126
127 nameEnd := 112 + bs
128
129 commentSize := bytes[nameEnd : nameEnd+2]
130 commentLen := binary.BigEndian.Uint16(commentSize)
131
132 commentStartPos := int(nameEnd) + 2
133 commentEndPos := int(nameEnd) + 2 + int(commentLen)
134
135 comment := bytes[commentStartPos:commentEndPos]
136
137 //dataSizeField := bytes[nameEnd+14+commentLen : nameEnd+18+commentLen]
138 //dataSize := binary.BigEndian.Uint32(dataSizeField)
139
140 ffo := flattenedFileObject{
72dd37f1 141 FlatFileHeader: NewFlatFileHeader(),
6988a057
JH
142 FlatFileInformationForkHeader: FlatFileInformationForkHeader{
143 ForkType: bytes[24:28],
144 CompressionType: bytes[28:32],
145 RSVD: bytes[32:36],
146 DataSize: bytes[36:40],
147 },
148 FlatFileInformationFork: FlatFileInformationFork{
149 Platform: bytes[40:44],
150 TypeSignature: bytes[44:48],
151 CreatorSignature: bytes[48:52],
152 Flags: bytes[52:56],
153 PlatformFlags: bytes[56:60],
154 RSVD: bytes[60:92],
155 CreateDate: bytes[92:100],
156 ModifyDate: bytes[100:108],
157 NameScript: bytes[108:110],
158 NameSize: bytes[110:112],
159 Name: bytes[112:nameEnd],
160 CommentSize: bytes[nameEnd : nameEnd+2],
161 Comment: comment,
162 },
163 FlatFileDataForkHeader: FlatFileDataForkHeader{
164 ForkType: bytes[commentEndPos : commentEndPos+4],
165 CompressionType: bytes[commentEndPos+4 : commentEndPos+8],
166 RSVD: bytes[commentEndPos+8 : commentEndPos+12],
167 DataSize: bytes[commentEndPos+12 : commentEndPos+16],
168 },
169 }
170
171 return ffo
172}
173
174func (f flattenedFileObject) Payload() []byte {
175 var out []byte
72dd37f1
JH
176 out = append(out, f.FlatFileHeader.Format[:]...)
177 out = append(out, f.FlatFileHeader.Version[:]...)
178 out = append(out, f.FlatFileHeader.RSVD[:]...)
179 out = append(out, f.FlatFileHeader.ForkCount[:]...)
6988a057
JH
180
181 out = append(out, []byte("INFO")...)
182 out = append(out, []byte{0, 0, 0, 0}...)
183 out = append(out, make([]byte, 4)...)
184 out = append(out, f.FlatFileInformationFork.DataSize()...)
185
186 out = append(out, f.FlatFileInformationFork.Platform...)
187 out = append(out, f.FlatFileInformationFork.TypeSignature...)
188 out = append(out, f.FlatFileInformationFork.CreatorSignature...)
189 out = append(out, f.FlatFileInformationFork.Flags...)
190 out = append(out, f.FlatFileInformationFork.PlatformFlags...)
191 out = append(out, f.FlatFileInformationFork.RSVD...)
192 out = append(out, f.FlatFileInformationFork.CreateDate...)
193 out = append(out, f.FlatFileInformationFork.ModifyDate...)
194 out = append(out, f.FlatFileInformationFork.NameScript...)
195 out = append(out, f.FlatFileInformationFork.ReadNameSize()...)
196 out = append(out, f.FlatFileInformationFork.Name...)
197
198 // TODO: Implement commentlen and comment field
199 out = append(out, []byte{0, 0}...)
200
201 out = append(out, f.FlatFileDataForkHeader.ForkType...)
202 out = append(out, f.FlatFileDataForkHeader.CompressionType...)
203 out = append(out, f.FlatFileDataForkHeader.RSVD...)
204 out = append(out, f.FlatFileDataForkHeader.DataSize...)
205
206 return out
207}
208
72dd37f1 209func NewFlattenedFileObject(filePath, fileName string) (*flattenedFileObject, error) {
6988a057
JH
210 file, err := os.Open(fmt.Sprintf("%v/%v", filePath, fileName))
211 if err != nil {
72dd37f1 212 return nil, err
6988a057
JH
213 }
214 defer file.Close()
215
216 fileInfo, err := file.Stat()
217 if err != nil {
72dd37f1 218 return nil, err
6988a057
JH
219 }
220
221 dataSize := make([]byte, 4)
222 binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size()))
223
72dd37f1 224 return &flattenedFileObject{
6988a057
JH
225 FlatFileHeader: NewFlatFileHeader(),
226 FlatFileInformationFork: NewFlatFileInformationFork(fileName),
227 FlatFileDataForkHeader: FlatFileDataForkHeader{
228 ForkType: []byte("DATA"),
229 CompressionType: []byte{0, 0, 0, 0},
230 RSVD: []byte{0, 0, 0, 0},
231 DataSize: dataSize,
232 },
233 }, nil
234}