private void PopulatePaletteSettings()
        {
            int texDefID = lbxTexDef.SelectedIndex;

            if (texDefID == -1 || texDefID >= internalTexs)
            {
                grdPalette.ClearColours();
                return;
            }

            byte[] palette = m_TexDefs[texDefID].m_Tex.m_RawPaletteData;

            int nColours = palette.Length / 2;

            Color[] paletteColours = new Color[nColours];
            for (int i = 0; i < nColours; i++)
            {
                ushort palColour = (ushort)(palette[(i * 2)] | (palette[(i * 2) + 1] << 8));
                paletteColours[i] = Helper.BGR15ToColor(palColour);
            }

            grdPalette.SetColours(paletteColours);

            btnModelPalettesSelectedColour.Enabled = true;
        }
Example #2
0
        public Bitmap LoadImage(Boolean usingTMap, int sizeX, int sizeY, int bpp, int paletteRow = 0)
        {
            Bitmap bmp = new Bitmap(sizeX, sizeY);

            uint   tileoffset = 0, tilenum = 0;
            ushort tilecrap = 0;

            for (int my = 0; my < sizeY; my += 8)
            {
                for (int mx = 0; mx < sizeX; mx += 8)
                {
                    if (usingTMap)
                    {
                        tilecrap = m_TileMapFile.Read16(tileoffset);
                        tilenum  = (uint)(tilecrap & 0x03FF);
                    }

                    for (int ty = 0; ty < 8; ty++)
                    {
                        for (int tx = 0; tx < 8; tx++)
                        {
                            if (bpp == 8)
                            {
                                uint totaloffset = (uint)(tilenum * 64 + ty * 8 + tx); //Address of current pixel
                                byte palentry    = m_TileSetFile.Read8(totaloffset);   //Offset of current pixel's entry in palette file
                                //Palentry is double to get the position of the colour in the palette file
                                ushort pixel = m_PalFile.Read16((uint)(palentry * 2)); //Colour of current pixel from palette file
                                bmp.SetPixel(mx + tx, my + ty, Helper.BGR15ToColor(pixel));
                            }
                            else if (bpp == 4)
                            {
                                float totaloffset = (float)((float)(tilenum * 64 + ty * 8 + tx) / 2f);//Address of current pixel
                                byte  palentry    = 0;
                                if (totaloffset % 1 == 0)
                                {
                                    palentry = m_TileSetFile.Read8((uint)totaloffset); //Offset of current pixel's entry in palette file
                                    palentry = (byte)(palentry & 0x0F);                // Get 4 right bits
                                }
                                else
                                {
                                    palentry = m_TileSetFile.Read8((uint)totaloffset); //Offset of current pixel's entry in palette file
                                    palentry = (byte)(palentry >> 4);                  // Get 4 left bits
                                }
                                //Palentry is double to get the position of the colour in the palette file
                                ushort pixel = m_PalFile.Read16((uint)((palentry * 2) + (m_PaletteRow * 32)));//Colour of current pixel from palette file
                                bmp.SetPixel(mx + tx, my + ty, Helper.BGR15ToColor(pixel));
                            }
                        }
                    }

                    tileoffset += 2;
                    if (!usingTMap)
                    {
                        tilenum++;
                    }
                }
            }

            return(bmp);
        }
Example #3
0
        private void LoadPalette()
        {
            gridPalette.RowCount    = 16;
            gridPalette.ColumnCount = 16;

            //Read palette colours
            Color[] paletteColours = new Color[256];
            for (int i = 0; i < m_PalFile.m_Data.Length / 2; i++)
            {
                //Colour in BGR15 format (16 bits) written to every even address 0,2,4...
                ushort palColour = m_PalFile.Read16((uint)(i * 2));
                paletteColours[i] = Helper.BGR15ToColor(palColour);
            }
            for (int i = m_PalFile.m_Data.Length / 2; i < 256; i++)
            {
                paletteColours[i] = Helper.BGR15ToColor(0);// Fill blank entries with black
            }
            //Display palette colours
            int clr = 0;

            for (int row = 0; row < gridPalette.RowCount; row++)                               //For every row
            {
                gridPalette.Columns[row].Width = 16;                                           //Set each cell's width to 16
                for (int column = 0; column < gridPalette.ColumnCount; column++)               //For every column
                {
                    gridPalette.Rows[row].Cells[column].Style.BackColor = paletteColours[clr]; //Set the cell colour to the corresponding palette colour
                    clr += 1;
                }
            }
            gridPalette.CurrentCell.Selected = false;//Select none by default
        }
Example #4
0
        private void LoadPalette()
        {
            // Read palette colours
            Color[] paletteColours = new Color[256];
            for (int i = 0; i < m_PalFile.m_Data.Length / 2; i++)
            {
                // Colour in BGR15 format (16 bits) written to every even address 0,2,4...
                ushort palColour = m_PalFile.Read16((uint)(i * 2));
                paletteColours[i] = Helper.BGR15ToColor(palColour);
            }

            gridPalette.SetColours(paletteColours);
        }
Example #5
0
        private void ExportPaletteAsACT(string fileName)
        {
            byte[] data = new byte[256 * 3];

            int numColours = m_PalFile.m_Data.Length / 2;

            for (int i = 0; i < numColours; i++)
            {
                Color currentColour = Helper.BGR15ToColor(m_PalFile.Read16((uint)(i * 2)));
                data[(i * 3) + 0] = (byte)currentColour.R;
                data[(i * 3) + 1] = (byte)currentColour.G;
                data[(i * 3) + 2] = (byte)currentColour.B;
            }

            System.IO.File.WriteAllBytes(fileName, data);
        }
Example #6
0
        private void PopulatePaletteSettings()
        {
            byte[] palette = m_Texture.m_Tex.m_RawPaletteData;

            int nColours = palette.Length / 2;

            Color[] paletteColours = new Color[nColours];
            for (int i = 0; i < nColours; i++)
            {
                ushort palColour = (ushort)(palette[(i * 2)] | (palette[(i * 2) + 1] << 8));
                paletteColours[i] = Helper.BGR15ToColor(palColour);
            }

            grdPalette.SetColours(paletteColours);

            btnModelPalettesSelectedColour.Enabled = true;
        }
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            ColorDialog dialog   = new ColorDialog();
            int         oldColor = (int)(((Color)value).ToArgb() & ~0xff000000);

            oldColor            = oldColor & 0x0000ff00 | oldColor << 16 & 0x00ff0000 | oldColor >> 16 & 0x000000ff;
            dialog.CustomColors = new int[] { oldColor };
            dialog.Color        = (Color)value;
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                Color color = dialog.Color;
                color = Helper.BGR15ToColor(Helper.ColorToBGR15(color));
                return(color);
            }

            return(value);
        }
Example #8
0
        public void ImportBMP(string filename, int bpp, int sizeX, int sizeY, bool replaceMinimap = false, int numTilesX = 0,
                              int numTilesY = 0, byte[] tilePaletteRows = null)
        {
            // The tile maps (NSC / ISC) files for minimaps are always arranged a particular way - 0, 1, 2...15, 32 for 128 x 128
            Bitmap bmp = new Bitmap(filename);

            Color[] palette = bmp.Palette.Entries.ToArray <Color>();
            if (palette.Length > 256)
            {
                MessageBox.Show("Too many colours\n\nYou must import an indexed bitmap with a maximum of 256 colours.");
                return;
            }

            // Write new palette
            m_PalFile = Program.m_ROM.GetFileFromName(txtSelNCL.Text);
            m_PalFile.Clear();
            for (int i = 0; i < palette.Length; i++)
            {
                // Colour in BGR15 format (16 bits) written to every even address 0,2,4...
                m_PalFile.Write16((uint)i * 2, (ushort)(Helper.ColorToBGR15(palette[i])));
            }
            // Pad the palette to a multiple of 16 colours
            byte nColourSlots = (byte)((palette.Length % 16 != 0) ? ((palette.Length + 16) & ~15) : palette.Length);

            for (int i = palette.Length; i < nColourSlots; i++)
            {
                m_PalFile.Write16((uint)i * 2, 0);
            }

            m_PalFile.SaveChanges();

            // Fill current tmapfiles to use full mapsize x mapsize
            if (m_IsUsingTileMap)
            {
                m_TileMapFile = Program.m_ROM.GetFileFromName(txtSelNSC.Text);
                m_TileMapFile.Clear();
                sizeX = bmp.Width;
                sizeY = bmp.Height;
                uint addr    = 0;
                int  curTile = 0;
                int  row     = (int)(sizeX / 8);

                for (int my = 0; my < sizeY; my += 8)
                {
                    for (int mx = 0; mx < sizeX; mx += 8)
                    {
                        m_TileMapFile.Write16(addr, (ushort)curTile);
                        curTile++;
                        addr += 2;
                    }
                }
                if (chkNSCDcmp.Checked)
                {
                    m_TileMapFile.ForceCompression();
                }
                m_TileMapFile.SaveChanges();
            }// End If usingTMap

            //Check to see if there's already an identical tile and if so, change the current value to that
            //Works, but not if you want to keep existing data eg. multiple maps

            //List<List<byte>> tiles = new List<List<byte>>();
            //List<byte> curTilePal = new List<byte>();
            //uint tileoffset = 0;
            //for (int my = 0; my < sizeY; my += 8)
            //{
            //    for (int mx = 0; mx < sizeX; mx += 8)
            //    {
            //        ushort tilecrap = tmapfile.Read16(tileoffset);
            //        uint tilenum = (uint)(tilecrap & 0x03FF);

            //        curTilePal = new List<byte>();
            //        for (int ty = 0; ty < 8; ty++)
            //        {
            //            for (int tx = 0; tx < 8; tx++)
            //            {
            //                uint totaloffset = (uint)(tilenum * 64 + ty * 8 + tx);//Position of current pixel's entry
            //                curTilePal.Add((byte)(Array.IndexOf(palette, bmp.GetPixel(mx + tx, my + ty))));
            //            }
            //        }

            //        tiles.Add(curTilePal);

            //        if (posInList(tiles, curTilePal) != -1)
            //        {
            //            tmapfile.Write16(tileoffset, (ushort)(posInList(tiles, curTilePal)));
            //        }

            //        tileoffset += 2;
            //    }
            //}

            //Write the new image to file
            m_TileSetFile = Program.m_ROM.GetFileFromName(txtSelNCG.Text);
            m_TileSetFile.Clear();
            uint tileoffset = 0;
            uint tileNum    = 0;

            for (int my = 0; my < sizeY; my += 8)
            {
                for (int mx = 0; mx < sizeX; mx += 8)
                {
                    for (int ty = 0; ty < 8; ty++)
                    {
                        for (int tx = 0; tx < 8; tx++)
                        {
                            if (bpp == 8)
                            {
                                uint totaloffset = (uint)(tileNum * 64 + ty * 8 + tx);//Position of current pixel's entry
                                byte palentry    = (byte)(Array.IndexOf(palette, bmp.GetPixel(mx + tx, my + ty)));
                                m_TileSetFile.Write8(totaloffset, (byte)(palentry));
                            }
                            else if (bpp == 4)
                            {
                                float totaloffset = (float)((float)(tileNum * 64 + ty * 8 + tx) / 2f);//Address of current pixel
                                byte  palentry    = (byte)(Array.IndexOf(palette, bmp.GetPixel(mx + tx, my + ty)));

                                int  currentTileIndex    = (int)tileNum;
                                byte currentTileRowIndex = tilePaletteRows[currentTileIndex];

                                byte rowStartColourOffset = (byte)(16 * currentTileRowIndex);
                                byte rowEndColourOffset   = (byte)(16 * (currentTileRowIndex + 1) - 1);

                                if (palentry < rowStartColourOffset || palentry > rowEndColourOffset) // Referencing colour outisde its row
                                {
                                    Color referencedColour = Helper.BGR15ToColor(m_PalFile.Read16((uint)(palentry * 2)));

                                    // Find the same colour in the correct row and set the current pixel to reference that instead
                                    for (int col = rowStartColourOffset; col < rowEndColourOffset; col++)
                                    {
                                        uint offset = (uint)(col * 2);

                                        if (offset >= m_PalFile.m_Data.Length)
                                        {
                                            break;
                                        }

                                        Color currentColour = Helper.BGR15ToColor(m_PalFile.Read16(offset));
                                        if (currentColour.Equals(referencedColour))
                                        {
                                            palentry = (byte)col;
                                            break;
                                        }
                                    }
                                }

                                if (totaloffset % 1 == 0)
                                {
                                    // Right 4 bits
                                    m_TileSetFile.Write8((uint)totaloffset, (byte)palentry);
                                    //(byte)((tsetfile.Read8((uint)totaloffset) & 0xF0) | palentry));
                                }
                                else
                                {
                                    // Left 4 bits
                                    m_TileSetFile.Write8((uint)totaloffset, (byte)((palentry << 4) |
                                                                                   (m_TileSetFile.Read8((uint)totaloffset) & 0x0F)));
                                }
                            }
                        }
                    }

                    tileoffset += 2;
                    tileNum++;
                }
            }

            if (chkNCGDcmp.Checked)
            {
                m_TileSetFile.ForceCompression();
            }
            m_TileSetFile.SaveChanges();

            // If it's a minimap that's being replaced, fill the tile maps to allow for multiple maps
            // and ensure the image's displayed at the right size since minimap sizes are hard-coded,
            // they are not based on the size of the imported image.
            if (replaceMinimap)
            {
                try
                {
                    if (chk128.Checked)
                    {
                        FillMinimapTiles(128);
                    }
                    else if (chk256.Checked)
                    {
                        FillMinimapTiles(256);
                    }
                }
                catch (Exception ex) { MessageBox.Show(ex.Message + ex.Source + ex.StackTrace); }
            }

            m_SizeX = sizeX;
            m_SizeY = sizeY;
        }
Example #9
0
        public BMD(NitroFile file)
        {
            m_File     = file;
            m_FileName = file.m_Name;

            /* if (m_File.m_ID == 741)
             *   lolol = true;
             * else*/
            lolol = false;

            // Keep a list of pointers so it's easier to add/remove entries, space etc.
            m_PointerList = new List <PointerReference>();

            m_ScaleFactor = (float)(1 << (int)m_File.Read32(0x0));

            // ModelChunk refers to Bone
            m_NumModelChunks    = m_File.Read32(0x04);
            m_ModelChunksOffset = m_File.Read32(0x08);
            AddPointer(0x08);
            for (int i = 0; i < m_NumModelChunks; i++)
            {
                AddPointer((uint)(m_ModelChunksOffset + (i * 64) + 0x04));
                AddPointer((uint)(m_ModelChunksOffset + (i * 64) + 0x34));
                AddPointer((uint)(m_ModelChunksOffset + (i * 64) + 0x38));
            }

            // PolyChunk refers to Display List
            m_NumPolyChunks    = m_File.Read32(0x0C);
            m_PolyChunksOffset = m_File.Read32(0x10);
            AddPointer(0x10);
            for (int i = 0; i < m_NumPolyChunks; i++)
            {
                // Offset to Display List within Display List entries
                AddPointer((uint)(m_PolyChunksOffset + (i * 8) + 4));
                // Offsets within the Display List 16 byte headers
                AddPointer(m_File.Read32((uint)(m_PolyChunksOffset + (i * 8) + 4)) + 0x04);
                AddPointer(m_File.Read32((uint)(m_PolyChunksOffset + (i * 8) + 4)) + 0x0C);
            }
            m_NumTexChunks    = m_File.Read32(0x14);
            m_TexChunksOffset = m_File.Read32(0x18);
            m_TextureIDs      = new Dictionary <string, uint>();
            AddPointer(0x18);
            for (int i = 0; i < m_NumTexChunks; i++)
            {
                AddPointer((uint)(m_TexChunksOffset + (i * 20) + 0));
                AddPointer((uint)(m_TexChunksOffset + (i * 20) + 4));
                m_TextureIDs.Add(m_File.ReadString(m_File.Read32((uint)(m_TexChunksOffset + (20 * i))), 0), (uint)i);
            }
            m_NumPalChunks    = m_File.Read32(0x1C);
            m_PalChunksOffset = m_File.Read32(0x20);
            m_PaletteIDs      = new Dictionary <string, uint>();
            AddPointer(0x20);
            for (int i = 0; i < m_NumPalChunks; i++)
            {
                AddPointer((uint)(m_PalChunksOffset + (i * 16) + 0));
                AddPointer((uint)(m_PalChunksOffset + (i * 16) + 4));
                m_PaletteIDs.Add(m_File.ReadString(m_File.Read32((uint)(m_PalChunksOffset + (16 * i))), 0), (uint)i);
            }
            m_NumMatChunks    = m_File.Read32(0x24);
            m_MatChunksOffset = m_File.Read32(0x28);
            AddPointer(0x28);
            for (int i = 0; i < m_NumMatChunks; i++)
            {
                AddPointer((uint)(m_MatChunksOffset + (i * 48) + 0));
            }
            m_BoneMapOffset = m_File.Read32(0x2C);
            AddPointer(0x2C);

            m_Textures    = new Dictionary <string, NitroTexture>();
            m_ModelChunks = new ModelChunk[m_NumModelChunks];

            for (uint c = 0; c < m_NumModelChunks; c++)
            {
                ModelChunk mdchunk = new ModelChunk(this);
                m_ModelChunks[c] = mdchunk;

                uint mdchunkoffset = m_ModelChunksOffset + (c * 64);

                mdchunk.m_ID   = m_File.Read32(mdchunkoffset);
                mdchunk.m_Name = m_File.ReadString(m_File.Read32(mdchunkoffset + 0x04), 0);

                // transforms part
                {
                    int   xscale = (int)m_File.Read32(mdchunkoffset + 0x10);
                    int   yscale = (int)m_File.Read32(mdchunkoffset + 0x14);
                    int   zscale = (int)m_File.Read32(mdchunkoffset + 0x18);
                    short xrot   = (short)m_File.Read16(mdchunkoffset + 0x1C);
                    short yrot   = (short)m_File.Read16(mdchunkoffset + 0x1E);
                    short zrot   = (short)m_File.Read16(mdchunkoffset + 0x20);
                    int   xtrans = (int)m_File.Read32(mdchunkoffset + 0x24);
                    int   ytrans = (int)m_File.Read32(mdchunkoffset + 0x28);
                    int   ztrans = (int)m_File.Read32(mdchunkoffset + 0x2C);

                    mdchunk.m_Scale       = new Vector3((float)xscale / 4096.0f, (float)yscale / 4096.0f, (float)zscale / 4096.0f);
                    mdchunk.m_Rotation    = new Vector3(((float)xrot * (float)Math.PI) / 2048.0f, ((float)yrot * (float)Math.PI) / 2048.0f, ((float)zrot * (float)Math.PI) / 2048.0f);
                    mdchunk.m_Translation = new Vector3((float)xtrans / 4096.0f, (float)ytrans / 4096.0f, (float)ztrans / 4096.0f);
                    mdchunk.m_Transform   = Helper.SRTToMatrix(mdchunk.m_Scale, mdchunk.m_Rotation, mdchunk.m_Translation);

                    // Used when exporting bones
                    mdchunk.m_20_12Scale       = new uint[] { (uint)xscale, (uint)yscale, (uint)zscale };
                    mdchunk.m_4_12Rotation     = new ushort[] { (ushort)xrot, (ushort)yrot, (ushort)zrot };
                    mdchunk.m_20_12Translation = new uint[] { (uint)xtrans, (uint)ytrans, (uint)ztrans };

                    // if the chunk has a parent, apply the parent's transform to the chunk's transform.
                    // we don't need to go further than one level because the paren't transform already
                    // went through its parents' transforms.
                    short parent_offset = (short)m_File.Read16(mdchunkoffset + 0x8);
                    if (parent_offset < 0)
                    {
                        int parentchunkid = (int)(c + parent_offset);
                        Matrix4.Mult(ref mdchunk.m_Transform, ref m_ModelChunks[parentchunkid].m_Transform, out mdchunk.m_Transform);
                    }
                    mdchunk.m_ParentOffset = parent_offset;
                }
                // If 0x0A is set to 1 the bone has children, if 0 it doesn't
                mdchunk.m_HasChildren = (m_File.Read16(mdchunkoffset + 0x0A) == 1);

                mdchunk.m_SiblingOffset = (short)(m_File.Read16(mdchunkoffset + 0x0C));

                uint flags = m_File.Read32(mdchunkoffset + 0x3C);
                mdchunk.m_Billboard = ((flags & 0x1) == 0x1);

                uint numpairs = m_File.Read32(mdchunkoffset + 0x30);
                uint matlist  = m_File.Read32(mdchunkoffset + 0x34);
                uint polylist = m_File.Read32(mdchunkoffset + 0x38);

                mdchunk.m_MatGroups = new MaterialGroup[numpairs];

                for (uint i = 0; i < numpairs; i++)
                {
                    MaterialGroup matgroup = new MaterialGroup();
                    mdchunk.m_MatGroups[i] = matgroup;

                    byte matID  = m_File.Read8(matlist + i);
                    byte polyID = m_File.Read8(polylist + i);

                    uint mchunkoffset = (uint)(m_MatChunksOffset + (matID * 48));

                    matgroup.m_ID   = matID;
                    matgroup.m_Name = m_File.ReadString(m_File.Read32(mchunkoffset), 0);
                    uint texid = m_File.Read32(mchunkoffset + 0x04);
                    uint palid = m_File.Read32(mchunkoffset + 0x08);
                    matgroup.m_TexParams    = m_File.Read32(mchunkoffset + 0x20);
                    matgroup.m_PolyAttribs  = m_File.Read32(mchunkoffset + 0x24);
                    matgroup.m_DifAmbColors = m_File.Read32(mchunkoffset + 0x28);
                    matgroup.m_SpeEmiColors = m_File.Read32(mchunkoffset + 0x2C);

                    if ((matgroup.m_PolyAttribs & 0x30) == 0x10)
                    {
                        matgroup.m_TexEnvMode = TextureEnvMode.Decal;
                    }
                    else
                    {
                        matgroup.m_TexEnvMode = TextureEnvMode.Modulate;
                    }

                    switch (matgroup.m_PolyAttribs & 0xC0)
                    {
                    case 0x00: matgroup.m_CullMode = CullFaceMode.FrontAndBack; break;

                    case 0x40: matgroup.m_CullMode = CullFaceMode.Front; break;

                    case 0x80: matgroup.m_CullMode = CullFaceMode.Back; break;
                    }

                    matgroup.m_DiffuseColor  = Helper.BGR15ToColor((ushort)matgroup.m_DifAmbColors);
                    matgroup.m_AmbientColor  = Helper.BGR15ToColor((ushort)(matgroup.m_DifAmbColors >> 16));
                    matgroup.m_SpecularColor = Helper.BGR15ToColor((ushort)matgroup.m_SpeEmiColors);
                    matgroup.m_EmissionColor = Helper.BGR15ToColor((ushort)(matgroup.m_SpeEmiColors >> 16));

                    switch (matgroup.m_TexParams >> 30)
                    {
                    case 0:
                        matgroup.m_TexCoordScale = new Vector2(1.0f, 1.0f);
                        matgroup.m_TexCoordRot   = 0.0f;
                        matgroup.m_TexCoordTrans = new Vector2(0.0f, 0.0f);
                        break;

                    case 1:
                    {
                        int   sscale = (int)m_File.Read32(mchunkoffset + 0x0C);
                        int   tscale = (int)m_File.Read32(mchunkoffset + 0x10);
                        short trot   = (short)m_File.Read16(mchunkoffset + 0x14);
                        int   strans = (int)m_File.Read32(mchunkoffset + 0x18);
                        int   ttrans = (int)m_File.Read32(mchunkoffset + 0x1C);

                        matgroup.m_TexCoordScale = new Vector2((float)sscale / 4096.0f, (float)tscale / 4096.0f);
                        matgroup.m_TexCoordRot   = ((float)trot * (float)Math.PI) / 2048.0f;
                        matgroup.m_TexCoordTrans = new Vector2((float)strans / 4096.0f, (float)ttrans / 4096.0f);
                    }
                    break;

                    case 2:
                        goto case 1;

                    case 3:
                        goto case 1;

                    default:
                        break;
                        // throw new Exception(String.Format("BMD: unsupported texture coord transform mode {0}", matgroup.m_TexParams >> 30));
                    }

                    if (texid != 0xFFFFFFFF)
                    {
                        matgroup.m_Texture    = ReadTexture(texid, palid);
                        matgroup.m_TexParams |= matgroup.m_Texture.m_DSTexParam;
                    }
                    else
                    {
                        matgroup.m_Texture = null;
                    }

                    uint pchunkoffset = m_File.Read32((uint)(m_PolyChunksOffset + (polyID * 8) + 4));
                    uint dloffset     = m_File.Read32(pchunkoffset + 0x0C);
                    uint dlsize       = m_File.Read32(pchunkoffset + 0x08);
                    uint numbones     = m_File.Read32(pchunkoffset);
                    uint bonesoffset  = m_File.Read32(pchunkoffset + 0x04);

                    matgroup.m_BoneIDs = new ushort[numbones];
                    for (uint b = 0; b < numbones; b++)
                    {
                        byte idx1 = m_File.Read8(bonesoffset + b);
                        matgroup.m_BoneIDs[b] = m_File.Read16((uint)(m_BoneMapOffset + (2 * idx1)));
                    }

                    matgroup.m_Geometry = new List <VertexList>();

                    m_CurVertex.m_Position = new Vector3(0, 0, 0);
                    m_CurVertex.m_TexCoord = null;
                    m_CurVertex.m_Normal   = null;

                    if ((matgroup.m_PolyAttribs & 0x8000) != 0x8000)
                    {
                        byte alpha = (byte)((matgroup.m_PolyAttribs >> 16) & 0x1F);
                        alpha           |= (byte)(alpha >> 5);
                        matgroup.m_Alpha = alpha;
                    }

                    if ((matgroup.m_DifAmbColors & 0x8000) == 0x8000)
                    {
                        m_CurVertex.m_Color = Color.FromArgb(matgroup.m_Alpha << 3, matgroup.m_DiffuseColor);
                    }
                    else
                    {
                        m_CurVertex.m_Color = Color.Black;
                    }

                    m_CurVertex.m_MatrixID = 0;

                    uint dlend = dloffset + dlsize;
                    for (uint pos = dloffset; pos < dlend;)
                    {
                        byte cmd1 = m_File.Read8(pos++);
                        byte cmd2 = m_File.Read8(pos++);
                        byte cmd3 = m_File.Read8(pos++);
                        byte cmd4 = m_File.Read8(pos++);

                        ProcessGXCommand(matgroup, cmd1, ref pos);
                        ProcessGXCommand(matgroup, cmd2, ref pos);
                        ProcessGXCommand(matgroup, cmd3, ref pos);
                        ProcessGXCommand(matgroup, cmd4, ref pos);
                    }
                }
            }

            foreach (ModelChunk mdchunk in m_ModelChunks)
            {
                foreach (MaterialGroup matgroup in mdchunk.m_MatGroups)
                {
                    matgroup.m_BoneMatrices = new Matrix4[matgroup.m_BoneIDs.Length];
                    for (uint b = 0; b < matgroup.m_BoneIDs.Length; b++)
                    {
                        matgroup.m_BoneMatrices[b] = m_ModelChunks[matgroup.m_BoneIDs[b]].m_Transform;
                    }
                }
            }

            int index = 0;

            foreach (KeyValuePair <string, uint> entry in m_TextureIDs)
            {
                if (!m_Textures.ContainsKey(entry.Key))
                {
                    Console.WriteLine("NOT IN TEXTURES: " + entry.Key);
                    uint palID = Math.Min(m_PaletteIDs.ElementAt(index).Value, (uint)m_PaletteIDs.Count - 1);
                    ReadTexture(entry.Value, palID);
                }
                index++;
            }
        }