From f367b7f976016daa42d01907e1b9c558f72fa03e Mon Sep 17 00:00:00 2001 From: suloku Date: Wed, 2 Mar 2016 18:06:05 +0100 Subject: [PATCH] First commit: - BW/BW2 save file detection - Checksum verifier (no DLC yet) - Checksum updater (no DLC yet) - Block dumper /injector (only first save) - Block dumper decryption is hardcoded for entralink forest block right now Todo: - Add BW offset tables - Add per-block decryption with known decrypted blocks - Add boxes to specify encryption address, size and seed address (for use without editing source) - Add BW2 DLC blocks - Insert hidden grotto and swarm form - A lot more... --- BW_tool.sln | 18 ++ BW_tool/BW_tool.csproj | 82 +++++++ BW_tool/Dumper.Designer.cs | 193 +++++++++++++++++ BW_tool/Dumper.cs | 86 ++++++++ BW_tool/Dumper.resx | 120 +++++++++++ BW_tool/MainForm.Designer.cs | 125 +++++++++++ BW_tool/MainForm.cs | 74 +++++++ BW_tool/MainForm.resx | 120 +++++++++++ BW_tool/Program.cs | 31 +++ BW_tool/Properties/AssemblyInfo.cs | 31 +++ BW_tool/SAV/FileIO.cs | 98 +++++++++ BW_tool/SAV/PKX5.cs | 103 +++++++++ BW_tool/SAV/SAV5.cs | 329 +++++++++++++++++++++++++++++ BW_tool/app.config | 6 + 14 files changed, 1416 insertions(+) create mode 100644 BW_tool.sln create mode 100644 BW_tool/BW_tool.csproj create mode 100644 BW_tool/Dumper.Designer.cs create mode 100644 BW_tool/Dumper.cs create mode 100644 BW_tool/Dumper.resx create mode 100644 BW_tool/MainForm.Designer.cs create mode 100644 BW_tool/MainForm.cs create mode 100644 BW_tool/MainForm.resx create mode 100644 BW_tool/Program.cs create mode 100644 BW_tool/Properties/AssemblyInfo.cs create mode 100644 BW_tool/SAV/FileIO.cs create mode 100644 BW_tool/SAV/PKX5.cs create mode 100644 BW_tool/SAV/SAV5.cs create mode 100644 BW_tool/app.config diff --git a/BW_tool.sln b/BW_tool.sln new file mode 100644 index 0000000..94fe3a0 --- /dev/null +++ b/BW_tool.sln @@ -0,0 +1,18 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +# SharpDevelop 5.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BW_tool", "BW_tool\BW_tool.csproj", "{41ABAC38-D34B-489D-A450-3E9FADD07419}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {41ABAC38-D34B-489D-A450-3E9FADD07419}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41ABAC38-D34B-489D-A450-3E9FADD07419}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41ABAC38-D34B-489D-A450-3E9FADD07419}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41ABAC38-D34B-489D-A450-3E9FADD07419}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/BW_tool/BW_tool.csproj b/BW_tool/BW_tool.csproj new file mode 100644 index 0000000..003fa3e --- /dev/null +++ b/BW_tool/BW_tool.csproj @@ -0,0 +1,82 @@ + + + + {41ABAC38-D34B-489D-A450-3E9FADD07419} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Debug + AnyCPU + WinExe + BW_tool + BW_tool + v4.0 + Properties + + + x86 + + + bin\Debug\ + True + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + 4.0 + + + + 3.5 + + + + 3.5 + + + + + + 3.5 + + + + + + Dumper.cs + + + + MainForm.cs + + + + + + + + + + + + + + + + Dumper.cs + + + MainForm.cs + + + + \ No newline at end of file diff --git a/BW_tool/Dumper.Designer.cs b/BW_tool/Dumper.Designer.cs new file mode 100644 index 0000000..94af06e --- /dev/null +++ b/BW_tool/Dumper.Designer.cs @@ -0,0 +1,193 @@ +/* + * Created by SharpDevelop. + * User: suloku + * Date: 02/03/2016 + * Time: 0:19 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +namespace BW_tool +{ + partial class Dumper + { + /// + /// Designer variable used to keep track of non-visual components. + /// + private System.ComponentModel.IContainer components = null; + private System.Windows.Forms.Button dump_but; + private System.Windows.Forms.Button inject_but; + private System.Windows.Forms.Button dump_dec_but; + private System.Windows.Forms.Button inject_crypt_but; + private System.Windows.Forms.ComboBox selectedblock; + + /// + /// Disposes resources used by the form. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + private void InitializeComponent() + { + this.dump_but = new System.Windows.Forms.Button(); + this.inject_but = new System.Windows.Forms.Button(); + this.dump_dec_but = new System.Windows.Forms.Button(); + this.inject_crypt_but = new System.Windows.Forms.Button(); + this.selectedblock = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // dump_but + // + this.dump_but.Location = new System.Drawing.Point(254, 39); + this.dump_but.Name = "dump_but"; + this.dump_but.Size = new System.Drawing.Size(75, 23); + this.dump_but.TabIndex = 1; + this.dump_but.Text = "Dump"; + this.dump_but.UseVisualStyleBackColor = true; + this.dump_but.Click += new System.EventHandler(this.Dump_butClick); + // + // inject_but + // + this.inject_but.Location = new System.Drawing.Point(353, 39); + this.inject_but.Name = "inject_but"; + this.inject_but.Size = new System.Drawing.Size(75, 23); + this.inject_but.TabIndex = 2; + this.inject_but.Text = "Inject"; + this.inject_but.UseVisualStyleBackColor = true; + this.inject_but.Click += new System.EventHandler(this.Inject_butClick); + // + // dump_dec_but + // + this.dump_dec_but.Location = new System.Drawing.Point(204, 68); + this.dump_dec_but.Name = "dump_dec_but"; + this.dump_dec_but.Size = new System.Drawing.Size(125, 23); + this.dump_dec_but.TabIndex = 3; + this.dump_dec_but.Text = "Dump and Decrypt"; + this.dump_dec_but.UseVisualStyleBackColor = true; + this.dump_dec_but.Click += new System.EventHandler(this.Dump_dec_butClick); + // + // inject_crypt_but + // + this.inject_crypt_but.Location = new System.Drawing.Point(353, 68); + this.inject_crypt_but.Name = "inject_crypt_but"; + this.inject_crypt_but.Size = new System.Drawing.Size(125, 23); + this.inject_crypt_but.TabIndex = 4; + this.inject_crypt_but.Text = "Inject and Recrypt"; + this.inject_crypt_but.UseVisualStyleBackColor = true; + this.inject_crypt_but.Click += new System.EventHandler(this.Inject_crypt_butClick); + // + // selectedblock + // + this.selectedblock.FormattingEnabled = true; + this.selectedblock.Items.AddRange(new object[] { + "00 - Box Names", + "01 - Box 1", + "02 - Box 2", + "03 - Box 3", + "04 - Box 4", + "05 - Box 5", + "06 - Box 6", + "07 - Box 7", + "08 - Box 8", + "09 - Box 9", + "10 - Box 10", + "11 - Box 11", + "12 - Box 12", + "13 - Box 13", + "14 - Box 14", + "15 - Box 15", + "16 - Box 16", + "17 - Box 17", + "18 - Box 18", + "19 - Box 19", + "20 - Box 20", + "21 - Box 21", + "22 - Box 22", + "23 - Box 23", + "24 - Box 24", + "25 - Inventory", + "26 - Party Pokemon", + "27 - Trainer Data", + "28 - Trainer Position", + "29 - Unity Tower and survey stuff", + "30 - Pal Pad Player Data (30d)", + "31 - Pal Pad Friend Data", + "32 - C-Gear", + "33 - Card Signature Block & ????", + "34 - Mystery Gift", + "35 - Dream World Stuff (Catalog)", + "36 - Chatter", + "37 - Adventure data http://projectpokemon.org/forums/showthread.php?24589-B2W2-Ge" + + "neral-ROM-Info&p=167300&viewfull=1#post167300", + "38 - Trainer Card Records", + "39 - Unknown", + "40 - (40d)", + "41 - Unknown", + "42 - Contains flags and references for downloaded data (Musical)", + "43 - Fused Reshiram/Zekrom Storage", + "44 - Unknown", + "45 - Const Data Block and Event Flag Block (0x35E is the split)", + "46 - Unknown", + "47 - Tournament Block", + "48 - Unknown", + "49 - Battle Box Block", + "50 - Daycare Block (50d)", + "51 - Strength Boulder Status Block", + "52 - Badge Flags, Money, Trainer Sayings", + "53 - Entralink (Level & Powers etc)", + "54 - Pokedex", + "55 - Swarm and other overworld info - 2C - swarm, 2D - repel steps, 2E repel type" + + "", + "56 - Unknown", + "57 - Unknown", + "58 - Unknown", + "59 - Online Records", + "60 - Entralink Forest - encrypted (60d)", + "61 - Unknown", + "62 - Unknown", + "63 - Unknown", + "64 - Unknown", + "65 - Unknown", + "66 - Hollow/Rival Block", + "67 - Join Avenue Block", + "68 - Medal data", + "69 - Key-related data", + "70 - Unknown (70d)", + "71 - Unknown", + "72 - Unknown", + "73 - Checksum Block (73d)"}); + this.selectedblock.Location = new System.Drawing.Point(12, 12); + this.selectedblock.MaxDropDownItems = 10; + this.selectedblock.Name = "selectedblock"; + this.selectedblock.Size = new System.Drawing.Size(692, 21); + this.selectedblock.TabIndex = 5; + // + // Dumper + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(716, 103); + this.Controls.Add(this.selectedblock); + this.Controls.Add(this.inject_crypt_but); + this.Controls.Add(this.dump_dec_but); + this.Controls.Add(this.inject_but); + this.Controls.Add(this.dump_but); + this.Name = "Dumper"; + this.Text = "Block Dumper&Injector"; + this.ResumeLayout(false); + + } + } +} diff --git a/BW_tool/Dumper.cs b/BW_tool/Dumper.cs new file mode 100644 index 0000000..9e75257 --- /dev/null +++ b/BW_tool/Dumper.cs @@ -0,0 +1,86 @@ +/* + * Created by SharpDevelop. + * User: suloku + * Date: 02/03/2016 + * Time: 0:19 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; + +namespace BW_tool +{ + /// + /// Description of Form1. + /// + public partial class Dumper : Form + { + public Dumper() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + selectedblock.SelectedIndex = 0; + selectedblock.DropDownStyle = ComboBoxStyle.DropDownList; + + // + // TODO: Add constructor code after the InitializeComponent() call. + // + } + public string binaryfilter = "Binary data|*.bin|All Files (*.*)|*.*"; + void Dump_butClick(object sender, EventArgs e) + { + SaveFileDialog saveFD = new SaveFileDialog(); + //saveFD.InitialDirectory = "c:\\"; + saveFD.Filter = binaryfilter; + if (saveFD.ShowDialog() == DialogResult.OK) + { + System.IO.FileStream saveFile; + saveFile = new FileStream(saveFD.FileName, FileMode.Create); + //Write file + saveFile.Write(MainForm.save.getBlock((int)selectedblock.SelectedIndex), 0, MainForm.save.getBlockLength((int)selectedblock.SelectedIndex)); + saveFile.Close(); + MessageBox.Show("File Saved.", "Save file"); + } + } + public byte[] injectfile; + void Inject_butClick(object sender, EventArgs e) + { + string path = null; + FileIO.load_file(ref injectfile, ref path, binaryfilter); + MainForm.save.setBlock( injectfile, (int)selectedblock.SelectedIndex); + //MessageBox.Show(injectfile.Length.ToString()); + } + void Dump_dec_butClick(object sender, EventArgs e) + { + SaveFileDialog saveFD = new SaveFileDialog(); + //saveFD.InitialDirectory = "c:\\"; + saveFD.Filter = binaryfilter; + if (saveFD.ShowDialog() == DialogResult.OK) + { + System.IO.FileStream saveFile; + saveFile = new FileStream(saveFD.FileName, FileMode.Create); + //Write file + saveFile.Write(MainForm.save.getBlockDec((int)selectedblock.SelectedIndex), 0, MainForm.save.getBlockLength((int)selectedblock.SelectedIndex)); + saveFile.Close(); + MessageBox.Show("File Saved.", "Save file"); + } + } + void Inject_crypt_butClick(object sender, EventArgs e) + { + string path = null; + FileIO.load_file(ref injectfile, ref path, binaryfilter); + MainForm.save.setBlockCrypt( injectfile, (int)selectedblock.SelectedIndex); + //MessageBox.Show(injectfile.Length.ToString()); + } + } +} diff --git a/BW_tool/Dumper.resx b/BW_tool/Dumper.resx new file mode 100644 index 0000000..29dcb1b --- /dev/null +++ b/BW_tool/Dumper.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BW_tool/MainForm.Designer.cs b/BW_tool/MainForm.Designer.cs new file mode 100644 index 0000000..f5b006d --- /dev/null +++ b/BW_tool/MainForm.Designer.cs @@ -0,0 +1,125 @@ +/* + * Created by SharpDevelop. + * User: suloku + * Date: 01/03/2016 + * Time: 20:03 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +namespace BW_tool +{ + partial class MainForm + { + /// + /// Designer variable used to keep track of non-visual components. + /// + private System.ComponentModel.IContainer components = null; + private System.Windows.Forms.Button loadsave_but; + private System.Windows.Forms.TextBox savegamename; + private System.Windows.Forms.Button save_but; + private System.Windows.Forms.Label versiontext; + private System.Windows.Forms.Button dumper_but; + private System.Windows.Forms.Button chk_but; + + /// + /// Disposes resources used by the form. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + private void InitializeComponent() + { + this.loadsave_but = new System.Windows.Forms.Button(); + this.savegamename = new System.Windows.Forms.TextBox(); + this.save_but = new System.Windows.Forms.Button(); + this.versiontext = new System.Windows.Forms.Label(); + this.dumper_but = new System.Windows.Forms.Button(); + this.chk_but = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // loadsave_but + // + this.loadsave_but.Location = new System.Drawing.Point(12, 12); + this.loadsave_but.Name = "loadsave_but"; + this.loadsave_but.Size = new System.Drawing.Size(132, 23); + this.loadsave_but.TabIndex = 0; + this.loadsave_but.Text = "Load Savegame"; + this.loadsave_but.UseVisualStyleBackColor = true; + this.loadsave_but.Click += new System.EventHandler(this.Loadsave_butClick); + // + // savegamename + // + this.savegamename.Location = new System.Drawing.Point(12, 41); + this.savegamename.Name = "savegamename"; + this.savegamename.Size = new System.Drawing.Size(484, 20); + this.savegamename.TabIndex = 1; + // + // save_but + // + this.save_but.Location = new System.Drawing.Point(421, 12); + this.save_but.Name = "save_but"; + this.save_but.Size = new System.Drawing.Size(75, 23); + this.save_but.TabIndex = 2; + this.save_but.Text = "Save"; + this.save_but.UseVisualStyleBackColor = true; + this.save_but.Click += new System.EventHandler(this.Save_butClick); + // + // versiontext + // + this.versiontext.Location = new System.Drawing.Point(150, 17); + this.versiontext.Name = "versiontext"; + this.versiontext.Size = new System.Drawing.Size(100, 18); + this.versiontext.TabIndex = 3; + // + // dumper_but + // + this.dumper_but.Location = new System.Drawing.Point(12, 80); + this.dumper_but.Name = "dumper_but"; + this.dumper_but.Size = new System.Drawing.Size(92, 23); + this.dumper_but.TabIndex = 4; + this.dumper_but.Text = "Block dumper"; + this.dumper_but.UseVisualStyleBackColor = true; + this.dumper_but.Click += new System.EventHandler(this.Dumper_butClick); + // + // chk_but + // + this.chk_but.Location = new System.Drawing.Point(316, 12); + this.chk_but.Name = "chk_but"; + this.chk_but.Size = new System.Drawing.Size(99, 23); + this.chk_but.TabIndex = 5; + this.chk_but.Text = "Verify Checksums"; + this.chk_but.UseVisualStyleBackColor = true; + this.chk_but.Click += new System.EventHandler(this.Chk_butClick); + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(503, 119); + this.Controls.Add(this.chk_but); + this.Controls.Add(this.dumper_but); + this.Controls.Add(this.versiontext); + this.Controls.Add(this.save_but); + this.Controls.Add(this.savegamename); + this.Controls.Add(this.loadsave_but); + this.Name = "MainForm"; + this.Text = "Gen V save tool"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + } +} diff --git a/BW_tool/MainForm.cs b/BW_tool/MainForm.cs new file mode 100644 index 0000000..f11f7dc --- /dev/null +++ b/BW_tool/MainForm.cs @@ -0,0 +1,74 @@ +/* + * Created by SharpDevelop. + * User: suloku + * Date: 01/03/2016 + * Time: 20:03 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; + +namespace BW_tool +{ + /// + /// Description of MainForm. + /// + public partial class MainForm : Form + { + public MainForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + + // + // TODO: Add constructor code after the InitializeComponent() call. + // + } + public string dsfilter = "NDS save data|*.sav;*.dsv|All Files (*.*)|*.*"; + public byte[] savebuffer; + public static SAV5 save; + + + void Loadsave_butClick(object sender, EventArgs e) + { + versiontext.Text = ""; + string path = null; + if(FileIO.load_file(ref savebuffer, ref path, dsfilter) == 1) + { + savegamename.Text = path; + save = new SAV5(savebuffer); + if (save.B2W2) versiontext.Text = "Black/White 2"; + else if (save.BW) versiontext.Text = "Black/White 1"; + else versiontext.Text = "Invalid file"; + + }else{ + savegamename.Text = ""; + } + } + void Save_butClick(object sender, EventArgs e) + { + if (save.Edited) + FileIO.save_data(save.Data); + else MessageBox.Show("Save has not been edited"); + } + void Dumper_butClick(object sender, EventArgs e) + { + Form dumper = new Dumper(); + dumper.Show(); + } + void Chk_butClick(object sender, EventArgs e) + { + save.chkCheck(); + } + } +} diff --git a/BW_tool/MainForm.resx b/BW_tool/MainForm.resx new file mode 100644 index 0000000..29dcb1b --- /dev/null +++ b/BW_tool/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BW_tool/Program.cs b/BW_tool/Program.cs new file mode 100644 index 0000000..eb2abe6 --- /dev/null +++ b/BW_tool/Program.cs @@ -0,0 +1,31 @@ +/* + * Created by SharpDevelop. + * User: suloku + * Date: 01/03/2016 + * Time: 20:03 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using System; +using System.Windows.Forms; + +namespace BW_tool +{ + /// + /// Class with program entry point. + /// + internal sealed class Program + { + /// + /// Program entry point. + /// + [STAThread] + private static void Main(string[] args) + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + + } +} diff --git a/BW_tool/Properties/AssemblyInfo.cs b/BW_tool/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c5e94fd --- /dev/null +++ b/BW_tool/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BW_tool")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BW_tool")] +[assembly: AssemblyCopyright("Copyright 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] diff --git a/BW_tool/SAV/FileIO.cs b/BW_tool/SAV/FileIO.cs new file mode 100644 index 0000000..d3b6efc --- /dev/null +++ b/BW_tool/SAV/FileIO.cs @@ -0,0 +1,98 @@ +/* + * Created by SharpDevelop. + * User: suloku + * Date: 02/03/2016 + * Time: 8:01 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; + +namespace BW_tool +{ + /// + /// Description of FileIO. + /// + public class FileIO + { + /// + /// Reads data into a complete array, throwing an EndOfStreamException + /// if the stream runs out of data first, or if an IOException + /// naturally occurs. + /// + /// The stream to read data from + /// The array to read bytes into. The array + /// will be completely filled from the stream, so an appropriate + /// size must be given. + private static void ReadWholeArray (Stream stream, ref byte[] data) + { + int offset=0; + int remaining = data.Length; + while (remaining > 0) + { + int read = stream.Read(data, offset, remaining); + if (read <= 0) + throw new EndOfStreamException + (String.Format("End of stream reached with {0} bytes left to read", remaining)); + remaining -= read; + offset += read; + } + } + private static void _read_data(ref byte [] buffer, string path) + { + System.IO.FileStream saveFile; + saveFile = new FileStream(path, FileMode.Open); + if (saveFile.Length < 1){ + MessageBox.Show("Invalid file length", "Error"); + return; + } + buffer = new byte[saveFile.Length]; + //MessageBox.Show(buffer.Length.ToString()); + ReadWholeArray(saveFile, ref buffer); + saveFile.Close(); + return; + } + public static int load_file(ref byte[] buffer, ref string path, string filter) + { + OpenFileDialog openFD = new OpenFileDialog(); + //openFD.InitialDirectory = "c:\\"; + openFD.Filter = filter; + if (openFD.ShowDialog() == DialogResult.OK) + { + #region filename + path = openFD.FileName; + //MessageBox.Show(path.ToString()); + #endregion + _read_data(ref buffer, path); + return 1; + }else{ + return -1; + } + + } + public static void save_data(byte[] buffer) + { //if (savegamename.Text.Length < 1) return; + if (buffer == null) return; + SaveFileDialog saveFD = new SaveFileDialog(); + //saveFD.InitialDirectory = "c:\\"; + saveFD.Filter = "NDS save data|*.sav;*.dsv|All Files (*.*)|*.*"; + if (saveFD.ShowDialog() == DialogResult.OK) + { + System.IO.FileStream saveFile; + saveFile = new FileStream(saveFD.FileName, FileMode.Create); + //Write file + saveFile.Write(buffer, 0, buffer.Length); + saveFile.Close(); + MessageBox.Show("File Saved.", "Save file"); + } + } + } +} diff --git a/BW_tool/SAV/PKX5.cs b/BW_tool/SAV/PKX5.cs new file mode 100644 index 0000000..4f52c75 --- /dev/null +++ b/BW_tool/SAV/PKX5.cs @@ -0,0 +1,103 @@ +using System; +using System.Drawing; +using System.Drawing.Text; +using System.Linq; + +namespace BW_tool +{ + public class PKX5 + { + // C# PKX Function Library + // No WinForm object related code, only to calculate information. + // May require re-referencing to main form for string array referencing. + // Relies on Util for some common operations. + +/* // Data + internal static uint LCRNG(uint seed) + { + const uint a = 0x41C64E6D; + const uint c = 0x00006073; + + return seed * a + c; + } + internal static uint LCRNG(ref uint seed) + { + const uint a = 0x41C64E6D; + const uint c = 0x00006073; + + return seed = seed * a + c; + } + + internal static byte[] decryptArray(byte[] ekx, int start_offset, int length, int seed_offset) + { + byte[] pkx = (byte[])ekx.Clone(); + + uint pv = BitConverter.ToUInt32(pkx, seed_offset); + uint sv = (pv >> 0xD & 0x1F) % 24; + + uint seed = pv; + + // Decrypt Blocks with RNG Seed + for (int i = start_offset; i < length-4; i += 2) + BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(pkx, i) ^ LCRNG(ref seed) >> 16)).CopyTo(pkx, i); + + return pkx; + } +*/ + internal static UInt32 LCRNG(UInt32 seed) + // -------------------------------------------------- + { + UInt32 a = 0x41c64e6d; + UInt32 c = 0x00006073; + return (UInt32)(((UInt64)seed * (UInt64)a + (UInt64)c)&0x00000000FFFFFFFF); + } + internal static byte[] cryptoArray(byte[] ekx, int start_offset, int length, int seed_offset) + // -------------------------------------------------- + { + byte[] buffer_dec = (byte[])ekx.Clone(); + + UInt32 pv = BitConverter.ToUInt32(buffer_dec, seed_offset); + byte sv = (byte)((((pv & 0x3e000) >> 0xd) % 24)&0x000000FF); + + UInt32 seed = pv; + UInt16 tmp; + + for (Int32 i = start_offset; i < length-4; i += 0x2) + { + tmp = BitConverter.ToUInt16(buffer_dec, i); + + seed = LCRNG(seed); + tmp ^= (UInt16)((seed >> 16)&0x0000FFFF); + Array.Copy(BitConverter.GetBytes(tmp), 0, buffer_dec, i, 2); + } + return buffer_dec; + } + + // SAV Manipulation + /// Calculates the CRC16-CCITT checksum over an input byte array. + /// Input byte array + /// Checksum + //adapted from Gocario's PHBank (www.github.com/gocario/phbank) + internal static ushort ccitt16(byte[] data) + // -------------------------------------------------- + { + int len = data.Length; + UInt16 crc = 0xFFFF; + + for (UInt32 i = 0; i < len; i++) + { + crc ^= ((UInt16)((data[i] << 8)&0x0000FFFF)); + + for (UInt32 j = 0; j < 0x8; j++) + { + if ((crc & 0x8000) > 0) + crc = (UInt16)(((UInt16)((crc << 1)&0x0000FFFF ) ^ 0x1021) &0x0000FFFF); + else + crc <<= 1; + } + } + + return crc; + } + } +} \ No newline at end of file diff --git a/BW_tool/SAV/SAV5.cs b/BW_tool/SAV/SAV5.cs new file mode 100644 index 0000000..7d46ada --- /dev/null +++ b/BW_tool/SAV/SAV5.cs @@ -0,0 +1,329 @@ +//Base file from kaphotic's pkhex + +using System; +using System.Linq; + + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; + +namespace BW_tool +{ + public class BlockInfo + { + public int Offset; + public int Length; + public int ID; + public int updtcnt; + public int Checksum; + public bool encrypted; + public int seed; + } + public class SAV5 : PKX5 + { + private static int[] BlockTableBW2 = + {0x00000, 0x00400, 0x01400, 0x02400, 0x03400, 0x04400, 0x05400, //6 + 0x06400, 0x07400, 0x08400, 0x09400, 0x0A400, 0x0B400, 0x0C400, //13 + 0x0D400, 0x0E400, 0x0F400, 0x10400, 0x11400, 0x12400, 0x13400, //20 + 0x14400, 0x15400, 0x16400, 0x17400, 0x18400, 0x18E00, 0x19400, //27 + 0x19500, 0x19600, 0x1AA00, 0x1B200, 0x1C000, 0x1C100, 0x1C800, //34 + 0x1D300, 0x1D500, 0x1D900, 0x1DA00, 0x1DC00, 0x1DD00, 0x1E200, //41 + 0x1F700, 0x1FA00, 0x1FB00, 0x1FF00, 0x20400, 0x20500, 0x20800, //48 + 0x20900, 0x20D00, 0x20F00, 0x21100, 0x21200, 0x21400, 0x21900, //55 + 0x21A00, 0x21B00, 0x21D00, 0x22900, 0x22A00, 0x23300, 0x23600, //62 + 0x23700, 0x23800, 0x23A00, 0x23B00, 0x23C00, 0x25300, 0x25800, //69 + 0x25900, 0x25A00, 0x25E00, 0x25F00}; //73 + + private static int[] BlockTableLengthBW2 = + {0x3E0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, //6 + 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, //13 + 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0xFF0, //20 + 0xFF0, 0xFF0, 0xFF0, 0xFF0, 0x9EC, 0X534, 0xB0, //27 + 0xA8, 0x1338, 0x7C4, 0xD54, 0X94, 0x658, 0xA94, //34 + 0x1AC, 0x3EC, 0x5C, 0x1E0, 0xA8, 0x460, 0x1400, //41 + 0x2A4, 0xE0, 0x34C, 0x4E0, 0xF8, 0x2FC, 0x94, //48 + 0x35C, 0x1D4, 0x1E0, 0xF0, 0x1B4, 0x4DC, 0x34, //55 + 0X3C, 0x1AC, 0xB90, 0xAC, 0x850, 0x284, 0x10, //62 + 0xA8, 0x16C, 0x80, 0xFC, 0x16A8, 0x498, 0x60, //69 + 0xFC, 0x3E4, 0xF0, 0x94}; + + internal const int SIZERAW = 0x80000; // 512KB + internal const int SIZE1 = 0x24000; // B/W + internal const int SIZE2 = 0x26000; // B2/W2 + + // Global Settings + // Save Data Attributes + public byte[] Data; + public bool Edited; + public readonly bool Exportable; + public readonly byte[] BAK; + public string FileName, FilePath; + public BlockInfo[] blocks = new BlockInfo[74]; + public bool B2W2; + public bool BW; + public SAV5(byte[] data) + { + Data = (byte[])(data ?? new byte[SIZERAW]).Clone(); + BAK = (byte[])Data.Clone(); + Exportable = !Data.SequenceEqual(new byte[Data.Length]); + + // Get Version + // Validate Checksum over the Checksum Block to find BW or B2W2 + ushort chk2 = BitConverter.ToUInt16(Data, SIZE2 - 0x100 + 0x94 + 0xE); + ushort actual2 = ccitt16(Data.Skip(SIZE2 - 0x100).Take(0x94).ToArray()); + B2W2 = chk2 == actual2; + ushort chk1 = BitConverter.ToUInt16(Data, SIZE1 - 0x100 + 0x8C + 0xE); + ushort actual1 = ccitt16(Data.Skip(SIZE1 - 0x100).Take(0x8C).ToArray()); + BW = chk1 == actual1; + + if (!BW && !B2W2 && data != null){ + Data = null; // Invalid/Not G5 Save File + }else if (B2W2) + { + + //Build blockinfos + int i; + for (i=0;i<74;i++) + { + blocks[i] = new BlockInfo(); + blocks[i].Offset=BlockTableBW2[i]; + blocks[i].Length=BlockTableLengthBW2[i]; + blocks[i].ID= i; + blocks[i].updtcnt=blocks[i].Offset+blocks[i].Length; + if (i != 73) + { + blocks[i].Checksum=blocks[i].Offset+blocks[i].Length+2; + } + else + { + blocks[i].Checksum=0x25FA2; + } + + blocks[i].encrypted=false; + blocks[i].seed=blocks[i].Offset+blocks[i].Length-4; + + } + + // Different Offsets for different games. + BattleBox = B2W2 ? 0x20A00 : 0x20900; + } + } + + private const int Box = 0x400; + private const int Party = 0x18E00; + private readonly int BattleBox; + private const int Trainer = 0x19400; + private const int Wondercard = 0x1C800; + private const int wcSeed = 0x1D290; + + public byte[] getData(int Offset, int Length) + { + return Data.Skip(Offset).Take(Length).ToArray(); + } + public byte[] getBlock(int index) + { + if (B2W2) + { + if (index <73) + { + return Data.Skip(blocks[index].Offset).Take(blocks[index+1].Offset-blocks[index].Offset).ToArray(); + } + else if (index == 73) + { + return Data.Skip(blocks[index].Offset).Take(SIZE2-blocks[index].Offset).ToArray(); + } + else return null; + } + else return null; + } + public byte[] getBlockDec(int index) + { + byte[] decrypt = new byte[getBlockLength(index)]; + + if (B2W2) + { + if (index <73) + { + //MessageBox.Show(decrypt.Length.ToString()); + //MessageBox.Show((blocks[index].Length-4).ToString()); + decrypt = PKX5.cryptoArray(Data.Skip(blocks[index].Offset).Take(blocks[index+1].Offset-blocks[index].Offset).ToArray(), 0, blocks[index].Length, blocks[index].Length-4); + //MessageBox.Show((blocks[index].Length-4).ToString()); + return decrypt; + } + else if (index == 73) + { + return Data.Skip(blocks[index].Offset).Take(SIZE2-blocks[index].Offset).ToArray(); + } + else return null; + } + else return null; + } + public int getBlockLength(int index) + { + if (B2W2){ + if (index < 73) + return blocks[index+1].Offset-blocks[index].Offset; + else if (index == 73) + return SIZE2-blocks[index].Offset; + else return -1; + } + else return -1; + } + public void setBlock(byte[] input, int index) + { + if (getBlockLength(index) != input.Length) + { + MessageBox.Show("Block has invalid size!"); + return; + } + + if (B2W2) + { + if (index <74) + { + //Recalculate checksum before applying to savedata + ushort crc = ccitt16(input.Take(blocks[index].Length).ToArray()); + if (crc != BitConverter.ToUInt16(input, blocks[index].Checksum-blocks[index].Offset) ) + { + Array.Copy(BitConverter.GetBytes(crc), 0, input, blocks[index].Checksum-blocks[index].Offset, 2); + MessageBox.Show("Block's checksum updated"); + }else + { + MessageBox.Show("Checksum doesn't need update"); + } + + + + input.CopyTo(Data, blocks[index].Offset); + input.CopyTo(Data, blocks[index].Offset+SIZE2); + Edited = true; + } + else return; + } + else return; + } + public void setBlockCrypt(byte[] input, int index) + { + if (getBlockLength(index) != input.Length) + { + MessageBox.Show("Block has invalid size!"); + return; + } + + if (B2W2) + { + if (index <74) + { + //Recalculate checksum before applying to savedata + ushort crc = ccitt16(input.Take(blocks[index].Length).ToArray()); + if (crc != BitConverter.ToUInt16(input, blocks[index].Checksum-blocks[index].Offset) ) + { + Array.Copy(BitConverter.GetBytes(crc), 0, input, blocks[index].Checksum-blocks[index].Offset, 2); + MessageBox.Show("Block's checksum updated"); + }else + { + MessageBox.Show("Checksum doesn't need update"); + } + + + + input.CopyTo(Data, blocks[index].Offset); + input.CopyTo(Data, blocks[index].Offset+SIZE2); + Edited = true; + } + else return; + } + else return; + } + public void setData(byte[] input, int Offset) + { + input.CopyTo(Data, Offset); + Edited = true; + } + public void chkCheck() + { + int i = 0; + string badblocks; + badblocks = "Found bad checksums in MAIN save at blocks: "; + bool badcheck = false; + if (B2W2) + { + //Main save checksums + for(i=0; i<74; i++) + { + ushort crc = ccitt16(Data.Skip(blocks[i].Offset).Take(blocks[i].Length).ToArray()); + if ( crc != BitConverter.ToUInt16(Data, blocks[i].Checksum) ) + { + //Array.Copy(BitConverter.GetBytes(crc), 0, input, blocks[index].Checksum-blocks[index].Offset, 2); + //MessageBox.Show("Block's checksum is wrong " + i.ToString()); + if (!badcheck) + { + badcheck = true; + badblocks = badblocks + i.ToString(); + }else + { + badblocks = badblocks + ", " + i.ToString(); + } + } + else + { + //MessageBox.Show("Checksum doesn't need update " + i.ToString()); + } + } + + if(badcheck) + MessageBox.Show(badblocks); + else + MessageBox.Show("All 74 checksums OK in MAIN save"); + + //Backup save checksums + badcheck = false; + badblocks = "Found bad checksums in BACKUP save at blocks: "; + for(i=0; i<74; i++) + { + ushort crc = ccitt16(Data.Skip(blocks[i].Offset+SIZE2).Take(blocks[i].Length).ToArray()); + if ( crc != BitConverter.ToUInt16(Data, blocks[i].Checksum+SIZE2) ) + { + //Array.Copy(BitConverter.GetBytes(crc), 0, input, blocks[index].Checksum-blocks[index].Offset, 2); + //MessageBox.Show("Block's checksum is wrong " + i.ToString()); + if (!badcheck) + { + badcheck = true; + badblocks = badblocks + i.ToString(); + }else + { + badblocks = badblocks + ", " + i.ToString(); + } + } + else + { + //MessageBox.Show("Checksum doesn't need update " + i.ToString()); + } + } + + if(badcheck) + MessageBox.Show(badblocks); + else + MessageBox.Show("All 74 checksums OK in BACKUP save"); + + return; + } + else if (BW) + { + MessageBox.Show("Not yet implemented for BW 1"); + } + else + { + MessageBox.Show("Invalid file"); + return; + } + } + } +} \ No newline at end of file diff --git a/BW_tool/app.config b/BW_tool/app.config new file mode 100644 index 0000000..88f2be5 --- /dev/null +++ b/BW_tool/app.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file