NHSE/NHSE.Parsing/MSBT/MSBT.cs
2020-12-24 23:53:40 -08:00

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);
}
}
}