VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsDVD"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
' -------------------------------------------------------------------------------
'
'   AVStoDVD - by MrC
'   File: clsDVD.cls
'   Purpose: Class that parse DVD structure
'   Original v0.01 - by Doc hEx
'   Adapted by MrC
'
' -------------------------------------------------------------------------------


Option Explicit


'Consts for IFO Parsing 'from vStrip
Const ifo_SECTOR_SIZE = 2048

'NOTE:  This is a crap what to do this, but don't yet know anyother way
'Const ifo_First_PGC = "1018"  'Hard coded!  Don't like this but I can't find any other info on how to find this address through the sector pointers
'Const ifo_FirstSec_PGC_Diff = 840 'Decimal * Num Programs
'Const ifo_NumberProgramChainsInIFO = "800" 'Hard code
'Const ifo_StartOfFirstCellInfo = "118" 'Hard code
Const ifo_CellInfoSize = 24 'Decimal * CellNumber + Hard coded first

'Max amount of Cells read from a program chain
Const MAX_CellReadFromAProgramChain = 70
Const MAX_ProgramChainInformation = MAX_CellReadFromAProgramChain

'DVD should have no more then 9 VOB_0x_00.ifo
Const DVD_VOB_CountLimit = 24


'All these types are used for the IFO Parsing
Private Type VTS_PTT_SRPT
    PackedString As String
    NumberOfTitles As Long
    EndAddress_VST_PTT As Long
    OffsetTo_PPT As Long
End Type

'Program time information
Private Type DVD_Time_Type
    hours As Byte
    minutes As Byte
    seconds As Byte
    frames As Byte
End Type

'Individual Cell information
Private Type PGC_Cell_Info_Type
    Interleaved As Boolean
    CellPlayBackTime As DVD_Time_Type
End Type

'Based off vStrip.dll
Private Type VideoAtt_VTS_VOBS_Type
    No_Auto_Letterbox As Boolean
    No_Auto_pan_scan As Boolean
    Aspect_Ratio As Byte
    Video_Standard As Byte
    Coding_Mode As Byte
    Film As Boolean
    LetterBoxed As Boolean
    Resolution As Byte
    bitrate As Boolean
    foo As Byte
End Type

'Audio Type
Private Type AudioAtt_VTSM_VTS_Type
    Application_Mode As Byte
    LanguageType As Byte
    MultiChannelExtension As Byte
    CodingMode As Byte
    NumberOfChannels As Byte
    SampleRate As Byte
    Quantization As Byte
    Caption As Byte
    LanguageCode As String
End Type

'SubPicture Type
Private Type SubPictureAtt_VTSM_VTS_Type
    CodingMode As Byte
    LanguageCode As String
    CodeExtention As Byte
End Type

'IFO VST Program Chain Information
Private Type Program_Chain_Type
    NumberOfPrograms As Byte
    NumberOfCells As Byte
    PlayBackTime As DVD_Time_Type
    'Up to 64 Chapters
    PChainInformation(MAX_CellReadFromAProgramChain) As PGC_Cell_Info_Type
    'Currently only implamenting basic useful information
End Type

'IFO VST information
Private Type IFO_VST_Parse
    IFO_FileName As String
    
    FileType_Header As String
    LastSectorOfTitle As Long
    LastSectorOfIFO As Long
    VSTCategory As Long
    VersionNumber As Long

    EndByteAddress_VTS_MAT As Long

    StartSector_MenuVOB As Long
    StartSector_TitleVOB As Long

    SectorPointer_VTS_PTT_SRPT As Long
    SectorPointer_VTS_PGCI As Long
    SectorPointer_VTSM_PGCI_UT As Long
    SectorPointer_VTS_TMAPTI As Long
    SectorPointer_VSTM_C_ADT As Long
    SectorPointer_VSTM_VOBU_ADMAP As Long
    SectorPointer_VST_C_ADT As Long
    SectorPointer_VTS_VOBU_ADMAP As Long
    
    NumberOfProgramChains As Long
    ProgramChainInformation(MAX_ProgramChainInformation) As Program_Chain_Type
    
    VideoAtt_VSTM_VOBS As VideoAtt_VTS_VOBS_Type
    NumOfAudioStreamsIn_VTSM_VOBS As Long
    AudioAtt_VTSM_VOBS(8) As AudioAtt_VTSM_VTS_Type

    NumSubPictureStreams_VTSM_VOBS As Long
    SubPictureAtt_VTSM_VOBS As SubPictureAtt_VTSM_VTS_Type

    VideoAtt_VTS_VOBS As VideoAtt_VTS_VOBS_Type
    NumAudioStreams_VTS_VOBS As Long
    AudioAtt_VTS_VOBS(8) As AudioAtt_VTSM_VTS_Type

    NumSubPictureStreams_VTS_VOBS As Long
    SubPictureAtt_VTS_VOBS(32) As SubPictureAtt_VTSM_VTS_Type

    'MultiChannelExt(192) As Byte
End Type


'Variables
'Dim DVD_Information(DVD_VOB_CountLimit, 1) As Variant
Private mAudioModes As Variant          'Note this is converted to an Array in the Class Init
Private mVideoAspect As Variant
Private mVideoStandard As Variant
Private mVideoCodingMode As Variant
Private mVideoResolution(1) As Variant
Private mIFO_VTS_Array(DVD_VOB_CountLimit) As IFO_VST_Parse
Private mTotNumIn_mIFO_VTS_Array As Integer  'For MyVar(0) TotalNumberInArray = 1, MyVar(1) TotalNumberInArray = 2, so on
Private mOpenFile As Boolean  'true if at least one IFO file open


'Auth DVD Drive
Private Declare Function DLL_AuthDVD Lib "authdvd.dll" Alias "AuthDVD" () As Long


Private Sub Class_Initialize()

    'Video
    mVideoAspect = Array("4:3", "???", "???", "16:9")
    mVideoStandard = Array("NTSC", "PAL", "???", "???")
    mVideoCodingMode = Array("MPEG1", "MPEG2")
    mVideoResolution(0) = Array("720x480", "704x480", "352x480", "352x240")
    mVideoResolution(1) = Array("720x576", "704x576", "352x576", "352x288")
    
    'Audio
    mAudioModes = Array("AC3", "???", "MPA", "MPA", "WAV", "???", "DTS")
    'mAudioModes = Array("AC3", "???", "MPEG1", "MPEG2ext", "LPCM", "???", "DTS")
    
End Sub


Private Sub Class_Terminate()
    
    'Release Mem
    mAudioModes = ""

End Sub


'Usage:
'   ip_fileName = "C:\temp\MyDVDStuff\VIDEO_TS\VTS_02_0.ifo" - Opens the specifed file only
'   ip_fileName = "C:\temp\MyDVDStuff\VIDEO_TS" - Opens the first VTS*.IFO found
'
'   Return true A was File Openned
'   Return false No file
Public Function OpenFile(ByVal ip_FileName As String, Optional AuthDVDPath As String, Optional ErrorDisplay As Boolean) As Boolean

    Dim curFileName As String
    Dim curPath As String
    Dim OrigCurDir As String
    
    On Error GoTo Err_Handling
    
    OrigCurDir = CurDir
    If AuthDVDPath = "" Then AuthDVDPath = App.Path & "\Lib"
    ChDrive Left(AuthDVDPath, 2)
    ChDir AuthDVDPath
    
    mTotNumIn_mIFO_VTS_Array = 0
    
    If InStr(1, UCase(Dir(ip_FileName)), ".IFO") <> 0 Then
        curFileName = Dir(ip_FileName)
        curPath = Left(ip_FileName, Len(ip_FileName) - Len(curFileName) - 1)
    ElseIf UCase(Dir(ip_FileName, vbDirectory)) <> "" Then
        curFileName = Dir(ip_FileName & "\VTS*.IFO")
        curPath = ip_FileName
    End If
    
    If curFileName = "" Or curPath = "" Then
        mOpenFile = False
    Else
        While curFileName <> ""
            'Load the IFO File into memory
            mIFO_VTS_Array(mTotNumIn_mIFO_VTS_Array) = priv_ParseIFO_VST(curPath & "\" & curFileName)
            mTotNumIn_mIFO_VTS_Array = mTotNumIn_mIFO_VTS_Array + 1
            'Next File
            curFileName = Dir()
        Wend
        mOpenFile = True
    End If
    
    'Return
    OpenFile = mOpenFile
    
    GoTo Exit_Handling
        
Err_Handling:
Select Case Err.Number
Case 9999
    Resume
Case 9    'Indice non compreso nell'intervallo
    OpenFile = False
    Resume Next
Case Else
    If ErrorDisplay = True Then MsgBox "Error " & Err.Number & vbCrLf & vbCrLf & Err.Description, vbCritical, App.Title
End Select

Exit_Handling:
If Mid(OrigCurDir, 2, 1) = ":" Then
    ChDrive Left(OrigCurDir, 2)
End If
ChDir OrigCurDir

End Function


Property Get IFO_AudioNumberOfTracks(ByVal Index As Integer) As Integer
    
    'Return a 0 blank string if not valid entry
    
    IFO_AudioNumberOfTracks = 0
    If Index <= mTotNumIn_mIFO_VTS_Array And Index > 0 Then
        Index = Index - 1
        'Build String
        IFO_AudioNumberOfTracks = mIFO_VTS_Array(Index).NumAudioStreams_VTS_VOBS
    End If
    
End Property


Property Get IFO_SubPicNumberOf(ByVal Index As Integer) As Integer
    
    'Return a 0 blank string if not valid entry
    
    IFO_SubPicNumberOf = 0
    If Index <= mTotNumIn_mIFO_VTS_Array And Index > 0 Then
        Index = Index - 1
        'Build String
        IFO_SubPicNumberOf = mIFO_VTS_Array(Index).NumSubPictureStreams_VTS_VOBS
    End If
End Property


Property Get NumberIFOread() As Integer

    NumberIFOread = mTotNumIn_mIFO_VTS_Array
    
End Property


Property Get NumberProgramChains(ByVal IFO_Index As Integer) As Integer
    
    'Return a 0 if not valid entry
    
    NumberProgramChains = 0
    If IFO_Index <= mTotNumIn_mIFO_VTS_Array And IFO_Index > 0 Then
        NumberProgramChains = mIFO_VTS_Array(IFO_Index - 1).NumberOfProgramChains
    End If

End Property


Property Get ProgramChain_PlayBackTime(ByVal IFO_Index As Integer, ByVal ProChain_Index As Integer, Optional ByVal Cell_Index As Integer) As Single
    
    'Return a "" if not valid entry
    
    ProgramChain_PlayBackTime = 0
    If IFO_Index <= mTotNumIn_mIFO_VTS_Array And IFO_Index > 0 Then
        'Fix the 1 - 0 problem with indexs
        IFO_Index = IFO_Index - 1
                
        If ProChain_Index <= mIFO_VTS_Array(IFO_Index).NumberOfProgramChains Then
            ProChain_Index = ProChain_Index - 1
            
            If Cell_Index = 0 Then
                ProgramChain_PlayBackTime = priv_PlayBackTimeToNumber(mIFO_VTS_Array(IFO_Index).ProgramChainInformation(ProChain_Index).PlayBackTime, IFO_Index)
            Else
                ProgramChain_PlayBackTime = priv_PlayBackTimeToNumber(mIFO_VTS_Array(IFO_Index).ProgramChainInformation(ProChain_Index).PChainInformation(Cell_Index - 1).CellPlayBackTime, IFO_Index)
            End If
        End If 'ProChain Index
    End If 'IFO Index

End Property


Property Get ProgramChain_NumChapters(ByVal IFO_Index As Integer, ByVal ProChain_Index As Integer) As Integer
    
    'Return 0 if not valid entry
    
    ProgramChain_NumChapters = 0
    If IFO_Index <= mTotNumIn_mIFO_VTS_Array And IFO_Index > 0 Then
        'Fix the 1 - 0 problem with indexs
        IFO_Index = IFO_Index - 1
                
        If ProChain_Index <= mIFO_VTS_Array(IFO_Index).NumberOfProgramChains Then
            ProChain_Index = ProChain_Index - 1
            
            ProgramChain_NumChapters = mIFO_VTS_Array(IFO_Index).ProgramChainInformation(ProChain_Index).NumberOfPrograms - 1
        End If 'ProChain Index
    End If 'IFO Index

End Property


Property Get ProgramChain_AudioTrackCode(ByVal VobIndex As Integer, ByVal AudioIndex As Integer) As String

    'Return "00" if not valid entry
    'Index (second digit) is NOT correct: it is assumed as a progressive number, but it might not be always true
    
    ProgramChain_AudioTrackCode = ""
    If VobIndex <= mTotNumIn_mIFO_VTS_Array And VobIndex > 0 Then
        If AudioIndex <= mIFO_VTS_Array(VobIndex - 1).NumAudioStreams_VTS_VOBS And AudioIndex > 0 Then
            Select Case mIFO_VTS_Array(VobIndex - 1).AudioAtt_VTS_VOBS(AudioIndex - 1).CodingMode
            Case 0   'AC3
                ProgramChain_AudioTrackCode = "8" & AudioIndex - 1
            Case 2   'MPEG1
                ProgramChain_AudioTrackCode = "C" & AudioIndex - 1
            Case 4   'LPCM
                ProgramChain_AudioTrackCode = "A" & AudioIndex - 1
            Case 6   'DTS
                ProgramChain_AudioTrackCode = "8" & Hex(7 + AudioIndex)
            End Select
        End If
    End If

End Property

Property Get IFO_FileName(ByVal IFO_Index As Integer) As String

    'Return a "" blank string if not valid entry
    
    IFO_FileName = ""
    If IFO_Index <= mTotNumIn_mIFO_VTS_Array And IFO_Index > 0 Then IFO_FileName = mIFO_VTS_Array(IFO_Index - 1).IFO_FileName

End Property


Property Get IFO_Video(ByVal IFO_Index As Integer) As String

    'Return a "" blank string if not valid entry
    
    'See Class init
    'mVideoAspect = Array("4:3", "???", "???", "16:9")
    'mVideoStandard = Array("NTSC", "PAL", "???", "???")
    'mVideoCodingMode = Array("MPEG1", "MPEG2")
    'mVideoResolution(0) = Array("720x480", "704x480", "352x480", "352x240")
    'mVideoResolution(1) = Array("720x576", "704x576", "352x576", "352x288")
    
    IFO_Video = ""
    If IFO_Index <= mTotNumIn_mIFO_VTS_Array And IFO_Index > 0 Then
        'Build String
        IFO_Video = mVideoCodingMode(mIFO_VTS_Array(IFO_Index - 1).VideoAtt_VTS_VOBS.Coding_Mode) & " | "
        IFO_Video = IFO_Video & mVideoStandard(mIFO_VTS_Array(IFO_Index - 1).VideoAtt_VTS_VOBS.Video_Standard) & " | "
        IFO_Video = IFO_Video & mVideoResolution(mIFO_VTS_Array(IFO_Index - 1).VideoAtt_VTS_VOBS.Video_Standard)(mIFO_VTS_Array(IFO_Index - 1).VideoAtt_VTS_VOBS.Resolution) & " | "
        IFO_Video = IFO_Video & mVideoAspect(mIFO_VTS_Array(IFO_Index - 1).VideoAtt_VTS_VOBS.Aspect_Ratio)
    End If

End Property


Property Get IFO_AudioCompression(ByVal IFO_Index As Integer, ByVal AudioIndex As Integer) As String

    IFO_AudioCompression = mAudioModes(mIFO_VTS_Array(IFO_Index - 1).AudioAtt_VTS_VOBS(AudioIndex - 1).CodingMode)

End Property


Property Get IFO_AudioChannels(ByVal IFO_Index As Integer, ByVal AudioIndex As Integer) As String

    IFO_AudioChannels = mIFO_VTS_Array(IFO_Index - 1).AudioAtt_VTS_VOBS(AudioIndex - 1).NumberOfChannels & "ch"

End Property


Property Get IFO_AudioLanguage(ByVal IFO_Index As Integer, ByVal AudioIndex As Integer) As String

    Dim sLang As String
    
    sLang = mIFO_VTS_Array(IFO_Index - 1).AudioAtt_VTS_VOBS(AudioIndex - 1).LanguageCode
    IFO_AudioLanguage = UCase(Replace(sLang, Chr(0), " "))

End Property


Property Get IFO_AudioSummary(ByVal IFO_Index As Integer, ByVal AudioIndex As Integer) As String
    
    'Return a "" blank string if not valid entry
    
    IFO_AudioSummary = ""
    If IFO_Index <= mTotNumIn_mIFO_VTS_Array And IFO_Index > 0 Then
        If AudioIndex <= mIFO_VTS_Array(IFO_Index - 1).NumAudioStreams_VTS_VOBS And AudioIndex > 0 Then
            'Build String
            IFO_AudioSummary = "0x" & ProgramChain_AudioTrackCode(IFO_Index, AudioIndex)
            If IFO_AudioCompression(IFO_Index, AudioIndex) <> "" Then
                IFO_AudioSummary = IFO_AudioSummary & " | " & IFO_AudioCompression(IFO_Index, AudioIndex)
            End If
            If IFO_AudioChannels(IFO_Index, AudioIndex) <> "" Then
                IFO_AudioSummary = IFO_AudioSummary & " | " & IFO_AudioChannels(IFO_Index, AudioIndex)
            End If
            If IFO_AudioLanguage(IFO_Index, AudioIndex) <> "" Then
                IFO_AudioSummary = IFO_AudioSummary & " | " & IFO_AudioLanguage(IFO_Index, AudioIndex)
            End If
        End If
    End If

End Property


Property Get IFO_SubsLanguage(ByVal IFO_Index As Integer, ByVal SubPicIndex As Integer) As String

    Dim sLang As String
    
    sLang = mIFO_VTS_Array(IFO_Index - 1).SubPictureAtt_VTS_VOBS(SubPicIndex - 1).LanguageCode
    IFO_SubsLanguage = UCase(Replace(sLang, Chr(0), " "))

End Property


Property Get IFO_SubsSummary(ByVal IFO_Index As Integer, ByVal SubPicIndex As Integer) As String
    
    'Return a "" blank string if not valid entry

    IFO_SubsSummary = ""
    If IFO_Index <= mTotNumIn_mIFO_VTS_Array And IFO_Index > 0 Then
        If SubPicIndex <= mIFO_VTS_Array(IFO_Index - 1).NumSubPictureStreams_VTS_VOBS And SubPicIndex > 0 Then
            'Build String
            IFO_SubsSummary = "0x2" & SubPicIndex - 1
            If IFO_SubsLanguage(IFO_Index, SubPicIndex) <> "" Then
                IFO_SubsSummary = IFO_SubsSummary & " | " & IFO_SubsLanguage(IFO_Index, SubPicIndex)
            End If
        End If
    End If
    
End Property


'Convert the DVD time type to a number (seconds)
Private Function priv_PlayBackTimeToNumber(ByRef PlayBack As DVD_Time_Type, ByVal IFO_Index As Integer) As Single
    
    priv_PlayBackTimeToNumber = PlayBack.hours * 3600 + PlayBack.minutes * 60 + PlayBack.seconds + PlayBack.frames / IIf(mVideoStandard(mIFO_VTS_Array(IFO_Index).VideoAtt_VTS_VOBS.Video_Standard) = "PAL", 25, 30)

End Function


'Fill in the Audio Header Inforamtion
Private Function priv_AudioAtt_VTSM_VTS(ByVal AudioInfo As String) As AudioAtt_VTSM_VTS_Type

    Dim byteInfo(8) As Integer
    Dim LoopI As Long
    Dim tmpValue As Integer
    
    'Setup Byte info
    For LoopI = 0 To 7
        byteInfo(LoopI) = Asc(Mid(AudioInfo, LoopI + 1, 1))
    Next LoopI
    priv_AudioAtt_VTSM_VTS.LanguageCode = Chr(byteInfo(2)) & Chr(byteInfo(3))
    priv_AudioAtt_VTSM_VTS.Caption = byteInfo(5)
    
    'Using Logic AND's to check if bits are set dec 176 -> bin 10110000
    tmpValue = 0
    If (byteInfo(0) And 32) = 32 Then tmpValue = 1
    If (byteInfo(0) And 64) = 64 Then tmpValue = tmpValue + 2
    If (byteInfo(0) And 128) = 128 Then tmpValue = tmpValue + 4
    priv_AudioAtt_VTSM_VTS.CodingMode = tmpValue
    
    priv_AudioAtt_VTSM_VTS.MultiChannelExtension = (byteInfo(0) And 16)
    
    If (byteInfo(0) And 1) = 1 Then tmpValue = 1
    If (byteInfo(0) And 2) = 2 Then tmpValue = tmpValue + 2
    priv_AudioAtt_VTSM_VTS.Application_Mode = tmpValue
    
    If (byteInfo(0) And 4) = 4 Then tmpValue = 1
    If (byteInfo(0) And 8) = 8 Then tmpValue = tmpValue + 2
    priv_AudioAtt_VTSM_VTS.LanguageType = tmpValue

    If (byteInfo(1) And 1) = 1 Then tmpValue = 1
    If (byteInfo(1) And 2) = 2 Then tmpValue = tmpValue + 2
    If (byteInfo(1) And 4) = 4 Then tmpValue = tmpValue + 4
    priv_AudioAtt_VTSM_VTS.NumberOfChannels = tmpValue + 1
    
    If (byteInfo(1) And 8) = 8 Then tmpValue = 1
    If (byteInfo(1) And 16) = 16 Then tmpValue = tmpValue + 2
    priv_AudioAtt_VTSM_VTS.SampleRate = tmpValue
    
    If (byteInfo(1) And 32) = 32 Then tmpValue = 1
    If (byteInfo(1) And 64) = 64 Then tmpValue = tmpValue + 2
    If (byteInfo(1) And 128) = 128 Then tmpValue = tmpValue + 4
    priv_AudioAtt_VTSM_VTS.Quantization = tmpValue

End Function


Private Function priv_HexOffset(ByVal ip_HexString As String) As Long
    
    'Uses correct version
    'If IsNumeric(ip_HexString) Then
        'priv_HexOffset = ip_HexString + 1
    'Else
        priv_HexOffset = priv_HexStrToHex(ip_HexString) + 1
    'End If
    
End Function


Private Function priv_HexStrToHex(ByVal ip_HexString As Variant) As Long
    
    priv_HexStrToHex = Val("&H" + UCase(ip_HexString))

End Function


Private Function priv_HexTimeToFrame(ByVal AmountHex As Integer) As Integer
    
    'Do the Magic
    If AmountHex >= 192 Then
        'NTSC
        priv_HexTimeToFrame = Hex(AmountHex - 192)
    ElseIf AmountHex >= 64 Then
        'PAL
        priv_HexTimeToFrame = Hex(AmountHex - 64)
    End If
    
End Function


'Note:  This is for the Chapter Information
Private Function priv_PChainInformation(ByVal ProgramChainNumber As Integer, ByVal CellNumber As Integer, ByRef IFO_FileBuffer As String, ByRef tmpIFO As IFO_VST_Parse) As PGC_Cell_Info_Type
    
    Dim ChainLoc As Long
    
    On Error GoTo Err_Handling
    
    'Setup the Start loc for the File
    'ChainLoc = priv_HexOffset(ifo_StartOfFirstCellInfo) + (ifo_CellInfoSize * (CellNumber)) + priv_HexOffset(ifo_First_PGC)
    ChainLoc = (tmpIFO.SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE) _
             + priv_StrBYTEtoHex(Mid(IFO_FileBuffer, tmpIFO.SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE + 1 + 12 + (ProgramChainNumber) * 8, 4))
    ChainLoc = ChainLoc + priv_StrBYTEtoHex(Mid(IFO_FileBuffer, ChainLoc + 233, 2)) + 1 + (ifo_CellInfoSize * (CellNumber - 1))
    
    priv_PChainInformation.Interleaved = (priv_StrBYTEtoHex(Mid(IFO_FileBuffer, ChainLoc, 1)) And 8)
    
    priv_PChainInformation.CellPlayBackTime.hours = Hex(Asc(Mid(IFO_FileBuffer, ChainLoc + 4, 1)))
    priv_PChainInformation.CellPlayBackTime.minutes = Hex(Asc(Mid(IFO_FileBuffer, ChainLoc + 5, 1)))
    priv_PChainInformation.CellPlayBackTime.seconds = Hex(Asc(Mid(IFO_FileBuffer, ChainLoc + 6, 1)))
    priv_PChainInformation.CellPlayBackTime.frames = priv_HexTimeToFrame(Asc(Mid(IFO_FileBuffer, ChainLoc + 7, 1)))

    Exit Function
    
Err_Handling:
Select Case Err.Number
Case 13
    Resume Next
End Select

End Function


Private Function priv_ProgramChainInformation(ByVal ProgramChainNumber As Integer, ByRef IFO_FileBuffer As String, ByRef tmpIFO As IFO_VST_Parse) As Program_Chain_Type
    
    Dim ChainLoc As Long
    
    'Setup the Start loc for the File
    ChainLoc = (tmpIFO.SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE) _
             + priv_StrBYTEtoHex(Mid(IFO_FileBuffer, tmpIFO.SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE + 1 + 12 + (ProgramChainNumber) * 8, 4))
    
    'MsgBox priv_StrBYTEtoHex(Mid(IFO_FileBuffer, tmpIFO.SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE + 1 + 12 + (ProgramChainNumber) * 8, 4)) _

    'The Program Number
    priv_ProgramChainInformation.NumberOfPrograms = priv_StrBYTEtoHex(Mid(IFO_FileBuffer, ChainLoc + 3, 1))
    'Number of Cells in Program Chain
    priv_ProgramChainInformation.NumberOfCells = priv_StrBYTEtoHex(Mid(IFO_FileBuffer, ChainLoc + 4, 1))
    'Get DVD Chain Type Info
    'priv_ProgramChainInformation.PlayBackTime = priv_PGC_DVD_Time(ProgramChainNumber, IFO_FileBuffer)
    priv_ProgramChainInformation.PlayBackTime.hours = Hex(Asc(Mid(IFO_FileBuffer, ChainLoc + 5, 1)))
    priv_ProgramChainInformation.PlayBackTime.minutes = Hex(Asc(Mid(IFO_FileBuffer, ChainLoc + 6, 1)))
    priv_ProgramChainInformation.PlayBackTime.seconds = Hex(Asc(Mid(IFO_FileBuffer, ChainLoc + 7, 1)))

End Function


'Open an IFO file and return the Parsed Variable
Private Function priv_ParseIFO_VST(ByVal ipFileName As String) As IFO_VST_Parse

    Dim tmpIFOFileIn As String
    Dim tmp_i As Long
    Dim tmp_o As Long
    
    'Read in the Info file name
    Open ipFileName For Binary Access Read As #1
        tmpIFOFileIn = Input(FileLen(ipFileName), #1)
        tmp_i = FileLen(ipFileName)
    Close #1
    
    'Used to Check that the file read correctly
    'Open "c:\tmp.ifo" For Binary As #1
    '    Put #1, , tmpIFOFileIn
    'Close #1
    
    With priv_ParseIFO_VST
    
        'Save the Ifo File name
        .IFO_FileName = ipFileName
        
        'See MPUcoder (http://www.mpucoder.com/DVD/ifo.html)
        'Parse the File in tmpIFOFileIn
        .FileType_Header = Mid(tmpIFOFileIn, priv_HexOffset("0"), 12)
        .LastSectorOfTitle = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("C"), 4))
        .LastSectorOfIFO = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("1C"), 4))
        .VersionNumber = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("20"), 2))
        .VSTCategory = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("22"), 4))
        .EndByteAddress_VTS_MAT = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("80"), 4))
        .StartSector_MenuVOB = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("C0"), 4))
        .StartSector_TitleVOB = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("C4"), 4))
        .SectorPointer_VTS_PTT_SRPT = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("C8"), 4))
        .SectorPointer_VTS_PGCI = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("CC"), 4))
        .SectorPointer_VTSM_PGCI_UT = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("D0"), 4))
        .SectorPointer_VTS_TMAPTI = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("D4"), 4))
        .SectorPointer_VSTM_C_ADT = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("D8"), 4))
        .SectorPointer_VSTM_VOBU_ADMAP = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("DC"), 4))
        .SectorPointer_VST_C_ADT = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("E0"), 4))
        .SectorPointer_VTS_VOBU_ADMAP = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("E4"), 4))
        
        'VTSM
        'Get Video of Main information
        .VideoAtt_VSTM_VOBS = priv_VideoAtt_VTS_VOBS(Mid(tmpIFOFileIn, priv_HexOffset("100"), 2))
        .NumOfAudioStreamsIn_VTSM_VOBS = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("102"), 2))
        'Read in all Audio Track Information
        For tmp_i = 0 To .NumOfAudioStreamsIn_VTSM_VOBS - 1
            .AudioAtt_VTSM_VOBS(tmp_i) = priv_AudioAtt_VTSM_VTS(Mid(tmpIFOFileIn, priv_HexOffset("104") + (tmp_i * 8), 8))
        Next tmp_i
        
        'Get SubPicture Main info
        .NumSubPictureStreams_VTSM_VOBS = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("154"), 2))
        .SubPictureAtt_VTSM_VOBS = priv_SubPictureAtt_VTSM_VTS(Mid(tmpIFOFileIn, priv_HexOffset("156"), 6))
        
        
        'VTS
        'Get Video Information
        .VideoAtt_VTS_VOBS = priv_VideoAtt_VTS_VOBS(Mid(tmpIFOFileIn, priv_HexOffset("200"), 2))
        '.NumAudioStreams_VTS_VOBS = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("202"), 2))
        .NumAudioStreams_VTS_VOBS = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("203"), 1))
        For tmp_i = 0 To .NumAudioStreams_VTS_VOBS - 1
            .AudioAtt_VTS_VOBS(tmp_i) = priv_AudioAtt_VTSM_VTS(Mid(tmpIFOFileIn, priv_HexOffset("204") + (tmp_i * 8), 8))
        Next tmp_i
        
        'Get SubPicture info
        '.NumSubPictureStreams_VTS_VOBS = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("254"), 2))
        .NumSubPictureStreams_VTS_VOBS = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset("255"), 1))
        For tmp_i = 0 To .NumSubPictureStreams_VTS_VOBS - 1
            .SubPictureAtt_VTS_VOBS(tmp_i) = priv_SubPictureAtt_VTSM_VTS(Mid(tmpIFOFileIn, priv_HexOffset("256") + (tmp_i * 6), 6))
        Next tmp_i
        
        'MsgBox .SectorPointer_VTS_PGCI & " Address = " & .SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE
        'MsgBox (.SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE) + priv_StrBYTEtoHex(Mid(tmpIFOFileIn, .SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE + 1 + 12, 4))
        
        'Get Program Chain Information
        'Note Hard Code Hack!
        '.NumberOfProgramChains = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, priv_HexOffset(ifo_NumberProgramChainsInIFO), 2))
        .NumberOfProgramChains = priv_StrBYTEtoHex(Mid(tmpIFOFileIn, .SectorPointer_VTS_PGCI * ifo_SECTOR_SIZE + 1, 2))
        If .NumberOfProgramChains > MAX_CellReadFromAProgramChain Then
            MsgBox "Warning!" & vbCrLf & vbCrLf & ipFileName & " contains too many (" & .NumberOfProgramChains & ") PGCs. Only " & MAX_CellReadFromAProgramChain & " PGCs will be read.", vbCritical, App.Title
            .NumberOfProgramChains = MAX_CellReadFromAProgramChain
        End If
        For tmp_i = 0 To .NumberOfProgramChains - 1
            .ProgramChainInformation(tmp_i) = priv_ProgramChainInformation(tmp_i, tmpIFOFileIn, priv_ParseIFO_VST)
            For tmp_o = 0 To .ProgramChainInformation(tmp_i).NumberOfPrograms
                .ProgramChainInformation(tmp_i).PChainInformation(tmp_o) = priv_PChainInformation(tmp_i, tmp_o, tmpIFOFileIn, priv_ParseIFO_VST)
            Next tmp_o
        Next tmp_i
        
        'MsgBox "Num Chains " & .NumberOfProgramChains
        '" Time: " & .ProgramChainInformation(0).PChainInformation(0).CellPlayBackTime.hours & ":" & _
                    .ProgramChainInformation(0).PChainInformation(0).CellPlayBackTime.minutes & ":" & _
                    .ProgramChainInformation(0).PChainInformation(0).CellPlayBackTime.seconds
        'MsgBox .ProgramChainInformation(1).PlayBackTime.hours & ":" & .ProgramChainInformation(1).PlayBackTime.minutes & ":" & .ProgramChainInformation(1).PlayBackTime.seconds
        'MsgBox .AudioAtt_VTS_VOBS(0).LanguageCode & "  " & .SubPictureAtt_VTS_VOBS(0).LanguageCode
        'MsgBox tmp_i & " 200 => " & priv_HexOffset("200")
    
    End With
    
    'Manually Release Memory
    tmpIFOFileIn = ""
    
End Function


Public Function AuthDVD() As Boolean
    
    '0 = Fail
    '1 = Success
    
    AuthDVD = CBool(DLL_AuthDVD)

End Function


'Convert a string of Bytes (0x00-0xFF) into a complete number
Private Function priv_StrBYTEtoHex(ByVal ipHexString As String) As Long

    Dim LoopI As Long
    Dim HexTotal As Double
    Dim HexMod As Double
    Dim CharNum As Long
    
    HexTotal = 0
    For LoopI = 1 To Len(ipHexString)
        CharNum = Asc(Mid(ipHexString, LoopI, 1))
        If LoopI <> Len(ipHexString) Then
            HexMod = 256 ^ (Len(ipHexString) - LoopI)
            HexTotal = HexTotal + CharNum * HexMod
        Else
            HexTotal = HexTotal + CharNum
        End If
    Next LoopI
    priv_StrBYTEtoHex = HexTotal

End Function


'Creates the Video info from a string of 2 bytes
Private Function priv_VideoAtt_VTS_VOBS(ByVal VideoInfo As String) As VideoAtt_VTS_VOBS_Type
    
    Dim byte1 As Long
    Dim byte2 As Long
    Dim tmpValue As Long
    
    'Split String for logic AND checks
    byte1 = Asc(Mid(VideoInfo, 1, 1))
    byte2 = Asc(Mid(VideoInfo, 2, 1))
    
    'BYTE 1
    'Using Logic AND's to check if bits are set dec 176 -> bin 10110000
    priv_VideoAtt_VTS_VOBS.No_Auto_Letterbox = (byte1 And 1) '00000001
    priv_VideoAtt_VTS_VOBS.No_Auto_pan_scan = (byte1 And 2) '00000010 so on
    
    'Note the Reverse of bit's Damn those indians!
'    tmpValue = 0
'    If (byte1 And 8) = 8 Then tmpValue = 1
'    If (byte1 And 4) = 4 Then tmpValue = tmpValue + 2
'    priv_VideoAtt_VTS_VOBS.Aspect_Ratio = tmpValue
'
'    tmpValue = 0
'    If (byte1 And 32) = 32 Then tmpValue = 1
'    If (byte1 And 16) = 16 Then tmpValue = tmpValue + 2
'    priv_VideoAtt_VTS_VOBS.Video_Standard = tmpValue
'
'    tmpValue = 0
'    If (byte1 And 128) = 128 Then tmpValue = 1
'    If (byte1 And 64) = 64 Then tmpValue = tmpValue + 2
'    priv_VideoAtt_VTS_VOBS.Coding_Mode = tmpValue
    
    'Revers bits
    tmpValue = 0
    If (byte1 And 4) = 4 Then tmpValue = 1
    If (byte1 And 8) = 8 Then tmpValue = tmpValue + 2
    priv_VideoAtt_VTS_VOBS.Aspect_Ratio = tmpValue
    
    tmpValue = 0
    If (byte1 And 16) = 16 Then tmpValue = 1
    If (byte1 And 32) = 32 Then tmpValue = tmpValue + 2
    priv_VideoAtt_VTS_VOBS.Video_Standard = tmpValue
    
    tmpValue = 0
    If (byte1 And 64) = 64 Then tmpValue = 1
    If (byte1 And 128) = 128 Then tmpValue = tmpValue + 2
    priv_VideoAtt_VTS_VOBS.Coding_Mode = tmpValue
    
    'BYTE 2
    priv_VideoAtt_VTS_VOBS.Film = (byte2 And 1)  '00000001
    priv_VideoAtt_VTS_VOBS.LetterBoxed = (byte2 And 2)  '00000010 so on
    priv_VideoAtt_VTS_VOBS.bitrate = (byte2 And 16)   '00010000
    
'    tmpValue = 0
'    If (byte2 And 8) = 8 Then tmpValue = 1
'    If (byte2 And 4) = 4 Then tmpValue = tmpValue + 2
'    priv_VideoAtt_VTS_VOBS.Resolution = tmpValue
'
'    tmpValue = 0
'    If (byte2 And 128) = 128 Then tmpValue = 1
'    If (byte2 And 64) = 64 Then tmpValue = tmpValue + 2
'    If (byte2 And 32) = 32 Then tmpValue = tmpValue + 4
    
    tmpValue = 0
    If (byte2 And 4) = 4 Then tmpValue = 1
    If (byte2 And 8) = 8 Then tmpValue = tmpValue + 2
    priv_VideoAtt_VTS_VOBS.Resolution = tmpValue
    
    tmpValue = 0
    If (byte2 And 32) = 32 Then tmpValue = 1
    If (byte2 And 64) = 64 Then tmpValue = tmpValue + 2
    If (byte2 And 128) = 128 Then tmpValue = tmpValue + 4
    
    priv_VideoAtt_VTS_VOBS.foo = tmpValue  'Don't now what this is used for

End Function


Private Function priv_SubPictureAtt_VTSM_VTS(ByVal SubPictureInfo As String) As SubPictureAtt_VTSM_VTS_Type
    
    priv_SubPictureAtt_VTSM_VTS.LanguageCode = Mid(SubPictureInfo, 3, 1) & Mid(SubPictureInfo, 4, 1)
    priv_SubPictureAtt_VTSM_VTS.CodeExtention = Asc(Mid(SubPictureInfo, 6, 1))

End Function

