0
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-06-19 16:08:02 +03:00

Implement SCR UI

This commit is contained in:
bird_egop
2025-02-26 04:27:16 +03:00
parent b47a9aff5d
commit d7eb23e9e0
17 changed files with 616 additions and 49 deletions

View File

@ -0,0 +1,262 @@
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Text.Json;
using ImGuiNET;
using NativeFileDialogSharp;
using NResUI.Abstractions;
using NResUI.Models;
namespace NResUI.ImGuiUI;
public class BinaryExplorerPanel : IImGuiPanel
{
private readonly BinaryExplorerViewModel _viewModel;
public BinaryExplorerPanel(BinaryExplorerViewModel viewModel)
{
_viewModel = viewModel;
}
public void OnImGuiRender()
{
return;
if (ImGui.Begin("Binary Explorer"))
{
if (ImGui.Button("Open File"))
{
OpenFile();
}
if (_viewModel.HasFile)
{
ImGui.SameLine();
ImGui.Text(_viewModel.Path);
if (ImGui.Button("Сохранить регионы"))
{
File.WriteAllText("preset.json", JsonSerializer.Serialize(_viewModel.Regions));
}
ImGui.SameLine();
if (ImGui.Button("Загрузить регионы"))
{
_viewModel.Regions = JsonSerializer.Deserialize<List<Region>>(File.ReadAllText("preset.json"))!;
}
const int bytesPerRow = 16;
if (ImGui.BeginTable("HexTable", bytesPerRow + 1, ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoHostExtendX))
{
ImGui.TableSetupColumn("Address", ImGuiTableColumnFlags.WidthFixed);
for (var i = 0; i < bytesPerRow; i++)
{
ImGui.TableSetupColumn(i.ToString());
}
ImGui.TableHeadersRow();
for (int i = 0; i < _viewModel.Data.Length; i += bytesPerRow)
{
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.Text($"{i:x8} ");
for (int j = 0; j < 16; j++)
{
var index = i + j;
ImGui.TableNextColumn();
if (index < _viewModel.Data.Length)
{
uint? regionColor = GetRegionColor(i + j);
if (regionColor is not null)
{
ImGui.PushStyleColor(ImGuiCol.Header, regionColor.Value);
}
if (ImGui.Selectable($"{_viewModel.Data[i + j]}##sel{i + j}", regionColor.HasValue))
{
HandleRegionSelect(i + j);
}
if (regionColor is not null)
{
ImGui.PopStyleColor();
}
}
else
{
ImGui.Text(" ");
}
}
}
ImGui.EndTable();
}
ImGui.SameLine();
ImGui.SetNextItemWidth(200f);
if (ImGui.ColorPicker4("NextColor", ref _viewModel.NextColor, ImGuiColorEditFlags.Float))
{
}
if (ImGui.BeginTable("Регионы", 5, ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoHostExtendX))
{
ImGui.TableSetupColumn("Номер");
ImGui.TableSetupColumn("Старт");
ImGui.TableSetupColumn("Длина");
ImGui.TableSetupColumn("Значение");
ImGui.TableSetupColumn("Действия");
ImGui.TableHeadersRow();
for (int k = 0; k < _viewModel.Regions.Count; k++)
{
var region = _viewModel.Regions[k];
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.Text(k.ToString());
ImGui.TableNextColumn();
ImGui.Text(region.Begin.ToString());
ImGui.TableNextColumn();
ImGui.Text(region.Length.ToString());
ImGui.TableNextColumn();
ImGui.Text(region.Value ?? "unknown");
ImGui.TableNextColumn();
if (ImGui.Button($"float##f{k}"))
{
region.Value = BinaryPrimitives.ReadSingleLittleEndian(_viewModel.Data.AsSpan()[region.Begin..(region.Begin + region.Length)])
.ToString("F2");
}
ImGui.SameLine();
if (ImGui.Button($"int##i{k}"))
{
region.Value = BinaryPrimitives.ReadInt32LittleEndian(_viewModel.Data.AsSpan()[region.Begin..(region.Begin + region.Length)])
.ToString();
}
ImGui.SameLine();
if (ImGui.Button($"ASCII##a{k}"))
{
region.Value = Encoding.ASCII.GetString(_viewModel.Data.AsSpan()[region.Begin..(region.Begin + region.Length)]);
}
ImGui.SameLine();
if (ImGui.Button($"raw##r{k}"))
{
region.Value = string.Join(
"",
_viewModel.Data[region.Begin..(region.Begin + region.Length)]
.Select(x => x.ToString("x2"))
);
}
}
ImGui.EndTable();
}
}
ImGui.End();
}
}
private uint? GetRegionColor(int index)
{
Region? inRegion = _viewModel.Regions.Find(x => x.Begin <= index && x.Begin + x.Length > index);
return inRegion?.Color;
}
private void HandleRegionSelect(int index)
{
Region? inRegion = _viewModel.Regions.FirstOrDefault(x => x.Begin <= index && index < x.Begin + x.Length);
if (inRegion is null)
{
// not in region
Region? prependRegion;
Region? appendRegion;
if ((prependRegion = _viewModel.Regions.Find(x => x.Begin + x.Length == index)) is not null)
{
if (prependRegion.Color == GetImGuiColor(_viewModel.NextColor))
{
prependRegion.Length += 1;
return;
}
}
if ((appendRegion = _viewModel.Regions.Find(x => x.Begin - 1 == index)) is not null)
{
if (appendRegion.Color == GetImGuiColor(_viewModel.NextColor))
{
appendRegion.Begin--;
appendRegion.Length += 1;
return;
}
}
var color = ImGui.ColorConvertFloat4ToU32(_viewModel.NextColor);
color = unchecked((uint) (0xFF << 24)) | color;
_viewModel.Regions.Add(
new Region()
{
Begin = index,
Length = 1,
Color = color
}
);
}
else
{
if (inRegion.Length == 1)
{
_viewModel.Regions.Remove(inRegion);
}
else
{
if (inRegion.Begin == index)
{
inRegion.Begin++;
inRegion.Length--;
}
if (inRegion.Begin + inRegion.Length - 1 == index)
{
inRegion.Length--;
}
}
}
}
private static uint GetImGuiColor(Vector4 vec)
{
var color = ImGui.ColorConvertFloat4ToU32(vec);
color = unchecked((uint) (0xFF << 24)) | color;
return color;
}
private bool OpenFile()
{
var result = Dialog.FileOpen("*");
if (result.IsOk)
{
var path = result.Path;
var bytes = File.ReadAllBytes(path);
_viewModel.HasFile = true;
_viewModel.Data = bytes;
_viewModel.Path = path;
}
return false;
}
}

View File

@ -6,25 +6,19 @@ using NativeFileDialogSharp;
using NResLib;
using NResUI.Abstractions;
using NResUI.Models;
using ScrLib;
using TexmLib;
namespace NResUI.ImGuiUI
{
public class MainMenuBar : IImGuiPanel
public class MainMenuBar(
NResExplorerViewModel nResExplorerViewModel,
TexmExplorerViewModel texmExplorerViewModel,
ScrViewModel scrViewModel,
MissionTmaViewModel missionTmaViewModel,
MessageBoxModalPanel messageBox)
: IImGuiPanel
{
private readonly NResExplorerViewModel _nResExplorerViewModel;
private readonly TexmExplorerViewModel _texmExplorerViewModel;
private readonly MissionTmaViewModel _missionTmaViewModel;
private readonly MessageBoxModalPanel _messageBox;
public MainMenuBar(NResExplorerViewModel nResExplorerViewModel, TexmExplorerViewModel texmExplorerViewModel, MessageBoxModalPanel messageBox, MissionTmaViewModel missionTmaViewModel)
{
_nResExplorerViewModel = nResExplorerViewModel;
_texmExplorerViewModel = texmExplorerViewModel;
_messageBox = messageBox;
_missionTmaViewModel = missionTmaViewModel;
}
public void OnImGuiRender()
{
if (ImGui.BeginMenuBar())
@ -41,7 +35,7 @@ namespace NResUI.ImGuiUI
var parseResult = NResParser.ReadFile(path);
_nResExplorerViewModel.SetParseResult(parseResult, path);
nResExplorerViewModel.SetParseResult(parseResult, path);
Console.WriteLine("Read NRES");
}
}
@ -55,10 +49,10 @@ namespace NResUI.ImGuiUI
var path = result.Path;
using var fs = new FileStream(path, FileMode.Open);
var parseResult = TexmParser.ReadFromStream(fs, path);
_texmExplorerViewModel.SetParseResult(parseResult, path);
texmExplorerViewModel.SetParseResult(parseResult, path);
Console.WriteLine("Read TEXM");
}
}
@ -74,11 +68,11 @@ namespace NResUI.ImGuiUI
using var fs = new FileStream(path, FileMode.Open);
fs.Seek(4116, SeekOrigin.Begin);
var parseResult = TexmParser.ReadFromStream(fs, path);
_texmExplorerViewModel.SetParseResult(parseResult, path);
Console.WriteLine("Read TEXM");
texmExplorerViewModel.SetParseResult(parseResult, path);
Console.WriteLine("Read TFNT TEXM");
}
}
@ -90,21 +84,27 @@ namespace NResUI.ImGuiUI
{
var path = result.Path;
var parseResult = MissionTmaParser.ReadFile(path);
_missionTmaViewModel.SetParseResult(parseResult, path);
var orderedBuildings = parseResult.Mission.GameObjectsData.GameObjectInfos.Where(x => x.Type == GameObjectType.Building)
.OrderBy(x => x.Order)
.ToList();
foreach (var gameObjectInfo in orderedBuildings)
{
Console.WriteLine($"{gameObjectInfo.Order} : {gameObjectInfo.DatString}");
}
missionTmaViewModel.SetParseResult(parseResult, path);
Console.WriteLine("Read TMA");
}
}
if (_nResExplorerViewModel.HasFile)
if (ImGui.MenuItem("Open SCR Scripts File"))
{
var result = Dialog.FileOpen("scr");
if (result.IsOk)
{
var path = result.Path;
var parseResult = ScrParser.ReadFile(path);
scrViewModel.SetParseResult(parseResult, path);
Console.WriteLine("Read SCR");
}
}
if (nResExplorerViewModel.HasFile)
{
if (ImGui.MenuItem("Экспортировать NRes"))
{
@ -113,10 +113,10 @@ namespace NResUI.ImGuiUI
if (result.IsOk)
{
var path = result.Path;
NResExporter.Export(_nResExplorerViewModel.Archive!, path, _nResExplorerViewModel.Path!);
_messageBox.Show("Успешно экспортировано");
NResExporter.Export(nResExplorerViewModel.Archive!, path, nResExplorerViewModel.Path!);
messageBox.Show("Успешно экспортировано");
}
}
}
@ -127,6 +127,5 @@ namespace NResUI.ImGuiUI
ImGui.EndMenuBar();
}
}
}
}

View File

@ -132,10 +132,10 @@ public class MissionTmaExplorer : IImGuiPanel
ImGui.SameLine();
ImGui.Text(clanInfo.ClanType.ToReadableString());
ImGui.Text("Неизвестная строка 1: ");
Utils.ShowHint("Кажется это путь к файлу поведения (Behavior), но пока не понятно. Обычно пути соответствуют 2 файла.");
ImGui.Text("Скрипты поведения: ");
Utils.ShowHint("Пути к файлам .scr и .fml описывающих настройку объектов и поведение AI");
ImGui.SameLine();
ImGui.Text(clanInfo.UnkString2);
ImGui.Text(clanInfo.ScriptsString);
if (clanInfo.UnknownParts.Count > 0)
{

View File

@ -0,0 +1,98 @@
using ImGuiNET;
using NResUI.Abstractions;
using NResUI.Models;
namespace NResUI.ImGuiUI;
public class ScrExplorer : IImGuiPanel
{
private readonly ScrViewModel _viewModel;
public ScrExplorer(ScrViewModel viewModel)
{
_viewModel = viewModel;
}
public void OnImGuiRender()
{
if (ImGui.Begin("SCR Explorer"))
{
var scr = _viewModel.Scr;
if (_viewModel.HasFile && scr is not null)
{
ImGui.Text("Магия: ");
Utils.ShowHint("тут всегда число 59 (0x3b) - это число известных игре скриптов");
ImGui.SameLine();
ImGui.Text(scr.Magic.ToString());
ImGui.Text("Кол-во секций: ");
ImGui.SameLine();
ImGui.Text(scr.EntryCount.ToString());
if (ImGui.TreeNodeEx("Секции"))
{
for (var i = 0; i < scr.Entries.Count; i++)
{
var entry = scr.Entries[i];
if (ImGui.TreeNodeEx($"Секция {i} - \"{entry.Title}\""))
{
ImGui.Text("Индекс: ");
ImGui.SameLine();
ImGui.Text(entry.Index.ToString());
ImGui.Text("Кол-во элементов: ");
ImGui.SameLine();
ImGui.Text(entry.InnerCount.ToString());
if (ImGui.BeginTable($"Элементы##{i:0000}", 8, ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.NoHostExtendX))
{
ImGui.TableSetupColumn("Индекс скрипта");
ImGui.TableSetupColumn("UnkInner2");
ImGui.TableSetupColumn("UnkInner3");
ImGui.TableSetupColumn("UnkInner4");
ImGui.TableSetupColumn("UnkInner5");
ImGui.TableSetupColumn("Кол-во аргументов");
ImGui.TableSetupColumn("Аргументы");
ImGui.TableSetupColumn("UnkInner7");
ImGui.TableHeadersRow();
for (int j = 0; j < entry.Inners.Count; j++)
{
var inner = entry.Inners[j];
ImGui.TableNextRow();
ImGui.TableNextColumn();
ImGui.Text(inner.ScriptIndex.ToString());
ImGui.TableNextColumn();
ImGui.Text(inner.UnkInner2.ToString());
ImGui.TableNextColumn();
ImGui.Text(inner.UnkInner3.ToString());
ImGui.TableNextColumn();
ImGui.Text(inner.UnkInner4.ToString());
ImGui.TableNextColumn();
ImGui.Text(inner.UnkInner5.ToString());
ImGui.TableNextColumn();
ImGui.Text(inner.ArgumentsCount.ToString());
ImGui.TableNextColumn();
ImGui.Text(string.Join(", ", inner.Arguments));
ImGui.TableNextColumn();
ImGui.Text(inner.UnkInner7.ToString());
}
ImGui.EndTable();
}
ImGui.TreePop();
}
}
ImGui.TreePop();
}
}
else
{
ImGui.Text("SCR не открыт");
}
ImGui.End();
}
}
}