Add patch creation interface (romfs redir)

Still have to redesign the main window for the ExeFS/RomFS/3DS/Patch
creation buttons to replace the existing on-click events.
This commit is contained in:
Kurt
2015-06-23 22:44:20 -07:00
parent 779b6b30a7
commit 3b0739f65c
5 changed files with 406 additions and 8 deletions

View File

@@ -65,7 +65,8 @@ public Main()
public static string ExeFSPath;
public static string ExHeaderPath;
public volatile int threads;
private static string[] allGARCs = { "gametext", "storytext", "personal", "trpoke", "trdata", "evolution", "megaevo", "levelup", "eggmove", "item", "move", "maisonpkS", "maisontrS", "maisonpkN", "maisontrN" };
internal volatile static int Language;
internal static string[] allGARCs = { "gametext", "storytext", "personal", "trpoke", "trdata", "evolution", "megaevo", "levelup", "eggmove", "item", "move", "maisonpkS", "maisontrS", "maisonpkN", "maisontrN" };
private bool skipBoth;
// Main Form Methods
@@ -113,6 +114,9 @@ private void B_Open_Click(object sender, EventArgs e)
}
private void changeLanguage(object sender, EventArgs e)
{
if (CB_Lang.InvokeRequired)
CB_Lang.Invoke((MethodInvoker)delegate { Language = CB_Lang.SelectedIndex; });
else Language = CB_Lang.SelectedIndex;
if (!GB_RomFS.Enabled) return;
new Thread(() =>
{
@@ -601,13 +605,12 @@ private void L_SubTools_Click(object sender, EventArgs e)
}
// GARC Requests
public string getGARCFileName(string requestedGARC)
private string getGARCFileName(string requestedGARC)
{
return getGARCFileName(requestedGARC, Language);
}
internal static string getGARCFileName(string requestedGARC, int lang)
{
int lang = 0;
if (CB_Lang.InvokeRequired)
CB_Lang.Invoke((MethodInvoker)delegate { lang = CB_Lang.SelectedIndex; });
else lang = CB_Lang.SelectedIndex;
string ans = "";
switch (requestedGARC)
{
@@ -633,7 +636,7 @@ public string getGARCFileName(string requestedGARC)
}
return ans;
}
public string getGARCPath(int A, int B, int C)
internal static string getGARCPath(int A, int B, int C)
{
return String.Format("{0}a{0}{1}{0}{2}{0}{3}", Path.DirectorySeparatorChar, A, B, C);
}

143
pk3DS/Subforms/Patch.Designer.cs generated Normal file
View File

@@ -0,0 +1,143 @@
namespace pk3DS
{
partial class Patch
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.CHKLB_GARCs = new System.Windows.Forms.CheckedListBox();
this.textBox1 = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.B_PatchCIA = new System.Windows.Forms.Button();
this.CHK_Lang = new System.Windows.Forms.CheckBox();
this.RTB_GARCs = new System.Windows.Forms.RichTextBox();
this.label2 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// CHKLB_GARCs
//
this.CHKLB_GARCs.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.CHKLB_GARCs.FormattingEnabled = true;
this.CHKLB_GARCs.Location = new System.Drawing.Point(12, 12);
this.CHKLB_GARCs.Name = "CHKLB_GARCs";
this.CHKLB_GARCs.Size = new System.Drawing.Size(150, 289);
this.CHKLB_GARCs.TabIndex = 0;
//
// textBox1
//
this.textBox1.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textBox1.Location = new System.Drawing.Point(168, 28);
this.textBox1.Name = "textBox1";
this.textBox1.ReadOnly = true;
this.textBox1.Size = new System.Drawing.Size(104, 20);
this.textBox1.TabIndex = 1;
this.textBox1.Text = "a/#/* -> a#/*";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(168, 12);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(109, 13);
this.label1.TabIndex = 2;
this.label1.Text = "ExeFS Path Override:";
//
// B_PatchCIA
//
this.B_PatchCIA.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.B_PatchCIA.Location = new System.Drawing.Point(168, 278);
this.B_PatchCIA.Name = "B_PatchCIA";
this.B_PatchCIA.Size = new System.Drawing.Size(104, 23);
this.B_PatchCIA.TabIndex = 3;
this.B_PatchCIA.Text = "Redirect [CIA]";
this.B_PatchCIA.UseVisualStyleBackColor = true;
this.B_PatchCIA.Click += new System.EventHandler(this.B_PatchCIA_Click);
//
// CHK_Lang
//
this.CHK_Lang.AutoSize = true;
this.CHK_Lang.Location = new System.Drawing.Point(171, 54);
this.CHK_Lang.Name = "CHK_Lang";
this.CHK_Lang.Size = new System.Drawing.Size(110, 17);
this.CHK_Lang.TabIndex = 4;
this.CHK_Lang.Text = "Patch Languages";
this.CHK_Lang.UseVisualStyleBackColor = true;
//
// RTB_GARCs
//
this.RTB_GARCs.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.RTB_GARCs.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.RTB_GARCs.Location = new System.Drawing.Point(168, 90);
this.RTB_GARCs.Name = "RTB_GARCs";
this.RTB_GARCs.Size = new System.Drawing.Size(75, 160);
this.RTB_GARCs.TabIndex = 5;
this.RTB_GARCs.Text = "";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(165, 74);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(72, 13);
this.label2.TabIndex = 6;
this.label2.Text = "Extra GARCs:";
//
// Patch
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 312);
this.Controls.Add(this.label2);
this.Controls.Add(this.RTB_GARCs);
this.Controls.Add(this.CHK_Lang);
this.Controls.Add(this.B_PatchCIA);
this.Controls.Add(this.label1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.CHKLB_GARCs);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Patch";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Patch Manager";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckedListBox CHKLB_GARCs;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button B_PatchCIA;
private System.Windows.Forms.CheckBox CHK_Lang;
private System.Windows.Forms.RichTextBox RTB_GARCs;
private System.Windows.Forms.Label label2;
}
}

123
pk3DS/Subforms/Patch.cs Normal file
View File

@@ -0,0 +1,123 @@
using System;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace pk3DS
{
public partial class Patch : Form
{
public Patch()
{
InitializeComponent();
RTB_GARCs.Clear();
CHKLB_GARCs.Items.Clear();
foreach (string s in Main.allGARCs)
CHKLB_GARCs.Items.Add(s);
}
internal static bool patchExeFS(string path, string[] oldstr, string[] newstr, ref string result, string outPath = null)
{
int ctr = 0;
if (oldstr.Length != newstr.Length)
{
result = "Input replacements do not match output replacements.";
return false;
}
string text = File.ReadAllText(path, Encoding.Unicode);
for (int i = 0; i < oldstr.Length; i++)
{
if (!text.Contains(oldstr[i]))
result += "Does not contain " + oldstr + Environment.NewLine;
else
ctr++;
text = text.Replace(oldstr[i], newstr[i]);
}
if (ctr == 0)
{ result = "Did not find the old path strings to replace."; return false; }
result += String.Format("Redirected {0} file paths.", ctr);
File.WriteAllText(outPath ?? path, text, Encoding.Unicode);
return true;
}
internal static string exportGARCs(string[] garcPaths, string[] newPaths, string parentRomFS, string patchFolder)
{
if (Directory.Exists(patchFolder))
Directory.Delete(patchFolder, true);
Directory.CreateDirectory(patchFolder);
// Stuff files into new patch folder
for (int i = 0; i < garcPaths.Length; i++)
{
if ((garcPaths[i] ?? "").Length == 0) continue;
string folder = Path.GetDirectoryName(newPaths[i]);
Directory.CreateDirectory(folder);
File.Copy(Path.Combine(parentRomFS, garcPaths[i]), folder);
}
return patchFolder;
}
private void B_PatchCIA_Click(object sender, EventArgs e)
{
try
{
string[] garcs = getGARCs();
string[] garcPaths = getPaths(garcs);
const string oldROM = "rom:/a/";
const string newROM = "rom2:/a";
string[] newPaths = (string[]) garcPaths.Clone();
// Patch the reference
for (int i = 0; i < newPaths.Length; i++)
{
int posA = newPaths[i].LastIndexOf(oldROM, StringComparison.Ordinal);
newPaths[i] = (posA == -1) ? null : newPaths[i].Remove(posA, oldROM.Length).Insert(posA, newROM);
}
string patchFolder = String.Format("{0} ({1})", "Patch", new DateTime().ToString("yyMMdd@HHmmss"));
string result = "";
if (!patchExeFS(Main.ExeFSPath, garcPaths, newPaths, ref result, Path.Combine(patchFolder, ".code.bin")))
return;
Util.Alert("Patch contents saved to:" + Environment.NewLine + exportGARCs(garcPaths, newPaths, Main.RomFSPath, patchFolder), result);
}
catch (Exception ex)
{ Util.Error("Could not create patch:", ex.ToString()); }
}
private string[] getGARCs()
{
StringCollection sc = new StringCollection();
foreach (int indexChecked in CHKLB_GARCs.CheckedIndices)
sc.Add(CHKLB_GARCs.Items[indexChecked].ToString());
string[] rtbLines = RTB_GARCs.Lines;
foreach (string s in rtbLines.Where(s => s.Length == 7 && !sc.Contains(s.Replace('/', Path.DirectorySeparatorChar))))
sc.Add(s.Replace('/', Path.DirectorySeparatorChar));
string[] garcs = new string[sc.Count];
sc.CopyTo(garcs, 0);
return garcs;
}
private string[] getPaths(string[] sc)
{
bool languages = CHK_Lang.Checked;
StringCollection paths = new StringCollection();
foreach (string s in sc)
if (!languages || (s != "gametext" || s != "storytext"))
paths.Add(Main.getGARCFileName(s, Main.Language));
else
for (int l = 0; l < 8; l++)
paths.Add(Main.getGARCFileName(s, l));
string[] garcs = new string[paths.Count];
paths.CopyTo(garcs, 0);
return garcs;
}
}
}

120
pk3DS/Subforms/Patch.resx Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -65,6 +65,12 @@
<Compile Include="3DS\BCLIM.cs" />
<Compile Include="Randomizer.cs" />
<Compile Include="3DS\RomFS.cs" />
<Compile Include="Subforms\Patch.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Subforms\Patch.Designer.cs">
<DependentUpon>Patch.cs</DependentUpon>
</Compile>
<Compile Include="Subforms\TitleScreen.cs">
<SubType>Form</SubType>
</Compile>
@@ -207,6 +213,9 @@
<EmbeddedResource Include="Main.resx">
<DependentUpon>Main.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Subforms\Patch.resx">
<DependentUpon>Patch.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Subforms\TitleScreen.resx">
<DependentUpon>TitleScreen.cs</DependentUpon>
</EmbeddedResource>