private static void HandleFile(string path) { if (Directory.Exists(path)) { Console.WriteLine($"Packing {Path.GetFileName(path)}..."); using var archive = ArchiveFile.FromDirectory(path); if (File.Exists(path + ".par") && !File.Exists(path + ".par.bak")) { File.Copy(path + ".par", path + ".par.bak"); } archive.Save(File.OpenWrite(path + ".par")); } else { switch (Path.GetExtension(path)) { case ".par": Console.WriteLine($"Extracting {Path.GetFileName(path)}"); using (var archive = ArchiveFile.FromFile(path)) { archive.Extract(Path.ChangeExtension(path, null)); } break; case ".gmd": Console.WriteLine($"Exporting {Path.GetFileName(path)}"); var gmdFile = GmdFile.FromFile(path); var mesh = YakuzaMesh.FromGmdFile(gmdFile); mesh.SaveToGltf2(Path.ChangeExtension(path, ".glb"), FindTextureFolder(path)); break; } } }
public static YakuzaMesh FromGmdFile(GmdFile GmdFile) { var mesh = new YakuzaMesh(); mesh.LoadGmdFile(GmdFile); return(mesh); }
private Bone LoadBone(GmdFile.BoneTransform boneTransform, Bone parent, GmdFile file, Bone[] boneTable) { var bone = new Bone(); bone.Id = boneTransform.BoneNo; bone.Name = file.BoneNames[boneTransform.BoneNameIndex].ToString(); bone.Position = boneTransform.LocalPosition; bone.Rotation = boneTransform.LocalRotation; bone.Scale = boneTransform.Scale; bone.Parent = parent; boneTable[boneTransform.BoneNo] = bone; bone.Children.AddRange(GmdUtils.GetChildren(boneTransform, file.BoneTransforms).Select(bt => LoadBone(bt, bone, file, boneTable))); return(bone); }
static void Main(string[] args) { var ddsPath = @"D:\Program Files (x86)\Steam\steamapps\common\Yakuza Kiwami 2\data\chara_unpack\lexus2\dds"; using var archiveIn = ArchiveFile.FromFile(@"D:\Program Files (x86)\Steam\steamapps\common\Yakuza Kiwami 2\data\chara_original.par"); using var archiveOut = File.OpenWrite(@"D:\Program Files (x86)\Steam\steamapps\common\Yakuza Kiwami 2\data\chara.par"); var fileName = "c_am_kiryu.gmd"; // var fileName = "c_am_dummy_01.gmd"; // var fileName = "c_aw_haruka.gmd"; // var fileName = "c_am_S03_soutenboripl.gmd"; var kiryu = archiveIn.Find(fileName); using var tempFile = new MemoryStream(); archiveIn.Read(kiryu).CopyTo(tempFile); tempFile.Seek(0, SeekOrigin.Begin); var gmd = GmdFile.FromStream(tempFile); var mesh = YakuzaMesh.FromGmdFile(gmd); mesh.SaveToGltf2("mesh.glb", ddsPath); if (Directory.Exists("textures")) { Directory.Delete("textures", true); } var mesh1 = YakuzaMesh.FromGltf2("mesh_edited.glb", "textures"); foreach (var file in Directory.EnumerateFiles("textures")) { Console.WriteLine($"Adding texture: {Path.GetFileName(file)}"); archiveIn.AddFile("lexus2\\dds\\" + Path.GetFileName(file), File.ReadAllBytes(file)); } var gmd2 = mesh1.ToGmdFile(); tempFile.Seek(0, SeekOrigin.Begin); gmd2.Write(tempFile); tempFile.Seek(0, SeekOrigin.Begin); archiveIn.ReplaceFile(fileName, tempFile.ToArray()); archiveIn.Save(archiveOut, Endianness.Big); }
public static YakuzaMesh FromGmdStream(Stream stream) { var gmd = GmdFile.FromStream(stream); return(FromGmdFile(gmd)); }
public static YakuzaMesh FromArchive(string name, ArchiveFile archive) { return(FromGmdFile(GmdFile.FromArchive(name, archive))); }
private void LoadGmdFile(GmdFile gmdFile) { Name = gmdFile.Name; Textures = gmdFile.Textures.Select(tex => tex.Name).ToArray(); Materials = gmdFile.Materials.Select(mat => { return(new Material { Id = mat.Id, Shader = gmdFile.Shaders[mat.ShaderIndex].ToString(), Textures = mat.TextureIndices.Select(texIdx => { if (texIdx >= 0 && texIdx < Textures.Length) { return Textures[texIdx]; } return null; }).ToArray() }); }).ToArray(); var bones = new Bone[gmdFile.BoneTransforms.Length]; foreach (var bt in gmdFile.BoneTransforms) { if (GmdUtils.GetParentBone(bt, gmdFile.BoneTransforms) == null) { LoadBone(bt, null, gmdFile, bones); } } Bones = bones.ToArray(); var nameList = new List <string>(); var meshes = new List <Mesh>(); foreach (var gmdMesh in gmdFile.Meshes) { var mesh = new Mesh(); meshes.Add(mesh); // Console.WriteLine($"------------"); foreach (var gmdSub in gmdFile.Submeshes.Where(s => s.MeshIndex == gmdMesh.MeshId)) { var submesh = new Submesh(); mesh.Submeshes.Add(submesh); submesh.Id = gmdSub.Id; submesh.Name = GetSubmeshName(Bones[gmdSub.BoneNo].Name, nameList); submesh.Transform = Bones[gmdSub.BoneNo].WorldMatrix; submesh.Material = Materials[gmdSub.MaterialIndex]; submesh.Vertices = ReadVertices(gmdSub, gmdMesh, gmdFile); submesh.Triangles = new int[gmdSub.IndicesCount / 3, 3]; // Console.WriteLine(submesh.Name.PadRight(25, ' ') // + submesh.Material.Shader.PadRight(25, ' ') // + $"{gmdMesh.HasFlag1} {gmdMesh.HasFlag2} {gmdMesh.HasFlag3}"); var offset = gmdSub.IndicesOffset; for (int i = 0; i < gmdSub.IndicesCount / 3; i++) { submesh.Triangles[i, 0] = gmdFile.Indices[offset + i * 3].Value - gmdSub.BufferOffset2; submesh.Triangles[i, 1] = gmdFile.Indices[offset + i * 3 + 1].Value - gmdSub.BufferOffset2; submesh.Triangles[i, 2] = gmdFile.Indices[offset + i * 3 + 2].Value - gmdSub.BufferOffset2; } } } Meshes = meshes.ToArray(); }
private Vertex[] ReadVertices(GmdFile.Submesh submesh, GmdFile.Mesh mesh, GmdFile file) { var vertices = new Vertex[submesh.VertexCount]; var meshIndex = Array.IndexOf(file.Meshes, mesh); using var memoryStream = new MemoryStream(file.VertexBuffers[meshIndex]); using var bs = new BinaryReader(memoryStream); bs.BaseStream.Seek((submesh.BufferOffset1 + submesh.BufferOffset2) * mesh.Stride, SeekOrigin.Begin); for (int j = 0; j < submesh.VertexCount; j++) { var v = new Vertex(); v.Position = GmdUtils.ReadVector3(bs); if (mesh.HasBones && file.BoneIndices.Length > 0) { v.BoneWeights = new[] { bs.ReadByteAsFloat(), bs.ReadByteAsFloat(), bs.ReadByteAsFloat(), bs.ReadByteAsFloat() }; v.BoneIndices = new[] { file.BoneIndices[bs.ReadByte() + submesh.BoneIndexOffset + 1].Value, file.BoneIndices[bs.ReadByte() + submesh.BoneIndexOffset + 1].Value, file.BoneIndices[bs.ReadByte() + submesh.BoneIndexOffset + 1].Value, file.BoneIndices[bs.ReadByte() + submesh.BoneIndexOffset + 1].Value }; } v.Normal = GmdUtils.ReadNormal(bs); bs.ReadByte(); if (mesh.HasFlag1) { // var a = bs.ReadByteAsFloat(); // var b = bs.ReadByteAsFloat(); // var c = bs.ReadByteAsFloat(); // var d = bs.ReadByteAsFloat(); var val = bs.ReadInt32(); // Console.WriteLine($"{a:0.00} / {b:0.00} / {c:0.00} / {d:0.00}"); // bs.ReadInt32(); } if (mesh.HasFlag2) { bs.ReadInt32(); } if (mesh.HasFlag3) { bs.ReadInt32(); } for (int l = 0; l < mesh.UvLayers; l++) { var uvs = new Vector2(Half.ToHalf(bs.ReadUInt16()), 1 - Half.ToHalf(bs.ReadUInt16())); switch (l) { case 0: v.Uv0 = uvs; break; case 1: v.Uv1 = uvs; break; case 2: v.Uv2 = uvs; break; case 3: v.Uv3 = uvs; break; default: Console.Error.WriteLine("Too many UV channels!"); break; } } vertices[j] = v; } return(vertices); }
public GmdFile ToGmdFile() { var file = new GmdFile(); file.Name = Name; // TEXTURES var textures = Textures.Concat(new string[] { "_dummy_rd", "dummy_nmap", "dummy_black", "dummy_gray", "dummy_white", "default_z", "noise" }) .Distinct().ToArray(); file.Textures = textures .Select(GmdFile.HashedName.FromString) .ToArray(); var shaders = Materials.Select(mat => mat.Shader).Distinct().ToArray(); var hlSubmeshes = Meshes.SelectMany(m => m.Submeshes).OrderBy(sm => sm.Id).ToArray(); // MATERIALS file.Materials = Materials.Select(mat => { var material = new GmdFile.Material() { Id = mat.Id, TextureIndices = mat.Textures.Select(tex => (int)(ushort)Array.IndexOf(textures, tex)) .Concat(Enumerable.Repeat((int)ushort.MaxValue, 8 - mat.Textures.Length)).ToArray(), ShaderIndex = (uint)Array.IndexOf(shaders, mat.Shader), SubmeshIndex = (uint)Array.FindIndex(hlSubmeshes, m => m.Material == mat) }; /* * 3 => rd (sd_o1dzt_m2dzt) / tr (skin) / dummy_white * 4 => rm / default_z [can be default_z, strange things happen when null] * 5 => rt / noise [can be noise, too smooth when null] * 6 => rs [can be null] * 7 => */ material.TextureIndices[0] = material.TextureIndices[0] >= 255 ? Array.IndexOf(textures, "dummy_black") : material.TextureIndices[0]; material.TextureIndices[1] = material.TextureIndices[1] >= 255 ? Array.IndexOf(textures, "default_z") : material.TextureIndices[1]; material.TextureIndices[2] = material.TextureIndices[2] >= 255 ? Array.IndexOf(textures, "dummy_nmap") : material.TextureIndices[2]; material.TextureIndices[3] = material.TextureIndices[3] >= 255 ? Array.IndexOf(textures, "dummy_white") : material.TextureIndices[3]; material.TextureIndices[4] = material.TextureIndices[4] >= 255 ? Array.IndexOf(textures, "default_z") : material.TextureIndices[4]; material.TextureIndices[5] = material.TextureIndices[5] >= 255 ? Array.IndexOf(textures, "noise") : material.TextureIndices[5]; material.TextureIndices[6] = material.TextureIndices[6] >= 255 ? ushort.MaxValue : material.TextureIndices[6]; //ushort.MaxValue; material.TextureIndices[7] = material.TextureIndices[7] >= 255 ? ushort.MaxValue : material.TextureIndices[7]; //ushort.MaxValue; // ushort.MaxValue material.Initialize(mat.Shader == GmdUtils.TRANSPARENCY_MAT); return(material); }).ToArray(); // SHADERS file.Shaders = shaders.Select(shader => GmdFile.HashedName.FromString(shader)).ToArray(); // MESHES var meshes = new List <GmdFile.Mesh>(); var vertexBuffers = new List <byte[]>(); var submeshes = new List <GmdFile.Submesh>(); var indices = new List <GmdFile.Index>(); var boneIndices = new List <GmdFile.BoneIndex>(); for (int i = 0; i < Meshes.Length; i++) { var mesh = Meshes[i]; var gmdMesh = new GmdFile.Mesh(); var firstVertex = mesh.Submeshes.First().Vertices.First(); gmdMesh.Initialize(); gmdMesh.MeshId = i; gmdMesh.Count = mesh.Submeshes.Sum(m => m.Vertices.Length); gmdMesh.Offset = vertexBuffers.Sum(b => b.Length); gmdMesh.UvLayers = firstVertex.UvLayerCount; gmdMesh.HasBones = firstVertex.BoneIndices != null; gmdMesh.FlagCount = GmdUtils.GetFlagCount(mesh.Submeshes.First().Material.Shader); using var buffer = new MemoryStream(); using var writer = new BinaryWriter(buffer); Console.WriteLine("m" + i); int vertexCount = 0; for (int j = 0; j < mesh.Submeshes.Count; j++) { var submesh = mesh.Submeshes[j]; var gmdSubmesh = new GmdFile.Submesh(); var boneIndexList = submesh.Vertices.SelectMany(v => v.BoneIndices ?? Enumerable.Empty <int>()).Distinct().ToList(); gmdSubmesh.Id = Array.IndexOf(hlSubmeshes, submesh); gmdSubmesh.MeshIndex = i; gmdSubmesh.IndicesCount = submesh.Triangles.GetLength(0) * 3; gmdSubmesh.IndicesOffset = indices.Count; gmdSubmesh.VertexCount = submesh.Vertices.Length; gmdSubmesh.MaterialIndex = (int)submesh.Material.Id; gmdSubmesh.BufferOffset2 = vertexCount; gmdSubmesh.BoneIndexOffset = boneIndices.Count; gmdSubmesh.BoneIndexCount = boneIndexList.Count; gmdMesh.Stride = WriteVertices(writer, submesh, gmdMesh.FlagCount, boneIndexList); boneIndexList.Insert(0, gmdSubmesh.BoneIndexCount); // Expects count in index list vertexCount += submesh.Vertices.Length; // Fill in indices for (int y = 0; y < submesh.Triangles.GetLength(0); y++) { indices.Add(new GmdFile.Index() { Value = submesh.Triangles[y, 0] + gmdSubmesh.BufferOffset2 }); indices.Add(new GmdFile.Index() { Value = submesh.Triangles[y, 1] + gmdSubmesh.BufferOffset2 }); indices.Add(new GmdFile.Index() { Value = submesh.Triangles[y, 2] + gmdSubmesh.BufferOffset2 }); } boneIndices.AddRange(boneIndexList.Select(idx => new GmdFile.BoneIndex() { Value = idx })); submeshes.Add(gmdSubmesh); } gmdMesh.Size = (int)buffer.Length; vertexBuffers.Add(buffer.ToArray()); meshes.Add(gmdMesh); } file.Indices = indices.ToArray(); file.Meshes = meshes.ToArray(); file.Submeshes = submeshes.OrderBy(sm => sm.Id).ToArray(); file.VertexBuffers = vertexBuffers.ToArray(); file.BoneIndices = boneIndices.ToArray(); file.BoneNames = Bones.Select(b => GmdFile.HashedName.FromString(b.Name)).ToArray(); file.BoneTransforms = Bones.Select(bone => { var bt = new GmdFile.BoneTransform(); bt.Position = bone.WorldMatrix.Translation; bt.LocalPosition = bone.Position; bt.LocalRotation = bone.Rotation; bt.Scale = bone.Scale; bt.BoneNo = bt.BoneNo2 = bone.Id; bt.BoneNameIndex = bone.Id; bt.TransformIndex = -1; bt.NextSiblingIndex = -1; bt.NextChildIndex = -1; bt.Footer = new byte[16]; if (bone.Name.Contains("[l0]")) { bt.TransformIndex = 0; bt.NodeType = GmdFile.BoneTransform.NODE_TYPE_TRANSFORM; } // If bone has children, set the nextChildIndex idx if (bone.Children.Count > 0) { bt.NextChildIndex = bone.Children[0].Id; } // If bone has another sibling that comes after itself, set NextSiblingIndex if (bone.Parent != null) { var childIdx = bone.Parent.Children.IndexOf(bone); if (childIdx >= 0 && childIdx + 1 < bone.Parent.Children.Count) { bt.NextSiblingIndex = bone.Parent.Children[childIdx + 1].Id; } } return(bt); }).ToArray(); return(file); }