import (
"encoding/binary"
- "fmt"
"os"
)
// FlatFileHeader is the first section of a "Flattened File Object". All fields have static values.
type FlatFileHeader struct {
- Format []byte // Always "FILP"
- Version []byte // Always 1
- RSVD []byte // Always empty zeros
- ForkCount []byte // Always 2
+ Format [4]byte // Always "FILP"
+ Version [2]byte // Always 1
+ RSVD [16]byte // Always empty zeros
+ ForkCount [2]byte // Always 2
}
// NewFlatFileHeader returns a FlatFileHeader struct
func NewFlatFileHeader() FlatFileHeader {
return FlatFileHeader{
- Format: []byte("FILP"),
- Version: []byte{0, 1},
- RSVD: make([]byte, 16),
- ForkCount: []byte{0, 2},
+ Format: [4]byte{0x46, 0x49, 0x4c, 0x50}, // FILP
+ Version: [2]byte{0, 1},
+ RSVD: [16]byte{},
+ ForkCount: [2]byte{0, 2},
}
}
Comment []byte // File comment
}
-func NewFlatFileInformationFork(fileName string) FlatFileInformationFork {
+func NewFlatFileInformationFork(fileName string, modifyTime []byte) FlatFileInformationFork {
return FlatFileInformationFork{
- Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?)
- TypeSignature: []byte(fileTypeFromFilename(fileName)), // TODO: Don't infer types from filename
- CreatorSignature: []byte(fileCreatorFromFilename(fileName)), // TODO: Don't infer types from filename
- Flags: []byte{0, 0, 0, 0}, // TODO: What is this?
- PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this?
- RSVD: make([]byte, 32), // Unimplemented in Hotline Protocol
- CreateDate: []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}, // TODO: implement
- ModifyDate: []byte{0x07, 0x70, 0x00, 0x00, 0xba, 0x74, 0x24, 0x73}, // TODO: implement
- NameScript: make([]byte, 2), // TODO: What is this?
+ Platform: []byte("AMAC"), // TODO: Remove hardcode to support "AWIN" Platform (maybe?)
+ TypeSignature: []byte(fileTypeFromFilename(fileName).TypeCode), // TODO: Don't infer types from filename
+ CreatorSignature: []byte(fileTypeFromFilename(fileName).CreatorCode), // TODO: Don't infer types from filename
+ Flags: []byte{0, 0, 0, 0}, // TODO: What is this?
+ PlatformFlags: []byte{0, 0, 1, 0}, // TODO: What is this?
+ RSVD: make([]byte, 32), // Unimplemented in Hotline Protocol
+ CreateDate: modifyTime, // some filesystems don't support createTime
+ ModifyDate: modifyTime,
+ NameScript: make([]byte, 2), // TODO: What is this?
Name: []byte(fileName),
- Comment: []byte("TODO"), // TODO: implement (maybe?)
+ CommentSize: []byte{0, 0},
+ Comment: []byte{}, // TODO: implement (maybe?)
}
}
-// Size of the flat file information fork, which is the fixed size of 72 bytes
-// plus the number of bytes in the FileName
-// TODO: plus the size of the Comment!
+// DataSize calculates the size of the flat file information fork, which is
+// 72 bytes for the fixed length fields plus the length of the Name + Comment
func (ffif FlatFileInformationFork) DataSize() []byte {
size := make([]byte, 4)
- nameLen := len(ffif.Name)
- //TODO: Can I do math directly on two byte slices?
- dataSize := nameLen + 74
+
+ // TODO: Can I do math directly on two byte slices?
+ dataSize := len(ffif.Name) + len(ffif.Comment) + 74
binary.BigEndian.PutUint32(size, uint32(dataSize))
}
func (ffo flattenedFileObject) TransferSize() []byte {
- payloadSize := len(ffo.Payload())
+ payloadSize := len(ffo.BinaryMarshal())
dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize)
transferSize := make([]byte, 4)
DataSize []byte
}
-func NewFlatFileDataForkHeader() FlatFileDataForkHeader {
- return FlatFileDataForkHeader{
- ForkType: []byte("DATA"),
- CompressionType: []byte{0, 0, 0, 0},
- RSVD: []byte{0, 0, 0, 0},
- // DataSize: []byte{0, 0, 0x03, 0xc3},
- }
-}
-
// ReadFlattenedFileObject parses a byte slice into a flattenedFileObject
func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
nameSize := bytes[110:112]
comment := bytes[commentStartPos:commentEndPos]
- //dataSizeField := bytes[nameEnd+14+commentLen : nameEnd+18+commentLen]
- //dataSize := binary.BigEndian.Uint32(dataSizeField)
+ // dataSizeField := bytes[nameEnd+14+commentLen : nameEnd+18+commentLen]
+ // dataSize := binary.BigEndian.Uint32(dataSizeField)
ffo := flattenedFileObject{
- FlatFileHeader: FlatFileHeader{
- Format: bytes[0:4],
- Version: bytes[4:6],
- RSVD: bytes[6:22],
- ForkCount: bytes[22:24],
- },
+ FlatFileHeader: NewFlatFileHeader(),
FlatFileInformationForkHeader: FlatFileInformationForkHeader{
ForkType: bytes[24:28],
CompressionType: bytes[28:32],
return ffo
}
-func (f flattenedFileObject) Payload() []byte {
+func (f flattenedFileObject) BinaryMarshal() []byte {
var out []byte
- out = append(out, f.FlatFileHeader.Format...)
- out = append(out, f.FlatFileHeader.Version...)
- out = append(out, f.FlatFileHeader.RSVD...)
- out = append(out, f.FlatFileHeader.ForkCount...)
+ out = append(out, f.FlatFileHeader.Format[:]...)
+ out = append(out, f.FlatFileHeader.Version[:]...)
+ out = append(out, f.FlatFileHeader.RSVD[:]...)
+ out = append(out, f.FlatFileHeader.ForkCount[:]...)
out = append(out, []byte("INFO")...)
out = append(out, []byte{0, 0, 0, 0}...)
out = append(out, f.FlatFileInformationFork.NameScript...)
out = append(out, f.FlatFileInformationFork.ReadNameSize()...)
out = append(out, f.FlatFileInformationFork.Name...)
-
- // TODO: Implement commentlen and comment field
- out = append(out, []byte{0, 0}...)
+ out = append(out, f.FlatFileInformationFork.CommentSize...)
+ out = append(out, f.FlatFileInformationFork.Comment...)
out = append(out, f.FlatFileDataForkHeader.ForkType...)
out = append(out, f.FlatFileDataForkHeader.CompressionType...)
return out
}
-func NewFlattenedFileObject(filePath string, fileName string) (flattenedFileObject, error) {
- file, err := os.Open(fmt.Sprintf("%v/%v", filePath, fileName))
+func NewFlattenedFileObject(fileRoot string, filePath, fileName []byte) (*flattenedFileObject, error) {
+ fullFilePath, err := readPath(fileRoot, filePath, fileName)
+ if err != nil {
+ return nil, err
+ }
+ file, err := os.Open(fullFilePath)
if err != nil {
- return flattenedFileObject{}, err
+ return nil, err
}
- defer file.Close()
+ defer func(file *os.File) { _ = file.Close() }(file)
fileInfo, err := file.Stat()
if err != nil {
- return flattenedFileObject{}, err
+ return nil, err
}
dataSize := make([]byte, 4)
binary.BigEndian.PutUint32(dataSize, uint32(fileInfo.Size()))
- return flattenedFileObject{
+ mTime := toHotlineTime(fileInfo.ModTime())
+
+ return &flattenedFileObject{
FlatFileHeader: NewFlatFileHeader(),
- FlatFileInformationFork: NewFlatFileInformationFork(fileName),
+ FlatFileInformationFork: NewFlatFileInformationFork(string(fileName), mTime),
FlatFileDataForkHeader: FlatFileDataForkHeader{
ForkType: []byte("DATA"),
CompressionType: []byte{0, 0, 0, 0},