diff --git a/PriFileFormat.backup.7z b/PriFileFormat.backup.7z new file mode 100644 index 0000000..e710bce Binary files /dev/null and b/PriFileFormat.backup.7z differ diff --git a/PriFileFormat/ComStreamWrapper.cs b/PriFileFormat/ComStreamWrapper.cs index 6ec1882..4524735 100644 --- a/PriFileFormat/ComStreamWrapper.cs +++ b/PriFileFormat/ComStreamWrapper.cs @@ -17,15 +17,15 @@ namespace PriFileFormat } public override bool CanRead { - get { return comStream != null; } + get { return true; } } public override bool CanSeek { - get { return comStream != null; } + get { return true; } } public override bool CanWrite { - get { return comStream != null; } + get { return true; } } public override long Length { @@ -144,8 +144,9 @@ namespace PriFileFormat } public override void Close () { - base.Close (); comStream = null; + base.Close (); } + ~ComStreamWrapper () { comStream = null;} } } diff --git a/PriFileFormat/DataItemSection.cs b/PriFileFormat/DataItemSection.cs index df2f0f7..da3e0b4 100644 --- a/PriFileFormat/DataItemSection.cs +++ b/PriFileFormat/DataItemSection.cs @@ -48,6 +48,7 @@ namespace PriFileFormat _dataItems?.Clear (); _dataItems = null; } + ~DataItemSection () { ClearData (); } } public struct DataItemRef diff --git a/PriFileFormat/DataTree.cs b/PriFileFormat/DataTree.cs new file mode 100644 index 0000000..e1ceb67 --- /dev/null +++ b/PriFileFormat/DataTree.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Serialization; + +namespace PriFileFormat +{ + /// + /// 树节点接口 + /// + public interface ITreeNode: IDisposable, IEnumerable + { + string Name { get; set; } + string Value { get; set; } + ITreeNode Parent { get; set; } + IList Children { get; } + ITreeNode AddChild (string name, string value); + IEnumerable DescendantsAndSelf (); + ITreeNode Find (string name); + IEnumerable FindAll (string name); + } + /// + /// 树节点实现 + /// + [Serializable] + public class TreeNode: ITreeNode + { + // 节点基本属性 + public string Name { get; set; } + public string Value { get; set; } + // 父节点引用 + [XmlIgnore] + public TreeNode Parent { get; set; } + ITreeNode ITreeNode.Parent + { + get { return Parent; } + set { Parent = (TreeNode)value; } + } + // 子节点列表 + [XmlArray ("Children")] + [XmlArrayItem ("Node")] + public IList Children { get; set; } + IList ITreeNode.Children + { + get + { + List list = new List (); + foreach (TreeNode node in Children) + { + list.Add (node); + } + return list; + } + } + public TreeNode () + { + Name = ""; + Value = ""; + Children = new List (); + Parent = null; + } + public TreeNode (string name, string value) + { + Name = name; + Value = value; + Children = new List (); + Parent = null; + } + /// + /// 添加子节点 + /// + public TreeNode AddChild (string name, string value) + { + TreeNode child = new TreeNode (name, value); + child.Parent = this; + Children.Add (child); + return child; + } + ITreeNode ITreeNode.AddChild (string name, string value) + { + return AddChild (name, value); + } + /// + /// 深度优先遍历节点,包括自身 + /// + public IEnumerable DescendantsAndSelf () + { + yield return this; + foreach (TreeNode child in Children) + { + foreach (TreeNode desc in child.DescendantsAndSelf ()) + { + yield return desc; + } + } + } + IEnumerable ITreeNode.DescendantsAndSelf () + { + foreach (TreeNode n in DescendantsAndSelf ()) + { + yield return n; + } + } + /// + /// 查找第一个匹配节点 + /// + public TreeNode Find (string name) + { + foreach (TreeNode n in DescendantsAndSelf ()) + { + if (n.Name == name) + return n; + } + return null; + } + ITreeNode ITreeNode.Find (string name) + { + return Find (name); + } + /// + /// 查找所有匹配节点 + /// + public IEnumerable FindAll (string name) + { + foreach (TreeNode n in DescendantsAndSelf ()) + { + if (n.Name == name) + yield return n; + } + } + IEnumerable ITreeNode.FindAll (string name) + { + foreach (TreeNode n in FindAll (name)) + { + yield return n; + } + } + #region IEnumerable + public IEnumerator GetEnumerator () + { + return Children.GetEnumerator (); + } + IEnumerator IEnumerable.GetEnumerator () + { + return GetEnumerator (); + } + #endregion + #region IEnumerable 显示实现 + IEnumerator IEnumerable.GetEnumerator () + { + foreach (TreeNode child in Children) + { + yield return child; + } + } + #endregion + #region XML 序列化 + public void SaveToXml (string filePath) + { + XmlSerializer serializer = new XmlSerializer (typeof (TreeNode)); + using (StreamWriter writer = new StreamWriter (filePath, false, System.Text.Encoding.UTF8)) + { + serializer.Serialize (writer, this); + } + } + + public static TreeNode LoadFromXml (string filePath) + { + XmlSerializer serializer = new XmlSerializer (typeof (TreeNode)); + using (StreamReader reader = new StreamReader (filePath, System.Text.Encoding.UTF8)) + { + TreeNode root = (TreeNode)serializer.Deserialize (reader); + SetParentRecursive (root, null); + return root; + } + } + + private static void SetParentRecursive (TreeNode node, TreeNode parent) + { + node.Parent = parent; + foreach (TreeNode child in node.Children) + { + SetParentRecursive (child, node); + } + } + #endregion + #region IDisposable + public virtual void Dispose () + { + foreach (TreeNode child in Children) + { + child.Dispose (); + } + Children.Clear (); + Parent = null; + Children = null; + } + #endregion + } + /// + /// 树根节点 + /// + [Serializable] + public class TreeRoot: TreeNode + { + public TreeRoot () + : base ("Root", "") + { + } + } +} diff --git a/PriFileFormat/HierarchicalSchemaSection.cs b/PriFileFormat/HierarchicalSchemaSection.cs index 77435c8..d6985f7 100644 --- a/PriFileFormat/HierarchicalSchemaSection.cs +++ b/PriFileFormat/HierarchicalSchemaSection.cs @@ -10,8 +10,8 @@ namespace PriFileFormat public HierarchicalSchemaVersionInfo Version { get; private set; } public string UniqueName { get; private set; } public string Name { get; private set; } - public IReadOnlyList Scopes { get; private set; } - public IReadOnlyList Items { get; private set; } + public IReadOnlyList Scopes { get; private set; } + public IReadOnlyList Items { get; private set; } bool extendedVersion; internal const string Identifier1 = "[mrm_hschema] \0"; internal const string Identifier2 = "[mrm_hschemaex] "; @@ -64,7 +64,7 @@ namespace PriFileFormat uint unicodeDataLength = binaryReader.ReadUInt32 (); binaryReader.ReadUInt32 (); // meaning unknown if (extendedHNames) binaryReader.ReadUInt32 (); // meaning unknown - List scopeAndItemInfos = new List ((int)(numScopes + numItems)); + List scopeAndItemInfos = new List ((int)(numScopes + numItems)); for (int i = 0; i < numScopes + numItems; i++) { ushort parent = binaryReader.ReadUInt16 (); @@ -78,7 +78,7 @@ namespace PriFileFormat bool nameInAscii = (flags & 0x20) != 0; scopeAndItemInfos.Add (new ScopeAndItemInfo (parent, fullPathLength, isScope, nameInAscii, nameOffset, index)); } - List scopeExInfos = new List ((int)numScopes); + List scopeExInfos = new List ((int)numScopes); for (int i = 0; i < numScopes; i++) { ushort scopeIndex = binaryReader.ReadUInt16 (); @@ -127,7 +127,7 @@ namespace PriFileFormat } for (int i = 0; i < numScopes; i++) { - List children = new List (scopeExInfos [i].ChildCount); + List children = new List (scopeExInfos [i].ChildCount); for (int j = 0; j < scopeExInfos [i].ChildCount; j++) { ScopeAndItemInfo saiInfo = scopeAndItemInfos [scopeExInfos [i].FirstChildIndex + j]; @@ -172,6 +172,18 @@ namespace PriFileFormat FirstChildIndex = firstChildIndex; } } + ~HierarchicalSchemaSection () + { + try + { + Version = null; + foreach (var item in Items) { item.Parent = null; } + foreach (var scope in Scopes) { scope.Parent = null; } + Scopes = null; + Items = null; + } + catch { } + } // Checksum computation is buggy for some files //private uint ComputeHierarchicalSchemaVersionInfoChecksum() @@ -258,19 +270,21 @@ namespace PriFileFormat return fullName; } } + ~ResourceMapEntry () { Parent = null; } } public class ResourceMapScope: ResourceMapEntry { - internal ResourceMapScope (ushort index, ResourceMapScope parent, string name) : base (index, parent, name) { } - public IReadOnlyList Children { get; internal set; } + internal ResourceMapScope (ushort index, ResourceMapScope parent, string name) : base (index, parent, name) {} + public IReadOnlyList Children { get; internal set; } public override string ToString () { return $"Scope {Index} {FullName} ({Children.Count} children)"; } + ~ResourceMapScope () { Children = null; } } public class ResourceMapItem: ResourceMapEntry { - internal ResourceMapItem (ushort index, ResourceMapScope parent, string name) : base (index, parent, name) { } + internal ResourceMapItem (ushort index, ResourceMapScope parent, string name) : base (index, parent, name) {} public override string ToString () { return $"Item {Index} {FullName}"; diff --git a/PriFileFormat/PriDescriptorSection.cs b/PriFileFormat/PriDescriptorSection.cs index 2234876..3650c90 100644 --- a/PriFileFormat/PriDescriptorSection.cs +++ b/PriFileFormat/PriDescriptorSection.cs @@ -46,6 +46,15 @@ namespace PriFileFormat DataItemSections = dataItemSections; return true; } + ~PriDescriptorSection () + { + HierarchicalSchemaSections = null; + DecisionInfoSections = null; + ResourceMapSections = null; + ReferencedFileSections = null; + DataItemSections = null; + PrimaryResourceMapSection = null; + } } [Flags] public enum PriDescriptorFlags: ushort diff --git a/PriFileFormat/PriFile.cs b/PriFileFormat/PriFile.cs index 308e7ee..8e0abf2 100644 --- a/PriFileFormat/PriFile.cs +++ b/PriFileFormat/PriFile.cs @@ -6,12 +6,14 @@ using System; namespace PriFileFormat { - public class PriFile + public class PriFile: IDisposable { public string Version { get; private set; } public uint TotalFileSize { get; private set; } public IReadOnlyList TableOfContents { get; private set; } public IReadOnlyList
Sections { get; private set; } + private bool _disposed = false; + private Stream _internalStream; // 跟踪内部流 private PriFile () { } @@ -35,6 +37,7 @@ namespace PriFileFormat private void ParseInternal (Stream stream, bool ownStream) { + if (ownStream) { _internalStream = stream; } using (BinaryReader binaryReader = new BinaryReader (stream, Encoding.ASCII, true)) { long fileStartOffset = binaryReader.BaseStream.Position; @@ -109,6 +112,48 @@ namespace PriFileFormat } } + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + protected virtual void Dispose (bool disposing) + { + if (_disposed) return; + if (disposing) + { + // 释放托管资源 + if (_internalStream != null) + { + _internalStream.Dispose (); + _internalStream = null; + } + + // 释放 section 内容 + if (Sections != null) + { + foreach (var section in Sections) + { + var unknown = section as UnknownSection; + if (unknown != null) + unknown.ClearContent (); + + var dataSection = section as DataItemSection; + if (dataSection != null) + dataSection.ClearData (); + } + Sections = null; + } + } + + _disposed = true; + } + + ~PriFile () + { + Dispose (false); + } + PriDescriptorSection priDescriptorSection; public PriDescriptorSection PriDescriptorSection diff --git a/PriFileFormat/ReferencedFileSection.cs b/PriFileFormat/ReferencedFileSection.cs index 162f323..48cde41 100644 --- a/PriFileFormat/ReferencedFileSection.cs +++ b/PriFileFormat/ReferencedFileSection.cs @@ -121,6 +121,11 @@ namespace PriFileFormat FileNameOffset = fileNameOffset; } } + ~ReferencedFileSection () + { + foreach (var file in ReferencedFiles) { file.Parent = null; } + ReferencedFiles = null; + } } public class ReferencedEntry { @@ -145,11 +150,13 @@ namespace PriFileFormat return fullName; } } + ~ReferencedEntry () { Parent = null; } } public class ReferencedFolder: ReferencedEntry { internal ReferencedFolder (ReferencedFolder parent, string name) : base (parent, name) {} public IReadOnlyList Children { get; internal set; } + ~ReferencedFolder () { Children = null; } } public class ReferencedFile: ReferencedEntry { diff --git a/PriFileFormat/ResourceMapSection.cs b/PriFileFormat/ResourceMapSection.cs index d0b3251..6d3a818 100644 --- a/PriFileFormat/ResourceMapSection.cs +++ b/PriFileFormat/ResourceMapSection.cs @@ -296,6 +296,11 @@ namespace PriFileFormat DataOffset = dataOffset; } } + ~ResourceMapSection () + { + HierarchicalSchemaReference = null; + CandidateSets = null; + } } public enum ResourceValueType { @@ -318,6 +323,10 @@ namespace PriFileFormat DecisionIndex = decisionIndex; Candidates = candidates; } + ~CandidateSet () + { + Candidates = null; + } } public class Candidate @@ -343,6 +352,12 @@ namespace PriFileFormat DataItem = null; Data = data; } + ~Candidate () + { + SourceFile = null; + DataItem = null; + Data = null; + } } public class HierarchicalSchemaReference { @@ -357,6 +372,10 @@ namespace PriFileFormat Unknown2 = unknown2; UniqueName = uniqueName; } + ~HierarchicalSchemaReference () + { + VersionInfo = null; + } } public struct ResourceMapItemRef { diff --git a/PriFileFormat/ReverseMapSection.cs b/PriFileFormat/ReverseMapSection.cs index 8398218..2d68af3 100644 --- a/PriFileFormat/ReverseMapSection.cs +++ b/PriFileFormat/ReverseMapSection.cs @@ -148,6 +148,12 @@ namespace PriFileFormat return true; } + ~ReverseMapSection () + { + Mapping = null; + Scopes = null; + Items = null; + } } } diff --git a/PriFileFormat/Section.cs b/PriFileFormat/Section.cs index abdb45f..d5b7395 100644 --- a/PriFileFormat/Section.cs +++ b/PriFileFormat/Section.cs @@ -81,5 +81,9 @@ namespace PriFileFormat } } + ~Section () + { + PriFile = null; + } } } diff --git a/PriFileFormat/SubStream.cs b/PriFileFormat/SubStream.cs index 57a5c2d..72a1ecd 100644 --- a/PriFileFormat/SubStream.cs +++ b/PriFileFormat/SubStream.cs @@ -71,10 +71,6 @@ namespace PriFileFormat throw new NotSupportedException (); } - public override void Close () - { - base.Close (); - baseStream = null; - } + ~SubStream () { baseStream = null; } } } diff --git a/PriFileFormat/UnknownSection.cs b/PriFileFormat/UnknownSection.cs index 75460dc..f445452 100644 --- a/PriFileFormat/UnknownSection.cs +++ b/PriFileFormat/UnknownSection.cs @@ -18,5 +18,6 @@ namespace PriFileFormat { SectionContent = null; } + ~UnknownSection () { ClearContent (); } } } diff --git a/priformatcli/priformatcli.cpp b/priformatcli/priformatcli.cpp index 1a22feb..dad909c 100644 --- a/priformatcli/priformatcli.cpp +++ b/priformatcli/priformatcli.cpp @@ -20,32 +20,6 @@ #include #include -LPWSTR AllocWideString (const std::wstring &str) -{ - size_t size = (str.length () + 1) * sizeof (WCHAR); - LPWSTR buf = (LPWSTR)CoTaskMemAlloc (size); - if (!buf) return nullptr; - ZeroMemory (buf, size); - wcscpy (buf, str.c_str ()); - return buf; -} -// -LPWSTR AllocWideString (LPCWSTR str) -{ - if (!str) return nullptr; - size_t size = (wcslen (str) + 1) * sizeof (WCHAR); - LPWSTR buf = (LPWSTR)CoTaskMemAlloc (size); - if (!buf) return nullptr; - ZeroMemory (buf, size); - wcscpy (buf, str); - return buf; -} -#define _wcsdup AllocWideString -#define free CoTaskMemFree -#define malloc CoTaskMemAlloc -#define realloc CoTaskMemRealloc -#define calloc(_cnt_, _size_) CoTaskMemAlloc (_cnt_ * _size_) - const std::wstring g_swMsResUriProtocolName = L"ms-resource:"; const size_t g_cbMsResPNameLength = lstrlenW (g_swMsResUriProtocolName.c_str ()); std::wstring g_swExcept = L"";