Update NCM Decoder
This commit is contained in:
parent
98a645a45c
commit
db85f60023
@ -1,11 +1,12 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
type Decoder interface {
|
type Decoder interface {
|
||||||
|
Validate() bool
|
||||||
Decode() error
|
Decode() error
|
||||||
GetCoverImage() []byte
|
GetCoverImage() []byte
|
||||||
GetAudioData() []byte
|
GetAudioData() []byte
|
||||||
GetAudioExt() string
|
GetAudioExt() string
|
||||||
GetMeta() *Meta
|
GetMeta() Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta interface {
|
type Meta interface {
|
||||||
|
146
algo/ncm/ncm.go
146
algo/ncm/ncm.go
@ -28,14 +28,14 @@ var (
|
|||||||
|
|
||||||
func NewDecoder(data []byte) *Decoder {
|
func NewDecoder(data []byte) *Decoder {
|
||||||
return &Decoder{
|
return &Decoder{
|
||||||
data: data,
|
file: data,
|
||||||
fileLength: uint32(len(data)),
|
fileLen: uint32(len(data)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
data []byte
|
file []byte
|
||||||
fileLength uint32
|
fileLen uint32
|
||||||
|
|
||||||
key []byte
|
key []byte
|
||||||
box []byte
|
box []byte
|
||||||
@ -53,51 +53,51 @@ type Decoder struct {
|
|||||||
offsetAudio uint32
|
offsetAudio uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Decoder) Validate() bool {
|
func (d *Decoder) Validate() bool {
|
||||||
if !bytes.Equal(magicHeader, f.data[:len(magicHeader)]) {
|
if !bytes.Equal(magicHeader, d.file[:len(magicHeader)]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if status.IsDebug {
|
/*if status.IsDebug {
|
||||||
logging.Log().Info("the unknown field of the header is: \n" + spew.Sdump(f.data[8:10]))
|
logging.Log().Info("the unknown field of the header is: \n" + spew.Sdump(d.file[8:10]))
|
||||||
}*/
|
}*/
|
||||||
f.offsetKey = 8 + 2
|
d.offsetKey = 8 + 2
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: 读取前进行检查长度,防止越界
|
//todo: 读取前进行检查长度,防止越界
|
||||||
func (f *Decoder) readKeyData() error {
|
func (d *Decoder) readKeyData() error {
|
||||||
if f.offsetKey == 0 || f.offsetKey+4 > f.fileLength {
|
if d.offsetKey == 0 || d.offsetKey+4 > d.fileLen {
|
||||||
return errors.New("invalid cover data offset")
|
return errors.New("invalid cover file offset")
|
||||||
}
|
}
|
||||||
bKeyLen := f.data[f.offsetKey : f.offsetKey+4]
|
bKeyLen := d.file[d.offsetKey : d.offsetKey+4]
|
||||||
iKeyLen := binary.LittleEndian.Uint32(bKeyLen)
|
iKeyLen := binary.LittleEndian.Uint32(bKeyLen)
|
||||||
f.offsetMeta = f.offsetKey + 4 + iKeyLen
|
d.offsetMeta = d.offsetKey + 4 + iKeyLen
|
||||||
|
|
||||||
bKeyRaw := make([]byte, iKeyLen)
|
bKeyRaw := make([]byte, iKeyLen)
|
||||||
for i := uint32(0); i < iKeyLen; i++ {
|
for i := uint32(0); i < iKeyLen; i++ {
|
||||||
bKeyRaw[i] = f.data[i+4+f.offsetKey] ^ 0x64
|
bKeyRaw[i] = d.file[i+4+d.offsetKey] ^ 0x64
|
||||||
}
|
}
|
||||||
|
|
||||||
f.key = utils.PKCS7UnPadding(utils.DecryptAes128Ecb(bKeyRaw, keyCore))[17:]
|
d.key = utils.PKCS7UnPadding(utils.DecryptAes128Ecb(bKeyRaw, keyCore))[17:]
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Decoder) readMetaData() error {
|
func (d *Decoder) readMetaData() error {
|
||||||
if f.offsetMeta == 0 || f.offsetMeta+4 > f.fileLength {
|
if d.offsetMeta == 0 || d.offsetMeta+4 > d.fileLen {
|
||||||
return errors.New("invalid meta data offset")
|
return errors.New("invalid meta file offset")
|
||||||
}
|
}
|
||||||
bMetaLen := f.data[f.offsetMeta : f.offsetMeta+4]
|
bMetaLen := d.file[d.offsetMeta : d.offsetMeta+4]
|
||||||
iMetaLen := binary.LittleEndian.Uint32(bMetaLen)
|
iMetaLen := binary.LittleEndian.Uint32(bMetaLen)
|
||||||
f.offsetCover = f.offsetMeta + 4 + iMetaLen
|
d.offsetCover = d.offsetMeta + 4 + iMetaLen
|
||||||
if iMetaLen == 0 {
|
if iMetaLen == 0 {
|
||||||
return errors.New("no any meta data found")
|
return errors.New("no any meta file found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Why sub 22: Remove "163 key(Don't modify):"
|
// Why sub 22: Remove "163 key(Don't modify):"
|
||||||
bKeyRaw := make([]byte, iMetaLen-22)
|
bKeyRaw := make([]byte, iMetaLen-22)
|
||||||
for i := uint32(0); i < iMetaLen-22; i++ {
|
for i := uint32(0); i < iMetaLen-22; i++ {
|
||||||
bKeyRaw[i] = f.data[f.offsetMeta+4+22+i] ^ 0x63
|
bKeyRaw[i] = d.file[d.offsetMeta+4+22+i] ^ 0x63
|
||||||
}
|
}
|
||||||
|
|
||||||
cipherText, err := base64.StdEncoding.DecodeString(string(bKeyRaw))
|
cipherText, err := base64.StdEncoding.DecodeString(string(bKeyRaw))
|
||||||
@ -107,129 +107,129 @@ func (f *Decoder) readMetaData() error {
|
|||||||
metaRaw := utils.PKCS7UnPadding(utils.DecryptAes128Ecb(cipherText, keyMeta))
|
metaRaw := utils.PKCS7UnPadding(utils.DecryptAes128Ecb(cipherText, keyMeta))
|
||||||
sepIdx := bytes.IndexRune(metaRaw, ':')
|
sepIdx := bytes.IndexRune(metaRaw, ':')
|
||||||
if sepIdx == -1 {
|
if sepIdx == -1 {
|
||||||
return errors.New("invalid ncm meta data")
|
return errors.New("invalid ncm meta file")
|
||||||
}
|
}
|
||||||
|
|
||||||
f.metaType = string(metaRaw[:sepIdx])
|
d.metaType = string(metaRaw[:sepIdx])
|
||||||
f.metaRaw = metaRaw[sepIdx+1:]
|
d.metaRaw = metaRaw[sepIdx+1:]
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Decoder) buildKeyBox() {
|
func (d *Decoder) buildKeyBox() {
|
||||||
box := make([]byte, 256)
|
box := make([]byte, 256)
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
box[i] = byte(i)
|
box[i] = byte(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
keyLen := len(f.key)
|
keyLen := len(d.key)
|
||||||
var j byte
|
var j byte
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
j = box[i] + j + f.key[i%keyLen]
|
j = box[i] + j + d.key[i%keyLen]
|
||||||
box[i], box[j] = box[j], box[i]
|
box[i], box[j] = box[j], box[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
f.box = make([]byte, 256)
|
d.box = make([]byte, 256)
|
||||||
var _i byte
|
var _i byte
|
||||||
for i := 0; i < 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
_i = byte(i + 1)
|
_i = byte(i + 1)
|
||||||
si := box[_i]
|
si := box[_i]
|
||||||
sj := box[_i+si]
|
sj := box[_i+si]
|
||||||
f.box[i] = box[si+sj]
|
d.box[i] = box[si+sj]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Decoder) parseMeta() error {
|
func (d *Decoder) parseMeta() error {
|
||||||
switch f.metaType {
|
switch d.metaType {
|
||||||
case "music":
|
case "music":
|
||||||
f.Meta = new(RawMetaMusic)
|
d.Meta = new(RawMetaMusic)
|
||||||
return json.Unmarshal(f.metaRaw, f.Meta)
|
return json.Unmarshal(d.metaRaw, d.Meta)
|
||||||
case "dj":
|
case "dj":
|
||||||
f.Meta = new(RawMetaDJ)
|
d.Meta = new(RawMetaDJ)
|
||||||
|
return json.Unmarshal(d.metaRaw, d.Meta)
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown ncm meta type: " + f.metaType)
|
return errors.New("unknown ncm meta type: " + d.metaType)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Decoder) readCoverData() error {
|
func (d *Decoder) readCoverData() error {
|
||||||
if f.offsetCover == 0 || f.offsetCover+13 > f.fileLength {
|
if d.offsetCover == 0 || d.offsetCover+13 > d.fileLen {
|
||||||
return errors.New("invalid cover data offset")
|
return errors.New("invalid cover file offset")
|
||||||
}
|
}
|
||||||
|
|
||||||
coverLenStart := f.offsetCover + 5 + 4
|
coverLenStart := d.offsetCover + 5 + 4
|
||||||
bCoverLen := f.data[coverLenStart : coverLenStart+4]
|
bCoverLen := d.file[coverLenStart : coverLenStart+4]
|
||||||
|
|
||||||
/*if status.IsDebug {
|
/*if status.IsDebug {
|
||||||
logging.Log().Info("the unknown field of the cover is: \n" +
|
logging.Log().Info("the unknown field of the cover is: \n" +
|
||||||
spew.Sdump(f.data[f.offsetCover:f.offsetCover+5]))
|
spew.Sdump(d.file[d.offsetCover:d.offsetCover+5]))
|
||||||
coverLen2 := f.data[f.offsetCover+5 : f.offsetCover+5+4] // it seems that always the same
|
coverLen2 := d.file[d.offsetCover+5 : d.offsetCover+5+4] // it seems that always the same
|
||||||
if !bytes.Equal(coverLen2, bCoverLen) {
|
if !bytes.Equal(coverLen2, bCoverLen) {
|
||||||
logging.Log().Warn("special file found! 2 cover length filed no the same!")
|
logging.Log().Warn("special file found! 2 cover length filed no the same!")
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
iCoverLen := binary.LittleEndian.Uint32(bCoverLen)
|
iCoverLen := binary.LittleEndian.Uint32(bCoverLen)
|
||||||
f.offsetAudio = coverLenStart + 4 + iCoverLen
|
d.offsetAudio = coverLenStart + 4 + iCoverLen
|
||||||
if iCoverLen == 0 {
|
if iCoverLen == 0 {
|
||||||
return errors.New("no any cover data found")
|
return errors.New("no any cover file found")
|
||||||
}
|
}
|
||||||
f.Cover = f.data[coverLenStart+4 : 4+coverLenStart+iCoverLen]
|
d.Cover = d.file[coverLenStart+4 : 4+coverLenStart+iCoverLen]
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Decoder) readAudioData() error {
|
func (d *Decoder) readAudioData() error {
|
||||||
if f.offsetAudio == 0 || f.offsetAudio > f.fileLength {
|
if d.offsetAudio == 0 || d.offsetAudio > d.fileLen {
|
||||||
return errors.New("invalid audio offset")
|
return errors.New("invalid audio offset")
|
||||||
}
|
}
|
||||||
audioRaw := f.data[f.offsetAudio:]
|
audioRaw := d.file[d.offsetAudio:]
|
||||||
audioLen := len(audioRaw)
|
audioLen := len(audioRaw)
|
||||||
f.Audio = make([]byte, audioLen)
|
d.Audio = make([]byte, audioLen)
|
||||||
for i := uint32(0); i < uint32(audioLen); i++ {
|
for i := uint32(0); i < uint32(audioLen); i++ {
|
||||||
f.Audio[i] = f.box[i&0xff] ^ audioRaw[i]
|
d.Audio[i] = d.box[i&0xff] ^ audioRaw[i]
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Decoder) Decode() error {
|
func (d *Decoder) Decode() error {
|
||||||
if err := f.readKeyData(); err != nil {
|
if err := d.readKeyData(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.buildKeyBox()
|
d.buildKeyBox()
|
||||||
|
|
||||||
err := f.readMetaData()
|
err := d.readMetaData()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = f.parseMeta()
|
err = d.parseMeta()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Log().Warn("parse ncm meta data failed", zap.Error(err))
|
logging.Log().Warn("parse ncm meta file failed", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = f.readCoverData()
|
err = d.readCoverData()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Log().Warn("parse ncm cover data failed", zap.Error(err))
|
logging.Log().Warn("parse ncm cover file failed", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.readAudioData()
|
return d.readAudioData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Decoder) GetAudioExt() string {
|
func (d Decoder) GetAudioExt() string {
|
||||||
if f.Meta != nil {
|
if d.Meta != nil {
|
||||||
return f.Meta.GetFormat()
|
return d.Meta.GetFormat()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Decoder) GetAudioData() []byte {
|
func (d Decoder) GetAudioData() []byte {
|
||||||
return f.Audio
|
return d.Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Decoder) GetCoverImage() []byte {
|
func (d Decoder) GetCoverImage() []byte {
|
||||||
if f.Cover != nil {
|
if d.Cover != nil {
|
||||||
return f.Cover
|
return d.Cover
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
imgURL := f.Meta.GetAlbumImageURL()
|
imgURL := d.Meta.GetAlbumImageURL()
|
||||||
if f.Meta != nil && !strings.HasPrefix(imgURL, "http") {
|
if d.Meta != nil && !strings.HasPrefix(imgURL, "http") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
resp, err := http.Get(imgURL)
|
resp, err := http.Get(imgURL)
|
||||||
@ -252,6 +252,6 @@ func (f Decoder) GetCoverImage() []byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Decoder) GetMeta() common.Meta {
|
func (d Decoder) GetMeta() common.Meta {
|
||||||
return f.Meta
|
return d.Meta
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user