public static GBAMapData GenerateMapData(IList <TileMap> tilemaps, int paletteBankIndexOffset)
        {
            List <GBAScreenEntry> seList          = new List <GBAScreenEntry>();
            List <int>            mapStartOffsets = new List <int>();

            // Write map sizes
            List <int> widthLists  = new List <int>();
            List <int> heightLists = new List <int>();

            List <bool> mapIsDynamic = new List <bool>();

            for (int tilemapIndex = 0; tilemapIndex < tilemaps.Count; ++tilemapIndex)
            {
                var tilemap = tilemaps[tilemapIndex];

                int paletteIndex = tilemap.paletteIndex + paletteBankIndexOffset;
                if (paletteIndex >= PaletteHelper.MAX_PALETTE_INDEX)
                {
                    throw new Exception(string.Format("Palette index {0} is not valid for map data", paletteIndex));
                }

                int width  = tilemap.mapData.GetLength(0);
                int height = tilemap.mapData.GetLength(1);

                widthLists.Add(width);
                heightLists.Add(height);

                mapStartOffsets.Add(seList.Count);

                bool widthValidForNesting  = width % GBA_MAP_TILE_WIDTH == 0 && width >= REG_TILEMAP_MIN_SIZE && width <= REG_TILEMAP_MAX_SIZE;
                bool heightValidForNesting = height % GBA_MAP_TILE_HEIGHT == 0 && height >= REG_TILEMAP_MIN_SIZE && height <= REG_TILEMAP_MAX_SIZE;

#if FORCE_DYNAMIC_RENDERING
                bool performGbaNesting = false;
#else
                bool performGbaNesting = widthValidForNesting && heightValidForNesting; // Disable for dynamic maps
#endif
                mapIsDynamic.Add(!performGbaNesting);

                if (performGbaNesting)
                {
                    // GBA nesting: https://www.coranac.com/tonc/text/regbg.htm 9.3.1
                    for (int mapStartY = 0; mapStartY < tilemap.mapData.GetLength(1); mapStartY += GBA_MAP_TILE_HEIGHT)
                    {
                        for (int mapStartX = 0; mapStartX < tilemap.mapData.GetLength(0); mapStartX += GBA_MAP_TILE_WIDTH)
                        {
                            for (int j = mapStartY; j < mapStartY + GBA_MAP_TILE_HEIGHT; ++j)
                            {
                                for (int i = mapStartX; i < mapStartX + GBA_MAP_TILE_WIDTH; ++i)
                                {
                                    var currentMapData = tilemap.mapData[i, j];

                                    GBAScreenEntry screenEntry = new GBAScreenEntry();
                                    screenEntry.SetTileIndex(currentMapData.tilesetIndex);

                                    if ((currentMapData.flags & TileMap.FlippingFlags.Horizontal) != 0)
                                    {
                                        screenEntry.SetHFlipFlag();
                                    }

                                    if ((currentMapData.flags & TileMap.FlippingFlags.Vertical) != 0)
                                    {
                                        screenEntry.SetVFlipFlag();
                                    }

                                    if (tilemap.paletteIndex >= 0)
                                    {
                                        screenEntry.SetPalIndex(paletteIndex);  // May be wrong when merging the palette
                                    }
                                    seList.Add(screenEntry);
                                }
                            }
                        }
                    }
                }
                else
                {
                    for (int mapY = 0; mapY < tilemap.mapData.GetLength(1); ++mapY)
                    {
                        for (int mapX = 0; mapX < tilemap.mapData.GetLength(0); ++mapX)
                        {
                            var currentMapData = tilemap.mapData[mapX, mapY];

                            GBAScreenEntry screenEntry = new GBAScreenEntry();
                            screenEntry.SetTileIndex(currentMapData.tilesetIndex);

                            if ((currentMapData.flags & TileMap.FlippingFlags.Horizontal) != 0)
                            {
                                screenEntry.SetHFlipFlag();
                            }

                            if ((currentMapData.flags & TileMap.FlippingFlags.Vertical) != 0)
                            {
                                screenEntry.SetVFlipFlag();
                            }

                            if (tilemap.paletteIndex >= 0)
                            {
                                screenEntry.SetPalIndex(paletteIndex);
                            }

                            seList.Add(screenEntry);
                        }
                    }
                }

                Console.WriteLine("Processed tilemap {0}:", tilemapIndex);
                if (performGbaNesting)
                {
                    Console.WriteLine("\tStatic/GBA nested map");
                }
                else
                {
                    Console.WriteLine("\tDynamically rendered map");
                }
            }

            GBAScreenEntry[] mapEntries = seList.ToArray();

            GBAMapData generatedMapData;
            generatedMapData.screenEntries = mapEntries;
            generatedMapData.mapIsDynamic  = mapIsDynamic;
            generatedMapData.widthLists    = widthLists;
            generatedMapData.heightLists   = heightLists;

            return(generatedMapData);
        }
        void WriteMapData(List <TileMap> tilemaps, StringBuilder sb)
        {
            const string tabs = namespaceTabs;

            List <GBAScreenEntry> seList          = new List <GBAScreenEntry>();
            List <int>            mapStartOffsets = new List <int>();

            // Write map sizes
            StringBuilder widthSb  = new StringBuilder();
            StringBuilder heightSb = new StringBuilder();

            widthSb.Append(VAR_MAPWIDTHS);
            widthSb.Append(namespaceTabs + "{\n");
            widthSb.Append(namespaceTabs + TAB_CHAR);

            heightSb.Append(VAR_MAPHEIGHTS);
            heightSb.Append(namespaceTabs + "{\n");
            heightSb.Append(namespaceTabs + TAB_CHAR);

            foreach (var tilemap in tilemaps)
            {
                widthSb.AppendFormat("{0}, ", tilemap.mapData.GetLength(0));
                heightSb.AppendFormat("{0}, ", tilemap.mapData.GetLength(1));

                mapStartOffsets.Add(seList.Count);

                bool performGbaNesting = false; // Disable for dynamic maps
                if (performGbaNesting)
                {
                    // GBA nesting: https://www.coranac.com/tonc/text/regbg.htm 9.3.1
                    for (int mapStartY = 0; mapStartY < tilemap.mapData.GetLength(1); mapStartY += GBA_MAP_TILE_HEIGHT)
                    {
                        for (int mapStartX = 0; mapStartX < tilemap.mapData.GetLength(0); mapStartX += GBA_MAP_TILE_WIDTH)
                        {
                            for (int j = mapStartY; j < mapStartY + GBA_MAP_TILE_HEIGHT; ++j)
                            {
                                for (int i = mapStartX; i < mapStartX + GBA_MAP_TILE_WIDTH; ++i)
                                {
                                    var currentMapData = tilemap.mapData[i, j];

                                    GBAScreenEntry screenEntry = new GBAScreenEntry();
                                    screenEntry.SetTileIndex(currentMapData.tilesetIndex);

                                    if ((currentMapData.flags & TileMap.FlippingFlags.Horizontal) != 0)
                                    {
                                        screenEntry.SetHFlipFlag();
                                    }

                                    if ((currentMapData.flags & TileMap.FlippingFlags.Vertical) != 0)
                                    {
                                        screenEntry.SetVFlipFlag();
                                    }

                                    if (tilemap.paletteIndex >= 0)
                                    {
                                        screenEntry.SetPalIndex(tilemap.paletteIndex);
                                    }

                                    seList.Add(screenEntry);
                                }
                            }
                        }
                    }
                }
                else
                {
                    for (int mapY = 0; mapY < tilemap.mapData.GetLength(1); ++mapY)
                    {
                        for (int mapX = 0; mapX < tilemap.mapData.GetLength(0); ++mapX)
                        {
                            var currentMapData = tilemap.mapData[mapX, mapY];

                            GBAScreenEntry screenEntry = new GBAScreenEntry();
                            screenEntry.SetTileIndex(currentMapData.tilesetIndex);

                            if ((currentMapData.flags & TileMap.FlippingFlags.Horizontal) != 0)
                            {
                                screenEntry.SetHFlipFlag();
                            }

                            if ((currentMapData.flags & TileMap.FlippingFlags.Vertical) != 0)
                            {
                                screenEntry.SetVFlipFlag();
                            }

                            if (tilemap.paletteIndex >= 0)
                            {
                                screenEntry.SetPalIndex(tilemap.paletteIndex);
                            }

                            seList.Add(screenEntry);
                        }
                    }
                }
            }

            {
                widthSb.Append("\n");
                widthSb.Append(namespaceTabs + "};\n");

                heightSb.Append("\n");
                heightSb.Append(namespaceTabs + "};\n");
            }

            GBAScreenEntry[] mapEntries = seList.ToArray();
            sb.AppendFormat(VAR_MAPCOUNT, tilemaps.Count);
            sb.AppendFormat(VAR_HEADER_TILEMAPLENGTH_FORMAT, mapEntries.Length);

            sb.Append(widthSb.ToString());
            sb.Append("\n");
            sb.Append(heightSb.ToString());
            sb.Append("\n");

            sb.Append(VAR_TILEMAP);
            sb.Append(namespaceTabs + "{\n\t" + namespaceTabs);

            int hexCount = 0;

            foreach (var mapEntry in mapEntries)
            {
                sb.AppendFormat("0x{0:X4}, ", mapEntry.m_data);
                if ((hexCount + 1) % c_arrayNewlineCount == 0)
                {
                    sb.Append("\n\t" + tabs);
                }

                ++hexCount;
            }

            sb.Append("\n" + namespaceTabs + "};\n\n");
        }