/// <summary> /// Loads a TLK file into memory. /// </summary> /// <param name="fileName"></param> public void LoadTlkData(string fileName) { path = fileName; name = Path.GetFileNameWithoutExtension(fileName); /* **************** STEP ONE **************** * -- load TLK file header -- * * reading first 28 (4 * 7) bytes */ Stream fs = File.OpenRead(fileName); BinaryReader r = new BinaryReader(fs); Header = new TLKHeader(r); //DebugTools.PrintHeader(Header); /* **************** STEP TWO **************** * -- read and store Huffman Tree nodes -- */ /* jumping to the beginning of Huffmann Tree stored in TLK file */ long pos = r.BaseStream.Position; r.BaseStream.Seek(pos + (Header.MaleEntryCount + Header.FemaleEntryCount) * 8, SeekOrigin.Begin); CharacterTree = new List <HuffmanNode>(); for (int i = 0; i < Header.treeNodeCount; i++) { CharacterTree.Add(new HuffmanNode(r)); } /* **************** STEP THREE **************** * -- read all of coded data into memory -- */ byte[] data = new byte[Header.dataLen]; r.BaseStream.Read(data, 0, data.Length); /* and store it as raw bits for further processing */ Bits = new BitArray(data); /* rewind BinaryReader just after the Header * at the beginning of TLK Entries data */ r.BaseStream.Seek(pos, SeekOrigin.Begin); /* **************** STEP FOUR **************** * -- decode (basing on Huffman Tree) raw bits data into actual strings -- * and store them in a Dictionary<int, string> where: * int: bit offset of the beginning of data (offset starting at 0 and counted for Bits array) * so offset == 0 means the first bit in Bits array * string: actual decoded string */ Dictionary <int, string> rawStrings = new Dictionary <int, string>(); int offset = 0; // int maxOffset = 0; while (offset < Bits.Length) { int key = offset; // if (key > maxOffset) // maxOffset = key; /* read the string and update 'offset' variable to store NEXT string offset */ string s = GetString(ref offset); rawStrings.Add(key, s); } // Console.WriteLine("Max offset = " + maxOffset); /* **************** STEP FIVE **************** * -- bind data to String IDs -- * go through Entries in TLK file and read it's String ID and offset * then check if offset is a key in rawStrings and if it is, then bind data. * Sometimes there's no such key, in that case, our String ID is probably a substring * of another String present in rawStrings. */ StringRefs = new List <ME1Explorer.Unreal.Classes.TalkFile.TLKStringRef>(); for (int i = 0; i < Header.MaleEntryCount + Header.FemaleEntryCount; i++) { ME1Explorer.Unreal.Classes.TalkFile.TLKStringRef sref = new ME1Explorer.Unreal.Classes.TalkFile.TLKStringRef(r, false); sref.Index = i; if (sref.BitOffset >= 0) { if (!rawStrings.ContainsKey(sref.BitOffset)) { int tmpOffset = sref.BitOffset; string partString = GetString(ref tmpOffset); /* actually, it should store the fullString and subStringOffset, * but as we don't have to use this compression feature, * we will store only the part of string we need */ /* int key = rawStrings.Keys.Last(c => c < sref.BitOffset); * string fullString = rawStrings[key]; * int subStringOffset = fullString.LastIndexOf(partString); * sref.StartOfString = subStringOffset; * sref.Data = fullString; */ sref.Data = partString; } else { sref.Data = rawStrings[sref.BitOffset]; } } StringRefs.Add(sref); } r.Close(); }
/* for sorting */ private static int CompareTlkStringRef(ME1Explorer.Unreal.Classes.TalkFile.TLKStringRef strRef1, ME1Explorer.Unreal.Classes.TalkFile.TLKStringRef strRef2) { int result = strRef1.StringID.CompareTo(strRef2.StringID); return(result); }