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