mirror of
https://github.com/kwsch/NHSE.git
synced 2026-05-21 17:59:47 -05:00
149 lines
4.2 KiB
C#
149 lines
4.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace NHSE.Parsing
|
|
{
|
|
public class MSBT
|
|
{
|
|
public readonly MSBTHeader Header;
|
|
public readonly LBL1 LBL1 = new();
|
|
public readonly TXT2 TXT2 = new();
|
|
public readonly Encoding FileEncoding;
|
|
public readonly List<string> SectionOrder;
|
|
public bool HasLabels;
|
|
|
|
public MSBT(byte[] rawBytes)
|
|
{
|
|
using var stream = new MemoryStream(rawBytes);
|
|
using var br = new BinaryReaderX(stream);
|
|
|
|
Header = new MSBTHeader(br);
|
|
FileEncoding = (Header.EncodingByte == MSBTEncodingByte.UTF8 ? Encoding.UTF8 : Encoding.Unicode);
|
|
|
|
SectionOrder = new List<string>();
|
|
for (int i = 0; i < Header.NumberOfSections; i++)
|
|
{
|
|
var peek = br.PeekString();
|
|
switch (peek)
|
|
{
|
|
case "LBL1":
|
|
ReadLBL1(br);
|
|
SectionOrder.Add("LBL1");
|
|
break;
|
|
|
|
case "ATR1":
|
|
// don't care
|
|
br.ReadBytes(sizeof(uint) + sizeof(uint) + sizeof(long) + 1);
|
|
while (br.BaseStream.Position % 16 != 0 && br.BaseStream.Position != br.BaseStream.Length)
|
|
br.ReadByte();
|
|
SectionOrder.Add("ATR1");
|
|
break;
|
|
|
|
case "TXT2":
|
|
ReadTXT2(br);
|
|
SectionOrder.Add("TXT2");
|
|
break;
|
|
}
|
|
}
|
|
|
|
br.Close();
|
|
}
|
|
|
|
private void ReadLBL1(BinaryReaderX br)
|
|
{
|
|
LBL1.Identifier = br.ReadString(4);
|
|
LBL1.SectionSize = br.ReadUInt32();
|
|
LBL1.Padding1 = br.ReadBytes(8);
|
|
long startOfLabels = br.BaseStream.Position;
|
|
LBL1.NumberOfGroups = br.ReadUInt32();
|
|
|
|
for (int i = 0; i < LBL1.NumberOfGroups; i++)
|
|
{
|
|
var grp = new MSBTGroup
|
|
{
|
|
NumberOfLabels = br.ReadUInt32(),
|
|
Offset = br.ReadUInt32()
|
|
};
|
|
LBL1.Groups.Add(grp);
|
|
}
|
|
|
|
foreach (var grp in LBL1.Groups)
|
|
{
|
|
br.BaseStream.Seek(startOfLabels + grp.Offset, SeekOrigin.Begin);
|
|
|
|
for (int i = 0; i < grp.NumberOfLabels; i++)
|
|
{
|
|
var length = Convert.ToUInt32(br.ReadByte());
|
|
var name = br.ReadString((int)length);
|
|
var index = br.ReadUInt32();
|
|
var lbl = new MSBTLabel(name) {Index = index, Length = length};
|
|
LBL1.Labels.Add(lbl);
|
|
}
|
|
}
|
|
|
|
if (LBL1.Labels.Count > 0)
|
|
HasLabels = true;
|
|
|
|
PaddingSeek(br);
|
|
}
|
|
|
|
private void ReadTXT2(BinaryReaderX br)
|
|
{
|
|
TXT2.Identifier = br.ReadString(4);
|
|
TXT2.SectionSize = br.ReadUInt32();
|
|
TXT2.Padding1 = br.ReadBytes(8);
|
|
long startOfStrings = br.BaseStream.Position;
|
|
TXT2.NumberOfStrings = br.ReadUInt32();
|
|
|
|
var offsets = new List<uint>();
|
|
for (int i = 0; i < TXT2.NumberOfStrings; i++)
|
|
offsets.Add(br.ReadUInt32());
|
|
|
|
for (int i = 0; i < TXT2.NumberOfStrings; i++)
|
|
{
|
|
uint nextOffset = (i + 1 < offsets.Count) ? ((uint)startOfStrings + offsets[i + 1]) : ((uint)startOfStrings + TXT2.SectionSize);
|
|
|
|
br.BaseStream.Seek(startOfStrings + offsets[i], SeekOrigin.Begin);
|
|
|
|
var result = new List<byte>();
|
|
while (br.BaseStream.Position < nextOffset && br.BaseStream.Position < Header.FileSize)
|
|
{
|
|
if (Header.EncodingByte == MSBTEncodingByte.UTF8)
|
|
{
|
|
result.Add(br.ReadByte());
|
|
}
|
|
else
|
|
{
|
|
byte[] unichar = br.ReadBytes(2);
|
|
|
|
if (br.ByteOrder == ByteOrder.BigEndian)
|
|
Array.Reverse(unichar);
|
|
|
|
result.AddRange(unichar);
|
|
}
|
|
}
|
|
var str = new MSBTTextString(result.ToArray(), (uint)i);
|
|
TXT2.Strings.Add(str);
|
|
}
|
|
|
|
// Tie in LBL1 labels
|
|
foreach (var lbl in LBL1.Labels)
|
|
lbl.String = TXT2.Strings[(int)lbl.Index];
|
|
|
|
PaddingSeek(br);
|
|
}
|
|
|
|
private static void PaddingSeek(BinaryReader br)
|
|
{
|
|
long remainder = br.BaseStream.Position % 16;
|
|
if (remainder <= 0)
|
|
return;
|
|
var _ = br.ReadByte();
|
|
br.BaseStream.Seek(-1, SeekOrigin.Current);
|
|
br.BaseStream.Seek(16 - remainder, SeekOrigin.Current);
|
|
}
|
|
}
|
|
}
|