예제 #1
0
파일: MapsControl.cs 프로젝트: xfixium/Rika
        /// <summary>
        /// Sets map fields for selected map
        /// </summary>
        private void SetMapFields(bool loading)
        {
            if (!HasData)
            {
                return;
            }

            Map            map        = MainForm.RomData.Maps.Find(x => x.ID == Convert.ToInt32(trMaps.SelectedNode.Tag));
            PaletteGroup   collection = MainForm.RomData.PaletteGroups.Find(x => x.ID == map.PaletteGroupID);
            List <Tileset> tilesets   = MainForm.RomData.GetTilesets(map.TilesetIDs);

            if (loading)
            {
                _loading = true;
                grdTilesetPalettes.AutoGenerateColumns = false;
                grdTilesetPalettes.DataSource          = collection.Palettes;
                lstTilesets.Items.Clear();
                foreach (Tileset tileset in tilesets)
                {
                    lstTilesets.Items.Add(new ObjectID(tileset.ID, tileset.Name));
                }
                lstTilesets.SelectedIndex = 0;
                _loading = false;
            }
            Palette palette  = (Palette)grdTilesetPalettes.CurrentRow.DataBoundItem;
            Tileset selected = tilesets.Find(x => x.ID == (lstTilesets.SelectedItem as ObjectID).ID);

            pnlTileset.Image  = selected.GetImage(1, palette.Colors);
            txtMapDialog.Text = MainForm.RomData.DialogGroups.Find(x => x.ID == map.DialogGroupID).ToString();
        }
예제 #2
0
        static PaletteGroup createPalette(Image image, int tileX, int tileY, Color bgColor)
        {
            Dictionary <Color, int> colorCounter = new Dictionary <Color, int>();

            colorCounter[bgColor] = 0;
            for (int y = 0; y < 16; y++)
            {
                for (int x = 0; x < 16; x++)
                {
                    var color = ((Bitmap)image).GetPixel(tileX * 16 + x, tileY * 16 + y);
                    if (!colorCounter.ContainsKey(color))
                    {
                        colorCounter[color] = 0;
                    }
                    colorCounter[color]++;
                }
            }

            // Пока их больше четырёх, надо удалить самый неиспользуемый
            while (colorCounter.Count > 4)
            {
                // Вычисляем самый малоиспользуемый цвет
                int          min       = int.MaxValue;
                Color        minColor  = Color.Black;
                List <Color> newColors = new List <Color>();
                foreach (var color in colorCounter.Keys)
                {
                    newColors.Add(color);
                    if (colorCounter[color] < min && color != bgColor)
                    {
                        minColor = color;
                        min      = colorCounter[color];
                    }
                }

                // Удаляем его из списка цветов
                newColors.Remove(minColor);
                // Находим максимально похожий
                var similar = findSimilarColor(newColors, minColor);
                // Заменяем старый цвет на новый в каждом пикселе
                for (int y = 0; y < 16; y++)
                {
                    for (int x = 0; x < 16; x++)
                    {
                        var color = ((Bitmap)image).GetPixel(tileX * 16 + x, tileY * 16 + y);
                        if (color == minColor)
                        {
                            ((Bitmap)image).SetPixel(tileX * 16 + x, tileY * 16 + y, similar);
                        }
                    }
                }

                colorCounter.Remove(minColor);
            }

            // Создаём палитру
            var paletteGroup = new PaletteGroup(colorCounter.Keys);

            return(paletteGroup);
        }
예제 #3
0
        /// <summary>
        /// Sets the selected palette colors
        /// </summary>
        /// <param name="paletteGroupID">The id of the given palette group</param>
        /// <param name="index">Index override</param>
        private void SetPalette(int paletteGroupID, int index)
        {
            // If no data, return
            if (!HasData || cbPaletteGroups.SelectedItem == null)
            {
                _palette = null;
            }

            paletteGroupID = paletteGroupID <= 0 ? (cbPaletteGroups.SelectedItem as ObjectID).ID : paletteGroupID;
            PaletteGroup group   = MainForm.RomData.PaletteGroups.Find(x => x.ID == paletteGroupID);
            Palette      palette = index < group.Palettes.Count ? group.Palettes[index] : grdPaletteGroups.CurrentRow != null ? (Palette)grdPaletteGroups.CurrentRow.DataBoundItem : group.Palettes[0];

            _palette = palette.Colors;
        }
예제 #4
0
        /// <summary>
        /// Sets palette grid based on selected palette collection
        /// </summary>
        private void SetPalettes(int index)
        {
            // If no data, return
            if (!HasData || cbPaletteGroups.SelectedItem == null)
            {
                _palette = null;
            }

            PaletteGroup collection = MainForm.RomData.PaletteGroups.Find(x => x.ID == (cbPaletteGroups.SelectedItem as ObjectID).ID);

            grdPaletteGroups.AutoGenerateColumns = false;
            grdPaletteGroups.DataSource          = collection.Palettes;
            if (index < grdPaletteGroups.Rows.Count)
            {
                grdPaletteGroups.Rows[index].Selected = true;
            }
        }
예제 #5
0
        static int Main(string[] args)
        {
            /*
             * var mask = Image.FromFile(@"D:\Projects\NES\BroWord\keyboard_mask.png");
             * var chromaKey = Color.FromArgb(0xFF, 0, 0xFF, 0);
             * var maskResultX1 = new List<int>();
             * var maskResultY1 = new List<int>();
             * var maskResultX2 = new List<int>();
             * var maskResultY2 = new List<int>();
             * for (int y = 0; y < mask.Height; y++)
             *  for (int x = 0; x < mask.Width; x++)
             *  {
             *      var c = (mask as Bitmap).GetPixel(x, y);
             *      if (x > 0 && y > 0 && c == chromaKey && (mask as Bitmap).GetPixel(x - 1, y) != chromaKey && (mask as Bitmap).GetPixel(x, y - 1) != chromaKey)
             *      {
             *          for (int y2 = y + 1; y2 < mask.Height; y2++)
             *              for (int x2 = x + 1; x2 < mask.Width; x2++)
             *              {
             *                  if (x2 < mask.Width - 1 && y2 < mask.Height - 1 && (mask as Bitmap).GetPixel(x2, y2) == chromaKey && (mask as Bitmap).GetPixel(x2 + 1, y2) != chromaKey && (mask as Bitmap).GetPixel(x2, y2 + 1) != chromaKey)
             *                  {
             *                      Console.WriteLine("{0} {1} {2} {3}", x, y, x2, y2);
             *                      maskResultX1.Add(x);
             *                      maskResultY1.Add(y);
             *                      maskResultX2.Add(x2);
             *                      maskResultY2.Add(y2);
             *                      x2 = y2 = 256;
             *                  }
             *              }
             *
             *      }
             *  }
             * var maskResult = new StringBuilder();
             * maskResult.Append("keyboard_coords_x1:");
             * for (int i = 0; i < maskResultX1.Count; i++)
             * {
             *  if ((i == 0) || (i % 16 == 0))
             *      maskResult.Append("\r\n  .db ");
             *  if (i % 16 != 0) maskResult.Append(", ");
             *  maskResult.AppendFormat("{0}", maskResultX1[i]);
             * }
             * maskResult.Append("\r\n\r\nkeyboard_coords_y1:");
             * for (int i = 0; i < maskResultY1.Count; i++)
             * {
             *  if ((i == 0) || (i % 16 == 0))
             *      maskResult.Append("\r\n  .db ");
             *  if (i % 16 != 0) maskResult.Append(", ");
             *  maskResult.AppendFormat("{0}", maskResultY1[i]);
             * }
             * File.WriteAllText(@"D:\Projects\NES\BroWord\keyboard_mask.asm", maskResult.ToString());
             * maskResult.Append("\r\n\r\nkeyboard_coords_x2:");
             * for (int i = 0; i < maskResultX2.Count; i++)
             * {
             *  if ((i == 0) || (i % 16 == 0))
             *      maskResult.Append("\r\n  .db ");
             *  if (i % 16 != 0) maskResult.Append(", ");
             *  maskResult.AppendFormat("{0}", maskResultX2[i]);
             * }
             * maskResult.Append("\r\n\r\nkeyboard_coords_y2:");
             * for (int i = 0; i < maskResultY2.Count; i++)
             * {
             *  if ((i == 0) || (i % 16 == 0))
             *      maskResult.Append("\r\n  .db ");
             *  if (i % 16 != 0) maskResult.Append(", ");
             *  maskResult.AppendFormat("{0}", maskResultY2[i]);
             * }
             * maskResult.AppendFormat("\r\n\r\nkeyboard_key_count:\r\n  .db {0}", maskResultX1.Count);
             * File.WriteAllText(@"D:\Projects\NES\BroWord\keyboard_mask.asm", maskResult.ToString());
             * return 0;
             */

            if (args.Length < 4)
            {
                Console.WriteLine("NES image converter");
                Console.WriteLine("(c) Cluster, 2015");
                Console.WriteLine("http://clusterrr.com");
                Console.WriteLine("*****@*****.**");
                Console.WriteLine("Usage: tilesconverter.exe <input.png> <pattern.bin> <nametable.bin> <palette.bin> [palette_offset]");
                return(2);
            }

            try
            {
                NesPalette[0x00] = Color.FromArgb(0x7C, 0x7C, 0x7C);
                NesPalette[0x01] = Color.FromArgb(0x00, 0x00, 0xFC);
                NesPalette[0x02] = Color.FromArgb(0x00, 0x00, 0xBC);
                NesPalette[0x03] = Color.FromArgb(0x44, 0x28, 0xBC);
                NesPalette[0x04] = Color.FromArgb(0x94, 0x00, 0x84);
                NesPalette[0x05] = Color.FromArgb(0xA8, 0x00, 0x20);
                NesPalette[0x06] = Color.FromArgb(0xA8, 0x10, 0x00);
                NesPalette[0x07] = Color.FromArgb(0x88, 0x14, 0x00);
                NesPalette[0x08] = Color.FromArgb(0x50, 0x30, 0x00);
                NesPalette[0x09] = Color.FromArgb(0x00, 0x78, 0x00);
                NesPalette[0x0A] = Color.FromArgb(0x00, 0x68, 0x00);
                NesPalette[0x0B] = Color.FromArgb(0x00, 0x58, 0x00);
                NesPalette[0x0C] = Color.FromArgb(0x00, 0x40, 0x58);
                NesPalette[0x0F] = Color.FromArgb(0x00, 0x00, 0x00);

                NesPalette[0x10] = Color.FromArgb(0xBC, 0xBC, 0xBC);
                NesPalette[0x11] = Color.FromArgb(0x00, 0x78, 0xF8);
                NesPalette[0x12] = Color.FromArgb(0x00, 0x58, 0xF8);
                NesPalette[0x13] = Color.FromArgb(0x68, 0x44, 0xFC);
                NesPalette[0x14] = Color.FromArgb(0xD8, 0x00, 0xCC);
                NesPalette[0x15] = Color.FromArgb(0xE4, 0x00, 0x58);
                NesPalette[0x16] = Color.FromArgb(0xF8, 0x38, 0x00);
                NesPalette[0x17] = Color.FromArgb(0xE4, 0x5C, 0x10);
                NesPalette[0x18] = Color.FromArgb(0xAC, 0x7C, 0x00);
                NesPalette[0x19] = Color.FromArgb(0x00, 0xB8, 0x00);
                NesPalette[0x1A] = Color.FromArgb(0x00, 0xA8, 0x00);
                NesPalette[0x1B] = Color.FromArgb(0x00, 0xA8, 0x44);
                NesPalette[0x1C] = Color.FromArgb(0x00, 0x88, 0x88);
                //NesPalette[0x1D] = Color.FromArgb(0x08, 0x08, 0x08);

                //NesPalette[0x20] = Color.FromArgb(0xFC, 0xFC, 0xFC);
                NesPalette[0x21] = Color.FromArgb(0x3C, 0xBC, 0xFC);
                NesPalette[0x22] = Color.FromArgb(0x68, 0x88, 0xFC);
                NesPalette[0x23] = Color.FromArgb(0x98, 0x78, 0xF8);
                NesPalette[0x24] = Color.FromArgb(0xF8, 0x78, 0xF8);
                NesPalette[0x25] = Color.FromArgb(0xF8, 0x58, 0x98);
                NesPalette[0x26] = Color.FromArgb(0xF8, 0x78, 0x58);
                NesPalette[0x27] = Color.FromArgb(0xFC, 0xA0, 0x44);
                NesPalette[0x28] = Color.FromArgb(0xF8, 0xB8, 0x00);
                NesPalette[0x29] = Color.FromArgb(0xB8, 0xF8, 0x18);
                NesPalette[0x2A] = Color.FromArgb(0x58, 0xD8, 0x54);
                NesPalette[0x2B] = Color.FromArgb(0x58, 0xF8, 0x98);
                NesPalette[0x2C] = Color.FromArgb(0x00, 0xE8, 0xD8);
                NesPalette[0x2D] = Color.FromArgb(0x7C, 0x7C, 0x7C);

                NesPalette[0x30] = Color.FromArgb(0xFC, 0xFC, 0xFC);
                NesPalette[0x31] = Color.FromArgb(0xA4, 0xE4, 0xFC);
                NesPalette[0x32] = Color.FromArgb(0xB8, 0xB8, 0xF8);
                NesPalette[0x33] = Color.FromArgb(0xD8, 0xB8, 0xF8);
                NesPalette[0x34] = Color.FromArgb(0xF8, 0xB8, 0xF8);
                NesPalette[0x35] = Color.FromArgb(0xF8, 0xA4, 0xC0);
                NesPalette[0x36] = Color.FromArgb(0xF0, 0xD0, 0xB0);
                NesPalette[0x37] = Color.FromArgb(0xFC, 0xE0, 0xA8);
                NesPalette[0x38] = Color.FromArgb(0xF8, 0xD8, 0x78);
                NesPalette[0x39] = Color.FromArgb(0xD8, 0xF8, 0x78);
                NesPalette[0x3A] = Color.FromArgb(0xB8, 0xF8, 0xB8);
                NesPalette[0x3B] = Color.FromArgb(0xB8, 0xF8, 0xD8);
                NesPalette[0x3C] = Color.FromArgb(0x00, 0xFC, 0xFC);
                NesPalette[0x3D] = Color.FromArgb(0xD8, 0xD8, 0xD8);

                /*
                 * var image = Image.FromFile(@"D:\Temp\NES_palette.png");
                 * for (int y = 0; y < 4; y++)
                 * for (int x = 0; x < 14; x++)
                 * {
                 *  var color = ((Bitmap)image).GetPixel(x * 16 + 8, y * 16 + 8);
                 *  var index = x + y * 16;
                 *  Console.WriteLine(string.Format("NesPalette[0x{0:X2}] = Color.FromArgb(0x{1:X2}, 0x{2:X2}, 0x{3:X2});", index, color.R, color.G, color.B));
                 * }
                 * Console.ReadLine();
                 */

                //var image = Image.FromFile(@"D:\hello_world.png");
                Console.WriteLine("Converting {0}...", args[0]);
                var image = Image.FromFile(args[0]);
                image = new Bitmap(image);

                PaletteGroup[] palettes;
                if (!args[3].StartsWith("!"))
                {
                    // Приводим все цвета на рисунке к тем, что допустимы на NES
                    for (int y = 0; y < image.Height; y++)
                    {
                        for (int x = 0; x < image.Width; x++)
                        {
                            var color        = ((Bitmap)image).GetPixel(x, y);
                            var similarColor = NesPalette[findSimilarColor(NesPalette, color)];
                            ((Bitmap)image).SetPixel(x, y, similarColor);
                        }
                    }

                    Color bgColor = ((Bitmap)image).GetPixel(0, 0);
                    Dictionary <PaletteGroup, int> paletteGroupCounter = new Dictionary <PaletteGroup, int>(new PaletteGroupComparer());

                    // Перебираем все тайлы 16*16
                    for (int tileY = 0; tileY < image.Height / 16; tileY++)
                    {
                        for (int tileX = 0; tileX < image.Width / 16; tileX++)
                        {
                            // Создаём палитру
                            var paletteGroup = createPalette(image, tileX, tileY, bgColor);

                            // Считаем количество таких палитр
                            if (!paletteGroupCounter.ContainsKey(paletteGroup))
                            {
                                paletteGroupCounter[paletteGroup] = 0;
                            }
                            paletteGroupCounter[paletteGroup]++;
                        }
                    }
                    //image.Save(@"test0.png", ImageFormat.Png);

                    // Группируем палитры. Некоторые из них могут содержать все цвета других
                    var paletteGroupGrouped = new Dictionary <PaletteGroup, int>(paletteGroupCounter, new PaletteGroupComparer());
                    foreach (var palette2 in paletteGroupCounter.Keys)
                    {
                        foreach (var palette1 in paletteGroupCounter.Keys)
                        {
                            if (paletteGroupGrouped.ContainsKey(palette1) && !palette1.Equals(palette2) && palette2.Contains(palette1))
                            {
                                paletteGroupGrouped[palette2] += paletteGroupGrouped[palette1];
                                paletteGroupGrouped.Remove(palette1);
                            }
                        }
                    }

                    // Ну и выбираем наконец-то четыре самые часто используемые палитры

                    palettes = (from palette in paletteGroupGrouped.Keys
                                orderby paletteGroupGrouped[palette] descending
                                select palette).Take(4).ToArray();
                }
                // Загружаем уже готовую палитру
                else
                {
                    var paletteIn = File.ReadAllBytes(args[3].Substring(1));
                    palettes = new PaletteGroup[]
                    {
                        new PaletteGroup(new Color[] { NesPalette[paletteIn[0]], NesPalette[paletteIn[1]], NesPalette[paletteIn[2]], NesPalette[paletteIn[3]] }),
                        new PaletteGroup(new Color[] { NesPalette[paletteIn[4]], NesPalette[paletteIn[5]], NesPalette[paletteIn[6]], NesPalette[paletteIn[7]] }),
                        new PaletteGroup(new Color[] { NesPalette[paletteIn[8]], NesPalette[paletteIn[9]], NesPalette[paletteIn[10]], NesPalette[paletteIn[11]] }),
                        new PaletteGroup(new Color[] { NesPalette[paletteIn[12]], NesPalette[paletteIn[13]], NesPalette[paletteIn[14]], NesPalette[paletteIn[15]] })
                    };
                }

                // Иногда нужно сдвинуть палитру
                int paletteOffset = 0;
                if (args.Length >= 5)
                {
                    paletteOffset = int.Parse(args[4]);
                    var new_palettes = new PaletteGroup[4];
                    Array.Copy(palettes, new_palettes, palettes.Length);
                    palettes = new_palettes;
                    for (int i = 0; i < paletteOffset; i++)
                    {
                        palettes = new PaletteGroup[] { palettes[3], palettes[0], palettes[1], palettes[2] }
                    }
                    ;
                }

                // Сохраним палитры в виде картинки, для отладки
                var paletteImage = new Bitmap(4, 4);
                for (int p = 0; p < palettes.Length; p++)
                {
                    int c = 0;
                    if (palettes[p] != null)
                    {
                        foreach (var color in palettes[p].Colors)
                        {
                            if (color != null)
                            {
                                paletteImage.SetPixel(c, p, color);
                            }
                            c++;
                        }
                    }
                }


                // Палитра
                var paletteRaw = new byte[16];
                paletteRaw[0] = paletteRaw[4] = paletteRaw[8] = paletteRaw[12] =
                    //paletteRaw[16] = paletteRaw[20] = paletteRaw[24] = paletteRaw[28] =
                    (byte)findSimilarColor(NesPalette, palettes[paletteOffset].Colors[0]);
                for (int p = 0; p < palettes.Length; p++)
                {
                    if (palettes[p] != null)
                    {
                        for (int c = 1; c < palettes[p].Colors.Count; c++)
                        {
                            paletteRaw[p * 4 + c] = (byte)findSimilarColor(NesPalette, palettes[p].Colors[c]);
                            // В спрайты скопируем тоже самое
                            //paletteRaw[p * 4 + c + 16] = (byte)findSimilarColor(NesPalette, palettes[p].Colors[c]);
                        }
                    }
                }
                File.WriteAllBytes(args[3] /*@"palette.dat"*/, paletteRaw);

                //paletteImage.Save("paltest.png", ImageFormat.Png);
                if (args[1] == "NUL" && args[2] == "NUL")
                {
                    Console.WriteLine("Palette created.");
                    return(0);
                }

                // Перебираем все тайлы 16*16
                var palleteIndexes = new byte[image.Width / 16, image.Height / 16];
                for (int tileY = 0; tileY < image.Height / 16; tileY++)
                {
                    for (int tileX = 0; tileX < image.Width / 16; tileX++)
                    {
                        long         minDifference    = long.MaxValue;
                        PaletteGroup bestPalette      = null;
                        byte         bestPaletteIndex = 0;
                        // Пробуем каждую палитру
                        for (byte paletteIndex = 0; paletteIndex < palettes.Length; paletteIndex++)
                        {
                            if (palettes[paletteIndex] == null)
                            {
                                continue;
                            }
                            long difference = 0;
                            for (int y = 0; y < 16; y++)      // И применяем к каждому пикселю
                            {
                                for (int x = 0; x < 16; x++)
                                {
                                    var color        = ((Bitmap)image).GetPixel(tileX * 16 + x, tileY * 16 + y);
                                    var similarColor = findSimilarColor(palettes[paletteIndex].Colors, color);
                                    // Вычисляем разницу в цвете с макисмально похожим цветом
                                    var delta = getColorDifference(color, similarColor);
                                    // И суммируем
                                    difference += delta;
                                }
                            }
                            // Ищем палитру, которая встанет с минимумом изменений
                            if (difference < minDifference)
                            {
                                minDifference    = difference;
                                bestPalette      = palettes[paletteIndex];
                                bestPaletteIndex = paletteIndex;
                            }
                        }
                        // Запоминаем номер палитры
                        palleteIndexes[tileX, tileY] = bestPaletteIndex;

                        // В итоге применяем эту палитру к тайлу
                        for (int y = 0; y < 16; y++)
                        {
                            for (int x = 0; x < 16; x++)
                            {
                                var color        = ((Bitmap)image).GetPixel(tileX * 16 + x, tileY * 16 + y);
                                var similarColor = findSimilarColor(bestPalette.Colors, color);
                                ((Bitmap)image).SetPixel(tileX * 16 + x, tileY * 16 + y, similarColor);
                            }
                        }
                    }
                }

                // Осталось составить базу тайлов, теперь уже размером 8 на 8
                var patternTable = new List <PatternTableEntry>();
                var nameTable    = new byte[32 * 30];
                for (int tileY = 0; tileY < image.Height / 8; tileY++)
                {
                    for (int tileX = 0; tileX < image.Width / 8; tileX++)
                    {
                        var tileData = new byte[8, 8];
                        for (int y = 0; y < 8; y++)      // И применяем к каждому пикселю
                        {
                            for (int x = 0; x < 8; x++)
                            {
                                var color      = ((Bitmap)image).GetPixel(tileX * 8 + x, tileY * 8 + y);
                                var palette    = palettes[palleteIndexes[tileX / 2, tileY / 2]];
                                var colorIndex = (byte)palette.Colors.FindIndex(c => c == color);
                                tileData[x, y] = colorIndex;
                            }
                        }
                        // Создаём тайл на основе массива с номерами цветов (палитра при этом не важна)
                        var tile = new PatternTableEntry(tileData);
                        // Добавляем его в список, если его там ещё нет
                        if (!patternTable.Contains(tile))
                        {
                            patternTable.Add(tile);
                        }
                        // Запоминаем номер тайла
                        nameTable[tileX + tileY * 32] = (byte)patternTable.FindIndex(t => t.Equals(tile));
                    }
                }

                //image.Save(@"test1.png", ImageFormat.Png);
                // Всё, осталось полученную информацию как-то сохранить в формате пригодном для NES
                if (patternTable.Count > 256)
                {
                    throw new Exception("Too many tiles: " + patternTable.Count);
                }
                Console.WriteLine("Tiles count: " + patternTable.Count);

                // Сами тайлы
                var patternTableRaw = new byte[0x1000];
                for (int p = 0; p < patternTable.Count; p++)
                {
                    var pixels = patternTable[p].pixels;

                    for (int y = 0; y < 8; y++)
                    {
                        patternTableRaw[p * 16 + y]     = 0;
                        patternTableRaw[p * 16 + y + 8] = 0;
                        for (int x = 0; x < 8; x++)
                        {
                            if ((pixels[x, y] & 1) != 0)
                            {
                                patternTableRaw[p * 16 + y] |= (byte)(1 << (7 - x));
                            }
                            if ((pixels[x, y] & 2) != 0)
                            {
                                patternTableRaw[p * 16 + y + 8] |= (byte)(1 << (7 - x));
                            }
                        }
                    }
                }

                // Ну и nametable
                var nametableRaw = new byte[1024];
                Array.Copy(nameTable, nametableRaw, 30 * 32);
                // В которой ещё attribute table
                for (int tileY = 0; tileY <= image.Height / 32; tileY++)
                {
                    for (int tileX = 0; tileX < image.Width / 32; tileX++)
                    {
                        var topLeft     = palleteIndexes[tileX * 2, tileY * 2];
                        var topRight    = palleteIndexes[tileX * 2 + 1, tileY * 2];
                        var bottomLeft  = tileY < 7 ? palleteIndexes[tileX * 2, tileY * 2 + 1] : 0;
                        var bottomRight = tileY < 7 ? palleteIndexes[tileX * 2 + 1, tileY * 2 + 1] : 0;

                        nametableRaw[0x3C0 + tileY * 8 + tileX] = (byte)
                                                                  (topLeft                // top left
                                                                   | (topRight << 2)      // top right
                                                                   | (bottomLeft << 4)    // bottom left
                                                                   | (bottomRight << 6)); // bottom right
                    }
                }

                //paletteImage.Save(@"palette.png", ImageFormat.Png);
                File.WriteAllBytes(args[1] /*@"pattern0.dat"*/, patternTableRaw);
                File.WriteAllBytes(args[2] /*@"nametable0.dat"*/, nametableRaw);
                Console.WriteLine("Done.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message + ex.StackTrace);
                return(1);
            }
            return(0);
        }