public LightEffectProvider(ExecutableData.ExecutableData executableData)
 {
     this.executableData = executableData;
 }
        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);
                }
            }
        }