Example #1
0
void Export_ID3(FILE *inf, FILE *outf)
{
	struct ID3Tag tag={NULL,0,0,0,0,0,0.0,NULL,0,NULL,NULL,NULL,NULL};

//Validate parameters
	assert_wrapper((inf != NULL) && (outf != NULL));
	assert_wrapper(Lyrics.piececount != 0);	//This function is not to be called with an empty Lyrics structure

	tag.fp=inf;	//Store the input file pointer into the ID3 tag structure

//Seek to first MPEG frame in input file (after ID3 tag, otherwise presume it's at the beginning of the file)
	if(Lyrics.verbose)	(void) puts("Parsing input MPEG audio file");
	if(FindID3Tag(&tag))		//Find start and end of ID3 tag
		fseek_err(tag.fp,tag.tagend,SEEK_SET);	//Seek to the first MP3 frame (immediately after the ID3 tag)
	else
		rewind_err(tag.fp);	//Rewind file if no ID3 tag is found

//Load MPEG information
	if(GetMP3FrameDuration(&tag) == 0)	//Find the sample rate defined in the MP3 frame at the current file position
	{
		printf("Error loading MPEG information from file \"%s\"\nAborting",Lyrics.srcfilename);
		exit_wrapper(1);
	}

//Load any existing ID3 frames
	(void) ID3FrameProcessor(&tag);

//Build output file
	(void) BuildID3Tag(&tag,outf);

//Release memory
	DestroyID3(&tag);	//Release the ID3 structure's memory
	DestroyOmitID3framelist(Lyrics.nosrctag);	//Release ID3 frame omission list
}
Example #2
0
void DisplayID3Tag(char *filename)
{
	char *buffer=NULL;	//A buffer to store strings returned from ReadTextInfoFrame()
	struct ID3Tag tag={NULL,0,0,0,0,0,0.0,NULL,0,NULL,NULL,NULL,NULL};
	struct ID3Frame *temp=NULL;	//Conductor for the ID3 frames list

//Validate parameters
	if(!filename)
		return;

	tag.fp=fopen(filename,"rb");
	if(tag.fp == NULL)
		return;

	(void) ID3FrameProcessor(&tag);
	if(tag.id3v1present > 1)	//If the ID3v1 tag exists and is populated
	{
		(void) puts("ID3v1 tag:");
		if(tag.id3v1title != NULL)
			printf("\tTitle = \"%s\"\n",tag.id3v1title);
		if(tag.id3v1artist != NULL)
			printf("\tArtist = \"%s\"\n",tag.id3v1artist);
		if(tag.id3v1album != NULL)
			printf("\tAlbum = \"%s\"\n",tag.id3v1album);
		if(tag.id3v1year != NULL)
			printf("\tYear = \"%s\"\n",tag.id3v1year);
	}

//Display ID3v2 frames
	if(tag.frames != NULL)
	{
		(void) puts("ID3v2 tag:");
		for(temp=tag.frames;temp != NULL;temp=temp->next)
		{	//For each frame that was parsed, check the tags[] array to see if it is a text info frame
			assert_wrapper(temp->frameid != NULL);
			if(IsTextInfoID3FrameID(temp->frameid))
			{
				fseek_err(tag.fp,temp->pos,SEEK_SET);	//Seek to the ID3 frame in question
				buffer=ReadTextInfoFrame(tag.fp);		//Read and store the text info frame content
				if(buffer == NULL)
					printf("\tFrame: %s = Unreadable or Unicode format\n",temp->frameid);
				else
					printf("\tFrame: %s = \"%s\"\n",temp->frameid,buffer);
				free(buffer);	//Release text info frame content
				buffer=NULL;
			}
			else
				printf("\t%s frame present\n",temp->frameid);
		}//For each frame that was parsed
	}
	else if(!tag.id3v1present)
		(void) puts("No ID3 Tag detected");

	DestroyID3(&tag);	//Release the ID3 structure's memory
	(void) fclose(tag.fp);
}
Example #3
0
int ValidateID3FrameHeader(struct ID3Tag *ptr)
{
	unsigned long position=0;
	unsigned char buffer[10]={0};	//Used to read the frame header
	unsigned long ctr=0;
	unsigned long framesize=0;
	char success=1;	//Will assume success unless failure is detected

//Validate input parameters
	if((ptr == NULL) || (ptr->fp == NULL))
		return 0;	//Return failure

//Record file position
	position=ftell_err(ptr->fp);

//If file position is less than the start of the ID3 tag, return failure
	if(position < ptr->framestart)
		return 0;	//Return failure

//Read 10 bytes into buffer
	if(fread(buffer,10,1,ptr->fp) != 1)	//If there was a file I/O error
		success=0;	//Record failure

//Validate header:  Verify that each of the first four buffered characters are an uppercase letter or numerical character
	if(success)
	{
		for(ctr=0;ctr<4;ctr++)
		{
			if(!isupper(buffer[ctr]) && !isdigit(buffer[ctr]))	//If the character is valid for an ID3 frame ID
			{
				success=0;	//Record failure
				break;
			}
		}
	}

//Validate frame position:  Verify that the end of the frame occurs at or before the end of the ID3 tag
	if(success)
	{
		framesize=((unsigned long)buffer[4]<<24) | ((unsigned long)buffer[5]<<16) | ((unsigned long)buffer[6]<<8) | ((unsigned long)buffer[7]);	//Convert to 4 byte integer
		if(position + framesize + 10 >= ptr->tagend)	//If the defined frame size would cause the frame to extend beyond the end of the ID3 tag
			success=0;	//Record failure
	}

//Seek to original file position
	fseek_err(ptr->fp,position,SEEK_SET);

//Return failure/success status
	return success;	//Return success/failure status
}
Example #4
0
int EOF_EXPORT_TO_LC(EOF_VOCAL_TRACK * tp, char *outputfilename, char *string2, int format)
{
	unsigned long linectr = 0, lyrctr = 0, lastlyrtime = 0, linestart = 0, lineend = 0;
	unsigned char pitch = 0;
	FILE *outf = NULL;			//Used to open output file
	FILE *pitchedlyrics = NULL;	//Used to open output pitched lyric fle
	char *vrhythmid = NULL;
	EOF_PHRASE_SECTION temp;	//Used to store the first lyric line in the project, which gets overridden with one covering all lyrics during RS1 export
	unsigned long original_lines;
	char *tempoutputfilename = "lyrics.temp";

	eof_log("EOF_EXPORT_TO_LC() entered", 1);

	if((tp == NULL) || (outputfilename == NULL) || (tp->lyrics == 0))
		return -1;	//Return failure

//Initialize variables
	InitLyrics();	//Initialize all variables in the Lyrics structure
	InitMIDI();		//Initialize all variables in the MIDI structure

	qsort(tp->line, (size_t)tp->lines, sizeof(EOF_PHRASE_SECTION), eof_song_qsort_phrase_sections);	//Sort the lyric lines
	temp = tp->line[0];			//Preserve the original lyric line information
	original_lines = tp->lines;

//Set export-specific settings
	if(format == SCRIPT_FORMAT)
	{
		Lyrics.grouping = 2;	//Enable line grouping for script.txt export
		Lyrics.nohyphens = 3;	//Disable hyphen output
		Lyrics.noplus = 1;	//Disable plus output
		Lyrics.filter = DuplicateString("^=%#/");	//Use default filter list
		Lyrics.defaultfilter = 1;	//Track that the above string will need to be freed
	}
	else if((format == RS_FORMAT) || (format == RS2_FORMAT))
	{
		Lyrics.noplus = 1;	//Disable plus output
		Lyrics.filter = DuplicateString("^=%#/");	//Use default filter list
		Lyrics.defaultfilter = 1;	//Track that the above string will need to be freed
		if((format == RS_FORMAT) || (!tp->lines))
		{	//If exporting to Rocksmith 1 format or if the lyrics don't have any lines defined
			tp->lines = 0;		//Temporarily disregard any existing lyric lines
			(void) eof_vocal_track_add_line(tp, 0, tp->lyric[tp->lyrics - 1]->pos + 1, 0xFF);	//Create a single line encompassing all lyrics
		}
	}
	else if(format == PLAIN_FORMAT)
	{	//This format option is meant to invoke script export with the plain flag set and filtering enabled
		Lyrics.nohyphens = 3;	//Disable hyphen output
		Lyrics.noplus = 1;	//Disable plus output
		Lyrics.filter = DuplicateString("^=%#/");	//Use default filter list
		Lyrics.defaultfilter = 1;	//Track that the above string will need to be freed
		format = SCRIPT_FORMAT;
		Lyrics.plain = 1;
		Lyrics.grouping = 2;	//Enable line grouping for script.txt export
	}

//Import lyrics from EOF structure
	lyrctr = 0;		//Begin indexing into lyrics from the very first
	lastlyrtime = 0;	//First lyric is expected to be greater than or equal to this timestamp
	for(linectr = 0; linectr < (unsigned long)tp->lines; linectr++)
	{	//For each line of lyrics in the EOF structure
		linestart = (tp->line[linectr]).start_pos;
		lineend = (tp->line[linectr]).end_pos;

		if(linestart > lineend)	//If the line starts after it ends
		{
			ReleaseMemory(1);
			return -1;			//Return failure
		}

		if(lyrctr < tp->lyrics)	//If there are lyrics remaining
			CreateLyricLine();	//Initialize new line of lyrics

		if((tp->line[linectr]).flags & EOF_LYRIC_LINE_FLAG_OVERDRIVE)	//If this line is overdrive
			Lyrics.overdrive_on = 1;
		else
			Lyrics.overdrive_on = 0;

		while(lyrctr < tp->lyrics)
		{	//For each lyric
			if((tp->lyric[lyrctr])->text[0] != '\0')
			{	//If this lyric's text isn't an empty string
				if((tp->lyric[lyrctr])->pos < lastlyrtime)	//If this lyric precedes the previous lyric
				{
					(void) snprintf(eof_log_string, sizeof(eof_log_string) - 1, "\tLogic error while preparing lyrics for export to file \"%s\"", tempoutputfilename);
					eof_log(eof_log_string, 1);
					ReleaseMemory(1);
					return -1;				//Return failure
				}
				if((tp->lyric[lyrctr])->pos < linestart)		//If this lyric precedes the beginning of the line
				{
					(void) snprintf(eof_log_string, sizeof(eof_log_string) - 1, "\tWarning:  Lyric \"%s\" at %lums is outside of defined lyric lines", tp->lyric[lyrctr]->text, tp->lyric[lyrctr]->pos);
					eof_log(eof_log_string, 1);
					CreateLyricLine();	//Initialize new line of lyrics
				}
				if((tp->lyric[lyrctr])->pos > lineend)		//If this lyric is placed beyond the end of this line
				{
					break;					//Break from this while loop to have another line created
				}

				pitch = (tp->lyric[lyrctr])->note;			//Store the lyric's pitch
				if((tp->lyric[lyrctr])->note == 0)			//Remap EOF's pitchless value to FLC's pitchless value
					pitch = PITCHLESS;

				if(!Lyrics.line_on)		//If a lyric line is not in progress
					CreateLyricLine();	//Force one to be before adding the next lyric
				AddLyricPiece((tp->lyric[lyrctr])->text, (tp->lyric[lyrctr])->pos, (tp->lyric[lyrctr])->pos+(tp->lyric[lyrctr])->length, pitch, 0);
					//Add the lyric to the Lyrics structure

				if((Lyrics.lastpiece != NULL) && (Lyrics.lastpiece->lyric[strlen(Lyrics.lastpiece->lyric)-1] == '-'))	//If the piece that was just added ended in a hyphen
					Lyrics.lastpiece->groupswithnext = 1;	//Set its grouping status
			}//If this lyric's text isn't an empty string

			lyrctr++;	//Advance to next lyric
		}

		ForceEndLyricLine();	//End the current line of lyrics
	}

	if(Lyrics.piececount == 0)	//No lyrics imported
	{
		ReleaseMemory(1);
		return 0;		//Return no lyrics found
	}

//Load chart tags
	if(eof_song->tags->artist[0] != '\0')
		Lyrics.Artist = DuplicateString(eof_song->tags->artist);
	if(eof_song->tags->title[0] != '\0')
		Lyrics.Title = DuplicateString(eof_song->tags->title);
	if(eof_song->tags->frettist[0] != '\0')
		Lyrics.Editor = DuplicateString(eof_song->tags->frettist);
	if(eof_song->tags->album[0] != '\0')
		Lyrics.Album = DuplicateString(eof_song->tags->album);

	PostProcessLyrics();	//Perform hyphen and grouping validation/handling

	Lyrics.outfilename = tempoutputfilename;
	Lyrics.out_format = format;

	//If the export format is MIDI-based, write a MIDI file header and a MIDI track (track 0) specifying a tempo of 120BPM
	if((Lyrics.out_format == MIDI_FORMAT) || (Lyrics.out_format == VRHYTHM_FORMAT) || (Lyrics.out_format == SKAR_FORMAT) || (Lyrics.out_format == KAR_FORMAT))
	{
		outf = fopen_err(Lyrics.outfilename,"wb");	//These are binary formats
		Write_Default_Track_Zero(outf);
	}

//Export lyrics
	switch(Lyrics.out_format)
	{
		case SCRIPT_FORMAT:	//Export as script.txt format file
			outf = fopen_err(Lyrics.outfilename,"wt");	//Script.txt is a text format
			Export_Script(outf);
		break;

		case VL_FORMAT:	//Export as VL format file
			outf = fopen_err(Lyrics.outfilename,"wb");	//VL is a binary format
			Export_VL(outf);
		break;

		case MIDI_FORMAT:	//Export as MIDI format file.  Default export track is "PART VOCALS"
			if(string2 == NULL)						//If a destination track name wasn't given
				Lyrics.outputtrack = DuplicateString("PART VOCALS");	//Write track name as PART VOCALS by default
			else
				Lyrics.outputtrack = DuplicateString(string2);
			Export_MIDI(outf);
		break;

		case USTAR_FORMAT:	//Export as UltraStar format file
			outf = fopen_err(Lyrics.outfilename,"wt");	//UltraStar is a text format
			Export_UStar(outf);
		break;

		case LRC_FORMAT:	//Export as simple LRC
		case ELRC_FORMAT:	//Export as extended LRC
			outf = fopen_err(Lyrics.outfilename,"wt");	//LRC is a text format
			Export_LRC(outf);
		break;

		case VRHYTHM_FORMAT:	//Export as Vocal Rhythm (MIDI and text file)
			if(string2 == NULL)	//If a pitched lyric file wasn't given
			{
				fclose_err(outf);
				return -1;	//Return failure
			}

			pitchedlyrics = fopen_err(string2,"wt");	//Pitched lyrics is a text format
			vrhythmid = DuplicateString("G4");
			Export_Vrhythm(outf, pitchedlyrics, vrhythmid);
			fflush_err(pitchedlyrics);	//Commit any pending pitched lyric writes to file
			fclose_err(pitchedlyrics);	//Close pitched lyric file
			free(vrhythmid);
		break;

		case SKAR_FORMAT:	//Export as Soft Karaoke.  Default export track is "Words"
			if(string2 == NULL)						//If a destination track name wasn't given
				Lyrics.outputtrack = DuplicateString("Words");	//Write track name as "Words" by default
			else
				Lyrics.outputtrack = DuplicateString(string2);
			Export_SKAR(outf);
		break;

		case KAR_FORMAT:	//Export as unofficial KAR.  Default export track is "Melody"
			if(Lyrics.outputtrack == NULL)
			{
				(void) puts("\aNo ouput track name for KAR file was given.  A track named \"Melody\" will be used by default");
				Lyrics.outputtrack = DuplicateString("Melody");
			}
			Export_MIDI(outf);
		break;

		case RS_FORMAT:	//Export as Rocksmith XML
			outf = fopen_err(Lyrics.outfilename,"wt");	//XML is a text format
			Lyrics.rocksmithver = 1;
			Export_RS(outf);
		break;

		case RS2_FORMAT:	//Export as Rocksmith 2 XML
			outf = fopen_err(Lyrics.outfilename,"wt");	//XML is a text format
			Lyrics.rocksmithver = 2;
			Export_RS(outf);
		break;

		default:
			(void) puts("Unexpected error in export switch\nAborting");
			exit_wrapper(4);
		break;
	}

	if((Lyrics.out_format == MIDI_FORMAT) || (Lyrics.out_format == VRHYTHM_FORMAT) || (Lyrics.out_format == SKAR_FORMAT) || (Lyrics.out_format == KAR_FORMAT))
	{	//Update the MIDI header to reflect the number of MIDI tracks written to file for all applicable export formats
		fseek_err(outf, 10, SEEK_SET);		//The number of tracks is 10 bytes in from the start of the file header
		fputc_err(MIDIstruct.trackswritten>>8, outf);
		fputc_err(MIDIstruct.trackswritten&0xFF, outf);
	}
Example #5
0
void Export_VL(FILE *outf)
{
    struct _VLSTRUCT_ *OutVL=NULL;			//The prepared VL structure to write to file
    struct VL_Text_entry *curtext=NULL;		//Conductor for the text chunk list
    struct VL_Text_entry *textnext=NULL;	//Used for OutVL deallocation
    struct VL_Sync_entry *cursync=NULL;		//Conductor for the sync chunk list
    struct VL_Sync_entry *syncnext=NULL;	//Used for OutVL deallocation
    unsigned long ctr=0;
    long filepos=0;

    assert_wrapper(outf != NULL);			//This must not be NULL
    assert_wrapper(Lyrics.piececount != 0);	//This function is not to be called with an empty Lyrics structure

    if(Lyrics.verbose)	printf("\nExporting VL lyrics to file \"%s\"\n\n",Lyrics.outfilename);

//Write VL header: 'V','L','2',0
    fputc_err('V',outf);
    fputc_err('L',outf);
    fputc_err('2',outf);
    fputc_err(0,outf);

//Pad the next four bytes, which are supposed to hold the size of the file, minus the size of the VL header (8 bytes)
    WriteDWORDLE(outf,0);

    if(Lyrics.verbose>=2)	(void) puts("Writing text chunk");

//Write Text chunk header: 'T','E','X','T'
    fputc_err('T',outf);
    fputc_err('E',outf);
    fputc_err('X',outf);
    fputc_err('T',outf);

//Pad the next four bytes, which are supposed to hold the size of the header chunk, minus the size of the chunk header (8 bytes)
    WriteDWORDLE(outf,0);

//Convert the Offset string to centiseconds from milliseconds (as this is how the VL format interprets it)
    if((Lyrics.Offset != NULL))	//If there is an offset
    {
        if(strlen(Lyrics.Offset) < 2)	//If the offset is 0 or 1 characters long, it will be lost, free the string
        {
            free(Lyrics.Offset);
            Lyrics.Offset=NULL;
        }
        else
            Lyrics.Offset[strlen(Lyrics.Offset)-1]='\0';	//Truncate last digit to effectively divide by ten
    }

//Write 8 Unicode strings, for Title, Artist, Album, Editor, Offset and 3 empty strings
    WriteUnicodeString(outf,Lyrics.Title);
    WriteUnicodeString(outf,Lyrics.Artist);
    WriteUnicodeString(outf,Lyrics.Album);
    WriteUnicodeString(outf,Lyrics.Editor);
    WriteUnicodeString(outf,NULL);	//The Offset tag should be 0 because all timestamps are written as being absolute
    WriteUnicodeString(outf,NULL);
    WriteUnicodeString(outf,NULL);
    WriteUnicodeString(outf,NULL);

//Create sync and lyric structures
    OutVL=VL_PreWrite();

//Write lyric strings
    curtext=OutVL->Lyrics;

    if(Lyrics.verbose)	(void) puts("Writing VL file");

    while(curtext != NULL)	//For each text chunk
    {
        assert_wrapper(curtext->text != NULL);

        if(Lyrics.verbose)	printf("\tText chunk entry written: \"%s\"\n",curtext->text);

        WriteUnicodeString(outf,curtext->text);
        curtext=curtext->next;					//Point to next text chunk entry
    }

//Write padding if necessary
    ctr=ftell_err(outf)%4;	//ctr=# of padding bytes needed to align output file position to a DWORD boundary
    while(ctr--)	//For each byte of padding necessary
        fputc_err(0,outf);

//Go back and write the text chunk size after the text chunk header
    filepos=ftell_err(outf);		//Store file position
    OutVL->textsize=filepos-16;		//File position - VL and Text header sizes = text chunk size
    fseek_err(outf,12,SEEK_SET);	//File position 12 is where the text chunk size is to be stored
    WriteDWORDLE(outf,OutVL->textsize);	//Write text chunk size to file
    fseek_err(outf,filepos,SEEK_SET);		//Seek back to the end of the text chunk (where the Sync chunk will begin)

    if(Lyrics.verbose>=2)	(void) puts("Writing Sync chunk");

//Write Sync chunk header: 'S','Y','N','C'
    fputc_err('S',outf);
    fputc_err('Y',outf);
    fputc_err('N',outf);
    fputc_err('C',outf);

//Pad the next four bytes, which are supposed to hold the size of the sync chunk, minus the size of the chunk header (8 bytes)
    WriteDWORDLE(outf,0);

//Write sync entries
    cursync=OutVL->Syncs;
    while(cursync != NULL)
    {
        WriteWORDLE(outf,cursync->lyric_number);//Write lyric line number this sync entry pertains to
        WriteWORDLE(outf,0);					//Write reserved Word value
        WriteWORDLE(outf,cursync->start_char);	//Write start offset
        WriteWORDLE(outf,cursync->end_char);	//Write end offset
        WriteDWORDLE(outf,cursync->start_time);	//Write start time
        WriteDWORDLE(outf,cursync->end_time);	//Write end time
        cursync=cursync->next;
    }

//Go back and write the sync chunk size after the sync chunk header
    filepos=ftell_err(outf);						//Store file position
    OutVL->syncsize=filepos - OutVL->textsize - 24;	//File position - Text chunk size - 3 header sizes = sync chunk size
    fseek_err(outf,OutVL->textsize+20,SEEK_SET);	//File position for sync chunk size is text chunk size +2.5 headers
    WriteDWORDLE(outf,OutVL->syncsize);				//Write sync chunk size to file

//Go back and write the VL file size after the VL header, which completes the VL export
    filepos-=8;							//Subtract the VL header size from the total file size
    fseek_err(outf,4,SEEK_SET);			//File posistion for VL file size is after the "VL2\0" string
    WriteDWORDLE(outf,filepos);			//Write the VL file size - header value

//Destroy the OutVL structure
    if(Lyrics.verbose>=2)	(void) puts("\tReleasing memory from export VL structure");
    curtext=OutVL->Lyrics;	//Point conductor at first text chunk entry
    while(curtext != NULL)	//For each text chunk entry
    {
        textnext=curtext->next;
        if(curtext->text != NULL)
            free(curtext->text);	//Free string
        free(curtext);				//Free text chunk structure
        curtext=textnext;
    }

    cursync=OutVL->Syncs;	//Point conductor at first sync chunk entry
    while(cursync != NULL)	//For each sync chunk entry
    {
        syncnext=cursync->next;
        free(cursync);		//Free sync chunk structure
        cursync=syncnext;
    }

    free(OutVL);
    if(Lyrics.verbose)	printf("\nVL export complete.  %lu lyrics written\n",Lyrics.piececount);
}
Example #6
0
int VL_PreLoad(FILE *inf,char validate)
{
    long ftell_result=0;				//Used to store return value from ftell()
    char buffer[5]= {0};					//Used to read word/doubleword integers from file
    unsigned long ctr=0;				//Generic counter
    char *temp=NULL;					//Temporary pointer for allocated strings
    struct VL_Sync_entry se= {0,0,0,0,0,NULL};		//Used to store sync entries from file during parsing
    struct VL_Text_entry *ptr1=NULL;	//Used for allocating links for the text chunk list
    struct VL_Sync_entry *ptr2=NULL;	//Used for allocation links for the sync chunk list
    struct VL_Text_entry *curtext;		//Conductor for text chunk linked list
    struct VL_Sync_entry *cursync;		//Conductor for sync chunk linked list

    assert_wrapper(inf != NULL);

//Initialize variables
    VL.numlines=0;
    VL.numsyncs=0;
    VL.filesize=0;
    VL.textsize=0;
    VL.syncsize=0;
    VL.Lyrics=NULL;
    VL.Syncs=NULL;
    curtext=NULL;	//list starts out empty
    cursync=NULL;	//list starts out empty
    se.next=NULL;	//In terms of linked lists, this always represents the last link

    if(Lyrics.verbose)	(void) puts("Reading VL file header");

//Read file header
    fread_err(buffer,4,1,inf);			//Read 4 bytes, which should be 'V','L','2',0
    ReadDWORDLE(inf,&(VL.filesize));	//Read doubleword from file in little endian format

    if(strncmp(buffer,"VL2",4) != 0)
    {
        if(validate)
            return 1;
        else
        {
            (void) puts("Error: Invalid file header string\nAborting");
            exit_wrapper(1);
        }
    }

//Read text header
    fread_err(buffer,4,1,inf);			//Read 4 bytes, which should be 'T','E','X','T'
    buffer[4]='\0';						//Add a NULL character to make buffer into a proper string
    ReadDWORDLE(inf,&(VL.textsize));	//Read doubleword from file in little endian format

    if(VL.textsize % 4 != 0)
    {
        (void) puts("Error: VL spec. violation: Sync chunk is not padded to a double-word alignment");
        if(validate)
            return 2;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(2);
        }
    }

    if(strncmp(buffer,"TEXT",5) != 0)
    {
        (void) puts("Error: Invalid text header string");
        if(validate)
            return 3;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(3);
        }
    }

//Read sync header, which should be th.textsize+16 bytes into the file
    fseek_err(inf,VL.textsize+16,SEEK_SET);
    fread_err(buffer,4,1,inf);			//Read 4 bytes, which should be 'S','Y','N','C'
    buffer[4]='\0';						//Add a NULL character to make buffer into a proper string
    ReadDWORDLE(inf,&(VL.syncsize));	//Read doubleword from file in little endian format

    if(strncmp(buffer,"SYNC",5) != 0)
    {
        (void) puts("Error: Invalid sync header string");
        if(validate)
            return 4;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(4);
        }
    }

//Validate chunk sizes given in headers (textsize was already validated if the Sync header was correctly read)
    fseek_err(inf,0,SEEK_END);	//Seek to end of file
    ftell_result = ftell(inf);	//Get position of end of file
    if(ftell_result < 0)
    {
        printf("Error determining file length during file size validation: %s\n",strerror(errno));
        if(validate)
            return 5;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(5);
        }
    }
    if((unsigned long)ftell_result != VL.filesize+8)	//Validate filesize
    {
        (void) puts("Error: Filesize does not match size given in file header");
        if(validate)
            return 6;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(6);
        }
    }
    if(VL.filesize != VL.textsize + VL.syncsize + 16)	//Validate syncsize
    {
        (void) puts("Error: Incorrect size given in sync chunk");
        if(validate)
            return 7;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(7);
        }
    }

//Load tags
    if(Lyrics.verbose)	(void) puts("Loading VL tag strings");
    fseek_err(inf,16,SEEK_SET);		//Seek to the Title tag

    for(ctr=0; ctr<5; ctr++)	//Expecting 5 null terminated unicode strings
    {
        temp=ReadUnicodeString(inf);	//Allocate array and read Unicode string

        if(temp[0] != '\0')	//If the string wasn't empty, save the tag
            switch(ctr)
            {   //Look at ctr to determine which tag we have read, and assign to the appropriate tag string:
            case 0:
                SetTag(temp,'n',0);
                break;

            case 1:
                SetTag(temp,'s',0);
                break;

            case 2:
                SetTag(temp,'a',0);
                break;

            case 3:
                SetTag(temp,'e',0);
                break;

            case 4:	//Offset
                SetTag(temp,'o',0);	//Do not multiply the offset by -1
                break;

            default:
                (void) puts("Unexpected error");
                if(validate)
                    return 8;
                else
                {
                    (void) puts("Aborting");
                    exit_wrapper(8);
                }
                break;
            }//end switch(ctr)

        free(temp);	//Free string, a copy of which would have been stored in the Lyrics structure as necessary
    }//end for(ctr=0;ctr<5;)

//Validate the existence of three empty unicode strings
    for(ctr=0; ctr<3; ctr++)	//Expecting 3 empty, null terminated unicode strings
        if(ParseUnicodeString(inf) != 0)
        {
            (void) puts("Error: Reserved string is not empty during load");
            if(validate)
                return 9;
            else
            {
                (void) puts("Aborting");
                exit_wrapper(9);
            }
        }

//Load the lyric strings
    if(Lyrics.verbose)	(void) puts("Loading text chunk entries");

    while(1)	//Read lyric lines
    {
        //Check to see if the Sync Chunk has been reached
        if((unsigned long)ftell_err(inf) >= VL.textsize + 16)	//The Text chunk size + the file and text header sizes is the position of the Sync chunk
            break;

        //Read lyric string
        temp=ReadUnicodeString(inf);

        ftell_result=ftell_err(inf);	//If this string is empty
        if(temp[0] == '\0')
        {
            if(VL.textsize+16 - ftell_result <= 3)	//This 0 word value ends within 3 bytes of the sync header (is padding)
            {
                free(temp);	//Release string as it won't be used
                break;		//text chunk has been read
            }
            else
            {
                printf("Error: Empty lyric string detected before file position 0x%lX\n",ftell_result);
                free(temp);
                if(validate)
                    return 10;
                else
                {
                    (void) puts("Aborting");
                    exit_wrapper(10);
                }
            }
        }
        if((unsigned long)ftell_result > VL.textsize + 16)	//If reading this string caused the file position to cross into Sync chunk
        {
            (void) puts("Error: Lyric string overlapped into Sync chunk");
            free(temp);
            if(validate)
                return 11;
            else
            {
                (void) puts("Aborting");
                exit_wrapper(11);
            }
        }

        if(Lyrics.verbose)	printf("\tLoading text piece #%lu: \"%s\"\n",VL.numlines,temp);

        //Allocate link
        ptr1=malloc_err(sizeof(struct VL_Text_entry));	//Allocate memory for new link

        //Build link and insert into list
        ptr1->text=temp;
        ptr1->next=NULL;

        if(VL.Lyrics == NULL)	//This is the first lyric piece read from the input file
            VL.Lyrics=ptr1;
        else
        {
            assert_wrapper(curtext != NULL);
            curtext->next=ptr1;	//The end of the list points forward to this new link
        }

        curtext=ptr1;	//This link is now at the end of the list

        VL.numlines++;	//one more lyric string has been parsed
    }//end while(1)

    if(Lyrics.verbose)	printf("%lu text chunk entries loaded\n",VL.numlines);

//Load the Sync points
    if(Lyrics.verbose)	(void) puts("Loading sync chunk entries");

    fseek_err(inf,VL.textsize+16+8,SEEK_SET);	//Seek to first sync entry (8 bytes past start of sync header)

    while(ReadSyncEntry(&se,inf) == 0)	//Read all entries
    {
        if(se.start_char > se.end_char)
        {
            (void) puts("Error: Invalid sync point offset is specified to start after end offset");
            if(validate)
                return 12;
            else
            {
                (void) puts("Aborting");
                exit_wrapper(12);
            }
        }

        if(se.lyric_number!=0xFFFF && se.start_char!=0xFFFF && se.start_time!=0xFFFFFFFF)
        {   //This is a valid sync entry
            //Allocate link
            ptr2=malloc_err(sizeof(struct VL_Sync_entry));

            //Build link and insert into list
            memcpy(ptr2,&se,sizeof(struct VL_Sync_entry));	//copy structure into newly allocated link
            if(VL.Syncs == NULL)	//This is the first sync piece read from the input file
                VL.Syncs=ptr2;
            else					//The end of the list points forward to this new link
            {
                assert_wrapper(cursync != NULL);
                cursync->next=ptr2;
            }

            cursync=ptr2;		//This link is now at the end of the list
            VL.numsyncs++;		//one more sync entry has been parsed
        }
    }

    if(Lyrics.verbose)	(void) puts("VL_PreLoad completed");
    return 0;
}
Example #7
0
unsigned long BuildID3Tag(struct ID3Tag *ptr,FILE *outf)
{
	unsigned long tagpos=0;		//Records the position of the ID3 tag in the output file
	unsigned long framepos=0;	//Records the position of the SYLT frame in the output file
	unsigned long ctr=0;
	struct ID3Frame *temp=NULL;
	struct Lyric_Line *curline=NULL;	//Conductor of the lyric line linked list
	struct Lyric_Piece *curpiece=NULL;	//A conductor for the lyric pieces list
	unsigned long framesize=0;	//The calculated size of the written SYLT frame
	unsigned long tagsize=0;	//The calculated size of the modified ID3 tag
	unsigned char array[4]={0};	//Used to build the 28 bit ID3 tag size
	unsigned char defaultID3tag[10]={'I','D','3',3,0,0,0,0,0,0};
		//If the input file had no ID3 tag, this tag will be written
	unsigned long fileendpos=0;	//Used to store the size of the input file
	char newline=0;				//ID3 spec indicates that newline characters should be used at the beginning
								//of the new line instead of at the end of a line, use this to track for this

//Validate input parameters
	if((ptr == NULL) || (ptr->fp == NULL) || (outf == NULL))
		return 0;	//Return failure

	if(Lyrics.verbose)	printf("\nExporting ID3 lyrics to file \"%s\"\n",Lyrics.outfilename);

//Conditionally copy the existing ID3v2 tag from the source file, or create one from scratch
	if(ptr->frames == NULL)
	{	//If there was no ID3 tag in the input file
		if(Lyrics.verbose)	(void) puts("Writing new ID3v2 tag");
		tagpos=ftell_err(outf);	//Record this file position so the tag size can be rewritten later
		fwrite_err(defaultID3tag,10,1,outf);	//Write a pre-made ID3 tag
	//Write tag information obtained from input file
		if(Lyrics.Title != NULL)
			WriteTextInfoFrame(outf,"TIT2",Lyrics.Title);					//Write song title frame
		if(Lyrics.Artist != NULL)
			WriteTextInfoFrame(outf,"TPE1",Lyrics.Artist);					//Write song artist frame
		if(Lyrics.Album != NULL)
			WriteTextInfoFrame(outf,"TALB",Lyrics.Album);					//Write album frame
}
	else
	{	//If there was an ID3v2 tag in the source file
		rewind_err(ptr->fp);

//If the ID3v2 header isn't at the start of the source file, copy all data that precedes it to the output file
		BlockCopy(ptr->fp,outf,(size_t)(ptr->tagstart - ftell_err(ptr->fp)));

//Copy the original ID3v2 header from source file to output file (record the file position)
		if(Lyrics.verbose)	(void) puts("Copying ID3v2 tag header");
		tagpos=ftell_err(outf);	//Record this file position so the tag size can be rewritten later
		BlockCopy(ptr->fp,outf,(size_t)(ptr->framestart - ftell_err(ptr->fp)));

//Write tag information from input file if applicable, and ensure that equivalent ID3v2 frames from the source file are omitted
		if(Lyrics.Title != NULL)
		{
			WriteTextInfoFrame(outf,"TIT2",Lyrics.Title);					//Write song title frame
			Lyrics.nosrctag=AddOmitID3framelist(Lyrics.nosrctag,"TIT2");	//Linked list create/append to omit source song title frame
		}
		if(Lyrics.Artist != NULL)
		{
			WriteTextInfoFrame(outf,"TPE1",Lyrics.Artist);					//Write song artist frame
			Lyrics.nosrctag=AddOmitID3framelist(Lyrics.nosrctag,"TPE1");	//Linked list create/append to omit source song artist frame
		}
		if(Lyrics.Album != NULL)
		{
			WriteTextInfoFrame(outf,"TALB",Lyrics.Album);					//Write album frame
			Lyrics.nosrctag=AddOmitID3framelist(Lyrics.nosrctag,"TALB");	//Linked list create/append to omit source album frame
		}
		if(Lyrics.Year != NULL)
		{
			WriteTextInfoFrame(outf,"TYER",Lyrics.Year);					//Write year frame
			Lyrics.nosrctag=AddOmitID3framelist(Lyrics.nosrctag,"TYER");	//Linked list create/append to omit source year frame
		}

//Omit any existing SYLT frame from the source file
		Lyrics.nosrctag=AddOmitID3framelist(Lyrics.nosrctag,"SYLT");		//Linked list create/append with SYLT frame ID to ensure SYLT source frame is omitted

//Write all frames from source MP3 to export MP3 except for those in the omit list
		for(temp=ptr->frames;temp!=NULL;temp=temp->next)
		{	//For each ID3Frame in the list
			if(SearchOmitID3framelist(Lyrics.nosrctag,temp->frameid) == 0)	//If the source frame isn't to be omitted
			{
				if(Lyrics.verbose >= 2)	printf("\tCopying frame \"%s\"\n",temp->frameid);

				if((unsigned long)ftell_err(ptr->fp) != temp->pos)	//If the input file isn't already positioned at the frame
					fseek_err(ptr->fp,temp->pos,SEEK_SET);			//Seek to it now
				BlockCopy(ptr->fp,outf,(size_t)temp->length + 10);	//Copy frame body size + header size number of bytes
				ctr++;	//Increment counter
			}
			else if(Lyrics.verbose >= 2)	printf("\tOmitting \"%s\" frame from source file\n",temp->frameid);
		}
	}//If there was an ID3 tag in the input file

	if(Lyrics.verbose)	(void) puts("Writing SYLT frame header");

//Write SYLT frame header
	framepos=ftell_err(outf);	//Record this file position so the frame size can be rewritten later
	fputs_err("SYLTxxxx\xC0",outf);	//Write the Frame ID, 4 dummy bytes for the unknown frame size and the first flag byte (preserve frame for both tag and file alteration)
	fputc_err(0,outf);		//Write second flag byte (no compression, encryption or grouping identity)
	fputc_err(0,outf);		//ASCII encoding
	fputs_err("eng\x02\x01",outf);	//Write the language as "English", the timestamp format as milliseconds and the content type as "lyrics"
	fputs_err(PROGVERSION,outf);	//Embed the program version as the content descriptor
	fputc_err(0,outf);		//Write NULL terminator for content descriptor

//Write SYLT frame using the Lyrics structure
	curline=Lyrics.lines;	//Point lyric line conductor to first line of lyrics

	if(Lyrics.verbose)	(void) puts("Writing SYLT lyrics");

	while(curline != NULL)	//For each line of lyrics
	{
		curpiece=curline->pieces;	//Starting with the first piece of lyric in this line
		while(curpiece != NULL)		//For each piece of lyric in this line
		{
			if(newline)					//If the previous lyric was the last in its line
			{
				fputc_err('\n',outf);	//Append a newline character in front of this lyric entry
				newline=0;				//Reset this status
			}
			fputs_err(curpiece->lyric,outf);	//Write the lyric
			if(Lyrics.verbose >= 2)	printf("\t\"%s\"\tstart=%lu\t",curpiece->lyric,curpiece->start);

//Line/word grouping logic
			if(curpiece->next == NULL)		//If this is the last lyric in the line
			{
				newline=1;
				if(Lyrics.verbose >= 2) printf("(newline)");
			}
			else if(!curpiece->groupswithnext)	//Otherwise, if this lyric does not group with the next
			{
				fputc_err(' ',outf);			//Append a space
				if(Lyrics.verbose >= 2)	printf("(whitespace)");
			}
			if(Lyrics.verbose >= 2)	(void) putchar('\n');

			fputc_err(0,outf);					//Write a NULL terminator
			WriteDWORDBE(outf,curpiece->start);	//Write the lyric's timestamp as a big endian value
			curpiece=curpiece->next;			//Point to next lyric in the line
		}
		curline=curline->next;				//Point to next line of lyrics
	}

	framesize=ftell_err(outf)-framepos-10;	//Find the length of the SYLT frame that was written (minus frame header size)
	ctr++;	//Increment counter

	if(ptr->frames != NULL)
	{	//If the input MP3 had an ID3 tag
//Copy any unidentified data (ie. padding) that occurs between the end of the last ID3 frame in the list and the defined end of the ID3 tag
		for(temp=ptr->frames;temp->next!=NULL;temp=temp->next);		//Seek to last frame link
		fseek_err(ptr->fp,temp->pos + temp->length + 10,SEEK_SET);	//Seek to one byte past the end of the frame
		BlockCopy(ptr->fp,outf,(size_t)(ptr->tagend - ftell_err(ptr->fp)));
	}

	tagsize=ftell_err(outf)-tagpos-10;	//Find the length of the ID3 tag that has been written (minus tag header size)

	if(Lyrics.verbose)	(void) puts("Copying audio data");
	fileendpos=GetFileEndPos(ptr->fp);	//Find the position of the last byte in the input MP3 file (the filesize)

	if(ptr->id3v1present && SearchOmitID3framelist(Lyrics.nosrctag,"*"))	//If the user specified to leave off the ID3v1 tag, and the source MP3 has an ID3 tag
	{
		BlockCopy(ptr->fp,outf,(size_t)(fileendpos - ftell_err(ptr->fp) - 128));			//Copy rest of source file to output file (minus 128 bytes, the size of the ID3v1 tag)
		ptr->id3v1present=0;												//Consider the tag as being removed, so a new ID3v1 tag is written below
	}
	else
		BlockCopy(ptr->fp,outf,(size_t)(fileendpos - ftell_err(ptr->fp)));				//Copy rest of source file to output file

//Write/Overwrite ID3v1 tag
	if(ptr->id3v1present)			//If an ID3v1 tag existed in the source MP3
	{	//Overwrite it with tags from the input file
		if(Lyrics.verbose)	(void) puts("Editing ID3v1 tag");
		fseek_err(outf,-125,SEEK_CUR);		//Seek 125 bytes back, where the first field of this tag should exist
		if(Lyrics.Title != NULL)						//If the input file defined a Title
			WritePaddedString(outf,Lyrics.Title,30,0);	//Overwrite the Title field (30 bytes)
		else
			fseek_err(outf,30,SEEK_CUR);				//Otherwise seek 30 bytes ahead to the next field

		if(Lyrics.Artist != NULL)						//If the input file defined an Artist
			WritePaddedString(outf,Lyrics.Artist,30,0);	//Overwrite the Artist field (30 bytes)
		else
			fseek_err(outf,30,SEEK_CUR);				//Otherwise seek 30 bytes ahead to the next field

		if(Lyrics.Album != NULL)						//If the input file defined an Album
			WritePaddedString(outf,Lyrics.Album,30,0);	//Overwrite the Album field (30 bytes)
		else
			fseek_err(outf,30,SEEK_CUR);				//Otherwise seek 30 bytes ahead to the next field

		if(Lyrics.Year != NULL)							//If the input file defined a Year
			WritePaddedString(outf,Lyrics.Year,4,0);	//Overwrite the Year field (4 bytes)
	}
	else
	{	//Write a new ID3v1 tag
		if(Lyrics.verbose)	(void) puts("Writing new ID3v1 tag");
		fseek_err(outf,0,SEEK_END);	//Seek to end of file
		fputs_err("TAG",outf);		//Write ID3v1 header
		WritePaddedString(outf,Lyrics.Title,30,0);	//Write the Title field (30 bytes)
		WritePaddedString(outf,Lyrics.Artist,30,0);	//Write the Artist field (30 bytes)
		WritePaddedString(outf,Lyrics.Album,30,0);	//Write the Album field (30 bytes)
		WritePaddedString(outf,ptr->id3v1year,4,0);	//Write the Year field (4 bytes)
		WritePaddedString(outf,NULL,30,0);			//Write a blank Comment field (30 bytes)
		fputc_err(255,outf);						//Write unknown genre (1 byte)
	}

	if(Lyrics.verbose)	(void) puts("Correcting ID3 headers");

//Rewind to the SYLT header in the output file and write the correct frame length
	fseek_err(outf,framepos+4,SEEK_SET);	//Seek to where the SYLT frame size is to be written
	WriteDWORDBE(outf,framesize);			//Write the SYLT frame size

//Rewind to the ID3 header in the output file and write the correct ID3 tag length
	fseek_err(outf,ptr->tagstart+6,SEEK_SET);	//Seek to where the ID3 tag size is to be written
	if(tagsize > 0x0FFFFFFF)
	{
		(void) puts("\aError:  Tag size is larger than the ID3 specification allows");
		exit_wrapper(2);
	}
	array[3]=tagsize & 127;			//Mask out everything except the lowest 7 bits
	array[2]=(tagsize>>7) & 127;	//Mask out everything except the 2nd set of 7 bits
	array[1]=(tagsize>>14) & 127;	//Mask out everything except the 3rd set of 7 bits
	array[0]=(tagsize>>21) & 127;	//Mask out everything except the 4th set of 7 bits
	fwrite_err(array,4,1,outf);		//Write the ID3 tag size

	if(Lyrics.verbose)	printf("\nID3 export complete.  %lu lyrics written\n",Lyrics.piececount);

	return ctr;	//Return counter
}
Example #8
0
unsigned long ID3FrameProcessor(struct ID3Tag *ptr)
{
	unsigned char header[10]={0};	//Used to store the ID3 frame header
	unsigned long filepos=0;
	int ctr2=0;						//Used to validate frame ID
	struct ID3Frame *temp=NULL;		//Used to allocate an ID3Frame link
	struct ID3Frame *head=NULL;		//Used as the head pointer
	struct ID3Frame *cond=NULL;		//Used as the conductor
	unsigned long ctr=0;			//Count the number of frames processed
	unsigned long framesize=0;		//Used to validate frame size
	char buffer[31]={0};			//Used to read an ID3v1 tag in the absence of an ID3v2 tag
	static struct ID3Frame emptyID3Frame;	//Auto-initialize all members to 0/NULL

//Validate input parameter
	if((ptr == NULL) || (ptr->fp == NULL))
		return 0;	//Return failure

//Find and parse the ID3 header
//ID3v1
	fseek_err(ptr->fp,-128,SEEK_END);	//Seek 128 back from the end of the file, where this tag would exist
	fread_err(buffer,3,1,ptr->fp);		//Read 3 bytes for the "TAG" header
	buffer[3] = '\0';	//Ensure NULL termination
	if(strcasecmp(buffer,"TAG") == 0)	//If this is an ID3v1 header
	{
		if(Lyrics.verbose)	(void) puts("Loading ID3v1 tag");
		ptr->id3v1present=1;	//Track that this tag exists
		fread_err(buffer,30,1,ptr->fp);		//Read the first field in the tag (song title)
		buffer[30] = '\0';	//Ensure NULL termination
		if(buffer[0] != '\0')	//If the string isn't empty
		{
			ptr->id3v1present=2;	//Track that this tag is populated
			ptr->id3v1title=DuplicateString(buffer);
			if(Lyrics.verbose)	(void) puts("\tTitle loaded");
		}
		fread_err(buffer,30,1,ptr->fp);		//Read the second field in the tag (artist)
		buffer[30] = '\0';	//Ensure NULL termination
		if(buffer[0] != '\0')	//If the string isn't empty
		{
			ptr->id3v1present=2;	//Track that this tag is populated
			ptr->id3v1artist=DuplicateString(buffer);
			if(Lyrics.verbose)	(void) puts("\tArtist loaded");
		}
		fread_err(buffer,30,1,ptr->fp);		//Read the third field in the tag (album)
		buffer[30] = '\0';	//Ensure NULL termination
		if(buffer[0] != '\0')	//If the string isn't empty
		{
			ptr->id3v1present=2;	//Track that this tag is populated
			ptr->id3v1album=DuplicateString(buffer);
			if(Lyrics.verbose)	(void) puts("\tAlbum loaded");
		}
		fread_err(buffer,4,1,ptr->fp);		//Read the fourth field in the tag (year)
		buffer[4]='\0';	//Terminate the buffer to make it a string
		if(buffer[0] != '\0')	//If the string isn't empty
		{
			ptr->id3v1present=2;	//Track that this tag is populated
			ptr->id3v1year=DuplicateString(buffer);
			if(Lyrics.verbose)	(void) puts("\tYear loaded");
		}
	}

//ID3v2
//Load ID3 frames into linked list
	rewind_err(ptr->fp);		//Rewind file so that the ID3v2 header can be searched for
	if(FindID3Tag(ptr) == 0)	//Locate the ID3 tag
		return 0;				//Return if there was none

	if(Lyrics.verbose)	(void) puts("Loading ID3v2 tag");
	fseek_err(ptr->fp,ptr->framestart,SEEK_SET);	//Seek to first ID3 frame
	filepos=ftell_err(ptr->fp);						//Record file position of first expected ID3 frame
	while((filepos >= ptr->framestart) && (filepos < ptr->tagend))
	{	//While file position is at or after the end of the ID3 header, before or at end of the ID3 tag
	//Read frame header into buffer
		if(fread(header,10,1,ptr->fp) != 1)
		{
			ptr->frames=head;	//Store the linked list into the ID3 tag structure
			return 0;	//Return failure on I/O error
		}

	//Validate frame ID
		for(ctr2=0;ctr2<4;ctr2++)
		{
			if(!isupper(header[ctr2]) && !isdigit(header[ctr2]))	//If the character is NOT valid for an ID3 frame ID
			{
				ptr->frames=head;	//Store the linked list into the ID3 tag structure
				return ctr;	//Return the number of frames that were already processed
			}
		}

	//Validate frame end
		framesize=((unsigned long)header[4]<<24) | ((unsigned long)header[5]<<16) | ((unsigned long)header[6]<<8) | ((unsigned long)header[7]);	//Convert to 4 byte integer
		if(filepos + framesize + 10 > ptr->tagend)	//If the defined frame size would cause the frame to extend beyond the end of the ID3 tag
		{
			ptr->frames=head;	//Store the linked list into the ID3 tag structure
			return 0;	//Return failure
		}

	//Initialize an ID3Frame structure
		temp=malloc_err(sizeof(struct ID3Frame));	//Allocate memory
		*temp=emptyID3Frame;						//Reliably initialize all values to 0/NULL
		temp->frameid=malloc_err(5);				//Allocate 5 bytes for the ID3 frame ID
		memcpy(temp->frameid,header,4);				//Copy the 4 byte ID3 frame ID into the pre-terminated string
		temp->frameid[4]='\0';						//Terminate the string
		temp->pos=filepos;	//Record the file position of the ID3 frame
		temp->length=((unsigned long)header[4]<<24) | ((unsigned long)header[5]<<16) | ((unsigned long)header[6]<<8) | ((unsigned long)header[7]);
			//Record the frame length (defined in header as 4 byte Big Endian integer)

		//Append ID3Frame link to the list
		if(head == NULL)	//Empty list
		{
			head=temp;	//Point head to new link
			cond=temp;	//Point conductor to new link
		}
		else
		{
			assert_wrapper(cond != NULL);	//The linked list is expected to not be corrupted
			cond->next=temp;	//Conductor points forward to new link
			temp->prev=cond;	//New link points back to conductor
			cond=temp;		//Conductor points to new link
		}

		if(Lyrics.verbose >= 2)	printf("\tFrame ID %s loaded\n",temp->frameid);
		ctr++;	//Iterate counter

		(void) fseek(ptr->fp,framesize,SEEK_CUR);	//Seek ahead to the beginning of the next ID3 frame
		filepos+=framesize + 10;	//Update file position
	}//While file position is at or after the end of the ID3 header, before or at end of the ID3 tag

	if(Lyrics.verbose)	printf("%lu ID3 frames loaded\n\n",ctr);

	ptr->frames=head;	//Store the linked list into the ID3 tag structure
	return ctr;			//Return the number of frames loaded
}
Example #9
0
void ID3_Load(FILE *inf)
{	//Parses the file looking for an ID3 tag.  If found, the first MP3 frame is examined to obtain the sample rate
	//Then an SYLT frame is searched for within the ID3 tag.  If found, synchronized lyrics are imported
	struct ID3Tag tag={NULL,0,0,0,0,0,0.0,NULL,0,NULL,NULL,NULL,NULL};
	struct ID3Frame *frameptr=NULL;
	struct ID3Frame *frameptr2=NULL;

	assert_wrapper(inf != NULL);	//This must not be NULL
	tag.fp=inf;

	if(Lyrics.verbose)	printf("Importing ID3 lyrics from file \"%s\"\n\nParsing input MPEG audio file\n",Lyrics.infilename);

	if(ID3FrameProcessor(&tag) == 0)	//Build a list of the ID3 frames
	{
		DestroyID3(&tag);	//Release the ID3 structure's memory
		return;				//Return if no frames were successfully parsed
	}

//Parse MPEG frame header
	fseek_err(tag.fp,tag.tagend,SEEK_SET);	//Seek to the first MP3 frame (immediately after the ID3 tag)
	if(GetMP3FrameDuration(&tag) == 0)		//Find the sample rate defined in the MP3 frame
	{
		DestroyID3(&tag);	//Release the ID3 structure's memory
		return;				//Return if the sample rate was not found or was invalid
	}

	frameptr=FindID3Frame(&tag,"SYLT");		//Search ID3 frame list for SYLT ID3 frame
	frameptr2=FindID3Frame(&tag,"USLT");	//Search ID3 frame list for USLT ID3 frame
	if(frameptr != NULL)
	{	//Perform Synchronized ID3 Lyric import
		fseek_err(tag.fp,frameptr->pos,SEEK_SET);	//Seek to SYLT ID3 frame
		SYLT_Parse(&tag);
	}
	else if(frameptr2 != NULL)
	{	//Perform Unsynchronized ID3 Lyric import
		(void) puts("\aUnsynchronized ID3 lyric import currently not supported");
		exit_wrapper(1);
	}
	else
	{
		DestroyID3(&tag);	//Release the ID3 structure's memory
		return;				//Return if neither lyric frame is present
	}

//Load song tags
	Lyrics.Title=GrabID3TextFrame(&tag,"TIT2",NULL,0);	//Return the defined song title, if it exists
	Lyrics.Artist=GrabID3TextFrame(&tag,"TPE1",NULL,0);	//Return the defined artist, if it exists
	Lyrics.Album=GrabID3TextFrame(&tag,"TALB",NULL,0);	//Return the defined album, if it exists
	Lyrics.Year=GrabID3TextFrame(&tag,"TYER",NULL,0);	//Return the defined year, if it exists

	if(Lyrics.Title == NULL)	//If there was no Title defined in the ID3v2 tag
		Lyrics.Title=DuplicateString(tag.id3v1title);	//Use one defined in the ID3v1 tag if it exists
	if(Lyrics.Artist == NULL)	//If there was no Artist defined in the ID3v2 tag
		Lyrics.Artist=DuplicateString(tag.id3v1artist);	//Use one defined in the ID3v1 tag if it exists
	if(Lyrics.Album == NULL)	//If there was no Album defined in the ID3v2 tag
		Lyrics.Album=DuplicateString(tag.id3v1album);	//Use one defined in the ID3v1 tag if it exists
	if(Lyrics.Year == NULL)		//If there was no Year defined in the ID3v2 tag
		Lyrics.Year=DuplicateString(tag.id3v1year);		//Use one defined in the ID3v1 tag if it exists

	ForceEndLyricLine();
	DestroyID3(&tag);	//Release the ID3 structure's memory

	if(Lyrics.verbose)	printf("ID3 import complete.  %lu lyrics loaded\n\n",Lyrics.piececount);
}