//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); }
//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(); ModeldefParserSE parser = new ModeldefParserSE(); TextResourceData data = new TextResourceData(stream, new DataLocation(), "MODELDEF", false); if (parser.Parse(data, false)) { target.Items.AddRange(parser.Models.ToArray()); } if (parser.HasError) { result.Add(new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine)); } return(result); }
//mxd protected bool AddTextResource(TextResourceData parsedata) { // Script Editor resources don't have actual path and should always be parsed if(string.IsNullOrEmpty(parsedata.SourceLocation.location)) { if(parsedata.Trackable) throw new NotSupportedException("Trackable TextResource must have a valid path."); return true; } string path = Path.Combine(parsedata.SourceLocation.location, parsedata.Filename + (parsedata.LumpIndex != -1 ? "#" + parsedata.LumpIndex : "")); if(scriptresources.ContainsKey(path) || untrackedtextresources.Contains(path)) return false; //mxd. Create TextResource for this file if(parsedata.Trackable) { textresourcepath = path; ScriptResource res = new ScriptResource(parsedata, this.ScriptType); scriptresources.Add(textresourcepath, res); } // Track the untrackable! else { untrackedtextresources.Add(path); textresourcepath = string.Empty; } return true; }
public override bool Parse(TextResourceData data, bool clearerrors) { // Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream char[] space = { ' ', '\t' }; while (SkipWhitespace(true)) { string line = ReadLine(); if (string.IsNullOrEmpty(line) || line.StartsWith("!")) { continue; // Skip comments } // "R G B Name with spaces" string[] parts = line.Split(space, StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 4) { ReportError("Incorrect X11R6RGB color assignment"); return(false); } // Parse colors byte r = 0, g = 0, b = 0; if (!ReadByte(parts[0], ref r)) { ReportError("Expected red color value in [0 .. 255] range"); return(false); } if (!ReadByte(parts[1], ref g)) { ReportError("Expected green color value in [0 .. 255] range"); return(false); } if (!ReadByte(parts[2], ref b)) { ReportError("Expected blue color value in [0 .. 255] range"); return(false); } // Assemble name string colorname = string.Join("", parts, 3, parts.Length - 3); // Add to collection knowncolors[colorname] = new PixelColor(255, r, g, b); } return(true); }
override public bool Parse(TextResourceData data, bool clearerrors) { if (string.IsNullOrEmpty(mapname)) { throw new NotSupportedException("Map name required!"); } return(Parse(data, mapname, clearerrors)); }
public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken(); if (string.IsNullOrEmpty(token) || token.ToUpperInvariant() != "MODEL") { continue; } SkipWhitespace(true); int startpos = (int)datastream.Position; string modelname = ReadToken(); SkipWhitespace(true); token = ReadToken(); //this should be "{" if (token == "{") { ScriptItem i = new ScriptItem(modelname, startpos, false); models.Add(i); } while (SkipWhitespace(true)) { token = ReadToken(); if (string.IsNullOrEmpty(token) || token == "}") { break; } } } // Sort nodes models.Sort(ScriptItem.SortByName); return(true); }
public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } char[] dots = { ':' }; char[] brace = { '[' }; // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken(); if (!string.IsNullOrEmpty(token)) { // Sound sequence definition if (token.StartsWith(":")) { string val = token.TrimStart(dots); if (!string.IsNullOrEmpty(val) && !seqencenames.Contains(val.ToUpper())) { sequences.Add(val); seqencenames.Add(val.ToUpper()); } } // Group definition else if (token.StartsWith("[")) { string val = token.TrimStart(brace); if (!string.IsNullOrEmpty(val) && !seqencenames.Contains(val.ToUpper())) { sequencegroups.Add(val); seqencenames.Add(val.ToUpper()); } } } } return(true); }
public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken(); if (string.IsNullOrEmpty(token) || token.ToUpperInvariant() != "ACTOR") { continue; } SkipWhitespace(true); int startpos = (int)datastream.Position; List <string> definition = new List <string>(); do { token = ReadToken(false); // Don't skip newline if (string.IsNullOrEmpty(token) || token == "{" || token == "}") { break; } definition.Add(token); } while(SkipWhitespace(false)); // Don't skip newline string name = string.Join(" ", definition.ToArray()); if (!string.IsNullOrEmpty(name)) { actors.Add(new ScriptItem(name, startpos, false)); } } // Sort nodes actors.Sort(ScriptItem.SortByName); return(true); }
//mxd protected bool AddTextResource(TextResourceData parsedata) { // Script Editor resources don't have actual path and should always be parsed if (string.IsNullOrEmpty(parsedata.SourceLocation.location)) { if (parsedata.Trackable) { throw new NotSupportedException("Trackable TextResource must have a valid path."); } return(true); } string path = Path.Combine(parsedata.SourceLocation.location, parsedata.Filename + (parsedata.LumpIndex != -1 ? "#" + parsedata.LumpIndex : "")); if (textresources.ContainsKey(path) || untrackedtextresources.Contains(path)) { return(false); } //mxd. Create TextResource for this file if (parsedata.Trackable) { textresourcepath = path; TextResource res = new TextResource { Resource = parsedata.Source, Entries = new HashSet <string>(StringComparer.OrdinalIgnoreCase), Filename = parsedata.Filename.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), LumpIndex = parsedata.LumpIndex, ScriptType = this.ScriptType, }; textresources.Add(textresourcepath, res); } // Track the untrackable! else { untrackedtextresources.Add(path); textresourcepath = string.Empty; } return(true); }
public ScriptResource(TextResourceData source, ScriptType type) { resource = source.Source; resourcepath = resource.Location.location; resourcedisplayname = resource.Location.GetDisplayName(); filename = source.Filename.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); filepathname = Path.Combine(resourcepath, filename); entries = new HashSet <string>(StringComparer.OrdinalIgnoreCase); lumpindex = source.LumpIndex; scripttype = type; isreadonly = resource.IsReadOnly; // Embedded resources require additional tender loving care... if (resource is WADReader) { WADReader wr = (WADReader)resource; if (wr.ParentResource is PK3Reader) { parentresourcelocation = wr.ParentResource.Location.location; } } }
//mxd. TODO: remove this internal ScriptType VerifyScriptType() { ScriptTypeParserSE parser = new ScriptTypeParserSE(); TextResourceData data = new TextResourceData(new MemoryStream(editor.GetText()), new DataLocation(), config.Description); if (parser.Parse(data, false)) { if (parser.ScriptType != ScriptType.UNKNOWN && config.ScriptType != parser.ScriptType) { return(parser.ScriptType); } } if (parser.HasError) { panel.ShowErrors(new List <CompilerError> { new CompilerError(parser.ErrorDescription, parser.ErrorSource, parser.ErrorLine) }, true); } return(ScriptType.UNKNOWN); }
//mxd. This parses the given decorate stream. Returns false on errors public virtual bool Parse(TextResourceData parsedata, bool clearerrors) { // Clear error status? if (clearerrors) { ClearError(); } // Integrity checks // INFO: MapManager.CompileLump() prepends lumpname with "?" to distinguish between temporary files and files compiled in place // We don't want this to show up in error messages if (parsedata.Stream == null) { ReportError("Unable to load \"" + parsedata.Filename.Replace("?", "") + "\""); return(false); } if (parsedata.Stream.Length == 0) { if (!string.IsNullOrEmpty(sourcename) && sourcename != parsedata.Filename) { LogWarning("Include file \"" + parsedata.Filename.Replace("?", "") + "\" is empty"); } else { sourcename = parsedata.Filename; // LogWarning() needs "sourcename" property to properly log the warning... LogWarning("File is empty"); } } datastream = parsedata.Stream; datareader = new BinaryReader(parsedata.Stream, Encoding.ASCII); sourcename = parsedata.Filename; sourcelumpindex = parsedata.LumpIndex; datalocation = parsedata.SourceLocation; datastream.Seek(0, SeekOrigin.Begin); return(true); }
// This parses the given decorate stream // Returns false on errors public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // [ZZ] For whatever reason, the parser is closely tied to the tokenizer, and to the general scripting lumps framework (see scripttype). // For this reason I have to still inherit the old tokenizer while only using the new one. //ReportError("found zscript? :)"); prevstreamposition = -1; tokenizer = new ZScriptTokenizer(datareader); // region-as-category, ZScript ver List <DecorateCategoryInfo> regions = new List <DecorateCategoryInfo>(); while (true) { ZScriptToken token = tokenizer.ExpectToken(ZScriptTokenType.Identifier, // const, enum, class, etc ZScriptTokenType.Whitespace, ZScriptTokenType.Newline, ZScriptTokenType.BlockComment, ZScriptTokenType.LineComment, ZScriptTokenType.Preprocessor); if (token == null) // EOF reached, whatever. { break; } if (!token.IsValid) { ReportError("Expected preprocessor statement, const, enum or class declaraction, got " + token); return(false); } // toplevel tokens allowed are only Preprocessor and Identifier. switch (token.Type) { case ZScriptTokenType.Whitespace: case ZScriptTokenType.Newline: case ZScriptTokenType.BlockComment: break; case ZScriptTokenType.LineComment: { string cmtval = token.Value.TrimStart(); if (cmtval.Length <= 0 || cmtval[0] != '$') { break; } // check for $GZDB_SKIP if (cmtval.Trim().ToLowerInvariant() == "$gzdb_skip") { return(true); } // if we are in a region, read property using function from ZScriptActorStructure if (regions.Count > 0) { ZScriptActorStructure.ParseGZDBComment(regions.Last().Properties, cmtval); } } break; case ZScriptTokenType.Preprocessor: { tokenizer.SkipWhitespace(); ZScriptToken directive = tokenizer.ExpectToken(ZScriptTokenType.Identifier); if (directive == null || !directive.IsValid) { ReportError("Expected preprocessor directive, got " + ((Object)directive ?? "<null>").ToString()); return(false); } string d_value = directive.Value.ToLowerInvariant(); if (d_value == "include") { tokenizer.SkipWhitespace(); ZScriptToken include_name = tokenizer.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.String, ZScriptTokenType.Name); if (include_name == null || !include_name.IsValid) { ReportError("Cannot include: expected a string value, got " + ((Object)include_name ?? "<null>").ToString()); return(false); } if (!ParseInclude(include_name.Value)) { return(false); } } else if (d_value == "region") { // just read everything until newline. string region_name = ""; while (true) { token = tokenizer.ReadToken(); if (token == null || token.Type == ZScriptTokenType.Newline) { break; } region_name += token.Value; } DecorateCategoryInfo region = new DecorateCategoryInfo(); string[] cattitle = region_name.Split(DataManager.CATEGORY_SPLITTER, StringSplitOptions.RemoveEmptyEntries); if (regions.Count > 0) { region.Category.AddRange(regions.Last().Category); region.Properties = new Dictionary <string, List <string> >(regions.Last().Properties, StringComparer.OrdinalIgnoreCase); } region.Category.AddRange(cattitle); regions.Add(region); } else if (d_value == "endregion") { // read everything until newline too? // - only if it causes problems if (regions.Count > 0) { regions.RemoveAt(regions.Count - 1); // remove last region from the list } else { LogWarning("Superfluous #endregion found without corresponding #region"); } } else { ReportError("Unknown preprocessor directive: " + directive.Value); return(false); } break; } case ZScriptTokenType.Identifier: { // identifier can be one of: class, enum, const, struct // the only type that we really care about is class, as it's the one that has all actors. switch (token.Value.ToLowerInvariant()) { case "extend": tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Identifier); if (token == null || !token.IsValid || ((token.Value.ToLowerInvariant() != "class") && (token.Value.ToLowerInvariant() != "struct"))) { ReportError("Expected class or struct, got " + ((Object)token ?? "<null>").ToString()); return(false); } if (!ParseClassOrStruct((token.Value.ToLowerInvariant() == "struct"), true, (regions.Count > 0 ? regions.Last() : null))) { return(false); } break; case "class": // todo parse class if (!ParseClassOrStruct(false, false, (regions.Count > 0 ? regions.Last() : null))) { return(false); } break; case "struct": // todo parse struct if (!ParseClassOrStruct(true, false, null)) { return(false); } break; case "const": if (!ParseConst()) { return(false); } break; case "enum": if (!ParseEnum()) { return(false); } break; case "version": // expect a string. do nothing about it. tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.String); if (token == null || !token.IsValid) { ReportError("Expected version string, got " + ((Object)token ?? "<null>").ToString()); return(false); } break; default: ReportError("Expected preprocessor statement, const, enum or class declaraction, got " + token); return(false); } break; } } } return(true); }
public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream string currentgametype = GameType.UNKNOWN; while (SkipWhitespace(true)) { //INFO: For many commands, using * as the sound name will mean that //INFO: the command will apply to all sounds that do not specify otherwise. string token = StripTokenQuotes(ReadToken()).ToLowerInvariant(); if (string.IsNullOrEmpty(token)) { continue; } // Skipping block for different game? if (currentgametype != GameType.UNKNOWN && currentgametype != General.Map.Config.BaseGame) { // Should we stop skipping? if (token == "$endif") { currentgametype = GameType.UNKNOWN; } continue; } switch (token) { // Must parse all commands to reliably get sound assignments... case "$alias": if (!ParseAlias()) { return(false); } break; case "$ambient": if (!ParseAmbient()) { return(false); } break; case "$archivepath": if (!SkipTokens(1)) { return(false); } break; case "$attenuation": if (!ParseAttenuation()) { return(false); } break; case "$edfoverride": break; case "$limit": if (!ParseLimit()) { return(false); } break; case "$map": if (!SkipTokens(2)) { return(false); } break; case "$mididevice": if (!ParseMidiDevice()) { return(false); } break; case "$musicalias": if (!SkipTokens(2)) { return(false); } break; case "$musicvolume": if (!SkipTokens(2)) { return(false); } break; case "$pitchshift": if (!SkipTokens(2)) { return(false); } break; case "$pitchshiftrange": if (!SkipTokens(1)) { return(false); } break; case "$playeralias": if (!SkipTokens(4)) { return(false); } break; case "$playercompat": if (!SkipTokens(4)) { return(false); } break; case "$playersound": if (!SkipTokens(4)) { return(false); } break; case "$playersounddup": if (!SkipTokens(4)) { return(false); } break; case "$random": if (!ParseRandom()) { return(false); } break; case "$registered": break; case "$rolloff": if (!ParseRolloff()) { return(false); } break; case "$singular": if (!SkipTokens(1)) { return(false); } break; case "$volume": if (!ParseVolume()) { return(false); } break; // Game type blocks... case "$ifdoom": currentgametype = GameType.DOOM; break; case "$ifheretic": currentgametype = GameType.HERETIC; break; case "$ifhexen": currentgametype = GameType.HEXEN; break; case "$ifstrife": currentgametype = GameType.STRIFE; break; // Should be logicalname lumpname pair... default: if (!ParseSoundAssignment(token)) { return(false); } break; } } return(true); }
// Should be called after all decorate actors are parsed public override bool Parse(TextResourceData data, bool clearerrors) { // Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Keep local data Stream localstream = datastream; string localsourcename = sourcename; BinaryReader localreader = datareader; DataLocation locallocation = datalocation; //mxd string localtextresourcepath = textresourcepath; //mxd // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken(); if (string.IsNullOrEmpty(token) || token.ToLowerInvariant() != "model") { if (token != null && token.ToLowerInvariant() == "#include") { //INFO: ZDoom DECORATE include paths can't be relative ("../actor.txt") //or absolute ("d:/project/actor.txt") //or have backward slashes ("info\actor.txt") //include paths are relative to the first parsed entry, not the current one //also include paths may or may not be quoted SkipWhitespace(true); string filename = StripQuotes(ReadToken(false)); //mxd. Don't skip newline //mxd. Sanity checks if (string.IsNullOrEmpty(filename)) { ReportError("Expected file name to include"); return(false); } //mxd. Check invalid path chars if (!CheckInvalidPathChars(filename)) { return(false); } //mxd. Absolute paths are not supported... if (Path.IsPathRooted(filename)) { ReportError("Absolute include paths are not supported by ZDoom"); return(false); } //mxd. Relative paths are not supported if (filename.StartsWith(RELATIVE_PATH_MARKER) || filename.StartsWith(CURRENT_FOLDER_PATH_MARKER) || filename.StartsWith(ALT_RELATIVE_PATH_MARKER) || filename.StartsWith(ALT_CURRENT_FOLDER_PATH_MARKER)) { ReportError("Relative include paths are not supported by ZDoom"); return(false); } //mxd. Backward slashes are not supported if (filename.Contains("\\")) { ReportError("Only forward slashes are supported by ZDoom"); return(false); } //mxd. Already parsed? if (parsedlumps.Contains(filename)) { ReportError("Already parsed \"" + filename + "\". Check your include directives"); return(false); } //mxd. Add to collection parsedlumps.Add(filename); // Callback to parse this file now if (OnInclude != null) { OnInclude(this, filename); } //mxd. Bail out on error if (this.HasError) { return(false); } // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; sourcename = localsourcename; datalocation = locallocation; //mxd textresourcepath = localtextresourcepath; //mxd } continue; } // Find classname SkipWhitespace(true); string classname = StripQuotes(ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS)); if (string.IsNullOrEmpty(classname)) { ReportError("Expected actor class"); return(false); } // Check if actor exists bool haveplaceableactor = actorsbyclass.ContainsKey(classname); if (!haveplaceableactor && (General.Map.Data.GetZDoomActor(classname) == null)) { LogWarning("DECORATE class \"" + classname + "\" does not exist"); } // Now find opening brace if (!NextTokenIs("{")) { return(false); } // Parse the structure ModeldefStructure mds = new ModeldefStructure(); if (mds.Parse(this)) { // Fetch Actor info if (haveplaceableactor) { ThingTypeInfo info = General.Map.Data.GetThingInfoEx(actorsbyclass[classname]); if (info != null) { // Already have a voxel model? if (General.Map.Data.ModeldefEntries.ContainsKey(info.Index) && General.Map.Data.ModeldefEntries[info.Index].IsVoxel) { LogWarning("Both voxel(s) and model(s) are defined for actor\"" + classname + "\". Consider using either former or latter"); } // Actor has a valid sprite? else if (!string.IsNullOrEmpty(info.Sprite) && !info.Sprite.ToLowerInvariant().StartsWith(DataManager.INTERNAL_PREFIX) && (info.Sprite.Length == 6 || info.Sprite.Length == 8)) { string targetsprite = info.Sprite.Substring(0, 5); if (mds.Frames.ContainsKey(targetsprite)) { // Create model data ModelData md = new ModelData { InheritActorPitch = mds.InheritActorPitch, UseActorPitch = mds.UseActorPitch, UseActorRoll = mds.UseActorRoll, Path = mds.DataPath }; // Things are complicated in GZDoom... Matrix moffset = Matrix.Translation(mds.Offset.Y, -mds.Offset.X, mds.Offset.Z); //Matrix mrotation = Matrix.RotationZ(Angle2D.DegToRad(mds.AngleOffset)) * Matrix.RotationY(-Angle2D.DegToRad(mds.RollOffset)) * Matrix.RotationX(-Angle2D.DegToRad(mds.PitchOffset)); Matrix mrotation = Matrix.RotationY((float)-Angle2D.DegToRad(mds.RollOffset)) * Matrix.RotationX((float)-Angle2D.DegToRad(mds.PitchOffset)) * Matrix.RotationZ((float)Angle2D.DegToRad(mds.AngleOffset)); md.SetTransform(mrotation, moffset, mds.Scale); // Add models int disabledframescount = 0; foreach (var fs in mds.Frames[targetsprite]) { // Sanity checks if (string.IsNullOrEmpty(mds.ModelNames[fs.ModelIndex])) { LogWarning("Model definition \"" + classname + "\", frame \"" + fs.SpriteName + " " + fs.FrameName + "\" references undefined model index " + fs.ModelIndex); continue; } //INFO: setting frame index to a negative number disables model rendering in GZDoom if (fs.FrameIndex < 0) { disabledframescount++; continue; } // Texture name will be empty when skin path is embedded in the model string skinname = (!string.IsNullOrEmpty(mds.SkinNames[fs.ModelIndex]) ? mds.SkinNames[fs.ModelIndex].ToLowerInvariant() : string.Empty); md.SkinNames.Add(skinname); md.SurfaceSkinNames.Add(mds.SurfaceSkinNames[fs.ModelIndex]); md.ModelNames.Add(mds.ModelNames[fs.ModelIndex].ToLowerInvariant()); md.FrameNames.Add(fs.FrameName); md.FrameIndices.Add(fs.FrameIndex); } // More sanity checks... if (md.ModelNames.Count == 0) { // Show warning only when frames were not delibeartely disabled if (mds.Frames[targetsprite].Count > 0 && disabledframescount < mds.Frames[targetsprite].Count) { LogWarning("Model definition \"" + classname + "\" has no defined models"); } } else { // Add to collection entries[classname] = md; } } } } } } if (HasError) { LogError(); ClearError(); } } return(true); }
public bool Parse(TextResourceData data, string mapname, bool clearerrors) { this.mapname = mapname.ToLowerInvariant(); //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Keep local data Stream localstream = datastream; string localsourcename = sourcename; int localsourcelumpindex = sourcelumpindex; BinaryReader localreader = datareader; DataLocation locallocation = datalocation; string localtextresourcepath = textresourcepath; // Classic format skip stoppers... HashSet <string> breakat = new HashSet <string> { "map", "defaultmap", "adddefaultmap" }; while (SkipWhitespace(true)) { string token = ReadToken().ToLowerInvariant(); if (string.IsNullOrEmpty(token) || token == "$gzdb_skip") { break; } switch (token) { case "adddefaultmap": // Parse properties if (!ParseMapBlock()) { return(false); } break; case "defaultmap": // Reset MapInfo mapinfo = new MapInfo(); // Parse properties if (!ParseMapBlock()) { return(false); } break; case "map": // Get map lump name SkipWhitespace(true); token = ReadToken().ToLowerInvariant(); if (token != this.mapname) { // Map number? Try to build map name from it... int n; if (int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out n)) { token = ((n > 0 && n < 10) ? "map0" + n : "map" + n); } // Still no dice? if (token != this.mapname) { SkipStructure(breakat); continue; // Not finished, search for next "map", "defaultmap" or "adddefaultmap" block } } // Try to get map name SkipWhitespace(true); token = ReadToken(); if (token.ToLowerInvariant() == "lookup") { // No dice... SkipWhitespace(true); ReadToken(); } else { mapinfo.Title = StripQuotes(token); } // Parse properties if (!ParseMapBlock()) { return(false); } // There is a map entry for current map, which makes it defined mapinfo.IsDefined = true; break; case "include": if (!ParseInclude(clearerrors)) { return(false); } // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; sourcename = localsourcename; sourcelumpindex = localsourcelumpindex; datalocation = locallocation; textresourcepath = localtextresourcepath; break; case "gameinfo": if (!ParseGameInfo()) { return(false); } break; case "doomednums": if (!ParseDoomEdNums()) { return(false); } break; case "spawnnums": if (!ParseSpawnNums()) { return(false); } break; } } // Check values if (mapinfo.FogDensity > 0 && (mapinfo.FadeColor.Red > 0 || mapinfo.FadeColor.Green > 0 || mapinfo.FadeColor.Blue > 0)) { mapinfo.HasFadeColor = true; } if (mapinfo.OutsideFogDensity > 0 && (mapinfo.OutsideFogColor.Red > 0 || mapinfo.OutsideFogColor.Green > 0 || mapinfo.OutsideFogColor.Blue > 0)) { mapinfo.HasOutsideFogColor = true; } if (string.IsNullOrEmpty(mapinfo.Sky2) && mapinfo.DoubleSky) { LogWarning("\"doublesky\" flag is defined without \"Sky2\" property."); mapinfo.DoubleSky = false; } // All done return(!this.HasError); }
/// <summary> /// Parses DECALDEF data /// </summary> /// <param name="data">The data to parse</param> /// <param name="clearerrors">If errors should be cleared</param> /// <returns>true if paring worked, otherwise false</returns> public override bool Parse(TextResourceData data, bool clearerrors) { if (!AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } while (SkipWhitespace(true)) { string token = ReadToken().ToLowerInvariant(); if (string.IsNullOrEmpty(token)) { continue; } switch (token) { case "decal": case "decalgroup": DecalInfo.DecalType type = token == "decal" ? DecalInfo.DecalType.DECAL : DecalInfo.DecalType.DECALGROUP; string decalname; int decalid = -1; SkipWhitespace(false); token = ReadToken(); if (!string.IsNullOrEmpty(token)) { decalname = token; } else { ReportError("Expected decal name, got nothing"); return(false); } SkipWhitespace(false); // Try to read the optional decal id token = ReadToken(); if (!string.IsNullOrEmpty(token)) { ReadSignedInt(token, ref decalid); } SkipWhitespace(true); DecalInfo di = new DecalInfo(decalname, decalid, type); if (!di.Parse(this)) { return(false); } if (decals.ContainsKey(decalname)) { // TODO: report problem // Overwrite existing decal with new one (who knows if that's the correct way do handle duplicate entries?) decals[decalname] = di; } else { decals.Add(decalname, di); } break; } } return(true); }
// 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); }
// This parses the given stream // Returns false on errors public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } //mxd. Make vitrual path from filename string virtualpath; if (data.LumpIndex != -1) // It's TEXTURES lump { virtualpath = data.Filename; } else // If it's actual filename, try to use extension(s) as virtualpath { virtualpath = Path.GetFileName(data.Filename); if (!string.IsNullOrEmpty(virtualpath)) { virtualpath = virtualpath.Substring(8).TrimStart(pathtrimchars); } if (!string.IsNullOrEmpty(virtualpath) && virtualpath.ToLowerInvariant() == "txt") { virtualpath = string.Empty; } if (string.IsNullOrEmpty(virtualpath)) { virtualpath = "[TEXTURES]"; } } // Continue until at the end of the stream while (SkipWhitespace(true)) { // Read a token string objdeclaration = ReadToken(); if (!string.IsNullOrEmpty(objdeclaration)) { objdeclaration = objdeclaration.ToLowerInvariant(); switch (objdeclaration) { case "texture": { // Read texture structure TextureStructure tx = new TextureStructure(this, "texture", virtualpath); if (this.HasError) { return(false); } // if a limit for the texture name length is set make sure that it's not exceeded if (tx.Name.Length > General.Map.Config.MaxTextureNameLength) { ReportError("Texture name \"" + tx.Name + "\" too long. Texture names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); return(false); } //mxd. Can't load image without name if (string.IsNullOrEmpty(tx.Name)) { ReportError("Can't load an unnamed texture. Please consider giving names to your resources"); return(false); } // Add the texture textures[tx.Name] = tx; if (!General.Map.Config.MixTexturesFlats) { flats[tx.Name] = tx; //mxd. If MixTexturesFlats is set, textures and flats will be mixed in DataManager anyway } } break; case "sprite": { // Read sprite structure TextureStructure tx = new TextureStructure(this, "sprite", virtualpath); if (this.HasError) { return(false); } //mxd. Sprite name length must be either 6 or 8 chars if (tx.Name.Length != 6 && tx.Name.Length != 8) { ReportError("Sprite name \"" + tx.Name + "\" is incorrect. Sprite names must have a length of 6 or 8 characters"); return(false); } //mxd. Can't load image without name if (string.IsNullOrEmpty(tx.Name)) { ReportError("Can't load an unnamed sprite. Please consider giving names to your resources"); return(false); } // Add the sprite sprites[tx.Name] = tx; } break; case "walltexture": { // Read walltexture structure TextureStructure tx = new TextureStructure(this, "walltexture", virtualpath); if (this.HasError) { return(false); } // if a limit for the walltexture name length is set make sure that it's not exceeded if (tx.Name.Length > General.Map.Config.MaxTextureNameLength) { ReportError("WallTexture name \"" + tx.Name + "\" too long. WallTexture names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); return(false); } //mxd. Can't load image without name if (string.IsNullOrEmpty(tx.Name)) { ReportError("Can't load an unnamed WallTexture. Please consider giving names to your resources"); return(false); } // Add the walltexture if (!textures.ContainsKey(tx.Name) || (textures[tx.Name].TypeName != "texture")) { textures[tx.Name] = tx; } } break; case "flat": { // Read flat structure TextureStructure tx = new TextureStructure(this, "flat", virtualpath); if (this.HasError) { return(false); } // if a limit for the flat name length is set make sure that it's not exceeded if (tx.Name.Length > General.Map.Config.MaxTextureNameLength) { ReportError("Flat name \"" + tx.Name + "\" too long. Flat names must have a length of " + General.Map.Config.MaxTextureNameLength + " characters or less"); return(false); } //mxd. Can't load image without name if (string.IsNullOrEmpty(tx.Name)) { ReportError("Can't load an unnamed flat. Please consider giving names to your resources"); return(false); } // Add the flat if (!flats.ContainsKey(tx.Name) || (flats[tx.Name].TypeName != "texture")) { flats[tx.Name] = tx; } } break; case "$gzdb_skip": return(!this.HasError); default: { // Unknown structure! // Best we can do now is just find the first { and then // follow the scopes until the matching } is found string token2; do { if (!SkipWhitespace(true)) { break; } token2 = ReadToken(); if (string.IsNullOrEmpty(token2)) { break; } }while(token2 != "{"); int scopelevel = 1; do { if (!SkipWhitespace(true)) { break; } token2 = ReadToken(); if (string.IsNullOrEmpty(token2)) { break; } if (token2 == "{") { scopelevel++; } if (token2 == "}") { scopelevel--; } }while(scopelevel > 0); } break; } } } // Return true when no errors occurred return(ErrorDescription == null); }
public bool Parse(TextResourceData data, HashSet <string> configincludes, bool processincludes, IncludeType includetype, bool clearerrors) { string source = data.Filename.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); //INFO: files included or imported inside a library are not visible to the code outside it //and must be included/imported separately // Includes tracking. "Regular" includes go to "SCRIPTS" group, library includes are tracked per-library string includecategory = (processincludes && includetype == IncludeType.LIBRARY ? source : "SCRIPTS"); if (!includes.ContainsKey(includecategory)) { includes.Add(includecategory, new HashSet <string>(StringComparer.OrdinalIgnoreCase)); } includestoskip = configincludes; int bracelevel = 0; // Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Keep local data Stream localstream = datastream; string localsourcename = sourcename; int localsourcelumpindex = sourcelumpindex; BinaryReader localreader = datareader; DataLocation locallocation = datalocation; string localincludecategory = includecategory; // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken().ToLowerInvariant(); if (string.IsNullOrEmpty(token)) { continue; } // Ignore inner scope stuff if (token == "{") { bracelevel++; continue; } if (token == "}") { bracelevel--; continue; } if (bracelevel > 0) { continue; } switch (token) { case "script": { SkipWhitespace(true); int startpos = (int)datastream.Position; token = ReadToken(); // Is this a named script? if (token.IndexOf('"') != -1) { startpos += 1; string scriptname = StripQuotes(token); // Try to parse argument names List <KeyValuePair <string, string> > args = ParseArgs(); List <string> argnames = new List <string>(); foreach (KeyValuePair <string, string> group in args) { argnames.Add(group.Value); } // Make full name if (AddArgumentsToScriptNames) { scriptname += " " + GetArgumentNames(args); } // Add to collection namedscripts.Add(new ScriptItem(scriptname, argnames, startpos, includetype != IncludeType.NONE)); } // Should be numbered script else { int n; if (int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out n)) { // Try to parse argument names List <KeyValuePair <string, string> > args = ParseArgs(); // Now find the opening brace do { if (!SkipWhitespace(true)) { break; } token = ReadToken(); } while(!string.IsNullOrEmpty(token) && token != "{"); token = ReadLine(); string name = ""; bracelevel = 1; if (!string.IsNullOrEmpty(token)) { int commentstart = token.IndexOf("//", StringComparison.Ordinal); if (commentstart != -1) // Found comment { commentstart += 2; name = token.Substring(commentstart, token.Length - commentstart).Trim(); } } bool customname = (name.Length > 0); name = (customname ? name + " [Script " + n + "]" : "Script " + n); List <string> argnames = new List <string>(); foreach (KeyValuePair <string, string> group in args) { argnames.Add(group.Value); } // Make full name if (AddArgumentsToScriptNames) { name += " " + GetArgumentNames(args); } // Add to collection numberedscripts.Add(new ScriptItem(n, name, argnames, startpos, includetype != IncludeType.NONE, customname)); } } } break; case "function": { SkipWhitespace(true); string funcname = ReadToken(); // Read return type SkipWhitespace(true); int startpos = (int)datastream.Position; funcname += " " + ReadToken(); // Read function name // Try to parse argument names List <KeyValuePair <string, string> > args = ParseArgs(); List <string> argnames = new List <string>(); foreach (KeyValuePair <string, string> group in args) { argnames.Add(group.Value); } // Make full name if (AddArgumentsToScriptNames) { funcname += GetArgumentNames(args); } // Add to collection functions.Add(new ScriptItem(funcname, argnames, startpos, includetype != IncludeType.NONE)); } break; case "#library": if (IsMapScriptsLump) { if (!IgnoreErrors) { ReportError("SCRIPTS lump can't be compiled as library."); } return(IgnoreErrors); } SkipWhitespace(true); string libname = ReadToken(false); // Don't skip newline if (!libname.StartsWith("\"") || !libname.EndsWith("\"")) { if (!IgnoreErrors) { ReportError("#library name should be quoted."); } return(IgnoreErrors); } libname = StripQuotes(libname); if (string.IsNullOrEmpty(libname)) { if (!IgnoreErrors) { ReportError("Expected library name."); } return(IgnoreErrors); } // Store only when the script compiling was executed for is library if (includetype == IncludeType.NONE) { libraryname = libname; includetype = IncludeType.LIBRARY; } break; default: if (processincludes && (token == "#include" || token == "#import")) { //INFO: ZDoom ACC include paths can be absolute ("d:\stuff\coollib.acs"), relative ("../coollib.acs") //and can use forward and backward slashes ("acs\map01/script.acs") //also include paths must be quoted //long filenames are supported SkipWhitespace(true); string includelump = ReadToken(false); // Don't skip newline if (!includelump.StartsWith("\"") || !includelump.EndsWith("\"")) { if (!IgnoreErrors) { ReportError(token + " filename should be quoted."); } return(IgnoreErrors); } includelump = StripQuotes(includelump); if (string.IsNullOrEmpty(includelump)) { if (!IgnoreErrors) { ReportError("Expected file name to " + token + "."); } return(IgnoreErrors); } includelump = includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); // Compiler files? Track them, but don't parse them if (includestoskip.Contains(includelump)) { // These can also be included several times... if (includes[includecategory].Contains(includelump)) { if (!IgnoreErrors) { ReportError("Already parsed \"" + includelump + "\". Check your " + token + " directives."); } return(IgnoreErrors); } // Add to collection includes[includecategory].Add(includelump); continue; } // Convert to a path we can use string includelumppath = GetRootedPath(source, includelump); // Rooting succeeded? if (this.HasError || string.IsNullOrEmpty(includelumppath)) { return(IgnoreErrors); } // Already parsed? if (includes[includecategory].Contains(includelumppath)) { if (!IgnoreErrors) { ReportError("Already parsed \"" + includelump + "\". Check your " + token + " directives."); } return(IgnoreErrors); } // Add to collection includes[includecategory].Add(includelumppath); // Callback to parse this file if (OnInclude != null) { IsMapScriptsLump = false; if (!OnInclude(this, includelumppath, (token == "#import" ? IncludeType.LIBRARY : IncludeType.INCLUDE))) { return(IgnoreErrors); // Bail out on errors } } // Bail out on error if (this.HasError) { return(IgnoreErrors); } // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; sourcename = localsourcename; sourcelumpindex = localsourcelumpindex; datalocation = locallocation; includecategory = localincludecategory; } break; } } return(true); }
public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken(); long cpos = datastream.Position; if (!string.IsNullOrEmpty(token)) { token = token.ToUpperInvariant(); if (token == "MODEL") { SkipWhitespace(true); ReadToken(); //should be model name SkipWhitespace(true); token = ReadToken(); //should be opening brace if (token == "{") { scripttype = ScriptType.MODELDEF; return(true); } } else if (token == "SCRIPT") { SkipWhitespace(true); ReadToken(); //should be script name or number SkipWhitespace(true); ReadToken(); //should be script parameters/type SkipWhitespace(true); token = ReadToken(); //should be opening brace if (token == "{") { scripttype = ScriptType.ACS; return(true); } } else if (token == "ACTOR") // [ZZ] note: by the looks of it, this doesn't handle the case when we write actor with DoomEdNum. { SkipWhitespace(true); ReadToken(); //should be actor name SkipWhitespace(true); token = ReadToken(); // [ZZ] note: original code compared token to REPLACES without doing ToUpper if (token == ":" || token == "{" || (token != null && token.ToUpperInvariant() == "REPLACES")) { scripttype = ScriptType.DECORATE; return(true); } SkipWhitespace(true); token = ReadToken(); //should be actor name // [ZZ] if (token != "{") // actor bla : bla2 10666 { { SkipWhitespace(true); token = ReadToken(); } if (token == "{") { scripttype = ScriptType.DECORATE; return(true); } } else if (token == "CLASS" || token == "STRUCT" || token == "ENUM" || token == "EXTEND") { if (token == "EXTEND") { SkipWhitespace(true); token = ReadToken(); if (!string.IsNullOrEmpty(token)) { token = token.ToUpperInvariant(); } } string otoken = token; // original token SkipWhitespace(true); ReadToken(); //should be actor name SkipWhitespace(true); token = ReadToken(); if ((otoken != "ENUM" && token == ":") || token == "{" || (otoken == "CLASS" && (token != null && token.ToUpperInvariant() == "REPLACES"))) { scripttype = ScriptType.ZSCRIPT; return(true); } SkipWhitespace(true); token = ReadToken(); //should be actor name if (token == "{") { scripttype = ScriptType.ZSCRIPT; return(true); } return(true); } } datastream.Position = cpos; // [ZZ] read next token, not whatever is left after possibly unsuccessful parsing. } return(false); }
public override bool Parse(TextResourceData data, bool clearerrors) { // Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken(); if (string.IsNullOrEmpty(token) || string.Compare(token, "CAMERATEXTURE", true) != 0) { continue; } // Texture name SkipWhitespace(true); string texturename = StripQuotes(ReadToken(false)); if (string.IsNullOrEmpty(texturename)) { ReportError("Expected camera texture name"); return(false); } /* * // Camera texture names are limited to 8 chars * if(texturename.Length > DataManager.CLASIC_IMAGE_NAME_LENGTH) * { * ReportError("Camera texture names must be no longer than " + DataManager.CLASIC_IMAGE_NAME_LENGTH + " chars"); * return false; * }*/ // [ZZ] not really. I've successfully tried it with >8 chars and it works. // Width int width = -1; SkipWhitespace(true); if (!ReadSignedInt(ref width) || width < 1) { ReportError("Expected camera texture width"); return(false); } // Height int height = -1; SkipWhitespace(true); if (!ReadSignedInt(ref height) || height < 1) { ReportError("Expected camera texture height"); return(false); } // "Fit" keyword? bool worldpanning = false; bool fit = false; float scalex = 1.0f; float scaley = 1.0f; if (NextTokenIs("fit", false)) { fit = true; int fitwidth = width; int fitheight = height; // Fit width SkipWhitespace(true); if (!ReadSignedInt(ref fitwidth) || fitwidth < 1) { ReportError("Expected camera texture fit width"); return(false); } // Fit height SkipWhitespace(true); if (!ReadSignedInt(ref fitheight) || fitheight < 1) { ReportError("Expected camera texture fit height"); return(false); } // Update scale scalex = (float)fitwidth / width; scaley = (float)fitheight / height; // WorldPanning worldpanning = NextTokenIs("worldpanning", false); } else if (NextTokenIs("worldpanning", false)) { worldpanning = true; } // Check results if (cameratextures.ContainsKey(texturename.ToUpperInvariant())) { ReportError("Camera texture \"" + texturename + "\" is defined more than once"); return(false); } // Store results texturename = texturename.ToUpperInvariant(); cameratextures[texturename] = new CameraTextureData { Name = texturename, Width = width, Height = height, ScaleX = scalex, ScaleY = scaley, WorldPanning = worldpanning, FitTexture = fit }; } return(true); }
public override bool Parse(TextResourceData data, bool clearerrors) { // Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream HashSet <string> knowntypes = new HashSet <string> { "int", "float", "color", "bool", "string" }; HashSet <string> flags = new HashSet <string> { "noarchive", "cheat", "latch" }; while (SkipWhitespace(true)) { string token = ReadToken().ToLowerInvariant(); if (string.IsNullOrEmpty(token)) { continue; } //<scope> [noarchive] [cheat] [latch] <type> <name> [= <defaultvalue>]; switch (token) { case "user": case "server": case "nosave": // read (skip) flags while (true) { string flagtoken; SkipWhitespace(true); flagtoken = ReadToken().ToLowerInvariant(); if (!flags.Contains(flagtoken)) { DataStream.Seek(-flagtoken.Length - 1, SeekOrigin.Current); break; } } // Type SkipWhitespace(true); string type = ReadToken().ToLowerInvariant(); if (!knowntypes.Contains(type)) { ReportError("Unknown cvar type"); return(false); } // Name SkipWhitespace(true); string name = ReadToken(); if (string.IsNullOrEmpty(name)) { ReportError("Expected cvar name"); return(false); } // Either "=" or ";" SkipWhitespace(true); token = ReadToken(); switch (token) { case "=": SkipWhitespace(true); string value = ReadToken(); if (string.IsNullOrEmpty(value)) { ReportError("Expected \"" + name + "\" cvar value"); return(false); } // Add to collection if (!AddValue(name, type, value)) { return(false); } // Next should be ";" if (!NextTokenIs(";")) { return(false); } break; case ";": if (!AddValue(name, type, string.Empty)) { return(false); } break; } break; default: ReportError("Unknown keyword"); return(false); } } return(true); }
public override bool Parse(TextResourceData data, bool clearerrors) { entries = new Dictionary <string, ModelData>(StringComparer.Ordinal); //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } List <string> spriteNames = new List <string>(); string modelName = string.Empty; string prevToken = string.Empty; // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken().ToLowerInvariant(); if (string.IsNullOrEmpty(token)) { continue; } if (token == ",") //previous token was a sprite name { if (!string.IsNullOrEmpty(prevToken) && !spriteNames.Contains(prevToken)) { spriteNames.Add(prevToken); } prevToken = StripQuotes(token).ToUpperInvariant(); } else if (token == "=") //next token should be a voxel model name { if (!string.IsNullOrEmpty(prevToken) && !spriteNames.Contains(prevToken)) { spriteNames.Add(prevToken); } SkipWhitespace(true); token = ReadToken(); if (string.IsNullOrEmpty(token)) { ReportError("Expected voxel name"); return(false); } modelName = StripQuotes(token).ToUpperInvariant(); } else if (token == "{") //read the settings { ModelData mde = new ModelData { IsVoxel = true }; float scale = 1.0f; while (SkipWhitespace(true)) { token = ReadToken().ToLowerInvariant(); if (string.IsNullOrEmpty(token)) { continue; } if (token == "}") //store data { if (!string.IsNullOrEmpty(modelName) && spriteNames.Count > 0) { mde.ModelNames.Add(modelName); mde.SetTransform(Matrix.RotationZ((float)Angle2D.DegToRad(mde.AngleOffset)), Matrix.Identity, new Vector3f(scale)); foreach (string s in spriteNames) { //TODO: is this the proper behaviour? entries[s] = mde; } //reset local data modelName = string.Empty; prevToken = string.Empty; spriteNames.Clear(); } break; } else if (token == "overridepalette") { mde.OverridePalette = true; } else if (token == "angleoffset") { if (!NextTokenIs("=")) { return(false); } token = ReadToken(); if (!ReadSignedFloat(token, ref mde.AngleOffset)) { // Not numeric! ReportError("Expected AngleOffset value, but got \"" + token + "\""); return(false); } } else if (token == "scale") { if (!NextTokenIs("=")) { return(false); } token = ReadToken(); if (!ReadSignedFloat(token, ref scale)) { // Not numeric! ReportError("Expected Scale value, but got \"" + token + "\""); return(false); } } prevToken = StripQuotes(token).ToUpperInvariant(); } } else { prevToken = StripQuotes(token).ToUpperInvariant(); } } return(entries.Count > 0); }
public bool Parse(TextResourceData data, bool processincludes, IncludeType includetype, bool clearerrors) { return(Parse(data, includestoskip, processincludes, includetype, clearerrors)); }
// This parses the given decorate stream // Returns false on errors public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } //mxd. Region-as-category stuff... List <DecorateCategoryInfo> regions = new List <DecorateCategoryInfo>(); //mxd // Keep local data Stream localstream = datastream; string localsourcename = sourcename; BinaryReader localreader = datareader; DataLocation locallocation = datalocation; //mxd string localtextresourcepath = textresourcepath; //mxd // Continue until at the end of the stream while (SkipWhitespace(true)) { // Read a token string objdeclaration = ReadToken(); if (!string.IsNullOrEmpty(objdeclaration)) { objdeclaration = objdeclaration.ToLowerInvariant(); if (objdeclaration == "$gzdb_skip") { break; } switch (objdeclaration) { case "actor": { // Read actor structure ActorStructure actor = new ActorStructure(this, (regions.Count > 0 ? regions[regions.Count - 1] : null)); if (this.HasError) { return(false); } // Add the actor archivedactors[actor.ClassName.ToLowerInvariant()] = actor; if (actor.CheckActorSupported()) { actors[actor.ClassName.ToLowerInvariant()] = actor; } // Replace an actor? if (actor.ReplacesClass != null) { if (GetArchivedActorByName(actor.ReplacesClass) != null) { archivedactors[actor.ReplacesClass.ToLowerInvariant()] = actor; } else { LogWarning("Unable to find \"" + actor.ReplacesClass + "\" class to replace, while parsing \"" + actor.ClassName + "\""); } if (actor.CheckActorSupported() && GetActorByName(actor.ReplacesClass) != null) { actors[actor.ReplacesClass.ToLowerInvariant()] = actor; } } //mxd. Add to current text resource if (!textresources[textresourcepath].Entries.Contains(actor.ClassName)) { textresources[textresourcepath].Entries.Add(actor.ClassName); } } break; case "#include": { //INFO: ZDoom DECORATE include paths can't be relative ("../actor.txt") //or absolute ("d:/project/actor.txt") //or have backward slashes ("info\actor.txt") //include paths are relative to the first parsed entry, not the current one //also include paths may or may not be quoted SkipWhitespace(true); string filename = StripQuotes(ReadToken(false)); //mxd. Don't skip newline //mxd. Sanity checks if (string.IsNullOrEmpty(filename)) { ReportError("Expected file name to include"); return(false); } //mxd. Check invalid path chars if (!CheckInvalidPathChars(filename)) { return(false); } //mxd. Absolute paths are not supported... if (Path.IsPathRooted(filename)) { ReportError("Absolute include paths are not supported by ZDoom"); return(false); } //mxd. Relative paths are not supported if (filename.StartsWith(RELATIVE_PATH_MARKER) || filename.StartsWith(CURRENT_FOLDER_PATH_MARKER) || filename.StartsWith(ALT_RELATIVE_PATH_MARKER) || filename.StartsWith(ALT_CURRENT_FOLDER_PATH_MARKER)) { ReportError("Relative include paths are not supported by ZDoom"); return(false); } //mxd. Backward slashes are not supported if (filename.Contains(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture))) { ReportError("Only forward slashes are supported by ZDoom"); return(false); } //mxd. Already parsed? if (parsedlumps.Contains(filename)) { ReportError("Already parsed \"" + filename + "\". Check your include directives"); return(false); } //mxd. Add to collection parsedlumps.Add(filename); // Callback to parse this file now if (OnInclude != null) { OnInclude(this, filename); } //mxd. Bail out on error if (this.HasError) { return(false); } // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; sourcename = localsourcename; datalocation = locallocation; //mxd textresourcepath = localtextresourcepath; //mxd } break; case "damagetype": //mxd // Get DamageType name SkipWhitespace(true); string damagetype = StripQuotes(ReadToken(false)); if (string.IsNullOrEmpty(damagetype)) { ReportError("Expected DamageType name"); return(false); } // Next should be "{" SkipWhitespace(true); if (!NextTokenIs("{")) { return(false); } // Skip the structure while (SkipWhitespace(true)) { string t = ReadToken(); if (string.IsNullOrEmpty(t) || t == "}") { break; } } // Add to collection if (!damagetypes.Contains(damagetype)) { damagetypes.Add(damagetype); } break; case "enum": case "native": case "const": while (SkipWhitespace(true)) { string t = ReadToken(); if (string.IsNullOrEmpty(t) || t == ";") { break; } } break; //mxd. Region-as-category handling case "#region": SkipWhitespace(false); string cattitle = ReadLine(); if (!string.IsNullOrEmpty(cattitle)) { // Make new category info string[] parts = cattitle.Split(DataManager.CATEGORY_SPLITTER, StringSplitOptions.RemoveEmptyEntries); DecorateCategoryInfo info = new DecorateCategoryInfo(); if (regions.Count > 0) { // Preserve nesting info.Category.AddRange(regions[regions.Count - 1].Category); info.Properties = new Dictionary <string, List <string> >(regions[regions.Count - 1].Properties); } info.Category.AddRange(parts); // Add to collection regions.Add(info); } break; //mxd. Region-as-category handling case "#endregion": if (regions.Count > 0) { regions.RemoveAt(regions.Count - 1); } else { LogWarning("Unexpected #endregion token"); } break; default: { //mxd. In some special cases (like the whole actor commented using "//") our special comments will be detected here... if (objdeclaration.StartsWith("$")) { if (regions.Count > 0) { // Store region property regions[regions.Count - 1].Properties[objdeclaration] = new List <string> { (SkipWhitespace(false) ? ReadLine() : "") }; } else { // Skip the whole line, then carry on ReadLine(); } break; } // Unknown structure! // Best we can do now is just find the first { and then // follow the scopes until the matching } is found string token2; do { if (!SkipWhitespace(true)) { break; } token2 = ReadToken(); if (string.IsNullOrEmpty(token2)) { break; } }while(token2 != "{"); int scopelevel = 1; do { if (!SkipWhitespace(true)) { break; } token2 = ReadToken(); if (string.IsNullOrEmpty(token2)) { break; } if (token2 == "{") { scopelevel++; } if (token2 == "}") { scopelevel--; } }while(scopelevel > 0); } break; } } } // Return true when no errors occurred return(ErrorDescription == null); }
public override bool Parse(TextResourceData data, bool clearerrors) { return(Parse(data, new HashSet <string>(), false, IncludeType.NONE, clearerrors)); }
// Should be called after all decorate actors are parsed public override bool Parse(TextResourceData data, bool clearerrors) { // Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken(); if (string.IsNullOrEmpty(token) || token.ToLowerInvariant() != "model") { continue; } // Find classname SkipWhitespace(true); string classname = StripQuotes(ReadToken(ActorStructure.ACTOR_CLASS_SPECIAL_TOKENS)); if (string.IsNullOrEmpty(classname)) { ReportError("Expected actor class"); return(false); } // Check if actor exists bool haveplaceableactor = actorsbyclass.ContainsKey(classname); if (!haveplaceableactor && (General.Map.Data.GetZDoomActor(classname) == null)) { LogWarning("DECORATE class \"" + classname + "\" does not exist"); } // Now find opening brace if (!NextTokenIs("{")) { return(false); } // Parse the structure ModeldefStructure mds = new ModeldefStructure(); if (mds.Parse(this)) { // Fetch Actor info if (haveplaceableactor) { ThingTypeInfo info = General.Map.Data.GetThingInfoEx(actorsbyclass[classname]); if (info != null) { // Already have a voxel model? if (General.Map.Data.ModeldefEntries.ContainsKey(info.Index) && General.Map.Data.ModeldefEntries[info.Index].IsVoxel) { LogWarning("Both voxel(s) and model(s) are defined for actor\"" + classname + "\". Consider using either former or latter"); } // Actor has a valid sprite? else if (!string.IsNullOrEmpty(info.Sprite) && !info.Sprite.ToLowerInvariant().StartsWith(DataManager.INTERNAL_PREFIX) && (info.Sprite.Length == 6 || info.Sprite.Length == 8)) { string targetsprite = info.Sprite.Substring(0, 5); if (mds.Frames.ContainsKey(targetsprite)) { // Create model data ModelData md = new ModelData { InheritActorPitch = mds.InheritActorPitch, UseActorPitch = mds.UseActorPitch, UseActorRoll = mds.UseActorRoll }; // Things are complicated in GZDoom... Matrix moffset = Matrix.Translation(mds.Offset.Y, -mds.Offset.X, mds.Offset.Z); Matrix mrotation = Matrix.RotationY(-Angle2D.DegToRad(mds.RollOffset)) * Matrix.RotationX(-Angle2D.DegToRad(mds.PitchOffset)) * Matrix.RotationZ(Angle2D.DegToRad(mds.AngleOffset)); md.SetTransform(mrotation, moffset, mds.Scale); // Add models int disabledframescount = 0; foreach (var fs in mds.Frames[targetsprite]) { // Sanity checks if (string.IsNullOrEmpty(mds.ModelNames[fs.ModelIndex])) { LogWarning("Model definition \"" + classname + "\", frame \"" + fs.SpriteName + " " + fs.FrameName + "\" references undefiend model index " + fs.ModelIndex); continue; } //INFO: setting frame index to a negative number disables model rendering in GZDoom if (fs.FrameIndex < 0) { disabledframescount++; continue; } // Texture name will be empty when skin path is embedded in the model string skinname = (!string.IsNullOrEmpty(mds.SkinNames[fs.ModelIndex]) ? mds.SkinNames[fs.ModelIndex].ToLowerInvariant() : string.Empty); md.SkinNames.Add(skinname); md.SurfaceSkinNames.Add(mds.SurfaceSkinNames[fs.ModelIndex]); md.ModelNames.Add(mds.ModelNames[fs.ModelIndex].ToLowerInvariant()); md.FrameNames.Add(fs.FrameName); md.FrameIndices.Add(fs.FrameIndex); } // More sanity checks... if (md.ModelNames.Count == 0) { // Show warning only when frames were not delibeartely disabled if (mds.Frames[targetsprite].Count > 0 && disabledframescount < mds.Frames[targetsprite].Count) { LogWarning("Model definition \"" + classname + "\" has no defined models"); } } else { // Add to collection entries[classname] = md; } } } } } } if (HasError) { LogError(); ClearError(); } } return(true); }
public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Continue until at the end of the stream bool skipdefinitions = false; while (SkipWhitespace(true)) { string token = ReadToken().ToLowerInvariant(); if (string.IsNullOrEmpty(token)) { continue; } if (skipdefinitions) { do { SkipWhitespace(true); token = ReadToken(); } while(!string.IsNullOrEmpty(token) && token != "endif"); skipdefinitions = false; continue; } switch (token) { case "ifheretic": skipdefinitions = (General.Map.Config.BaseGame != GameType.HERETIC); break; case "ifhexen": skipdefinitions = (General.Map.Config.BaseGame != GameType.HEXEN); break; case "ifstrife": skipdefinitions = (General.Map.Config.BaseGame != GameType.STRIFE); break; case "ifdoom": // TODO: is it even a thing?.. skipdefinitions = (General.Map.Config.BaseGame != GameType.DOOM); break; case "terrain": SkipWhitespace(true); token = ReadToken(); if (string.IsNullOrEmpty(token)) { ReportError("Expected terrain name"); return(false); } // Add to collection if (!terrainnames.Contains(token)) { terrainnames.Add(token); } break; case "{": // Skip inner properties do { SkipWhitespace(true); token = ReadToken(); } while(!string.IsNullOrEmpty(token) && token != "}"); break; } } return(true); }
public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // Keep local data Stream localstream = datastream; string localsourcename = sourcename; int localsourcelumpindex = sourcelumpindex; BinaryReader localreader = datareader; DataLocation locallocation = datalocation; string localtextresourcepath = textresourcepath; // Continue until at the end of the stream while (SkipWhitespace(true)) { string token = ReadToken().ToLowerInvariant(); if (string.IsNullOrEmpty(token)) { continue; } switch (token) { case GldefsLightType.POINT: case GldefsLightType.PULSE: case GldefsLightType.SECTOR: case GldefsLightType.FLICKER: case GldefsLightType.FLICKER2: if (!ParseLight(token)) { return(false); } break; case "object": if (!ParseObject()) { return(false); } break; case "glow": if (!ParseGlowingFlats()) { return(false); } break; case "skybox": if (!ParseSkybox()) { return(false); } break; case "#include": if (!ParseInclude(clearerrors)) { return(false); } // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; sourcename = localsourcename; sourcelumpindex = localsourcelumpindex; datalocation = locallocation; textresourcepath = localtextresourcepath; break; case "$gzdb_skip": return(!this.HasError); default: // Unknown structure! SkipStructure(); break; } } // All done return(!this.HasError); }