Exemple #1
0
        /// <summary>
        /// Reads the lump in the BSP file using the information in "<paramref name="info"/>".
        /// </summary>
        /// <param name="info">The <see cref="LumpInfo"/> object representing the lump's information.</param>
        /// <returns>A <c>byte</c> array containing the data from the file for the lump at the offset with the length from "<paramref name="info"/>".</returns>
        public byte[] ReadLump(LumpInfo info)
        {
            if (info.length == 0)
            {
                return(new byte[0]);
            }
            byte[] output;

            if (info.lumpFile != null)
            {
                using (FileStream fs = new FileStream(info.lumpFile.FullName, FileMode.Open, FileAccess.Read)) {
                    BinaryReader br = new BinaryReader(fs);
                    fs.Seek(info.offset, SeekOrigin.Begin);
                    output = br.ReadBytes(info.length);
                    br.Close();
                    return(output);
                }
            }

            using (FileStream stream = new FileStream(bspFile.FullName, FileMode.Open, FileAccess.Read)) {
                BinaryReader binaryReader = new BinaryReader(stream);
                stream.Seek(info.offset, SeekOrigin.Begin);
                output = binaryReader.ReadBytes(info.length);
                binaryReader.Close();
            }

            if (key.Length != 0)
            {
                output = XorWithKeyStartingAtIndex(output, info.offset);
            }

            return(output);
        }
        /// <summary>
        /// Factory method to parse a <c>byte</c> array into a <see cref="DisplacementVertices"/> object.
        /// </summary>
        /// <param name="data">The data 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>
        /// <returns>A <see cref="DisplacementVertices"/> object.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="data"/> parameter was <c>null</c>.</exception>
        public static DisplacementVertices LumpFactory(byte[] data, BSP bsp, LumpInfo lumpInfo)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }

            return(new DisplacementVertices(data, GetStructLength(bsp.version, lumpInfo.version), bsp, lumpInfo));
        }
Exemple #3
0
        /// <summary>
        /// Factory method for an <see cref="Entities"/> object from a <c>byte</c> array.
        /// </summary>
        /// <param name="data">The data to parse.</param>
        /// <param name="type">The map type.</param>
        /// <param name="version">The version of this lump.</param>
        /// <returns>An <see cref="Entities"/> object, which is a <c>List</c> of <see cref="Entity"/>s.</returns>
        public static Entities LumpFactory(byte[] data, BSP bsp, LumpInfo lumpInfo)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }

            return(new Entities(data, bsp, lumpInfo));
        }
Exemple #4
0
        /// <summary>
        /// Factory method to parse a <c>byte</c> array into a <see cref="Lump{Leaf}"/>.
        /// </summary>
        /// <param name="data">The data 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>
        /// <returns>A <see cref="Lump{Leaf}"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="data"/> parameter was <c>null</c>.</exception>
        public static Lump <Leaf> LumpFactory(byte[] data, BSP bsp, LumpInfo lumpInfo)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }

            return(new Lump <Leaf>(data, GetStructLength(bsp.version, lumpInfo.version), bsp, lumpInfo));
        }
Exemple #5
0
        /// <summary>
        /// Parses the passed <c>byte</c> array into a <see cref="StaticProps"/> 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 StaticProps(byte[] data, int structLength, BSP bsp, LumpInfo lumpInfo = default(LumpInfo)) : base(bsp, lumpInfo)
        {
            if (data == null || bsp == null)
            {
                throw new ArgumentNullException();
            }

            if (data.Length > 0)
            {
                int offset = 0;
                ModelDictionary = new string[BitConverter.ToInt32(data, 0)];
                offset         += 4;
                for (int i = 0; i < ModelDictionary.Length; ++i)
                {
                    ModelDictionary[i] = data.ToNullTerminatedString(offset, 128);
                    offset            += 128;
                }
                LeafIndices = new short[BitConverter.ToInt32(data, offset)];
                offset     += 4;
                for (int i = 0; i < LeafIndices.Length; ++i)
                {
                    LeafIndices[i] = BitConverter.ToInt16(data, offset);
                    offset        += 2;
                }
                if (Bsp.version == MapType.Vindictus && lumpInfo.version == 6)
                {
                    int numPropScales = BitConverter.ToInt32(data, offset);
                    offset += 4 + (numPropScales * 16);
                }
                int numProps = BitConverter.ToInt32(data, offset);
                if (lumpInfo.version == 12)                   // So far only Titanfall
                {
                    offset += 12;
                }
                else
                {
                    offset += 4;
                }
                if (numProps > 0)
                {
                    structLength = (data.Length - offset) / numProps;
                    for (int i = 0; i < numProps; ++i)
                    {
                        byte[] bytes = new byte[structLength];
                        Array.Copy(data, offset, bytes, 0, structLength);
                        Add(new StaticProp(bytes, this));
                        offset += structLength;
                    }
                }
            }
            else
            {
                ModelDictionary = new string[0];
            }
        }
Exemple #6
0
 /// <summary>
 /// Creates a new <see cref="NumList"/> object from a <c>byte</c> array.
 /// </summary>
 /// <param name="data"><c>byte</c> array to parse.</param>
 /// <param name="type">The type of number to store.</param>
 /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
 public NumList(byte[] data, DataType type, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo))
 {
     if (data == null)
     {
         throw new ArgumentNullException();
     }
     Bsp       = bsp;
     LumpInfo  = lumpInfo;
     this.data = data;
     this.type = type;
 }
Exemple #7
0
        /// <summary>
        /// Factory method to parse a <c>byte</c> array into a <see cref="Lump{Plane}"/>.
        /// </summary>
        /// <param name="data">The data 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>
        /// <returns>A <see cref="Lump{Plane}"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="data" /> was null.</exception>
        /// <remarks>This function goes here since it can't go into Unity's Plane class, and so can't depend
        /// on having a constructor taking a byte array.</remarks>
        public static Lump <Plane> LumpFactory(byte[] data, BSP bsp, LumpInfo lumpInfo)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }
            int          structLength = GetStructLength(bsp.version, lumpInfo.version);
            int          numObjects   = data.Length / structLength;
            Lump <Plane> lump         = new Lump <Plane>(numObjects, bsp, lumpInfo);

            for (int i = 0; i < numObjects; ++i)
            {
                Vector3 normal   = new Vector3(BitConverter.ToSingle(data, structLength * i), BitConverter.ToSingle(data, (structLength * i) + 4), BitConverter.ToSingle(data, (structLength * i) + 8));
                float   distance = BitConverter.ToSingle(data, (structLength * i) + 12);
                lump.Add(new Plane(normal, distance));
            }
            return(lump);
        }
Exemple #8
0
        /// <summary>
        /// Factory method to parse a <c>byte</c> array into a <see cref="Lump{Vertex}"/>.
        /// </summary>
        /// <param name="data">The data 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>
        /// <returns>A <see cref="Lump{Vertex}"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="data"/> was <c>null</c>.</exception>
        /// <remarks>This function goes here since it can't be in Unity's <c>UIVertex</c> class, and so I can't
        /// depend on having a constructor taking a byte array.</remarks>
        public static Lump <Vertex> LumpFactory(byte[] data, BSP bsp, LumpInfo lumpInfo)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }
            int           structLength = GetStructLength(bsp.version, lumpInfo.version);
            int           numObjects   = data.Length / structLength;
            Lump <Vertex> lump         = new Lump <Vertex>(numObjects, bsp, lumpInfo);

            byte[] bytes = new byte[structLength];
            for (int i = 0; i < numObjects; ++i)
            {
                Array.Copy(data, i * structLength, bytes, 0, structLength);
                lump.Add(CreateVertex(bytes, bsp.version, lumpInfo.version));
            }
            return(lump);
        }
Exemple #9
0
        /// <summary>
        /// Parses the passed <c>byte</c> array into a <c>Lump</c> of <typeparamref name="T"/> objects.
        /// </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 elements. Negative values indicate a variable length, which is not supported by this constructor.</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>
        /// <exception cref="ArgumentNullException"><paramref name="data" /> was <c>null</c>.</exception>
        /// <exception cref="NotSupportedException"><paramref name="structLength"/> is negative.</exception>
        public Lump(byte[] data, int structLength, BSP bsp = null, LumpInfo lumpInfo = default(LumpInfo)) : base(data.Length / structLength)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }
            if (structLength <= 0)
            {
                throw new NotSupportedException("Cannot use the base Lump constructor for variable length lumps (structLength was negative). Create a derived class with a new constructor instead.");
            }

            Bsp      = bsp;
            LumpInfo = lumpInfo;
            for (int i = 0; i < data.Length / structLength; ++i)
            {
                byte[] bytes = new byte[structLength];
                Array.Copy(data, (i * structLength), bytes, 0, structLength);
                Add((T)Activator.CreateInstance(typeof(T), new object[] { bytes, this }));
            }
        }
Exemple #10
0
        /// <summary>
        /// Loads any lump files associated with the BSP.
        /// </summary>
        private void LoadLumpFiles()
        {
            lumpFiles = new Dictionary <int, LumpInfo>();
            // Scan the BSP's directory for lump files
            DirectoryInfo   dir   = bspFile.Directory;
            List <FileInfo> files = dir.GetFiles(bspFile.Name.Substring(0, bspFile.Name.Length - 4) + "_?_*.lmp").ToList();

            // Sort the list by the number on the file
            files.Sort((f1, f2) => {
                int startIndex = bspFile.Name.Length - 1;
                int f1EndIndex = f1.Name.LastIndexOf('.');
                int f2EndIndex = f2.Name.LastIndexOf('.');
                int f1Position = int.Parse(f1.Name.Substring(startIndex, f1EndIndex - startIndex));
                int f2Position = int.Parse(f2.Name.Substring(startIndex, f2EndIndex - startIndex));
                return(f1Position - f2Position);
            });
            // Read the files in order. The last file in the list for a specific lump will replace that lump.
            foreach (FileInfo file in files)
            {
                using (FileStream fs = new FileStream(file.FullName, FileMode.Open, FileAccess.Read)) {
                    BinaryReader br = new BinaryReader(fs);
                    fs.Seek(0, SeekOrigin.Begin);
                    byte[] input     = br.ReadBytes(20);
                    int    offset    = BitConverter.ToInt32(input, 0);
                    int    lumpIndex = BitConverter.ToInt32(input, 4);
                    int    version   = BitConverter.ToInt32(input, 8);
                    int    length    = BitConverter.ToInt32(input, 12);
                    lumpFiles[lumpIndex] = new LumpInfo()
                    {
                        offset   = offset,
                        version  = version,
                        length   = length,
                        lumpFile = file
                    };
                    br.Close();
                }
            }
        }
Exemple #11
0
 /// <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)
 {
 }
Exemple #12
0
 /// <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)
 {
 }
Exemple #13
0
 /// <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>
 /// 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>
 /// 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)
 {
 }
Exemple #16
0
        /// <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));
            }
        }
Exemple #17
0
 /// <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;
 }
Exemple #18
0
 /// <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)
 {
 }
Exemple #19
0
 /// <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)
 {
 }
Exemple #20
0
 /// <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)
 {
 }
Exemple #21
0
 /// <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)
 {
 }
Exemple #22
0
 /// <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[] { };
 }
Exemple #23
0
        /// <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));
            }
        }
Exemple #24
0
 /// <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="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)
 {
 }
Exemple #26
0
 /// <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)
 {
 }
Exemple #28
0
 /// <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[] { };
 }
Exemple #29
0
        /// <summary>
        /// Tries to get the <see cref="MapType"/> member most closely represented by the referenced file.
        /// </summary>
        /// <param name="bigEndian">Set to <c>true</c> to attempt reading the data in big-endian byte order.</param>
        /// <returns>The <see cref="MapType"/> of this BSP, <see cref="MapType.Undefined"/> if it could not be determined.</returns>
        private MapType GetVersion(bool bigEndian)
        {
            MapType current = MapType.Undefined;

            using (FileStream stream = new FileStream(bspFile.FullName, FileMode.Open, FileAccess.Read)) {
                if (stream.Length < 4)
                {
                    return(current);
                }
                BinaryReader binaryReader = new BinaryReader(stream);
                stream.Seek(0, SeekOrigin.Begin);
                int num = binaryReader.ReadInt32();
                if (bigEndian)
                {
                    byte[] bytes = BitConverter.GetBytes(num);
                    Array.Reverse(bytes);
                    num = BitConverter.ToInt32(bytes, 0);
                }
                switch (num)
                {
                case 1347633737: {
                    // 1347633737 reads in ASCII as "IBSP"
                    // Versions: CoD, CoD2, CoD4, Quake 2, Daikatana, Quake 3 (RtCW), Soldier of Fortune
                    int num2 = binaryReader.ReadInt32();
                    if (bigEndian)
                    {
                        byte[] bytes = BitConverter.GetBytes(num2);
                        Array.Reverse(bytes);
                        num2 = BitConverter.ToInt32(bytes, 0);
                    }
                    switch (num2)
                    {
                    case 4: {
                        current = MapType.CoD2;
                        break;
                    }

                    case 22: {
                        current = MapType.CoD4;
                        break;
                    }

                    case 38: {
                        current = MapType.Quake2;
                        break;
                    }

                    case 41: {
                        current = MapType.Daikatana;
                        break;
                    }

                    case 46: {
                        current = MapType.Quake3;
                        // This version number is both Quake 3 and Soldier of Fortune. Find out the length of the
                        // header, based on offsets.
                        for (int i = 0; i < 17; i++)
                        {
                            stream.Seek((i + 1) * 8, SeekOrigin.Begin);
                            int temp = binaryReader.ReadInt32();
                            if (bigEndian)
                            {
                                byte[] bytes = BitConverter.GetBytes(temp);
                                Array.Reverse(bytes);
                                temp = BitConverter.ToInt32(bytes, 0);
                            }
                            if (temp == 184)
                            {
                                current = MapType.SoF;
                                break;
                            }
                            else
                            {
                                if (temp == 144)
                                {
                                    break;
                                }
                            }
                        }
                        break;
                    }

                    case 47: {
                        current = MapType.Quake3;
                        break;
                    }

                    case 59: {
                        current = MapType.CoD;
                        break;
                    }
                    }
                    break;
                }

                case 892416050:
                case 1095516485: {
                    // 892416050 reads in ASCII as "2015," the game studio which developed MoHAA
                    // 1095516485 reads in ASCII as "EALA," the ones who developed MoHAA Spearhead and Breakthrough
                    current = MapType.MOHAA;
                    break;
                }

                case 1347633750: {
                    // 1347633750 reads in ASCII as "VBSP." Indicates Source engine.
                    // Some source games handle this as 2 shorts.
                    // TODO: Big endian?
                    // Formats: Source 17-23 and 27, DMoMaM, Vindictus
                    int num2 = (int)binaryReader.ReadUInt16();
                    switch (num2)
                    {
                    case 17: {
                        current = MapType.Source17;
                        break;
                    }

                    case 18: {
                        current = MapType.Source18;
                        break;
                    }

                    case 19: {
                        current = MapType.Source19;
                        break;
                    }

                    case 20: {
                        int version2 = (int)binaryReader.ReadUInt16();
                        if (version2 == 4)
                        {
                            // TODO: This doesn't necessarily mean the whole map should be read as DMoMaM.
                            current = MapType.DMoMaM;
                        }
                        else
                        {
                            current = MapType.Source20;
                            // Hack for detecting Vindictus: Look in the GameLump for offset/length/flags outside of ranges we'd expect
                            LumpInfo gameLumpInfo = GetLumpInfo(35, MapType.Source20);
                            stream.Seek(gameLumpInfo.offset, SeekOrigin.Begin);
                            int numGameLumps = binaryReader.ReadInt32();
                            if (numGameLumps > 0)
                            {
                                // Normally this would be the offset and length for the first game lump.
                                // But in Vindictus it's the version indicator for it instead.
                                stream.Seek(gameLumpInfo.offset + 12, SeekOrigin.Begin);
                                int testOffset = binaryReader.ReadInt32();
                                if (numGameLumps > 1)
                                {
                                    if (testOffset < 24)
                                    {
                                        current = MapType.Vindictus;
                                        break;
                                    }
                                }
                                else
                                {
                                    // Normally this would be the ident for the second game lump.
                                    // But in Vindictus it's the length for the first instead.
                                    stream.Seek(gameLumpInfo.offset + 20, SeekOrigin.Begin);
                                    int testName = binaryReader.ReadInt32();
                                    // A lump ident tends to have a value far above 1090519040, longer than any GameLump should be.
                                    if (testOffset < 24 && testName < gameLumpInfo.length)
                                    {
                                        current = MapType.Vindictus;
                                        break;
                                    }
                                }
                            }
                        }
                        break;
                    }

                    case 21: {
                        current = MapType.Source21;
                        // Hack to determine if this is a L4D2 map. Read what would normally be the offset of
                        // a lump. If it is less than the header length it's probably not an offset, indicating L4D2.
                        stream.Seek(8, SeekOrigin.Begin);
                        int test = binaryReader.ReadInt32();
                        if (bigEndian)
                        {
                            byte[] bytes = BitConverter.GetBytes(test);
                            Array.Reverse(bytes);
                            test = BitConverter.ToInt32(bytes, 0);
                        }
                        if (test < 1032)
                        {
                            current = MapType.L4D2;
                        }
                        break;
                    }

                    case 22: {
                        current = MapType.Source22;
                        break;
                    }

                    case 23: {
                        current = MapType.Source23;
                        break;
                    }

                    case 27: {
                        current = MapType.Source27;
                        break;
                    }
                    }
                    break;
                }

                case 1347633746: {
                    // Reads in ASCII as "RBSP". Raven software's modification of Q3BSP, or Ritual's modification of Q2.
                    // Formats: Raven, SiN
                    current = MapType.Raven;
                    for (int i = 0; i < 17; i++)
                    {
                        // Find out where the first lump starts, based on offsets.
                        stream.Seek((i + 1) * 8, SeekOrigin.Begin);
                        int temp = binaryReader.ReadInt32();
                        if (bigEndian)
                        {
                            byte[] bytes = BitConverter.GetBytes(temp);
                            Array.Reverse(bytes);
                            temp = BitConverter.ToInt32(bytes, 0);
                        }
                        if (temp == 168)
                        {
                            current = MapType.SiN;
                            break;
                        }
                        else
                        {
                            if (temp == 152)
                            {
                                break;
                            }
                        }
                    }
                    break;
                }

                case 556942917: {
                    // "EF2!"
                    current = MapType.STEF2;
                    break;
                }

                case 1347633778: {
                    // "rBSP". Respawn's format for Titanfall
                    current = MapType.Titanfall;
                    break;
                }

                case 1263223110: {
                    // "FAKK"
                    // Formats: STEF2 demo, Heavy Metal FAKK2 (American McGee's Alice)
                    int num2 = binaryReader.ReadInt32();
                    if (bigEndian)
                    {
                        byte[] bytes = BitConverter.GetBytes(num2);
                        Array.Reverse(bytes);
                        num2 = BitConverter.ToInt32(bytes, 0);
                    }
                    switch (num2)
                    {
                    case 19: {
                        current = MapType.STEF2Demo;
                        break;
                    }

                    case 12:
                    case 42: {                                    // American McGee's Alice
                        current = MapType.FAKK;
                        break;
                    }
                    }
                    break;
                }

                // Various numbers not representing a string
                // Formats: HL1, Quake, Nightfire, or perhaps Tactical Intervention's encrypted format
                case 29:
                case 30: {
                    current = MapType.Quake;
                    break;
                }

                case 42: {
                    current = MapType.Nightfire;
                    break;
                }

                default: {
                    // Hack to get Tactical Intervention's encryption key. At offset 384, there are two unused lumps whose
                    // values in the header are always 0s. Grab these 32 bytes (256 bits) and see if they match an expected value.
                    stream.Seek(384, SeekOrigin.Begin);
                    key = binaryReader.ReadBytes(32);
                    stream.Seek(0, SeekOrigin.Begin);
                    int num2 = BitConverter.ToInt32(XorWithKeyStartingAtIndex(binaryReader.ReadBytes(4)), 0);
                    if (num2 == 1347633750)
                    {
                        current = MapType.TacticalInterventionEncrypted;
                    }
                    else
                    {
                        current = MapType.Undefined;
                    }
                    break;
                }
                }
                binaryReader.Close();
            }
            return(current);
        }
Exemple #30
0
 /// <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;
 }