using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Text; namespace PriFormat { public class ReverseMapSection: Section { public uint [] Mapping { get; private set; } public IList Scopes { get; private set; } public IList 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 (); if (numEntries != numScopes + numItems) throw new InvalidDataException (); binaryReader.ExpectUInt32 (numItems); uint unicodeDataLength = binaryReader.ReadUInt32 (); binaryReader.ReadUInt32 (); // meaning unknown List scopeAndItemInfos = new List ((int)(numScopes + numItems)); for (int i = 0; i < numScopes + numItems; i++) { ushort parent = binaryReader.ReadUInt16 (); ushort fullPathLength = binaryReader.ReadUInt16 (); char uppercaseFirstChar = (char)binaryReader.ReadUInt16 (); byte nameLength2 = binaryReader.ReadByte (); byte flags = binaryReader.ReadByte (); uint nameOffset = binaryReader.ReadUInt16 () | (uint)((flags & 0xF) << 16); ushort index = binaryReader.ReadUInt16 (); scopeAndItemInfos.Add (new ScopeAndItemInfo (parent, fullPathLength, flags, nameOffset, index)); } List scopeExInfos = new List ((int)numScopes); for (int i = 0; i < numScopes; i++) { ushort scopeIndex = binaryReader.ReadUInt16 (); ushort childCount = binaryReader.ReadUInt16 (); ushort firstChildIndex = binaryReader.ReadUInt16 (); binaryReader.ExpectUInt16 (0); scopeExInfos.Add (new ScopeExInfo (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++) { long pos; if (scopeAndItemInfos [i].NameInAscii) pos = asciiDataOffset + scopeAndItemInfos [i].NameOffset; else pos = unicodeDataOffset + scopeAndItemInfos [i].NameOffset * 2; binaryReader.BaseStream.Seek (pos, SeekOrigin.Begin); string name; if (scopeAndItemInfos [i].FullPathLength != 0) name = binaryReader.ReadNullTerminatedString ( scopeAndItemInfos [i].NameInAscii ? Encoding.ASCII : Encoding.Unicode); else name = string.Empty; ushort index = scopeAndItemInfos [i].Index; if (scopeAndItemInfos [i].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 = scopeAndItemInfos [i].Index; ushort parent = scopeAndItemInfos [scopeAndItemInfos [i].Parent].Index; if (parent != 0xFFFF) { if (scopeAndItemInfos [i].IsScope) { if (parent != index) scopes [index].Parent = scopes [parent]; } else items [index].Parent = scopes [parent]; } } for (int i = 0; i < numScopes; i++) { List children = new List (scopeExInfos [i].ChildCount); for (int j = 0; j < scopeExInfos [i].ChildCount; j++) { ScopeAndItemInfo saiInfo = scopeAndItemInfos [scopeExInfos [i].FirstChildIndex + j]; if (saiInfo.IsScope) children.Add (scopes [saiInfo.Index]); else children.Add (items [saiInfo.Index]); } scopes [i].Children = children; } Scopes = new ReadOnlyCollection (scopes); Items = new ReadOnlyCollection (items); return true; } private struct ScopeAndItemInfo { public ushort Parent; public ushort FullPathLength; public byte Flags; public uint NameOffset; public ushort Index; public ScopeAndItemInfo (ushort parent, ushort fullPathLength, byte flags, uint nameOffset, ushort index) { Parent = parent; FullPathLength = fullPathLength; Flags = flags; NameOffset = nameOffset; Index = index; } public bool IsScope { get { return (Flags & 0x10) != 0; } } public bool NameInAscii { get { return (Flags & 0x20) != 0; } } } private struct ScopeExInfo { public ushort ScopeIndex; public ushort ChildCount; public ushort FirstChildIndex; public ScopeExInfo (ushort scopeIndex, ushort childCount, ushort firstChildIndex) { ScopeIndex = scopeIndex; ChildCount = childCount; FirstChildIndex = firstChildIndex; } } public override void Dispose () { Mapping = null; Scopes?.Clear (); Scopes = null; Items?.Clear (); Items = null; base.Dispose (); } ~ReverseMapSection () { Dispose (); } } }