package qmc import ( "encoding/base64" "encoding/binary" "errors" "github.com/umlock-music/cli/algo/common" ) var ( ErrQmcFileLength = errors.New("invalid qmc file length") ErrQmcKeyDecodeFailed = errors.New("base64 decode qmc key failed") ErrQmcKeyLength = errors.New("unexpected decoded qmc key length") ) type Decoder struct { file []byte maskDetector func(encodedData []byte) (*Key256Mask, error) mask *Key256Mask audioExt string key []byte audio []byte } func NewDefaultDecoder(data []byte) *Decoder { return &Decoder{file: data, mask: getDefaultMask()} } func NewMflac256Decoder(data []byte) *Decoder { return &Decoder{file: data, maskDetector: detectMflac256Mask, audioExt: "flac"} } func NewMgg256Decoder(data []byte) *Decoder { return &Decoder{file: data, maskDetector: detectMgg256Mask, audioExt: "ogg"} } func (d *Decoder) Validate() error { if nil != d.mask { return nil } if nil != d.maskDetector { if err := d.validateKey(); err != nil { return err } var err error d.mask, err = d.maskDetector(d.file) return err } return errors.New("no mask or mask detector found") } func (d *Decoder) validateKey() error { lenData := len(d.file) if lenData < 4 { return ErrQmcFileLength } keyLen := binary.LittleEndian.Uint32(d.file[lenData-4:]) if lenData < int(keyLen+4) { return ErrQmcFileLength } var err error d.key, err = base64.StdEncoding.DecodeString( string(d.file[lenData-4-int(keyLen) : lenData-4])) if err != nil { return ErrQmcKeyDecodeFailed } if len(d.key) != 272 { return ErrQmcKeyLength } d.file = d.file[:lenData-4-int(keyLen)] return nil } func (d *Decoder) Decode() error { d.audio = d.mask.Decrypt(d.file) return nil } func (d Decoder) GetCoverImage() []byte { return nil } func (d Decoder) GetAudioData() []byte { return d.audio } func (d Decoder) GetAudioExt() string { return d.audioExt } func (d Decoder) GetMeta() common.Meta { return nil }