init: dec-music 项目初始化
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package qmc
|
||||
|
||||
import (
|
||||
bytes "bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type musicExTagV1 struct {
|
||||
songID uint32
|
||||
unknown1 uint32
|
||||
unknown2 uint32
|
||||
mediaID string
|
||||
mediaFileName string
|
||||
unknown3 uint32
|
||||
tagSize uint32
|
||||
tagVersion uint32
|
||||
tagMagic []byte
|
||||
}
|
||||
|
||||
func newMusicExTag(f io.ReadSeeker) (*musicExTagV1, error) {
|
||||
_, err := f.Seek(-16, io.SeekEnd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("musicex seek error: %w", err)
|
||||
}
|
||||
|
||||
buffer := make([]byte, 16)
|
||||
bytesRead, err := f.Read(buffer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get musicex error: %w", err)
|
||||
}
|
||||
if bytesRead != 16 {
|
||||
return nil, fmt.Errorf("MusicExV1: read %d bytes (expected %d)", bytesRead, 16)
|
||||
}
|
||||
|
||||
tag := &musicExTagV1{
|
||||
tagSize: binary.LittleEndian.Uint32(buffer[0x00:0x04]),
|
||||
tagVersion: binary.LittleEndian.Uint32(buffer[0x04:0x08]),
|
||||
tagMagic: buffer[0x08:0x10],
|
||||
}
|
||||
|
||||
if !bytes.Equal(tag.tagMagic, []byte("musicex\x00")) {
|
||||
return nil, errors.New("MusicEx magic mismatch")
|
||||
}
|
||||
if tag.tagVersion != 1 {
|
||||
return nil, fmt.Errorf("unsupported musicex tag version: %d", tag.tagVersion)
|
||||
}
|
||||
|
||||
if tag.tagSize < 0xC0 {
|
||||
return nil, fmt.Errorf("unsupported musicex tag size: 0x%x", tag.tagSize)
|
||||
}
|
||||
|
||||
buffer = make([]byte, tag.tagSize)
|
||||
bytesRead, err = f.Read(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if uint32(bytesRead) != tag.tagSize {
|
||||
return nil, fmt.Errorf("MusicExV1: read %d bytes (expected %d)", bytesRead, tag.tagSize)
|
||||
}
|
||||
|
||||
tag.songID = binary.LittleEndian.Uint32(buffer[0x00:0x04])
|
||||
tag.unknown1 = binary.LittleEndian.Uint32(buffer[0x04:0x08])
|
||||
tag.unknown2 = binary.LittleEndian.Uint32(buffer[0x08:0x0C])
|
||||
tag.mediaID = readUnicodeTagName(buffer[0x0C:], 30*2)
|
||||
tag.mediaFileName = readUnicodeTagName(buffer[0x48:], 50*2)
|
||||
tag.unknown3 = binary.LittleEndian.Uint32(buffer[0xAC:0xB0])
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// readUnicodeTagName reads a buffer to maxLen.
|
||||
// reconstruct text by skipping alternate char (ascii chars encoded in UTF-16-LE),
|
||||
// until finding a zero or reaching maxLen.
|
||||
func readUnicodeTagName(buffer []byte, maxLen int) string {
|
||||
builder := strings.Builder{}
|
||||
|
||||
for i := 0; i < maxLen; i += 2 {
|
||||
chr := buffer[i]
|
||||
if chr != 0 {
|
||||
builder.WriteByte(chr)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
Reference in New Issue
Block a user