|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
euni "golang.org/x/text/encoding/unicode"
|
|
|
|
)
|
|
|
|
|
|
|
|
// got the logic from: https://github.com/bogem/id3v2 , thank you very much.
|
|
|
|
const first7BitsMask = uint32(254 << 24) // shifting 11111110 to the end of uint32
|
|
|
|
|
|
|
|
// Converts given bytes into integer
|
|
|
|
func BytesToInt(gBytes []byte) uint32 {
|
|
|
|
var integer uint32 = 0
|
|
|
|
for _, b := range gBytes {
|
|
|
|
integer = integer << 8
|
|
|
|
integer = integer | uint32(b)
|
|
|
|
}
|
|
|
|
return integer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simply converts given uint32 into synch unsafe bytes
|
|
|
|
func IntToBytes(gInt uint32) []byte {
|
|
|
|
buff := make([]byte, 4)
|
|
|
|
binary.BigEndian.PutUint32(buff, gInt)
|
|
|
|
return buff
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decodes given integer bytes into integer, ignores the first bit
|
|
|
|
// of every given byte in binary form
|
|
|
|
func BytesToIntSynchsafe(gBytes []byte) uint32 {
|
|
|
|
var integer uint32 = 0
|
|
|
|
for _, b := range gBytes {
|
|
|
|
integer = integer << 7
|
|
|
|
integer = integer | uint32(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
return integer
|
|
|
|
}
|
|
|
|
|
|
|
|
// The exact opposite of what `BytesToIntSynchsafe` does
|
|
|
|
// Finally understood with the help of: https://github.com/bogem/id3v2/blob/master/size.go ,
|
|
|
|
// thank you very much !
|
|
|
|
func IntToBytesSynchsafe(gInt uint32) []byte {
|
|
|
|
var synchsafeIBytes []byte
|
|
|
|
|
|
|
|
// skip 4 0`ed bits
|
|
|
|
gInt = gInt << 4
|
|
|
|
|
|
|
|
// int32 == 4 bytes
|
|
|
|
for i := 0; i < 32/8; i++ {
|
|
|
|
// get first 7 bits
|
|
|
|
first7Bits := gInt & first7BitsMask
|
|
|
|
|
|
|
|
// shift captured bits to the beginning
|
|
|
|
first7Bits = first7Bits >> (3*8 + 1)
|
|
|
|
|
|
|
|
b := byte(first7Bits)
|
|
|
|
synchsafeIBytes = append(synchsafeIBytes, b)
|
|
|
|
|
|
|
|
// prepare next 7 bits for the next iteration
|
|
|
|
gInt = gInt << 7
|
|
|
|
}
|
|
|
|
return synchsafeIBytes
|
|
|
|
}
|
|
|
|
|
|
|
|
// Converts given bytes into string, ignoring the first 31 non-printable ASCII characters.
|
|
|
|
// (LOSSY, if given bytes contain some nasty ones)
|
|
|
|
func ToStringLossy(gBytes []byte) string {
|
|
|
|
var runes []rune
|
|
|
|
for _, b := range gBytes {
|
|
|
|
if b <= 31 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
runes = append(runes, rune(b))
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.ToValidUTF8(string(runes), "")
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
EncodingISO8859 byte = iota
|
|
|
|
EncodingUTF16BOM
|
|
|
|
EncodingUTF16
|
|
|
|
EncodingUTF8
|
|
|
|
)
|
|
|
|
|
|
|
|
// Decodes the given frame`s contents
|
|
|
|
func DecodeText(fContents []byte) string {
|
|
|
|
textEncoding := fContents[0] // the first byte is the encoding
|
|
|
|
|
|
|
|
switch textEncoding {
|
|
|
|
case EncodingISO8859:
|
|
|
|
// ISO-8859-1
|
|
|
|
return ToStringLossy(fContents[1:])
|
|
|
|
case EncodingUTF16BOM:
|
|
|
|
// UTF-16 with BOM
|
|
|
|
encoding := euni.UTF16(euni.BigEndian, euni.ExpectBOM)
|
|
|
|
decoder := encoding.NewDecoder()
|
|
|
|
|
|
|
|
decodedBytes := make([]byte, len(fContents)*2)
|
|
|
|
_, _, err := decoder.Transform(decodedBytes, fContents[1:], true)
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(decodedBytes)
|
|
|
|
|
|
|
|
case EncodingUTF16:
|
|
|
|
// UTF-16
|
|
|
|
encoding := euni.UTF16(euni.BigEndian, euni.IgnoreBOM)
|
|
|
|
decoder := encoding.NewDecoder()
|
|
|
|
|
|
|
|
decodedBytes := make([]byte, len(fContents)*2)
|
|
|
|
_, _, err := decoder.Transform(decodedBytes, fContents[1:], true)
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(decodedBytes)
|
|
|
|
|
|
|
|
case EncodingUTF8:
|
|
|
|
// UTF-8
|
|
|
|
return ToStringLossy(fContents[1:])
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|