package cpe import ( "bufio" "encoding/binary" "errors" "fmt" "io" "os" ) type CPEFileSection struct { Name string Size int Data []byte } type CPEMetadata struct { Segments map[string]CPEFileSection } type CPEReader struct { FileHandle *os.File streamReader *bufio.Reader cursorOffset uint32 Metadata CPEMetadata //h header } const ( CPE_FILE_IDENTIFIER string = "CPDFile" CPE_FILE_IDENTIFIER_SIZE int = 7 CPE_FILE_VER int = 2 CPE_FILE_VER_SIZE int = 1 CPE_ENCRYPTED_FILE_VER int = 3 CPE_FILE_SUBVER int = 0 CPE_FILE_SUBVER_SIZE int = 1 CPE_FILE_HEADER_LENGTH int = 9 CPE_SECTION_NAME_SIZE int = 3 CPE_SECTION_LEN_SIZE int = 4 CPE_SECTION_COUNT int = 3 CPE_SECTION_EXT string = "EXT" CPE_SECTION_CP string = "CPS" CPE_SECTION_LLP string = "LLP" CPE_SECTION_RADIOPASS string = "RPP" CPE_SECTION_TONEFILE string = "TON" ) func NewReader(fileName string) (_ CPEReader, err error) { return CPEReader{}.NewReader(fileName) } func (r CPEReader) NewReader(fileName string) (_ CPEReader, err error) { r.FileHandle, err = os.Open(fileName) if err != nil { return r, err } r.streamReader = bufio.NewReader(r.FileHandle) return r, nil } func (r *CPEReader) Close() { r.FileHandle.Close() } func (r *CPEReader) ValidateHeader() (err error) { // first7Chars := make([]byte, 7) _, err = io.ReadFull(r.streamReader, first7Chars) if err != nil { return err } if string(first7Chars) != "CPDFile" { return fmt.Errorf("missing 'cpdfile' header") } versionByte, err := r.streamReader.ReadByte() if err != nil { return err } if int(versionByte) != CPE_FILE_VER { if int(versionByte) == CPE_ENCRYPTED_FILE_VER { return fmt.Errorf("file is encrypted (version %d)", versionByte) } return fmt.Errorf("file-version %d not supported", versionByte) } // waste 1 byte r.streamReader.Discard(1) return nil } func (r *CPEReader) readSegment() (fileSection CPEFileSection, err error) { sectionNameBytes := make([]byte, 3) _, err = io.ReadFull(r.streamReader, sectionNameBytes) if err != nil { return fileSection, err } fileSection.Name = string(sectionNameBytes) sectionSizeBytes := make([]byte, 4) _, err = io.ReadFull(r.streamReader, sectionSizeBytes) if err != nil { return fileSection, err } fileSection.Size = int(binary.LittleEndian.Uint32(sectionSizeBytes)) fileSection.Data = make([]byte, fileSection.Size) _, err = io.ReadFull(r.streamReader, fileSection.Data) if err != nil { return fileSection, err } return fileSection, nil } func (r *CPEReader) ReadData() (err error) { r.Metadata.Segments = make(map[string]CPEFileSection) for { nextSegment, err := r.readSegment() if err != nil { if errors.Is(err, io.EOF) { return nil } return err } r.Metadata.Segments[nextSegment.Name] = nextSegment } }