using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Text; namespace PriFormat { public class ReferencedFileSection: Section { public IList ReferencedFiles { get; private set; } internal const string Identifier = "[def_file_list]\0"; internal ReferencedFileSection (PriFile priFile) : base (Identifier, priFile) { } protected override bool ParseSectionContent (BinaryReader binaryReader) { ushort numRoots = binaryReader.ReadUInt16 (); ushort numFolders = binaryReader.ReadUInt16 (); ushort numFiles = binaryReader.ReadUInt16 (); binaryReader.ExpectUInt16 (0); uint totalDataLength = binaryReader.ReadUInt32 (); List folderInfos = new List (numFolders); for (int i = 0; i < numFolders; i++) { binaryReader.ExpectUInt16 (0); ushort parentFolder = binaryReader.ReadUInt16 (); ushort numFoldersInFolder = binaryReader.ReadUInt16 (); ushort firstFolderInFolder = binaryReader.ReadUInt16 (); ushort numFilesInFolder = binaryReader.ReadUInt16 (); ushort firstFileInFolder = binaryReader.ReadUInt16 (); ushort folderNameLength = binaryReader.ReadUInt16 (); ushort fullPathLength = binaryReader.ReadUInt16 (); uint folderNameOffset = binaryReader.ReadUInt32 (); folderInfos.Add (new FolderInfo ( parentFolder, numFoldersInFolder, firstFolderInFolder, numFilesInFolder, firstFileInFolder, folderNameLength, fullPathLength, folderNameOffset)); } List fileInfos = new List (numFiles); for (int i = 0; i < numFiles; i++) { binaryReader.ReadUInt16 (); ushort parentFolder = binaryReader.ReadUInt16 (); ushort fullPathLength = binaryReader.ReadUInt16 (); ushort fileNameLength = binaryReader.ReadUInt16 (); uint fileNameOffset = binaryReader.ReadUInt32 (); fileInfos.Add (new FileInfo (parentFolder, fullPathLength, fileNameLength, fileNameOffset)); } long dataStartPosition = binaryReader.BaseStream.Position; List referencedFolders = new List (numFolders); for (int i = 0; i < numFolders; i++) { binaryReader.BaseStream.Seek (dataStartPosition + folderInfos [i].FolderNameOffset * 2, SeekOrigin.Begin); string name = binaryReader.ReadString (Encoding.Unicode, folderInfos [i].FolderNameLength); referencedFolders.Add (new ReferencedFolder (null, name)); } for (int i = 0; i < numFolders; i++) { if (folderInfos [i].ParentFolder != 0xFFFF) referencedFolders [i].Parent = referencedFolders [folderInfos [i].ParentFolder]; } List referencedFiles = new List (numFiles); for (int i = 0; i < numFiles; i++) { binaryReader.BaseStream.Seek (dataStartPosition + fileInfos [i].FileNameOffset * 2, SeekOrigin.Begin); string name = binaryReader.ReadString (Encoding.Unicode, fileInfos [i].FileNameLength); ReferencedFolder parentFolder; if (fileInfos [i].ParentFolder != 0xFFFF) parentFolder = referencedFolders [fileInfos [i].ParentFolder]; else parentFolder = null; referencedFiles.Add (new ReferencedFile (parentFolder, name)); } for (int i = 0; i < numFolders; i++) { List children = new List ( folderInfos [i].NumFoldersInFolder + folderInfos [i].NumFilesInFolder); for (int j = 0; j < folderInfos [i].NumFoldersInFolder; j++) children.Add (referencedFolders [folderInfos [i].FirstFolderInFolder + j]); for (int j = 0; j < folderInfos [i].NumFilesInFolder; j++) children.Add (referencedFiles [folderInfos [i].FirstFileInFolder + j]); referencedFolders [i].Children = children; } ReferencedFiles = new ReadOnlyCollection (referencedFiles); return true; } private struct FolderInfo { public ushort ParentFolder; public ushort NumFoldersInFolder; public ushort FirstFolderInFolder; public ushort NumFilesInFolder; public ushort FirstFileInFolder; public ushort FolderNameLength; public ushort FullPathLength; public uint FolderNameOffset; public FolderInfo ( ushort parentFolder, ushort numFoldersInFolder, ushort firstFolderInFolder, ushort numFilesInFolder, ushort firstFileInFolder, ushort folderNameLength, ushort fullPathLength, uint folderNameOffset) { ParentFolder = parentFolder; NumFoldersInFolder = numFoldersInFolder; FirstFolderInFolder = firstFolderInFolder; NumFilesInFolder = numFilesInFolder; FirstFileInFolder = firstFileInFolder; FolderNameLength = folderNameLength; FullPathLength = fullPathLength; FolderNameOffset = folderNameOffset; } } private struct FileInfo { public ushort ParentFolder; public ushort FullPathLength; public ushort FileNameLength; public uint FileNameOffset; public FileInfo (ushort parentFolder, ushort fullPathLength, ushort fileNameLength, uint fileNameOffset) { ParentFolder = parentFolder; FullPathLength = fullPathLength; FileNameLength = fileNameLength; FileNameOffset = fileNameOffset; } } public override void Dispose () { ReferencedFiles?.Clear (); ReferencedFiles = null; base.Dispose (); } ~ReferencedFileSection () { Dispose (); } } public abstract class ReferencedEntry: IDisposable { public ReferencedFolder Parent { get; internal set; } public string Name { get; private set; } internal ReferencedEntry (ReferencedFolder parent, string name) { Parent = parent; Name = name; } private string fullName; public string FullName { get { if (fullName == null) { if (Parent == null) fullName = Name; else fullName = Parent.FullName + "\\" + Name; } return fullName; } } public virtual void Dispose () { Parent = null; } ~ReferencedEntry () { Dispose (); } } public sealed class ReferencedFolder: ReferencedEntry { internal ReferencedFolder (ReferencedFolder parent, string name) : base (parent, name) { } public IList Children { get; internal set; } public override void Dispose () { Children?.Clear (); Children = null; base.Dispose (); } ~ReferencedFolder () { Dispose (); } } public sealed class ReferencedFile: ReferencedEntry { internal ReferencedFile (ReferencedFolder parent, string name) : base (parent, name) { } } public class ReferencedFileRef { internal int fileIndex; public int FileIndex => fileIndex; internal ReferencedFileRef (int fileIndex) { this.fileIndex = fileIndex; } } }