Search code examples
c#.netid3v2

ID3v2 tag issue with APIC in C#/.NET


I'm writing my own ID3v2 tag reader, but I seem to be having an issue with the APIC tag. Whenever it reads it, it'll say it's 2,038,335 bytes big when it's really 511,281 bytes big. I have no idea as to why and I have a feeling it's something to do with this line:

int size = ( data[ i ] << 24 ) | ( data[ ++i ] << 16 ) | ( data[ ++i ] << 8 ) | ( data[ ++i ] << 0 ) - 1;

But this line works for every other tag I've tested against. It could also be that I'm missing something in the ID3v2 tag standard or something entirely else. Here is the piece of code that is meant to store the image for later:

List<byte> temp = new List<byte>();

            for( int i = 1; i < frame.Length; i++ ) {
                if(frame[i] == 0x00 ) {
                    break;
                } else {
                    temp.Add( frame[ i ] );
                }
            }

            string strVer = Encoding.Default.GetString( temp.ToArray() );

            if ( strVer.StartsWith( "image/" ) ) {
                List<byte> image = new List<byte>();

                for(int i = temp.Count + 1 + frame[ temp.Count + 2 ]; i < frame.Length; i++ ) {
                    image.Add( frame[ i ] );
                }

                tagInfo[ tag ] = image.ToArray();
            }

            tagInfo[ tag ] = frame;

The file I'm using for testing is "Resistence by Aero Chord" with a 2000x2000 pixel image.

Here's a script that produces the same size:

using System;
using System.IO;
using System.Text;
using System.Threading;

class Program {
    static void Main( string[] args ) {
        int tagSize = 0;

        byte[] fileData = byte[] fileData = { 0x41, 0x50, 0x49, 0x43, 0x00, 0x1f, 0x1a, 0x3f };

        for ( int i = 0; i < fileData.Length - 3; i++ ) {
            string tag = Encoding.Default.GetString( new byte[] { fileData[ i ], fileData[ i + 1 ], fileData[ i + 2 ], fileData[ i + 3 ] } );

            if ( tag.Equals( "APIC" ) ) {
                i += 4;

                tagSize = ( fileData[ i ] << 24 ) | ( fileData[ ++i ] << 16 ) | ( fileData[ ++i ] << 8 ) | ( fileData[ ++i ] << 0 ) - 1;
                break;
            }
        }

        Console.Write( $"Tag stated size: {tagSize} bytes." );

        Thread.Sleep( 60000 );
    }
}

Expected: 511,281 bytes

Result: 2,038,334 bytes

Uses the image that was embedded into the file and the APIC tag from the file.


Solution

  • From the standard:

    The ID3 tag size is encoded with four bytes where the first bit (bit 7) is set to zero in every byte, making a total of 28 bits. The zeroed bits are ignored, so a 257 bytes long tag is represented as $00 00 02 01

    Your byte values are eight bits in size, but you need the seven last bits, dropping the first bit which'll always have a zero value.

    So:

    tagSize = (tagSize[i] << 21) | (tagSize[++i] << 14) | (tagSize[++i] << 7) | (tagSize[++i] << 0);
    

    This will yield a size of 511295 for an input of 0x00, 0x1f, 0x1a, 0x3f.