private void ParseScript(string data, string[] args, int lnoffs, ref Stack <ScriptArray> stack) { // chars to trim from strings char[] trim = new char[] { ' ', '\t' }; // some variables to help with stuff Stack <ScriptCondition> co = new Stack <ScriptCondition>(); if (debug) { Debug(new string('-', 80)); } // use line num to accurately report issues uint lnum = (uint)lnoffs, tabs = 0; foreach (string ln in data.Replace("\r", "").Split('\n')) { lnum++; // trim whitespaces and tabs from start. Also C# is gay sometimes string line = ln.Trim(trim); // ignore empty lines if (line.Length > 0) { try { switch (line.ElementAt(0)) { case '#': //ignore comments break; case '}': tabs--; // we need 2 entries to remove one of them, to have one entry still in stack if (stack.Count < 2) { screrr(lnum, "Stack is empty. Maybe there is an extra script block end?"); } // } { -> else if (line.EndsWith("{")) { // if co not set, its a problem if (co.Count <= 0) { screrr(lnum, "Else called when not in condition block"); } // if already in false block, error if (stack.Peek() == co.Peek().False) { screrr(lnum, "Else called when already inside else block"); } // pop last entry and push false block in stack.Pop(); stack.Push(co.Peek().False); if (debug) { Debug(lnum, tabs++, "}{"); } } else { // pop from co stack (NEEDS TO BE FIRST FUU) if (co.Count > 0 && (co.Peek().True == stack.Peek() || co.Peek().False == stack.Peek())) { co.Pop(); } stack.Pop(); if (debug) { Debug(lnum, tabs, "}"); } } break; case '?': if (line.Length < 3) { // if line is too short, it is a major problem screrr(lnum, "Name not specified."); } string name = line.Substring(1, line.Length - 2).Trim(trim); if (name.Length <= 0) { // no usable name screrr(lnum, "Name not specified."); } if (line.EndsWith("{")) { // declaring subscript ScriptArray f = new ScriptArray(stack.Peek()); subscripts.Add(name, f); stack.Push(f); if (debug) { Debug(lnum, tabs++, '?' + name + " {"); } } else if (line.EndsWith(";")) { // subscript importing stack.Peek().Add(new ScriptImport(lnum, stack.Peek(), name)); if (debug) { Debug(lnum, tabs, '?' + name + ';'); } } else { // if line doesnt end with ; or {, there is an issue screrr(lnum, "invalid end token."); } break; case '/': // get the label only if there is a space char bool singlemode = line.Length > 1 && line.ElementAt(1) == '/'; line = line.Substring(singlemode ? 2 : 1); string lbl = ""; int ind; if ((ind = line.IndexOf(' ')) != -1) { lbl = line.Substring(0, ind); line = line.Substring(ind + 1); } // check if rest of the info exists if (line.Length < 2) { screrr(lnum, "Illegal line"); } List <bool> types = new List <bool>(); List <string> names = new List <string>(); while (line.Length > 0) { // get the caller type types.Add(line.StartsWith("?")); if (!types.Last() && !line.StartsWith(">")) { screrr(lnum, "Illegal type '" + line.ElementAt(0) + "'"); } // get the offset of the last char int last = line.IndexOf(' ') + 1; if (last == 0) { last = line.Length; } // add the string in and remove from line names.Add(line.Substring(1, last - 1).Trim(trim)); line = line.Substring(last); } stack.Peek().Add(new ScriptExecute(lnum, stack.Peek(), lbl, types.ToArray(), names.ToArray(), singlemode)); // write debug info if (debug) { string db = '/' + lbl; for (int i = 0; i < types.Count; i++) { db += ' ' + (types[i] ? "?" : ">") + names[i]; } Debug(lnum, tabs, db); } break; case '=': int indx = line.IndexOf(' '); if (indx == -1) { screrr(lnum, "Expected a whitespace (' '), but found none."); } // just some sanity checks, dont worry string equ = line.Substring(1, indx - 1), val = line.Substring(indx + 1); if (equ.Length < 1) { screrr(lnum, "Equate name not specified."); } if (val.Length < 1) { screrr(lnum, "Equate value not specified."); } ScriptEquate scre = new ScriptEquate(lnum, stack.Peek(), equ, val); stack.Peek().Add(scre); if (debug) { string n = scre.GetName(); Equate e = GetEquate(n); Debug(lnum, tabs, '=' + n + ' ' + val + (e.calculated ? " " + e.value : "")); } break; case '!': // skip ! and any spaces line = line.Substring(1).TrimStart(trim); // loop til all arguments are found int index; List <string> pre = new List <string>(); while (!line.StartsWith(">")) { // find the next , index = line.IndexOf(','); // if > is earlier than , (because arguments also can have them), then use > instead if (line.IndexOf('>') < index || index < 1) { index = line.IndexOf('>'); } if (index < 1) { screrr(lnum, "Invalid or nonexistent trigger byte at macro block."); } // create new argument and pre.Add(line.Substring(0, index).Trim(trim)); line = line.Substring(index); // if starts with , then remove it if (line.StartsWith(",")) { line = line.Substring(1); } } // get the name index = line.IndexOf(':'); if (index == -1) { screrr(lnum, "Expecting semicolon (':')"); } string nam = line.Substring(1, index - 1).Trim(trim); line = line.Substring(index + 1).TrimStart(trim); if (nam.Length < 1) { screrr(lnum, "Invalid name"); } // get the arguments List <string> arg = new List <string>(); while (!line.StartsWith(";") && !line.StartsWith("{")) { // find the next , index = line.IndexOf(','); // if failed, then try to find the end icon if (index < 1) { index = line.IndexOf(';'); } if (index < 1) { index = line.IndexOf('{'); } if (index < 1) { screrr(lnum, "Invalid or nonexistent argument at macro block."); } // create new argument and arg.Add(line.Substring(0, index).Trim(trim)); line = line.Substring(index); // if starts with , then remove it if (line.StartsWith(",")) { line = line.Substring(1); } } // create a new macro ScriptMacro s = new ScriptMacro(lnum, stack.Peek(), nam, pre.ToArray(), arg.ToArray()); stack.Peek().Add(s); // if ends with this, put shit in inner block if (line.EndsWith("{")) { stack.Push(s.Inner); } // write debug shit if (debug) { Debug(lnum, tabs, '!' + string.Join(", ", pre) + " > " + nam + ": " + string.Join(", ", arg) + line.ElementAt(line.Length - 1)); if (line.EndsWith("{")) { tabs++; } } break; case '@': // check if there is a space int indie = line.IndexOf(' '); if (indie == -1) { screrr(lnum, "Expected whitespace (' '), but found none."); } // get the equate name and remove from line string eqq = line.Substring(1, indie - 1); line = line.Substring(indie + 1).TrimStart(trim); // check for next space indie = line.IndexOf(' '); if (indie == -1) { screrr(lnum, "Expected whitespace (' '), but found none."); } // get the arg number int argnum; if (!Int32.TryParse(line.Substring(0, indie), out argnum)) { screrr(lnum, "Line '" + line.Substring(0, indie) + "' can not be parsed as a number!"); } string da; // check if there are arguments at this offset if (argnum >= args.Length) { // print out the string indie = line.IndexOf("\"") + 1; if (indie == 0) { screrr(lnum, "Expected string, but found none."); } Console.Write(line.Substring(indie, line.LastIndexOf("\"") - indie) + ": "); da = ConsoleArguments.Get(args, new ArgHandler[] { new ArgHandler(line.Substring(indie, line.LastIndexOf("\"") - indie) + ":", (_data, ret) => ret ? _data : null), }, new ButtonHandler[] { })[0]; if (debug) { Debug(lnum, tabs, "@? " + eqq + ' ' + argnum + ' ' + (indie > 0 ? line.Substring(indie, line.LastIndexOf("\"") - indie) : "INVALID") + ' ' + da); } } else { // read argument da = args[argnum]; if (debug) { indie = line.IndexOf("\"") + 1; Debug(lnum, tabs, "@ " + eqq + ' ' + argnum + ' ' + (indie > 0 ? line.Substring(indie, line.LastIndexOf("\"") - indie) : "INVALID") + ' ' + da); } } stack.Peek().Add(new ScriptEquate(lnum, stack.Peek(), eqq, da)); break; case 'c': // ehhhhhhh =/ if (!line.EndsWith("{")) { screrr(lnum, "No opening script block."); } // create the condition block, and push true block in stack co.Push(new ScriptCondition(lnum, stack.Peek(), line.Substring(1, line.Length - 2).Trim(trim))); stack.Peek().Add(co.Peek()); stack.Push(co.Peek().True); if (debug) { Debug(lnum, tabs++, "c " + line.Substring(1, line.Length - 2).Trim(trim) + " {"); } break; case 'f': // check for { at the end of line int inde = line.IndexOf('{'); if (inde == -1 || !line.EndsWith("{")) { screrr(lnum, "Expected block start ('{') at end of line, but found none."); } string counter = line.Substring(1, inde - 1).Trim(trim); ScriptRepeat r = new ScriptRepeat(lnum, stack.Peek(), counter); stack.Peek().Add(r); stack.Push(r.Inner); if (debug) { Debug(lnum, tabs++, "f " + counter + " {"); } break; case 'w': // check for { at the end of line int ine = line.IndexOf('{'); if (ine == -1 || !line.EndsWith("{")) { screrr(lnum, "Expected block start ('{') at end of line, but found none."); } string cond = line.Substring(1, ine - 1).Trim(trim); ScriptWhile w = new ScriptWhile(lnum, stack.Peek(), cond); stack.Peek().Add(w); stack.Push(w.Inner); if (debug) { Debug(lnum, tabs++, "w " + cond + " {"); } break; case ':': if (line.Length < 3) { screrr(lnum, "Unexpected end of line!"); } switch (line[1]) { case '?': { // check for { at the end of line int inxd = line.IndexOf('{'); if (inxd == -1) { screrr(lnum, "Expected block start ('{') at end of line, but found none."); } string num = line.Substring(2, inxd - 2).Trim(trim); try { ScriptArgMod m = new ScriptArgMod(lnum, stack.Peek(), num); stack.Peek().Add(m); stack.Push(m.Inner); if (debug) { Debug(lnum, tabs++, ":? " + m.num + " {"); } } catch (Exception) { screrr(lnum, "Failed to parse '" + num + "'!"); } } break; case '-': { string num = line.Substring(2, line.Length - 2).Trim(trim); try { ScriptArgRmv m = new ScriptArgRmv(lnum, stack.Peek(), num); stack.Peek().Add(m); if (debug) { Debug(lnum, tabs++, ":- " + m.num); } } catch (Exception) { screrr(lnum, "Failed to parse '" + num + "'!"); } } break; case '=': { int inxd = line.IndexOf(' '); if (inxd == -1) { screrr(lnum, "Expected space separator (' ') in the middle of the line, but found none."); } string num = line.Substring(2, inxd - 2).Trim(trim); string oper = line.Substring(inxd, line.Length - inxd).Trim(trim); try { ScriptArgEqu m = new ScriptArgEqu(lnum, stack.Peek(), num, oper); stack.Peek().Add(m); if (debug) { Debug(lnum, tabs++, ":= " + m.num + " " + m.operation); } } catch (Exception) { screrr(lnum, "Failed to parse '" + num + "'!"); } } break; default: screrr(lnum, "Unrecognized argument modifier type '" + line[1] + "'!"); break; } break; case '~': int indix = line.IndexOf(' '); if (indix == -1) { screrr(lnum, "Expected a whitespace (' '), but found none."); } // just some sanity checks, dont worry string labl = line.Substring(1, indix - 1), type = line.Substring(indix + 1); if (labl.Length < 1) { screrr(lnum, "Lable name not specified."); } if (type.Length < 1) { screrr(lnum, "Lable type or command not specified."); } if (type.StartsWith(":")) { // lable mod LableMod l = new LableMod(lnum, stack.Peek(), labl, type.Substring(1)); stack.Peek().Add(l); if (debug) { Debug(lnum, tabs, '~' + labl + " :" + l.num); } } else // lable create { stack.Peek().Add(new LableCreate(lnum, stack.Peek(), labl.Trim(trim), type)); if (debug) { Debug(lnum, tabs, '~' + labl + ' ' + type); } } break; case '$': stack.Peek().Add(new ScriptOperation(lnum, stack.Peek(), line = line.Substring(1).Trim(trim))); if (debug) { Debug(lnum, tabs, '$' + line); } break; case '>': // if line not long enough, problem if (line.Length < 3) { screrr(lnum, "Expected type and offset!"); } // check if valid type char ttype = line.ElementAt(1); if (ttype != 'a' && ttype != 'b' && ttype != 'f') { screrr(lnum, "Goto type '" + ttype + "' not recognized!"); } // arg1 = type (char), arg2 = rest of the line stack.Peek().Add(new ScriptGoto(lnum, stack.Peek(), ttype, line = line.Substring(2).Trim(trim))); if (debug) { Debug(lnum, tabs, ">" + ttype + " " + line); } break; case ';': stack.Peek().Add(new ScriptStop(lnum, stack.Peek())); if (debug) { Debug(lnum, tabs, ";"); } break; case '%': stack.Peek().Add(new ScriptComment(lnum, stack.Peek(), line.Substring(1))); if (debug) { Debug(lnum, tabs, line); } break; case '+': stack.Peek().Add(new ScriptPrint(lnum, stack.Peek(), line.Substring(1))); if (debug) { Debug(lnum, tabs, line); } break; case 's': { int idx = line.IndexOf(' '); string mac = line.Substring(1, idx >= 0 ? idx - 1 : line.Length - 1); switch (mac.ToLowerInvariant()) { case "inc": { if (idx == -1) { screrr(lnum, "Macro does not have a file name!"); } string path = line.Substring(idx + 1, line.Length - idx - 1); if (path.StartsWith("\"") && path.EndsWith("\"")) { path = path.Substring(1, path.Length - 2); } if (!File.Exists(path)) { screrr(lnum, "File '" + path + "' does not exist!"); } try { string[] file = File.ReadAllLines(path); if (debug) { Debug(lnum, tabs, "--; macro: parse another file '" + path + "' (" + file.Length + " lines)"); } ParseScript(string.Join("\n", file), args, 0, ref stack); if (debug) { Debug(lnum, tabs, "--; return to previous file"); } } catch (Exception) { screrr(lnum, "Failed to load file contents for file '" + path + "'!"); } } break; case "datamacro": { if (idx == -1) { screrr(lnum, "Data macro has not been defined!"); } Output.DataMacro = line.Substring(idx + 1, line.Length - idx - 1).Trim(); } break; case "lablenumber": { if (idx == -1) { screrr(lnum, "Lable number format has not been defined!"); } string fmt = line.Substring(idx + 1, line.Length - idx - 1).Trim().ToLowerInvariant(); if (LableRule.RandomRules[fmt] == null) { screrr(lnum, "Invalid lable number format '" + fmt + "'!"); } LableRule.GetNextRandom = LableRule.RandomRules[fmt]; } break; case "version": if (line.Substring(idx + 1, line.Length - idx - 1).Trim().ToLowerInvariant() != VERSION) { screrr(lnum, "Your script is out of date, and may not be executed! Please update the script to " + VERSION + "!"); } break; default: screrr(lnum, "Macro type '" + mac + "' not recognized!"); break; } } break; default: // incase we cant figure out what command this is screrr(lnum, "Symbol not recognized: '" + line.ElementAt(0) + "'"); return; } } catch (Exception e) { screrr(lnum, e.ToString()); } } } if (debug) { Debug(new string('-', 80)); } }
static void Main(string[] args) { Console.Title = "SMPS2ASM/NAT Built: " + new FileInfo(Assembly.GetExecutingAssembly().Location).LastWriteTime.ToShortDateString() + " " + new FileInfo(Assembly.GetExecutingAssembly().Location).LastWriteTime.ToShortTimeString(); // args[input file with ext, Sound driver name, label, extra: may be used by script] // get the exe folder string[] a = args; //check if we have a debug option opcheck: if (args.Length > 0 && args[0] == "-d") { args = args.Skip(1).ToArray(); debug = true; goto opcheck; } //check if we have a pause option if (args.Length > 0 && args[0] == "-p") { args = args.Skip(1).ToArray(); pause = true; goto opcheck; } //check if we have a type option if (args.Length > 1 && args[0] == "-t") { type = args[1]; args = args.Skip(2).ToArray(); goto opcheck; } //check if a script file was dragged in if (args.Length > 0) { if (File.Exists(args[0]) && args[0].EndsWith(".smpss")) { folder = Environment.CurrentDirectory; string script = args[0]; args = args.Skip(1).ToArray(); // check if all arguments are gotten if (args.Length < 2) { pause = true; args = ConsoleArguments.Get(args, new ArgHandler[] { new ArgHandler("Music file name with extension:", chkfilext2), new ArgHandler("Project name:", chkname), }, new ButtonHandler[] { new ButtonHandler(ConsoleKey.Escape, "Quit the program", quitprg, quitcl), new ButtonHandler(ConsoleKey.F1, "Pause program at the end", pauseprg, pausecl), new ButtonHandler(ConsoleKey.F2, "Print debug info", debugprg, debugcl), }); } else { args[0] = chkfilext2(args[0], true); args[1] = chkname(args[1], true); } string[] ax = new string[1 + args.Length]; ax[0] = args[0]; ax[2] = args[1]; ax[1] = script; for (int i = 3; i < ax.Length; i++) { ax[i] = args[i - 1]; } args = ax; goto oops; } } // check if all arguments are gotten if (args.Length < 3) { folder = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Environment.CurrentDirectory), @"")); pause = true; args = ConsoleArguments.Get(args, new ArgHandler[] { new ArgHandler("Music file name with extension:", chkfilext), new ArgHandler("Sound driver folder name:", chkfolext), new ArgHandler("Project name:", chkname), }, new ButtonHandler[] { new ButtonHandler(ConsoleKey.Escape, "Quit the program", quitprg, quitcl), new ButtonHandler(ConsoleKey.F1, "Pause program at the end", pauseprg, pausecl), new ButtonHandler(ConsoleKey.F2, "Print debug info", debugprg, debugcl), }); } else { folder = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Environment.CurrentDirectory), @"")); args[0] = chkfilext(args[0], true); args[1] = chkfolext(args[1], true); args[2] = chkname(args[2], true); } // time how long this will take oops: timer = new Stopwatch(); timer.Start(); // remove bin folder from path // if (folder.EndsWith("\\bin") || folder.EndsWith("\\bin\\")) folder = folder.Substring(0, folder.LastIndexOf("\\")); // removes the extension of input file and adds .asm as the extension of output file string fileout; if (args[0].IndexOf(".", args[0].LastIndexOf("\\")) > 0) { fileout = args[0].Substring(0, args[0].LastIndexOf(".")) + ".asm"; } else { fileout = args[0] + ".asm"; } // init debugwriter and put in debug info if (debug) { string db; if (args[0].IndexOf(".", args[0].LastIndexOf("\\")) > 0) { db = args[0].Substring(0, args[0].LastIndexOf(".")) + ".smpsd"; } else { db = args[0] + ".smpsd"; } //init stream dbWr = new StreamWriter(db); // write info about args Debug("--; args=[" + string.Join(", ", a) + "]"); Debug("--; filein=" + args[0]); Debug("--; fileout=" + fileout); Debug("--; folder=" + folder); Debug("--; script=" + args[1]); Debug("--; lable=" + args[2]); Debug("--; type=" + type); } // get new SMPS object ConvertSMPS cvt = new ConvertSMPS(args[0], fileout, args[2]); // get the file for smps2asm script S2AScript scr = new S2AScript(args[1], args.Skip(3).ToArray(), type); // print timer info long tra = timer.ElapsedMilliseconds; Console.WriteLine("Script translated! Took " + tra + " ms!"); // restart timer timer.Reset(); timer.Start(); // do teh conversion cvt.Convert(scr); // print timer info long con = timer.ElapsedMilliseconds; Console.WriteLine("File converted! Took " + con + " ms!"); // restart timer timer.Reset(); timer.Start(); // write teh file Output.DoIt(cvt); // print timer info long pot = timer.ElapsedMilliseconds; Console.WriteLine("File saved to disk! Took " + pot + " ms!"); Console.WriteLine("Conversion done! Took " + (pot + tra + con) + " ms!"); if (debug) { Debug(new string('-', 80)); Debug("--; Time for Script " + tra + " ms"); Debug("--; Time for Convert " + con + " ms"); Debug("--; Time for Save " + pot + " ms"); Debug("--; Time for Total " + (pot + tra + con) + " ms"); dbWr.Flush(); } if (pause) { Console.ReadKey(); } }