mirror of
				https://github.com/sampletext32/ParkanPlayground.git
				synced 2025-11-04 07:19:45 +03:00 
			
		
		
		
	Allow to view cp .dat in UI
This commit is contained in:
		
							
								
								
									
										12
									
								
								CpDatLib/CpDatEntry.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								CpDatLib/CpDatEntry.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					namespace CpDatLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public record CpDatEntry(
 | 
				
			||||||
 | 
					    string ArchiveFile,
 | 
				
			||||||
 | 
					    string ArchiveEntryName,
 | 
				
			||||||
 | 
					    int Magic1,
 | 
				
			||||||
 | 
					    int Magic2,
 | 
				
			||||||
 | 
					    string Description,
 | 
				
			||||||
 | 
					    int Magic3,
 | 
				
			||||||
 | 
					    int ChildCount, // игра не хранит это число в объекте, но оно есть в файле
 | 
				
			||||||
 | 
					    List<CpDatEntry> Children
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
							
								
								
									
										5
									
								
								CpDatLib/CpDatLib.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								CpDatLib/CpDatLib.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <ProjectReference Include="..\Common\Common.csproj" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
							
								
								
									
										3
									
								
								CpDatLib/CpDatParseResult.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								CpDatLib/CpDatParseResult.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					namespace CpDatLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public record CpDatParseResult(CpDatScheme? Scheme, string? Error);
 | 
				
			||||||
							
								
								
									
										68
									
								
								CpDatLib/CpDatParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								CpDatLib/CpDatParser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					using Common;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace CpDatLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CpDatParser
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static CpDatParseResult Parse(string filePath)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Span<byte> f0f1 = stackalloc byte[4];
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (fs.Length < 8)
 | 
				
			||||||
 | 
					            return new CpDatParseResult(null, "File too small to be a valid \"cp\" .dat file.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fs.ReadExactly(f0f1);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (f0f1[0] != 0xf1 || f0f1[1] != 0xf0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new CpDatParseResult(null, "File does not start with expected header bytes f1_f0");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var schemeType = (SchemeType)fs.ReadInt32LittleEndian();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var entryLength = 0x6c + 4; // нам нужно прочитать 0x6c (108) байт - это root, и ещё 4 байта - кол-во вложенных объектов
 | 
				
			||||||
 | 
					        if ((fs.Length - 8) % entryLength != 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new CpDatParseResult(null, "File size is not valid according to expected entry length.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CpDatEntry root = ReadEntryRecursive(fs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var scheme = new CpDatScheme(schemeType, root);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new CpDatParseResult(scheme, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static CpDatEntry ReadEntryRecursive(FileStream fs)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var str1 = fs.ReadNullTerminatedString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fs.Seek(32 - str1.Length - 1, SeekOrigin.Current); // -1 ignore null terminator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var str2 = fs.ReadNullTerminatedString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fs.Seek(32 - str2.Length - 1, SeekOrigin.Current); // -1 ignore null terminator
 | 
				
			||||||
 | 
					        var magic1 = fs.ReadInt32LittleEndian();
 | 
				
			||||||
 | 
					        var magic2 = fs.ReadInt32LittleEndian();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var descriptionString = fs.ReadNullTerminatedString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fs.Seek(32 - descriptionString.Length - 1, SeekOrigin.Current); // -1 ignore null terminator
 | 
				
			||||||
 | 
					        var magic3 = fs.ReadInt32LittleEndian();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // игра не читает количество внутрь схемы, вместо этого она сразу рекурсией читает нужно количество вложенных объектов
 | 
				
			||||||
 | 
					        var childCount = fs.ReadInt32LittleEndian();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        List<CpDatEntry> children = new List<CpDatEntry>(childCount);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for (var i = 0; i < childCount; i++)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var child = ReadEntryRecursive(fs);
 | 
				
			||||||
 | 
					            children.Add(child);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new CpDatEntry(str1, str2, magic1, magic2, descriptionString, magic3, childCount, Children: children);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								CpDatLib/CpDatScheme.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								CpDatLib/CpDatScheme.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					namespace CpDatLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public record CpDatScheme(SchemeType Type, CpDatEntry Root);
 | 
				
			||||||
							
								
								
									
										29
									
								
								CpDatLib/CpEntryType.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								CpDatLib/CpEntryType.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					namespace CpDatLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum SchemeType : uint
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    ClassBuilding = 0x80000000,
 | 
				
			||||||
 | 
					    ClassRobot = 0x01000000,
 | 
				
			||||||
 | 
					    ClassAnimal = 0x20000000,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BunkerSmall = 0x80010000,
 | 
				
			||||||
 | 
					    BunkerMedium = 0x80020000,
 | 
				
			||||||
 | 
					    BunkerLarge = 0x80040000,
 | 
				
			||||||
 | 
					    Generator = 0x80000002,
 | 
				
			||||||
 | 
					    Mine = 0x80000004,
 | 
				
			||||||
 | 
					    Storage = 0x80000008,
 | 
				
			||||||
 | 
					    Plant = 0x80000010,
 | 
				
			||||||
 | 
					    Hangar = 0x80000040,
 | 
				
			||||||
 | 
					    TowerMedium = 0x80100000,
 | 
				
			||||||
 | 
					    TowerLarge = 0x80200000,
 | 
				
			||||||
 | 
					    MainTeleport = 0x80000200,
 | 
				
			||||||
 | 
					    Institute = 0x80000400,
 | 
				
			||||||
 | 
					    Bridge = 0x80001000,
 | 
				
			||||||
 | 
					    Ruine = 0x80002000,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RobotTransport = 0x01002000,
 | 
				
			||||||
 | 
					    RobotBuilder = 0x01004000,
 | 
				
			||||||
 | 
					    RobotBattleunit = 0x01008000,
 | 
				
			||||||
 | 
					    RobotHq = 0x01010000,
 | 
				
			||||||
 | 
					    RobotHero = 0x01020000,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -56,6 +56,7 @@ public class App
 | 
				
			|||||||
            serviceCollection.AddSingleton(new BinaryExplorerViewModel());
 | 
					            serviceCollection.AddSingleton(new BinaryExplorerViewModel());
 | 
				
			||||||
            serviceCollection.AddSingleton(new ScrViewModel());
 | 
					            serviceCollection.AddSingleton(new ScrViewModel());
 | 
				
			||||||
            serviceCollection.AddSingleton(new VarsetViewModel());
 | 
					            serviceCollection.AddSingleton(new VarsetViewModel());
 | 
				
			||||||
 | 
					            serviceCollection.AddSingleton(new CpDatSchemeViewModel());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var serviceProvider = serviceCollection.BuildServiceProvider();
 | 
					            var serviceProvider = serviceCollection.BuildServiceProvider();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										120
									
								
								NResUI/ImGuiUI/CpDatSchemeExplorer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								NResUI/ImGuiUI/CpDatSchemeExplorer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
				
			|||||||
 | 
					using CpDatLib;
 | 
				
			||||||
 | 
					using ImGuiNET;
 | 
				
			||||||
 | 
					using NResUI.Abstractions;
 | 
				
			||||||
 | 
					using NResUI.Models;
 | 
				
			||||||
 | 
					using ScrLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NResUI.ImGuiUI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CpDatSchemeExplorer : IImGuiPanel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private readonly CpDatSchemeViewModel _viewModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CpDatSchemeExplorer(CpDatSchemeViewModel viewModel)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _viewModel = viewModel;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void OnImGuiRender()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (ImGui.Begin("cp .dat Scheme Explorer"))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var cpDat = _viewModel.CpDatScheme;
 | 
				
			||||||
 | 
					            if (_viewModel.HasFile && cpDat is not null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ImGui.Text("Тип объекта в схеме: ");
 | 
				
			||||||
 | 
					                ImGui.SameLine();
 | 
				
			||||||
 | 
					                ImGui.Text(cpDat.Type.ToString("G"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var root = cpDat.Root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                DrawEntry(root, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ImGui.Separator();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (ImGui.BeginTable("content", 7,
 | 
				
			||||||
 | 
					                        ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoHostExtendX))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ImGui.TableSetupColumn("Уровень вложенности");
 | 
				
			||||||
 | 
					                    ImGui.TableSetupColumn("Архив");
 | 
				
			||||||
 | 
					                    ImGui.TableSetupColumn("Элемент");
 | 
				
			||||||
 | 
					                    ImGui.TableSetupColumn("Magic1");
 | 
				
			||||||
 | 
					                    ImGui.TableSetupColumn("Magic2");
 | 
				
			||||||
 | 
					                    ImGui.TableSetupColumn("Описание");
 | 
				
			||||||
 | 
					                    ImGui.TableSetupColumn("Magic3");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    ImGui.TableHeadersRow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    for (int i = 0; i < _viewModel.FlatList.Count; i++)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        ImGui.TableNextRow();
 | 
				
			||||||
 | 
					                        ImGui.TableNextColumn();
 | 
				
			||||||
 | 
					                        ImGui.Text(_viewModel.FlatList[i].Level.ToString());
 | 
				
			||||||
 | 
					                        ImGui.TableNextColumn();
 | 
				
			||||||
 | 
					                        ImGui.Text(_viewModel.FlatList[i].Entry.ArchiveFile);
 | 
				
			||||||
 | 
					                        ImGui.TableNextColumn();
 | 
				
			||||||
 | 
					                        ImGui.Text(_viewModel.FlatList[i].Entry.ArchiveEntryName);
 | 
				
			||||||
 | 
					                        ImGui.TableNextColumn();
 | 
				
			||||||
 | 
					                        ImGui.Text(_viewModel.FlatList[i].Entry.Magic1.ToString());
 | 
				
			||||||
 | 
					                        ImGui.TableNextColumn();
 | 
				
			||||||
 | 
					                        ImGui.Text(_viewModel.FlatList[i].Entry.Magic2.ToString());
 | 
				
			||||||
 | 
					                        ImGui.TableNextColumn();
 | 
				
			||||||
 | 
					                        ImGui.Text(_viewModel.FlatList[i].Entry.Description);
 | 
				
			||||||
 | 
					                        ImGui.TableNextColumn();
 | 
				
			||||||
 | 
					                        ImGui.Text(_viewModel.FlatList[i].Entry.Magic3.ToString());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    ImGui.EndTable();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                void DrawEntry(CpDatEntry entry, int index)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (ImGui.TreeNodeEx(
 | 
				
			||||||
 | 
					                            $"Элемент: \"{entry.ArchiveFile}/{entry.ArchiveEntryName}\" - {entry.Description}##entry_{index}"))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        ImGui.Text("Magic1: ");
 | 
				
			||||||
 | 
					                        ImGui.SameLine();
 | 
				
			||||||
 | 
					                        ImGui.Text(entry.Magic1.ToString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        ImGui.Text("Magic2: ");
 | 
				
			||||||
 | 
					                        ImGui.SameLine();
 | 
				
			||||||
 | 
					                        ImGui.Text(entry.Magic2.ToString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        ImGui.Text("Magic3: ");
 | 
				
			||||||
 | 
					                        ImGui.SameLine();
 | 
				
			||||||
 | 
					                        ImGui.Text(entry.Magic3.ToString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        ImGui.Text("Кол-во дочерних элементов: ");
 | 
				
			||||||
 | 
					                        ImGui.SameLine();
 | 
				
			||||||
 | 
					                        ImGui.Text(entry.ChildCount.ToString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (entry.Children.Count > 0)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (ImGui.TreeNodeEx("Дочерние элементы"))
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                foreach (var child in entry.Children)
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    DrawEntry(child, ++index);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                ImGui.TreePop();
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        ImGui.TreePop();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (_viewModel.Error is not null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ImGui.Text(_viewModel.Error);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ImGui.Text("cp .dat не открыт");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ImGui.End();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.Numerics;
 | 
					using System.Numerics;
 | 
				
			||||||
 | 
					using CpDatLib;
 | 
				
			||||||
using ImGuiNET;
 | 
					using ImGuiNET;
 | 
				
			||||||
using MissionTmaLib;
 | 
					using MissionTmaLib;
 | 
				
			||||||
using MissionTmaLib.Parsing;
 | 
					using MissionTmaLib.Parsing;
 | 
				
			||||||
@@ -18,6 +19,7 @@ namespace NResUI.ImGuiUI
 | 
				
			|||||||
        ScrViewModel scrViewModel,
 | 
					        ScrViewModel scrViewModel,
 | 
				
			||||||
        MissionTmaViewModel missionTmaViewModel,
 | 
					        MissionTmaViewModel missionTmaViewModel,
 | 
				
			||||||
        VarsetViewModel varsetViewModel,
 | 
					        VarsetViewModel varsetViewModel,
 | 
				
			||||||
 | 
					        CpDatSchemeViewModel cpDatSchemeViewModel,
 | 
				
			||||||
        MessageBoxModalPanel messageBox)
 | 
					        MessageBoxModalPanel messageBox)
 | 
				
			||||||
        : IImGuiPanel
 | 
					        : IImGuiPanel
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -121,6 +123,21 @@ namespace NResUI.ImGuiUI
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (ImGui.MenuItem("Open cp .dat Scheme File"))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var result = Dialog.FileOpen("dat");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (result.IsOk)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            var path = result.Path;
 | 
				
			||||||
 | 
					                            var parseResult = CpDatParser.Parse(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            cpDatSchemeViewModel.SetParseResult(parseResult, path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            Console.WriteLine("Read cp .dat");
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (nResExplorerViewModel.HasFile)
 | 
					                    if (nResExplorerViewModel.HasFile)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        if (ImGui.MenuItem("Экспортировать NRes"))
 | 
					                        if (ImGui.MenuItem("Экспортировать NRes"))
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								NResUI/Models/CpDatSchemeViewModel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								NResUI/Models/CpDatSchemeViewModel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					using CpDatLib;
 | 
				
			||||||
 | 
					using ScrLib;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace NResUI.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CpDatSchemeViewModel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public bool HasFile { get; set; }
 | 
				
			||||||
 | 
					    public string? Error { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CpDatScheme? CpDatScheme { get; set; }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public List<(int Level, CpDatEntry Entry)> FlatList { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public string? Path { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void SetParseResult(CpDatParseResult parseResult, string path)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        CpDatScheme = parseResult.Scheme;
 | 
				
			||||||
 | 
					        Error = parseResult.Error;
 | 
				
			||||||
 | 
					        HasFile = true;
 | 
				
			||||||
 | 
					        Path = path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (CpDatScheme is not null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            FlatList = [];
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            CollectEntries(CpDatScheme.Root, 0);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            void CollectEntries(CpDatEntry entry, int level)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                FlatList.Add((level, entry));
 | 
				
			||||||
 | 
					                foreach (var child in entry.Children)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    CollectEntries(child, level + 1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -16,6 +16,7 @@
 | 
				
			|||||||
    </ItemGroup>
 | 
					    </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <ItemGroup>
 | 
					    <ItemGroup>
 | 
				
			||||||
 | 
					      <ProjectReference Include="..\CpDatLib\CpDatLib.csproj" />
 | 
				
			||||||
      <ProjectReference Include="..\MissionTmaLib\MissionTmaLib.csproj" />
 | 
					      <ProjectReference Include="..\MissionTmaLib\MissionTmaLib.csproj" />
 | 
				
			||||||
      <ProjectReference Include="..\NResLib\NResLib.csproj" />
 | 
					      <ProjectReference Include="..\NResLib\NResLib.csproj" />
 | 
				
			||||||
      <ProjectReference Include="..\ScrLib\ScrLib.csproj" />
 | 
					      <ProjectReference Include="..\ScrLib\ScrLib.csproj" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
    <File Path="README.md" />
 | 
					    <File Path="README.md" />
 | 
				
			||||||
  </Folder>
 | 
					  </Folder>
 | 
				
			||||||
  <Project Path="Common\Common.csproj" Type="Classic C#" />
 | 
					  <Project Path="Common\Common.csproj" Type="Classic C#" />
 | 
				
			||||||
 | 
					  <Project Path="CpDatLib\CpDatLib.csproj" Type="Classic C#" />
 | 
				
			||||||
  <Project Path="MeshUnpacker/MeshUnpacker.csproj" />
 | 
					  <Project Path="MeshUnpacker/MeshUnpacker.csproj" />
 | 
				
			||||||
  <Project Path="MissionDataUnpacker/MissionDataUnpacker.csproj" />
 | 
					  <Project Path="MissionDataUnpacker/MissionDataUnpacker.csproj" />
 | 
				
			||||||
  <Project Path="MissionTmaLib/MissionTmaLib.csproj" />
 | 
					  <Project Path="MissionTmaLib/MissionTmaLib.csproj" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,161 +0,0 @@
 | 
				
			|||||||
using Common;
 | 
					 | 
				
			||||||
using MissionTmaLib.Parsing;
 | 
					 | 
				
			||||||
using NResLib;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace ParkanPlayground;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// <summary>
 | 
					 | 
				
			||||||
/// Игра называет этот объект "схемой"
 | 
					 | 
				
			||||||
/// </summary>
 | 
					 | 
				
			||||||
/// <remarks>
 | 
					 | 
				
			||||||
/// В игре файл .dat читается в ArealMap.dll/CreateObjectFromScheme
 | 
					 | 
				
			||||||
/// </remarks>
 | 
					 | 
				
			||||||
/// <code>
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// struct Scheme
 | 
					 | 
				
			||||||
/// {
 | 
					 | 
				
			||||||
///     char[32] str1; // имя архива
 | 
					 | 
				
			||||||
///     char[32] str2; // имя объекта в архиве
 | 
					 | 
				
			||||||
///     undefined4 magic1;
 | 
					 | 
				
			||||||
///     undefined4 magic2;
 | 
					 | 
				
			||||||
///     char[32] str3; // описание объекта
 | 
					 | 
				
			||||||
///     undefined4 magic3;
 | 
					 | 
				
			||||||
/// }
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// </code>
 | 
					 | 
				
			||||||
public class CpDatEntryConverter
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    const string gameRoot = "C:\\Program Files (x86)\\Nikita\\Iron Strategy";
 | 
					 | 
				
			||||||
    const string missionTmaPath = $"{gameRoot}\\MISSIONS\\Campaign\\Campaign.01\\Mission.01\\data.tma";
 | 
					 | 
				
			||||||
    const string staticRlbPath = $"{gameRoot}\\static.rlb";
 | 
					 | 
				
			||||||
    const string objectsRlbPath = $"{gameRoot}\\objects.rlb";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Схема такая:
 | 
					 | 
				
			||||||
    // Файл обязан начинаться с 0xf1 0xf0 ("cp\0\0") - типа заголовок
 | 
					 | 
				
			||||||
    // Далее 4 байта - тип объекта, который содержится в схеме (их я выдернул из .var файла)
 | 
					 | 
				
			||||||
    // Далее 0x6c (108) байт - root объект
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public void Convert()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        var tma = MissionTmaParser.ReadFile(missionTmaPath);
 | 
					 | 
				
			||||||
        var staticRlbResult = NResParser.ReadFile(staticRlbPath);
 | 
					 | 
				
			||||||
        var objectsRlbResult = NResParser.ReadFile(objectsRlbPath);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var mission = tma.Mission!;
 | 
					 | 
				
			||||||
        var sRlb = staticRlbResult.Archive!;
 | 
					 | 
				
			||||||
        var oRlb = objectsRlbResult.Archive!;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Span<byte> f0f1 = stackalloc byte[4];
 | 
					 | 
				
			||||||
        foreach (var gameObject in mission.GameObjectsData.GameObjectInfos)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var gameObjectDatPath = gameObject.DatString;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (gameObjectDatPath.Contains('\\'))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // если это путь, то надо искать его в папке
 | 
					 | 
				
			||||||
                string datFullPath = $"{gameRoot}\\{gameObjectDatPath}";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                using FileStream fs = new FileStream(datFullPath, FileMode.Open, FileAccess.Read, FileShare.Read);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                fs.ReadExactly(f0f1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (f0f1[0] != 0xf1 || f0f1[1] != 0xf0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _ = 5;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var fileFlags = (CpEntryType)fs.ReadInt32LittleEndian();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var entryLength = 0x6c + 4; // нам нужно прочитать 0x6c (108) байт - это root, и ещё 4 байта - кол-во вложенных объектов
 | 
					 | 
				
			||||||
                if ((fs.Length - 8) % entryLength != 0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _ = 5;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                DatEntry entry = ReadEntryRecursive(fs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // var objects = entries.Select(x => oRlb.Files.FirstOrDefault(y => y.FileName == x.ArchiveEntryName))
 | 
					 | 
				
			||||||
                //     .ToList();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _ = 5;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // это статический объект, который будет в objects.rlb
 | 
					 | 
				
			||||||
                var sEntry = oRlb.Files.FirstOrDefault(x => x.FileName == gameObjectDatPath);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _ = 5;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private DatEntry ReadEntryRecursive(FileStream fs)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        var str1 = fs.ReadNullTerminatedString();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fs.Seek(32 - str1.Length - 1, SeekOrigin.Current);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var str2 = fs.ReadNullTerminatedString();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fs.Seek(32 - str2.Length - 1, SeekOrigin.Current);
 | 
					 | 
				
			||||||
        var magic1 = fs.ReadInt32LittleEndian();
 | 
					 | 
				
			||||||
        var magic2 = fs.ReadInt32LittleEndian();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var descriptionString = fs.ReadNullTerminatedString();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fs.Seek(32 - descriptionString.Length - 1, SeekOrigin.Current);
 | 
					 | 
				
			||||||
        var magic3 = fs.ReadInt32LittleEndian();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // игра не читает количество внутрь схемы, вместо этого она сразу рекурсией читает нужно количество вложенных объектов
 | 
					 | 
				
			||||||
        var childCount = fs.ReadInt32LittleEndian();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        List<DatEntry> children = new List<DatEntry>();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        for (var i = 0; i < childCount; i++)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var child = ReadEntryRecursive(fs);
 | 
					 | 
				
			||||||
            children.Add(child);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new DatEntry(str1, str2, magic1, magic2, descriptionString, magic3, childCount, Children: children);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public record DatEntry(
 | 
					 | 
				
			||||||
        string ArchiveFile,
 | 
					 | 
				
			||||||
        string ArchiveEntryName,
 | 
					 | 
				
			||||||
        int Magic1,
 | 
					 | 
				
			||||||
        int Magic2,
 | 
					 | 
				
			||||||
        string Description,
 | 
					 | 
				
			||||||
        int Magic3,
 | 
					 | 
				
			||||||
        int ChildCount, // игра не хранит это число в объекте, но оно есть в файле
 | 
					 | 
				
			||||||
        List<DatEntry> Children
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    enum CpEntryType : uint
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        ClassBuilding = 0x80000000,
 | 
					 | 
				
			||||||
        ClassRobot = 0x01000000,
 | 
					 | 
				
			||||||
        ClassAnimal = 0x20000000,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        BunkerSmall = 0x80010000,
 | 
					 | 
				
			||||||
        BunkerMedium = 0x80020000,
 | 
					 | 
				
			||||||
        BunkerLarge = 0x80040000,
 | 
					 | 
				
			||||||
        Generator = 0x80000002,
 | 
					 | 
				
			||||||
        Mine = 0x80000004,
 | 
					 | 
				
			||||||
        Storage = 0x80000008,
 | 
					 | 
				
			||||||
        Plant = 0x80000010,
 | 
					 | 
				
			||||||
        Hangar = 0x80000040,
 | 
					 | 
				
			||||||
        TowerMedium = 0x80100000,
 | 
					 | 
				
			||||||
        TowerLarge = 0x80200000,
 | 
					 | 
				
			||||||
        MainTeleport = 0x80000200,
 | 
					 | 
				
			||||||
        Institute = 0x80000400,
 | 
					 | 
				
			||||||
        Bridge = 0x80001000,
 | 
					 | 
				
			||||||
        Ruine = 0x80002000,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        RobotTransport = 0x01002000,
 | 
					 | 
				
			||||||
        RobotBuilder = 0x01004000,
 | 
					 | 
				
			||||||
        RobotBattleunit = 0x01008000,
 | 
					 | 
				
			||||||
        RobotHq = 0x01010000,
 | 
					 | 
				
			||||||
        RobotHero = 0x01020000,
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -304,7 +304,7 @@ IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4)
 | 
				
			|||||||
- `6` - IGameObject (0x138)
 | 
					- `6` - IGameObject (0x138)
 | 
				
			||||||
- `7` - IShadeConfig (у меня в папке с игрой его не оказалось)
 | 
					- `7` - IShadeConfig (у меня в папке с игрой его не оказалось)
 | 
				
			||||||
- `8` - ICamera
 | 
					- `8` - ICamera
 | 
				
			||||||
- `9` - Queue
 | 
					- `9` - IQueue
 | 
				
			||||||
- `10` - IControl
 | 
					- `10` - IControl
 | 
				
			||||||
- `0xb` - IAnimation
 | 
					- `0xb` - IAnimation
 | 
				
			||||||
- `0xd` - IMatManager
 | 
					- `0xd` - IMatManager
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user