#Region "System Includes"
Imports System.IO
Imports WindowsApplication1.MatrixMath
Imports WindowsApplication1.VectorMath
Imports WindowsApplication1.Utility
#End Region
#Region "C/C++ types in VB.NET"
Imports qword = System.UInt64
Imports dword = System.UInt32
Imports float = System.Single
Imports word = System.UInt16
Imports bool = System.Boolean
#End Region
Partial Public Class NJCM
    Partial Public Class Model
#Region "NJCM.Model Internal Variables"
        'Local Model Buffer
        Public lBuf() As Byte
        'Local Model Processing Key
        Public lKey As dword
        'Internal Workings
        'IO Stream for Model
        Public lStream As IO.ExBinaryReader
        Public lStream1 As IO.ExBinaryWriter
        'Required for Texture Handling
        Public TexturesPresent As Boolean = False
        Public Trial As Boolean = False
        Public mtl() As String
        Public itl() As String
        Public GBIX() As Integer
        'Internal Face List
        Public fc As New List(Of Types.Face)
        'Required Output Lists for Exporting
        Dim tr As New List(Of Types.Triangle)
        Public gr As New List(Of Types.FaceGroup)
        Public mt As New List(Of Types.Material)
        Public vg As New List(Of Types.XYZCoord)
        Public vn As New List(Of Types.XYZCoord)
        Public vt As New List(Of Types.UVCoord)
        Public bn As New List(Of Types.Bone)
        Public mv As New List(Of Types.MVertex)

        'Transformation Matrices
        Private xformv As New Types.Matrix
        Private xformn As New Types.Matrix
        'Re-Entrypoints
        Private EP(256) As dword

        'Vertex Buffers
        Private VB(32767) As Types.Vertex

        'Group Counter
        Private GroupNumber As Integer = 0
        'Various Important Variables
        Private InFIle As String = ""
        Private OutFile As String = ""
        Private NodeId As Integer = 0
        Private Master As New Types.Matrix
        Private HighVert As Integer = 0
        Private InMode As Integer = 0
        Private OutMode As Integer = 0
        Private ContFlag As Boolean = False
        'Write after read buffer - currently not implemented...
        Private warb As New List(Of Types.Vertex)
#End Region
#Region "Public Properties"
        ReadOnly Property FaceCount() As Integer
            Get
                Dim t As New Integer
                t = Me.fc.Count
                Return t
            End Get
        End Property
        ReadOnly Property VertexCount() As Integer
            Get
                Return Me.vg.Count
            End Get
        End Property
        ReadOnly Property NormalCount() As Integer
            Get
                Return Me.vn.Count
            End Get
        End Property
        ReadOnly Property UVCount() As Integer
            Get
                Return Me.vt.Count
            End Get
        End Property
        ReadOnly Property MatCount()
            Get
                Return Me.mt.Count
            End Get
        End Property
        ReadOnly Property MaxVertices() As Integer
            Get
                Dim t As New Integer
                t = Me.HighVert
                Return t
            End Get
        End Property
        Property Face(ByVal idx As Integer) As Types.Face
            Get
                Return Me.fc(idx)
            End Get
            Set(ByVal value As Types.Face)
                Me.fc(idx) = value.Clone
            End Set
        End Property
        Property Vertex(ByVal Index As Integer) As Types.XYZCoord
            Get
                Return Me.VB(Index).V.Clone
            End Get
            Set(ByVal value As Types.XYZCoord)
                With Me.VB(Index).V
                    .X = value.X
                    .Y = value.Y
                    .Z = value.Z
                End With
            End Set
        End Property
        Property Normal(ByVal Index As Integer) As Types.XYZCoord
            Get
                Return Me.VB(Index).N.Clone
            End Get
            Set(ByVal value As Types.XYZCoord)
                With Me.VB(Index).N
                    .X = value.X
                    .Y = value.Y
                    .Z = value.Z
                End With
            End Set
        End Property
        Property Bone(ByVal Index As Integer) As Integer
            Get
                Return Me.VB(Index).B
            End Get
            Set(ByVal value As Integer)
                Me.VB(Index).B = value
            End Set
        End Property
        Property Index(ByVal idx As Integer) As Integer
            Get
                Return Me.VB(idx).I
            End Get
            Set(ByVal value As Integer)
                Me.VB(idx).I = value
            End Set
        End Property
        Property VFlags(ByVal Index As Integer) As Boolean
            Get
                Return Me.VB(Index).F
            End Get
            Set(ByVal value As Boolean)
                Me.VB(Index).F = value
            End Set
        End Property
        Property ImportMode() As Integer
            Get
                Return Me.InMode
            End Get
            Set(ByVal value As Integer)
                Me.InMode = value
            End Set
        End Property
        Property ExportMode() As Integer
            Get
                Return Me.OutMode
            End Get
            Set(ByVal value As Integer)
                Me.OutMode = value
            End Set
        End Property
        ReadOnly Property InputFile() As String
            Get
                Return Me.InFIle
            End Get
        End Property
        ReadOnly Property OutputMode() As Integer
            Get
                Return Me.OutMode
            End Get
        End Property
        Property OutputFile() As String
            Get
                Return Me.OutFile
            End Get
            Set(ByVal value As String)
                Me.OutFile = value
            End Set
        End Property
#End Region
#Region "Initializers"
        Sub New()
            Me.lBuf = Nothing
            Me.lStream = Nothing
            Me.lStream1 = Nothing
            Me.InFIle = ""
            Me.Master = New Types.Matrix
        End Sub
        Sub New(ByVal FileName As String)
            'Initialize IO Stream
            Me.lBuf = My.Computer.FileSystem.ReadAllBytes(FileName)
            Me.lStream = New IO.ExBinaryReader(Me.lBuf)
            Me.lStream1 = New IO.ExBinaryWriter(Me.lBuf)
            Me.InFIle = FileName
            Me.Master = New Types.Matrix

            Dim Tag() As Byte = MakeBytes("NJTL")

            If Strings.Mid(FileName, InStrRev(FileName, ".") + 1) = "NJ" Then
                Me.TexturesPresent = False
            Else
                If InBin(Tag, Me.lBuf) <> -1 Then Me.TexturesPresent = True
            End If

        End Sub
        Sub New(ByVal FileName As String, ByVal Scale As Single)
            'Initialize IO Stream
            Me.lBuf = My.Computer.FileSystem.ReadAllBytes(FileName)
            Me.lStream = New IO.ExBinaryReader(Me.lBuf)
            Me.lStream1 = New IO.ExBinaryWriter(Me.lBuf)
            Me.InFIle = FileName
            Me.Master = MatrixMath.Scale(Me.Master, Scale, Scale, Scale)
            Dim Tag() As Byte = MakeBytes("NJTL")
            If Strings.Mid(FileName, InStrRev(FileName, ".") + 1) = "NJ" Then
                Me.TexturesPresent = False
            Else
                If InBin(Tag, Me.lBuf) <> -1 Then Me.TexturesPresent = True
            End If
        End Sub
#End Region
#Region "Model Parsing Code"
        'Functions and Subs for Model Handling
        Private Sub ClearCollections()
            With Me
                .mt.Clear()
                .vg.Clear()
                .vn.Clear()
                .vt.Clear()
                .bn.Clear()
                .mv.Clear()
                .fc.Clear()
                .tr.Clear()
                .gr.Clear()
                For I As Integer = 0 To 32767
                    .VB(I) = New Types.Vertex
                Next
                'Clear Cache Table
                For I As Integer = 0 To 255
                    Me.EP(I) = 0
                Next
                Me.NodeId = 0
                Me.HighVert = -1
            End With
        End Sub
        Private Function InBin(ByVal A() As Byte, ByVal B() As Byte, Optional ByVal sp As UInteger = 0) As Integer
            Dim tmp As Integer = -1
            Dim tst As Boolean = True
            Dim i As Integer = 0
            Dim il As Integer = A.GetUpperBound(0)
            Dim j As Integer = 0
            Dim jl As Integer = B.GetUpperBound(0) - il
            i = 0
            For j = sp To jl
                If B(j) = A(i) Then
                    i += 1
                    If i > il Then
                        tmp = j - il
                        Exit For
                    End If
                Else
                    i = 0
                End If
            Next
            Return tmp
        End Function
        Public Function FindKeys() As Collection
            Dim retc As New Collection
            Dim Tag() As Byte = System.Text.Encoding.ASCII.GetBytes("NJCM")
            Dim sp As Integer = InBin(Tag, Me.lBuf)
            Do Until sp = -1
                retc.Add((sp + 8))
                sp = InBin(Tag, Me.lBuf, sp + 4)
            Loop
            Return retc
        End Function
        Public Sub ProcessFaceGroups()
            If Me.fc.Count = 0 Then Exit Sub
            Dim tgl As New List(Of Integer)
            Dim tml As New List(Of Integer)
            Dim gs As Integer = 0
            Dim ge As Integer = 0
            Dim gl As Integer = 0
            Dim cg As Integer = Me.fc(0).Group

            For I As Integer = 0 To Me.fc.Count - 1
                If Me.fc(I).Group <> cg Then
                    tgl.Add(gs)
                    tml.Add(Me.fc(gs).Mi)
                    gs = I
                    cg = Me.fc(I).Group
                End If
            Next
            'Add End of List
            tgl.Add(Me.fc.Count)
            tml.Add(-1)
            For I As Integer = 0 To tgl.Count - 2
                gs = tgl(I)
                ge = tgl(I + 1) - 1
                gl = ge - gs
                Report("MakeGroups : Group " & (I + 1).ToString & " starts at " & gs.ToString & " and ends at " & ge.ToString & ".", 1)
                Me.gr.Add(New Types.FaceGroup(gs, ge, tml(I)))
            Next
        End Sub
        Public Sub BuildFaceGroups()
            Dim cs As Integer = 0
            Dim cf As Integer = 0
            If Me.fc.Count = 0 Then Exit Sub 'If no faces, no need to build face groups
            Dim lg As Integer = Me.fc(0).Group
            Dim lv As Types.Face
            Dim I As Integer = 0
            For I = 0 To Me.fc.Count - 1
                lv = Me.fc(I)
                If lv.Group <> lg Then
                    cf = I - 1
                    Me.gr.Add(New Types.FaceGroup(cs, cf, Me.fc(cf).MaterialIndex))
                    cs = I
                    lg = lv.Group
                End If
            Next
            Me.gr.Add(New Types.FaceGroup(cs, I - 1, Me.fc(Me.fc.Count - 1).Mi))
        End Sub
        Public Sub RenumberGroups()
            Dim lg As Integer = -1
            Dim cg As Integer = 0
            Dim cm As Integer = -1
            For I As Integer = 0 To Me.fc.Count - 1
                'Renumber Groups
                If Me.fc(I).Group <> lg Then
                    lg = Me.fc(I).GroupNumber
                    cm = Me.fc(I).MaterialIndex
                    cg += 1
                End If
                Me.fc(I).GroupNumber = cg
                Me.fc(I).MaterialIndex = cm
            Next
        End Sub
        Public Sub WriteTextures(ByVal InFile As String, ByVal OutFile As String)
            Dim p1 As String = Strings.Left(OutFile, Strings.InStrRev(OutFile, "\") - 1)
            TextureExtract.ExtractToFile(InFile, p1)
        End Sub
        Public Function GetBonePosition(ByVal N As Integer) As Types.XYZCoord
            Dim retv As New Types.XYZCoord
            If N = -1 Then
                retv = New Types.XYZCoord(0, 0, 0)
            Else
                retv = GetBonePosition(Me.bn(N).Parent) + Me.bn(N).Position
            End If
            Return retv.Clone
        End Function
        Private Function MakeMatrix(ByVal N As Types.Node) As Types.Matrix
            'Takes Flags, Translate, Scale and Rotation and makes the local transformation matrix out of them...
            'Does Not Cause Problem verified 08-01-20
            Dim a As New Types.Matrix
            a.Identity()
            If N.SCA Then a = ScaleNew(a, N.Scale)
            If N.ROT Then
                If Not N.LWO Then
                    a = RotateNew(a, N.Rotate)
                Else
                    a = Rotate2New(a, N.Rotate)
                End If
            End If
            If N.XLAT Then a = TranslateNew(a, N.Translate)
            Return multiply(a, MatStack.Peek)
        End Function
        Private Function MakeBoneMatrix(ByVal N As Integer) As Types.Matrix
            Dim retv As New Types.Matrix
            Dim tm As New Types.Matrix
            If N = -1 Then
                Return Me.Master
            Else
                tm = TranslateNew(tm, Me.bn(N).Position)
                tm = RotateNew(tm, Me.bn(N).Rotation)
                retv = multiply(tm, MakeBoneMatrix(Me.bn(N).Parent))
            End If
            Return retv
        End Function
        Private Function Clamp(ByVal a As Single, ByVal b As Single) As Single
            If a > b Then Clamp = b Else Clamp = a
        End Function
        Private Function GetMindex(ByVal A As Types.Material) As Integer
            Dim wrkv As Types.Material = A.Clone
            Dim retv As Integer = -1
            Dim idx As Integer = 0

            Do While idx < Me.mt.Count
                If Me.mt(idx) = wrkv Then retv = idx : Exit Do
                idx += 1
            Loop
            If retv = -1 Then
                retv = Me.mt.Count
                Me.mt.Add(wrkv)
            End If
            Return retv
        End Function
        Private Function GenStripString(ByVal A As String, ByVal B As Integer) As String
            Dim t As String = A
            t &= A.ToString
            Return t
        End Function
        Public Function BuildGBIXIndex() As Integer()
            Dim n As Integer = Me.itl.GetUpperBound(0)
            Dim m As Integer = Me.mtl.GetUpperBound(0)
            Dim TxNum(n) As Integer
            Dim j As Integer = n
            Dim I As Integer = m
            If m <> 0 Then
                Do
                    Dim ta As String = itl(j)
                    Dim tb As String = mtl(I)
                    If ta = tb Then TxNum(j) = I : j -= 1
                    I -= 1 : If I = 0 Then I = m
                Loop Until j = 0
            End If
            Return TxNum
        End Function
        'Rebuilt Functions and Subs Go Here
        Public Function NJTLProcess(ByVal position As UInt32) As String()
            With Me.lStream
                Dim cursor As UInt32 = .Position
                Dim currentInt As Int32 = .ReadInt32
                Dim textureCount As Int32 = .ReadInt32
                Dim textureNamesOffset(textureCount) As Int32
                Dim textureNames(textureCount) As String
                Dim remainingTextures As Int32 = textureCount

                Dim currentTextureName As String
                Dim currentTextureChar As Char

                Do
                    textureNamesOffset(textureCount - remainingTextures) = .ReadInt32
                    .Seek(8, SeekOrigin.Current)
                    remainingTextures -= 1
                Loop Until remainingTextures = 0

                remainingTextures = textureCount

                Do
                    currentTextureName = ""
                    .Seek(position + textureNamesOffset(textureCount - remainingTextures), SeekOrigin.Begin)
                    'textureNames((textureCount - remainingTextures) + 1) = MakeString(.ReadBytes(currentInt))
                    currentTextureChar = .ReadChar
                    While Not currentTextureChar.Equals(ControlChars.NullChar)
                        currentTextureName = currentTextureName & currentTextureChar
                        currentTextureChar = .ReadChar
                    End While
                    textureNames((textureCount - remainingTextures)) = currentTextureName
                    remainingTextures -= 1
                Loop Until remainingTextures = 0
                'textureNames(0) = New String("*", currentInt)
                .Seek(cursor, SeekOrigin.Begin)
                Return textureNames
            End With
        End Function
        Public Function TEXTProcess(ByVal Mode As Integer) As String()
            'Mode 1 is NJ Model File, where Texture Information is headed off by CMCK
            'Mode 2 is MLD Container, where Texture Information is referenced in NMLD Header
            'This will replace BuildMasterTextureList
            Select Case Mode
                Case 1
                    Return Me.itl
                Case 2
                    Return Me.MSTLProcess()
                Case Else
                    Dim tmp() As String = {"Error!", "Danger!"}
                    Return tmp
            End Select
        End Function
        Public Function MSTLProcess() As String()
            'Generates a Master Texture List of Texture Names inside an MLD File
            Dim tmp() As String
            With Me.lStream
                Dim rtp As Integer = .Position
                .Seek(&H10, SeekOrigin.Begin)
                .Seek(.ReadInt32, SeekOrigin.Begin)
                'Top of the List
                Dim NE As Integer = .ReadInt32
                Dim WE As Integer = NE
                Dim TxNames(NE) As String

                Dim te As String = ""
                Dim tp As Integer = .Position
                Dim bc As Integer = Me.itl(0).Length
                Do
                    TxNames((NE - WE) + 1) = MakeString(.ReadBytes(bc))
                    .Seek(&H2C - bc, SeekOrigin.Current)
                    WE -= 1
                Loop Until WE = 0
                .Seek(rtp, SeekOrigin.Begin)
                tmp = TxNames
            End With
            Return tmp
        End Function
        Public Function GBIXProcess(ByVal Mode As Int32) As Integer()
            Dim n As Integer = Me.itl.GetUpperBound(0)
            Dim m As Integer = Me.mtl.GetUpperBound(0)
            Dim TxNum(n) As Integer
            Dim j As Integer = n
            Dim I As Integer = m
            If m <> 0 Then
                Do
                    Dim ta As String = itl(j)
                    Dim tb As String = mtl(I)
                    If ta = tb Then TxNum(j) = I : j -= 1
                    I -= 1 : If I = 0 Then I = m
                Loop Until j = 0
            End If
            Return TxNum
        End Function
        Public Sub LOADProcess(ByVal sp As UInteger)
            ClearCollections()
            Me.NodeId = 0
            Me.lKey = sp
            Me.HighVert = -1
            mt.Add(New Types.Material(New Types.RGBA(1, 0, 0, 0), New Types.RGBA(0, 0, 0, 0), New Types.RGBA(0, 0, 0, 0), -1, 0))
            MatStack.Push(Me.Master)
            bn.Add(New Types.Bone(-1, New Types.XYZCoord(0, 0, 0), New Types.XYZCoord(0, 0, 0)))
            NJCMProcess2(Me.lStream, Me.lKey, 0)
            MatStack.Pop()
        End Sub
        Sub ProcessVerts2(ByVal ex As IO.ExBinaryReader, ByVal LC As Types.Chunk, ByVal BoneID As Integer)
            Dim ft() As Byte = {3, 15, 1, 3, 3, 3, 3, 3, 3, 5, 13, 13, 13, 13, 13, 13, 3, 11, 11}
            Dim cl As Integer = 4 * ex.ReadUInt16

            ex.PushPosition()
            Dim ib As Integer = ex.ReadUInt16
            Dim vc As Integer = ex.ReadUInt16
            Dim mb As Integer = LC.flag And &H3
            Dim fv As Integer = LC.type - &H20
            Dim rf() As Boolean = {BitTest(ft(fv), 0), BitTest(ft(fv), 1), BitTest(ft(fv), 2), BitTest(ft(fv), 3)}
            Dim I As Integer = ib
            Dim tv As New Types.Vertex
            Dim v As New Types.XYZCoord
            Dim n As New Types.XYZCoord
            Report("          Format = " & HexOut(LC.type) & " , Start of Block = " & ib.ToString & " , Flags = " & HexOut(LC.flag), 1)
            If mb = 0 Then
                Do
                    If rf(0) Then v = MatrixMath.Apply2(ex.ReadXYZ, Me.xformv)
                    If rf(1) Then ex.ReadUInt32()
                    If rf(2) Then n = VectorMath.Normalize(MatrixMath.Apply2(ex.ReadXYZ, Me.xformn))
                    If rf(3) Then ex.ReadUInt32()
                    Report("                    Vertex " & (I).ToString & " = (" & v.ToString2 & ")", 2)
                    Me.Vertex(I) = v.Clone
                    Me.Normal(I) = n.Clone
                    Me.Bone(I) = BoneID
                    Me.Index(I) = I
                    Me.VB(I).F = False
                    I += 1
                    vc -= 1
                Loop Until vc = 0
            End If

            ex.PopPosition()
            ex.Seek(cl, SeekOrigin.Current)
        End Sub
        Sub WriteVertexAfterRead()
            'Local count of Overwrite Buffer
            Dim lc As Integer = warb.Count
            'Local Pointer into Overwrite Buffer
            Dim lp As Integer = 0
            'Local Index to Vertex Buffer
            Dim li As Integer = 0
            'Local Deletions to take place after pass through list is made
            Dim ld As New List(Of Integer)
            Do While ((lp < lc) And (lc > 0))
                li = warb(lp).I
                If Me.VB(li).F = True Then
                    'Able to Write to Vertex
                    Report("                         Write After Read : Vertex " & li.ToString & " " & warb(lp).V.ToString2, 2)
                    Me.VB(li) = warb(lp).Clone
                    'Set the Write Flag
                    Me.VB(li).F = False
                    ld.Add(lp)
                End If
                lp += 1
            Loop
            'Do List Deletions AFTER one complete pass through list
            Do While ld.Count < 0
                warb.RemoveAt(ld(0))
                ld.RemoveAt(0)
            Loop
        End Sub
        Sub ProcessStrips2(ByVal ex As IO.ExBinaryReader, ByVal LC As Types.Chunk, ByVal mat As Types.Material, ByVal LG As Integer)
            Dim cl As word = 2 * ex.ReadUInt16
            Dim rp As dword = ex.Position
            ex.PushPosition()
            Dim uvc() As Integer = {1, 255, 1023}
            Dim sfmt() As Byte = {1, 3, 3, 9, 11, 11, 5, 7, 7, 1, 19, 19}
            Dim rd As word = ex.ReadUInt16
            Dim sc As word = rd And &H3FFF
            Dim uf As word = rd >> 13
            Dim usc As word = 1
            Report("          " & HexOut(rp) & " Triangle Strips, Format " & HexOut(LC.type) & ", Number of Strips = " & sc.ToString & ", Group = " & (LG + 1).ToString, 1)
            Report("                     User Flags Present = " & uf.ToString, 1)
            Dim td As Byte = sfmt(LC.type - &H40)
            Dim rf() As Boolean = {BitTest(sfmt(LC.type - &H40), 0), BitTest(sfmt(LC.type - &H40), 1), _
                                   BitTest(sfmt(LC.type - &H40), 2), BitTest(sfmt(LC.type - &H40), 3), _
                                   BitTest(sfmt(LC.type - &H40), 4)}
            Dim f As Boolean
            Dim tf As New Types.Face
            Dim nc, q, g As Short
            Dim fq, fr As Single
            Dim tv, tm As New Types.XYZCoord
            Dim tt As New Types.UVCoord
            Dim tb, tc, ti As Integer
            Dim tn() As Types.FaceNode = New Types.FaceNode(3) {}
            Dim tp As Types.Material = mat.Clone
            Dim ss As String

            Do
                f = False
                tc = -1
                nc = ex.ReadInt16
                If nc < 1 Then
                    f = Not f
                    nc = Math.Abs(nc)
                End If
                Report("               Strip " & usc.ToString & ", " & (Math.Abs(nc) - 2).ToString & " Triangles.", 2)
                ss = "               "
                Do
                    ti = LC.type - &H40
                    If rf(0) Then
                        q = ex.ReadInt16
                        ss &= q.ToString & " "
                        tv = Me.VB(q).V.Clone
                        tm = Me.VB(q).N.Clone
                        tb = Me.VB(q).B
                        Me.VB(q).F = True
                    End If
                    If rf(1) Then
                        fq = ex.ReadInt16 / uvc(ti Mod 3)
                        fr = ex.ReadInt16 / uvc(ti Mod 3)
                        If BitTest(mat.Fl, 3) Then fq = 1 - fq
                        If Not BitTest(mat.Fl, 2) Then fr = 1 - fr
                        If BitTest(mat.Fl, 1) Then fq = Clamp(fq, 1)
                        If BitTest(mat.Fl, 0) Then fr = Clamp(fr, 1)
                        tt = New Types.UVCoord(fq, fr)
                    Else
                        tt = New Types.UVCoord(0, 0)
                    End If
                    If rf(2) Then ex.ReadUInt32()
                    If rf(3) Then ex.ReadBytes(6)
                    If rf(4) Then ex.ReadUInt32()
                    tn(tc + 1) = New Types.FaceNode(tv, tm, tt, tb)
                    If tc > 0 Then
                        Dim I As Integer = 0
                        Do While I < uf
                            g = ex.ReadInt16
                            I += 1
                        Loop
                        If f Then
                            tf.A = tn(0) : tf.B = tn(2) : tf.C = tn(1)
                        Else
                            tf.A = tn(0) : tf.B = tn(1) : tf.C = tn(2)
                        End If
                        tf.Mi = GetMindex(tp)
                        tf.Group = LG
                        Me.fc.Add(tf.Clone)
                        f = Not f
                        tn(0) = tn(1).Clone
                        tn(1) = tn(2).Clone
                    End If
                    If tc < 1 Then tc += 1
                    nc -= 1
                Loop Until nc = 0
                Report(ss, 2)
                ss = "               "
                sc -= 1
                usc += 1
            Loop Until sc = 0
            ex.PopPosition()
            ex.Seek(cl, SeekOrigin.Current)

        End Sub
        Function ProcessChunks(ByVal ex As IO.ExBinaryReader, ByVal sp As dword, ByVal BoneID As Integer, ByVal mode As Integer) As dword
            Dim retv As Integer = 0
            Dim cont As Boolean = True
            'Common Chunk Processing Routine
            'Calls ProcVerts and ProcPolys, does minor bits of Processing...
            ex.PushPosition()
            Dim C As New Types.Chunk
            Dim T As New Types.Material
            Dim cl As UInteger = 0
            Dim cs As UShort = 0
            Dim s As String = ""
            Dim s1 As UInt32

            ex.Seek(sp, SeekOrigin.Begin)
            Do
                s1 = ex.Position
                s = "     " & HexOut(s1) & " "
                C = ex.ReadChunk
                Select Case C.type
                    Case &H0
                        Report(s & "NJ_NULL")
                    Case &H1 To &H3
                        Report(s & "NJ_TINY " & HexOut(C.type) & " ")
                    Case &H4
                        Report(s & "NJ_TINY - Save Cache Position (" & HexOut(ex.Position) & ") In " & HexOut(C.flag) & " ")
                        Me.EP(C.flag) = ex.Position
                        retv = 0
                        cont = False
                    Case &H5
                        Report(s & "NJ_TINY - Draw From Saved Cache " & HexOut(C.flag) & " ")
                        ProcessChunks(ex, Me.EP(C.flag), BoneID, 1)
                        Report(s & " Return from Cache Draw ")
                    Case &H8
                        cs = ex.ReadUInt16
                        T.Fl = C.flag >> 4
                        T.Km = cs And &H3FFF
                        Report(s & "NJ_TEX1 - Assign GBIX (" & HexOut(T.Km) & ") To 1st Texture Unit")
                    Case &H9
                        cs = ex.ReadUInt16
                        T.Fl = C.flag >> 4
                        T.Km = cs And &H3FFF
                        Report(s & "NJ_TEX2 - Assign GBIX (" & HexOut(T.Km) & ") To 2nd Texture Unit")
                    Case &H10 To &H17
                        Report(s & "NJ_MAT1 - 1st Texture Unit Material Definition")
                        cl = 2 * ex.ReadUInt16
                        If BitTest(C.type, 0) Then T.Kd = ex.ReadARGB
                        If BitTest(C.type, 1) Then T.Ka = ex.ReadARGB : T.Ka.A = 0
                        If BitTest(C.type, 2) Then T.Ks = ex.ReadARGB : T.Ks.A = 0
                    Case &H18
                        Report(s & "NJ_BUMP - Bump Map Definition")
                        cl = 2 * ex.ReadUInt16
                        ex.Seek(cl, SeekOrigin.Current)
                    Case &H18 To &H1F
                        Report(s & "NJ_MAT2 - 2nd Texture Unit Material Definition")
                        cl = 2 * ex.ReadUInt16
                        ex.Seek(cl, SeekOrigin.Current)
                    Case &H20 To &H37
                        Report(s & "NJ_VERT - Vertex Data, Flags = " & HexOut(C.flag))
                        ProcessVerts2(ex, C, BoneID)
                    Case &H38 To &H3A
                        Report(s & "NJ_VOLM - Volume Chunk")
                        cl = 2 * ex.ReadUInt16
                        ex.Seek(cl, SeekOrigin.Current)
                    Case &H40 To &H4B
                        Report(s & "NJ_POLY - Triangle Strip Data")
                        ProcessStrips2(ex, C, T.Clone, Me.GroupNumber)
                        Me.GroupNumber += 1
                    Case &HFF
                        Report(s & "NJ_END  - End of Chunk")
                        retv = 0
                        cont = False
                    Case Else
                        Report(s & "NJ_HUH? - Unimplemented Chunk Type (Type " & HexOut(C.type) & " ")
                End Select
            Loop While cont
            Application.DoEvents()

            ex.PopPosition()
            Return retv
        End Function
        Public Sub NJCMProcess2(ByVal ex As IO.ExBinaryReader, ByVal sp As UInteger, ByVal parent As Integer)
            'Does the basic recursive Node Walking...
            Dim N As New Types.Node
            Dim M As New Types.Model
            Dim tx As New Types.XYZCoord
            Dim nn As Integer = parent
            Dim retv As Integer = 0
            Dim tv As dword = 0
            Dim tp As dword = 0

            Me.ContFlag = False
            ex.Seek(sp, SeekOrigin.Begin)
            Report("Node @ " & HexOut(ex.Position), 0)
            N = ex.ReadNode
            Me.xformv = MakeMatrix(N)
            Me.xformn = MatrixMath.Translate(Me.xformv, -Me.xformv.m(4, 1), -Me.xformv.m(4, 2), -Me.xformv.m(4, 3))
            Report(N.FlagReport, 1)

            If N.Model > 0 Then
                Report("     Model Information @ " & HexOut(Me.lKey + N.Model), 0)
                ex.Seek(Me.lKey + N.Model, SeekOrigin.Begin)
                M = ex.ReadModel
                If M.Vertex > 0 Then
                    tx = Apply2(M.Center, Me.xformv) - GetBonePosition(parent)
                    If tx <> New Types.XYZCoord(0, 0, 0) Then
                        Me.bn.Add(New Types.Bone(parent, tx.Clone, New Types.XYZCoord(0, 0, 0)))
                        Me.NodeId += 1
                        nn = Me.NodeId
                    End If
                End If
                tv = (M.Vertex + Me.lKey) * BooltoInt(M.Vertex > 0)
                tp = (M.Poly + Me.lKey) * BooltoInt(M.Poly > 0)
                Do
                    If tv > 0 Then tv = ProcessChunks(ex, tv, nn, 0)
                    If tp > 0 Then tp = ProcessChunks(ex, tp, 0, 0)
                Loop Until ((tv = 0) And (tp = 0))
            End If

            If N.Child <> 0 Then Report("     Child Node = " & HexOut(N.Child + Me.lKey), 1) Else Report("     No Child Node.", 1)
            If N.Sibling <> 0 Then Report("     Sibling Node = " & HexOut(N.Sibling + Me.lKey), 1) Else Report("     No Sibling Node.", 1)

            'If N.TRACE Then
            If N.Child > 0 Then
                MatStack.Push(Me.xformv)
                NJCMProcess2(ex, N.Child + Me.lKey, nn)
                MatStack.Pop()
            End If
            'End If

            If N.Sibling > 0 Then
                NJCMProcess2(ex, N.Sibling + Me.lKey, nn)
            End If
        End Sub
        Public Sub NJCMSaveProcess2(ByVal FileName As String, ByVal Fmt As Integer)
            Dim ExportModule As New ModelExport
            Me.OutputFile = FileName
            Select Case Fmt
                Case Is = 1
                    'Raw Triangle Output
                    'ExportToRaw(FileName)
                    ExportModule.ToRaw(FileName, Me)
                Case Is = 2
                    'Wavefront OBJ Output
                    'ExportObj(FileName)
                    ExportModule.ToObj(FileName, Me)
                Case Is = 3
                    'Milkshape MS3D Output
                    'ExportToMS3D(FileName)
                    ExportModule.ToMS3DASCII(FileName, Me)
                Case Else
                    WriteLn("Crash and Burn!  File format Not Supported!")
            End Select
        End Sub
#End Region
    End Class
End Class
