From 52d0e17c1449f492edf4e9898a9a671394ecbe8b Mon Sep 17 00:00:00 2001 From: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Sat, 31 Mar 2018 16:48:48 +0300 Subject: [PATCH] Add ACB Injector --- SonicAudioTools.sln | 8 +- Source/AcbInjector/AcbInjector.csproj | 74 ++++++ Source/AcbInjector/App.config | 6 + Source/AcbInjector/Program.cs | 211 ++++++++++++++++++ Source/AcbInjector/Properties/AssemblyInfo.cs | 36 +++ Source/AcbInjector/TxtBxDialog.Designer.cs | 132 +++++++++++ Source/AcbInjector/TxtBxDialog.cs | 46 ++++ Source/AcbInjector/TxtBxDialog.resx | 120 ++++++++++ 8 files changed, 632 insertions(+), 1 deletion(-) create mode 100644 Source/AcbInjector/AcbInjector.csproj create mode 100644 Source/AcbInjector/App.config create mode 100644 Source/AcbInjector/Program.cs create mode 100644 Source/AcbInjector/Properties/AssemblyInfo.cs create mode 100644 Source/AcbInjector/TxtBxDialog.Designer.cs create mode 100644 Source/AcbInjector/TxtBxDialog.cs create mode 100644 Source/AcbInjector/TxtBxDialog.resx diff --git a/SonicAudioTools.sln b/SonicAudioTools.sln index 8bbbc4c..70e88d2 100644 --- a/SonicAudioTools.sln +++ b/SonicAudioTools.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +VisualStudioVersion = 15.0.26430.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SonicAudioLib", "Source\SonicAudioLib\SonicAudioLib.csproj", "{63138773-1F47-474C-9345-15EB6183ECC6}" EndProject @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsbEditor", "Source\CsbEdit EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsbBuilder", "Source\CsbBuilder\CsbBuilder.csproj", "{70A6A571-23EC-4B2C-A579-1E6812DDC34E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AcbInjector", "Source\AcbInjector\AcbInjector.csproj", "{4F68FD56-37A6-40D5-8C57-19067F7C2141}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,6 +40,10 @@ Global {70A6A571-23EC-4B2C-A579-1E6812DDC34E}.Debug|Any CPU.Build.0 = Debug|Any CPU {70A6A571-23EC-4B2C-A579-1E6812DDC34E}.Release|Any CPU.ActiveCfg = Release|Any CPU {70A6A571-23EC-4B2C-A579-1E6812DDC34E}.Release|Any CPU.Build.0 = Release|Any CPU + {4F68FD56-37A6-40D5-8C57-19067F7C2141}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F68FD56-37A6-40D5-8C57-19067F7C2141}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F68FD56-37A6-40D5-8C57-19067F7C2141}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F68FD56-37A6-40D5-8C57-19067F7C2141}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/AcbInjector/AcbInjector.csproj b/Source/AcbInjector/AcbInjector.csproj new file mode 100644 index 0000000..c7fa10a --- /dev/null +++ b/Source/AcbInjector/AcbInjector.csproj @@ -0,0 +1,74 @@ + + + + + Debug + AnyCPU + {4F68FD56-37A6-40D5-8C57-19067F7C2141} + WinExe + AcbInjector + AcbInjector + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + ..\..\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + ..\..\Release\ + TRACE + prompt + 4 + + + AcbInjector.Program + + + + + + + + + + + + + + + + + + Form + + + TxtBxDialog.cs + + + + + + + + {63138773-1f47-474c-9345-15eb6183ecc6} + SonicAudioLib + + + + + TxtBxDialog.cs + + + + \ No newline at end of file diff --git a/Source/AcbInjector/App.config b/Source/AcbInjector/App.config new file mode 100644 index 0000000..88fa402 --- /dev/null +++ b/Source/AcbInjector/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Source/AcbInjector/Program.cs b/Source/AcbInjector/Program.cs new file mode 100644 index 0000000..d58fd32 --- /dev/null +++ b/Source/AcbInjector/Program.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using SonicAudioLib.CriMw; +using SonicAudioLib.Archives; +using System.IO; + +using HedgeEdit.UI; + +namespace AcbInjector +{ + class Program + { + static List Junk = new List(); + + [STAThread] + static void Main(string[] args) + { + Application.EnableVisualStyles(); + + if (args.Length < 1) + { + MessageBox.Show("This program can inject audio files into ACB files without the need of repacking their AWBs.\n\nTo start, drag and drop an ACB file to the executable.", "ACB Injector", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + string sourceFileName = null; + string sourceAudioFileName = null; + int waveformId = -1; + string destinationFileName = null; + + foreach (var arg in args) + { + if (int.TryParse(arg, out int ret) && waveformId < 0) + { + waveformId = ret; + } + + else if (sourceFileName == null) + { + sourceFileName = arg; + } + + else if (sourceAudioFileName == null) + { + sourceAudioFileName = arg; + } + + else if (destinationFileName == null) + { + destinationFileName = arg; + } + } + + CriTable acbFile = new CriTable(); + acbFile.Load(sourceFileName); + + CriTable waveformTable = new CriTable(); + waveformTable.Load(acbFile.Rows[0].GetValue("WaveformTable")); + + var waveforms = waveformTable.Rows.Where(x => x.GetValue("Streaming") == 0).ToList(); + var streamedWaveforms = waveformTable.Rows.Except(waveforms).ToList(); + + if (streamedWaveforms.Count < 1) + { + throw new InvalidDataException("This ACB file has no streamed waveforms, aka an AWB file."); + } + + if (waveformId < 0) + { + while (true) + { + using (TxtBxDialog textBoxDialog = new TxtBxDialog( + streamedWaveforms.Select(x => x.GetValue("Id")).OrderBy(x => x).Select(x => x.ToString()).ToArray())) + { + if (textBoxDialog.ShowDialog() == DialogResult.OK) + { + if (!int.TryParse(textBoxDialog.Result, out waveformId) || !streamedWaveforms.Any(x => x.GetValue("Id") == waveformId)) + { + MessageBox.Show("Invalid waveform id.", "ACB Injector", MessageBoxButtons.OK); + } + + else + { + break; + } + } + + else + { + return; + } + } + } + } + + CriRow waveformToInject = streamedWaveforms.FirstOrDefault( + x => x.GetValue("Id") == waveformId); + + int newWaveformId = (waveforms.Count > 0 ? + waveforms.Max(x => x.GetValue("Id")) : -1) + 1; + + waveformToInject["Id"] = (ushort)newWaveformId; + waveformToInject["Streaming"] = (byte)0; + + if (string.IsNullOrEmpty(sourceAudioFileName)) + { + using (OpenFileDialog openFileDialog = new OpenFileDialog + { + Title = "Select the audio file that you are going to inject", + InitialDirectory = Path.GetDirectoryName(sourceFileName), + Filter = "All Files|*.*", + }) + { + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + sourceAudioFileName = openFileDialog.FileName; + } + + else + { + return; + } + } + } + + CriAfs2Archive archive = new CriAfs2Archive(); + + if (acbFile.Rows[0]["AwbFile"] is byte[] archiveData && archiveData.Length > 0) + { + archive.Load(archiveData); + + // Proof that SonicAudioLib needs a rewrite. + // I hate this... + foreach (var entry in archive) + { + var filePath = new FileInfo( + Path.GetTempFileName()); + + File.WriteAllBytes(filePath.FullName, + archiveData.Skip((int)entry.Position).Take((int)entry.Length).ToArray()); + + entry.FilePath = filePath; + Junk.Add(entry.FilePath); + } + } + + archive.Add(new CriAfs2Entry + { + Id = (uint)newWaveformId, + FilePath = new FileInfo(sourceAudioFileName) + }); + + acbFile.Rows[0]["AwbFile"] = archive.Save(); + + acbFile.WriterSettings = + waveformTable.WriterSettings = + CriTableWriterSettings.Adx2Settings; + + acbFile.Rows[0]["WaveformTable"] = waveformTable.Save(); + + if (string.IsNullOrEmpty(destinationFileName)) + { + if (args.Length < 2) + { + using (SaveFileDialog saveFileDialog = new SaveFileDialog + { + Title = "Save ACB file", + InitialDirectory = Path.GetDirectoryName(sourceFileName), + FileName = Path.GetFileName(sourceFileName), + Filter = "ACB Files|*.acb", + }) + { + if (saveFileDialog.ShowDialog() == DialogResult.OK) + { + destinationFileName = saveFileDialog.FileName; + } + + else + { + return; + } + } + } + + else + { + destinationFileName = sourceFileName; + } + } + + acbFile.Save(destinationFileName); + } + + catch (Exception ex) + { + MessageBox.Show(ex.Message, "ACB Injector", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + foreach (var junk in Junk) + { + junk.Delete(); + } + } + } +} diff --git a/Source/AcbInjector/Properties/AssemblyInfo.cs b/Source/AcbInjector/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c8eb36d --- /dev/null +++ b/Source/AcbInjector/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 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("AcbInjector")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AcbInjector")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4f68fd56-37a6-40d5-8c57-19067f7c2141")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/AcbInjector/TxtBxDialog.Designer.cs b/Source/AcbInjector/TxtBxDialog.Designer.cs new file mode 100644 index 0000000..4cd11ae --- /dev/null +++ b/Source/AcbInjector/TxtBxDialog.Designer.cs @@ -0,0 +1,132 @@ +namespace HedgeEdit.UI +{ + partial class TxtBxDialog + { + /// + /// 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.label = new System.Windows.Forms.Label(); + this.textBox = new System.Windows.Forms.TextBox(); + this.okBtn = new System.Windows.Forms.Button(); + this.cancelBtn = new System.Windows.Forms.Button(); + this.comboBox = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // label + // + this.label.Dock = System.Windows.Forms.DockStyle.Fill; + this.label.Location = new System.Drawing.Point(0, 0); + this.label.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label.Name = "label"; + this.label.Padding = new System.Windows.Forms.Padding(10, 10, 0, 0); + this.label.Size = new System.Drawing.Size(252, 94); + this.label.TabIndex = 3; + this.label.Text = "Select the ID of waveform:"; + // + // textBox + // + this.textBox.Location = new System.Drawing.Point(8, 41); + this.textBox.Margin = new System.Windows.Forms.Padding(2); + this.textBox.Name = "textBox"; + this.textBox.Size = new System.Drawing.Size(237, 20); + this.textBox.TabIndex = 0; + this.textBox.TextChanged += new System.EventHandler(this.ValueChanged); + // + // okBtn + // + this.okBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okBtn.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okBtn.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.okBtn.Location = new System.Drawing.Point(187, 65); + this.okBtn.Margin = new System.Windows.Forms.Padding(2); + this.okBtn.Name = "okBtn"; + this.okBtn.Size = new System.Drawing.Size(57, 21); + this.okBtn.TabIndex = 1; + this.okBtn.Text = "&OK"; + this.okBtn.UseVisualStyleBackColor = true; + this.okBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // cancelBtn + // + this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelBtn.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.cancelBtn.Location = new System.Drawing.Point(127, 65); + this.cancelBtn.Margin = new System.Windows.Forms.Padding(2); + this.cancelBtn.Name = "cancelBtn"; + this.cancelBtn.Size = new System.Drawing.Size(57, 21); + this.cancelBtn.TabIndex = 2; + this.cancelBtn.Text = "&Cancel"; + this.cancelBtn.UseVisualStyleBackColor = true; + // + // comboBox + // + this.comboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.comboBox.Location = new System.Drawing.Point(8, 38); + this.comboBox.Margin = new System.Windows.Forms.Padding(2); + this.comboBox.Name = "comboBox"; + this.comboBox.Size = new System.Drawing.Size(237, 21); + this.comboBox.TabIndex = 4; + this.comboBox.SelectedIndexChanged += new System.EventHandler(this.ValueChanged); + // + // WaveformIdDialog + // + this.AcceptButton = this.okBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.White; + this.CancelButton = this.cancelBtn; + this.ClientSize = new System.Drawing.Size(252, 94); + this.Controls.Add(this.cancelBtn); + this.Controls.Add(this.okBtn); + this.Controls.Add(this.textBox); + this.Controls.Add(this.comboBox); + this.Controls.Add(this.label); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.HelpButton = true; + this.Margin = new System.Windows.Forms.Padding(2); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "WaveformIdDialog"; + this.ShowIcon = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "ACB Injector"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.OnHelpButtonClicked); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + protected System.Windows.Forms.Label label; + protected System.Windows.Forms.TextBox textBox; + protected System.Windows.Forms.Button okBtn; + protected System.Windows.Forms.Button cancelBtn; + protected System.Windows.Forms.ComboBox comboBox; + } +} \ No newline at end of file diff --git a/Source/AcbInjector/TxtBxDialog.cs b/Source/AcbInjector/TxtBxDialog.cs new file mode 100644 index 0000000..895161a --- /dev/null +++ b/Source/AcbInjector/TxtBxDialog.cs @@ -0,0 +1,46 @@ +using System; +using System.Windows.Forms; + +// Stolen and modified from HedgeEdit (https://github.com/Radfordhound/HedgeLib) +namespace HedgeEdit.UI +{ + public partial class TxtBxDialog : Form + { + // Variables/Constants + public string Result; + + public TxtBxDialog(string[] choices) + { + InitializeComponent(); + + textBox.Visible = false; + comboBox.Items.AddRange(choices); + UpdateOKEnabled(); + } + + // Methods + protected void UpdateOKEnabled() + { + okBtn.Enabled = (textBox.Visible && + !string.IsNullOrWhiteSpace(textBox.Text)) || (comboBox.Visible && + comboBox.SelectedIndex >= 0); + } + + // GUI Events + protected void OkBtn_Click(object sender, EventArgs e) + { + Result = (comboBox.Visible) ? (string)comboBox.SelectedItem : textBox.Text; + } + + protected void ValueChanged(object sender, EventArgs e) + { + UpdateOKEnabled(); + } + + private void OnHelpButtonClicked(object sender, System.ComponentModel.CancelEventArgs e) + { + MessageBox.Show("This is the ID of audio file you are going to replace.\n\nTo get the ID, get the numerical part of file name. (eg. 00005_streaming, where the ID is 5)\n\nPlease note that you need to use files that ACB Editor extracts for reference.", "ACB Injector", MessageBoxButtons.OK, MessageBoxIcon.Information); + e.Cancel = true; + } + } +} \ No newline at end of file diff --git a/Source/AcbInjector/TxtBxDialog.resx b/Source/AcbInjector/TxtBxDialog.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Source/AcbInjector/TxtBxDialog.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