Merge remote-tracking branch 'refs/remotes/origin/check-updates'

This commit is contained in:
morkt 2017-02-15 22:40:51 +04:00
commit ddf928f02a
17 changed files with 778 additions and 29 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
{
@ -37,9 +38,8 @@ namespace GARbro.GUI
/// </summary>
public partial class App : Application
{
const StringComparison StringIgnoreCase = StringComparison.OrdinalIgnoreCase;
public static string Name { get { return "GARbro"; } }
public static string Name { get { return "GARbro"; } }
public static string FormatsDat { get { return "Formats.dat"; } }
/// <summary>
/// Initial browsing directory.
@ -79,9 +79,24 @@ namespace GARbro.GUI
if (string.IsNullOrEmpty (InitPath))
InitPath = Directory.GetCurrentDirectory();
string scheme_file = Path.Combine (FormatCatalog.Instance.DataDirectory, "Formats.dat");
DeserializeScheme (Path.Combine (FormatCatalog.Instance.DataDirectory, FormatsDat));
DeserializeScheme (Path.Combine (GetLocalAppDataFolder(), FormatsDat));
}
public 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);
}
public 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 +131,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">

308
GUI/GarUpdate.cs Normal file
View File

@ -0,0 +1,308 @@
//! \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;
using GARbro.GUI.Strings;
using System.IO;
namespace GARbro.GUI
{
public partial class MainWindow : Window
{
GarUpdate m_updater;
private void InitUpdatesChecker ()
{
var update_url = App.Resources["UpdateUrl"] as Uri;
m_updater = new GarUpdate (this, update_url);
m_updater.CanExecuteChanged += (s, e) => CommandManager.InvalidateRequerySuggested();
}
public void CanExecuteUpdate (object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = m_updater.CanExecute (e.Parameter);
}
/// <summary>
/// Handle "Check for updates" command.
/// </summary>
private void CheckUpdatesExec (object sender, ExecutedRoutedEventArgs e)
{
m_updater.Execute (e.Parameter);
}
}
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; }
public static GarUpdateInfo Parse (XmlDocument xml)
{
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)
{
// XXX node?.InnerText ?? ""
return node != null ? node.InnerText : "";
}
static 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;
}
}
internal sealed class GarUpdate : ICommand, IDisposable
{
private readonly MainWindow m_main;
private readonly BackgroundWorker m_update_checker = new BackgroundWorker();
private readonly Uri m_url;
const int RequestTimeout = 20000; // milliseconds
public GarUpdate (MainWindow main, Uri url)
{
m_main = main;
m_url = url;
m_update_checker.DoWork += StartUpdatesCheck;
m_update_checker.RunWorkerCompleted += UpdatesCheckComplete;
}
public void Execute (object parameter)
{
if (!m_update_checker.IsBusy)
m_update_checker.RunWorkerAsync();
}
public bool CanExecute (object parameter)
{
return !m_update_checker.IsBusy;
}
public event EventHandler CanExecuteChanged;
void OnCanExecuteChanged ()
{
var handler = CanExecuteChanged;
if (handler != null)
handler (this, EventArgs.Empty);
}
private void StartUpdatesCheck (object sender, DoWorkEventArgs e)
{
OnCanExecuteChanged();
if (m_url != null)
e.Result = Check (m_url);
}
private void UpdatesCheckComplete (object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (e.Error != null)
{
m_main.SetStatusText (string.Format ("{0} {1}", guiStrings.MsgUpdateFailed, e.Error.Message));
return;
}
else if (e.Cancelled)
return;
var result = e.Result as GarUpdateInfo;
if (null == result)
{
m_main.SetStatusText (guiStrings.MsgNoUpdates);
return;
}
ShowUpdateResult (result);
}
finally
{
OnCanExecuteChanged();
}
}
UpdateDialog m_dialog;
Uri m_formats_url;
private void ShowUpdateResult (GarUpdateInfo result)
{
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)
{
m_main.SetStatusText (guiStrings.MsgUpToDate);
return;
}
m_formats_url = result.FormatsUrl;
m_dialog = new UpdateDialog (result, has_app_update, has_db_update);
m_dialog.Owner = m_main;
m_dialog.FormatsDownload.Click += StartFormatsDownload;
m_dialog.ShowDialog();
}
private async void StartFormatsDownload (object control, RoutedEventArgs e)
{
var dialog = m_dialog;
try
{
dialog.FormatsDownload.IsEnabled = false;
var app_data_folder = m_main.App.GetLocalAppDataFolder();
Directory.CreateDirectory (app_data_folder);
using (var client = new WebClientEx())
using (var tmp_file = new GARbro.Shell.TemporaryFile (app_data_folder, Path.GetRandomFileName()))
{
client.Timeout = RequestTimeout;
await client.DownloadFileTaskAsync (m_formats_url, tmp_file.Name);
m_main.App.DeserializeScheme (tmp_file.Name);
var local_formats_dat = Path.Combine (app_data_folder, App.FormatsDat);
if (!GARbro.Shell.File.Rename (tmp_file.Name, local_formats_dat))
throw new Win32Exception (GARbro.Shell.File.GetLastError());
}
SetFormatsUpdateStatus (dialog, guiStrings.MsgUpdateComplete);
}
catch (Exception X)
{
SetFormatsUpdateStatus (dialog, guiStrings.MsgDownloadFailed, X.Message);
}
finally
{
dialog.FormatsDownload.Visibility = Visibility.Hidden;
}
}
void SetFormatsUpdateStatus (UpdateDialog dialog, string text1, string text2 = null)
{
if (dialog.IsClosed)
m_main.SetStatusText (text1);
else if (null == text2)
dialog.FormatsUpdateText.Text = text1;
else
dialog.FormatsUpdateText.Text = string.Format ("{0}\n{1}", text1, text2);
}
/// <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;
}
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);
return GarUpdateInfo.Parse (xml);
}
}
bool m_disposed = false;
public void Dispose ()
{
if (!m_disposed)
{
m_update_checker.Dispose();
m_disposed = true;
}
GC.SuppressFinalize (this);
}
}
/// <summary>
/// WebClient with timeout setting.
/// </summary>
internal class WebClientEx : WebClient
{
/// <summary>
/// Request timeout, in milliseconds.
/// </summary>
public int Timeout { get; set; }
public WebClientEx ()
{
Timeout = 60000;
}
protected override WebRequest GetWebRequest (Uri uri)
{
var request = base.GetWebRequest (uri);
request.Timeout = Timeout;
return request;
}
}
}

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="{x:Static s:guiStrings.MenuCheckUpdates}" 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="CanExecuteUpdate"/>
<CommandBinding Command="{x:Static local:Commands.Exit}" Executed="ExitExec" CanExecute="CanExecuteAlways"/>
</Window.CommandBindings>
</Window>

View File

@ -54,6 +54,8 @@ namespace GARbro.GUI
{
private App m_app;
public App App { get { return m_app; } }
const StringComparison StringIgnoreCase = StringComparison.CurrentCultureIgnoreCase;
public MainWindow()
@ -64,6 +66,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 +1481,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();

View File

@ -96,6 +96,15 @@ namespace GARbro.GUI.Strings {
}
}
/// <summary>
/// Looks up a localized string similar to _Download.
/// </summary>
public static string ButtonDownload {
get {
return ResourceManager.GetString("ButtonDownload", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Extract.
/// </summary>
@ -456,6 +465,15 @@ namespace GARbro.GUI.Strings {
}
}
/// <summary>
/// Looks up a localized string similar to Release notes.
/// </summary>
public static string LabelReleaseNotes {
get {
return ResourceManager.GetString("LabelReleaseNotes", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Skip incovertible files..
/// </summary>
@ -474,6 +492,15 @@ namespace GARbro.GUI.Strings {
}
}
/// <summary>
/// Looks up a localized string similar to _Check for updates....
/// </summary>
public static string MenuCheckUpdates {
get {
return ResourceManager.GetString("MenuCheckUpdates", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to E_xit.
/// </summary>
@ -636,6 +663,15 @@ namespace GARbro.GUI.Strings {
}
}
/// <summary>
/// Looks up a localized string similar to Update download failed..
/// </summary>
public static string MsgDownloadFailed {
get {
return ResourceManager.GetString("MsgDownloadFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to archive is empty.
/// </summary>
@ -771,6 +807,15 @@ namespace GARbro.GUI.Strings {
}
}
/// <summary>
/// Looks up a localized string similar to No updates currently available..
/// </summary>
public static string MsgNoUpdates {
get {
return ResourceManager.GetString("MsgNoUpdates", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to File {0}
///already exists.
@ -837,6 +882,42 @@ namespace GARbro.GUI.Strings {
}
}
/// <summary>
/// Looks up a localized string similar to Formats database update available..
/// </summary>
public static string MsgUpdateAvailable {
get {
return ResourceManager.GetString("MsgUpdateAvailable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Formats database updated..
/// </summary>
public static string MsgUpdateComplete {
get {
return ResourceManager.GetString("MsgUpdateComplete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update check failed..
/// </summary>
public static string MsgUpdateFailed {
get {
return ResourceManager.GetString("MsgUpdateFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to GARbro version is up to date..
/// </summary>
public static string MsgUpToDate {
get {
return ResourceManager.GetString("MsgUpToDate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Version {0}.
/// </summary>
@ -1093,6 +1174,15 @@ namespace GARbro.GUI.Strings {
}
}
/// <summary>
/// Looks up a localized string similar to New version available:.
/// </summary>
public static string TextNewVersion {
get {
return ResourceManager.GetString("TextNewVersion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Archive parameters.
/// </summary>
@ -1138,6 +1228,24 @@ namespace GARbro.GUI.Strings {
}
}
/// <summary>
/// Looks up a localized string similar to Application update.
/// </summary>
public static string TextUpdateTitle {
get {
return ResourceManager.GetString("TextUpdateTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Visit download page.
/// </summary>
public static string TextVisitPage {
get {
return ResourceManager.GetString("TextVisitPage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Either WAV, MP3 or OGG.
/// </summary>

View File

@ -493,4 +493,52 @@
<data name="TextFileAlreadyExists" xml:space="preserve">
<value>대상폴더에 파일 {0} 이(가) 이미 존재합니다.</value>
</data>
</root>
<data name="ButtonDownload" xml:space="preserve">
<value>Download</value>
<comment>translation pending</comment>
</data>
<data name="LabelReleaseNotes" xml:space="preserve">
<value>Release notes</value>
<comment>translation pending</comment>
</data>
<data name="MenuCheckUpdates" xml:space="preserve">
<value>Check for updates...</value>
<comment>translation pending</comment>
</data>
<data name="MsgDownloadFailed" xml:space="preserve">
<value>Update download failed.</value>
<comment>translation pending</comment>
</data>
<data name="MsgNoUpdates" xml:space="preserve">
<value>No updates currently available.</value>
<comment>translation pending</comment>
</data>
<data name="MsgUpdateAvailable" xml:space="preserve">
<value>Formats database update available.</value>
<comment>translation pending</comment>
</data>
<data name="MsgUpdateComplete" xml:space="preserve">
<value>Formats database updated.</value>
<comment>translation pending</comment>
</data>
<data name="MsgUpdateFailed" xml:space="preserve">
<value>Update check failed.</value>
<comment>translation pending</comment>
</data>
<data name="MsgUpToDate" xml:space="preserve">
<value>GARbro version is up to date.</value>
<comment>translation pending</comment>
</data>
<data name="TextNewVersion" xml:space="preserve">
<value>New version available:</value>
<comment>translation pending</comment>
</data>
<data name="TextUpdateTitle" xml:space="preserve">
<value>Application update</value>
<comment>translation pending</comment>
</data>
<data name="TextVisitPage" xml:space="preserve">
<value>Visit download page</value>
<comment>translation pending</comment>
</data>
</root>

View File

@ -497,4 +497,40 @@ Overwrite?</value>
<data name="TextFileAlreadyExists" xml:space="preserve">
<value>File {0} already exists in the destination folder.</value>
</data>
<data name="ButtonDownload" xml:space="preserve">
<value>_Download</value>
</data>
<data name="LabelReleaseNotes" xml:space="preserve">
<value>Release notes</value>
</data>
<data name="MenuCheckUpdates" xml:space="preserve">
<value>_Check for updates...</value>
</data>
<data name="MsgNoUpdates" xml:space="preserve">
<value>No updates currently available.</value>
</data>
<data name="MsgUpdateAvailable" xml:space="preserve">
<value>Formats database update available.</value>
</data>
<data name="MsgUpdateComplete" xml:space="preserve">
<value>Formats database updated.</value>
</data>
<data name="MsgUpdateFailed" xml:space="preserve">
<value>Update check failed.</value>
</data>
<data name="MsgUpToDate" xml:space="preserve">
<value>GARbro version is up to date.</value>
</data>
<data name="TextNewVersion" xml:space="preserve">
<value>New version available:</value>
</data>
<data name="TextUpdateTitle" xml:space="preserve">
<value>Application update</value>
</data>
<data name="TextVisitPage" xml:space="preserve">
<value>Visit download page</value>
</data>
<data name="MsgDownloadFailed" xml:space="preserve">
<value>Update download failed.</value>
</data>
</root>

View File

@ -518,4 +518,40 @@
<data name="TextFileAlreadyExists" xml:space="preserve">
<value>Файл с именем {0} уже существует.</value>
</data>
<data name="ButtonDownload" xml:space="preserve">
<value>Обновить</value>
</data>
<data name="LabelReleaseNotes" xml:space="preserve">
<value>Примечания к выпуску</value>
</data>
<data name="MenuCheckUpdates" xml:space="preserve">
<value>Проверить обновления...</value>
</data>
<data name="MsgDownloadFailed" xml:space="preserve">
<value>Не удалось обновить базу форматов.</value>
</data>
<data name="MsgNoUpdates" xml:space="preserve">
<value>Обновления недоступны.</value>
</data>
<data name="MsgUpdateAvailable" xml:space="preserve">
<value>Доступна обновлённая база форматов.</value>
</data>
<data name="MsgUpdateComplete" xml:space="preserve">
<value>База форматов успешно обновлена.</value>
</data>
<data name="MsgUpdateFailed" xml:space="preserve">
<value>Сбой проверки обновлений.</value>
</data>
<data name="MsgUpToDate" xml:space="preserve">
<value>Установлена актуальная версия GARbro.</value>
</data>
<data name="TextNewVersion" xml:space="preserve">
<value>Доступна новая версия:</value>
</data>
<data name="TextUpdateTitle" xml:space="preserve">
<value>Обновление программы</value>
</data>
<data name="TextVisitPage" xml:space="preserve">
<value>Перейти на страницу загрузки</value>
</data>
</root>

View File

@ -505,4 +505,52 @@
<data name="TextFileAlreadyExists" xml:space="preserve">
<value>文件{0}已经存在。</value>
</data>
<data name="ButtonDownload" xml:space="preserve">
<value>Download</value>
<comment>translation pending</comment>
</data>
<data name="LabelReleaseNotes" xml:space="preserve">
<value>Release notes</value>
<comment>translation pending</comment>
</data>
<data name="MenuCheckUpdates" xml:space="preserve">
<value>Check for updates...</value>
<comment>translation pending</comment>
</data>
<data name="MsgDownloadFailed" xml:space="preserve">
<value>Update download failed.</value>
<comment>translation pending</comment>
</data>
<data name="MsgNoUpdates" xml:space="preserve">
<value>No updates currently available.</value>
<comment>translation pending</comment>
</data>
<data name="MsgUpdateAvailable" xml:space="preserve">
<value>Formats database update available.</value>
<comment>translation pending</comment>
</data>
<data name="MsgUpdateComplete" xml:space="preserve">
<value>Formats database updated.</value>
<comment>translation pending</comment>
</data>
<data name="MsgUpdateFailed" xml:space="preserve">
<value>Update check failed.</value>
<comment>translation pending</comment>
</data>
<data name="MsgUpToDate" xml:space="preserve">
<value>GARbro version is up to date.</value>
<comment>translation pending</comment>
</data>
<data name="TextNewVersion" xml:space="preserve">
<value>New version available:</value>
<comment>translation pending</comment>
</data>
<data name="TextUpdateTitle" xml:space="preserve">
<value>Application update</value>
<comment>translation pending</comment>
</data>
<data name="TextVisitPage" xml:space="preserve">
<value>Visit download page</value>
<comment>translation pending</comment>
</data>
</root>

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="{x:Static s:guiStrings.TextUpdateTitle}" 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,20"
SnapsToDevicePixels="True" VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality"/>
<StackPanel DockPanel.Dock="Right" Orientation="Vertical">
<StackPanel x:Name="ReleasePane" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Static s:guiStrings.TextNewVersion}" Margin="0,10,0,0"/>
<TextBlock x:Name="ReleaseVersion" Text="{Binding ReleaseVersion}" Margin="5,10,10,0"/>
</StackPanel>
<Expander x:Name="ReleaseNotes" Header="{x:Static s:guiStrings.LabelReleaseNotes}" 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="{x:Static s:guiStrings.TextVisitPage}" ToolTip="{Binding ReleaseUrl}"/>
</Hyperlink>
</TextBlock>
</StackPanel>
<StackPanel x:Name="FormatsPane" Orientation="Vertical">
<Separator Visibility="{Binding ElementName=ReleasePane, Path=Visibility}"/>
<TextBlock x:Name="FormatsUpdateText" Text="{x:Static s:guiStrings.MsgUpdateAvailable}" Margin="0,10,10,10"/>
<Button x:Name="FormatsDownload" Content="{x:Static s:guiStrings.ButtonDownload}" Width="75" Height="25" Margin="0,0,10,10" HorizontalAlignment="Left"/>
</StackPanel>
</StackPanel>
</DockPanel>
</Window>

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

@ -0,0 +1,34 @@
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;
this.Closed += (s, e) => IsClosed = true;
}
public bool IsClosed { get; private set; }
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;
}
}
}

View File

@ -254,15 +254,9 @@ namespace GameRes
public void DeserializeScheme (Stream input)
{
using (var reader = new BinaryReader (input, System.Text.Encoding.UTF8, true))
{
var header = reader.ReadChars (SchemeID.Length);
if (!header.SequenceEqual (SchemeID))
throw new FormatException ("Invalid serialization file");
int version = reader.ReadInt32();
if (version <= CurrentSchemeVersion)
return;
}
int version = GetSerializedSchemeVersion (input);
if (version <= CurrentSchemeVersion)
return;
using (var zs = new ZLibStream (input, CompressionMode.Decompress, true))
{
var bin = new BinaryFormatter();
@ -307,6 +301,17 @@ namespace GameRes
using (var zs = new ZLibStream (output, CompressionMode.Compress, true))
bin.Serialize (zs, db);
}
public int GetSerializedSchemeVersion (Stream input)
{
using (var reader = new BinaryReader (input, System.Text.Encoding.UTF8, true))
{
var header = reader.ReadChars (SchemeID.Length);
if (!header.SequenceEqual (SchemeID))
throw new FormatException ("Invalid serialization file");
return reader.ReadInt32();
}
}
}
[Serializable]

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>