GenealogicalDataCommunication
This commit is contained in:
400
Shared/Models/Stateless/Methods/GenealogicalDataCommunication.cs
Normal file
400
Shared/Models/Stateless/Methods/GenealogicalDataCommunication.cs
Normal file
@ -0,0 +1,400 @@
|
||||
namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
internal abstract class GenealogicalDataCommunication
|
||||
{
|
||||
|
||||
// ...
|
||||
|
||||
private static string[] GetHeaderLines(string startsWith, string[] sourceLines)
|
||||
{
|
||||
List<string> results = new();
|
||||
for (int i = 0; i < sourceLines.Length; i++)
|
||||
{
|
||||
if (sourceLines[i].StartsWith(startsWith))
|
||||
break;
|
||||
results.Add(sourceLines[i]);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
internal static (string[] headerLines, Dictionary<string, List<string>> individuals, string[] footerLines) GetIndividuals(string genealogicalDataCommunicationFile, bool requireNickName)
|
||||
{
|
||||
Dictionary<string, List<string>> results = new();
|
||||
string? nick;
|
||||
List<string> lines = new();
|
||||
const string startsWith = "0 @";
|
||||
List<string> footerLines = new();
|
||||
string[] sourceLines = string.IsNullOrEmpty(genealogicalDataCommunicationFile) || !File.Exists(genealogicalDataCommunicationFile) ? Array.Empty<string>() : File.ReadAllLines(genealogicalDataCommunicationFile);
|
||||
string[] headerLines = GetHeaderLines(startsWith, sourceLines);
|
||||
for (int i = headerLines.Length; i < sourceLines.Length; i++)
|
||||
{
|
||||
if (!sourceLines[i].StartsWith(startsWith))
|
||||
continue;
|
||||
nick = null;
|
||||
lines.Add(sourceLines[i]);
|
||||
if (sourceLines[i].EndsWith("@ SOUR") || sourceLines[i].EndsWith("@ SUBM") || sourceLines[i].EndsWith("@ OBJE") || sourceLines[i].EndsWith("@ REPO"))
|
||||
continue;
|
||||
else if (sourceLines[i].EndsWith("@ FAM"))
|
||||
{
|
||||
for (int j = i + 1; j < sourceLines.Length; j++)
|
||||
lines.Add(sourceLines[j]);
|
||||
footerLines.AddRange(lines);
|
||||
break;
|
||||
}
|
||||
else if (sourceLines[i].EndsWith("@ INDI"))
|
||||
{
|
||||
for (int j = i + 1; j < sourceLines.Length; j++)
|
||||
{
|
||||
if (sourceLines[j].StartsWith(startsWith))
|
||||
break;
|
||||
lines.Add(sourceLines[j]);
|
||||
if (!sourceLines[j].StartsWith("2 NICK "))
|
||||
continue;
|
||||
nick = sourceLines[j][7..];
|
||||
}
|
||||
if (requireNickName && string.IsNullOrEmpty(nick))
|
||||
throw new Exception(string.Join(Environment.NewLine, lines));
|
||||
nick ??= Guid.NewGuid().ToString();
|
||||
results.Add(nick, new());
|
||||
if (!lines.Any())
|
||||
continue;
|
||||
results[nick].AddRange(lines);
|
||||
lines.Clear();
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
return (headerLines, results, footerLines.ToArray());
|
||||
}
|
||||
|
||||
private static string[] GetFilteredOutMapped(List<string> individualsLines)
|
||||
{
|
||||
List<string> results = new();
|
||||
for (int i = 0; i < individualsLines.Count; i++)
|
||||
{
|
||||
if (individualsLines[i].StartsWith("0 @I"))
|
||||
continue;
|
||||
else if (individualsLines[i].StartsWith("1 _UID"))
|
||||
continue;
|
||||
else if (individualsLines[i].StartsWith("1 NAME"))
|
||||
continue;
|
||||
else if (individualsLines[i].StartsWith("2 GIVN"))
|
||||
continue;
|
||||
else if (individualsLines[i].StartsWith("2 SURN"))
|
||||
continue;
|
||||
else if (individualsLines[i].StartsWith("2 NSFX"))
|
||||
continue;
|
||||
else if (individualsLines[i].StartsWith("2 NICK"))
|
||||
continue;
|
||||
else if (individualsLines[i].StartsWith("1 SEX"))
|
||||
continue;
|
||||
else if (individualsLines[i] == "1 BIRT")
|
||||
{
|
||||
if (individualsLines.Count > i + 1 && individualsLines[i + 1].StartsWith("2 DATE"))
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
else if (individualsLines[i].StartsWith("1 DEAT"))
|
||||
{
|
||||
if (individualsLines.Count > i + 1 && individualsLines[i + 1].StartsWith("2 DATE"))
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
else if (individualsLines[i] == "1 CHAN")
|
||||
{
|
||||
if (individualsLines.Count > i + 1 && individualsLines[i + 1].StartsWith("2 DATE"))
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
results.Add(individualsLines[i]);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
internal static List<string> GetMappedLines(string genealogicalDataCommunicationFile, bool requireNickName)
|
||||
{
|
||||
List<string> results = new();
|
||||
GenealogicalDataCommunicationLines genealogicalDataCommunicationLines;
|
||||
(string[] headerLines, Dictionary<string, List<string>> individuals, string[] footerLines) = GetIndividuals(genealogicalDataCommunicationFile, requireNickName);
|
||||
results.AddRange(headerLines);
|
||||
foreach (KeyValuePair<string, List<string>> keyValuePair in individuals)
|
||||
{
|
||||
genealogicalDataCommunicationLines = GetGenealogicalDataCommunicationLines(keyValuePair.Value);
|
||||
if (!string.IsNullOrEmpty(genealogicalDataCommunicationLines.Id))
|
||||
results.Add(genealogicalDataCommunicationLines.Id);
|
||||
if (!string.IsNullOrEmpty(genealogicalDataCommunicationLines.UId))
|
||||
results.Add(genealogicalDataCommunicationLines.UId);
|
||||
if (!string.IsNullOrEmpty(genealogicalDataCommunicationLines.Name))
|
||||
results.Add(genealogicalDataCommunicationLines.Name);
|
||||
if (!string.IsNullOrEmpty(genealogicalDataCommunicationLines.GivenName))
|
||||
results.Add(genealogicalDataCommunicationLines.GivenName);
|
||||
if (!string.IsNullOrEmpty(genealogicalDataCommunicationLines.SurName))
|
||||
results.Add(genealogicalDataCommunicationLines.SurName);
|
||||
if (!string.IsNullOrEmpty(genealogicalDataCommunicationLines.NickName))
|
||||
results.Add(genealogicalDataCommunicationLines.NickName);
|
||||
if (!string.IsNullOrEmpty(genealogicalDataCommunicationLines.Sex))
|
||||
results.Add(genealogicalDataCommunicationLines.Sex);
|
||||
results.AddRange(genealogicalDataCommunicationLines.Birth);
|
||||
results.AddRange(genealogicalDataCommunicationLines.Death);
|
||||
results.AddRange(genealogicalDataCommunicationLines.Changed);
|
||||
}
|
||||
results.AddRange(footerLines);
|
||||
return results;
|
||||
}
|
||||
|
||||
internal static GenealogicalDataCommunicationLines GetGenealogicalDataCommunicationLines(List<string> individualsLines)
|
||||
{
|
||||
GenealogicalDataCommunicationLines result;
|
||||
string? idLine = null;
|
||||
string? sexLine = null;
|
||||
string? uIdLine = null;
|
||||
string? nameLine = null;
|
||||
string? suffixLine = null;
|
||||
string? surNameLine = null;
|
||||
string? nickNameLine = null;
|
||||
string? givenNameLine = null;
|
||||
List<string> birthLines = new();
|
||||
List<string> deathLines = new();
|
||||
List<string> changedLines = new();
|
||||
for (int i = 0; i < individualsLines.Count; i++)
|
||||
{
|
||||
if (idLine is null && individualsLines[i].EndsWith("@ INDI"))
|
||||
idLine = individualsLines[i];
|
||||
else if (uIdLine is null && individualsLines[i].StartsWith("1 _UID"))
|
||||
uIdLine = individualsLines[i];
|
||||
else if (nameLine is null && individualsLines[i].StartsWith("1 NAME"))
|
||||
nameLine = individualsLines[i];
|
||||
else if (givenNameLine is null && individualsLines[i].StartsWith("2 GIVN"))
|
||||
givenNameLine = individualsLines[i];
|
||||
else if (surNameLine is null && individualsLines[i].StartsWith("2 SURN"))
|
||||
surNameLine = individualsLines[i];
|
||||
else if (suffixLine is null && individualsLines[i].StartsWith("2 NSFX"))
|
||||
suffixLine = individualsLines[i];
|
||||
else if (nickNameLine is null && individualsLines[i].StartsWith("2 NICK"))
|
||||
nickNameLine = individualsLines[i];
|
||||
else if (sexLine is null && individualsLines[i].StartsWith("1 SEX"))
|
||||
sexLine = individualsLines[i];
|
||||
else if (!birthLines.Any() && individualsLines[i] == "1 BIRT")
|
||||
{
|
||||
birthLines.Add(individualsLines[i]);
|
||||
if (individualsLines.Count > i + 1 && individualsLines[i + 1].StartsWith("2 DATE"))
|
||||
{
|
||||
i += 1;
|
||||
birthLines.Add(individualsLines[i]);
|
||||
}
|
||||
}
|
||||
else if (!deathLines.Any() && individualsLines[i].StartsWith("1 DEAT"))
|
||||
{
|
||||
deathLines.Add(individualsLines[i]);
|
||||
if (individualsLines.Count > i + 1 && individualsLines[i + 1].StartsWith("2 DATE"))
|
||||
{
|
||||
i += 1;
|
||||
deathLines.Add(individualsLines[i]);
|
||||
}
|
||||
}
|
||||
else if (!changedLines.Any() && individualsLines[i] == "1 CHAN")
|
||||
{
|
||||
changedLines.Add(individualsLines[i]);
|
||||
if (individualsLines.Count > i + 1 && individualsLines[i + 1].StartsWith("2 DATE"))
|
||||
{
|
||||
i += 1;
|
||||
changedLines.Add(individualsLines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
result = new(idLine, uIdLine, nameLine, givenNameLine, surNameLine, suffixLine, nickNameLine, sexLine, birthLines, deathLines, changedLines);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static Models.GenealogicalDataCommunication GetGenealogicalDataCommunication(Models.PersonBirthday personBirthday, GenealogicalDataCommunicationLines genealogicalDataCommunicationLines)
|
||||
{
|
||||
Models.GenealogicalDataCommunication result;
|
||||
DateTime? birth;
|
||||
DateTime? death;
|
||||
DateTime? changed;
|
||||
char sex = string.IsNullOrEmpty(genealogicalDataCommunicationLines.Sex) || !genealogicalDataCommunicationLines.Sex.Contains("1 SEX ") ? 'U' : genealogicalDataCommunicationLines.Sex.Split("1 SEX ")[1][0];
|
||||
string? uId = string.IsNullOrEmpty(genealogicalDataCommunicationLines.UId) || !genealogicalDataCommunicationLines.UId.Contains("1 _UID ") ? null : genealogicalDataCommunicationLines.UId.Split("1 _UID ")[1];
|
||||
string? name = string.IsNullOrEmpty(genealogicalDataCommunicationLines.Name) || !genealogicalDataCommunicationLines.Name.Contains("1 NAME ") ? null : genealogicalDataCommunicationLines.Name.Split("1 NAME ")[1];
|
||||
string? suffix = string.IsNullOrEmpty(genealogicalDataCommunicationLines.Suffix) || !genealogicalDataCommunicationLines.Suffix.Contains("2 NSFX ") ? null : genealogicalDataCommunicationLines.Suffix.Split("2 NSFX ")[1];
|
||||
string? surName = string.IsNullOrEmpty(genealogicalDataCommunicationLines.SurName) || !genealogicalDataCommunicationLines.SurName.Contains("2 SURN ") ? null : genealogicalDataCommunicationLines.SurName.Split("2 SURN ")[1];
|
||||
string? nickName = string.IsNullOrEmpty(genealogicalDataCommunicationLines.NickName) || !genealogicalDataCommunicationLines.NickName.Contains("2 NICK ") ? null : genealogicalDataCommunicationLines.NickName.Split("2 NICK ")[1];
|
||||
string? givenName = string.IsNullOrEmpty(genealogicalDataCommunicationLines.GivenName) || !genealogicalDataCommunicationLines.GivenName.Contains("2 GIVN ") ? null : genealogicalDataCommunicationLines.GivenName.Split("2 GIVN ")[1];
|
||||
string? id = string.IsNullOrEmpty(genealogicalDataCommunicationLines.Id) || !genealogicalDataCommunicationLines.Id.Contains("0 @I") || !genealogicalDataCommunicationLines.Id.Contains("@ INDI") ? null : genealogicalDataCommunicationLines.Id.Split('@')[1][1..];
|
||||
string[] birthLines = (from l in genealogicalDataCommunicationLines.Birth where l.Contains("2 DATE ") select l.Split("2 DATE ")[1]).ToArray();
|
||||
string[] deathLines = (from l in genealogicalDataCommunicationLines.Death where l.Contains("2 DATE ") select l.Split("2 DATE ")[1]).ToArray();
|
||||
string[] changedLines = (from l in genealogicalDataCommunicationLines.Changed where l.Contains("2 DATE ") select l.Split("2 DATE ")[1]).ToArray();
|
||||
if (!birthLines.Any() || !DateTime.TryParse(birthLines[0], out DateTime parseBirth))
|
||||
birth = null;
|
||||
else
|
||||
birth = parseBirth;
|
||||
if (!deathLines.Any() || !DateTime.TryParse(deathLines[0], out DateTime parseDeath))
|
||||
death = null;
|
||||
else
|
||||
death = parseDeath;
|
||||
if (!changedLines.Any() || !DateTime.TryParse(changedLines[0], out DateTime parseChanged))
|
||||
changed = null;
|
||||
else
|
||||
changed = parseChanged;
|
||||
if (birth is not null)
|
||||
{
|
||||
bool alive = death is null && !genealogicalDataCommunicationLines.Death.Any(l => l == "1 DEAT Y");
|
||||
(int age, _) = IAge.GetAge(DateTime.Now.Ticks, birth.Value.Ticks);
|
||||
int hours = IPersonBirthday.GetHour(alive, sex);
|
||||
if (death == birth)
|
||||
death = death.Value.AddHours(hours);
|
||||
birth = birth.Value.AddHours(hours);
|
||||
if (age < 1)
|
||||
birth = null;
|
||||
if (death is null && (!alive || age > 110))
|
||||
death = birth;
|
||||
}
|
||||
death ??= !genealogicalDataCommunicationLines.Death.Any(l => l == "1 DEAT Y") ? null : birth is not null ? birth : personBirthday.Value;
|
||||
result = new(id, uId, name, givenName, surName, suffix, nickName, sex, birth, death, changed);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static void WriteFile(string personKeyFormatted, Models.PersonBirthday personBirthday, Models.PersonName personName, List<string>? individualsLines, bool isDefaultName, string directory, Models.GenealogicalDataCommunication? genealogicalDataCommunication, bool verify)
|
||||
{
|
||||
char sex;
|
||||
bool alive;
|
||||
bool first;
|
||||
if (personKeyFormatted[^2..] is "15")
|
||||
(sex, alive, first) = ('M', false, true);
|
||||
else if (personKeyFormatted[^2..] is "14")
|
||||
(sex, alive, first) = ('F', false, true);
|
||||
else if (personKeyFormatted[^2..] is "05")
|
||||
(sex, alive, first) = ('M', true, true);
|
||||
else if (personKeyFormatted[^2..] is "04")
|
||||
(sex, alive, first) = ('F', true, true);
|
||||
else if (personKeyFormatted[^2..] is "23" or "21" or "19" or "17")
|
||||
(sex, alive, first) = ('M', false, false);
|
||||
else if (personKeyFormatted[^2..] is "22" or "20" or "18" or "16")
|
||||
(sex, alive, first) = ('F', false, false);
|
||||
else if (personKeyFormatted[^2..] is "13" or "11" or "09" or "07")
|
||||
(sex, alive, first) = ('M', true, false);
|
||||
else if (personKeyFormatted[^2..] is "12" or "10" or "08" or "06")
|
||||
(sex, alive, first) = ('F', true, false);
|
||||
else
|
||||
(sex, alive, first) = ('U', true, false);
|
||||
if (verify && genealogicalDataCommunication is not null)
|
||||
{
|
||||
if (genealogicalDataCommunication.SurName != personName.Last.Value)
|
||||
throw new Exception($"{genealogicalDataCommunication.Name} - {personKeyFormatted}");
|
||||
if (genealogicalDataCommunication.Sex != sex)
|
||||
throw new Exception($"{genealogicalDataCommunication.Name} - {personKeyFormatted}");
|
||||
if (genealogicalDataCommunication.Death is not null && alive || genealogicalDataCommunication.Death is null && !alive)
|
||||
throw new Exception($"{genealogicalDataCommunication.Name} - {personKeyFormatted}");
|
||||
if (genealogicalDataCommunication.Birth is not null && genealogicalDataCommunication.Birth.Value.ToString("yyyy-MM-dd") != personBirthday.Value.ToString("yyyy-MM-dd"))
|
||||
throw new Exception($"{genealogicalDataCommunication.Name} - {personKeyFormatted}");
|
||||
}
|
||||
string jrOrSr;
|
||||
if (string.IsNullOrEmpty(personName.Alias.Value))
|
||||
jrOrSr = string.Empty;
|
||||
else
|
||||
{
|
||||
if (personName.Alias.Value.Contains(" Jr"))
|
||||
jrOrSr = " Jr";
|
||||
else if (personName.Alias.Value.Contains(" Sr"))
|
||||
jrOrSr = " Sr";
|
||||
else
|
||||
jrOrSr = string.Empty;
|
||||
}
|
||||
string code = IPersonBirthday.GetHour(alive, sex).ToString("00");
|
||||
if (directory.EndsWith("00"))
|
||||
directory = string.Concat(directory[..^2], code);
|
||||
else if (directory.EndsWith("01"))
|
||||
directory = string.Concat(directory[..^2], code);
|
||||
if (!Directory.Exists(directory))
|
||||
_ = Directory.CreateDirectory(directory);
|
||||
if (first && !personKeyFormatted.EndsWith(code))
|
||||
personKeyFormatted = $"{personKeyFormatted[..^2]}{code}";
|
||||
List<string> pGedLines = new();
|
||||
if (individualsLines is null || !individualsLines.Any())
|
||||
pGedLines.Add($"0 @I{personKeyFormatted}@ INDI");
|
||||
else
|
||||
{
|
||||
if (!individualsLines[0].StartsWith("0 @I"))
|
||||
throw new NotSupportedException();
|
||||
pGedLines.Add(individualsLines[0]);
|
||||
}
|
||||
pGedLines.Add($"1 NAME {personName.First.Value} /{personName.Last.Value}/{jrOrSr}");
|
||||
if (!string.IsNullOrEmpty(personName.First.Value))
|
||||
pGedLines.Add($"2 GIVN {personName.First.Value}");
|
||||
if (!string.IsNullOrEmpty(personName.Last.Value))
|
||||
pGedLines.Add($"2 SURN {personName.Last.Value}");
|
||||
if (!string.IsNullOrEmpty(jrOrSr))
|
||||
pGedLines.Add($"2 NSFX {jrOrSr.Trim()}");
|
||||
pGedLines.Add($"2 NICK {personKeyFormatted}");
|
||||
pGedLines.Add($"1 SEX {sex}");
|
||||
if (!IPersonBirthday.IsCounterPersonBirthday(personBirthday))
|
||||
{
|
||||
pGedLines.Add("1 BIRT");
|
||||
pGedLines.Add($"2 DATE {personBirthday.Value:dd MMM yyyy}");
|
||||
}
|
||||
if (!alive)
|
||||
{
|
||||
if (genealogicalDataCommunication?.Death is null || genealogicalDataCommunication.Death == genealogicalDataCommunication.Birth || IPersonBirthday.IsCounterPersonBirthday(new(genealogicalDataCommunication.Death.Value)))
|
||||
pGedLines.Add("1 DEAT Y");
|
||||
else
|
||||
{
|
||||
pGedLines.Add("1 DEAT");
|
||||
pGedLines.Add($"2 DATE {genealogicalDataCommunication.Death.Value:dd MMM yyyy}");
|
||||
}
|
||||
}
|
||||
if (isDefaultName)
|
||||
pGedLines.Add("9 NOTE");
|
||||
if (individualsLines is not null)
|
||||
pGedLines.AddRange(GetFilteredOutMapped(individualsLines));
|
||||
string text = string.Join(Environment.NewLine, pGedLines);
|
||||
_ = IPath.WriteAllText(Path.Combine(directory, $"{personKeyFormatted}.pged"), text, updateDateWhenMatches: false, compareBeforeWrite: true);
|
||||
}
|
||||
|
||||
internal static void CreateTree(string mappingDefaultName, string personBirthdayFormat, string resultAllInOne, Models.PersonContainer[] personContainers, string[] genealogicalDataCommunicationHeaderLines, string[] genealogicalDataCommunicationFooterLines, long ticks, string a2PeopleContentDirectory, Dictionary<long, List<int>> personKeyToIds)
|
||||
{
|
||||
string by;
|
||||
string[] matches;
|
||||
const int zero = 0;
|
||||
string[] pGedFiles;
|
||||
string[] pGedLines;
|
||||
string personKeyFormatted;
|
||||
List<string> lines = new();
|
||||
List<long> distinct = new();
|
||||
lines.AddRange(genealogicalDataCommunicationHeaderLines);
|
||||
Models.PersonBirthday personBirthday;
|
||||
string rootDirectory = Path.Combine(a2PeopleContentDirectory, $"{ticks}-Tree");
|
||||
foreach (Models.PersonContainer personContainer in personContainers)
|
||||
{
|
||||
if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Person is null || !personContainer.Birthdays.Any())
|
||||
continue;
|
||||
if (IPerson.IsDefaultName(mappingDefaultName, personContainer.DisplayDirectoryName) || IPerson.IsDefaultName(mappingDefaultName, personContainer.Person))
|
||||
continue;
|
||||
if (distinct.Contains(personContainer.Key.Value))
|
||||
continue;
|
||||
distinct.Add(personContainer.Key.Value);
|
||||
personBirthday = personContainer.Birthdays[zero];
|
||||
personKeyFormatted = IPersonBirthday.GetFormatted(personBirthdayFormat, personBirthday);
|
||||
by = IPersonBirthday.IsCounterPersonBirthday(personBirthday) ? resultAllInOne : "People";
|
||||
matches = (from l in personContainer.DisplayDirectoryAllFiles where !string.IsNullOrEmpty(personKeyFormatted) && l.Contains(personKeyFormatted) select l).ToArray();
|
||||
if (!matches.Any())
|
||||
continue;
|
||||
pGedFiles = (from l in matches where l.EndsWith(".pged") select l).ToArray();
|
||||
if (!pGedFiles.Any())
|
||||
continue;
|
||||
pGedLines = File.ReadAllLines(pGedFiles[0]);
|
||||
lines.AddRange(pGedLines);
|
||||
if (!personKeyToIds.ContainsKey(personContainer.Key.Value))
|
||||
lines.Add("1 NOTE");
|
||||
// segments = personContainer.DisplayDirectoryName.Split(_Configuration.PersonCharacters.ToArray());
|
||||
// if (segments.Length < 2)
|
||||
// directory = Path.Combine(rootDirectory, $"000 {personKeyFormatted} {personContainer.DisplayDirectoryName}");
|
||||
// else
|
||||
// directory = Path.Combine(rootDirectory, $"{segments[1].PadLeft(3, '0')} {personKeyFormatted} {personContainer.DisplayDirectoryName}");
|
||||
// if (Directory.Exists(directory))
|
||||
// continue;
|
||||
// _ = Directory.CreateDirectory(directory);
|
||||
}
|
||||
lines.AddRange(genealogicalDataCommunicationFooterLines);
|
||||
File.WriteAllLines(Path.Combine(a2PeopleContentDirectory, $"{ticks}.ged"), lines);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user