]> git.r.bdr.sh - rbdr/mobius/blob - hotline/flattened_file_object.go
Use strings.ReplaceAll method
[rbdr/mobius] / hotline / flattened_file_object.go
1 package hotline
2
3 import (
4 "encoding/binary"
5 "io"
6 )
7
8 type flattenedFileObject struct {
9 FlatFileHeader FlatFileHeader
10 FlatFileInformationForkHeader FlatFileForkHeader
11 FlatFileInformationFork FlatFileInformationFork
12 FlatFileDataForkHeader FlatFileForkHeader
13 FlatFileResForkHeader FlatFileForkHeader
14 }
15
16 // FlatFileHeader is the first section of a "Flattened File Object". All fields have static values.
17 type FlatFileHeader struct {
18 Format [4]byte // Always "FILP"
19 Version [2]byte // Always 1
20 RSVD [16]byte // Always empty zeros
21 ForkCount [2]byte // Number of forks, either 2 or 3 if there is a resource fork
22 }
23
24 type FlatFileInformationFork struct {
25 Platform []byte // Operating System used. ("AMAC" or "MWIN")
26 TypeSignature []byte // File type signature
27 CreatorSignature []byte // File creator signature
28 Flags []byte
29 PlatformFlags []byte
30 RSVD []byte
31 CreateDate []byte
32 ModifyDate []byte
33 NameScript []byte
34 NameSize []byte // Length of file name (Maximum 128 characters)
35 Name []byte // File name
36 CommentSize []byte // Length of the comment
37 Comment []byte // File comment
38 }
39
40 func NewFlatFileInformationFork(fileName string, modifyTime []byte, typeSignature string, creatorSignature string) FlatFileInformationFork {
41 return FlatFileInformationFork{
42 Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?)
43 TypeSignature: []byte(typeSignature), // TODO: Don't infer types from filename
44 CreatorSignature: []byte(creatorSignature), // TODO: Don't infer types from filename
45 Flags: []byte{0, 0, 0, 0}, // TODO: What is this?
46 PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this?
47 RSVD: make([]byte, 32), // Unimplemented in Hotline Protocol
48 CreateDate: modifyTime, // some filesystems don't support createTime
49 ModifyDate: modifyTime,
50 NameScript: make([]byte, 2), // TODO: What is this?
51 Name: []byte(fileName),
52 CommentSize: []byte{0, 0},
53 Comment: []byte{}, // TODO: implement (maybe?)
54 }
55 }
56
57 func (ffif *FlatFileInformationFork) friendlyType() []byte {
58 if name, ok := friendlyCreatorNames[string(ffif.TypeSignature)]; ok {
59 return []byte(name)
60 }
61 return ffif.TypeSignature
62 }
63
64 func (ffif *FlatFileInformationFork) friendlyCreator() []byte {
65 if name, ok := friendlyCreatorNames[string(ffif.CreatorSignature)]; ok {
66 return []byte(name)
67 }
68 return ffif.CreatorSignature
69 }
70
71 func (ffif *FlatFileInformationFork) setComment(comment []byte) error {
72 ffif.CommentSize = make([]byte, 2)
73 ffif.Comment = comment
74 binary.BigEndian.PutUint16(ffif.CommentSize, uint16(len(comment)))
75
76 // TODO: return err if comment is too long
77 return nil
78 }
79
80 // DataSize calculates the size of the flat file information fork, which is
81 // 72 bytes for the fixed length fields plus the length of the Name + Comment
82 func (ffif *FlatFileInformationFork) DataSize() []byte {
83 size := make([]byte, 4)
84
85 dataSize := len(ffif.Name) + len(ffif.Comment) + 74 // 74 = len of fixed size headers
86
87 binary.BigEndian.PutUint32(size, uint32(dataSize))
88
89 return size
90 }
91
92 func (ffif *FlatFileInformationFork) Size() [4]byte {
93 size := [4]byte{}
94
95 dataSize := len(ffif.Name) + len(ffif.Comment) + 74 // 74 = len of fixed size headers
96
97 binary.BigEndian.PutUint32(size[:], uint32(dataSize))
98
99 return size
100 }
101
102 func (ffo *flattenedFileObject) TransferSize(offset int64) []byte {
103 // get length of the flattenedFileObject, including the info fork
104 payloadSize := len(ffo.BinaryMarshal())
105
106 // length of data fork
107 dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:])
108
109 // length of resource fork
110 resForkSize := binary.BigEndian.Uint32(ffo.FlatFileResForkHeader.DataSize[:])
111
112 size := make([]byte, 4)
113 binary.BigEndian.PutUint32(size, dataSize+resForkSize+uint32(payloadSize)-uint32(offset))
114
115 return size
116 }
117
118 func (ffif *FlatFileInformationFork) ReadNameSize() []byte {
119 size := make([]byte, 2)
120 binary.BigEndian.PutUint16(size, uint16(len(ffif.Name)))
121
122 return size
123 }
124
125 type FlatFileForkHeader struct {
126 ForkType [4]byte // Either INFO, DATA or MACR
127 CompressionType [4]byte
128 RSVD [4]byte
129 DataSize [4]byte
130 }
131
132 func (ffif *FlatFileInformationFork) MarshalBinary() []byte {
133 var b []byte
134 b = append(b, ffif.Platform...)
135 b = append(b, ffif.TypeSignature...)
136 b = append(b, ffif.CreatorSignature...)
137 b = append(b, ffif.Flags...)
138 b = append(b, ffif.PlatformFlags...)
139 b = append(b, ffif.RSVD...)
140 b = append(b, ffif.CreateDate...)
141 b = append(b, ffif.ModifyDate...)
142 b = append(b, ffif.NameScript...)
143 b = append(b, ffif.ReadNameSize()...)
144 b = append(b, ffif.Name...)
145 b = append(b, ffif.CommentSize...)
146 b = append(b, ffif.Comment...)
147
148 return b
149 }
150
151 func (ffif *FlatFileInformationFork) UnmarshalBinary(b []byte) error {
152 nameSize := b[70:72]
153 bs := binary.BigEndian.Uint16(nameSize)
154 nameEnd := 72 + bs
155
156 ffif.Platform = b[0:4]
157 ffif.TypeSignature = b[4:8]
158 ffif.CreatorSignature = b[8:12]
159 ffif.Flags = b[12:16]
160 ffif.PlatformFlags = b[16:20]
161 ffif.RSVD = b[20:52]
162 ffif.CreateDate = b[52:60]
163 ffif.ModifyDate = b[60:68]
164 ffif.NameScript = b[68:70]
165 ffif.NameSize = b[70:72]
166 ffif.Name = b[72:nameEnd]
167
168 if len(b) > int(nameEnd) {
169 ffif.CommentSize = b[nameEnd : nameEnd+2]
170 commentLen := binary.BigEndian.Uint16(ffif.CommentSize)
171
172 commentStartPos := int(nameEnd) + 2
173 commentEndPos := int(nameEnd) + 2 + int(commentLen)
174
175 ffif.Comment = b[commentStartPos:commentEndPos]
176 }
177
178 return nil
179 }
180
181 func (ffo *flattenedFileObject) BinaryMarshal() []byte {
182 var out []byte
183 out = append(out, ffo.FlatFileHeader.Format[:]...)
184 out = append(out, ffo.FlatFileHeader.Version[:]...)
185 out = append(out, ffo.FlatFileHeader.RSVD[:]...)
186 out = append(out, ffo.FlatFileHeader.ForkCount[:]...)
187
188 out = append(out, []byte("INFO")...)
189 out = append(out, []byte{0, 0, 0, 0}...)
190 out = append(out, make([]byte, 4)...)
191 out = append(out, ffo.FlatFileInformationFork.DataSize()...)
192
193 out = append(out, ffo.FlatFileInformationFork.Platform...)
194 out = append(out, ffo.FlatFileInformationFork.TypeSignature...)
195 out = append(out, ffo.FlatFileInformationFork.CreatorSignature...)
196 out = append(out, ffo.FlatFileInformationFork.Flags...)
197 out = append(out, ffo.FlatFileInformationFork.PlatformFlags...)
198 out = append(out, ffo.FlatFileInformationFork.RSVD...)
199 out = append(out, ffo.FlatFileInformationFork.CreateDate...)
200 out = append(out, ffo.FlatFileInformationFork.ModifyDate...)
201 out = append(out, ffo.FlatFileInformationFork.NameScript...)
202 out = append(out, ffo.FlatFileInformationFork.ReadNameSize()...)
203 out = append(out, ffo.FlatFileInformationFork.Name...)
204 out = append(out, ffo.FlatFileInformationFork.CommentSize...)
205 out = append(out, ffo.FlatFileInformationFork.Comment...)
206
207 out = append(out, ffo.FlatFileDataForkHeader.ForkType[:]...)
208 out = append(out, ffo.FlatFileDataForkHeader.CompressionType[:]...)
209 out = append(out, ffo.FlatFileDataForkHeader.RSVD[:]...)
210 out = append(out, ffo.FlatFileDataForkHeader.DataSize[:]...)
211
212 return out
213 }
214
215 func (ffo *flattenedFileObject) ReadFrom(r io.Reader) (int, error) {
216 var n int
217
218 if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileHeader); err != nil {
219 return n, err
220 }
221
222 if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileInformationForkHeader); err != nil {
223 return n, err
224 }
225
226 dataLen := binary.BigEndian.Uint32(ffo.FlatFileInformationForkHeader.DataSize[:])
227 ffifBuf := make([]byte, dataLen)
228 if _, err := io.ReadFull(r, ffifBuf); err != nil {
229 return n, err
230 }
231
232 if err := ffo.FlatFileInformationFork.UnmarshalBinary(ffifBuf); err != nil {
233 return n, err
234 }
235
236 if err := binary.Read(r, binary.BigEndian, &ffo.FlatFileDataForkHeader); err != nil {
237 return n, err
238 }
239
240 return n, nil
241 }
242
243 func (ffo *flattenedFileObject) dataSize() int64 {
244 return int64(binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:]))
245 }
246
247 func (ffo *flattenedFileObject) rsrcSize() int64 {
248 return int64(binary.BigEndian.Uint32(ffo.FlatFileResForkHeader.DataSize[:]))
249 }