UpdateMods,RenameMods,CleanLocales
This commit is contained in:
parent
49e7e21626
commit
c449c5cb4d
@ -16,6 +16,6 @@
|
||||
<ProjectReference Include="..\..\DTLib\DTLib\DTLib.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<PackageReference Include="DTLib" Version="1.2.0" />
|
||||
<PackageReference Include="DTLib" Version="1.2.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -7,14 +7,13 @@ using DTLib.Ben.Demystifier;
|
||||
namespace ParadoxModMerger;
|
||||
|
||||
|
||||
public record struct ConflictingModFile(string FilePath, string[] Mods);
|
||||
|
||||
public enum DiffState
|
||||
{
|
||||
Added, Equal, Removed, Changed
|
||||
}
|
||||
|
||||
public record struct DiffPart<T>(T Value, DiffState State);
|
||||
public record struct ConflictingModFile(string FilePath, string[] Mods);
|
||||
|
||||
static class Diff
|
||||
{
|
||||
@ -23,7 +22,7 @@ static class Diff
|
||||
|
||||
public static void DiffCommandHandler(string connected_pathes)
|
||||
{
|
||||
IOPath[] moddirs = Program.SplitStringToPaths(connected_pathes);
|
||||
IOPath[] moddirs = Program.SplitArgToPaths(connected_pathes, false);
|
||||
var conflicts = FindModConflicts(moddirs);
|
||||
LogModConflicts(conflicts);
|
||||
}
|
||||
@ -148,14 +147,14 @@ static class Diff
|
||||
case ConsoleKey.LeftArrow:
|
||||
{
|
||||
ColoredConsole.Write("w", "enter left mod number: ");
|
||||
string answ = Console.ReadLine();
|
||||
string answ = Console.ReadLine()!;
|
||||
selected_mod0_i = answ.ToInt();
|
||||
break;
|
||||
}
|
||||
case ConsoleKey.RightArrow:
|
||||
{
|
||||
ColoredConsole.Write("w", "enter right mod number: ");
|
||||
string answ = Console.ReadLine();
|
||||
string answ = Console.ReadLine()!;
|
||||
selected_mod1_i = answ.ToInt();
|
||||
break;
|
||||
}
|
||||
@ -194,7 +193,7 @@ static class Diff
|
||||
|
||||
public static IEnumerable<DiffPart<T>> DiffCollections<T>(ICollection<T> col0, ICollection<T> col1)
|
||||
{
|
||||
foreach (T el in col0)
|
||||
foreach (var el in col0)
|
||||
yield return new DiffPart<T>(el, col1.Contains(el) ? DiffState.Equal : DiffState.Removed);
|
||||
foreach (var el in col1)
|
||||
if (!col0.Contains(el))
|
||||
|
||||
@ -23,4 +23,52 @@ static class Localisation
|
||||
else Log("y", $"file {rusFileName} already exists");
|
||||
}
|
||||
}
|
||||
|
||||
// deletes all localisations except l_russian and l_english
|
||||
public static void Clean(IOPath _loc_dir)
|
||||
{
|
||||
Log("g", $"deleted {RemoveUnneededDirs(_loc_dir)} dirs");
|
||||
Log("g", $"deleted {RemoveUnneededFiles(_loc_dir)} files");
|
||||
|
||||
|
||||
int RemoveUnneededDirs(IOPath loc_dir)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var subdir in Directory.GetDirectories(loc_dir))
|
||||
{
|
||||
string dir_basename = subdir.LastName().Str;
|
||||
if (dir_basename == "russian" || dir_basename == "english")
|
||||
continue;
|
||||
|
||||
if (dir_basename == "replace")
|
||||
{
|
||||
RemoveUnneededDirs(subdir);
|
||||
RemoveUnneededFiles(subdir);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dir_basename.Contains("rus"))
|
||||
Log("y", $"unexpected dir: {subdir}");
|
||||
Directory.Delete(subdir);
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int RemoveUnneededFiles(IOPath loc_dir)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var file in Directory.GetFiles(loc_dir))
|
||||
{
|
||||
if(file.EndsWith("l_russian") || file.EndsWith("l_enghish"))
|
||||
continue;
|
||||
if (!file.Contains("_l_") || !file.EndsWith(".yml"))
|
||||
Log("y",$"unexpected file: {file}");
|
||||
File.Delete(file);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,18 +56,22 @@ static class Merge
|
||||
File.AppendAllText(out_modlist_file, $"{moddir.LastName()}\n");
|
||||
}
|
||||
|
||||
public static void ConsoleAskYN(string question, Action yes, Action no)
|
||||
public static void ConsoleAskYN(string question, Action? yes, Action? no)
|
||||
{
|
||||
Log("y", question + " [y/n]");
|
||||
string answ = ColoredConsole.Read("w");
|
||||
if (answ == "y") yes();
|
||||
else no();
|
||||
string answ = ColoredConsole.Read("w").ToLower();
|
||||
if (answ == "y") yes?.Invoke();
|
||||
else no?.Invoke();
|
||||
}
|
||||
|
||||
static void HandleConflicts(IOPath[] moddirs)
|
||||
{
|
||||
var conflicts = Diff.FindModConflicts(moddirs);
|
||||
if (conflicts.Count <= 0) return;
|
||||
conflicts = conflicts.Where(cfl =>
|
||||
!cfl.FilePath.EndsWith("descriptor.mod") &&
|
||||
!cfl.FilePath.EndsWith(modlist_filename)).ToList();
|
||||
if (conflicts.Count < 1)
|
||||
return;
|
||||
|
||||
Diff.LogModConflicts(conflicts);
|
||||
ConsoleAskYN("continue merge?",
|
||||
@ -80,4 +84,117 @@ static class Merge
|
||||
() => {});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static void UpdateMods(IOPath updated_mods_dir, IOPath[] outdated_dirs, IOPath backup_dir)
|
||||
{
|
||||
var src_dir_mods = Directory.GetDirectories(updated_mods_dir).ToList();
|
||||
List<IOPath> not_found_mods = new List<IOPath>();
|
||||
List<IOPath> changed_mods = new List<IOPath>();
|
||||
List<IOPath> unchanged_mods = new List<IOPath>();
|
||||
|
||||
foreach (IOPath outdated_mods_dir in outdated_dirs)
|
||||
foreach (var mod in Directory.GetDirectories(outdated_mods_dir))
|
||||
{
|
||||
Log("b", "updating mod ", "c", mod.LastName().Str);
|
||||
var mod_updated = mod.ReplaceBase(outdated_mods_dir, updated_mods_dir);
|
||||
int updated_index = src_dir_mods.IndexOf(mod_updated); // IOPath comparison doesnt work?
|
||||
|
||||
if (updated_index == -1)
|
||||
{
|
||||
Log("m", $"mod {mod.LastName()} not found in {updated_mods_dir}");
|
||||
not_found_mods.Add(mod);
|
||||
continue;
|
||||
}
|
||||
|
||||
var diff = Diff.DiffDirs(mod, mod_updated)
|
||||
.Where(d => d.State != DiffState.Equal)
|
||||
.ToList();
|
||||
if (!diff.Any())
|
||||
{
|
||||
Log("gray","unchanged");
|
||||
unchanged_mods.Add(mod);
|
||||
continue;
|
||||
}
|
||||
|
||||
Log("file difference:");
|
||||
foreach (var fileD in diff)
|
||||
{
|
||||
string color = fileD.State switch
|
||||
{
|
||||
DiffState.Added => "g",
|
||||
DiffState.Changed => "y",
|
||||
DiffState.Removed => "m",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
Log(color, fileD.Value.Str);
|
||||
}
|
||||
|
||||
ConsoleAskYN("replace mod with its updated version?",
|
||||
() =>
|
||||
{
|
||||
var mod_backup = Path.Concat(backup_dir, mod.LastName());
|
||||
Log($"moving {mod} to {backup_dir}");
|
||||
Directory.Move(mod, mod_backup, false);
|
||||
Log($"copying {mod_updated} to {outdated_mods_dir}");
|
||||
Directory.Copy(mod_updated, mod, false);
|
||||
Log("g", $"mod {mod.LastName()} updated");
|
||||
changed_mods.Add(mod);
|
||||
},
|
||||
()=>unchanged_mods.Add(mod));
|
||||
}
|
||||
|
||||
List<IOPath> added_mods = new List<IOPath>(src_dir_mods.Count - changed_mods.Count);
|
||||
var found_mods = Enumerable
|
||||
.Concat(changed_mods, unchanged_mods)
|
||||
.Select(m=>Path.Concat(updated_mods_dir, m.LastName()))
|
||||
.ToList();
|
||||
foreach (var modD in Diff.DiffCollections(found_mods, src_dir_mods))
|
||||
{
|
||||
if (modD.State == DiffState.Added)
|
||||
added_mods.Add(modD.Value);
|
||||
}
|
||||
|
||||
Log("b", "mod update summary:");
|
||||
if (added_mods.Count > 0)
|
||||
Log("w", $"added {added_mods.Count}:\n",
|
||||
"g", added_mods.MergeToString('\n'));
|
||||
if (changed_mods.Count > 0)
|
||||
Log("w", $"changed {changed_mods.Count}:\n",
|
||||
"y", changed_mods.MergeToString('\n'));
|
||||
if (not_found_mods.Count > 0)
|
||||
Log("w", $"not found {not_found_mods.Count}:\n",
|
||||
"m", not_found_mods.MergeToString('\n'));
|
||||
if (unchanged_mods.Count>0)
|
||||
Log("w", $"unchanged {unchanged_mods.Count}");
|
||||
}
|
||||
|
||||
public static void RenameModsCommandHandler(string dir_with_mods, string rename_pairs_str)
|
||||
{
|
||||
string[] split = Program.SplitArgToStrings(rename_pairs_str, false);
|
||||
int rename_pairs_length = split.Length / 2;
|
||||
if (split.Length % 2 != 0)
|
||||
throw new Exception($"rename_pairs length is not even ({rename_pairs_length})");
|
||||
var rename_pairs = new (string old_name, string new_name)[rename_pairs_length];
|
||||
for (int i = 0; i < rename_pairs_length; i++)
|
||||
{
|
||||
rename_pairs[i] = (split[i * 2], split[i * 2 + 1]);
|
||||
}
|
||||
|
||||
RenameMods(dir_with_mods, rename_pairs);
|
||||
}
|
||||
|
||||
public static void RenameMods(IOPath dir_with_mods, (string old_name,string new_name)[] rename_pairs)
|
||||
{
|
||||
foreach (var re in rename_pairs)
|
||||
{
|
||||
var old_mod_path = Path.Concat(dir_with_mods, re.old_name);
|
||||
var new_mod_path = Path.Concat(dir_with_mods, re.new_name);
|
||||
if (Directory.Exists(old_mod_path))
|
||||
{
|
||||
Log("b","renaming ", "c", re.old_name, "b", " to ", "c", re.new_name);
|
||||
Directory.Move(old_mod_path, new_mod_path, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,8 @@ global using DTLib;
|
||||
global using DTLib.Extensions;
|
||||
global using DTLib.Filesystem;
|
||||
global using DTLib.Logging;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using DTLib.Console;
|
||||
|
||||
namespace ParadoxModMerger;
|
||||
@ -15,7 +17,7 @@ public static class Program
|
||||
static ConsoleLogger logger = new($"logs", "main");
|
||||
static void Log(params string[] msg) => logger.Log(msg);
|
||||
|
||||
static void Main(string[] args)
|
||||
static int Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -25,47 +27,62 @@ public static class Program
|
||||
string outPath = "" ;
|
||||
|
||||
new LaunchArgumentParser(
|
||||
new LaunchArgument(new []{"o", "out"},
|
||||
"sets output path",
|
||||
p => outPath=p,
|
||||
"out_path",
|
||||
0),
|
||||
new LaunchArgument(new []{"clear"},
|
||||
"Clear mod files and put them into separate dirs in output dir. Requires -o",
|
||||
wdir=>Workshop.ClearWorkshop(wdir, outPath),
|
||||
"workshop_dir",
|
||||
1),
|
||||
new LaunchArgument(new []{"diff"},
|
||||
"Compares mod files by hash",
|
||||
p=>Diff.DiffCommandHandler(p),
|
||||
"first_mod_directory:second_mod_directory:...",
|
||||
1),
|
||||
new LaunchArgument(new []{"diff-detailed"},
|
||||
"reads conflicts_XXX.dtsod file and shows text diff for each file",
|
||||
p=>Diff.DiffDetailedCommandHandler(p),
|
||||
"conflicts_dtsod_path",
|
||||
1
|
||||
),
|
||||
new LaunchArgument(new []{"merge-subdirs"},
|
||||
"Merges mods and shows conflicts. Requires -o",
|
||||
d => Merge.MergeAll(Directory.GetDirectories(d), outPath),
|
||||
"dir_with_mods",
|
||||
1),
|
||||
new LaunchArgument(new []{"merge-into", "merge-single"},
|
||||
"Merges one mod into output dir and shows conflicts. Requires -o",
|
||||
mod=>Merge.MergeInto(mod, outPath),
|
||||
"mod_dir",
|
||||
1),
|
||||
new LaunchArgument(new []{"gen-rus-locale"},
|
||||
"Creates l_russian copy of english locale in output directory. Requires -o",
|
||||
eng=>Localisation.GenerateRussian(eng, outPath),
|
||||
"english_locale_path",
|
||||
1),
|
||||
new LaunchArgument(new []{"desc"},
|
||||
"Downloads mod description from steam to new file in outDir. Requires -o",
|
||||
id=>Workshop.CreateDescFile(id, outPath).GetAwaiter().GetResult(),
|
||||
"mod_id",
|
||||
1)
|
||||
new LaunchArgument(new []{"o", "out"},
|
||||
"sets output path",
|
||||
p => outPath=p,
|
||||
"out_path",
|
||||
0),
|
||||
new LaunchArgument(new []{"clear"},
|
||||
"Clear mod files and put them into separate dirs in output dir. Requires -o",
|
||||
wdir=>Workshop.ClearWorkshop(wdir, outPath),
|
||||
"workshop_dir",
|
||||
1),
|
||||
new LaunchArgument(new []{"diff"},
|
||||
"Compares mod files by hash",
|
||||
p=>Diff.DiffCommandHandler(p),
|
||||
"first_mod_directory:second_mod_directory:...",
|
||||
1),
|
||||
new LaunchArgument(new []{"diff-detailed"},
|
||||
"reads conflicts_XXX.dtsod file and shows text diff for each file",
|
||||
p=>Diff.DiffDetailedCommandHandler(p),
|
||||
"conflicts_dtsod_path",
|
||||
1),
|
||||
new LaunchArgument(new []{"merge-subdirs"},
|
||||
"Merges mods and shows conflicts. Requires -o",
|
||||
d => Merge.MergeAll(Directory.GetDirectories(d), outPath),
|
||||
"dir_with_mods",
|
||||
1),
|
||||
new LaunchArgument(new []{"merge-into", "merge-single"},
|
||||
"Merges one mod into output dir and shows conflicts. Requires -o",
|
||||
mod=>Merge.MergeInto(mod, outPath),
|
||||
"mod_dir",
|
||||
1),
|
||||
new LaunchArgument(new []{"gen-rus-locale"},
|
||||
"Creates l_russian copy of english locale in output directory. Requires -o",
|
||||
eng=>Localisation.GenerateRussian(eng, outPath),
|
||||
"english_locale_path",
|
||||
1),
|
||||
new LaunchArgument(new []{"desc"},
|
||||
"Downloads mod description from steam to new file in outDir. Requires -o",
|
||||
id=>Workshop.CreateDescFile(id, outPath).GetAwaiter().GetResult(),
|
||||
"mod_id",
|
||||
1),
|
||||
new LaunchArgument(new []{"rename"},
|
||||
"Renames mods in directory",
|
||||
(modsdir, replace_pairs)=>Merge.RenameModsCommandHandler(modsdir, replace_pairs),
|
||||
"dir_with_mods", "replace_pairs (old_name:new_name:...)",
|
||||
1),
|
||||
new LaunchArgument(new []{"update-mods"},
|
||||
"Updates mods in [outdated_dir0...outdated_dirN] to new versions if found in updated_mods_dir. " +
|
||||
"Moves old mods to backup_dir defined by -o.",
|
||||
(updated, outdated)=>Merge.UpdateMods(updated, SplitArgToPaths(outdated, true), outPath),
|
||||
"updated_mods_dir", "outdated_dir OR outdated_dir0:...:outdated_dirN",
|
||||
1),
|
||||
new LaunchArgument(new []{"clean-locales"},
|
||||
"Deletes all localisations except l_russian and l_english.",
|
||||
locdir=> Localisation.Clean(locdir),
|
||||
"localisation_dir",
|
||||
1)
|
||||
).ParseAndHandle(args);
|
||||
}
|
||||
catch (LaunchArgumentParser.ExitAfterHelpException)
|
||||
@ -73,23 +90,27 @@ public static class Program
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log("r", DTLib.Ben.Demystifier.ExceptionExtensions.ToStringDemystified(ex));
|
||||
Console.ResetColor();
|
||||
return 1;
|
||||
}
|
||||
Console.ResetColor();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static IOPath[] SplitStringToPaths(string connected_paths)
|
||||
public static string[] SplitArgToStrings(string connected_parts, bool allow_one_part)
|
||||
{
|
||||
char part_sep;
|
||||
if (connected_paths.Contains(':'))
|
||||
if (connected_parts.Contains(':'))
|
||||
part_sep = ':';
|
||||
else if (!connected_paths.Contains(':'))
|
||||
else if (connected_parts.Contains(';'))
|
||||
part_sep = ';';
|
||||
else throw new Exception($"<{connected_paths}> doesn't contain any separators (:/;)");
|
||||
else if (allow_one_part)
|
||||
return new []{connected_parts};
|
||||
else throw new Exception($"<{connected_parts}> doesn't contain any separators (:/;)");
|
||||
|
||||
string[] split = connected_paths.Split(part_sep);
|
||||
IOPath[] split_iop = new IOPath[split.Length];
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
split_iop[i] = new IOPath(split[i]);
|
||||
return split_iop;
|
||||
return connected_parts.Split(part_sep);
|
||||
}
|
||||
|
||||
public static IOPath[] SplitArgToPaths(string connected_parts, bool allow_one_part) =>
|
||||
IOPath.ArrayCast(SplitArgToStrings(connected_parts, allow_one_part));
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user