diff --git a/algo/kgm/kgm.go b/algo/kgm/kgm.go index 37595c0..37a68fa 100644 --- a/algo/kgm/kgm.go +++ b/algo/kgm/kgm.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/binary" "errors" + "github.com/unlock-music/cli/algo/common" - "github.com/unlock-music/cli/internal/logging" ) var ( @@ -31,19 +31,19 @@ func NewDecoder(file []byte) common.Decoder { } } -func (d Decoder) GetCoverImage() []byte { +func (d *Decoder) GetCoverImage() []byte { return nil } -func (d Decoder) GetAudioData() []byte { +func (d *Decoder) GetAudioData() []byte { return d.audio } -func (d Decoder) GetAudioExt() string { +func (d *Decoder) GetAudioExt() string { return "" // use sniffer } -func (d Decoder) GetMeta() common.Meta { +func (d *Decoder) GetMeta() common.Meta { return nil } @@ -64,22 +64,14 @@ func (d *Decoder) Validate() error { 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("The file is too large and the processed audio is incomplete, " + - "please report to us about this file at https://github.com/unlock-music/cli/issues") - lenData = fullMaskLen - } - d.audio = make([]byte, lenData) + d.audio = d.file[headerLen:] - for i := 0; i < lenData; i++ { - med8 := dataEncrypted[i] ^ d.key[i%17] ^ maskV2PreDef[i%(16*17)] ^ maskV2[i>>4] + for i := 0; i < len(d.audio); i++ { + med8 := d.audio[i] ^ d.key[i%17] ^ maskV2PreDef[i%(16*17)] ^ getKgmMaskV2(i>>4) d.audio[i] = med8 ^ (med8&0xf)<<4 } if d.isVpr { - for i := 0; i < lenData; i++ { + for i := 0; i < len(d.audio); i++ { d.audio[i] ^= maskDiffVpr[i%17] } } diff --git a/algo/kgm/kgm.v2.mask b/algo/kgm/kgm.v2.mask deleted file mode 100644 index c7db7e5..0000000 Binary files a/algo/kgm/kgm.v2.mask and /dev/null differ diff --git a/algo/kgm/mask.go b/algo/kgm/mask.go index dc9fa62..1b0d61a 100644 --- a/algo/kgm/mask.go +++ b/algo/kgm/mask.go @@ -1,12 +1,7 @@ package kgm import ( - "bytes" _ "embed" - "github.com/ulikunitz/xz" - "github.com/unlock-music/cli/internal/logging" - "go.uber.org/zap" - "io" ) var maskDiffVpr = []byte{ @@ -34,26 +29,53 @@ var maskV2PreDef = []byte{ 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: decompress mask on demand -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 = io.ReadAll(maskReader) - if err != nil { - logging.Log().Fatal("load kgm mask failed", zap.Error(err)) - } - fullMaskLen = len(maskV2) * 16 - initMaskOK = true +var kgmMaskTable1 = []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x21, 0x01, 0x61, 0x01, 0x21, 0x01, 0xe1, 0x01, 0x21, 0x01, 0x61, 0x01, 0x21, 0x01, + 0xd2, 0x23, 0x02, 0x02, 0x42, 0x42, 0x02, 0x02, 0xc2, 0xc2, 0x02, 0x02, 0x42, 0x42, 0x02, 0x02, + 0xd3, 0xd3, 0x02, 0x03, 0x63, 0x43, 0x63, 0x03, 0xe3, 0xc3, 0xe3, 0x03, 0x63, 0x43, 0x63, 0x03, + 0x94, 0xb4, 0x94, 0x65, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0x84, 0x84, 0x04, 0x04, 0x04, 0x04, + 0x95, 0x95, 0x95, 0x95, 0x04, 0x05, 0x25, 0x05, 0xe5, 0x85, 0xa5, 0x85, 0xe5, 0x05, 0x25, 0x05, + 0xd6, 0xb6, 0x96, 0xb6, 0xd6, 0x27, 0x06, 0x06, 0xc6, 0xc6, 0x86, 0x86, 0xc6, 0xc6, 0x06, 0x06, + 0xd7, 0xd7, 0x97, 0x97, 0xd7, 0xd7, 0x06, 0x07, 0xe7, 0xc7, 0xe7, 0x87, 0xe7, 0xc7, 0xe7, 0x07, + 0x18, 0x38, 0x18, 0x78, 0x18, 0x38, 0x18, 0xe9, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x08, 0x09, 0x29, 0x09, 0x69, 0x09, 0x29, 0x09, + 0xda, 0x3a, 0x1a, 0x3a, 0x5a, 0x3a, 0x1a, 0x3a, 0xda, 0x2b, 0x0a, 0x0a, 0x4a, 0x4a, 0x0a, 0x0a, + 0xdb, 0xdb, 0x1b, 0x1b, 0x5b, 0x5b, 0x1b, 0x1b, 0xdb, 0xdb, 0x0a, 0x0b, 0x6b, 0x4b, 0x6b, 0x0b, + 0x9c, 0xbc, 0x9c, 0x7c, 0x1c, 0x3c, 0x1c, 0x7c, 0x9c, 0xbc, 0x9c, 0x6d, 0x0c, 0x0c, 0x0c, 0x0c, + 0x9d, 0x9d, 0x9d, 0x9d, 0x1d, 0x1d, 0x1d, 0x1d, 0x9d, 0x9d, 0x9d, 0x9d, 0x0c, 0x0d, 0x2d, 0x0d, + 0xde, 0xbe, 0x9e, 0xbe, 0xde, 0x3e, 0x1e, 0x3e, 0xde, 0xbe, 0x9e, 0xbe, 0xde, 0x2f, 0x0e, 0x0e, + 0xdf, 0xdf, 0x9f, 0x9f, 0xdf, 0xdf, 0x1f, 0x1f, 0xdf, 0xdf, 0x9f, 0x9f, 0xdf, 0xdf, 0x0e, 0x0f, + 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0xe0, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0xf1, +} + +var kgmMaskTable2 = []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x23, 0x01, 0x67, 0x01, 0x23, 0x01, 0xef, 0x01, 0x23, 0x01, 0x67, 0x01, 0x23, 0x01, + 0xdf, 0x21, 0x02, 0x02, 0x46, 0x46, 0x02, 0x02, 0xce, 0xce, 0x02, 0x02, 0x46, 0x46, 0x02, 0x02, + 0xde, 0xde, 0x02, 0x03, 0x65, 0x47, 0x65, 0x03, 0xed, 0xcf, 0xed, 0x03, 0x65, 0x47, 0x65, 0x03, + 0x9d, 0xbf, 0x9d, 0x63, 0x04, 0x04, 0x04, 0x04, 0x8c, 0x8c, 0x8c, 0x8c, 0x04, 0x04, 0x04, 0x04, + 0x9c, 0x9c, 0x9c, 0x9c, 0x04, 0x05, 0x27, 0x05, 0xeb, 0x8d, 0xaf, 0x8d, 0xeb, 0x05, 0x27, 0x05, + 0xdb, 0xbd, 0x9f, 0xbd, 0xdb, 0x25, 0x06, 0x06, 0xca, 0xca, 0x8e, 0x8e, 0xca, 0xca, 0x06, 0x06, + 0xda, 0xda, 0x9e, 0x9e, 0xda, 0xda, 0x06, 0x07, 0xe9, 0xcb, 0xe9, 0x8f, 0xe9, 0xcb, 0xe9, 0x07, + 0x19, 0x3b, 0x19, 0x7f, 0x19, 0x3b, 0x19, 0xe7, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x09, 0x2b, 0x09, 0x6f, 0x09, 0x2b, 0x09, + 0xd7, 0x39, 0x1b, 0x39, 0x5f, 0x39, 0x1b, 0x39, 0xd7, 0x29, 0x0a, 0x0a, 0x4e, 0x4e, 0x0a, 0x0a, + 0xd6, 0xd6, 0x1a, 0x1a, 0x5e, 0x5e, 0x1a, 0x1a, 0xd6, 0xd6, 0x0a, 0x0b, 0x6d, 0x4f, 0x6d, 0x0b, + 0x95, 0xb7, 0x95, 0x7b, 0x1d, 0x3f, 0x1d, 0x7b, 0x95, 0xb7, 0x95, 0x6b, 0x0c, 0x0c, 0x0c, 0x0c, + 0x94, 0x94, 0x94, 0x94, 0x1c, 0x1c, 0x1c, 0x1c, 0x94, 0x94, 0x94, 0x94, 0x0c, 0x0d, 0x2f, 0x0d, + 0xd3, 0xb5, 0x97, 0xb5, 0xd3, 0x3d, 0x1f, 0x3d, 0xd3, 0xb5, 0x97, 0xb5, 0xd3, 0x2d, 0x0e, 0x0e, + 0xd2, 0xd2, 0x96, 0x96, 0xd2, 0xd2, 0x1e, 0x1e, 0xd2, 0xd2, 0x96, 0x96, 0xd2, 0xd2, 0x0e, 0x0f, + 0x00, 0x22, 0x00, 0x66, 0x00, 0x22, 0x00, 0xee, 0x00, 0x22, 0x00, 0x66, 0x00, 0x22, 0x00, 0xfe, +} + +func getKgmMaskV2(offset int) byte { + ret := byte(0) + for offset >= 0x11 { + ret ^= kgmMaskTable1[offset%272] + offset = offset >> 4 + ret ^= kgmMaskTable2[offset%272] + offset = offset >> 4 + } + return ret }