UpdateMods,RenameMods,CleanLocales
This commit is contained in:
parent
49e7e21626
commit
c449c5cb4d
@ -16,6 +16,6 @@
|
|||||||
<ProjectReference Include="..\..\DTLib\DTLib\DTLib.csproj" />
|
<ProjectReference Include="..\..\DTLib\DTLib\DTLib.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||||
<PackageReference Include="DTLib" Version="1.2.0" />
|
<PackageReference Include="DTLib" Version="1.2.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -7,14 +7,13 @@ using DTLib.Ben.Demystifier;
|
|||||||
namespace ParadoxModMerger;
|
namespace ParadoxModMerger;
|
||||||
|
|
||||||
|
|
||||||
public record struct ConflictingModFile(string FilePath, string[] Mods);
|
|
||||||
|
|
||||||
public enum DiffState
|
public enum DiffState
|
||||||
{
|
{
|
||||||
Added, Equal, Removed, Changed
|
Added, Equal, Removed, Changed
|
||||||
}
|
}
|
||||||
|
|
||||||
public record struct DiffPart<T>(T Value, DiffState State);
|
public record struct DiffPart<T>(T Value, DiffState State);
|
||||||
|
public record struct ConflictingModFile(string FilePath, string[] Mods);
|
||||||
|
|
||||||
static class Diff
|
static class Diff
|
||||||
{
|
{
|
||||||
@ -23,7 +22,7 @@ static class Diff
|
|||||||
|
|
||||||
public static void DiffCommandHandler(string connected_pathes)
|
public static void DiffCommandHandler(string connected_pathes)
|
||||||
{
|
{
|
||||||
IOPath[] moddirs = Program.SplitStringToPaths(connected_pathes);
|
IOPath[] moddirs = Program.SplitArgToPaths(connected_pathes, false);
|
||||||
var conflicts = FindModConflicts(moddirs);
|
var conflicts = FindModConflicts(moddirs);
|
||||||
LogModConflicts(conflicts);
|
LogModConflicts(conflicts);
|
||||||
}
|
}
|
||||||
@ -148,14 +147,14 @@ static class Diff
|
|||||||
case ConsoleKey.LeftArrow:
|
case ConsoleKey.LeftArrow:
|
||||||
{
|
{
|
||||||
ColoredConsole.Write("w", "enter left mod number: ");
|
ColoredConsole.Write("w", "enter left mod number: ");
|
||||||
string answ = Console.ReadLine();
|
string answ = Console.ReadLine()!;
|
||||||
selected_mod0_i = answ.ToInt();
|
selected_mod0_i = answ.ToInt();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ConsoleKey.RightArrow:
|
case ConsoleKey.RightArrow:
|
||||||
{
|
{
|
||||||
ColoredConsole.Write("w", "enter right mod number: ");
|
ColoredConsole.Write("w", "enter right mod number: ");
|
||||||
string answ = Console.ReadLine();
|
string answ = Console.ReadLine()!;
|
||||||
selected_mod1_i = answ.ToInt();
|
selected_mod1_i = answ.ToInt();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -194,7 +193,7 @@ static class Diff
|
|||||||
|
|
||||||
public static IEnumerable<DiffPart<T>> DiffCollections<T>(ICollection<T> col0, ICollection<T> col1)
|
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);
|
yield return new DiffPart<T>(el, col1.Contains(el) ? DiffState.Equal : DiffState.Removed);
|
||||||
foreach (var el in col1)
|
foreach (var el in col1)
|
||||||
if (!col0.Contains(el))
|
if (!col0.Contains(el))
|
||||||
|
|||||||
@ -23,4 +23,52 @@ static class Localisation
|
|||||||
else Log("y", $"file {rusFileName} already exists");
|
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");
|
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]");
|
Log("y", question + " [y/n]");
|
||||||
string answ = ColoredConsole.Read("w");
|
string answ = ColoredConsole.Read("w").ToLower();
|
||||||
if (answ == "y") yes();
|
if (answ == "y") yes?.Invoke();
|
||||||
else no();
|
else no?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HandleConflicts(IOPath[] moddirs)
|
static void HandleConflicts(IOPath[] moddirs)
|
||||||
{
|
{
|
||||||
var conflicts = Diff.FindModConflicts(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);
|
Diff.LogModConflicts(conflicts);
|
||||||
ConsoleAskYN("continue merge?",
|
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.Extensions;
|
||||||
global using DTLib.Filesystem;
|
global using DTLib.Filesystem;
|
||||||
global using DTLib.Logging;
|
global using DTLib.Logging;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using DTLib.Console;
|
using DTLib.Console;
|
||||||
|
|
||||||
namespace ParadoxModMerger;
|
namespace ParadoxModMerger;
|
||||||
@ -15,7 +17,7 @@ public static class Program
|
|||||||
static ConsoleLogger logger = new($"logs", "main");
|
static ConsoleLogger logger = new($"logs", "main");
|
||||||
static void Log(params string[] msg) => logger.Log(msg);
|
static void Log(params string[] msg) => logger.Log(msg);
|
||||||
|
|
||||||
static void Main(string[] args)
|
static int Main(string[] args)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -44,8 +46,7 @@ public static class Program
|
|||||||
"reads conflicts_XXX.dtsod file and shows text diff for each file",
|
"reads conflicts_XXX.dtsod file and shows text diff for each file",
|
||||||
p=>Diff.DiffDetailedCommandHandler(p),
|
p=>Diff.DiffDetailedCommandHandler(p),
|
||||||
"conflicts_dtsod_path",
|
"conflicts_dtsod_path",
|
||||||
1
|
1),
|
||||||
),
|
|
||||||
new LaunchArgument(new []{"merge-subdirs"},
|
new LaunchArgument(new []{"merge-subdirs"},
|
||||||
"Merges mods and shows conflicts. Requires -o",
|
"Merges mods and shows conflicts. Requires -o",
|
||||||
d => Merge.MergeAll(Directory.GetDirectories(d), outPath),
|
d => Merge.MergeAll(Directory.GetDirectories(d), outPath),
|
||||||
@ -65,6 +66,22 @@ public static class Program
|
|||||||
"Downloads mod description from steam to new file in outDir. Requires -o",
|
"Downloads mod description from steam to new file in outDir. Requires -o",
|
||||||
id=>Workshop.CreateDescFile(id, outPath).GetAwaiter().GetResult(),
|
id=>Workshop.CreateDescFile(id, outPath).GetAwaiter().GetResult(),
|
||||||
"mod_id",
|
"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)
|
1)
|
||||||
).ParseAndHandle(args);
|
).ParseAndHandle(args);
|
||||||
}
|
}
|
||||||
@ -73,23 +90,27 @@ public static class Program
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log("r", DTLib.Ben.Demystifier.ExceptionExtensions.ToStringDemystified(ex));
|
Log("r", DTLib.Ben.Demystifier.ExceptionExtensions.ToStringDemystified(ex));
|
||||||
}
|
|
||||||
Console.ResetColor();
|
Console.ResetColor();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IOPath[] SplitStringToPaths(string connected_paths)
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] SplitArgToStrings(string connected_parts, bool allow_one_part)
|
||||||
{
|
{
|
||||||
char part_sep;
|
char part_sep;
|
||||||
if (connected_paths.Contains(':'))
|
if (connected_parts.Contains(':'))
|
||||||
part_sep = ':';
|
part_sep = ':';
|
||||||
else if (!connected_paths.Contains(':'))
|
else if (connected_parts.Contains(';'))
|
||||||
part_sep = ';';
|
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);
|
return connected_parts.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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