diff --git a/algo/kgm/kgm.go b/algo/kgm/kgm.go new file mode 100644 index 0000000..7de2f21 --- /dev/null +++ b/algo/kgm/kgm.go @@ -0,0 +1,91 @@ +package kgm + +import ( + "bytes" + "encoding/binary" + "errors" + "github.com/umlock-music/cli/algo/common" + "github.com/umlock-music/cli/internal/logging" +) + +var ( + vprHeader = []byte{ + 0x05, 0x28, 0xBC, 0x96, 0xE9, 0xE4, 0x5A, 0x43, + 0x91, 0xAA, 0xBD, 0xD0, 0x7A, 0xF5, 0x36, 0x31} + kgmHeader = []byte{ + 0x7C, 0xD5, 0x32, 0xEB, 0x86, 0x02, 0x7F, 0x4B, + 0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14} + ErrKgmMagicHeader = errors.New("kgm/vpr magic header not matched") +) + +type Decoder struct { + file []byte + key []byte + isVpr bool + audio []byte +} + +func NewDecoder(file []byte) *Decoder { + return &Decoder{ + file: file, + } +} + +func (d Decoder) GetCoverImage() []byte { + return nil +} + +func (d Decoder) GetAudioData() []byte { + return d.audio +} + +func (d Decoder) GetAudioExt() string { + return "" +} + +func (d Decoder) GetMeta() common.Meta { + return nil +} + +func (d *Decoder) Validate() error { + if bytes.Equal(kgmHeader, d.file[:len(kgmHeader)]) { + d.isVpr = false + } else if bytes.Equal(vprHeader, d.file[:len(vprHeader)]) { + d.isVpr = true + } else { + return ErrKgmMagicHeader + } + + d.key = d.file[0x1c:0x2c] + d.key = append(d.key, 0x00) + _ = d.file[0x2c:0x3c] //key2 + return nil + +} + +func (d *Decoder) Decode() error { + headerLen := binary.LittleEndian.Uint32(d.file[0x10:0x14]) + dataEncrypted := d.file[headerLen:] + lenData := len(dataEncrypted) + initMask() + if fullMaskLen < lenData { + logging.Log().Warn("文件过大,处理后的音频不完整,请向我们报告此文件的信息") + lenData = fullMaskLen + } + d.audio = make([]byte, lenData) + + for i := 0; i < lenData; i++ { + med8 := d.key[i%17] ^ dataEncrypted[i] + med8 ^= (med8 & 0xf) << 4 + + msk8 := maskV2PreDef[i%272] ^ maskV2[i>>4] + msk8 ^= (msk8 & 0xf) << 4 + d.audio[i] = med8 ^ msk8 + } + if d.isVpr { + for i := 0; i < lenData; i++ { + d.audio[i] ^= maskDiffVpr[i%17] + } + } + return nil +} diff --git a/algo/kgm/kgm.v2.mask b/algo/kgm/kgm.v2.mask new file mode 100644 index 0000000..c7db7e5 Binary files /dev/null and b/algo/kgm/kgm.v2.mask differ diff --git a/algo/kgm/mask.go b/algo/kgm/mask.go new file mode 100644 index 0000000..204d969 --- /dev/null +++ b/algo/kgm/mask.go @@ -0,0 +1,59 @@ +package kgm + +import ( + "bytes" + _ "embed" + "github.com/ulikunitz/xz" + "github.com/umlock-music/cli/internal/logging" + "go.uber.org/zap" + "io/ioutil" +) + +var maskDiffVpr = []byte{ + 0x25, 0xDF, 0xE8, 0xA6, 0x75, 0x1E, 0x75, 0x0E, + 0x2F, 0x80, 0xF3, 0x2D, 0xB8, 0xB6, 0xE3, 0x11, + 0x00} + +var maskV2PreDef = []byte{ + 0xB8, 0xD5, 0x3D, 0xB2, 0xE9, 0xAF, 0x78, 0x8C, 0x83, 0x33, 0x71, 0x51, 0x76, 0xA0, 0xCD, 0x37, + 0x2F, 0x3E, 0x35, 0x8D, 0xA9, 0xBE, 0x98, 0xB7, 0xE7, 0x8C, 0x22, 0xCE, 0x5A, 0x61, 0xDF, 0x68, + 0x69, 0x89, 0xFE, 0xA5, 0xB6, 0xDE, 0xA9, 0x77, 0xFC, 0xC8, 0xBD, 0xBD, 0xE5, 0x6D, 0x3E, 0x5A, + 0x36, 0xEF, 0x69, 0x4E, 0xBE, 0xE1, 0xE9, 0x66, 0x1C, 0xF3, 0xD9, 0x02, 0xB6, 0xF2, 0x12, 0x9B, + 0x44, 0xD0, 0x6F, 0xB9, 0x35, 0x89, 0xB6, 0x46, 0x6D, 0x73, 0x82, 0x06, 0x69, 0xC1, 0xED, 0xD7, + 0x85, 0xC2, 0x30, 0xDF, 0xA2, 0x62, 0xBE, 0x79, 0x2D, 0x62, 0x62, 0x3D, 0x0D, 0x7E, 0xBE, 0x48, + 0x89, 0x23, 0x02, 0xA0, 0xE4, 0xD5, 0x75, 0x51, 0x32, 0x02, 0x53, 0xFD, 0x16, 0x3A, 0x21, 0x3B, + 0x16, 0x0F, 0xC3, 0xB2, 0xBB, 0xB3, 0xE2, 0xBA, 0x3A, 0x3D, 0x13, 0xEC, 0xF6, 0x01, 0x45, 0x84, + 0xA5, 0x70, 0x0F, 0x93, 0x49, 0x0C, 0x64, 0xCD, 0x31, 0xD5, 0xCC, 0x4C, 0x07, 0x01, 0x9E, 0x00, + 0x1A, 0x23, 0x90, 0xBF, 0x88, 0x1E, 0x3B, 0xAB, 0xA6, 0x3E, 0xC4, 0x73, 0x47, 0x10, 0x7E, 0x3B, + 0x5E, 0xBC, 0xE3, 0x00, 0x84, 0xFF, 0x09, 0xD4, 0xE0, 0x89, 0x0F, 0x5B, 0x58, 0x70, 0x4F, 0xFB, + 0x65, 0xD8, 0x5C, 0x53, 0x1B, 0xD3, 0xC8, 0xC6, 0xBF, 0xEF, 0x98, 0xB0, 0x50, 0x4F, 0x0F, 0xEA, + 0xE5, 0x83, 0x58, 0x8C, 0x28, 0x2C, 0x84, 0x67, 0xCD, 0xD0, 0x9E, 0x47, 0xDB, 0x27, 0x50, 0xCA, + 0xF4, 0x63, 0x63, 0xE8, 0x97, 0x7F, 0x1B, 0x4B, 0x0C, 0xC2, 0xC1, 0x21, 0x4C, 0xCC, 0x58, 0xF5, + 0x94, 0x52, 0xA3, 0xF3, 0xD3, 0xE0, 0x68, 0xF4, 0x00, 0x23, 0xF3, 0x5E, 0x0A, 0x7B, 0x93, 0xDD, + 0xAB, 0x12, 0xB2, 0x13, 0xE8, 0x84, 0xD7, 0xA7, 0x9F, 0x0F, 0x32, 0x4C, 0x55, 0x1D, 0x04, 0x36, + 0x52, 0xDC, 0x03, 0xF3, 0xF9, 0x4E, 0x42, 0xE9, 0x3D, 0x61, 0xEF, 0x7C, 0xB6, 0xB3, 0x93, 0x50, +} + +//go:embed kgm.v2.mask +var maskV2Xz []byte + +var maskV2 []byte +var fullMaskLen int +var initMaskOK = false + +//todo: 根据需求解压Mask大小 +func initMask() { + if initMaskOK { + return + } + maskReader, err := xz.NewReader(bytes.NewReader(maskV2Xz)) + if err != nil { + logging.Log().Fatal("load kgm mask failed", zap.Error(err)) + } + maskV2, err = ioutil.ReadAll(maskReader) + if err != nil { + logging.Log().Fatal("load kgm mask failed", zap.Error(err)) + } + fullMaskLen = len(maskV2) * 16 + initMaskOK = true +} diff --git a/go.mod b/go.mod index 12e3f0d..657ffd1 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,10 @@ module github.com/umlock-music/cli -go 1.15 +go 1.16 require ( + github.com/davecgh/go-spew v1.1.1 + github.com/ulikunitz/xz v0.5.9 go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.16.0 ) diff --git a/go.sum b/go.sum index 727c0b1..c6b8cfa 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -19,15 +17,14 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= +github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= @@ -50,7 +47,6 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=