Example #1
0
        /// <summary>
        /// Automatically generates a TSA-compliant image from a 256(or fewer)-color image
        /// </summary>
        /// <param name="width">Width of the TSA (in tiles)</param>
        /// <param name="height">Height of the TSA (in tiles)</param>
        /// <param name="image">The bitmap to convert</param>
        /// <param name="paletteAmount">the maximum amount of 16-color palettes</param>
        /// <param name="checkRedundantTiles">whether or not to add all tiles found to the tileset</param>
        public TSA_Image(
            int width, int height,
            GBA.Bitmap image,
            int paletteAmount,
            bool checkRedundantTiles)
        {
            Width  = width * 8;
            Height = height * 8;

            if (image.Width != Width || image.Height != Height)
            {
                throw new Exception("Image given has invalid dimensions.\n" +
                                    "It must be " + Width + "x" + Height + " pixels.");
            }

            if (image.Colors.Count <= 16)
            {   // No need to run TSA-ifier code
                Palettes = Palette.Split(image.Colors, paletteAmount);

                Graphics = new Tileset(new Image(image));

                Tiling = TSA_Array.GetBasicTSA(width, height);
            }
            else
            {
                using (FormLoading loading = new FormLoading())
                { // Run the image TSA-ifier
                    loading.Show();

                    int    tileAmount  = width * height;
                    byte[] bytes       = image.ToBytes();
                    int[]  colorTotals = new int[image.Colors.Count];
                    int[,] colorAmounts = new int[image.Colors.Count, tileAmount];

                    loading.SetLoading("Checking colors...", 2);

                    int index;
                    int tile = 0;
                    for (int tileY = 0; tileY < height; tileY++)
                    {
                        for (int tileX = 0; tileX < width; tileX++)
                        {
                            index = tileX * 8 + tileY * 8 * Width;
                            for (int y = 0; y < 8; y++)
                            {
                                for (int x = 0; x < 8; x++)
                                {
                                    colorTotals[bytes[index]]++;
                                    colorAmounts[bytes[index], tile]++;
                                }
                                index += Width - 8;
                            }
                            tile++;
                        } // first we take a look at which colors are most present (in all, and per tile)
                    }
                    loading.SetPercent(7);

                    List <int> colors = new List <int>();
                    for (int i = 0; i < colorTotals.Length; i++)
                    {
                        index = 0;
                        while (index < colors.Count && colorTotals[i] > colors[index])
                        {
                            index++;
                        }

                        if (index < colors.Count)
                        {
                            colors.Insert(index, i);
                        }
                        else
                        {
                            colors.Add(i);
                        }

                        if (colors.Count > paletteAmount * 15)
                        {
                            colors.RemoveAt(0);
                        }
                    } // then we create a list of indices to colors with only the most used colors

                    loading.SetPercent(9);

                    Palettes = new Palette[paletteAmount];
                    for (int i = 0; i < paletteAmount; i++)
                    {
                        Palettes[i] = new GBA.Palette(16);
                        Palettes[i].Add(new GBA.Color(0x0000));
                    } // we create our palettes, forcing the 1st color to be black on each palette

                    loading.SetLoading("Asserting tile palettes...", 10);

                    byte[]    tilePalettes = new byte[tileAmount];
                    int[]     certainty    = new int[tileAmount];
                    GBA.Color color;
                    int       amount;
                    for (int i = colors.Count - 1; i >= 0; i--)
                    {
                        color = image.Colors[colors[i]];
                        tile  = 0;

                        while (tile < tilePalettes.Length)
                        {
                            amount = colorAmounts[colors[i], tile];

                            if (Palettes[tilePalettes[tile]].Contains(color))
                            {
                                certainty[tile] += amount;
                            }
                            else
                            {
                                if (Palettes[tilePalettes[tile]].IsFull)
                                {
                                    if (certainty[tile] < amount)
                                    {
                                        tilePalettes[tile]++;
                                        tilePalettes[tile] %= (byte)Palettes.Length;
                                        certainty[tile]     = 0;
                                        i++; break;
                                    }
                                    else
                                    {
                                        certainty[tile] -= amount;
                                    }
                                }
                                else if (amount != 0)
                                {
                                    Palettes[tilePalettes[tile]].Add(color);
                                    certainty[tile] += amount;
                                }
                            }
                            tile++;
                        }
                        loading.SetPercent(10 + (50 - 50 * ((float)i / (float)(colors.Count))));
                    } // and we do a loop going from  most used color to least used, setting tilePalettes and filling said palettes

                    amount = 0;
                    for (int p = 0; p < Palettes.Length; p++)
                    {
                        Palettes[p].Sort(delegate(GBA.Color first, GBA.Color second)
                        {
                            return((first.GetValueR() + first.GetValueG() + first.GetValueB())
                                   - (second.GetValueR() + second.GetValueG() + second.GetValueB()));
                        });
                        for (int i = Palettes[p].Count; i < 16; i++)
                        {
                            Palettes[p].Add(new Color(0x0000));
                        }
                    }

                    loading.SetMessage("Creating TSA information...");

                    Graphics = new Tileset(width * height);
                    Tiling   = new TSA_Array(width, height);
                    int pixel;
                    tile = 0;
                    byte HI_nibble;
                    byte LO_nibble;
                    Tile currentTile;
                    Tuple <int, bool, bool> current;
                    for (int tileY = 0; tileY < height; tileY++)
                    {
                        for (int tileX = 0; tileX < width; tileX++)
                        {
                            index = (Palettes[tilePalettes[tile]].IsFull) ? tilePalettes[tile] : 0;
                            bytes = new byte[GBA.Tile.LENGTH];

                            for (int y = 0; y < 8; y++)
                            {
                                for (int x = 0; x < 4; x++)
                                {
                                    color     = image.GetColor(tileX * 8 + x * 2, tileY * 8 + y);
                                    pixel     = GBA.Color.GetNearest(Palettes[index], color);
                                    LO_nibble = (pixel == -1) ? (byte)0x00 : (byte)(pixel);

                                    color     = image.GetColor(tileX * 8 + x * 2 + 1, tileY * 8 + y);
                                    pixel     = GBA.Color.GetNearest(Palettes[index], color);
                                    HI_nibble = (pixel == -1) ? (byte)0x00 : (byte)(pixel);

                                    bytes[x + y * 4] = (byte)((HI_nibble << 4) | LO_nibble);
                                }
                            }

                            currentTile = new Tile(bytes);

                            if (checkRedundantTiles)
                            {
                                if (currentTile.IsEmpty())
                                {
                                    current = Tuple.Create(0, false, false);
                                }
                                else
                                {
                                    current = Graphics.FindMatch(currentTile);

                                    if (current == null)
                                    {
                                        current = Tuple.Create(Graphics.Count, false, false);
                                        Graphics.Add(currentTile);
                                    }
                                }
                            }
                            else
                            {
                                current = Tuple.Create(Graphics.Count, false, false);
                                Graphics.Add(currentTile);
                            }
                            // try {
                            Tiling[tileX, tileY] = new TSA(
                                (UInt16)current.Item1,
                                tilePalettes[tile],
                                current.Item2,
                                current.Item3);
                            // } catch { }

                            loading.SetPercent(60 + 40 * ((float)tile / (float)tileAmount));
                            tile++;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Processes a battle animation from a txt file, reading all referenced image files
        /// </summary>
        public BattleAnimMaker(string folder, string[] file)
        {
            FormLoading loading = new FormLoading();

            loading.SetMessage("Processing animation...");
            loading.Show();

            AnimCode = new List <string> [12];
            Graphics = new List <TileSheet>();
            for (int i = 0; i < AnimCode.Length; i++)
            {
                AnimCode[i] = new List <string>();
            }
            AddTileSheet();

            Palettes = Palette.Split(GetPaletteFromFile(folder), 4);



            Frames    = new List <OAM_Array>();
            FrameData = new List <Tuple <uint, uint> >();
            List <string> filenames = new List <string>();
            List <Tuple <string, Point, Size> > affines = new List <Tuple <string, Point, Size> >();
            CompileMode compile   = CompileMode.Usual;
            bool        new_frame = true;
            int         mode      = 0; // the current animation mode being processed
            int         frame     = 0; // the current frame number
            int         affine    = 0; // the current affine sprite number
            int         duration  = 0; // the current frame's duration

            decimal[] arguments;
            for (int line = 0; line < file.Length; line++)
            {
                loading.SetPercent(100 * ((float)line / (float)file.Length));
                loading.SetMessage("Processing Anim Mode" +
                                   ((mode == 0 || mode == 2 || mode == 8) ?
                                    "s " + (mode + 1) + " and " + (mode + 2) :
                                    " " + (mode + 1)) + "...");

                if (file[line] == null)
                {
                    continue;
                }
                else if (file[line] == "")
                {
                    continue;
                }
                else //try
                {
                    for (int i = 0; i < file[line].Length; i++)
                    {
                        // Comments
                        if (file[line][i] == '#')
                        {
                            break;
                        }
                        if (file[line][i] == '/' && file[line][i + 1] == '/')
                        {
                            break;
                        }

                        // Syntax
                        switch (file[line][i])
                        {
                        case ' ': continue;

                        case '\t': continue;

                        case 'c':
                        case 'C':
                            if (compile == CompileMode.Extra)
                            {
                                compile = CompileMode.Usual;
                            }
                            if (compile == CompileMode.Usual)
                            {
                                AnimCode[mode].Add("c" + file[line].Substring(i + 1, 2));
                                if (mode == 0 || mode == 2)
                                {
                                    AnimCode[mode + 1].Add("c" + file[line].Substring(i + 1, 2));
                                }
                                i += 2;
                                continue;
                            }
                            else
                            {
                                throw new Exception("Unexpected 'c' command read.");
                            }

                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9':
                            if (compile == CompileMode.Extra)
                            {
                                compile = CompileMode.Usual;
                            }
                            if (compile == CompileMode.Usual)
                            {
                                int length = 0;
                                while (i + length < file[line].Length)
                                {
                                    if (file[line][i + length] == ' ' ||
                                        file[line][i + length] == '\t')
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        length++;
                                    }
                                }
                                try
                                {
                                    duration = int.Parse(file[line].Substring(i, length));
                                }
                                catch
                                {
                                    throw new Exception("Invalid duration number read:" + file[line].Substring(i, length));
                                }
                                compile = CompileMode.Frame;
                                i      += (length - 1);
                                continue;
                            }
                            else
                            {
                                throw new Exception("Unexpected frame duration number read.");
                            }

                        case 'f':
                        case 'F':
                        case 'b':
                        case 'B':
                            bool bg = (file[line][i] == 'b' || file[line][i] == 'B');
                            if ((bg && compile == CompileMode.Extra) || compile == CompileMode.Frame)
                            {
                                i++;
                                string filename = ReadArgument_FileName(ref file, ref line, ref i);
                                frame     = -1;
                                new_frame = true;
                                for (int f = 0; f < filenames.Count; f++)
                                {
                                    if (filename.Equals(filenames[f]))
                                    {
                                        frame     = f;
                                        new_frame = false;
                                        break;
                                    }
                                }
                                if (frame == -1)
                                {
                                    frame = filenames.Count;
                                    filenames.Add(filename);
                                    AddFrame(bg, new GBA.Image(folder + filename, Palettes[0]), filename);
                                }       // add the new frame's OAM if it hasn't yet been used

                                if (bg)
                                {
                                    if (mode == 0 || mode == 2 || mode == 8)
                                    {
                                        AnimCode[mode + 1][AnimCode[mode + 1].Count - 1] = duration + " f" + Util.ByteToHex((byte)frame);
                                    }
                                    else
                                    {
                                        throw new Exception("'b' background layer frame commands can only be used in animation modes 1, 3 and 9.");
                                    }
                                }
                                else
                                {
                                    AnimCode[mode].Add(duration + " f" + Util.ByteToHex((byte)frame));
                                    if (mode == 0 || mode == 2 || mode == 8)
                                    {
                                        AnimCode[mode + 1].Add(duration + " fFF");
                                    }
                                }

                                compile = CompileMode.Extra;
                                continue;
                            }
                            else
                            {
                                throw new Exception("Unexpected '" + (bg ? 'b' : 'f') + "' frame command read.");
                            }

                        case 'a':
                        case 'A':
                        case 'd':
                        case 'D':
                            bool big = (file[line][i] == 'd' || file[line][i] == 'D');
                            if (compile == CompileMode.Extra)
                            {
                                i++;
                                string affinefile = ReadArgument_FileName(ref file, ref line, ref i);
                                i++;
                                arguments = ReadArgument_Numbers(ref file, ref line, ref i);
                                if (arguments.Length != 2)
                                {
                                    throw new Exception(
                                              "Expected affine sprite X and Y screen coordinates.");
                                }
                                Point coords = new Point((int)arguments[0], (int)arguments[1]);

                                arguments = ReadArgument_Numbers(ref file, ref line, ref i);
                                if (arguments.Length != 1 && arguments.Length != 4)
                                {
                                    throw new Exception(
                                              "Expected affine sprite (angle) argument, or (Ux, Vx, Uy, and Vy) vector arguments.");
                                }
                                float[] vectors;
                                if (arguments.Length == 1)     // convert angle into (Ux Vx Uy Vy)
                                {
                                    float cos = (float)Math.Cos((double)arguments[0]);
                                    float sin = (float)Math.Sin((double)arguments[1]);
                                    vectors = new float[4] {
                                        cos, sin, -sin, cos
                                    };
                                }
                                else
                                {
                                    vectors = new float[4]
                                    {
                                        (float)arguments[0],
                                        (float)arguments[1],
                                        (float)arguments[2],
                                        (float)arguments[3]
                                    }
                                };
                                if (new_frame == false)
                                {       // if there's an affine on a preexisting frame, duplicate the OAM data
                                    if (AddDuplicateFrameWithAffines(mode, ref frame, ref filenames, coords, vectors))
                                    {   // if it returned true, that means an identical frame with identical affine exists
                                        continue;
                                    }
                                }
                                affine = -1;
                                for (int a = 0; a < affines.Count; a++)
                                {
                                    if (affinefile.Equals(affines[a].Item1))
                                    {
                                        affine = a;
                                        break;
                                    }
                                }
                                if (affine == -1)
                                {
                                    affine = affines.Count;
                                    Tuple <Point, Size> sheet = AddAffineToTilesheet(
                                        frame, new GBA.Image(folder + affinefile, Palettes[0]));
                                    affines.Add(Tuple.Create(affinefile, sheet.Item1, sheet.Item2));
                                }
                                AddAffine(big, frame, coords, vectors,
                                          affines[affine].Item2, affines[affine].Item3);
                                continue;
                            }
                            else
                            {
                                throw new Exception("Unexpected '" + (big ? 'd' : 'a') + "' affine command read.");
                            }

                        case 'e':
                        case 'E':
                            if (compile == CompileMode.Extra)
                            {
                                compile = CompileMode.Usual;
                            }
                            if (compile == CompileMode.Usual)
                            {
                                if ((file[line][i + 1] == 'n' || file[line][i + 1] == 'N') &&
                                    (file[line][i + 2] == 'd' || file[line][i + 2] == 'D'))
                                {
                                    AnimCode[mode].Add("end");
                                    if (mode == 0 || mode == 2 || mode == 8)
                                    {
                                        AnimCode[mode + 1].Add("end");
                                        mode += 2;
                                    }
                                    else
                                    {
                                        mode++;
                                    }
                                    i += 2;
                                    continue;
                                }
                                else
                                {
                                    throw new Exception("Invalid terminator read:" + file[line].Substring(i, 3));
                                }
                            }
                            else
                            {
                                throw new Exception("Unexpected terminator read.");
                            }

                        default: throw new Exception("Unexpected character: " + file[line][i]);
                        }
                    }
                }/*
                  * catch (Exception ex)
                  * {
                  * throw new Exception("At line " + line + ":\r\n'" + file[line] + "'\r\n" + ex.Message);
                  * }*/
            }
            uint emptyFrame = (uint)(Frames[0].Sprites.Count * OAM.LENGTH);

            // this uint is just the offset to the 1st terminator, so it produces an empty frame
            while (FrameData.Count < 256)
            {
                FrameData.Add(Tuple.Create((uint)0x00000000, emptyFrame));
            }   // fill framedata with empty frames so 'fFF' commands or such are proeperly compiled
            int oam_total = 0;

            for (int i = 0; i < Frames.Count; i++)
            {
                oam_total +=
                    Frames[i].Affines.Count * OAM.LENGTH +
                    Frames[i].Sprites.Count * OAM.LENGTH +
                    OAM.LENGTH;
            }
            if (oam_total > BattleAnimation.MAXIMUM_OAM_LENGTH)
            {
                Program.ShowWarning("The final OAM block is too large: " + oam_total +
                                    "\nIt should weigh " + BattleAnimation.MAXIMUM_OAM_LENGTH + " bytes or less.");
            }
        }