package kgm import ( "fmt" "io" "jsuse.com/dev/dec-music/decrypt" ) type decoder struct { rd io.ReadSeeker cipher decrypt.StreamDecoder offset int header header } func newDecoder(p *decrypt.DecoderParams) decrypt.Decoder { return &decoder{rd: p.Reader} } // Validate checks if the file is a valid Kugou (.kgm, .vpr, .kgma) file. // rd will be seeked to the beginning of the encrypted audio. func (d *decoder) Validate() (err error) { if err := d.header.FromFile(d.rd); err != nil { return err } // TODO; validate crypto version switch d.header.CryptoVersion { case 3: d.cipher, err = newKgmCryptoV3(&d.header) if err != nil { return fmt.Errorf("kgm init crypto v3: %w", err) } default: return fmt.Errorf("kgm: unsupported crypto version %d", d.header.CryptoVersion) } // prepare for read if _, err := d.rd.Seek(int64(d.header.AudioOffset), io.SeekStart); err != nil { return fmt.Errorf("kgm seek to audio: %w", err) } return nil } func (d *decoder) Read(buf []byte) (int, error) { n, err := d.rd.Read(buf) if n > 0 { d.cipher.Decrypt(buf[:n], d.offset) d.offset += n } return n, err } func init() { // Kugou decrypt.RegisterDecoder("kgm", false , newDecoder) decrypt.RegisterDecoder("kgma", false , newDecoder) // Viper decrypt.RegisterDecoder("vpr", false , newDecoder) // Kugou Android decrypt.RegisterDecoder("kgm.flac", false , newDecoder) decrypt.RegisterDecoder("vpr.flac", false , newDecoder) }