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