1
0
mirror of https://github.com/SirusDoma/VoxCharger.git synced 2024-11-28 01:10:49 +01:00

Fix Music ID Handling

Also, Music ID field is now disabled
This commit is contained in:
SirusDoma 2020-04-19 15:02:55 +07:00
parent e3564e690d
commit 2d6eb38dff
5 changed files with 418 additions and 286 deletions

View File

@ -12,6 +12,8 @@ namespace VoxCharger
{ {
#region --- Properties --- #region --- Properties ---
private static List<string> MixList = new List<string>(); private static List<string> MixList = new List<string>();
private static MusicDb InternalHeaders = null;
private static int LastOriginalID = 0;
public static string MixName { get; private set; } public static string MixName { get; private set; }
@ -32,13 +34,6 @@ namespace VoxCharger
if (!File.Exists(dbFilename)) if (!File.Exists(dbFilename))
throw new FormatException("Invalid Game Directory"); throw new FormatException("Invalid Game Directory");
// Validate whether cache exists, perform full load when cache is not available
string cacheFilename = Path.Combine(gamePath, @"data_mods\_cache\others\music_db.xml");
// Load original headers data
Headers = new MusicDb();
Headers.Load(File.Exists(cacheFilename) ? cacheFilename : dbFilename);
// Look for other mixes // Look for other mixes
MixList.Clear(); MixList.Clear();
string modsPath = Path.Combine(gamePath, @"data_mods\"); string modsPath = Path.Combine(gamePath, @"data_mods\");
@ -59,10 +54,10 @@ namespace VoxCharger
continue; continue;
// Confirmed mod path, append into music db, ignore cache to avoid uncached mix being excluded // Confirmed mod path, append into music db, ignore cache to avoid uncached mix being excluded
Headers.Load(dbFilename, true);
MixList.Add(modName); MixList.Add(modName);
} }
LastOriginalID = 0;
GamePath = gamePath; GamePath = gamePath;
} }
@ -77,6 +72,9 @@ namespace VoxCharger
Directory.CreateDirectory(Path.Combine(mixPath, @"music\")); Directory.CreateDirectory(Path.Combine(mixPath, @"music\"));
Directory.CreateDirectory(Path.Combine(mixPath, @"others\")); Directory.CreateDirectory(Path.Combine(mixPath, @"others\"));
// Load Existing song DB to avoid duplicate id
LoadInternalDb(mixName);
// Create empty db // Create empty db
MdbFilename = Path.Combine(mixPath, @"others\music_db.merged.xml"); MdbFilename = Path.Combine(mixPath, @"others\music_db.merged.xml");
File.WriteAllText(MdbFilename, "<?xml version=\"1.0\" encoding=\"Shift_JIS\"?><mdb></mdb>"); File.WriteAllText(MdbFilename, "<?xml version=\"1.0\" encoding=\"Shift_JIS\"?><mdb></mdb>");
@ -93,7 +91,8 @@ namespace VoxCharger
if (!Directory.Exists(mixPath)) if (!Directory.Exists(mixPath))
throw new DirectoryNotFoundException("Mix directory missing"); throw new DirectoryNotFoundException("Mix directory missing");
// No way it happen since combo box is dropdownlist, but well.. :v // Load Existing song DB to avoid duplicate id
LoadInternalDb(mixName);
if (!string.IsNullOrEmpty(mixName) && !MixList.Contains(mixName)) if (!string.IsNullOrEmpty(mixName) && !MixList.Contains(mixName))
MixList.Add(mixName); MixList.Add(mixName);
@ -110,6 +109,38 @@ namespace VoxCharger
return MixList.ToArray(); return MixList.ToArray();
} }
private static void LoadInternalDb(string mixName)
{
// First, we need to populate available ids outside selected mix
// This will prevent duplicate ID not only with originals but with other mixes too
// Load original music, ignore cache to avoid uncached mix being excluded or included
string dbFilename = Path.Combine(GamePath, @"data\others\music_db.xml");
string modsPath = Path.Combine(GamePath, @"data_mods\");
// Load original headers data
InternalHeaders = new MusicDb();
InternalHeaders.Load(dbFilename);
LastOriginalID = InternalHeaders.LastID;
// Load other music db
foreach (var modDir in Directory.GetDirectories(modsPath))
{
// Get directory name, exclude selected mix
string modName = new DirectoryInfo(modDir).Name;
if (modName == "_cache" || modName == mixName)
continue;
// Validate whether the mod is a mix mod
// Do not skip unsupported mods, just read the db and ignore the rest of assets
dbFilename = Path.Combine(modDir, @"others\music_db.merged.xml");
if (!File.Exists(dbFilename))
continue;
// Confirmed mod path, append into music db
InternalHeaders.Load(dbFilename, true);
}
}
#endregion #endregion
#region --- Asset Management --- #region --- Asset Management ---
@ -217,6 +248,23 @@ namespace VoxCharger
#endregion #endregion
#region --- Asset Identifier --- #region --- Asset Identifier ---
public static int GetNextMusicID()
{
// TODO: This probably inefficient in scenario where one or more mix has gap between each ids
// This could be waste to those gaps, and eat up our precious limited id
// However, these gaps may indicate deleted song, which should be taken by omnimix
int id = InternalHeaders.LastID + 1;
while (InternalHeaders.Contains(id) || Headers.Contains(id)) // Contains is O(1) so its should be fine
id++;
return id;
}
public static bool ValidateMusicID(int id)
{
return !InternalHeaders.Contains(id);
}
public static string GetDifficultyCodes(VoxHeader header, Difficulty difficulty) public static string GetDifficultyCodes(VoxHeader header, Difficulty difficulty)
{ {

View File

@ -37,8 +37,9 @@ namespace VoxCharger
private bool converter; private bool converter;
private Dictionary<Difficulty, ChartInfo> charts = new Dictionary<Difficulty, ChartInfo>(); private Dictionary<Difficulty, ChartInfo> charts = new Dictionary<Difficulty, ChartInfo>();
public VoxHeader Header { get; private set; } = null; public VoxHeader Header { get; private set; } = null;
public Action Action { get; private set; } = null; public Action Action { get; private set; } = null;
public Ksh.ParseOption Options { get; private set; } = new Ksh.ParseOption();
public ConverterForm(string path, bool asConverter = false) public ConverterForm(string path, bool asConverter = false)
{ {
@ -94,9 +95,9 @@ namespace VoxCharger
} }
defaultAscii = AsciiTextBox.Text = Header.Ascii; defaultAscii = AsciiTextBox.Text = Header.Ascii;
BackgroundDropDown.SelectedItem = LastBackground; BackgroundDropDown.SelectedItem = LastBackground;
VersionDropDown.SelectedIndex = 4; VersionDropDown.SelectedIndex = 4;
InfVerDropDown.SelectedIndex = 0; InfVerDropDown.SelectedIndex = 0;
charts[main.Difficulty] = new ChartInfo(main, ToLevelHeader(main), target); charts[main.Difficulty] = new ChartInfo(main, ToLevelHeader(main), target);
LoadJacket(charts[main.Difficulty]); LoadJacket(charts[main.Difficulty]);
@ -110,30 +111,12 @@ namespace VoxCharger
MessageBoxIcon.Error MessageBoxIcon.Error
); );
CancelButton.PerformClick(); Close();
return;
} }
// Try to locate another difficulty // Try to locate another difficulty
string dir = Path.GetDirectoryName(target); charts = GetCharts(target, main);
foreach (string fn in Directory.GetFiles(dir, "*.ksh"))
{
try
{
var chart = new Ksh();
chart.Parse(fn);
// Different chart
if (chart.Title != main.Title)
continue;
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);
}
}
UpdateUI(); UpdateUI();
} }
@ -207,8 +190,7 @@ namespace VoxCharger
private void OnProcessConvertButtonClick(object sender, EventArgs e) private void OnProcessConvertButtonClick(object sender, EventArgs e)
{ {
bool warned = false; Options = new Ksh.ParseOption()
var options = new Ksh.ParseOption()
{ {
RealignOffset = RealignOffsetCheckBox.Checked, RealignOffset = RealignOffsetCheckBox.Checked,
EnableChipFx = ChipFxCheckBox.Checked, EnableChipFx = ChipFxCheckBox.Checked,
@ -221,235 +203,16 @@ namespace VoxCharger
// Act as converter // Act as converter
if (converter) if (converter)
{ Convert();
try else
{ Process();
if (File.Exists(target) || Directory.Exists(target))
{
if (File.Exists(target))
SingleConvert(options);
else if (Directory.Exists(target))
BulkConvert(options);
}
else
{
MessageBox.Show(
"Target path not found",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
Close();
}
}
catch (Exception ex)
{
MessageBox.Show(
$"Failed to convert ksh chart.\n{ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
return;
}
// Again, stupid input get stupid output
foreach (var header in AssetManager.Headers)
{
if (Header.Ascii == header.Ascii)
{
MessageBox.Show(
$"Music Code is already exists.\n{AssetManager.GetMusicPath(header)}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
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>();
// Again, stupid input get stupid output
if (Directory.Exists(AssetManager.GetMusicPath(Header)))
{
MessageBox.Show(
$"Music asset for {Header.CodeName} is already exists.",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
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;
}
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));
}
}
});
DialogResult = DialogResult.OK;
Close();
} }
private VoxHeader ToHeader(Ksh chart) private VoxHeader ToHeader(Ksh chart)
{ {
return new VoxHeader() return new VoxHeader()
{ {
ID = AssetManager.Headers.LastID + 1, ID = AssetManager.GetNextMusicID(),
Title = chart.Title, Title = chart.Title,
Artist = chart.Artist, Artist = chart.Artist,
BpmMin = chart.BpmMin, BpmMin = chart.BpmMin,
@ -472,6 +235,279 @@ namespace VoxCharger
}; };
} }
private void Process()
{
try
{
bool warned = false;
foreach (var header in AssetManager.Headers)
{
if (Header.Ascii == header.Ascii)
{
MessageBox.Show(
$"Music Code is already exists.\n{AssetManager.GetMusicPath(header).Replace(AssetManager.GamePath, "")}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
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)))
{
MessageBox.Show(
$"Music asset for {Header.CodeName} is already exists.",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
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));
}
}
});
DialogResult = DialogResult.OK;
Close();
}
catch (Exception ex)
{
MessageBox.Show(
$"Failed to import ksh chart.\n{ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
}
private void Convert()
{
// Only serve as options dialog
if (string.IsNullOrEmpty(target))
{
DialogResult = DialogResult.OK;
Close();
}
try
{
if (File.Exists(target) || Directory.Exists(target))
{
if (File.Exists(target))
SingleConvert(Options);
else if (Directory.Exists(target))
BulkConvert(Options);
}
else
{
MessageBox.Show(
"Target path not found",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
DialogResult = DialogResult.Cancel;
Close();
}
}
catch (Exception ex)
{
MessageBox.Show(
$"Failed to convert ksh chart.\n{ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
}
private Dictionary<Difficulty, ChartInfo> GetCharts(string target, Ksh main)
{
// Try to locate another difficulty
string dir = Path.GetDirectoryName(target);
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 != main.Title)
continue;
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);
}
}
return charts;
}
private void LoadJacket(ChartInfo info) private void LoadJacket(ChartInfo info)
{ {
var chart = info.Source; var chart = info.Source;
@ -608,6 +644,7 @@ namespace VoxCharger
MessageBoxIcon.Information MessageBoxIcon.Information
); );
DialogResult = DialogResult.OK;
Close(); Close();
} }
} }
@ -692,6 +729,7 @@ namespace VoxCharger
MessageBoxIcon.Information MessageBoxIcon.Information
); );
DialogResult = DialogResult.OK;
Close(); Close();
} }
} }

View File

@ -858,9 +858,9 @@
this.IdTextBox.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.IdTextBox.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.IdTextBox.Location = new System.Drawing.Point(83, 23); this.IdTextBox.Location = new System.Drawing.Point(83, 23);
this.IdTextBox.Name = "IdTextBox"; this.IdTextBox.Name = "IdTextBox";
this.IdTextBox.ReadOnly = true;
this.IdTextBox.Size = new System.Drawing.Size(383, 21); this.IdTextBox.Size = new System.Drawing.Size(383, 21);
this.IdTextBox.TabIndex = 1; this.IdTextBox.TabIndex = 1;
this.IdTextBox.TextChanged += new System.EventHandler(this.OnMetadataChanged);
// //
// IdLabel // IdLabel
// //

View File

@ -107,13 +107,15 @@ namespace VoxCharger
{ {
try try
{ {
Save(AssetManager.MdbFilename); if (Save(AssetManager.MdbFilename))
MessageBox.Show( {
"Mix has been saved successfully", MessageBox.Show(
"Information", "Mix has been saved successfully",
MessageBoxButtons.OK, "Information",
MessageBoxIcon.Information MessageBoxButtons.OK,
); MessageBoxIcon.Information
);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -139,13 +141,15 @@ namespace VoxCharger
if (exporter.ShowDialog() != DialogResult.OK) if (exporter.ShowDialog() != DialogResult.OK)
return; return;
Save(exporter.FileName); if (Save(exporter.FileName))
MessageBox.Show( {
"Mix has been saved successfully", MessageBox.Show(
"Information", "Mix has been saved successfully",
MessageBoxButtons.OK, "Information",
MessageBoxIcon.Information MessageBoxButtons.OK,
); MessageBoxIcon.Information
);
}
} }
} }
catch (Exception ex) catch (Exception ex)
@ -491,7 +495,16 @@ namespace VoxCharger
return; return;
if (int.TryParse(IdTextBox.Text, out int id)) if (int.TryParse(IdTextBox.Text, out int id))
header.ID = id; {
// Validate ID
if (!AssetManager.ValidateMusicID(id))
{
IdTextBox.Text = header.ID.ToString();
MessageBox.Show("Music ID is already taken", "Duplicate", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
header.ID = id;
}
else else
IdTextBox.Text = header.ID.ToString(); IdTextBox.Text = header.ID.ToString();
@ -703,19 +716,21 @@ namespace VoxCharger
} }
} }
private void Save(string dbFilename) private bool Save(string dbFilename)
{ {
var errors = new List<string>();
using (var loader = new LoadingForm()) using (var loader = new LoadingForm())
{ {
var proc = new Action(() => var proc = new Action(() =>
{ {
int max = actions.Count + 1; int max = actions.Count + 1;
foreach (var queue in actions.Values) foreach (var action in actions)
{ {
float progress = ((float)(max - actions.Count) / max) * 100f; float progress = ((float)(max - actions.Count) / max) * 100f;
loader.SetStatus($"[{progress:00}%] - Processing assets.."); loader.SetStatus($"[{progress:00}%] - Processing assets..");
loader.SetProgress(progress); loader.SetProgress(progress);
var queue = action.Value;
while (queue.Count > 0) while (queue.Count > 0)
{ {
try try
@ -724,6 +739,7 @@ namespace VoxCharger
} }
catch (Exception ex) catch (Exception ex)
{ {
errors.Add($"{action.Key}: {ex.Message}");
Debug.WriteLine(ex.Message); Debug.WriteLine(ex.Message);
} }
} }
@ -740,8 +756,19 @@ namespace VoxCharger
loader.ShowDialog(); loader.ShowDialog();
} }
if (errors.Count > 0)
{
string message = "Error occured when processing following assets:\n";
foreach (var err in errors)
message += $"\n{err}";
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
actions.Clear(); actions.Clear();
Pristine = true; Pristine = true;
return errors.Count == 0;
} }
private void Import2DX(bool preview = false) private void Import2DX(bool preview = false)
@ -880,6 +907,8 @@ namespace VoxCharger
control.Enabled = safe; control.Enabled = safe;
} }
// Modifying this could lead into disaster, must be left untouched
IdTextBox.ReadOnly = true;
LevelGroupBox.Enabled = true; LevelGroupBox.Enabled = true;
foreach (Control control in LevelGroupBox.Controls) foreach (Control control in LevelGroupBox.Controls)
{ {

View File

@ -15,10 +15,11 @@ namespace VoxCharger
private static readonly Encoding DefaultEncoding = Encoding.GetEncoding("Shift_JIS"); private static readonly Encoding DefaultEncoding = Encoding.GetEncoding("Shift_JIS");
private Dictionary<int, VoxHeader> headers; private Dictionary<int, VoxHeader> headers;
private int max = 0;
public int LastID => headers.Count > 0 ? headers.Values.Max(h => h.ID) : 0; public int Count => headers.Count;
public int Count => headers.Count; public int LastID => headers.Values.Max(h => h.ID);
public MusicDb() public MusicDb()
{ {
@ -178,17 +179,33 @@ namespace VoxCharger
public void Add(VoxHeader header) public void Add(VoxHeader header)
{ {
if (max < header.ID)
max = header.ID;
headers[header.ID] = header; headers[header.ID] = header;
} }
public void Remove(int id) public void Remove(int id)
{ {
if (max == id)
max = 0;
headers.Remove(id); headers.Remove(id);
} }
public void Remove(VoxHeader header) public void Remove(VoxHeader header)
{ {
headers.Remove(header.ID); Remove(header.ID);
}
public bool Contains(int id)
{
return headers.ContainsKey(id);
}
public bool Contains(VoxHeader header)
{
return Contains(header.ID);
} }
public VoxHeader GetHeader(int id) public VoxHeader GetHeader(int id)