Skip to content

Commit

Permalink
Add download import option as per KSP-CKAN#1788
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Dec 2, 2017
1 parent a3d36f7 commit 1fd6f7f
Show file tree
Hide file tree
Showing 14 changed files with 515 additions and 33 deletions.
2 changes: 2 additions & 0 deletions ConsoleUI/CKAN-ConsoleUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
</Compile>
<Compile Include="ConsoleCKAN.cs" />
<Compile Include="DependencyScreen.cs" />
<Compile Include="DownloadImportDialog.cs" />
<Compile Include="InstallScreen.cs" />
<Compile Include="KSPAddScreen.cs" />
<Compile Include="KSPEditScreen.cs" />
Expand All @@ -88,6 +89,7 @@
<Compile Include="SplashScreen.cs" />
<Compile Include="Toolkit\ConsoleButton.cs" />
<Compile Include="Toolkit\ConsoleChoiceDialog.cs" />
<Compile Include="Toolkit\ConsoleFileMultiSelectDialog.cs" />
<Compile Include="Toolkit\ConsoleDialog.cs" />
<Compile Include="Toolkit\ConsoleField.cs" />
<Compile Include="Toolkit\ConsoleFrame.cs" />
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/DependencyScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ private void AddDependencies(HashSet<string> alreadyInstalling, string identifie

AddDep(dependency.name, installByDefault, identifier);
}
} catch (ModuleNotFoundKraken k) {
} catch (ModuleNotFoundKraken) {
// LatestAvailable throws if you recommend a "provides" name,
// so ask the registry again for provides-based choices
List<CkanModule> opts = registry.LatestAvailableWithProvides(
Expand Down
122 changes: 122 additions & 0 deletions ConsoleUI/DownloadImportDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Cryptography;
using CKAN.ConsoleUI.Toolkit;

namespace CKAN.ConsoleUI {

/// <summary>
/// A popup to let the user import manually downloaded zip files into the mod cache.
/// </summary>
public static class DownloadImportDialog {

/// <summary>
/// Let the user choose some zip files, then import them to the mod cache.
/// </summary>
/// <param name="gameInst">Game instance to import into</param>
/// <param name="cp">Change plan object for marking things to be installed</param>
public static void ImportDownloads(KSP gameInst, ChangePlan cp)
{
ConsoleFileMultiSelectDialog cfmsd = new ConsoleFileMultiSelectDialog(
"Import Downloads",
FindDownloadsPath(gameInst),
"*.zip",
"Import"
);
HashSet<FileInfo> files = cfmsd.Run();

if (files.Count > 0) {
ProgressScreen ps = new ProgressScreen("Importing Downloads", "Calculating...");
ModuleInstaller inst = ModuleInstaller.GetInstance(gameInst, ps);
ps.Run(() => ImportFiles(gameInst, files, ps, inst,
(string identifier) => cp.Install.Add(identifier)));
}
}

private static readonly string[] downloadPaths = new string[] {
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads"),
Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
};

private static string FindDownloadsPath(KSP gameInst)
{
foreach (string p in downloadPaths) {
if (!string.IsNullOrEmpty(p) && Directory.Exists(p)) {
return p;
}
}
return gameInst.GameDir();
}

/// <summary>
/// Import a list of files into the download cache, with progress bar and
/// interactive prompts for installation and deletion.
/// </summary>
/// <param name="gameInst">Game instance to install into</param>
/// <param name="files">Set of files to import</param>
/// <param name="user">Object for user interaction</param>
/// <param name="inst">Module installer object</param>
/// <param name="installMod">Function to call to mark a mod for installation</param>
public static void ImportFiles(KSP gameInst, HashSet<FileInfo> files,
IUser user, ModuleInstaller inst, Action<string> installMod)
{
Registry registry = RegistryManager.Instance(gameInst).registry;
HashSet<string> installable = new HashSet<string>();
List<FileInfo> deletable = new List<FileInfo>();
// Get the mapping of known hashes to modules
Dictionary<string, List<CkanModule>> index = registry.GetSha1Index();
int i = 0;
foreach (FileInfo f in files) {
int percent = i * 100 / files.Count;
user.RaiseProgress($"Importing {f.Name}... ({percent}%)", percent);
// Calc SHA-1 sum
string sha1 = GetFileHashSha1(f.FullName);
// Find SHA-1 sum in registry (potentially multiple)
if (index.ContainsKey(sha1)) {
deletable.Add(f);
List<CkanModule> matches = index[sha1];
foreach (CkanModule mod in matches) {
if (mod.IsCompatibleKSP(gameInst.VersionCriteria())) {
installable.Add(mod.identifier);
}
if (inst.Cache.IsCachedZip(mod.download)) {
user.RaiseMessage("Already cached: {0}", f.Name);
} else {
user.RaiseMessage($"Importing {mod.identifier} {mod.version}...");
inst.Cache.Store(mod.download, f.FullName);
}
}
} else {
user.RaiseMessage("Not found in index: {0}", f.Name);
}
++i;
}
if (installable.Count > 0 && user.RaiseYesNoDialog($"Install {installable.Count} compatible imported mods?")) {
// Install the imported mods
foreach (string identifier in installable) {
installMod(identifier);
}
}
if (user.RaiseYesNoDialog($"Import complete. Delete {deletable.Count} old files?")) {
// Delete old files
foreach (FileInfo f in deletable) {
f.Delete();
}
}
}

private static string GetFileHashSha1(string filePath)
{
using (FileStream fs = new FileStream(filePath, FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (SHA1Cng sha1 = new SHA1Cng())
{
return BitConverter.ToString(sha1.ComputeHash(bs)).Replace("-", "");
}
}

}

}
2 changes: 0 additions & 2 deletions ConsoleUI/KSPEditScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ public KSPEditScreen(KSPManager mgr, KSP k)
});
repoList.AddTip("R", "Remove");
repoList.AddBinding(Keys.R, (object sender) => {
// TODO: This should edit a temp list instead
int oldPrio = repoList.Selection.priority;
editList.Remove(repoList.Selection.name);
// Reshuffle the priorities to fill
Expand Down Expand Up @@ -106,7 +105,6 @@ public KSPEditScreen(KSPManager mgr, KSP k)
});
repoList.AddTip("+", "Down");
repoList.AddBinding(Keys.Plus, (object sender) => {
// TODO: This should edit a temp list instead
Repository next = SortedDictFind(editList,
r => r.priority == repoList.Selection.priority + 1);
if (next != null) {
Expand Down
18 changes: 13 additions & 5 deletions ConsoleUI/ModInfoScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public ModInfoScreen(KSPManager mgr, ChangePlan cp, CkanModule m)
AddObject(new ConsoleLabel(
1, 1, -1,
() => mod.name == mod.identifier ? mod.name : $"{mod.name} ({mod.identifier})",
null,
() => ConsoleTheme.Current.ActiveFrameFg
));
AddObject(new ConsoleLabel(
Expand All @@ -46,6 +47,7 @@ public ModInfoScreen(KSPManager mgr, ChangePlan cp, CkanModule m)
AddObject(new ConsoleLabel(
3, 4, 11,
() => "License:",
null,
() => ConsoleTheme.Current.DimLabelFg
));
AddObject(new ConsoleLabel(
Expand All @@ -56,6 +58,7 @@ public ModInfoScreen(KSPManager mgr, ChangePlan cp, CkanModule m)
AddObject(new ConsoleLabel(
3, 5, 12,
() => "Download:",
null,
() => ConsoleTheme.Current.DimLabelFg
));
AddObject(new ConsoleLabel(
Expand Down Expand Up @@ -193,6 +196,7 @@ private int addDependencies(int top = 8)
AddObject(new ConsoleLabel(
3, top + 1, 3 + lblW - 1,
() => $"Required ({numDeps}):",
null,
() => ConsoleTheme.Current.DimLabelFg
));
ConsoleTextBox tb = new ConsoleTextBox(
Expand All @@ -215,6 +219,7 @@ private int addDependencies(int top = 8)
AddObject(new ConsoleLabel(
3, top + 1 + numDeps, 3 + lblW - 1,
() => $"Conflicts ({numConfs}):",
null,
() => ConsoleTheme.Current.DimLabelFg
));
ConsoleTextBox tb = new ConsoleTextBox(
Expand Down Expand Up @@ -381,16 +386,19 @@ private void addVersionBox(int l, int t, int r, int b, Func<string> title, Func<
() => minMod == maxMod
? $"{ModUtils.WithAndWithoutEpoch(minMod.ToString())}"
: $"{ModUtils.WithAndWithoutEpoch(minMod.ToString())} - {ModUtils.WithAndWithoutEpoch(maxMod.ToString())}",
null,
color
));
AddObject(new ConsoleLabel(
l + 2, t + 2, r - 2,
() => "Compatible with:",
null,
() => ConsoleTheme.Current.DimLabelFg
));
AddObject(new ConsoleLabel(
l + 4, t + 3, r - 2,
() => VersionSpan(minKsp, maxKsp),
null,
color
));

Expand Down Expand Up @@ -522,16 +530,16 @@ public static string FmtSize(long bytes)
if (bytes < K) {
return $"{bytes} bytes";
} else if (bytes < K * K) {
return $"{bytes / K:N1} KB";
return $"{bytes / K :N1} KB";
} else if (bytes < K * K * K) {
return $"{bytes / K / K:N1} MB";
return $"{bytes / K / K :N1} MB";
} else {
return $"{bytes / K / K / K:N1} GB";
return $"{bytes / K / K / K :N1} GB";
}
}

private static readonly Regex epochMatch = new Regex(@"^[0-9][0-9]*:[^:]+$");
private static readonly Regex epochReplace = new Regex(@"^([^:]+):([^:]+)$");
private static readonly Regex epochMatch = new Regex(@"^[0-9][0-9]*:[^:]+$", RegexOptions.Compiled);
private static readonly Regex epochReplace = new Regex(@"^([^:]+):([^:]+)$", RegexOptions.Compiled);
}

}
12 changes: 12 additions & 0 deletions ConsoleUI/ModListScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ public ModListScreen(KSPManager mgr, bool dbg)
AddObject(new ConsoleLabel(
1, -1, searchWidth,
() => $"{ModUtils.FmtSize(totalInstalledDownloadSize())} installed",
null,
() => ConsoleTheme.Current.DimLabelFg
));

Expand All @@ -213,6 +214,7 @@ public ModListScreen(KSPManager mgr, bool dbg)
: days == 1 ? $"Updated at least {days} day ago"
: $"Updated at least {days} days ago";
},
null,
() => {
int daysSince = daysSinceUpdated(registryFilePath());
if (daysSince < daysTillStale) {
Expand Down Expand Up @@ -242,6 +244,9 @@ public ModListScreen(KSPManager mgr, bool dbg)
new ConsoleMenuOption("Scan KSP dir", "",
"Check for manually installed mods",
true, ScanForMods),
new ConsoleMenuOption("Import downloads...", "",
"Select manually downloaded mods to import into CKAN",
true, ImportDownloads),
new ConsoleMenuOption("Export installed...", "",
"Save your mod list",
true, ExportInstalled),
Expand Down Expand Up @@ -276,6 +281,13 @@ public ModListScreen(KSPManager mgr, bool dbg)
? "F1"
: "F1, Alt+H";

private bool ImportDownloads()
{
DownloadImportDialog.ImportDownloads(manager.CurrentInstance, plan);
RefreshList();
return true;
}

private bool CaptureKey()
{
ConsoleKeyInfo k = default(ConsoleKeyInfo);
Expand Down
39 changes: 27 additions & 12 deletions ConsoleUI/Toolkit/ConsoleDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ public static void DrawShadow(int l, int t, int r, int b)
}
}

/// <summary>
/// Function to call to get the title of the popup.
/// If non-empty, the value will be drawn centered at the top,
/// otherwise the border will go all the way across.
/// </summary>
protected Func<string> CenterHeader = () => "";

/// <returns>
/// X coordinate of left edge of dialog
/// </returns>
Expand All @@ -67,17 +74,6 @@ public static void DrawShadow(int l, int t, int r, int b)
/// </returns>
protected int GetBottom() { return FmtUtils.ConvertCoord(bottom, Console.WindowHeight); }

private bool validX(int x)
{
x = FmtUtils.ConvertCoord(x, Console.WindowWidth);
return x >= 0 && x < Console.WindowWidth;
}
private bool validY(int y)
{
y = FmtUtils.ConvertCoord(y, Console.WindowHeight);
return y >= 0 && y < Console.WindowHeight;
}

/// <summary>
/// Set position of dialog
/// </summary>
Expand Down Expand Up @@ -110,7 +106,15 @@ protected override void DrawBackground()
Console.SetCursorPosition(GetLeft(), y);
if (y == GetTop()) {
// Top row
Console.Write(Symbols.upperLeftCornerDouble + fullHorizLineDouble + Symbols.upperRightCornerDouble);
string curTitle = CenterHeader();
if (string.IsNullOrEmpty(curTitle)) {
Console.Write(Symbols.upperLeftCornerDouble + fullHorizLineDouble + Symbols.upperRightCornerDouble);
} else {
// Title centered
Console.Write(Symbols.upperLeftCornerDouble
+ ScreenObject.PadCenter($" {curTitle} ", w - 2, Symbols.horizLineDouble)
+ Symbols.upperRightCornerDouble);
}
} else if (y == GetBottom()) {
// Bottom row
Console.Write(Symbols.lowerLeftCornerDouble + fullHorizLineDouble + Symbols.lowerRightCornerDouble);
Expand All @@ -122,6 +126,17 @@ protected override void DrawBackground()
DrawShadow(GetLeft(), GetTop(), GetRight(), GetBottom());
}

private bool validX(int x)
{
x = FmtUtils.ConvertCoord(x, Console.WindowWidth);
return x >= 0 && x < Console.WindowWidth;
}
private bool validY(int y)
{
y = FmtUtils.ConvertCoord(y, Console.WindowHeight);
return y >= 0 && y < Console.WindowHeight;
}

private int left, top, right, bottom;
}

Expand Down
Loading

0 comments on commit 1fd6f7f

Please sign in to comment.