public override bool Execute(List<string> args) { if (args.Count != 1) return false; var outDir = args[0]; Directory.CreateDirectory(outDir); Console.WriteLine("Loading resource caches..."); var resourceManager = new ResourceDataManager(); try { resourceManager.LoadCachesFromDirectory(_info.CacheFile.DirectoryName); } catch { Console.WriteLine("Unable to load the resource .dat files."); Console.WriteLine("Make sure that they all exist and are valid."); return true; } var extractor = new BitmapDdsExtractor(resourceManager); var count = 0; using (var tagsStream = _info.OpenCacheRead()) { foreach (var tag in _info.Cache.Tags.FindAllInGroup("bitm")) { Console.Write("Extracting "); TagPrinter.PrintTagShort(tag); try { var tagContext = new TagSerializationContext(tagsStream, _info.Cache, _info.StringIDs, tag); var bitmap = _info.Deserializer.Deserialize<TagDefinitions.Bitmap>(tagContext); var ddsOutDir = outDir; if (bitmap.Images.Count > 1) { ddsOutDir = Path.Combine(outDir, tag.Index.ToString("X8")); Directory.CreateDirectory(ddsOutDir); } for (var i = 0; i < bitmap.Images.Count; i++) { var outPath = Path.Combine(ddsOutDir, ((bitmap.Images.Count > 1) ? i.ToString() : tag.Index.ToString("X8")) + ".dds"); using (var outStream = File.Open(outPath, FileMode.Create, FileAccess.Write)) { extractor.ExtractDds(_info.Deserializer, bitmap, i, outStream); } } count++; } catch (Exception ex) { Console.WriteLine("ERROR: Failed to extract bitmap: " + ex.Message); } } } Console.WriteLine("Extracted {0} bitmaps.", count); return true; }
public override bool Execute(List<string> args) { if (args.Count != 2) return false; var tag = ArgumentParser.ParseTagIndex(Info, args[0]); var imagePath = args[1]; Console.WriteLine("Loading textures.dat..."); var resourceManager = new ResourceDataManager(); resourceManager.LoadCacheFromDirectory(Info.CacheFile.DirectoryName, ResourceLocation.Textures); Console.WriteLine("Importing image..."); var bitmap = new TagDefinitions.Bitmap { Flags = TagDefinitions.Bitmap.RuntimeFlags.UseResource, Sequences = new List<TagDefinitions.Bitmap.Sequence> { new TagDefinitions.Bitmap.Sequence { FirstBitmapIndex = 0, BitmapCount = 1 } }, Images = new List<TagDefinitions.Bitmap.Image> { new TagDefinitions.Bitmap.Image { Signature = new Tag("bitm").Value, Unknown28 = -1, } }, Resources = new List<TagDefinitions.Bitmap.BitmapResource> { new TagDefinitions.Bitmap.BitmapResource() } }; using (var imageStream = File.OpenRead(imagePath)) { var injector = new BitmapDdsInjector(resourceManager); injector.InjectDds(Info.Serializer, Info.Deserializer, bitmap, 0, imageStream); } Console.WriteLine("Creating a new tag..."); using (var tagsStream = Info.OpenCacheReadWrite()) { var tagContext = new TagSerializationContext(tagsStream, Info.Cache, Info.StringIDs, tag); Info.Serializer.Serialize(tagContext, bitmap); } Console.WriteLine(); Console.WriteLine("All done! The new bitmap tag is:"); TagPrinter.PrintTagShort(tag); return true; }
public override bool Execute(List<string> args) { if (args.Count != 2) return false; int imageIndex; if (!int.TryParse(args[0], NumberStyles.HexNumber, null, out imageIndex)) return false; if (imageIndex < 0 || imageIndex >= Bitmap.Images.Count) { Console.Error.WriteLine("Invalid image index."); return true; } var imagePath = args[1]; Console.WriteLine("Loading resource caches..."); var resourceManager = new ResourceDataManager(); try { resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); } catch { Console.WriteLine("Unable to load the resource .dat files."); Console.WriteLine("Make sure that they all exist and are valid."); return true; } Console.WriteLine("Importing image data..."); try { using (var imageStream = File.OpenRead(imagePath)) { var injector = new BitmapDdsInjector(resourceManager); injector.InjectDds(Info.Serializer, Info.Deserializer, Bitmap, imageIndex, imageStream); } using (var tagsStream = Info.OpenCacheReadWrite()) { var tagContext = new TagSerializationContext(tagsStream, Info.Cache, Info.StringIDs, Tag); Info.Serializer.Serialize(tagContext, Bitmap); } } catch (Exception ex) { Console.WriteLine("Importing image data failed: " + ex.Message); return true; } Console.WriteLine("Done!"); return true; }
public override bool Execute(List<string> args) { if (args.Count != 0) return false; // Load resource caches Console.WriteLine("Loading resource caches..."); var resourceManager = new ResourceDataManager(); try { resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); } catch { Console.WriteLine("Unable to load the resource .dat files."); Console.WriteLine("Make sure that they all exist and are valid."); return true; } if (BSP.CollisionBSPResource == null) { Console.WriteLine("Collision BSP does not have a resource associated with it."); return true; } // Deserialize the collision resource definition var resourceContext = new ResourceSerializationContext(BSP.CollisionBSPResource); var definition = Info.Deserializer.Deserialize<CollisionBSPResourceDefinition>(resourceContext); // Reserialize the collision resource definition Info.Serializer.Serialize(resourceContext, definition); // Reserialize the tag definition using (var cacheStream = Info.OpenCacheReadWrite()) { var tagContext = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, Tag); Info.Serializer.Serialize(tagContext, BSP); } return true; }
public BitmapDdsInjector(ResourceDataManager resourceManager) { _resourceManager = resourceManager; }
public override bool Execute(List<string> args) { if (args.Count != 1) return false; var destDir = new DirectoryInfo(args[0]); if (!destDir.Exists) { Write("Destination directory does not exist. Create it? [y/n] "); var answer = ReadLine().ToLower(); if (answer.Length == 0 || !(answer.StartsWith("y") || answer.StartsWith("n"))) return false; if (answer.StartsWith("y")) destDir.Create(); else return false; } WriteLine($"Generating cache files in \"{destDir.FullName}\"..."); var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat")); Write($"Generating {destTagsFile.FullName}..."); using (var tagCacheStream = destTagsFile.Create()) using (var writer = new BinaryWriter(tagCacheStream)) { writer.Write((int)0); // padding writer.Write((int)0); // tag list offset writer.Write((int)0); // tag count writer.Write((int)0); // padding writer.Write((long)130713360239499012); // timestamp writer.Write((long)0); // padding } WriteLine("done."); var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat")); Write($"Generating {destStringIDsFile.FullName}..."); using (var stringIDCacheStream = destStringIDsFile.Create()) using (var writer = new BinaryWriter(stringIDCacheStream)) { writer.Write((int)0); // string count writer.Write((int)0); // data size } WriteLine("done."); var resourceCachePaths = new string[] { Combine(destDir.FullName, "audio.dat"), Combine(destDir.FullName, "resources.dat"), Combine(destDir.FullName, "textures.dat"), Combine(destDir.FullName, "textures_b.dat"), Combine(destDir.FullName, "video.dat") }; foreach (var resourceCachePath in resourceCachePaths) { Write($"Generating {resourceCachePath}..."); using (var resourceCacheStream = File.Create(resourceCachePath)) using (var writer = new BinaryWriter(resourceCacheStream)) { writer.Write((int)0); // padding writer.Write((int)0); // table offset writer.Write((int)0); // resource count writer.Write((int)0); // padding } WriteLine("done."); } var dependencies = new Dictionary<int, TagInstance>(); LoadTagDependencies(0, ref dependencies); LoadTagDependencies(0x16, ref dependencies); LoadTagDependencies(0x27D7, ref dependencies); var destResourcesFile = new FileInfo(Combine(destDir.FullName, "resources.dat")); if (!destResourcesFile.Exists) { WriteLine($"Destination resource cache file does not exist: {destResourcesFile.FullName}"); return false; } var destTexturesFile = new FileInfo(Combine(destDir.FullName, "textures.dat")); if (!destTexturesFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesFile.FullName}"); return false; } var destTexturesBFile = new FileInfo(Combine(destDir.FullName, "textures_b.dat")); if (!destTexturesBFile.Exists) { WriteLine($"Destination texture cache file does not exist: {destTexturesBFile.FullName}"); return false; } var destAudioFile = new FileInfo(Combine(destDir.FullName, "audio.dat")); if (!destAudioFile.Exists) { WriteLine($"Destination audio cache file does not exist: {destAudioFile.FullName}"); return false; } TagCache destTagCache; using (var stream = destTagsFile.OpenRead()) destTagCache = new TagCache(stream); DefinitionSet guessedVersion; var destVersion = Detect(destTagCache, out guessedVersion); if (destVersion == Unknown) { WriteLine($"Unrecognized target version! (guessed {GetVersionString(guessedVersion)})"); return true; } WriteLine($"Destination cache version: {GetVersionString(destVersion)}"); StringIDCache destStringIDCache; using (var stream = destStringIDsFile.OpenRead()) destStringIDCache = new StringIDCache(stream, Create(destVersion)); var destResources = new ResourceDataManager(); destResources.LoadCachesFromDirectory(destDir.FullName); var srcResources = new ResourceDataManager(); srcResources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); var destSerializer = new TagSerializer(destVersion); var destDeserializer = new TagDeserializer(destVersion); var destInfo = new OpenTagCache { Cache = destTagCache, CacheFile = destTagsFile, StringIDs = destStringIDCache, StringIDsFile = destStringIDsFile, Version = destVersion, Serializer = destSerializer, Deserializer = destDeserializer }; using (Stream srcStream = Info.OpenCacheRead(), destStream = destInfo.OpenCacheReadWrite()) { var maxDependency = dependencies.Keys.Max(); for (var i = 0; i <= maxDependency; i++) { var srcTag = Info.Cache.Tags[i]; if (srcTag == null) { destInfo.Cache.AllocateTag(); continue; } var srcData = Info.Cache.ExtractTagRaw(srcStream, srcTag); var destTag = destInfo.Cache.AllocateTag(srcTag.Group); destInfo.Cache.SetTagDataRaw(destStream, destTag, srcData); srcData = new byte[0]; } } WriteLine($"Done generating cache files in \"{destDir.FullName}\"."); return true; }
public override bool Execute(List<string> args) { if (args.Count != 1) return false; // // Verify and load the blam shader // var shaderName = args[0]; CacheBase.IndexItem item = null; Console.WriteLine("Verifying blam shader tag..."); foreach (var tag in BlamCache.IndexItems) { if ((tag.ParentClass == "rm") && tag.Filename == shaderName) { item = tag; break; } } if (item == null) { Console.WriteLine("Blam shader tag does not exist: " + shaderName); return false; } var renderMethod = DefinitionsManager.rmsh(BlamCache, item); var templateItem = BlamCache.IndexItems.Find(i => i.ID == renderMethod.Properties[0].TemplateTagID); var template = DefinitionsManager.rmt2(BlamCache, templateItem); // // Determine the blam shader's base bitmap // var bitmapIndex = -1; var bitmapArgName = ""; for (var i = 0; i < template.UsageBlocks.Count; i++) { var entry = template.UsageBlocks[i]; if (entry.Usage.StartsWith("base_map") || entry.Usage.StartsWith("diffuse_map") || entry.Usage == "foam_texture") { bitmapIndex = i; bitmapArgName = entry.Usage; break; } } // // Load and decode the blam shader's base bitmap // var bitmItem = BlamCache.IndexItems.Find(i => i.ID == renderMethod.Properties[0].ShaderMaps[bitmapIndex].BitmapTagID); var bitm = DefinitionsManager.bitm(BlamCache, bitmItem); var submap = bitm.Bitmaps[0]; byte[] raw; if (BlamCache.Version <= DefinitionSet.Halo2Vista) raw = BlamCache.GetRawFromID(submap.PixelsOffset, submap.RawSize); else { if (bitm.RawChunkBs.Count > 0) { int rawID = bitm.RawChunkBs[submap.InterleavedIndex].RawID; byte[] buffer = BlamCache.GetRawFromID(rawID); raw = new byte[submap.RawSize]; Array.Copy(buffer, submap.Index2 * submap.RawSize, raw, 0, submap.RawSize); } else { int rawID = bitm.RawChunkAs[0].RawID; raw = BlamCache.GetRawFromID(rawID, submap.RawSize); } } var vHeight = submap.VirtualHeight; var vWidth = submap.VirtualWidth; var ms = new MemoryStream(); var bw = new BinaryWriter(ms); if (submap.Flags.Values[3]) raw = DXTDecoder.ConvertToLinearTexture(raw, vWidth, vHeight, submap.Format); if (submap.Format != BitmapFormat.A8R8G8B8) for (int i = 0; i < raw.Length; i += 2) Array.Reverse(raw, i, 2); else for (int i = 0; i < (raw.Length); i += 4) Array.Reverse(raw, i, 4); new DDS(submap).Write(bw); bw.Write(raw); raw = ms.ToArray(); bw.Close(); bw.Dispose(); // // ElDorado Serialization // using (var cacheStream = Info.CacheFile.Open(FileMode.Open, FileAccess.ReadWrite)) { // // Create the new eldorado bitmap // var resourceManager = new ResourceDataManager(); resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); var newBitm = Info.Cache.DuplicateTag(cacheStream, Info.Cache.Tags[0x101F]); var bitmap = new TagDefinitions.Bitmap { Flags = TagDefinitions.Bitmap.RuntimeFlags.UseResource, Sequences = new List<TagDefinitions.Bitmap.Sequence> { new TagDefinitions.Bitmap.Sequence { Name = "", FirstBitmapIndex = 0, BitmapCount = 1 } }, Images = new List<TagDefinitions.Bitmap.Image> { new TagDefinitions.Bitmap.Image { Signature = new Tag("bitm").Value, Unknown28 = -1 } }, Resources = new List<TagDefinitions.Bitmap.BitmapResource> { new TagDefinitions.Bitmap.BitmapResource() } }; using (var imageStream = new MemoryStream(raw)) { var injector = new BitmapDdsInjector(resourceManager); imageStream.Seek(0, SeekOrigin.Begin); injector.InjectDds(Info.Serializer, Info.Deserializer, bitmap, 0, imageStream); } var context = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, newBitm); Info.Serializer.Serialize(context, bitmap); // // Create the new eldorado shader // var newRmsh = Info.Cache.DuplicateTag(cacheStream, Info.Cache.Tags[0x331A]); context = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, newRmsh); var shader = Info.Deserializer.Deserialize<TagDefinitions.Shader>(context); shader.ShaderProperties[0].ShaderMaps[0].Bitmap = newBitm; Info.Serializer.Serialize(context, shader); Console.WriteLine("Done! New shader tag is 0x" + newRmsh.Index.ToString("X8")); } return true; }
public override bool Execute(List<string> args) { if (args.Count < 1 || args.Count > 3) return false; ResourceLocation location = ResourceLocation.Resources; TagInstance destination = Info.Cache.Tags[0x3317]; if (args.Count == 3) { var value = args[0]; switch (value) { case "resources": location = ResourceLocation.Resources; break; case "textures": location = ResourceLocation.Textures; break; case "textures_b": location = ResourceLocation.TexturesB; break; case "audio": location = ResourceLocation.Audio; break; case "video": location = ResourceLocation.Video; break; case "render_models": location = ResourceLocation.RenderModels; break; case "lightmaps": location = ResourceLocation.Lightmaps; break; default: Console.WriteLine("Invalid resource location: " + value); return false; } args.RemoveAt(0); } if (args.Count == 2) { destination = ArgumentParser.ParseTagIndex(Info, args[0]); if (!destination.IsInGroup("mode")) { Console.WriteLine("Specified tag is not a render_model: " + args[0]); return false; } args.RemoveAt(0); } var builder = new RenderModelBuilder(Info.Version); // Add a root node var node = builder.AddNode(new RenderModel.Node { Name = Info.StringIDs.GetStringID("street_cone"), ParentNode = -1, FirstChildNode = -1, NextSiblingNode = -1, DefaultRotation = new Vector4(0, 0, 0, -1), DefaultScale = 1, InverseForward = new Vector3(1, 0, 0), InverseLeft = new Vector3(0, 1, 0), InverseUp = new Vector3(0, 0, 1), }); // Begin building the default region and permutation builder.BeginRegion(Info.StringIDs.GetStringID("default")); builder.BeginPermutation(Info.StringIDs.GetStringID("default")); using (var importer = new AssimpContext()) { Scene model; using (var logStream = new LogStream((msg, userData) => Console.WriteLine(msg))) { logStream.Attach(); model = importer.ImportFile(args[0], PostProcessSteps.CalculateTangentSpace | PostProcessSteps.GenerateNormals | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.PreTransformVertices | PostProcessSteps.Triangulate); logStream.Detach(); } Console.WriteLine("Assembling vertices..."); // Build a multipart mesh from the model data, // with each model mesh mapping to a part of one large mesh and having its own material builder.BeginMesh(); ushort partStartVertex = 0; ushort partStartIndex = 0; var vertices = new List<RigidVertex>(); var indices = new List<ushort>(); foreach (var mesh in model.Meshes) { for (var i = 0; i < mesh.VertexCount; i++) { var position = mesh.Vertices[i]; var normal = mesh.Normals[i]; var uv = mesh.TextureCoordinateChannels[0][i]; var tangent = mesh.Tangents[i]; var bitangent = mesh.BiTangents[i]; vertices.Add(new RigidVertex { Position = new Vector4(position.X, position.Y, position.Z, 1), Normal = new Vector3(normal.X, normal.Y, normal.Z), Texcoord = new Vector2(uv.X, uv.Y), Tangent = new Vector4(tangent.X, tangent.Y, tangent.Z, 1), Binormal = new Vector3(bitangent.X, bitangent.Y, bitangent.Z), }); } // Build the index buffer var meshIndices = mesh.GetIndices(); indices.AddRange(meshIndices.Select(i => (ushort)(i + partStartVertex))); // Define a material and part for this mesh var material = builder.AddMaterial(new RenderMaterial { RenderMethod = Info.Cache.Tags[0x101F], }); builder.BeginPart(material, partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); builder.DefineSubPart(partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); builder.EndPart(); // Move to the next part partStartVertex += (ushort)mesh.VertexCount; partStartIndex += (ushort)meshIndices.Length; } // Bind the vertex and index buffers builder.BindRigidVertexBuffer(vertices, node); builder.BindIndexBuffer(indices, PrimitiveType.TriangleList); builder.EndMesh(); } builder.EndPermutation(); builder.EndRegion(); Console.WriteLine("Building Blam mesh data..."); var resourceStream = new MemoryStream(); var renderModel = builder.Build(Info.Serializer, resourceStream); Console.WriteLine("Writing resource data..."); // Add a new resource for the model data var resources = new ResourceDataManager(); resources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); resourceStream.Position = 0; resources.Add(renderModel.Geometry.Resource, location, resourceStream); Console.WriteLine("Writing tag data..."); using (var cacheStream = Info.OpenCacheReadWrite()) { var tag = destination; var context = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, tag); Info.Serializer.Serialize(context, renderModel); } Console.WriteLine("Model imported successfully!"); return true; }
public BitmapDdsExtractor(ResourceDataManager resourceManager) { _resourceManager = resourceManager; }
public override bool Execute(List<string> args) { if (args.Count != 3) return false; var variantName = args[0]; var fileType = args[1]; var fileName = args[2]; if (fileType != "obj" && fileType != "amf") return false; // Find the variant to extract if (Definition.RenderModel == null) { Console.WriteLine("The model does not have a render model associated with it."); return true; } var variant = Definition.Variants.FirstOrDefault(v => (Info.StringIDs.GetString(v.Name) ?? v.Name.ToString()) == variantName); if (variant == null && Definition.Variants.Count > 0) { Console.WriteLine("Unable to find variant \"{0}\"", variantName); Console.WriteLine("Use \"listvariants\" to list available variants."); return true; } if (fileType == "amf") return ExtractAMF(variant, fileName); // Load resource caches Console.WriteLine("Loading resource caches..."); var resourceManager = new ResourceDataManager(); try { resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); } catch { Console.WriteLine("Unable to load the resource .dat files."); Console.WriteLine("Make sure that they all exist and are valid."); return true; } // Deserialize the render model tag Console.WriteLine("Reading model data..."); RenderModel renderModel; using (var cacheStream = Info.CacheFile.OpenRead()) { var renderModelContext = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, Definition.RenderModel); renderModel = Info.Deserializer.Deserialize<RenderModel>(renderModelContext); } if (renderModel.Geometry.Resource == null) { Console.WriteLine("Render model does not have a resource associated with it"); return true; } // Deserialize the resource definition var resourceContext = new ResourceSerializationContext(renderModel.Geometry.Resource); var definition = Info.Deserializer.Deserialize<RenderGeometryResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // Extract the resource data resourceManager.Extract(renderModel.Geometry.Resource, resourceStream); Directory.CreateDirectory(Path.GetDirectoryName(fileName)); using (var objFile = new StreamWriter(File.Open(fileName, FileMode.Create, FileAccess.Write))) { var objExtractor = new ObjExtractor(objFile); var vertexCompressor = new VertexCompressor(renderModel.Geometry.Compression[0]); // Create a (de)compressor from the first compression block if (variant != null) { // Extract each region in the variant foreach (var region in variant.Regions) { // Get the corresonding region in the render model tag if (region.RenderModelRegionIndex >= renderModel.Regions.Count) continue; var renderModelRegion = renderModel.Regions[region.RenderModelRegionIndex]; // Get the corresponding permutation in the render model tag // (Just extract the first permutation for now) if (region.Permutations.Count == 0) continue; var permutation = region.Permutations[0]; if (permutation.RenderModelPermutationIndex < 0 || permutation.RenderModelPermutationIndex >= renderModelRegion.Permutations.Count) continue; var renderModelPermutation = renderModelRegion.Permutations[permutation.RenderModelPermutationIndex]; // Extract each mesh in the permutation var meshIndex = renderModelPermutation.MeshIndex; var meshCount = renderModelPermutation.MeshCount; var regionName = Info.StringIDs.GetString(region.Name) ?? region.Name.ToString(); var permutationName = Info.StringIDs.GetString(permutation.Name) ?? permutation.Name.ToString(); Console.WriteLine("Extracting {0} mesh(es) for {1}:{2}...", meshCount, regionName, permutationName); for (var i = 0; i < meshCount; i++) { // Create a MeshReader for the mesh and pass it to the obj extractor var meshReader = new MeshReader(Info.Version, renderModel.Geometry.Meshes[meshIndex + i], definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } } } else { // No variant - just extract every mesh Console.WriteLine("Extracting {0} mesh(es)...", renderModel.Geometry.Meshes.Count); foreach (var mesh in renderModel.Geometry.Meshes) { // Create a MeshReader for the mesh and pass it to the obj extractor var meshReader = new MeshReader(Info.Version, mesh, definition); objExtractor.ExtractMesh(meshReader, vertexCompressor, resourceStream); } } objExtractor.Finish(); } } Console.WriteLine("Done!"); return true; }
private bool ExtractAMF(Model.Variant variant, string fileName) { // Load resource caches Console.WriteLine("Loading resource caches..."); var resourceManager = new ResourceDataManager(); try { resourceManager.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); } catch { Console.WriteLine("Unable to load the resource .dat files."); Console.WriteLine("Make sure that they all exist and are valid."); return true; } // Deserialize the render model tag Console.WriteLine("Reading model data..."); RenderModel renderModel; using (var cacheStream = Info.CacheFile.OpenRead()) { var renderModelContext = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, Definition.RenderModel); renderModel = Info.Deserializer.Deserialize<RenderModel>(renderModelContext); } if (renderModel.Geometry.Resource == null) { Console.WriteLine("Render model does not have a resource associated with it"); return true; } // Deserialize the resource definition var resourceContext = new ResourceSerializationContext(renderModel.Geometry.Resource); var definition = Info.Deserializer.Deserialize<RenderGeometryResourceDefinition>(resourceContext); using (var resourceStream = new MemoryStream()) { // Extract the resource data resourceManager.Extract(renderModel.Geometry.Resource, resourceStream); var regionMeshes = new Dictionary<string, Mesh>(); foreach (var region in variant.Regions) { regionMeshes[Info.StringIDs.GetString(region.Name)] = renderModel.Geometry.Meshes[region.RenderModelRegionIndex]; } var headerAddressList = new List<int>(); var headerValueList = new List<int>(); var markerAddressList = new List<int>(); var markerValueList = new List<int>(); var permAddressList = new List<int>(); var permValueList = new List<int>(); var vertAddressList = new List<int>(); var indxAddressList = new List<int>(); var meshAddressList = new List<int>(); using (var bw = new BinaryWriter(File.Create(fileName))) { #region Header bw.Write("AMF!".ToCharArray()); bw.Write(2.0f); //format version bw.Write((Info.StringIDs.GetString(renderModel.Name) + "\0").ToCharArray()); bw.Write(renderModel.Nodes.Count); headerAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); bw.Write(renderModel.MarkerGroups.Count); headerAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); bw.Write(regionMeshes.Count); headerAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); bw.Write(renderModel.Materials.Count); headerAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); #endregion #region Nodes headerValueList.Add((int)bw.BaseStream.Position); foreach (var node in renderModel.Nodes) { bw.Write((Info.StringIDs.GetString(node.Name) + "\0").ToCharArray()); bw.Write((short)node.ParentNode); bw.Write((short)node.FirstChildNode); bw.Write((short)node.NextSiblingNode); bw.Write(node.DefaultTranslation.X * 100); bw.Write(node.DefaultTranslation.Y * 100); bw.Write(node.DefaultTranslation.Z * 100); bw.Write(node.DefaultRotation.X); bw.Write(node.DefaultRotation.Y); bw.Write(node.DefaultRotation.Z); bw.Write(node.DefaultRotation.W); } #endregion #region Marker Groups headerValueList.Add((int)bw.BaseStream.Position); foreach (var group in renderModel.MarkerGroups) { bw.Write((Info.StringIDs.GetString(group.Name) + "\0").ToCharArray()); bw.Write(group.Markers.Count); markerAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); } #endregion #region Markers foreach (var group in renderModel.MarkerGroups) { markerValueList.Add((int)bw.BaseStream.Position); foreach (var marker in group.Markers) { bw.Write((byte)marker.RegionIndex); bw.Write((byte)marker.PermutationIndex); bw.Write((short)marker.NodeIndex); bw.Write(marker.Translation.X * 100); bw.Write(marker.Translation.Y * 100); bw.Write(marker.Translation.Z * 100); bw.Write(marker.Rotation.X); bw.Write(marker.Rotation.Y); bw.Write(marker.Rotation.Z); bw.Write(marker.Rotation.W); } } #endregion #region Regions headerValueList.Add((int)bw.BaseStream.Position); foreach (var region in renderModel.Regions) { bw.Write((Info.StringIDs.GetString(region.Name) + "\0").ToCharArray()); bw.Write(regionMeshes.Count); permAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); } #endregion #region Permutations foreach (var part in regionMeshes) { permValueList.Add((int)bw.BaseStream.Position); bw.Write((Info.StringIDs.GetString(variant.Name) + "\0").ToCharArray()); if (part.Value.Type == VertexType.Rigid) bw.Write((byte)1); else if (part.Value.Type == VertexType.Skinned) bw.Write((byte)2); else throw new NotImplementedException(); bw.Write((byte)part.Value.RigidNodeIndex); bw.Write(definition.VertexBuffers[part.Value.VertexBuffers[0]].Definition.Count); vertAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); int count = 0; foreach (var submesh in part.Value.SubParts) count += submesh.IndexCount; bw.Write(count); indxAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); bw.Write(part.Value.SubParts.Count); meshAddressList.Add((int)bw.BaseStream.Position); bw.Write(0); bw.Write(float.NaN); //no transforms (render_models are pre-transformed) } #endregion } } return true; }