mirror of
https://github.com/SirusDoma/VoxCharger.git
synced 2024-11-27 17:00:51 +01:00
Add Bulk Import
Also, fix some bugs
This commit is contained in:
parent
2d6eb38dff
commit
1b82079a37
17
README.md
17
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
- **Author**: CXO2
|
||||
- **Email**: com@cxo2.me
|
||||
- **Version**: 0.9.7b
|
||||
- **Version**: 0.9.8b
|
||||
|
||||
Recharge your KFC Chicken sauce ([Download](https://github.com/SirusDoma/VoxCharger/releases))
|
||||
|
||||
@ -44,16 +44,21 @@ Vox have some sense in it's file format than ksh file, as the result, not all at
|
||||
|
||||
Remember, stupid input get stupid output. But if you believe it's a bug, feel free to open issue or PR.
|
||||
|
||||
### Music Preview
|
||||
Imported music preview from normal audio files may broken or not trimmed properly in the output file. Preview offset in ksh is also ignored, you need dedicated preview file with proper fade in and out. Keep in mind that `2dxwavconvert.exe` will not do this for you, it only trim your audio into 10 second for preview purpose. Until program supports built in encoder, you have to provide 2dx preview manually for proper preview file.
|
||||
|
||||
If you can't live without it, consider making PR to this feature.
|
||||
### Network Scores
|
||||
Make sure you are using under Offline environment and **NOT** connected to any KFC network server while using the output files of this tool. It could break network score table and you may get banned from the network for doing so.
|
||||
|
||||
### Music DB
|
||||
Some attributes are kept hidden from editor, For existing songs, some of their attributes might untouched throughout save iteration, but the rest of hidden attributes might be replaced with dummy / stub data.
|
||||
The program also prevent you to modify Music ID and some attributes are kept hidden from editor, For existing songs, some of their attributes might untouched throughout save iteration, but the rest of hidden attributes might be replaced with dummy / stub data.
|
||||
|
||||
If this really concern you, make sure to backup your `music_db.xml` / `music_db.merged.xml`.
|
||||
|
||||
### Music Preview
|
||||
Imported music preview from normal audio files may broken or not trimmed properly in the output file. Preview offset in ksh is also ignored, you need dedicated preview file with proper fade in and out.
|
||||
|
||||
Keep in mind that `2dxwavconvert.exe` will not do this for you, it only trim your audio into 10 second for preview purpose. Until program supports built in encoder, you have to provide 2dx preview manually for proper preview file.
|
||||
|
||||
If you can't live without it, consider making PR to this feature.
|
||||
|
||||
### Asset File Modification
|
||||
Replacing asset files such as vox, 2dx and graphic files are happen immediately after changes are confirmed. In other hand, metadata need to be saved manually by clicking `File -> Save` or `CTRL+S`.
|
||||
|
||||
|
@ -11,43 +11,40 @@ using System.Diagnostics;
|
||||
|
||||
namespace VoxCharger
|
||||
{
|
||||
public enum ConvertMode
|
||||
{
|
||||
Converter = 0,
|
||||
Importer = 1,
|
||||
BulkConverter = 2,
|
||||
BulkImporter = 3
|
||||
}
|
||||
|
||||
public partial class ConverterForm : Form
|
||||
{
|
||||
private class ChartInfo
|
||||
{
|
||||
public Ksh Source { get; set; }
|
||||
public VoxLevelHeader Header { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string MusicFileName { get; set; }
|
||||
public string JacketFileName { get; set; }
|
||||
|
||||
public ChartInfo(Ksh source, VoxLevelHeader header, string fileName)
|
||||
{
|
||||
Source = source;
|
||||
Header = header;
|
||||
FileName = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Image DummyJacket = VoxCharger.Properties.Resources.jk_dummy_s;
|
||||
public static string LastBackground { get; private set; } = "63";
|
||||
|
||||
private string target;
|
||||
private string defaultAscii;
|
||||
private bool converter;
|
||||
private ConvertMode mode;
|
||||
private Dictionary<Difficulty, ChartInfo> charts = new Dictionary<Difficulty, ChartInfo>();
|
||||
private Ksh.Exporter exporter;
|
||||
|
||||
public VoxHeader Header { get; private set; } = null;
|
||||
public Action Action { get; private set; } = null;
|
||||
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();
|
||||
public Action Action { get; private set; } = null;
|
||||
public Dictionary<string, Action> ActionSet { get; private set; } = new Dictionary<string, Action>();
|
||||
|
||||
public ConverterForm(string path, bool asConverter = false)
|
||||
public ConverterForm(string path, ConvertMode convert)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
target = path;
|
||||
converter = asConverter;
|
||||
if (asConverter)
|
||||
mode = convert;
|
||||
|
||||
// Cancerous code to adjust layout depending what this form going to be
|
||||
if (mode == ConvertMode.Converter)
|
||||
{
|
||||
MusicCodeLabel.Visible = false;
|
||||
InfVerLabel.Visible = false;
|
||||
@ -65,42 +62,67 @@ namespace VoxCharger
|
||||
Height -= LevelGroupBox.Height + componentHeight;
|
||||
|
||||
ProcessConvertButton.Text = "Convert";
|
||||
}
|
||||
else
|
||||
ProcessConvertButton.Text = "Add";
|
||||
PathTextBox.Text = target;
|
||||
|
||||
PathTextBox.Text = target;
|
||||
return;
|
||||
}
|
||||
else if (mode == ConvertMode.BulkImporter)
|
||||
{
|
||||
MusicCodeLabel.Visible = false;
|
||||
LevelGroupBox.Enabled = LevelGroupBox.Visible = false;
|
||||
AsciiTextBox.Enabled = AsciiTextBox.Visible = false;
|
||||
AsciiAutoCheckBox.Enabled = AsciiAutoCheckBox.Visible = false;
|
||||
|
||||
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";
|
||||
|
||||
}
|
||||
|
||||
private void OnConverterFormLoad(object sender, EventArgs e)
|
||||
{
|
||||
var main = new Ksh();
|
||||
if (converter)
|
||||
if (mode != ConvertMode.Importer)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var main = new Ksh();
|
||||
main.Parse(target);
|
||||
|
||||
Result = main.ToHeader();
|
||||
Result.ID = AssetManager.GetNextMusicID();
|
||||
Result.Ascii = defaultAscii = AsciiTextBox.Text = Path.GetFileName(Path.GetDirectoryName(target));
|
||||
exporter = new Ksh.Exporter(main);
|
||||
|
||||
Header = ToHeader(main);
|
||||
Header.Ascii = new DirectoryInfo(Path.GetDirectoryName(target)).Name;
|
||||
defaultAscii = AsciiTextBox.Text = Header.Ascii;
|
||||
|
||||
for (int i = 1; Directory.Exists(AssetManager.GetMusicPath(Header)); i++)
|
||||
for (int i = 1; Directory.Exists(AssetManager.GetMusicPath(Result)); i++)
|
||||
{
|
||||
if (i >= 100)
|
||||
break; // seriously? stupid input get stupid output
|
||||
|
||||
Header.Ascii = $"{defaultAscii}{i:D2}";
|
||||
Result.Ascii = $"{defaultAscii}{i:D2}";
|
||||
}
|
||||
|
||||
defaultAscii = AsciiTextBox.Text = Header.Ascii;
|
||||
BackgroundDropDown.SelectedItem = LastBackground;
|
||||
VersionDropDown.SelectedIndex = 4;
|
||||
InfVerDropDown.SelectedIndex = 0;
|
||||
|
||||
charts[main.Difficulty] = new ChartInfo(main, ToLevelHeader(main), target);
|
||||
defaultAscii = AsciiTextBox.Text = Result.Ascii;
|
||||
charts[main.Difficulty] = new ChartInfo(main, main.ToLevelHeader(), target);
|
||||
LoadJacket(charts[main.Difficulty]);
|
||||
|
||||
// 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();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -111,13 +133,9 @@ namespace VoxCharger
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to locate another difficulty
|
||||
charts = GetCharts(target, main);
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
private void OnAsciiAutoCheckBoxCheckedChanged(object sender, EventArgs e)
|
||||
@ -160,7 +178,7 @@ namespace VoxCharger
|
||||
chart.Parse(browser.FileName);
|
||||
chart.Difficulty = diff; // make sure to replace diff
|
||||
|
||||
charts[diff] = new ChartInfo(chart, ToLevelHeader(chart), browser.FileName);
|
||||
charts[diff] = new ChartInfo(chart, chart.ToLevelHeader(), browser.FileName);
|
||||
UpdateUI();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -202,71 +220,46 @@ namespace VoxCharger
|
||||
};
|
||||
|
||||
// Act as converter
|
||||
if (converter)
|
||||
Convert();
|
||||
else
|
||||
Process();
|
||||
switch (mode)
|
||||
{
|
||||
case ConvertMode.Converter: SingleConvert(); break;
|
||||
case ConvertMode.BulkConverter: BulkConvert(); break;
|
||||
case ConvertMode.Importer: SingleImport(); break;
|
||||
case ConvertMode.BulkImporter: BulkImport(); break;
|
||||
}
|
||||
}
|
||||
|
||||
private VoxHeader ToHeader(Ksh chart)
|
||||
private void SingleImport()
|
||||
{
|
||||
return new VoxHeader()
|
||||
{
|
||||
ID = AssetManager.GetNextMusicID(),
|
||||
Title = chart.Title,
|
||||
Artist = chart.Artist,
|
||||
BpmMin = chart.BpmMin,
|
||||
BpmMax = chart.BpmMax,
|
||||
Volume = chart.Volume > 0 ? (short)chart.Volume : (short)91,
|
||||
DistributionDate = DateTime.Now,
|
||||
BackgroundId = 63,
|
||||
GenreId = 16,
|
||||
};
|
||||
}
|
||||
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>();
|
||||
|
||||
private VoxLevelHeader ToLevelHeader(Ksh chart)
|
||||
{
|
||||
return new VoxLevelHeader
|
||||
{
|
||||
Difficulty = chart.Difficulty,
|
||||
Illustrator = chart.Illustrator,
|
||||
Effector = chart.Effector,
|
||||
Level = chart.Level
|
||||
};
|
||||
}
|
||||
|
||||
private void Process()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool warned = false;
|
||||
foreach (var header in AssetManager.Headers)
|
||||
if (Result.BpmMin != Result.BpmMax && exporter.Source.MusicOffset % 48 != 0 && Options.RealignOffset)
|
||||
{
|
||||
if (Header.Ascii == header.Ascii)
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"Music Code is already exists.\n{AssetManager.GetMusicPath(header).Replace(AssetManager.GamePath, "")}",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
// 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
|
||||
);
|
||||
|
||||
if (prompt == DialogResult.No)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign metadata
|
||||
Header.Ascii = AsciiTextBox.Text;
|
||||
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>();
|
||||
|
||||
if (Directory.Exists(AssetManager.GetMusicPath(Header)))
|
||||
if (Directory.Exists(AssetManager.GetMusicPath(Result)) || AssetManager.Headers.Any(h => h.Ascii == Result.Ascii))
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"Music asset for {Header.CodeName} is already exists.",
|
||||
$"Music Code {Result.CodeName} is already taken.",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
@ -275,155 +268,8 @@ namespace VoxCharger
|
||||
return;
|
||||
}
|
||||
|
||||
// Uhh, remove empty level?
|
||||
var entries = charts.Where(x => x.Value != null);
|
||||
charts = new Dictionary<Difficulty, ChartInfo>();
|
||||
foreach (var entry in entries)
|
||||
charts[entry.Key] = entry.Value;
|
||||
|
||||
// Assign level info and charts
|
||||
foreach (var chart in charts)
|
||||
{
|
||||
var info = chart.Value;
|
||||
if (!File.Exists(info.FileName))
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"Chart file was moved or deleted\n{info.FileName}",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
|
||||
charts[chart.Key] = null;
|
||||
UpdateUI();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If you happen to read the source, this is probably what you're looking for
|
||||
var ksh = new Ksh();
|
||||
ksh.Parse(info.FileName, Options);
|
||||
|
||||
var bpmCount = ksh.Events.Count(ev => ev is Event.BPM);
|
||||
if (!warned && bpmCount > 1 && ksh.MusicOffset % 48 != 0 && Options.RealignOffset)
|
||||
{
|
||||
// You've been warned!
|
||||
var prompt = MessageBox.Show(
|
||||
"Chart contains multiple bpm with music offset that non multiple of 48.\n" +
|
||||
"Adapting music offset could break the chart.\n\n" +
|
||||
"Do you want to continue?",
|
||||
"Warning",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
|
||||
warned = true;
|
||||
if (prompt == DialogResult.No)
|
||||
return;
|
||||
}
|
||||
|
||||
// Conversion is actually boring because its already "pre-converted"
|
||||
var vox = new VoxChart();
|
||||
vox.Import(ksh);
|
||||
|
||||
var level = ToLevelHeader(ksh);
|
||||
level.Chart = vox;
|
||||
|
||||
info.Source = ksh;
|
||||
Header.Levels[chart.Key] = charts[chart.Key].Header = level;
|
||||
}
|
||||
|
||||
// Prevent resource files being moved or deleted, copy them into temporary storage
|
||||
string musicFile = string.Empty;
|
||||
string tmpFile = string.Empty;
|
||||
foreach (var chart in charts)
|
||||
{
|
||||
var info = chart.Value;
|
||||
|
||||
// Make sure to reuse 2dx file for music that share same file
|
||||
if (string.IsNullOrEmpty(musicFile) || chart.Value.MusicFileName != musicFile)
|
||||
{
|
||||
string music = Path.Combine(Path.GetDirectoryName(info.FileName), info.Source.MusicFileName);
|
||||
if (File.Exists(music))
|
||||
{
|
||||
string tmp = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
$"{Path.GetRandomFileName()}{new FileInfo(info.Source.MusicFileName).Extension}"
|
||||
);
|
||||
|
||||
musicFile = music;
|
||||
info.MusicFileName = tmpFile = tmp;
|
||||
|
||||
File.Copy(music, tmp);
|
||||
}
|
||||
else
|
||||
info.MusicFileName = string.Empty;
|
||||
}
|
||||
else
|
||||
info.MusicFileName = tmpFile;
|
||||
|
||||
string jacket = Path.Combine(Path.GetDirectoryName(info.FileName), info.Source.JacketFileName);
|
||||
if (File.Exists(jacket))
|
||||
{
|
||||
string tmp = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
$"{Path.GetRandomFileName()}{new FileInfo(info.Source.JacketFileName).Extension}"
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
using (var image = Image.FromFile(jacket))
|
||||
info.Header.Jacket = new Bitmap(image);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
info.Header.Jacket = null;
|
||||
}
|
||||
|
||||
info.JacketFileName = tmp;
|
||||
File.Copy(jacket, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
Action = new Action(() =>
|
||||
{
|
||||
bool unique = false;
|
||||
musicFile = charts.Values.First().MusicFileName;
|
||||
foreach (var chart in charts)
|
||||
{
|
||||
if (chart.Value.MusicFileName != musicFile)
|
||||
{
|
||||
unique = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Import all music assets
|
||||
AssetManager.ImportVox(Header);
|
||||
|
||||
// Make sure to use single asset for music for shared music file
|
||||
if (!unique)
|
||||
{
|
||||
AssetManager.Import2DX(musicFile, Header);
|
||||
AssetManager.Import2DX(musicFile, Header, true);
|
||||
}
|
||||
|
||||
foreach (var chart in charts.Values)
|
||||
{
|
||||
if (unique && File.Exists(chart.MusicFileName))
|
||||
{
|
||||
AssetManager.Import2DX(chart.MusicFileName, Header, chart.Header.Difficulty);
|
||||
AssetManager.Import2DX(chart.MusicFileName, Header, chart.Header.Difficulty, true);
|
||||
}
|
||||
|
||||
if (File.Exists(chart.JacketFileName))
|
||||
{
|
||||
using (var image = Image.FromFile(chart.JacketFileName))
|
||||
AssetManager.ImportJacket(Header, chart.Header.Difficulty, new Bitmap(image));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exporter.Export(Result, charts, Options);
|
||||
Action = exporter.Action;
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
@ -431,42 +277,140 @@ namespace VoxCharger
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"Failed to import ksh chart.\n{ex.Message}",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
$"Failed to import ksh chart.\n{ex.Message}",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
|
||||
// Eliminate non-existent files
|
||||
foreach (var chart in charts.Values.ToArray())
|
||||
{
|
||||
if (!File.Exists(chart.FileName))
|
||||
charts.Remove(chart.Header.Difficulty);
|
||||
}
|
||||
|
||||
// Reload jacket
|
||||
UpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void Convert()
|
||||
private void BulkImport()
|
||||
{
|
||||
// Only serve as options dialog
|
||||
if (string.IsNullOrEmpty(target))
|
||||
var output = new List<VoxHeader>();
|
||||
var actions = new Dictionary<string, Action>();
|
||||
var errors = new List<string>();
|
||||
using (var loader = new LoadingForm())
|
||||
{
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
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);
|
||||
|
||||
var files = Directory.GetFiles(dir, "*.ksh");
|
||||
if (files.Length == 0)
|
||||
continue;
|
||||
|
||||
string fn = files[0];
|
||||
|
||||
try
|
||||
{
|
||||
var ksh = new Ksh();
|
||||
ksh.Parse(fn, Options);
|
||||
|
||||
var header = ksh.ToHeader();
|
||||
header.ID = AssetManager.GetNextMusicID();
|
||||
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;
|
||||
}
|
||||
|
||||
var charts = Ksh.Exporter.GetCharts(Path.GetDirectoryName(fn), header.Title);
|
||||
var exporter = new Ksh.Exporter(ksh);
|
||||
|
||||
header.Ascii = ascii;
|
||||
exporter.Export(header, charts, Options);
|
||||
|
||||
output.Add(header);
|
||||
actions.Add(ascii, exporter.Action);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
try
|
||||
if (errors.Count != 0)
|
||||
{
|
||||
if (File.Exists(target) || Directory.Exists(target))
|
||||
{
|
||||
if (File.Exists(target))
|
||||
SingleConvert(Options);
|
||||
else if (Directory.Exists(target))
|
||||
BulkConvert(Options);
|
||||
}
|
||||
else
|
||||
string message = "Failed to import one or more charts:";
|
||||
foreach (string err in errors)
|
||||
message += $"\n{err}";
|
||||
|
||||
MessageBox.Show(
|
||||
message,
|
||||
"Warning",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
|
||||
ResultSet = output.ToArray();
|
||||
ActionSet = actions;
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void SingleConvert()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Single convert
|
||||
using (var browser = new SaveFileDialog())
|
||||
{
|
||||
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(
|
||||
"Target path not found",
|
||||
"Error",
|
||||
"Chart has been converted successfully",
|
||||
"Information",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
|
||||
DialogResult = DialogResult.Cancel;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
@ -481,31 +425,91 @@ namespace VoxCharger
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<Difficulty, ChartInfo> GetCharts(string target, Ksh main)
|
||||
private void BulkConvert()
|
||||
{
|
||||
// Try to locate another difficulty
|
||||
string dir = Path.GetDirectoryName(target);
|
||||
var charts = new Dictionary<Difficulty, ChartInfo>();
|
||||
foreach (string fn in Directory.GetFiles(dir, "*.ksh"))
|
||||
var errors = new List<string>();
|
||||
using (var browser = new FolderBrowserDialog())
|
||||
{
|
||||
try
|
||||
browser.Description = "Select output directory";
|
||||
if (browser.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
using (var loader = new LoadingForm())
|
||||
{
|
||||
var chart = new Ksh();
|
||||
chart.Parse(fn);
|
||||
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")));
|
||||
|
||||
// Different chart
|
||||
if (chart.Title != main.Title)
|
||||
continue;
|
||||
// If you happen to read the source, you're probably looking for these boring lines
|
||||
var ksh = new Ksh();
|
||||
ksh.Parse(fn, Options);
|
||||
|
||||
charts[chart.Difficulty] = new ChartInfo(chart, ToLevelHeader(chart), fn);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Failed attempt to parse ksh file: {0} ({1})", fn, ex.Message);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
return charts;
|
||||
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();
|
||||
}
|
||||
|
||||
private void LoadJacket(ChartInfo info)
|
||||
@ -590,9 +594,9 @@ namespace VoxCharger
|
||||
{
|
||||
switch (diff)
|
||||
{
|
||||
case Difficulty.Novice: button.Text = "NOV"; break;
|
||||
case Difficulty.Novice: button.Text = "NOV"; break;
|
||||
case Difficulty.Advanced: button.Text = "ADV"; break;
|
||||
case Difficulty.Exhaust: button.Text = "EXH"; break;
|
||||
case Difficulty.Exhaust: button.Text = "EXH"; break;
|
||||
default:
|
||||
button.Text = InfVerDropDown.SelectedItem.ToString();
|
||||
break;
|
||||
@ -603,134 +607,5 @@ namespace VoxCharger
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SingleConvert(Ksh.ParseOption options)
|
||||
{
|
||||
// Single convert
|
||||
using (var browser = new SaveFileDialog())
|
||||
{
|
||||
browser.Filter = "Sound Voltex Chart File|*.vox|All Files|*.*";
|
||||
if (browser.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var ksh = new Ksh();
|
||||
ksh.Parse(target, options);
|
||||
|
||||
var bpmCount = ksh.Events.Count(ev => ev is Event.BPM);
|
||||
if (bpmCount > 1 && ksh.MusicOffset % 48 != 0 && options.RealignOffset)
|
||||
{
|
||||
// You've been warned!
|
||||
var prompt = MessageBox.Show(
|
||||
"Chart contains multiple bpm with music offset that non multiple of 48.\n" +
|
||||
"Adapting music offset could break the chart.\n\n" +
|
||||
"Do you want to continue?",
|
||||
"Warning",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
|
||||
if (prompt == DialogResult.No)
|
||||
return;
|
||||
}
|
||||
|
||||
var vox = new VoxChart();
|
||||
vox.Import(ksh);
|
||||
vox.Serialize(browser.FileName);
|
||||
|
||||
MessageBox.Show(
|
||||
"Chart has been converted successfully",
|
||||
"Information",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void BulkConvert(Ksh.ParseOption options)
|
||||
{
|
||||
bool warned = false;
|
||||
using (var browser = new FolderBrowserDialog())
|
||||
{
|
||||
browser.Description = "Select output directory";
|
||||
if (browser.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
using (var loader = new LoadingForm())
|
||||
{
|
||||
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
|
||||
{
|
||||
var ksh = new Ksh();
|
||||
ksh.Parse(fn, options);
|
||||
|
||||
var bpmCount = ksh.Events.Count(ev => ev is Event.BPM);
|
||||
if (!warned && bpmCount > 1 && ksh.MusicOffset % 48 != 0 && options.RealignOffset)
|
||||
{
|
||||
// You've been warned!
|
||||
var prompt = MessageBox.Show(
|
||||
"Chart contains multiple bpm with music offset that non multiple of 48.\n" +
|
||||
"Adapting music offset could break the chart.\n\n" +
|
||||
"Do you want to continue?",
|
||||
"Warning",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
|
||||
warned = true;
|
||||
if (prompt == DialogResult.No)
|
||||
return;
|
||||
}
|
||||
|
||||
var vox = new VoxChart();
|
||||
vox.Import(ksh);
|
||||
|
||||
string path = Path.Combine(
|
||||
$"{browser.SelectedPath}",
|
||||
$"{Path.GetFileName(dir)}\\"
|
||||
);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
vox.Serialize($"{Path.Combine(path, Path.GetFileName(fn.Replace(".ksh", ".vox")))}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Failed attempt to convert ksh file: {0} ({1})", fn, ex.Message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loader.Complete();
|
||||
});
|
||||
|
||||
loader.SetAction(action);
|
||||
loader.ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show(
|
||||
"Chart has been converted successfully",
|
||||
"Information",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
Sources/Forms/MainForm.Designer.cs
generated
44
Sources/Forms/MainForm.Designer.cs
generated
@ -46,6 +46,8 @@
|
||||
this.AddEditMenu = new System.Windows.Forms.MenuItem();
|
||||
this.AddNewEditMenu = new System.Windows.Forms.MenuItem();
|
||||
this.ImportKshEditMenu = new System.Windows.Forms.MenuItem();
|
||||
this.MenuSeparator7 = new System.Windows.Forms.MenuItem();
|
||||
this.BulkImportKshEditMenu = new System.Windows.Forms.MenuItem();
|
||||
this.RemoveEditMenu = new System.Windows.Forms.MenuItem();
|
||||
this.MenuSeparator4 = new System.Windows.Forms.MenuItem();
|
||||
this.Import2DXEditMenu = new System.Windows.Forms.MenuItem();
|
||||
@ -69,6 +71,8 @@
|
||||
this.ImportContextMenu = new System.Windows.Forms.ContextMenu();
|
||||
this.ImportVoxMenu = new System.Windows.Forms.MenuItem();
|
||||
this.ImportKshMenu = new System.Windows.Forms.MenuItem();
|
||||
this.MenuSeparator8 = new System.Windows.Forms.MenuItem();
|
||||
this.BulkImportKshMenu = new System.Windows.Forms.MenuItem();
|
||||
this.RemoveButton = new System.Windows.Forms.Button();
|
||||
this.MusicListBox = new System.Windows.Forms.ListBox();
|
||||
this.MetadataGroupBox = new System.Windows.Forms.GroupBox();
|
||||
@ -228,7 +232,9 @@
|
||||
this.AddEditMenu.Index = 0;
|
||||
this.AddEditMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
|
||||
this.AddNewEditMenu,
|
||||
this.ImportKshEditMenu});
|
||||
this.ImportKshEditMenu,
|
||||
this.MenuSeparator7,
|
||||
this.BulkImportKshEditMenu});
|
||||
this.AddEditMenu.Shortcut = System.Windows.Forms.Shortcut.Ins;
|
||||
this.AddEditMenu.Text = "Add";
|
||||
//
|
||||
@ -241,8 +247,19 @@
|
||||
// ImportKshEditMenu
|
||||
//
|
||||
this.ImportKshEditMenu.Index = 1;
|
||||
this.ImportKshEditMenu.Text = "Import from Ksh..";
|
||||
this.ImportKshEditMenu.Click += new System.EventHandler(this.OnImportKshMenuClick);
|
||||
this.ImportKshEditMenu.Text = "Import Ksh..";
|
||||
this.ImportKshEditMenu.Click += new System.EventHandler(this.OnSingleImportMenuClick);
|
||||
//
|
||||
// MenuSeparator7
|
||||
//
|
||||
this.MenuSeparator7.Index = 2;
|
||||
this.MenuSeparator7.Text = "-";
|
||||
//
|
||||
// BulkImportKshEditMenu
|
||||
//
|
||||
this.BulkImportKshEditMenu.Index = 3;
|
||||
this.BulkImportKshEditMenu.Text = "Bulk Import Ksh..";
|
||||
this.BulkImportKshEditMenu.Click += new System.EventHandler(this.OnBulkImportKshMenuClick);
|
||||
//
|
||||
// RemoveEditMenu
|
||||
//
|
||||
@ -410,7 +427,9 @@
|
||||
//
|
||||
this.ImportContextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
|
||||
this.ImportVoxMenu,
|
||||
this.ImportKshMenu});
|
||||
this.ImportKshMenu,
|
||||
this.MenuSeparator8,
|
||||
this.BulkImportKshMenu});
|
||||
//
|
||||
// ImportVoxMenu
|
||||
//
|
||||
@ -422,7 +441,18 @@
|
||||
//
|
||||
this.ImportKshMenu.Index = 1;
|
||||
this.ImportKshMenu.Text = "Import Ksh..";
|
||||
this.ImportKshMenu.Click += new System.EventHandler(this.OnImportKshMenuClick);
|
||||
this.ImportKshMenu.Click += new System.EventHandler(this.OnSingleImportMenuClick);
|
||||
//
|
||||
// MenuSeparator8
|
||||
//
|
||||
this.MenuSeparator8.Index = 2;
|
||||
this.MenuSeparator8.Text = "-";
|
||||
//
|
||||
// BulkImportKshMenu
|
||||
//
|
||||
this.BulkImportKshMenu.Index = 3;
|
||||
this.BulkImportKshMenu.Text = "Bulk Import Ksh..";
|
||||
this.BulkImportKshMenu.Click += new System.EventHandler(this.OnBulkImportKshMenuClick);
|
||||
//
|
||||
// RemoveButton
|
||||
//
|
||||
@ -1079,6 +1109,10 @@
|
||||
private System.Windows.Forms.MenuItem BulkConvertToolsMenu;
|
||||
private System.Windows.Forms.MenuItem MenuSeparator6;
|
||||
private System.Windows.Forms.MenuItem AutosaveEditMenu;
|
||||
private System.Windows.Forms.MenuItem BulkImportKshEditMenu;
|
||||
private System.Windows.Forms.MenuItem MenuSeparator7;
|
||||
private System.Windows.Forms.MenuItem MenuSeparator8;
|
||||
private System.Windows.Forms.MenuItem BulkImportKshMenu;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,7 +259,7 @@ namespace VoxCharger
|
||||
if (browser.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
using (var converter = new ConverterForm(browser.FileName, true))
|
||||
using (var converter = new ConverterForm(browser.FileName, ConvertMode.Converter))
|
||||
converter.ShowDialog();
|
||||
}
|
||||
}
|
||||
@ -272,7 +272,7 @@ namespace VoxCharger
|
||||
if (browser.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
using (var converter = new ConverterForm(browser.SelectedPath, true))
|
||||
using (var converter = new ConverterForm(browser.SelectedPath, ConvertMode.Converter))
|
||||
converter.ShowDialog();
|
||||
}
|
||||
}
|
||||
@ -453,7 +453,7 @@ namespace VoxCharger
|
||||
MusicListBox.Items.Add(header);
|
||||
}
|
||||
|
||||
private void OnImportKshMenuClick(object sender, EventArgs e)
|
||||
private void OnSingleImportMenuClick(object sender, EventArgs e)
|
||||
{
|
||||
using (var browser = new OpenFileDialog())
|
||||
{
|
||||
@ -463,12 +463,12 @@ namespace VoxCharger
|
||||
if (browser.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
using (var converter = new ConverterForm(browser.FileName))
|
||||
using (var converter = new ConverterForm(browser.FileName, ConvertMode.Importer))
|
||||
{
|
||||
if (converter.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var header = converter.Header;
|
||||
var header = converter.Result;
|
||||
if (!actions.ContainsKey(header.Ascii))
|
||||
actions[header.Ascii] = new Queue<Action>();
|
||||
|
||||
@ -483,6 +483,37 @@ namespace VoxCharger
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBulkImportKshMenuClick(object sender, EventArgs e)
|
||||
{
|
||||
using (var browser = new FolderBrowserDialog())
|
||||
{
|
||||
browser.Description = "Select Kshoot chart repository";
|
||||
if (browser.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
using (var converter = new ConverterForm(browser.SelectedPath, ConvertMode.BulkImporter))
|
||||
{
|
||||
if (converter.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
foreach (var header in converter.ResultSet)
|
||||
{
|
||||
if (!actions.ContainsKey(header.Ascii))
|
||||
actions[header.Ascii] = new Queue<Action>();
|
||||
|
||||
AssetManager.Headers.Add(header);
|
||||
MusicListBox.Items.Add(header);
|
||||
|
||||
actions[header.Ascii].Enqueue(converter.ActionSet[header.Ascii]);
|
||||
}
|
||||
|
||||
Pristine = false;
|
||||
if (Autosave)
|
||||
Save(AssetManager.MdbFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMetadataChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is TextBox textBox && !textBox.Modified)
|
||||
@ -723,11 +754,11 @@ namespace VoxCharger
|
||||
{
|
||||
var proc = new Action(() =>
|
||||
{
|
||||
int max = actions.Count + 1;
|
||||
float it = 1f;
|
||||
foreach (var action in actions)
|
||||
{
|
||||
float progress = ((float)(max - actions.Count) / max) * 100f;
|
||||
loader.SetStatus($"[{progress:00}%] - Processing assets..");
|
||||
float progress = (it++ / actions.Count) * 100f;
|
||||
loader.SetStatus($"[{progress:00}%] - Processing {action.Key} assets..");
|
||||
loader.SetProgress(progress);
|
||||
|
||||
var queue = action.Value;
|
||||
@ -953,5 +984,6 @@ namespace VoxCharger
|
||||
ExplorerEditMenu.Enabled = false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
179
Sources/Ksh/Exporter.cs
Normal file
179
Sources/Ksh/Exporter.cs
Normal file
@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VoxCharger
|
||||
{
|
||||
|
||||
|
||||
public class ChartInfo
|
||||
{
|
||||
public Ksh Source { get; set; }
|
||||
public VoxLevelHeader Header { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string MusicFileName { get; set; }
|
||||
|
||||
public ChartInfo(Ksh source, VoxLevelHeader header, string fileName)
|
||||
{
|
||||
Source = source;
|
||||
Header = header;
|
||||
FileName = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Ksh
|
||||
{
|
||||
public class Exporter
|
||||
{
|
||||
public Ksh Source { get; private set; } = null;
|
||||
public Action Action { get; private set; } = null;
|
||||
|
||||
public Exporter(Ksh src)
|
||||
{
|
||||
Source = src;
|
||||
}
|
||||
|
||||
public void Export(VoxHeader header, ParseOption options = null)
|
||||
{
|
||||
Export(header, null, options);
|
||||
}
|
||||
|
||||
public void Export(VoxHeader header, Dictionary<Difficulty, ChartInfo> charts, ParseOption options = null)
|
||||
{
|
||||
// Assign level info and charts
|
||||
foreach (var chart in charts)
|
||||
{
|
||||
var info = chart.Value;
|
||||
if (!File.Exists(info.FileName))
|
||||
throw new IOException($"Chart file was moved or deleted\n{info.FileName}");
|
||||
|
||||
// If you happen to read the source, this is probably what you're looking for
|
||||
var ksh = new Ksh();
|
||||
ksh.Parse(info.FileName, options);
|
||||
|
||||
// Conversion is actually boring because its already "pre-converted"
|
||||
var vox = new VoxChart();
|
||||
vox.Import(ksh);
|
||||
|
||||
var level = ksh.ToLevelHeader();
|
||||
level.Chart = vox;
|
||||
|
||||
info.Source = ksh;
|
||||
header.Levels[chart.Key] = charts[chart.Key].Header = level;
|
||||
}
|
||||
|
||||
// Prevent resource files being moved or deleted, copy them into temporary storage
|
||||
string musicFile = string.Empty;
|
||||
string tmpFile = string.Empty;
|
||||
foreach (var chart in charts)
|
||||
{
|
||||
var info = chart.Value;
|
||||
|
||||
// Make sure to reuse 2dx file for music that share same file
|
||||
if (string.IsNullOrEmpty(musicFile) || chart.Value.MusicFileName != musicFile)
|
||||
{
|
||||
string music = Path.Combine(Path.GetDirectoryName(info.FileName), info.Source.MusicFileName);
|
||||
if (File.Exists(music))
|
||||
{
|
||||
string tmp = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
$"{Path.GetRandomFileName()}{new FileInfo(info.Source.MusicFileName).Extension}"
|
||||
);
|
||||
|
||||
musicFile = music;
|
||||
info.MusicFileName = tmpFile = tmp;
|
||||
|
||||
File.Copy(music, tmp);
|
||||
}
|
||||
else
|
||||
info.MusicFileName = string.Empty;
|
||||
}
|
||||
else
|
||||
info.MusicFileName = tmpFile;
|
||||
|
||||
string jacket = Path.Combine(Path.GetDirectoryName(info.FileName), info.Source.JacketFileName);
|
||||
if (File.Exists(jacket))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var image = Image.FromFile(jacket))
|
||||
info.Header.Jacket = new Bitmap(image);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
info.Header.Jacket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Action = new Action(() =>
|
||||
{
|
||||
bool unique = false;
|
||||
musicFile = charts.Values.First().MusicFileName;
|
||||
foreach (var chart in charts)
|
||||
{
|
||||
if (chart.Value.MusicFileName != musicFile)
|
||||
{
|
||||
unique = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Import all music assets
|
||||
AssetManager.ImportVox(header);
|
||||
|
||||
// Make sure to use single asset for music for shared music file
|
||||
if (!unique)
|
||||
{
|
||||
AssetManager.Import2DX(musicFile, header);
|
||||
AssetManager.Import2DX(musicFile, header, true);
|
||||
}
|
||||
|
||||
foreach (var chart in charts.Values)
|
||||
{
|
||||
if (unique && File.Exists(chart.MusicFileName))
|
||||
{
|
||||
AssetManager.Import2DX(chart.MusicFileName, header, chart.Header.Difficulty);
|
||||
AssetManager.Import2DX(chart.MusicFileName, header, chart.Header.Difficulty, true);
|
||||
}
|
||||
|
||||
if (chart.Header.Jacket != null)
|
||||
AssetManager.ImportJacket(header, chart.Header.Difficulty, chart.Header.Jacket);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Dictionary<Difficulty, ChartInfo> GetCharts(string dir, string title)
|
||||
{
|
||||
// Try to locate another difficulty
|
||||
var charts = new Dictionary<Difficulty, ChartInfo>();
|
||||
foreach (string fn in Directory.GetFiles(dir, "*.ksh"))
|
||||
{
|
||||
try
|
||||
{
|
||||
var chart = new Ksh();
|
||||
chart.Parse(fn);
|
||||
|
||||
// Different chart
|
||||
if (chart.Title != title)
|
||||
continue;
|
||||
|
||||
charts[chart.Difficulty] = new ChartInfo(chart, chart.ToLevelHeader(), fn);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Failed attempt to parse ksh file: {0} ({1})", fn, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return charts;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -44,6 +44,32 @@ namespace VoxCharger
|
||||
{
|
||||
}
|
||||
|
||||
public VoxHeader ToHeader()
|
||||
{
|
||||
return new VoxHeader()
|
||||
{
|
||||
Title = Title,
|
||||
Artist = Artist,
|
||||
BpmMin = BpmMin,
|
||||
BpmMax = BpmMax,
|
||||
Volume = Volume > 0 ? (short)Volume : (short)91,
|
||||
DistributionDate = DateTime.Now,
|
||||
BackgroundId = 63,
|
||||
GenreId = 16,
|
||||
};
|
||||
}
|
||||
|
||||
public VoxLevelHeader ToLevelHeader()
|
||||
{
|
||||
return new VoxLevelHeader
|
||||
{
|
||||
Difficulty = Difficulty,
|
||||
Illustrator = Illustrator,
|
||||
Effector = Effector,
|
||||
Level = Level
|
||||
};
|
||||
}
|
||||
|
||||
public void Parse(string fileName, ParseOption opt = null)
|
||||
{
|
||||
// I pulled all nighters for few days, all for this piece of trash codes :)
|
||||
@ -108,13 +134,8 @@ namespace VoxCharger
|
||||
if (opt.RealignOffset)
|
||||
position -= MusicOffset; // Attempt to align
|
||||
|
||||
time = Time.FromOffset(position, signature);
|
||||
//int p = time.AsAbsoluteOffset(signature);
|
||||
//if ((int)Math.Round(position) != p)
|
||||
// Debug.WriteLine("");
|
||||
|
||||
|
||||
// Magic happens here!
|
||||
time = Time.FromOffset(position, signature);
|
||||
if (time.Measure < 0)
|
||||
continue; // Might happen when try to realign music offset
|
||||
|
||||
|
@ -97,6 +97,7 @@
|
||||
<Compile Include="Sources\Events\Signature.cs" />
|
||||
<Compile Include="Sources\Events\Stop.cs" />
|
||||
<Compile Include="Sources\Events\TiltMode.cs" />
|
||||
<Compile Include="Sources\Ksh\Exporter.cs" />
|
||||
<Compile Include="Sources\Ksh\Ksh.cs" />
|
||||
<Compile Include="Sources\Events\EventCollection.cs" />
|
||||
<Compile Include="Sources\Events\Event.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user