]> git.r.bdr.sh - rbdr/mobius/blame - hotline/flattened_file_object.go
Merge pull request #39 from benabernathy/mobius_38
[rbdr/mobius] / hotline / flattened_file_object.go
CommitLineData
6988a057
JH
1package hotline
2
3import (
4 "encoding/binary"
6988a057
JH
5 "os"
6)
7
8type flattenedFileObject struct {
9 FlatFileHeader FlatFileHeader
10 FlatFileInformationForkHeader FlatFileInformationForkHeader
11 FlatFileInformationFork FlatFileInformationFork
12 FlatFileDataForkHeader FlatFileDataForkHeader
13 FileData []byte
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
85767504 21 ForkCount [2]byte // Number of forks
6988a057
JH
22}
23
24// NewFlatFileHeader returns a FlatFileHeader struct
25func NewFlatFileHeader() FlatFileHeader {
26 return FlatFileHeader{
72dd37f1
JH
27 Format: [4]byte{0x46, 0x49, 0x4c, 0x50}, // FILP
28 Version: [2]byte{0, 1},
29 RSVD: [16]byte{},
30 ForkCount: [2]byte{0, 2},
6988a057
JH
31 }
32}
33
34// FlatFileInformationForkHeader is the second section of a "Flattened File Object"
35type FlatFileInformationForkHeader struct {
85767504
JH
36 ForkType [4]byte // Always "INFO"
37 CompressionType [4]byte // Always 0; Compression was never implemented in the Hotline protocol
38 RSVD [4]byte // Always zeros
39 DataSize [4]byte // Size of the flat file information fork
6988a057
JH
40}
41
42type FlatFileInformationFork struct {
43 Platform []byte // Operating System used. ("AMAC" or "MWIN")
44 TypeSignature []byte // File type signature
45 CreatorSignature []byte // File creator signature
46 Flags []byte
47 PlatformFlags []byte
48 RSVD []byte
49 CreateDate []byte
50 ModifyDate []byte
51 NameScript []byte // TODO: what is this?
52 NameSize []byte // Length of file name (Maximum 128 characters)
53 Name []byte // File name
54 CommentSize []byte // Length of file comment
55 Comment []byte // File comment
56}
57
2d52424e 58func NewFlatFileInformationFork(fileName string, modifyTime []byte, typeSignature string, creatorSignature string) FlatFileInformationFork {
6988a057 59 return FlatFileInformationFork{
2d52424e
JH
60 Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?)
61 TypeSignature: []byte(typeSignature), // TODO: Don't infer types from filename
62 CreatorSignature: []byte(creatorSignature), // TODO: Don't infer types from filename
63 Flags: []byte{0, 0, 0, 0}, // TODO: What is this?
64 PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this?
65 RSVD: make([]byte, 32), // Unimplemented in Hotline Protocol
66 CreateDate: modifyTime, // some filesystems don't support createTime
29f329ae
JH
67 ModifyDate: modifyTime,
68 NameScript: make([]byte, 2), // TODO: What is this?
6988a057 69 Name: []byte(fileName),
5218c782
JH
70 CommentSize: []byte{0, 0},
71 Comment: []byte{}, // TODO: implement (maybe?)
6988a057
JH
72 }
73}
74
2d52424e
JH
75func (ffif *FlatFileInformationFork) friendlyType() []byte {
76
77 if name, ok := friendlyCreatorNames[string(ffif.TypeSignature)]; ok {
78 return []byte(name)
79 }
80 return ffif.CreatorSignature
81}
82
bb7fe19f
JH
83// DataSize calculates the size of the flat file information fork, which is
84// 72 bytes for the fixed length fields plus the length of the Name + Comment
85767504 85func (ffif *FlatFileInformationFork) DataSize() []byte {
6988a057 86 size := make([]byte, 4)
bb7fe19f 87
aebc4d36 88 // TODO: Can I do math directly on two byte slices?
bb7fe19f 89 dataSize := len(ffif.Name) + len(ffif.Comment) + 74
6988a057
JH
90
91 binary.BigEndian.PutUint32(size, uint32(dataSize))
92
93 return size
94}
95
85767504 96func (ffo *flattenedFileObject) TransferSize() []byte {
c5d9af5a 97 payloadSize := len(ffo.BinaryMarshal())
85767504 98 dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:])
6988a057
JH
99
100 transferSize := make([]byte, 4)
101 binary.BigEndian.PutUint32(transferSize, dataSize+uint32(payloadSize))
102
103 return transferSize
104}
105
85767504 106func (ffif *FlatFileInformationFork) ReadNameSize() []byte {
6988a057
JH
107 size := make([]byte, 2)
108 binary.BigEndian.PutUint16(size, uint16(len(ffif.Name)))
109
110 return size
111}
112
113type FlatFileDataForkHeader struct {
85767504
JH
114 ForkType [4]byte
115 CompressionType [4]byte
116 RSVD [4]byte
117 DataSize [4]byte
6988a057
JH
118}
119
85767504 120func (ffif *FlatFileInformationFork) UnmarshalBinary(b []byte) error {
85767504 121 nameSize := b[70:72]
6988a057 122 bs := binary.BigEndian.Uint16(nameSize)
85767504 123 nameEnd := 72 + bs
6988a057 124
85767504
JH
125 ffif.Platform = b[0:4]
126 ffif.TypeSignature = b[4:8]
127 ffif.CreatorSignature = b[8:12]
128 ffif.Flags = b[12:16]
129 ffif.PlatformFlags = b[16:20]
130 ffif.RSVD = b[20:52]
131 ffif.CreateDate = b[52:60]
132 ffif.ModifyDate = b[60:68]
133 ffif.NameScript = b[68:70]
134 ffif.NameSize = b[70:72]
135 ffif.Name = b[72:nameEnd]
050407a3
JH
136
137 if len(b) > int(nameEnd) {
138 ffif.CommentSize = b[nameEnd : nameEnd+2]
139 commentLen := binary.BigEndian.Uint16(ffif.CommentSize)
140
141 commentStartPos := int(nameEnd) + 2
142 commentEndPos := int(nameEnd) + 2 + int(commentLen)
143
144 ffif.Comment = b[commentStartPos:commentEndPos]
145 }
85767504
JH
146
147 return nil
6988a057
JH
148}
149
85767504 150func (f *flattenedFileObject) BinaryMarshal() []byte {
6988a057 151 var out []byte
72dd37f1
JH
152 out = append(out, f.FlatFileHeader.Format[:]...)
153 out = append(out, f.FlatFileHeader.Version[:]...)
154 out = append(out, f.FlatFileHeader.RSVD[:]...)
155 out = append(out, f.FlatFileHeader.ForkCount[:]...)
6988a057
JH
156
157 out = append(out, []byte("INFO")...)
158 out = append(out, []byte{0, 0, 0, 0}...)
159 out = append(out, make([]byte, 4)...)
160 out = append(out, f.FlatFileInformationFork.DataSize()...)
161
162 out = append(out, f.FlatFileInformationFork.Platform...)
163 out = append(out, f.FlatFileInformationFork.TypeSignature...)
164 out = append(out, f.FlatFileInformationFork.CreatorSignature...)
165 out = append(out, f.FlatFileInformationFork.Flags...)
166 out = append(out, f.FlatFileInformationFork.PlatformFlags...)
167 out = append(out, f.FlatFileInformationFork.RSVD...)
168 out = append(out, f.FlatFileInformationFork.CreateDate...)
169 out = append(out, f.FlatFileInformationFork.ModifyDate...)
170 out = append(out, f.FlatFileInformationFork.NameScript...)
171 out = append(out, f.FlatFileInformationFork.ReadNameSize()...)
172 out = append(out, f.FlatFileInformationFork.Name...)
bb7fe19f
JH
173 out = append(out, f.FlatFileInformationFork.CommentSize...)
174 out = append(out, f.FlatFileInformationFork.Comment...)
6988a057 175
85767504
JH
176 out = append(out, f.FlatFileDataForkHeader.ForkType[:]...)
177 out = append(out, f.FlatFileDataForkHeader.CompressionType[:]...)
178 out = append(out, f.FlatFileDataForkHeader.RSVD[:]...)
179 out = append(out, f.FlatFileDataForkHeader.DataSize[:]...)
6988a057
JH
180
181 return out
182}
183
16a4ad70 184func NewFlattenedFileObject(fileRoot string, filePath, fileName []byte, dataOffset int64) (*flattenedFileObject, error) {
92a7e455
JH
185 fullFilePath, err := readPath(fileRoot, filePath, fileName)
186 if err != nil {
187 return nil, err
188 }
16a4ad70 189 file, err := effectiveFile(fullFilePath)
6988a057 190 if err != nil {
72dd37f1 191 return nil, err
6988a057 192 }
16a4ad70 193
29f329ae 194 defer func(file *os.File) { _ = file.Close() }(file)
6988a057
JH
195
196 fileInfo, err := file.Stat()
197 if err != nil {
72dd37f1 198 return nil, err
6988a057
JH
199 }
200
201 dataSize := make([]byte, 4)
16a4ad70 202 binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size()-dataOffset))
6988a057 203
29f329ae
JH
204 mTime := toHotlineTime(fileInfo.ModTime())
205
2d52424e
JH
206 ft, _ := fileTypeFromInfo(fileInfo)
207
72dd37f1 208 return &flattenedFileObject{
6988a057 209 FlatFileHeader: NewFlatFileHeader(),
2d52424e 210 FlatFileInformationFork: NewFlatFileInformationFork(string(fileName), mTime, ft.TypeCode, ft.CreatorCode),
6988a057 211 FlatFileDataForkHeader: FlatFileDataForkHeader{
85767504
JH
212 ForkType: [4]byte{0x44, 0x41, 0x54, 0x41}, // "DATA"
213 CompressionType: [4]byte{},
214 RSVD: [4]byte{},
215 DataSize: [4]byte{dataSize[0], dataSize[1], dataSize[2], dataSize[3]},
6988a057
JH
216 },
217 }, nil
218}