diff --git a/OpenDiablo2.Common/ResourcePaths.cs b/OpenDiablo2.Common/ResourcePaths.cs index 6d209cff..87bfed54 100644 --- a/OpenDiablo2.Common/ResourcePaths.cs +++ b/OpenDiablo2.Common/ResourcePaths.cs @@ -97,5 +97,7 @@ namespace OpenDiablo2.Common // --- Data --- // TODO: Doesn't sound right :) public static string EnglishTable = "data\\local\\lng\\eng\\English.txt"; + public static string ExpansionStringTable = "data\\local\\lng\\eng\\expansionstring.tbl"; + } } diff --git a/OpenDiablo2.Core/TextDictionary.cs b/OpenDiablo2.Core/TextDictionary.cs index 4c295f6b..c8bbbc54 100644 --- a/OpenDiablo2.Core/TextDictionary.cs +++ b/OpenDiablo2.Core/TextDictionary.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -8,6 +9,16 @@ using OpenDiablo2.Common.Interfaces; namespace OpenDiablo2.Core { + struct HashTableEntry + { + public bool IsActive { get; set; } + public UInt16 Index { get; set; } + public UInt32 HashValue { get; set; } + public UInt32 IndexString { get; set; } + public UInt32 NameString { get; set; } + public UInt16 NameLength { get; set; } + } + public sealed class TextDictionary : ITextDictionary { private readonly IMPQProvider mpqProvider; @@ -18,6 +29,62 @@ namespace OpenDiablo2.Core { this.mpqProvider = mpqProvider; LoadDictionary(); + LoadExpansionTable(); + } + + private void LoadExpansionTable() + { + using (var stream = mpqProvider.GetStream(ResourcePaths.ExpansionStringTable)) + using (var br = new BinaryReader(stream)) + { + br.ReadBytes(2); // CRC + var numberOfElements = br.ReadUInt16(); + var hashTableSize = br.ReadUInt32(); + br.ReadByte(); // Version (always 0) + var stringOffset = br.ReadUInt32(); + var numberOfLoopsOffset = br.ReadUInt32(); + var fileSize = br.ReadUInt32(); + + var elementIndexes = new List(); + for (var elementIndex = 0; elementIndex < numberOfElements; elementIndex++) + { + elementIndexes.Add(br.ReadUInt16()); + } + + var hashEntries = new List(); + for (var hashEntryIndex = 0; hashEntryIndex < hashTableSize; hashEntryIndex++) + { + hashEntries.Add(new HashTableEntry + { + IsActive = br.ReadByte() == 1, + Index = br.ReadUInt16(), + HashValue = br.ReadUInt32(), + IndexString = br.ReadUInt32(), + NameString = br.ReadUInt32(), + NameLength = br.ReadUInt16() + }); + } + + foreach (var hashEntry in hashEntries.Where(x => x.IsActive)) + { + stream.Seek(hashEntry.NameString, SeekOrigin.Begin); + var value = Encoding.ASCII.GetString(br.ReadBytes(hashEntry.NameLength - 1)); + + stream.Seek(hashEntry.IndexString, SeekOrigin.Begin); + + var key = ""; + while(true) + { + var b = br.ReadByte(); + if (b == 0) + break; + key += (char)b; + } + + lookupTable[key] = value; + + } + } } private void LoadDictionary() @@ -25,7 +92,7 @@ namespace OpenDiablo2.Core var text = mpqProvider.GetTextFile(ResourcePaths.EnglishTable).First(); var rowstoLoad = text.Where(x => x.Split(',').Count() == 3).Select(x => x.Split(',').Select(z => z.Trim()).ToArray()); - foreach(var row in rowstoLoad) + foreach (var row in rowstoLoad) lookupTable[row[1]] = !(row[2].StartsWith("\"") && row[2].EndsWith("\"")) ? row[2] : row[2].Substring(1, row[2].Length - 2); }