Add Skin.cs and utilities to Serialize and Deserialize them

This commit is contained in:
miku-666
2024-03-08 12:31:28 +01:00
parent ea7084e959
commit 075c662e1a
13 changed files with 350 additions and 210 deletions

View File

@@ -7,8 +7,10 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OMI.Formats.Languages;
using OMI.Formats.Pck;
using OMI.Workers;
using PckStudio.Internal;
namespace PckStudio.Extensions
{
@@ -26,18 +28,107 @@ namespace PckStudio.Extensions
{
throw new Exception("File is not suitable to contain image data.");
}
using (var stream = new MemoryStream(file.Data))
using var stream = new MemoryStream(file.Data);
try
{
try
{
return Image.FromStream(stream);
}
catch(Exception ex)
{
Trace.WriteLine($"Failed to read image from pck file data({file.Filename}).", category: nameof(PckFileDataExtensions) + "." + nameof(GetTexture));
Debug.WriteLine(ex.Message);
return EmptyImage;
}
return Image.FromStream(stream);
}
catch (Exception ex)
{
Trace.TraceError($"Failed to read image from pck file data({file.Filename}).");
Debug.WriteLine(ex.Message);
return EmptyImage;
}
}
/// <summary>
/// Tries to get the skin id of the skin <paramref name="file"/>
/// </summary>
/// <param name="file"></param>
/// <returns>Non-zero base number on success, otherwise 0</returns>
/// <exception cref="InvalidOperationException"></exception>
internal static int GetSkinId(this PckFileData file)
{
if (file.Filetype != PckFileType.SkinFile)
throw new InvalidOperationException("File is not a skin file");
string filename = Path.GetFileNameWithoutExtension(file.Filename);
if (!filename.StartsWith("dlcskin"))
{
Trace.TraceWarning($"[{nameof(GetSkin)}] File does not start with 'dlcskin'");
return 0;
}
int skinId = 0;
if (!int.TryParse(filename.Substring("dlcskin".Length), out skinId))
{
Trace.TraceWarning($"[{nameof(GetSkin)}] Failed to parse Skin Id");
}
return skinId;
}
internal static Skin GetSkin(this PckFileData file)
{
if (file.Filetype != PckFileType.SkinFile)
throw new InvalidOperationException("File is not a skin file");
if (file.Properties.Contains("CAPEPATH"))
Debug.WriteLine($"[{nameof(GetSkin)}] TODO: add cape texture/path.");
int skinId = file.GetSkinId();
string name = file.Properties.GetPropertyValue("DISPLAYNAME");
Image texture = file.GetTexture();
SkinANIM anim = file.Properties.GetPropertyValue("ANIM", SkinANIM.FromString);
IEnumerable<SkinBOX> boxes = file.Properties.GetProperties("BOX").Select(kv => SkinBOX.FromString(kv.Value));
IEnumerable<SkinPartOffset> offsets = file.Properties.GetProperties("OFFSET").Select(kv => SkinPartOffset.FromString(kv.Value));
return new Skin(name, skinId, texture, anim, boxes, offsets);
}
internal static void SetSkin(this PckFileData file, Skin skin, LOCFile localizationFile)
{
if (file.Filetype != PckFileType.SkinFile)
throw new InvalidOperationException("File is not a skin file");
file.SetData(skin.Texture, ImageFormat.Png);
string skinId = skin.Id.ToString("d08");
// TODO: keep filepath
file.Filename = $"dlcskin{skinId}.png";
string skinLocKey = $"IDS_dlcskin{skinId}_DISPLAYNAME";
file.Properties.SetProperty("DISPLAYNAME", skin.Name);
file.Properties.SetProperty("DISPLAYNAMEID", skinLocKey);
localizationFile.AddLocKey(skinLocKey, skin.Name);
if (!string.IsNullOrEmpty(skin.Theme))
{
file.Properties.SetProperty("THEMENAME", skin.Theme);
file.Properties.SetProperty("THEMENAMEID", $"IDS_dlcskin{skinId}_THEMENAME");
localizationFile.AddLocKey($"IDS_dlcskin{skinId}_THEMENAME", skin.Theme);
}
if (skin.HasCape)
{
file.Properties.SetProperty("CAPEPATH", $"dlccape{skinId}.png");
}
file.Properties.SetProperty("ANIM", skin.ANIM.ToString());
file.Properties.SetProperty("GAME_FLAGS", "0x18");
file.Properties.SetProperty("FREE", "1");
file.Properties.RemoveAll(kv => kv.Key == "BOX");
file.Properties.RemoveAll(kv => kv.Key == "OFFSET");
foreach (SkinBOX box in skin.AdditionalBoxes)
{
file.Properties.Add(box.ToProperty());
}
foreach (SkinPartOffset offset in skin.PartOffsets)
{
file.Properties.Add(offset.ToProperty());
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OMI.Formats.Languages;
using OMI.Formats.Pck;
using PckStudio.Internal;
namespace PckStudio.Extensions
{
internal static class SkinExtensions
{
public static PckFileData CreateFile(this Skin skin, LOCFile localizationFile)
{
string skinId = skin.Id.ToString("d08");
PckFileData skinFile = new PckFileData($"dlcskin{skinId}.png", PckFileType.SkinFile);
string skinLocKey = $"IDS_dlcskin{skinId}_DISPLAYNAME";
skinFile.Properties.Add("DISPLAYNAME", skin.Name);
skinFile.Properties.Add("DISPLAYNAMEID", skinLocKey);
localizationFile.AddLocKey(skinLocKey, skin.Name);
if (!string.IsNullOrEmpty(skin.Theme))
{
skinFile.Properties.Add("THEMENAME", skin.Theme);
skinFile.Properties.Add("THEMENAMEID", $"IDS_dlcskin{skinId}_THEMENAME");
localizationFile.AddLocKey($"IDS_dlcskin{skinId}_THEMENAME", skin.Theme);
}
if (skin.HasCape)
{
skinFile.Properties.Add("CAPEPATH", $"dlccape{skinId}.png");
}
skinFile.Properties.Add("ANIM", skin.ANIM);
skinFile.Properties.Add("GAME_FLAGS", "0x18");
skinFile.Properties.Add("FREE", "1");
foreach (SkinBOX box in skin.AdditionalBoxes)
{
skinFile.Properties.Add(box.ToProperty());
}
foreach (SkinPartOffset offset in skin.PartOffsets)
{
skinFile.Properties.Add(offset.ToProperty());
}
skinFile.SetData(skin.Texture, ImageFormat.Png);
return skinFile;
}
public static PckFileData CreateCapeFile(this Skin skin)
{
if (!skin.HasCape)
throw new InvalidOperationException("Skin does not contain a cape.");
string skinId = skin.Id.ToString("d08");
PckFileData capeFile = new PckFileData($"dlccape{skinId}.png", PckFileType.CapeFile);
capeFile.SetData(skin.CapeTexture, ImageFormat.Png);
return capeFile;
}
}
}

View File

@@ -437,7 +437,7 @@
this.Name = "CustomSkinEditor";
this.Style = MetroFramework.MetroColorStyle.Silver;
this.Theme = MetroFramework.MetroThemeStyle.Dark;
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.generateModel_FormClosing);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.CustomSkinEditor_FormClosing);
this.contextMenuStrip1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.SizeXUpDown)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.SizeYUpDown)).EndInit();

View File

@@ -21,10 +21,12 @@ namespace PckStudio.Forms.Editor
{
public partial class CustomSkinEditor : MetroForm
{
private Image _previewImage;
public Image PreviewImage => _previewImage;
private PckFileData _file;
public Skin ResultSkin => _skin;
private Image _previewImage;
private Skin _skin;
private Random rng;
private BindingSource skinPartListBindingSource;
@@ -36,35 +38,45 @@ namespace PckStudio.Forms.Editor
PixelOffsetMode = PixelOffsetMode.HighQuality,
};
public CustomSkinEditor(PckFileData file)
private CustomSkinEditor()
{
InitializeComponent();
_file = file;
rng = new Random();
}
public CustomSkinEditor(Skin skin) : this()
{
_skin = skin;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
renderer3D1.InitializeGL();
renderer3D1.OutlineColor = Color.DarkSlateBlue;
if (_file.Size > 0)
if (_skin.Texture is not null)
{
renderer3D1.Texture = _file.GetTexture();
renderer3D1.Texture = _skin.Texture;
}
LoadModelData(_file.Properties);
renderer3D1.ANIM = _skin.ANIM;
renderer3D1.OutlineColor = Color.DarkSlateBlue;
LoadModelData();
}
private void LoadModelData(PckFileProperties properties)
private void LoadModelData()
{
renderer3D1.ANIM = properties.GetPropertyValue("ANIM", SkinANIM.FromString);
var boxProperties = properties.GetProperties("BOX");
var offsetProperties = properties.GetProperties("OFFSET");
var boxProperties = _skin.AdditionalBoxes;
var offsetProperties = _skin.PartOffsets;
skinNameLabel.Text = properties.HasProperty("DISPLAYNAME") ? properties.GetPropertyValue("DISPLAYNAME") : "";
skinNameLabel.Text = _skin.Name;
Array.ForEach(boxProperties, kv => renderer3D1.ModelData.Add(SkinBOX.FromString(kv.Value)));
Array.ForEach(offsetProperties, kv => renderer3D1.SetPartOffset(SkinPartOffset.FromString(kv.Value)));
foreach (SkinBOX box in boxProperties)
{
renderer3D1.ModelData.Add(box);
}
foreach (SkinPartOffset offset in offsetProperties)
{
renderer3D1.SetPartOffset(offset);
}
skinPartListBindingSource = new BindingSource(renderer3D1.ModelData, null);
skinPartListBox.DataSource = skinPartListBindingSource;
@@ -128,22 +140,28 @@ namespace PckStudio.Forms.Editor
private void buttonDone_Click(object sender, EventArgs e)
{
_file.Properties.RemoveAll(kv => kv.Key == "BOX");
foreach (var part in renderer3D1.ModelData)
{
_file.Properties.Add("BOX", part);
}
_previewImage = renderer3D1.GetThumbnail();
//Debug.Fail("TODO: Implement");
_skin.AdditionalBoxes.Clear();
_skin.AdditionalBoxes.AddRange(renderer3D1.ModelData);
// TODO: Get part offset list/IEnumerable from renderer
//_skin.PartOffsets.Clear();
//_skin.PartOffsets.AddRange();
//_previewImage = renderer3D1.GetThumbnail();
DialogResult = DialogResult.OK;
}
// TODO
private void buttonExportModel_Click(object sender, EventArgs e)
{
//SaveFileDialog saveFileDialog = new SaveFileDialog();
//saveFileDialog.Filter = "Custom Skin Model File | *.CSM";
//if (saveFileDialog.ShowDialog() != DialogResult.OK)
// return;
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Title = "Save Model File";
saveFileDialog.Filter = "Custom Skin Model File (*.csm,*.CSM)|*.csm;*.CSM|" +
"Custom Skin Model Binary File (*.csmb)|*.csmb|";
if (saveFileDialog.ShowDialog() != DialogResult.OK)
return;
//string contents = "";
//foreach (ListViewItem listViewItem in listViewBoxes.Items)
//{
@@ -159,12 +177,13 @@ namespace PckStudio.Forms.Editor
//File.WriteAllText(saveFileDialog.FileName, contents);
}
[Obsolete("Kept for backwards compatibility, remove later.")]
[Obsolete("Kept for backwards compatibility.")]
private void importCustomSkinButton_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Custom Skin Model File (*.csm,*.CSM)|*.csm;*.CSM|Custom Skin Model Binary File (*.csmb)|*.csmb|JSON Model File(*.json)|*.JSON;*.json";
openFileDialog.Title = "Select Model File";
openFileDialog.Filter = "Custom Skin Model File (*.csm,*.CSM)|*.csm;*.CSM|" +
"Custom Skin Model Binary File (*.csmb)|*.csmb|";
if (MessageBox.Show("Import custom model project file? Your current work will be lost!", "", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1) == DialogResult.Yes && openFileDialog.ShowDialog() == DialogResult.OK)
{
string fileExtension = Path.GetExtension(openFileDialog.FileName);
@@ -210,9 +229,8 @@ namespace PckStudio.Forms.Editor
}
}
private void generateModel_FormClosing(object sender, FormClosingEventArgs e)
{
private void CustomSkinEditor_FormClosing(object sender, FormClosingEventArgs e)
{
}
private void outlineColorButton_Click(object sender, EventArgs e)
@@ -264,7 +282,7 @@ namespace PckStudio.Forms.Editor
return;
}
generateTextureCheckBox.Checked = false;
uvPictureBox.BackgroundImage = img;
uvPictureBox.BackgroundImage = _skin.Texture = img;
}
private void skinPartListBox_DoubleClick(object sender, EventArgs e)

View File

@@ -33,7 +33,6 @@
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AddNewSkin));
System.Windows.Forms.Label label2;
System.Windows.Forms.Label label1;
this.textTheme = new System.Windows.Forms.TextBox();
this.contextMenuSkin = new System.Windows.Forms.ContextMenuStrip(this.components);
this.replaceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.contextMenuCape = new System.Windows.Forms.ContextMenuStrip(this.components);
@@ -81,11 +80,6 @@
label1.ForeColor = System.Drawing.Color.White;
label1.Name = "label1";
//
// textTheme
//
resources.ApplyResources(this.textTheme, "textTheme");
this.textTheme.Name = "textTheme";
//
// contextMenuSkin
//
this.contextMenuSkin.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -289,6 +283,7 @@
// capePictureBox
//
resources.ApplyResources(this.capePictureBox, "capePictureBox");
this.capePictureBox.BackgroundInterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
this.capePictureBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.capePictureBox.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
this.capePictureBox.Name = "capePictureBox";
@@ -298,6 +293,7 @@
// skinPictureBox
//
resources.ApplyResources(this.skinPictureBox, "skinPictureBox");
this.skinPictureBox.BackgroundInterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
this.skinPictureBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.skinPictureBox.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
this.skinPictureBox.Name = "skinPictureBox";
@@ -343,7 +339,6 @@
}
#endregion
private System.Windows.Forms.TextBox textTheme;
private System.Windows.Forms.ContextMenuStrip contextMenuSkin;
private System.Windows.Forms.ToolStripMenuItem replaceToolStripMenuItem;
private System.Windows.Forms.ContextMenuStrip contextMenuCape;

View File

@@ -12,123 +12,93 @@ using PckStudio.IO._3DST;
using PckStudio.Properties;
using PckStudio.Forms;
using PckStudio.Extensions;
using System.Linq;
using System.Diagnostics;
namespace PckStudio.Popups
{
public partial class AddNewSkin : MetroFramework.Forms.MetroForm
{
public PckFileData SkinFile => skin;
public PckFileData CapeFile => cape;
public bool HasCape => cape is not null;
public Skin NewSkin => newSkin;
//public PckFileData SkinFile => _skinFile;
//public PckFileData CapeFile => cape;
//public bool HasCape => cape is not null;
private LOCFile currentLoc;
private PckFileData skin = new PckFileData("dlcskinXYXYXYXY", PckFileType.SkinFile);
private PckFileData cape;
private SkinANIM anim = new SkinANIM();
//private PckFileData _skinFile = new PckFileData("dlcskinXYXYXYXY", PckFileType.SkinFile);
//private PckFileData cape;
private Skin newSkin;
private Random rng = new Random();
private eSkinType skinType;
private enum eSkinType
{
Invalid = -1,
_64x64,
_64x32,
_64x64HD,
_64x32HD,
Custom,
}
public AddNewSkin(LOCFile loc)
{
InitializeComponent();
currentLoc = loc;
newSkin = new Skin("", 0, Resources.classic_template, new SkinANIM(), Enumerable.Empty<SkinBOX>(), Enumerable.Empty<SkinPartOffset>());
}
private void CheckImage(Image img)
private void SetNewTexture(Image img)
{
switch (img.Height)
if (img is null)
{
case 64:
anim.SetFlag(SkinAnimFlag.RESOLUTION_64x64, true);
MessageBox.Show("64x64 Skin Detected");
skinType = eSkinType._64x64;
break;
case 32:
anim.SetFlag(SkinAnimFlag.RESOLUTION_64x64 | SkinAnimFlag.SLIM_MODEL, false);
MessageBox.Show("64x32 Skin Detected");
skinType = eSkinType._64x32;
break;
default:
if (img.Width == img.Height)
{
anim.SetFlag(SkinAnimFlag.RESOLUTION_64x64, true);
MessageBox.Show("64x64 HD Skin Detected");
skinType = eSkinType._64x64HD;
break;
}
if (img.Height == img.Width / 2)
{
anim.SetFlag(SkinAnimFlag.RESOLUTION_64x64 | SkinAnimFlag.SLIM_MODEL, false);
MessageBox.Show("64x32 HD Skin Detected");
skinType = eSkinType._64x32HD;
break;
}
MessageBox.Show("Not a Valid Skin File");
skinType = eSkinType.Invalid;
return;
Debug.Assert(false, "Image is null.");
}
if (img.Width != img.Height && img.Height != img.Width / 2)
{
MessageBox.Show("The selected image does not suit a skin texture.", "Invalid image dimensions.", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
newSkin.ANIM.SetFlag(SkinAnimFlag.RESOLUTION_64x64, img.Width == img.Height);
skinPictureBox.Image = img;
capePictureBox.Visible = true;
buttonCape.Visible = true;
capeLabel.Visible = true;
buttonDone.Enabled = true;
buttonAnimGen.Enabled = true;
skinPictureBox.Image = newSkin.Texture = img;
labelSelectTexture.Visible = false;
//capePictureBox.Visible = true;
//buttonCape.Visible = true;
//capeLabel.Visible = true;
//buttonDone.Enabled = true;
//buttonAnimGen.Enabled = true;
}
private void DrawModel()
{
bool isSlim = anim.GetFlag(SkinAnimFlag.SLIM_MODEL);
bool isSlim = newSkin.ANIM.GetFlag(SkinAnimFlag.SLIM_MODEL);
Pen outlineColor = Pens.LightGray;
Brush fillColor = Brushes.Gray;
Image previewTexture = new Bitmap(displayBox.Width, displayBox.Height);
using (Graphics g = Graphics.FromImage(previewTexture))
{
if(!anim.GetFlag(SkinAnimFlag.HEAD_DISABLED))
if(!newSkin.ANIM.GetFlag(SkinAnimFlag.HEAD_DISABLED))
{
//Head
g.DrawRectangle(outlineColor, 70, 15, 40, 40);
g.FillRectangle(fillColor, 71, 16, 39, 39);
}
if (!anim.GetFlag(SkinAnimFlag.BODY_DISABLED))
if (!newSkin.ANIM.GetFlag(SkinAnimFlag.BODY_DISABLED))
{
//Body
g.DrawRectangle(outlineColor, 70, 55, 40, 60);
g.FillRectangle(fillColor, 71, 56, 39, 59);
}
if (!anim.GetFlag(SkinAnimFlag.RIGHT_ARM_DISABLED))
if (!newSkin.ANIM.GetFlag(SkinAnimFlag.RIGHT_ARM_DISABLED))
{
//Arm0
g.DrawRectangle(outlineColor, isSlim ? 55 : 50, 55, isSlim ? 15 : 20, 60);
g.FillRectangle(fillColor , isSlim ? 56 : 51, 56, isSlim ? 14 : 19, 59);
}
if (!anim.GetFlag(SkinAnimFlag.LEFT_ARM_DISABLED))
if (!newSkin.ANIM.GetFlag(SkinAnimFlag.LEFT_ARM_DISABLED))
{
//Arm1
g.DrawRectangle(outlineColor, 110, 55, isSlim ? 15 : 20, 60);
g.FillRectangle(fillColor, 111, 56, isSlim ? 14 : 19, 59);
}
if (!anim.GetFlag(SkinAnimFlag.RIGHT_LEG_DISABLED))
if (!newSkin.ANIM.GetFlag(SkinAnimFlag.RIGHT_LEG_DISABLED))
{
//Leg0
g.DrawRectangle(outlineColor, 70, 115, 20, 60);
g.FillRectangle(fillColor, 71, 116, 19, 59);
}
if (!anim.GetFlag(SkinAnimFlag.LEFT_LEG_DISABLED))
if (!newSkin.ANIM.GetFlag(SkinAnimFlag.LEFT_LEG_DISABLED))
{
//Leg1
g.DrawRectangle(outlineColor, 90, 115, 20, 60);
@@ -158,7 +128,7 @@ namespace PckStudio.Popups
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
CheckImage(Image.FromFile(ofd.FileName));
SetNewTexture(Image.FromFile(ofd.FileName));
}
}
@@ -187,12 +157,12 @@ namespace PckStudio.Popups
using (var fs = File.OpenRead(ofd.FileName))
{
var reader = new _3DSTextureReader();
CheckImage(reader.FromStream(fs));
SetNewTexture(reader.FromStream(fs));
}
textSkinName.Text = Path.GetFileNameWithoutExtension(ofd.FileName);
return;
}
CheckImage(Image.FromFile(ofd.FileName));
SetNewTexture(Image.FromFile(ofd.FileName));
}
}
}
@@ -223,9 +193,7 @@ namespace PckStudio.Popups
MessageBox.Show("Not a Valid Cape File");
return;
}
capePictureBox.Image = Image.FromFile(ofd.FileName);
cape ??= new PckFileData("dlccapeXYXYXYXY", PckFileType.CapeFile);
cape.SetData(File.ReadAllBytes(ofd.FileName));
newSkin.CapeTexture = capePictureBox.Image = Image.FromFile(ofd.FileName);
contextMenuCape.Items[0].Text = "Replace";
capeLabel.Visible = false;
contextMenuCape.Visible = true;
@@ -235,35 +203,18 @@ namespace PckStudio.Popups
private void CreateButton_Click(object sender, EventArgs e)
{
if (!int.TryParse(textSkinID.Text, out int _skinId))
if (radioButtonManual.Checked)
{
MessageBox.Show("The Skin ID Must be a Unique 8 Digit Number Thats Not Already in Use", "Invalid Skin ID", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
if (!int.TryParse(textSkinID.Text, out int _skinId))
{
MessageBox.Show("The Skin Id must be a unique 8 digit number that is not already in use", "Invalid Skin Id", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
newSkin.Id = _skinId;
}
string skinId = _skinId.ToString("d08");
skin.Filename = $"dlcskin{skinId}.png";
string skinDisplayNameLocKey = $"IDS_dlcskin{skinId}_DISPLAYNAME";
currentLoc.AddLocKey(skinDisplayNameLocKey, textSkinName.Text);
skin.Properties.Add("DISPLAYNAME", textSkinName.Text);
skin.Properties.Add("DISPLAYNAMEID", skinDisplayNameLocKey);
if (!string.IsNullOrEmpty(textThemeName.Text))
{
skin.Properties.Add("THEMENAME", textThemeName.Text);
skin.Properties.Add("THEMENAMEID", $"IDS_dlcskin{skinId}_THEMENAME");
currentLoc.AddLocKey($"IDS_dlcskin{skinId}_THEMENAME", textThemeName.Text);
}
skin.Properties.Add("ANIM", anim);
skin.Properties.Add("GAME_FLAGS", "0x18");
skin.Properties.Add("FREE", "1");
if (HasCape)
{
cape.Filename = $"dlccape{skinId}.png";
skin.Properties.Add("CAPEPATH", cape.Filename);
}
skin.SetData(skinPictureBox.Image, ImageFormat.Png);
newSkin.Name = textSkinName.Text;
newSkin.Theme = textThemeName.Text;
DialogResult = DialogResult.OK;
Close();
}
private void textSkinID_TextChanged(object sender, EventArgs e)
@@ -274,24 +225,18 @@ namespace PckStudio.Popups
private void CreateCustomModel_Click(object sender, EventArgs e)
{
//Prompt for skin model generator
if (MessageBox.Show("Create your own custom skin model?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1) != DialogResult.Yes)
return;
skin.SetData(Resources.classic_template, ImageFormat.Png);
using CustomSkinEditor customSkinEditor = new CustomSkinEditor(newSkin);
using CustomSkinEditor generate = new CustomSkinEditor(skin);
if (generate.ShowDialog() == DialogResult.OK)
if (customSkinEditor.ShowDialog() == DialogResult.OK)
{
displayBox.Image = generate.PreviewImage;
skinPictureBox.Image = customSkinEditor.ResultSkin.Texture;
newSkin = customSkinEditor.ResultSkin;
buttonDone.Enabled = true;
labelSelectTexture.Visible = false;
if (skinType != eSkinType._64x64 && skinType != eSkinType._64x64HD)
{
buttonSkin.Location = new Point(buttonSkin.Location.X - skinPictureBox.Width, buttonSkin.Location.Y);
skinType = eSkinType._64x64;
}
DrawModel();
}
}
@@ -299,8 +244,8 @@ namespace PckStudio.Popups
{
if (radioButtonAuto.Checked)
{
int num = rng.Next(100000, 99999999);
textSkinID.Text = num.ToString();
newSkin.Id = rng.Next(100000, 99999999);
textSkinID.Text = newSkin.Id.ToString();
textSkinID.Enabled = false;
}
}
@@ -312,10 +257,10 @@ namespace PckStudio.Popups
private void buttonAnimGen_Click(object sender, EventArgs e)
{
using ANIMEditor diag = new ANIMEditor(anim.ToString());
using ANIMEditor diag = new ANIMEditor(newSkin.ANIM);
if (diag.ShowDialog(this) == DialogResult.OK)
{
anim = diag.ResultAnim;
newSkin.ANIM = diag.ResultAnim;
DrawModel();
}
}

View File

@@ -219,21 +219,6 @@
<data name="&gt;&gt;label1.ZOrder" xml:space="preserve">
<value>19</value>
</data>
<data name="textTheme.Location" type="System.Drawing.Point, System.Drawing">
<value>102, 78</value>
</data>
<data name="textTheme.Size" type="System.Drawing.Size, System.Drawing">
<value>239, 20</value>
</data>
<data name="textTheme.TabIndex" type="System.Int32, mscorlib">
<value>32</value>
</data>
<data name="&gt;&gt;textTheme.Name" xml:space="preserve">
<value>textTheme</value>
</data>
<data name="&gt;&gt;textTheme.Type" xml:space="preserve">
<value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<metadata name="contextMenuSkin.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>

View File

@@ -18,11 +18,11 @@ namespace PckStudio.Forms
private Image texture;
private IEnumerable<SkinBOX> data;
public SkinPreview(Image image, IEnumerable<SkinBOX> modelData)
public SkinPreview(Skin skin)
{
InitializeComponent();
texture = image;
data = modelData;
texture = skin.Texture;
data = skin.AdditionalBoxes;
}
protected override void OnLoad(EventArgs e)

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using OMI.Formats.Pck;
namespace PckStudio.Internal
{
public sealed class Skin
{
public string Name { get; set; }
public string Theme { get; set; }
public int Id { get; set; }
public Image Texture { get; set; }
public Image CapeTexture { get; set; }
public bool HasCape => CapeTexture is not null;
public SkinANIM ANIM { get; set; }
public List<SkinBOX> AdditionalBoxes { get; }
public List<SkinPartOffset> PartOffsets { get; }
public Skin(string name, int id, Image texture, SkinANIM anim, IEnumerable<SkinBOX> additionalBoxes, IEnumerable<SkinPartOffset> partOffsets)
{
Name = name;
Id = id;
Texture = texture;
ANIM = anim;
AdditionalBoxes = new List<SkinBOX>(additionalBoxes);
PartOffsets = new List<SkinPartOffset>(partOffsets);
}
}
}

View File

@@ -57,6 +57,10 @@ namespace PckStudio.Internal
=> IsValidANIM(value)
? new SkinANIM(Convert.ToInt32(value.TrimEnd(' ', '\n', '\r'), 16))
: new SkinANIM();
public static SkinANIM FromValue(int value) => new SkinANIM(value);
public int ToValue() => _flags.Data;
public static SkinANIM operator |(SkinANIM _this, SkinANIM other) => new SkinANIM(_this._flags.Data | other._flags.Data);

View File

@@ -7,7 +7,7 @@ using System.Text.RegularExpressions;
namespace PckStudio.Internal
{
internal readonly struct SkinPartOffset
public readonly struct SkinPartOffset
{
private static readonly Regex sWhitespace = new Regex(@"\s+");
internal static string ReplaceWhitespace(string input, string replacement)

View File

@@ -432,21 +432,17 @@ namespace PckStudio
public void HandleSkinFile(PckFileData file)
{
if (file.Properties.HasProperty("BOX"))
using CustomSkinEditor skinEditor = new CustomSkinEditor(file.GetSkin());
if (skinEditor.ShowDialog() == DialogResult.OK)
{
using CustomSkinEditor generate = new CustomSkinEditor(file);
if (generate.ShowDialog() == DialogResult.OK)
{
entryDataTextBox.Text = entryTypeTextBox.Text = string.Empty;
wasModified = true;
ReloadMetaTreeView();
}
return;
}
if (!TryGetLocFile(out var locFile))
Debug.Fail("Failed to aquire loc file.");
file.SetSkin(skinEditor.ResultSkin, locFile);
var skinPreview = new SkinPreview(file.GetTexture(), file.Properties.GetProperties("BOX").Select(kv => SkinBOX.FromString(kv.Value)));
skinPreview.ANIM = file.Properties.GetPropertyValue("ANIM", SkinANIM.FromString);
skinPreview.ShowDialog();
entryDataTextBox.Text = entryTypeTextBox.Text = string.Empty;
wasModified = true;
ReloadMetaTreeView();
}
}
public void HandleModelsFile(PckFileData file)
@@ -783,43 +779,50 @@ namespace PckStudio
MessageBox.Show("No .loc file found", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
using (AddNewSkin add = new AddNewSkin(locFile))
if (add.ShowDialog() == DialogResult.OK)
using (AddNewSkin addNewSkinDialog = new AddNewSkin(locFile))
if (addNewSkinDialog.ShowDialog() == DialogResult.OK)
{
if (currentPCK.HasFile("Skins.pck", PckFileType.SkinDataFile)) // Prioritize Skins.pck
var skinFile = addNewSkinDialog.NewSkin.CreateFile(locFile);
currentPCK.AddFile(skinFile);
if (currentPCK.HasFile("Skins.pck", PckFileType.SkinDataFile)) // Prioritize Skins.pck
{
TreeNode subPCK = treeViewMain.Nodes.Find("Skins.pck", false).FirstOrDefault();
if (subPCK.Nodes.ContainsKey("Skins")) add.SkinFile.Filename = add.SkinFile.Filename.Insert(0, "Skins/");
add.SkinFile.Filename = add.SkinFile.Filename.Insert(0, "Skins.pck/");
TreeNode newNode = new TreeNode(Path.GetFileName(add.SkinFile.Filename));
newNode.Tag = add.SkinFile;
if (subPCK.Nodes.ContainsKey("Skins"))
skinFile.Filename = skinFile.Filename.Insert(0, "Skins/");
skinFile.Filename = skinFile.Filename.Insert(0, "Skins.pck/");
TreeNode newNode = new TreeNode(Path.GetFileName(skinFile.Filename));
newNode.Tag = skinFile;
SetNodeIcon(newNode, PckFileType.SkinFile);
subPCK.Nodes.Add(newNode);
RebuildSubPCK(newNode.FullPath);
}
else
{
if (treeViewMain.Nodes.ContainsKey("Skins")) add.SkinFile.Filename = add.SkinFile.Filename.Insert(0, "Skins/"); // Then Skins folder
currentPCK.AddFile(add.SkinFile);
if (treeViewMain.Nodes.ContainsKey("Skins"))
skinFile.Filename = skinFile.Filename.Insert(0, "Skins/"); // Then Skins folder
currentPCK.AddFile(skinFile);
}
if (add.HasCape)
if (addNewSkinDialog.NewSkin.HasCape)
{
if (currentPCK.HasFile("Skins.pck", PckFileType.SkinDataFile)) // Prioritize Skins.pck
var capeFile = addNewSkinDialog.NewSkin.CreateCapeFile();
if (currentPCK.HasFile("Skins.pck", PckFileType.SkinDataFile)) // Prioritize Skins.pck
{
TreeNode subPCK = treeViewMain.Nodes.Find("Skins.pck", false).FirstOrDefault();
if (subPCK.Nodes.ContainsKey("Skins")) add.CapeFile.Filename = add.CapeFile.Filename.Insert(0, "Skins/");
add.CapeFile.Filename = add.CapeFile.Filename.Insert(0, "Skins.pck/");
TreeNode newNode = new TreeNode(Path.GetFileName(add.CapeFile.Filename));
newNode.Tag = add.CapeFile;
if (subPCK.Nodes.ContainsKey("Skins"))
capeFile.Filename = capeFile.Filename.Insert(0, "Skins/");
capeFile.Filename = capeFile.Filename.Insert(0, "Skins.pck/");
TreeNode newNode = new TreeNode(Path.GetFileName(capeFile.Filename));
newNode.Tag = capeFile;
SetNodeIcon(newNode, PckFileType.SkinFile);
subPCK.Nodes.Add(newNode);
RebuildSubPCK(newNode.FullPath);
}
else
{
if (treeViewMain.Nodes.ContainsKey("Skins")) add.CapeFile.Filename = add.CapeFile.Filename.Insert(0, "Skins/"); // Then Skins folder
currentPCK.AddFile(add.CapeFile);
if (treeViewMain.Nodes.ContainsKey("Skins"))
capeFile.Filename = capeFile.Filename.Insert(0, "Skins/"); // Then Skins folder
currentPCK.AddFile(capeFile);
}
}

View File

@@ -137,8 +137,10 @@
<Compile Include="Extensions\CursorExtensions.cs" />
<Compile Include="Extensions\PckFileDataExtensions.cs" />
<Compile Include="Extensions\SkinBOXExtensions.cs" />
<Compile Include="Extensions\SkinExtensions.cs" />
<Compile Include="Extensions\System.Numerics.cs" />
<Compile Include="Extensions\TreeNodeExtensions.cs" />
<Compile Include="Internal\Skin.cs" />
<Compile Include="Internal\SkinPartOffset.cs" />
<Compile Include="Helper\AnimationHelper.cs" />
<Compile Include="Internal\AnimationCategory.cs" />