public void ExtractDds(TagDeserializer deserializer, Bitmap bitmap, int imageIndex, Stream outStream) { // TODO: Make sure 3D textures and cube maps work // Deserialize the resource definition and verify it var resource = bitmap.Resources[imageIndex]; var resourceContext = new ResourceSerializationContext(resource.Resource); var definition = deserializer.Deserialize <BitmapTextureResourceDefinition>(resourceContext); if (definition.Texture == null || definition.Texture.Definition == null) { throw new ArgumentException("Invalid bitmap definition"); } var dataReference = definition.Texture.Definition.Data; if (dataReference.Address.Type != ResourceAddressType.Resource) { throw new InvalidOperationException("Invalid resource data address"); } var header = CreateDdsHeader(definition); var resourceDataStream = new MemoryStream(); _resourceManager.Extract(resource.Resource, resourceDataStream); header.WriteTo(outStream); resourceDataStream.Position = dataReference.Address.Offset; StreamUtil.Copy(resourceDataStream, outStream, dataReference.Size); }
public static GeometryReference ConvertGeometry(GeometryReference geometry, OpenTagCache srcInfo, ResourceDataManager srcResources, OpenTagCache destInfo, ResourceDataManager destResources) { if (geometry == null || geometry.Resource == null || geometry.Resource.Index < 0) { return(geometry); } // The format changed starting with version 1.235640, so if both versions are on the same side then they can be converted normally var srcCompare = Definition.Compare(srcInfo.Version, DefinitionSet.HaloOnline235640); var destCompare = Definition.Compare(destInfo.Version, DefinitionSet.HaloOnline235640); if ((srcCompare < 0 && destCompare < 0) || (srcCompare >= 0 && destCompare >= 0)) { geometry.Resource = ConvertResource(geometry.Resource, srcInfo, srcResources, destInfo, destResources); return(geometry); } Console.WriteLine("- Rebuilding geometry resource {0} in {1}...", geometry.Resource.Index, geometry.Resource.GetLocation()); using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream()) { // First extract the model data srcResources.Extract(geometry.Resource, inStream); // Now open source and destination vertex streams inStream.Position = 0; var inVertexStream = VertexStreamFactory.Create(srcInfo.Version, inStream); var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream); // Deserialize the definition data var resourceContext = new ResourceSerializationContext(geometry.Resource); var definition = srcInfo.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext); // Convert each vertex buffer foreach (var buffer in definition.VertexBuffers) { ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream); } // Copy each index buffer over foreach (var buffer in definition.IndexBuffers) { if (buffer.Definition.Data.Size == 0) { continue; } inStream.Position = buffer.Definition.Data.Address.Offset; buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position); var bufferData = new byte[buffer.Definition.Data.Size]; inStream.Read(bufferData, 0, bufferData.Length); outStream.Write(bufferData, 0, bufferData.Length); StreamUtil.Align(outStream, 4); } // Update the definition data destInfo.Serializer.Serialize(resourceContext, definition); // Now inject the new resource data var newLocation = FixResourceLocation(geometry.Resource.GetLocation(), srcInfo.Version, destInfo.Version); outStream.Position = 0; destResources.Add(geometry.Resource, newLocation, outStream); } return(geometry); }
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") { 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); } // 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); 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); }
public override bool Execute(List <string> args) { if (args.Count != 2) { return(false); } var destDir = new DirectoryInfo(args[1]); if (!destDir.Exists) { WriteLine($"Destination cache directory does not exist: {destDir.FullName}"); return(false); } var destTagsFile = new FileInfo(Combine(destDir.FullName, "tags.dat")); if (!destTagsFile.Exists) { WriteLine($"Destination tag cache file does not exist: {destTagsFile.FullName}"); return(false); } var destStringIDsFile = new FileInfo(Combine(destDir.FullName, "string_ids.dat")); if (!destStringIDsFile.Exists) { WriteLine($"Destination string id cache file does not exist: {destStringIDsFile.FullName}"); return(false); } 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 }; var destTag = ParseTagIndex(destInfo, args[0]); if (destTag == null || !destTag.IsInGroup(new Tag("mode"))) { WriteLine("Destination tag must be of group 'mode'."); return(false); } RenderModel destDefinition; using (var destStream = destInfo.OpenCacheRead()) { var context = new TagSerializationContext(destStream, destInfo.Cache, destInfo.StringIDs, destTag); destDefinition = destInfo.Deserializer.Deserialize <RenderModel>(context); } using (MemoryStream inStream = new MemoryStream(), outStream = new MemoryStream()) { // First extract the model data srcResources.Extract(Definition.Geometry.Resource, inStream); // Now open source and destination vertex streams inStream.Position = 0; var inVertexStream = VertexStreamFactory.Create(Info.Version, inStream); var outVertexStream = VertexStreamFactory.Create(destInfo.Version, outStream); // Deserialize the definition data var resourceContext = new ResourceSerializationContext(Definition.Geometry.Resource); var definition = Info.Deserializer.Deserialize <RenderGeometryResourceDefinition>(resourceContext); // Convert each vertex buffer foreach (var buffer in definition.VertexBuffers) { TagConverter.ConvertVertexBuffer(buffer.Definition, inStream, inVertexStream, outStream, outVertexStream); } // Copy each index buffer over foreach (var buffer in definition.IndexBuffers) { if (buffer.Definition.Data.Size == 0) { continue; } inStream.Position = buffer.Definition.Data.Address.Offset; buffer.Definition.Data.Address = new ResourceAddress(ResourceAddressType.Resource, (int)outStream.Position); var bufferData = new byte[buffer.Definition.Data.Size]; inStream.Read(bufferData, 0, bufferData.Length); outStream.Write(bufferData, 0, bufferData.Length); StreamUtil.Align(outStream, 4); } destInfo.Serializer.Serialize(resourceContext, definition); outStream.Position = 0; destResources.Replace(destDefinition.Geometry.Resource, outStream); } return(true); }
/// <summary> /// Creates a CollisionModel bsp from a Scenario StructureBsp. /// This does not work for sbsps with > 65536 planes, which use a /// larger encoding for their bsp related structs differing from the /// struct used in common with collision model bsps. /// </summary> /// <returns></returns> public static BSP fromSbsp(ScenarioStructureBsp sbsp, OpenTagCache info) { // Need to work out how to do that class attribute enumeration thing // so all of this folds down to < 10 lines BSP bsp = fromSbspInit(sbsp); var resource = sbsp.Resource3; 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."); } //Create a binary reader for the resource Stream stream = new MemoryStream(); resourceManager.Extract(sbsp.Resource3, stream); BinaryReader reader = new BinaryReader(stream); reader.BaseStream.Position = 0; Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position); for (int i = 0; i < bsp.Bsp3dNodes.Count; ++i) { BSP.Bsp3dNode node = new BSP.Bsp3dNode(); node.Plane = reader.ReadInt16(); node.BackChildLower = reader.ReadByte(); node.BackChildMid = reader.ReadByte(); node.BackChildUpper = reader.ReadByte(); node.FrontChildLower = reader.ReadByte(); node.FrontChildMid = reader.ReadByte(); node.FrontChildUpper = reader.ReadByte(); bsp.Bsp3dNodes[i] = node; } //Align to the next multiple of 16 reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf); Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position); for (int i = 0; i < bsp.Planes.Count; ++i) { BSP.Plane plane = new BSP.Plane(); plane.PlaneI = reader.ReadSingle(); plane.PlaneJ = reader.ReadSingle(); plane.PlaneK = reader.ReadSingle(); plane.PlaneD = reader.ReadSingle(); bsp.Planes[i] = plane; } //Put here for consistency reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf); Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position); for (int i = 0; i < bsp.Leaves.Count; ++i) { BSP.Leaf leaf = new BSP.Leaf(); leaf.Flags = reader.ReadInt16(); leaf.Bsp2dReferenceCount = reader.ReadInt16(); leaf.Unknown = reader.ReadInt16(); leaf.FirstBsp2dReference = reader.ReadInt16(); bsp.Leaves[i] = leaf; } //Align to the next multiple of 16 reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf); Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position); for (int i = 0; i < bsp.Bsp2dReferences.Count; ++i) { BSP.Bsp2dReference bsp2dref = new BSP.Bsp2dReference(); bsp2dref.Plane = reader.ReadInt16(); bsp2dref.Bsp2dNode = reader.ReadInt16(); bsp.Bsp2dReferences[i] = bsp2dref; } //Align to the next multiple of 16 reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf); Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position); for (int i = 0; i < bsp.Bsp2dNodes.Count; ++i) { BSP.Bsp2dNode node = new BSP.Bsp2dNode(); node.PlaneI = reader.ReadSingle(); node.PlaneJ = reader.ReadSingle(); node.PlaneD = reader.ReadSingle(); node.LeftChild = reader.ReadInt16(); node.RightChild = reader.ReadInt16(); bsp.Bsp2dNodes[i] = node; } //Put here for consistency reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf); Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position); for (int i = 0; i < bsp.Surfaces.Count; ++i) { BSP.Surface surface = new BSP.Surface(); surface.Plane = reader.ReadUInt16(); surface.FirstEdge = reader.ReadUInt16(); surface.Material = reader.ReadInt16(); surface.Unknown = reader.ReadInt16(); surface.BreakableSurface = reader.ReadInt16(); surface.Unknown2 = reader.ReadInt16(); bsp.Surfaces[i] = surface; } //Align to the next multiple of 16 reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf); Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position); for (int i = 0; i < bsp.Edges.Count; ++i) { BSP.Edge edge = new BSP.Edge(); edge.StartVertex = reader.ReadUInt16(); edge.EndVertex = reader.ReadUInt16(); edge.ForwardEdge = reader.ReadUInt16(); edge.ReverseEdge = reader.ReadUInt16(); edge.LeftSurface = reader.ReadUInt16(); edge.RightSurface = reader.ReadUInt16(); bsp.Edges[i] = edge; } //Align to the next multiple of 16 reader.BaseStream.Position = -((-reader.BaseStream.Position) & ~0xf); Console.WriteLine("Stream position: {0:X8}", reader.BaseStream.Position); for (int i = 0; i < bsp.Vertices.Count; ++i) { BSP.Vertex vert = new BSP.Vertex(); vert.PointX = reader.ReadSingle(); vert.PointY = reader.ReadSingle(); vert.PointZ = reader.ReadSingle(); vert.FirstEdge = reader.ReadInt16(); vert.Unknown = reader.ReadInt16(); bsp.Vertices[i] = vert; } return bsp; }
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); }