diff --git a/UndertaleModCli/Program.cs b/UndertaleModCli/Program.cs
index 3becbb15d..2b05d822f 100644
--- a/UndertaleModCli/Program.cs
+++ b/UndertaleModCli/Program.cs
@@ -194,7 +194,8 @@ public Program(FileInfo datafile, FileInfo[] scripts, FileInfo output, bool verb
GetType().GetTypeInfo().Assembly,
typeof(JsonConvert).GetTypeInfo().Assembly,
typeof(System.Text.RegularExpressions.Regex).GetTypeInfo().Assembly,
- typeof(TextureWorker).GetTypeInfo().Assembly)
+ typeof(TextureWorker).GetTypeInfo().Assembly,
+ typeof(ImageMagick.MagickImage).GetTypeInfo().Assembly)
// "WithEmitDebugInformation(true)" not only lets us to see a script line number which threw an exception,
// but also provides other useful debug info when we run UMT in "Debug".
.WithEmitDebugInformation(true);
diff --git a/UndertaleModLib/Util/GMImage.cs b/UndertaleModLib/Util/GMImage.cs
index eef1eb7a7..3ce06e930 100644
--- a/UndertaleModLib/Util/GMImage.cs
+++ b/UndertaleModLib/Util/GMImage.cs
@@ -849,12 +849,12 @@ public MagickImage GetMagickImage()
}
///
- /// Creates a new raw format with the contents of the provided .
+ /// Creates a new raw format with the contents of the provided .
///
///
- /// This modifies the image format of the provided to avoid unnecessary copies.
+ /// This modifies the image format of the provided to avoid unnecessary copies.
///
- public static GMImage FromMagickImage(MagickImage image)
+ public static GMImage FromMagickImage(IMagickImage image)
{
image.Format = MagickFormat.Bgra;
image.SetCompression(CompressionMethod.NoCompression);
diff --git a/UndertaleModLib/Util/TextureWorker.cs b/UndertaleModLib/Util/TextureWorker.cs
index 7c387b5fa..30c712c4e 100644
--- a/UndertaleModLib/Util/TextureWorker.cs
+++ b/UndertaleModLib/Util/TextureWorker.cs
@@ -92,7 +92,7 @@ public IMagickImage GetTextureFor(UndertaleTexturePageItem texPageItem, st
IMagickImage returnImage = croppedImage;
if (includePadding)
{
- returnImage = new MagickImage(MagickColor.FromRgba(0, 0, 0, 0), exportWidth, exportHeight);
+ returnImage = new MagickImage(MagickColors.Transparent, exportWidth, exportHeight);
returnImage.Composite(croppedImage, texPageItem.TargetX, texPageItem.TargetY, CompositeOperator.Copy);
croppedImage.Dispose();
}
@@ -122,13 +122,14 @@ public static MagickImage ReadBGRAImageFromFile(string filePath)
}
///
- /// Performs a resize of the given image, if required, using bilinear interpolation. Always returns a new image.
+ /// Performs a resize of the given image, if required, using the specified interpolation (bilinear by default). Always returns a new image.
///
/// Image to be resized (without being modified).
/// Desired width to resize to.
/// Desired height to resize to.
+ /// Pixel interpolation method to use, or specify none to use bilinear interpolation.
/// A copy of the provided image, which is resized to the given dimensions when required.
- public static IMagickImage ResizeImage(IMagickImage image, int width, int height)
+ public static IMagickImage ResizeImage(IMagickImage image, int width, int height, PixelInterpolateMethod interpolateMethod = PixelInterpolateMethod.Bilinear)
{
// Clone image
IMagickImage newImage = image.Clone();
@@ -140,7 +141,7 @@ public static IMagickImage ResizeImage(IMagickImage image, int width
}
// Resize using bilinear interpolation
- newImage.InterpolativeResize(width, height, PixelInterpolateMethod.Bilinear);
+ newImage.InterpolativeResize(width, height, interpolateMethod);
return newImage;
}
diff --git a/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs
index b6817b0cc..bd2ec647a 100644
--- a/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs
+++ b/UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs
@@ -74,35 +74,43 @@ private void SwitchDataContext(object sender, DependencyPropertyChangedEventArgs
private void ReloadTexturePage(object sender, PropertyChangedEventArgs e)
{
- UndertaleTexturePageItem item = (DataContext as UndertaleTexturePageItem);
- if (item is null)
- return;
+ // Invoke dispatcher to only perform updates on UI thread
+ Dispatcher.Invoke(() =>
+ {
+ UndertaleTexturePageItem item = (DataContext as UndertaleTexturePageItem);
+ if (item is null)
+ return;
- if (e.PropertyName != nameof(UndertaleTexturePageItem.TexturePage))
- return;
+ if (e.PropertyName != nameof(UndertaleTexturePageItem.TexturePage))
+ return;
- UpdateImages(item);
+ UpdateImages(item);
- // Start listening for (new) texture image updates
- if (_textureDataContext is not null)
- {
- _textureDataContext.PropertyChanged -= ReloadTextureImage;
- }
- _textureDataContext = item.TexturePage.TextureData;
- _textureDataContext.PropertyChanged += ReloadTextureImage;
+ // Start listening for (new) texture image updates
+ if (_textureDataContext is not null)
+ {
+ _textureDataContext.PropertyChanged -= ReloadTextureImage;
+ }
+ _textureDataContext = item.TexturePage.TextureData;
+ _textureDataContext.PropertyChanged += ReloadTextureImage;
+ });
}
private void ReloadTextureImage(object sender, PropertyChangedEventArgs e)
{
- UndertaleTexturePageItem item = (DataContext as UndertaleTexturePageItem);
- if (item is null)
- return;
+ // Invoke dispatcher to only perform updates on UI thread
+ Dispatcher.Invoke(() =>
+ {
+ UndertaleTexturePageItem item = (DataContext as UndertaleTexturePageItem);
+ if (item is null)
+ return;
- if (e.PropertyName != nameof(UndertaleEmbeddedTexture.TexData.Image))
- return;
+ if (e.PropertyName != nameof(UndertaleEmbeddedTexture.TexData.Image))
+ return;
- // If the texture's image was updated, reload it
- UpdateImages(item);
+ // If the texture's image was updated, reload it
+ UpdateImages(item);
+ });
}
private void UnloadTexture(object sender, RoutedEventArgs e)
diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs
index b0751c93a..603544d4a 100644
--- a/UndertaleModTool/MainWindow.xaml.cs
+++ b/UndertaleModTool/MainWindow.xaml.cs
@@ -248,7 +248,8 @@ public MainWindow()
.AddReferences(typeof(UndertaleObject).GetTypeInfo().Assembly,
GetType().GetTypeInfo().Assembly,
typeof(JsonConvert).GetTypeInfo().Assembly,
- typeof(System.Text.RegularExpressions.Regex).GetTypeInfo().Assembly)
+ typeof(System.Text.RegularExpressions.Regex).GetTypeInfo().Assembly,
+ typeof(ImageMagick.MagickImage).GetTypeInfo().Assembly)
.WithEmitDebugInformation(true); //when script throws an exception, add a exception location (line number)
});
diff --git a/UndertaleModTool/Scripts/Builtin Scripts/BorderEnabler.csx b/UndertaleModTool/Scripts/Builtin Scripts/BorderEnabler.csx
index 4ca1a10cc..5edcfcd88 100644
--- a/UndertaleModTool/Scripts/Builtin Scripts/BorderEnabler.csx
+++ b/UndertaleModTool/Scripts/Builtin Scripts/BorderEnabler.csx
@@ -1,5 +1,7 @@
// Imports and unlocks border images into PC version of the game
+using UndertaleModLib.Util;
+
EnsureDataLoaded();
if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1 & 2")
@@ -20,37 +22,37 @@ else if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter
ScriptMessage("Border enabler (1080p edition)\nby krzys_h");
// Change os_type == 14 checks in scr_draw_screen_border to always pass
-ReplaceTextInGML(("gml_Script_scr_draw_screen_border"), @"os_type == os_psvita", "0", true);
-ReplaceTextInGML(("gml_Script_scr_draw_screen_border"), @"os_type == os_ps4", "1", true);
+ReplaceTextInGML("gml_Script_scr_draw_screen_border", @"os_type == os_psvita", "0", true);
+ReplaceTextInGML("gml_Script_scr_draw_screen_border", @"os_type == os_ps4", "1", true);
// Same for the code that calls it
-ReplaceTextInGML(("gml_Object_obj_time_Draw_77"), @"global.osflavor >= 3", "1", true);
+ReplaceTextInGML("gml_Object_obj_time_Draw_77", @"global.osflavor >= 3", "1", true);
//Remove checks from obj_time creation event
-ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"os_type == os_psvita", "0", true);
-ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"os_type == os_ps4", "1", true);
-ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"global.osflavor >= 4", "1", true);
-ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"global.osflavor >= 3", "1", true);
+ReplaceTextInGML("gml_Object_obj_time_Create_0", @"os_type == os_psvita", "0", true);
+ReplaceTextInGML("gml_Object_obj_time_Create_0", @"os_type == os_ps4", "1", true);
+ReplaceTextInGML("gml_Object_obj_time_Create_0", @"global.osflavor >= 4", "1", true);
+ReplaceTextInGML("gml_Object_obj_time_Create_0", @"global.osflavor >= 3", "1", true);
//Now patch out the check for the window scale, make it always be true
-ReplaceTextInGML(("gml_Object_obj_time_Draw_76"), @"global.osflavor >= 4", "1", true);
-ReplaceTextInGML(("gml_Object_obj_time_Draw_76"), @"os_type == os_switch_beta", "1", true);
+ReplaceTextInGML("gml_Object_obj_time_Draw_76", @"global.osflavor >= 4", "1", true);
+ReplaceTextInGML("gml_Object_obj_time_Draw_76", @"os_type == os_switch_beta", "1", true);
//Attempt border display fix in gml_Object_obj_time_Draw_76
//Patch out the OS checks for gml_Script_scr_draw_background_ps4, make PS Vita always false, and PS4 always true, simplifying code.
-ReplaceTextInGML(("gml_Script_scr_draw_background_ps4"), @"os_type == os_psvita", "0", true);
-ReplaceTextInGML(("gml_Script_scr_draw_background_ps4"), @"os_type == os_ps4", "1", true);
+ReplaceTextInGML("gml_Script_scr_draw_background_ps4", @"os_type == os_psvita", "0", true);
+ReplaceTextInGML("gml_Script_scr_draw_background_ps4", @"os_type == os_ps4", "1", true);
// Now, patch the settings menu!
-ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"obj_time.j_ch > 0", "0", true);
-ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"global.osflavor <= 2", "0", true);
-ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"global.osflavor >= 4", "1", true);
+ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"obj_time.j_ch > 0", "0", true);
+ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"global.osflavor <= 2", "0", true);
+ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"global.osflavor >= 4", "1", true);
//Remove code not applicable (PS Vita, Windows, <=2) and make some code always true (global.osflavor >= 4)
-ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"os_type == os_psvita", "0", true);
-ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"global.osflavor <= 2", "0", true);
-ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"global.osflavor == 1", "0", true);
-ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"global.osflavor >= 4", "1", true);
+ReplaceTextInGML("gml_Object_obj_time_Step_1", @"os_type == os_psvita", "0", true);
+ReplaceTextInGML("gml_Object_obj_time_Step_1", @"global.osflavor <= 2", "0", true);
+ReplaceTextInGML("gml_Object_obj_time_Step_1", @"global.osflavor == 1", "0", true);
+ReplaceTextInGML("gml_Object_obj_time_Step_1", @"global.osflavor >= 4", "1", true);
// Also resize the window so that the border can be seen without going fullscreen
Data.Functions.EnsureDefined("window_set_size", Data.Strings);
@@ -72,21 +74,23 @@ int lastTextPageItem = Data.TexturePageItems.Count - 1;
foreach (var path in Directory.EnumerateFiles(bordersPath))
{
UndertaleEmbeddedTexture newtex = new UndertaleEmbeddedTexture();
- newtex.Name = new UndertaleString("Texture " + ++lastTextPage);
- newtex.TextureData.TextureBlob = File.ReadAllBytes(path);
+ newtex.Name = new UndertaleString($"Texture {++lastTextPage}");
+ newtex.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(path)); // Possibly other formats than PNG in the future, but no Undertale versions currently have them
Data.EmbeddedTextures.Add(newtex);
textures.Add(Path.GetFileName(path), newtex);
}
// Create texture fragments and assign them to existing (but empty) backgrounds
-Action AssignBorderBackground = (name, tex, x, y, width, height) => {
+Action AssignBorderBackground = (name, tex, x, y, width, height) =>
+{
var bg = Data.Backgrounds.ByName(name);
- if (bg == null) {
+ if (bg is null)
+ {
// The anime border does not exist on PC yet ;)
return;
}
UndertaleTexturePageItem tpag = new UndertaleTexturePageItem();
- tpag.Name = new UndertaleString("PageItem " + ++lastTextPageItem);
+ tpag.Name = new UndertaleString($"PageItem {++lastTextPageItem}");
tpag.SourceX = x; tpag.SourceY = y; tpag.SourceWidth = width; tpag.SourceHeight = height;
tpag.TargetX = 0; tpag.TargetY = 0; tpag.TargetWidth = width; tpag.TargetHeight = height;
tpag.BoundingWidth = width; tpag.BoundingHeight = height;
diff --git a/UndertaleModTool/Scripts/Builtin Scripts/UndertaleDialogSimulator.csx b/UndertaleModTool/Scripts/Builtin Scripts/UndertaleDialogSimulator.csx
index cdf8b698b..b03e186da 100644
--- a/UndertaleModTool/Scripts/Builtin Scripts/UndertaleDialogSimulator.csx
+++ b/UndertaleModTool/Scripts/Builtin Scripts/UndertaleDialogSimulator.csx
@@ -3,7 +3,6 @@
using System.IO;
using System;
-using System.Drawing;
using System.Windows.Forms;
using UndertaleModLib.Util;
@@ -25,10 +24,10 @@ else if (GameName == "deltarune chapter 1&2")
}
if (Data.GeneralInfo.Name.Content == "NXTALE" || Data.GeneralInfo.Name.Content.StartsWith("UNDERTALE"))
{
- if (!ScriptQuestion("Would you like to apply this mod?"))
- {
- return;
- }
+ if (!ScriptQuestion("Would you like to apply this mod?"))
+ {
+ return;
+ }
}
else if (Data.GeneralInfo.DisplayName.Content == "SURVEY_PROGRAM" || Data.GeneralInfo.DisplayName.Content == "DELTARUNE Chapter 1")
{
diff --git a/UndertaleModTool/Scripts/Community Scripts/BorderEnablerv1_11.csx b/UndertaleModTool/Scripts/Community Scripts/BorderEnablerv1_11.csx
index 2a2ee9136..d8cc70f21 100644
--- a/UndertaleModTool/Scripts/Community Scripts/BorderEnablerv1_11.csx
+++ b/UndertaleModTool/Scripts/Community Scripts/BorderEnablerv1_11.csx
@@ -24,25 +24,25 @@ by Jockeholm, based off krzys_h's original script.
Converted to be more efficient by Grossley.");
// Show the border settings on PC.
-ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"if (global.osflavor <= 2)
+ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"if (global.osflavor <= 2)
{
menu_max = 2
if (obj_time.j_ch == 0)
menu_max = 1
}", "", true);
-ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"if (global.osflavor >= 4)", "if (global.osflavor >= 1)", true);
+ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"if (global.osflavor >= 4)", "if (global.osflavor >= 1)", true);
-ReplaceTextInGML(("gml_Script_scr_draw_background_ps4"), @"if (os_type == os_ps4 || os_type == os_switch_beta)", "if (os_type == os_ps4 || os_type == os_switch_beta || os_type == os_windows)", true);
+ReplaceTextInGML("gml_Script_scr_draw_background_ps4", @"if (os_type == os_ps4 || os_type == os_switch_beta)", "if (os_type == os_ps4 || os_type == os_switch_beta || os_type == os_windows)", true);
-ReplaceTextInGML(("gml_Script_scr_draw_screen_border"), @"if (os_type == os_ps4 || os_type == os_switch_beta)", "if (os_type == os_ps4 || os_type == os_switch_beta || os_type == os_windows)", true);
+ReplaceTextInGML("gml_Script_scr_draw_screen_border", @"if (os_type == os_ps4 || os_type == os_switch_beta)", "if (os_type == os_ps4 || os_type == os_switch_beta || os_type == os_windows)", true);
-ReplaceTextInGML(("gml_Script_scr_draw_screen_border"), @"if (os_type == os_switch_beta)", "if (os_type == os_switch_beta || os_type == os_windows)", true);
+ReplaceTextInGML("gml_Script_scr_draw_screen_border", @"if (os_type == os_switch_beta)", "if (os_type == os_switch_beta || os_type == os_windows)", true);
// Enable the dog border unlock
-ReplaceTextInGML(("gml_Object_obj_rarependant_Step_1"), @"if (global.osflavor == 5)", "if (global.osflavor >= 1)", true);
+ReplaceTextInGML("gml_Object_obj_rarependant_Step_1", @"if (global.osflavor == 5)", "if (global.osflavor >= 1)", true);
// Load borders
-ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"scr_enable_screen_border(global.osflavor >= 4)", "scr_enable_screen_border(global.osflavor >= 1)", true);
+ReplaceTextInGML("gml_Object_obj_time_Step_1", @"scr_enable_screen_border(global.osflavor >= 4)", "scr_enable_screen_border(global.osflavor >= 1)", true);
// Resize the game window to account for the borders
//Data.GeneralInfo.DefaultWindowWidth = 1920; // This setup prevents the game from starting??
@@ -51,12 +51,12 @@ Data.Code.ByName("gml_Script_SCR_GAMESTART").AppendGML(@"
window_set_size(960, 540)
", Data);
-ReplaceTextInGML(("gml_Object_obj_time_Draw_77"), @"else
+ReplaceTextInGML("gml_Object_obj_time_Draw_77", @"else
{
global.window_xofs = 0
global.window_yofs = 0
}", "", true);
-ReplaceTextInGML(("gml_Object_obj_time_Draw_77"), @"if (global.osflavor >= 3)", "if (true)", true);
-ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"if (global.osflavor >= 3)", "if (global.osflavor >= 1)", true);
-ReplaceTextInGML(("gml_Object_obj_time_Draw_76"), @"else if (global.osflavor >= 4)", "else if (global.osflavor >= 1)", true);
+ReplaceTextInGML("gml_Object_obj_time_Draw_77", @"if (global.osflavor >= 3)", "if (true)", true);
+ReplaceTextInGML("gml_Object_obj_time_Create_0", @"if (global.osflavor >= 3)", "if (global.osflavor >= 1)", true);
+ReplaceTextInGML("gml_Object_obj_time_Draw_76", @"else if (global.osflavor >= 4)", "else if (global.osflavor >= 1)", true);
ScriptMessage("Finished.");
diff --git a/UndertaleModTool/Scripts/Community Scripts/FontEditor.csx b/UndertaleModTool/Scripts/Community Scripts/FontEditor.csx
index 871d9a4e1..1438fd0d4 100644
--- a/UndertaleModTool/Scripts/Community Scripts/FontEditor.csx
+++ b/UndertaleModTool/Scripts/Community Scripts/FontEditor.csx
@@ -1,4 +1,5 @@
-//by porog
+// by porog
+// TODO: this heavily uses Windows stuff, should be made cross platform
using System.Drawing;
using System.Drawing.Imaging;
@@ -7,25 +8,33 @@ using System.Linq;
using System.Text;
using System.Windows.Forms;
using UndertaleModLib.Util;
+using ImageMagick;
EnsureDataLoaded();
-UndertaleFont font = FontPickerResult(); //GUI dropdown selection list of fonts
-if (font == null) return; //the 'Cancel' or 'X' button is hit
-new FontEditorGUI(font).ShowDialog(); //font editor GUI
+UndertaleFont font = FontPickerResult(); // GUI dropdown selection list of fonts
+if (font is null)
+{
+ return; // the 'Cancel' or 'X' button is hit
+}
+using (TextureWorker textureWorker = new())
+{
+ _ = new FontEditorGUI(font, textureWorker).ShowDialog(); // Font editor GUI
+}
class FontEditorGUI : Form
{
UndertaleFont font;
List letterData = new List();
- TextureWorker textureWorker = new TextureWorker();
+ TextureWorker textureWorker = null;
ListView listView;
bool savePrompt = false;
- public FontEditorGUI(UndertaleFont font)
+ public FontEditorGUI(UndertaleFont font, TextureWorker textureWorker)
{
this.font = font;
+ this.textureWorker = textureWorker;
Text = font.Name.Content;
MinimumSize = new Size(275, 150);
@@ -294,9 +303,12 @@ class FontEditorGUI : Form
((FormClosingEventArgs)e).Cancel = true;
}
};
-
+
//Populate list from font
- Bitmap fontSheetImg = textureWorker.GetTextureFor(font.Texture, null);
+ using IMagickImage fontSheetMagickImg = textureWorker.GetTextureFor(font.Texture, null);
+ IUnsafePixelCollection fontSheetMagickPixels = fontSheetMagickImg.GetPixelsUnsafe();
+ Bitmap fontSheetImg = new Bitmap(fontSheetMagickImg.Width, fontSheetMagickImg.Height, 4 * fontSheetMagickImg.Width, PixelFormat.Format32bppArgb,
+ fontSheetMagickPixels.GetAreaPointer(0, 0, fontSheetMagickImg.Width, fontSheetMagickImg.Height));
List letters = new List();
foreach (UndertaleFont.Glyph glyph in font.Glyphs)
{
@@ -575,7 +587,7 @@ class FontEditorGUI : Form
//Set font sheet image
Bitmap spriteSheetImg = null;
- using (var ms = new MemoryStream(font.Texture.TexturePage.TextureData.TextureBlob))
+ using (var ms = new MemoryStream(font.Texture.TexturePage.TextureData.Image.ConvertToPng().ToSpan().ToArray()))
{
spriteSheetImg = new Bitmap(ms);
}
@@ -601,7 +613,7 @@ class FontEditorGUI : Form
}
copy.Save(ms, ImageFormat.Png);
- font.Texture.TexturePage.TextureData.TextureBlob = ms.ToArray();
+ font.Texture.TexturePage.TextureData.Image = GMImage.FromPng(ms.ToArray());
font.Texture.TargetX = 0;
font.Texture.TargetY = 0;
}
diff --git a/UndertaleModTool/Scripts/Community Scripts/ImportGMS2FontData.csx b/UndertaleModTool/Scripts/Community Scripts/ImportGMS2FontData.csx
index c6bd9aba8..dc89d0a52 100644
--- a/UndertaleModTool/Scripts/Community Scripts/ImportGMS2FontData.csx
+++ b/UndertaleModTool/Scripts/Community Scripts/ImportGMS2FontData.csx
@@ -2,7 +2,6 @@
using System;
using System.IO;
-using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -96,33 +95,35 @@ else if (attemptToFixFontNotAppearing)
fontTexGroup.Fonts.Add(new UndertaleResourceById() { Resource = font });
}
-// Prepare font texture
-Bitmap textureBitmap = new Bitmap(fontTexturePath);
-// Make the DPI exactly 96 for this bitmap
-textureBitmap.SetResolution(96.0F, 96.0F);
+// Get texture properties
+(int parsedWidth, int parsedHeight) = TextureWorker.GetImageSizeFromFile(fontTexturePath);
+if (parsedWidth == -1 || parsedHeight == -1)
+ throw new ScriptException("Invalid font texture image");
+ushort width = (ushort)parsedWidth;
+ushort height = (ushort)parsedHeight;
UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture();
-// ??? Why?
-texture.Name = new UndertaleString("Texture " + Data.EmbeddedTextures.Count);
-texture.TextureData.TextureBlob = File.ReadAllBytes(fontTexturePath);
+texture.Name = new UndertaleString($"Texture {Data.EmbeddedTextures.Count}");
+texture.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(fontTexturePath)); // TODO: generate other formats
Data.EmbeddedTextures.Add(texture);
if (attemptToFixFontNotAppearing)
fontTexGroup.TexturePages.Add(new UndertaleResourceById() { Resource = texture });
-UndertaleTexturePageItem texturePageItem = new UndertaleTexturePageItem();
-// ??? Same as above
-texturePageItem.Name = new UndertaleString("PageItem " + Data.TexturePageItems.Count);
-texturePageItem.TexturePage = texture;
-texturePageItem.SourceX = 0;
-texturePageItem.SourceY = 0;
-texturePageItem.SourceWidth = (ushort)textureBitmap.Width;
-texturePageItem.SourceHeight = (ushort)textureBitmap.Height;
-texturePageItem.TargetX = 0;
-texturePageItem.TargetY = 0;
-texturePageItem.TargetWidth = (ushort)textureBitmap.Width;
-texturePageItem.TargetHeight = (ushort)textureBitmap.Height;
-texturePageItem.BoundingWidth = (ushort)textureBitmap.Width;
-texturePageItem.BoundingHeight = (ushort)textureBitmap.Height;
+UndertaleTexturePageItem texturePageItem = new UndertaleTexturePageItem()
+{
+ Name = new UndertaleString($"PageItem {Data.TexturePageItems.Count}"),
+ TexturePage = texture,
+ SourceX = 0,
+ SourceY = 0,
+ SourceWidth = width,
+ SourceHeight = height,
+ TargetX = 0,
+ TargetY = 0,
+ TargetWidth = width,
+ TargetHeight = height,
+ BoundingWidth = width,
+ BoundingHeight = height
+};
Data.TexturePageItems.Add(texturePageItem);
font.DisplayName = Data.Strings.MakeString((string)fontData["fontName"]);
diff --git a/UndertaleModTool/Scripts/Community Scripts/ScaleAllTextures.csx b/UndertaleModTool/Scripts/Community Scripts/ScaleAllTextures.csx
index f1adc7fb8..71f5d23df 100644
--- a/UndertaleModTool/Scripts/Community Scripts/ScaleAllTextures.csx
+++ b/UndertaleModTool/Scripts/Community Scripts/ScaleAllTextures.csx
@@ -1,13 +1,11 @@
using System;
using System.IO;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Drawing.Drawing2D;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UndertaleModLib.Util;
+using ImageMagick;
// By Grossley
@@ -19,148 +17,134 @@ if (!ScriptQuestion("Visual glitches are very likely to occur in game. Do you ac
return;
}
-TextureWorker worker = new TextureWorker();
-double scale = -1;
-bool SelectScale = true;
-
-if (SelectScale)
+using (TextureWorker worker = new())
{
- bool success = false;
- while (scale <= 0 || scale > 10)
+ double scale = -1;
+ bool selectScale = true;
+
+ if (selectScale)
{
- while (!success)
+ bool success = false;
+ while (scale <= 0 || scale > 10)
{
- success = Double.TryParse(SimpleTextInput("Enter scale between 0 and 10 (not including 0).", "Enter scale", "", false), out scale);
+ while (!success)
+ {
+ success = double.TryParse(SimpleTextInput("Enter scale between 0 and 10 (not including 0).", "Enter scale", "", false), out scale);
+ }
+ success = false;
}
- success = false;
}
-}
-else
- scale = 2;
+ else
+ scale = 2;
-for (var i = 0; i < Data.EmbeddedTextures.Count; i++)
-{
- ScaleEmbeddedTexture(Data.EmbeddedTextures[i]);
-}
-for (int i = 0; i < Data.TexturePageItems.Count; i++)
-{
- var tpage = Data.TexturePageItems[i];
- double offset = (Math.Pow(2, Math.Floor(Math.Abs(Math.Log2(scale))) - (Math.Log2(scale) >= 0 ? 1 : 0)));
- offset *= (Math.Log2(scale) >= 0 ? 1 : Math.Floor((Math.Log2(scale))) / 2);
- double sourceOffset = -(offset + (Math.Log2(scale) >= 0 ? 0 : Math.Floor(Math.Abs(Math.Log2(scale)))));
- tpage.SourceX = (ushort)((tpage.SourceX * scale) + sourceOffset);
- tpage.SourceY = (ushort)((tpage.SourceY * scale) + sourceOffset);
- tpage.TargetX = (ushort)(tpage.TargetX * scale);
- tpage.TargetY = (ushort)(tpage.TargetY * scale);
- double newOffset = -(offset >= 0 ? 0 : offset * 0.5);
- tpage.SourceWidth = (ushort)((tpage.SourceWidth * scale) + newOffset);
- tpage.SourceHeight = (ushort)((tpage.SourceHeight * scale) + newOffset);
- tpage.TargetWidth = (ushort)((tpage.TargetWidth * scale) + newOffset);
- tpage.TargetHeight = (ushort)((tpage.TargetHeight * scale) + newOffset);
- tpage.BoundingWidth = (ushort)(tpage.BoundingWidth * scale);
- tpage.BoundingHeight = (ushort)(tpage.BoundingHeight * scale);
-}
-foreach (UndertaleFont fnt in Data.Fonts)
-{
- //fnt.ScaleX = scale;
- //fnt.ScaleY = scale;
- foreach (UndertaleFont.Glyph glyph in fnt.Glyphs)
+ for (var i = 0; i < Data.EmbeddedTextures.Count; i++)
{
+ ScaleEmbeddedTexture(Data.EmbeddedTextures[i]);
+ }
+ for (int i = 0; i < Data.TexturePageItems.Count; i++)
+ {
+ var tpage = Data.TexturePageItems[i];
double offset = (Math.Pow(2, Math.Floor(Math.Abs(Math.Log2(scale))) - (Math.Log2(scale) >= 0 ? 1 : 0)));
offset *= (Math.Log2(scale) >= 0 ? 1 : Math.Floor((Math.Log2(scale))) / 2);
double sourceOffset = -(offset + (Math.Log2(scale) >= 0 ? 0 : Math.Floor(Math.Abs(Math.Log2(scale)))));
+ tpage.SourceX = (ushort)((tpage.SourceX * scale) + sourceOffset);
+ tpage.SourceY = (ushort)((tpage.SourceY * scale) + sourceOffset);
+ tpage.TargetX = (ushort)(tpage.TargetX * scale);
+ tpage.TargetY = (ushort)(tpage.TargetY * scale);
double newOffset = -(offset >= 0 ? 0 : offset * 0.5);
- glyph.SourceX = (ushort)((glyph.SourceX * scale) + sourceOffset);
- glyph.SourceY = (ushort)((glyph.SourceY * scale) + sourceOffset);
- glyph.SourceWidth = (ushort)((glyph.SourceWidth * scale) + newOffset);
- glyph.SourceHeight = (ushort)((glyph.SourceHeight * scale) + newOffset);
- glyph.Shift = (short)(((double)glyph.Shift) * ((double)scale));
- glyph.Offset = (short)(((double)glyph.Offset) * ((double)scale));
+ tpage.SourceWidth = (ushort)((tpage.SourceWidth * scale) + newOffset);
+ tpage.SourceHeight = (ushort)((tpage.SourceHeight * scale) + newOffset);
+ tpage.TargetWidth = (ushort)((tpage.TargetWidth * scale) + newOffset);
+ tpage.TargetHeight = (ushort)((tpage.TargetHeight * scale) + newOffset);
+ tpage.BoundingWidth = (ushort)(tpage.BoundingWidth * scale);
+ tpage.BoundingHeight = (ushort)(tpage.BoundingHeight * scale);
}
-}
-foreach (UndertaleRoom room in Data.Rooms)
-{
- foreach (UndertaleRoom.Background background in room.Backgrounds)
+ foreach (UndertaleFont fnt in Data.Fonts)
{
- if (background.Enabled)
+ //fnt.ScaleX = scale;
+ //fnt.ScaleY = scale;
+ foreach (UndertaleFont.Glyph glyph in fnt.Glyphs)
{
- background.Stretch = true;
+ double offset = (Math.Pow(2, Math.Floor(Math.Abs(Math.Log2(scale))) - (Math.Log2(scale) >= 0 ? 1 : 0)));
+ offset *= (Math.Log2(scale) >= 0 ? 1 : Math.Floor((Math.Log2(scale))) / 2);
+ double sourceOffset = -(offset + (Math.Log2(scale) >= 0 ? 0 : Math.Floor(Math.Abs(Math.Log2(scale)))));
+ double newOffset = -(offset >= 0 ? 0 : offset * 0.5);
+ glyph.SourceX = (ushort)((glyph.SourceX * scale) + sourceOffset);
+ glyph.SourceY = (ushort)((glyph.SourceY * scale) + sourceOffset);
+ glyph.SourceWidth = (ushort)((glyph.SourceWidth * scale) + newOffset);
+ glyph.SourceHeight = (ushort)((glyph.SourceHeight * scale) + newOffset);
+ glyph.Shift = (short)(((double)glyph.Shift) * ((double)scale));
+ glyph.Offset = (short)(((double)glyph.Offset) * ((double)scale));
}
}
- //room.Width = (uint)((double)(room.Width) * ((double)scale));
- //room.Height = (uint)((double)(room.Height) * ((double)scale));
- //room.Top = (uint)((double)(room.Top) * ((double)scale));
- //room.Left = (uint)((double)(room.Left) * ((double)scale));
- //room.Right = (uint)((double)(room.Right) * ((double)scale));
- //room.Bottom = (uint)((double)(room.Bottom) * ((double)scale));
- //foreach (UndertaleRoom.View myView in room.Views)
- //{
- //myView.ViewX = (int)((double)(myView.ViewX) * ((double)scale));
- //myView.ViewY = (int)((double)(myView.ViewY) * ((double)scale));
- //myView.ViewWidth = (int)((double)(myView.ViewWidth) * ((double)scale));
- //myView.ViewHeight = (int)((double)(myView.ViewHeight) * ((double)scale));
- //myView.PortX = (int)((double)(myView.PortX) * ((double)scale));
- //myView.PortY = (int)((double)(myView.PortY) * ((double)scale));
- //myView.PortWidth = (int)((double)(myView.PortWidth) * ((double)scale));
- //myView.PortHeight = (int)((double)(myView.PortHeight) * ((double)scale));
- //myView.BorderX = (uint)((double)(myView.BorderX) * ((double)scale));
- //myView.BorderY = (uint)((double)(myView.BorderY) * ((double)scale));
- //}
- foreach (UndertaleRoom.GameObject myGameObject in room.GameObjects)
+ foreach (UndertaleRoom room in Data.Rooms)
{
- //myGameObject.X = (int)((double)(myGameObject.X) / ((double)scale));
- //myGameObject.Y = (int)((double)(myGameObject.Y) / ((double)scale));
- //myGameObject.ScaleX = (float)((double)(myGameObject.ScaleX) / ((double)scale));
- //myGameObject.ScaleY = (float)((double)(myGameObject.ScaleY) / ((double)scale));
- }
- foreach (UndertaleRoom.Tile myTile in room.Tiles)
- {
- //myTile.X = (int)((double)(myTile.X) * ((double)scale));
- //myTile.Y = (int)((double)(myTile.Y) * ((double)scale));
- myTile.SourceX = (uint)((double)(myTile.SourceX) * ((double)scale));
- myTile.SourceY = (uint)((double)(myTile.SourceY) * ((double)scale));
- myTile.Width = (uint)((double)(myTile.Width) * ((double)scale));
- myTile.Height = (uint)((double)(myTile.Height) * ((double)scale));
- myTile.ScaleX = (float)((double)(myTile.ScaleX) / ((double)scale)); ;
- myTile.ScaleY = (float)((double)(myTile.ScaleY) / ((double)scale)); ;
+ foreach (UndertaleRoom.Background background in room.Backgrounds)
+ {
+ if (background.Enabled)
+ {
+ background.Stretch = true;
+ }
+ }
+ //room.Width = (uint)((double)(room.Width) * ((double)scale));
+ //room.Height = (uint)((double)(room.Height) * ((double)scale));
+ //room.Top = (uint)((double)(room.Top) * ((double)scale));
+ //room.Left = (uint)((double)(room.Left) * ((double)scale));
+ //room.Right = (uint)((double)(room.Right) * ((double)scale));
+ //room.Bottom = (uint)((double)(room.Bottom) * ((double)scale));
+ //foreach (UndertaleRoom.View myView in room.Views)
+ //{
+ //myView.ViewX = (int)((double)(myView.ViewX) * ((double)scale));
+ //myView.ViewY = (int)((double)(myView.ViewY) * ((double)scale));
+ //myView.ViewWidth = (int)((double)(myView.ViewWidth) * ((double)scale));
+ //myView.ViewHeight = (int)((double)(myView.ViewHeight) * ((double)scale));
+ //myView.PortX = (int)((double)(myView.PortX) * ((double)scale));
+ //myView.PortY = (int)((double)(myView.PortY) * ((double)scale));
+ //myView.PortWidth = (int)((double)(myView.PortWidth) * ((double)scale));
+ //myView.PortHeight = (int)((double)(myView.PortHeight) * ((double)scale));
+ //myView.BorderX = (uint)((double)(myView.BorderX) * ((double)scale));
+ //myView.BorderY = (uint)((double)(myView.BorderY) * ((double)scale));
+ //}
+ foreach (UndertaleRoom.GameObject myGameObject in room.GameObjects)
+ {
+ //myGameObject.X = (int)((double)(myGameObject.X) / ((double)scale));
+ //myGameObject.Y = (int)((double)(myGameObject.Y) / ((double)scale));
+ //myGameObject.ScaleX = (float)((double)(myGameObject.ScaleX) / ((double)scale));
+ //myGameObject.ScaleY = (float)((double)(myGameObject.ScaleY) / ((double)scale));
+ }
+ foreach (UndertaleRoom.Tile myTile in room.Tiles)
+ {
+ //myTile.X = (int)((double)(myTile.X) * ((double)scale));
+ //myTile.Y = (int)((double)(myTile.Y) * ((double)scale));
+ myTile.SourceX = (uint)((double)(myTile.SourceX) * ((double)scale));
+ myTile.SourceY = (uint)((double)(myTile.SourceY) * ((double)scale));
+ myTile.Width = (uint)((double)(myTile.Width) * ((double)scale));
+ myTile.Height = (uint)((double)(myTile.Height) * ((double)scale));
+ myTile.ScaleX = (float)((double)(myTile.ScaleX) / ((double)scale)); ;
+ myTile.ScaleY = (float)((double)(myTile.ScaleY) / ((double)scale)); ;
+ }
}
-}
-ChangeSelection(Data.Rooms.ByName("room_ruins1"));
-//ChangeSelection(Data.TexturePageItems[2832]);
+ ChangeSelection(Data.Rooms.ByName("room_ruins1"));
+ //ChangeSelection(Data.TexturePageItems[2832]);
-void ScaleEmbeddedTexture(UndertaleEmbeddedTexture tex)
-{
- Bitmap embImage = worker.GetEmbeddedTexture(tex);
- embImage = ResizeBitmap(embImage, (int)(embImage.Width * scale), (int)(embImage.Height * scale));
- embImage.SetResolution(96.0F, 96.0F);
- try
+ void ScaleEmbeddedTexture(UndertaleEmbeddedTexture tex)
{
- var width = (uint)embImage.Width;
- var height = (uint)embImage.Height;
- if ((width & (width - 1)) != 0 || (height & (height - 1)) != 0)
+ MagickImage embImage = worker.GetEmbeddedTexture(tex);
+ using IMagickImage scaledEmbImage = TextureWorker.ResizeImage(embImage, (int)(embImage.Width * scale), (int)(embImage.Height * scale), PixelInterpolateMethod.Nearest);
+ try
{
- //ScriptError("WARNING: texture page dimensions are not powers of 2. Sprite blurring is very likely in game.", "Unexpected texture dimensions");
+ uint width = (uint)scaledEmbImage.Width;
+ uint height = (uint)scaledEmbImage.Height;
+ if ((width & (width - 1)) != 0 || (height & (height - 1)) != 0)
+ {
+ //ScriptError("WARNING: texture page dimensions are not powers of 2. Sprite blurring is very likely in game.", "Unexpected texture dimensions");
+ }
+ tex.TextureData.Image = GMImage.FromMagickImage(scaledEmbImage).ConvertToFormat(tex.TextureData.Image.Format);
}
- using (var stream = new MemoryStream())
+ catch (Exception ex)
{
- embImage.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
- tex.TextureData.TextureBlob = stream.ToArray();
+ ScriptError("Failed to import file: " + ex.Message, "Failed to import file");
}
}
- catch (Exception ex)
- {
- //ScriptError("Failed to import file: " + ex.Message, "Failed to import file");
- }
-}
-
-private Bitmap ResizeBitmap(Bitmap sourceBMP, int width, int height)
-{
- Bitmap result = new Bitmap(width, height);
- using (Graphics g = Graphics.FromImage(result))
- {
- g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
- g.DrawImage(sourceBMP, 0, 0, width, height);
- }
- return result;
-}
+}
\ No newline at end of file
diff --git a/UndertaleModTool/Scripts/Community Scripts/TouchControlsEnabler.csx b/UndertaleModTool/Scripts/Community Scripts/TouchControlsEnabler.csx
index ef83e4ea0..0e05a6383 100644
--- a/UndertaleModTool/Scripts/Community Scripts/TouchControlsEnabler.csx
+++ b/UndertaleModTool/Scripts/Community Scripts/TouchControlsEnabler.csx
@@ -24,7 +24,7 @@ string dataPath = Path.Combine(Path.GetDirectoryName(ScriptPath), "TouchControls
Dictionary textures = new Dictionary();
UndertaleEmbeddedTexture controlsTexturePage = new UndertaleEmbeddedTexture();
-controlsTexturePage.TextureData.TextureBlob = File.ReadAllBytes(Path.Combine(dataPath, "controls.png"));
+controlsTexturePage.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(Path.Combine(dataPath, "controls.png"))); // TODO: generate other formats
Data.EmbeddedTextures.Add(controlsTexturePage);
textures.Add(Path.GetFileName(Path.Combine(dataPath, "controls.png")), controlsTexturePage);
@@ -38,7 +38,21 @@ UndertaleTexturePageItem AddNewTexturePageItem(ushort sourceX, ushort sourceY, u
ushort boundingHeight = sourceHeight;
var texturePage = textures["controls.png"];
- UndertaleTexturePageItem tpItem = new UndertaleTexturePageItem { SourceX = sourceX, SourceY = sourceY, SourceWidth = sourceWidth, SourceHeight = sourceHeight, TargetX = targetX, TargetY = targetY, TargetWidth = targetWidth, TargetHeight = targetHeight, BoundingWidth = boundingWidth, BoundingHeight = boundingHeight, TexturePage = texturePage };
+ UndertaleTexturePageItem tpItem = new()
+ {
+ SourceX = sourceX,
+ SourceY = sourceY,
+ SourceWidth = sourceWidth,
+ SourceHeight = sourceHeight,
+ TargetX = targetX,
+ TargetY = targetY,
+ TargetWidth = targetWidth,
+ TargetHeight = targetHeight,
+ BoundingWidth = boundingWidth,
+ BoundingHeight = boundingHeight,
+ TexturePage = texturePage,
+ Name = new UndertaleString($"PageItem {Data.TexturePageItems.Count}")
+ };
Data.TexturePageItems.Add(tpItem);
return tpItem;
}
@@ -73,9 +87,10 @@ void AddNewUndertaleSprite(string spriteName, ushort width, ushort height, Under
int marginBottom = height - 1;
var sItem = new UndertaleSprite { Name = name, Width = width, Height = height, MarginLeft = marginLeft, MarginRight = marginRight, MarginTop = marginTop, MarginBottom = marginBottom };
- foreach (var spriteTexture in spriteTextures) {
+ foreach (var spriteTexture in spriteTextures)
+ {
sItem.Textures.Add(new UndertaleSprite.TextureEntry() { Texture = spriteTexture });
- };
+ }
Data.Sprites.Add(sItem);
}
diff --git a/UndertaleModTool/Scripts/Community Scripts/TouchControls_data/gml_Object_obj_mobilecontrols_Draw_64.gml b/UndertaleModTool/Scripts/Community Scripts/TouchControls_data/gml_Object_obj_mobilecontrols_Draw_64.gml
index 00d4220fc..dbda9c007 100644
--- a/UndertaleModTool/Scripts/Community Scripts/TouchControls_data/gml_Object_obj_mobilecontrols_Draw_64.gml
+++ b/UndertaleModTool/Scripts/Community Scripts/TouchControls_data/gml_Object_obj_mobilecontrols_Draw_64.gml
@@ -21,4 +21,4 @@ draw_sprite_ext(spr_x_button, keyboard_check(ord("X")), (xx * ratio), (xy * rati
draw_sprite_ext(spr_c_button, keyboard_check(ord("C")), (cx * ratio), (cy * ratioVertical), (button_scale * ratio), (button_scale * ratioVertical), 0, c_white, controls_opacity)
draw_sprite_ext(spr_joybase, joystick_type, (analog_posx * ratio), (analog_posy * ratioVertical), (analog_scale * ratio), (analog_scale * ratioVertical), 0, c_white, controls_opacity)
draw_sprite_ext(spr_joystick, joystick_type, (analog_center_x * ratio), (analog_center_y * ratioVertical), (analog_scale * ratio), (analog_scale * ratioVertical), 0, c_white, controls_opacity)
-draw_sprite_ext(spr_settings, keyboard_check(ord("\")), (settingsx * ratio), (settingsy * ratioVertical), (button_scale * ratio), (button_scale * ratioVertical), 0, c_white, controls_opacity)
+draw_sprite_ext(spr_settings, keyboard_check(92 /* ord("\") */), (settingsx * ratio), (settingsy * ratioVertical), (button_scale * ratio), (button_scale * ratioVertical), 0, c_white, controls_opacity)
diff --git a/UndertaleModTool/Scripts/Resource Repackers/ApplyBasicGraphicsMod.csx b/UndertaleModTool/Scripts/Resource Repackers/ApplyBasicGraphicsMod.csx
index 05018a98a..ab0348ef5 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/ApplyBasicGraphicsMod.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/ApplyBasicGraphicsMod.csx
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -8,6 +7,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UndertaleModLib.Util;
+using ImageMagick;
EnsureDataLoaded();
@@ -17,129 +17,113 @@ if (importFolder == null)
throw new ScriptException("The import folder was not set.");
string[] dirFiles = Directory.GetFiles(importFolder);
+List<(string filename, string strippedFilename, string spriteName, UndertaleSprite sprite, int frame)> images = new();
-//Stop the script if there's missing sprite entries or w/e.
-foreach (string file in dirFiles)
+await Task.Run(() =>
{
- string FileNameWithExtension = Path.GetFileName(file);
- if (!FileNameWithExtension.EndsWith(".png"))
- continue; // Restarts loop if file is not a valid mask asset.
- string stripped = Path.GetFileNameWithoutExtension(file);
- int lastUnderscore = stripped.LastIndexOf('_');
- string spriteName = "";
- try
- {
- spriteName = stripped.Substring(0, lastUnderscore);
- }
- catch
- {
- throw new ScriptException("Getting the sprite name of " + FileNameWithExtension + " failed.");
- }
- if (Data.Sprites.ByName(spriteName) == null) // Reject non-existing sprites
- {
- throw new ScriptException(FileNameWithExtension + " could not be imported as the sprite " + spriteName + " does not exist.");
- }
- using (Image img = Image.FromFile(file))
+ // Stop the script if there's missing sprite entries or w/e.
+ foreach (string file in dirFiles)
{
- if ((Data.Sprites.ByName(spriteName).Width != (uint)img.Width) || (Data.Sprites.ByName(spriteName).Height != (uint)img.Height))
+ string filenameWithExtension = Path.GetFileName(file);
+ if (!filenameWithExtension.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase) || !filenameWithExtension.Contains("_"))
{
- // This check isn't working right
- // throw new ScriptException(FileNameWithExtension + " is not the proper size to be imported! Please correct this before importing! The proper dimensions are width: " + Data.Sprites.ByName(spriteName).Width.ToString() + " px, height: " + Data.Sprites.ByName(spriteName).Height.ToString() + " px.");
+ // Skip all non-images
+ continue;
}
- }
- Int32 validFrameNumber = 0;
- try
- {
- validFrameNumber = Int32.Parse(stripped.Substring(lastUnderscore + 1));
- }
- catch
- {
- throw new ScriptException("The index of " + FileNameWithExtension + " could not be determined.");
- }
- int frame = 0;
- try
- {
- frame = Int32.Parse(stripped.Substring(lastUnderscore + 1));
- }
- catch
- {
- throw new ScriptException(FileNameWithExtension + " is using letters instead of numbers. The script has stopped for your own protection.");
- }
- int prevframe = 0;
- if (frame != 0)
- {
- prevframe = (frame - 1);
- }
- if (frame < 0)
- {
- throw new ScriptException(spriteName + " is using an invalid numbering scheme. The script has stopped for your own protection.");
+ string stripped = Path.GetFileNameWithoutExtension(file);
+ int lastUnderscore = stripped.LastIndexOf('_');
+ string spriteName;
+ try
+ {
+ spriteName = stripped.Substring(0, lastUnderscore);
+ }
+ catch
+ {
+ throw new ScriptException($"Getting the sprite name of {filenameWithExtension} failed.");
+ }
+
+ // Reject non-existing sprites
+ UndertaleSprite sprite = Data.Sprites.ByName(spriteName);
+ if (sprite is null)
+ {
+ throw new ScriptException($"{filenameWithExtension} could not be imported, as the sprite \"{spriteName}\" does not exist.");
+ }
+
+ // Parse and validate frame number
+ if (!int.TryParse(stripped.Substring(lastUnderscore + 1), out int frame))
+ {
+ throw new ScriptException($"The frame index of {filenameWithExtension} could not be determined (should be an integer).");
+ }
+ if (frame < 0)
+ {
+ throw new ScriptException($"The frame index of {filenameWithExtension} appears to be negative (should be 0 or greater).");
+ }
+ if (frame >= sprite.Textures.Count)
+ {
+ throw new ScriptException($"The frame index of {filenameWithExtension} is too large (sprite in the data only has {sprite.Textures.Count} frames).");
+ }
+
+ // Check that the previous frame exists, if not the first frame
+ if (frame > 0)
+ {
+ int prevframe = frame - 1;
+ string prevFrameName = $"{spriteName}_{prevframe}.png";
+ if (!File.Exists(Path.Combine(importFolder, prevFrameName)))
+ {
+ throw new ScriptException($"{spriteName} is missing image index {prevframe} (failed to find {prevFrameName}).");
+ }
+ }
+
+ // Add to validated image list
+ images.Add((file, stripped, spriteName, sprite, frame));
}
- var prevFrameName = spriteName + "_" + prevframe.ToString() + ".png";
- string[] previousFrameFiles = Directory.GetFiles(importFolder, prevFrameName);
- if (previousFrameFiles.Length < 1)
- throw new ScriptException(spriteName + " is missing one or more indexes. The detected missing index is: " + prevFrameName);
-}
+});
SetProgressBar(null, "Files", 0, dirFiles.Length);
StartProgressBarUpdater();
-await Task.Run(() => {
- foreach (string file in dirFiles)
+bool errored = false;
+await Task.Run(() =>
+{
+ foreach ((string filename, string strippedFilename, string spriteName, UndertaleSprite sprite, int frame) in images)
{
IncrementProgress();
- string fileName = Path.GetFileName(file);
- if (!fileName.EndsWith(".png") || !fileName.Contains("_"))
- continue; // Not an image.
-
- string stripped = Path.GetFileNameWithoutExtension(file);
-
- int lastUnderscore = stripped.LastIndexOf('_');
- string spriteName = stripped.Substring(0, lastUnderscore);
- int frame = Int32.Parse(stripped.Substring(lastUnderscore + 1));
-
- UndertaleSprite sprite = Data.Sprites.ByName(spriteName);
-
- if (frame < sprite.Textures.Count)
+ try
{
- try
+ using MagickImage image = TextureWorker.ReadBGRAImageFromFile(filename);
+ UndertaleTexturePageItem item = sprite.Textures[frame].Texture;
+ if (image.Width != item.TargetWidth || image.Height != item.TargetHeight)
{
- Bitmap bmp;
- using (var ms = new MemoryStream(TextureWorker.ReadTextureBlob(file)))
- {
- bmp = new Bitmap(ms);
- }
- bmp.SetResolution(96.0F, 96.0F);
- var width = (uint)bmp.Width;
- var height = (uint)bmp.Height;
- var CheckWidth = (uint)(sprite.Textures[frame].Texture.TargetWidth);
- var CheckHeight = (uint)(sprite.Textures[frame].Texture.TargetHeight);
- if ((width != CheckWidth) || (height != CheckHeight))
+ // Generic error message when the width/height mismatch
+ string error = $"Incorrect dimensions of {strippedFilename}; should be {item.TargetWidth}x{item.TargetHeight}, to fit on the texture page." +
+ "\n\nStopping early. Some sprites may already be modified.";
+ if (image.Width == sprite.Width && image.Height == sprite.Height)
{
- string error = "Incorrect dimensions of " + stripped + ". Sprite blurring is very likely in game. Aborting!";
- ScriptError(error, "Unexpected texture dimensions");
- SetUMTConsoleText(error);
- SetFinishedMessage(false);
- return;
+ // Sprite was likely exported with padding - give a more helpful error message
+ error = $"{strippedFilename} appears to be exported with padding. The resulting sprite would be too large to fit in the same space on the texture page. " +
+ "Export the sprite without padding, or use ImportGraphics.csx to import sprites of arbitrary dimensions, on new texture pages." +
+ "\n\nStopping early. Some sprites may already be modified.";
}
- sprite.Textures[frame].Texture.ReplaceTexture(bmp);
- }
- catch
- {
- string error = file + " encountered an unknown error during import. Contact the Underminers discord with as much information as possible, the file, and this error message. Aborting!";
- ScriptError(error, "Sprite Error");
+ ScriptError(error, "Unexpected texture dimensions");
SetUMTConsoleText(error);
SetFinishedMessage(false);
+ errored = true;
return;
}
+
+ // Actually replace texture
+ item.ReplaceTexture(image);
}
- else
+ catch
{
- string error = fileName + ": Index out of range. Index " + frame.ToString() + " exceeds maximum index (" + ((sprite.Textures.Count) - 1).ToString() + ") of " + spriteName + ". Aborting!";
+ string error = $"{filename} encountered an unknown error during import. " +
+ "Contact the Underminers discord with as much information as possible, the file, and this error message. Aborting!";
ScriptError(error, "Sprite Error");
SetUMTConsoleText(error);
SetFinishedMessage(false);
+ errored = true;
return;
}
}
@@ -147,4 +131,7 @@ await Task.Run(() => {
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Import Complete!");
\ No newline at end of file
+if (!errored)
+{
+ ScriptMessage("Import complete!");
+}
\ No newline at end of file
diff --git a/UndertaleModTool/Scripts/Resource Repackers/CopySpriteBgFont.csx b/UndertaleModTool/Scripts/Resource Repackers/CopySpriteBgFont.csx
index f19528a37..fca4df4f9 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/CopySpriteBgFont.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/CopySpriteBgFont.csx
@@ -11,22 +11,22 @@ EnsureDataLoaded();
// Initialization Start
var DataEmbeddedTexturesCount = Data.EmbeddedTextures.Count;
-List tex_TargetX = new List();
-List tex_TargetY = new List();
-List tex_SourceX = new List();
-List tex_SourceY = new List();
-List tex_SourceWidth = new List();
-List tex_SourceHeight = new List();
-List tex_TargetWidth = new List();
-List tex_TargetHeight = new List();
-List tex_BoundingWidth = new List();
-List tex_BoundingHeight = new List();
-List tex_Frame = new List();
-List tex_EmbeddedTextureID = new List();
-List tex_Name = new List();
-List tex_Type = new List();
-List tex_IsNull = new List();
-List TexturePageItemsUsed = new List();
+List tex_TargetX = new();
+List tex_TargetY = new();
+List tex_SourceX = new();
+List tex_SourceY = new();
+List tex_SourceWidth = new();
+List tex_SourceHeight = new();
+List tex_TargetWidth = new();
+List tex_TargetHeight = new();
+List tex_BoundingWidth = new();
+List tex_BoundingHeight = new();
+List tex_Frame = new();
+List tex_EmbeddedTextureID = new();
+List tex_Name = new();
+List tex_Type = new();
+List tex_IsNull = new();
+List TexturePageItemsUsed = new();
// Initialization End
@@ -38,7 +38,7 @@ if (DonorDataPath == null)
throw new ScriptException("The donor data path was not set.");
using (var stream = new FileStream(DonorDataPath, FileMode.Open, FileAccess.Read))
- DonorData = UndertaleIO.Read(stream, warning => ScriptMessage("A warning occured while trying to load " + DonorDataPath + ":\n" + warning));
+ DonorData = UndertaleIO.Read(stream, warning => ScriptMessage($"A warning occured while trying to load {DonorDataPath}:\n" + warning));
var DonorDataEmbeddedTexturesCount = DonorData.EmbeddedTextures.Count;
int copiedSpritesCount = 0;
int copiedBackgroundsCount = 0;
@@ -54,7 +54,8 @@ SetProgressBar(null, "Textures Exported", 0, DonorData.TexturePageItems.Count);
StartProgressBarUpdater();
SyncBinding("EmbeddedTextures, Strings, Backgrounds, Sprites, Fonts, TexturePageItems", true);
-await Task.Run(() => {
+await Task.Run(() =>
+{
for (int i = 0; i < SpriteSheetsCopyNeeded.Length; i++)
{
SpriteSheetsCopyNeeded[i] = false;
@@ -67,7 +68,7 @@ await Task.Run(() => {
{
UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture();
texture.Name = new UndertaleString("Texture " + ++lastTextPage);
- texture.TextureData.TextureBlob = DonorData.EmbeddedTextures[i].TextureData.TextureBlob;
+ texture.TextureData.Image = DonorData.EmbeddedTextures[i].TextureData.Image;
Data.EmbeddedTextures.Add(texture);
}
for (var j = 0; j < splitStringsList.Count; j++)
@@ -270,7 +271,7 @@ DisableAllSyncBindings();
await StopProgressBarUpdater();
HideProgressBar();
copiedAssetsCount = (copiedFontsCount + copiedBackgroundsCount + copiedSpritesCount);
-ScriptMessage(copiedAssetsCount.ToString() + " assets were copied (" + copiedSpritesCount.ToString() + " Sprites, " + copiedBackgroundsCount.ToString() + " Backgrounds, and " + copiedFontsCount.ToString() + " Fonts)");
+ScriptMessage($"{copiedAssetsCount} assets were copied ({copiedSpritesCount} Sprites, {copiedBackgroundsCount} Backgrounds, and {copiedFontsCount} Fonts)");
diff --git a/UndertaleModTool/Scripts/Resource Repackers/CopySpriteBgFontInternal.csx b/UndertaleModTool/Scripts/Resource Repackers/CopySpriteBgFontInternal.csx
index 4a1cf50eb..4c417364c 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/CopySpriteBgFontInternal.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/CopySpriteBgFontInternal.csx
@@ -11,22 +11,22 @@ EnsureDataLoaded();
// Initialization Start
var DataEmbeddedTexturesCount = Data.EmbeddedTextures.Count;
-List tex_TargetX = new List();
-List tex_TargetY = new List();
-List tex_SourceX = new List();
-List tex_SourceY = new List();
-List tex_SourceWidth = new List();
-List tex_SourceHeight = new List();
-List tex_TargetWidth = new List();
-List tex_TargetHeight = new List();
-List tex_BoundingWidth = new List();
-List tex_BoundingHeight = new List();
-List tex_Frame = new List();
-List tex_EmbeddedTextureID = new List();
-List tex_Name = new List();
-List tex_Type = new List();
-List tex_IsNull = new List();
-List TexturePageItemsUsed = new List();
+List tex_TargetX = new();
+List tex_TargetY = new();
+List tex_SourceX = new();
+List tex_SourceY = new();
+List tex_SourceWidth = new();
+List tex_SourceHeight = new();
+List tex_TargetWidth = new();
+List tex_TargetHeight = new();
+List tex_BoundingWidth = new();
+List tex_BoundingHeight = new();
+List tex_Frame = new();
+List tex_EmbeddedTextureID = new();
+List tex_Name = new();
+List tex_Type = new();
+List tex_IsNull = new();
+List TexturePageItemsUsed = new();
// Initialization End
@@ -44,7 +44,8 @@ SetProgressBar(null, "Textures Exported", 0, Data.TexturePageItems.Count);
StartProgressBarUpdater();
SyncBinding("EmbeddedTextures, Strings, Backgrounds, Sprites, Fonts, TexturePageItems", true);
-await Task.Run(() => {
+await Task.Run(() =>
+{
for (int i = 0; i < SpriteSheetsCopyNeeded.Length; i++)
{
SpriteSheetsCopyNeeded[i] = false;
@@ -57,7 +58,7 @@ await Task.Run(() => {
{
UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture();
texture.Name = new UndertaleString("Texture " + ++lastTextPage);
- texture.TextureData.TextureBlob = Data.EmbeddedTextures[i].TextureData.TextureBlob;
+ texture.TextureData.Image = Data.EmbeddedTextures[i].TextureData.Image;
Data.EmbeddedTextures.Add(texture);
}
for (var j = 0; j < splitStringsList.Count; j++)
@@ -250,7 +251,7 @@ DisableAllSyncBindings();
await StopProgressBarUpdater();
HideProgressBar();
copiedAssetsCount = (copiedFontsCount + copiedBackgroundsCount + copiedSpritesCount);
-ScriptMessage(copiedAssetsCount.ToString() + " assets were copied (" + copiedSpritesCount.ToString() + " Sprites, " + copiedBackgroundsCount.ToString() + " Backgrounds, and " + copiedFontsCount.ToString() + " Fonts)");
+ScriptMessage($"{copiedAssetsCount} assets were copied ({copiedSpritesCount} Sprites, {copiedBackgroundsCount} Backgrounds, and {copiedFontsCount} Fonts)");
// Functions
diff --git a/UndertaleModTool/Scripts/Resource Repackers/ImportAllEmbeddedTextures.csx b/UndertaleModTool/Scripts/Resource Repackers/ImportAllEmbeddedTextures.csx
index 1fd8aae1e..86297f082 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/ImportAllEmbeddedTextures.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/ImportAllEmbeddedTextures.csx
@@ -9,8 +9,7 @@ using System.Threading.Tasks;
EnsureDataLoaded();
// Setup root export folder.
-string winFolder = Path.GetDirectoryName(FilePath); // The folder data.win is located in.
-string embeddedTexturesPath = Path.Combine(winFolder, "EmbeddedTextures");
+string embeddedTexturesPath = Path.Combine(Path.GetDirectoryName(FilePath), "EmbeddedTextures");
// Folder Check One
if (!Directory.Exists(embeddedTexturesPath))
@@ -24,14 +23,17 @@ int i = 0;
foreach (var txtr in Data.EmbeddedTextures)
{
UndertaleEmbeddedTexture target = txtr as UndertaleEmbeddedTexture;
- try
+ string filename = $"{i}.png";
+ try
{
- byte[] data = File.ReadAllBytes(Path.Combine(subPath, i + ".png"));
- target.TextureData.TextureBlob = data;
+ target.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(Path.Combine(subPath, filename)))
+ .ConvertToFormat(target.TextureData.Image.Format);
}
catch (Exception ex)
{
- ScriptMessage("Failed to import file number " + i + " due to: " + ex.Message);
+ ScriptMessage($"Failed to import {filename}: {ex.Message}");
}
i++;
}
+
+ScriptMessage("Import complete.");
diff --git a/UndertaleModTool/Scripts/Resource Repackers/ImportAllTilesets.csx b/UndertaleModTool/Scripts/Resource Repackers/ImportAllTilesets.csx
index 4d214de22..a2d5956d5 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/ImportAllTilesets.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/ImportAllTilesets.csx
@@ -3,23 +3,17 @@
using System.Text;
using System;
using System.IO;
-using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
+using UndertaleModLib.Util;
+using ImageMagick;
EnsureDataLoaded();
// Setup root export folder.
-string winFolder = GetFolder(FilePath); // The folder data.win is located in.
-
-string subPath = Path.Combine(winFolder, "Export_Tilesets");
+string subPath = Path.Combine(Path.GetDirectoryName(FilePath), "Export_Tilesets");
int i = 0;
-string GetFolder(string path)
-{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
-}
-
// Folder Check One
if (!Directory.Exists(subPath))
{
@@ -34,7 +28,7 @@ await ImportTilesets();
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Import Complete.");
+ScriptMessage("Import complete.");
async Task ImportTilesets()
@@ -44,18 +38,19 @@ async Task ImportTilesets()
void ImportTileset(UndertaleBackground tileset)
{
+ string filename = $"{tileset.Name.Content}.png";
try
{
- string path = Path.Combine(subPath, tileset.Name.Content + ".png");
+ string path = Path.Combine(subPath, filename);
if (File.Exists(path))
{
- Bitmap img = new Bitmap(path);
- tileset.Texture.ReplaceTexture((Image)img);
+ using MagickImage img = TextureWorker.ReadBGRAImageFromFile(path);
+ tileset.Texture.ReplaceTexture(img);
}
}
catch (Exception ex)
{
- ScriptMessage($"Failed to import file {tileset.Name} (index - {Data.Backgrounds.IndexOf(tileset)}) due to: " + ex.Message);
+ ScriptMessage($"Failed to import {filename} (index {Data.Backgrounds.IndexOf(tileset)}): {ex.Message}");
}
IncrementProgress();
diff --git a/UndertaleModTool/Scripts/Resource Repackers/ImportFontData.csx b/UndertaleModTool/Scripts/Resource Repackers/ImportFontData.csx
index f849db582..215627112 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/ImportFontData.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/ImportFontData.csx
@@ -1,5 +1,6 @@
//Texture packer by Samuel Roy
// Uses code from https://github.com/mfascia/TexturePacker
+// TODO: this heavily uses Windows stuff, should be made cross platform
using System;
using System.IO;
@@ -36,18 +37,18 @@ string prefix = outName.Replace(Path.GetExtension(outName), "");
int atlasCount = 0;
foreach (Atlas atlas in packer.Atlasses)
{
- string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount);
+ string atlasName = $"{prefix}{atlasCount:000}.png";
Bitmap atlasBitmap = new Bitmap(atlasName);
UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture();
- texture.Name = new UndertaleString("Texture " + ++lastTextPage);
- texture.TextureData.TextureBlob = File.ReadAllBytes(atlasName);
+ texture.Name = new UndertaleString($"Texture {++lastTextPage}");
+ texture.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(atlasName)); // TODO: generate other formats
Data.EmbeddedTextures.Add(texture);
foreach (Node n in atlas.Nodes)
{
if (n.Texture != null)
{
UndertaleTexturePageItem texturePageItem = new UndertaleTexturePageItem();
- texturePageItem.Name = new UndertaleString("PageItem " + ++lastTextPageItem);
+ texturePageItem.Name = new UndertaleString($"PageItem {++lastTextPageItem}");
texturePageItem.SourceX = (ushort)n.Bounds.X;
texturePageItem.SourceY = (ushort)n.Bounds.Y;
texturePageItem.SourceWidth = (ushort)n.Bounds.Width;
@@ -93,7 +94,7 @@ ScriptMessage("Import Complete!");
public void fontUpdate(UndertaleFont newFont)
{
- using (StreamReader reader = new StreamReader(Path.Combine(sourcePath, "glyphs_" + newFont.Name.Content + ".csv")))
+ using (StreamReader reader = new StreamReader(Path.Combine(sourcePath, $"glyphs_{newFont.Name.Content}.csv")))
{
newFont.Glyphs.Clear();
string line;
@@ -254,7 +255,7 @@ public class Packer
tw.WriteLine("source_tex, atlas_tex, x, y, width, height");
foreach (Atlas atlas in Atlasses)
{
- string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount);
+ string atlasName = $"{prefix}{atlasCount:000}.png";
//1: Save images
Image img = CreateAtlasImage(atlas);
//DPI fix start
diff --git a/UndertaleModTool/Scripts/Resource Repackers/ImportGraphics.csx b/UndertaleModTool/Scripts/Resource Repackers/ImportGraphics.csx
index f2fd5e374..04b054cb9 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/ImportGraphics.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/ImportGraphics.csx
@@ -3,12 +3,12 @@
using System;
using System.IO;
-using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UndertaleModLib.Util;
+using ImageMagick;
EnsureDataLoaded();
@@ -37,24 +37,27 @@ packer.SaveAtlasses(outName);
int lastTextPage = Data.EmbeddedTextures.Count - 1;
int lastTextPageItem = Data.TexturePageItems.Count - 1;
-// Import everything into UMT
+// Import everything into UTMT
string prefix = outName.Replace(Path.GetExtension(outName), "");
int atlasCount = 0;
foreach (Atlas atlas in packer.Atlasses)
{
- string atlasName = Path.Combine(packDir, String.Format(prefix + "{0:000}" + ".png", atlasCount));
- Bitmap atlasBitmap = new Bitmap(atlasName);
- UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture();
- texture.Name = new UndertaleString("Texture " + ++lastTextPage);
- texture.TextureData.TextureBlob = File.ReadAllBytes(atlasName);
+ string atlasName = Path.Combine(packDir, $"{prefix}{atlasCount:000}.png");
+ using MagickImage atlasImage = TextureWorker.ReadBGRAImageFromFile(atlasName);
+ IPixelCollection atlasPixels = atlasImage.GetPixels();
+
+ UndertaleEmbeddedTexture texture = new();
+ texture.Name = new UndertaleString($"Texture {++lastTextPage}");
+ texture.TextureData.Image = GMImage.FromMagickImage(atlasImage).ConvertToPng(); // TODO: other formats?
Data.EmbeddedTextures.Add(texture);
+
foreach (Node n in atlas.Nodes)
{
if (n.Texture != null)
{
// Initalize values of this texture
- UndertaleTexturePageItem texturePageItem = new UndertaleTexturePageItem();
- texturePageItem.Name = new UndertaleString("PageItem " + ++lastTextPageItem);
+ UndertaleTexturePageItem texturePageItem = new();
+ texturePageItem.Name = new UndertaleString($"PageItem {++lastTextPageItem}");
texturePageItem.SourceX = (ushort)n.Bounds.X;
texturePageItem.SourceY = (ushort)n.Bounds.Y;
texturePageItem.SourceWidth = (ushort)n.Bounds.Width;
@@ -74,10 +77,9 @@ foreach (Atlas atlas in packer.Atlasses)
string stripped = Path.GetFileNameWithoutExtension(n.Texture.Source);
SpriteType spriteType = GetSpriteType(n.Texture.Source);
-
if (importAsSprite)
{
- if ((spriteType == SpriteType.Unknown) || (spriteType == SpriteType.Font))
+ if (spriteType == SpriteType.Unknown || spriteType == SpriteType.Font)
{
spriteType = SpriteType.Sprite;
}
@@ -85,7 +87,6 @@ foreach (Atlas atlas in packer.Atlasses)
setTextureTargetBounds(texturePageItem, stripped, n);
-
if (spriteType == SpriteType.Background)
{
UndertaleBackground background = Data.Backgrounds.ByName(stripped);
@@ -97,7 +98,7 @@ foreach (Atlas atlas in packer.Atlasses)
{
// No background found, let's make one
UndertaleString backgroundUTString = Data.Strings.MakeString(stripped);
- UndertaleBackground newBackground = new UndertaleBackground();
+ UndertaleBackground newBackground = new();
newBackground.Name = backgroundUTString;
newBackground.Transparent = false;
newBackground.Preload = false;
@@ -118,21 +119,20 @@ foreach (Atlas atlas in packer.Atlasses)
}
catch (Exception e)
{
- ScriptMessage("Error: Image " + stripped + " has an invalid name. Skipping...");
+ ScriptMessage($"Error: Image {stripped} has an invalid name. Skipping...");
continue;
}
- UndertaleSprite sprite = null;
- sprite = Data.Sprites.ByName(spriteName);
// Create TextureEntry object
- UndertaleSprite.TextureEntry texentry = new UndertaleSprite.TextureEntry();
+ UndertaleSprite.TextureEntry texentry = new();
texentry.Texture = texturePageItem;
// Set values for new sprites
- if (sprite == null)
+ UndertaleSprite sprite = Data.Sprites.ByName(spriteName);
+ if (sprite is null)
{
UndertaleString spriteUTString = Data.Strings.MakeString(spriteName);
- UndertaleSprite newSprite = new UndertaleSprite();
+ UndertaleSprite newSprite = new();
newSprite.Name = spriteUTString;
newSprite.Width = (uint)n.Bounds.Width;
newSprite.Height = (uint)n.Bounds.Height;
@@ -148,16 +148,14 @@ foreach (Atlas atlas in packer.Atlasses)
newSprite.Textures.Add(null);
}
newSprite.CollisionMasks.Add(newSprite.NewMaskEntry());
- Rectangle bmpRect = new Rectangle(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height);
- System.Drawing.Imaging.PixelFormat format = atlasBitmap.PixelFormat;
- Bitmap cloneBitmap = atlasBitmap.Clone(bmpRect, format);
+
int width = ((n.Bounds.Width + 7) / 8) * 8;
BitArray maskingBitArray = new BitArray(width * n.Bounds.Height);
for (int y = 0; y < n.Bounds.Height; y++)
{
for (int x = 0; x < n.Bounds.Width; x++)
{
- Color pixelColor = cloneBitmap.GetPixel(x, y);
+ IMagickColor pixelColor = atlasPixels.GetPixel(x + n.Bounds.X, y + n.Bounds.Y).ToColor();
maskingBitArray[y * width + x] = (pixelColor.A > 0);
}
}
@@ -169,16 +167,18 @@ foreach (Atlas atlas in packer.Atlasses)
tempBitArray[j + i] = maskingBitArray[-(j - 7) + i];
}
}
- int numBytes;
- numBytes = maskingBitArray.Length / 8;
+
+ int numBytes = maskingBitArray.Length / 8;
byte[] bytes = new byte[numBytes];
tempBitArray.CopyTo(bytes, 0);
for (int i = 0; i < bytes.Length; i++)
newSprite.CollisionMasks[0].Data[i] = bytes[i];
newSprite.Textures.Add(texentry);
Data.Sprites.Add(newSprite);
+
continue;
}
+
if (frame > sprite.Textures.Count - 1)
{
while (frame > sprite.Textures.Count - 1)
@@ -187,10 +187,12 @@ foreach (Atlas atlas in packer.Atlasses)
}
continue;
}
+
sprite.Textures[frame] = texentry;
}
}
}
+
// Increment atlas
atlasCount++;
}
@@ -234,9 +236,17 @@ public enum BestFitHeuristic
MaxOneAxis,
}
+public struct Rect
+{
+ public int X { get; set; }
+ public int Y { get; set; }
+ public int Width { get; set; }
+ public int Height { get; set; }
+}
+
public class Node
{
- public Rectangle Bounds;
+ public Rect Bounds;
public TextureInfo Texture;
public SplitType SplitType;
}
@@ -307,15 +317,18 @@ public class Packer
int atlasCount = 0;
string prefix = _Destination.Replace(Path.GetExtension(_Destination), "");
string descFile = _Destination;
+
StreamWriter tw = new StreamWriter(_Destination);
tw.WriteLine("source_tex, atlas_tex, x, y, width, height");
foreach (Atlas atlas in Atlasses)
{
- string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount);
- //1: Save images
- Image img = CreateAtlasImage(atlas);
- img.Save(atlasName, System.Drawing.Imaging.ImageFormat.Png);
- //2: save description in file
+ string atlasName = $"{prefix}{atlasCount:000}.png";
+
+ // 1: Save images
+ using (MagickImage img = CreateAtlasImage(atlas))
+ TextureWorker.SaveImageToFile(img, atlasName);
+
+ // 2: save description in file
foreach (Node n in atlas.Nodes)
{
if (n.Texture != null)
@@ -341,29 +354,29 @@ public class Packer
private void ScanForTextures(string _Path, string _Wildcard)
{
- DirectoryInfo di = new DirectoryInfo(_Path);
+ DirectoryInfo di = new(_Path);
FileInfo[] files = di.GetFiles(_Wildcard, SearchOption.AllDirectories);
foreach (FileInfo fi in files)
{
- Image img = Image.FromFile(fi.FullName);
- if (img != null)
+ (int width, int height) = TextureWorker.GetImageSizeFromFile(fi.FullName);
+ if (width == -1 || height == -1)
+ continue;
+
+ if (width <= AtlasSize && height <= AtlasSize)
{
- if (img.Width <= AtlasSize && img.Height <= AtlasSize)
- {
- TextureInfo ti = new TextureInfo();
+ TextureInfo ti = new();
- ti.Source = fi.FullName;
- ti.Width = img.Width;
- ti.Height = img.Height;
+ ti.Source = fi.FullName;
+ ti.Width = width;
+ ti.Height = height;
- SourceTextures.Add(ti);
+ SourceTextures.Add(ti);
- Log.WriteLine("Added " + fi.FullName);
- }
- else
- {
- Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!");
- }
+ Log.WriteLine($"Added {fi.FullName}");
+ }
+ else
+ {
+ Error.WriteLine($"{fi.FullName} is too large to fix in the atlas. Skipping!");
}
}
}
@@ -456,7 +469,8 @@ public class Packer
_Atlas.Nodes = new List();
textures = _Textures.ToList();
Node root = new Node();
- root.Bounds.Size = new Size(_Atlas.Width, _Atlas.Height);
+ root.Bounds.Width = _Atlas.Width;
+ root.Bounds.Height = _Atlas.Height;
root.SplitType = SplitType.Horizontal;
freeList.Add(root);
while (freeList.Count > 0 && textures.Count > 0)
@@ -484,24 +498,21 @@ public class Packer
return textures;
}
- private Image CreateAtlasImage(Atlas _Atlas)
+ private MagickImage CreateAtlasImage(Atlas _Atlas)
{
- Image img = new Bitmap(_Atlas.Width, _Atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
- Graphics g = Graphics.FromImage(img);
+ MagickImage img = new(MagickColors.Transparent, _Atlas.Width, _Atlas.Height);
+
foreach (Node n in _Atlas.Nodes)
{
- if (n.Texture != null)
+ if (n.Texture is not null)
{
- Image sourceImg = Image.FromFile(n.Texture.Source);
- g.DrawImage(sourceImg, n.Bounds);
+ using MagickImage sourceImg = TextureWorker.ReadBGRAImageFromFile(n.Texture.Source);
+ using IMagickImage resizedSourceImg = TextureWorker.ResizeImage(sourceImg, n.Bounds.Width, n.Bounds.Height);
+ img.Composite(resizedSourceImg, n.Bounds.X, n.Bounds.Y, CompositeOperator.Copy);
}
}
- // DPI FIX START
- Bitmap ResolutionFix = new Bitmap(img);
- ResolutionFix.SetResolution(96.0F, 96.0F);
- Image img2 = ResolutionFix;
- return img2;
- // DPI FIX END
+
+ return img;
}
}
@@ -587,9 +598,9 @@ Pressing ""No"" will cause the program to ignore these images.");
spriteName = spriteParts.Groups[1].Value;
if (!Int32.TryParse(spriteParts.Groups[2].Value, out int frame))
- throw new ScriptException(spriteName + " has an invalid frame index.");
+ throw new ScriptException($"{spriteName} has an invalid frame index.");
if (frame < 0)
- throw new ScriptException(spriteName + " is using an invalid numbering scheme. The script has stopped for your own protection.");
+ throw new ScriptException($"{spriteName} is using an invalid numbering scheme. The script has stopped for your own protection.");
// If it's not a first frame of the sprite
if (spriteName == currSpriteName)
diff --git a/UndertaleModTool/Scripts/Resource Repackers/ImportMasks.csx b/UndertaleModTool/Scripts/Resource Repackers/ImportMasks.csx
index a9ac426d8..ce34062d3 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/ImportMasks.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/ImportMasks.csx
@@ -48,7 +48,7 @@ foreach (string file in dirFiles)
throw new ScriptException(FileNameWithExtension + " is not the proper size to be imported! Please correct this before importing! The proper dimensions are width: " + Data.Sprites.ByName(spriteName).Width.ToString() + " px, height: " + Data.Sprites.ByName(spriteName).Height.ToString() + " px.");
}
- Int32 validFrameNumber = 0;
+ int validFrameNumber = 0;
try
{
validFrameNumber = Int32.Parse(stripped.Substring(lastUnderscore + 1));
@@ -75,7 +75,7 @@ foreach (string file in dirFiles)
{
throw new ScriptException(spriteName + " is using an invalid numbering scheme. The script has stopped for your own protection.");
}
- var prevFrameName = spriteName + "_" + prevframe.ToString() + ".png";
+ var prevFrameName = $"{spriteName}_{prevframe}.png";
string[] previousFrameFiles = Directory.GetFiles(importFolder, prevFrameName);
if (previousFrameFiles.Length < 1)
throw new ScriptException(spriteName + " is missing one or more indexes. The detected missing index is: " + prevFrameName);
@@ -84,13 +84,14 @@ foreach (string file in dirFiles)
SetProgressBar(null, "Files", 0, dirFiles.Length);
StartProgressBarUpdater();
-await Task.Run(() => {
+await Task.Run(() =>
+{
foreach (string file in dirFiles)
{
IncrementProgress();
string FileNameWithExtension = Path.GetFileName(file);
- if (!FileNameWithExtension.EndsWith(".png"))
+ if (!FileNameWithExtension.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
continue; // Restarts loop if file is not a valid mask asset.
string stripped = Path.GetFileNameWithoutExtension(file);
int lastUnderscore = stripped.LastIndexOf('_');
@@ -105,7 +106,8 @@ await Task.Run(() => {
}
try
{
- sprite.CollisionMasks[frame].Data = TextureWorker.ReadMaskData(file);
+ (uint maskWidth, uint maskHeight) = sprite.CalculateMaskDimensions(Data);
+ sprite.CollisionMasks[frame].Data = TextureWorker.ReadMaskData(file, (int)maskWidth, (int)maskHeight);
}
catch
{
diff --git a/UndertaleModTool/Scripts/Resource Repackers/NewTextureRepacker.csx b/UndertaleModTool/Scripts/Resource Repackers/NewTextureRepacker.csx
index ed88ba3c5..63a25d052 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/NewTextureRepacker.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/NewTextureRepacker.csx
@@ -26,11 +26,11 @@ using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.Concurrent;
-using System.Drawing;
using UndertaleModLib.Scripting;
using UndertaleModLib.Util;
using UndertaleModLib.Models;
using System.Numerics;
+using ImageMagick;
public class Rect
{
@@ -238,14 +238,13 @@ TPageItem dumpTexturePageItem(UndertaleTexturePageItem pageItem, TextureWorker w
async Task> dumpTexturePageItems(string dir, bool reuse)
{
- var worker = new TextureWorker();
+ using var worker = new TextureWorker();
var tpageitems = await Task.Run(() => Data.TexturePageItems
.AsParallel()
.Select(item => dumpTexturePageItem(item, worker, Path.Combine(dir, $"texture_page_{Data.TexturePageItems.IndexOf(item)}.png"), reuse))
.ToList());
- worker.Cleanup();
return tpageitems;
}
@@ -438,14 +437,11 @@ await Task.Run(() =>
{
// Textures that are contained into an atlas
UndertaleEmbeddedTexture tex = new UndertaleEmbeddedTexture();
- tex.Name = new UndertaleString("Texture " + ++lastTextPage);
+ tex.Name = new UndertaleString($"Texture {++lastTextPage}");
Data.EmbeddedTextures.Add(tex);
- Bitmap img = new Bitmap(atlas.Width, atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
+
+ using MagickImage newAtlasImage = new(MagickColors.Transparent, atlas.Width, atlas.Height);
- // DPI fix
- img.SetResolution(96.0F, 96.0F);
-
- Graphics g = Graphics.FromImage(img);
tex.Scaled = group.First().Scaled; // Make sure the original pane "Scaled" value is mantained.
// Dump debug info regarding splits
@@ -457,8 +453,10 @@ await Task.Run(() =>
{
f.WriteLine($"tex: {texPageItems.IndexOf(item)}: {item.NewRect.X}, {item.NewRect.Y}, {item.NewRect.Width}, {item.NewRect.Height}");
- using (Bitmap source = new Bitmap(item.Filename))
- g.DrawImage(source, item.NewRect.X, item.NewRect.Y);
+ using (MagickImage source = TextureWorker.ReadBGRAImageFromFile(item.Filename))
+ {
+ newAtlasImage.Composite(source, item.NewRect.X, item.NewRect.Y, CompositeOperator.Copy);
+ }
item.Item.TexturePage = tex;
item.Item.SourceX = (ushort)item.NewRect.X;
@@ -468,12 +466,12 @@ await Task.Run(() =>
UpdateProgress(1);
}
- // Save atlas into a file and load it back into
+ // Save atlas into a file
string atlasFile = Path.Combine(packagerDirectory, $"atlas_{atlasName}.png");
- img.Save(atlasFile, System.Drawing.Imaging.ImageFormat.Png);
- tex.TextureData.TextureBlob = File.ReadAllBytes(atlasFile);
+ TextureWorker.SaveImageToFile(newAtlasImage, atlasFile);
- img.Dispose();
+ // Assign new texture image
+ tex.TextureData.Image = GMImage.FromMagickImage(newAtlasImage).ConvertToPng(); // TODO: generate other formats
}
else
{
@@ -483,7 +481,7 @@ await Task.Run(() =>
f.WriteLine($"tex: {texPageItems.IndexOf(item)}: {0}, {0}, {item.OriginalRect.Width}, {item.OriginalRect.Height}");
UndertaleEmbeddedTexture tex = new UndertaleEmbeddedTexture();
- tex.Name = new UndertaleString("Texture " + ++lastTextPage);
+ tex.Name = new UndertaleString($"Texture {++lastTextPage}");
Data.EmbeddedTextures.Add(tex);
// Create POT texture if needed
@@ -493,24 +491,26 @@ await Task.Run(() =>
int potw = NearestPowerOf2((uint)item.OriginalRect.Width),
poth = NearestPowerOf2((uint)item.OriginalRect.Height);
- Bitmap img = new Bitmap(potw, poth, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
-
- // DPI fix
- img.SetResolution(96.0F, 96.0F);
+ using MagickImage newAtlasImage = new(MagickColors.Transparent, potw, poth);
- Graphics g = Graphics.FromImage(img);
-
- // Load texture
- using (Bitmap source = new Bitmap(item.Filename))
- g.DrawImage(source, 0, 0);
+ // Load texture, composite onto top left of new atlas
+ using (MagickImage source = TextureWorker.ReadBGRAImageFromFile(item.Filename))
+ {
+ newAtlasImage.Composite(source, 0, 0, CompositeOperator.Copy);
+ }
itemFile = Path.Combine(packagerDirectory, $"pot_{texPageItems.IndexOf(item)}.png");
- img.Save(itemFile, System.Drawing.Imaging.ImageFormat.Png);
+ TextureWorker.SaveImageToFile(newAtlasImage, itemFile);
- img.Dispose();
+ // Assign new texture image
+ tex.TextureData.Image = GMImage.FromMagickImage(newAtlasImage).ConvertToPng(); // TODO: generate other formats
+ }
+ else
+ {
+ // Load image from file, and assign it
+ tex.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(itemFile)); // TODO: generate other formats
}
- tex.TextureData.TextureBlob = File.ReadAllBytes(itemFile);
tex.Scaled = item.Scaled;
item.Item.TexturePage = tex;
diff --git a/UndertaleModTool/Scripts/Resource Repackers/ReduceEmbeddedTexturePages.csx b/UndertaleModTool/Scripts/Resource Repackers/ReduceEmbeddedTexturePages.csx
index 4ecd1ba8f..5cf218be8 100644
--- a/UndertaleModTool/Scripts/Resource Repackers/ReduceEmbeddedTexturePages.csx
+++ b/UndertaleModTool/Scripts/Resource Repackers/ReduceEmbeddedTexturePages.csx
@@ -4,7 +4,6 @@
using System;
using System.IO;
-using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -12,6 +11,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UndertaleModLib.Util;
+using ImageMagick;
EnsureDataLoaded();
@@ -26,20 +26,21 @@ foreach (DirectoryInfo di in dir.GetDirectories())
// Start export of all existing textures
-string exportedTexturesFolder = dir.FullName + Path.DirectorySeparatorChar + "Textures" + Path.DirectorySeparatorChar;
-TextureWorker worker = new TextureWorker();
-Dictionary assetCoordinateDict = new Dictionary();
-Dictionary assetTypeDict = new Dictionary();
-
-Directory.CreateDirectory(exportedTexturesFolder);
+string exportedTexturesFolder = Path.Combine(dir.FullName, "Textures");
+TextureWorker worker = null;
+Dictionary assetCoordinateDict = new();
+Dictionary assetTypeDict = new();
+using (worker = new())
+{
+ Directory.CreateDirectory(exportedTexturesFolder);
-SetProgressBar(null, "Existing Textures Exported", 0, Data.TexturePageItems.Count);
-StartProgressBarUpdater();
+ SetProgressBar(null, "Existing Textures Exported", 0, Data.TexturePageItems.Count);
+ StartProgressBarUpdater();
-await DumpSprites();
-await DumpFonts();
-await DumpBackgrounds();
-worker.Cleanup();
+ await DumpSprites();
+ await DumpFonts();
+ await DumpBackgrounds();
+}
await StopProgressBarUpdater();
HideProgressBar();
@@ -66,9 +67,9 @@ void DumpSprite(UndertaleSprite sprite)
if (sprite.Textures[i]?.Texture != null)
{
UndertaleTexturePageItem tex = sprite.Textures[i].Texture;
- worker.ExportAsPNG(tex, exportedTexturesFolder + sprite.Name.Content + "_" + i + ".png");
- assetCoordinateDict.Add(sprite.Name.Content + "_" + i, new int[] { tex.TargetX, tex.TargetY, tex.SourceWidth, tex.SourceHeight, tex.TargetWidth, tex.TargetHeight, tex.BoundingWidth, tex.BoundingHeight });
- assetTypeDict.Add(sprite.Name.Content + "_" + i, "spr");
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{sprite.Name.Content}_{i}.png"));
+ assetCoordinateDict.Add($"{sprite.Name.Content}_{i}", new int[] { tex.TargetX, tex.TargetY, tex.SourceWidth, tex.SourceHeight, tex.TargetWidth, tex.TargetHeight, tex.BoundingWidth, tex.BoundingHeight });
+ assetTypeDict.Add($"{sprite.Name.Content}_{i}", "spr");
}
}
@@ -80,7 +81,7 @@ void DumpFont(UndertaleFont font)
if (font.Texture != null)
{
UndertaleTexturePageItem tex = font.Texture;
- worker.ExportAsPNG(tex, exportedTexturesFolder + font.Name.Content + ".png");
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{font.Name.Content}.png"));
assetCoordinateDict.Add(font.Name.Content, new int[] { tex.TargetX, tex.TargetY, tex.SourceWidth, tex.SourceHeight, tex.TargetWidth, tex.TargetHeight, tex.BoundingWidth, tex.BoundingHeight });
assetTypeDict.Add(font.Name.Content, "fnt");
@@ -93,7 +94,7 @@ void DumpBackground(UndertaleBackground background)
if (background.Texture != null)
{
UndertaleTexturePageItem tex = background.Texture;
- worker.ExportAsPNG(tex, exportedTexturesFolder + background.Name.Content + ".png");
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{background.Name.Content}.png"));
assetCoordinateDict.Add(background.Name.Content, new int[] { tex.TargetX, tex.TargetY, tex.SourceWidth, tex.SourceHeight, tex.TargetWidth, tex.TargetHeight, tex.BoundingWidth, tex.BoundingHeight });
assetTypeDict.Add(background.Name.Content, "bg");
IncrementProgressParallel();
@@ -104,7 +105,7 @@ void DumpBackground(UndertaleBackground background)
string sourcePath = exportedTexturesFolder;
string searchPattern = "*.png";
-string outName = dir.FullName + Path.DirectorySeparatorChar + "atlas.txt";
+string outName = Path.Combine(dir.FullName, "atlas.txt");
int textureSize = 2048;
int PaddingValue = 2;
bool debug = false;
@@ -127,11 +128,10 @@ string prefix = outName.Replace(Path.GetExtension(outName), "");
int atlasCount = 0;
foreach (Atlas atlas in packer.Atlasses)
{
- string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount);
- Bitmap atlasBitmap = new Bitmap(atlasName);
+ string atlasName = $"{prefix}{atlasCount:000}.png";
UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture();
- texture.Name = new UndertaleString("Texture " + ++lastTextPage);
- texture.TextureData.TextureBlob = File.ReadAllBytes(atlasName);
+ texture.Name = new UndertaleString($"Texture {++lastTextPage}");
+ texture.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(atlasName)); // TODO: generate other formats
Data.EmbeddedTextures.Add(texture);
foreach (Node n in atlas.Nodes)
{
@@ -139,7 +139,7 @@ foreach (Atlas atlas in packer.Atlasses)
{
// Initalize values of this texture
UndertaleTexturePageItem texturePageItem = new UndertaleTexturePageItem();
- texturePageItem.Name = new UndertaleString("PageItem " + ++lastTextPageItem);
+ texturePageItem.Name = new UndertaleString($"PageItem {++lastTextPageItem}");
texturePageItem.SourceX = (ushort)n.Bounds.X;
texturePageItem.SourceY = (ushort)n.Bounds.Y;
texturePageItem.SourceWidth = (ushort)n.Bounds.Width;
@@ -202,7 +202,7 @@ foreach (Atlas atlas in packer.Atlasses)
}
catch (Exception e)
{
- ScriptMessage("Error: Image " + stripped + " has an invalid name. Skipping...");
+ ScriptMessage($"Error: Image {stripped} has an invalid name. Skipping...");
continue;
}
UndertaleSprite sprite = null;
@@ -273,9 +273,17 @@ public enum BestFitHeuristic
MaxOneAxis,
}
+public struct Rect
+{
+ public int X { get; set; }
+ public int Y { get; set; }
+ public int Width { get; set; }
+ public int Height { get; set; }
+}
+
public class Node
{
- public Rectangle Bounds;
+ public Rect Bounds;
public TextureInfo Texture;
public SplitType SplitType;
}
@@ -350,10 +358,10 @@ public class Packer
tw.WriteLine("source_tex, atlas_tex, x, y, width, height");
foreach (Atlas atlas in Atlasses)
{
- string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount);
+ string atlasName = $"{prefix}{atlasCount:000}.png";
//1: Save images
- Image img = CreateAtlasImage(atlas);
- img.Save(atlasName, System.Drawing.Imaging.ImageFormat.Png);
+ using MagickImage img = CreateAtlasImage(atlas);
+ TextureWorker.SaveImageToFile(img, atlasName);
//2: save description in file
foreach (Node n in atlas.Nodes)
{
@@ -384,25 +392,25 @@ public class Packer
FileInfo[] files = di.GetFiles(_Wildcard, SearchOption.AllDirectories);
foreach (FileInfo fi in files)
{
- Image img = Image.FromFile(fi.FullName);
- if (img != null)
+ (int width, int height) = TextureWorker.GetImageSizeFromFile(fi.FullName);
+ if (width == -1 || height == -1)
+ continue;
+
+ if (width <= AtlasSize && height <= AtlasSize)
{
- if (img.Width <= AtlasSize && img.Height <= AtlasSize)
- {
- TextureInfo ti = new TextureInfo();
+ TextureInfo ti = new TextureInfo();
- ti.Source = fi.FullName;
- ti.Width = img.Width;
- ti.Height = img.Height;
+ ti.Source = fi.FullName;
+ ti.Width = width;
+ ti.Height = height;
- SourceTextures.Add(ti);
+ SourceTextures.Add(ti);
- Log.WriteLine("Added " + fi.FullName);
- }
- else
- {
- Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!");
- }
+ Log.WriteLine($"Added {fi.FullName}");
+ }
+ else
+ {
+ Error.WriteLine($"{fi.FullName} is too large to fix in the atlas. Skipping!");
}
}
}
@@ -495,7 +503,8 @@ public class Packer
_Atlas.Nodes = new List();
textures = _Textures.ToList();
Node root = new Node();
- root.Bounds.Size = new Size(_Atlas.Width, _Atlas.Height);
+ root.Bounds.Width = _Atlas.Width;
+ root.Bounds.Height = _Atlas.Height;
root.SplitType = SplitType.Horizontal;
freeList.Add(root);
while (freeList.Count > 0 && textures.Count > 0)
@@ -523,23 +532,20 @@ public class Packer
return textures;
}
- private Image CreateAtlasImage(Atlas _Atlas)
+ private MagickImage CreateAtlasImage(Atlas _Atlas)
{
- Image img = new Bitmap(_Atlas.Width, _Atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
- Graphics g = Graphics.FromImage(img);
+ MagickImage img = new(MagickColors.Transparent, _Atlas.Width, _Atlas.Height);
+
foreach (Node n in _Atlas.Nodes)
{
- if (n.Texture != null)
+ if (n.Texture is not null)
{
- Image sourceImg = Image.FromFile(n.Texture.Source);
- g.DrawImage(sourceImg, n.Bounds);
+ using MagickImage sourceImg = TextureWorker.ReadBGRAImageFromFile(n.Texture.Source);
+ using IMagickImage resizedSourceImg = TextureWorker.ResizeImage(sourceImg, n.Bounds.Width, n.Bounds.Height);
+ img.Composite(resizedSourceImg, n.Bounds.X, n.Bounds.Y, CompositeOperator.Copy);
}
}
- // DPI FIX START
- Bitmap ResolutionFix = new Bitmap(img);
- ResolutionFix.SetResolution(96.0F, 96.0F);
- Image img2 = ResolutionFix;
- return img2;
- // DPI FIX END
+
+ return img;
}
}
\ No newline at end of file
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllEmbeddedTextures.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllEmbeddedTextures.csx
index 146e93967..1ff4d5a3e 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllEmbeddedTextures.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllEmbeddedTextures.csx
@@ -8,15 +8,13 @@ EnsureDataLoaded();
const string texturesName = "EmbeddedTextures";
-// The folder data.win is located in.
-string dataFolder = Path.GetDirectoryName(FilePath);
// The folder to write the image data to.
-string texturesFolder = Path.Combine(dataFolder, texturesName);
+string texturesFolder = Path.Combine(Path.GetDirectoryName(FilePath), texturesName);
if (!CanOverwrite())
return;
-MakeFolder(texturesFolder);
+Directory.CreateDirectory(texturesFolder);
SetProgressBar(null, texturesName, 0, Data.EmbeddedTextures.Count);
StartProgressBarUpdater();
@@ -27,7 +25,8 @@ await Task.Run(() =>
{
try
{
- File.WriteAllBytes(Path.Combine(texturesFolder, i + ".png"), Data.EmbeddedTextures[i].TextureData.TextureBlob);
+ using FileStream fs = new(Path.Combine(texturesFolder, $"{i}.png"), FileMode.Create);
+ Data.EmbeddedTextures[i].TextureData.Image.SavePng(fs);
}
catch (Exception ex)
{
@@ -40,29 +39,16 @@ await Task.Run(() =>
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Export Complete.\n\nLocation: " + texturesFolder);
-
-// Helper functions below. //
-
-// Gets the full directory of a file path
-string GetFolder(string path)
-{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
-}
-
-// Creates the folder, if it does not exist already
-void MakeFolder(string folder)
-{
- if (!Directory.Exists(folder))
- Directory.CreateDirectory(folder);
-}
+ScriptMessage($"Export Complete.\n\nLocation: {texturesFolder}");
// Tries to delete the texturesFolder if it doesn't exist. Returns false if the user does not want the folder deleted.
bool CanOverwrite()
{
- // Overwrite Folder Check One
- if (!Directory.Exists(texturesFolder)) return true;
+ // If folder doesn't exist, we're not overwriting anything
+ if (!Directory.Exists(texturesFolder))
+ return true;
+ // Prompt user to see if we should delete the folder
bool overwriteCheckOne = ScriptQuestion($"An '{texturesName}' folder already exists.\nWould you like to remove it? This may some time.\n\nNote: If an error window stating that 'the directory is not empty' appears, please try again or delete the folder manually.");
if (!overwriteCheckOne)
{
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllRoomsToPng.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllRoomsToPng.csx
index eed354462..c84be5393 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllRoomsToPng.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllRoomsToPng.csx
@@ -42,7 +42,6 @@ if (mainWindow.IsGMS2 == Visibility.Visible)
TileLayerTemplateSelector.ForcedMode = 1; // render tile layers as whole images
DirectoryInfo dir = new DirectoryInfo(exportedTexturesFolder);
-TextureWorker worker = new TextureWorker();
mainWindow.LastOpenedObject = mainWindow.Selected;
@@ -50,7 +49,6 @@ SetProgressBar(null, "Rooms Exported", 0, roomCount);
StartUpdater();
await DumpRooms();
-worker.Cleanup();
await StopUpdater();
HideProgressBar();
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllSprites.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllSprites.csx
index aee42baff..eecd5b557 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllSprites.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllSprites.csx
@@ -8,12 +8,11 @@ using UndertaleModLib.Util;
EnsureDataLoaded();
-bool padded = (!ScriptQuestion("Export all sprites unpadded?"));
+bool padded = (ScriptQuestion("Export sprites with padding?"));
bool useSubDirectories = ScriptQuestion("Export sprites into subdirectories?");
string texFolder = GetFolder(FilePath) + "Export_Sprites" + Path.DirectorySeparatorChar;
-TextureWorker worker = new TextureWorker();
if (Directory.Exists(texFolder))
{
ScriptError("A sprites export already exists. Please remove it.", "Error");
@@ -25,12 +24,15 @@ Directory.CreateDirectory(texFolder);
SetProgressBar(null, "Sprites", 0, Data.Sprites.Count);
StartProgressBarUpdater();
-await DumpSprites();
-worker.Cleanup();
+TextureWorker worker = null;
+using (worker = new())
+{
+ await DumpSprites();
+}
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Export Complete.\n\nLocation: " + texFolder);
+ScriptMessage($"Export Complete.\n\nLocation: {texFolder}");
string GetFolder(string path)
@@ -54,7 +56,7 @@ void DumpSprite(UndertaleSprite sprite)
for (int i = 0; i < sprite.Textures.Count; i++)
{
if (sprite.Textures[i]?.Texture != null)
- worker.ExportAsPNG(sprite.Textures[i].Texture, Path.Combine(outputFolder, sprite.Name.Content + "_" + i + ".png"), null, padded); // Include padding to make sprites look neat!
+ worker.ExportAsPNG(sprite.Textures[i].Texture, Path.Combine(outputFolder, $"{sprite.Name.Content}_{i}.png"), null, padded); // Include padding to make sprites look neat!
}
IncrementProgressParallel();
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTextures.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTextures.csx
index 88ff02a36..b98542bc9 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTextures.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTextures.csx
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -13,10 +12,10 @@ EnsureDataLoaded();
// Start export of all existing textures
-string texFolder = Path.Combine(GetFolder(FilePath), "Export_Textures");
+string texFolder = Path.Combine(Path.GetDirectoryName(FilePath), "Export_Textures");
if (Directory.Exists(texFolder))
{
- ScriptError("A sprites export already exists. Please remove it.", "Error");
+ ScriptError("A texture export already exists. Please remove it.", "Error");
return;
}
@@ -27,19 +26,21 @@ string fntFolder = Path.Combine(texFolder, "Fonts");
Directory.CreateDirectory(fntFolder);
string bgrFolder = Path.Combine(texFolder, "Backgrounds");
Directory.CreateDirectory(bgrFolder);
-TextureWorker worker = new TextureWorker();
SetProgressBar(null, "Textures Exported", 0, Data.TexturePageItems.Count);
StartProgressBarUpdater();
-await DumpSprites();
-await DumpFonts();
-await DumpBackgrounds();
-worker.Cleanup();
+TextureWorker worker = null;
+using (worker = new())
+{
+ await DumpSprites();
+ await DumpFonts();
+ await DumpBackgrounds();
+}
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Export Complete.\n\nLocation: " + texFolder);
+ScriptMessage($"Export Complete.\n\nLocation: {texFolder}");
async Task DumpSprites()
@@ -64,7 +65,7 @@ void DumpSprite(UndertaleSprite sprite)
if (sprite.Textures[i]?.Texture != null)
{
UndertaleTexturePageItem tex = sprite.Textures[i].Texture;
- worker.ExportAsPNG(tex, Path.Combine(sprFolder, sprite.Name.Content + "_" + i + ".png"));
+ worker.ExportAsPNG(tex, Path.Combine(sprFolder, $"{sprite.Name.Content}_{i}.png"));
}
}
@@ -76,7 +77,7 @@ void DumpFont(UndertaleFont font)
if (font.Texture != null)
{
UndertaleTexturePageItem tex = font.Texture;
- worker.ExportAsPNG(tex, Path.Combine(fntFolder, font.Name.Content + "_0.png"));
+ worker.ExportAsPNG(tex, Path.Combine(fntFolder, $"{font.Name.Content}_0.png"));
IncrementProgressParallel();
}
@@ -87,13 +88,8 @@ void DumpBackground(UndertaleBackground background)
if (background.Texture != null)
{
UndertaleTexturePageItem tex = background.Texture;
- worker.ExportAsPNG(tex, Path.Combine(bgrFolder, background.Name.Content + "_0.png"));
+ worker.ExportAsPNG(tex, Path.Combine(bgrFolder, $"{background.Name.Content}_0.png"));
IncrementProgressParallel();
}
}
-
-string GetFolder(string path)
-{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
-}
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTexturesGrouped.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTexturesGrouped.csx
index 47b399b59..e04ab85e3 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTexturesGrouped.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTexturesGrouped.csx
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -13,7 +12,7 @@ EnsureDataLoaded();
// Start export of all existing textures
-string texFolder = Path.Combine(GetFolder(FilePath), "Export_Textures");
+string texFolder = Path.Combine(Path.GetDirectoryName(FilePath), "Export_Textures");
if (Directory.Exists(texFolder))
{
ScriptError("A sprites export already exists. Please remove it.", "Error");
@@ -27,19 +26,21 @@ string fntFolder = Path.Combine(texFolder, "Fonts");
Directory.CreateDirectory(fntFolder);
string bgrFolder = Path.Combine(texFolder, "Backgrounds");
Directory.CreateDirectory(bgrFolder);
-TextureWorker worker = new TextureWorker();
SetProgressBar(null, "Textures Exported", 0, Data.TexturePageItems.Count);
StartProgressBarUpdater();
-await DumpSprites();
-await DumpFonts();
-await DumpBackgrounds();
-worker.Cleanup();
+TextureWorker worker = null;
+using (worker = new())
+{
+ await DumpSprites();
+ await DumpFonts();
+ await DumpBackgrounds();
+}
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Export Complete.\n\nLocation: " + texFolder);
+ScriptMessage($"Export Complete.\n\nLocation: {texFolder}");
async Task DumpSprites()
{
@@ -65,7 +66,7 @@ void DumpSprite(UndertaleSprite sprite)
UndertaleTexturePageItem tex = sprite.Textures[i].Texture;
string sprFolder2 = Path.Combine(sprFolder, sprite.Name.Content);
Directory.CreateDirectory(sprFolder2);
- worker.ExportAsPNG(tex, Path.Combine(sprFolder2, sprite.Name.Content + "_" + i + ".png"));
+ worker.ExportAsPNG(tex, Path.Combine(sprFolder2, $"{sprite.Name.Content}_{i}.png"));
}
}
@@ -79,7 +80,7 @@ void DumpFont(UndertaleFont font)
UndertaleTexturePageItem tex = font.Texture;
string fntFolder2 = Path.Combine(fntFolder, font.Name.Content);
Directory.CreateDirectory(fntFolder2);
- worker.ExportAsPNG(tex, Path.Combine(fntFolder2, font.Name.Content + "_0.png"));
+ worker.ExportAsPNG(tex, Path.Combine(fntFolder2, $"{font.Name.Content}_0.png"));
IncrementProgressParallel();
}
}
@@ -91,12 +92,7 @@ void DumpBackground(UndertaleBackground background)
UndertaleTexturePageItem tex = background.Texture;
string bgrFolder2 = Path.Combine(bgrFolder, background.Name.Content);
Directory.CreateDirectory(bgrFolder2);
- worker.ExportAsPNG(tex, Path.Combine(bgrFolder2, background.Name.Content + "_0.png"));
+ worker.ExportAsPNG(tex, Path.Combine(bgrFolder2, $"{background.Name.Content}_0.png"));
IncrementProgressParallel();
}
}
-
-string GetFolder(string path)
-{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
-}
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTilesets.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTilesets.csx
index f11e718a0..aaf7ae24c 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTilesets.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTilesets.csx
@@ -7,11 +7,10 @@ using UndertaleModLib.Util;
EnsureDataLoaded();
-string texFolder = GetFolder(FilePath) + "Export_Tilesets" + Path.DirectorySeparatorChar;
-TextureWorker worker = new TextureWorker();
+string texFolder = Path.Combine(Path.GetDirectoryName(FilePath), "Export_Tilesets");
if (Directory.Exists(texFolder))
{
- ScriptError("A texture export already exists. Please remove it.", "Error");
+ ScriptError("A tileset export already exists. Please remove it.", "Error");
return;
}
@@ -20,19 +19,15 @@ Directory.CreateDirectory(texFolder);
SetProgressBar(null, "Tilesets", 0, Data.Backgrounds.Count);
StartProgressBarUpdater();
-await DumpTilesets();
-worker.Cleanup();
-
-await StopProgressBarUpdater();
-HideProgressBar();
-ScriptMessage("Export Complete.\n\nLocation: " + texFolder);
-
-
-string GetFolder(string path)
+TextureWorker worker = null;
+using (worker = new())
{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
+ await DumpTilesets();
}
+await StopProgressBarUpdater();
+HideProgressBar();
+ScriptMessage($"Export Complete.\n\nLocation: {texFolder}");
async Task DumpTilesets()
{
@@ -42,7 +37,7 @@ async Task DumpTilesets()
void DumpTileset(UndertaleBackground tileset)
{
if (tileset.Texture != null)
- worker.ExportAsPNG(tileset.Texture, texFolder + tileset.Name.Content + ".png");
+ worker.ExportAsPNG(tileset.Texture, Path.Combine(texFolder, $"{tileset.Name.Content}.png"));
IncrementProgressParallel();
}
\ No newline at end of file
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportFontData.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportFontData.csx
index dfc52b999..21f803b71 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportFontData.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportFontData.csx
@@ -1,4 +1,5 @@
// Made by mono21400
+// TODO: this heavily uses Windows stuff, should be made cross platform
using System.Text;
using System;
@@ -11,8 +12,7 @@ using System.Windows.Forms;
EnsureDataLoaded();
-string fntFolder = GetFolder(FilePath) + "Export_Fonts" + Path.DirectorySeparatorChar;
-TextureWorker worker = new TextureWorker();
+string fntFolder = Path.Combine(Path.GetDirectoryName(FilePath), "Export_Fonts");
Directory.CreateDirectory(fntFolder);
List input = new List();
if (ShowInputDialog() == System.Windows.Forms.DialogResult.Cancel)
@@ -23,18 +23,15 @@ string[] arrayString = input.ToArray();
SetProgressBar(null, "Fonts", 0, Data.Fonts.Count);
StartProgressBarUpdater();
-await DumpFonts();
-worker.Cleanup();
+TextureWorker worker = null;
+using (worker = new())
+{
+ await DumpFonts();
+}
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Export Complete.\n\nLocation: " + fntFolder);
-
-
-string GetFolder(string path)
-{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
-}
+ScriptMessage($"Export Complete.\n\nLocation: {fntFolder}");
async Task DumpFonts()
{
@@ -45,14 +42,14 @@ void DumpFont(UndertaleFont font)
{
if (arrayString.Contains(font.Name.ToString().Replace("\"", "")))
{
- worker.ExportAsPNG(font.Texture, fntFolder + font.Name.Content + ".png");
- using (StreamWriter writer = new StreamWriter(fntFolder + "glyphs_" + font.Name.Content + ".csv"))
+ worker.ExportAsPNG(font.Texture, Path.Combine(fntFolder, $"{font.Name.Content}.png"));
+ using (StreamWriter writer = new(Path.Combine(fntFolder, $"glyphs_{font.Name.Content}.csv")))
{
- writer.WriteLine(font.DisplayName + ";" + font.EmSize + ";" + font.Bold + ";" + font.Italic + ";" + font.Charset + ";" + font.AntiAliasing + ";" + font.ScaleX + ";" + font.ScaleY);
+ writer.WriteLine($"{font.DisplayName};{font.EmSize};{font.Bold};{font.Italic};{font.Charset};{font.AntiAliasing};{font.ScaleX};{font.ScaleY}");
foreach (var g in font.Glyphs)
{
- writer.WriteLine(g.Character + ";" + g.SourceX + ";" + g.SourceY + ";" + g.SourceWidth + ";" + g.SourceHeight + ";" + g.Shift + ";" + g.Offset);
+ writer.WriteLine($"{g.Character};{g.SourceX};{g.SourceY};{g.SourceWidth};{g.SourceHeight};{g.Shift};{g.Offset}");
}
}
}
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportMasks.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportMasks.csx
index 71c01699e..9f8e30549 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportMasks.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportMasks.csx
@@ -1,6 +1,4 @@
// Made by Grossley
-// Version 1
-// 12/07/2020
using System.Text;
using System;
@@ -11,11 +9,10 @@ using UndertaleModLib.Util;
EnsureDataLoaded();
-string texFolder = GetFolder(FilePath) + "Export_Masks" + Path.DirectorySeparatorChar;
-TextureWorker worker = new TextureWorker();
+string texFolder = Path.Combine(Path.GetDirectoryName(FilePath), "Export_Masks");
if (Directory.Exists(texFolder))
{
- ScriptError("A texture export already exists. Please remove it.", "Error");
+ ScriptError("A mask export already exists. Please remove it.", "Error");
return;
}
@@ -24,18 +21,15 @@ Directory.CreateDirectory(texFolder);
SetProgressBar(null, "Sprites", 0, Data.Sprites.Count);
StartProgressBarUpdater();
-await DumpSprites();
-worker.Cleanup();
+TextureWorker worker = null;
+using (worker = new())
+{
+ await DumpSprites();
+}
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Export Complete.\n\nLocation: " + texFolder);
-
-
-string GetFolder(string path)
-{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
-}
+ScriptMessage($"Export Complete.\n\nLocation: {texFolder}");
async Task DumpSprites()
{
@@ -46,9 +40,10 @@ void DumpSprite(UndertaleSprite sprite)
{
for (int i = 0; i < sprite.CollisionMasks.Count; i++)
{
- if ((sprite.CollisionMasks[i]?.Data != null))
+ if (sprite.CollisionMasks[i]?.Data is not null)
{
- TextureWorker.ExportCollisionMaskPNG(sprite, sprite.CollisionMasks[i], texFolder + sprite.Name.Content + "_" + i + ".png");
+ (uint maskWidth, uint maskHeight) = sprite.CalculateMaskDimensions(Data);
+ TextureWorker.ExportCollisionMaskPNG(sprite.CollisionMasks[i], Path.Combine(texFolder, $"{sprite.Name.Content}_{i}.png"), (int)maskWidth, (int)maskHeight);
}
}
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportSpecificSprites.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportSpecificSprites.csx
index c9e7e52d2..8f1b39747 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportSpecificSprites.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportSpecificSprites.csx
@@ -17,8 +17,6 @@ if (texFolder is null)
Directory.CreateDirectory(Path.Combine(texFolder, "Sprites"));
texFolder = Path.Combine(texFolder, "Sprites");
-TextureWorker worker = new TextureWorker();
-
List spritesToDump = new List();
List splitStringsList = new List();
@@ -42,19 +40,20 @@ foreach (string listElement in splitStringsList)
SetProgressBar(null, "Sprites", 0, spritesToDump.Count);
StartProgressBarUpdater();
-
-
-await Task.Run(() => {
- foreach(UndertaleSprite sprToDump in spritesToDump)
+TextureWorker worker = null;
+using (worker = new())
+{
+ await Task.Run(() =>
{
- DumpSprite(sprToDump);
- }
-});
-
-worker.Cleanup();
+ foreach (UndertaleSprite sprToDump in spritesToDump)
+ {
+ DumpSprite(sprToDump);
+ }
+ });
+}
await StopProgressBarUpdater();
HideProgressBar();
-ScriptMessage("Export Complete.\n\nLocation: " + texFolder);
+ScriptMessage($"Export Complete.\n\nLocation: {texFolder}");
void DumpSprite(UndertaleSprite sprite)
{
@@ -62,7 +61,7 @@ void DumpSprite(UndertaleSprite sprite)
{
if (sprite.Textures[i]?.Texture is not null)
{
- worker.ExportAsPNG(sprite.Textures[i].Texture, Path.Combine(texFolder , sprite.Name.Content + "_" + i + ".png"), null, padded); // Include padding to make sprites look neat!
+ worker.ExportAsPNG(sprite.Textures[i].Texture, Path.Combine(texFolder, $"{sprite.Name.Content}_{i}.png"), null, padded); // Include padding to make sprites look neat!
}
}
IncrementProgress();
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportTextureGroups.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportTextureGroups.csx
index d07be5afe..53cc30efb 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExportTextureGroups.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportTextureGroups.csx
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -20,109 +19,114 @@ if (Data.TextureGroupInfo == null)
}
ScriptMessage("Exports graphics by texture group.");
bool padding = ScriptQuestion("Use padding?");
-int progress_tgin = 0;
-TextureWorker worker = new TextureWorker();
-foreach (UndertaleTextureGroupInfo tgin in Data.TextureGroupInfo)
+int processTgin = 0;
+
+string mainOutputFolder = Path.Combine(Path.GetDirectoryName(FilePath), "TextureGroups");
+Directory.CreateDirectory(mainOutputFolder);
+
+TextureWorker worker = null;
+using (worker = new())
{
- int progress = 0;
- int sum = 0;
- if (tgin.TexturePages != null)
- sum += tgin.TexturePages.Count;
- if (tgin.Sprites != null)
- sum += tgin.Sprites.Count;
- if (tgin.Fonts != null)
- sum += tgin.Fonts.Count;
- if (tgin.Tilesets != null)
- sum += tgin.Tilesets.Count;
- UpdateProgressBar(null, "Processing \"" + tgin.Name.Content + "\" (TGIN Group " + (progress_tgin++) + ")", progress, sum);
- string output_folder = Path.Combine(GetFolder(FilePath), "TextureGroups"); // The folder data.win is located in.
- Directory.CreateDirectory(output_folder);
- output_folder = Path.Combine(output_folder, tgin.Name.Content); // The folder data.win is located in.
- Directory.CreateDirectory(output_folder);
- if (tgin.TexturePages != null)
+ await Task.Run(() =>
{
- for (var i = 0; i < tgin.TexturePages.Count; i++)
+ foreach (UndertaleTextureGroupInfo tgin in Data.TextureGroupInfo)
{
- UpdateProgressBar(null, "Processing \"" + tgin.Name.Content + "\" EmbeddedTextures (TGIN Group " + (progress_tgin) + ")", progress++, sum);
- DumpEmbeddedTexturePage(output_folder, tgin.TexturePages[i].Resource);
+ int progress = 0;
+ int sum = 0;
+ if (tgin.TexturePages != null)
+ sum += tgin.TexturePages.Count;
+ if (tgin.Sprites != null)
+ sum += tgin.Sprites.Count;
+ if (tgin.Fonts != null)
+ sum += tgin.Fonts.Count;
+ if (tgin.Tilesets != null)
+ sum += tgin.Tilesets.Count;
+ UpdateProgressBar(null, $"Processing \"{tgin.Name.Content}\" (TGIN Group {processTgin++})", progress, sum);
+ string outputFolder = Path.Combine(mainOutputFolder, tgin.Name.Content); // TODO: replace invalid characters?
+ Directory.CreateDirectory(outputFolder);
+ if (tgin.TexturePages != null)
+ {
+ for (var i = 0; i < tgin.TexturePages.Count; i++)
+ {
+ UpdateProgressBar(null, $"Processing \"{tgin.Name.Content}\" EmbeddedTextures (TGIN Group {processTgin})", progress++, sum);
+ DumpEmbeddedTexturePage(outputFolder, tgin.TexturePages[i].Resource);
+ }
+ }
+ if (tgin.Sprites != null)
+ {
+ for (var i = 0; i < tgin.Sprites.Count; i++)
+ {
+ UpdateProgressBar(null, $"Processing \"{tgin.Name.Content}\" Sprites (TGIN Group {processTgin})", progress++, sum);
+ DumpSprite(outputFolder, tgin.Sprites[i].Resource);
+ }
+ }
+ if (tgin.Fonts != null)
+ {
+ for (var i = 0; i < tgin.Fonts.Count; i++)
+ {
+ UpdateProgressBar(null, $"Processing \"{tgin.Name.Content}\" Fonts (TGIN Group {processTgin})", progress++, sum);
+ DumpFont(outputFolder, tgin.Fonts[i].Resource);
+ }
+ }
+ if (tgin.Tilesets != null)
+ {
+ for (var i = 0; i < tgin.Tilesets.Count; i++)
+ {
+ UpdateProgressBar(null, $"Processing \"{tgin.Name.Content}\" Tilesets (TGIN Group {processTgin})", progress++, sum);
+ DumpTileset(outputFolder, tgin.Tilesets[i].Resource);
+ }
+ }
}
- }
- if (tgin.Sprites != null)
- {
- for (var i = 0; i < tgin.Sprites.Count; i++)
- {
- UpdateProgressBar(null, "Processing \"" + tgin.Name.Content + "\" Sprites (TGIN Group " + (progress_tgin) + ")", progress++, sum);
- DumpSprite(output_folder, tgin.Sprites[i].Resource);
- }
- }
- if (tgin.Fonts != null)
- {
- for (var i = 0; i < tgin.Fonts.Count; i++)
- {
- UpdateProgressBar(null, "Processing \"" + tgin.Name.Content + "\" Fonts (TGIN Group " + (progress_tgin) + ")", progress++, sum);
- DumpFont(output_folder, tgin.Fonts[i].Resource);
- }
- }
- if (tgin.Tilesets != null)
- {
- for (var i = 0; i < tgin.Tilesets.Count; i++)
- {
- UpdateProgressBar(null, "Processing \"" + tgin.Name.Content + "\" Tilesets (TGIN Group " + (progress_tgin) + ")", progress++, sum);
- DumpTileset(output_folder, tgin.Tilesets[i].Resource);
- }
- }
+ });
}
HideProgressBar();
ScriptMessage(@"All graphics texture groups successfully exported.
Graphics are in the ""TextureGroups"" folder in the data.win directory.");
-void DumpEmbeddedTexturePage(string output_folder, UndertaleEmbeddedTexture Emb)
+void DumpEmbeddedTexturePage(string outputFolder, UndertaleEmbeddedTexture Emb)
{
- string exportedTexturesFolder = Path.Combine(output_folder, "EmbeddedTextures");
+ string exportedTexturesFolder = Path.Combine(outputFolder, "EmbeddedTextures");
Directory.CreateDirectory(exportedTexturesFolder);
try
{
- File.WriteAllBytes(Path.Combine(exportedTexturesFolder, Data.EmbeddedTextures.IndexOf(Emb) + ".png"), Emb.TextureData.TextureBlob);
+ using (FileStream fs = new(Path.Combine(exportedTexturesFolder, $"{Data.EmbeddedTextures.IndexOf(Emb)}.png"), FileMode.Create))
+ Emb.TextureData.Image.SavePng(fs);
}
catch (Exception ex)
{
ScriptMessage("Failed to export file: " + ex.Message);
}
}
-void DumpSprite(string output_folder, UndertaleSprite Spr)
+void DumpSprite(string outputFolder, UndertaleSprite spr)
{
- for (int i = 0; i < Spr.Textures.Count; i++)
+ for (int i = 0; i < spr.Textures.Count; i++)
{
- if (Spr.Textures[i]?.Texture != null)
+ if (spr.Textures[i]?.Texture != null)
{
- string exportedTexturesFolder = Path.Combine(output_folder, "Sprites");
+ string exportedTexturesFolder = Path.Combine(outputFolder, "Sprites");
Directory.CreateDirectory(exportedTexturesFolder);
- UndertaleTexturePageItem tex = Spr.Textures[i].Texture;
- worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, Spr.Name.Content + "_" + i + ".png"), null, padding); // Include padding to make sprites look neat!
+ UndertaleTexturePageItem tex = spr.Textures[i].Texture;
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{spr.Name.Content}_{i}.png"), null, padding); // Include padding to make sprites look neat!
}
}
}
-void DumpFont(string output_folder, UndertaleFont Fnt)
+void DumpFont(string outputFolder, UndertaleFont fnt)
{
- if (Fnt.Texture != null)
+ if (fnt.Texture != null)
{
- string exportedTexturesFolder = Path.Combine(output_folder, "Fonts");
+ string exportedTexturesFolder = Path.Combine(outputFolder, "Fonts");
Directory.CreateDirectory(exportedTexturesFolder);
- UndertaleTexturePageItem tex = Fnt.Texture;
- worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, Fnt.Name.Content + ".png"));
+ UndertaleTexturePageItem tex = fnt.Texture;
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{fnt.Name.Content}.png"));
}
}
-void DumpTileset(string output_folder, UndertaleBackground Tile)
+void DumpTileset(string outputFolder, UndertaleBackground Tile)
{
if (Tile.Texture != null)
{
- string exportedTexturesFolder = Path.Combine(output_folder, "Tilesets");
+ string exportedTexturesFolder = Path.Combine(outputFolder, "Tilesets");
Directory.CreateDirectory(exportedTexturesFolder);
UndertaleTexturePageItem tex = Tile.Texture;
- worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, Tile.Name.Content + ".png"));
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{Tile.Name.Content}.png"));
}
}
-string GetFolder(string path)
-{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
-}
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExtractEmbeddedDataFile.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExtractEmbeddedDataFile.csx
index b1f4e2cd4..fb01e6e96 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/ExtractEmbeddedDataFile.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/ExtractEmbeddedDataFile.csx
@@ -4,7 +4,7 @@ using System;
using System.IO;
using UndertaleModLib.Util;
-byte[] extracted_data_file;
+byte[] extractedDataBuffer;
ScriptMessage("This script can extract an embedded data file from a YYC compiled game or from a dump file from memory.");
@@ -15,7 +15,8 @@ if (assetNamePath == null)
return;
}
-if (File.Exists(Path.GetDirectoryName(assetNamePath) + Path.DirectorySeparatorChar + "data_extracted.win"))
+string extractedDataPath = Path.Combine(Path.GetDirectoryName(assetNamePath), "data_extracted.win");
+if (File.Exists(extractedDataPath))
{
bool overwriteCheckOne = ScriptQuestion(@"A 'data_extracted.win' file already exists.
Would you like to remove it for overwriting? This may some time.
@@ -23,7 +24,7 @@ Would you like to remove it for overwriting? This may some time.
Note: If an error window appears, please try again or delete the file manually.
");
if (overwriteCheckOne)
- File.Delete(Path.GetDirectoryName(assetNamePath) + Path.DirectorySeparatorChar + "data_extracted.win");
+ File.Delete(extractedDataPath);
if (!overwriteCheckOne)
{
ScriptError("A 'data_extracted.win' file already exists. Please remove it.", "Error: Dump already exists.");
@@ -47,7 +48,7 @@ void ExtractDataFile(string assetNamePath)
uint size = reader.ReadUInt32();
reader.BaseStream.Seek(-8L, SeekOrigin.Current);
size += 8U;
- extracted_data_file = reader.ReadBytes((int)size);
+ extractedDataBuffer = reader.ReadBytes((int)size);
}
}
catch (Exception exc)
@@ -57,10 +58,9 @@ void ExtractDataFile(string assetNamePath)
" + exc.ToString(), "Locked.");
}
}
- string path = Path.GetDirectoryName(assetNamePath) + Path.DirectorySeparatorChar + "data_extracted.win";
try
{
- File.WriteAllBytes(path, extracted_data_file);
+ File.WriteAllBytes(extractedDataPath, extractedDataBuffer);
ScriptMessage("The data file has been extracted to \"data_extracted.win\" in the exe directory.");
return;
}
@@ -74,16 +74,16 @@ Error:
}
bool CheckFORMHeader(BinaryReader reader)
{
- //Check to see if the byte section is "FORM"
+ // Check to see if the byte section is "FORM"
bool isFORM = reader.ReadByte() == 0x46 && reader.ReadByte() == 0x4F && reader.ReadByte() == 0x52 && reader.ReadByte() == 0x4D;
bool isGEN8 = false;
if (isFORM)
{
- //Skip 4 bytes, GEN8 should be right after.
+ // Skip 4 bytes, GEN8 should be right after.
reader.ReadUInt32();
- //Check to see if the byte section is "GEN8".
+ // Check to see if the byte section is "GEN8".
isGEN8 = reader.ReadByte() == 0x47 && reader.ReadByte() == 0x45 && reader.ReadByte() == 0x4E && reader.ReadByte() == 0x38;
- //If it is, it's time to read it, starting from behind "FORM".
+ // If it is, it's time to read it, starting from behind "FORM".
if (isGEN8)
{
reader.BaseStream.Seek(-8L, SeekOrigin.Current);
@@ -91,9 +91,3 @@ bool CheckFORMHeader(BinaryReader reader)
}
return isFORM && isGEN8;
}
-
-string GetFolder(string path)
-{
- return Path.GetDirectoryName(path) + Path.DirectorySeparatorChar;
-}
-
diff --git a/UndertaleModTool/Scripts/Resource Unpackers/MergeImages.csx b/UndertaleModTool/Scripts/Resource Unpackers/MergeImages.csx
index 66982b35b..3c3bd02e8 100644
--- a/UndertaleModTool/Scripts/Resource Unpackers/MergeImages.csx
+++ b/UndertaleModTool/Scripts/Resource Unpackers/MergeImages.csx
@@ -1,60 +1,44 @@
using System;
using System.IO;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Drawing.Drawing2D;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UndertaleModLib.Util;
+using ImageMagick;
string importFolderA = PromptChooseDirectory();
-if (importFolderA == null) {
+if (importFolderA is null)
throw new ScriptException("The import folder was not set.");
-}
string importFolderB = PromptChooseDirectory();
-if (importFolderB == null) {
+if (importFolderB is null)
throw new ScriptException("The import folder was not set.");
-}
string exportFolder = PromptChooseDirectory();
-if (exportFolder == null) {
+if (exportFolder is null)
throw new ScriptException("The export folder was not set.");
-}
-
-string searchPattern = "*.png";
+// Loop over all PNG files in folder A
DirectoryInfo textureDirectoryA = new DirectoryInfo(importFolderA);
-DirectoryInfo textureDirectoryB = new DirectoryInfo(importFolderB);
-FileInfo[] filesA = textureDirectoryA.GetFiles(searchPattern, SearchOption.AllDirectories);
-
-foreach (FileInfo fileA in filesA) {
- FileInfo[] fileMatch = textureDirectoryB.GetFiles(fileA.Name);
- if (fileMatch == null) {
+FileInfo[] filesA = textureDirectoryA.GetFiles("*.png", SearchOption.AllDirectories);
+foreach (FileInfo fileA in filesA)
+{
+ // If there's no matching file found, abort
+ if (!File.Exists(Path.Combine(importFolderB, fileA.Name)))
continue;
- }
- FileInfo fileB = fileMatch[0];
- Bitmap bitmapA = new Bitmap(System.IO.Path.Combine(importFolderA, fileA.Name));
- Bitmap bitmapB = new Bitmap(System.IO.Path.Combine(importFolderB, fileA.Name));
- int width = bitmapA.Size.Width + bitmapB.Size.Width;
- int height = bitmapA.Size.Width;
- if (bitmapB.Size.Width > height) {
- height = bitmapB.Size.Width;
- }
-
- Bitmap outputBitmap = new Bitmap(width, height);
-
- outputBitmap = SuperimposeOntoBitmap(bitmapA, outputBitmap, 0);
- outputBitmap = SuperimposeOntoBitmap(bitmapB, outputBitmap, bitmapA.Size.Width);
-
- outputBitmap.Save(System.IO.Path.Combine(exportFolder, fileA.Name), ImageFormat.Png);
-}
-
-Bitmap SuperimposeOntoBitmap(Bitmap bitmapToAdd, Bitmap baseBitmap, int x) {
- Graphics g = Graphics.FromImage(baseBitmap);
- g.CompositingMode = CompositingMode.SourceOver;
- g.DrawImage(bitmapToAdd, new System.Drawing.Point(x, 0));
- return baseBitmap;
+
+ // Load both images, and calculate dimensions of resulting image
+ using MagickImage imageA = TextureWorker.ReadBGRAImageFromFile(Path.Combine(importFolderA, fileA.Name));
+ using MagickImage imageB = TextureWorker.ReadBGRAImageFromFile(Path.Combine(importFolderB, fileA.Name));
+ int width = imageA.Width + imageB.Width;
+ int height = Math.Max(imageA.Height, imageB.Height);
+
+ // Make combined image, and composite both images onto it
+ using MagickImage outputImage = new(MagickColor.FromRgba(0, 0, 0, 0), width, height);
+ outputImage.Composite(imageA, 0, 0, CompositeOperator.Copy);
+ outputImage.Composite(imageB, imageA.Width, 0, CompositeOperator.Copy);
+
+ // Save image to output folder
+ TextureWorker.SaveImageToFile(outputImage, Path.Combine(exportFolder, fileA.Name));
}
diff --git a/UndertaleModTool/Scripts/Technical Scripts/ImportGraphics_Full_Repack.csx b/UndertaleModTool/Scripts/Technical Scripts/ImportGraphics_Full_Repack.csx
index 7d038bea6..e182d3057 100644
--- a/UndertaleModTool/Scripts/Technical Scripts/ImportGraphics_Full_Repack.csx
+++ b/UndertaleModTool/Scripts/Technical Scripts/ImportGraphics_Full_Repack.csx
@@ -12,6 +12,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UndertaleModLib.Util;
+using ImageMagick;
EnsureDataLoaded();
@@ -41,16 +42,16 @@ foreach (string file in dirFiles)
}
catch
{
- throw new ScriptException("Getting the sprite name of " + FileNameWithExtension + " failed.");
+ throw new ScriptException($"Getting the sprite name of {FileNameWithExtension} failed.");
}
- Int32 validFrameNumber = 0;
+ int validFrameNumber = 0;
try
{
validFrameNumber = Int32.Parse(stripped.Substring(lastUnderscore + 1));
}
catch
{
- throw new ScriptException("The index of " + FileNameWithExtension + " could not be determined.");
+ throw new ScriptException($"The index of {FileNameWithExtension} could not be determined.");
}
int frame = 0;
try
@@ -59,7 +60,7 @@ foreach (string file in dirFiles)
}
catch
{
- throw new ScriptException(FileNameWithExtension + " is using letters instead of numbers. The script has stopped for your own protection.");
+ throw new ScriptException($"{FileNameWithExtension} is using letters instead of numbers. The script has stopped for your own protection.");
}
int prevframe = 0;
if (frame != 0)
@@ -68,15 +69,15 @@ foreach (string file in dirFiles)
}
if (frame < 0)
{
- throw new ScriptException(spriteName + " is using an invalid numbering scheme. The script has stopped for your own protection.");
+ throw new ScriptException($"{spriteName} is using an invalid numbering scheme. The script has stopped for your own protection.");
}
string[] dupFiles = Directory.GetFiles(importFolder, FileNameWithExtension, SearchOption.AllDirectories);
if (dupFiles.Length > 1)
- throw new ScriptException("Duplicate file detected. There are " + dupFiles.Length + " files named: " + FileNameWithExtension);
- var prevFrameName = spriteName + "_" + prevframe.ToString() + ".png";
+ throw new ScriptException($"Duplicate file detected. There are {dupFiles.Length} files named: {FileNameWithExtension}");
+ var prevFrameName = $"{spriteName}_{prevframe}.png";
string[] previousFrameFiles = Directory.GetFiles(importFolder, prevFrameName, SearchOption.AllDirectories);
if (previousFrameFiles.Length < 1)
- throw new ScriptException(spriteName + " is missing one or more indexes. The detected missing index is: " + prevFrameName);
+ throw new ScriptException($"{spriteName} is missing one or more indexes. The detected missing index is: {prevFrameName}");
}
// Get directory path
@@ -91,20 +92,21 @@ foreach (DirectoryInfo di in dir.GetDirectories())
// Start export of all existing textures
int progress = 0;
-string exportedTexturesFolder = dir.FullName + Path.DirectorySeparatorChar + "Textures" + Path.DirectorySeparatorChar;
-TextureWorker worker = new TextureWorker();
-Dictionary assetCoordinateDict = new Dictionary();
-Dictionary assetTypeDict = new Dictionary();
-
-Directory.CreateDirectory(exportedTexturesFolder);
+string exportedTexturesFolder = Path.Combine(dir.FullName, "Textures");
+TextureWorker worker = null;
+Dictionary assetCoordinateDict = new();
+Dictionary assetTypeDict = new();
+using (worker = new())
+{
+ Directory.CreateDirectory(exportedTexturesFolder);
-SetProgressBar(null, "Existing Textures Exported", 0, Data.TexturePageItems.Count);
-StartProgressBarUpdater();
+ SetProgressBar(null, "Existing Textures Exported", 0, Data.TexturePageItems.Count);
+ StartProgressBarUpdater();
-await DumpSprites();
-await DumpFonts();
-await DumpBackgrounds();
-worker.Cleanup();
+ await DumpSprites();
+ await DumpFonts();
+ await DumpBackgrounds();
+}
await StopProgressBarUpdater();
HideProgressBar();
@@ -131,9 +133,9 @@ void DumpSprite(UndertaleSprite sprite)
if (sprite.Textures[i]?.Texture != null)
{
UndertaleTexturePageItem tex = sprite.Textures[i].Texture;
- worker.ExportAsPNG(tex, exportedTexturesFolder + sprite.Name.Content + "_" + i + ".png");
- assetCoordinateDict.Add(sprite.Name.Content + "_" + i, new int[] { tex.TargetX, tex.TargetY, tex.TargetWidth, tex.TargetHeight, tex.BoundingWidth, tex.BoundingHeight });
- assetTypeDict.Add(sprite.Name.Content + "_" + i, "spr");
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{sprite.Name.Content}_{i}.png"));
+ assetCoordinateDict.Add($"{sprite.Name.Content}_{i}", new int[] { tex.TargetX, tex.TargetY, tex.TargetWidth, tex.TargetHeight, tex.BoundingWidth, tex.BoundingHeight });
+ assetTypeDict.Add($"{sprite.Name.Content}_{i}", "spr");
}
}
@@ -145,7 +147,7 @@ void DumpFont(UndertaleFont font)
if (font.Texture != null)
{
UndertaleTexturePageItem tex = font.Texture;
- worker.ExportAsPNG(tex, exportedTexturesFolder + font.Name.Content + ".png");
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{font.Name.Content}.png"));
assetCoordinateDict.Add(font.Name.Content, new int[] { tex.TargetX, tex.TargetY, tex.TargetWidth, tex.TargetHeight, tex.BoundingWidth, tex.BoundingHeight });
assetTypeDict.Add(font.Name.Content, "fnt");
@@ -158,7 +160,7 @@ void DumpBackground(UndertaleBackground background)
if (background.Texture != null)
{
UndertaleTexturePageItem tex = background.Texture;
- worker.ExportAsPNG(tex, exportedTexturesFolder + background.Name.Content + ".png");
+ worker.ExportAsPNG(tex, Path.Combine(exportedTexturesFolder, $"{background.Name.Content}.png"));
assetCoordinateDict.Add(background.Name.Content, new int[] { tex.TargetX, tex.TargetY, tex.TargetWidth, tex.TargetHeight, tex.BoundingWidth, tex.BoundingHeight });
assetTypeDict.Add(background.Name.Content, "bg");
@@ -170,7 +172,7 @@ void DumpBackground(UndertaleBackground background)
string sourcePath = exportedTexturesFolder;
string searchPattern = "*.png";
-string outName = dir.FullName + Path.DirectorySeparatorChar + "atlas.txt";
+string outName = Path.Combine(dir.FullName, "atlas.txt");
int textureSize = 2048;
int PaddingValue = 2;
bool debug = false;
@@ -190,7 +192,7 @@ foreach (FileInfo file in files)
try
{
- string[] marginLines = File.ReadAllLines(importFolder + Path.DirectorySeparatorChar + "margins.txt");
+ string[] marginLines = File.ReadAllLines(Path.Combine(importFolder, "margins.txt"));
foreach (String str in marginLines)
{
string key = str.Substring(0, str.IndexOf(','));
@@ -233,19 +235,22 @@ string prefix = outName.Replace(Path.GetExtension(outName), "");
int atlasCount = 0;
foreach (Atlas atlas in packer.Atlasses)
{
- string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount);
- Bitmap atlasBitmap = new Bitmap(atlasName);
- UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture();
- texture.Name = new UndertaleString("Texture " + ++lastTextPage);
- texture.TextureData.TextureBlob = File.ReadAllBytes(atlasName);
+ string atlasName = $"{prefix}{atlasCount:000}.png";
+ using MagickImage atlasImage = TextureWorker.ReadBGRAImageFromFile(atlasName);
+ IPixelCollection atlasPixels = atlasImage.GetPixels();
+
+ UndertaleEmbeddedTexture texture = new();
+ texture.Name = new UndertaleString($"Texture {++lastTextPage}");
+ texture.TextureData.Image = GMImage.FromMagickImage(atlasImage).ConvertToPng(); // TODO: other formats?
Data.EmbeddedTextures.Add(texture);
+
foreach (Node n in atlas.Nodes)
{
if (n.Texture != null)
{
// Initalize values of this texture
UndertaleTexturePageItem texturePageItem = new UndertaleTexturePageItem();
- texturePageItem.Name = new UndertaleString("PageItem " + ++lastTextPageItem);
+ texturePageItem.Name = new UndertaleString($"PageItem {++lastTextPageItem}");
texturePageItem.SourceX = (ushort)n.Bounds.X;
texturePageItem.SourceY = (ushort)n.Bounds.Y;
texturePageItem.SourceWidth = (ushort)n.Bounds.Width;
@@ -279,7 +284,7 @@ foreach (Atlas atlas in packer.Atlasses)
}
else
{
- ScriptMessage("Error: Image " + stripped + " has an invalid name.");
+ ScriptMessage($"Error: Image {stripped} has an invalid name.");
continue;
}
}
@@ -308,7 +313,7 @@ foreach (Atlas atlas in packer.Atlasses)
}
catch (Exception e)
{
- ScriptMessage("Error: Image " + stripped + " has an invalid name. Skipping...");
+ ScriptMessage($"Error: Image {stripped} has an invalid name. Skipping...");
continue;
}
UndertaleSprite sprite = null;
@@ -319,10 +324,10 @@ foreach (Atlas atlas in packer.Atlasses)
texentry.Texture = texturePageItem;
// Set values for new sprites
- if (sprite == null)
+ if (sprite is null)
{
UndertaleString spriteUTString = Data.Strings.MakeString(spriteName);
- UndertaleSprite newSprite = new UndertaleSprite();
+ UndertaleSprite newSprite = new();
newSprite.Name = spriteUTString;
newSprite.Width = (uint)n.Bounds.Width;
newSprite.Height = (uint)n.Bounds.Height;
@@ -338,16 +343,14 @@ foreach (Atlas atlas in packer.Atlasses)
newSprite.Textures.Add(null);
}
newSprite.CollisionMasks.Add(newSprite.NewMaskEntry());
- Rectangle bmpRect = new Rectangle(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height);
- System.Drawing.Imaging.PixelFormat format = atlasBitmap.PixelFormat;
- Bitmap cloneBitmap = atlasBitmap.Clone(bmpRect, format);
+
int width = ((n.Bounds.Width + 7) / 8) * 8;
BitArray maskingBitArray = new BitArray(width * n.Bounds.Height);
for (int y = 0; y < n.Bounds.Height; y++)
{
for (int x = 0; x < n.Bounds.Width; x++)
{
- Color pixelColor = cloneBitmap.GetPixel(x, y);
+ IMagickColor pixelColor = atlasPixels.GetPixel(x + n.Bounds.X, y + n.Bounds.Y).ToColor();
maskingBitArray[y * width + x] = (pixelColor.A > 0);
}
}
@@ -359,14 +362,15 @@ foreach (Atlas atlas in packer.Atlasses)
tempBitArray[j + i] = maskingBitArray[-(j - 7) + i];
}
}
- int numBytes;
- numBytes = maskingBitArray.Length / 8;
+
+ int numBytes = maskingBitArray.Length / 8;
byte[] bytes = new byte[numBytes];
tempBitArray.CopyTo(bytes, 0);
for (int i = 0; i < bytes.Length; i++)
newSprite.CollisionMasks[0].Data[i] = bytes[i];
newSprite.Textures.Add(texentry);
Data.Sprites.Add(newSprite);
+
continue;
}
if (frame > sprite.Textures.Count - 1)
@@ -428,9 +432,17 @@ public enum BestFitHeuristic
MaxOneAxis,
}
+public struct Rect
+{
+ public int X { get; set; }
+ public int Y { get; set; }
+ public int Width { get; set; }
+ public int Height { get; set; }
+}
+
public class Node
{
- public Rectangle Bounds;
+ public Rect Bounds;
public TextureInfo Texture;
public SplitType SplitType;
}
@@ -505,10 +517,12 @@ public class Packer
tw.WriteLine("source_tex, atlas_tex, x, y, width, height");
foreach (Atlas atlas in Atlasses)
{
- string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount);
+ string atlasName = $"{prefix}{atlasCount:000}.png";
+
//1: Save images
- Image img = CreateAtlasImage(atlas);
- img.Save(atlasName, System.Drawing.Imaging.ImageFormat.Png);
+ using (MagickImage img = CreateAtlasImage(atlas))
+ TextureWorker.SaveImageToFile(img, atlasName);
+
//2: save description in file
foreach (Node n in atlas.Nodes)
{
@@ -535,29 +549,29 @@ public class Packer
private void ScanForTextures(string _Path, string _Wildcard)
{
- DirectoryInfo di = new DirectoryInfo(_Path);
+ DirectoryInfo di = new(_Path);
FileInfo[] files = di.GetFiles(_Wildcard, SearchOption.AllDirectories);
foreach (FileInfo fi in files)
{
- Image img = Image.FromFile(fi.FullName);
- if (img != null)
+ (int width, int height) = TextureWorker.GetImageSizeFromFile(fi.FullName);
+ if (width == -1 || height == -1)
+ continue;
+
+ if (width <= AtlasSize && height <= AtlasSize)
{
- if (img.Width <= AtlasSize && img.Height <= AtlasSize)
- {
- TextureInfo ti = new TextureInfo();
+ TextureInfo ti = new();
- ti.Source = fi.FullName;
- ti.Width = img.Width;
- ti.Height = img.Height;
+ ti.Source = fi.FullName;
+ ti.Width = width;
+ ti.Height = height;
- SourceTextures.Add(ti);
+ SourceTextures.Add(ti);
- Log.WriteLine("Added " + fi.FullName);
- }
- else
- {
- Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!");
- }
+ Log.WriteLine($"Added {fi.FullName}");
+ }
+ else
+ {
+ Error.WriteLine($"{fi.FullName} is too large to fix in the atlas. Skipping!");
}
}
}
@@ -650,7 +664,8 @@ public class Packer
_Atlas.Nodes = new List();
textures = _Textures.ToList();
Node root = new Node();
- root.Bounds.Size = new Size(_Atlas.Width, _Atlas.Height);
+ root.Bounds.Width = _Atlas.Width;
+ root.Bounds.Height = _Atlas.Height;
root.SplitType = SplitType.Horizontal;
freeList.Add(root);
while (freeList.Count > 0 && textures.Count > 0)
@@ -678,23 +693,20 @@ public class Packer
return textures;
}
- private Image CreateAtlasImage(Atlas _Atlas)
+ private MagickImage CreateAtlasImage(Atlas _Atlas)
{
- Image img = new Bitmap(_Atlas.Width, _Atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
- Graphics g = Graphics.FromImage(img);
+ MagickImage img = new(MagickColors.Transparent, _Atlas.Width, _Atlas.Height);
+
foreach (Node n in _Atlas.Nodes)
{
- if (n.Texture != null)
+ if (n.Texture is not null)
{
- Image sourceImg = Image.FromFile(n.Texture.Source);
- g.DrawImage(sourceImg, n.Bounds);
+ using MagickImage sourceImg = TextureWorker.ReadBGRAImageFromFile(n.Texture.Source);
+ using IMagickImage resizedSourceImg = TextureWorker.ResizeImage(sourceImg, n.Bounds.Width, n.Bounds.Height);
+ img.Composite(resizedSourceImg, n.Bounds.X, n.Bounds.Y, CompositeOperator.Copy);
}
}
- // DPI FIX START
- Bitmap ResolutionFix = new Bitmap(img);
- ResolutionFix.SetResolution(96.0F, 96.0F);
- Image img2 = ResolutionFix;
- return img2;
- // DPI FIX END
+
+ return img;
}
}
\ No newline at end of file