static void Main(string[] args) { Console.WriteLine("m4a2s version 0.1, Copyright (C) 2017 ipatix"); Console.WriteLine("m4a2s comes with ABSOLUTELY NO WARRANTY; for details see LICENSE.txt"); Console.WriteLine("This is free software, and you are welcome to redistribute it"); Console.WriteLine("under certain conditions; see LICENSE.txt for details."); int songtable = 0; if (args.Length != 3) { ShowUsage(); } if (!File.Exists(args[0])) { ShowUsage(); } if (!Directory.Exists(args[2])) { ShowUsage(); } try { songtable = Convert.ToInt32(args[1], 16); } catch { ShowUsage(); } string romPath = args[0]; string destFolder = args[2]; if (!Directory.Exists(Path.Combine(destFolder, "seq"))) { Directory.CreateDirectory(Path.Combine(destFolder, "seq")); } if (!Directory.Exists(Path.Combine(destFolder, "wave"))) { Directory.CreateDirectory(Path.Combine(destFolder, "wave")); } if (!Directory.Exists(Path.Combine(destFolder, "bank"))) { Directory.CreateDirectory(Path.Combine(destFolder, "bank")); } Rom.LoadRom(romPath, songtable); /* * build up the rom index */ Console.WriteLine("Building up index"); Stopwatch sw = new Stopwatch(); sw.Start(); Index.IndexRom(); sw.Stop(); Console.WriteLine("Index successfully build in {0} ms", sw.ElapsedMilliseconds); sw.Reset(); sw.Start(); Hashtable index = Index.GetHashtable(); foreach (DictionaryEntry Hent in index) { Entity ent = (Entity)Hent.Value; string fileName; switch (ent.Type) { case EntityType.Bank: fileName = Path.Combine(Path.Combine(destFolder, "bank"), ent.Guid + ".s"); Voicegroup.disassemble(index, ent, fileName); break; case EntityType.KeyMap: fileName = Path.Combine(Path.Combine(destFolder, "bank"), ent.Guid + ".s"); KeyMap.disassemble(ent, fileName); break; case EntityType.Wave: fileName = Path.Combine(Path.Combine(destFolder, "wave"), ent.Guid + ".s"); Wave.disassemble(ent, fileName); break; case EntityType.GbWave: fileName = Path.Combine(Path.Combine(destFolder, "wave"), ent.Guid + ".s"); GbWave.disassemble(ent, fileName); break; case EntityType.Song: fileName = Path.Combine(Path.Combine(destFolder, "seq"), ent.Guid + ".s"); Song.disassemble(index, ent, fileName); break; default: throw new Exception("Invalid Entity Type!"); } Console.WriteLine("Succesfully disassembled Entity from 0x{0} to file: {1}", ent.Offset.ToString("X7"), fileName); } Songtable.disassemble(index, Path.Combine(destFolder, "_songtable.s")); sw.Stop(); Console.WriteLine("Succesfully disassembled Songtable from 0x{0}", Rom.SongtableOffset); Console.WriteLine("Finished exporting data after {0} ms", sw.ElapsedMilliseconds); }
public static void disassemble(Hashtable index, Entity song, string destFile) { StringBuilder oasm = new StringBuilder(); oasm.AppendLine("@ File generated by m4a2s Song-Module"); // read header information from ROM int songOffset = song.Offset; Rom.Reader.BaseStream.Position = songOffset; byte numTracks = Rom.Reader.ReadByte(); byte numBlocks = Rom.Reader.ReadByte(); // as usual, the usage of this byte is unknown byte priority = Rom.Reader.ReadByte(); byte reverb = Rom.Reader.ReadByte(); int vgrOffset = Rom.Reader.ReadInt32() - Rom.Map; int[] trackOffsets = new int[numTracks]; for (int i = 0; i < numTracks; i++) { trackOffsets[i] = Rom.Reader.ReadInt32() - Rom.Map; } oasm.AppendLine("\t.include \"MPlayDef.s\""); oasm.AppendLine(); oasm.AppendLine("\t.equ\t" + song.Guid + "_grp, " + ((Entity)index[vgrOffset]).Guid); oasm.AppendLine("\t.equ\t" + song.Guid + "_pri, " + priority); oasm.AppendLine("\t.equ\t" + song.Guid + "_rev, " + reverb); oasm.AppendLine("\t.equ\t" + song.Guid + "_mvl, 127"); oasm.AppendLine("\t.equ\t" + song.Guid + "_key, 0"); oasm.AppendLine(); oasm.AppendLine("\t.section .rodata"); oasm.AppendLine("\t.global\t" + song.Guid); oasm.AppendLine("\t.align\t2"); oasm.AppendLine(); for (int cTrack = 0; cTrack < numTracks; cTrack++) { oasm.AppendLine("@*********************** Track " + (cTrack + 1).ToString("D2") + " ***********************@"); oasm.AppendLine(); oasm.AppendLine(song.Guid + "_" + cTrack + ":"); // set reader position Rom.Reader.BaseStream.Position = trackOffsets[cTrack]; Event lastEvent = Event.None; while (true) { byte cmd = Rom.Reader.ReadByte(); // if delay if (cmd >= 0x80 && cmd <= (0x80 + 48)) { oasm.AppendLine("\t.byte\t" + Tables.Wxx[cmd - 0x80]); } // if FINE else if (cmd == 0xB1) { oasm.AppendLine("\t.byte\tFINE"); oasm.AppendLine(); break; } // if GOTO else if (cmd == 0xB2) { oasm.AppendLine("\t.byte\tGOTO"); int targetPosition = Rom.Reader.ReadInt32() - Rom.Map; oasm.AppendLine("\t .word\t" + song.Guid + "_" + cTrack + " + 0x" + (targetPosition - trackOffsets[cTrack]).ToString("X")); oasm.AppendLine("\t.byte\tFINE"); oasm.AppendLine(); break; } // if PATT else if (cmd == 0xB3) { oasm.AppendLine("\t.byte\tPATT"); int targetPosition = Rom.Reader.ReadInt32() - Rom.Map; oasm.AppendLine("\t .word\t" + song.Guid + "_" + cTrack + " + 0x" + (targetPosition - trackOffsets[cTrack]).ToString("X")); } // if PEND else if (cmd == 0xB4) { oasm.AppendLine("\t.byte\tPEND"); } // if REPT else if (cmd == 0xB5) { byte numRepeats = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\tREPT , " + numRepeats); int targetPosition = Rom.Reader.ReadInt32() - Rom.Map; oasm.AppendLine("\t .word\t" + song.Guid + "_" + cTrack + " + 0x" + (targetPosition - trackOffsets[cTrack]).ToString("X")); } // if MEMACC else if (cmd == 0xB9) { byte par1 = Rom.Reader.ReadByte(); byte par2 = Rom.Reader.ReadByte(); byte par3 = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\tMEMACC, " + par1 + ", " + par2 + ", " + par3); } // if PRIO else if (cmd == 0xBA) { byte prio = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\tPRIO , " + prio); } // if TEMPO else if (cmd == 0xBB) { int speed = Rom.Reader.ReadByte() * 2; oasm.AppendLine("\t.byte\tTEMPO , " + speed + "/2"); } // if KEYSH else if (cmd == 0xBC) { int shift = Rom.Reader.ReadSByte(); string str; if (shift < 0) { str = shift.ToString(); } else { str = "+" + shift; } oasm.AppendLine("\t.byte\tKEYSH , " + song.Guid + "_key" + str); } // if VOICE else if (cmd == 0xBD) { byte instr = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\t\tVOICE , " + instr); } /* * from here repeatable commands will follow */ // if VOL else if (cmd == 0xBE) { byte vol = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\t\tVOL , " + vol + "*" + song.Guid + "_mvl/mxv"); lastEvent = Event.Vol; } // if PAN else if (cmd == 0xBF) { byte pan = Rom.Reader.ReadByte(); string str; if (pan < 0x40) { str = (pan - 0x40).ToString(); } else { str = "+" + (pan - 0x40); } oasm.AppendLine("\t.byte\t\tPAN , c_v" + str); lastEvent = Event.Pan; } // if BEND else if (cmd == 0xC0) { byte bend = Rom.Reader.ReadByte(); string str; if (bend < 0x40) { str = (bend - 0x40).ToString(); } else { str = "+" + (bend - 0x40); } oasm.AppendLine("\t.byte\t\tBEND , c_v" + str); lastEvent = Event.Bend; } // if BENDR else if (cmd == 0xC1) { byte bendr = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\t\tBENDR , " + bendr); lastEvent = Event.Bendr; } // if LFOS else if (cmd == 0xC2) { byte lfos = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\t\tLFOS , " + lfos); } // if LFODL else if (cmd == 0xC3) { byte lfodl = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\t\tLFODL , " + lfodl); } // if MOD else if (cmd == 0xC4) { byte mod = Rom.Reader.ReadByte(); oasm.AppendLine("\t.byte\t\tMOD , " + mod); lastEvent = Event.Mod; } // if MODT else if (cmd == 0xC5) { byte modt = Rom.Reader.ReadByte(); if (modt < 3) { oasm.AppendLine("\t.byte\t\tMODT , " + Tables.Modt[modt]); } else { oasm.AppendLine("\t.byte\t\tMODT , " + modt); } } // if TUNE else if (cmd == 0xC8) { byte tune = Rom.Reader.ReadByte(); string str; if (tune < 0x40) { str = (tune - 0x40).ToString(); } else { str = "+" + (tune - 0x40); } oasm.AppendLine("\t.byte\t\tTUNE , c_v" + str); lastEvent = Event.Tune; } // if XCMD else if (cmd == 0xCD) { byte xcmdtype = Rom.Reader.ReadByte(); byte xcmdarg = Rom.Reader.ReadByte(); if (xcmdtype == 0x8) { oasm.AppendLine("\t.byte\t\tXCMD , xIECV , " + xcmdarg); } else if (xcmdtype == 0x9) { oasm.AppendLine("\t.byte\t\tXCMD , xIECL , " + xcmdarg); } else { throw new NotSupportedException("Invalid XCMD Type"); } lastEvent = Event.Xcmd; } // if EOT else if (cmd == 0xCE) { if (Rom.ReaderPeekByte() <= 127) { oasm.AppendLine("\t.byte\t\tEOT , " + Tables.Note[Rom.Reader.ReadByte()]); } else { oasm.AppendLine("\t.byte\t\tEOT"); } lastEvent = Event.Eot; } // if NOTE else if (cmd >= 0xCF && cmd <= (0xD0 + 47)) { oasm.Append("\t.byte\t\t" + Tables.Nxx[cmd - 0xCF]); if (Rom.ReaderPeekByte() > 127) // peek note pitch { oasm.AppendLine(); } else { oasm.Append(" , " + Tables.Note[Rom.Reader.ReadByte()]); if (Rom.ReaderPeekByte() > 127) // peek note velocity { oasm.AppendLine(); } else { oasm.Append(", v" + Rom.Reader.ReadByte().ToString("D3")); if (Rom.ReaderPeekByte() > 127) { oasm.AppendLine(); } else { oasm.AppendLine(", " + Tables.Gtp(Rom.Reader.ReadByte())); } } } lastEvent = Event.Note; } else if (cmd >= 0x0 && cmd <= 0x7F) { if (lastEvent == Event.Bend) { int bend = cmd - 0x40; string str; if (bend < 0) { str = bend.ToString(); } else { str = "+" + bend; } oasm.AppendLine("\t.byte\t\t c_v" + str); } else if (lastEvent == Event.Bendr) { oasm.AppendLine("\t.byte\t\t " + cmd); } else if (lastEvent == Event.Eot) { oasm.AppendLine("\t.byte\t\t " + Tables.Note[cmd]); } else if (lastEvent == Event.Mod) { oasm.AppendLine("\t.byte\t\t " + cmd); } else if (lastEvent == Event.None) { Console.Write(oasm.ToString()); throw new Exception("Trying to repeat an event that hasn't occured yet"); } else if (lastEvent == Event.Note) { oasm.Append("\t.byte\t\t " + Tables.Note[cmd]); if (Rom.ReaderPeekByte() > 128) // probe for velocity { oasm.AppendLine(); } else { oasm.Append(", v" + Rom.Reader.ReadByte().ToString("D3")); if (Rom.ReaderPeekByte() > 128) // peek for gate time { oasm.AppendLine(); } else { oasm.AppendLine(", " + Tables.Gtp(Rom.Reader.ReadByte())); } } } else if (lastEvent == Event.Pan) { int pan = cmd - 0x40; string str; if (pan < 0) { str = pan.ToString(); } else { str = "+" + pan; } oasm.AppendLine("\t.byte\t\t c_v" + str); } else if (lastEvent == Event.Tune) { int tune = cmd - 0x40; string str; if (tune < 0) { str = tune.ToString(); } else { str = "+" + tune; } oasm.AppendLine("\t.byte\t\t c_v" + str); } else if (lastEvent == Event.Vol) { oasm.AppendLine("\t.byte\t\t " + cmd + "*" + song.Guid + "_mvl/mxv"); } else if (lastEvent == Event.Xcmd) { byte xcmdtype = cmd; byte xcmdarg = Rom.Reader.ReadByte(); if (xcmdtype == 0x8) { oasm.AppendLine("\t.byte\t\t xIECV , " + xcmdarg); } else if (xcmdtype == 0x9) { oasm.AppendLine("\t.byte\t\t xIECL , " + xcmdarg); } else { Console.Write(oasm.ToString()); throw new NotSupportedException("Invalid XCMD Type"); } } else { throw new Exception("This Exception shouldn't be able to occur!"); } } // if an unsupported command occurs else { throw new Exception("Unsupported command 0x" + cmd.ToString("X2") + " at offset 0x" + (Rom.Reader.BaseStream.Position - 1).ToString("X7") + " on track " + cTrack + " from song-header at 0x" + song.Offset.ToString("X7")); } } } oasm.AppendLine("@******************************************************@"); oasm.AppendLine(); oasm.AppendLine("\t.align\t2"); oasm.AppendLine(); oasm.AppendLine(song.Guid + ":"); oasm.AppendLine("\t.byte\t" + numTracks + "\t@ NumTrks"); oasm.AppendLine("\t.byte\t" + numBlocks + "\t@ NumBlks"); oasm.AppendLine("\t.byte\t" + song.Guid + "_pri\t@ Priority"); oasm.AppendLine("\t.byte\t" + song.Guid + "_rev\t@ Reverb"); oasm.AppendLine(); oasm.AppendLine("\t.word\t" + song.Guid + "_grp"); oasm.AppendLine(); for (int i = 0; i < numTracks; i++) { oasm.AppendLine("\t.word\t" + song.Guid + "_" + i); } oasm.AppendLine(); oasm.AppendLine("\t.end"); File.WriteAllText(destFile, oasm.ToString()); }