2

I am making an id3 tag editor in C. I am having trouble figuring out how to pull the last 128 bytes off the end of the binary file in order to manipulate/printout the area where the id3 tag sits. Heres some code:

struct Tag{
    char tagMark[3];
    char trackName[30];
    char artistName[30];
    char albumName[30];
    char year[4];
    char comment[30];
    char genre;
};

int main(int argc, char *argv[]){
    struct Tag fileTag;
    FILE *fp;
    fp=fopen(argv[0], "r+b");
    if(!fp){
        printf("ERROR: File does not exist.");
    }

    int bufLength=129;
    fseek(fp, 0, SEEK_END);
    long fileLength=ftell(fp);
    fseek(fp, fileLength-bufLength+1, SEEK_SET);
    fread(&fileTag, sizeof(fileTag), 1, fp);

    printf("%s\n", fileTag.tagMark);
    return 0;
}

I am using a file to test this with that contains a properly formatted id3 tag. In an id3 tag, the first three bytes contain 'T', 'A', and 'G' respectively in order to identify that the tag exists. Does someone know why when I run this program, "_main" is the only thing that prints out?

dcfc_rph
  • 299
  • 2
  • 3
  • 10

2 Answers2

5

Use fseek() (or lseek() if you're using file descriptors instead of file streams) with a negative offset (-128) bytes from the end of the file. Then read the 128 bytes of information.

Hence:

fseek(fp, -128L, SEEK_END);
lseek(fd, -128L, SEEK_END);

(Interestingly, the SEEK_END, SEEK_SET and SEEK_CUR macros are defined in both <stdio.h> for standard C and in <unistd.h> for POSIX.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • This is good advice, certainly, but in this case it will still be reading from the wrong file! @fvu has identified the actual problem. – Matthew Slattery Sep 22 '11 at 23:33
  • Yeah...I was kinda assuming that the user would check that they're opening the correct file. I forget that the basics are necessary, sometimes. – Jonathan Leffler Sep 22 '11 at 23:39
4

Your program does

fp=fopen(argv[0], "r+b");

argv[0] contains the name of the running executable, you want argv[1], the first command line parameter. The next problem you'll probably run into (when starting with ID3v2) is structure field alignment, depending on the datatypes used the compiler may leave "gaps" between the consecutive members of a structure. To avoid this problem the compiler should be instructed to align the structure on byte boundaries with (in most cases, check your compiler's documentation)

#pragma pack(1) 

This Structure padding and packing SO answer explains clearly what happens if you don't use the correct packing.

Also, see the ID3V1 layout, there's not always a terminating 0 after the individual fields, so

printf("%s\n", fileTag.tagMark);

will print TAG followed by the song title (printf %s only stops when it encounters a \0).

IMHO, given the jungle that ID3 is you're probably better off delegating the raw ID3 manipulation to an existing library like id3lib and focus on the functionality of your editor.

Community
  • 1
  • 1
fvu
  • 32,488
  • 6
  • 61
  • 79
  • +1 for the correct file name issue. I'm not aware of a C compiler that will align char fields (as found in the structure shown) on multiples of 4 bytes; they would be aligned on multiples of 1 byte on all the compilers I've seen. (If one of the types was `int` or `double`, that would be a different issue.) – Jonathan Leffler Sep 22 '11 at 23:38
  • @Jonathan "next problem" refers to ID3v2.x which OP will have to support as well to make his program useful, and that one has fields with several datatypes. – fvu Sep 22 '11 at 23:54
  • For the given situation, id3v1 is the only version I have to worry about. – dcfc_rph Sep 23 '11 at 01:47