/// <summary> /// Initializes a new <see cref="Entities"/> object. /// </summary> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public Entities(BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(bsp, lumpInfo) { }
/// <summary> /// Initializes a new <see cref="Entities"/> object, and parses the passed <c>byte</c> array as a <c>string</c>. /// </summary> /// <param name="data"><c>Byte</c>s read from a file.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public Entities(byte[] data, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(bsp, lumpInfo) { // Keep track of whether or not we're currently in a set of quotation marks. // I came across a map where the map maker used { and } within a value. bool inQuotes = false; int braceCount = 0; // The current character being read in the file. This is necessary because // we need to know exactly when the { and } characters occur and capture // all text between them. char currentChar; // This will be the resulting entity, fed into Entity.FromString StringBuilder current = new StringBuilder(); for (int offset = 0; offset < data.Length; ++offset) { currentChar = (char)data[offset]; if (currentChar == '\"') { if (offset == 0) { inQuotes = !inQuotes; } else if ((char)data[offset - 1] != '\\') { // Allow for escape-sequenced quotes to not affect the state machine, but only if the quote isn't at the end of a line. // Some Source engine entities use escape sequence quotes in values, but MoHAA has a map with an obvious erroneous backslash before a quote at the end of a line. if (inQuotes && (offset + 1 >= data.Length || (char)data[offset + 1] == '\n' || (char)data[offset + 1] == '\r')) { inQuotes = false; } } else { inQuotes = !inQuotes; } } if (!inQuotes) { if (currentChar == '{') { // Occasionally, texture paths have been known to contain { or }. Since these aren't always contained // in quotes, we must be a little more precise about how we want to select our delimiters. // As a general rule, though, making sure we're not in quotes is still very effective at error prevention. if (offset == 0 || (char)data[offset - 1] == '\n' || (char)data[offset - 1] == '\t' || (char)data[offset - 1] == ' ' || (char)data[offset - 1] == '\r') { ++braceCount; } } } if (braceCount > 0) { current.Append(currentChar); } if (!inQuotes) { if (currentChar == '}') { if (offset == 0 || (char)data[offset - 1] == '\n' || (char)data[offset - 1] == '\t' || (char)data[offset - 1] == ' ' || (char)data[offset - 1] == '\r') { --braceCount; if (braceCount == 0) { Entity entity = new Entity(this); entity.ParseString(current.ToString()); Add(entity); // Reset StringBuilder current.Length = 0; } } } } } if (braceCount != 0) { throw new ArgumentException(string.Format("Brace mismatch when parsing entities! Entity: {0} Brace level: {1}", Count, braceCount)); } }
/// <summary> /// Initializes a new instance of an <see cref="Entities"/> object copying a passed <c>IEnumerable</c> of <see cref="Entity"/> objects. /// </summary> /// <param name="data">Collection of <see cref="Entity"/> objects to copy.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public Entities(IEnumerable <Entity> entities, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(entities, bsp, lumpInfo) { }
/// <summary> /// Initializes a new instance of an <see cref="Entities"/> object with a specified initial capacity. /// </summary> /// <param name="initialCapacity">Initial capacity of the <c>List</c> of <see cref="Entity"/> objects.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public Entities(int initialCapacity, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(initialCapacity, bsp, lumpInfo) { }
/// <summary> /// Creates an empty <see cref="Textures"/> object with the specified initial capactiy. /// </summary> /// <param name="capacity">The number of elements that can initially be stored.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public Textures(int capacity, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(capacity, bsp, lumpInfo) { }
/// <summary> /// Creates a new <see cref="StaticProps"/> that contains elements copied from the passed <see cref="IEnumerable{StaticProp}"/> and the passed <paramref name="dictionary"/>. /// </summary> /// <param name="items">The elements to copy into this <c>Lump</c>.</param> /// <param name="dictionary">A dictionary of static prop models. This is referenced from <see cref="StaticProp"/> objects.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public StaticProps(IEnumerable <StaticProp> items, IList <string> dictionary, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(items, bsp, lumpInfo) { this.ModelDictionary = dictionary.ToArray(); }
/// <summary> /// Creates an empty <see cref="DisplacementVertices"/> object. /// </summary> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public DisplacementVertices(BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(bsp, lumpInfo) { }
/// <summary> /// Creates an empty <see cref="Textures"/> object. /// </summary> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public Textures(BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(bsp, lumpInfo) { }
/// <summary> /// Creates an empty <c>Lump</c> of <typeparamref name="T"/> objects with the specified initial capactiy. /// </summary> /// <param name="capacity">The number of elements that can initially be stored.</param> /// <param name="bsp">The <see cref="BSP"/> which <paramref name="data"/> came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> object for this <c>Lump</c>.</param> public Lump(int capacity, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(capacity) { Bsp = bsp; LumpInfo = lumpInfo; }
/// <summary> /// Parses the passed <c>byte</c> array into a <see cref="GameLump"/> object. /// </summary> /// <param name="data">Array of <c>byte</c>s to parse.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception> /// <exception cref="ArgumentException">This structure is not implemented for the given maptype.</exception> public GameLump(byte[] data, BSP bsp, LumpInfo lumpInfo = default(LumpInfo)) { if (data == null) { throw new ArgumentNullException(); } int structLength = 0; switch (bsp.version) { case MapType.TacticalInterventionEncrypted: case MapType.Source17: case MapType.Source18: case MapType.Source19: case MapType.Source20: case MapType.Source21: case MapType.Source22: case MapType.Source23: case MapType.L4D2: case MapType.Source27: case MapType.Titanfall: { structLength = 16; break; } case MapType.Vindictus: case MapType.DMoMaM: { structLength = 20; break; } default: { throw new ArgumentException("Game lump does not exist in map type " + bsp.version + " or has not been implemented."); } } int numGameLumps = BitConverter.ToInt32(data, 0); if (numGameLumps > 0) { int lumpDictionaryOffset = (bsp.version == MapType.DMoMaM) ? 8 : 4; int lowestLumpOffset = int.MaxValue; for (int i = 0; i < numGameLumps; ++i) { int lumpIdent = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset); int lumpFlags; int lumpVersion; int lumpOffset; int lumpLength; if (bsp.version == MapType.Vindictus) { lumpFlags = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 4); lumpVersion = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 8); lumpOffset = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 12); lumpLength = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 16); } else { lumpFlags = BitConverter.ToUInt16(data, (i * structLength) + lumpDictionaryOffset + 4); lumpVersion = BitConverter.ToUInt16(data, (i * structLength) + lumpDictionaryOffset + 6); lumpOffset = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 8); lumpLength = BitConverter.ToInt32(data, (i * structLength) + lumpDictionaryOffset + 12); } LumpInfo info = new LumpInfo { ident = lumpIdent, flags = lumpFlags, version = lumpVersion, offset = lumpOffset, length = lumpLength, }; this[(GameLumpType)info.ident] = info; if (info.offset < lowestLumpOffset) { lowestLumpOffset = info.offset; } } } }
/// <summary> /// Creates a new <c>Lump</c> that contains elements copied from the passed <see cref="IEnumerable{T}"/>. /// </summary> /// <param name="items">The elements to copy into this <c>Lump</c>.</param> /// <param name="bsp">The <see cref="BSP"/> which <paramref name="data"/> came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> object for this <c>Lump</c>.</param> public Lump(IEnumerable <T> items, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(items) { Bsp = bsp; LumpInfo = lumpInfo; }
/// <summary> /// Creates an empty <c>Lump</c> of <typeparamref name="T"/> objects. /// </summary> /// <param name="bsp">The <see cref="BSP"/> which <paramref name="data"/> came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> object for this <c>Lump</c>.</param> public Lump(BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) { Bsp = bsp; LumpInfo = lumpInfo; }
/// <summary> /// Creates an empty <see cref="StaticProps"/> object with the specified initial capactiy. /// </summary> /// <param name="capacity">The number of elements that can initially be stored.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public StaticProps(int capacity, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(capacity, bsp, lumpInfo) { ModelDictionary = new string[] { }; }
/// <summary> /// Gets the information for lump "<paramref name="index"/>" for this BSP file when reading it as "<paramref name="version"/>". /// </summary> /// <param name="index">The numerical index of this lump.</param> /// <param name="version">The type of BSP to interpret the file as.</param> /// <returns>A <see cref="LumpInfo"/> object containing information about the lump.</returns> /// <exception cref="IndexOutOfRangeException">"<paramref name="index"/>" is less than zero, or greater than the number of lumps allowed by "<paramref name="version"/>".</exception> public LumpInfo GetLumpInfo(int index, MapType version) { if (index < 0 || index >= BSP.GetNumLumps(version)) { throw new IndexOutOfRangeException(); } switch (version) { case MapType.Quake: case MapType.Nightfire: { return(GetLumpInfoAtOffset(4 + (8 * index), version)); } case MapType.Quake2: case MapType.Daikatana: case MapType.SiN: case MapType.SoF: case MapType.Quake3: case MapType.Raven: case MapType.CoD: case MapType.CoD2: { return(GetLumpInfoAtOffset(8 + (8 * index), version)); } case MapType.STEF2: case MapType.STEF2Demo: case MapType.MOHAA: case MapType.FAKK: { return(GetLumpInfoAtOffset(12 + (8 * index), version)); } case MapType.Source17: case MapType.Source18: case MapType.Source19: case MapType.Source20: case MapType.Source21: case MapType.Source22: case MapType.Source23: case MapType.Source27: case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: case MapType.DMoMaM: { if (lumpFiles == null) { LoadLumpFiles(); } if (lumpFiles.ContainsKey(index)) { return(lumpFiles[index]); } return(GetLumpInfoAtOffset(8 + (16 * index), version)); } case MapType.Titanfall: { if (lumpFiles == null) { LoadLumpFiles(); } if (lumpFiles.ContainsKey(index)) { return(lumpFiles[index]); } return(GetLumpInfoAtOffset((16 * (index + 1)), version)); } case MapType.CoD4: { using (FileStream stream = new FileStream(bspFile.FullName, FileMode.Open, FileAccess.Read)) { BinaryReader binaryReader = new BinaryReader(stream); stream.Seek(8, SeekOrigin.Begin); int numlumps = binaryReader.ReadInt32(); int offset = (numlumps * 8) + 12; for (int i = 0; i < numlumps; i++) { int id = binaryReader.ReadInt32(); int length = binaryReader.ReadInt32(); if (id == index) { return(new LumpInfo() { offset = offset, length = length }); } else { offset += length; while (offset % 4 != 0) { offset++; } } } binaryReader.Close(); } return(default(LumpInfo)); } default: { return(default(LumpInfo)); } } }
/// <summary> /// Creates a new <see cref="DisplacementVertices"/> that contains elements copied from the passed <see cref="IEnumerable{DisplacementVertex}"/>. /// </summary> /// <param name="items">The elements to copy into this <c>Lump</c>.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public DisplacementVertices(IEnumerable <DisplacementVertex> items, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(items, bsp, lumpInfo) { }
/// <summary> /// Creates a new <see cref="NumList"/> object from a <c>byte</c> array and returns it. /// </summary> /// <param name="data"><c>byte</c> array to parse.</param> /// <param name="type">The type of number to store.</param> /// <returns>The resulting <see cref="NumList"/>.</returns> public static NumList LumpFactory(byte[] data, DataType type, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) { return(new NumList(data, type, bsp, lumpInfo)); }
/// <summary> /// Creates an empty <see cref="DisplacementVertices"/> object with the specified initial capactiy. /// </summary> /// <param name="capacity">The number of elements that can initially be stored.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public DisplacementVertices(int capacity, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(capacity, bsp, lumpInfo) { }
/// <summary> /// Creates a new <see cref="Textures"/> that contains elements copied from the passed <see cref="IEnumerable{Texture}"/>. /// </summary> /// <param name="items">The elements to copy into this <c>Lump</c>.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public Textures(IEnumerable <Texture> items, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(items, bsp, lumpInfo) { }
/// <summary> /// Parses the passed <c>byte</c> array into a <see cref="DisplacementVertices"/> object. /// </summary> /// <param name="data">Array of <c>byte</c>s to parse.</param> /// <param name="structLength">Number of <c>byte</c>s to copy into the children.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception> public DisplacementVertices(byte[] data, int structLength, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(data, structLength, bsp, lumpInfo) { }
/// <summary> /// Parses the passed <c>byte</c> array into a <see cref="Textures"/> object. /// </summary> /// <param name="data">Array of <c>byte</c>s to parse.</param> /// <param name="structLength">Number of <c>byte</c>s to copy into the children. Will be recalculated based on BSP format.</param> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> /// <exception cref="ArgumentNullException"><paramref name="data"/> or <paramref name="bsp"/> was <c>null</c>.</exception> public Textures(byte[] data, int structLength, BSP bsp, LumpInfo lumpInfo = default(LumpInfo)) : base(bsp, lumpInfo) { if (data == null || bsp == null) { throw new ArgumentNullException(); } switch (bsp.version) { case MapType.Nightfire: { structLength = 64; break; } case MapType.Quake3: case MapType.Raven: case MapType.CoD: case MapType.CoD2: case MapType.CoD4: { structLength = 72; break; } case MapType.Quake2: case MapType.Daikatana: case MapType.SoF: case MapType.STEF2: case MapType.STEF2Demo: case MapType.FAKK: { structLength = 76; break; } case MapType.MOHAA: { structLength = 140; break; } case MapType.SiN: { structLength = 180; break; } case MapType.Source17: case MapType.Source18: case MapType.Source19: case MapType.Source20: case MapType.Source21: case MapType.Source22: case MapType.Source23: case MapType.Source27: case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: case MapType.DMoMaM: { int offset = 0; for (int i = 0; i < data.Length; ++i) { if (data[i] == (byte)0x00) { // They are null-terminated strings, of non-constant length (not padded) byte[] myBytes = new byte[i - offset]; Array.Copy(data, offset, myBytes, 0, i - offset); Add(new Texture(myBytes, this)); offset = i + 1; } } return; } case MapType.Quake: { int numElements = BitConverter.ToInt32(data, 0); structLength = 40; for (int i = 0; i < numElements; ++i) { byte[] myBytes = new byte[structLength]; Array.Copy(data, BitConverter.ToInt32(data, (i + 1) * 4), myBytes, 0, structLength); Add(new Texture(myBytes, this)); } return; } default: { throw new ArgumentException("Lump object Texture does not exist in map type " + bsp.version + " or has not been implemented."); } } int numObjects = data.Length / structLength; for (int i = 0; i < numObjects; ++i) { byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); Add(new Texture(bytes, this)); } }
/// <summary> /// Creates an empty <see cref="StaticProps"/> object. /// </summary> /// <param name="bsp">The <see cref="BSP"/> this lump came from.</param> /// <param name="lumpInfo">The <see cref="LumpInfo"/> associated with this lump.</param> public StaticProps(BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(bsp, lumpInfo) { dictionary = new string[] { }; }