mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-05-18 19:31:17 +03:00
распаковка миссий - tma
This commit is contained in:
parent
bba3f2a810
commit
b2010618ee
10
MissionDataUnpacker/MissionDataUnpacker.csproj
Normal file
10
MissionDataUnpacker/MissionDataUnpacker.csproj
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
219
MissionDataUnpacker/Program.cs
Normal file
219
MissionDataUnpacker/Program.cs
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
// var missionFilePath = "C:\\Program Files (x86)\\Nikita\\Iron Strategy\\MISSIONS\\Autodemo.00\\data.tma";
|
||||||
|
// var missionFilePath = "C:\\Program Files (x86)\\Nikita\\Iron Strategy\\MISSIONS\\Tutorial.01\\data.tma";
|
||||||
|
var missionFilePath = "C:\\Program Files (x86)\\Nikita\\Iron Strategy\\MISSIONS\\CAMPAIGN\\CAMPAIGN.01\\Mission.03\\data.tma";
|
||||||
|
// var missionFilePath = "C:\\Program Files (x86)\\Nikita\\Iron Strategy\\MISSIONS\\Single.01\\data.tma";
|
||||||
|
var fs = new FileStream(missionFilePath, FileMode.Open);
|
||||||
|
|
||||||
|
var arealData = LoadAreals(fs);
|
||||||
|
|
||||||
|
var clans = LoadClans(fs);
|
||||||
|
|
||||||
|
_ = 5;
|
||||||
|
// LoadGameObjects(fs);
|
||||||
|
|
||||||
|
ArealsFileData LoadAreals(FileStream fileStream)
|
||||||
|
{
|
||||||
|
var unusedHeader = fileStream.ReadInt32LittleEndian();
|
||||||
|
var arealCount = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
// В демо миссии нет ареалов, ровно как и в первой миссии кампании
|
||||||
|
// Span<byte> arealBuffer = stackalloc byte[12];
|
||||||
|
|
||||||
|
List<ArealInfo> infos = [];
|
||||||
|
for (var i = 0; i < arealCount; i++)
|
||||||
|
{
|
||||||
|
// игра читает 4 байта - видимо количество
|
||||||
|
var unknown4Bytes = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
var count = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
List<Vector3> vectors = [];
|
||||||
|
if (0 < count)
|
||||||
|
{
|
||||||
|
for (var i1 = 0; i1 < count; i1++)
|
||||||
|
{
|
||||||
|
// потом читает 12 байт разом (тут видимо какой-то вектор)
|
||||||
|
var unknownFloat1 = fileStream.ReadFloatLittleEndian();
|
||||||
|
var unknownFloat2 = fileStream.ReadFloatLittleEndian();
|
||||||
|
var unknownFloat3 = fileStream.ReadFloatLittleEndian();
|
||||||
|
|
||||||
|
vectors.Add(new Vector3(unknownFloat1, unknownFloat2, unknownFloat3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
infos.Add(new ArealInfo(unknown4Bytes, count, vectors));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArealsFileData(unusedHeader, arealCount, infos);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClansFileData? LoadClans(FileStream fileStream)
|
||||||
|
{
|
||||||
|
var clanFeatureSet = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
if (clanFeatureSet is <= 0 or >= 7) return null;
|
||||||
|
|
||||||
|
var treeInfoCount = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
List<ClanTreeInfo> infos = [];
|
||||||
|
for (var i = 0; i < treeInfoCount; i++)
|
||||||
|
{
|
||||||
|
var clanTreeInfo = new ClanTreeInfo();
|
||||||
|
|
||||||
|
clanTreeInfo.ClanName = fileStream.ReadLengthPrefixedString();
|
||||||
|
clanTreeInfo.UnkInt1 = fileStream.ReadInt32LittleEndian();
|
||||||
|
clanTreeInfo.X = fileStream.ReadFloatLittleEndian();
|
||||||
|
clanTreeInfo.Y = fileStream.ReadFloatLittleEndian();
|
||||||
|
clanTreeInfo.ClanType = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
if (1 < clanFeatureSet)
|
||||||
|
{
|
||||||
|
// MISSIONS\SCRIPTS\default
|
||||||
|
// MISSIONS\SCRIPTS\tut1_pl
|
||||||
|
// MISSIONS\SCRIPTS\tut1_en
|
||||||
|
clanTreeInfo.UnkString2 = fileStream.ReadLengthPrefixedString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (2 < clanFeatureSet)
|
||||||
|
{
|
||||||
|
clanTreeInfo.UnknownClanPartCount = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
// тут игра читает число, затем 12 байт и ещё 2 числа
|
||||||
|
|
||||||
|
List<UnknownClanTreeInfoPart> unknownClanTreeInfoParts = [];
|
||||||
|
for (var i1 = 0; i1 < clanTreeInfo.UnknownClanPartCount; i1++)
|
||||||
|
{
|
||||||
|
unknownClanTreeInfoParts.Add(
|
||||||
|
new UnknownClanTreeInfoPart(
|
||||||
|
fileStream.ReadInt32LittleEndian(),
|
||||||
|
new Vector3(
|
||||||
|
fileStream.ReadFloatLittleEndian(),
|
||||||
|
fileStream.ReadFloatLittleEndian(),
|
||||||
|
fileStream.ReadFloatLittleEndian()
|
||||||
|
),
|
||||||
|
fileStream.ReadFloatLittleEndian(),
|
||||||
|
fileStream.ReadFloatLittleEndian()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
clanTreeInfo.UnknownParts = unknownClanTreeInfoParts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (3 < clanFeatureSet)
|
||||||
|
{
|
||||||
|
// MISSIONS\SCRIPTS\auto.trf
|
||||||
|
// MISSIONS\SCRIPTS\data.trf
|
||||||
|
// указатель на NRes файл с данными
|
||||||
|
// может быть пустым, например у Ntrl в туториале
|
||||||
|
clanTreeInfo.ResearchNResPath = fileStream.ReadLengthPrefixedString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (4 < clanFeatureSet)
|
||||||
|
{
|
||||||
|
clanTreeInfo.UnkInt3 = fileStream.ReadInt32LittleEndian();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (5 < clanFeatureSet)
|
||||||
|
{
|
||||||
|
clanTreeInfo.AlliesMapCount = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
// тут какая-то мапа
|
||||||
|
// в демо миссии тут
|
||||||
|
// player -> 1
|
||||||
|
// player2 -> 0
|
||||||
|
|
||||||
|
// в туториале
|
||||||
|
// Plr -> 1
|
||||||
|
// Trgt -> 1
|
||||||
|
// Enm -> 0
|
||||||
|
// Ntrl -> 1
|
||||||
|
Dictionary<string, int> map = [];
|
||||||
|
for (var i1 = 0; i1 < clanTreeInfo.AlliesMapCount; i1++)
|
||||||
|
{
|
||||||
|
var keyIdString = fileStream.ReadLengthPrefixedString();
|
||||||
|
// это число всегда либо 0 либо 1
|
||||||
|
var unkNumber = fileStream.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
map[keyIdString] = unkNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
clanTreeInfo.AlliesMap = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
infos.Add(clanTreeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
var clanInfo = new ClansFileData(clanFeatureSet, treeInfoCount, infos);
|
||||||
|
|
||||||
|
return clanInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static int ReadInt32LittleEndian(this FileStream fs)
|
||||||
|
{
|
||||||
|
Span<byte> buf = stackalloc byte[4];
|
||||||
|
fs.ReadExactly(buf);
|
||||||
|
|
||||||
|
return BinaryPrimitives.ReadInt32LittleEndian(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float ReadFloatLittleEndian(this FileStream fs)
|
||||||
|
{
|
||||||
|
Span<byte> buf = stackalloc byte[4];
|
||||||
|
fs.ReadExactly(buf);
|
||||||
|
|
||||||
|
return BinaryPrimitives.ReadSingleLittleEndian(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReadLengthPrefixedString(this FileStream fs)
|
||||||
|
{
|
||||||
|
var len = fs.ReadInt32LittleEndian();
|
||||||
|
|
||||||
|
var buffer = new byte[len];
|
||||||
|
|
||||||
|
fs.ReadExactly(buffer, 0, len);
|
||||||
|
|
||||||
|
return Encoding.ASCII.GetString(buffer, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ArealsFileData(int UnusedHeader, int ArealCount, List<ArealInfo> ArealInfos);
|
||||||
|
|
||||||
|
public record ArealInfo(int Index, int CoordsCount, List<Vector3> Coords);
|
||||||
|
|
||||||
|
public record Vector3(float X, float Y, float Z);
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
|
public record ClansFileData(int ClanFeatureSet, int TreeCount, List<ClanTreeInfo> TreeInfos);
|
||||||
|
|
||||||
|
public class ClanTreeInfo
|
||||||
|
{
|
||||||
|
public string ClanName { get; set; }
|
||||||
|
public int UnkInt1 { get; set; }
|
||||||
|
public float X { get; set; }
|
||||||
|
public float Y { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 1 - игрок, 2 AI, 3 - нейтральный
|
||||||
|
/// </summary>
|
||||||
|
public int ClanType { get; set; }
|
||||||
|
public string UnkString2 { get; set; }
|
||||||
|
public int UnknownClanPartCount { get; set; }
|
||||||
|
public List<UnknownClanTreeInfoPart> UnknownParts { get; set; }
|
||||||
|
public string ResearchNResPath { get; set; }
|
||||||
|
public int UnkInt3 { get; set; }
|
||||||
|
public int AlliesMapCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// мапа союзников (ключ - имя клана, значение - число, всегда либо 0 либо 1)
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, int> AlliesMap { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public record UnknownClanTreeInfoPart(int UnkInt1, Vector3 UnkVector, float UnkInt2, float UnkInt3);
|
@ -14,6 +14,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeshUnpacker", "MeshUnpacke
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TexmLib", "TexmLib\TexmLib.csproj", "{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TexmLib", "TexmLib\TexmLib.csproj", "{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MissionDataUnpacker", "MissionDataUnpacker\MissionDataUnpacker.csproj", "{7BF5C860-9194-4AF2-B5DA-216F98B03DBE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -48,5 +50,9 @@ Global
|
|||||||
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7BF5C860-9194-4AF2-B5DA-216F98B03DBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7BF5C860-9194-4AF2-B5DA-216F98B03DBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7BF5C860-9194-4AF2-B5DA-216F98B03DBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7BF5C860-9194-4AF2-B5DA-216F98B03DBE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
Loading…
x
Reference in New Issue
Block a user