From 055694a4b4baf552d48aa230b3c8bad4895986b2 Mon Sep 17 00:00:00 2001 From: bird_egop Date: Sun, 31 Aug 2025 01:07:24 +0300 Subject: [PATCH] Hack .msh --- ParkanPlayground/Msh01.cs | 49 +++--- ParkanPlayground/Msh02.cs | 24 +-- ParkanPlayground/Msh03.cs | 35 +++++ ParkanPlayground/Msh07.cs | 6 +- ParkanPlayground/Msh0D.cs | 21 ++- ParkanPlayground/MshConverter.cs | 252 ++++++++++++++++++++----------- ParkanPlayground/Program.cs | 3 +- README.md | 72 +++++---- 8 files changed, 284 insertions(+), 178 deletions(-) create mode 100644 ParkanPlayground/Msh03.cs diff --git a/ParkanPlayground/Msh01.cs b/ParkanPlayground/Msh01.cs index 672c552..60edefa 100644 --- a/ParkanPlayground/Msh01.cs +++ b/ParkanPlayground/Msh01.cs @@ -32,23 +32,24 @@ public static class Msh01 OffsetIntoFile13 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 4)), IndexInFile08 = - BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 6)), - State00 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 8)), - State01 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 10)), - State10 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 12)), - State11 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 14)), - State20 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 16)), - State21 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 18)), - S20 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 20)), - S22 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 22)), - S24 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 24)), - S26 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 26)), - S28 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 28)), - S30 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 30)), - S32 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 32)), - S34 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 34)), - S36 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 36)) + BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 6)) }; + + element.Lod[0] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 8)); + element.Lod[1] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 10)); + element.Lod[2] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 12)); + element.Lod[3] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 14)); + element.Lod[4] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 16)); + element.Lod[5] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 18)); + element.Lod[6] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 20)); + element.Lod[7] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 22)); + element.Lod[8] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 24)); + element.Lod[9] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 26)); + element.Lod[10] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 28)); + element.Lod[11] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 30)); + element.Lod[12] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 32)); + element.Lod[13] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 34)); + element.Lod[14] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 36)); elements.Add(element); } @@ -71,20 +72,6 @@ public static class Msh01 public short ParentIndex { get; set; } public short OffsetIntoFile13 { get; set; } public short IndexInFile08 { get; set; } - public ushort State00 { get; set; } - public ushort State01 { get; set; } - public ushort State10 { get; set; } - public ushort State11 { get; set; } - public ushort State20 { get; set; } - public ushort State21 { get; set; } - public short S20 { get; set; } - public short S22 { get; set; } - public short S24 { get; set; } - public short S26 { get; set; } - public short S28 { get; set; } - public short S30 { get; set; } - public short S32 { get; set; } - public short S34 { get; set; } - public short S36 { get; set; } + public ushort[] Lod { get; set; } = new ushort[15]; } } \ No newline at end of file diff --git a/ParkanPlayground/Msh02.cs b/ParkanPlayground/Msh02.cs index 9d7eecd..92659f5 100644 --- a/ParkanPlayground/Msh02.cs +++ b/ParkanPlayground/Msh02.cs @@ -159,18 +159,6 @@ public static class Msh02 public float XYRadius { get; set; } } - public class BoundingBox - { - public Vector3 Vec1 { get; set; } - public Vector3 Vec2 { get; set; } - public Vector3 Vec3 { get; set; } - public Vector3 Vec4 { get; set; } - public Vector3 Vec5 { get; set; } - public Vector3 Vec6 { get; set; } - public Vector3 Vec7 { get; set; } - public Vector3 Vec8 { get; set; } - } - public class Msh02Element { public ushort StartIndexIn07 { get; set; } @@ -183,4 +171,16 @@ public static class Msh02 public Vector3 Vector4 { get; set; } public Vector3 Vector5 { get; set; } } + + public class BoundingBox + { + public Vector3 Vec1 { get; set; } + public Vector3 Vec2 { get; set; } + public Vector3 Vec3 { get; set; } + public Vector3 Vec4 { get; set; } + public Vector3 Vec5 { get; set; } + public Vector3 Vec6 { get; set; } + public Vector3 Vec7 { get; set; } + public Vector3 Vec8 { get; set; } + } } \ No newline at end of file diff --git a/ParkanPlayground/Msh03.cs b/ParkanPlayground/Msh03.cs new file mode 100644 index 0000000..b7f31e4 --- /dev/null +++ b/ParkanPlayground/Msh03.cs @@ -0,0 +1,35 @@ +using System.Buffers.Binary; +using Common; +using NResLib; + +namespace ParkanPlayground; + +public class Msh03 +{ + public static List ReadComponent(FileStream mshFs, NResArchive mshNres) + { + var verticesFileEntry = mshNres.Files.FirstOrDefault(x => x.FileType == "03 00 00 00"); + + if (verticesFileEntry is null) + { + throw new Exception("Archive doesn't contain vertices file (03)"); + } + + if (verticesFileEntry.ElementSize != 12) + { + throw new Exception("Vertices file (03) element size is not 12"); + } + + var verticesFile = new byte[verticesFileEntry.ElementCount * verticesFileEntry.ElementSize]; + mshFs.Seek(verticesFileEntry.OffsetInFile, SeekOrigin.Begin); + mshFs.ReadExactly(verticesFile, 0, verticesFile.Length); + + var vertices = verticesFile.Chunk(12).Select(x => new Vector3( + BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(0)), + BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(4)), + BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(8)) + ) + ).ToList(); + return vertices; + } +} \ No newline at end of file diff --git a/ParkanPlayground/Msh07.cs b/ParkanPlayground/Msh07.cs index 78c7bdd..64cbc3e 100644 --- a/ParkanPlayground/Msh07.cs +++ b/ParkanPlayground/Msh07.cs @@ -25,7 +25,8 @@ public static class Msh07 { Flags = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0)), Magic02 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(2)), - Magic04 = BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(4)), + Magic04 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(4)), + Magic06 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(6)), OffsetX = BinaryPrimitives.ReadInt16LittleEndian(x.AsSpan(8)), OffsetY = BinaryPrimitives.ReadInt16LittleEndian(x.AsSpan(10)), OffsetZ = BinaryPrimitives.ReadInt16LittleEndian(x.AsSpan(12)), @@ -39,7 +40,8 @@ public static class Msh07 { public ushort Flags { get; set; } public ushort Magic02 { get; set; } - public uint Magic04 { get; set; } + public ushort Magic04 { get; set; } + public ushort Magic06 { get; set; } // normalized vector X, need to divide by 32767 to get float in range -1..1 public short OffsetX { get; set; } // normalized vector Y, need to divide by 32767 to get float in range -1..1 diff --git a/ParkanPlayground/Msh0D.cs b/ParkanPlayground/Msh0D.cs index 4e45689..1ce8b4a 100644 --- a/ParkanPlayground/Msh0D.cs +++ b/ParkanPlayground/Msh0D.cs @@ -26,11 +26,12 @@ public static class Msh0D var elements = elementBytes.Select(x => new Msh0DElement() { Flags = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0)), - Magic04 = BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(4)), - number_of_triangles = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(8)), - IndexInto06 = BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(10)), - Magic0C = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(14)), - IndexInto03 = BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(16)), + Magic04 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(4)), + Magic06 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(6)), + CountOf06 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(8)), + IndexInto06 = BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0xA)), + CountOf03 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0xE)), + IndexInto03 = BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x10)), }).ToList(); return elements; @@ -39,10 +40,14 @@ public static class Msh0D public class Msh0DElement { public uint Flags { get; set; } - public uint Magic04 { get; set; } - public ushort number_of_triangles { get; set; } + + // Magic04 и Magic06 обрабатываются вместе + + public ushort Magic04 { get; set; } + public ushort Magic06 { get; set; } + public ushort CountOf06 { get; set; } public int IndexInto06 { get; set; } - public ushort Magic0C { get; set; } + public ushort CountOf03 { get; set; } public int IndexInto03 { get; set; } } } \ No newline at end of file diff --git a/ParkanPlayground/MshConverter.cs b/ParkanPlayground/MshConverter.cs index 92ac0b1..e125784 100644 --- a/ParkanPlayground/MshConverter.cs +++ b/ParkanPlayground/MshConverter.cs @@ -1,4 +1,5 @@ -using System.Buffers.Binary; +using System.Buffers.Binary; +using System.Globalization; using System.Text; using Common; using NResLib; @@ -19,11 +20,17 @@ public class MshConverter var component0A = Msh0A.ReadComponent(mshFs, mshNres); var component07 = Msh07.ReadComponent(mshFs, mshNres); var component0D = Msh0D.ReadComponent(mshFs, mshNres); + + // Triangle Vertex Indices var component06 = Msh06.ReadComponent(mshFs, mshNres); - var component03 = Read03Component(mshFs, mshNres); - + + // vertices + var component03 = Msh03.ReadComponent(mshFs, mshNres); + + _ = 5; + // --- Write OBJ --- - using var sw = new StreamWriter("test.obj", false, Encoding.UTF8); + using var sw = new StreamWriter("test.obj", false, new UTF8Encoding(false)); foreach (var v in component03) sw.WriteLine($"v {v.X:F8} {v.Y:F8} {v.Z:F8}"); @@ -31,79 +38,168 @@ public class MshConverter var vertices = new List(); var faces = new List<(int, int, int)>(); // store indices into vertices list + // 01 - это части меша (Piece) for (var pieceIndex = 0; pieceIndex < component01.Elements.Count; pieceIndex++) { - var piece = component01.Elements[pieceIndex]; - var state = (piece.State00 == 0xffff) ? 0 : piece.State00; + var piece01 = component01.Elements[pieceIndex]; + // var state = (piece.State00 == 0xffff) ? 0 : piece.State00; - var mesh02Element = component02.Elements[state]; - - int indexInto07 = mesh02Element.StartIndexIn07; - - var element0Dstart = mesh02Element.StartOffsetIn0d; - var element0Dcount = mesh02Element.ByteLengthIn0D; - - Console.WriteLine($"Started piece {pieceIndex}. State={state}. 0D start={element0Dstart}, count={element0Dcount}"); - - for (var comp0Dindex = 0; comp0Dindex < element0Dcount; comp0Dindex++) + for (var lodIndex = 0; lodIndex < piece01.Lod.Length; lodIndex++) { - var element0D = component0D[element0Dstart + comp0Dindex]; - - var indexInto03 = element0D.IndexInto03; - var indexInto06 = element0D.IndexInto06; - - var count = (uint)element0D.number_of_triangles; - - // Convert IndexInto06 to ushort array index (3 ushorts per triangle) - Console.WriteLine( - $"Processing 0D element[{element0Dstart + comp0Dindex}]. IndexInto03={indexInto03}, IndexInto06={indexInto06}. Number of triangles={count}"); - - if (count != 0) + var lod = piece01.Lod[lodIndex]; + if (lod == 0xffff) { - sw.WriteLine($"o piece_{pieceIndex++}_of_mesh_{comp0Dindex}"); - if (count % 3 != 0) + Console.WriteLine($"Piece {pieceIndex} has lod -1 at {lodIndex}. Skipping"); + continue; + } + + sw.WriteLine($"o piece_{pieceIndex}_lod_{lodIndex}"); + // 02 - Submesh + var part02 = component02.Elements[lod]; + + int indexInto07 = part02.StartIndexIn07; + + var element0Dstart = part02.StartOffsetIn0d; + var element0Dcount = part02.ByteLengthIn0D; + + // Console.WriteLine($"Started piece {pieceIndex}. LOD={lod}. 0D start={element0Dstart}, count={element0Dcount}"); + + for (var comp0Dindex = 0; comp0Dindex < element0Dcount; comp0Dindex++) + { + var element0D = component0D[element0Dstart + comp0Dindex]; + + var indexInto03 = element0D.IndexInto03; + var indexInto06 = element0D.IndexInto06; // indices + + uint maxIndex = element0D.CountOf03; + uint indicesCount = element0D.CountOf06; + + + // Convert IndexInto06 to ushort array index (3 ushorts per triangle) + // Console.WriteLine($"Processing 0D element[{element0Dstart + comp0Dindex}]. IndexInto03={indexInto03}, IndexInto06={indexInto06}. Number of triangles={indicesCount}"); + + if (indicesCount != 0) { - Console.WriteLine("Number of triangles is not multiple of 3"); + // sw.WriteLine($"o piece_{pieceIndex}_of_mesh_{comp0Dindex}"); + + for (int ind = 0; ind < indicesCount; ind += 3) + { + // Each triangle uses 3 consecutive ushorts in component06 + + // sw.WriteLine($"o piece_{pieceIndex}_of_mesh_{comp0Dindex}_tri_{ind}"); + + var comp07 = component07[indexInto07]; + + var i1 = indexInto03 + component06[indexInto06]; + var i2 = indexInto03 + component06[indexInto06 + 1]; + var i3 = indexInto03 + component06[indexInto06 + 2]; + + var v1 = component03[i1]; + var v2 = component03[i2]; + var v3 = component03[i3]; + + sw.WriteLine($"f {i1 + 1} {i2 + 1} {i3 + 1}"); + + // push vertices to global list + vertices.Add(v1); + vertices.Add(v2); + vertices.Add(v3); + + int baseIndex = vertices.Count; + // record face (OBJ is 1-based indexing!) + faces.Add((baseIndex - 2, baseIndex - 1, baseIndex)); + + indexInto07++; + indexInto06 += 3; // step by 3 since each triangle uses 3 ushorts + } + _ = 5; } - - count = (count + 2) / 3; // number of triangles - - for (int tri = 0; tri < count; tri++) - { - // Each triangle uses 3 consecutive ushorts in component06 - - var comp07 = component07[indexInto07]; - - var i1 = indexInto03 + component06[indexInto06]; - var i2 = indexInto03 + component06[indexInto06 + 1]; - var i3 = indexInto03 + component06[indexInto06 + 2]; - - var v1 = component03[i1]; - var v2 = component03[i2]; - var v3 = component03[i3]; - - sw.WriteLine($"f {i1 + 1} {i2 + 1} {i3 + 1}"); - - // push vertices to global list - vertices.Add(v1); - vertices.Add(v2); - vertices.Add(v3); - - int baseIndex = vertices.Count; - // record face (OBJ is 1-based indexing!) - faces.Add((baseIndex - 2, baseIndex - 1, baseIndex)); - - indexInto07++; - indexInto06 += 3; // step by 3 since each triangle uses 3 ushorts - } - - _ = 5; } } } } + public void Convert2(string mshPath) + { + var mshNresResult = NResParser.ReadFile(mshPath); + var mshNres = mshNresResult.Archive!; + + using var mshFs = new FileStream(mshPath, FileMode.Open, FileAccess.Read, FileShare.Read); + + var component01 = Msh01.ReadComponent(mshFs, mshNres); + var component02 = Msh02.ReadComponent(mshFs, mshNres); + var component0A = Msh0A.ReadComponent(mshFs, mshNres); + var component07 = Msh07.ReadComponent(mshFs, mshNres); + var component0D = Msh0D.ReadComponent(mshFs, mshNres); + + // Triangle Vertex Indices + var component06 = Msh06.ReadComponent(mshFs, mshNres); + + // vertices + var component03 = Msh03.ReadComponent(mshFs, mshNres); + + var csv06 = new StreamWriter("06.csv", false, Encoding.UTF8); + + for (var i = 0; i < component06.Count; i += 3) + { + // csv06.WriteLine($"{component06[i]}"); + csv06.WriteLine($"{component06[i]}, {component06[i + 1]}, {component06[i + 2]}"); + } + + csv06.Dispose(); + + var csv03 = new StreamWriter("03.obj", false, Encoding.UTF8); + + csv03.WriteLine(); + for (var i = 7525; i < component03.Count; i++) + { + // csv06.WriteLine($"{component06[i]}"); + // csv03.WriteAsync($"o {i - 7525}"); + csv03.WriteLine( + $"v {component03[i].X.ToString("F2", CultureInfo.InvariantCulture)} {component03[i].Y.ToString("F2", CultureInfo.InvariantCulture)} {component03[i].Z.ToString("F2", CultureInfo.InvariantCulture)}"); + } + + for (var i = 10485; i < component06.Count; i += 3) + { + csv03.WriteLine($"f {component06[i] + 1} {component06[i + 1] + 1} {component06[i + 2] + 1}"); + } + + csv03.Dispose(); + + // --- Write OBJ --- + using var sw = new StreamWriter("test.obj", false, Encoding.UTF8); + + for (var index = 0; index < component03.Count; index++) + { + var v = component03[index]; + sw.WriteLine($"v {v.X:F8} {v.Y:F8} {v.Z:F8}"); + } + + for (var i = 0; i < 1; i++) + { + sw.WriteLine($"o elem_0d_{i}"); + var element0D = component0D[i]; + + Console.WriteLine($"Processing element 0D [{i}]:"); + + var elementsOf06 = component06 + .Skip(element0D.IndexInto06) + .Take(element0D.CountOf06) + .ToList(); + + Console.WriteLine($"\tCount of 06: {elementsOf06.Count}, starting from {element0D.IndexInto06}"); + var indexInto03 = element0D.IndexInto03; + + Console.WriteLine($"\tIndexInto03: {indexInto03}"); + + if (elementsOf06.Count < 3) + { + throw new Exception("Less than 3 points"); + } + } + } + public record Face(Vector3 P1, Vector3 P2, Vector3 P3); public static void ExportCube(string filePath, Vector3[] points) @@ -189,34 +285,6 @@ public class MshConverter } } - - private static List Read03Component(FileStream mshFs, NResArchive mshNres) - { - var verticesFileEntry = mshNres.Files.FirstOrDefault(x => x.FileType == "03 00 00 00"); - - if (verticesFileEntry is null) - { - throw new Exception("Archive doesn't contain vertices file (03)"); - } - - if (verticesFileEntry.ElementSize != 12) - { - throw new Exception("Vertices file (03) element size is not 12"); - } - - var verticesFile = new byte[verticesFileEntry.ElementCount * verticesFileEntry.ElementSize]; - mshFs.Seek(verticesFileEntry.OffsetInFile, SeekOrigin.Begin); - mshFs.ReadExactly(verticesFile, 0, verticesFile.Length); - - var vertices = verticesFile.Chunk(12).Select(x => new Vector3( - BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(0)), - BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(4)), - BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(8)) - ) - ).ToList(); - return vertices; - } - void Export(string filePath, IEnumerable vertices, List edges) { using (var writer = new StreamWriter(filePath)) diff --git a/ParkanPlayground/Program.cs b/ParkanPlayground/Program.cs index 940afdf..3d41a33 100644 --- a/ParkanPlayground/Program.cs +++ b/ParkanPlayground/Program.cs @@ -1,4 +1,4 @@ -using System.Buffers.Binary; +using System.Buffers.Binary; using Common; using MissionTmaLib.Parsing; using NResLib; @@ -10,6 +10,7 @@ using ParkanPlayground; var converter = new MshConverter(); converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\133_fr_m_bunker.msh"); +// converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\73_fr_m_brige.msh"); // converter.Convert("E:\\ParkanUnpacked\\intsys.rlb\\277_MESH_o_pws_l_01.msh"); // converter.Convert("E:\\ParkanUnpacked\\static.rlb\\2_MESH_s_stn_0_01.msh"); // converter.Convert("E:\\ParkanUnpacked\\bases.rlb\\25_MESH_R_H_02.msh"); \ No newline at end of file diff --git a/README.md b/README.md index da94244..21934f9 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,10 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' . Затем `Comp.ini` - тут системные функции, которые используются для загрузки объектов. +``` +IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4) +``` + - `Host.url` - этого файла нет - `palettes.lib` - тут палитры, но этот NRes пустой - `system.rlb` - не понятно что @@ -211,17 +215,16 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' . Загружается в `AniMesh.dll/LoadAniMesh` -- Тип 01 - заголовок +- Тип 01 - заголовок. Он хранит список деталей (submesh) в разных LOD ``` нулевому элементу добавляется флаг 0x1000000 - Хранит стейты меша (в один стейт может входить несколько submesh) Содержит 2 ссылки на файлы анимаций (короткие - файл 13, длинные - файл 08) Если интерполируется анимация -0.5s короче чем magic1 у файла 13 И у файла есть OffsetIntoFile13 И ushort значение в файле 13 по этому оффсету > IndexInFile08 (это по-моему выполняется всегда) Тогда вместо IndexInFile08 используется значение из файла 13 по этому оффсету (второй байт) ``` -- Тип 02 - описание submesh +- Тип 02 - описание одного LOD Submesh ``` Вначале идёт заголовок 0x8C (140) байт В заголовке: @@ -233,7 +236,7 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' . Далее инфа про куски меша ``` - Тип 03 - это вершины (vertex) -- Тип 06 - +- Тип 06 - индексы треугольников в файле 03 - Тип 04 - скорее всего какие-то цвета RGBA или типа того - Тип 08 - меш-анимации (см файл 01) ``` @@ -257,21 +260,19 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' . Буквально (hex) 00 01 01 02 ... ``` -- Тип 0A +- Тип 0A - ссылка на части меша, не упакованные в текущий меш (например у бункера 4 и 5 части хранятся в parts.rlb) ``` - Не имеет фиксированной длины. Хранит какие-то строки в следующем формате. + Не имеет фиксированной длины. Хранит строки в следующем формате. Игра обращается по индексу, пропуская суммарную длину и пропуская 4 байта на каждую строку (длина). т.е. буквально файл выглядит так 00 00 00 00 - пустая строка 03 00 00 00 - длина строки 1 73 74 72 00 - строка "str" + null terminator .. и повторяется до конца файла - Кол-во элементов из NRes должно быть равно кол-ву строк в этом файле, хотя игра это не проверяет. + Кол-во элементов из файла 01 должно быть равно кол-ву строк в этом файле, хотя игра это не проверяет. Если у элемента эта строка равна "central", ему выставляется флаг (flag |= 1) ``` -Тип 02 имеет заголовок 140 байт. - ## `.wea` Загружается в `World3D.dll/LoadMatManager` @@ -289,26 +290,32 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' . - `0xb` - IAnimation - `0xd` - IMatManager - `0xe` - ILightManager -- `0x10` - unknown (implemented by Wizard in Wizard.dll, also by Hallway in ArealMap.dll) +- `0x10` - IBehavior - `0x11` - IBasement -- `0x12` - ICamera2 - BufferingCamera +- `0x12` - ICamera2 или IBufferingCamera - `0x13` - IEffectManager - `0x14` - IPosition +- `0x15` - IAgent - `0x16` - ILifeSystem -- `0x17` - IBuilding +- `0x17` - IBuilding - точно он, т.к. ArealMap.CreateObject на него проверяет - `0x18` - IMesh2 -- `0x19` - unknown (implemented by Wizard in Wizard.dll, also by Agent in AniMesh.dll) +- `0x19` - IManManager - `0x20` - IJointMesh - `0x21` - IShade - `0x23` - IGameSettings - `0x24` - IGameObject2 +- `0x25` - unknown (implemented by AniMesh) +- `0x26` - unknown (implemented by AniMesh) +- `0x28` - ICollObject - `0x101` - 3DRender +- `0x105` - NResFile - `0x201` - IWizard - `0x202` - IItemManager - `0x203` - ICollManager - `0x301` - IArealMap - `0x302` - ISystemArealMap - `0x303` - IHallway +- `0x304` - Distributor - `0x401` - ISuperAI - `0x105` - NResFile - `0x106` - NResFileMetadata @@ -329,25 +336,26 @@ World3D.dll содержит функцию CreateGameSettings. Остальные наверное не трогают настройки. -| Resource ID | wOptionID | Name | Default | Description | -|:-----------:|:---------------:|:--------------------------:|:-------:|---------------------| -| 1 | 100 (0x64) | "Texture detail" | | | -| 2 | 101 (0x65) | "3D Sound" | | | -| 3 | 102 (0x66) | "Mouse sensitivity" | | | -| 4 | 103 (0x67) | "Joystick sensitivity" | | | -| 5 | !not a setting! | "Illegal wOptionID" | | | -| 6 | 104 (0x68) | "Wait for retrace" | | | -| 7 | 105 (0x69) | "Inverse mouse X" | | | -| 8 | 106 (0x6a) | "Inverse mouse Y" | | | -| 9 | 107 (0x6b) | "Inverse joystick X" | | | -| 10 | 108 (0x6c) | "Inverse joystick Y" | | | -| 11 | 109 (0x6d) | "Use BumpMapping" | | | -| 12 | 110 (0x6e) | "3D Sound quality" | | | -| 13 | 90 (0x5a) | "Reverse sound" | | | -| 14 | 91 (0x5b) | "Sound buffer frequency" | | | -| 15 | 92 (0x5c) | "Play sound buffer always" | | | -| 16 | 93 (0x5d) | "Select best sound device" | | | -| ---- | 30 (0x1e) | ShadeConfig | | из файла shade.cfg | +| Resource ID | wOptionID | Name | Default | Description | +|:-----------:|:---------------:|:--------------------------:|:-------:|--------------------| +| 1 | 100 (0x64) | "Texture detail" | | | +| 2 | 101 (0x65) | "3D Sound" | | | +| 3 | 102 (0x66) | "Mouse sensitivity" | | | +| 4 | 103 (0x67) | "Joystick sensitivity" | | | +| 5 | !not a setting! | "Illegal wOptionID" | | | +| 6 | 104 (0x68) | "Wait for retrace" | | | +| 7 | 105 (0x69) | "Inverse mouse X" | | | +| 8 | 106 (0x6a) | "Inverse mouse Y" | | | +| 9 | 107 (0x6b) | "Inverse joystick X" | | | +| 10 | 108 (0x6c) | "Inverse joystick Y" | | | +| 11 | 109 (0x6d) | "Use BumpMapping" | | | +| 12 | 110 (0x6e) | "3D Sound quality" | | | +| 13 | 90 (0x5a) | "Reverse sound" | | | +| 14 | 91 (0x5b) | "Sound buffer frequency" | | | +| 15 | 92 (0x5c) | "Play sound buffer always" | | | +| 16 | 93 (0x5d) | "Select best sound device" | | | +| ---- | 30 (0x1e) | ShadeConfig | | из файла shade.cfg | +| ---- | (0x8001e) | | | добавляет AniMesh | ## Контакты