1
0
mirror of https://github.com/SirusDoma/VoxCharger.git synced 2024-11-24 15:10:14 +01:00
VoxCharger/Sources/Forms/ConverterForm.cs

612 lines
23 KiB
C#
Raw Normal View History

2020-04-18 22:24:48 +02:00
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
namespace VoxCharger
{
2020-04-19 14:13:12 +02:00
public enum ConvertMode
2020-04-18 22:24:48 +02:00
{
2020-04-19 14:13:12 +02:00
Converter = 0,
Importer = 1,
BulkConverter = 2,
BulkImporter = 3
}
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
public partial class ConverterForm : Form
{
2020-04-18 22:24:48 +02:00
private readonly Image DummyJacket = VoxCharger.Properties.Resources.jk_dummy_s;
public static string LastBackground { get; private set; } = "63";
private string target;
private string defaultAscii;
2020-04-19 14:13:12 +02:00
private ConvertMode mode;
2020-04-18 22:24:48 +02:00
private Dictionary<Difficulty, ChartInfo> charts = new Dictionary<Difficulty, ChartInfo>();
2020-04-19 14:13:12 +02:00
private Ksh.Exporter exporter;
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
public VoxHeader Result { get; private set; } = null;
public VoxHeader[] ResultSet { get; private set; } = new VoxHeader[0];
public Ksh.ParseOption Options { get; private set; } = new Ksh.ParseOption();
2020-04-19 14:13:12 +02:00
public Action Action { get; private set; } = null;
public Dictionary<string, Action> ActionSet { get; private set; } = new Dictionary<string, Action>();
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
public ConverterForm(string path, ConvertMode convert)
2020-04-18 22:24:48 +02:00
{
InitializeComponent();
target = path;
2020-04-19 14:13:12 +02:00
mode = convert;
// Cancerous code to adjust layout depending what this form going to be
if (mode == ConvertMode.Converter)
2020-04-18 22:24:48 +02:00
{
MusicCodeLabel.Visible = false;
InfVerLabel.Visible = false;
BackgroundLabel.Visible = false;
LevelGroupBox.Enabled = LevelGroupBox.Visible = false;
AsciiTextBox.Enabled = AsciiTextBox.Visible = false;
AsciiAutoCheckBox.Enabled = AsciiAutoCheckBox.Visible = false;
VersionDropDown.Enabled = VersionDropDown.Visible = false;
InfVerDropDown.Enabled = InfVerDropDown.Visible = false;
BackgroundDropDown.Enabled = BackgroundDropDown.Visible = false;
int componentHeight = AsciiTextBox.Height + VersionDropDown.Height + BackgroundDropDown.Height;
OptionsGroupBox.Location = LevelGroupBox.Location;
OptionsGroupBox.Height -= componentHeight;
Height -= LevelGroupBox.Height + componentHeight;
ProcessConvertButton.Text = "Convert";
2020-04-19 14:13:12 +02:00
PathTextBox.Text = target;
return;
2020-04-18 22:24:48 +02:00
}
2020-04-19 14:13:12 +02:00
else if (mode == ConvertMode.BulkImporter)
{
MusicCodeLabel.Visible = false;
LevelGroupBox.Enabled = LevelGroupBox.Visible = false;
AsciiTextBox.Enabled = AsciiTextBox.Visible = false;
AsciiAutoCheckBox.Enabled = AsciiAutoCheckBox.Visible = false;
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
int componentHeight = AsciiTextBox.Height;
OptionsGroupBox.Location = LevelGroupBox.Location;
OptionsGroupBox.Height -= componentHeight;
Height -= LevelGroupBox.Height + componentHeight;
}
PathTextBox.Text = target;
BackgroundDropDown.SelectedItem = LastBackground;
VersionDropDown.SelectedIndex = 4;
InfVerDropDown.SelectedIndex = 0;
ProcessConvertButton.Text = "Add";
2020-04-18 22:24:48 +02:00
}
private void OnConverterFormLoad(object sender, EventArgs e)
{
2020-04-19 14:13:12 +02:00
if (mode != ConvertMode.Importer)
2020-04-18 22:24:48 +02:00
return;
try
{
2020-04-19 14:13:12 +02:00
var main = new Ksh();
2020-04-18 22:24:48 +02:00
main.Parse(target);
2020-04-19 14:13:12 +02:00
Result = main.ToHeader();
Result.ID = AssetManager.GetNextMusicID();
Result.Ascii = defaultAscii = AsciiTextBox.Text = Path.GetFileName(Path.GetDirectoryName(target));
exporter = new Ksh.Exporter(main);
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
for (int i = 1; Directory.Exists(AssetManager.GetMusicPath(Result)); i++)
2020-04-18 22:24:48 +02:00
{
if (i >= 100)
break; // seriously? stupid input get stupid output
2020-04-19 14:13:12 +02:00
Result.Ascii = $"{defaultAscii}{i:D2}";
2020-04-18 22:24:48 +02:00
}
2020-04-19 14:13:12 +02:00
defaultAscii = AsciiTextBox.Text = Result.Ascii;
charts[main.Difficulty] = new ChartInfo(main, main.ToLevelHeader(), target);
2020-04-18 22:24:48 +02:00
LoadJacket(charts[main.Difficulty]);
2020-04-19 14:13:12 +02:00
// Try to locate another difficulty
foreach (var lv in Ksh.Exporter.GetCharts(Path.GetDirectoryName(target), main.Title))
{
// Don't replace main file, there might 2 files with similar meta or another stupid cases
if (lv.Key != main.Difficulty)
charts[lv.Key] = lv.Value;
}
UpdateUI();
2020-04-18 22:24:48 +02:00
}
catch (Exception ex)
{
MessageBox.Show(
$"Failed to load ksh chart.\n{ex.Message}",
"Error",
MessageBoxButtons.OK,
2020-04-18 22:24:48 +02:00
MessageBoxIcon.Error
);
2020-04-19 14:13:12 +02:00
DialogResult = DialogResult.Cancel;
Close();
2020-04-18 22:24:48 +02:00
}
}
private void OnAsciiAutoCheckBoxCheckedChanged(object sender, EventArgs e)
{
AsciiTextBox.ReadOnly = AsciiAutoCheckBox.Checked;
if (AsciiTextBox.ReadOnly)
AsciiTextBox.Text = defaultAscii;
}
private void OnBackgroundDropDownSelectedIndexChanged(object sender, EventArgs e)
{
LastBackground = BackgroundDropDown.SelectedItem.ToString();
}
private void OnInfVerDropDownSelectedIndexChanged(object sender, EventArgs e)
{
if (!charts.ContainsKey(Difficulty.Infinite))
InfEditButton.Text = "--";
else
InfEditButton.Text = InfVerDropDown.SelectedItem.ToString();
}
private void OnLevelEditButtonClick(object sender, EventArgs e)
{
using (var browser = new OpenFileDialog())
{
browser.Filter = "KShoot Mania Chart|*.ksh";
browser.CheckFileExists = true;
if (browser.ShowDialog() != DialogResult.OK)
return;
var control = (Button)sender;
if (!Enum.TryParse(control.Tag.ToString(), out Difficulty diff))
return;
try
{
var chart = new Ksh();
chart.Parse(browser.FileName);
chart.Difficulty = diff; // make sure to replace diff
2020-04-19 14:13:12 +02:00
charts[diff] = new ChartInfo(chart, chart.ToLevelHeader(), browser.FileName);
2020-04-18 22:24:48 +02:00
UpdateUI();
}
catch (Exception ex)
{
MessageBox.Show(
$"Failed to load ksh chart.\n{ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
}
}
private void OnHelpButtonClicked(object sender, CancelEventArgs e)
{
e.Cancel = true;
using (var help = new HelpForm())
help.ShowDialog();
}
private void OnCancelConvertButtonClick(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
private void OnProcessConvertButtonClick(object sender, EventArgs e)
{
Options = new Ksh.ParseOption()
2020-04-18 22:24:48 +02:00
{
RealignOffset = RealignOffsetCheckBox.Checked,
EnableChipFx = ChipFxCheckBox.Checked,
EnableLongFx = LongFxCheckBox.Checked,
EnableCamera = CameraCheckBox.Checked,
EnableSlamImpact = SlamImpactCheckBox.Checked,
EnableLaserTrack = TrackLaserCheckBox.Checked,
EnableButtonTrack = TrackButtonCheckBox.Checked
};
// Act as converter
2020-04-19 14:13:12 +02:00
switch (mode)
2020-04-18 22:24:48 +02:00
{
2020-04-19 14:13:12 +02:00
case ConvertMode.Converter: SingleConvert(); break;
case ConvertMode.BulkConverter: BulkConvert(); break;
case ConvertMode.Importer: SingleImport(); break;
case ConvertMode.BulkImporter: BulkImport(); break;
}
}
2020-04-19 14:13:12 +02:00
private void SingleImport()
{
2020-04-19 14:13:12 +02:00
try
{
// Assign metadata
Result.Ascii = AsciiTextBox.Text;
Result.BackgroundId = short.Parse((BackgroundDropDown.SelectedItem ?? "0").ToString().Split(' ')[0]);
Result.Version = (GameVersion)(VersionDropDown.SelectedIndex + 1);
Result.InfVersion = InfVerDropDown.SelectedIndex == 0 ? InfiniteVersion.MXM : (InfiniteVersion)(InfVerDropDown.SelectedIndex + 1);
Result.GenreId = 16;
Result.Levels = new Dictionary<Difficulty, VoxLevelHeader>();
if (Result.BpmMin != Result.BpmMax && exporter.Source.MusicOffset % 48 != 0 && Options.RealignOffset)
2020-04-18 22:24:48 +02:00
{
2020-04-19 14:13:12 +02:00
// You've been warned!
var prompt = MessageBox.Show(
"Adapting music offset could break this chart.\n" +
"Do you want to continue?",
"Warning",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning
);
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
if (prompt == DialogResult.No)
return;
2020-04-18 22:24:48 +02:00
}
2020-04-19 14:13:12 +02:00
if (Directory.Exists(AssetManager.GetMusicPath(Result)) || AssetManager.Headers.Any(h => h.Ascii == Result.Ascii))
2020-04-18 22:24:48 +02:00
{
MessageBox.Show(
2020-04-19 14:13:12 +02:00
$"Music Code {Result.CodeName} is already taken.",
2020-04-18 22:24:48 +02:00
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
return;
}
2020-04-19 14:13:12 +02:00
exporter.Export(Result, charts, Options);
Action = exporter.Action;
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
DialogResult = DialogResult.OK;
Close();
}
catch (Exception ex)
{
MessageBox.Show(
$"Failed to import ksh chart.\n{ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
// Eliminate non-existent files
foreach (var chart in charts.Values.ToArray())
{
if (!File.Exists(chart.FileName))
charts.Remove(chart.Header.Difficulty);
2020-04-18 22:24:48 +02:00
}
2020-04-19 14:13:12 +02:00
// Reload jacket
UpdateUI();
}
}
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
private void BulkImport()
{
var output = new List<VoxHeader>();
var actions = new Dictionary<string, Action>();
var errors = new List<string>();
using (var loader = new LoadingForm())
{
var action = new Action(() =>
{
var directories = Directory.GetDirectories(target);
2020-04-19 14:39:32 +02:00
int current = 0;
2020-04-19 14:13:12 +02:00
foreach (string dir in directories)
{
2020-04-19 14:13:12 +02:00
loader.SetStatus($"Processing {Path.GetFileName(dir)}..");
2020-04-19 14:39:32 +02:00
loader.SetProgress((current + 1 / (float)directories.Length) * 100f);
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
var files = Directory.GetFiles(dir, "*.ksh");
if (files.Length == 0)
continue;
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
string fn = files[0];
2020-04-18 22:24:48 +02:00
try
{
2020-04-19 14:13:12 +02:00
var ksh = new Ksh();
ksh.Parse(fn, Options);
var header = ksh.ToHeader();
2020-04-19 14:35:38 +02:00
header.ID = AssetManager.GetNextMusicID() + current++;
2020-04-19 14:13:12 +02:00
header.BackgroundId = short.Parse((BackgroundDropDown.SelectedItem ?? "0").ToString().Split(' ')[0]);
header.Version = (GameVersion)(VersionDropDown.SelectedIndex + 1);
header.InfVersion = InfVerDropDown.SelectedIndex == 0 ? InfiniteVersion.MXM : (InfiniteVersion)(InfVerDropDown.SelectedIndex + 1);
header.GenreId = 16;
header.Levels = new Dictionary<Difficulty, VoxLevelHeader>();
string ascii = Path.GetFileName(Path.GetDirectoryName(fn));
if (AssetManager.Headers.Any(v => v.Ascii == ascii) || // Duplicate header with same ascii
Directory.Exists(AssetManager.GetMusicPath(header)) || // Asset that use the ascii is already exists
output.Any(h => h.Ascii == ascii)) // Output with same ascii is already exists
{
continue;
}
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
var charts = Ksh.Exporter.GetCharts(Path.GetDirectoryName(fn), header.Title);
var exporter = new Ksh.Exporter(ksh);
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
header.Ascii = ascii;
exporter.Export(header, charts, Options);
2020-04-18 22:24:48 +02:00
2020-04-19 14:13:12 +02:00
output.Add(header);
actions.Add(ascii, exporter.Action);
}
2020-04-19 14:13:12 +02:00
catch (Exception ex)
{
2020-04-19 14:13:12 +02:00
string err = $"Failed attempt to convert ksh file: {Path.GetFileName(fn)} ({ex.Message})";
errors.Add(err);
Debug.WriteLine(err);
continue;
}
2020-04-18 22:24:48 +02:00
}
2020-04-19 14:13:12 +02:00
loader.Complete();
});
2020-04-19 14:13:12 +02:00
loader.SetAction(action);
loader.ShowDialog();
}
2020-04-19 14:13:12 +02:00
if (errors.Count != 0)
{
2020-04-19 14:13:12 +02:00
string message = "Failed to import one or more charts:";
foreach (string err in errors)
message += $"\n{err}";
MessageBox.Show(
2020-04-19 14:13:12 +02:00
message,
"Warning",
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
}
2020-04-19 14:13:12 +02:00
ResultSet = output.ToArray();
ActionSet = actions;
DialogResult = DialogResult.OK;
Close();
2020-04-18 22:24:48 +02:00
}
2020-04-19 14:13:12 +02:00
private void SingleConvert()
2020-04-18 22:24:48 +02:00
{
2020-04-19 14:13:12 +02:00
try
{
2020-04-19 14:13:12 +02:00
// Single convert
using (var browser = new SaveFileDialog())
{
2020-04-19 14:13:12 +02:00
browser.Filter = "Sound Voltex Chart File|*.vox|All Files|*.*";
if (browser.ShowDialog() != DialogResult.OK)
return;
var ksh = new Ksh();
ksh.Parse(target, Options);
var vox = new VoxChart();
vox.Import(ksh);
vox.Serialize(browser.FileName);
MessageBox.Show(
2020-04-19 14:13:12 +02:00
"Chart has been converted successfully",
"Information",
MessageBoxButtons.OK,
2020-04-19 14:13:12 +02:00
MessageBoxIcon.Information
);
2020-04-19 14:13:12 +02:00
DialogResult = DialogResult.OK;
Close();
}
}
catch (Exception ex)
{
MessageBox.Show(
$"Failed to convert ksh chart.\n{ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
2020-04-18 22:24:48 +02:00
}
2020-04-19 14:13:12 +02:00
private void BulkConvert()
2020-04-18 22:24:48 +02:00
{
2020-04-19 14:13:12 +02:00
var errors = new List<string>();
using (var browser = new FolderBrowserDialog())
2020-04-18 22:24:48 +02:00
{
2020-04-19 14:13:12 +02:00
browser.Description = "Select output directory";
if (browser.ShowDialog() != DialogResult.OK)
return;
using (var loader = new LoadingForm())
{
2020-04-19 14:13:12 +02:00
var action = new Action(() =>
{
var directories = Directory.GetDirectories(target);
int progress = 0;
foreach (string dir in directories)
{
loader.SetStatus($"Processing {Path.GetFileName(dir)}..");
loader.SetProgress((progress++ / (float)directories.Length) * 100f);
foreach (var fn in Directory.GetFiles(dir, "*.ksh"))
{
try
{
// Determine output path
string path = Path.Combine(
$"{browser.SelectedPath}",
$"{Path.GetFileName(dir)}\\"
);
// Create output folder if it's not exists
Directory.CreateDirectory(path);
string output = Path.Combine(path, Path.GetFileName(fn.Replace(".ksh", ".vox")));
2020-04-19 14:13:12 +02:00
// If you happen to read the source, you're probably looking for these boring lines
var ksh = new Ksh();
ksh.Parse(fn, Options);
2020-04-19 14:13:12 +02:00
var vox = new VoxChart();
vox.Import(ksh);
vox.Serialize(output);
}
catch (Exception ex)
{
string err = $"Failed attempt to convert ksh file: {Path.GetFileName(fn)} ({ex.Message})";
errors.Add(err);
Debug.WriteLine(err);
continue;
}
}
}
loader.Complete();
});
loader.SetAction(action);
loader.ShowDialog();
}
}
2020-04-19 14:13:12 +02:00
if (errors.Count == 0)
{
MessageBox.Show(
"Chart has been converted successfully",
"Information",
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
}
else
{
string message = "Failed to convert one or more charts:";
foreach (string err in errors)
message += $"\n{err}";
MessageBox.Show(
message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Warning
);
}
DialogResult = DialogResult.OK;
Close();
2020-04-18 22:24:48 +02:00
}
2020-04-18 22:24:48 +02:00
private void LoadJacket(ChartInfo info)
{
var chart = info.Source;
if (chart == null)
return;
PictureBox pictureBox = null;
string tag = ((int)chart.Difficulty).ToString();
foreach (var control in LevelGroupBox.Controls)
{
if (control is PictureBox p && p.Tag.ToString() == tag)
{
pictureBox = p;
break;
}
}
if (pictureBox == null)
return;
string filename = Path.Combine(Path.GetDirectoryName(info.FileName), chart.JacketFileName);
if (!File.Exists(filename))
{
pictureBox.Image = DummyJacket;
return;
}
try
{
using (var image = Image.FromFile(filename))
pictureBox.Image = new Bitmap(image);
}
catch (Exception ex)
{
pictureBox.Image = DummyJacket;
Debug.WriteLine("Failed load ksh jacket: {0} ({1})", filename, ex.Message);
}
}
private void UpdateUI()
{
var buttons = new List<Button>();
foreach (var control in LevelGroupBox.Controls)
{
if (control is Button button)
buttons.Add(button);
}
foreach (Difficulty diff in Enum.GetValues(typeof(Difficulty)))
{
Button button = null;
string tag = ((int)diff).ToString();
foreach (var bt in buttons)
{
if (bt.Tag.ToString() == tag)
{
button = bt;
break;
}
}
if (!charts.ContainsKey(diff) || charts[diff] == null)
{
charts.Remove(diff); // should be UpdateUiButAlterMapCharts, ya whatever
if (button != null)
button.Text = "--";
foreach (var control in LevelGroupBox.Controls)
{
if (control is PictureBox picture && picture.Tag.ToString() == tag)
{
picture.Image = DummyJacket;
break;
}
}
}
else
{
if (button != null)
{
switch (diff)
{
2020-04-19 14:13:12 +02:00
case Difficulty.Novice: button.Text = "NOV"; break;
2020-04-18 22:24:48 +02:00
case Difficulty.Advanced: button.Text = "ADV"; break;
2020-04-19 14:13:12 +02:00
case Difficulty.Exhaust: button.Text = "EXH"; break;
2020-04-18 22:24:48 +02:00
default:
button.Text = InfVerDropDown.SelectedItem.ToString();
break;
}
}
LoadJacket(charts[diff]);
}
}
}
}
}