mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-27 07:34:00 +08:00
implemented Majiro RCT images decryption.
This commit is contained in:
parent
28a57db9e6
commit
7fb26371e3
@ -127,12 +127,18 @@
|
||||
<Compile Include="ArcS25.cs" />
|
||||
<Compile Include="ArcSAF.cs" />
|
||||
<Compile Include="ArcVPK.cs" />
|
||||
<Compile Include="ArcXuse.cs" />
|
||||
<Compile Include="AudioVAW.cs" />
|
||||
<Compile Include="AudioWPN.cs" />
|
||||
<Compile Include="AudioWWA.cs" />
|
||||
<Compile Include="EriReader.cs" />
|
||||
<Compile Include="ImageABM.cs" />
|
||||
<Compile Include="ImageDWQ.cs" />
|
||||
<Compile Include="ImageHIZ.cs" />
|
||||
<Compile Include="MioDecoder.cs" />
|
||||
<Compile Include="WidgetRCT.xaml.cs">
|
||||
<DependentUpon>WidgetRCT.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ZLibStream.cs" />
|
||||
<None Include="ArcSeraph.cs" />
|
||||
<Compile Include="ArcSPack.cs" />
|
||||
@ -388,6 +394,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="WidgetRCT.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="WidgetWARC.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
@ -2,7 +2,7 @@
|
||||
//! \date Fri Aug 01 11:36:31 2014
|
||||
//! \brief RCT/RC8 image format implementation.
|
||||
//
|
||||
// Copyright (C) 2014 by morkt
|
||||
// Copyright (C) 2014-2015 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
|
||||
@ -29,9 +29,11 @@ using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Formats.Properties;
|
||||
using GameRes.Formats.Strings;
|
||||
using GameRes.Utility;
|
||||
|
||||
@ -46,6 +48,11 @@ namespace GameRes.Formats.Majiro
|
||||
public uint AddSize;
|
||||
}
|
||||
|
||||
internal class RctOptions : ResourceOptions
|
||||
{
|
||||
public string Password;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class RctFormat : ImageFormat
|
||||
{
|
||||
@ -53,6 +60,12 @@ namespace GameRes.Formats.Majiro
|
||||
public override string Description { get { return "Majiro game engine RGB image format"; } }
|
||||
public override uint Signature { get { return 0x9a925a98; } }
|
||||
|
||||
public static Dictionary<string, string> KnownKeys = new Dictionary<string, string> {
|
||||
{ "Akatsuki no Goei", "おぬぐり食べる?" },
|
||||
{ "Nagisa no", "青い空に向かって、溜息を一つこぼす。" },
|
||||
{ "White ~blanche comme la lune~", "たった3枚の紙" },
|
||||
};
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
stream.Position = 4;
|
||||
@ -99,6 +112,8 @@ namespace GameRes.Formats.Majiro
|
||||
}
|
||||
}
|
||||
|
||||
byte[] Key = null;
|
||||
|
||||
public override ImageData Read (Stream file, ImageMetaData info)
|
||||
{
|
||||
var meta = info as RctMetaData;
|
||||
@ -110,22 +125,83 @@ namespace GameRes.Formats.Majiro
|
||||
file.Position = meta.DataOffset;
|
||||
byte[] data = new byte[meta.AddSize];
|
||||
if (data.Length != file.Read (data, 0, data.Length))
|
||||
return null;
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
file.Position = meta.DataOffset + meta.AddSize;
|
||||
if (meta.IsEncrypted)
|
||||
{
|
||||
throw new NotImplementedException ("RCT image decryption is not implemented");
|
||||
if (null == Key)
|
||||
{
|
||||
var password = QueryPassword();
|
||||
if (string.IsNullOrEmpty (password))
|
||||
throw new UnknownEncryptionScheme();
|
||||
Key = InitDecryptionKey (password);
|
||||
}
|
||||
byte[] data = new byte[meta.DataSize];
|
||||
if (data.Length != file.Read (data, 0, data.Length))
|
||||
throw new EndOfStreamException();
|
||||
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
data[i] ^= Key[i & 0x3FF];
|
||||
}
|
||||
file = new MemoryStream (data);
|
||||
}
|
||||
using (var reader = new Reader (file, meta))
|
||||
try
|
||||
{
|
||||
reader.Unpack();
|
||||
byte[] pixels = reader.Data;
|
||||
var bitmap = BitmapSource.Create ((int)meta.Width, (int)meta.Height, 96, 96,
|
||||
PixelFormats.Bgr24, null, pixels, (int)meta.Width*3);
|
||||
bitmap.Freeze();
|
||||
return new ImageData (bitmap, meta);
|
||||
using (var reader = new Reader (file, meta))
|
||||
{
|
||||
reader.Unpack();
|
||||
return ImageData.Create (meta, PixelFormats.Bgr24, null, reader.Data, (int)meta.Width*3);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (meta.IsEncrypted)
|
||||
Key = null; // probably incorrect encryption scheme caused exception, reset key
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] InitDecryptionKey (string password)
|
||||
{
|
||||
byte[] bin_pass = Encodings.cp932.GetBytes (password);
|
||||
uint crc32 = Crc32.Compute (bin_pass, 0, bin_pass.Length);
|
||||
byte[] key_table = new byte[0x400];
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* key_ptr = key_table)
|
||||
{
|
||||
uint* key32 = (uint*)key_ptr;
|
||||
for (int i = 0; i < 0x100; ++i)
|
||||
*key32++ = crc32 ^ Crc32.Table[(i + crc32) & 0xFF];
|
||||
}
|
||||
}
|
||||
return key_table;
|
||||
}
|
||||
|
||||
private string QueryPassword ()
|
||||
{
|
||||
var options = Query<RctOptions> (arcStrings.RCTNotice);
|
||||
return options.Password;
|
||||
}
|
||||
|
||||
public override ResourceOptions GetDefaultOptions ()
|
||||
{
|
||||
return new RctOptions { Password = Settings.Default.RCTPassword };
|
||||
}
|
||||
|
||||
public override ResourceOptions GetOptions (object widget)
|
||||
{
|
||||
var w = widget as GUI.WidgetRCT;
|
||||
if (null != w)
|
||||
Settings.Default.RCTPassword = w.Password.Text;
|
||||
return GetDefaultOptions();
|
||||
}
|
||||
|
||||
public override object GetAccessWidget ()
|
||||
{
|
||||
return new GUI.WidgetRCT();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
|
24
ArcFormats/Properties/Settings.Designer.cs
generated
24
ArcFormats/Properties/Settings.Designer.cs
generated
@ -393,5 +393,29 @@ namespace GameRes.Formats.Properties {
|
||||
this["MCGLastKey"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string RCTPassword {
|
||||
get {
|
||||
return ((string)(this["RCTPassword"]));
|
||||
}
|
||||
set {
|
||||
this["RCTPassword"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string RCTTitle {
|
||||
get {
|
||||
return ((string)(this["RCTTitle"]));
|
||||
}
|
||||
set {
|
||||
this["RCTTitle"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,5 +95,11 @@
|
||||
<Setting Name="MCGLastKey" Type="System.Byte" Scope="User">
|
||||
<Value Profile="(Default)">0</Value>
|
||||
</Setting>
|
||||
<Setting Name="RCTPassword" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="RCTTitle" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
18
ArcFormats/Strings/arcStrings.Designer.cs
generated
18
ArcFormats/Strings/arcStrings.Designer.cs
generated
@ -524,6 +524,24 @@ namespace GameRes.Formats.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Choose title or enter a password.
|
||||
/// </summary>
|
||||
public static string RCTChoose {
|
||||
get {
|
||||
return ResourceManager.GetString("RCTChoose", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Image is encrypted..
|
||||
/// </summary>
|
||||
public static string RCTNotice {
|
||||
get {
|
||||
return ResourceManager.GetString("RCTNotice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Ren'Py game engine archive.
|
||||
/// </summary>
|
||||
|
@ -325,4 +325,10 @@ Choose encryption scheme or enter a passphrase.</value>
|
||||
<data name="ISFIgnoreEncryption" xml:space="preserve">
|
||||
<value>Ignore encryption</value>
|
||||
</data>
|
||||
<data name="RCTNotice" xml:space="preserve">
|
||||
<value>Image is encrypted.</value>
|
||||
</data>
|
||||
<data name="RCTChoose" xml:space="preserve">
|
||||
<value>Choose title or enter a password</value>
|
||||
</data>
|
||||
</root>
|
@ -245,6 +245,12 @@
|
||||
<data name="PDScrambleContents" xml:space="preserve">
|
||||
<value>Шифровать содержимое</value>
|
||||
</data>
|
||||
<data name="RCTChoose" xml:space="preserve">
|
||||
<value>Выберите наименование или введите пароль</value>
|
||||
</data>
|
||||
<data name="RCTNotice" xml:space="preserve">
|
||||
<value>Изображение зашифровано.</value>
|
||||
</data>
|
||||
<data name="RPALabelKey" xml:space="preserve">
|
||||
<value>32-битный ключ</value>
|
||||
</data>
|
||||
|
18
ArcFormats/WidgetRCT.xaml
Normal file
18
ArcFormats/WidgetRCT.xaml
Normal file
@ -0,0 +1,18 @@
|
||||
<Grid x:Class="GameRes.Formats.GUI.WidgetRCT"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:GameRes.Formats.Strings"
|
||||
xmlns:p="clr-namespace:GameRes.Formats.Properties"
|
||||
xmlns:majiro="clr-namespace:GameRes.Formats.Majiro">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Label Content="{x:Static s:arcStrings.RCTChoose}" Grid.Row="0" HorizontalAlignment="Left"/>
|
||||
<ComboBox Name="Title" ItemsSource="{Binding Source={x:Static majiro:RctFormat.KnownKeys}, Mode=OneWay}"
|
||||
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=RCTTitle, Mode=TwoWay}"
|
||||
SelectedValuePath="Key" DisplayMemberPath="Key" SelectionChanged="Title_SelectionChanged"
|
||||
Width="200" Grid.Row="1" HorizontalAlignment="Left"/>
|
||||
<TextBox Name="Password" Width="200" HorizontalAlignment="Left" Grid.Row="2" Margin="0,5,0,0"/>
|
||||
</Grid>
|
33
ArcFormats/WidgetRCT.xaml.cs
Normal file
33
ArcFormats/WidgetRCT.xaml.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Controls;
|
||||
using GameRes.Formats.Properties;
|
||||
|
||||
namespace GameRes.Formats.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for WidgetRCT.xaml
|
||||
/// </summary>
|
||||
public partial class WidgetRCT : Grid
|
||||
{
|
||||
public WidgetRCT ()
|
||||
{
|
||||
InitializeComponent ();
|
||||
this.Password.Text = Settings.Default.RCTPassword;
|
||||
if (null != this.Title.SelectedItem)
|
||||
{
|
||||
var selected = (KeyValuePair<string, string>)this.Title.SelectedItem;
|
||||
if (Settings.Default.RCTPassword != selected.Value)
|
||||
this.Title.SelectedIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void Title_SelectionChanged (object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (null != this.Title.SelectedItem && null != this.Password)
|
||||
{
|
||||
var selected = (KeyValuePair<string, string>)this.Title.SelectedItem;
|
||||
this.Password.Text = selected.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -97,6 +97,12 @@
|
||||
<setting name="MCGLastKey" serializeAs="String">
|
||||
<value>0</value>
|
||||
</setting>
|
||||
<setting name="RCTPassword" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="RCTTitle" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</GameRes.Formats.Properties.Settings>
|
||||
</userSettings>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
|
||||
|
Loading…
Reference in New Issue
Block a user