//TODO: Remove ScriptDocumentTab from here public override List <CompilerError> UpdateFunctionBarItems(ScriptDocumentTab tab, MemoryStream stream, ComboBox target) { List <CompilerError> result = new List <CompilerError>(); if (stream == null) { return(result); } target.Items.Clear(); AcsParserSE parser = new AcsParserSE { AddArgumentsToScriptNames = true, IsMapScriptsLump = tab is ScriptLumpDocumentTab, IgnoreErrors = true }; DataLocation dl = new DataLocation(DataLocation.RESOURCE_DIRECTORY, Path.GetDirectoryName(string.IsNullOrEmpty(tab.Filename)? tab.Title : tab.Filename), false, false, false); TextResourceData data = new TextResourceData(stream, dl, (parser.IsMapScriptsLump ? "?SCRIPTS" : tab.Filename)); if (parser.Parse(data, false)) { target.Items.AddRange(parser.NamedScripts.ToArray()); target.Items.AddRange(parser.NumberedScripts.ToArray()); target.Items.AddRange(parser.Functions.ToArray()); } if (parser.HasError) { result.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine)); } return(result); }
// This runs the compiler public override bool Run() { Process process; int line = 0; string sourcedir = Path.GetDirectoryName(sourcefile); // Preprocess the file parser = new AcsParserSE { IsMapScriptsLump = SourceIsMapScriptsLump, OnInclude = delegate(AcsParserSE se, string includefile, AcsParserSE.IncludeType includetype) { TextResourceData data = General.Map.Data.GetTextResourceData(includefile); if (data == null) { se.ReportError("Unable to find include file \"" + includefile + "\"."); return(false); // Fial } return(se.Parse(data, true, includetype, false)); } }; string inputfilepath = Path.Combine(this.tempdir.FullName, inputfile); using (FileStream stream = File.OpenRead(inputfilepath)) { // Map SCRIPTS lump is empty. Abort the process without generating any warnings or errors. if (SourceIsMapScriptsLump && stream.Length == 0) { return(false); } DataLocation dl = new DataLocation(DataLocation.RESOURCE_DIRECTORY, Path.GetDirectoryName(inputfilepath), false, false, false); //mxd. TextResourceData must point to temp path when compiling WAD lumps for lump to be recognized as map lump when reporting errors... TextResourceData data = new TextResourceData(stream, dl, (SourceIsMapScriptsLump ? inputfile : sourcefile)); if (!parser.Parse(data, info.Files, true, AcsParserSE.IncludeType.NONE, false)) { // Check for errors if (parser.HasError) { ReportError(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine)); } return(true); } } //mxd. External lumps should be libraries if (!SourceIsMapScriptsLump && !parser.IsLibrary) { ReportError(new CompilerError("External ACS files can only be compiled as libraries.", sourcefile)); return(true); } //mxd. Update script names if we are compiling the map SCRIPTS lump if (SourceIsMapScriptsLump) { General.Map.UpdateScriptNames(parser); } //xabis // Copy includes from the resources into the compiler's folder, preserving relative pathing and naming HashSet <string> includes = parser.GetIncludes(); //mxd foreach (string include in includes) { // Grab the script text from the resources TextResourceData data = General.Map.Data.GetTextResourceData(include); if (data != null && data.Stream != null) { // Pull the pk3 or directory sub folder out if applicable FileInfo fi = new FileInfo(Path.Combine(this.tempdir.FullName, include)); // Do not allow files to be overwritten, either accidentally or maliciously if (!fi.Exists) { General.WriteLogLine("Copying script include: " + include); // Create the directory path as needed if (!string.IsNullOrEmpty(fi.DirectoryName)) { Directory.CreateDirectory(fi.DirectoryName); } // Dump the script into the target file BinaryReader reader = new BinaryReader(data.Stream); File.WriteAllBytes(fi.FullName, reader.ReadBytes((int)data.Stream.Length)); } } } // Create parameters string args = this.parameters; args = args.Replace("%FI", inputfile); args = args.Replace("%FO", outputfile); args = args.Replace("%FS", sourcefile); args = args.Replace("%PT", this.tempdir.FullName); args = args.Replace("%PS", sourcedir); args = args.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); //mxd. This fixes include path when the map is in a root directory // Setup process info ProcessStartInfo processinfo = new ProcessStartInfo(); processinfo.Arguments = args; processinfo.FileName = Path.Combine(info.Path, info.ProgramFile); //mxd processinfo.CreateNoWindow = false; processinfo.ErrorDialog = false; processinfo.UseShellExecute = true; processinfo.WindowStyle = ProcessWindowStyle.Hidden; processinfo.WorkingDirectory = this.workingdir; // Output info General.WriteLogLine("Running compiler..."); General.WriteLogLine("Program: " + processinfo.FileName); General.WriteLogLine("Arguments: " + processinfo.Arguments); try { // Start the compiler process = Process.Start(processinfo); } catch (Exception e) { // Unable to start the compiler General.ShowErrorMessage("Unable to start the compiler (" + info.Name + "). " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); return(false); } // Wait for compiler to complete process.WaitForExit(); TimeSpan deltatime = TimeSpan.FromTicks(process.ExitTime.Ticks - process.StartTime.Ticks); General.WriteLogLine("Compiler process has finished."); General.WriteLogLine("Compile time: " + deltatime.TotalSeconds.ToString("########0.00") + " seconds"); // Now find the error file string errfile = Path.Combine(this.workingdir, ACS_ERROR_FILE); if (File.Exists(errfile)) { try { // Regex to find error lines Regex errlinematcher = new Regex(":[0-9]+: ", RegexOptions.Compiled | RegexOptions.CultureInvariant); // Read all lines bool erroradded = false; //mxd string[] errlines = File.ReadAllLines(errfile); string temppath = this.tempdir.FullName + Path.DirectorySeparatorChar.ToString(); //mxd. Need trailing slash.. while (line < errlines.Length) { // Check line string linestr = errlines[line]; Match match = errlinematcher.Match(linestr); if (match.Success && (match.Index > 0)) { CompilerError err = new CompilerError(); // The match without spaces and semicolon is the line number string linenr = match.Value.Replace(":", "").Trim(); if (!int.TryParse(linenr, out err.linenumber)) { err.linenumber = CompilerError.NO_LINE_NUMBER; } else { err.linenumber--; } // Everything before the match is the filename err.filename = linestr.Substring(0, match.Index).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); //mxd. Get rid of temp directory path if (err.filename.StartsWith(temppath)) { err.filename = err.filename.Replace(temppath, string.Empty); } if (!Path.IsPathRooted(err.filename)) { //mxd. If the error is in an include file, try to find it in loaded resources if (includes.Contains(err.filename)) { foreach (DataReader dr in General.Map.Data.Containers) { if (dr is DirectoryReader && dr.FileExists(err.filename)) { err.filename = Path.Combine(dr.Location.location, err.filename); break; } } } else { // Add working directory to filename, so it could be recognized as map namespace lump in MapManager.CompileLump() err.filename = Path.Combine(processinfo.WorkingDirectory, err.filename); } } // Everything after the match is the description err.description = linestr.Substring(match.Index + match.Length).Trim(); // Report the error ReportError(err); erroradded = true; //mxd } // Next line line++; } //mxd. Some ACC errors are not properly formatted. If that's the case, threat the whole acs.err as an error... if (!erroradded && errlines.Length > 0) { ReportError(new CompilerError(string.Join(Environment.NewLine, errlines))); } } catch (Exception e) { // Error reading errors (ironic, isn't it) ReportError(new CompilerError("Failed to retrieve compiler error report. " + e.GetType().Name + ": " + e.Message)); } } return(true); }