using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Runtime.InteropServices; namespace PriFileFormat { public class ReverseMapSection: Section { public uint [] Mapping { get; private set; } public IReadOnlyList Scopes { get; private set; } public IReadOnlyList Items { get; private set; } internal const string Identifier = "[mrm_rev_map] \0"; internal ReverseMapSection (PriFile priFile) : base (Identifier, priFile) { } protected override bool ParseSectionContent (BinaryReader binaryReader) { uint numItems = binaryReader.ReadUInt32 (); binaryReader.ExpectUInt32 ((uint)(binaryReader.BaseStream.Length - 8)); uint [] mapping = new uint [numItems]; for (int i = 0; i < numItems; i++) mapping [i] = binaryReader.ReadUInt32 (); Mapping = mapping; ushort maxFullPathLength = binaryReader.ReadUInt16 (); binaryReader.ExpectUInt16 (0); uint numEntries = binaryReader.ReadUInt32 (); uint numScopes = binaryReader.ReadUInt32 (); binaryReader.ExpectUInt32 (numItems); uint unicodeDataLength = binaryReader.ReadUInt32 (); binaryReader.ReadUInt32 (); List> scopeAndItemInfo = new List> (); for (int i = 0; i < numScopes + numItems; i++) { ushort parent = binaryReader.ReadUInt16 (); ushort fullPathLength = binaryReader.ReadUInt16 (); uint hashCode = binaryReader.ReadUInt32 (); uint nameOffset = binaryReader.ReadUInt16 () | (((hashCode >> 24) & 0xF) << 16); ushort index = binaryReader.ReadUInt16 (); scopeAndItemInfo.Add (new Tuple (parent, fullPathLength, hashCode, nameOffset, index)); } List> scopeExInfo = new List> (); for (int i = 0; i < numScopes; i++) { ushort scopeIndex = binaryReader.ReadUInt16 (); ushort childCount = binaryReader.ReadUInt16 (); ushort firstChildIndex = binaryReader.ReadUInt16 (); binaryReader.ExpectUInt16 (0); scopeExInfo.Add (new Tuple (scopeIndex, childCount, firstChildIndex)); } ushort [] itemIndexPropertyToIndex = new ushort [numItems]; for (int i = 0; i < numItems; i++) { itemIndexPropertyToIndex [i] = binaryReader.ReadUInt16 (); } long unicodeDataOffset = binaryReader.BaseStream.Position; long asciiDataOffset = binaryReader.BaseStream.Position + unicodeDataLength * 2; ResourceMapScope [] scopes = new ResourceMapScope [numScopes]; ResourceMapItem [] items = new ResourceMapItem [numItems]; for (int i = 0; i < numScopes + numItems; i++) { bool nameInAscii = (scopeAndItemInfo [i].Item3 & 0x20000000) != 0; long pos = (nameInAscii ? asciiDataOffset : unicodeDataOffset) + (scopeAndItemInfo [i].Item4 * (nameInAscii ? 1 : 2)); binaryReader.BaseStream.Seek (pos, SeekOrigin.Begin); string name; if (scopeAndItemInfo [i].Item2 != 0) name = binaryReader.ReadNullTerminatedString (nameInAscii ? Encoding.ASCII : Encoding.Unicode); else name = string.Empty; ushort index = scopeAndItemInfo [i].Item5; bool isScope = (scopeAndItemInfo [i].Item3 & 0x10000000) != 0; if (isScope) { if (scopes [index] != null) throw new InvalidDataException (); scopes [index] = new ResourceMapScope (index, null, name); } else { if (items [index] != null) throw new InvalidDataException (); items [index] = new ResourceMapItem (index, null, name); } } for (int i = 0; i < numScopes + numItems; i++) { ushort index = scopeAndItemInfo [i].Item5; bool isScope = (scopeAndItemInfo [i].Item3 & 0x10000000) != 0; ushort parent = scopeAndItemInfo [i].Item1; parent = scopeAndItemInfo [parent].Item5; if (parent != 0xFFFF) if (isScope) { if (parent != index) scopes [index].Parent = scopes [parent]; } else items [index].Parent = scopes [parent]; } for (int i = 0; i < numScopes; i++) { ResourceMapEntry [] children = new ResourceMapEntry [scopeExInfo [i].Item2]; for (int j = 0; j < children.Length; j++) { var saiInfo = scopeAndItemInfo [scopeExInfo [i].Item3 + j]; bool isScope = (saiInfo.Item3 & 0x10000000) != 0; if (isScope) children [j] = scopes [saiInfo.Item5]; else children [j] = items [saiInfo.Item5]; } scopes [i].Children = children; } Scopes = scopes; Items = items; return true; } ~ReverseMapSection () { Mapping = null; Scopes = null; Items = null; } } }