ID3v2 help :)

BlitzMax Forums/BlitzMax Programming/ID3v2 help :)

Hi,
I'm working on an application which will read the ID3 tags of my MP3 files, and sort them in directories after Artist/Album etc.
ID3v1 was easy to understand, but ID3v2 tags are alot harder and I'm currently stuck.. So I'd appritiate some help with this :)

Here's my current code:
Framework BRL.FileSystem
Import BRL.StandardIO
Import BRL.Retro
Import BRL.EndianStream

Global FromDir:String = "CHANGE ME!"
Global ToDir:String = "Not in use yet."
Global ErrorList:TList = CreateList()

Print ""

SortDirectory( FromDir )

Print ""
Print "Sorted '"+FromDir+"' with "+CountList(ErrorList)+" errors."
Print "## Printing Errors ##"
For Local A:String = EachIn ErrorList
	Print A
Next

Function SortDirectory( Dir:String )
	
	Print "! DIRECTORY ! ("+Dir+")"
	
	Local SortDir = ReadDir( Dir )
	
	Repeat
		Local ObjName:String = NextFile( SortDir )
		Select ObjName
			Case ""
				Exit
			Case "."
			Case ".."
			Default
				Select FileType( Dir+ObjName )
					Case FILETYPE_FILE
						If Upper(Right( ObjName, 3 )) = "MP3"
							ProcessMP3File( Dir+ObjName )
						EndIf
					Case FILETYPE_DIR
						SortDirectory( Dir+ObjName+"" )
					Default
				End Select
				
		End Select
	Forever
	
End Function

Function ProcessMP3File( FileDir:String )
	Print ""
	Print "## FILE START ## '"+FileDir+"'."
	'If ReadID3v1Tag( FileDir ) = ""
		ReadID3v2Tag( FileDir )
	'EndIf
	Print "## FILE END ##"
	Print ""
End Function

Function ReadID3v1Tag:String( FileName:String )
	
	Local Id3Tag:String = ""
	Local Artist:String
	Local Album:String
	Local Title:String
	
	Local File:TStream = ReadFile( FileName )
		SeekStream( File, StreamSize( File )-128)
		If ReadPart( File, 3 ) = "TAG"
			Title = Trim(ReadPart( File, 30))
			Artist = Trim(ReadPart( File, 30))
			Album = Trim(ReadPart( File, 30))
			Id3Tag = Artist + "|" + Album + "|" + Title
			Print "ID3v1: " + Artist + " => " + Album + " => " + Title
		EndIf
	CloseFile( File )
	
	Return Id3Tag
	
End Function

Function ReadID3v2Tag:String( FileName:String )
	
	Local Artist:String
	Local Album:String
	Local Title:String
	
	Local Id3Tag:String = ""
	Local Id3Version:Int = 0
	Local Id3Flags:Int = 0
	Local Id3Size:Int = 0
	
	'#################################################################################################
	Local File:TStream = BigEndianStream( ReadFile( FileName ) )
		
		'############################################################################################
		'ID3v2 Header
		If Upper(ReadPart( File, 3 )) = "ID3"
			
			Print "ID3v2"
			
			Id3Version = Int(ReadPart( File, 2, False ))
			Print "ID3v2 Version: "+Id3Version
			
			Id3Flags = Int( Bin( ReadPartInt( File, 1 ) ) )
			Print "ID3v2 Flags: "+Id3Flags
			
			Id3Size = UnSynch( ReadPartInt( File, 4 ) )
			Print "ID3v2 Size: " + Id3Size
		
			'#################################################################################################
			'ID3v2 Header Flags
			Select Id3Flags
				Case 0'NO FLAGS
				Case 1000000
					Print "Unsynchronisation Not Supported!"
					Return ""
				Case 100000
					Print "Extended Header Not Supported!"
					Return ""
				Case 10000
					Print "Experimental Indicator Not Supported!"
					Return ""
				Default
					Print "HeaderFlag Not Supported!"
				'	Return ""
			End Select
			
			'#################################################################################################
			'Loop Through Frames
			
			Local FrameID:String
			Local FrameSize:Int
			Local FrameFlags:Int
			
			Repeat
				If Id3Version = 30
					FrameID = ReadPart( File, 4 )
	'				Print "FrameID: "+FrameID
					'FrameSize = UnSynch( Int(ReadPart( File, 4, False )) )
					FrameSize = UnSynch( ReadPartInt( File, 4 ) )
					Print "FrameSize: "+FrameSize
					FrameFlags = Int(ReadPart( File, 2, 0 ))
					Print "FrameFlags: "+FrameFlags
					
					Select Trim(FrameID)
						
						Case "TIT2"'Title
							Print "Title: "+ReadPart( File, FrameSize )
							
						Case "TPE2", "TPE1", "TP1"'Artist
							Print "Artist: "+ReadPart( File, FrameSize )
							
						Case "TALB"'Album
							Print "Album: "+ReadPart( File, FrameSize )
						
						Default
							Print "Unknown FrameID: "+FrameID
							Print ReadPart( File, FrameSize )
						
					End Select
					
					If StreamPos( File ) => Id3Size
						Exit
					EndIf
				Else
					Exit
				EndIf
			Forever
			
			'#################################################################################################
			
		EndIf
	CloseFile( File )
	
	Return Id3Tag
	
End Function

Function UnSynch( In:Int )
	Local Out:Int, I:Int
	Local Mask:Int = Int("$7F000000")
 
	For I = 0 Until 4
		Out = Out Shr 1
		Out :| ( In & Mask )
		Mask :Shr 8
	Next
 
	Return Out
End Function

Function ReadPart:String( Stream:TStream, Size:Int, Char:Int = 1 )
	Local Str:String = ""
	For Local X:Int = 1 To Size
		If Char = 1
			Str = Str + Chr(ReadByte(Stream))
		Else
			Str = Str + ReadByte(Stream)
		EndIf
	Next
	Return Str
End Function

Function ReadPartInt:Int( Stream:TStream, Size:Int )
	Local I:Int = 0
	For Local X:Int = 1 To Size
		I = I + Int(ReadByte(Stream))
	Next
	Return I
End Function


And here's the documentation I've been using:
http://www.id3.org/id3v2.4.0-structure
http://en.wikipedia.org/wiki/Synchsafe

Currently it does read some information from the MP3 ID3v2 tag, but it skips ALOT for some reason, Either it's the UnSynch() function that doesn't work as intented, or there's some other bug in the code :P

Change the FromDir to a diretory where you have some MP3s ;P

Thanks :)

Looks like brucey came to the rescue, as he tends to do all too often =p

http://blitzmax.com/Community/posts.php?topic=83139#938334

Oh, great! :)
Thanks Retimer, and Thanks Brucey! :)