コード例 #1
0
ファイル: NKitForm.cs プロジェクト: pepdiz/NKit
        private Task process(int mode, string regex, string path, ListViewItem[] items, Action <ListViewItem> startItem, Func <ListViewItem, bool> completedItem)
        {
            var guiSettings = new
            {
                Regex      = txtSettingsRegex.Text,
                SystemOnly = chkSettingsSystemFiles.Checked
            };

            return(Task.Run(() =>
            {
                foreach (ListViewItem item in items)
                {
                    ProcessFile pf = (ProcessFile)item.Tag;
                    SourceFile src = pf.SourceFile;
                    Converter nkitConvert = new Converter(src, false);
                    startItem(item);

                    try
                    {
                        nkitConvert.LogMessage += NkitConvert_LogMessage;

                        int fileTotalLen = src.TotalFiles.ToString().Length;

                        using (NDisc dx = new NDisc(nkitConvert, src.Name))
                        {
                            if (dx != null)
                            {
                                try
                                {
                                    switch (mode)
                                    {
                                    case 1:
                                        pf.Results = dx.ExtractRecoveryFiles();
                                        break;

                                    case 2:
                                        Regex rx = new Regex(guiSettings.Regex, RegexOptions.Compiled | RegexOptions.IgnoreCase);
                                        pf.Results = dx.ExtractFiles(
                                            /*test*/ f => (guiSettings.SystemOnly && (f.Type == ExtractedFileType.System || f.Type == ExtractedFileType.WiiDiscItem)) || (!guiSettings.SystemOnly && rx.IsMatch(string.Format("{0}/{1}", f.Path, f.Name))),
                                            /*extract*/ (s, f) => saveFile(path, pf.SourceFile.Name, f, s));
                                        break;

                                    case 3:
                                        pf.Results = dx.ExtractBasicInfo();
                                        break;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    this.Invoke((MethodInvoker) delegate { addToLog(ex.Message); });
                                }
                                finally
                                {
                                }
                            }
                        }
                    }
                    finally
                    {
                        nkitConvert.LogMessage -= NkitConvert_LogMessage;
                    }
                    if (!completedItem(item))
                    {
                        break;
                    }
                }
            }));
        }
コード例 #2
0
        private Task process(int convertMode, ListViewItem[] items, Action <ListViewItem> startItem, Func <ListViewItem, bool> completedItem)
        {
            //get the settings on the main thread to be used on another thread
            var guiSettings = new
            {
                SummaryLog                 = txtSettingsSummaryLog.Text,
                TempPath                   = txtSettingsTempPath.Text,
                CalculateHashes            = chkSettingsCalculateHashes.Checked,
                DeleteSource               = chkSettingsDeleteSource.Checked,
                EnableSummaryLog           = chkSettingsSummaryLog.Checked,
                FullVerify                 = chkSettingsFullVerify.Checked,
                NkitReencode               = chkSettingsReencodeNkit.Checked,
                NkitUpdatePartitionRemoval = chkSettingsRemoveUpdate.Checked,
                TestMode                   = chkSettingsTestMode.Checked,
                MaskRename                 = chkSettingsUseMasks.Checked,
                RecoveryMatchFailDelete    = chkSettingsRecoveryMatchFailDelete.Checked,
                Masks = _masks
            };

            return(Task.Run(() =>
            {
                foreach (ListViewItem item in items)
                {
                    bool exitLoop = false;
                    Converter nkitConvert = null;
                    ProcessFile pf = (ProcessFile)item.Tag;
                    try
                    {
                        SourceFile src = pf.SourceFile;
                        nkitConvert = new Converter(src, false);
                        startItem(item);
                        nkitConvert.Settings.SummaryLog = guiSettings.SummaryLog;
                        nkitConvert.Settings.TempPath = guiSettings.TempPath;
                        nkitConvert.Settings.CalculateHashes = guiSettings.CalculateHashes;
                        nkitConvert.Settings.DeleteSource = guiSettings.DeleteSource;
                        nkitConvert.Settings.EnableSummaryLog = guiSettings.EnableSummaryLog;
                        nkitConvert.Settings.FullVerify = guiSettings.FullVerify;
                        nkitConvert.Settings.NkitReencode = guiSettings.NkitReencode;
                        nkitConvert.Settings.NkitUpdatePartitionRemoval = guiSettings.NkitUpdatePartitionRemoval;
                        nkitConvert.Settings.TestMode = guiSettings.TestMode;
                        nkitConvert.Settings.MaskRename = guiSettings.MaskRename;
                        nkitConvert.Settings.RecoveryMatchFailDelete = guiSettings.RecoveryMatchFailDelete;

                        if (nkitConvert.Settings.DiscType == DiscType.GameCube)
                        {
                            nkitConvert.Settings.RedumpMatchRenameToMask = guiSettings.Masks["GameCube:RedumpMatchRenameToMask"];
                            nkitConvert.Settings.CustomMatchRenameToMask = guiSettings.Masks["GameCube:CustomMatchRenameToMask"];
                            nkitConvert.Settings.MatchFailRenameToMask = guiSettings.Masks["GameCube:MatchFailRenameToMask"];
                        }
                        else
                        {
                            nkitConvert.Settings.RedumpMatchRenameToMask = guiSettings.Masks["Wii:RedumpMatchRenameToMask"];
                            nkitConvert.Settings.CustomMatchRenameToMask = guiSettings.Masks["Wii:CustomMatchRenameToMask"];
                            nkitConvert.Settings.MatchFailRenameToMask = guiSettings.Masks["Wii:MatchFailRenameToMask"];
                        }

                        nkitConvert.LogMessage += NkitConvert_LogMessage;
                        nkitConvert.LogProgress += NkitConvert_LogProgress;
                        pf.Log = "";

                        switch (convertMode)
                        {
                        case 1:     //Recover to ISO
                            pf.Results = nkitConvert.RecoverToIso();
                            break;

                        case 2:     //Recover to Nkit.iso
                            nkitConvert.Settings.NkitFormat = NkitFormatType.Iso;
                            pf.Results = nkitConvert.RecoverToNkit();
                            break;

                        case 3:     //Recover to Nkit.gcz
                            nkitConvert.Settings.NkitFormat = NkitFormatType.Gcz;
                            pf.Results = nkitConvert.RecoverToNkit();
                            break;

                        case 4:     //Convert to .iso
                            pf.Results = nkitConvert.ConvertToIso();
                            break;

                        case 5:     //Convert to Nkit.iso
                            nkitConvert.Settings.NkitFormat = NkitFormatType.Iso;
                            pf.Results = nkitConvert.ConvertToNkit();
                            break;

                        case 6:     //Convert to Nkit.gcz
                            nkitConvert.Settings.NkitFormat = NkitFormatType.Gcz;
                            pf.Results = nkitConvert.ConvertToNkit();
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            HandledException hex = ex as HandledException;
                            if (hex == null && ex is AggregateException)
                            {
                                hex = (HandledException)((AggregateException)ex).InnerExceptions.FirstOrDefault(a => a is HandledException);
                            }

                            pf.Log += string.Format("Failed{0}-------{0}{1}", Environment.NewLine, hex != null ? hex.FriendlyErrorMessage : ex.Message);
                        }
                        catch { }
                    }
                    finally
                    {
                        exitLoop = !completedItem(item);
                        if (nkitConvert != null)
                        {
                            nkitConvert.LogMessage -= NkitConvert_LogMessage;
                            nkitConvert.LogProgress -= NkitConvert_LogProgress;
                        }
                    }
                    if (exitLoop)
                    {
                        break;
                    }
                }
            }));
        }
コード例 #3
0
        private OutputResults process(string conversion, SourceFile sourceFile, bool renameWithMasks, bool toNkit, NkitFormatType nkitFormat, bool isRecovery, bool fullVerify, bool calcHashes, bool testMode, Func <NStream, IEnumerable <Processor> > passes)
        {
            OutputResults results = null;
            NStream       nstream = _nstream;
            string        lastTmp = null;
            string        tmp     = null;

            ConvertionName = conversion;

            try
            {
                SourceFile sf         = null;
                long       sourceSize = nstream.SourceSize;

                _context = new Context();
                _context.Initialise(ConvertionName, sourceFile, _settings, true, isRecovery, nstream.IsGameCube, nstream.Id8, this);

                List <Processor> processors = passes(nstream).Where(a => a != null).ToList();
                if (nkitFormat == NkitFormatType.Gcz)
                {
                    processors.Add(new Processor(new IsoReader(), new GczWriter(), "To GCZ", this, false, true, ProcessorSizeMode.Stream));
                }

                if (calcHashes)
                {
                    processors.Add(new Processor(new IsoReader(), new HashWriter(), "Calc Hashes", this, false, true, ProcessorSizeMode.Stream));
                }

                if (fullVerify)
                {
                    if (!toNkit)
                    {
                        processors.Add(new Processor(new IsoReader(), new VerifyWriter(), "Full Verify", this, false, true, ProcessorSizeMode.Stream));
                    }
                    else
                    {
                        processors.Add(new Processor(nstream.IsGameCube ? new NkitReaderGc() : (IReader) new NkitReaderWii(), new VerifyWriter(), "NKit Verify", this, true, true, ProcessorSizeMode.Image));
                    }

                    processors.Last().Writer.RequireVerifyCrc = true;
                    processors.Last().Writer.VerifyIsWrite = true; //read verify
                }
                _totalPasses = processors.Count();

                if (processors.Count == 0)
                {
                    return(null);
                }

                DateTime dt = DateTime.Now;
                _completedPasses = 0;

                Log("PROCESSING" + (testMode ? " [TEST MODE]" : ((_context.Settings.DeleteSource ? " [DELETE SOURCE]" : ""))));
                Log("-------------------------------------------------------------------------------");
                if (_forcedWiiFullNkitRebuild)
                {
                    LogBlank();
                    Log(string.Format("Nkit Reencode forced: NkitUpdatePartitionRemoval is {0} and source image has {1} Update Partition", _context.Settings.NkitUpdatePartitionRemoval.ToString(), nstream.IsNkitUpdateRemoved ? "no" : "an"));
                    LogBlank();
                }
                Log(string.Format("{0} [{1}]  {2}  [MiB:{3,2:#.0}]", friendly(nstream.Title), friendly(nstream.Id), nstream.IsGameCube ? "GameCube" : "Wii", (sourceSize / (double)(1024 * 1024))));
                LogBlank();
                string passesText = getPassesLine(nstream, processors);
                Log(passesText);

                int i = 1;
                foreach (Processor pro in processors.Where(pro => pro != null))
                {
                    LogDebug(string.Format("Pass {0}: {1}", (i++).ToString(), pro.ToString()));
                }

                LogBlank();

                foreach (Processor pro in processors)
                {
                    //sort out temp file and open input as nstream each time
                    //raise progress and output messages from processors

                    if (sf != null)
                    {
                        nstream = sf.OpenNStream(!(pro.Writer is HashWriter)); //if hashWriter then read as raw file
                        sf      = null;
                    }

                    tmp = null;
                    FileStream tmpFs = null;

                    try
                    {
                        if (pro.HasWriteStream)
                        {
                            tmp = Path.Combine(_context.Settings.TempPath, Path.GetFileName(Path.GetTempFileName()));
                            if (!Directory.Exists(_context.Settings.TempPath))
                            {
                                Directory.CreateDirectory(_context.Settings.TempPath);
                            }

                            tmpFs = File.Create(tmp, 0x400 * 0x400 * 4, FileOptions.SequentialScan);
                        }

                        _logCache = new List <Tuple <string, LogMessageType> >();
                        OutputResults nr = pro.Process(_context, nstream, tmpFs ?? Stream.Null);
                        _logCache = null;

                        if (results == null)
                        {
                            results                  = nr;
                            results.DiscType         = nstream.IsGameCube ? DiscType.GameCube : DiscType.Wii;
                            results.InputFileName    = sourceFile.AllFiles.Length != 0 ? sourceFile.AllFiles[0] : sourceFile.FilePath;
                            results.InputDiscNo      = nstream.DiscNo;
                            results.InputDiscVersion = nstream.Version;
                            results.InputTitle       = nstream.Title;
                            results.InputId8         = nstream.Id8;
                            results.InputSize        = sourceSize;
                            results.FullSize         = nstream.ImageSize;
                            results.Passes           = passesText;
                            if (pro.IsVerify)
                            {
                                results.OutputSize = results.InputSize;
                            }
                        }
                        else if (pro.Writer is HashWriter) //hash writer gives no meaningful info back other than md5 and sha1 (the crc is forced when nkit, so ignore)
                        {
                            results.OutputMd5  = nr.OutputMd5;
                            results.OutputSha1 = nr.OutputSha1;
                        }
                        else
                        {
                            if (nr.AliasJunkId != null)
                            {
                                results.AliasJunkId = nr.AliasJunkId;
                            }

                            if (nr.OutputTitle != null)
                            {
                                results.OutputDiscNo      = nr.OutputDiscNo;
                                results.OutputDiscVersion = nr.OutputDiscVersion;
                                results.OutputTitle       = nr.OutputTitle;
                            }
                            results.OutputId8         = nr.OutputId8;
                            results.OutputCrc         = nr.OutputCrc;
                            results.OutputPrePatchCrc = nr.OutputPrePatchCrc;
                            results.FullSize          = nstream.ImageSize;
                            if (tmp != null)
                            {
                                results.OutputSize = nr.OutputSize;
                            }

                            if (nr.ValidationCrc != 0 && results.VerifyCrc != 0)
                            {
                                results.VerifyCrc = 0; //blank verify if a new ValidationCrc is set - verification happened when both != 0
                            }

                            if (nr.VerifyCrc != 0)
                            {
                                results.VerifyCrc = nr.VerifyCrc;
                            }

                            if (nr.ValidationCrc != 0)
                            {
                                results.ValidationCrc = nr.ValidationCrc;
                            }

                            if (nr.ValidateReadResult != VerifyResult.Unverified)
                            {
                                results.ValidateReadResult = nr.ValidateReadResult;
                            }

                            if (nr.VerifyOutputResult != VerifyResult.Unverified)
                            {
                                if (results.ValidateReadResult == VerifyResult.Unverified && nstream.IsNkit)
                                {
                                    results.ValidateReadResult = nr.VerifyOutputResult;
                                }

                                results.VerifyOutputResult = nr.VerifyOutputResult;
                            }
                            if (nr.IsRecoverable)
                            {
                                results.IsRecoverable = nr.IsRecoverable;
                            }
                        }
                    }
                    finally
                    {
                        if (tmpFs != null)
                        {
                            tmpFs.Dispose();
                        }

                        nstream.Close();
                    }

                    if (lastTmp != null && tmp != null)
                    {
                        File.Delete(lastTmp);
                    }

                    //handle failures well
                    if (results.ValidateReadResult == VerifyResult.VerifyFailed || results.VerifyOutputResult == VerifyResult.VerifyFailed)
                    {
                        lastTmp = tmp; //keep post loop happy
                        break;
                    }

                    if (_completedPasses != _totalPasses) //last item
                    {
                        sf = SourceFiles.OpenFile(tmp ?? lastTmp);
                    }

                    if (tmp != null)
                    {
                        lastTmp = tmp;
                    }
                }

                TimeSpan ts = DateTime.Now - dt;
                results.ProcessingTime = ts;

                //FAIL
                if (results.ValidateReadResult == VerifyResult.VerifyFailed || results.VerifyOutputResult == VerifyResult.VerifyFailed)
                {
                    LogBlank();
                    Log(string.Format("Verification Failed Crc:{0} - Failed Test Crc:{1}", results.OutputCrc.ToString("X8"), results.ValidationCrc.ToString("X8")));

                    if (lastTmp != null) //only null when verify only
                    {
                        Log("Deleting Output" + (Settings.OutputLevel != 3 ? "" : " (Skipped as OutputLevel is 3:Debug)"));
                        results.OutputFileName = null;
                        if (Settings.OutputLevel != 3)
                        {
                            File.Delete(lastTmp);
                        }

                        LogBlank();
                    }
                }
                else //SUCCESS
                {
                    LogBlank();
                    Log(string.Format("Completed ~ {0}m {1}s  [MiB:{2:#.0}]", ((int)ts.TotalMinutes).ToString(), ts.Seconds.ToString(), (results.OutputSize / (double)(1024 * 1024))));
                    LogBlank();
                    Log("RESULTS");
                    Log("-------------------------------------------------------------------------------");

                    uint   finalCrc = results.ValidationCrc != 0 ? results.ValidationCrc : results.OutputCrc;
                    string mask     = _context.Settings.MatchFailRenameToMask;
                    results.OutputFileExt = "." + SourceFiles.ExtensionString(false, false, toNkit, nkitFormat == NkitFormatType.Gcz).ToLower();
                    results.RedumpInfo    = _context.Dats.GetRedumpEntry(_context.Settings, _context.Dats, finalCrc);
                    if (results.RedumpInfo.MatchType == MatchType.Redump || results.RedumpInfo.MatchType == MatchType.Custom)
                    {
                        Log(string.Format("{0} [{1} Name]", results.RedumpInfo.MatchName, results.RedumpInfo.MatchType.ToString()));
                        if (results.IsRecoverable)
                        {
                            Log(string.Format("Missing Recovery data is required to correctly restore this image!", results.RedumpInfo.MatchName, results.RedumpInfo.MatchType.ToString()));
                        }

                        mask = results.RedumpInfo.MatchType == MatchType.Custom ? _context.Settings.CustomMatchRenameToMask : _context.Settings.RedumpMatchRenameToMask;
                    }
                    else
                    {
                        Log(string.Format("CRC {0} not in Redump or Custom Dat", finalCrc.ToString("X8")));
                    }

                    LogBlank();


                    outputResults(results);


                    if (lastTmp != null) //only null when verify only
                    {
                        if (testMode)
                        {
                            Log("TestMode: Deleting Output");
                            results.OutputFileName = null;
                            if (File.Exists(lastTmp))
                            {
                                File.Delete(lastTmp);
                            }
                        }
                        else if (isRecovery && _context.Settings.RecoveryMatchFailDelete && results.RedumpInfo.MatchType == MatchType.MatchFail)
                        {
                            Log("Failed to Recover to Dat Entry: Deleting Output");
                            results.OutputFileName = null;
                            File.Delete(lastTmp);
                        }
                        else
                        {
                            if (renameWithMasks)
                            {
                                results.OutputFileName = _context.Dats.GetFilename(results, mask);
                                Log("Renaming Output Using Masks");
                            }
                            else
                            {
                                results.OutputFileName = SourceFiles.GetUniqueName(sourceFile.CreateOutputFilename(results.OutputFileExt));
                                Log("Renaming Output Based on Source File" + (sourceFile.AllFiles.Count() > 1 ? "s" : ""));
                            }
                            LogBlank();

                            string path = Path.GetDirectoryName(results.OutputFileName);
                            if (!Directory.Exists(path))
                            {
                                Directory.CreateDirectory(path);
                            }

                            File.Move(lastTmp, results.OutputFileName);

                            Log(string.Format("Output: {0}", Path.GetDirectoryName(results.OutputFileName)));
                            Log(string.Format("    {0}", Path.GetFileName(results.OutputFileName)));

                            //double check test mode just to be sure
                            if (_context.Settings.DeleteSource && !testMode && results.VerifyOutputResult == VerifyResult.VerifySuccess)
                            {
                                LogBlank();
                                Log("Deleting Source:");
                                foreach (string s in sourceFile.AllFiles.Length == 0 ? new string[] { sourceFile.FilePath } : sourceFile.AllFiles)
                                {
                                    Log(string.Format("    {0}", s));
                                    File.Delete(s);
                                }
                            }
                        }
                        LogBlank();
                    }
                }
            }
            catch (Exception ex)
            {
                try
                {
                    if (lastTmp == null)
                    {
                        lastTmp = tmp;
                    }

                    if (lastTmp != null)
                    {
                        LogBlank();
                        Log("Deleting Output" + (Settings.OutputLevel != 3 ? "" : " (Skipped as OutputLevel is 3:Debug)"));
                        if (results != null)
                        {
                            results.OutputFileName = null;
                        }

                        if (Settings.OutputLevel != 3)
                        {
                            File.Delete(lastTmp);
                        }
                    }
                }
                catch { }

                if (_context.Settings.EnableSummaryLog)
                {
                    if (results == null)
                    {
                        results = new OutputResults
                        {
                            Conversion       = ConvertionName,
                            DiscType         = (nstream?.IsGameCube ?? true) ? DiscType.GameCube : DiscType.Wii,
                            InputFileName    = (sourceFile?.AllFiles?.Length ?? 0) == 0 ? (sourceFile?.FilePath ?? "") : (sourceFile?.AllFiles.FirstOrDefault() ?? ""),
                            InputDiscNo      = nstream?.DiscNo ?? 0,
                            InputDiscVersion = nstream?.Version ?? 0,
                            InputTitle       = nstream?.Title ?? "",
                            InputId8         = nstream?.Id8 ?? "",
                            InputSize        = sourceFile?.Length ?? 0
                        };
                    }
                    results.VerifyOutputResult = VerifyResult.Error;
                    HandledException hex = ex as HandledException;
                    if (hex == null)
                    {
                        hex = new HandledException(ex, "Unhandled Exception");
                    }

                    results.ErrorMessage = hex.FriendlyErrorMessage;
                }
                throw;
            }
            finally
            {
                if (_context.Settings.EnableSummaryLog)
                {
                    summaryLog(_context.Settings, results);
                    Log("Summary Log Written" + (results.VerifyOutputResult != VerifyResult.Error ? "" : " as Errored!"));
                    LogBlank();
                }
            }

            return(results);
        }
コード例 #4
0
        private static IEnumerable <SourceFile> buildCollection(List <string> files)
        {
            List <SourceFile> srcFiles = new List <SourceFile>();
            long length;

            List <string> firstParts = new List <string>();

            firstParts = files.Where(a => Regex.IsMatch(a, @"\.(part0*1\.rar|z01|001|r00|wbf1)$")).ToList();

            //get multi file sets
            foreach (string s in firstParts)
            {
                length = 0;
                try
                {
                    Match m = Regex.Match(s.ToLower(), @"^(.*\.)(part0*1\.rar|z01|001|r00|wbf1)$");
                    if (m.Success)
                    {
                        string firstName = Regex.Replace(s, @".r00$", ".rar", RegexOptions.IgnoreCase);
                        firstName = Regex.Replace(firstName, @".wbf1$", ".wbfs", RegexOptions.IgnoreCase);
                        List <string> parts = files.Where(a => string.Compare(firstName, a, true) != 0 && a.Length == s.Length && a.ToLower().StartsWith(m.Groups[1].Value)).OrderBy(a => a).ToList();
                        if (parts.Count != 0)
                        {
                            parts.Insert(0, firstName);
                        }
                        SourceFile sf = new SourceFile()
                        {
                            Name     = Path.GetFileName(firstName),
                            Path     = Path.GetDirectoryName(firstName),
                            FilePath = firstName,
                            AllFiles = parts.ToArray(),
                            IsSplit  = firstName.EndsWith(".001") || firstName.ToLower().EndsWith("wbfs")
                        };
                        srcFiles.Add(sf);
                        files.RemoveAll(a => sf.AllFiles.Contains(a)); //relies on ToLower used above
                        foreach (FileInfo fi in sf.AllFiles.Select(a => new FileInfo(a)))
                        {
                            length += fi.Length;
                        }
                        sf.Length = length;
                    }
                }
                catch (Exception ex)
                {
                    throw new HandledException(ex, "SourceFiles.buildCollection failed on '{0}' getting multi file sets", s ?? "");
                }
            }

            //get single file sets
            foreach (string fn in files.Where(a => Regex.IsMatch(Path.GetExtension(a), @"\.(gcz|gcm|iso|dec|wbfs|zip|rar|7z|gz|z)(:?_[0-9]*)?$", RegexOptions.IgnoreCase)))
            {
                try
                {
                    SourceFile sf = new SourceFile()
                    {
                        Name     = Path.GetFileName(fn),
                        Path     = Path.GetDirectoryName(fn),
                        FilePath = fn,
                        AllFiles = new string[0],
                        IsSplit  = false,
                        Length   = (new FileInfo(fn)).Length
                    };
                    srcFiles.Add(sf);
                }
                catch (Exception ex)
                {
                    throw new HandledException(ex, "SourceFiles.buildCollection failed on '{0}' getting single file info", fn ?? "");
                }
            }

            //get files from archives
            for (int i = srcFiles.Count - 1; i >= 0; i--)
            {
                try
                {
                    string[] arcs = GetArchiveFiles(srcFiles[i]);
                    if (arcs == null)
                    {
                        continue;              //not archive, ignore
                    }
                    else if (arcs.Length == 0) //archive with no valid files
                    {
                        srcFiles.RemoveAt(i);
                    }
                    else
                    {
                        for (int j = 0; j < arcs.Length; j++)
                        {
                            if (j == 0)
                            {
                                srcFiles[i].IsArchive = true;
                                srcFiles[i].Name      = Path.GetFileName(arcs[j]);
                                srcFiles[i].Path      = Path.GetDirectoryName(arcs[j]);
                            }
                            else
                            {
                                SourceFile sf = new SourceFile()
                                {
                                    IsArchive = srcFiles[i].IsArchive,
                                    Name      = Path.GetFileName(arcs[j]),
                                    Path      = Path.GetDirectoryName(arcs[j]),
                                    FilePath  = srcFiles[i].FilePath,
                                    AllFiles  = srcFiles[i].AllFiles,
                                    IsSplit   = srcFiles[i].IsSplit,
                                    Length    = srcFiles[i].Length
                                };
                                srcFiles.Add(sf);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new HandledException(ex, "SourceFiles.buildCollection failed on '{0}' listing files from archive", (srcFiles[i]?.FilePath) ?? "");
                }
            }

            int idx = 0;

            foreach (SourceFile f in srcFiles.OrderBy(a => a.Name))
            {
                f.Index      = idx++;
                f.TotalFiles = srcFiles.Count;
                yield return(f);
            }
        }