kwm.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package kwm
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "strconv"
  8. "strings"
  9. "unicode"
  10. "unlock-music.dev/cli/algo/common"
  11. )
  12. const magicHeader1 = "yeelion-kuwo-tme"
  13. const magicHeader2 = "yeelion-kuwo\x00\x00\x00\x00"
  14. const keyPreDefined = "MoOtOiTvINGwd2E6n0E1i7L5t2IoOoNk"
  15. type Decoder struct {
  16. rd io.ReadSeeker
  17. cipher common.StreamDecoder
  18. offset int
  19. outputExt string
  20. bitrate int
  21. }
  22. func (d *Decoder) GetAudioExt() string {
  23. return "." + d.outputExt
  24. }
  25. func NewDecoder(p *common.DecoderParams) common.Decoder {
  26. return &Decoder{rd: p.Reader}
  27. }
  28. // Validate checks if the file is a valid Kuwo .kw file.
  29. // rd will be seeked to the beginning of the encrypted audio.
  30. func (d *Decoder) Validate() error {
  31. header := make([]byte, 0x400) // kwm header is fixed to 1024 bytes
  32. _, err := io.ReadFull(d.rd, header)
  33. if err != nil {
  34. return fmt.Errorf("kwm read header: %w", err)
  35. }
  36. // check magic header, 0x00 - 0x0F
  37. magicHeader := header[:0x10]
  38. if !bytes.Equal([]byte(magicHeader1), magicHeader) &&
  39. !bytes.Equal([]byte(magicHeader2), magicHeader) {
  40. return errors.New("kwm magic header not matched")
  41. }
  42. d.cipher = newKwmCipher(header[0x18:0x20]) // Crypto Key, 0x18 - 0x1F
  43. d.bitrate, d.outputExt = parseBitrateAndType(header[0x30:0x38]) // Bitrate & File Extension, 0x30 - 0x38
  44. return nil
  45. }
  46. func parseBitrateAndType(header []byte) (int, string) {
  47. tmp := strings.TrimRight(string(header), "\x00")
  48. sep := strings.IndexFunc(tmp, func(r rune) bool {
  49. return !unicode.IsDigit(r)
  50. })
  51. bitrate, _ := strconv.Atoi(tmp[:sep]) // just ignore the error
  52. outputExt := strings.ToLower(tmp[sep:])
  53. return bitrate, outputExt
  54. }
  55. func (d *Decoder) Read(b []byte) (int, error) {
  56. n, err := d.rd.Read(b)
  57. if n > 0 {
  58. d.cipher.Decrypt(b[:n], d.offset)
  59. d.offset += n
  60. }
  61. return n, err
  62. }
  63. func padOrTruncate(raw string, length int) string {
  64. lenRaw := len(raw)
  65. out := raw
  66. if lenRaw == 0 {
  67. out = string(make([]byte, length))
  68. } else if lenRaw > length {
  69. out = raw[:length]
  70. } else if lenRaw < length {
  71. _tmp := make([]byte, 32)
  72. for i := 0; i < 32; i++ {
  73. _tmp[i] = raw[i%lenRaw]
  74. }
  75. out = string(_tmp)
  76. }
  77. return out
  78. }
  79. func init() {
  80. // Kuwo Mp3/Flac
  81. common.RegisterDecoder("kwm", false, NewDecoder)
  82. common.RegisterDecoder("kwm", false, common.NewRawDecoder)
  83. }