public MaterialLoader(string currentFileName, Package currentPackage)
        {
            CurrentPackage = currentPackage;
            CurrentFileName = currentFileName;

            MaxTextureMaxAnisotropy = 0;
        }
        internal void AddObjects(Renderer.Renderer renderer, string path, Package package)
        {
            var data = Resource.Blocks[BlockType.DATA] as NTRO;

            // Output is World_t we need to iterate m_worldNodes inside it.
            var worldNodes = (NTROArray)data.Output["m_worldNodes"];
            if (worldNodes.Count > 0)
            {
                var nodeData = ((NTROValue<NTROStruct>)worldNodes[0]).Value; //TODO: Not be 0.

                var worldNode = ((NTROValue<string>)nodeData["m_worldNodePrefix"]).Value;
                if (worldNode != null)
                {
                    var newResource = FileExtensions.LoadFileByAnyMeansNecessary(worldNode + ".vwnod_c", path, package);
                    if (newResource == null)
                    {
                        Console.WriteLine("unable to load model " + worldNode + ".vwnod_c");
                        throw new Exception("WTF");
                    }

                    var node = new WorldNode(newResource);
                    node.AddMeshes(renderer, path, package);
                }
            }

            var entityLumps = (NTROArray)data.Output["m_entityLumps"];
            foreach (var lump in entityLumps)
            {
                LoadEntities(lump, renderer, path, package);
            }
        }
        public void LoadMeshes(Renderer.Renderer renderer, string path, Matrix4 transform, Vector4 tintColor, Package currentPackage = null, string skin = null)
        {
            var data = (NTRO)Resource.Blocks[BlockType.DATA];

            var refMeshes = (NTROArray)data.Output["m_refMeshes"];
            var materialGroups = (NTROArray)data.Output["m_materialGroups"];

            for (var i = 0; i < refMeshes.Count; i++)
            {
                var refMesh = ((NTROValue<ResourceExtRefList.ResourceReferenceInfo>)refMeshes[i]).Value;

                var newResource = FileExtensions.LoadFileByAnyMeansNecessary(refMesh.Name + "_c", path, currentPackage);
                if (newResource == null)
                {
                    Console.WriteLine("unable to load mesh " + refMesh.Name);

                    continue;
                }

                if (!newResource.Blocks.ContainsKey(BlockType.VBIB))
                {
                    Console.WriteLine("Old style model, no VBIB!");

                    continue;
                }

                var skinMaterials = new List<string>();

                if (!string.IsNullOrEmpty(skin))
                {
                    foreach (var materialGroup2 in materialGroups)
                    {
                        var materialGroup = ((NTROValue<NTROStruct>)materialGroup2).Value;

                        if (((NTROValue<string>)materialGroup["m_name"]).Value == skin)
                        {
                            var materials = (NTROArray)materialGroup["m_materials"];

                            foreach (var material in materials)
                            {
                                skinMaterials.Add(((NTROValue<ResourceExtRefList.ResourceReferenceInfo>)material).Value.Name);
                            }

                            break;
                        }
                    }
                }

                renderer.AddMeshObject(new MeshObject
                {
                    Resource = newResource,
                    Transform = transform,
                    TintColor = tintColor,
                    SkinMaterials = skinMaterials
                });

                // TODO: Only first, again.
                break;
            }
        }
        public void ParseVPK()
        {
            var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "VPK", "platform_misc_dir.vpk");

            using (var package = new Package())
            {
                package.Read(path);
            }
        }
        public void CorrectHeaderWrongVersionThrows()
        {
            using (var resource = new Package())
            {
                resource.SetFileName("a.vpk");

                using (var ms = new MemoryStream(new byte[] { 0x34, 0x12, 0xAA, 0x55, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22 }))
                {
                    Assert.Throws<InvalidDataException>(() => resource.Read(ms));
                }
            }
        }
        public void InvalidPackageThrows()
        {
            using (var resource = new Package())
            {
                using (var ms = new MemoryStream(Enumerable.Repeat<byte>(1, 12).ToArray()))
                {
                    // Should yell about not setting file name
                    Assert.Throws<InvalidOperationException>(() => resource.Read(ms));

                    resource.SetFileName("a.vpk");

                    Assert.Throws<InvalidDataException>(() => resource.Read(ms));
                }
            }
        }
        public Renderer(TabControl mainTabs, string fileName, Package currentPackage)
        {
            MeshesToRender = new List<MeshObject>();
            Animations = new List<Animation.Animation>();
            cameras = new List<Tuple<string, Matrix4>>();

            CurrentPackage = currentPackage;
            CurrentFileName = fileName;
            tabs = mainTabs;

            Debug = new DebugUtil();

            Skeleton = new Skeleton(); // Default empty skeleton

            MaterialLoader = new MaterialLoader(CurrentFileName, CurrentPackage);
        }
        private void TestVPKExtraction(string path)
        {
            using (var package = new Package())
            {
                package.Read(path);

                Assert.AreEqual(2, package.Entries.Count);
                Assert.Contains("jpg", package.Entries.Keys);
                Assert.Contains("proto", package.Entries.Keys);

                var flatEntries = new Dictionary<string, PackageEntry>();

                using (var sha1 = new SHA1CryptoServiceProvider())
                {
                    var data = new Dictionary<string, string>();

                    foreach (var a in package.Entries)
                    {
                        foreach (var b in a.Value)
                        {
                            Assert.AreEqual(a.Key, b.TypeName);

                            flatEntries.Add(b.FileName, b);

                            byte[] entry;
                            package.ReadEntry(b, out entry);

                            data.Add(b.FileName + '.' + b.TypeName, BitConverter.ToString(sha1.ComputeHash(entry)).Replace("-", ""));
                        }
                    }

                    Assert.AreEqual(3, data.Count);
                    Assert.AreEqual("E0D865F19F0A4A7EA3753FBFCFC624EE8B46928A", data["kitten.jpg"]);
                    Assert.AreEqual("2EFFCB09BE81E8BEE88CB7BA8C18E87D3E1168DB", data["steammessages_base.proto"]);
                    Assert.AreEqual("22741F66442A4DC880725D2CC019E6C9202FD70C", data["steammessages_clientserver.proto"]);
                }

                Assert.AreEqual(flatEntries["kitten"].TotalLength, 16361);
                Assert.AreEqual(flatEntries["steammessages_base"].TotalLength, 2563);
                Assert.AreEqual(flatEntries["steammessages_clientserver"].TotalLength, 39177);
            }
        }
示例#9
0
        private static void ParseVPK(string path)
        {
            lock (ConsoleWriterLock)
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("--- Listing files in package \"{0}\" ---", path);
                Console.ResetColor();
            }

            var sw = Stopwatch.StartNew();

            var package = new Package();

            try
            {
                package.Read(path);
            }
            catch (Exception e)
            {
                lock (ConsoleWriterLock)
                {
                    Console.ForegroundColor = ConsoleColor.Cyan;
                    Console.WriteLine(e);
                    Console.ResetColor();
                }
            }

            if (Options.OutputFile == null)
            {
                Console.WriteLine("--- Files in package:");

                var orderedEntries = package.Entries.OrderByDescending(x => x.Value.Count).ThenBy(x => x.Key);

                foreach (var entry in orderedEntries)
                {
                    Console.WriteLine("\t{0}: {1} files", entry.Key, entry.Value.Count);
                }
            }
            else
            {
                Console.WriteLine("--- Dumping decompiled files...");

                DumpVPK(package, "vxml_c", "xml");
                DumpVPK(package, "vjs_c", "js");
                DumpVPK(package, "vcss_c", "css");

                DumpVPK(package, "txt", "txt");
                DumpVPK(package, "cfg", "cfg");
                DumpVPK(package, "res", "res");
            }

            if (Options.OutputVPKDir)
            {
                foreach (var type in package.Entries)
                {
                    foreach (var file in type.Value)
                    {
                        Console.WriteLine(file);
                    }
                }
            }

            sw.Stop();

            Console.WriteLine("Processed in {0}ms", sw.ElapsedMilliseconds);
        }
示例#10
0
        private static void DumpVPK(Package package, string type, string newType)
        {
            if (!package.Entries.ContainsKey(type))
            {
                Console.WriteLine("There are no files of type \"{0}\".", type);

                return;
            }

            var entries = package.Entries[type];

            foreach (var file in entries)
            {
                var filePath = string.Format("{0}.{1}", file.FileName, file.TypeName);

                if (!string.IsNullOrWhiteSpace(file.DirectoryName))
                {
                    filePath = Path.Combine(file.DirectoryName, filePath);
                }

                filePath = FixPathSlahes(filePath);

                Console.WriteLine("\t[archive index: {0:D3}] {1}", file.ArchiveIndex, filePath);

                byte[] output;
                package.ReadEntry(file, out output);

                if (type.EndsWith("_c", StringComparison.Ordinal))
                {
                    using (var resource = new Resource())
                    {
                        using (var memory = new MemoryStream(output))
                        {
                            resource.Read(memory);
                        }

                        output = ((Panorama)resource.Blocks[BlockType.DATA]).Data;
                    }
                }

                if (Options.OutputFile != null)
                {
                    if (type != newType)
                    {
                        filePath = Path.ChangeExtension(filePath, newType);
                    }

                    DumpFile(filePath, output);
                }
            }
        }
示例#11
0
        private TabPage ProcessFile(string fileName, byte[] input = null)
        {
            var tab = new TabPage();

            if (fileName.EndsWith(".vpk", StringComparison.Ordinal))
            {
                var package = new Package();
                if (input != null)
                {
                    package.SetFileName(fileName);
                    package.Read(new MemoryStream(input));
                }
                else
                {
                    package.Read(fileName);
                }

                // create a TreeView with search capabilities, register its events, and add it to the tab
                var treeViewWithSearch = new GUI.Controls.TreeViewWithSearchResults(ImageList);
                treeViewWithSearch.Dock = DockStyle.Fill;
                treeViewWithSearch.InitializeTreeViewFromPackage("treeViewVpk", package);
                treeViewWithSearch.TreeNodeMouseDoubleClick += VPK_OpenFile;
                treeViewWithSearch.TreeNodeMouseClick += VPK_OnClick;
                treeViewWithSearch.ListViewItemDoubleClick += VPK_OpenFile;
                treeViewWithSearch.ListViewItemRightClick += VPK_OnClick;
                tab.Controls.Add(treeViewWithSearch);

                // since we're in a separate thread, invoke to update the UI
                this.Invoke((MethodInvoker)delegate
                {
                    findToolStripButton.Enabled = true;
                });
            }
            else
            {
                var resource = new Resource();
                if (input != null)
                {
                    resource.Read(new MemoryStream(input));
                }
                else
                {
                    resource.Read(fileName);
                }

                var resTabs = new TabControl();
                resTabs.Dock = DockStyle.Fill;

                switch (resource.ResourceType)
                {
                    case ResourceType.Texture:
                        var tab2 = new TabPage("TEXTURE");
                        tab2.AutoScroll = true;

                        try
                        {
                            var tex = (Texture)resource.Blocks[BlockType.DATA];

                            var control = new Forms.Texture();
                            control.BackColor = Color.Black;
                            control.SetImage(tex.GenerateBitmap(), Path.GetFileNameWithoutExtension(fileName), tex.Width, tex.Height);

                            tab2.Controls.Add(control);
                        }
                        catch (Exception e)
                        {
                            var control = new TextBox
                            {
                                Dock = DockStyle.Fill,
                                Font = new Font(FontFamily.GenericMonospace, 8),
                                Multiline = true,
                                ReadOnly = true,
                                Text = e.ToString()
                            };

                            tab2.Controls.Add(control);
                        }

                        resTabs.TabPages.Add(tab2);
                        break;
                    case ResourceType.Panorama:
                        if (((Panorama)resource.Blocks[BlockType.DATA]).Names.Count > 0)
                        {
                            var nameTab = new TabPage("PANORAMA NAMES");
                            var nameControl = new DataGridView();
                            nameControl.Dock = DockStyle.Fill;
                            nameControl.AutoSize = true;
                            nameControl.ReadOnly = true;
                            nameControl.AllowUserToAddRows = false;
                            nameControl.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
                            nameControl.DataSource = new BindingSource(new BindingList<Panorama.NameEntry>(((Panorama)resource.Blocks[BlockType.DATA]).Names), null);
                            nameTab.Controls.Add(nameControl);
                            resTabs.TabPages.Add(nameTab);
                        }
                        break;
                }

                foreach (var block in resource.Blocks)
                {
                    if (block.Key == BlockType.RERL)
                    {
                        var externalRefsTab = new TabPage("External Refs");

                        var externalRefs = new DataGridView();
                        externalRefs.Dock = DockStyle.Fill;
                        externalRefs.AutoGenerateColumns = true;
                        externalRefs.AutoSize = true;
                        externalRefs.ReadOnly = true;
                        externalRefs.AllowUserToAddRows = false;
                        externalRefs.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
                        externalRefs.DataSource = new BindingSource(new BindingList<ResourceExtRefList.ResourceReferenceInfo>(resource.ExternalReferences.ResourceRefInfoList), null);

                        externalRefsTab.Controls.Add(externalRefs);

                        resTabs.TabPages.Add(externalRefsTab);

                        continue;
                    }

                    if (block.Key == BlockType.NTRO)
                    {
                        if (((ResourceIntrospectionManifest)block.Value).ReferencedStructs.Count > 0)
                        {
                            var externalRefsTab = new TabPage("Introspection Manifest: Structs");

                            var externalRefs = new DataGridView();
                            externalRefs.Dock = DockStyle.Fill;
                            externalRefs.AutoGenerateColumns = true;
                            externalRefs.AutoSize = true;
                            externalRefs.ReadOnly = true;
                            externalRefs.AllowUserToAddRows = false;
                            externalRefs.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
                            externalRefs.DataSource = new BindingSource(new BindingList<ResourceIntrospectionManifest.ResourceDiskStruct>(((ResourceIntrospectionManifest)block.Value).ReferencedStructs), null);

                            externalRefsTab.Controls.Add(externalRefs);
                            resTabs.TabPages.Add(externalRefsTab);
                        }

                        if (((ResourceIntrospectionManifest)block.Value).ReferencedEnums.Count > 0)
                        {
                            var externalRefsTab = new TabPage("Introspection Manifest: Enums");
                            var externalRefs2 = new DataGridView();
                            externalRefs2.Dock = DockStyle.Fill;
                            externalRefs2.AutoGenerateColumns = true;
                            externalRefs2.AutoSize = true;
                            externalRefs2.ReadOnly = true;
                            externalRefs2.AllowUserToAddRows = false;
                            externalRefs2.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
                            externalRefs2.DataSource = new BindingSource(new BindingList<ResourceIntrospectionManifest.ResourceDiskEnum>(((ResourceIntrospectionManifest)block.Value).ReferencedEnums), null);

                            externalRefsTab.Controls.Add(externalRefs2);
                            resTabs.TabPages.Add(externalRefsTab);
                        }

                        //continue;
                    }

                    var tab2 = new TabPage(block.Key.ToString());
                    var control = new TextBox();
                    control.Font = new Font(FontFamily.GenericMonospace, control.Font.Size);

                    try
                    {
                        control.Text = block.Value.ToString().Replace("\r", ""); //Prevent copy+paste with double new line
                        control.Text = block.Value.ToString().Replace("\n", Environment.NewLine); //make sure panorama is new lines
                    }
                    catch (Exception e)
                    {
                        control.Text = e.ToString();
                    }

                    control.Dock = DockStyle.Fill;
                    control.Multiline = true;
                    control.ReadOnly = true;
                    control.ScrollBars = ScrollBars.Both;
                    tab2.Controls.Add(control);
                    resTabs.TabPages.Add(tab2);
                }

                tab.Controls.Add(resTabs);
            }

            return tab;
        }
        public static Resource LoadFileByAnyMeansNecessary(string file, string currentFullPath, Package currentPackage)
        {
            Resource resource;

            // TODO: Might conflict where same file name is available in different paths
            if (CachedResources.TryGetValue(file, out resource) && resource.Reader != null)
            {
                return resource;
            }

            resource = new Resource();

            var entry = currentPackage?.FindEntry(file);

            if (entry != null)
            {
                byte[] output;
                currentPackage.ReadEntry(entry, out output);
                resource.Read(new MemoryStream(output));
                CachedResources[file] = resource;

                return resource;
            }

            var paths = Settings.GameSearchPaths.ToList();
            var packages = new List<Package>();

            foreach (var searchPath in paths.Where(searchPath => searchPath.EndsWith(".vpk")).ToList())
            {
                paths.Remove(searchPath);

                Package package;
                if (!CachedPackages.TryGetValue(searchPath, out package))
                {
                    Console.WriteLine("Preloading vpk {0}", searchPath);

                    package = new Package();
                    package.Read(searchPath);
                    CachedPackages[searchPath] = package;
                }

                packages.Add(package);
            }

            foreach (var package in packages)
            {
                entry = package?.FindEntry(file);

                if (entry != null)
                {
                    byte[] output;
                    package.ReadEntry(entry, out output);
                    resource.Read(new MemoryStream(output));
                    CachedResources[file] = resource;

                    return resource;
                }
            }

            var path = FindResourcePath(paths, file, currentFullPath);

            if (path == null)
            {
                return null;
            }

            resource.Read(path);
            CachedResources[file] = resource;

            return resource;
        }
        /// <summary>
        /// Initializes the TreeView in the control with the contents of the passed Package. Contents are sorted and expanded by default.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="package"></param>
        internal void InitializeTreeViewFromPackage(string name, Package package)
        {
            mainListView.Tag = package;

            var control = mainTreeView;
            control.Name = name;
            control.Tag = package; //so we can access it later
            control.Dock = DockStyle.Fill;
            control.ImageList = imageList;

            //http://stackoverflow.com/a/24591871

            foreach (var fileType in package.Entries)
            {
                foreach (var file in fileType.Value)
                {
                    control.AddFileNode(file, fileType);
                }
            }

            control.Sort();
            control.ExpandAll();
        }
        internal void AddMeshes(Renderer.Renderer renderer, string path, Package package)
        {
            var data = Resource.Blocks[BlockType.DATA] as NTRO;

            // Output is WorldNode_t we need to iterate m_sceneObjects inside it.

            var sceneObjectLayerIndices = (NTROArray)data.Output["m_sceneObjectLayerIndices"];
            var sceneObjects = (NTROArray)data.Output["m_sceneObjects"];
            var i = 0;
            foreach (var entry in sceneObjects)
            {
                var layerIndice = ((NTROValue<byte>)sceneObjectLayerIndices[i]).Value;
                i++;

                // TODO: We want UI for this
                if (layerIndice == 2 || layerIndice == 4)
                {
                    continue;
                }

                // sceneObject is SceneObject_t
                var sceneObject = ((NTROValue<NTROStruct>)entry).Value;
                var renderableModel = ((NTROValue<ResourceExtRefList.ResourceReferenceInfo>)sceneObject["m_renderableModel"]).Value;
                var transform = (NTROArray)sceneObject["m_vTransform"];

                var matrix = default(Matrix4);

                // what is this
                for (var x = 0; x < 4; x++)
                {
                    var a = ((NTROValue<Vector4>)transform[x]).Value;

                    switch (x)
                    {
                        case 0: matrix.Column0 = new OpenTK.Vector4(a.X, a.Y, a.Z, a.W); break;
                        case 1: matrix.Column1 = new OpenTK.Vector4(a.X, a.Y, a.Z, a.W); break;
                        case 2: matrix.Column2 = new OpenTK.Vector4(a.X, a.Y, a.Z, a.W); break;
                        case 3: matrix.Column3 = new OpenTK.Vector4(a.X, a.Y, a.Z, a.W); break;
                    }
                }

                var tintColorWrongVector = ((NTROValue<Vector4>)sceneObject["m_vTintColor"]).Value;

                OpenTK.Vector4 tintColor;
                if (tintColorWrongVector.W == 0)
                {
                    tintColor = OpenTK.Vector4.One;
                    Console.WriteLine("Ignoring tintColor, it will f**k things up.");
                }
                else
                {
                    tintColor = new OpenTK.Vector4(tintColorWrongVector.X, tintColorWrongVector.Y, tintColorWrongVector.Z, tintColorWrongVector.W);
                }

                if (renderableModel != null)
                {
                    var newResource = FileExtensions.LoadFileByAnyMeansNecessary(renderableModel.Name + "_c", path, package);
                    if (newResource == null)
                    {
                        Console.WriteLine("unable to load model " + renderableModel.Name + "_c");

                        continue;
                    }

                    var modelEntry = new Model(newResource);
                    modelEntry.LoadMeshes(renderer, path, matrix, tintColor, package);
                }

                var renderable = ((NTROValue<ResourceExtRefList.ResourceReferenceInfo>)sceneObject["m_renderable"]).Value;

                if (renderable != null)
                {
                    var newResource = FileExtensions.LoadFileByAnyMeansNecessary(renderable.Name + "_c", path, package);
                    if (newResource == null)
                    {
                        Console.WriteLine("unable to load renderable " + renderable.Name + "_c");

                        continue;
                    }

                    renderer.AddMeshObject(new MeshObject
                    {
                        Resource = newResource,
                        Transform = matrix,
                        TintColor = tintColor
                    });
                }
            }
        }
示例#15
0
        private static void LoadEntities(NTROValue lump, Renderer.Renderer renderer, string path, Package package)
        {
            var reference = ((NTROValue<ResourceExtRefList.ResourceReferenceInfo>)lump).Value;
            if (reference == null)
            {
                return;
            }

            var newResource = FileExtensions.LoadFileByAnyMeansNecessary(reference.Name + "_c", path, package);
            if (newResource == null)
            {
                Console.WriteLine("unable to load entity lump " + reference.Name + "_c");

                return;
            }

            var entityLump = newResource.Blocks[BlockType.DATA] as EntityLump;

            var childLumps = (NTROArray)entityLump.Output["m_childLumps"];

            foreach (var lump2 in childLumps)
            {
                var lol = ((NTROValue<ResourceExtRefList.ResourceReferenceInfo>)lump).Value;

                // TODO: Should be controlled in UI with world layers
                if (lol.Name.Contains("_destruction"))
                {
                    continue;
                }

                LoadEntities(lump2, renderer, path, package);
            }

            foreach (var entity in entityLump.Datas)
            {
                var scale = string.Empty;
                var position = string.Empty;
                var angles = string.Empty;
                var model = string.Empty;
                var skin = string.Empty;
                var colour = new byte[0];
                var classname = string.Empty;
                var name = string.Empty;

                foreach (var property in entity)
                {
                    //metadata
                    switch (property.Item2)
                    {
                        case 3368008710: //World Model
                            model = property.Item3 as string;
                            break;
                        case 3827302934: //Position
                            position = property.Item3 as string;
                            break;
                        case 3130579663: //Angles
                            angles = property.Item3 as string;
                            break;
                        case 432137260: //Scale
                            scale = property.Item3 as string;
                            break;
                        case 2020856412: //Skin
                            skin = property.Item3 as string;
                            break;
                        case 588463423: //Colour
                            colour = property.Item3 as byte[];
                            break;
                        case 3323665506: //Classname
                            classname = property.Item3 as string;
                            break;
                        case 1094168427:
                            name = property.Item3 as string;
                            break;
                    }
                }

                if (scale == string.Empty || position == string.Empty || angles == string.Empty)
                {
                    continue;
                }

                if (classname == "point_camera" || classname == "vr_teleport_marker" || model != string.Empty)
                {
                    var scaleMatrix = Matrix4.CreateScale(ParseCoordinates(scale));
                    var positionMatrix = Matrix4.CreateTranslation(ParseCoordinates(position));

                    var rotationVector = ParseCoordinates(angles);
                    var rotationMatrix = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(rotationVector.Z));
                    rotationMatrix *= Matrix4.CreateRotationY(MathHelper.DegreesToRadians(rotationVector.X));
                    rotationMatrix *= Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(rotationVector.Y));

                    var megaMatrix = scaleMatrix * rotationMatrix * positionMatrix;

                    var objColor = Vector4.One;
                    // Parse colour if present
                    if (colour.Length == 4)
                    {
                        for (var i = 0; i < 4; i++)
                        {
                            objColor[i] = colour[i] / 255.0f;
                        }
                    }

                    //This model is hardcoded into the FGD
                    if (classname == "vr_teleport_marker")
                    {
                        model = "models/effects/teleport/teleport_marker.vmdl";
                    }

                    if (classname == "point_camera")
                    {
                        renderer.AddCamera(name == string.Empty ? $"Camera {anonymousCameraCount++}" : name, megaMatrix);
                    }
                    else
                    {
                        var newEntity = FileExtensions.LoadFileByAnyMeansNecessary(model + "_c", path, package);
                        if (newEntity == null)
                        {
                            Console.WriteLine("unable to load entity " + model + "_c");

                            continue;
                        }

                        var entityModel = new Model(newEntity);
                        entityModel.LoadMeshes(renderer, path, megaMatrix, objColor, package, skin);
                    }
                }
            }
        }
        private static void ParseVPK(string path)
        {
            lock (ConsoleWriterLock)
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("--- Listing files in package \"{0}\" ---", path);
                Console.ResetColor();
            }

            var sw = Stopwatch.StartNew();

            var package = new Package();

            try
            {
                package.Read(path);
            }
            catch (Exception e)
            {
                lock (ConsoleWriterLock)
                {
                    Console.ForegroundColor = ConsoleColor.Cyan;
                    Console.WriteLine(e);
                    Console.ResetColor();
                }
            }

            if (Options.VerifyVPKChecksums)
            {
                try
                {
                    package.VerifyHashes();

                    Console.WriteLine("VPK verification succeeded");
                }
                catch (Exception e)
                {
                    lock (ConsoleWriterLock)
                    {
                        Console.WriteLine("Failed to verify checksums and signature of given VPK:");
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine(e.Message);
                        Console.ResetColor();
                    }
                }

                return;
            }

            if (Options.OutputFile == null)
            {
                Console.WriteLine("--- Files in package:");

                var orderedEntries = package.Entries.OrderByDescending(x => x.Value.Count).ThenBy(x => x.Key);

                if (Options.CollectStats)
                {
                    TotalFiles += orderedEntries
                        .Where(entry => entry.Key.EndsWith("_c", StringComparison.Ordinal))
                        .Sum(x => x.Value.Count);
                }

                foreach (var entry in orderedEntries)
                {
                    Console.WriteLine("\t{0}: {1} files", entry.Key, entry.Value.Count);

                    if (Options.CollectStats && entry.Key.EndsWith("_c", StringComparison.Ordinal))
                    {
                        foreach (var file in entry.Value)
                        {
                            lock (ConsoleWriterLock)
                            {
                                Console.ForegroundColor = ConsoleColor.Green;
                                Console.WriteLine("[{0}/{1}] {2}", ++CurrentFile, TotalFiles, file.GetFullPath());
                                Console.ResetColor();
                            }

                            byte[] output;
                            package.ReadEntry(file, out output);

                            using (var stream = new MemoryStream(output))
                            {
                                ProcessFile(file.GetFullPath(), stream);
                            }
                        }
                    }
                }
            }
            else
            {
                Console.WriteLine("--- Dumping decompiled files...");

                DumpVPK(package, "vxml_c", "xml");
                DumpVPK(package, "vjs_c", "js");
                DumpVPK(package, "vcss_c", "css");
                DumpVPK(package, "vsndevts_c", "vsndevts");
                DumpVPK(package, "vpcf_c", "vpcf");

                DumpVPK(package, "txt", "txt");
                DumpVPK(package, "cfg", "cfg");
                DumpVPK(package, "res", "res");
            }

            if (Options.OutputVPKDir)
            {
                foreach (var type in package.Entries)
                {
                    foreach (var file in type.Value)
                    {
                        Console.WriteLine(file);
                    }
                }
            }

            sw.Stop();

            Console.WriteLine("Processed in {0}ms", sw.ElapsedMilliseconds);
        }