mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-08-02 17:36:32 +03:00
create NResUI
This commit is contained in:
45
NResLib/NResArchive.cs
Normal file
45
NResLib/NResArchive.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace NResLib;
|
||||
|
||||
/// <summary>
|
||||
/// Архив NRes (файл NRes)
|
||||
/// </summary>
|
||||
public record NResArchive(NResArchiveHeader Header, List<ListMetadataItem> Files);
|
||||
|
||||
/// <summary>
|
||||
/// Заголовок файла
|
||||
/// </summary>
|
||||
/// <param name="NRes">[0..4] ASCII NRes</param>
|
||||
/// <param name="Version">[4..8] Версия кодировщика (должно быть всегда 0x100)</param>
|
||||
/// <param name="FileCount">[8..12] Количество файлов </param>
|
||||
/// <param name="TotalFileLengthBytes">[12..16] Длина всего архива</param>
|
||||
public record NResArchiveHeader(string NRes, int Version, int FileCount, int TotalFileLengthBytes);
|
||||
|
||||
/// <summary>
|
||||
/// В конце файла есть список метаданных,
|
||||
/// каждый элемент это 64 байта,
|
||||
/// найти начало можно как (Header.TotalFileLengthBytes - Header.FileCount * 64)
|
||||
/// </summary>
|
||||
/// <param name="FileType">[0..8] ASCII описание типа файла, например TEXM или MAT0</param>
|
||||
/// <param name="Magic1">[8..12] Неизвестное число</param>
|
||||
/// <param name="FileLength">[12..16] Длина файла в байтах</param>
|
||||
/// <param name="Magic2">[16..20] Неизвестное число</param>
|
||||
/// <param name="FileName">[20..40] ASCII имя файла</param>
|
||||
/// <param name="Magic3">[40..44] Неизвестное число</param>
|
||||
/// <param name="Magic4">[44..48] Неизвестное число</param>
|
||||
/// <param name="Magic5">[48..52] Неизвестное число</param>
|
||||
/// <param name="Magic6">[52..56] Неизвестное число</param>
|
||||
/// <param name="OffsetInFile">[56..60] Смещение подфайла от начала NRes (именно самого NRes) в байтах</param>
|
||||
/// <param name="Index">[60..64] Индекс в файле (от 0, не больше чем кол-во файлов)</param>
|
||||
public record ListMetadataItem(
|
||||
string FileType,
|
||||
int Magic1,
|
||||
int FileLength,
|
||||
int Magic2,
|
||||
string FileName,
|
||||
int Magic3,
|
||||
int Magic4,
|
||||
int Magic5,
|
||||
int Magic6,
|
||||
int OffsetInFile,
|
||||
int Index
|
||||
);
|
9
NResLib/NResLib.csproj
Normal file
9
NResLib/NResLib.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
3
NResLib/NResParseResult.cs
Normal file
3
NResLib/NResParseResult.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace NResLib;
|
||||
|
||||
public record NResParseResult(NResArchive? Archive = null, string? Error = null);
|
78
NResLib/NResParser.cs
Normal file
78
NResLib/NResParser.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace NResLib;
|
||||
|
||||
public static class NResParser
|
||||
{
|
||||
public static NResParseResult ReadFile(string path)
|
||||
{
|
||||
using FileStream nResFs = new FileStream(path, FileMode.Open);
|
||||
|
||||
if (nResFs.Length < 16)
|
||||
{
|
||||
return new NResParseResult(null, "Файл не может быть NRes, менее 16 байт");
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
|
||||
nResFs.ReadExactly(buffer);
|
||||
|
||||
if (buffer[0] != 'N' || buffer[1] != 'R' || buffer[2] != 'e' || buffer[3] != 's')
|
||||
{
|
||||
return new NResParseResult(null, "Файл не начинается с NRes");
|
||||
}
|
||||
|
||||
var header = new NResArchiveHeader(
|
||||
NRes: Encoding.ASCII.GetString(buffer[0..4]),
|
||||
Version: BinaryPrimitives.ReadInt32LittleEndian(buffer[4..8]),
|
||||
FileCount: BinaryPrimitives.ReadInt32LittleEndian(buffer[8..12]),
|
||||
TotalFileLengthBytes: BinaryPrimitives.ReadInt32LittleEndian(buffer[12..16])
|
||||
);
|
||||
|
||||
if (header.TotalFileLengthBytes != nResFs.Length)
|
||||
{
|
||||
return new NResParseResult(
|
||||
null,
|
||||
$"Длина файла не совпадает с заявленным в заголовке.\n" +
|
||||
$"Заявлено: {header.TotalFileLengthBytes}\n" +
|
||||
$"Фактически: {nResFs.Length}"
|
||||
);
|
||||
}
|
||||
|
||||
nResFs.Seek(-header.FileCount * 64, SeekOrigin.End);
|
||||
|
||||
var elements = new List<ListMetadataItem>(header.FileCount);
|
||||
|
||||
Span<byte> metaDataBuffer = stackalloc byte[64];
|
||||
for (int i = 0; i < header.FileCount; i++)
|
||||
{
|
||||
nResFs.ReadExactly(metaDataBuffer);
|
||||
|
||||
elements.Add(
|
||||
new ListMetadataItem(
|
||||
FileType: Encoding.ASCII.GetString(metaDataBuffer[..8]),
|
||||
Magic1: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[8..12]),
|
||||
FileLength: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[12..16]),
|
||||
Magic2: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[16..20]),
|
||||
FileName: Encoding.ASCII.GetString(metaDataBuffer[20..40]),
|
||||
Magic3: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[40..44]),
|
||||
Magic4: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[44..48]),
|
||||
Magic5: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[48..52]),
|
||||
Magic6: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[52..56]),
|
||||
OffsetInFile: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[56..60]),
|
||||
Index: BinaryPrimitives.ReadInt32LittleEndian(metaDataBuffer[60..64])
|
||||
)
|
||||
);
|
||||
|
||||
metaDataBuffer.Clear();
|
||||
}
|
||||
|
||||
return new NResParseResult(
|
||||
new NResArchive(
|
||||
Header: header,
|
||||
Files: elements
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user