using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System; namespace PriFileFormat { 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 () { } public static PriFile Parse (Stream stream) { PriFile priFile = new PriFile (); priFile.ParseInternal (stream, false); return priFile; } public static PriFile Parse (System.Runtime.InteropServices.ComTypes.IStream stream, out Stream output) { ComStreamWrapper csw = new ComStreamWrapper (stream); output = csw; PriFile priFile = new PriFile (); priFile.ParseInternal (csw, true); return priFile; } 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; string magic = new string (binaryReader.ReadChars (8)); switch (magic) { case "mrm_pri0": case "mrm_pri1": case "mrm_pri2": case "mrm_prif": Version = magic; break; default: throw new InvalidDataException ("Data does not start with a PRI file header."); } binaryReader.ExpectUInt16 (0); binaryReader.ExpectUInt16 (1); TotalFileSize = binaryReader.ReadUInt32 (); uint tocOffset = binaryReader.ReadUInt32 (); uint sectionStartOffset = binaryReader.ReadUInt32 (); ushort numSections = binaryReader.ReadUInt16 (); binaryReader.ExpectUInt16 (0xFFFF); binaryReader.ExpectUInt32 (0); binaryReader.BaseStream.Seek (fileStartOffset + TotalFileSize - 16, SeekOrigin.Begin); binaryReader.ExpectUInt32 (0xDEFFFADE); binaryReader.ExpectUInt32 (TotalFileSize); binaryReader.ExpectString (magic); binaryReader.BaseStream.Seek (tocOffset, SeekOrigin.Begin); List toc = new List (numSections); for (int i = 0; i < numSections; i++) toc.Add (TocEntry.Parse (binaryReader)); TableOfContents = toc; Section [] sections = new Section [numSections]; Sections = sections; bool parseSuccess = false; bool parseFailure = false; do { for (int i = 0; i < sections.Length; i++) if (sections [i] == null) { binaryReader.BaseStream.Seek (sectionStartOffset + toc [i].SectionOffset, SeekOrigin.Begin); Section section = Section.CreateForIdentifier (toc [i].SectionIdentifier, this); if (section.Parse (binaryReader)) { sections [i] = section; parseSuccess = true; } else parseFailure = true; } } while (parseFailure && parseSuccess); if (parseFailure) throw new InvalidDataException (); } } 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 { get { if (priDescriptorSection == null) priDescriptorSection = Sections.OfType ().Single (); return priDescriptorSection; } } public T GetSectionByRef (SectionRef sectionRef) where T : Section { return (T)Sections [sectionRef.sectionIndex]; } public ResourceMapItem GetResourceMapItemByRef (ResourceMapItemRef resourceMapItemRef) { return GetSectionByRef (resourceMapItemRef.schemaSection).Items [resourceMapItemRef.itemIndex]; } public ByteSpan GetDataItemByRef (DataItemRef dataItemRef) { return GetSectionByRef (dataItemRef.dataItemSection).DataItems [dataItemRef.itemIndex]; } public ReferencedFile GetReferencedFileByRef (ReferencedFileRef referencedFileRef) { return GetSectionByRef (PriDescriptorSection.ReferencedFileSections.First ()).ReferencedFiles [referencedFileRef.fileIndex]; } } }