ImportInternal(string iniFile, bool returnUnmergedFMsList = false, FieldsToImport?fields = null) { string[] lines = await Task.Run(() => File.ReadAllLines(iniFile)); var fms = new List <FanMission>(); ImportError error = await Task.Run(() => { bool archiveDirRead = false; string archiveDir = ""; for (int i = 0; i < lines.Length; i++) { string line = lines[i]; #region Read archive directory if (!archiveDirRead && line == "[Config]") { while (i < lines.Length - 1) { string lc = lines[i + 1]; if (lc.StartsWithFast_NoNullChecks("ArchiveRoot=")) { archiveDir = lc.Substring(12).Trim(); break; } else if (!lc.IsEmpty() && lc[0] == '[' && lc[lc.Length - 1] == ']') { break; } i++; } if (archiveDir.IsEmpty()) { return(ImportError.NoArchiveDirsFound); } i = -1; archiveDirRead = true; continue; } #endregion #region Read FM entries // MUST CHECK archiveDirRead OR IT ADDS EVERY FM TWICE! if (archiveDirRead && line.Length >= 5 && line[0] == '[' && line[1] == 'F' && line[2] == 'M' && line[3] == '=') { // NOTE: There can be a problem like: // installed name is CoolMission[1] // it gets written like [FM=CoolMission[1]] // it gets read and all [ and ] chars are removed // it gets written back out like [FM=CoolMission1] // Rare I guess, so just ignore? string instName = line.Substring(4, line.Length - 5); var fm = new FanMission { InstalledDir = instName }; // Unfortunately NDL doesn't store its archive names, so we have to do a file search // similar to DarkLoader try { // NDL always searches subdirectories as well foreach (string f in Directory.EnumerateFiles(archiveDir, "*", SearchOption.AllDirectories)) { // @DIRSEP: '/' conversion due to string.ContainsI() if (!f.ToForwardSlashes().ContainsI("/.fix/")) { string fn = Path.GetFileNameWithoutExtension(f); if (fn.ToInstDirNameNDL().EqualsI(instName) || fn.EqualsI(instName)) { fm.Archive = Path.GetFileName(f); break; } } } } catch (Exception ex) { Log("Exception in NewDarkLoader archive dir file enumeration", ex); } while (i < lines.Length - 1) { string lineFM = lines[i + 1]; if (lineFM.StartsWithFast_NoNullChecks("NiceName=")) { fm.Title = lineFM.Substring(9); } else if (lineFM.StartsWithFast_NoNullChecks("ReleaseDate=")) { fm.ReleaseDate.UnixDateString = lineFM.Substring(12); } else if (lineFM.StartsWithFast_NoNullChecks("LastCompleted=")) { fm.LastPlayed.UnixDateString = lineFM.Substring(14); } else if (lineFM.StartsWithFast_NoNullChecks("Finished=")) { uint.TryParse(lineFM.Substring(9), out uint result); // result will be 0 on fail, which is the empty value so it's fine fm.FinishedOn = result; } else if (lineFM.StartsWithFast_NoNullChecks("Rating=")) { fm.Rating = int.TryParse(lineFM.Substring(7), out int result) ? result : -1; } else if (lineFM.StartsWithFast_NoNullChecks("Comment=")) { fm.Comment = lineFM.Substring(8); } else if (lineFM.StartsWithFast_NoNullChecks("ModExclude=")) { string val = lineFM.Substring(11); if (val == "*") { fm.DisableAllMods = true; } else { fm.DisabledMods = val; } } else if (lineFM.StartsWithFast_NoNullChecks("Tags=")) { string val = lineFM.Substring(5); if (!val.IsEmpty() && val != "[none]") { fm.TagsString = val; } } else if (lineFM.StartsWithFast_NoNullChecks("InfoFile=")) { fm.SelectedReadme = lineFM.Substring(9); } else if (lineFM.StartsWithFast_NoNullChecks("FMSize=")) { ulong.TryParse(lineFM.Substring(7), out ulong result); fm.SizeBytes = result; } else if (!lineFM.IsEmpty() && lineFM[0] == '[' && lineFM[lineFM.Length - 1] == ']') { break; } i++; } fms.Add(fm); } #endregion } return(ImportError.None); }); if (error != ImportError.None) { return(error, fms); } var importedFMs = returnUnmergedFMsList ? fms : ImportCommon.MergeImportedFMData(ImportType.NewDarkLoader, fms, fields); return(ImportError.None, importedFMs); }
ImportInternal(string iniFile, bool importFMData, bool importSaves, bool returnUnmergedFMsList = false, FieldsToImport?fields = null) { string[] lines = await Task.Run(() => File.ReadAllLines(iniFile)); var fms = new List <FanMission>(); var error = ImportError.None; if (importFMData) { bool missionDirsRead = false; var archiveDirs = new List <string>(); error = await Task.Run(() => { try { for (int i = 0; i < lines.Length; i++) { string line = lines[i]; string lineTS = line.TrimStart(); string lineTB = lineTS.TrimEnd(); #region Read archive directories // We need to know the archive dirs before doing anything, because we may need to recreate // some lossy names (if any bad chars have been removed by DarkLoader). if (!missionDirsRead && lineTB == "[mission directories]") { while (i < lines.Length - 1) { string lt = lines[i + 1].Trim(); if (!lt.IsEmpty() && lt[0] != '[' && lt.EndsWith("=1")) { archiveDirs.Add(lt.Substring(0, lt.Length - 2)); } else if (!lt.IsEmpty() && lt[0] == '[' && lt[lt.Length - 1] == ']') { break; } i++; } if (archiveDirs.Count == 0 || archiveDirs.All(x => x.IsWhiteSpace())) { return(ImportError.NoArchiveDirsFound); } // Restart from the beginning of the file, this time skipping anything that isn't an // FM entry i = -1; missionDirsRead = true; continue; } #endregion #region Read FM entries // MUST CHECK missionDirsRead OR IT ADDS EVERY FM TWICE! if (missionDirsRead && !NonFMHeaders.Contains(lineTB) && lineTB.Length > 0 && lineTB[0] == '[' && lineTB[lineTB.Length - 1] == ']' && lineTB.Contains('.') && DarkLoaderFMRegex.Match(lineTB).Success) { int lastIndexDot = lineTB.LastIndexOf('.'); string archive = lineTB.Substring(1, lastIndexDot - 1); string size = lineTB.Substring(lastIndexDot + 1, lineTB.Length - lastIndexDot - 2); foreach (string dir in archiveDirs) { if (!Directory.Exists(dir)) { continue; } try { // DarkLoader only does zip format foreach (string f in FastIO.GetFilesTopOnly(dir, "*.zip")) { string fn = Path.GetFileNameWithoutExtension(f); if (RemoveDLArchiveBadChars(fn).EqualsI(archive)) { archive = fn; goto breakout; } } } catch (Exception ex) { Log("Exception in DarkLoader archive dir file enumeration", ex); } } breakout: // Add .zip back on; required because everything expects it, and furthermore if there's // a dot anywhere in the name then everything after it will be treated as the extension // and is liable to be lopped off at any time archive += ".zip"; ulong.TryParse(size, out ulong sizeBytes); var fm = new FanMission { Archive = archive, InstalledDir = archive.ToInstDirNameFMSel(), SizeBytes = sizeBytes }; // We don't import game type, because DarkLoader by default gets it wrong for NewDark // FMs (the user could have changed it manually in the ini file, and in fact it's // somewhat likely they would have done so, but still, better to just scan for it // ourselves later) while (i < lines.Length - 1) { string lts = lines[i + 1].TrimStart(); string ltb = lts.TrimEnd(); if (lts.StartsWith("comment=\"")) { string comment = ltb.Substring(9); if (comment.Length >= 2 && comment[comment.Length - 1] == '\"') { comment = comment.Substring(0, comment.Length - 1); fm.Comment = DLUnescapeChars(comment); } } else if (lts.StartsWith("title=\"")) { string title = ltb.Substring(7); if (title.Length >= 2 && title[title.Length - 1] == '\"') { title = title.Substring(0, title.Length - 1); fm.Title = DLUnescapeChars(title); } } else if (lts.StartsWith("misdate=")) { ulong.TryParse(ltb.Substring(8), out ulong result); try { var date = new DateTime(1899, 12, 30).AddDays(result); fm.ReleaseDate.DateTime = date.Year > 1998 ? date : (DateTime?)null; } catch (ArgumentOutOfRangeException) { fm.ReleaseDate.DateTime = null; } } else if (lts.StartsWith("date=")) { ulong.TryParse(ltb.Substring(5), out ulong result); try { var date = new DateTime(1899, 12, 30).AddDays(result); fm.LastPlayed.DateTime = date.Year > 1998 ? date : (DateTime?)null; } catch (ArgumentOutOfRangeException) { fm.LastPlayed.DateTime = null; } } else if (lts.StartsWith("finished=")) { uint.TryParse(ltb.Substring(9), out uint result); // result will be 0 on fail, which is the empty value so it's fine fm.FinishedOn = result; } else if (!ltb.IsEmpty() && ltb[0] == '[' && ltb[ltb.Length - 1] == ']') { break; } i++; } fms.Add(fm); } #endregion } return(ImportError.None); } catch (Exception ex) { Log("Exception in " + nameof(ImportDarkLoader) + "." + nameof(ImportInternal), ex); return(ImportError.Unknown); } finally { Core.View.InvokeSync(new Action(Core.View.HideProgressBox)); } }); } if (error != ImportError.None) { return(error, fms); } if (importSaves) { bool success = await ImportSaves(lines); } var importedFMs = returnUnmergedFMsList ? fms : ImportCommon.MergeImportedFMData(ImportType.DarkLoader, fms, fields); return(ImportError.None, importedFMs); }
ImportInternal(string iniFile, bool returnUnmergedFMsList = false, FieldsToImport?fields = null) { string[] lines = await Task.Run(() => File.ReadAllLines(iniFile)); var fms = new List <FanMission>(); await Task.Run(() => { for (int i = 0; i < lines.Length; i++) { string line = lines[i]; if (line.Length >= 5 && line[0] == '[' && line[1] == 'F' && line[2] == 'M' && line[3] == '=') { string instName = line.Substring(4, line.Length - 5); var fm = new FanMission { InstalledDir = instName }; while (i < lines.Length - 1) { string lineFM = lines[i + 1]; if (lineFM.StartsWithFast_NoNullChecks("NiceName=")) { fm.Title = lineFM.Substring(9); } else if (lineFM.StartsWithFast_NoNullChecks("Archive=")) { fm.Archive = lineFM.Substring(8); } else if (lineFM.StartsWithFast_NoNullChecks("ReleaseDate=")) { fm.ReleaseDate.UnixDateString = lineFM.Substring(12); } else if (lineFM.StartsWithFast_NoNullChecks("LastStarted=")) { fm.LastPlayed.UnixDateString = lineFM.Substring(12); } else if (lineFM.StartsWithFast_NoNullChecks("Completed=")) { int.TryParse(lineFM.Substring(10), out int result); // Unfortunately FMSel doesn't let you choose the difficulty you finished on, so // we have to have this fallback value as a best-effort thing. if (result > 0) { fm.FinishedOnUnknown = true; } } else if (lineFM.StartsWithFast_NoNullChecks("Rating=")) { fm.Rating = int.TryParse(lineFM.Substring(7), out int result) ? result : -1; } else if (lineFM.StartsWithFast_NoNullChecks("Notes=")) { fm.Comment = lineFM.Substring(6).Replace(@"\n", @"\r\n"); } else if (lineFM.StartsWithFast_NoNullChecks("ModExclude=")) { string val = lineFM.Substring(11); if (val == "*") { fm.DisableAllMods = true; } else { fm.DisabledMods = val; } } else if (lineFM.StartsWithFast_NoNullChecks("Tags=")) { fm.TagsString = lineFM.Substring(5); } else if (lineFM.StartsWithFast_NoNullChecks("InfoFile=")) { fm.SelectedReadme = lineFM.Substring(9); } else if (!lineFM.IsEmpty() && lineFM[0] == '[' && lineFM[lineFM.Length - 1] == ']') { break; } i++; } fms.Add(fm); } } }); var importedFMs = returnUnmergedFMsList ? fms : ImportCommon.MergeImportedFMData(ImportType.FMSel, fms, fields); return(ImportError.None, importedFMs); }