0
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-09-13 10:20:28 +03:00

read msh 0A file

This commit is contained in:
bird_egop
2025-08-23 19:03:03 +03:00
parent 4b1c4bf3aa
commit 35af4da326
3 changed files with 105 additions and 7 deletions

View File

@@ -1,4 +1,5 @@
using System.Buffers.Binary;
using System.Text;
using Common;
using NResLib;
@@ -26,6 +27,8 @@ public class MshConverter
using var mshFs = new FileStream(mshPath, FileMode.Open, FileAccess.Read, FileShare.Read);
ReadComponent01(mshFs, mshNres);
var vertices = ReadVertices(verticesFileEntry, mshFs);
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)
{
var verticesFile = new byte[verticesFileEntry.ElementCount * verticesFileEntry.ElementSize];

View File

@@ -4,10 +4,9 @@ using MissionTmaLib.Parsing;
using NResLib;
using ParkanPlayground;
var cpDatEntryConverter = new CpDatEntryConverter();
cpDatEntryConverter.Convert();
// var cpDatEntryConverter = new CpDatEntryConverter();
// cpDatEntryConverter.Convert();
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");

View File

@@ -196,20 +196,51 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
| 11 | Research | `misload.dll LoadResearch` |
| 12 | Agent | `animesh.dll LoadAgent` |
Будет дополняться по мере реверса/
Будет дополняться по мере реверса.
Всем этим функциям передаётся `nres_file_name, nres_entry_name, 0, player_id`
## `fr FORT` файл
Всегда 0x80 байт
Содержит 2 ссылки на файлы:
- `.bas`
- `.ctl` - вызывается `LoadAgent`
## `.msh`
Загружается в `AniMesh.dll/LoadAniMesh`
- Тип 03 - это вершины (vertex)
- Тип 06 - это рёбра (edge)
- Тип 04 - скорее всего какие-то цвета RGBA или типа того
- Тип 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 байт
## `.wea`
Загружается в `World3D.dll/LoadMatManager`
## `.wea`
Загружается в `World3D.dll/LoadMatManager`
# Внутренняя система ID
- `1` - IMesh2 ???
- `4` - IShader
- `5` - ITerrain
- `6` - IGameObject (0x138)
@@ -226,7 +257,7 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
- `0x16` - ILifeSystem
- `0x17` - IBuilding
- `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
- `0x21` - IShade
- `0x24` - IGameObject2