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); } }
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); }
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); } } }
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 }); } } }
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); }