private void File_SaveScript_Click(object sender, EventArgs e) { using (SaveFileDialog saveWindow = new SaveFileDialog()) { saveWindow.Filter = "Text file (*.txt)|*.txt|All files (*.*)|*.*"; saveWindow.FilterIndex = 1; saveWindow.RestoreDirectory = true; saveWindow.CreatePrompt = true; saveWindow.OverwritePrompt = true; if (saveWindow.ShowDialog() == DialogResult.OK) { using (FormLoading loading = new FormLoading()) { loading.Show(); loading.SetLoading("Dumping script...", 0); UInt16 entry = 0; string file = ""; while (++entry < Int16.MaxValue) { loading.SetPercent(100 * ((float)entry / (float)0x5000)); try { file += Core_GetText(entry) + "\r\n"; } catch { break; } } File.WriteAllText(saveWindow.FileName, file); } } } }
private void File_SaveFolder_Click(object sender, EventArgs e) { using (var folderWindow = new FolderBrowserDialog()) { folderWindow.ShowNewFolderButton = true; if (folderWindow.ShowDialog() == DialogResult.OK && !string.IsNullOrWhiteSpace(folderWindow.SelectedPath)) { using (FormLoading loading = new FormLoading()) { loading.Show(); loading.SetLoading("Dumping script...", 0); int amount = 0; UInt16 entry = 0; string name; while (++entry < Int16.MaxValue) { loading.SetPercent(30 + 70 * ((float)entry / (float)Int16.MaxValue)); try { name = folderWindow.SelectedPath + "\\0x" + Util.UInt16ToHex((UInt16)entry) + ".txt"; File.WriteAllText(name, Core_GetText(entry)); amount++; } catch { break; } } Program.ShowMessage(amount + " text entry files have been created."); } } } }
/// <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."); } }