478 lines
15 KiB
C#
478 lines
15 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
|
|
using Eto.Forms;
|
|
using Eto.Drawing;
|
|
|
|
namespace PC2Converter
|
|
{
|
|
public partial class MainForm : Form
|
|
{
|
|
DropDown InputTypeSelector = new DropDown();
|
|
DropDown OutputTypeSelector = new DropDown();
|
|
DropDown CharacterSelector = new DropDown();
|
|
TextBox FilePathTextBox = new TextBox();
|
|
Button BrowseFileButton = new Button { Text = "Browse", Width = 100 };
|
|
Button ConvertButton = new Button { Text = "Convert", Width = 100 };
|
|
CheckBox CompressAudioCheckBox = new CheckBox { Text = "Compress Audio", Width = 100 };
|
|
|
|
string[] InputTypes = {
|
|
"RSHW File (RR Engine)", "CSHW File (RR Engine)", "SSHW File (RR Engine)"/*,
|
|
"3ST File (APS)", "ROA/R12/C&R File (APS)"*/
|
|
};
|
|
|
|
string[] RAE3stOutputTypes = { "UST 32 Bits (Single Bot RAE/3st)", "UST 8 Bits (Single Bot Cybers Rosetta)", "UST 256 Bits (Full RAE/3st)" , "UST 256 Bits (Full Cybers Rosetta)" };
|
|
string[] CyberOutputTypes = { "UST 8 Bits (Single Bot Cybers)", "UST 256 Bits (Full Cybers)" };
|
|
string[] StudioCOutputTypes = { "UST 32 Bits (Single Bot Studio C)", "UST 256 Bits (Full Studio C)" };
|
|
|
|
string[] RAE3stCharacters = { "Rolfe/Chuck E.", "Mitzi/Helen", "Fatz/Munch", "Beach Bear/Jasper", "Dook/Pasqually", "Billy Bob", "Looney Bird/PizzaCam"};
|
|
string[] CyberCharacters = { "Chuck E.", "Helen/Guest Star", "Munch", "Jasper", "Pasqually" };
|
|
string[] StudioCCharacters = { "Chuck E." };
|
|
|
|
int[][] SC_CyberBitMapR12 =
|
|
{
|
|
/* chuck */ new int[]{ 1, 2, 3, 4, 8, 5, 6, 7 },
|
|
/* helen */ new int[]{ 65, 66, 67, 68, 72, 69, 70, 71 },
|
|
/* munch */ new int[]{ 49, 50, 51, 56, 53, 54, 55, 52 },
|
|
/* jasper */ new int[]{ 17, 18, 19, 20, 24, 21, 22, 23 },
|
|
/* pasqually */ new int[]{ 33, 34, 35, 40, 37, 38, 39, 36 }
|
|
};
|
|
|
|
int[][] SC_CyberBitMapRosetta =
|
|
{
|
|
/* chuck */ new int[]{ 1, 6, 7, 8, 4, 5, 2, 19 },
|
|
/* helen */ new int[]{ 185, 178, 179, 180, 183, 184, 181, 169 },
|
|
/* munch */ new int[]{ 45, 54, 55, 43, 44, 41, 58, 59 },
|
|
/* jasper */ new int[]{ 166, 156, 157, 158, 156, 157, 151, 163 },
|
|
/* pasqually */ new int[]{ 30, 25, 21, 28, 29, 26, 31, 35 }
|
|
};
|
|
|
|
int[][] SC_RAE3stBitMap =
|
|
{
|
|
/* rolfe/chuck */ new int[]{ 1, 9, 10, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 17, 18, 19, 14, 15, 16, 20, 36, 37 },
|
|
/* mitzi/helen */ new int[]{ 185, 176, 177, 181, 182, 183, 184, 178, 179, 180, 173, 175, 174, 168, 170, 169, 186, 187, 188 },
|
|
/* fatz/munch */ new int[]{ 45, 41, 42, 43, 44, 54, 55, 51, 52, 53, 57, 59, 58, 60, 61, 62 },
|
|
/* beach bear/jasper */ new int[]{ 166, 151, 152, 153, 156, 157, 158, 161, 162, 163, 164, 154, 155, 159, 160, 165 },
|
|
/* dook/pasqually */ new int[]{ 30, 23, 24, 26, 27, 28, 29, 25, 21, 22, 33, 35, 34, 31, 32, 63, 64 },
|
|
/* billy bob */ new int[]{ 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 189, 190, 194, 211, 212, 213 },
|
|
/* looney/pizzacam */ new int[]{ 167, 191, 192, 193, 171, 172, 48 }
|
|
};
|
|
|
|
int[][] SC_StudioCBitMap =
|
|
{
|
|
/* chuck e. */ new int[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }
|
|
};
|
|
|
|
int[] CyberRosettaInvertedBits = { 2, 8, 181, 169, 41, 59, 58, 151, 158, 26, 35, 31 };
|
|
int[] RAE3stAPSDescrambleTable = { 10, 11, 0, 1, 2, 3, 4, 12, 13, 5, 6, 7, 8, 9, 14, 15};
|
|
|
|
bool FullStage = false;
|
|
const int USTVersion = 1;
|
|
|
|
public MainForm()
|
|
{
|
|
Title = "PinkConnection2 Showtape Converter";
|
|
Size = new Size(512, 384);
|
|
Maximizable = false;
|
|
Resizable = false;
|
|
|
|
BrowseFileButton.Command = new Command((sender, e) => { Event_BrowseFileButton(); });
|
|
ConvertButton.Command = new Command((sender, e) => { Event_ConvertFileButton(); });
|
|
|
|
InputTypeSelector.SelectedValueChanged += (sender, e) => Event_ChangeInputType();
|
|
OutputTypeSelector.SelectedValueChanged += (sender, e) => Event_ChangeOutputType();
|
|
|
|
foreach (string s in InputTypes)
|
|
{
|
|
InputTypeSelector.Items.Add(s);
|
|
}
|
|
|
|
Content = BuildLayout();
|
|
}
|
|
|
|
DynamicLayout BuildLayout()
|
|
{
|
|
DynamicLayout layout = new DynamicLayout { DefaultSpacing = new Size(10, 10), DefaultPadding = new Padding(5, 5, 5, 5) };
|
|
|
|
layout.BeginVertical();
|
|
layout.BeginHorizontal();
|
|
layout.Add(new Label { Text = "Input Format" });
|
|
layout.EndHorizontal();
|
|
|
|
layout.BeginHorizontal();
|
|
layout.Add(InputTypeSelector, true);
|
|
layout.EndHorizontal();
|
|
|
|
layout.BeginHorizontal();
|
|
layout.Add(new Label { Text = "Output Type" });
|
|
layout.EndHorizontal();
|
|
|
|
layout.BeginHorizontal();
|
|
layout.Add(OutputTypeSelector, true);
|
|
layout.EndHorizontal();
|
|
|
|
layout.BeginHorizontal();
|
|
layout.Add(new Label { Text = "Character" });
|
|
layout.EndHorizontal();
|
|
|
|
layout.BeginHorizontal();
|
|
layout.Add(CharacterSelector, true);
|
|
layout.EndHorizontal();
|
|
layout.EndVertical();
|
|
|
|
layout.BeginVertical();
|
|
layout.BeginHorizontal();
|
|
layout.Add(new Label { Text = "File to convert" });
|
|
layout.EndHorizontal();
|
|
|
|
layout.BeginHorizontal();
|
|
layout.Add(FilePathTextBox, true);
|
|
layout.Add(BrowseFileButton);
|
|
layout.EndHorizontal();
|
|
layout.EndVertical();
|
|
|
|
layout.BeginVertical();
|
|
layout.BeginHorizontal();
|
|
layout.Add(null, true);
|
|
layout.Add(CompressAudioCheckBox);
|
|
layout.Add(ConvertButton);
|
|
layout.EndHorizontal();
|
|
layout.EndVertical();
|
|
|
|
layout.BeginVertical();
|
|
layout.Add(null, true);
|
|
layout.EndVertical();
|
|
|
|
return layout;
|
|
}
|
|
|
|
string MakeHeader(string showName, string outputType, string character)
|
|
{
|
|
string headerOut = $"UST,{USTVersion.ToString()},{showName.Replace(",", "_").Replace(";", "_")},";
|
|
bool fullStage = false;
|
|
switch (outputType)
|
|
{
|
|
case "UST 32 Bits (Single Bot RAE/3st)":
|
|
headerOut += "32,";
|
|
headerOut += "Rockafire Explosion/3-Stage (Single Character),";
|
|
break;
|
|
case "UST 8 Bits (Single Bot Cybers Rosetta)":
|
|
headerOut += "8,";
|
|
headerOut += "Cyberamics (Single Character),";
|
|
break;
|
|
case "UST 256 Bits (Full RAE/3st)":
|
|
headerOut += "256,";
|
|
headerOut += "Rockafire Explosion/3-Stage (Full Stage),";
|
|
fullStage = true;
|
|
break;
|
|
case "UST 256 Bits (Full Cybers Rosetta)":
|
|
headerOut += "256,";
|
|
headerOut += "Cyberamics (Full Stage),";
|
|
fullStage = true;
|
|
break;
|
|
case "UST 8 Bits (Single Bot Cybers)":
|
|
headerOut += "8,";
|
|
headerOut += "Cyberamics (Single Character),";
|
|
break;
|
|
case "UST 256 Bits (Full Cybers)":
|
|
headerOut += "256,";
|
|
headerOut += "Cyberamics (Full Stage),";
|
|
fullStage = true;
|
|
break;
|
|
case "UST 32 Bits (Single Bot Studio C)":
|
|
headerOut += "32,";
|
|
headerOut += "Studio C (Single Character),";
|
|
break;
|
|
case "UST 256 Bits (Full Studio C)":
|
|
headerOut += "256,";
|
|
headerOut += "Studio C (Full Stage),";
|
|
fullStage = true;
|
|
break;
|
|
}
|
|
if (fullStage) headerOut += "All,";
|
|
else headerOut += character + ";";
|
|
return headerOut;
|
|
}
|
|
|
|
void Event_ChangeInputType()
|
|
{
|
|
switch(InputTypeSelector.SelectedIndex)
|
|
{
|
|
case 0:
|
|
OutputTypeSelector.Items.Clear();
|
|
CharacterSelector.Items.Clear();
|
|
foreach (string s in RAE3stOutputTypes)
|
|
{
|
|
OutputTypeSelector.Items.Add(s);
|
|
}
|
|
break;
|
|
case 1:
|
|
OutputTypeSelector.Items.Clear();
|
|
CharacterSelector.Items.Clear();
|
|
foreach (string s in CyberOutputTypes)
|
|
{
|
|
OutputTypeSelector.Items.Add(s);
|
|
}
|
|
break;
|
|
case 2:
|
|
OutputTypeSelector.Items.Clear();
|
|
CharacterSelector.Items.Clear();
|
|
foreach (string s in StudioCOutputTypes)
|
|
{
|
|
OutputTypeSelector.Items.Add(s);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Event_ChangeOutputType()
|
|
{
|
|
switch(InputTypeSelector.SelectedIndex)
|
|
{
|
|
case 0:
|
|
switch(OutputTypeSelector.SelectedIndex)
|
|
{
|
|
case 0:
|
|
CharacterSelector.Items.Clear();
|
|
foreach (string s in RAE3stCharacters)
|
|
{
|
|
CharacterSelector.Items.Add(s);
|
|
}
|
|
CharacterSelector.Enabled = true;
|
|
FullStage = false;
|
|
break;
|
|
case 1:
|
|
CharacterSelector.Items.Clear();
|
|
foreach (string s in CyberCharacters)
|
|
{
|
|
CharacterSelector.Items.Add(s);
|
|
}
|
|
CharacterSelector.Enabled = true;
|
|
FullStage = false;
|
|
break;
|
|
case 2:
|
|
CharacterSelector.Items.Clear();
|
|
CharacterSelector.Enabled = false;
|
|
FullStage = true;
|
|
break;
|
|
case 3:
|
|
CharacterSelector.Items.Clear();
|
|
CharacterSelector.Enabled = false;
|
|
FullStage = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
switch(OutputTypeSelector.SelectedIndex)
|
|
{
|
|
case 0:
|
|
CharacterSelector.Items.Clear();
|
|
foreach (string s in CyberCharacters)
|
|
{
|
|
CharacterSelector.Items.Add(s);
|
|
}
|
|
CharacterSelector.Enabled = true;
|
|
FullStage = false;
|
|
break;
|
|
case 1:
|
|
CharacterSelector.Items.Clear();
|
|
CharacterSelector.Enabled = false;
|
|
FullStage = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
switch(OutputTypeSelector.SelectedIndex)
|
|
{
|
|
case 0:
|
|
CharacterSelector.Items.Clear();
|
|
foreach (string s in StudioCCharacters)
|
|
{
|
|
CharacterSelector.Items.Add(s);
|
|
}
|
|
CharacterSelector.Enabled = true;
|
|
FullStage = false;
|
|
break;
|
|
case 1:
|
|
CharacterSelector.Items.Clear();
|
|
CharacterSelector.Enabled = false;
|
|
FullStage = true;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Event_BrowseFileButton()
|
|
{
|
|
OpenFileDialog fileDialog = new OpenFileDialog();
|
|
string[] showFiles = { ".rshw", ".cshw", ".sshw" };
|
|
string[] allFiles = { ".*" };
|
|
fileDialog.Filters.Add(new FileFilter("Show Files (.rshw, .cshw, .sshw)", showFiles));
|
|
fileDialog.Filters.Add(new FileFilter("All Files", allFiles));
|
|
fileDialog.Title = "Select Show File.";
|
|
fileDialog.ShowDialog(this);
|
|
if (File.Exists(fileDialog.FileName)) FilePathTextBox.Text = fileDialog.FileName;
|
|
}
|
|
|
|
void Event_ConvertFileButton()
|
|
{
|
|
// safeties
|
|
if (InputTypeSelector.SelectedIndex == -1)
|
|
{
|
|
MessageBox.Show("Fatal: You must select an input type.");
|
|
return;
|
|
}
|
|
if (OutputTypeSelector.SelectedIndex == -1)
|
|
{
|
|
MessageBox.Show("Fatal: You must select an output type.");
|
|
return;
|
|
}
|
|
if (CharacterSelector.SelectedIndex == -1 && !FullStage)
|
|
{
|
|
MessageBox.Show("Fatal: You must select a character.");
|
|
return;
|
|
}
|
|
if (FilePathTextBox.Text.Trim() == "")
|
|
{
|
|
MessageBox.Show("Fatal: You must select an input file.");
|
|
return;
|
|
}
|
|
if (!File.Exists(FilePathTextBox.Text.Trim()))
|
|
{
|
|
MessageBox.Show("Fatal: Specified input file does not exist.");
|
|
return;
|
|
}
|
|
|
|
if (FullStage)
|
|
{
|
|
MessageBox.Show("Fatal: Full Stage mode is not supported at this time.");
|
|
return;
|
|
}
|
|
|
|
SaveFileDialog fileDialog = new SaveFileDialog();
|
|
fileDialog.Filters.Add(new FileFilter("UST Files (.ust)", new string[]{".ust"}));
|
|
fileDialog.Title = "Select Show File.";
|
|
fileDialog.ShowDialog(this);
|
|
if (fileDialog.FileName.Trim() == "") return;
|
|
string outFilePath = fileDialog.FileName;
|
|
|
|
// now its actually time to start doing some shit
|
|
string inputType = InputTypeSelector.SelectedKey;
|
|
string outputType = OutputTypeSelector.SelectedKey;
|
|
string filePath = FilePathTextBox.Text.Trim();
|
|
if (inputType == "RSHW File (RR Engine)" || inputType == "CSHW File (RR Engine)" || inputType == "SSHW File (RR Engine)")
|
|
{
|
|
// at some point there should be a warning displayed if the input type does not match the file extension but i dont feel like doing that right now
|
|
|
|
// rshw time
|
|
RSHWFile file = RSHWLoader.Load(filePath);
|
|
if (file.signalData == null)
|
|
{
|
|
MessageBox.Show("Fatal: This file contains no signal data.");
|
|
return;
|
|
}
|
|
if (file.audioData == null)
|
|
{
|
|
MessageBox.Show("Fatal: This file contains no audio data.");
|
|
return;
|
|
}
|
|
|
|
List<BitArray> rshwBits = new List<BitArray>();
|
|
int countlength = 0;
|
|
if (file.signalData[0] != 0)
|
|
{
|
|
countlength = 1;
|
|
BitArray bit = new BitArray(300);
|
|
rshwBits.Add(bit);
|
|
}
|
|
for (int i = 0; i < file.signalData.Length; i++)
|
|
{
|
|
if (file.signalData[i] == 0)
|
|
{
|
|
countlength += 1;
|
|
BitArray bit = new BitArray(300);
|
|
rshwBits.Add(bit);
|
|
}
|
|
else
|
|
{
|
|
rshwBits[countlength - 1].Set(file.signalData[i], true);
|
|
}
|
|
}
|
|
|
|
// pick which target bits to use and how many bytes the output should be
|
|
int bitsCount = 0;
|
|
int[] targetBits = { 0 };
|
|
switch (outputType)
|
|
{
|
|
case "UST 32 Bits (Single Bot RAE/3st)":
|
|
bitsCount = 32;
|
|
targetBits = SC_RAE3stBitMap[Array.IndexOf(RAE3stCharacters, CharacterSelector.SelectedKey)];
|
|
break;
|
|
case "UST 8 Bits (Single Bot Cybers Rosetta)":
|
|
bitsCount = 8;
|
|
targetBits = SC_CyberBitMapRosetta[Array.IndexOf(CyberCharacters, CharacterSelector.SelectedKey)];
|
|
break;
|
|
case "UST 256 Bits (Full RAE/3st)":
|
|
bitsCount = 256;
|
|
break;
|
|
case "UST 256 Bits (Full Cybers Rosetta)":
|
|
bitsCount = 256;
|
|
break;
|
|
case "UST 8 Bits (Single Bot Cybers)":
|
|
bitsCount = 8;
|
|
targetBits = SC_CyberBitMapR12[Array.IndexOf(CyberCharacters, CharacterSelector.SelectedKey)];
|
|
break;
|
|
case "UST 256 Bits (Full Cybers)":
|
|
bitsCount = 256;
|
|
break;
|
|
case "UST 32 Bits (Single Bot Studio C)":
|
|
bitsCount = 32;
|
|
targetBits = SC_StudioCBitMap[Array.IndexOf(StudioCCharacters, CharacterSelector.SelectedKey)];
|
|
break;
|
|
case "UST 256 Bits (Full Studio C)":
|
|
bitsCount = 256;
|
|
break;
|
|
}
|
|
|
|
List<string> writeOut = new List<string>();
|
|
foreach (BitArray bits in rshwBits)
|
|
{
|
|
int frameByte = 0;
|
|
for (int i = 0; i < targetBits.Length; i++)
|
|
{
|
|
if (bits.Get(targetBits[i])) frameByte += 1 << i;
|
|
}
|
|
writeOut.Add(frameByte.ToString($"X{(bitsCount/4).ToString()}"));
|
|
}
|
|
|
|
byte[] audioOut = file.audioData;
|
|
if ((bool)CompressAudioCheckBox.Checked)
|
|
{
|
|
if (File.Exists("tmp.mp3")) File.Delete("tmp.mp3");
|
|
File.WriteAllBytes("tmp.wav", audioOut);
|
|
Process ffmpegProcess = new Process();
|
|
ffmpegProcess.StartInfo.FileName = "ffmpeg";
|
|
ffmpegProcess.StartInfo.Arguments = "-i tmp.wav -c:a mp3 -b:a 128 tmp.mp3";
|
|
ffmpegProcess.Start();
|
|
ffmpegProcess.WaitForExit();
|
|
audioOut = File.ReadAllBytes("tmp.mp3");
|
|
File.Delete("tmp.wav");
|
|
File.Delete("tmp.mp3");
|
|
}
|
|
|
|
File.WriteAllText(outFilePath, MakeHeader(Path.GetFileNameWithoutExtension(outFilePath), outputType, CharacterSelector.SelectedKey)+String.Join(",", writeOut.ToArray())+";"+Convert.ToBase64String(audioOut));
|
|
}
|
|
MessageBox.Show("Conversion complete!");
|
|
}
|
|
|
|
int RRtoAPSBit(int rrBit) // aps lower drawer starts at 129 instead of 151
|
|
{
|
|
int outAPSBit = rrBit;
|
|
if (rrBit > 150) outAPSBit = rrBit - 22;
|
|
return outAPSBit;
|
|
}
|
|
}
|
|
}
|