implemented Majiro RCT images decryption.

This commit is contained in:
morkt 2015-08-12 01:33:51 +04:00
parent 28a57db9e6
commit 7fb26371e3
10 changed files with 213 additions and 10 deletions

View File

@ -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>

View File

@ -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)

View File

@ -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;
}
}
}
}

View File

@ -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>

View File

@ -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&apos;Py game engine archive.
/// </summary>

View File

@ -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>

View File

@ -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
View 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>

View 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;
}
}
}
}

View File

@ -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>