private void MaskExport_Click(object sender, RoutedEventArgs e) { UndertaleSprite sprite = this.DataContext as UndertaleSprite; UndertaleSprite.MaskEntry target = (sender as Button).DataContext as UndertaleSprite.MaskEntry; SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = ".png"; dlg.Filter = "PNG files (.png)|*.png|All files|*"; if (dlg.ShowDialog() == true) { try { BitmapSource source = BitmapSource.Create((int)sprite.Width, (int)sprite.Height, 96, 96, PixelFormats.BlackWhite, null, target.Data, (int)((sprite.Width + 7) / 8)); PngBitmapEncoder encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(source)); using (FileStream stream = new FileStream(dlg.FileName, FileMode.Create)) { encoder.Save(stream); } } catch (Exception ex) { MessageBox.Show("Failed to export file: " + ex.Message, "Failed to export file", MessageBoxButton.OK, MessageBoxImage.Error); } } }
private void MaskImport_Click(object sender, RoutedEventArgs e) { UndertaleSprite sprite = this.DataContext as UndertaleSprite; UndertaleSprite.MaskEntry target = (sender as Button).DataContext as UndertaleSprite.MaskEntry; OpenFileDialog dlg = new OpenFileDialog(); dlg.DefaultExt = ".png"; dlg.Filter = "PNG files (.png)|*.png|All files|*"; if (dlg.ShowDialog() == true) { try { System.Drawing.Image img = System.Drawing.Image.FromFile(dlg.FileName); if ((sprite.Width != (uint)img.Width) || (sprite.Height != (uint)img.Height)) { throw new System.Exception(dlg.FileName + " is not the proper size to be imported! Please correct this before importing! The proper dimensions are width: " + sprite.Width.ToString() + " px, height: " + sprite.Height.ToString() + " px."); } target.Data = TextureWorker.ReadMaskData(dlg.FileName); } catch (Exception ex) { mainWindow.ShowError("Failed to import file: " + ex.Message, "Failed to import file"); } } }
private void ExportAll_Click(object sender, RoutedEventArgs e) { UndertaleSprite sprite = this.DataContext as UndertaleSprite; SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = sprite.Name.Content + ".png"; dlg.DefaultExt = ".png"; dlg.Filter = "PNG files (.png)|*.png|All files|*"; TextureWorker worker = new TextureWorker(); if (dlg.ShowDialog() == true) { try { if (sprite.Textures.Count > 1) { string dir = System.IO.Path.GetDirectoryName(dlg.FileName); string name = System.IO.Path.GetFileNameWithoutExtension(dlg.FileName); string path = System.IO.Path.Combine(dir, name); string ext = System.IO.Path.GetExtension(dlg.FileName); Directory.CreateDirectory(path); foreach (var tex in sprite.Textures.Select((tex, id) => new { id, tex })) { try { worker.ExportAsPNG(tex.tex.Texture, System.IO.Path.Combine(path, tex.id + ext)); } catch (Exception ex) { MessageBox.Show("Failed to export file: " + ex.Message, "Failed to export file", MessageBoxButton.OK, MessageBoxImage.Error); } } } else if (sprite.Textures.Count == 1) { try { worker.ExportAsPNG(sprite.Textures[0].Texture, dlg.FileName); } catch (Exception ex) { MessageBox.Show("Failed to export file: " + ex.Message, "Failed to export file", MessageBoxButton.OK, MessageBoxImage.Error); } } else { MessageBox.Show("No frames to export", "Failed to export sprite", MessageBoxButton.OK, MessageBoxImage.Error); } } catch (Exception ex) { MessageBox.Show("Failed to export: " + ex.Message, "Failed to export sprite", MessageBoxButton.OK, MessageBoxImage.Error); } } worker.Cleanup(); }
private void MaskList_AddingNewItem(object sender, AddingNewItemEventArgs e) { UndertaleSprite.MaskEntry obj = new UndertaleSprite.MaskEntry(); UndertaleSprite sprite = this.DataContext as UndertaleSprite; uint len = (sprite.Width + 7) / 8 * sprite.Height; obj.Data = new byte[len]; e.NewItem = obj; }
public static Bitmap GetCollisionMaskImage(UndertaleSprite sprite, UndertaleSprite.MaskEntry mask) { byte[] maskData = mask.Data; Bitmap bitmap = new Bitmap((int)sprite.Width, (int)sprite.Height, PixelFormat.Format32bppArgb); // Ugh. I want to use 1bpp, but for some BS reason C# doesn't allow SetPixel in that mode. for (int y = 0; y < sprite.Height; y++) { int rowStart = y * (int)((sprite.Width + 7) / 8); for (int x = 0; x < sprite.Width; x++) { byte temp = maskData[rowStart + (x / 8)]; bool pixelBit = (temp & (0b1 << (7 - (x % 8)))) != 0b0; bitmap.SetPixel(x, y, pixelBit ? Color.White : Color.Black); } } return(bitmap); }
private void ExportAllSpine(SaveFileDialog dlg, UndertaleSprite sprite) { mainWindow.ShowWarning("This seems to be a Spine sprite, .json and .atlas files will be exported together with the frames. " + "PLEASE EDIT THEM CAREFULLY! SOME MANUAL EDITING OF THE JSON MAY BE REQUIRED! THE DATA IS EXPORTED AS-IS.", "Spine warning"); if (dlg.ShowDialog() == true) { try { string dir = System.IO.Path.GetDirectoryName(dlg.FileName); string name = System.IO.Path.GetFileNameWithoutExtension(dlg.FileName); string path = System.IO.Path.Combine(dir, name); string ext = System.IO.Path.GetExtension(dlg.FileName); if (sprite.SpineTextures.Count > 0) { Directory.CreateDirectory(path); // textures foreach (var tex in sprite.SpineTextures.Select((tex, id) => new { id, tex })) { try { File.WriteAllBytes(System.IO.Path.Combine(path, tex.id + ext), tex.tex.PNGBlob); } catch (Exception ex) { mainWindow.ShowError("Failed to export file: " + ex.Message, "Failed to export file"); } } // json and atlas File.WriteAllText(System.IO.Path.Combine(path, "spine.json"), sprite.SpineJSON); File.WriteAllText(System.IO.Path.Combine(path, "spine.atlas"), sprite.SpineAtlas); } } catch (Exception ex) { mainWindow.ShowError("Failed to export: " + ex.Message, "Failed to export sprite"); } } }
private void MaskImport_Click(object sender, RoutedEventArgs e) { UndertaleSprite sprite = this.DataContext as UndertaleSprite; UndertaleSprite.MaskEntry target = (sender as Button).DataContext as UndertaleSprite.MaskEntry; OpenFileDialog dlg = new OpenFileDialog(); dlg.DefaultExt = ".png"; dlg.Filter = "PNG files (.png)|*.png|All files|*"; if (dlg.ShowDialog() == true) { try { using (FileStream stream = new FileStream(dlg.FileName, FileMode.Open)) { PngBitmapDecoder decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); BitmapSource source = decoder.Frames[0]; if (source.Format.BitsPerPixel != 1) { throw new Exception("Must be a 1 bit-per-pixel image"); } if (source.PixelWidth != sprite.Width || source.PixelHeight != sprite.Height) { throw new Exception("Mask size doesn't match sprite size"); } int stride = (int)((sprite.Width + 7) / 8); byte[] data = new byte[source.PixelHeight * stride]; source.CopyPixels(data, stride, 0); target.Data = data; } } catch (Exception ex) { MessageBox.Show("Failed to import file: " + ex.Message, "Failed to import file", MessageBoxButton.OK, MessageBoxImage.Error); } } }
private void MaskExport_Click(object sender, RoutedEventArgs e) { UndertaleSprite sprite = this.DataContext as UndertaleSprite; UndertaleSprite.MaskEntry target = (sender as Button).DataContext as UndertaleSprite.MaskEntry; SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = ".png"; dlg.Filter = "PNG files (.png)|*.png|All files|*"; if (dlg.ShowDialog() == true) { try { TextureWorker.ExportCollisionMaskPNG(sprite, target, dlg.FileName); } catch (Exception ex) { MessageBox.Show("Failed to export file: " + ex.Message, "Failed to export file", MessageBoxButton.OK, MessageBoxImage.Error); } } }
public static void ExportCollisionMaskPNG(UndertaleSprite sprite, UndertaleSprite.MaskEntry mask, string fullPath) { SaveImageToFile(fullPath, GetCollisionMaskImage(sprite, mask)); }
public static void SpeedrunPatches(UndertaleData data) { UndertaleScript setboi = new UndertaleScript(); setboi.Name = data.Strings.MakeString("set_speedrun_category"); setboi.Code = new UndertaleCode(); setboi.Code.Name = data.Strings.MakeString("gml_Script_set_speedrun_category"); setboi.Code.ReplaceGML(RabbitRunCode.set_speedrun_category, data); data.Code.Add(setboi.Code); data.Scripts.Add(setboi); UndertaleScript sprun = new UndertaleScript(); sprun.Name = new UndertaleString("menu_speedrun_script"); data.Strings.Add(sprun.Name); sprun.Code = new UndertaleCode(); data.Code.Add(sprun.Code); sprun.Code.Name = new UndertaleString("gml_Script_menu_speedrun_script"); data.Strings.Add(sprun.Code.Name); sprun.Code.ReplaceGML(RabbitRunCode.menu_speedrun_script, data); sprun.Code.UpdateAddresses(); data.Scripts.Add(sprun); UndertaleCode ae = data.Code.ByName("gml_Script_setfile"); ae.AppendGML(RabbitRunCode.gml_Script_setfile, data); ae.UpdateAddresses(); UndertaleCode ee = data.Code.ByName("gml_Object_obj_mainmenus_Create_0"); ReplaceInGML("GAME\", 1, 8],", "GAME\", 1, 8], [\"SPEEDBUN\", 1, 19], ", ee, data); ee.UpdateAddresses(); ReplaceInGML("i = 0", RabbitRunCode.speedrunMenuInit, ee, data); UndertaleCode ie = data.Code.ByName("gml_Script_cKeys_beginstep"); ie.AppendGML(RabbitRunCode.tasBeginStepInput, data); UndertaleCode oe = data.Code.ByName("gml_Object_obj_init_Create_0"); oe.AppendGML(@"global.playRun = false; global.watchRun = false; global.speedrunning = true; global.inrun = false; global.onehun = false;//one hundred percent global.allbun = false;//all cuties global.anyper = false;//any percent", data); UndertaleCode ue = data.Code.ByName("gml_Script_SaveStringToFile"); ue.ReplaceGML(RabbitRunCode.saveStringFile, data); UndertaleSprite mico = data.Sprites.ByName("spr_menuicons"); UndertaleSprite.TextureEntry te = new UndertaleSprite.TextureEntry(); UndertaleTexturePageItem ti = mico.Textures[1].Texture; UndertaleTexturePageItem to = data.Sprites.ByName("spr_antibunidle").Textures[0].Texture; te.Texture = new UndertaleTexturePageItem(); te.Texture.TargetX = ti.TargetX; te.Texture.TargetY = ti.TargetY; te.Texture.SourceX = to.SourceX; te.Texture.SourceY = to.SourceY; te.Texture.BoundingHeight = ti.BoundingHeight; te.Texture.BoundingWidth = ti.BoundingWidth; te.Texture.SourceWidth = 16; te.Texture.TargetWidth = 16; te.Texture.SourceHeight = 15; te.Texture.TargetHeight = 15; te.Texture.TexturePage = to.TexturePage; data.TexturePageItems.Add(te.Texture); mico.Textures.Insert(2, te); }