package v2 import ( "bytes" "fmt" "io" "strings" "github.com/Unbewohnte/id3ed/util" ) var ErrGotPadding error = fmt.Errorf("got padding") var ErrBiggerThanSize error = fmt.Errorf("frame size is bigger than size of the whole tag") var ErrInvalidFHeaderSize error = fmt.Errorf("frame header must be 6 or 10 bytes long") var ErrInvalidID error = fmt.Errorf("invalid identifier") type FrameFlags struct { TagAlterPreservation bool FileAlterPreservation bool ReadOnly bool Compressed bool Encrypted bool InGroup bool Unsyrchronised bool HasDataLengthIndicator bool } type FrameHeader struct { ID string Size uint32 Flags FrameFlags } type Frame struct { Header FrameHeader Contents []byte } // Checks if provided frame identifier is valid by // its length and presence of invalid characters. func isValidFrameID(frameID []byte) bool { if len(frameID) != 3 && len(frameID) != 4 { return false } str := strings.ToValidUTF8(string(frameID), "invalidChar") if len(str) != 3 && len(str) != 4 { return false } return true } func getV22FrameHeader(fHeaderbytes []byte) (FrameHeader, error) { var header FrameHeader if !isValidFrameID(fHeaderbytes[0:3]) { return FrameHeader{}, ErrInvalidID } header.ID = string(fHeaderbytes[0:3]) framesizeBytes := util.BytesToIntSynchsafe(fHeaderbytes[3:6]) header.Size = framesizeBytes return header, nil } func getV23FrameHeader(fHeaderbytes []byte) (FrameHeader, error) { var header FrameHeader // ID if !isValidFrameID(fHeaderbytes[0:4]) { return FrameHeader{}, ErrInvalidID } header.ID = string(fHeaderbytes[0:4]) // Size framesizeBytes := fHeaderbytes[4:8] framesize := util.BytesToIntSynchsafe(framesizeBytes) header.Size = framesize // Flags frameFlags1 := fHeaderbytes[8] frameFlags2 := fHeaderbytes[9] var flags FrameFlags if util.GetBit(frameFlags1, 1) { flags.TagAlterPreservation = true } else { flags.TagAlterPreservation = false } if util.GetBit(frameFlags1, 2) { flags.FileAlterPreservation = true } else { flags.FileAlterPreservation = false } if util.GetBit(frameFlags1, 3) { flags.ReadOnly = true } else { flags.ReadOnly = false } if util.GetBit(frameFlags2, 1) { flags.Compressed = true } else { flags.Compressed = false } if util.GetBit(frameFlags2, 1) { flags.Encrypted = true } else { flags.Encrypted = false } if util.GetBit(frameFlags2, 1) { flags.InGroup = true } else { flags.InGroup = false } header.Flags = flags return header, nil } func getV24FrameHeader(fHeaderbytes []byte) (FrameHeader, error) { var header FrameHeader // ID if !isValidFrameID(fHeaderbytes[0:4]) { return FrameHeader{}, ErrInvalidID } header.ID = string(fHeaderbytes[0:4]) // Size framesizeBytes := fHeaderbytes[4:8] framesize := util.BytesToIntSynchsafe(framesizeBytes) header.Size = framesize // Flags frameFlags1 := fHeaderbytes[8] frameFlags2 := fHeaderbytes[9] var flags FrameFlags if util.GetBit(frameFlags1, 2) { flags.TagAlterPreservation = true } else { flags.TagAlterPreservation = false } if util.GetBit(frameFlags1, 3) { flags.FileAlterPreservation = true } else { flags.FileAlterPreservation = false } if util.GetBit(frameFlags1, 4) { flags.ReadOnly = true } else { flags.ReadOnly = false } if util.GetBit(frameFlags2, 2) { flags.InGroup = true } else { flags.InGroup = false } if util.GetBit(frameFlags2, 5) { flags.Compressed = true } else { flags.Compressed = false } if util.GetBit(frameFlags2, 6) { flags.Encrypted = true } else { flags.Encrypted = false } if util.GetBit(frameFlags2, 7) { flags.Unsyrchronised = true } else { flags.Unsyrchronised = false } if util.GetBit(frameFlags2, 8) { flags.HasDataLengthIndicator = true } else { flags.HasDataLengthIndicator = false } header.Flags = flags return header, nil } // Structuralises frame header from given bytes. For versions see: constants. func getFrameHeader(fHeaderbytes []byte, version string) (FrameHeader, error) { // validation check if int(len(fHeaderbytes)) != int(10) && int(len(fHeaderbytes)) != int(6) { return FrameHeader{}, ErrInvalidFHeaderSize } var header FrameHeader var err error switch version { case V2_2: header, err = getV22FrameHeader(fHeaderbytes) if err != nil { return FrameHeader{}, err } case V2_3: header, err = getV23FrameHeader(fHeaderbytes) if err != nil { return FrameHeader{}, err } case V2_4: header, err = getV24FrameHeader(fHeaderbytes) if err != nil { return FrameHeader{}, err } } return header, nil } // Reads ID3v2.3.0 or ID3v2.4.0 frame from given frame bytes. // Returns a blank Frame struct if encountered an error, amount of // bytes read from io.Reader. func readNextFrame(r io.Reader, h Header) (Frame, uint64, error) { var frame Frame var read uint64 = 0 // Frame header headerBytes, err := util.Read(r, uint64(HEADERSIZE)) if err != nil { return Frame{}, 0, err } read += uint64(HEADERSIZE) frameHeader, err := getFrameHeader(headerBytes, h.Version) if err == ErrGotPadding || err == ErrInvalidID { return Frame{}, read, err } else if err != nil { return Frame{}, read, fmt.Errorf("could not get header of a frame: %s", err) } frame.Header = frameHeader // Contents contents, err := util.Read(r, uint64(frameHeader.Size)) if err != nil { return Frame{}, read, err } frame.Contents = contents read += uint64(frameHeader.Size) return frame, read, err } // Returns decoded string from f.Contents. // Note that it can and probably will return // corrupted data if you use it on non-text frames such as APIC // for such cases please deal with raw []byte func (f *Frame) Text() string { return util.DecodeText(f.Contents) } func v23FlagsToBytes(v23f FrameFlags) []byte { var flags = []byte{0, 0} if v23f.TagAlterPreservation { flags[0] = util.SetBit(flags[0], 8) } if v23f.FileAlterPreservation { flags[0] = util.SetBit(flags[0], 7) } if v23f.ReadOnly { flags[0] = util.SetBit(flags[0], 6) } if v23f.Compressed { flags[1] = util.SetBit(flags[1], 8) } if v23f.Encrypted { flags[1] = util.SetBit(flags[1], 7) } if v23f.InGroup { flags[1] = util.SetBit(flags[1], 6) } return flags } func v24FlagsToBytes(v24f FrameFlags) []byte { var flagBytes = []byte{0, 0} if v24f.TagAlterPreservation { flagBytes[0] = util.SetBit(flagBytes[0], 7) } if v24f.FileAlterPreservation { flagBytes[0] = util.SetBit(flagBytes[0], 6) } if v24f.ReadOnly { flagBytes[0] = util.SetBit(flagBytes[0], 5) } if v24f.InGroup { flagBytes[1] = util.SetBit(flagBytes[1], 7) } if v24f.Compressed { flagBytes[1] = util.SetBit(flagBytes[1], 4) } if v24f.Encrypted { flagBytes[1] = util.SetBit(flagBytes[1], 3) } if v24f.Unsyrchronised { flagBytes[1] = util.SetBit(flagBytes[1], 2) } if v24f.HasDataLengthIndicator { flagBytes[1] = util.SetBit(flagBytes[1], 1) } return flagBytes } // Converts frame to ready-to-write bytes func (f *Frame) toBytes(version string) []byte { buff := new(bytes.Buffer) // identifier buff.Write([]byte(f.Header.ID)) // size buff.Write(util.IntToBytesSynchsafe(f.Header.Size)) // flags var flagBytes []byte switch version { case V2_2: break case V2_3: flagBytes = v23FlagsToBytes(f.Header.Flags) buff.Write(flagBytes) case V2_4: flagBytes = v24FlagsToBytes(f.Header.Flags) buff.Write(flagBytes) } return buff.Bytes() }