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