diff --git a/VG Music Studio/Core/GBA/MP2K/Player.cs b/VG Music Studio/Core/GBA/MP2K/Player.cs index 954b8608..e3a3e237 100644 --- a/VG Music Studio/Core/GBA/MP2K/Player.cs +++ b/VG Music Studio/Core/GBA/MP2K/Player.cs @@ -1,4 +1,5 @@ -using Kermalis.VGMusicStudio.Properties; +using Kermalis.EndianBinaryIO; +using Kermalis.VGMusicStudio.Properties; using Kermalis.VGMusicStudio.Util; using Sanford.Multimedia.Midi; using System; @@ -9,9 +10,9 @@ namespace Kermalis.VGMusicStudio.Core.GBA.MP2K { - internal class Player : IPlayer + internal sealed class Player : IPlayer { - public class MIDISaveArgs + public sealed class MIDISaveArgs { public bool SaveCommandsBeforeTranspose; public bool ReverseVolume; @@ -22,7 +23,9 @@ public class MIDISaveArgs private readonly Config _config; private readonly TimeBarrier _time; private Thread _thread; - private int _voiceTableOffset = -1; + private byte[] _songBinary; + public int VoiceTableOffset { get; private set; } = -1; + private string[] _voiceTypeCache; private Track[] _tracks; private ushort _tempo; private int _tempoStack; @@ -113,6 +116,12 @@ private void SetTicks() } public void LoadSong(long index) { + SongEntry entry = _config.Reader.ReadObject(_config.SongTableOffsets[0] + (index * 8)); + LoadSong(_config.ROM, _config.Reader, entry.HeaderOffset - GBA.Utils.CartridgeOffset); + } + public void LoadSong(byte[] songBinary, EndianBinaryReader songBinaryReader, long songHeaderOffset) + { + _songBinary = songBinary; if (_tracks != null) { for (int i = 0; i < _tracks.Length; i++) @@ -122,11 +131,10 @@ public void LoadSong(long index) _tracks = null; } Events = null; - SongEntry entry = _config.Reader.ReadObject(_config.SongTableOffsets[0] + (index * 8)); - SongHeader header = _config.Reader.ReadObject(entry.HeaderOffset - GBA.Utils.CartridgeOffset); - int oldVoiceTableOffset = _voiceTableOffset; - _voiceTableOffset = header.VoiceTableOffset - GBA.Utils.CartridgeOffset; - if (oldVoiceTableOffset != _voiceTableOffset) + SongHeader header = songBinaryReader.ReadObject(songHeaderOffset); + int oldVoiceTableOffset = VoiceTableOffset; + VoiceTableOffset = header.VoiceTableOffset - GBA.Utils.CartridgeOffset; + if (oldVoiceTableOffset != VoiceTableOffset) { _voiceTypeCache = new string[byte.MaxValue + 1]; } @@ -147,11 +155,11 @@ bool EventExists(long offset) AddEvents(trackStart); void AddEvents(long startOffset) { - _config.Reader.BaseStream.Position = startOffset; + songBinaryReader.BaseStream.Position = startOffset; bool cont = true; while (cont) { - long offset = _config.Reader.BaseStream.Position; + long offset = songBinaryReader.BaseStream.Position; void AddEvent(ICommand command) { Events[trackIndex].Add(new SongEvent(offset, command)); @@ -171,7 +179,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } } - byte cmd = _config.Reader.ReadByte(); + byte cmd = songBinaryReader.ReadByte(); if (cmd >= 0xBD) // Commands that work within running status { runCmd = cmd; @@ -182,7 +190,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) if (runCmd >= 0xCF && cmd <= 0x7F) // Within running status { byte velocity, addedDuration; - byte[] peek = _config.Reader.PeekBytes(2); + byte[] peek = songBinaryReader.PeekBytes(2); if (peek[0] > 0x7F) { velocity = prevVelocity; @@ -190,20 +198,20 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } else if (peek[1] > 3) { - velocity = _config.Reader.ReadByte(); + velocity = songBinaryReader.ReadByte(); addedDuration = 0; } else { - velocity = _config.Reader.ReadByte(); - addedDuration = _config.Reader.ReadByte(); + velocity = songBinaryReader.ReadByte(); + addedDuration = songBinaryReader.ReadByte(); } EmulateNote(cmd, velocity, addedDuration); } else if (cmd >= 0xCF) { byte key, velocity, addedDuration; - byte[] peek = _config.Reader.PeekBytes(3); + byte[] peek = songBinaryReader.PeekBytes(3); if (peek[0] > 0x7F) { key = prevKey; @@ -212,22 +220,22 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } else if (peek[1] > 0x7F) { - key = _config.Reader.ReadByte(); + key = songBinaryReader.ReadByte(); velocity = prevVelocity; addedDuration = 0; } // TIE (0xCF) cannot have an added duration so it needs to stop here else if (cmd == 0xCF || peek[2] > 3) { - key = _config.Reader.ReadByte(); - velocity = _config.Reader.ReadByte(); + key = songBinaryReader.ReadByte(); + velocity = songBinaryReader.ReadByte(); addedDuration = 0; } else { - key = _config.Reader.ReadByte(); - velocity = _config.Reader.ReadByte(); - addedDuration = _config.Reader.ReadByte(); + key = songBinaryReader.ReadByte(); + velocity = songBinaryReader.ReadByte(); + addedDuration = songBinaryReader.ReadByte(); } EmulateNote(key, velocity, addedDuration); } @@ -334,7 +342,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xCD: { - byte arg = _config.Reader.ReadByte(); + byte arg = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new LibraryCommand { Command = cmd, Argument = arg }); @@ -369,7 +377,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xB2: { - int jumpOffset = _config.Reader.ReadInt32() - GBA.Utils.CartridgeOffset; + int jumpOffset = songBinaryReader.ReadInt32() - GBA.Utils.CartridgeOffset; if (!EventExists(offset)) { AddEvent(new JumpCommand { Offset = jumpOffset }); @@ -383,17 +391,17 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xB3: { - int callOffset = _config.Reader.ReadInt32() - GBA.Utils.CartridgeOffset; + int callOffset = songBinaryReader.ReadInt32() - GBA.Utils.CartridgeOffset; if (!EventExists(offset)) { AddEvent(new CallCommand { Offset = callOffset }); } if (callStackDepth < 3) { - long backup = _config.Reader.BaseStream.Position; + long backup = songBinaryReader.BaseStream.Position; callStackDepth++; AddEvents(callOffset); - _config.Reader.BaseStream.Position = backup; + songBinaryReader.BaseStream.Position = backup; } else { @@ -416,8 +424,8 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } /*case 0xB5: // TODO: Logic so this isn't an infinite loop { - byte times = config.Reader.ReadByte(); - int repeatOffset = config.Reader.ReadInt32() - GBA.Utils.CartridgeOffset; + byte times = songBinaryReader.ReadByte(); + int repeatOffset = songBinaryReader.ReadInt32() - GBA.Utils.CartridgeOffset; if (!EventExists(offset)) { AddEvent(new RepeatCommand { Times = times, Offset = repeatOffset }); @@ -426,9 +434,9 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) }*/ case 0xB9: { - byte op = _config.Reader.ReadByte(); - byte address = _config.Reader.ReadByte(); - byte data = _config.Reader.ReadByte(); + byte op = songBinaryReader.ReadByte(); + byte address = songBinaryReader.ReadByte(); + byte data = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new MemoryAccessCommand { Operator = op, Address = address, Data = data }); @@ -437,7 +445,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xBA: { - byte priority = _config.Reader.ReadByte(); + byte priority = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new PriorityCommand { Priority = priority }); @@ -446,7 +454,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xBB: { - byte tempoArg = _config.Reader.ReadByte(); + byte tempoArg = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new TempoCommand { Tempo = (ushort)(tempoArg * 2) }); @@ -455,7 +463,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xBC: { - sbyte transpose = _config.Reader.ReadSByte(); + sbyte transpose = songBinaryReader.ReadSByte(); if (!EventExists(offset)) { AddEvent(new TransposeCommand { Transpose = transpose }); @@ -465,7 +473,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) // Commands that work within running status: case 0xBD: { - byte voice = _config.Reader.ReadByte(); + byte voice = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new VoiceCommand { Voice = voice }); @@ -474,7 +482,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xBE: { - byte volume = _config.Reader.ReadByte(); + byte volume = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new VolumeCommand { Volume = volume }); @@ -483,7 +491,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xBF: { - byte panArg = _config.Reader.ReadByte(); + byte panArg = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new PanpotCommand { Panpot = (sbyte)(panArg - 0x40) }); @@ -492,7 +500,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xC0: { - byte bendArg = _config.Reader.ReadByte(); + byte bendArg = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new PitchBendCommand { Bend = (sbyte)(bendArg - 0x40) }); @@ -501,7 +509,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xC1: { - byte range = _config.Reader.ReadByte(); + byte range = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new PitchBendRangeCommand { Range = range }); @@ -510,7 +518,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xC2: { - byte speed = _config.Reader.ReadByte(); + byte speed = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new LFOSpeedCommand { Speed = speed }); @@ -519,7 +527,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xC3: { - byte delay = _config.Reader.ReadByte(); + byte delay = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new LFODelayCommand { Delay = delay }); @@ -528,7 +536,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xC4: { - byte depth = _config.Reader.ReadByte(); + byte depth = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new LFODepthCommand { Depth = depth }); @@ -537,7 +545,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xC5: { - byte type = _config.Reader.ReadByte(); + byte type = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new LFOTypeCommand { Type = (LFOType)type }); @@ -546,7 +554,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xC8: { - byte tuneArg = _config.Reader.ReadByte(); + byte tuneArg = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new TuneCommand { Tune = (sbyte)(tuneArg - 0x40) }); @@ -555,8 +563,8 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xCD: { - byte command = _config.Reader.ReadByte(); - byte arg = _config.Reader.ReadByte(); + byte command = songBinaryReader.ReadByte(); + byte arg = songBinaryReader.ReadByte(); if (!EventExists(offset)) { AddEvent(new LibraryCommand { Command = command, Argument = arg }); @@ -565,7 +573,7 @@ void EmulateNote(byte key, byte velocity, byte addedDuration) } case 0xCE: { - int key = _config.Reader.PeekByte() <= 0x7F ? (prevKey = _config.Reader.ReadByte()) : -1; + int key = songBinaryReader.PeekByte() <= 0x7F ? (prevKey = songBinaryReader.ReadByte()) : -1; if (!EventExists(offset)) { AddEvent(new EndOfTieCommand { Key = key }); @@ -921,7 +929,6 @@ public void Dispose() WaitThread(); } } - private string[] _voiceTypeCache; public void GetSongState(UI.SongInfoControl.SongInfo info) { info.Tempo = _tempo; @@ -935,7 +942,7 @@ public void GetSongState(UI.SongInfoControl.SongInfo info) tin.LFO = track.LFODepth; if (_voiceTypeCache[track.Voice] == null) { - byte t = _config.ROM[_voiceTableOffset + (track.Voice * 0xC)]; + byte t = _config.ROM[VoiceTableOffset + (track.Voice * 0xC)]; if (t == (byte)VoiceFlags.KeySplit) { _voiceTypeCache[track.Voice] = "Key Split"; @@ -1018,7 +1025,7 @@ private void PlayNote(Track track, byte key, byte velocity, byte addedDuration) if (track.Ready) { bool fromDrum = false; - int offset = _voiceTableOffset + (track.Voice * 12); + int offset = VoiceTableOffset + (track.Voice * 12); while (true) { VoiceEntry v = _config.Reader.ReadObject(offset); @@ -1083,15 +1090,15 @@ private void PlayNote(Track track, byte key, byte velocity, byte addedDuration) } private void ExecuteNext(Track track, ref bool update) { - byte cmd = _config.ROM[track.DataOffset++]; + byte cmd = _songBinary[track.DataOffset++]; if (cmd >= 0xBD) // Commands that work within running status { track.RunCmd = cmd; } if (track.RunCmd >= 0xCF && cmd <= 0x7F) // Within running status { - byte peek0 = _config.ROM[track.DataOffset]; - byte peek1 = _config.ROM[track.DataOffset + 1]; + byte peek0 = _songBinary[track.DataOffset]; + byte peek1 = _songBinary[track.DataOffset + 1]; byte velocity, addedDuration; if (peek0 > 0x7F) { @@ -1114,9 +1121,9 @@ private void ExecuteNext(Track track, ref bool update) } else if (cmd >= 0xCF) { - byte peek0 = _config.ROM[track.DataOffset]; - byte peek1 = _config.ROM[track.DataOffset + 1]; - byte peek2 = _config.ROM[track.DataOffset + 2]; + byte peek0 = _songBinary[track.DataOffset]; + byte peek1 = _songBinary[track.DataOffset + 1]; + byte peek2 = _songBinary[track.DataOffset + 2]; byte key, velocity, addedDuration; if (peek0 > 0x7F) { @@ -1255,14 +1262,14 @@ private void ExecuteNext(Track track, ref bool update) } case 0xB2: { - track.DataOffset = (_config.ROM[track.DataOffset++] | (_config.ROM[track.DataOffset++] << 8) | (_config.ROM[track.DataOffset++] << 16) | (_config.ROM[track.DataOffset++] << 24)) - GBA.Utils.CartridgeOffset; + track.DataOffset = (_songBinary[track.DataOffset++] | (_songBinary[track.DataOffset++] << 8) | (_songBinary[track.DataOffset++] << 16) | (_songBinary[track.DataOffset++] << 24)) - GBA.Utils.CartridgeOffset; break; } case 0xB3: { if (track.CallStackDepth < 3) { - int callOffset = (_config.ROM[track.DataOffset++] | (_config.ROM[track.DataOffset++] << 8) | (_config.ROM[track.DataOffset++] << 16) | (_config.ROM[track.DataOffset++] << 24)) - GBA.Utils.CartridgeOffset; + int callOffset = (_songBinary[track.DataOffset++] | (_songBinary[track.DataOffset++] << 8) | (_songBinary[track.DataOffset++] << 16) | (_songBinary[track.DataOffset++] << 24)) - GBA.Utils.CartridgeOffset; track.CallStack[track.CallStackDepth] = track.DataOffset; track.CallStackDepth++; track.DataOffset = callOffset; @@ -1299,53 +1306,53 @@ private void ExecuteNext(Track track, ref bool update) } case 0xBA: { - track.Priority = _config.ROM[track.DataOffset++]; + track.Priority = _songBinary[track.DataOffset++]; break; } case 0xBB: { - _tempo = (ushort)(_config.ROM[track.DataOffset++] * 2); + _tempo = (ushort)(_songBinary[track.DataOffset++] * 2); break; } case 0xBC: { - track.Transpose = (sbyte)_config.ROM[track.DataOffset++]; + track.Transpose = (sbyte)_songBinary[track.DataOffset++]; break; } // Commands that work within running status: case 0xBD: { - track.Voice = _config.ROM[track.DataOffset++]; + track.Voice = _songBinary[track.DataOffset++]; track.Ready = true; break; } case 0xBE: { - track.Volume = _config.ROM[track.DataOffset++]; + track.Volume = _songBinary[track.DataOffset++]; update = true; break; } case 0xBF: { - track.Panpot = (sbyte)(_config.ROM[track.DataOffset++] - 0x40); + track.Panpot = (sbyte)(_songBinary[track.DataOffset++] - 0x40); update = true; break; } case 0xC0: { - track.PitchBend = (sbyte)(_config.ROM[track.DataOffset++] - 0x40); + track.PitchBend = (sbyte)(_songBinary[track.DataOffset++] - 0x40); update = true; break; } case 0xC1: { - track.PitchBendRange = _config.ROM[track.DataOffset++]; + track.PitchBendRange = _songBinary[track.DataOffset++]; update = true; break; } case 0xC2: { - track.LFOSpeed = _config.ROM[track.DataOffset++]; + track.LFOSpeed = _songBinary[track.DataOffset++]; track.LFOPhase = 0; track.LFODelayCount = 0; update = true; @@ -1353,7 +1360,7 @@ private void ExecuteNext(Track track, ref bool update) } case 0xC3: { - track.LFODelay = _config.ROM[track.DataOffset++]; + track.LFODelay = _songBinary[track.DataOffset++]; track.LFOPhase = 0; track.LFODelayCount = 0; update = true; @@ -1361,19 +1368,19 @@ private void ExecuteNext(Track track, ref bool update) } case 0xC4: { - track.LFODepth = _config.ROM[track.DataOffset++]; + track.LFODepth = _songBinary[track.DataOffset++]; update = true; break; } case 0xC5: { - track.LFOType = (LFOType)_config.ROM[track.DataOffset++]; + track.LFOType = (LFOType)_songBinary[track.DataOffset++]; update = true; break; } case 0xC8: { - track.Tune = (sbyte)(_config.ROM[track.DataOffset++] - 0x40); + track.Tune = (sbyte)(_songBinary[track.DataOffset++] - 0x40); update = true; break; } @@ -1384,7 +1391,7 @@ private void ExecuteNext(Track track, ref bool update) } case 0xCE: { - byte peek = _config.ROM[track.DataOffset]; + byte peek = _songBinary[track.DataOffset]; if (peek > 0x7F) { track.ReleaseChannels(track.PrevKey); diff --git a/VG Music Studio/UI/AssemblerDialog.cs b/VG Music Studio/UI/AssemblerDialog.cs new file mode 100644 index 00000000..fbec9765 --- /dev/null +++ b/VG Music Studio/UI/AssemblerDialog.cs @@ -0,0 +1,147 @@ +using Kermalis.EndianBinaryIO; +using Kermalis.VGMusicStudio.Core; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Windows.Forms; + +namespace Kermalis.VGMusicStudio.UI +{ + [DesignerCategory("")] + internal class AssemblerDialog : ThemedForm + { + private Assembler _assembler; + private readonly ThemedButton _previewButton; + private readonly ValueTextBox _offsetValueBox; + private readonly ThemedLabel _sizeLabel; + private readonly ThemedTextBox _headerLabelTextBox; + private readonly DataGridView _addedDefsGrid; + + public AssemblerDialog() + { + var openButton = new ThemedButton + { + Location = new Point(150, 0), + Text = "TODO"//Strings.AssemblerOpenFile + }; + openButton.Click += OpenASM; + _previewButton = new ThemedButton + { + Enabled = false, + Location = new Point(150, 50), + Size = new Size(120, 23), + Text = "TODO"//Strings.AssemblerPreviewSong + }; + _previewButton.Click += PreviewSong; + _sizeLabel = new ThemedLabel + { + Location = new Point(0, 100), + Size = new Size(150, 23) + }; + _offsetValueBox = new ValueTextBox + { + Hexadecimal = true, + Maximum = Core.GBA.Utils.CartridgeCapacity - 1 + }; + _headerLabelTextBox = new ThemedTextBox + { + Location = new Point(0, 50), + Size = new Size(150, 22) + }; + _addedDefsGrid = new DataGridView + { + ColumnCount = 2, + Location = new Point(0, 150), + MultiSelect = false + }; + _addedDefsGrid.Columns[0].Name = "TODO";//Strings.AssemblerDefinition; + _addedDefsGrid.Columns[1].Name = "TODO";//Strings.AssemblerValue; + _addedDefsGrid.Columns[1].DefaultCellStyle.NullValue = "0"; + int voiceTableOffset = ((Core.GBA.MP2K.Player)Engine.Instance.Player).VoiceTableOffset; + if (voiceTableOffset == -1) + { + voiceTableOffset = 0; + } + voiceTableOffset += Core.GBA.Utils.CartridgeOffset; + _addedDefsGrid.Rows.Add(new string[] { "voicegroup000", $"0x{voiceTableOffset:X7}" }); + _addedDefsGrid.CellValueChanged += AddedDefsGrid_CellValueChanged; + + Controls.AddRange(new Control[] { openButton, _previewButton, _sizeLabel, _offsetValueBox, _headerLabelTextBox, _addedDefsGrid }); + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + Size = new Size(600, 400); + //Text = $"{Util.Utils.ProgramName} ― {Strings.AssemblerTitle}"; + Text = $"{Util.Utils.ProgramName} ― {"TODO"}"; + } + + private void AddedDefsGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e) + { + DataGridViewCell cell = _addedDefsGrid.Rows[e.RowIndex].Cells[e.ColumnIndex]; + if (cell.Value == null) + { + return; + } + + if (e.ColumnIndex == 0) + { + if (char.IsDigit(cell.Value.ToString()[0])) + { + //FlexibleMessageBox.Show(Strings.AssemblerErrorDefinitionDigit, Strings.TitleError, MessageBoxButtons.OK, MessageBoxIcon.Error); + cell.Value = cell.Value.ToString().Substring(1); + } + } + else + { + if (!Util.Utils.TryParseValue(cell.Value.ToString(), int.MinValue, int.MaxValue, out _)) + { + //FlexibleMessageBox.Show(string.Format(Strings.AssemblerErrorInvalidValue, cell.Value), Strings.TitleError, MessageBoxButtons.OK, MessageBoxIcon.Error); + cell.Value = null; + } + } + } + + private void PreviewSong(object sender, EventArgs e) + { + ((MainForm)Owner).PreviewMP2KAssemblerSong(_assembler, Path.GetFileName(_assembler.FileName), _assembler[Assembler.FixLabel(Path.GetFileNameWithoutExtension(_assembler.FileName))]); + } + + private void OpenASM(object sender, EventArgs e) + { + var d = new OpenFileDialog + { + Title = "TODO",//Strings.TitleOpenASM, + Filter = "TODO|*.s"//$"{Strings.FilterOpenASM}|*.s" + }; + if (d.ShowDialog() != DialogResult.OK) + { + return; + } + + try + { + var s = new Dictionary(); + foreach (DataGridViewRow r in _addedDefsGrid.Rows.Cast()) + { + if (r.Cells[0].Value == null || r.Cells[1].Value == null) + { + continue; + } + s.Add(r.Cells[0].Value.ToString(), (int)Util.Utils.ParseValue("TODO", r.Cells[1].Value.ToString(), int.MinValue, int.MaxValue)); + } + _assembler = new Assembler(d.FileName, (int)(Core.GBA.Utils.CartridgeOffset + _offsetValueBox.Value), Endianness.LittleEndian, s); + _headerLabelTextBox.Text = Assembler.FixLabel(Path.GetFileNameWithoutExtension(d.FileName)); + //sizeLabel.Text = string.Format(Strings.AssemblerSizeInBytes, assembler.BinaryLength); + _sizeLabel.Text = string.Format("TODO Size in bytes: 0x{0:X}", _assembler.BinaryLength); + _previewButton.Enabled = true; + } + catch (Exception ex) + { + //FlexibleMessageBox.Show(ex.Message, Strings.TitleAssemblerError, MessageBoxButtons.OK, MessageBoxIcon.Error); + FlexibleMessageBox.Show(ex, "TODO", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} \ No newline at end of file diff --git a/VG Music Studio/UI/FlexibleMessageBox.cs b/VG Music Studio/UI/FlexibleMessageBox.cs index 30d29ad1..c006b2cf 100644 --- a/VG Music Studio/UI/FlexibleMessageBox.cs +++ b/VG Music Studio/UI/FlexibleMessageBox.cs @@ -117,6 +117,11 @@ internal class FlexibleMessageBox #region Public show functions + private static string ParseException(Exception ex) + { + return string.Format("Error Details:{1}{1}{0}{1}{2}", ex.Message, Environment.NewLine, ex.StackTrace); + } + public static DialogResult Show(string text) { return FlexibleMessageBoxForm.Show(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); @@ -131,7 +136,7 @@ public static DialogResult Show(string text, string caption) } public static DialogResult Show(Exception ex, string caption) { - return FlexibleMessageBoxForm.Show(null, string.Format("Error Details:{1}{1}{0}{1}{2}", ex.Message, Environment.NewLine, ex.StackTrace), caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); + return FlexibleMessageBoxForm.Show(null, ParseException(ex), caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); } public static DialogResult Show(IWin32Window owner, string text, string caption) { @@ -149,6 +154,10 @@ public static DialogResult Show(string text, string caption, MessageBoxButtons b { return FlexibleMessageBoxForm.Show(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1); } + public static DialogResult Show(Exception ex, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) + { + return FlexibleMessageBoxForm.Show(null, ParseException(ex), caption, buttons, icon, MessageBoxDefaultButton.Button1); + } public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) { return FlexibleMessageBoxForm.Show(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1); diff --git a/VG Music Studio/UI/MainForm.cs b/VG Music Studio/UI/MainForm.cs index 761394fa..d6c6a084 100644 --- a/VG Music Studio/UI/MainForm.cs +++ b/VG Music Studio/UI/MainForm.cs @@ -1,4 +1,5 @@ -using Kermalis.VGMusicStudio.Core; +using Kermalis.EndianBinaryIO; +using Kermalis.VGMusicStudio.Core; using Kermalis.VGMusicStudio.Properties; using Kermalis.VGMusicStudio.Util; using Microsoft.WindowsAPICodePack.Dialogs; @@ -30,12 +31,13 @@ internal class MainForm : ThemedForm private readonly List _remainingSongs = new List(); private TrackViewer _trackViewer; + private AssemblerDialog _assembler; #region Controls private readonly MenuStrip _mainMenu; private readonly ToolStripMenuItem _fileItem, _openDSEItem, _openAlphaDreamItem, _openMP2KItem, _openSDATItem, - _dataItem, _trackViewerItem, _exportDLSItem, _exportMIDIItem, _exportSF2Item, _exportWAVItem, + _dataItem, _trackViewerItem, _exportDLSItem, _exportMIDIItem, _exportSF2Item, _exportWAVItem, _importASMItem, _playlistItem, _endPlaylistItem; private readonly Timer _timer; private readonly ThemedNumeric _songNumerical; @@ -79,16 +81,18 @@ private MainForm() // Data Menu _trackViewerItem = new ToolStripMenuItem { ShortcutKeys = Keys.Control | Keys.T, Text = Strings.TrackViewerTitle }; _trackViewerItem.Click += OpenTrackViewer; - _exportDLSItem = new ToolStripMenuItem { Enabled = false, Text = Strings.MenuSaveDLS }; - _exportDLSItem.Click += ExportDLS; - _exportMIDIItem = new ToolStripMenuItem { Enabled = false, Text = Strings.MenuSaveMIDI }; + _exportWAVItem = new ToolStripMenuItem { Text = Strings.MenuSaveWAV }; + _exportWAVItem.Click += ExportWAV; + _exportMIDIItem = new ToolStripMenuItem { Text = Strings.MenuSaveMIDI }; _exportMIDIItem.Click += ExportMIDI; - _exportSF2Item = new ToolStripMenuItem { Enabled = false, Text = Strings.MenuSaveSF2 }; + _exportDLSItem = new ToolStripMenuItem { Text = Strings.MenuSaveDLS }; + _exportDLSItem.Click += ExportDLS; + _exportSF2Item = new ToolStripMenuItem { Text = Strings.MenuSaveSF2 }; _exportSF2Item.Click += ExportSF2; - _exportWAVItem = new ToolStripMenuItem { Enabled = false, Text = Strings.MenuSaveWAV }; - _exportWAVItem.Click += ExportWAV; - _dataItem = new ToolStripMenuItem { Text = Strings.MenuData }; - _dataItem.DropDownItems.AddRange(new ToolStripItem[] { _trackViewerItem, _exportDLSItem, _exportMIDIItem, _exportSF2Item, _exportWAVItem }); + _importASMItem = new ToolStripMenuItem { Text = "TODO" }; + _importASMItem.Click += OpenAssembler; + _dataItem = new ToolStripMenuItem { Text = Strings.MenuData, Visible = false }; + _dataItem.DropDownItems.AddRange(new ToolStripItem[] { _trackViewerItem, _exportWAVItem, _exportMIDIItem, _exportDLSItem, _exportSF2Item, _importASMItem }); // Playlist Menu _endPlaylistItem = new ToolStripMenuItem { Enabled = false, Text = Strings.MenuEndPlaylist }; @@ -222,7 +226,7 @@ private void SongNumerical_ValueChanged(object sender, EventArgs e) Config.Song song = songs.SingleOrDefault(s => s.Index == index); if (song != null) { - Text = $"{Utils.ProgramName} - {song.Name}"; + Text = $"{Utils.ProgramName} ― {song.Name}"; _songsComboBox.SelectedIndex = songs.IndexOf(song) + 1; // + 1 because the "Music" playlist is first in the combobox } _positionBar.Maximum = Engine.Instance.Player.MaxTicks; @@ -314,6 +318,49 @@ private void EndCurrentPlaylist(object sender, EventArgs e) ResetPlaylistStuff(true); } } + public void PreviewMP2KAssemblerSong(Assembler ass, string caption, long songHeaderOffset) + { + var p = (Core.GBA.MP2K.Player)Engine.Instance.Player; + bool playing = p.State == PlayerState.Playing; + Stop(); + Text = Utils.ProgramName; + _songsComboBox.SelectedIndex = 0; + _songInfo.DeleteData(); + bool success; + try + { + using (var reader = new EndianBinaryReader(new MemoryStream(ass.Binary), endianness: ass.Endianness)) + { + p.LoadSong(ass.Binary, reader, songHeaderOffset); + } + success = true; + } + catch (Exception ex) + { + FlexibleMessageBox.Show(ex, string.Format(Strings.ErrorLoadSong, caption)); + success = false; + } + + _trackViewer?.UpdateTracks(); + if (success) + { + Text = $"{Utils.ProgramName} ― {caption}"; + _positionBar.Maximum = p.MaxTicks; + _positionBar.LargeChange = _positionBar.Maximum / 10; + _positionBar.SmallChange = _positionBar.LargeChange / 4; + _songInfo.SetNumTracks(p.Events.Length); + if (playing) + { + Play(); + } + } + else + { + _songInfo.SetNumTracks(0); + } + int numTracks = (p.Events?.Length).GetValueOrDefault(); + _positionBar.Enabled = _exportWAVItem.Enabled = _exportMIDIItem.Enabled = success && numTracks > 0; + } private void OpenDSE(object sender, EventArgs e) { @@ -344,6 +391,7 @@ private void OpenDSE(object sender, EventArgs e) _exportDLSItem.Visible = false; _exportMIDIItem.Visible = false; _exportSF2Item.Visible = false; + _importASMItem.Visible = false; } } } @@ -376,6 +424,7 @@ private void OpenAlphaDream(object sender, EventArgs e) _exportDLSItem.Visible = true; _exportMIDIItem.Visible = false; _exportSF2Item.Visible = true; + _importASMItem.Visible = false; } } } @@ -408,6 +457,7 @@ private void OpenMP2K(object sender, EventArgs e) _exportDLSItem.Visible = false; _exportMIDIItem.Visible = true; _exportSF2Item.Visible = false; + _importASMItem.Visible = true; } } } @@ -440,6 +490,7 @@ private void OpenSDAT(object sender, EventArgs e) _exportDLSItem.Visible = false; _exportMIDIItem.Visible = false; _exportSF2Item.Visible = false; + _importASMItem.Visible = false; } } } @@ -655,6 +706,7 @@ private void FinishLoading(long numSongs) _autoplay = false; SetAndLoadSong(Engine.Instance.Config.Playlists[0].Songs.Count == 0 ? 0 : Engine.Instance.Config.Playlists[0].Songs[0].Index); _songsComboBox.Enabled = _songNumerical.Enabled = _playButton.Enabled = _volumeBar.Enabled = true; + _dataItem.Visible = true; UpdateTaskbarButtons(); } private void DisposeEngine() @@ -672,6 +724,7 @@ private void DisposeEngine() ResetPlaylistStuff(false); UpdatePositionIndicators(0L); UpdateTaskbarState(); + _dataItem.Visible = false; _songsComboBox.SelectedIndexChanged -= SongsComboBox_SelectedIndexChanged; _songNumerical.ValueChanged -= SongNumerical_ValueChanged; _songNumerical.Visible = false; @@ -773,6 +826,17 @@ private void OpenTrackViewer(object sender, EventArgs e) _trackViewer.FormClosed += (o, s) => _trackViewer = null; _trackViewer.Show(); } + private void OpenAssembler(object sender, EventArgs e) + { + if (_assembler != null) + { + _assembler.Focus(); + return; + } + _assembler = new AssemblerDialog { Owner = this }; + _assembler.FormClosed += (o, s) => _assembler = null; + _assembler.Show(); + } protected override void OnFormClosing(FormClosingEventArgs e) { diff --git a/VG Music Studio/VG Music Studio.csproj b/VG Music Studio/VG Music Studio.csproj index d2e743d6..ceebe977 100644 --- a/VG Music Studio/VG Music Studio.csproj +++ b/VG Music Studio/VG Music Studio.csproj @@ -162,6 +162,7 @@ True Strings.resx +