mirror of
https://github.com/GTA-ASM/SanAndreasUnity
synced 2024-11-30 15:59:09 +00:00
212 lines
5.1 KiB
C#
212 lines
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.IO;
|
|
using System.Text;
|
|
using SanAndreasUnity.Utilities;
|
|
|
|
namespace SanAndreasUnity.Importing.GXT
|
|
{
|
|
public class GXT : IDisposable
|
|
{
|
|
|
|
#region LoadPath
|
|
private static string _language= "american";
|
|
public static string Language
|
|
{
|
|
get => _language;
|
|
set
|
|
{
|
|
var path = GetPathFromLanguage(value);
|
|
if (File.Exists(path))
|
|
{
|
|
_language = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static string GXTDir => Path.Combine(Config.GamePath, "text");
|
|
private static string GetPathFromLanguage(string language) => Path.Combine(GXTDir, $"{language}.gxt");
|
|
private static string LoadPath => GetPathFromLanguage(_language);
|
|
|
|
#endregion
|
|
|
|
#region GXTData
|
|
|
|
private Int16 _version;
|
|
public List<string> SubTableNames { get; } = new List<string>();
|
|
public Dictionary<string, List<int>> TableEntryNameDict { get; } = new Dictionary<string, List<Int32>>();
|
|
public Dictionary<int, string> EntryNameWordDict { get; } = new Dictionary<int, string>();
|
|
|
|
private static GXT _current;
|
|
public static GXT Current
|
|
{
|
|
get
|
|
{
|
|
return _current;
|
|
}
|
|
}
|
|
|
|
private MemoryStream _rawData;
|
|
|
|
#endregion
|
|
|
|
public GXT(string fp)
|
|
{
|
|
var bytes = File.ReadAllBytes(LoadPath);
|
|
_rawData = new MemoryStream(bytes, false);
|
|
}
|
|
|
|
public static void Load()
|
|
{
|
|
if (_current != null)
|
|
{
|
|
return;
|
|
}
|
|
_current = new GXT(LoadPath);
|
|
_current.InternalLoad();
|
|
_current._rawData.Dispose();
|
|
}
|
|
|
|
|
|
private void InternalLoad()
|
|
{
|
|
var encoding = LoadHeader();
|
|
var tkeyEntryOffsets = LoadTableBlock(encoding);
|
|
|
|
for (var i = 0; i < SubTableNames.Count; i++)
|
|
{
|
|
var subTableName = SubTableNames[i];
|
|
_rawData.Seek(tkeyEntryOffsets[i], SeekOrigin.Begin);
|
|
using (var binReader = new BinaryReader(_rawData, encoding, true))
|
|
{
|
|
if (i == 0) //parse tkey directly
|
|
{
|
|
LoadTKEY(binReader, subTableName);
|
|
}
|
|
else //skip tkeyname
|
|
{
|
|
_rawData.Seek(8, SeekOrigin.Current);
|
|
LoadTKEY(binReader, subTableName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LoadTKEY(BinaryReader binaryReader, string tableName)
|
|
{
|
|
//Console.WriteLine(nameof(LoadTKEY));
|
|
var tkey = new string(binaryReader.ReadChars(4));
|
|
var blockSize = binaryReader.ReadInt32();
|
|
|
|
//Console.WriteLine($"tkey:{tkey} blockSize:{blockSize}");
|
|
if (tkey != "TKEY")
|
|
{
|
|
throw new ConstraintException();
|
|
}
|
|
|
|
var entryOffsets = new List<Int32>();
|
|
var tdataEntryCount = blockSize / 8;
|
|
var entryNames = new List<Int32>();
|
|
for (int i = 0; i < tdataEntryCount; i++)
|
|
{
|
|
|
|
var entryOffset = binaryReader.ReadInt32();
|
|
var crc32 = binaryReader.ReadInt32();
|
|
entryNames.Add(crc32);
|
|
entryOffsets.Add(entryOffset);
|
|
}
|
|
|
|
TableEntryNameDict[tableName] = entryNames;
|
|
|
|
//read tdat
|
|
var tdat = new string(binaryReader.ReadChars(4));
|
|
if (tdat != "TDAT")
|
|
{
|
|
throw new ConstraintException();
|
|
}
|
|
var tdatBlockSize = binaryReader.ReadInt32();
|
|
var bytes = binaryReader.ReadBytes(tdatBlockSize);
|
|
var wordBytes = new List<byte>();
|
|
for (int i = 0; i < entryOffsets.Count; i++)
|
|
{
|
|
var entryName = entryNames[i];
|
|
//todo this should be double checked; I think it could be used a lib method.
|
|
//but not familiar with relevant api.
|
|
for (int j = entryOffsets[i]; ; j++)
|
|
{
|
|
var c = Convert.ToChar(bytes[j]);
|
|
if (c == '\0')
|
|
{
|
|
break;
|
|
}
|
|
wordBytes.Add(bytes[j]);
|
|
}
|
|
EntryNameWordDict[entryName] = Win1252ToString(wordBytes.ToArray());
|
|
wordBytes.Clear();
|
|
}
|
|
}
|
|
|
|
private string Win1252ToString(byte[] bytes)
|
|
{
|
|
Encoding win1252 = Encoding.GetEncoding(1252);
|
|
var utf8bytes = Encoding.Convert(win1252, Encoding.UTF8, bytes);
|
|
return Encoding.UTF8.GetString(utf8bytes);
|
|
}
|
|
|
|
private Encoding LoadHeader()
|
|
{
|
|
using (var binReader = new BinaryReader(_rawData, Encoding.ASCII, true))
|
|
{
|
|
_version = binReader.ReadInt16();
|
|
var bitsPerChar = binReader.ReadInt16();
|
|
if (bitsPerChar == 8)
|
|
{
|
|
return Encoding.ASCII;
|
|
}
|
|
else if (bitsPerChar == 16)
|
|
{
|
|
return Encoding.Unicode;
|
|
}
|
|
}
|
|
return Encoding.ASCII;
|
|
}
|
|
|
|
private List<Int32> LoadTableBlock(Encoding encoding)
|
|
{
|
|
using (var binReader = new BinaryReader(_rawData, encoding, true))
|
|
{
|
|
var offsetList = new List<Int32>();
|
|
var tablConst = binReader.ReadChars(4);
|
|
if (new string(tablConst) != "TABL")
|
|
{
|
|
throw new ConstraintException();
|
|
}
|
|
|
|
var blockSize = binReader.ReadInt32();
|
|
var entryCount = blockSize / 12;
|
|
for (int i = 0; i < entryCount; i++)
|
|
{
|
|
var subtableName = new string(binReader.ReadChars(8));
|
|
var offset = binReader.ReadInt32();
|
|
|
|
//Console.WriteLine($"subtablename:{subtableName} offset:{offset}");
|
|
SubTableNames.Add(subtableName);
|
|
offsetList.Add(offset);
|
|
}
|
|
|
|
return offsetList;
|
|
}
|
|
}
|
|
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_rawData != null)
|
|
{
|
|
_rawData.Dispose();
|
|
_rawData = null;
|
|
}
|
|
}
|
|
}
|
|
}
|