check for updates - initial implementation.

This commit is contained in:
morkt 2017-02-14 07:22:48 +04:00
parent 2df8145f83
commit 987d57a4db
11 changed files with 370 additions and 16 deletions

View File

@ -33,7 +33,6 @@ IN THE SOFTWARE.
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner">
<Window.Resources>
<sys:Uri x:Key="DevLink">https://github.com/morkt/GARbro#readme</sys:Uri>
<local:BooleanToVisibiltyConverter x:Key="guiBoolToVisibilityConverter" />
<local:CanCreateConverter x:Key="guiCanCreateConverter"/>
<CollectionViewSource x:Key="ArcFormatsSource" Source="{Binding Source={x:Static gr:FormatCatalog.Instance}, Path=ArcFormats, Mode=OneWay}">

View File

@ -189,20 +189,8 @@ namespace GARbro.GUI
private void Hyperlink_RequestNavigate (object sender, RequestNavigateEventArgs e)
{
try
{
if (e.Uri.IsAbsoluteUri)
{
Process.Start (new ProcessStartInfo (e.Uri.AbsoluteUri));
e.Handled = true;
}
else
throw new ApplicationException ("URI is not absolute");
}
catch (Exception X)
{
Trace.WriteLine ("Link navigation failed: "+X.Message, e.Uri.ToString());
}
if (App.NavigateUri (e.Uri))
e.Handled = true;
}
}

View File

@ -1,9 +1,12 @@
<Application x:Class="GARbro.GUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=System"
StartupUri="MainWindow.xaml" Startup="ApplicationStartup"
ShutdownMode="OnMainWindowClose" Exit="ApplicationExit">
<Application.Resources>
<sys:Uri x:Key="DevLink">https://github.com/morkt/GARbro#readme</sys:Uri>
<sys:Uri x:Key="UpdateUrl">https://morkt.github.io/version.xml</sys:Uri>
<BitmapImage x:Key="IconSearch" UriSource="pack://application:,,,/Images/search4files.ico" />
</Application.Resources>
</Application>

View File

@ -29,6 +29,7 @@ using System.Diagnostics;
using GARbro.GUI.Properties;
using GameRes;
using GameRes.Compression;
using System.Reflection;
namespace GARbro.GUI
{
@ -79,9 +80,25 @@ namespace GARbro.GUI
if (string.IsNullOrEmpty (InitPath))
InitPath = Directory.GetCurrentDirectory();
string scheme_file = Path.Combine (FormatCatalog.Instance.DataDirectory, "Formats.dat");
string formats_dat = "Formats.dat";
DeserializeScheme (Path.Combine (FormatCatalog.Instance.DataDirectory, formats_dat));
DeserializeScheme (Path.Combine (GetLocalAppDataFolder(), formats_dat));
}
string GetLocalAppDataFolder ()
{
string local_app_data = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
var attribs = Assembly.GetExecutingAssembly().GetCustomAttributes (typeof(AssemblyCompanyAttribute), false);
string company = attribs.Length > 0 ? ((AssemblyCompanyAttribute)attribs[0]).Company : "";
return Path.Combine (local_app_data, company, Name);
}
void DeserializeScheme (string scheme_file)
{
try
{
if (!File.Exists (scheme_file))
return;
using (var file = File.OpenRead (scheme_file))
FormatCatalog.Instance.DeserializeScheme (file);
}
@ -116,5 +133,24 @@ namespace GARbro.GUI
if (Settings.Default.winState == System.Windows.WindowState.Minimized)
Settings.Default.winState = System.Windows.WindowState.Normal;
}
public static bool NavigateUri (Uri uri)
{
try
{
if (uri.IsAbsoluteUri)
{
Process.Start (new ProcessStartInfo (uri.AbsoluteUri));
return true;
}
else
throw new ApplicationException ("URI is not absolute");
}
catch (Exception X)
{
Trace.WriteLine ("Link navigation failed: "+X.Message, uri.ToString());
}
return false;
}
}
}

View File

@ -154,6 +154,7 @@
<Compile Include="GarCreate.cs" />
<Compile Include="GarExtract.cs" />
<Compile Include="GarOperation.cs" />
<Compile Include="GarUpdate.cs" />
<Compile Include="HistoryStack.cs" />
<Compile Include="ImagePreview.cs" />
<Compile Include="ListViewEx.cs" />
@ -173,6 +174,9 @@
<Compile Include="TextViewer.xaml.cs">
<DependentUpon>TextViewer.xaml</DependentUpon>
</Compile>
<Compile Include="UpdateDialog.xaml.cs">
<DependentUpon>UpdateDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Utility.cs" />
<Compile Include="ViewModel.cs" />
<Page Include="AboutBox.xaml">
@ -227,6 +231,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UpdateDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">

197
GUI/GarUpdate.cs Normal file
View File

@ -0,0 +1,197 @@
//! \file GarUpdate.cs
//! \date Tue Feb 14 00:02:14 2017
//! \brief Application update routines.
//
// Copyright (C) 2017 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Xml;
using GameRes;
namespace GARbro.GUI
{
public partial class MainWindow : Window
{
private readonly BackgroundWorker m_update_checker = new BackgroundWorker();
private void InitUpdatesChecker ()
{
m_update_checker.DoWork += StartUpdatesCheck;
m_update_checker.RunWorkerCompleted += UpdatesCheckComplete;
}
/// <summary>
/// Handle "Check for updates" command.
/// </summary>
private void CheckUpdatesExec (object sender, ExecutedRoutedEventArgs e)
{
if (!m_update_checker.IsBusy)
m_update_checker.RunWorkerAsync();
}
private void StartUpdatesCheck (object sender, DoWorkEventArgs e)
{
var url = m_app.Resources["UpdateUrl"] as Uri;
if (null == url)
return;
using (var updater = new GarUpdate (this))
{
e.Result = updater.Check (url);
}
}
private void UpdatesCheckComplete (object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
SetStatusText (string.Format ("{0} {1}", "Update failed.", e.Error.Message));
return;
}
else if (e.Cancelled)
return;
var result = e.Result as GarUpdateInfo;
if (null == result)
{
SetStatusText ("No updates currently available.");
return;
}
var app_version = Assembly.GetExecutingAssembly().GetName().Version;
var db_version = FormatCatalog.Instance.CurrentSchemeVersion;
bool has_app_update = app_version < result.ReleaseVersion;
bool has_db_update = db_version < result.FormatsVersion && CheckAssemblies (result.Assemblies);
if (!has_app_update && !has_db_update)
{
SetStatusText ("GARbro version is up to date.");
return;
}
var dialog = new UpdateDialog (result, has_app_update, has_db_update);
dialog.Owner = this;
dialog.FormatsDownload.Click = FormatsDownloadExec;
dialog.ShowDialog();
}
private void FormatsDownloadExec (object sender, RoutedEventArgs e)
{
}
/// <summary>
/// Check if loaded assemblies match required versions.
/// </summary>
bool CheckAssemblies (IDictionary<string, Version> assemblies)
{
var loaded = AppDomain.CurrentDomain.GetAssemblies().Select (a => a.GetName())
.ToDictionary (a => a.Name, a => a.Version);
foreach (var item in assemblies)
{
if (!loaded.ContainsKey (item.Key))
return false;
if (loaded[item.Key] < item.Value)
return false;
}
return true;
}
}
public class GarUpdateInfo
{
public Version ReleaseVersion { get; set; }
public Uri ReleaseUrl { get; set; }
public string ReleaseNotes { get; set; }
public int FormatsVersion { get; set; }
public Uri FormatsUrl { get; set; }
public IDictionary<string, Version> Assemblies { get; set; }
}
internal sealed class GarUpdate : IDisposable
{
Window m_main;
const int RequestTimeout = 20000; // milliseconds
public GarUpdate (Window main)
{
m_main = main;
}
public GarUpdateInfo Check (Uri version_url)
{
var request = WebRequest.Create (version_url);
request.Timeout = RequestTimeout;
var response = (HttpWebResponse)request.GetResponse();
using (var input = response.GetResponseStream())
{
var xml = new XmlDocument();
xml.Load (input);
var root = xml.DocumentElement.SelectSingleNode ("/GARbro");
if (null == root)
return null;
var info = new GarUpdateInfo
{
ReleaseVersion = Version.Parse (GetInnerText (root.SelectSingleNode ("Release/Version"))),
ReleaseUrl = new Uri (GetInnerText (root.SelectSingleNode ("Release/Url"))),
ReleaseNotes = GetInnerText (root.SelectSingleNode ("Release/Notes")),
FormatsVersion = Int32.Parse (GetInnerText (root.SelectSingleNode ("FormatsData/FileVersion"))),
FormatsUrl = new Uri (GetInnerText (root.SelectSingleNode ("FormatsData/Url"))),
Assemblies = ParseAssemblies (root.SelectNodes ("FormatsData/Requires/Assembly")),
};
return info;
}
}
static string GetInnerText (XmlNode node)
{
return node != null ? node.InnerText : "";
}
IDictionary<string, Version> ParseAssemblies (XmlNodeList nodes)
{
var dict = new Dictionary<string, Version>();
foreach (XmlNode node in nodes)
{
var attr = node.Attributes;
var name = attr["Name"];
var version = attr["Version"];
if (name != null && version != null)
dict[name.Value] = Version.Parse (version.Value);
}
return dict;
}
bool m_disposed = false;
public void Dispose ()
{
if (!m_disposed)
{
m_disposed = true;
}
GC.SuppressFinalize (this);
}
}
}

View File

@ -151,6 +151,7 @@
</MenuItem>
<MenuItem Header="{x:Static s:guiStrings.MenuHelp}">
<MenuItem Header="{x:Static s:guiStrings.MenuAbout}" Command="{x:Static local:Commands.About}"/>
<MenuItem Header="Check for updates..." Command="{x:Static local:Commands.CheckUpdates}"/>
</MenuItem>
</Menu>
<Separator Height="1" Margin="0"/>
@ -402,6 +403,7 @@
<CommandBinding Command="{x:Static local:Commands.HideMenuBar}" Executed="HideMenuBarExec" CanExecute="CanExecuteAlways"/>
<CommandBinding Command="{x:Static local:Commands.HideToolBar}" Executed="HideToolBarExec" CanExecute="CanExecuteAlways"/>
<CommandBinding Command="{x:Static local:Commands.About}" Executed="AboutExec" CanExecute="CanExecuteAlways"/>
<CommandBinding Command="{x:Static local:Commands.CheckUpdates}" Executed="CheckUpdatesExec" CanExecute="CanExecuteAlways"/>
<CommandBinding Command="{x:Static local:Commands.Exit}" Executed="ExitExec" CanExecute="CanExecuteAlways"/>
</Window.CommandBindings>
</Window>

View File

@ -64,6 +64,7 @@ namespace GARbro.GUI
if (this.Left < 0) this.Left = 0;
InitDirectoryChangesWatcher();
InitPreviewPane();
InitUpdatesChecker();
if (null == Settings.Default.appRecentFiles)
Settings.Default.appRecentFiles = new StringCollection();
@ -1478,6 +1479,7 @@ namespace GARbro.GUI
public static readonly RoutedCommand SortBy = new RoutedCommand();
public static readonly RoutedCommand Exit = new RoutedCommand();
public static readonly RoutedCommand About = new RoutedCommand();
public static readonly RoutedCommand CheckUpdates = new RoutedCommand();
public static readonly RoutedCommand GoBack = new RoutedCommand();
public static readonly RoutedCommand GoForward = new RoutedCommand();
public static readonly RoutedCommand DeleteItem = new RoutedCommand();

61
GUI/UpdateDialog.xaml Normal file
View File

@ -0,0 +1,61 @@
<!-- Game Resource browser
Copyright (C) 2017 by morkt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
-->
<Window x:Class="GARbro.GUI.UpdateDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:GARbro.GUI.Strings"
Title="Application update" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<DockPanel>
<DockPanel DockPanel.Dock="Bottom" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<Border BorderThickness="0,1,0,0" BorderBrush="Black">
<Button HorizontalAlignment="Right" Content="{x:Static s:guiStrings.ButtonOK}"
Margin="10" Width="75" Height="25" Click="Button_Click" IsCancel="True"/>
</Border>
</DockPanel>
<Image DockPanel.Dock="Left" Source="Images/64x64/actions.png" Width="32" Height="32" Margin="10"
SnapsToDevicePixels="True" VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality"/>
<StackPanel DockPanel.Dock="Right" Orientation="Vertical">
<StackPanel x:Name="ReleasePane" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="New version available:" Margin="0,10,0,0"/>
<TextBlock x:Name="ReleaseVersion" Text="{Binding ReleaseVersion}" Margin="5,10,10,0"/>
</StackPanel>
<Expander x:Name="ReleaseNotes" Header="Release notes" ExpandDirection="Down" IsExpanded="False" Margin="0,0,10,0">
<TextBlock Text="{Binding ReleaseNotes}"/>
</Expander>
<TextBlock Margin="0,5,10,10">
<Hyperlink NavigateUri="{Binding ReleaseUrl}" RequestNavigate="Hyperlink_RequestNavigate">
<TextBlock Text="Visit download page" ToolTip="{Binding ReleaseUrl}"/>
</Hyperlink>
</TextBlock>
</StackPanel>
<StackPanel x:Name="FormatsPane" Orientation="Vertical">
<Separator Visibility="{Binding ElementName=ReleasePane, Path=Visibility}"/>
<TextBlock Text="Formats database update available." Margin="0,10,10,0"/>
<Button x:Name="FormatsDownload" Content="_Download" MinWidth="75" Height="25" Margin="0,10,10,10"/>
</StackPanel>
</StackPanel>
</DockPanel>
</Window>

31
GUI/UpdateDialog.xaml.cs Normal file
View File

@ -0,0 +1,31 @@
using System.Windows;
namespace GARbro.GUI
{
/// <summary>
/// Interaction logic for UpdateDialog.xaml
/// </summary>
public partial class UpdateDialog : Window
{
public UpdateDialog (GarUpdateInfo info, bool enable_release, bool enable_formats)
{
InitializeComponent ();
this.ReleasePane.Visibility = enable_release ? Visibility.Visible : Visibility.Collapsed;
this.FormatsPane.Visibility = enable_formats ? Visibility.Visible : Visibility.Collapsed;
if (string.IsNullOrEmpty (info.ReleaseNotes))
this.ReleaseNotes.Visibility = Visibility.Collapsed;
this.DataContext = info;
}
private void Hyperlink_RequestNavigate (object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
if (App.NavigateUri (e.Uri))
e.Handled = true;
}
private void Button_Click (object sender, RoutedEventArgs e)
{
this.DialogResult = false;
}
}
}

27
docs/version.xml Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<GARbro>
<Release>
<Version>1.3.25</Version>
<Url>https://github.com/morkt/GARbro/releases/latest</Url>
<Notes>New formats:
* VNSystem VFS archives
* GD archives and VMD audio
* DPMX archives
* CDT archives
* TAC archives
* One-up ARC archives
* ARCG, BMX, MBF, VPK1, WVX0 and WSM archives
* cromwell 'Graphic PackData' archives
* AKB+ images
* more KiriKiri and ShiinaRio encryption schemes</Notes>
</Release>
<FormatsData>
<FileVersion>53</FileVersion>
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
<Requires>
<Assembly Name="ArcFormats" Version="1.2.29.1274"/>
<Assembly Name="GameRes" Version="1.4.26.238"/>
</Requires>
</FormatsData>
</GARbro>