Example #1
0
        public UnknownBlock(ref BinaryReader reader, string type)
        {
            BlockType = type;

            // Switch from big-endian to little-endian
            int dataLength    = BitConverter.ToInt32(Utils.SwapEndian(reader.ReadBytes(4)));
            int subdataLength = BitConverter.ToInt32(Utils.SwapEndian(reader.ReadBytes(4)));

            Unknown0C = BitConverter.ToInt32(Utils.SwapEndian(reader.ReadBytes(4)));

            if (dataLength > 0)
            {
                Data = reader.ReadBytes(dataLength);
                Utils.ReadPadding(ref reader);
            }

            if (subdataLength > 0)
            {
                byte[] subdata = reader.ReadBytes(subdataLength);
                Utils.ReadPadding(ref reader);

                BinaryReader subReader = new BinaryReader(new MemoryStream(subdata));
                Children = SrdFile.ReadBlocks(ref subReader);
                subReader.Close();
            }
        }
Example #2
0
        public VtxBlock(ref BinaryReader reader)
        {
            // Switch from big-endian to little-endian
            int dataLength    = BitConverter.ToInt32(Utils.SwapEndian(reader.ReadBytes(4)));
            int subdataLength = BitConverter.ToInt32(Utils.SwapEndian(reader.ReadBytes(4)));

            Unknown0C = BitConverter.ToInt32(Utils.SwapEndian(reader.ReadBytes(4)));

            // Read and parse data
            if (dataLength > 0)
            {
                byte[] data = reader.ReadBytes(dataLength);
                Utils.ReadPadding(ref reader);

                Unknown10   = reader.ReadInt32();
                Unknown14   = reader.ReadInt32();
                VertexCount = reader.ReadInt32();
                Unknown1C   = reader.ReadInt32();
            }

            if (subdataLength > 0)
            {
                byte[] subdata = reader.ReadBytes(subdataLength);
                Utils.ReadPadding(ref reader);

                BinaryReader subReader = new BinaryReader(new MemoryStream(subdata));
                Children = SrdFile.ReadBlocks(ref subReader);
                subReader.Close();
            }
        }
Example #3
0
        static void Main(string[] args)
        {
            Console.WriteLine("SRD Tool by CaptainSwag101\n" +
                              "Version 0.0.2, built on 2019-08-15\n");

            FileInfo info = new FileInfo(args[0]);

            if (!info.Exists)
            {
                Console.WriteLine("ERROR: \"{0}\" does not exist.", args[0]);
                return;
            }

            if (info.Extension.ToLower() != ".srd")
            {
                Console.WriteLine("ERROR: Input file does not have the \".srd\" extension.");
                return;
            }

            SrdFile srd = new SrdFile();

            srd.Load(args[0]);

            Console.WriteLine("\"{0}\" contains the following blocks:\n", info.FullName);
            PrintBlocks(srd.Blocks);

            Console.WriteLine("Press Enter to close...");
            Console.Read();

            //srd.Save(args[0] + ".test");
        }
        private static Node GetNodeTree(SrdFile srd, List <Material> materials, List <Mesh> meshes)
        {
            var scn          = srd.Blocks.Where(b => b is ScnBlock).First() as ScnBlock;
            var scnResources = scn.Children[0] as RsiBlock;
            var treBlocks    = srd.Blocks.Where(b => b is TreBlock).ToList();

            // Create a manual root node in case the scene has multiple roots
            Node RootNode = new Node("RootNode");

            foreach (TreBlock tre in treBlocks)
            {
                var treResources = tre.Children[0] as RsiBlock;

                var flattenedTreeNodes = tre.RootNode.Flatten().ToList();

                // This is done in two passes, one to generate the list of nodes, second to assign children to parents
                var flattenedNodes = new List <Node>();

                // Generate a list of real nodes in the scene
                foreach (TreeNode treeNode in flattenedTreeNodes)
                {
                    Node n = new Node(treeNode.StringValue);

                    foreach (Mesh mesh in meshes)
                    {
                        if (mesh.Name == n.Name)
                        {
                            n.MeshIndices.Add(meshes.IndexOf(mesh));
                        }
                    }

                    flattenedNodes.Add(n);
                    //nodeNameList.Add(n.Name);
                }
                foreach (TreeNode treeNode in flattenedTreeNodes)
                {
                    int currentNodeIndex = flattenedTreeNodes.IndexOf(treeNode);

                    foreach (TreeNode childTreeNode in treeNode)
                    {
                        int childNodeIndex = flattenedTreeNodes.IndexOf(childTreeNode);
                        flattenedNodes[currentNodeIndex].Children.Add(flattenedNodes[childNodeIndex]);
                    }
                }

                // Add any root nodes to the manual root node
                foreach (string rootNodeName in scn.SceneRootNodes)
                {
                    foreach (Node matchingNode in flattenedNodes.Where(n => n.Name == rootNodeName))
                    {
                        RootNode.Children.Add(matchingNode);
                    }
                }
            }

            return(RootNode);
        }
Example #5
0
        static void Main(string[] args)
        {
            Console.WriteLine("SRD Tool by CaptainSwag101\n" +
                              "Version 1.1.0, built on 2020-10-15\n");

            // Setup text encoding so we can use Shift-JIS text later on
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            if (args.Length == 0)
            {
                Console.WriteLine("Usage: SrdTool.exe <SRD file> (optional auto-execute commands and parameters, encapsulated in {})");
                Console.WriteLine("Example: SrdTool.exe test.srd {extract_textures} {n} {print_blocks} {exit}");
                return;
            }

            FileInfo info = new FileInfo(args[0]);

            if (!info.Exists)
            {
                Console.WriteLine($"ERROR: \"{args[0]}\" does not exist.");
                return;
            }

            // If the file exists and is valid, load it
            Srd     = new SrdFile();
            SrdName = args[0];

            // Search for linked files like SRDI and SRDV
            SrdiName = SrdName.Remove(SrdName.LastIndexOf(new FileInfo(SrdName).Extension)) + ".srdi";
            if (!File.Exists(SrdiName))
            {
                SrdiName = string.Empty;
            }

            SrdvName = SrdName.Remove(SrdName.LastIndexOf(new FileInfo(SrdName).Extension)) + ".srdv";
            if (!File.Exists(SrdvName))
            {
                SrdvName = string.Empty;
            }

            Srd.Load(SrdName, SrdiName, SrdvName);


            // Combine any remaining args into a single long string to be broken down by our regex
            Queue <string>?autoExecQueue = null;

            if (args.Length > 1)
            {
                autoExecQueue = new Queue <string>();

                StringBuilder remainingArgsBuilder = new StringBuilder();
                remainingArgsBuilder.AppendJoin(" ", args[1..args.Length]);
        public static void ExportModel(SrdFile srd, string exportName)
        {
            // Setup the scene for the root of our model
            Scene scene = new Scene();

            // Get all our various data
            scene.Materials.AddRange(GetMaterials(srd));
            scene.Meshes.AddRange(GetMeshes(srd, scene.Materials));
            scene.RootNode = GetNodeTree(srd, scene.Materials, scene.Meshes);

            AssimpContext exportContext = new AssimpContext();
            var           exportFormats = exportContext.GetSupportedExportFormats();

            foreach (var format in exportFormats)
            {
                if (format.FileExtension == "gltf")
                {
                    exportContext.ExportFile(scene, exportName + '.' + format.FileExtension, format.FormatId);
                    break;
                }
            }
        }
        private static List <Material> GetMaterials(SrdFile srd)
        {
            var materialList = new List <Material>();

            var matBlocks = srd.Blocks.Where(b => b is MatBlock).ToList();
            var txiBlocks = srd.Blocks.Where(b => b is TxiBlock).ToList();

            foreach (MatBlock mat in matBlocks)
            {
                var matResources = mat.Children[0] as RsiBlock;

                Material material = new Material();
                material.Name = matResources.ResourceStringList[0];

                foreach (var pair in mat.MapTexturePairs)
                {
                    // Find the TXI block associated with the current map
                    TxiBlock matchingTxi = new TxiBlock();
                    foreach (TxiBlock txi in txiBlocks)
                    {
                        var txiResources = txi.Children[0] as RsiBlock;
                        if (txiResources.ResourceStringList[0] == pair.Value)
                        {
                            matchingTxi = txi;
                            break;
                        }
                    }

                    TextureSlot texSlot = new TextureSlot
                    {
                        FilePath = matchingTxi.TextureFilename,
                        Mapping  = TextureMapping.FromUV,
                        UVIndex  = 0,
                    };

                    // Determine map type
                    if (pair.Key.StartsWith("COLORMAP"))
                    {
                        if (matchingTxi.TextureFilename.StartsWith("lm"))
                        {
                            texSlot.TextureType = TextureType.Lightmap;
                        }
                        else
                        {
                            texSlot.TextureType = TextureType.Diffuse;
                        }
                    }
                    else if (pair.Key.StartsWith("NORMALMAP"))
                    {
                        texSlot.TextureType = TextureType.Normals;
                    }
                    else if (pair.Key.StartsWith("SPECULARMAP"))
                    {
                        texSlot.TextureType = TextureType.Specular;
                    }
                    else if (pair.Key.StartsWith("TRANSPARENCYMAP"))
                    {
                        texSlot.TextureType = TextureType.Opacity;
                    }
                    else if (pair.Key.StartsWith("REFLECTMAP"))
                    {
                        texSlot.TextureType = TextureType.Reflection;
                    }
                    else
                    {
                        Console.WriteLine($"WARNING: Texture map type {pair.Key} is not currently supported.");
                    }
                    texSlot.TextureIndex = material.GetMaterialTextureCount(texSlot.TextureType);

                    if (!material.AddMaterialTexture(texSlot))
                    {
                        Console.WriteLine($"WARNING: Adding map ({pair.Key}, {pair.Value}) did not update or create new data!");
                    }
                }

                materialList.Add(material);
            }

            return(materialList);
        }
        private static List <Mesh> GetMeshes(SrdFile srd, List <Material> materials)
        {
            var meshList = new List <Mesh>();

            //var treBlocks = srd.Blocks.Where(b => b is TreBlock).ToList();
            var sklBlocks = srd.Blocks.Where(b => b is SklBlock).ToList();
            var mshBlocks = srd.Blocks.Where(b => b is MshBlock).ToList();
            var vtxBlocks = srd.Blocks.Where(b => b is VtxBlock).ToList();

            // For debugging
            //var vtxNameList = new List<string>();
            //var mshNameList = new List<string>();
            //foreach (VtxBlock vtx in vtxBlocks)
            //{
            //    vtxNameList.Add((vtx.Children[0] as RsiBlock).ResourceStringList[0]);
            //}
            //foreach (MshBlock msh in mshBlocks)
            //{
            //    mshNameList.Add((msh.Children[0] as RsiBlock).ResourceStringList[0]);
            //}

            // Iterate through each VTX block simultaneously and extract the data we need
            var extractedData = new List <MeshData>();

            foreach (MshBlock msh in mshBlocks)
            {
                var vtx          = vtxBlocks.Where(b => (b.Children[0] as RsiBlock).ResourceStringList[0] == msh.VertexBlockName).First() as VtxBlock;
                var vtxResources = vtx.Children[0] as RsiBlock;

                // Extract position data
                using BinaryReader positionReader = new BinaryReader(new MemoryStream(vtxResources.ExternalData[0]));
                var curVertexList   = new List <Vector3>();
                var curNormalList   = new List <Vector3>();
                var curTexcoordList = new List <Vector2>();
                var curWeightList   = new List <float>();

                foreach (var section in vtx.VertexDataSections)
                {
                    positionReader.BaseStream.Seek(section.StartOffset, SeekOrigin.Begin);
                    for (int vNum = 0; vNum < vtx.VertexCount; ++vNum)
                    {
                        long oldPos = positionReader.BaseStream.Position;
                        switch (vtx.VertexDataSections.IndexOf(section))
                        {
                        case 0:     // Vertex/Normal data (and Texture UV for boneless models)
                        {
                            Vector3 vertex;
                            vertex.X = positionReader.ReadSingle() * -1.0f;                 // X
                            vertex.Y = positionReader.ReadSingle();                         // Y
                            vertex.Z = positionReader.ReadSingle();                         // Z
                            curVertexList.Add(vertex);

                            Vector3 normal;
                            normal.X = positionReader.ReadSingle() * -1.0f;                 // X
                            normal.Y = positionReader.ReadSingle();                         // Y
                            normal.Z = positionReader.ReadSingle();                         // Z
                            curNormalList.Add(normal);

                            if (vtx.VertexDataSections.Count == 1)
                            {
                                Console.WriteLine($"Mesh type: {vtx.MeshType}");
                                Vector2 texcoord;
                                texcoord.X = float.PositiveInfinity;
                                texcoord.X = positionReader.ReadSingle();                   // U
                                while (float.IsNaN(texcoord.X) || !float.IsFinite(texcoord.X))
                                {
                                    texcoord.X = positionReader.ReadSingle();
                                }
                                texcoord.Y = positionReader.ReadSingle();                   // V, invert for non-glTF exports
                                while (float.IsNaN(texcoord.Y) || !float.IsFinite(texcoord.Y))
                                {
                                    texcoord.Y = positionReader.ReadSingle();
                                }

                                if (float.IsNaN(texcoord.X) || float.IsNaN(texcoord.Y) || Math.Abs(texcoord.X) > 1 || Math.Abs(texcoord.Y) > 1)
                                {
                                    Console.WriteLine($"INVALID UVs DETECTED!");
                                }
                                curTexcoordList.Add(texcoord);
                            }
                        }
                        break;

                        case 1:                                                           // Bone weights?
                        {
                            var weightsPerVert = (section.SizePerVertex / sizeof(float)); // TODO: Is this always 8?
                            for (int wNum = 0; wNum < weightsPerVert; ++wNum)
                            {
                                curWeightList.Add(positionReader.ReadSingle());
                            }
                        }
                        break;

                        case 2:     // Texture UVs (only for models with bones)
                        {
                            Vector2 texcoord;
                            texcoord.X = positionReader.ReadSingle();                       // U
                            texcoord.Y = positionReader.ReadSingle();                       // V, invert for non-glTF exports
                            curTexcoordList.Add(texcoord);
                        }
                        break;

                        default:
                            Console.WriteLine($"WARNING: Unknown vertex sub-block index {vtx.VertexDataSections.IndexOf(section)} is present in VTX block {vtxBlocks.IndexOf(vtx)}!");
                            break;
                        }

                        // Skip data we don't currently use, though I may add support for this data later
                        long remainingBytes = section.SizePerVertex - (positionReader.BaseStream.Position - oldPos);
                        positionReader.BaseStream.Seek(remainingBytes, SeekOrigin.Current);
                    }
                }

                // Extract index data
                using BinaryReader indexReader = new BinaryReader(new MemoryStream(vtxResources.ExternalData[1]));
                var curIndexList = new List <ushort[]>();
                while (indexReader.BaseStream.Position < indexReader.BaseStream.Length)
                {
                    ushort[] indices = new ushort[3];
                    for (int i = 0; i < 3; ++i)
                    {
                        ushort index = indexReader.ReadUInt16();
                        // We need to reverse the order of the indices to prevent the normals
                        // from becoming permanently flipped due to the clockwise/counter-clockwise
                        // order of the indices determining the face's direction
                        indices[3 - (i + 1)] = index;
                    }
                    curIndexList.Add(indices);
                }

                // Add the extracted data to our list
                extractedData.Add(new MeshData(curVertexList, curNormalList, curTexcoordList, curIndexList, vtx.BindBoneList, curWeightList));
            }

            // Now that we've extracted the data we need, convert it to Assimp equivalents
            for (int d = 0; d < extractedData.Count; ++d)
            {
                MeshData meshData     = extractedData[d];
                var      msh          = mshBlocks[d] as MshBlock;
                var      mshResources = msh.Children[0] as RsiBlock;

                Mesh mesh = new Mesh()
                {
                    Name          = mshResources.ResourceStringList[0],
                    PrimitiveType = PrimitiveType.Triangle,
                    MaterialIndex = materials.IndexOf(materials.Where(m => m.Name == msh.MaterialName).First()),
                };

                // Add vertices
                foreach (var vertex in meshData.Vertices)
                {
                    Vector3D vec3D = new Vector3D(vertex.X, vertex.Y, vertex.Z);
                    mesh.Vertices.Add(vec3D);
                }

                // Add normals
                foreach (var normal in meshData.Normals)
                {
                    Vector3D vec3D = new Vector3D(normal.X, normal.Y, normal.Z);
                    mesh.Normals.Add(vec3D);
                }

                // Add UVs
                mesh.UVComponentCount[0]          = 2;
                mesh.TextureCoordinateChannels[0] = new List <Vector3D>();
                foreach (var uv in meshData.Texcoords)
                {
                    Vector3D vec3D = new Vector3D(uv.X, uv.Y, 0.0f);
                    mesh.TextureCoordinateChannels[0].Add(vec3D);
                }

                // Add faces
                foreach (var indexArray in meshData.Indices)
                {
                    Face face = new Face();

                    foreach (ushort index in indexArray)
                    {
                        face.Indices.Add(index);
                    }

                    mesh.Faces.Add(face);
                }

                // Add bones
                foreach (string boneName in meshData.Bones)
                {
                    var boneInfoList = (sklBlocks.First() as SklBlock).BoneInfoList;

                    var matchingBone = boneInfoList.Where(b => b.BoneName == boneName).First();

                    Bone bone = new Bone();
                    bone.Name = boneName;

                    mesh.Bones.Add(bone);
                }

                // Add weights to those bones
                int weightsPerVert = (meshData.Weights.Count / meshData.Vertices.Count);
                for (int vNum = 0; vNum < meshData.Vertices.Count; ++vNum)
                {
                    for (int wNum = 0; wNum < weightsPerVert; ++wNum)
                    {
                        // Make sure the bone actually exists
                        if (mesh.BoneCount <= (wNum % weightsPerVert))
                        {
                            break;
                        }

                        VertexWeight vWeight;
                        vWeight.VertexID = vNum;
                        vWeight.Weight   = meshData.Weights[wNum + (vNum * weightsPerVert)];
                        mesh.Bones[wNum % weightsPerVert].VertexWeights.Add(vWeight);
                    }
                }

                meshList.Add(mesh);
            }

            return(meshList);
        }