From e61b3160bc8936b7ffb0af5c89b13742a7081826 Mon Sep 17 00:00:00 2001 From: Jeff Halter <868228+jhalter@users.noreply.github.com> Date: Sun, 6 Jul 2025 16:02:53 -0700 Subject: Add ForkType type alias for improved type safety - Replace [4]byte with ForkType in ForkInfoList struct - Update constants ForkTypeDATA and ForkTypeMACR to use ForkType - Add String() method to ForkType for better debugging - Improve consistency with existing type patterns (FieldType, TranType) - Clean up unused code and improve documentation --- hotline/file_resume_data.go | 57 +++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 33 deletions(-) (limited to 'hotline/file_resume_data.go') diff --git a/hotline/file_resume_data.go b/hotline/file_resume_data.go index 5dfd2d8..80f5fe5 100644 --- a/hotline/file_resume_data.go +++ b/hotline/file_resume_data.go @@ -3,38 +3,54 @@ package hotline import ( "bytes" "encoding/binary" + "fmt" ) // FileResumeData is sent when a client or server would like to resume a transfer from an offset type FileResumeData struct { Format [4]byte // "RFLT" Version [2]byte // Always 1 - RSVD [34]byte // Unused + RSVD [34]byte // Present in the Hotline protocol docs, but unused. Left here for documentation purposes. ForkCount [2]byte // Length of ForkInfoList. Either 2 or 3 depending on whether file has a resource fork ForkInfoList []ForkInfoList +} + +// ForkType represents a 4-byte fork type identifier +type ForkType [4]byte - //readOffset int // TODO +// String returns a string representation of the fork type +func (ft ForkType) String() string { + return fmt.Sprintf("%c%c%c%c", ft[0], ft[1], ft[2], ft[3]) } type ForkInfoList struct { - Fork [4]byte // "DATA" or "MACR" - DataSize [4]byte // offset from which to resume the transfer of data - RSVDA [4]byte // Unused - RSVDB [4]byte // Unused + Fork ForkType // "DATA" or "MACR" + DataSize [4]byte // offset from which to resume the transfer of data + RSVDA [4]byte // Present in the Hotline protocol docs, but unused. Left here for documentation purposes. + RSVDB [4]byte // Present in the Hotline protocol docs, but unused. Left here for documentation purposes. } +var ( + ForkTypeDATA = ForkType{0x44, 0x41, 0x54, 0x41} // DATA: Data fork + ForkTypeMACR = ForkType{0x4d, 0x41, 0x43, 0x52} // MACR: Mac resource fork +) + func NewForkInfoList(b []byte) *ForkInfoList { return &ForkInfoList{ - Fork: [4]byte{0x44, 0x41, 0x54, 0x41}, + Fork: ForkTypeDATA, DataSize: [4]byte{b[0], b[1], b[2], b[3]}, RSVDA: [4]byte{}, RSVDB: [4]byte{}, } } +var ( + FormatRFLT = [4]byte{0x52, 0x46, 0x4C, 0x54} // File resume format: "RFLT" (?) +) + func NewFileResumeData(list []ForkInfoList) *FileResumeData { return &FileResumeData{ - Format: [4]byte{0x52, 0x46, 0x4C, 0x54}, // RFLT + Format: FormatRFLT, Version: [2]byte{0, 1}, RSVD: [34]byte{}, ForkCount: [2]byte{0, uint8(len(list))}, @@ -42,31 +58,6 @@ func NewFileResumeData(list []ForkInfoList) *FileResumeData { } } -// -//func (frd *FileResumeData) Read(p []byte) (int, error) { -// buf := slices.Concat( -// frd.Format[:], -// frd.Version[:], -// frd.RSVD[:], -// frd.ForkCount[:], -// ) -// for _, fil := range frd.ForkInfoList { -// buf = append(buf, fil...) -// _ = binary.Write(&buf, binary.LittleEndian, fil) -// } -// -// var buf bytes.Buffer -// _ = binary.Write(&buf, binary.LittleEndian, frd.Format) -// _ = binary.Write(&buf, binary.LittleEndian, frd.Version) -// _ = binary.Write(&buf, binary.LittleEndian, frd.RSVD) -// _ = binary.Write(&buf, binary.LittleEndian, frd.ForkCount) -// for _, fil := range frd.ForkInfoList { -// _ = binary.Write(&buf, binary.LittleEndian, fil) -// } -// -// return buf.Bytes(), nil -//} - func (frd *FileResumeData) BinaryMarshal() ([]byte, error) { var buf bytes.Buffer _ = binary.Write(&buf, binary.LittleEndian, frd.Format) -- cgit From 489e26d1b3c224beffba2649406efd83580989fe Mon Sep 17 00:00:00 2001 From: Jeff Halter <868228+jhalter@users.noreply.github.com> Date: Tue, 18 Nov 2025 20:56:12 -0800 Subject: Replace hardcoded magic values with named constants and improve type safety Adds new constants for file format identifiers (FormatFILP), fork types (ForkTypeINFO), and platform identifiers (PlatformAMAC, PlatformMWIN) to improve code maintainability and readability. Changes FlatFileForkHeader.ForkType field from [4]byte to ForkType type alias for better compile-time type checking, matching the pattern used in ForkInfoList.Fork. --- hotline/file_resume_data.go | 5 +++++ hotline/file_wrapper.go | 8 ++++---- hotline/flattened_file_object.go | 4 ++-- hotline/transfer_test.go | 4 ++-- internal/mobius/transaction_handlers_test.go | 6 +++--- 5 files changed, 16 insertions(+), 11 deletions(-) (limited to 'hotline/file_resume_data.go') diff --git a/hotline/file_resume_data.go b/hotline/file_resume_data.go index 80f5fe5..c9f27f6 100644 --- a/hotline/file_resume_data.go +++ b/hotline/file_resume_data.go @@ -32,6 +32,7 @@ type ForkInfoList struct { var ( ForkTypeDATA = ForkType{0x44, 0x41, 0x54, 0x41} // DATA: Data fork + ForkTypeINFO = ForkType{0x49, 0x4E, 0x46, 0x4F} // INFO: Information fork ForkTypeMACR = ForkType{0x4d, 0x41, 0x43, 0x52} // MACR: Mac resource fork ) @@ -45,7 +46,11 @@ func NewForkInfoList(b []byte) *ForkInfoList { } var ( + FormatFILP = [4]byte{0x46, 0x49, 0x4c, 0x50} // Flattened file format: "FILP" FormatRFLT = [4]byte{0x52, 0x46, 0x4C, 0x54} // File resume format: "RFLT" (?) + + PlatformAMAC = [4]byte{0x41, 0x4D, 0x41, 0x43} // Mac platform: "AMAC" + PlatformMWIN = [4]byte{0x4D, 0x57, 0x49, 0x4E} // Windows platform: "MWIN" ) func NewFileResumeData(list []ForkInfoList) *FileResumeData { diff --git a/hotline/file_wrapper.go b/hotline/file_wrapper.go index b6aff94..5ec99c3 100644 --- a/hotline/file_wrapper.go +++ b/hotline/file_wrapper.go @@ -227,7 +227,7 @@ func (f *fileWrapper) flattenedFileObject() (*flattenedFileObject, error) { } f.Ffo.FlatFileHeader = FlatFileHeader{ - Format: [4]byte{0x46, 0x49, 0x4c, 0x50}, // "FILP" + Format: FormatFILP, Version: [2]byte{0, 1}, ForkCount: [2]byte{0, 2}, } @@ -247,7 +247,7 @@ func (f *fileWrapper) flattenedFileObject() (*flattenedFileObject, error) { } } else { f.Ffo.FlatFileInformationFork = FlatFileInformationFork{ - Platform: [4]byte{0x41, 0x4D, 0x41, 0x43}, // "AMAC" TODO: Remove hardcode to support "AWIN" Platform (maybe?) + Platform: PlatformAMAC, // TODO: Remove hardcode to support "MWIN" Platform (maybe?) TypeSignature: [4]byte([]byte(ft.TypeCode)), CreatorSignature: [4]byte([]byte(ft.CreatorCode)), PlatformFlags: [4]byte{0, 0, 1, 0}, // TODO: What is this? @@ -263,12 +263,12 @@ func (f *fileWrapper) flattenedFileObject() (*flattenedFileObject, error) { } f.Ffo.FlatFileInformationForkHeader = FlatFileForkHeader{ - ForkType: [4]byte{0x49, 0x4E, 0x46, 0x4F}, // "INFO" + ForkType: ForkTypeINFO, DataSize: f.Ffo.FlatFileInformationFork.Size(), } f.Ffo.FlatFileDataForkHeader = FlatFileForkHeader{ - ForkType: [4]byte{0x44, 0x41, 0x54, 0x41}, // "DATA" + ForkType: ForkTypeDATA, DataSize: [4]byte{dataSize[0], dataSize[1], dataSize[2], dataSize[3]}, } f.Ffo.FlatFileResForkHeader = f.rsrcForkHeader() diff --git a/hotline/flattened_file_object.go b/hotline/flattened_file_object.go index 99ce204..7c0cd7d 100644 --- a/hotline/flattened_file_object.go +++ b/hotline/flattened_file_object.go @@ -45,7 +45,7 @@ type FlatFileInformationFork struct { func NewFlatFileInformationFork(fileName string, modifyTime [8]byte, typeSignature string, creatorSignature string) FlatFileInformationFork { return FlatFileInformationFork{ - Platform: [4]byte{0x41, 0x4D, 0x41, 0x43}, // "AMAC" TODO: Remove hardcode to support "AWIN" Platform (maybe?) + Platform: PlatformAMAC, // TODO: Remove hardcode to support "MWIN" Platform (maybe?) TypeSignature: [4]byte([]byte(typeSignature)), // TODO: Don't infer types from filename CreatorSignature: [4]byte([]byte(creatorSignature)), // TODO: Don't infer types from filename PlatformFlags: [4]byte{0, 0, 1, 0}, // TODO: What is this? @@ -128,7 +128,7 @@ func (ffif *FlatFileInformationFork) ReadNameSize() []byte { } type FlatFileForkHeader struct { - ForkType [4]byte // Either INFO, DATA or MACR + ForkType ForkType // Either INFO, DATA or MACR CompressionType [4]byte RSVD [4]byte DataSize [4]byte diff --git a/hotline/transfer_test.go b/hotline/transfer_test.go index 4aa142b..c915498 100644 --- a/hotline/transfer_test.go +++ b/hotline/transfer_test.go @@ -120,14 +120,14 @@ func Test_receiveFile(t *testing.T) { conn: func() io.Reader { testFile := flattenedFileObject{ FlatFileHeader: FlatFileHeader{ - Format: [4]byte{0x46, 0x49, 0x4c, 0x50}, // "FILP" + Format: FormatFILP, Version: [2]byte{0, 1}, ForkCount: [2]byte{0, 2}, }, FlatFileInformationForkHeader: FlatFileForkHeader{}, FlatFileInformationFork: NewFlatFileInformationFork("testfile.txt", [8]byte{}, "TEXT", "TEXT"), FlatFileDataForkHeader: FlatFileForkHeader{ - ForkType: [4]byte{0x4d, 0x41, 0x43, 0x52}, // DATA + ForkType: ForkTypeMACR, DataSize: [4]byte{0x00, 0x00, 0x00, 0x03}, }, } diff --git a/internal/mobius/transaction_handlers_test.go b/internal/mobius/transaction_handlers_test.go index 1c3fac0..d943649 100644 --- a/internal/mobius/transaction_handlers_test.go +++ b/internal/mobius/transaction_handlers_test.go @@ -1917,11 +1917,11 @@ func TestHandleDownloadFile(t *testing.T) { ForkCount: [2]byte{0, 2}, ForkInfoList: []hotline.ForkInfoList{ { - Fork: [4]byte{0x44, 0x41, 0x54, 0x41}, // "DATA" - DataSize: [4]byte{0, 0, 0x01, 0x00}, // request offset 256 + Fork: hotline.ForkTypeDATA, + DataSize: [4]byte{0, 0, 0x01, 0x00}, // request offset 256 }, { - Fork: [4]byte{0x4d, 0x41, 0x43, 0x52}, // "MACR" + Fork: hotline.ForkTypeMACR, DataSize: [4]byte{0, 0, 0, 0}, }, }, -- cgit From b395859da4937fa4c3064f4e022e46690df8f2d4 Mon Sep 17 00:00:00 2001 From: Jeff Halter <868228+jhalter@users.noreply.github.com> Date: Tue, 18 Nov 2025 21:01:55 -0800 Subject: Rename hotline.fileWrapper struct to hotline.File --- hotline/file_resume_data.go | 2 +- hotline/file_wrapper.go | 38 +++++++++++++++++++------------------- hotline/transfer_test.go | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'hotline/file_resume_data.go') diff --git a/hotline/file_resume_data.go b/hotline/file_resume_data.go index c9f27f6..1926bc6 100644 --- a/hotline/file_resume_data.go +++ b/hotline/file_resume_data.go @@ -24,7 +24,7 @@ func (ft ForkType) String() string { } type ForkInfoList struct { - Fork ForkType // "DATA" or "MACR" + Fork ForkType // "DATA", "INFO", or "MACR" DataSize [4]byte // offset from which to resume the transfer of data RSVDA [4]byte // Present in the Hotline protocol docs, but unused. Left here for documentation purposes. RSVDB [4]byte // Present in the Hotline protocol docs, but unused. Left here for documentation purposes. diff --git a/hotline/file_wrapper.go b/hotline/file_wrapper.go index 5ec99c3..41b3338 100644 --- a/hotline/file_wrapper.go +++ b/hotline/file_wrapper.go @@ -17,8 +17,8 @@ const ( RsrcForkNameTemplate = ".rsrc_%s" // template string for resource fork filenames ) -// fileWrapper encapsulates the data, info, and resource forks of a Hotline file and provides methods to manage the files. -type fileWrapper struct { +// File encapsulates the data, info, and resource forks of a Hotline file and provides methods to manage the files. +type File struct { fs FileStore Name string // Name of the file path string // path to file directory @@ -30,10 +30,10 @@ type fileWrapper struct { Ffo *flattenedFileObject } -func NewFileWrapper(fs FileStore, path string, dataOffset int64) (*fileWrapper, error) { +func NewFileWrapper(fs FileStore, path string, dataOffset int64) (*File, error) { dir := filepath.Dir(path) fName := filepath.Base(path) - f := fileWrapper{ + f := File{ fs: fs, Name: fName, path: dir, @@ -54,7 +54,7 @@ func NewFileWrapper(fs FileStore, path string, dataOffset int64) (*fileWrapper, return &f, nil } -func (f *fileWrapper) TotalSize() []byte { +func (f *File) TotalSize() []byte { var s int64 size := make([]byte, 4) @@ -73,7 +73,7 @@ func (f *fileWrapper) TotalSize() []byte { return size } -func (f *fileWrapper) rsrcForkSize() (s [4]byte) { +func (f *File) rsrcForkSize() (s [4]byte) { info, err := f.fs.Stat(f.rsrcPath) if err != nil { return s @@ -83,26 +83,26 @@ func (f *fileWrapper) rsrcForkSize() (s [4]byte) { return s } -func (f *fileWrapper) rsrcForkHeader() FlatFileForkHeader { +func (f *File) rsrcForkHeader() FlatFileForkHeader { return FlatFileForkHeader{ ForkType: ForkTypeMACR, DataSize: f.rsrcForkSize(), } } -func (f *fileWrapper) incompleteDataName() string { +func (f *File) incompleteDataName() string { return f.Name + IncompleteFileSuffix } -func (f *fileWrapper) rsrcForkName() string { +func (f *File) rsrcForkName() string { return fmt.Sprintf(RsrcForkNameTemplate, f.Name) } -func (f *fileWrapper) infoForkName() string { +func (f *File) infoForkName() string { return fmt.Sprintf(InfoForkNameTemplate, f.Name) } -func (f *fileWrapper) rsrcForkWriter() (io.WriteCloser, error) { +func (f *File) rsrcForkWriter() (io.WriteCloser, error) { file, err := os.OpenFile(f.rsrcPath, os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nil, err @@ -111,7 +111,7 @@ func (f *fileWrapper) rsrcForkWriter() (io.WriteCloser, error) { return file, nil } -func (f *fileWrapper) InfoForkWriter() (io.WriteCloser, error) { +func (f *File) InfoForkWriter() (io.WriteCloser, error) { file, err := os.OpenFile(f.infoPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return nil, err @@ -120,7 +120,7 @@ func (f *fileWrapper) InfoForkWriter() (io.WriteCloser, error) { return file, nil } -func (f *fileWrapper) incFileWriter() (io.WriteCloser, error) { +func (f *File) incFileWriter() (io.WriteCloser, error) { file, err := os.OpenFile(f.incompletePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return nil, err @@ -129,15 +129,15 @@ func (f *fileWrapper) incFileWriter() (io.WriteCloser, error) { return file, nil } -func (f *fileWrapper) dataForkReader() (io.Reader, error) { +func (f *File) dataForkReader() (io.Reader, error) { return f.fs.Open(f.dataPath) } -func (f *fileWrapper) rsrcForkFile() (*os.File, error) { +func (f *File) rsrcForkFile() (*os.File, error) { return f.fs.Open(f.rsrcPath) } -func (f *fileWrapper) DataFile() (os.FileInfo, error) { +func (f *File) DataFile() (os.FileInfo, error) { if fi, err := f.fs.Stat(f.dataPath); err == nil { return fi, nil } @@ -154,7 +154,7 @@ func (f *fileWrapper) DataFile() (os.FileInfo, error) { // * Resource fork starting with .rsrc_ // * Info fork starting with .info // During Move of the meta files, os.ErrNotExist is ignored as these files may legitimately not exist. -func (f *fileWrapper) Move(newPath string) error { +func (f *File) Move(newPath string) error { err := f.fs.Rename(f.dataPath, filepath.Join(newPath, f.Name)) if err != nil { return err @@ -179,7 +179,7 @@ func (f *fileWrapper) Move(newPath string) error { } // Delete a file and its associated metadata files if they exist -func (f *fileWrapper) Delete() error { +func (f *File) Delete() error { err := f.fs.RemoveAll(f.dataPath) if err != nil { return err @@ -203,7 +203,7 @@ func (f *fileWrapper) Delete() error { return nil } -func (f *fileWrapper) flattenedFileObject() (*flattenedFileObject, error) { +func (f *File) flattenedFileObject() (*flattenedFileObject, error) { dataSize := make([]byte, 4) mTime := [8]byte{} diff --git a/hotline/transfer_test.go b/hotline/transfer_test.go index c915498..9155ad7 100644 --- a/hotline/transfer_test.go +++ b/hotline/transfer_test.go @@ -143,7 +143,7 @@ func Test_receiveFile(t *testing.T) { wantErr: assert.NoError, }, // { - // Name: "transfers fileWrapper when there is a resource fork", + // Name: "transfers File when there is a resource fork", // args: args{ // conn: func() io.Reader { // testFile := flattenedFileObject{ -- cgit