diff --git a/README.md b/README.md index c1a3feb..ab02d9d 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,8 @@ # Project status -Right now it`s capable of reading and writing ID3v1 and ID3v1.1 tags. - -ID3v2 support is still in making, but it can read header and frames +Right now it`s capable of reading and writing ID3v1 and ID3v1.1 tags, +reading ID3v2. ID3v2 writing support is still not implemented. --- diff --git a/util/conversion.go b/util/conversion.go index a873d3c..927616e 100644 --- a/util/conversion.go +++ b/util/conversion.go @@ -8,6 +8,10 @@ import ( 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 + // Decodes given byte into integer func ByteToInt(gByte byte) (int, error) { integer, err := strconv.Atoi(fmt.Sprintf("%d", gByte)) @@ -19,23 +23,29 @@ func ByteToInt(gByte byte) (int, error) { // Decodes given integer bytes into integer, ignores the first bit // of every given byte in binary form -func BytesToIntIgnoreFirstBit(gBytes []byte) (int64, error) { - // represent each byte in size as binary and get rid from the first bit, - // then concatenate filtered parts - var filteredBits string +func BytesToIntIgnoreFirstBit(gBytes []byte) uint32 { + var integer uint32 = 0 for _, b := range gBytes { - // ignore the first bit - filteredPart := fmt.Sprintf("%08b", b)[1:] // byte is 8 bits - filteredBits += filteredPart + integer = integer << 7 + integer = integer | uint32(b) } - // convert filtered binary into usable int64 - integer, err := strconv.ParseInt(filteredBits, 2, 64) - if err != nil { - return -1, err + return integer +} + +// The exact opposite of what `BytesToIntIgnoreFirstBit` does +func IntToBytesFirstBitZeroed(gInt uint32) []byte { + bytes := make([]byte, 32) + + // looping 4 times (32 bits / 8 bits (4 bytes in int32)) + for i := 0; i < 32; i += 8 { + gIntCopy := gInt //ie: 11010100 11001011 00100000 10111111 + first7 := gIntCopy & first7BitsMask + shifted := first7 >> 25 // 00000000 00000000 00000000 01101010 + bytes = append(bytes, byte(shifted)) } - return integer, nil + return bytes } // Converts given bytes into string, ignoring the first 31 non-printable ASCII characters. diff --git a/util/conversion_test.go b/util/conversion_test.go index 5afdc48..9736e0b 100644 --- a/util/conversion_test.go +++ b/util/conversion_test.go @@ -1,6 +1,8 @@ package util -import "testing" +import ( + "testing" +) func TestToStringLossy(t *testing.T) { someVeryNastyBytes := []byte{0, 1, 2, 3, 4, 5, 6, 50, 7, 8, 9, 10, 11, 50, 50} @@ -22,3 +24,14 @@ func TestDecodeText(t *testing.T) { t.Errorf("DecodeText failed: expected text %s, got %s", "22222", decodedUtf8text) } } + +// func TestIntToBytesFirstBitZeroed(t *testing.T) { +// var testint uint32 = 123456 + +// intbytes := IntToBytesFirstBitZeroed(testint) + +// if BytesToIntIgnoreFirstBit(intbytes) != testint { +// t.Errorf("IntToBytesFirstBitZeroed failed: expected to get %v; got %v", +// testint, BytesToIntIgnoreFirstBit(intbytes)) +// } +// } diff --git a/v2/frame.go b/v2/frame.go index 589ea4c..dcfb4d8 100644 --- a/v2/frame.go +++ b/v2/frame.go @@ -24,7 +24,7 @@ type FrameFlags struct { type FrameHeader struct { ID string - Size int64 + Size uint32 Flags FrameFlags } @@ -63,10 +63,7 @@ func getFrameHeader(fHeaderbytes []byte, version string) (FrameHeader, error) { } header.ID = string(fHeaderbytes[0:3]) - framesizeBytes, err := util.BytesToIntIgnoreFirstBit(fHeaderbytes[3:6]) - if err != nil { - return FrameHeader{}, err - } + framesizeBytes := util.BytesToIntIgnoreFirstBit(fHeaderbytes[3:6]) header.Size = framesizeBytes case V2_3: @@ -85,10 +82,7 @@ func getFrameHeader(fHeaderbytes []byte, version string) (FrameHeader, error) { // Size framesizeBytes := fHeaderbytes[4:8] - framesize, err := util.BytesToIntIgnoreFirstBit(framesizeBytes) - if err != nil { - return FrameHeader{}, err - } + framesize := util.BytesToIntIgnoreFirstBit(framesizeBytes) header.Size = framesize @@ -183,3 +177,17 @@ func readNextFrame(r io.Reader, h Header) (Frame, uint64, error) { func (f *Frame) Text() string { return util.DecodeText(f.Contents) } + +// Returns bytes of the frame that can be +// written in a file. +// func (f *Frame) Bytes() ([]byte, error) { +// header := f.Header +// contents := f.Contents + +// var headerbytes []byte + +// identifierBytes := []byte(header.ID) +// // sizeBytes + +// return nil, nil +// } diff --git a/v2/header.go b/v2/header.go index 70292c0..b468553 100644 --- a/v2/header.go +++ b/v2/header.go @@ -22,7 +22,7 @@ type Header struct { Identifier string Flags HeaderFlags Version string - Size int64 // size of the whole tag - 10 header bytes + Size uint32 } // Reads and structuralises ID3v2 header from given bytes. @@ -121,10 +121,7 @@ func readHeader(rs io.ReadSeeker) (Header, error) { // size sizeBytes := hBytes[6:] - size, err := util.BytesToIntIgnoreFirstBit(sizeBytes) - if err != nil { - return Header{}, err - } + size := util.BytesToIntIgnoreFirstBit(sizeBytes) header.Size = size diff --git a/v2/read_test.go b/v2/read_test.go index 56951ea..b0e473f 100644 --- a/v2/read_test.go +++ b/v2/read_test.go @@ -32,6 +32,6 @@ func TestReadV2Tag(t *testing.T) { picture := tag.Picture() if picture != nil { - t.Errorf("ReadV2Tag failed: expected it not to have a picture") + t.Errorf("ReadV2Tag failed: expected file not to have a picture") } } diff --git a/v2/write.go b/v2/write.go new file mode 100644 index 0000000..ee70704 --- /dev/null +++ b/v2/write.go @@ -0,0 +1,19 @@ +package v2 + +// func WriteFrame(frame *Frame) error { +// return nil +// } + +// // Writes ID3v2Tag to ws +// func (tag *ID3v2Tag) write(ws io.WriteSeeker, version string) error { +// _, err := ws.Seek(0, io.SeekStart) +// if err != nil { +// return fmt.Errorf("could not seek: %s", err) +// } + +// return nil +// } + +// func (tag *ID3v2Tag) WriteToFile(f *os.File) error { +// return nil +// } diff --git a/v2/write_test.go b/v2/write_test.go new file mode 100644 index 0000000..1d8988c --- /dev/null +++ b/v2/write_test.go @@ -0,0 +1,16 @@ +package v2 + +// var TESTTAG = &ID3v2Tag{ +// Frames: []Frame{ +// Frame{ +// Header: FrameHeader{ +// ID: "COMM", +// }, +// Contents: []byte("comment_here"), +// }, +// }, +// } + +// func TestWrite(t *testing.T) { +// t.Errorf("%v", TESTTAG.Header.Version) +// }