mirror of
				https://github.com/sampletext32/ParkanPlayground.git
				synced 2025-11-04 07:19:45 +03:00 
			
		
		
		
	read msh 0A file
This commit is contained in:
		@@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.Buffers.Binary;
 | 
					using System.Buffers.Binary;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
using Common;
 | 
					using Common;
 | 
				
			||||||
using NResLib;
 | 
					using NResLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,6 +27,8 @@ public class MshConverter
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        using var mshFs = new FileStream(mshPath, FileMode.Open, FileAccess.Read, FileShare.Read);
 | 
					        using var mshFs = new FileStream(mshPath, FileMode.Open, FileAccess.Read, FileShare.Read);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        ReadComponent01(mshFs, mshNres);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var vertices = ReadVertices(verticesFileEntry, mshFs);
 | 
					        var vertices = ReadVertices(verticesFileEntry, mshFs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var edgesFileEntry = mshNres.Files.FirstOrDefault(x => x.FileType == "06 00 00 00");
 | 
					        var edgesFileEntry = mshNres.Files.FirstOrDefault(x => x.FileType == "06 00 00 00");
 | 
				
			||||||
@@ -52,6 +55,71 @@ public class MshConverter
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static List<string> TryRead0AComponent(FileStream mshFs, NResArchive archive)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var aFileEntry = archive.Files.FirstOrDefault(x => x.FileType == "0A 00 00 00");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (aFileEntry is null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var data = new byte[aFileEntry.FileLength];
 | 
				
			||||||
 | 
					        mshFs.Seek(aFileEntry.OffsetInFile, SeekOrigin.Begin);
 | 
				
			||||||
 | 
					        mshFs.ReadExactly(data, 0, data.Length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int pos = 0;
 | 
				
			||||||
 | 
					        var strings = new List<string>();
 | 
				
			||||||
 | 
					        while (pos < data.Length)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var len = BinaryPrimitives.ReadInt32LittleEndian(data.AsSpan(pos));
 | 
				
			||||||
 | 
					            if (len == 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                pos += 4; // empty entry, no string attached
 | 
				
			||||||
 | 
					                strings.Add(""); // add empty string
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // len is not 0, we need to read it
 | 
				
			||||||
 | 
					                var strBytes = data.AsSpan(pos + 4, len);
 | 
				
			||||||
 | 
					                var str = Encoding.UTF8.GetString(strBytes);
 | 
				
			||||||
 | 
					                strings.Add(str);
 | 
				
			||||||
 | 
					                pos += len + 4 + 1; // skip length prefix and string itself, +1, because it's null-terminated
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (strings.Count != aFileEntry.ElementCount)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new Exception("String count mismatch in 0A component");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return strings;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static void ReadComponent01(FileStream mshFs, NResArchive archive)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var headerFileEntry = archive.Files.FirstOrDefault(x => x.FileType == "01 00 00 00");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (headerFileEntry is null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new Exception("Archive doesn't contain header file (01)");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var headerData = new byte[headerFileEntry.ElementCount * headerFileEntry.ElementSize];
 | 
				
			||||||
 | 
					        mshFs.Seek(headerFileEntry.OffsetInFile, SeekOrigin.Begin);
 | 
				
			||||||
 | 
					        mshFs.ReadExactly(headerData, 0, headerData.Length);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var descriptions = TryRead0AComponent(mshFs, archive);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var chunks = headerData.Chunk(headerFileEntry.ElementSize).ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var converted = chunks.Select(x => new
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Byte00 = x[0],
 | 
				
			||||||
 | 
					            Byte01 = x[1],
 | 
				
			||||||
 | 
					            Bytes0204 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(2)),
 | 
				
			||||||
 | 
					        }).ToList();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        _ = 5;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static List<Vector3> ReadVertices(ListMetadataItem verticesFileEntry, FileStream mshFs)
 | 
					    private static List<Vector3> ReadVertices(ListMetadataItem verticesFileEntry, FileStream mshFs)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var verticesFile = new byte[verticesFileEntry.ElementCount * verticesFileEntry.ElementSize];
 | 
					        var verticesFile = new byte[verticesFileEntry.ElementCount * verticesFileEntry.ElementSize];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,9 @@ using MissionTmaLib.Parsing;
 | 
				
			|||||||
using NResLib;
 | 
					using NResLib;
 | 
				
			||||||
using ParkanPlayground;
 | 
					using ParkanPlayground;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var cpDatEntryConverter = new CpDatEntryConverter();
 | 
					// var cpDatEntryConverter = new CpDatEntryConverter();
 | 
				
			||||||
 | 
					// cpDatEntryConverter.Convert();
 | 
				
			||||||
cpDatEntryConverter.Convert();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
var converter = new MshConverter();
 | 
					var converter = new MshConverter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\161_fr_b_tower.msh");
 | 
					converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\133_fr_m_bunker.msh");
 | 
				
			||||||
							
								
								
									
										37
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								README.md
									
									
									
									
									
								
							@@ -196,20 +196,51 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
 | 
				
			|||||||
|     11     |   Research    | `misload.dll LoadResearch`     |
 | 
					|     11     |   Research    | `misload.dll LoadResearch`     |
 | 
				
			||||||
|     12     |     Agent     | `animesh.dll LoadAgent`        |
 | 
					|     12     |     Agent     | `animesh.dll LoadAgent`        |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Будет дополняться по мере реверса/
 | 
					Будет дополняться по мере реверса.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Всем этим функциям передаётся `nres_file_name, nres_entry_name, 0, player_id`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## `fr FORT` файл
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Всегда 0x80 байт
 | 
				
			||||||
 | 
					Содержит 2 ссылки на файлы:
 | 
				
			||||||
 | 
					- `.bas`
 | 
				
			||||||
 | 
					- `.ctl` - вызывается `LoadAgent`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## `.msh`
 | 
					## `.msh`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Загружается в `AniMesh.dll/LoadAniMesh`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Тип 03 - это вершины (vertex)
 | 
					- Тип 03 - это вершины (vertex)
 | 
				
			||||||
- Тип 06 - это рёбра (edge)
 | 
					- Тип 06 - это рёбра (edge)
 | 
				
			||||||
- Тип 04 - скорее всего какие-то цвета RGBA или типа того
 | 
					- Тип 04 - скорее всего какие-то цвета RGBA или типа того
 | 
				
			||||||
- Тип 12 - microtexture mapping
 | 
					- Тип 12 - microtexture mapping
 | 
				
			||||||
 | 
					- Тип 0A
 | 
				
			||||||
 | 
					  ```
 | 
				
			||||||
 | 
					  Не имеет фиксированной длины. Хранит какие-то строки в следующем формате.
 | 
				
			||||||
 | 
					  Игра обращается по индексу, пропуская суммарную длину и пропуская 4 байта на каждую строку (длина).
 | 
				
			||||||
 | 
					  т.е. буквально файл выглядит так
 | 
				
			||||||
 | 
					  00 00 00 00 - пустая строка
 | 
				
			||||||
 | 
					  03 00 00 00 - длина строки 1
 | 
				
			||||||
 | 
					  73 74 72 00 - строка "str" + null terminator
 | 
				
			||||||
 | 
					  .. и повторяется до конца файла
 | 
				
			||||||
 | 
					  Кол-во элементов из NRes должно быть равно кол-ву строк в этом файле, хотя игра это не проверяет.
 | 
				
			||||||
 | 
					  ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Тип 02 имеет какой-то заголовок. 
 | 
					Тип 02 имеет заголовок 140 байт.
 | 
				
			||||||
Игра сначала читает из него первые 96 байт. А затем пропускает с начала 148 байт
 | 
					Игра сначала читает из него первые 96 байт. А затем пропускает с начала 148 байт
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## `.wea`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Загружается в `World3D.dll/LoadMatManager`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## `.wea`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Загружается в `World3D.dll/LoadMatManager`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Внутренняя система ID
 | 
					# Внутренняя система ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `1` - IMesh2 ???
 | 
				
			||||||
- `4` - IShader
 | 
					- `4` - IShader
 | 
				
			||||||
- `5` - ITerrain
 | 
					- `5` - ITerrain
 | 
				
			||||||
- `6` - IGameObject (0x138)
 | 
					- `6` - IGameObject (0x138)
 | 
				
			||||||
@@ -226,7 +257,7 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
 | 
				
			|||||||
- `0x16` - ILifeSystem
 | 
					- `0x16` - ILifeSystem
 | 
				
			||||||
- `0x17` - IBuilding
 | 
					- `0x17` - IBuilding
 | 
				
			||||||
- `0x18` - IMesh2
 | 
					- `0x18` - IMesh2
 | 
				
			||||||
- `0x19` - unknown (implemented by Wizard in Wizard.dll)
 | 
					- `0x19` - unknown (implemented by Wizard in Wizard.dll, also by Agent in AniMesh.dll)
 | 
				
			||||||
- `0x20` - IJointMesh
 | 
					- `0x20` - IJointMesh
 | 
				
			||||||
- `0x21` - IShade
 | 
					- `0x21` - IShade
 | 
				
			||||||
- `0x24` - IGameObject2
 | 
					- `0x24` - IGameObject2
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user