Пример #1
0
        public MainWindow()
        {
            Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);

            Program.MainSynchronizationContext = SynchronizationContext.Current;

            Instance = this;

            InitializeComponent();

            InstallDirectoryHelper.Initialize(GetGameDirectory());

            SetupTabs();

            Task.Run((Action)PopulateStartMenu);

#if DEBUG
            var version = Assembly.GetExecutingAssembly().GetName().Version;
#else
            var version = Assembly.GetExecutingAssembly().GetName().Version.ToString(3);
#endif
            var gameName   = InstallDirectoryHelper.GameType.GetFancyGameName();
            var installDir = InstallDirectoryHelper.GameDirectory.FullName;
            Text = $"KK Manager {version} (New downloader edition) - [{gameName}] in {installDir}";
            Console.WriteLine($"Game: {gameName}   Path: {installDir}");

            Settings.Default.Binder.BindControl(checkForUpdatesOnStartupToolStripMenuItem, settings => settings.AutoUpdateSearch, this);
            Settings.Default.Binder.SendUpdates(this);
        }
Пример #2
0
        public MainWindow()
        {
            Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);

            Program.MainSynchronizationContext = SynchronizationContext.Current;

            Instance = this;

            InitializeComponent();

            InstallDirectoryHelper.KoikatuDirectory = GetGameDirectory();

            SetupTabs();

            Task.Run((Action)PopulateStartMenu);

#if DEBUG
            var version = Assembly.GetExecutingAssembly().GetName().Version;
#else
            var version = Assembly.GetExecutingAssembly().GetName().Version.ToString(3);
#endif
            Text = $"KK Manager {version} (HS2 support edition) - [{InstallDirectoryHelper.GetGameType().GetFancyGameName()}] in {InstallDirectoryHelper.KoikatuDirectory.FullName}";

            Settings.Default.Binder.BindControl(checkForUpdatesOnStartupToolStripMenuItem, settings => settings.AutoUpdateSearch, this);
            Settings.Default.Binder.SendUpdates(this);
        }
Пример #3
0
        private static string ShowInstallDirectoryDialog(string currentPath)
        {
            using (var fb = new CommonOpenFileDialog
            {
                IsFolderPicker = true,
                InitialDirectory = currentPath,
                AllowNonFileSystemItems = false,
                AddToMostRecentlyUsedList = false,
                EnsurePathExists = true,
                EnsureFileExists = true,
                Multiselect = false,
                Title = "Select the install directory of the game."
            })
            {
retryFolderSelect:
                if (fb.ShowDialog() == CommonFileDialogResult.Ok)
                {
                    var path = fb.FileName;
                    if (!InstallDirectoryHelper.IsValidGamePath(path))
                    {
                        if (MessageBox.Show(
                                "The selected directory doesn't seem to contain the game. Make sure the directory is correct and try again.",
                                "Select install directory", MessageBoxButtons.OKCancel, MessageBoxIcon.Error) == DialogResult.OK)
                        {
                            goto retryFolderSelect;
                        }
                    }
                    return(path);
                }

                return(null);
            }
        }
Пример #4
0
        private void SetupTabs()
        {
            // Try to load saved state first, if that fails load defaults
            try
            {
                if (!string.IsNullOrWhiteSpace(Settings.Default.DockState))
                {
                    using (var s = new MemoryStream(Encoding.Unicode.GetBytes(Settings.Default.DockState)))
                    {
                        dockPanel.LoadFromXml(s, DeserializeTab);
                        return;
                    }
                }
            }
            catch (Exception ex) { Console.WriteLine("Failed to read opened tabs from config: " + ex); }

            OpenOrGetCardWindow(InstallDirectoryHelper.GetMaleCardDir());
            OpenOrGetCardWindow(InstallDirectoryHelper.GetFemaleCardDir());

            GetOrCreateWindow <SideloaderModsWindow>();
            GetOrCreateWindow <PluginsWindow>();

            dockPanel.DockRightPortion = 400;
            var propertiesToolWindow = GetOrCreateWindow <PropertiesToolWindow>();

            propertiesToolWindow.DockState = DockState.DockRight;
        }
Пример #5
0
        private IEnumerable <SideloaderUpdateItem> CollectTasks(List <INode> nodes, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var root = nodes.Single(x => x.Type == NodeType.Root);

            var modsDirPath = InstallDirectoryHelper.GetModsPath();

            modsDirPath.Create();

            var results = Enumerable.Empty <SideloaderUpdateItem>();

            foreach (var remoteModpackDir in GetSubNodes(root).Where(x => x.Type == NodeType.Directory))
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (remoteModpackDir.Name.StartsWith("Sideloader Modpack"))
                {
                    var localModpackDir = new DirectoryInfo(Path.Combine(modsDirPath.FullName, remoteModpackDir.Name));
                    if (localModpackDir.Exists)
                    {
                        results = results.Concat(ProcessDirectory(remoteModpackDir, localModpackDir, cancellationToken));
                    }
                }
                else
                {
                    Console.WriteLine("Skipping non-modpack directory " + remoteModpackDir.Name);
                }
            }

            return(results);
        }
Пример #6
0
        private static string ShowInstallDirectoryDialog(string currentPath)
        {
            using (var fb = new FolderBrowserDialog())
            {
                fb.RootFolder          = Environment.SpecialFolder.MyComputer;
                fb.SelectedPath        = currentPath ?? "";
                fb.ShowNewFolderButton = false;
                fb.Description         = "Select the install directory of the game.";

retryFolderSelect:
                if (fb.ShowDialog() == DialogResult.OK)
                {
                    var path = fb.SelectedPath;
                    if (!InstallDirectoryHelper.IsValidGamePath(path))
                    {
                        if (MessageBox.Show(
                                "The selected directory doesn't seem to contain the game. Make sure the directory is correct and try again.",
                                "Select install directory", MessageBoxButtons.OKCancel, MessageBoxIcon.Error) == DialogResult.OK)
                        {
                            goto retryFolderSelect;
                        }
                    }
                    return(path);
                }

                return(null);
            }
        }
Пример #7
0
        private async Task <bool> UpdateSingleItem(IGrouping <string, Tuple <UpdateInfo, IUpdateItem> > task)
        {
            var firstItem = task.First().Item2;
            var progress  = new Progress <double>(d => labelPercent.Text = $"Downloaded {d:F1}% of {firstItem.ItemSize}.  Overall: {_completedSize} / {_overallSize}.");

            SetStatus($"Updating {firstItem.TargetPath.Name}");
            SetStatus($"Updating {InstallDirectoryHelper.GetRelativePath(firstItem.TargetPath)}", false, true);

            // todo move logging to update methods
            if (firstItem.TargetPath.Exists)
            {
                SetStatus($"Deleting old file {firstItem.TargetPath.FullName}", false, true);
                firstItem.TargetPath.Delete();
            }

            var sourcesToAttempt = task.Where(x => !_badUpdateSources.Contains(x.Item1)).OrderByDescending(x => x.Item1.SourcePriority).ToList();

            if (sourcesToAttempt.Count == 0)
            {
                Console.WriteLine("There are no working sources to download from. Check the log for reasons why the sources failed.");

                _failedItems.Add(task);
                return(false);
            }

            Exception ex = null;

            foreach (var source in sourcesToAttempt)
            {
                try
                {
                    SetStatus($"Attempting download from source {source.Item1.Origin}", false, true);

                    await RetryHelper.RetryOnExceptionAsync(() => source.Item2.Update(progress, _cancelToken.Token), 3, TimeSpan.FromSeconds(3), _cancelToken.Token);

                    ex = null;
                    break;
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Marking source {source.Item1.Origin} as broken because of exception: {e}");

                    ex = e;
                    _badUpdateSources.Add(source.Item1);
                }
            }
            // Check if all sources failed
            if (ex != null)
            {
                Console.WriteLine("There are no working sources to download from. Check the log for reasons why the sources failed.");

                _failedItems.Add(task);
                _failedExceptions.Add(ex);
                return(false);
            }

            SetStatus($"Download OK {firstItem.ItemSize}", false, true);
            return(true);
        }
Пример #8
0
        private static void Main(string[] args)
        {
            AppDomain.CurrentDomain.UnhandledException += (sender, arg) => Console.WriteLine("UNHANDLED EXCEPTION: " + arg.ExceptionObject);
            AppDomain.CurrentDomain.UnhandledException += NBug.Handler.UnhandledException;

            using (LogWriter.StartLogging())
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                const string guidValueName      = "-guid:";
                var          silentInstallGuids = args.Where(x => x.StartsWith(guidValueName)).Select(x => x.Substring(guidValueName.Length).Trim(' ', '"')).ToArray();

                args = args.Where(x => !x.StartsWith("-")).Where(x => !string.IsNullOrWhiteSpace(x)).ToArray();

                if (args.Length == 0)
                {
                    MessageBox.Show("Not enough arguments - the following arguments are supported:\n" +
                                    "Path to game root directory. This is mandatory and has to be the 1st argument.\n" +
                                    "One or more links to update sources. If no update source links are provided then UpdateSources file is used.\n\n" +
                                    "You can also add -guid:UPDATE_GUID arguments to not show the mod selection screen, and instead automatically install mods with the specified GUIDs.", "Invalid arguments", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                try
                {
                    var gameDir = new DirectoryInfo(args[0]);
                    if (!gameDir.Exists)
                    {
                        throw new IOException("Directory doesn't exist");
                    }
                    InstallDirectoryHelper.Initialize(gameDir);
                }
                catch (Exception e)
                {
                    MessageBox.Show($"Error opening game directory: {args[0]}\n\n{e}", "Invalid arguments", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    Console.WriteLine(e);
                    return;
                }

                if (SelfUpdater.CheckForUpdatesAndShowDialog().Result == true)
                {
                    return;
                }

                var updateSources = GetUpdateSources(args.Skip(1).ToArray());

                if (updateSources == null || !updateSources.Any())
                {
                    return;
                }

                var window = ModUpdateProgressDialog.CreateUpdateDialog(updateSources, silentInstallGuids);
                window.Icon = Icon.ExtractAssociatedIcon(typeof(Program).Assembly.Location);
                Application.Run(window);
            }
        }
 private void toolStripButtonOpenModsDir_Click(object sender, EventArgs e)
 {
     try
     {
         Process.Start(InstallDirectoryHelper.GetModsPath().FullName);
     }
     catch (SystemException ex)
     {
         MessageBox.Show(ex.Message, "Failed to start application", MessageBoxButtons.OK, MessageBoxIcon.Error);
     }
 }
Пример #10
0
        private static DefaultAssemblyResolver GetResolver(FileInfo location)
        {
            var resolver = new DefaultAssemblyResolver();

            if (location.Directory != null)
            {
                var coreDir = Path.Combine(InstallDirectoryHelper.GetPluginPath(), "core");
                if (Directory.Exists(coreDir))
                {
                    resolver.AddSearchDirectory(coreDir);
                }
            }

            return(resolver);
        }
Пример #11
0
        private void PopulateStartMenu()
        {
            var toAdd        = new List <ToolStripItem>();
            var pluginPath   = InstallDirectoryHelper.GetPluginPath();
            var allExes      = InstallDirectoryHelper.KoikatuDirectory.GetFiles("*.exe", SearchOption.AllDirectories);
            var filteredExes = allExes.Where(x => !x.Name.Equals("bepinex.patcher.exe", StringComparison.OrdinalIgnoreCase) && !x.FullName.StartsWith(pluginPath, StringComparison.OrdinalIgnoreCase));

            foreach (var file in filteredExes.OrderBy(x => x.Name))
            {
                var item = new ToolStripMenuItem(file.Name);
                item.AutoToolTip = false;
                item.ToolTipText = file.FullName;
                item.Click      += (o, args) => { ProcessTools.SafeStartProcess(file.FullName); };
                toAdd.Add(item);
            }
            this.SafeInvoke(() => startTheGameToolStripMenuItem.DropDownItems.AddRange(toAdd.ToArray()));
        }
Пример #12
0
        private void PopulateStartMenu()
        {
            var toAdd        = new List <ToolStripItem>();
            var pluginPath   = InstallDirectoryHelper.GetPluginPath();
            var allExes      = InstallDirectoryHelper.KoikatuDirectory.GetFiles("*.exe", SearchOption.AllDirectories);
            var filteredExes = allExes.Where(x => !x.Name.Equals("bepinex.patcher.exe", StringComparison.OrdinalIgnoreCase) && !x.FullName.StartsWith(pluginPath, StringComparison.OrdinalIgnoreCase));

            var first = true;

            foreach (var folder in filteredExes.GroupBy(x => x.DirectoryName, StringComparer.OrdinalIgnoreCase).OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
            {
                if (!first)
                {
                    toAdd.Add(new ToolStripSeparator());
                }
                first = false;

                foreach (var file in folder.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase))
                {
                    // Trim .exe but leave other extensions
                    var trimmedName = file.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) ? file.Name.Substring(0, file.Name.Length - 4) : file.Name;
                    var item        = new ToolStripMenuItem(trimmedName);

                    item.AutoToolTip = false;
                    item.ToolTipText = file.FullName;

                    item.Click += (o, args) => { ProcessTools.SafeStartProcess(file.FullName); };

                    try { item.Image = Icon.ExtractAssociatedIcon(file.FullName)?.ToBitmap(); }
                    catch { item.Image = null; }

                    toAdd.Add(item);
                }
            }

            this.SafeInvoke(() =>
            {
                foreach (var item in toAdd)
                {
                    startTheGameToolStripMenuItem.DropDownItems.Add(item);
                }
            });
        }
Пример #13
0
        public void ReloadList()
        {
            CancelListReload();
            objectListView1.ClearObjects();

            _cancellationTokenSource = new CancellationTokenSource();
            var token      = _cancellationTokenSource.Token;
            var observable = SideloaderModLoader.TryReadSideloaderMods(InstallDirectoryHelper.GetModsPath().FullName, token);

            observable
            .Buffer(TimeSpan.FromSeconds(3))
            .ObserveOn(this)
            .Subscribe(list => objectListView1.AddObjects((ICollection)list),
                       () =>
            {
                objectListView1.FastAutoResizeColumns();
                MainWindow.SetStatusText("Done loading zipmods");
            }, token);
        }
Пример #14
0
        private void SetupTabs()
        {
            // Try to load saved state first, if that fails load defaults
            try
            {
                if (!string.IsNullOrWhiteSpace(Settings.Default.DockState))
                {
                    using (var s = new MemoryStream(Encoding.Unicode.GetBytes(Settings.Default.DockState)))
                    {
                        dockPanel.LoadFromXml(s, DeserializeTab);
                        return;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed to read opened tabs from config: " + ex.ToStringDemystified());
                foreach (var content in dockPanel.Contents)
                {
                    content.DockHandler.Close();
                }
                dockPanel.ResumeLayout(true, true);
            }

            OpenOrGetCardWindow(InstallDirectoryHelper.GetMaleCardDir());
            OpenOrGetCardWindow(InstallDirectoryHelper.GetFemaleCardDir());

            GetOrCreateWindow <SideloaderModsWindow>();
            GetOrCreateWindow <PluginsWindow>();

            dockPanel.DockRightPortion = 400;
            var propertiesToolWindow = GetOrCreateWindow <PropertiesToolWindow>();

            propertiesToolWindow.DockState = DockState.DockRight;

            var logWindow = GetOrCreateWindow <LogViewer>();

            logWindow.DockState = DockState.DockBottomAutoHide;
        }
        private ModUpdateProgressDialog()
        {
            InitializeComponent();

            switch (InstallDirectoryHelper.GetGameType())
            {
            case GameType.PlayHome:
            case GameType.AiShoujoSteam:
            case GameType.AiShoujo:
            case GameType.HoneySelect2:
                pictureBox1.Image = Resources.aichika;
                break;

            case GameType.Koikatsu:
            case GameType.KoikatsuSteam:
            case GameType.EmotionCreators:
                pictureBox1.Image = Resources.chikajump;
                break;

            case GameType.Unknown:
                break;
            }
        }
Пример #16
0
        private static DirectoryInfo GetKoikatuDirectory()
        {
            var path = Settings.Default.GamePath;

            if (!InstallDirectoryHelper.IsValidGamePath(path))
            {
                try
                {
                    path = Registry.CurrentUser.OpenSubKey(@"Software\Illusion\Koikatu\koikatu")
                           ?.GetValue("INSTALLDIR") as string;
                }
                catch (SystemException) { }

                if (!InstallDirectoryHelper.IsValidGamePath(path))
                {
                    MessageBox.Show(
                        "Koikatu is either not registered properly or its install directory is missing or inaccessible.\n\nYou will have to select game directory manually.",
                        "Failed to find Koikatu install directory", MessageBoxButtons.OK, MessageBoxIcon.Error);

                    path = ShowInstallDirectoryDialog(path);
                }

                if (string.IsNullOrEmpty(path) || !InstallDirectoryHelper.IsValidGamePath(path))
                {
                    MessageBox.Show(
                        "Koikatu is either not registered properly or its install directory is missing or inaccessible.",
                        "Failed to get Koikatu install directory", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    Environment.Exit(1);
                }

                Settings.Default.GamePath = path;
            }

            CheckInstallPathPermissions(path);

            return(new DirectoryInfo(path));
        }
        private async Task <bool> UpdateSingleItem(IGrouping <string, Tuple <UpdateInfo, UpdateItem> > task)
        {
            var firstItem = task.First().Item2;
            var itemSize  = firstItem.GetDownloadSize();

            var lastTimestamp        = DateTime.UtcNow;
            var lastDownloadedKBytes = 0l;

            var progress = new Progress <double>(thisPercent =>
            {
                var timeNow = DateTime.UtcNow;
                var secondsSinceLastUpdate = (timeNow - lastTimestamp).TotalSeconds;

                if (secondsSinceLastUpdate < 1 && thisPercent < 100)
                {
                    return;
                }

                //This item: 70% done (1MB / 20MB)
                //Overall: 50% done (111MB / 1221MB)
                //Speed: 1234KB/s (average 1111KB/s)

                var downloadedKBytes    = (long)(itemSize.GetRawSize() * (thisPercent / 100d));
                var downloadedSize      = FileSize.FromKilobytes(downloadedKBytes);
                var totalDownloadedSize = _completedSize + downloadedSize;
                var totalPercent        = ((double)totalDownloadedSize.GetRawSize() / (double)_overallSize.GetRawSize()) * 100d;

                var speed = (downloadedKBytes - lastDownloadedKBytes) / secondsSinceLastUpdate;
                if (double.IsNaN(speed))
                {
                    speed = 0;
                }
                lastDownloadedKBytes = downloadedKBytes;
                lastTimestamp        = timeNow;

                labelPercent.Text =
                    $@"This item: {thisPercent:F1}% done ({downloadedSize} / {itemSize})
Overall: {totalPercent:F1}% done ({totalDownloadedSize} / {_overallSize})
Speed: {speed:F1}KB/s";

                progressBar1.Value = Math.Min((int)(totalPercent * 10), progressBar1.Maximum);
            });

            SetStatus($"Updating {firstItem.TargetPath.Name}");
            SetStatus($"Updating {InstallDirectoryHelper.GetRelativePath(firstItem.TargetPath)}", false, true);

            var sourcesToAttempt = task.Where(x => !_badUpdateSources.Contains(x.Item1)).OrderBy(x => GetPing(x.Item1)).ToList();

            if (sourcesToAttempt.Count == 0)
            {
                Console.WriteLine("There are no working sources to download from. Check the log for reasons why the sources failed.");

                _failedItems.Add(task);
                return(false);
            }

            Exception ex = null;

            foreach (var source in sourcesToAttempt)
            {
                try
                {
                    // Needed because ZipUpdater doesn't support progress
                    if (source.Item2.RemoteFile is ZipUpdater.ArchiveItem)
                    {
                        labelPercent.Text = $"Extracting... Overall progress: {_completedSize} / {_overallSize}.";
                    }

                    await RetryHelper.RetryOnExceptionAsync(() => source.Item2.Update(progress, _cancelToken.Token), 3,
                                                            TimeSpan.FromSeconds(3), _cancelToken.Token);

                    _completedSize += source.Item2.GetDownloadSize();
                    ex              = null;
                    break;
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Marking source {source.Item1.Source.Origin} as broken because of exception: {e.ToStringDemystified()}");

                    ex = e;
                    _badUpdateSources.Add(source.Item1);
                }
            }
            // Check if all sources failed
            if (ex != null)
            {
                Console.WriteLine("There are no working sources to download from. Check the log for reasons why the sources failed.");

                _failedItems.Add(task);
                _failedExceptions.Add(ex);
                return(false);
            }

            return(true);
        }
Пример #18
0
        private static DirectoryInfo GetGameDirectory()
        {
            var path = Settings.Default.GamePath;

            if (!InstallDirectoryHelper.IsValidGamePath(path))
            {
                for (var dir = new DirectoryInfo(Program.ProgramLocation); dir.Exists && dir.Parent != null; dir = dir.Parent)
                {
                    if (InstallDirectoryHelper.IsValidGamePath(dir.FullName))
                    {
                        path = dir.FullName;
                        break;
                    }
                }

                if (!InstallDirectoryHelper.IsValidGamePath(path))
                {
                    MessageBox.Show(
                        "Your game's install directory could not be detected or is inaccessible.\n\nYou will have to select the game directory manually.",
                        "Failed to find game install directory", MessageBoxButtons.OK, MessageBoxIcon.Error);

                    if (string.IsNullOrWhiteSpace(path))
                    {
                        // Get a plausible initial path
                        try
                        {
                            path = Registry.CurrentUser.OpenSubKey(@"Software\Illusion\Koikatu\koikatu")
                                   ?.GetValue("INSTALLDIR") as string;
                        }
                        catch (SystemException) { }
                    }

                    path = ShowInstallDirectoryDialog(path);
                }

                if (!InstallDirectoryHelper.IsValidGamePath(path))
                {
                    MessageBox.Show(
                        "Your game is either not registered properly or its install directory is missing or inaccessible.",
                        "Failed to find game install directory", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    Environment.Exit(1);
                }

                Settings.Default.GamePath = path;

                if (path.Length > 100)
                {
                    MessageBox.Show(
                        "Your game path is very long and is likely to cause issues with running the game and/or KK Manager.\n\nPlease consider moving your game to a simpler directory like \"D:\\Games\\Koikatsu\" to avoid potential issues. ",
                        "Game directory warning", MessageBoxButtons.OK);
                }
                else if (path.Any(x => x > 127))
                {
                    MessageBox.Show(
                        "Your game path contains special characters that might cause issues with running the game.\n\nPlease consider moving your game to a simpler directory like \"D:\\Games\\Koikatsu\" to avoid potential issues. ",
                        "Game directory warning", MessageBoxButtons.OK);
                }
            }

            CheckInstallPathPermissions(path);

            return(new DirectoryInfo(path));
        }
Пример #19
0
 private void openMaleCardFolderToolStripMenuItem_Click(object sender, EventArgs e)
 {
     OpenOrGetCardWindow(InstallDirectoryHelper.GetMaleCardDir());
 }