12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- package qmc
- import (
- bytes "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "strings"
- )
- type MusicExTagV1 struct {
- SongID uint32 // Song ID
- Unknown1 uint32 // unused & unknown
- Unknown2 uint32 // unused & unknown
- MediaID string // Media ID
- MediaFileName string // real file name
- Unknown3 uint32 // unused; uninitialized memory?
- // 16 byte at the end of tag.
- // TagSize should be respected when parsing.
- TagSize uint32 // 19.57: fixed value: 0xC0
- TagVersion uint32 // 19.57: fixed value: 0x01
- TagMagic []byte // fixed value "musicex\0" (8 bytes)
- }
- 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[0x04:0x0C],
- }
- if !bytes.Equal(tag.TagMagic, []byte("musicex\x00")) {
- return nil, errors.New("MusicEx magic mismatch")
- }
- if tag.TagVersion != 1 {
- return nil, errors.New(fmt.Sprintf("unsupported musicex tag version. expecting 1, got %d", tag.TagVersion))
- }
- if tag.TagSize < 0xC0 {
- return nil, errors.New(fmt.Sprintf("unsupported musicex tag size. expecting at least 0xC0, got 0x%02x", 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()
- }
|