Add KGM Decoder

This commit is contained in:
Emmm Monster
2020-12-26 02:54:07 +08:00
parent a92ea82090
commit dc2fb2f471
5 changed files with 155 additions and 7 deletions

91
algo/kgm/kgm.go Normal file
View File

@ -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
}

BIN
algo/kgm/kgm.v2.mask Normal file

Binary file not shown.

59
algo/kgm/mask.go Normal file
View File

@ -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
}