Worklog for Malice
Generic Engine
Return to Worklogs
| ||
| Not sure ultimately how fast or slow this is, but the premise looks good at least. I'm hoping eventually, to modify the type structures to a simpler format that could work with banks to hopefully improve speed and reduce overheads that little bit more. Based on the reading/writing of 2DA data tables, in short a table can have (For quick calculations Ive used a bitwise tecnique, limiting the number of datafiles to 256 and each tables' columns to 32) multiple columns and a theortetically unlimited (given memory constraints) number of rows. Each column may be of a searate data type, so far this is simply Int, String and Float. Quick Identification and querying is enhanced by using the Handle pointetrs of type instances rather than any iteration. The only points of iteration are on initialisation, a special 'refresh' and 'counting' scenario and when reading/writing the files themselves to and from memory. It's very much still a WIP, but so far,m things are looking good. ____________________ lawks a lordy, my bottom's on fire! |
| ||
| I've now gotten the OGG audio fileformat down, though there's still a few more details I would liek to include (primarily duration!) However, as with the MP3 version, a wavveform image can be read as well as various metadata tags. ;CONFIGOGG
Const OGG_HEX_CHECK$="OggS"
Const OGG_SIZE_MIN%=512
Const OGG_MAX_TAGS%=5
Global OGG_TAG_LABEL$[OGG_MAX_TAGS]
OGG_TAG_LABEL[0]="ARTIST="
OGG_TAG_LABEL[1]="ALBUM="
OGG_TAG_LABEL[2]="GENRE="
OGG_TAG_LABEL[3]="TITLE="
OGG_TAG_LABEL[4]="ALBUM ARTIST="
; Constants should match for all media types. Hence gaps for currently unknown tag labels
Const OGG_INF_UNKNOWN% = 0
Const OGG_INF_TITLE% = 1
Const OGG_INF_ARTIST% = 3
Const OGG_INF_ALBUM% = 4
Const OGG_INF_ALBUM_ARTIST% = 5
Const OGG_INF_GENRE% = 6
;Const OGG_INF_TRACK% = 7
;Const OGG_INF_YEAR% = 8
;Const OGG_INF_DURATION% = 9
Type OGG_Info
Field OGG_Filepath$
Field OGG_FileSize%
Field OGG_Container_Version%
Field Ogg_PageCount%
Field OGG_Album$
Field OGG_Artist$
Field OGG_Album_Artist$
Field OGG_Genre$
Field OGG_Title$
; Field OGG_DurationMilliseconds%
End Type
Type OGG_PageInfo
Field OGG_Parent.OGG_Info
Field OGG_Header%
Field OGG_Header_Type
Field OGG_Granule1%
Field OGG_Granule2%
Field OGG_Bitstream%
Field OGG_Page%
Field OGG_Checksum%
Field OGG_Start%
Field Ogg_Length%
End Type
Function GetOGGSpecificInfo$(OGG_FileName$,OGGInfoType%,AutoClean%=True)
; Autoclean flag frees up stored OGG memory automatically
Local sReturn$=GetOGGData$(OGG_FileName$,OGGInfoType%)
If(AutoClean)
DebugLine("CONFIGOGG:GETOGGSPECIFICINFO",DBG_ERC_NOT_AN_ERROR,False,"Autoclean flag is set")
ClearAllOGGData
End If
If (Not(CheckMediaTagString%(sReturn$))) Then sReturn$=UNKNOWN$
Return sReturn$
End Function
Function CheckOGGDataExists%()
Local OGG_DataType.OGG_Info=First OGG_Info
Return (OGG_DataType.OGG_Info<>Null)
End Function
Function GetOGGData$(OGG_FileName$,OGG_Info_Type%)
;Ensure Matching OGG Data Has Been Read
If (Not(CheckOGGDataExists()))
ClearAllOggData
ReadOggData(OGG_FileName$)
End If
If (Not(CheckOGGDataExists()))
DebugLine("CONFIGOGG:GETOGGDATA",DBG_ERC_GEN_FAIL,False,"OGG Data Unavailable")
ClearAllOGGData
Return NULL_STRING$
End If
Local OGG_DataType.OGG_Info=First OGG_Info
If (OGG_DataType\OGG_Filepath<>OGG_FileName$)
DebugLine("CONFIGOGG:GETOGGDATA",DBG_ERC_GEN_FAIL,False,"OGG Data in memory does not match filepath: "+OGG_FileName$)
ClearAllOGGData
ReadOGGData(OGG_FileName$)
End If
If (Not(CheckOGGDataExists()))
ClearAllOGGData
DebugLine("CONFIGOGG:GETOGGDATA",DBG_ERC_GEN_FAIL,False,"OGG Data Unavailable")
Return NULL_STRING$
End If
OGG_DataType.OGG_Info=First OGG_Info
Select (OGG_Info_Type%)
Case OGG_INF_ALBUM%:Return OGG_DataType\OGG_Album$
Case OGG_INF_ARTIST%:Return OGG_DataType\OGG_Artist$
Case OGG_INF_ALBUM_ARTIST%:Return OGG_DataType\OGG_Album_Artist$
Case OGG_INF_GENRE%:Return OGG_DataType\OGG_Genre$
Case OGG_INF_TITLE%:Return OGG_DataType\OGG_Title$
; Case OGG_INF_DURATION%:Return Str(OGG_DataType\OGG_DurationMilliseconds)
; Case OGG_INF_TRACK%:Return Str(OGG_DataType\OGG_Track_No%)
; Case OGG_INF_YEAR%:Return OGG_DataType\OGG_ReleaseYear$
End Select
Return NULL_STRING$
End Function
Function AddOGGTag(nTagReference%,sTagData$)
If (nTagReference>=OGG_MAX_TAGS%)
DebugLine("CONFIGOGG:ADDOGGTAG",DBG_ERC_GEN_FAIL,False,"OGG tag reference "+Parenthesise(Str(nTagReference%))+" out of bounds")
Return
End If
Local OggInfo.OGG_Info=First OGG_Info
If (OggInfo.OGG_Info=Null)
DebugLine("CONFIGOGG:ADDOGGTAG",DBG_ERC_TYP_OBJECT_NULL,False,"OGG information does not exist")
Return
End If
OggInfo.OGG_Info=First OGG_Info
If (sTagData$=NULL_STRING$)
DebugLine("CONFIGOGG:ADDOGGTAG",DBG_ERC_MED_TAGEMPTY,False,"OGG Tag data string is null")
End If
Select (nTagReference%)
Case 0: OggInfo\OGG_Artist$=sTagData$
Case 1: OggInfo\OGG_Album$=sTagData$
Case 2: OggInfo\OGG_Genre$=sTagData$
Case 3: OggInfo\OGG_Title$=sTagData$
Case 4: OggInfo\OGG_Album_Artist$=sTagData$
Default:
DebugLine("CONFIGOGG:ADDOGGTAG",DBG_ERC_GEN_FAIL,False,"OGG tag reference "+Parenthesise(Str(nTagReference%))+" out of bounds")
Return
End Select
DebugLine("CONFIGOGG:ADDOGGTAG",DBG_ERC_NOT_AN_ERROR,False,"OGG Tag data "+OGG_TAG_LABEL[nTagReference]+EnQuote(sTagData$)+" added")
End Function
Function CheckOggTag(nTagReference%,OGGFileStream%)
Local OggInfo.OGG_Info=First OGG_Info
If (OggInfo.OGG_Info=Null)
DebugLine("CONFIGOGG:READOGGWAVEFORM",DBG_ERC_TYP_OBJECT_NULL,False,"OGG information does not exist")
Return
End If
If (Not(OGGFileStream))
DebugLine("CONFIGOGG:CHECKOGGTAG",DBG_ERC_FILE_RESHANDLEZERO,False,"OGG file handle is zero")
Return False
End If
Local sData$
Local sByte$=SPACER$
Local nIterBytes%
SeekFile(OGGFileStream,False)
For nIterBytes%=0 To OGG_SIZE_MIN
sData$=sData$+Chr(ReadByte(OGGFileStream))
If (Instr(sData$,OGG_TAG_LABEL[nTagReference%]))
sData$=NULL_STRING
While (sByte$<>Chr(False) And (Not(Eof(OGGFileStream))))
sByte$=Chr(ReadByte(OGGFileStream))
sData$=sData+sByte$
Wend
AddOGGTag(nTagReference%,Trim(sData$))
Exit
End If
Next
End Function
Function ReadOGGTags(OGG_Filestream%)
If (Not(OGG_Filestream%))
DebugLine("CONFIGOGG:READOGGTAGS",DBG_ERC_FILE_RESHANDLEZERO,False,"OGG file handle is zero")
Return False
End If
Local nCheck%
For nCheck%=0 To (OGG_MAX_TAGS-1)
CheckOggTag(nCheck%,OGG_Filestream%)
Next
End Function
Function ClearAllOggData()
Delete Each OGG_Info
Delete Each OGG_PageInfo
DebugLine("CONFIGOGG:CLEARALLOGGDATA",DBG_ERC_NOT_AN_ERROR,False,"OGG data cleared")
End Function
Function ReadOggData%(OGG_Filename$)
Local OggInfo.OGG_Info=First OGG_Info
;Ensure valid ogg info
If (OggInfo.OGG_Info=Null)
ClearAllOggData
OggInfo.OGG_Info=New OGG_Info
End If
If (OggInfo\OGG_Filepath$<>OGG_Filename$)
ClearAllOggData
OggInfo.OGG_Info=New OGG_Info
End If
Local OGG_FileHandle=LoadAs(OGG_Filename,FILE_AS_DATA,0,0,0,0,0)
If (Not(OGG_FileHandle))
DebugLine("CONFIGOGG:READOGGDATA",DBG_ERC_FILE_RESHANDLEZERO,False,"OGG file handle is zero")
ClearAllOggData
Return False
End If
OggInfo\OGG_Filepath$=OGG_Filename$
OggInfo\OGG_FileSize%=FileSize(OGG_Filename$)
DebugLine("CONFIGOGG:READOGGDATA",DBG_ERC_FILE_ATTEMPTREAD,False,"Attempting to read OGG information")
ReadOGGTags(OGG_FileHandle)
SeekFile(OGG_FileHandle,False)
Local sHexCheck$=NULL_STRING$
Local nIterByteInt%,bResult%
Local bValid%=False
OggInfo\Ogg_PageCount%=False
While (bValid=Eof(OGG_FileHandle))
sHexCheck=NULL_STRING$
For nIterByteInt%=0 To 3
sHexCheck$=sHexCheck$+Chr(ReadByte(OGG_FileHandle))
Next
If (sHexCheck$=OGG_HEX_CHECK$)
OggInfo\Ogg_PageCount%=OggInfo\Ogg_PageCount+1
DebugLog FilePos(OGG_FileHandle)
bResult%=ReadOGGPageData%(OGG_FileHandle,FilePos(OGG_FileHandle))
If (bResult%) Then SeekFile(OGG_FileHandle,bResult)
End If
If ((FilePos(OGG_FileHandle)<((OggInfo\OGG_FileSize%)-REG_NIBBLESPACE))) Then SeekFile(OGG_FileHandle,FilePos(OGG_FileHandle)-(REG_NIBBLESPACE-1))
Wend
Return True
End Function
Function ReadOGGPageData%(OGGFile%,nPos)
If (Not(OGGFile))
DebugLine("CONFIGOGG:READOGGPAGEDATA",DBG_ERC_FILE_RESHANDLEZERO,False,"OGG file handle is zero")
Return False
End If
Local OGGInfo.OGG_Info=First OGG_Info
If (OGGInfo.OGG_Info=Null)
DebugLine("CONFIGOGG:READOGGPAGEDATA",DBG_ERC_TYP_OBJECT_NULL,False,"OGG information does not exist")
ClearAllOggData
Return False
End If
Local OGG.OGG_PageInfo=New OGG_PageInfo
OGGInfo\OGG_Container_Version%=ReadByte(OGGFile)*ReadByte(OGGFile)
OGG\OGG_Parent.OGG_Info=OGGInfo
OGG\OGG_Header%=ReadByte(OGGFile)*ReadByte(OGGFile)
OGG\OGG_Granule1%=ReadInt(OGGFile)
OGG\OGG_Granule2%=ReadInt(OGGFile)
OGG\OGG_Bitstream%=ReadInt(OGGFile)
OGG\OGG_Page%=ReadInt(OGGFile)
OGG\OGG_Checksum%=ReadInt(OGGFile)
Local Segments%=ReadByte(OGGFile)*ReadByte(OGGFile)
Local NullByte%=False
While (Not(NullByte%))
NullByte%=ReadByte(OGGFile)
Wend
OGG\OGG_Start%=FilePos(OGGFile)
Local sHexCheck$=NULL_STRING
Local nIterbyteInt%
Local bValid=False
While (bValid=Eof(OGGFile))
If ((FilePos(OGGFile)>((OGG\OGG_Parent\OGG_FileSize%)-REG_NIBBLESPACE))) Then Exit
sHexCheck$=NULL_STRING$
While(Chr(ReadByte(OGGFile)))<>(Left(OGG_HEX_CHECK,1)) And (Not(Eof(OGGFile)))
Wend
If (Not(Eof(OGGFile)))
sHexCheck$=Left(OGG_HEX_CHECK,1)
For nIterbyteInt%=0 To 2
sHexCheck$=sHexCheck$+Chr(ReadByte(OGGFile))
Next
If (sHexCheck$=OGG_HEX_CHECK$) Then Exit
SeekFile(OGGFile,FilePos(OGGFile)-(REG_NIBBLESPACE-1))
EndIf
Wend
If ((FilePos(OGGFile)<((OGG\OGG_Parent\OGG_FileSize%)-REG_NIBBLESPACE))) Then SeekFile(OGGFile,FilePos(OGGFile)-REG_NIBBLESPACE)
OGG\Ogg_Length%=((FilePos(OGGFile))-OGG\OGG_Start)
Return OGG\Ogg_Length%+OGG\OGG_Start
End Function
Function ReadOGGWaveForm%(OGG_Filename$)
Local nOggReaderCheck%=False
If Not(CheckIsValidOGG%(OGG_Filename$))
DebugLine(DebugLine("CONFIGOGG:READOGGWAVEFORM",DBG_ERC_FILE_MISSING,False,EnQuote(OGG_Filename)+" is not a valid OGG file"))
Return False
End If
Local OGGInfo.OGG_Info = First OGG_Info
If (OGGInfo.OGG_Info=Null)
DebugLine("CONFIGOGG:READOGGWAVEFORM",DBG_ERC_TYP_OBJECT_NULL,False,"OGG information does not exist")
nOggReaderCheck%=ReadOggData(OGG_Filename$)
End If
If (Not(nOggReaderCheck%))
DebugLine("CONFIGOGG:READOGGWAVEFORM",DBG_ERC_GEN_FAIL,False,"Error in reading OGG data")
Return False
End If
OGGInfo.OGG_Info = First OGG_Info
If (OGGInfo.OGG_Info=Null)
DebugLine("CONFIGOGG:READOGGWAVEFORM",DBG_ERC_TYP_OBJECT_NULL,False,"OGG information does not exist")
Return False
End If
Local nStart%,nEnd%,nLength%,nBankStart%
Local bbank=CreateBank(REG_NIBBLESPACE)
Local nIterbytes
Local OGG.OGG_PageInfo
Local OGGFile=LoadAs(OGG_Filename$,FILE_AS_DATA,0,0,0,0,0)
If (Not(OGGFile))
DebugLine("CONFIGOGG:READOGGWAVEFORM",DBG_ERC_FILE_RESHANDLEZERO,False,"OGG file handle is zero")
Return False
End If
;We use the OGG granule just for debug tracking.
Local nGranule%=-1
For OGG.OGG_PageInfo=Each OGG_PageInfo
If OGG\OGG_Parent=OGGInfo.OGG_Info
If (nGranule<>OGG\OGG_Granule1%)
nGranule%=OGG\OGG_Granule1%
DebugLine("CONFIGOGG:READOGGWAVEFORM",DBG_ERC_NOT_AN_ERROR,False,"reading block "+Str(OGG\OGG_Granule1%)+" from "+Str(nStart))
End If
nLength=OGG\Ogg_Length
nStart=OGG\OGG_Start
nEnd=OGG\Ogg_Length+OGG\OGG_Start
nBankStart=BankSize(bbank)
ResizeBank(bbank,BankSize(bbank)+nLength)
SeekFile(OGGFile,nStart)
For nIterbytes=0 To nLength-1
PokeByte bbank,nIterbytes+nBankStart,AdjustWaveformOutput(ReadByte(OGGFile))
Next
End If
Next
CloseFile OGGFile
Return bbank%
End Function
Function CheckIsValidOGG%(OGG_Filename$)
If (Not (IntegrityCheckByExtension%(OGG_Filename$,EXT_OGG,OGG_SIZE_MIN)))
DebugLine("CONFIGOGG:CHECKISVALIDOGG",DBG_ERC_FILE_MISSING,False,OGG_Filename$+" Is not a valid OGG file.")
Return False
Else
Return True
EndIf
End Function
;~IDEal Editor Parameters:
;~C#Blitz3D ____________________ lawks a lordy, my bottom's on fire! |
| ||
| A more suitable and refined approach to identifying MP3 data and reading the Tag informmation. This updated version (see below for full code functions) skips unnecessary readings such as WMClassID and extracts the relevant information directly. There's no need to scan each AudioFrame, so it should be much quicker, as well as requiring less memory usage throughout the process. as well as the standard Tags such as Artist, Title, Album, Length etc. are useful information such as codecs (Streaming MP3 data can also be read, returning the codec used to stream the file too), average bitrate and samplerate, length (as discussed previously) and publisher/copyright details. This is not an exhaustive list of the information, and there is even still more available through uncommenting. Those lines which are commented out, were only rendered that way due to those tags being somewhat unpopular and rarely completed for most cases. ;CONFIGMP3
Dim Bytes(1)
Const MP3_INF_UNKNOWN% = 0
Const MP3_INF_TITLE% = 1
Const MP3_INF_ARTIST% = 3
Const MP3_INF_ALBUM% = 4
Const MP3_INF_ALBUM_ARTIST% = 5
Const MP3_INF_GENRE% = 6
Const MP3_INF_TRACK% = 7
Const MP3_INF_YEAR% = 8
Const MP3_INF_DURATION% = 9
Const MP3_INF_CATEGORY% = 10
Const MP3_INF_CHANNELS% = 11
Const MP3_INF_BITRATE% = 12
Const MP3_INF_SAMPLERATE% = 13
Const MP3_INF_EMPHASIS% = 14
Const MP3_INF_COMMENTS% = 15
Const MP3_INF_PUBLISHER% = 16
Const MP3_INF_MPG_VERSION% = 17
Const MP3_INF_MPG_LAYER% = 18
Const MP3_INF_COPYRIGHT% = 19
Const MP3_INF_ORIGIN% = 20
Const MP3_INF_LANGUAGE% = 21
Const MP3_INF_CODEC% = 22
Const MP3_INF_STREAMENC% = 23
Const MP3_INF_ART% = 24
Const MP3_INF_CRC% = 25
;Const MP3_INF_SUBTITLE% = 26
;Const MP3_INF_ORIGINAL_ARTIST% = 27
;Const MP3_INF_LYRICIST% = 28
;Const MP3_INF_ORIGINALLYRICIST%= 29
;Const MP3_INF_COMPOSER% = 30
;Const MP3_INF_CONDUCTOR% = 31
;Const MP3_INF_ORIGINAL_ALBUM% = 32
;Const MP3_INF_ALBUM_SET% = 33
;Const MP3_INF_CATEGORY_DESC% = 34
;Const MP3_INF_LYRICS% = 35
;Const MP3_INF_TEMPO% = 36
;Const MP3_INF_KEY% = 37
;Const MP3_INF_ISRC% = 38
;Const MP3_INF_MCDI% = 39
;Const MP3_INF_WM_F% = 40
;Const MP3_INF_WM_R% = 41
;Const MP3_INF_WM_S% = 42
;Const MP3_INF_WM_X% = 43
Const MP3_TAGV2_NAMESPACE_TITLE$="TIT2"
Const MP3_TAGV2_NAMESPACE_ARTIST$="TPE1"
Const MP3_TAGV2_NAMESPACE_ALBUM$="TALB"
Const MP3_TAGV2_NAMESPACE_ALBUM_ARTIST$="TPE2"
Const MP3_TAGV2_NAMESPACE_TRACK$="TRCK"
Const MP3_TAGV2_NAMESPACE_GENRE$="TCON"
Const MP3_TAGV2_NAMESPACE_COMMENTS$="COMM"
Const MP3_TAGV2_NAMESPACE_YEAR$="TYER"
Const MP3_TAGV2_NAMESPACE_DURATION$="TLEN"
Const MP3_TAGV2_NAMESPACE_LANGUAGE$="TLAN"
Const MP3_TAGV2_NAMESPACE_PUBLISHER$="TPUB"
Const MP3_TAGV2_NAMESPACE_COPYRIGHT$="TCOP"
Const MP3_TAGV2_NAMESPACE_CODEC$="TENC"
Const MP3_TAGV2_NAMESPACE_STREAMENC$="POPM"
Const MP3_TAGV2_NAMESPACE_ART$="APIC"
;Const MP3_TAGV2_NAMESPACE_SUBTITLE$="TIT3"
;Const MP3_TAGV2_NAMESPACE_COMPOSER$="TCOM"
;Const MP3_TAGV2_NAMESPACE_CONDUCTOR$="TPE3"
;Const MP3_TAGV2_NAMESPACE_ORIGINAL_ARTIST$="TOPE"
;Const MP3_TAGV2_NAMESPACE_ORIGINAL_ALBUM$="TOAL"
;Const MP3_TAGV2_NAMESPACE_LYRICS$="USLT"
;Const MP3_TAGV2_NAMESPACE_LYRICIST$="TEXT"
;Const MP3_TAGV2_NAMESPACE_ORIGINAL_LYRICIST$="TOLY"
;Const MP3_TAGV2_NAMESPACE_ALBUM_SET$="TPOS"
;Const MP3_TAGV2_NAMESPACE_CATDESC$="TIT1"
;Const MP3_TAGV2_NAMESPACE_ISRC$="TSRC"
;Const MP3_TAGV2_NAMESPACE_KEY$="TKEY"
;Const MP3_TAGV2_NAMESPACE_TEMPO$="TBPM"
;Const MP3_TAGV2_NAMESPACE_MCDI="MCDI" ; (Unnecessary)
;Const MP3_TAGV2_NAMESPACE_WM_F$="WOAF" ; (Unnecessary)
;Const MP3_TAGV2_NAMESPACE_WM_R$="WOAR" ; (Unnecessary)
;Const MP3_TAGV2_NAMESPACE_WM_S$="WOAS" ; (Unnecessary)
;Const MP3_TAGV2_NAMESPACE_WM_X$="WXXX" ; (Unnecessary)
;Const MP3_TAGV2_NAMESPACE_PRIVATE$="PRIV" ; (Applies to multiple hidden tags) such as MOOD
Const MP3_TAGV1_STARTBYTE% = 128
Const MP3_AF_LENGTH_RATIO# = 144000.0
Const MP3_MISSING_DATA$ = ">"
Const MP3_TAG$ = "TAG"
Const MP3_ID3$ = "ID3"
Const MP3_DEF_ALBUM$ = "Singles"
Type MP3_Info
;Added By me
Field MP3_FullPathName$
Field MP3_FileSize%
;TAG V1
Field MP3_Title$
Field MP3_Artist$
Field MP3_Album$
Field MP3_Genre$
Field MP3_ReleaseYear$
Field MP3_Comment$ ; Language||Comments separated by CHR$(10)
; TAG V2
Field MP3_Album_Artist$
Field MP3_Track_No%
Field MP3_Category$
Field MP3_Publisher$
Field MP3_DurationHours% ; Duration initially in Milliseconds only. Converted prior to storage here.
Field MP3_DurationMinutes%
Field MP3_DurationSeconds%
Field MP3_DurationMilliSeconds%
Field MP3_Codec$
Field MP3_StreamEncoding$
Field MP3_Copyright$
Field MP3_Language$
;Field MP3_SubTitle$
;Field MP3_Original_Artist$
;Field MP3_Lyricist$
;Field MP3_Original_Lyricist$
;Field MP3_Composer$
;Field MP3_Conductor$
;Field MP3_Original_Album$
;Field MP3_Album_Set$
;Field Category_Description$
;Field MP3_Lyrics$
;Field MP3_Tempo%
;Field MP3_Key$
;Field ISRC$
;Field MP3_Mood$
;Field MP3_MCDI$
Field MP3_Art$ ; Album Art jpeg/image data
;AF Values
Field MP3_AverageBitRate%
Field MP3_AverageSampleRate%
Field MP3_MPG_Version$
Field MP3_MPG_Layer$
Field MP3_Emphasis$
Field MP3_Channels$
Field MP3_Origin%
Field MP3_CRC%
;Field MP3_Rating ; Not really necessary, WMP Only
;Field MP3_WM_F% ; Not really necessary, WMP Only
;Field MP3_WM_R% ; Not really necessary, WMP Only
;Field MP3_WM_S% ; Not really necessary, WMP Only
;Coordinating
Field MP3_Tag_Key$
Field MP3_Tag_Data$
Field MP3_Tag_Flags%
Field MP3_FrameSize%
Field MP3_FrameStart%
End Type
Function GetMP3SpecificInfo$(MP3_FileName$,MP3InfoType%,AutoClean%=True)
; Autoclean flag frees up stored MP3 memory automatically
Local sReturn$=GetMP3Data$(MP3_FileName$,MP3InfoType%)
If(AutoClean)
DebugLine("CONFIGMP3:GETMP3SPECIFICINFO",DBG_ERC_NOT_AN_ERROR,False,"Autoclean flag is set")
ClearAllMP3Data
End If
If (Not(CheckMP3String%(sReturn$))) Then sReturn$=UNKNOWN$
Return sReturn$
End Function
Function ReadMP3Data(MP3_FileName$)
ClearAllMP3Data
If (Not(CheckIsValidMP3(MP3_FileName$)))
ClearAllMP3Data
DebugLine("CONFIGMP3:READMP3DATA",DBG_ERC_FILE_MISSING,False,"Not a valid MP3 File")
Else
ReadMP3Tags(MP3_FileName$)
End If
End Function
Function GetMP3Data$(MP3_FileName$,MP3_Info_Type%)
;Ensure Matching MP3 Data Has Been Read
If (Not(CheckMP3DataExists()))
ClearAllMP3Data
ReadMP3Data(MP3_FileName$)
End If
If (Not(CheckMP3DataExists()))
DebugLine("CONFIGMP3:GETMP3DATA",DBG_ERC_GEN_FAIL,False,"MP3 Data Unavailable")
ClearAllMP3Data
Return NULL_STRING$
End If
Local MP3_DataType.MP3_Info=First MP3_Info
If (MP3_DataType\MP3_FullPathName<>MP3_FileName$)
DebugLine("CONFIGMP3:GETMP3DATA",DBG_ERC_GEN_FAIL,False,"MP3 Data in memory does not match filepath: "+MP3_FileName$)
ClearAllMP3Data
ReadMP3Data(MP3_FileName$)
End If
If (Not(CheckMP3DataExists()))
ClearAllMP3Data
DebugLine("CONFIGMP3:GETMP3DATA",DBG_ERC_GEN_FAIL,False,"MP3 Data Unavailable")
Return NULL_STRING$
End If
MP3_DataType.MP3_Info=First MP3_Info
Select (MP3_Info_Type%)
Case MP3_INF_ALBUM:Return MP3_DataType\MP3_Album$
Case MP3_INF_ARTIST:Return MP3_DataType\MP3_Artist$
Case MP3_INF_ALBUM_ARTIST:Return MP3_DataType\MP3_Album_Artist$
Case MP3_INF_BITRATE:Return Str(MP3_DataType\MP3_AverageBitRate%)
Case MP3_INF_CATEGORY:Return MP3_DataType\MP3_Category$
Case MP3_INF_CHANNELS:Return MP3_DataType\MP3_Channels$
Case MP3_INF_MPG_VERSION:Return MP3_DataType\MP3_MPG_Version$
Case MP3_INF_COMMENTS:Return MP3_DataType\MP3_Comment$
Case MP3_INF_COPYRIGHT:Return MP3_DataType\MP3_Copyright$
Case MP3_INF_DURATION:Return DisplayTime$(MP3_DataType\MP3_DurationHours,MP3_DataType\MP3_DurationMinutes,MP3_DataType\MP3_DurationSeconds,MP3_DataType\MP3_DurationMilliSeconds,True)
Case MP3_INF_EMPHASIS:Return MP3_DataType\MP3_Emphasis$
Case MP3_INF_GENRE:Return MP3_DataType\MP3_Genre$
Case MP3_INF_MPG_LAYER:Return MP3_DataType\MP3_MPG_Layer$
Case MP3_INF_PUBLISHER :Return MP3_DataType\MP3_Publisher$
Case MP3_INF_SAMPLERATE :Return Str(MP3_DataType\MP3_AverageSampleRate%)
Case MP3_INF_TITLE :Return MP3_DataType\MP3_Title$
Case MP3_INF_TRACK :Return Str(MP3_DataType\MP3_Track_No%)
Case MP3_INF_YEAR:Return MP3_DataType\MP3_ReleaseYear$
;Case MP3_INF_SUBTITLE :Return MP3_DataType\MP3_SubTitle$
;Case MP3_INF_ALBUMSET:Return MP3_DataType\MP3_Album_Set$
;Case MP3_INF_BPM:Return Str(MP3_DataType\MP3_BPM%)
;Case MP3_INF_COMPOSER:Return MP3_DataType\MP3_Composer$
;Case MP3_INF_CONDUCTOR:Return MP3_DataType\MP3_Conductor$
;Case MP3_INF_KEY:Return MP3_DataType\MP3_Key$
;Case MP3_INF_LYRICIST:Return MP3_DataType\MP3_Lyricist$
;Case MP3_INF_LYRICS:Return MP3_DataType\MP3_Lyrics$
;Case MP3_INF_MOOD:Return MP3_DataType\MP3_Mood$
;Case MP3_INF_ORIGIN:Return MP3_DataType\MP3_Origin%
;Case MP3_INF_ORIGINALALBUM:Return MP3_DataType\MP3_Original_Album$
;Case MP3_INF_ORIGINALARTIST :Return MP3_DataType\MP3_Original_Artist$
;Case MP3_INF_ORIGINALLYRICIST :Return MP3_DataType\MP3_Original_Lyricist$
End Select
Return NULL_STRING$
End Function
Function CheckMP3DataExists%()
Local MP3_DataType.MP3_Info=First MP3_Info
Return (MP3_DataType.MP3_Info<>Null)
End Function
Function CheckMP3String%(MP3_String$)
If ((MP3_String$=UNKNOWN$) Or (MP3_String$=ZERO_STRING$) Or (MP3_String$=NULL_STRING$) Or (MP3_String$=MP3_MISSING_DATA$)) Then Return False
Return True
End Function
Function ReadMP3Tags(MP3_Filename$)
ClearAllMP3Data ; Precaution
Local Album$=NULL_STRING$
Local Title$=NULL_STRING$
Local Artist$=NULL_STRING$
Local Year$=NULL_STRING$
Local Comment$=NULL_STRING$
Local Category$=NULL_STRING$
Local txt$=NULL_STRING$
Local IterByte%
Local mp3File% = LoadAs%(MP3_Filename$,FILE_AS_DATA,0,0,0,0,0)
If (Not(mp3File%))
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_FILE_MISSING,False,EnQuote(MP3_Filename)+" is not a valid mp3 file")
Return False
End If
Local MP3_DataType.MP3_Info=New MP3_Info
MP3_DataType\MP3_FullPathName$=MP3_Filename$
MP3_DataType\MP3_FileSize%=FileSize(MP3_Filename$)
SeekFile(mp3File%,FileSize%(MP3_Filename$)-MP3_TAGV1_STARTBYTE)
For IterByte%=0 To 2
txt$ = txt$ + Chr$(ReadByte(mp3File%))
Next
If (txt$ = MP3_TAG$)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"ID3 v1 Tags present at "+DisplayBytes(Str(FilePos(mp3File%))))
txt$ = NULL_STRING$
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_FILE_ATTEMPTREAD,False,"Reading Title Tag")
For IterByte%=0 To 29
Title$= Title$ + Chr(ReadByte(mp3File%))
Next
Title$=Trim(Title$)
If ((Title$=NULL_STRING$) Or (Title$=MP3_MISSING_DATA))
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_MP3_TAGEMPTY,False,"Title Tag is empty")
Title$=MP3_MISSING_DATA
End If
MP3_DataType\MP3_Title$=Title$
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"Title Tag read: "+Title)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_FILE_ATTEMPTREAD,False,"Reading Artist Tag")
For IterByte%=0 To 29
Artist$ = Artist$ + Chr$(ReadByte(mp3File%))
Next
Artist$=Trim$(Artist$)
If ((Artist$=NULL_STRING$)Or (Artist$=MP3_MISSING_DATA))
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_MP3_TAGEMPTY,False,"Artist Tag is empty")
Artist$=MP3_DEF_ALBUM$
End If
MP3_DataType\MP3_Artist$=Artist$
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"Artist Tag read: "+Artist)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_FILE_ATTEMPTREAD,False,"Reading Album Tag")
For IterByte%=0 To 29
Album$ = Album$ + Chr$(ReadByte(mp3File%))
Next
Album$=Trim$(Album$)
If (Album$=NULL_STRING$)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_MP3_TAGEMPTY,False,"Album Tag is empty")
Album$=MP3_DEF_ALBUM$
End If
MP3_DataType\MP3_Album$=Album$
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"Album Tag read: "+Album)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_FILE_ATTEMPTREAD,False,"Reading Release Year Tag")
For IterByte%=0 To 3
Year$ = Year$ + Chr(ReadByte(mp3File))
Next
Year$=Trim$(Year$)
If (Year$=NULL_STRING) Then DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_MP3_TAGEMPTY,False,"Release Year Tag is empty")
MP3_DataType\MP3_ReleaseYear$=Year$
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"Release Year Tag read: "+Year)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_FILE_ATTEMPTREAD,False,"Reading Comment Tag")
For IterByte%=0 To 29
Comment$ = Comment$ + Chr(ReadByte(mp3File))
Next
Comment$=Trim(Comment$)
If (Comment$=NULL_STRING) Then DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_MP3_TAGEMPTY,False,"Comment Tag is empty")
MP3_DataType\MP3_Comment$=Comment$
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_GEN_SUCCESS,False,"Comment Tag read: "+Comment)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_FILE_ATTEMPTREAD,False,"Reading Category Tag")
Category$=ResolveMP3_Category$(Int(Chr(ReadByte(mp3File))))
Category$=Trim(Category$)
MP3_DataType\MP3_Category$=Category$
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"Category Tag read: "+Category)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_GEN_SUCCESS,False,"Reading v1 MP3 Tags Completed")
EndIf
SeekFile(mp3File%,False)
txt$=NULL_STRING$
IterByte%=False
Local IterReadBytes%
Local HiByte, LoByte
Local Flags%
Local Bytesize%
Local TagPosition=False
For IterByte=0 To 2
txt$ = txt$ + Chr(ReadByte(mp3File))
Next
Local Tagv2_Type%=False
If (txt = MP3_ID3)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"MP3 v2 Tags found at "+DisplayBytes(FilePos(mp3File)))
;read TAG version
HiByte = ReadByte(mp3File)
LoByte = ReadByte(mp3File)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"MP3 v2 Tag Version: "+Str(HiByte)+PATH_SEPARATOR_REVERSE+Str(LoByte))
;read flags
Flags = ReadByte(mp3File)
;read size of tag
Bytesize = RefineIntegerByte(ReadInt(mp3File))
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"MP3 v2 Frame size reported as "+DisplayBytes(MP3_DataType\MP3_FrameSize))
Dim Bytes(4)
While TagPosition < Bytesize
;read frame TAG
txt = NULL_STRING
Bytes(1) = ReadByte(mp3File)
TagPosition=TagPosition+1
If Bytes(1)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_FILE_ATTEMPTREAD,False,"Reading MP3 V2 Tag")
MP3_DataType\MP3_FrameStart=FilePos(mp3File)-1
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"MP3 v2 Tag begins at "+DisplayBytes(MP3_DataType\MP3_FrameStart))
txt$=Chr(Bytes(1))
For IterReadBytes=2 To 4
Bytes(IterReadBytes)=ReadByte(mp3File)
txt$=txt$+Chr(Bytes(IterReadBytes))
Next
TagPosition = TagPosition + 3
MP3_DataType\MP3_Tag_Key$=txt
Tagv2_Type=ResolveMP3_Tag2NameSpace(txt)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_GEN_SUCCESS,False,"MP3 v2 Tag Info type: "+txt)
MP3_DataType\MP3_FrameSize = RefineIntegerByte(ReadInt(mp3File))
TagPosition = TagPosition + REG_NIBBLESPACE
Flags = ReadShort(mp3File)
TagPosition = TagPosition + 2
MP3_DataType\MP3_Tag_Flags = Flags
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_GEN_SUCCESS,False,"MP3 v2 Tag Flags: "+Str(Flags))
txt$ = NULL_STRING
For IterReadBytes= 1 To MP3_DataType\MP3_FrameSize
txt = txt + Chr(ReadByte(mp3File))
TagPosition = TagPosition + 1
Next
txt=Trim(txt)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_GEN_SUCCESS,False,"MP3 v2 Tag information has been read: "+txt)
MP3_DataType\MP3_Tag_Data = txt
ApplyV2TagInfo(Tagv2_Type%,txt$)
EndIf
Wend
Else
SeekFile(mp3File,0)
EndIf
Dim Bytes(4)
Local pos = FilePos(mp3File)
Local iterBytes%
For iterBytes=1 To 4
Bytes(iterBytes%) = ReadByte(mp3File)
Next
If ((Bytes(True) <> $ff) And ((Bytes(2) And 224) <> 224))
; "Frame syncronizer not found"
SeekFile(mp3File,FilePos(mp3File)-4)
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_TYP_OBJECT_NULL,False,"Audioframe Data does not exist")
CloseFile mp3File
ClearAllMP3Data
Return
EndIf
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_NOT_AN_ERROR,False,"Audio Frame data found")
MP3_DataType.MP3_Info\MP3_AverageBitRate%=ResolveMP3_Bitrate%((Bytes(3) And $f0) Shr 4)
MP3_DataType.MP3_Info\MP3_AverageSampleRate%=ResolveMP3_SampleRate%((Bytes(3) And $c) Shr 2)
MP3_DataType.MP3_Info\MP3_MPG_Version$=ResolveMP3_Encode$((Bytes(2) And 24) Shr 3)
MP3_DataType.MP3_Info\MP3_MPG_Layer$=ResolveMP3_Layer$((Bytes(2) And 6) Shr 1)
MP3_DataType.MP3_Info\MP3_Channels$=ResolveMP3_Channels$((Bytes(4) And $C0) Shr 6)
MP3_DataType.MP3_Info\MP3_Emphasis$ = ResolveMP3_Emphasis$(Bytes(4) And $3)
MP3_DataType.MP3_Info\MP3_CRC%=(Bytes(2) And $1)
MP3_DataType.MP3_Info\MP3_Origin%=(Bytes(4) And $4)
CloseFile mp3File
Stop
End Function
Function CheckIsValidMP3%(MP3_FullPath$)
If ((Not ((CheckPath(MP3_FullPath$,FILETYPE_FILE))*(GetExtension(MP3_FullPath$)=EXT_MP3)))Or (FileSize(MP3_FullPath$)<129)Or (FileSize(MP3_FullPath$)<129)Or (FileSize(MP3_FullPath$)<129)Or (GetExtension(MP3_FullPath)<>EXT_MP3))
DebugLine("CONFIGMP3:CHECKISVALIDMP3",DBG_ERC_FILE_MISSING,False,MP3_FullPath$+" Is not a valid MP3 file.")
Return False
Else
DebugLine("CONFIGMP3:CHECKISVALIDMP3",DBG_ERC_GEN_SUCCESS,False,MP3_FullPath$+" Is a valid MP3 file.")
Return True
EndIf
End Function
Function ClearAllMP3Data()
DebugLine("CONFIGMP3:CLEARALLMP3DATA",DBG_ERC_NOT_AN_ERROR,False,"Clearing data")
Delete Each MP3_Info
End Function
Function ApplyV2TagInfo(MP3_Tagv2_Type,Tag_Data$)
Local MP3_DataType.MP3_Info=First MP3_Info
Local Calc_Duration_Hours%
Local Calc_Duration_Minutes%
Local Calc_Duration_Seconds%
Local Calc_Duration_MilliSeconds%
If (Tag_Data=NULL_STRING)
DebugLine("CONFIGMP3:APPLYV2TAGINFO",DBG_ERC_MP3_TAGEMPTY,False,"Tag Data is empty")
Return
End If
If (MP3_DataType.MP3_Info=Null)
DebugLine("CONFIGMP3:APPLYV2TAGINFO",DBG_ERC_TYP_OBJECT_NULL,False,"MP3 Tag data does not exist")
ClearAllMP3Data
Return
End If
DebugLine("CONFIGMP3:APPLYV2TAGINFO",DBG_ERC_NOT_AN_ERROR,False,"MP3 Data Type: "+Str(MP3_Tagv2_Type)+" found as "+Tag_Data$)
Select (MP3_Tagv2_Type)
Case MP3_INF_ALBUM%:MP3_DataType\MP3_Album$=Tag_Data$
Case MP3_INF_COMMENTS%:MP3_DataType\MP3_Comment$=Tag_Data$
Case MP3_INF_ARTIST%:MP3_DataType\MP3_Artist$=Tag_Data$
Case MP3_INF_GENRE%:MP3_DataType\MP3_Genre$=Tag_Data$
Case MP3_INF_TITLE%:MP3_DataType\MP3_Title$=Tag_Data$
Case MP3_INF_TRACK%:MP3_DataType\MP3_Track_No%=Int(Tag_Data$)
Case MP3_INF_DURATION%:
Calc_Duration_MilliSeconds%=(Int(Tag_Data))
Calc_Duration_Hours=GiveTimeAs(Calc_Duration_MilliSeconds,TIME_AS_HOUR24)
Calc_Duration_Minutes=GiveTimeAs(Calc_Duration_MilliSeconds,TIME_AS_MINUTE)
Calc_Duration_Seconds=GiveTimeAs(Calc_Duration_MilliSeconds,TIME_AS_SECOND)
MP3_DataType\MP3_DurationHours=Calc_Duration_Hours
MP3_DataType\MP3_DurationMinutes=Calc_Duration_Minutes
MP3_DataType\MP3_DurationSeconds=Calc_Duration_Seconds
MP3_DataType\MP3_DurationMilliSeconds=Calc_Duration_MilliSeconds Mod 1000
Case MP3_INF_ALBUM_ARTIST%:MP3_DataType\MP3_Album_Artist$=Tag_Data$
Case MP3_INF_YEAR%:MP3_DataType\MP3_ReleaseYear$=Tag_Data$
Case MP3_INF_PUBLISHER%:MP3_DataType\MP3_Publisher$=Tag_Data$
Case MP3_INF_LANGUAGE%:MP3_DataType\MP3_Language$=Tag_Data$
Case MP3_INF_CODEC%:MP3_DataType\MP3_Codec$=Tag_Data$
Case MP3_INF_STREAMENC%:MP3_DataType\MP3_StreamEncoding$=Tag_Data$
Case MP3_INF_ART%:MP3_DataType\MP3_Art$=Tag_Data$
;Case MP3_INF_SUBTITLE%:MP3_DataType\MP3_SubTitle$=Tag_Data$
;Case MP3_INF_ORIGINAL_ARTIST%:MP3_DataType\MP3_Original_Artist$=Tag_Data$
;Case MP3_INF_LYRICIST%:MP3_DataType\MP3_Lyricist$=Tag_Data$
;Case MP3_INF_ORIGINAL_LYRICIST%:MP3_DataType\MP3_Original_Lyricist$=Tag_Data$
;Case MP3_INF_COMPOSER%:MP3_DataType\MP3_Composer$=Tag_Data$
;Case MP3_INF_CONDUCTOR%:MP3_DataType\MP3_Conductor$=Tag_Data$
;Case MP3_INF_ORIGINAL_ALBUM%:MP3_DataType\MP3_Original_Album$=Tag_Data$
;Case MP3_INF_ALBUM_SET%:MP3_DataType\MP3_Album_Set$=Tag_Data$
;Case MP3_INF_CATDESC%:MP3_DataType\MP3_Category_Description$=Tag_Data$
;Case MP3_INF_LYRICS%:MP3_DataType\MP3_Lyrics$=Tag_Data$
;Case MP3_INF_TEMPO%:MP3_DataType\MP3_Tempo%=Int(Tag_Data$)
;Case MP3_INF_KEY%:MP3_DataType\MP3_Key$=Tag_Data$
;Case MP3_INF_ISRC%:MP3_DataType\MP3_ISRC$=Tag_Data$
;Case MP3_INF_MCDI%:MP3_DataType\MP3_MCDI$=Tag_Data$
;Case MP3_INF_MCDI%:MP3_DataType\MP3_WM_F%=Int(TagData$)
;Case MP3_INF_MCDI%:MP3_DataType\MP3_WM_R%=Int(TagData$)
;Case MP3_INF_MCDI%:MP3_DataType\MP3_WM_S%=Int(TagData$)
;Case MP3_INF_MCDI%:MP3_DataType\MP3_WM_X%=Int(TagData$)
Default
DebugLine("CONFIGMP3:APPLYV2TAGINFO",DBG_ERC_MP3_TAGUNKNOWN,False,"Unknown or Private data reference")
End Select
End Function
Function ResolveMP3_Category$(nCategoryByte%)
Select (nCategoryByte%)
Case 0 : Return"Blues"
Case 1 : Return"Classic Rock"
Case 2 : Return"Country"
Case 3 : Return"Dance"
Case 4 : Return"Disco"
Case 5 : Return"Funk"
Case 6 : Return"Grunge"
Case 7 : Return"Hip-Hop"
Case 8 : Return"Jazz"
Case 9 : Return"Metal"
Case 10 : Return"New Age"
Case 11 : Return"Oldies"
Case 12 : Return"Other"
Case 13 : Return"Pop"
Case 14 : Return"Rhythm & Blues"
Case 15 : Return"Rap"
Case 16 : Return"Reggae"
Case 17 : Return"Rock"
Case 18 : Return"Techno"
Case 19 : Return"Industrial"
Case 20 : Return"Alternative"
Case 21 : Return"Ska"
Case 22 : Return"Death Metal"
Case 23 : Return"Pranks"
Case 24 : Return"Soundtrack"
Case 25 : Return"Euro-Techno"
Case 26 : Return"Ambient"
Case 27 : Return"Trip-Hop"
Case 28 : Return"Vocal"
Case 29 : Return"Jazz & Funk"
Case 30 : Return"Fusion"
Case 31 : Return"Trance"
Case 32 : Return"Classical"
Case 33 : Return"Instrumental"
Case 34 : Return"Acid House"
Case 35 : Return"House & Garage"
Case 36 : Return"Game"
Case 37 : Return"Sound Clip"
Case 38 : Return"Gospel"
Case 39 : Return"Noise"
Case 40 : Return"Alternative Rock"
Case 41 : Return"Bass"
Case 42 : Return"Soul"
Case 43 : Return"Punk Rock"
Case 44 : Return"Space"
Case 45 : Return"Meditative"
Case 46 : Return"Instrumental Pop"
Case 47 : Return"Instrumental Rock"
Case 48 : Return"Tribal"
Case 49 : Return"Gothic Rock"
Case 50 : Return"Darkwave"
Case 51 : Return"Industrial Techno"
Case 52 : Return"Electronica"
Case 53 : Return"Folk Ballads"
Case 54 : Return"Eurodance"
Case 55 : Return"Dream Dance"
Case 56 : Return"Southern Rock"
Case 57 : Return"Comedy"
Case 58 : Return"Cult Classic"
Case 59 : Return"Gangsta Rap"
Case 60 : Return"Top 40"
Case 61 : Return"Christian"
Case 62 : Return"Funkadelica"
Case 63 : Return"Drum & Bass"
Case 64 : Return"Ethnic"
Case 65 : Return"Cabaret"
Case 66 : Return"New Wave"
Case 67 : Return"Psychadelic"
Case 68 : Return"Rave"
Case 69 : Return"Showtunes"
Case 70 : Return"Trailer"
Case 71 : Return"Lo-Fidelity"
Case 72 : Return"Big Beat"
Case 73 : Return"Acid Punk"
Case 74 : Return"Acid Jazz"
Case 75 : Return"Polka"
Case 76 : Return"Retro & Oldskool"
Case 77 : Return"Musical"
Case 78 : Return"Rock 'N' Roll"
Case 79 : Return"Hard Rock"
Default :
DebugLine("CONFIGMP3:READMP3TAGS",DBG_ERC_MP3_TAGUNKNOWN,False,"Category bytedata value: "+Str(nCategoryByte%))
Return MP3_MISSING_DATA$
End Select
End Function
Function ResolveMP3_Channels$(nChannelByte)
Select nChannelByte
Case 0:Return "Stereo"
Case 1:Return "Joint Stereo"
Case 2:Return "Dual"
Case 3:Return "Mono"
Default :
DebugLine("CONFIGMP3:RESOLVEMP3CHANNELS",DBG_ERC_MP3_TAGUNKNOWN,False,"Channel bytedata value: "+Str(nRateByte%))
Return UNKNOWN$
End Select
End Function
Function ResolveMP3_Bitrate%(nRateByte)
Select nRateByte
Case 1:Return 32
Case 2:Return 40
Case 3:Return 48
Case 4:Return 56
Case 5:Return 64
Case 6:Return 80
Case 7:Return 96
Case 8:Return 112
Case 9:Return 128
Case 10:Return 160
Case 11:Return 192
Case 12:Return 224
Case 13:Return 256
Case 14:Return 320
Case 15:Return 320 ;I'm not yet sure of the precise difference between 14 and 15...
Default :
DebugLine("CONFIGMP3:RESOLVEMP3BITRATE",DBG_ERC_MP3_TAGUNKNOWN,False,"Bitrate bytedata value: "+Str(nRateByte%))
Return False
End Select
End Function
Function ResolveMP3_SampleRate%(nRateByte%)
Select nRateByte
Case 0: Return 44100
Case 1: Return 48000
Case 2: Return 44000
Case 3: Return 44000
Default :
DebugLine("CONFIGMP3:RESOLVEMP3SAMPLERATE",DBG_ERC_MP3_TAGUNKNOWN,False,"Samplerate bytedata value: "+Str(nRateByte%))
Return False
End Select
End Function
Function ResolveMP3_Encode$(nVersionByte%)
Select nVersionByte
Case 0:Return "MPEG v2.5 Unofficial"
Case 2:Return "MPEG v2"
Case 3:Return "MPEG v1"
Default :
DebugLine("CONFIGMP3:RESOLVEMP3ENCODE",DBG_ERC_MP3_TAGUNKNOWN,False,"MPEG Version bytedata value: "+Str(nVersionByte%))
Return UNKNOWN$
End Select
End Function
Function ResolveMP3_Layer$(nLayerByte)
Select nLayerByte
Case 1:Return "MPEG Layer 3"
Case 2:Return "MPEG Layer 2"
Case 3:Return "MPEG Layer 1"
Default :
DebugLine("CONFIGMP3:CONFIGMP3:RESOLVEMP3LAYER",DBG_ERC_MP3_TAGUNKNOWN,False,"MPEG Layer bytedata value: "+Str(nLayerByte%))
Return UNKNOWN$
End Select
End Function
Function ResolveMP3_Emphasis$(nEmphasisByte)
Select nEmphasisByte
Case 0: Return "NONE"
Case 1: Return "50/15"
Case 3: Return "CCIT J.17"
Default:
DebugLine("CONFIGMP3:RESOLVEMP3EMPHASIS",DBG_ERC_MP3_TAGUNKNOWN,False,"Emphasis bytedata value: "+Str(nEmphasisByte%))
Return "Reserved"
End Select
End Function
Function ResolveMP3_Tag2NameSpace%(Namespace$)
Select Namespace$
Case MP3_TAGV2_NAMESPACE_ALBUM$:Return MP3_INF_ALBUM%
Case MP3_TAGV2_NAMESPACE_COMMENTS$:Return MP3_INF_COMMENTS%
Case MP3_TAGV2_NAMESPACE_ARTIST$:Return MP3_INF_ARTIST%
Case MP3_TAGV2_NAMESPACE_COPYRIGHT$:Return MP3_INF_COPYRIGHT%
Case MP3_TAGV2_NAMESPACE_GENRE$:Return MP3_INF_GENRE%
Case MP3_TAGV2_NAMESPACE_TITLE$:Return MP3_INF_TITLE%
Case MP3_TAGV2_NAMESPACE_TRACK$:Return MP3_INF_TRACK%
Case MP3_TAGV2_NAMESPACE_DURATION$:Return MP3_INF_DURATION%
Case MP3_TAGV2_NAMESPACE_ALBUM_ARTIST$:Return MP3_INF_ALBUM_ARTIST%
Case MP3_TAGV2_NAMESPACE_YEAR$:Return MP3_INF_YEAR%
Case MP3_TAGV2_NAMESPACE_PUBLISHER$:Return MP3_INF_PUBLISHER%
Case MP3_TAGV2_NAMESPACE_LANGUAGE$:Return MP3_INF_LANGUAGE%
Case MP3_TAGV2_NAMESPACE_STREAMENC:Return MP3_INF_STREAMENC%
Case MP3_TAGV2_NAMESPACE_CODEC$:Return MP3_INF_CODEC%
Case MP3_TAGV2_NAMESPACE_ART$:Return MP3_INF_ART%
;Case MP3_TAGV2_NAMESPACE_SUBTITLE$:Return MP3_INF_SUBTITLE%
;Case MP3_TAGV2_NAMESPACE_COMPOSER$:Return MP3_INF_COMPOSER%
;Case MP3_TAGV2_NAMESPACE_CONDUCTOR$:Return MP3_INF_CONDUCTOR%
;Case MP3_TAGV2_NAMESPACE_ORIGINAL_ARTIST$:Return MP3_INF_ORIGINAL_ARTIST%
;Case MP3_TAGV2_NAMESPACE_ORIGINAL_ALBUM$:Return MP3_INF_ORIGINAL _ALBUM%
;Case MP3_TAGV2_NAMESPACE_LYRICS$="USLT":Return MP3_INF_LYRICS%
;Case MP3_TAGV2_NAMESPACE_LYRICIST$:Return MP3_INF_LYRICIST%
;Case MP3_TAGV2_NAMESPACE_ORIGINAL_LYRICIST$:Return MP3_INF_ORIGINAL_LYRICIST%
;Case MP3_TAGV2_NAMESPACE_ALBUM_SET$:Return MP3_INF_ALBUM_SET%
;Case MP3_TAGV2_NAMESPACE_CATDESC$:Return MP3_INF_CATDESC%
;Case MP3_TAGV2_NAMESPACE_ISRC$:Return MP3_INF_ISRC%
;Case MP3_TAGV2_NAMESPACE_KEY$:Return MP3_INF_KEY%
;Case MP3_TAGV2_NAMESPACE_TEMPO$:Return MP3_INF_TEMPO%
;Case MP3_TAGV2_NAMESPACE_MCDI$:Return MP3_INF_MCDI%
;Case MP3_TAGV2_NAMESPACE_WM_F$:Return MP3_INF_WM_F%
;Case MP3_TAGV2_NAMESPACE_WM_R$:Return MP3_INF_WM_R%
;Case MP3_TAGV2_NAMESPACE_WM_S$:Return MP3_INF_WM_S%
;Case MP3_TAGV2_NAMESPACE_WM_X$:Return MP3_INF_WM_X%
;Case MP3_TAGV2_NAMESPACE_PRIVATE$:Return MP3_INF_PRIVATE%
Default:
DebugLine("CONFIGMP3:RESOLVEMP3TAGV2NAMESPACE",DBG_ERC_MP3_TAGUNKNOWN,False,"Unrecognised MP3 Tag v2 Namespace: "+Namespace$)
Return MP3_INF_UNKNOWN%
End Select
End Function
____________________ lawks a lordy, my bottom's on fire! |
| ||
| I was eager to smarten up the MP3 tag reading codes that are available in the archives. This grew into a further addition to the "Generic Engine" The MP3 Functions still store the data in Type fields, however, aside from the temporary AudioFrame data, (which is removed after use, for the sake of cleanliness and preventing memory leaks) there is only one Type instance required which contains ALL the information available in a single MP3. Whilst I personally omitted many of the 'PRIV' entries, and other deprecated or obsolete/unnecessary information, I was pleased to discover the duration IS stored within a TAG. The Identifier is "TLEN" and the value represents the duration of an mp3 sound in Milliseconds. Not only is this a lot easier to work with, it is also a lot more accurate than floating point calculations done with the bitrates etc. To further speed things up and make things easier for use, some quick-access functions have been added to jump straight to a particular value if you only wish to retrieve one or two pieces of data, such as, the Title and the Artist. I included some funcitonality to retrieve the "best" of fields, in the case where superceded tags may contain different or inaccurate data. For example, in the light of "Genre" (a version 1 Tag) not being correct, then checks are madew with both Genre2 (The V2 Tag) and even wth "Category" or "Mood" ifeven Genre2 is inaccurate. Finally, with the use of additional namespace identifiers, these functions should be fairly futureproof in the case of later TAG systems being added in the future. ____________________ lawks a lordy, my bottom's on fire! |
| ||
| I replaced the filesize checks with CRC checks for the updater funcions. This is not only more accurate but also secure and can now also be used as an integrity check for the installation overall. As with network code, All CRC related text is encrypted and only decrypted on runtime. Also renamed and reorganised all decls to ensure any dll functions contain a prefix with the specific DLL in front. Each DLL has its own decls file for ease of identification. Later, the unused funcitons may be stripped from decls (hopefully shrinking size of exe?) but at least, making it easier to locate any problematic functions, or identify where they are from. ____________________ lawks a lordy, my bottom's on fire! |
| ||
| Added check for File_As_Data File-Writing in SaveAs function to return the FileHandle in the caase of OpenToWrite. Allowing for Writing to the Filestream returned. For exporting of other resources, however, the return value is simply True or False whether the file was written successfully or not. Added GPU_HardwareCheck to ensure stability if a configuration file exists but graphics drivers have been altered. Subsequent re-discovery of Graphics Mode reference is also included. This change included a requirement to store the GfxDriverName as GPU_CURRENT_DRIVER_NAME to the configuration file Added Text input routines with cursor placement. Curentlyu supported HOME/END, Arrow Cursor (Left/Right)keys, CTRL+V To INSERT at cursor location, Caps Lock. Hoping to add a mousepointer Caret selection functionality, Numlock abnd Keypad definitons, Highlighting, and Overwrite/Insert toggling. Added Debugging error code for if GPU is updated and no valid resolutions exist. Added Pre-Install and Read Me text to installer. Enhanced and streamlined controller input functions, also included practical storage of known controller configurations. ____________________ lawks a lordy, my bottom's on fire! |
| ||
| Fixed a small bug where the Default font size was growing beyond viable parameters (There is an undocumented limit on Font Height values, that when loaded will result in SetFont failure. Not sure what the exact limit is, because the bug itself wasa causing extreme values over 10 000 - Typical values up to 300 or so seem to work fine I can find no reason why any larger would ever be required!) Amended the LoadAs() routines under "ExplorerMain.bb" (dependency) and Generic Resource functionality for TTF fonts to ensure a working font is found and loaded. TTF fonts have a Resource Type value of 13 Added generic Success/Failure report values to Debugger. SafeDelete reports Success or Failure to Debuglog regardless. (Missing file error results as Success since the file does not exist after completion. Otherwise, Manual decline of the Delete request results in Failure since the file is Not deleted.) Updated Null resource for mesh. The result is now a 3D depiction of the word "Null" Added Default values for ScreenWidth/height so that image resources and 2D screen placements can be scaled correctly if based on a specific original resolution. Trial functionality for scaling to ensure Widescreen modes or other ratios are scaled with the correct format accordingly: Function ScaleToResolution(Image%) ResizeImage Image,ResolutionScaledX%(ImageWidth(Image)),ResolutionScaledY%(ImageHeight(Image)) End Function Function ResolutionScaledX%(X) Return (SCREEN_WIDTH/DEFAULT_SCREEN_WIDTH%)*ResolutionRatioScaleX(X) End Function Function ResolutionScaledY%(Y) Return (SCREEN_HEIGHT/DEFAULT_SCREEN_HEIGHT%)*ResolutionRatioScaleY(Y) End Function Function ResolutionRatioScaleX%(X) If Not(DEFAULT_SCREEN_RATIO=SCREEN_RESOLUTION_TYPE) Select DEFAULT_SCREEN_RATIO Case RESOLUTION_TYPE_16_10 X=X Shr 4 Case RESOLUTION_TYPE_16_9: X=(X Shl 4) Case RESOLUTION_TYPE_3_2: X=(X / 3) Case RESOLUTION_TYPE_4_3: X=(X Shr 2) Case RESOLUTION_TYPE_40_27: X=((X Shr 2) * 0.1) Case RESOLUTION_TYPE_5_3: X=(X * 0.2) Case RESOLUTION_TYPE_5_4: X=(X * 0.2) Case RESOLUTION_TYPE_53_30: Return (X / 53) Case RESOLUTION_TYPE_85_48: Return (X / 85) Case RESOLUTION_TYPE_85_64: Return (X / 85) Default: End Select Select SCREEN_RESOLUTION_TYPE Case RESOLUTION_TYPE_16_10 Return (X Shl 4) Case RESOLUTION_TYPE_16_9: Return (X Shl 4) Case RESOLUTION_TYPE_3_2: Return (X * 3) Case RESOLUTION_TYPE_4_3: Return (X Shl 2) Case RESOLUTION_TYPE_40_27: Return ((X Shl 2) * 10) Case RESOLUTION_TYPE_5_3: Return (X * 5) Case RESOLUTION_TYPE_5_4: Return (X * 5) Case RESOLUTION_TYPE_53_30: Return (X * 53) Case RESOLUTION_TYPE_85_48: Return (X * 85) Case RESOLUTION_TYPE_85_64: Return (X * 85) Default: End Select End If Return X End Function Function ResolutionRatioScaleY%(Y) If Not(DEFAULT_SCREEN_RATIO=SCREEN_RESOLUTION_TYPE) Select DEFAULT_SCREEN_RATIO Case RESOLUTION_TYPE_16_10 Y=Y * 0.1 Case RESOLUTION_TYPE_16_9: Y=(Y / 9) Case RESOLUTION_TYPE_3_2: Y=(Y Shr True) Case RESOLUTION_TYPE_4_3: Y=(Y / 3) Case RESOLUTION_TYPE_40_27: Y=(Y / 27) Case RESOLUTION_TYPE_5_3: Y=(Y / 3) Case RESOLUTION_TYPE_5_4: Y=(Y Shr 2) Case RESOLUTION_TYPE_53_30: Return (Y / 30) Case RESOLUTION_TYPE_85_48: Return (Y / 48) Case RESOLUTION_TYPE_85_64: Return (Y Shr 6) Default: End Select Select SCREEN_RESOLUTION_TYPE Case RESOLUTION_TYPE_16_10 Return (Y * 10) Case RESOLUTION_TYPE_16_9: Return (Y * 9) Case RESOLUTION_TYPE_3_2: Return (Y Shl True) Case RESOLUTION_TYPE_4_3: Return (Y * 3) Case RESOLUTION_TYPE_40_27: Return (Y * 27) Case RESOLUTION_TYPE_5_3: Return (Y * 3) Case RESOLUTION_TYPE_5_4: Return (Y Shl 2) Case RESOLUTION_TYPE_53_30: Return (Y * 30) Case RESOLUTION_TYPE_85_48: Return (Y * 48) Case RESOLUTION_TYPE_85_64: Return (Y Shl 6) Default: End Select End If Return Y End Function ____________________ lawks a lordy, my bottom's on fire! |
| ||
| With a small custom Type and a clean (i.e. unused/rarely used fonts are/can be deleted as soon as writing has been done) coupled witha global Type andle to keep track of the current font - Sparkly new GetCurrentFont() function! - This manages font loading and 'setting' and combines it with optional generation of bitmap fonts. It's fairly robust and safe and will check for and if necessary generate new instances where nulls crop up, as well as ensuring no 'bad' fonts are used. For future reference, I will liekly add in a 'FixFontName' functionality to convert at least the standard MS fonts whose internal names differ from the filenames. In the cases these 'bad' fonts are attempted, the engine simply swaps for a default (currently "Arial" rather than the Blitz font, because that cannot easily be checked for if it replaces a bad 'Load Font' command. Although this default may be used for any customied font that ships with products once the user has determined it's "safe". Here's the guts of the Font functions
Function BuildDefaultFont()
If (FontByCriteria.FontType(DEFAULT_UI_FONT_FONTNAME,DEFAULT_UI_FONT_SIZE,0,0,0)=Null)
LoadUIFont.FontType(DEFAULT_UI_FONT_FONTNAME,DEFAULT_UI_FONT_SIZE,0,0,0)
End If
End Function
Function SetCurrentFontToDefault()
If (FontByCriteria.FontType(DEFAULT_UI_FONT_FONTNAME,DEFAULT_UI_FONT_SIZE)=Null) Then BuildDefaultFont()
CurrentFont.FontType=FontByCriteria(DEFAULT_UI_FONT_FONTNAME,DEFAULT_UI_FONT_SIZE)
End Function
Function LoadUIFont.FontType(FontName$,Height%,Bold%,Italic%,Underline%)
Local SeekFnt.FontType=GetFont.FontType(FontName$,Height%,Bold%,Italic%,Underline%,True)
If (SeekFnt.FontType<>Null)
DebugLine("CONFIGSCREEN:LOADUIFONT",DBG_ERC_NOT_AN_ERROR,False,"Font "+FontName$+" as "+Parenthesise(Str(SeekFnt\FNT_FontHandle)+" bitmap: "+Parenthesise(SeekFnt\FNT_BitmapHandle)+" already in memory"))
Return SeekFnt.FontType
End If
DebugLine("CONFIGSCREEN:LOADUIFONT",DBG_ERC_NOT_AN_ERROR,False,"Loading Font: "+FontName$)
Local FntPath$=FixPath$(DLLGetSpecialFolder$(CSIDL_FONTS,512),True)
SeekFnt.FontType = New FontType
SeekFnt\FNT_FontHandle=LoadFontHandle(FntPath$+FontName$+EXTENSION_SEPARATOR+EXT_TTF,Height,Bold,Italic,Underline)
If (SeekFnt\FNT_FontHandle)
SetFont SeekFnt\FNT_FontHandle
Else
SetCurrentFontToDefault()
Delete SeekFnt
SeekFnt.FontType=CurrentFont.FontType
End If
DebugLine("CONFIGSCREEN:LOADUIFONT",DBG_ERC_NOT_AN_ERROR,False,"Font Stored in memory as "+SeekFnt\FNT_FontHandle)
SeekFnt\FNT_BitmapHandle=BuildBitmapFont%(FontName$,Height%,Bold%,Italic%,Underline%)
Return SeekFnt.FontType
End Function
Function GetFont.FontType(FontName$,Height,Bold,Italic,Underline,Bitmap=False)
Local SeekFnt.FontType
Local nReturn%=False
Local FntPath$=FixPath$(DLLGetSpecialFolder$(CSIDL_FONTS,512),True)
For SeekFnt.FontType = Each FontType
If ((SeekFnt\FNT_FontName$ = FontName$) And (SeekFnt\FNT_Height = Height) And (SeekFnt\FNT_Bold = Bold) And (SeekFnt\FNT_Italic = Italic)And (SeekFnt\FNT_Underline = Underline) And (Not(SeekFnt\FNT_FontHandle)))
DebugLine("CONFIGSCREEN:GETFONT",DBG_ERC_FILE_RESHANDLEZERO,False,"Font: "+FontName$+" has no resource handle")
SeekFnt\FNT_FontHandle=LoadFontHandle(FntPath$+FontName$+EXTENSION_SEPARATOR+EXT_TTF,Height,Bold,Italic,Underline)
End If
If ((SeekFnt\FNT_FontName$ = FontName$) And (SeekFnt\FNT_Height = Height) And (SeekFnt\FNT_Bold = Bold) And (SeekFnt\FNT_Italic = Italic)And (SeekFnt\FNT_Underline = Underline) And (SeekFnt\FNT_FontHandle))
DebugLine("CONFIGSCREEN:GETFONT",DBG_ERC_NOT_AN_ERROR,False,"Font: "+FontName$+" found with criteria ("+Str(SeekFnt\FNT_FontHandle)+")")
End If
If ((SeekFnt\FNT_BitmapHandle) And (CheckPath(dUI$+FontName$+Str(Height)+DATE_SEPARATOR$+Str(Bold)+DATE_SEPARATOR$+Str(Italic)+DATE_SEPARATOR$+Str(Underline)+EXTENSION_SEPARATOR+EXT_BMP,FILETYPE_FILE)))
DebugLine("CONFIGSCREEN:GETFONT",DBG_ERC_NOT_AN_ERROR,False,"Bitmap Font: "+FontName$+" found with criteria ("+Str(SeekFnt\FNT_BitmapHandle)+")")
Return SeekFnt.FontType
Exit
End If
Next
DebugLine("CONFIGSCREEN:GETFONT",DBG_ERC_TYP_OBJECT_NULL,False,"Font: "+FontName$+" cannot be found in memory")
Return Null
End Function
Function LoadBitmapFontOnly%(FontName$,Height,Bold,Italic,Underline)
Local SeekFnt.FontType
Local nReturn%=False
For SeekFnt.FontType=Each FontType
If (SeekFnt<>Null)
If ((CheckPath(dUI$+FontName$+Str(Height)+DATE_SEPARATOR$+Str(Bold)+DATE_SEPARATOR$+Str(Italic)+DATE_SEPARATOR$+Str(Underline)+EXTENSION_SEPARATOR+EXT_BMP,FILETYPE_FILE)))
If ((SeekFnt\FNT_BitmapHandle))
DebugLine("CONFIGSCREEN:LOADBITMAPFONTONLY",DBG_ERC_NOT_AN_ERROR,False,"Bitmap Font: "+FontName$+" found in memory with criteria ("+Str(SeekFnt\FNT_BitmapHandle)+")")
Else
DebugLine("CONFIGSCREEN:LOADUIFONT",DBG_ERC_NOT_AN_ERROR,False,"Bitmap Font found in local directory: "+dUI$+". Loading that image")
SeekFnt\FNT_BitmapHandle=LoadAs(dUI$+FontName$+Str(Height)+DATE_SEPARATOR$+Str(Bold)+DATE_SEPARATOR$+Str(Italic)+DATE_SEPARATOR$+Str(Underline)+EXTENSION_SEPARATOR+EXT_BMP,FILE_AS_IMAGE,Height,Bold,Italic,Underline,False)
EndIf
Exit
End If
End If
Next
If (SeekFnt.FontType=Null)
DebugLine("CONFIGSCREEN:LOADUIFONT",DBG_ERC_TYP_OBJECT_NULL,False,"No Type object for this bitmap font. Creating new")
If ((CheckPath(dUI$+FontName$+Str(Height)+DATE_SEPARATOR$+Str(Bold)+DATE_SEPARATOR$+Str(Italic)+DATE_SEPARATOR$+Str(Underline)+EXTENSION_SEPARATOR+EXT_BMP,FILETYPE_FILE)))
SeekFnt.FontType=New FontType
SeekFnt\FNT_BitmapHandle=LoadAs(dUI$+FontName$+Str(Height)+DATE_SEPARATOR$+Str(Bold)+DATE_SEPARATOR$+Str(Italic)+DATE_SEPARATOR$+Str(Underline)+EXTENSION_SEPARATOR+EXT_BMP,FILE_AS_IMAGE,Height,Bold,Italic,Underline,False)
SeekFnt\FNT_FontHandle=False
SeekFnt\FNT_Bold=Bold
SeekFnt\FNT_Italic=Italic
SeekFnt\FNT_Underline=Underline
SeekFnt\FNT_Height=Height
SeekFnt\FNT_FontName=FontName$
Else
DebugLine("CONFIGSCREEN:LOADBITMAPFONTONLY",DBG_ERC_UI_BAD_FONT,False,"Bitmap Font file: "+dUI$+FontName$+Str(Height)+DATE_SEPARATOR$+Str(Bold)+DATE_SEPARATOR$+Str(Italic)+DATE_SEPARATOR$+Str(Underline)+EXTENSION_SEPARATOR+EXT_BMP" not found. Generating new font Type object with bitmap")
SeekFnt.FontType=GetFont.FontType(DEFAULT_UI_FONT_FONTNAME,DEFAULT_UI_FONT_SIZE,Bold,Italic,Underline,True)
End If
End If
nReturn%=SeekFnt\FNT_BitmapHandle%
Return nReturn%
End Function
Function LoadFontHandle(Filepath$,Height%,Bold%,Italic%,Underline%)
Local FontHandle%
Local Filename$
If (Not(CheckPath(Filepath$,FILETYPE_FILE)))
Local Path$=FixPath$(GetContainerDir$(Filepath$),True)
Filename$=GetFilename$(Filepath$)
If Path$=FixPath$(DLLGetSpecialFolder$(CSIDL_FONTS,512),True)
If (CheckPath(dUI$+Filename$,FILETYPE_FILE))
Filepath$=dUI$+Filename$
DebugLine("CONFIGSCREEN:LOADFONTHANDLE",DBG_ERC_NOT_AN_ERROR,False,"Font file "+Filename$+" found in local directory: "+GetContainerDir(Filepath$))
End If
End If
Filename$=GetFilename$(Filepath$)
If (Not(CheckPath(Filepath$,FILETYPE_FILE)))
DebugLine("CONFIGSCREEN:LOADFONTHANDLE",DBG_ERC_FILE_MISSING,False,"Font file "+Filepath$+" cannot be found. Replace with default font: "+DEFAULT_UI_FONT_FONTNAME)
Filepath$=FixPath$(DLLGetSpecialFolder$(CSIDL_FONTS,512),True)+DEFAULT_UI_FONT_FONTNAME+EXTENSION_SEPARATOR+EXT_TTF
Filename$=GetFilename$(Filepath$)
Height=DEFAULT_UI_FONT_SIZE
Bold=0
Italic=0
Underline=0
If (Not(CheckPath(Filepath$,FILETYPE_FILE)))
DebugLine("CONFIGSCREEN:LOADFONTHANDLE",DBG_ERC_FILE_CRITICAL_MISSING,True,"System Font: "+Filename$+" is not installed")
End If
End If
End If
Filename$=GetFilename$(Filepath$)
DebugLine("CONFIGSCREEN:LOADFONTHANDLE",DBG_ERC_NOT_AN_ERROR,False,"Assigning handle to font "+Filepath$)
FontHandle%=LoadAs(Filepath$,FILE_AS_FONT,Height%,Bold%,Italic%,Underline%,0)
Return FontHandle%
End Function
Function BuildBitmapFont%(Name$,Ex1%,Ex2$,Ex3$,Ex4$)
Local Image%
If (CheckPath(dUI$+Name$+Str(Ex1)+DATE_SEPARATOR$+Str(Ex2)+DATE_SEPARATOR$+Str(Ex3)+DATE_SEPARATOR$+Str(Ex4)+EXTENSION_SEPARATOR$+EXT_BMP,FILETYPE_FILE))
DebugLine("CONFIGSCREEN:BUILDFONTIMAGE",DBG_ERC_NOT_AN_ERROR,False,"Image for font "+Name$+" already exists as "+Image+". About to load that bitmap font")
Image=LoadAs(dUI$+Name$+Str(Ex1)+DATE_SEPARATOR$+Str(Ex2)+DATE_SEPARATOR$+Str(Ex3)+DATE_SEPARATOR$+Str(Ex4)+EXTENSION_SEPARATOR$+EXT_BMP,FILE_AS_IMAGE,Ex1,Ex2,Ex3,Ex4,0)
Return Image
End If
DebugLine("CONFIGSCREEN:BUILDFONTIMAGE",DBG_ERC_NOT_AN_ERROR,False,"Generating bitmap for font: "+Name$+" as "+Str(Ex1)+CSV+Str(Ex2)+CSV+Str(Ex3)+CSV+Str(Ex4))
Image%=CreateFontImage()
Local nTest=SaveAs(dUI$,Name$+Str(Ex1)+DATE_SEPARATOR$+Str(Ex2)+DATE_SEPARATOR$+Str(Ex3)+DATE_SEPARATOR$+Str(Ex4),EXT_BMP,FILE_AS_IMAGE,ImageBuffer(Image),0)
If (Not(nTest))
DebugLine("CONFIGSCREEN:BUILDFONTIMAGE",DBG_ERC_FILE_MISSING,False,"writing bitmap for font: "+Name$+" failed")
End If
Return Image%
End Function
Function FontByCriteria.FontType(Name$=NULL_STRING$,Height=-1,Bold=-1,Italic=-1,Underline=-1)
Local SeekFnt.FontType
For SeekFnt.FontType=Each FontType
If (SeekFnt.FontType<>Null)
If (Name$=NULL_STRING$) Then Name$=SeekFnt\FNT_FontName
If (Height=-1) Then Height=SeekFnt\FNT_Height
If (Bold=-1) Then Bold=SeekFnt\FNT_Bold
If (Italic=-1) Then Height=SeekFnt\FNT_Italic
If (Underline=-1) Then Height=SeekFnt\FNT_Underline
If ((SeekFnt\FNT_FontName$ = Name$) And (SeekFnt\FNT_Height = Height) And (SeekFnt\FNT_Bold = Bold) And (SeekFnt\FNT_Italic = Italic)And (SeekFnt\FNT_Underline = Underline))
Return SeekFnt.FontType
Exit
End If
End If
Next
Return Null
End Function
Function SetCurrentFont.FontType(FontHandle,FontAsBitmap=False)
Local SeekFnt.FontType
Local nCheck
For SeekFnt.FontType=Each FontType
If (SeekFnt.FontType<>Null)
If (FontAsBitmap)
nCheck=SeekFnt\FNT_BitmapHandle
Else
nCheck=SeekFnt\FNT_FontHandle
End If
If (nCheck=FontHandle) Then Exit
End If
Next
If (SeekFnt.FontType=Null)
DebugLine("CONFIGSCREEN:SETCURRENTFONT",DBG_ERC_TYP_OBJECT_NULL,False,"Font Type object for Handle: "+Str(FontHandle)+" is null")
CurrentFont.FontType=Null
Else
If (nCheck)
SetFont nCheck
CurrentFont.FontType=SeekFnt.FontType
Else
DebugLine("CONFIGSCREEN:SETCURRENTFONT",DBG_ERC_FILE_RESHANDLEZERO,False,"Font Type object: "+Str(SeekFnt.FontType)+" has no handle")
CurrentFont.FontType=Null
End If
End If
If (CurrentFont.FontType=Null) Then SetCurrentFontToDefault()
End Function
Function FreeFontType(SeekFnt.FontType)
If CurrentFont.FontType=SeekFnt.FontType Then CurrentFont.FontType=Null
If (SeekFnt\FNT_FontHandle) Then FreeFont SeekFnt\FNT_FontHandle
If (SeekFnt\FNT_BitmapHandle) Then FreeImage SeekFnt\FNT_BitmapHandle
Delete SeekFnt.FontType
End Function
Function GetFontCharMaxWidth%()
Local Max%=False
Local Char%=False
Local Width%=0
For Char%=31 To 126
Width=StringWidth(Chr$(Char))
If (Width>Max)
Max=Width
End If
Next
Return Max
End Function
Function GetFontCharMaxHeight%()
Local Max%=False
Local Char%=False
Local Height%=0
For Char%=31 To 126
Height=StringHeight(Chr$(Char))
If (Height>Max)
Max=Height
End If
Next
Return Max
End Function
Function CreateFontImage%()
Local OBuffer=GraphicsBuffer()
Local CharWidth=GetFontCharMaxWidth%()
Local CharHeight=GetFontCharMaxHeight%()
Local XPlus%=(CharWidth Shr True)
Local YPlus%=(CharHeight Shr True)
Local X%
Local Y%
Local clscol%=GetCLSColour()
Local col%=GetCurrentColour()
Local Image=CreateImage(CharWidth*16,CharHeight*16)
SetDrawBuffer(ImageBuffer(Image))
SetCLSColour(COL_BLACK)
SetColour(COL_WHITE)
For Y=0 To 15
For X=0 To 15
Text (X*CharWidth)+XPlus,(Y*CharHeight)+YPlus,Chr$(X + (Y*16)),True,True
Next
Next
SetDrawBuffer(OBuffer)
SetCLSColour(clscol%)
SetColour(col%)
Return Image
End Function ____________________ lawks a lordy, my bottom's on fire! |
| ||
| One problem facing a lot of developers is what resolution to use. Lower resolutions may be good for compatibilility with older systems, but even now, these lower resolutions are no longer being supported by modern cards. Then of course, there'ss widescreen monitors (and asasociated problems with the monitors i.e. many flatscreen monitor formats do not support 24-bit colour depth) So even if the software and the GPU can support certain resolutions, you can still run into problems with the monitor. An 'intelligent' iteration through drivers and available screen modes (including the cutrrent desktop as this is often a good benchmark to go by - people with widescreen monitors will most likely have a widescreen format desktop resolution) and ensure that the mode is supported and 'safe' for operation. Even if a configuration file exists, this can run, so in the event of new hardware etc. the program will continue to perform under the defaults. ____________________ lawks a lordy, my bottom's on fire! |
| ||
| Just to show that all's working as intended, as well as just how precise the debugging system is: This logfile demonstrates running of the Autopatcher. I purposefully removed and edited some files and modified the resgistry entry for the version number for testing purposes. As can be seen, all was restored faithfully and the temporary backups etc. all cleaned up nicely too! Edit - Updated due to changes in log output ____________________ lawks a lordy, my bottom's on fire! |
| ||
| I have overhauled the patching system and environment info (which also affects default settings and configuration file read/writes, initialisation etc.) In short, all app-specific environment information is now retrieved from the registry as set by the installation process. Patching policies permit the Autopatcher to alter the version number, but for now, the rest is kept locked as it's quite crucial to the running. For example, all file paths as well as validity checks rely on the environment information being accurate. This may result in problems if the app folder is moved from the install location and prompt for reinstallation, however, the debugger can adjust where necessary to prevent hard crashes. Realistically, there should be no reason for a user to move the app after install or amend the environmental details by any means other than uninstall/reinstall. Future provision for a 'manual patching' utility will be required to conform to these policies. AutoUpdater server-based patch files are now keyed to the hash ID of the application itself, this allows for multiple applications built with the engine to update separately as well as the engine itself. The ID Hash was added to the download path and each application will have its own directory containing the patch elements specific to it. Any shared patch elements that are not shared with the engine may be dealt with in a separate manner, this is for looking at later. The updated Autopatcher functions are given here: * edited: added some debug entries to identify whether searching online or for pre-downloaded local patch files ____________________ lawks a lordy, my bottom's on fire! |
| ||
| Added to the Debugger (so optionally these messages may be turned off) a new function to show a warning message, the user has no control at this point, only to be aware and click OK - Possibly, in the future, this may be removed from the deugger, or copied out for important must-acknowledge messages, though at present, I don't foresee any such necessities. Function DebugWarning(sCaption$,sReason$) If (DEBUG) DLLMessageBox(sCaption$,sReason$,MB_TOPMOST Or MB_ICONERROR Or MB_OK) End If End Function I also may remove the SafeDelete "ShowMessage" flag and incorporate this WarningMessage functionality, however for now, I am keeping the SafeDelete message disp[ay separate from the Debug settings (at least, until core dev is complete - I wouldn't like to accidentally delete legit code while working :D The SafeDelete function ShowMessage flag has been tremoved and the ____________________ lawks a lordy, my bottom's on fire! |
| ||
| Time functions have been restructured for efficiency and some previously poorly optimised routines weeded-out. No longer is the system Uptime recorded with a need for repeated update, instead, the more efficient method of a single, entry for initial running of the app is used. ____________________ lawks a lordy, my bottom's on fire! |
| ||
| The debugger is comprised of a few useful functions shown here, along with the current (I will try to keep it up to date!) list of debugging code values. The list of course, grows as more features are added, however as it stands, it provides a suitably accurate definition of the problem. Note the use of 0 - Unknown (This in fact, would perhaps indicate a bad call to the function) and 1 - Not an error : Though adding to the overhead of writing to the debugger more often, it can elp in showing te progress of functions, allowing the point of later failure to be more specifically identified. As you can see, the error codes are arranged into small groups to help with identifying the issue. A key compoonent is the function name. Although there are occasions, where a subsidiary function identifies the error, where the subsidiary function may not be especially relevant, by looking through the debug log, it will show where the subsidiary function was called from. ((Latest Update = No valid GPU modes (code 21))) ____________________ lawks a lordy, my bottom's on fire! |
| ||
| The idea was to create a whole library of functions and a comprehensive, versatile framework that would form the basis of a generic game engine. Note, this is NOT a game engine per se, but the bsaic framework in Blitz to create an engine. It will be useable, on completion, as a very broad engine, potentially allowing for multiple types of application to be developed from the same codebase, however, whereas an engine would be useable in runtime, this code will require the combination of functions to be coded separtely and the entire package compiled as a single application. It is the coding of the individual functionality and use of various features that make it 'work' as an engine. That is to say, unlike most engines that incorporate a scripting funcitonality and have a basic default preset for 'gameplay style', this framework requires Blitz3D additional code to compile as a runnnable application but is without constraint on the 'gameplay' style. One thing that's often a bugbear for me is coding such things as front-end UI's, or obtaining environment information, setting up/reading/writing and checking validity of configuration files and default settings. As well as numerous repeatable code snippets from the very simple, such as checking a file exists and if not, how to address the issue, safe-deletes, filetype analysis, informative and crash-proof debugging, to the more complex. There are overheads of course, and due to the versatility the entire engine itself has grown to quite some size. With a number of safety checks and systems to 'handle exceptions', i.e. prevent crashes when bugs are found, introduces a little slowdown. However, I believe these are still all well within acceptable limits, and further optimisations will be looked at once the various core essentials are fully in place. The main features currently in plaace are: Appearance: Bitmap fonts - All fonts used are converted into bitmaps for use as 3D sprite display. Allows for easily modified customiseable UI By default adopts a UI equivalent to the user OS (Windows '95 up to Windows 7) in a 3D emulation of 2D workspace. Application is aware of over 10 differing resolution scales and can auttomatically adjust for widescreen formats Initialisation/Configuration: Application is aware of environment and installation status. Checks for all vital paths and system files. Acquires 'safe' default settings if no settings exist (i.e first run). Environmental paths and settings are acquired at source, those that are static can be stored in registry. Identification of UCT offsets, location and OS language are all featured. AutoPatcher: Reports on available updates. If option to patch selected, autopatcher checks updated SVN files and applies changes only where required. No requirement for consecutive patching, only the most recent patch is ever required. If patching aborted or error part-way through, only the remainder needs to be downloaded. In case of patch failure, backups and rollback features prevent errors. Debugging/Safety Fully comprehensive debugger tracks progress of each process. Any errors an be pinpointed exactly. In instances of missing resources, placeholders are utilised or where necessary, created to maintain running. Critical, unsaveable errors found by debugger will exit with detailed descriptions. SafeDelete functionality can warn on deletion, delay deltion (where files in use may not be ready) and feedbak if sucessful. Multiple filetype handling: Wrappers for basic load functions and Code Archive importers allow for a vast array of resources to be loaded and used. Useful functions: A huge array of functions are built in allowing for blitz code to work with various data types without the need for coding them separately. Filesearches: File names, paths, typical resource identification, types and sizes, according to crteria, to be searched and collected for retrieval. (a preliminary version is available in code archives) UserInput: Fully redefinable control options including gamepads, steering wheels, joysticks and full mouse/keyboard inputs. When/If necessary, font, colour, directory and file searches by user can be achieved with single commands. ____________________ lawks a lordy, my bottom's on fire! |

