Example #1
0
        private static void ReadSidesOrThrow(MapData map, MapComponents components)
        {
            ByteReader reader = ByteReader.From(ByteOrder.Little, components.Sidedefs.Value.Data);

            int count = reader.Length / BytesPerSide;

            for (int index = 0; index < count; index++)
            {
                MapSidedef sidedef = new MapSidedef(index)
                {
                    Offset        = new Vec2I(reader.Short(), reader.Short()),
                    UpperTexture  = reader.StringWithoutNulls(8),
                    LowerTexture  = reader.StringWithoutNulls(8),
                    MiddleTexture = reader.StringWithoutNulls(8),
                    SectorID      = reader.UShort()
                };

                if (!sidedef.SectorID.InRangeExclusive(0, map.Sectors.Count))
                {
                    throw new Exception($"Sidedef {index} has out of range sector: {sidedef.SectorID}");
                }

                map.Sidedefs.Add(sidedef);
            }
        }
Example #2
0
        private static void ReadVerticesOrThrow(MapData map, MapComponents components)
        {
            ByteReader reader = ByteReader.From(ByteOrder.Little, components.Vertices.Value.Data);

            int count = reader.Length / BytesPerVertex;

            for (int index = 0; index < count; index++)
            {
                MapVertex vertex = new MapVertex(index, reader.Short(), reader.Short());
                map.Vertices.Add(vertex);
            }
        }
Example #3
0
        private static void ReadLinesOrThrow(MapData map, MapComponents components)
        {
            ByteReader reader = ByteReader.From(ByteOrder.Little, components.Linedefs.Value.Data);

            int count = reader.Length / BytesPerLine;

            for (int index = 0; index < count; index++)
            {
                MapLinedef linedef = new MapLinedef(index)
                {
                    StartVertex = reader.UShort(),
                    EndVertex   = reader.UShort()
                };

                ushort flags     = reader.UShort();
                ushort type      = reader.UShort();
                ushort sectorTag = reader.UShort();
                SetLineSpecial(linedef, flags, type, sectorTag);

                linedef.FrontSide = reader.UShort();
                int backID = reader.UShort();
                if (backID != NoSidedef)
                {
                    linedef.BackSide = backID;
                }

                if (!linedef.StartVertex.InRangeExclusive(0, map.Vertices.Count))
                {
                    throw new Exception($"Line {index} has out of range start vertex: {linedef.StartVertex}");
                }
                if (!linedef.EndVertex.InRangeExclusive(0, map.Vertices.Count))
                {
                    throw new Exception($"Line {index} has out of range start vertex: {linedef.EndVertex}");
                }
                if (!linedef.FrontSide.InRangeExclusive(0, map.Sidedefs.Count))
                {
                    throw new Exception($"Line {index} has out of range front sidedef: {linedef.FrontSide}");
                }
                if (linedef.BackSide != null && !linedef.FrontSide.InRangeExclusive(0, map.Sidedefs.Count))
                {
                    throw new Exception($"Line {index} has out of range back sidedef: {linedef.BackSide.Value}");
                }

                map.Linedefs.Add(linedef);
            }
        }
Example #4
0
        /// <summary>
        /// Reads a TEXTURE1/2 entry.
        /// </summary>
        /// <param name="number">The index of the X in the textureX. Should be
        /// either 1 or 2.</param>
        /// <param name="data">The data to read.</param>
        /// <returns>All the processed texture definitions, or null if the
        /// entry is corrupt.</returns>
        public static Optional <TextureX> From(int number, byte[] data)
        {
            Debug.Assert(number == 1 || number == 2, "TEXTUREx should have only 1 or 2 as a number");

            try
            {
                ByteReader reader = ByteReader.From(ByteOrder.Little, data);
                Dictionary <UpperString, TextureXImage> images = new Dictionary <UpperString, TextureXImage>();

                int numTextures = reader.Int();

                List <int> offsets = new List <int>();
                for (int i = 0; i < numTextures; i++)
                {
                    offsets.Add(reader.Int());
                }

                foreach (int offset in offsets)
                {
                    reader.Offset = offset;
                    Optional <TextureXImage> imageOptional = TextureXImage.From(reader);
                    if (!imageOptional)
                    {
                        return(Empty);
                    }

                    // Unfortunately we have to follow the specification as per
                    // the ZDoom wiki:  "...however if a texture is defined
                    // more than once in the same TEXTUREx lump, the later
                    // definitions are skipped".
                    TextureXImage image = imageOptional.Value;
                    if (!images.ContainsKey(image.Name))
                    {
                        images[image.Name] = image;
                    }
                }

                return(new TextureX(number, images.Values));
            }
            catch
            {
                return(Empty);
            }
        }
Example #5
0
        private static void ReadThingsOrThrow(MapData map, MapComponents components)
        {
            ByteReader reader = ByteReader.From(ByteOrder.Little, components.Things.Value.Data);

            int count = reader.Length / BytesPerThing;

            for (int index = 0; index < count; index++)
            {
                MapThing thing = new MapThing(index);
                float    x     = new Fixed(reader.Short(), 0).Float();
                float    y     = new Fixed(reader.Short(), 0).Float();
                thing.Position     = (x, y);
                thing.AngleDegrees = reader.UShort();
                thing.EditorID     = reader.UShort();
                SetThingFlags(thing, reader.UShort());

                map.Things.Add(thing);
            }
        }
Example #6
0
        /// <summary>
        /// Reads a pnames data from the entry provided.
        /// </summary>
        /// <param name="data">The data to read from.</param>
        /// <returns>A read pnames object from the entry, or an empty value if
        /// the entry has the wrong data amount or is corrupt.</returns>
        public static Optional <PNames> From(byte[] data)
        {
            try
            {
                ByteReader reader     = ByteReader.From(ByteOrder.Little, data);
                int        numStrings = reader.Int();

                List <UpperString> pnames = new List <UpperString>(numStrings);
                for (int i = 0; i < numStrings; i++)
                {
                    pnames.Add(reader.StringWithoutNulls(8));
                }

                return(new PNames(pnames));
            }
            catch
            {
                return(Empty);
            }
        }
Example #7
0
        private static void ReadSectorsOrThrow(MapData map, MapComponents components)
        {
            ByteReader reader = ByteReader.From(ByteOrder.Little, components.Sectors.Value.Data);

            int count = reader.Length / BytesPerSector;

            for (int index = 0; index < count; index++)
            {
                MapSector sector = new MapSector(index)
                {
                    FloorHeight    = reader.Short(),
                    CeilingHeight  = reader.Short(),
                    FloorTexture   = reader.StringWithoutNulls(8).ToUpper(),
                    CeilingTexture = reader.StringWithoutNulls(8).ToUpper(),
                    LightLevel     = reader.Short()
                };
                SetSpecialBits(sector, reader.UShort());
                sector.Tag = reader.UShort();

                map.Sectors.Add(sector);
            }
        }
Example #8
0
        private static List <WadEntry> ReadEntriesOrThrow(byte[] wadData)
        {
            ByteReader reader = ByteReader.From(ByteOrder.Little, wadData);

            // The header must exist fully or else we can't read it.
            if (!reader.HasRemaining(12))
            {
                throw new Exception("Cannot read Wad");
            }

            WadResourceNamespaceTracker namespaceTracker = new WadResourceNamespaceTracker();
            List <WadEntry>             entries          = new List <WadEntry>();

            WadHeader header = ReadHeader(reader);

            reader.Offset = header.DirectoryTableOffset;

            for (int i = 0; i < header.EntryCount; i++)
            {
                WadDirectoryEntry dirEntry = ReadWadEntry(reader);

                ResourceNamespace resourceNamespace = namespaceTracker.Update(dirEntry);
                byte[]            data = reader.Bytes(dirEntry.Size, dirEntry.Offset);

                // Unfortunately binary readers can silently fail and just
                // consume the remaining data. We want to let the caller
                // know the wad is in fact corrupt if it can't read enough.
                if (data.Length != dirEntry.Size)
                {
                    throw new Exception("Malformed wad entry length");
                }

                WadEntry entry = new WadEntry(dirEntry.Name, resourceNamespace, data);
                entries.Add(entry);
            }

            return(entries);
        }
Example #9
0
        /// <summary>
        /// Creates a palette image from a column-based image.
        /// </summary>
        /// <param name="data">The data for the image.</param>
        /// <param name="resourceNamespace">The namespace this image belongs
        /// to.</param>
        /// <returns>The palette image for the data, or null if the image is
        /// corrupt and would not make a valid column image.</returns>
        public static Optional <PaletteImage> FromColumn(byte[] data, ResourceNamespace resourceNamespace)
        {
            // OPTIMIZE: Short circuit if it's likely not a column image.
            // OPTIMIZE: Write posts horizontally, then rotate for speed?
            try
            {
                ByteReader reader = ByteReader.From(ByteOrder.Little, data);
                int        width  = reader.Short();
                int        height = reader.Short();
                Vec2I      offset = (reader.Short(), reader.Short());

                if (offset.X < 0 || offset.Y < 0)
                {
                    return(Empty);
                }

                int[] offsets = new int[width];
                for (int i = 0; i < width; i++)
                {
                    offsets[i] = reader.Int();
                }

                short[] indices = Arrays.Create(width * height, TransparentIndex);

                for (int col = 0; col < width; col++)
                {
                    reader.Offset = offsets[col];

                    while (true)
                    {
                        int rowStart = reader.Byte();
                        if (rowStart == 0xFF)
                        {
                            break;
                        }

                        int indicesCount = reader.Byte();
                        reader.Skip(1);
                        byte[] paletteIndices = reader.Bytes(indicesCount);
                        reader.Skip(1);

                        int indicesOffset = (rowStart * width) + col;
                        for (int i = 0; i < paletteIndices.Length; i++)
                        {
                            indices[indicesOffset] = paletteIndices[i];
                            indicesOffset         += width;
                        }
                    }
                }

                return(new PaletteImage(width, height, indices)
                {
                    Namespace = resourceNamespace,
                    Offset = offset
                });
            }
            catch
            {
                return(Empty);
            }
        }