Ejemplo n.º 1
0
        Graphic ReadPalette(GraphicReader graphicReader, IDataReader reader)
        {
            reader.Position = 0;
            var paletteGraphic = new Graphic();

            graphicReader.ReadGraphic(paletteGraphic, reader, paletteGraphicInfo);
            return(paletteGraphic);
        }
Ejemplo n.º 2
0
        internal static Graphic ReadPalette(IDataReader reader)
        {
            var graphicReader  = new GraphicReader();
            var paletteGraphic = new Graphic();

            graphicReader.ReadGraphic(paletteGraphic, reader, paletteGraphicInfo);
            return(paletteGraphic);
        }
Ejemplo n.º 3
0
        void LoadGraphics(GraphicType type)
        {
            if (!graphics.ContainsKey(type))
            {
                graphics.Add(type, new List <Graphic>());
                var reader      = new GraphicReader();
                var info        = GraphicInfoFromType(type);
                var graphicList = graphics[type];

                void LoadGraphic(IDataReader graphicDataReader, byte maskColor = 0)
                {
                    graphicDataReader.Position = 0;
                    int end = graphicDataReader.Size - info.DataSize;

                    while (graphicDataReader.Position <= end)
                    {
                        var graphic = new Graphic();
                        reader.ReadGraphic(graphic, graphicDataReader, info, maskColor);
                        graphicList.Add(graphic);
                    }
                }

                var allFiles = new SortedDictionary <int, IDataReader>();

                foreach (var graphicFile in graphicFiles[type])
                {
                    var containerFile = gameData.Files[graphicFile.File];

                    if (graphicFile.SubFiles == null)
                    {
                        foreach (var file in containerFile.Files)
                        {
                            allFiles[graphicFile.FileIndexOffset + file.Key] = file.Value;
                        }
                    }
                    else
                    {
                        foreach (var file in graphicFile.SubFiles)
                        {
                            allFiles[graphicFile.FileIndexOffset + file] = containerFile.Files[file];
                        }
                    }
                }

                foreach (var file in allFiles)
                {
                    LoadGraphic(file.Value, (byte)(type switch
                    {
                        GraphicType.Portrait => 25,
                        GraphicType.LabBackground => 9, // 9 is the sky color index
                        _ => 0
                    }));
Ejemplo n.º 4
0
        void LoadGraphics(GraphicType type)
        {
            if (!graphics.ContainsKey(type))
            {
                graphics.Add(type, new List <Graphic>());
                var reader      = new GraphicReader();
                var info        = GraphicInfoFromType(type);
                var graphicList = graphics[type];

                void LoadGraphic(IDataReader graphicDataReader)
                {
                    graphicDataReader.Position = 0;
                    int end = graphicDataReader.Size - info.DataSize;

                    while (graphicDataReader.Position <= end)
                    {
                        var graphic = new Graphic();
                        reader.ReadGraphic(graphic, graphicDataReader, info);
                        graphicList.Add(graphic);
                    }
                }

                var allFiles = new SortedDictionary <int, IDataReader>();

                foreach (var graphicFile in graphicFiles[type])
                {
                    var containerFile = gameData.Files[graphicFile.File];

                    if (graphicFile.SubFiles == null)
                    {
                        foreach (var file in containerFile.Files)
                        {
                            allFiles[graphicFile.FileIndexOffset + file.Key] = file.Value;
                        }
                    }
                    else
                    {
                        foreach (var file in graphicFile.SubFiles)
                        {
                            allFiles[graphicFile.FileIndexOffset + file] = containerFile.Files[file];
                        }
                    }
                }

                foreach (var file in allFiles)
                {
                    LoadGraphic(file.Value);
                }
            }
        }
Ejemplo n.º 5
0
        static Graphic ReadGraphic(GraphicReader graphicReader, IDataReader file, int width, int height, bool alpha, bool texture)
        {
            var graphic = new Graphic
            {
                Width          = width,
                Height         = height,
                IndexedGraphic = true
            };

            file.Position = 0; // TODO: or 4? sometimes the file is 4 bytes bigger than expected

            graphicReader.ReadGraphic(graphic, file, new GraphicInfo
            {
                Width         = width,
                Height        = height,
                GraphicFormat = texture ? GraphicFormat.Texture4Bit : GraphicFormat.Palette4Bit,
                PaletteOffset = 0,
                Alpha         = alpha
            });

            return(graphic);
        }
Ejemplo n.º 6
0
        void LoadTravelGraphics()
        {
            // Travel gfx stores graphics with a header:
            // uword NumberOfHorizontalSprites (a sprite has a width of 16 pixels)
            // uword Height (in pixels)
            // uword XOffset (in pixels relative to drawing position)
            // uword YOffset (in pixels relative to drawing position)
            IFileContainer container;

            try
            {
                container = Files["Travel_gfx.amb"];
            }
            catch (KeyNotFoundException)
            {
                if (stopAtFirstError)
                {
                    throw new FileNotFoundException("Unable to find travel graphics.");
                }
                else
                {
                    return;
                }
            }

            var graphicReader = new GraphicReader();
            var graphicInfo   = new GraphicInfo
            {
                GraphicFormat = GraphicFormat.Palette5Bit,
                Alpha         = true
            };

            foreach (var file in container.Files)
            {
                var reader = file.Value;
                reader.Position = 0;

                Graphic LoadGraphic()
                {
                    var graphic = new Graphic();

                    graphicReader.ReadGraphic(graphic, reader, graphicInfo);
                    return(graphic);
                }

                for (int direction = 0; direction < 4; ++direction)
                {
                    int numSprites = reader.ReadWord();
                    graphicInfo.Height = reader.ReadWord();
                    graphicInfo.Width  = numSprites * 16;
                    uint xOffset = reader.ReadWord();
                    uint yOffset = reader.ReadWord();

                    travelGraphicInfos.Add(new TravelGraphicInfo
                    {
                        Width   = (uint)graphicInfo.Width,
                        Height  = (uint)graphicInfo.Height,
                        OffsetX = xOffset,
                        OffsetY = yOffset
                    });
                    TravelGraphics.Add(LoadGraphic());
                }
            }
        }
Ejemplo n.º 7
0
        public GraphicProvider(GameData gameData)
        {
            this.gameData = gameData;
            var graphicReader = new GraphicReader();

            Palettes = gameData.Files[paletteFile].Files.ToDictionary(f => f.Key, f => ReadPalette(graphicReader, f.Value));

            // There is a special palette used for items and portraits.
            // Special thanks to Iceblizz who provided it to the Amberworld project.
            Palettes.Add(50, new Graphic
            {
                Width          = 32,
                Height         = 1,
                IndexedGraphic = false,
                Data           = new byte[]
                {
                    0x00, 0x00, 0x00, 0xff, 0xed, 0xdc, 0xcb, 0xff, 0xfe, 0xfe, 0xed, 0xff, 0xba, 0xba, 0xcb, 0xff,
                    0x87, 0x98, 0xa9, 0xff, 0x54, 0x76, 0x87, 0xff, 0x21, 0x54, 0x65, 0xff, 0x00, 0x32, 0x43, 0xff,
                    0xfe, 0xcb, 0x98, 0xff, 0xed, 0xa9, 0x76, 0xff, 0xcb, 0x87, 0x54, 0xff, 0xa9, 0x65, 0x32, 0xff,
                    0x87, 0x43, 0x21, 0xff, 0x54, 0x21, 0x10, 0xff, 0xba, 0x87, 0x00, 0xff, 0xdc, 0xa9, 0x00, 0xff,
                    0xfe, 0xcb, 0x00, 0xff, 0xfe, 0x98, 0x00, 0xff, 0xcb, 0x65, 0x00, 0xff, 0x87, 0x10, 0x21, 0xff,
                    0xcb, 0x43, 0x32, 0xff, 0xed, 0x65, 0x32, 0xff, 0xa9, 0xa9, 0x43, 0xff, 0x54, 0x76, 0x32, 0xff,
                    0x21, 0x54, 0x43, 0xff, 0x00, 0x10, 0x00, 0xff, 0x21, 0x21, 0x21, 0xff, 0x43, 0x43, 0x32, 0xff,
                    0x65, 0x65, 0x54, 0xff, 0x87, 0x87, 0x76, 0xff, 0xa9, 0xa9, 0x98, 0xff, 0xcb, 0xcb, 0xba, 0xff
                }
            });
            // We also use another palette for text rendering. It has transparent, black, white, gray, red, yellow etc.
            // Note: If a text is hovered (e.g. when choosing something to say) the color is inverted (e.g. yellow
            // background and text is just transparent to show the gray background).
            Palettes.Add(51, new Graphic
            {
                Width          = 32,
                Height         = 1,
                IndexedGraphic = false,
                Data           = new byte[]
                {
                    // transparent, black (shadow), white, gray
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xee, 0xff, 0xbb, 0xbb, 0xaa, 0xff,
                    // red (character), yellow (active character), orange (battle text), blueish gray (dead character)
                    0xcc, 0x44, 0x33, 0xff, 0xff, 0xcc, 0x00, 0xff, 0xdd, 0x66, 0x33, 0xff, 0x99, 0xaa, 0xaa, 0xff,
                    // rest unused (TODO: character with ailment?)
                    0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
                    0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
                    0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
                    0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
                    0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
                    0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff
                }
            });

            // TODO: The following bytes were extracted from AM2_CPU (behind cursors).
            // It really looks like 3 palettes. Maybe for text rendering?

            /* 00 00 0E DC 0F FE 0B BC 08 9A 05 78 02 56 00 34 0F C9 0E A7 0C 85 0A 63 08 42 05 21 0B 80 0D A0
             * 0F C0 0F 90 0C 60 08 12 0C 43 0E 63 0A A4 05 73 02 54 05 09 02 22 04 43 06 65 08 87 0A A9 0C CB
             *
             * 00 00 0F DC 0E B9 0C 96 04 10 08 51 0A 74 06 30 0F 60 0D 30 09 00 0F C0 0B 90 08 60 09 A3 05 70
             * 02 40 08 BE 03 8C 00 48 0C 8D 0A 69 08 36 0F FF 07 9D 01 6A 02 11 04 32 06 54 08 76 0A 98 0C BA
             *
             * 00 00 0E DC 0F FE 0B BC 08 9A 05 78 02 56 00 34 0F C9 0E A7 0C 85 0A 63 08 42 05 21 0B 80 0D A0
             * 0F C0 0F 90 0C 60 08 12 0C 43 0E 63 0A A4 05 73 04 9D 02 59 02 10 04 31 06 53 08 75 0A 97 0C B9
             */

            foreach (GraphicType type in Enum.GetValues(typeof(GraphicType)))
            {
                LoadGraphics(type);
            }
        }
Ejemplo n.º 8
0
        public void ReadLabdata(Labdata labdata, IDataReader dataReader, IGameData gameData)
        {
            labdata.WallHeight       = dataReader.ReadWord();
            labdata.Unknown1         = dataReader.ReadByte();   // Unknown
            labdata.CombatBackground = dataReader.ReadByte() & 0x0fu;
            labdata.Unknown2         = dataReader.ReadBytes(2); // Unknown
            // Note: The ceiling texture index can be 0 in which case a sky is used.
            //       The sky is composed of a color gradient and a lab background
            //       which is given inside the map data.
            uint ceilingTextureIndex = dataReader.ReadByte();
            uint floorTextureIndex   = dataReader.ReadByte();

            labdata.Objects.Clear();
            int numObjects = dataReader.ReadWord();
            var objects    = new List <Tuple <ushort, List <Tuple <float, float, float, int> > > >(numObjects);

            for (int i = 0; i < numObjects; ++i)
            {
                var obj = Tuple.Create(dataReader.ReadWord(), new List <Tuple <float, float, float, int> >(8));

                for (int n = 0; n < 8; ++n) // 8 sub entries (a map object can consist of up to 8 sub objects)
                {
                    obj.Item2.Add(Tuple.Create(
                                      (float)(short)dataReader.ReadWord(),
                                      (float)(short)dataReader.ReadWord(),
                                      (float)(short)dataReader.ReadWord(),
                                      (int)dataReader.ReadWord()));
                }

                objects.Add(obj);
            }

            labdata.ObjectInfos.Clear();
            int numObjectInfos = dataReader.ReadWord();

            for (int i = 0; i < numObjectInfos; ++i)
            {
                var objectInfo = new Labdata.ObjectInfo
                {
                    Unknown1            = dataReader.ReadBytes(3), // TODO: Collision info for all 3 axes?
                    Flags               = (Labdata.ObjectFlags)dataReader.ReadByte(),
                    TextureIndex        = dataReader.ReadWord(),
                    NumAnimationFrames  = dataReader.ReadByte(),
                    Unknown2            = dataReader.ReadByte(),
                    TextureWidth        = dataReader.ReadByte(),
                    TextureHeight       = dataReader.ReadByte(),
                    MappedTextureWidth  = dataReader.ReadWord(),
                    MappedTextureHeight = dataReader.ReadWord()
                };

                labdata.ObjectInfos.Add(objectInfo);

                //Console.WriteLine($"Type: {objectInfo.Type}, Texture: {objectInfo.TextureIndex},{objectInfo.TextureWidth}x{objectInfo.TextureHeight} -> {objectInfo.MappedTextureWidth}x{objectInfo.MappedTextureHeight}, Frames: {objectInfo.NumAnimationFrames}");
            }

            foreach (var obj in objects)
            {
                var subObjects = new List <Labdata.ObjectPosition>(8);

                foreach (var pos in obj.Item2)
                {
                    if (pos.Item4 != 0)
                    {
                        subObjects.Add(new Labdata.ObjectPosition
                        {
                            X      = pos.Item1,
                            Y      = pos.Item2,
                            Z      = pos.Item3,
                            Object = labdata.ObjectInfos[pos.Item4 - 1]
                        });
                    }
                }

                labdata.Objects.Add(new Labdata.Object
                {
                    Header     = obj.Item1,
                    SubObjects = subObjects
                });
            }

            labdata.Walls.Clear();
            int numWalls = dataReader.ReadWord();

            for (int i = 0; i < numWalls; ++i)
            {
                var wallData = new Labdata.WallData
                {
                    Unknown1     = dataReader.ReadBytes(3), // TODO: Collision info for all 3 axes?
                    Flags        = (Labdata.WallFlags)dataReader.ReadByte(),
                    TextureIndex = dataReader.ReadByte(),
                    AutomapType  = (AutomapType)dataReader.ReadByte(),
                    Unknown2     = dataReader.ReadByte()
                };
                int numOverlays = dataReader.ReadByte();
                if (numOverlays != 0)
                {
                    wallData.Overlays = new Labdata.OverlayData[numOverlays];

                    for (int o = 0; o < numOverlays; ++o)
                    {
                        wallData.Overlays[o] = new Labdata.OverlayData
                        {
                            NumAnimationFrames = dataReader.ReadByte(),
                            TextureIndex       = dataReader.ReadByte(),
                            PositionX          = dataReader.ReadByte(),
                            PositionY          = dataReader.ReadByte(),
                            TextureWidth       = dataReader.ReadByte(),
                            TextureHeight      = dataReader.ReadByte()
                        };
                    }
                }
                labdata.Walls.Add(wallData);

                //Console.WriteLine($"Wall{i+1} -> {wallData}");
            }

            // Load labyrinth graphics
            var graphicReader = new GraphicReader();

            if (floorTextureIndex != 0)
            {
                labdata.FloorGraphic = ReadGraphic(graphicReader, gameData.Files["Floors.amb"].Files[(int)floorTextureIndex], 64, 64, false, false);
            }
            if (ceilingTextureIndex != 0)
            {
                labdata.CeilingGraphic = ReadGraphic(graphicReader, gameData.Files["Floors.amb"].Files[(int)ceilingTextureIndex], 64, 64, false, false); // TODO
            }
            var objectTextureFiles = gameData.Files[$"2Object3D.amb"].Files;

            gameData.Files[$"3Object3D.amb"].Files.ToList().ForEach(f => objectTextureFiles[f.Key] = f.Value);
            labdata.ObjectGraphics.Clear();
            foreach (var objectInfo in labdata.ObjectInfos)
            {
                labdata.ObjectGraphics.Add(ReadGraphic(graphicReader, objectTextureFiles[(int)objectInfo.TextureIndex],
                                                       (int)objectInfo.TextureWidth, (int)objectInfo.TextureHeight, true, true));
            }
            var wallTextureFiles    = gameData.Files[$"2Wall3D.amb"].Files;
            var overlayTextureFiles = gameData.Files[$"2Overlay3D.amb"].Files;

            gameData.Files[$"3Wall3D.amb"].Files.ToList().ForEach(f => wallTextureFiles[f.Key]       = f.Value);
            gameData.Files[$"3Overlay3D.amb"].Files.ToList().ForEach(f => overlayTextureFiles[f.Key] = f.Value);
            labdata.WallGraphics.Clear();
            int wallIndex = 0;

            foreach (var wall in labdata.Walls)
            {
                var wallGraphic = ReadGraphic(graphicReader, wallTextureFiles[(int)wall.TextureIndex],
                                              128, 80, wall.Flags.HasFlag(Labdata.WallFlags.Transparency), true);

                labdata.WallGraphics.Add(wallGraphic);

                if (wall.Overlays != null && wall.Overlays.Length != 0)
                {
                    foreach (var overlay in wall.Overlays)
                    {
                        wallGraphic.AddOverlay(overlay.PositionX, overlay.PositionY, ReadGraphic(graphicReader,
                                                                                                 overlayTextureFiles[(int)overlay.TextureIndex], (int)overlay.TextureWidth, (int)overlay.TextureHeight, true, true));
                    }
                }

                ++wallIndex;
            }
        }
Ejemplo n.º 9
0
        public GraphicProvider(GameData gameData, ExecutableData.ExecutableData executableData,
                               IntroData introData, OutroData outroData)
        {
            this.gameData = gameData;
            var graphicReader = new GraphicReader();

            Palettes = gameData.Files[paletteFile].Files.ToDictionary(f => f.Key, f => ReadPalette(graphicReader, f.Value));

            // Add builtin palettes
            for (int i = 0; i < 3; ++i)
            {
                Palettes.Add(50 + i, executableData.BuiltinPalettes[i]);
            }

            // And another palette for some UI graphics.
            // The portraits have a blue gradient as background. It is also 32x34 pixels in size and the gradient
            // is in y-direction. All colors have R=0x00 and G=0x11. The blue component is increased by 0x11
            // every 2 pixels starting at y=4 (first 4 pixel rows have B=0x00, next 2 have B=0x11, etc).
            // Last 2 rows have B=0xff.
            Palettes.Add(53, new Graphic
            {
                Width          = 32,
                Height         = 1,
                IndexedGraphic = false,
                Data           = new byte[]
                {
                    // The first colors are used for spells which use a materialize animation (earth and wind spells, waterfall, etc).
                    // The animation uses black, dark red, light purple, dark purple, dark beige, light beige in that order.
                    // We start with these color at offset 1. We leave the first color as fully transparent.
                    // Index 7 is unused.
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0xff, 0x33, 0x11, 0x00, 0xff, 0x88, 0x77, 0xaa, 0xff,
                    0x66, 0x55, 0x88, 0xff, 0x99, 0x88, 0x77, 0xff, 0xbb, 0xbb, 0x99, 0xff, 0x00, 0x00, 0x00, 0x00,
                    // 16 colors for the blue background gradient of portraits
                    0x00, 0x11, 0x00, 0xff, 0x00, 0x11, 0x11, 0xff, 0x00, 0x11, 0x22, 0xff, 0x00, 0x11, 0x33, 0xff,
                    0x00, 0x11, 0x44, 0xff, 0x00, 0x11, 0x55, 0xff, 0x00, 0x11, 0x66, 0xff, 0x00, 0x11, 0x77, 0xff,
                    0x00, 0x11, 0x88, 0xff, 0x00, 0x11, 0x99, 0xff, 0x00, 0x11, 0xaa, 0xff, 0x00, 0x11, 0xbb, 0xff,
                    0x00, 0x11, 0xcc, 0xff, 0x00, 0x11, 0xdd, 0xff, 0x00, 0x11, 0xee, 0xff, 0x00, 0x11, 0xff, 0xff,
                    // some UI colors (TODO: character with ailment?)
                    0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x55, 0xff, 0x44, 0x44, 0x33, 0xff,
                    0x22, 0x22, 0x22, 0xff, 0x88, 0x88, 0x77, 0xff, 0xaa, 0xaa, 0x99, 0xff, 0xcc, 0xcc, 0xbb, 0xff
                }
            });

            // Add the 9 intro palettes
            int introPaletteCount = introData == null ? 0 : Math.Min(9, introData.IntroPalettes.Count);
            int p = 0;

            for (; p < introPaletteCount; ++p)
            {
                Palettes.Add(54 + p, introData.IntroPalettes[p]);
            }
            for (; p < 9; ++p)
            {
                Palettes.Add(54 + p, new Graphic
                {
                    Width          = 32,
                    Height         = 1,
                    IndexedGraphic = false,
                    Data           = new byte[32 * 4]
                });
            }

            // Add the 6 outro palettes
            int outroPaletteCount = outroData == null ? 0 : Math.Min(6, outroData.OutroPalettes.Count);

            p = 0;
            for (; p < outroPaletteCount; ++p)
            {
                Palettes.Add(63 + p, outroData.OutroPalettes[p]);
            }
            for (; p < 6; ++p)
            {
                Palettes.Add(63 + p, new Graphic
                {
                    Width          = 32,
                    Height         = 1,
                    IndexedGraphic = false,
                    Data           = new byte[32 * 4]
                });
            }

            foreach (var type in Enum.GetValues <GraphicType>())
            {
                if (type == GraphicType.Cursor)
                {
                    var cursorGraphics = graphics[GraphicType.Cursor] = new List <Graphic>();

                    foreach (var cursor in executableData.Cursors.Entries)
                    {
                        cursorGraphics.Add(cursor.Graphic);
                    }
                }
                else if (type == GraphicType.UIElements)
                {
                    graphics[type] = UIElementProvider.Create();
                    graphics[type].AddRange(executableData.UIGraphics.Entries.Values);
                    graphics[type].AddRange(executableData.Buttons.Entries.Values);
                }
                else if (type == GraphicType.TravelGfx)
                {
                    graphics[type] = gameData.TravelGraphics;
                }
                else if (type == GraphicType.Transports)
                {
                    var reader = gameData.Files["Stationary"].Files[1];
                    reader.Position = 0;
                    graphics[type]  = gameData.StationaryImageInfos.Select(info =>
                    {
                        var graphic = new Graphic();
                        graphicReader.ReadGraphic(graphic, reader, info.Value);
                        return(graphic);
                    }).ToList();
                }
                else if (type == GraphicType.NPC)
                {
                    var npcGraphics = new List <Graphic>(34);
                    var graphicInfo = new GraphicInfo
                    {
                        Width         = 16,
                        Height        = 32,
                        GraphicFormat = GraphicFormat.Palette5Bit,
                        Alpha         = true,
                        PaletteOffset = 0
                    };
                    var graphic = new Graphic();
                    foreach (var file in gameData.Files["NPC_gfx.amb"].Files)
                    {
                        NPCGraphicOffsets.Add(file.Key, npcGraphics.Count);
                        var reader = file.Value;
                        reader.Position = 0;

                        while (reader.Position <= reader.Size - graphicInfo.DataSize)
                        {
                            int numFrames = reader.ReadByte();
                            reader.AlignToWord();
                            var compoundGraphic = new Graphic(16 * numFrames, 32, 0);

                            for (int i = 0; i < numFrames; ++i)
                            {
                                graphicReader.ReadGraphic(graphic, reader, graphicInfo);
                                compoundGraphic.AddOverlay((uint)i * 16, 0, graphic, false);
                            }

                            npcGraphics.Add(compoundGraphic);
                        }
                    }

                    graphics[type] = npcGraphics;
                }
                else if (type == GraphicType.CombatGraphics)
                {
                    var combatGraphics = new List <Graphic>(42);
                    var graphicInfo    = new GraphicInfo
                    {
                        GraphicFormat = GraphicFormat.Palette5Bit,
                        Alpha         = true,
                        PaletteOffset = 0
                    };
                    var reader = gameData.Files["Combat_graphics"].Files[1];
                    reader.Position = 0;

                    foreach (var combatGraphic in CombatGraphics.Info)
                    {
                        var info = combatGraphic.Value;

                        if (combatGraphic.Key == CombatGraphicIndex.BattleFieldIcons)
                        {
                            var battleFieldIcons = new List <Graphic>(35);
                            var iconGraphicInfo  = new GraphicInfo
                            {
                                Width         = 16,
                                Height        = 14,
                                GraphicFormat = GraphicFormat.Palette5Bit,
                                Alpha         = true,
                                PaletteOffset = 0
                            };

                            for (int i = 0; i < 35; ++i)
                            {
                                var graphic = new Graphic();
                                graphicReader.ReadGraphic(graphic, reader, iconGraphicInfo);
                                battleFieldIcons.Add(graphic);
                            }

                            graphics[GraphicType.BattleFieldIcons] = battleFieldIcons;
                        }
                        else
                        {
                            var graphic         = new Graphic();
                            var compoundGraphic = new Graphic((int)info.FrameCount * info.GraphicInfo.Width, info.GraphicInfo.Height, 0);

                            for (int i = 0; i < info.FrameCount; ++i)
                            {
                                graphicReader.ReadGraphic(graphic, reader, info.GraphicInfo);
                                compoundGraphic.AddOverlay((uint)(i * info.GraphicInfo.Width), 0, graphic, false);
                            }

                            combatGraphics.Add(compoundGraphic);
                        }
                    }

                    graphics[type] = combatGraphics;
                }
                else if (type == GraphicType.BattleFieldIcons)
                {
                    // Do nothing. This is filled when processing GraphicType.CombatGraphics.
                }
                else if (type == GraphicType.RiddlemouthGraphics)
                {
                    var riddlemouthGraphics = new List <Graphic>(4 + 7);
                    var reader = gameData.Files["Riddlemouth_graphics"].Files[1];
                    reader.Position = 0;
                    // 4 eye frames
                    ReadAndAddGraphics(4, 48, 9);
                    // 7 mouth frames
                    ReadAndAddGraphics(7, 48, 15);
                    void ReadAndAddGraphics(int frames, int width, int height)
                    {
                        var graphicInfo = new GraphicInfo
                        {
                            Width         = width,
                            Height        = height,
                            GraphicFormat = GraphicFormat.Palette3Bit,
                            Alpha         = false,
                            PaletteOffset = 24
                        };
                        var graphic         = new Graphic();
                        var compoundGraphic = new Graphic(frames * width, height, 0);

                        for (int f = 0; f < frames; ++f)
                        {
                            graphicReader.ReadGraphic(graphic, reader, graphicInfo);
                            compoundGraphic.AddOverlay((uint)(f * width), 0, graphic, false);
                        }
                        riddlemouthGraphics.Add(compoundGraphic);
                    }

                    graphics[type] = riddlemouthGraphics;
                }
                else if (type == GraphicType.AutomapGraphics)
                {
                    var automapGraphics = new List <Graphic>(43);
                    var reader          = gameData.Files["Automap_graphics"].Files[1];
                    reader.Position = 0x100; // TODO: maybe decode the bytes before that later

                    void ReadAndAddGraphics(int amount, int width, int height, GraphicFormat graphicFormat,
                                            int frames = 1, bool alpha = false)
                    {
                        var graphicInfo = new GraphicInfo
                        {
                            Width         = width,
                            Height        = height,
                            GraphicFormat = graphicFormat,
                            Alpha         = alpha,
                            PaletteOffset = 0
                        };

                        for (int i = 0; i < amount; ++i)
                        {
                            Graphic graphic = new Graphic();
                            if (frames == 1)
                            {
                                graphicReader.ReadGraphic(graphic, reader, graphicInfo);
                            }
                            else
                            {
                                var compoundGraphic = new Graphic(frames * width, height, 0);

                                for (int f = 0; f < frames; ++f)
                                {
                                    graphicReader.ReadGraphic(graphic, reader, graphicInfo);
                                    compoundGraphic.AddOverlay((uint)(f * width), 0, graphic, false);
                                }

                                graphic = compoundGraphic;
                            }
                            automapGraphics.Add(graphic);
                        }
                    }

                    // Map corners
                    ReadAndAddGraphics(4, 32, 32, GraphicFormat.Palette3Bit);
                    // Top map border
                    ReadAndAddGraphics(4, 16, 32, GraphicFormat.Palette3Bit);
                    // Right map border
                    ReadAndAddGraphics(2, 32, 32, GraphicFormat.Palette3Bit);
                    // Bottom map border
                    ReadAndAddGraphics(4, 16, 32, GraphicFormat.Palette3Bit);
                    // Left map border
                    ReadAndAddGraphics(2, 32, 32, GraphicFormat.Palette3Bit);
                    // 10 pin graphics
                    ReadAndAddGraphics(10, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Riddlemouth (4 frames)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 4, true);
                    // Teleport (4 frames)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 4, true);
                    // Spinner (4 frames)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 4, true);
                    // Trap (4 frames)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 4, true);
                    // Trapdoor (4 frames)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 4, true);
                    // Special (4 frames)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 4, true);
                    // Monster (4 frames)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 4, true);
                    // Door closed (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Door open (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Merchant (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Inn (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Chest closed (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Exit (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Chest open (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Pile (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Person (1 frame)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 1, true);
                    // Goto point (7 frames)
                    ReadAndAddGraphics(1, 16, 16, GraphicFormat.Palette5Bit, 7, true);

                    graphics[type] = automapGraphics;
                }
                else
                {
                    LoadGraphics(type);
                }
            }
        }