/// <summary>Makes a new HawkFile based on the provided path.</summary> /// <param name="delayIOAndDearchive">Pass <see langword="true"/> to only populate a few fields (those that can be computed from the string <paramref name="path"/>), which is less computationally expensive.</param> /// <param name="nonArchiveExtensions"> /// These file extensions are assumed to not be archives. Include the leading period in each, and use lowercase.<br/> /// Does not apply when <paramref name="delayIOAndDearchive"/> is <see langword="true"/>.<br/> /// If <see langword="null"/> is passed (the default), uses <see cref="CommonNonArchiveExtensions"/> which should mitigate false positives caused by weak archive detection signatures. /// </param> public HawkFile([HawkFilePath] string path, bool delayIOAndDearchive = false, IReadOnlyCollection <string>?nonArchiveExtensions = null) { if (delayIOAndDearchive) { var split = SplitArchiveMemberPath(path); if (split != null) { (path, ArchiveMemberPath) = split.Value; IsArchive = true; // we'll assume that the '|' is only used for archives } FullPathWithoutMember = path; return; } string?autobind = null; var split1 = SplitArchiveMemberPath(path); if (split1 != null) { (path, autobind) = split1.Value; } FullPathWithoutMember = path; Exists = _rootExists = !string.IsNullOrEmpty(path) && new FileInfo(path).Exists; if (!_rootExists) { return; } if (DearchivalMethod != null && !(nonArchiveExtensions ?? CommonNonArchiveExtensions).Contains(Path.GetExtension(path).ToLowerInvariant()) && DearchivalMethod.CheckSignature(path, out _, out _)) { _extractor = DearchivalMethod.Construct(path); try { _archiveItems = _extractor.Scan(); IsArchive = true; } catch { _archiveItems = null; _extractor.Dispose(); _extractor = null; } } if (_extractor == null) { _rootStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); // we could autobind here, but i don't want to // bind it later with the desired extensions. } if (autobind == null) { // non-archive files can be automatically bound this way BindRoot(); } else { if (_extractor != null) { var scanResults = _extractor.Scan(); for (int i = 0, l = scanResults.Count; i < l; i++) { if (string.Equals(scanResults[i].Name, autobind, StringComparison.InvariantCultureIgnoreCase)) { BindArchiveMember(i); return; } } } Exists = false; } }
/// <summary>Opens the file at <paramref name="path"/>. This may take a while if the file is an archive, as it may be accessed and scanned.</summary> /// <exception cref="InvalidOperationException">already opened via <see cref="HawkFile(string)"/>, this method, or <see cref="Parse"/></exception> public void Open([HawkFilePath] string path) { if (FullPathWithoutMember != null) { throw new InvalidOperationException($"Don't reopen a {nameof(HawkFile)}."); } string?autobind = null; var split = SplitArchiveMemberPath(path); if (split != null) { (path, autobind) = split.Value; } _rootExists = new FileInfo(path).Exists; if (!_rootExists) { return; } FullPathWithoutMember = path; Exists = true; if (DearchivalMethod != null && !NonArchiveExtensions.Contains(Path.GetExtension(path).ToLowerInvariant()) && DearchivalMethod.CheckSignature(path, out _, out _)) { _extractor = DearchivalMethod.Construct(path); try { _archiveItems = _extractor.Scan(); IsArchive = true; } catch { _archiveItems = null; _extractor.Dispose(); _extractor = null; } } if (_extractor == null) { _rootStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); // we could autobind here, but i don't want to // bind it later with the desired extensions. } if (autobind == null) { // non-archive files can be automatically bound this way BindRoot(); } else { if (_extractor != null) { var scanResults = _extractor.Scan(); for (int i = 0, l = scanResults.Count; i < l; i++) { if (string.Equals(scanResults[i].Name, autobind, StringComparison.InvariantCultureIgnoreCase)) { BindArchiveMember(i); return; } } } Exists = false; } }