diff --git a/File_Format_Library/FileFormats/Rom/NCA.cs b/File_Format_Library/FileFormats/Rom/NCA.cs index ffef2571..cd216f22 100644 --- a/File_Format_Library/FileFormats/Rom/NCA.cs +++ b/File_Format_Library/FileFormats/Rom/NCA.cs @@ -46,11 +46,9 @@ namespace FirstPlugin public void Load(System.IO.Stream stream) { - string homeFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - string KeyFile = Path.Combine(homeFolder, ".switch", "prod.keys"); - string TitleKeyFile = Path.Combine(homeFolder, ".switch", "title.keys"); - - var Keys = ExternalKeys.ReadKeyFile(KeyFile, TitleKeyFile); + var Keys = Forms.SwitchKeySelectionForm.ShowKeySelector(); + if (Keys == null) + throw new Exception("Failed to get keys. Please select valid paths!"); var Nca = new Nca(Keys, stream.AsStorage(), true); @@ -59,6 +57,15 @@ namespace FirstPlugin (s => s?.Type == SectionType.Romfs || s?.Type == SectionType.Bktr) .SectionNum, false, IntegrityCheckLevel.None, true)); + if (Nca.CanOpenSection((int)ProgramPartitionType.Code)) + { + var exefs = new Pfs(Nca.OpenSection((int)ProgramPartitionType.Code, + false, IntegrityCheckLevel.None, true)); + + foreach (var file in exefs.Files) + files.Add(new NSP.ExefsEntry(exefs, file)); + } + for (int i = 0; i < romfs.Files.Count; i++) files.Add(new NSP.FileEntry(romfs, romfs.Files[i])); } diff --git a/File_Format_Library/FileFormats/Rom/NSP.cs b/File_Format_Library/FileFormats/Rom/NSP.cs index c2ee3ba9..bb8e677b 100644 --- a/File_Format_Library/FileFormats/Rom/NSP.cs +++ b/File_Format_Library/FileFormats/Rom/NSP.cs @@ -55,11 +55,9 @@ namespace FirstPlugin public void Load(System.IO.Stream stream) { - string homeFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - string KeyFile = Path.Combine(homeFolder, ".switch", "prod.keys"); - string TitleKeyFile = Path.Combine(homeFolder, ".switch", "title.keys"); - - var Keys = ExternalKeys.ReadKeyFile(KeyFile, TitleKeyFile); + var Keys = Forms.SwitchKeySelectionForm.ShowKeySelector(); + if (Keys == null) + throw new Exception("Failed to get keys. Please select valid paths!"); Stream Input; @@ -80,6 +78,15 @@ namespace FirstPlugin (s => s?.Type == SectionType.Romfs || s?.Type == SectionType.Bktr) .SectionNum, false, IntegrityCheckLevel.None, true)); + if (Nca.CanOpenSection((int)ProgramPartitionType.Code)) + { + var exefs = new Pfs(Nca.OpenSection((int)ProgramPartitionType.Code, + false, IntegrityCheckLevel.None, true)); + + foreach (var file in exefs.Files) + files.Add(new ExefsEntry(exefs,file)); + } + for (int i = 0; i < romfs.Files.Count; i++) files.Add(new FileEntry(romfs,romfs.Files[i])); } @@ -105,6 +112,42 @@ namespace FirstPlugin return false; } + public class ExefsEntry : FileEntry + { + private Pfs ParentPfs; + private PfsFileEntry fileEntry; + + public override Stream FileDataStream + { + get + { + return ParentPfs.OpenFile(fileEntry).AsStream(); + } + } + + public override void Export() + { + string fileName = Path.GetFileName(FileName.RemoveIllegaleFolderNameCharacters()); + + SaveFileDialog sfd = new SaveFileDialog(); + sfd.FileName = fileName; + sfd.DefaultExt = Path.GetExtension(fileName); + sfd.Filter = "Raw Data (*.*)|*.*"; + + if (sfd.ShowDialog() == DialogResult.OK) + { + FileDataStream.ExportToFile(sfd.FileName); + } + } + + public ExefsEntry(Pfs pfs, PfsFileEntry entry) + { + ParentPfs = pfs; + fileEntry = entry; + FileName = $"Exefs/{fileEntry.Name}"; + } + } + public class FileEntry : ArchiveFileInfo { private Romfs ParentROMFS; @@ -138,11 +181,16 @@ namespace FirstPlugin } } + public FileEntry() + { + + } + public FileEntry(Romfs romfs, RomfsFile romfsFile) { ParentROMFS = romfs; File = romfsFile; - FileName = File.FullPath; + FileName = $"Romfs/{File.FullPath}"; } } } diff --git a/File_Format_Library/FileFormats/Rom/XCI.cs b/File_Format_Library/FileFormats/Rom/XCI.cs index 3e12d743..78c78b56 100644 --- a/File_Format_Library/FileFormats/Rom/XCI.cs +++ b/File_Format_Library/FileFormats/Rom/XCI.cs @@ -49,11 +49,9 @@ namespace FirstPlugin public void Load(System.IO.Stream stream) { - string homeFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - string KeyFile = Path.Combine(homeFolder, ".switch", "prod.keys"); - string TitleKeyFile = Path.Combine(homeFolder, ".switch", "title.keys"); - - var Keys = ExternalKeys.ReadKeyFile(KeyFile, TitleKeyFile); + var Keys = Forms.SwitchKeySelectionForm.ShowKeySelector(); + if (Keys == null) + throw new Exception("Failed to get keys. Please select valid paths!"); Xci xci = new Xci(Keys, stream.AsStorage()); var CnmtNca = new Nca(Keys, xci.SecurePartition.OpenFile( @@ -68,6 +66,15 @@ namespace FirstPlugin var Nca = new Nca(Keys, Input.AsStorage(), true); + if (Nca.CanOpenSection((int)ProgramPartitionType.Code)) + { + var exefs = new Pfs(Nca.OpenSection((int)ProgramPartitionType.Code, + false, IntegrityCheckLevel.None, true)); + + foreach (var file in exefs.Files) + files.Add(new NSP.ExefsEntry(exefs, file)); + } + Romfs romfs = new Romfs( Nca.OpenSection(Nca.Sections.FirstOrDefault (s => s?.Type == SectionType.Romfs || s?.Type == SectionType.Bktr) diff --git a/File_Format_Library/File_Format_Library.csproj b/File_Format_Library/File_Format_Library.csproj index bedaa1c4..4b44266a 100644 --- a/File_Format_Library/File_Format_Library.csproj +++ b/File_Format_Library/File_Format_Library.csproj @@ -520,6 +520,12 @@ EmitterEditorNX.cs + + Form + + + SwitchKeySelectionForm.cs + Form @@ -1453,6 +1459,9 @@ Texture Selector.cs + + SwitchKeySelectionForm.cs + OdysseyCostumeLoader.cs diff --git a/File_Format_Library/GUI/KeySelection.cs b/File_Format_Library/GUI/KeySelection.cs index 780c00de..8cec98bd 100644 --- a/File_Format_Library/GUI/KeySelection.cs +++ b/File_Format_Library/GUI/KeySelection.cs @@ -60,6 +60,7 @@ namespace Toolbox private void btnOk_Click(object sender, EventArgs e) { + } } } diff --git a/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.Designer.cs b/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.Designer.cs new file mode 100644 index 00000000..d47c73c4 --- /dev/null +++ b/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.Designer.cs @@ -0,0 +1,170 @@ +namespace FirstPlugin.Forms +{ + partial class SwitchKeySelectionForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.setProdKeyPath = new Toolbox.Library.Forms.STButton(); + this.setTitleKeyPath = new Toolbox.Library.Forms.STButton(); + this.btnOk = new Toolbox.Library.Forms.STButton(); + this.stLabel2 = new Toolbox.Library.Forms.STLabel(); + this.TextBoxTitleKey = new Toolbox.Library.Forms.STTextBox(); + this.stLabel1 = new Toolbox.Library.Forms.STLabel(); + this.TextBoxProdKeyPath = new Toolbox.Library.Forms.STTextBox(); + this.stCheckBox1 = new Toolbox.Library.Forms.STCheckBox(); + this.contentContainer.SuspendLayout(); + this.SuspendLayout(); + // + // contentContainer + // + this.contentContainer.Controls.Add(this.stCheckBox1); + this.contentContainer.Controls.Add(this.setProdKeyPath); + this.contentContainer.Controls.Add(this.setTitleKeyPath); + this.contentContainer.Controls.Add(this.btnOk); + this.contentContainer.Controls.Add(this.stLabel2); + this.contentContainer.Controls.Add(this.TextBoxTitleKey); + this.contentContainer.Controls.Add(this.stLabel1); + this.contentContainer.Controls.Add(this.TextBoxProdKeyPath); + this.contentContainer.Size = new System.Drawing.Size(430, 188); + this.contentContainer.Controls.SetChildIndex(this.TextBoxProdKeyPath, 0); + this.contentContainer.Controls.SetChildIndex(this.stLabel1, 0); + this.contentContainer.Controls.SetChildIndex(this.TextBoxTitleKey, 0); + this.contentContainer.Controls.SetChildIndex(this.stLabel2, 0); + this.contentContainer.Controls.SetChildIndex(this.btnOk, 0); + this.contentContainer.Controls.SetChildIndex(this.setTitleKeyPath, 0); + this.contentContainer.Controls.SetChildIndex(this.setProdKeyPath, 0); + this.contentContainer.Controls.SetChildIndex(this.stCheckBox1, 0); + // + // setProdKeyPath + // + this.setProdKeyPath.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.setProdKeyPath.Location = new System.Drawing.Point(9, 50); + this.setProdKeyPath.Name = "setProdKeyPath"; + this.setProdKeyPath.Size = new System.Drawing.Size(53, 23); + this.setProdKeyPath.TabIndex = 6; + this.setProdKeyPath.Text = "Set"; + this.setProdKeyPath.UseVisualStyleBackColor = false; + this.setProdKeyPath.Click += new System.EventHandler(this.setProdKeyPath_Click); + // + // setTitleKeyPath + // + this.setTitleKeyPath.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.setTitleKeyPath.Location = new System.Drawing.Point(9, 113); + this.setTitleKeyPath.Name = "setTitleKeyPath"; + this.setTitleKeyPath.Size = new System.Drawing.Size(53, 23); + this.setTitleKeyPath.TabIndex = 5; + this.setTitleKeyPath.Text = "Set"; + this.setTitleKeyPath.UseVisualStyleBackColor = false; + this.setTitleKeyPath.Click += new System.EventHandler(this.setTitleKeyPath_Click); + // + // btnOk + // + this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOk.Enabled = false; + this.btnOk.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnOk.Location = new System.Drawing.Point(332, 149); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(75, 23); + this.btnOk.TabIndex = 4; + this.btnOk.Text = "Ok"; + this.btnOk.UseVisualStyleBackColor = false; + this.btnOk.Click += new System.EventHandler(this.btnOk_Click); + // + // stLabel2 + // + this.stLabel2.AutoSize = true; + this.stLabel2.Location = new System.Drawing.Point(9, 88); + this.stLabel2.Name = "stLabel2"; + this.stLabel2.Size = new System.Drawing.Size(53, 13); + this.stLabel2.TabIndex = 3; + this.stLabel2.Text = "Title Keys"; + // + // TextBoxTitleKey + // + this.TextBoxTitleKey.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); + this.TextBoxTitleKey.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.TextBoxTitleKey.Location = new System.Drawing.Point(68, 115); + this.TextBoxTitleKey.Name = "TextBoxTitleKey"; + this.TextBoxTitleKey.ReadOnly = true; + this.TextBoxTitleKey.Size = new System.Drawing.Size(339, 20); + this.TextBoxTitleKey.TabIndex = 2; + // + // stLabel1 + // + this.stLabel1.AutoSize = true; + this.stLabel1.Location = new System.Drawing.Point(9, 28); + this.stLabel1.Name = "stLabel1"; + this.stLabel1.Size = new System.Drawing.Size(55, 13); + this.stLabel1.TabIndex = 1; + this.stLabel1.Text = "Prod Keys"; + // + // TextBoxProdKeyPath + // + this.TextBoxProdKeyPath.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); + this.TextBoxProdKeyPath.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.TextBoxProdKeyPath.Location = new System.Drawing.Point(68, 53); + this.TextBoxProdKeyPath.Name = "TextBoxProdKeyPath"; + this.TextBoxProdKeyPath.ReadOnly = true; + this.TextBoxProdKeyPath.Size = new System.Drawing.Size(339, 20); + this.TextBoxProdKeyPath.TabIndex = 0; + // + // stCheckBox1 + // + this.stCheckBox1.AutoSize = true; + this.stCheckBox1.Location = new System.Drawing.Point(3, 153); + this.stCheckBox1.Name = "stCheckBox1"; + this.stCheckBox1.Size = new System.Drawing.Size(170, 17); + this.stCheckBox1.TabIndex = 11; + this.stCheckBox1.Text = "Use \"UserName/.switch path\""; + this.stCheckBox1.UseVisualStyleBackColor = true; + this.stCheckBox1.CheckedChanged += new System.EventHandler(this.stCheckBox1_CheckedChanged); + // + // SwitchKeySelectionForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(436, 193); + this.Name = "SwitchKeySelectionForm"; + this.Text = "Select Key Files"; + this.contentContainer.ResumeLayout(false); + this.contentContainer.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private Toolbox.Library.Forms.STTextBox TextBoxProdKeyPath; + private Toolbox.Library.Forms.STLabel stLabel1; + private Toolbox.Library.Forms.STLabel stLabel2; + private Toolbox.Library.Forms.STTextBox TextBoxTitleKey; + private Toolbox.Library.Forms.STButton btnOk; + private Toolbox.Library.Forms.STButton setTitleKeyPath; + private Toolbox.Library.Forms.STButton setProdKeyPath; + private Toolbox.Library.Forms.STCheckBox stCheckBox1; + } +} \ No newline at end of file diff --git a/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.cs b/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.cs new file mode 100644 index 00000000..0cd12e9d --- /dev/null +++ b/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Toolbox.Library.Forms; +using LibHac; +using Toolbox.Library; + +namespace FirstPlugin.Forms +{ + public partial class SwitchKeySelectionForm : STForm + { + public SwitchKeySelectionForm() + { + InitializeComponent(); + } + + public static Keyset ShowKeySelector() + { + if (Runtime.SwitchKeys.HasKeys()) + return ExternalKeys.ReadKeyFile(Runtime.SwitchKeys.ProdKeys, Runtime.SwitchKeys.TitleKeys); + + SwitchKeySelectionForm selectorKeys = new SwitchKeySelectionForm(); + if (selectorKeys.ShowDialog() == DialogResult.OK) + { + var Keys = ExternalKeys.ReadKeyFile(Runtime.SwitchKeys.ProdKeys, Runtime.SwitchKeys.TitleKeys); + return Keys; + } + + return null; + } + + private void setProdKeyPath_Click(object sender, EventArgs e) + { + string ProdKeyPath = GetOpenedFileName(); + TextBoxProdKeyPath.Text = ProdKeyPath; + + if (File.Exists(ProdKeyPath)) + { + TextBoxProdKeyPath.BackColor = FormThemes.BaseTheme.FormBackColor; + Runtime.SwitchKeys.ProdKeys = ProdKeyPath; + } + else + TextBoxProdKeyPath.BackColor = Color.Red; + + CheckKeys(); + } + + private void setTitleKeyPath_Click(object sender, EventArgs e) + { + string TitleKeyPath = GetOpenedFileName(); + TextBoxTitleKey.Text = TitleKeyPath; + + if (File.Exists(TitleKeyPath)) + { + TextBoxTitleKey.BackColor = FormThemes.BaseTheme.FormBackColor; + Runtime.SwitchKeys.TitleKeys = TitleKeyPath; + } + else + TextBoxTitleKey.BackColor = Color.Red; + + CheckKeys(); + } + + private void UpdateTextBoxes(string titlePath, string prodPath) + { + if (File.Exists(titlePath)) + { + TextBoxTitleKey.Text = titlePath; + TextBoxTitleKey.BackColor = FormThemes.BaseTheme.FormBackColor; + Runtime.SwitchKeys.TitleKeys = titlePath; + } + else + TextBoxTitleKey.BackColor = Color.Red; + + if (File.Exists(prodPath)) + { + TextBoxTitleKey.Text = prodPath; + TextBoxProdKeyPath.BackColor = FormThemes.BaseTheme.FormBackColor; + Runtime.SwitchKeys.ProdKeys = prodPath; + } + else + TextBoxProdKeyPath.BackColor = Color.Red; + + CheckKeys(); + } + + private void CheckKeys() + { + if (File.Exists(Runtime.SwitchKeys.ProdKeys) && File.Exists(Runtime.SwitchKeys.TitleKeys)) + btnOk.Enabled = true; + else + btnOk.Enabled = false; + } + + private string GetOpenedFileName() + { + OpenFileDialog ofd = new OpenFileDialog(); + if (ofd.ShowDialog() == DialogResult.OK) + return ofd.FileName; + else return null; + } + + private void btnOk_Click(object sender, EventArgs e) + { + try + { + var Keys = ExternalKeys.ReadKeyFile(Runtime.SwitchKeys.ProdKeys, Runtime.SwitchKeys.TitleKeys); + Toolbox.Library.Config.Save(); + } + catch + { + DialogResult = DialogResult.Ignore; + } + } + + private void stCheckBox1_CheckedChanged(object sender, EventArgs e) + { + if (stCheckBox1.Checked) + { + TextBoxProdKeyPath.Text = Path.Combine(Runtime.SwitchKeys.SwitchFolder, "prod.keys"); + TextBoxTitleKey.Text = Path.Combine(Runtime.SwitchKeys.SwitchFolder, "title.keys"); + + } + else + { + TextBoxProdKeyPath.Text = Runtime.SwitchKeys.ProdKeys; + TextBoxTitleKey.Text = Runtime.SwitchKeys.TitleKeys; + } + + UpdateTextBoxes(TextBoxTitleKey.Text , TextBoxProdKeyPath.Text); + } + } +} diff --git a/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.resx b/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/File_Format_Library/GUI/Rom/SwitchKeySelectionForm.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/Switch_Toolbox_Library/Config.cs b/Switch_Toolbox_Library/Config.cs index cc87167d..302a93d1 100644 --- a/Switch_Toolbox_Library/Config.cs +++ b/Switch_Toolbox_Library/Config.cs @@ -273,6 +273,12 @@ namespace Toolbox.Library case "LayoutDisplayGrid": bool.TryParse(node.InnerText, out Runtime.LayoutEditor.DisplayGrid); break; + case "TitleKeys": + Runtime.SwitchKeys.TitleKeys = node.InnerText; + break; + case "ProdKeys": + Runtime.SwitchKeys.ProdKeys = node.InnerText; + break; } } @@ -357,6 +363,7 @@ namespace Toolbox.Library doc.AppendChild(mainNode); AppendPathSettings(doc, mainNode); + AppendSwitchKeyPathSettings(doc, mainNode); return doc; } @@ -435,6 +442,7 @@ namespace Toolbox.Library PathsNode.AppendChild(createNode(doc, "EnableImageZoom", Runtime.ImageEditor.EnableImageZoom.ToString())); parentNode.AppendChild(createNode(doc, "CustomPicureBoxBGColor", ColorTranslator.ToHtml(Runtime.CustomPicureBoxBGColor))); } + private static void AppendPathSettings(XmlDocument doc, XmlNode parentNode) { XmlNode PathsNode = doc.CreateElement("PATHS"); @@ -447,6 +455,15 @@ namespace Toolbox.Library PathsNode.AppendChild(createNode(doc, "SpecularCubeMapPath", Runtime.PBR.SpecularCubeMapPath.ToString())); PathsNode.AppendChild(createNode(doc, "DiffuseCubeMapPath", Runtime.PBR.DiffuseCubeMapPath.ToString())); } + + private static void AppendSwitchKeyPathSettings(XmlDocument doc, XmlNode parentNode) + { + XmlNode PathsNode = doc.CreateElement("SWITCH_KEY_PATHS"); + parentNode.AppendChild(PathsNode); + PathsNode.AppendChild(createNode(doc, "TitleKeys", Runtime.SwitchKeys.TitleKeys.ToString())); + PathsNode.AppendChild(createNode(doc, "ProdKeys", Runtime.SwitchKeys.ProdKeys.ToString())); + } + private static void AppenPBRSettings(XmlDocument doc, XmlNode parentNode) { XmlNode SettingsNode = doc.CreateElement("PBR"); diff --git a/Switch_Toolbox_Library/Runtime.cs b/Switch_Toolbox_Library/Runtime.cs index 9198d571..068b6573 100644 --- a/Switch_Toolbox_Library/Runtime.cs +++ b/Switch_Toolbox_Library/Runtime.cs @@ -37,6 +37,24 @@ namespace Toolbox.Library public static string TpGamePath = ""; public static string BotwGamePath = ""; + public class SwitchKeys + { + public static string SwitchFolder = System.IO.Path.Combine(Environment.GetFolderPath( + Environment.SpecialFolder.UserProfile), ".switch"); + + public static string TitleKeys = System.IO.Path.Combine(SwitchFolder, "title.keys"); + public static string ProdKeys = System.IO.Path.Combine(SwitchFolder, "prod.keys"); + + public static bool HasKeys() + { + Console.WriteLine($"ProdKeys {ProdKeys} Exists? {System.IO.File.Exists(ProdKeys)}"); + Console.WriteLine($"TitleKeys {TitleKeys} Exists? {System.IO.File.Exists(TitleKeys)}"); + + return System.IO.File.Exists(ProdKeys) && + System.IO.File.Exists(TitleKeys); + } + } + public class MessageEditor { public static FontFamily FontFamily = new FontFamily("Arial");