mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-07-06 02:38:11 +00:00
Add Skin.cs and utilities to Serialize and Deserialize them
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
65
PCK-Studio/Extensions/SkinExtensions.cs
Normal file
65
PCK-Studio/Extensions/SkinExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,21 +219,6 @@
|
||||
<data name=">>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=">>textTheme.Name" xml:space="preserve">
|
||||
<value>textTheme</value>
|
||||
</data>
|
||||
<data name=">>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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
32
PCK-Studio/Internal/Skin.cs
Normal file
32
PCK-Studio/Internal/Skin.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user