diff --git a/PCK-Studio/App.config b/PCK-Studio/App.config index 521fd32a..fff538b6 100644 --- a/PCK-Studio/App.config +++ b/PCK-Studio/App.config @@ -11,6 +11,13 @@ + + + + + + + @@ -64,6 +71,9 @@ True + + False + diff --git a/PCK-Studio/Classes/IO/Sounds/SoundIO.cs b/PCK-Studio/Classes/IO/Sounds/SoundIO.cs deleted file mode 100644 index a66f2907..00000000 --- a/PCK-Studio/Classes/IO/Sounds/SoundIO.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace PckStudio.Classes.IO.Sounds -{ - public class SoundIO - { - public Dictionary Read(string Filepath) - { - var jObj = (Newtonsoft.Json.Linq.JObject)JsonConvert.DeserializeObject(File.ReadAllText(Filepath)); - var dict = JsonConvert.DeserializeObject>(jObj.ToString()); - - return dict; - } - - public string Serialize(Dictionary input) - { - return JsonConvert.SerializeObject(input, Formatting.Indented); - } - } -} diff --git a/PCK-Studio/Classes/IO/Sounds/Sounds.cs b/PCK-Studio/Classes/IO/Sounds/Sounds.cs deleted file mode 100644 index b5350b69..00000000 --- a/PCK-Studio/Classes/IO/Sounds/Sounds.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PckStudio.Classes.IO.Sounds -{ - public class SoundInfo - { - public bool replace { get; set; } - public List sounds = new List(); - } - - public class Sound - { - public string name { get; set; } - public string type { get; set; } - public bool stream { get; set; } - } -} diff --git a/PCK-Studio/Controls/PckEditor.cs b/PCK-Studio/Controls/PckEditor.cs index c4768716..c65e15ce 100644 --- a/PCK-Studio/Controls/PckEditor.cs +++ b/PCK-Studio/Controls/PckEditor.cs @@ -42,24 +42,24 @@ namespace PckStudio.Controls private bool _isTemplateFile = false; private int _timesSaved = 0; - private readonly Dictionary> pckFileTypeHandler; + private readonly Dictionary> pckFileTypeHandler; public PckEditor() { InitializeComponent(); - skinToolStripMenuItem1.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.SkinFile); - capeToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.CapeFile); - textureToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.TextureFile); - languagesFileLOCToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.LocalisationFile); - gameRulesFileGRFToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.GameRulesFile); - audioPCKFileToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.AudioFile); - coloursCOLFileToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.ColourTableFile); - gameRulesHeaderGRHToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.GameRulesHeader); - skinsPCKToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.SkinDataFile); - modelsFileBINToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.ModelsFile); - behavioursFileBINToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.BehavioursFile); - entityMaterialsFileBINToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFile.FileData.FileType.MaterialFile); + skinToolStripMenuItem1.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.SkinFile); + capeToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.CapeFile); + textureToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.TextureFile); + languagesFileLOCToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.LocalisationFile); + gameRulesFileGRFToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.GameRulesFile); + audioPCKFileToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.AudioFile); + coloursCOLFileToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.ColourTableFile); + gameRulesHeaderGRHToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.GameRulesHeader); + skinsPCKToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.SkinDataFile); + modelsFileBINToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.ModelsFile); + behavioursFileBINToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.BehavioursFile); + entityMaterialsFileBINToolStripMenuItem.Click += (sender, e) => setFileType_Click(sender, e, PckFileType.MaterialFile); imageList.Images.Add(Resources.ZZFolder); // Icon for folders imageList.Images.Add(Resources.BINKA_ICON); // Icon for music cue file (audio.pck) @@ -79,23 +79,23 @@ namespace PckStudio.Controls imageList.Images.Add(Resources.BEHAVIOURS_ICON); // Icon for Behaviour files (behaviours.bin) imageList.Images.Add(Resources.ENTITY_MATERIALS_ICON); // Icon for Entity Material files (entityMaterials.bin) - pckFileTypeHandler = new Dictionary>(15) + pckFileTypeHandler = new Dictionary>(15) { - [PckFile.FileData.FileType.SkinFile] = HandleSkinFile, - [PckFile.FileData.FileType.CapeFile] = null, - [PckFile.FileData.FileType.TextureFile] = HandleTextureFile, - [PckFile.FileData.FileType.UIDataFile] = _ => throw new NotSupportedException("unused in-game"), - [PckFile.FileData.FileType.InfoFile] = null, - [PckFile.FileData.FileType.TexturePackInfoFile] = null, - [PckFile.FileData.FileType.LocalisationFile] = HandleLocalisationFile, - [PckFile.FileData.FileType.GameRulesFile] = HandleGameRuleFile, - [PckFile.FileData.FileType.AudioFile] = HandleAudioFile, - [PckFile.FileData.FileType.ColourTableFile] = HandleColourFile, - [PckFile.FileData.FileType.GameRulesHeader] = HandleGameRuleFile, - [PckFile.FileData.FileType.SkinDataFile] = null, - [PckFile.FileData.FileType.ModelsFile] = HandleModelsFile, - [PckFile.FileData.FileType.BehavioursFile] = HandleBehavioursFile, - [PckFile.FileData.FileType.MaterialFile] = HandleMaterialFile, + [PckFileType.SkinFile] = HandleSkinFile, + [PckFileType.CapeFile] = null, + [PckFileType.TextureFile] = HandleTextureFile, + [PckFileType.UIDataFile] = _ => throw new NotSupportedException("unused in-game"), + [PckFileType.InfoFile] = null, + [PckFileType.TexturePackInfoFile] = null, + [PckFileType.LocalisationFile] = HandleLocalisationFile, + [PckFileType.GameRulesFile] = HandleGameRuleFile, + [PckFileType.AudioFile] = HandleAudioFile, + [PckFileType.ColourTableFile] = HandleColourFile, + [PckFileType.GameRulesHeader] = HandleGameRuleFile, + [PckFileType.SkinDataFile] = null, + [PckFileType.ModelsFile] = HandleModelsFile, + [PckFileType.BehavioursFile] = HandleBehavioursFile, + [PckFileType.MaterialFile] = HandleMaterialFile, }; } @@ -115,7 +115,7 @@ namespace PckStudio.Controls private void CheckForPasswordAndRemove() { - if (_pck.TryGetFile("0", PckFile.FileData.FileType.InfoFile, out PckFile.FileData file)) + if (_pck.TryGetFile("0", PckFileType.InfoFile, out PckFileData file)) { file.Properties.RemoveAll(t => t.Key.Equals("LOCK")); } @@ -140,15 +140,15 @@ namespace PckStudio.Controls private void BuildPckTreeView(TreeNodeCollection root, PckFile pckFile, string parentPath = "") { - foreach (var file in pckFile.Files) + foreach (var file in pckFile.GetFiles()) { // fix any file paths that may be incorrect - if (file.Filename.StartsWith(parentPath)) - file.Filename = file.Filename.Remove(0, parentPath.Length); + //if (file.Filename.StartsWith(parentPath)) + // file.Filename = file.Filename.Remove(0, parentPath.Length); TreeNode node = BuildNodeTreeBySeperator(root, file.Filename, '/'); node.Tag = file; if (Settings.Default.LoadSubPcks && - (file.Filetype == PckFile.FileData.FileType.SkinDataFile || file.Filetype == PckFile.FileData.FileType.TexturePackInfoFile) && + (file.Filetype == PckFileType.SkinDataFile || file.Filetype == PckFileType.TexturePackInfoFile) && file.Data.Length > 0) { using (var stream = new MemoryStream(file.Data)) @@ -173,63 +173,63 @@ namespace PckStudio.Controls }; } - private void SetPckFileIcon(TreeNode node, PckFile.FileData.FileType type) + private void SetPckFileIcon(TreeNode node, PckFileType type) { switch (type) { - case PckFile.FileData.FileType.AudioFile: + case PckFileType.AudioFile: node.ImageIndex = 1; node.SelectedImageIndex = 1; break; - case PckFile.FileData.FileType.LocalisationFile: + case PckFileType.LocalisationFile: node.ImageIndex = 3; node.SelectedImageIndex = 3; break; - case PckFile.FileData.FileType.TexturePackInfoFile: + case PckFileType.TexturePackInfoFile: node.ImageIndex = 4; node.SelectedImageIndex = 4; break; - case PckFile.FileData.FileType.ColourTableFile: + case PckFileType.ColourTableFile: node.ImageIndex = 6; node.SelectedImageIndex = 6; break; - case PckFile.FileData.FileType.ModelsFile: + case PckFileType.ModelsFile: node.ImageIndex = 8; node.SelectedImageIndex = 8; break; - case PckFile.FileData.FileType.SkinDataFile: + case PckFileType.SkinDataFile: node.ImageIndex = 7; node.SelectedImageIndex = 7; break; - case PckFile.FileData.FileType.GameRulesFile: + case PckFileType.GameRulesFile: node.ImageIndex = 9; node.SelectedImageIndex = 9; break; - case PckFile.FileData.FileType.GameRulesHeader: + case PckFileType.GameRulesHeader: node.ImageIndex = 10; node.SelectedImageIndex = 10; break; - case PckFile.FileData.FileType.InfoFile: + case PckFileType.InfoFile: node.ImageIndex = 11; node.SelectedImageIndex = 11; break; - case PckFile.FileData.FileType.SkinFile: + case PckFileType.SkinFile: node.ImageIndex = 12; node.SelectedImageIndex = 12; break; - case PckFile.FileData.FileType.CapeFile: + case PckFileType.CapeFile: node.ImageIndex = 13; node.SelectedImageIndex = 13; break; - case PckFile.FileData.FileType.TextureFile: + case PckFileType.TextureFile: node.ImageIndex = 14; node.SelectedImageIndex = 14; break; - case PckFile.FileData.FileType.BehavioursFile: + case PckFileType.BehavioursFile: node.ImageIndex = 15; node.SelectedImageIndex = 15; break; - case PckFile.FileData.FileType.MaterialFile: + case PckFileType.MaterialFile: node.ImageIndex = 16; node.SelectedImageIndex = 16; break; @@ -249,7 +249,7 @@ namespace PckStudio.Controls treeViewMain.Nodes.Clear(); BuildPckTreeView(treeViewMain.Nodes, _pck); - if (_isTemplateFile && _pck.HasFile("Skins.pck", PckFile.FileData.FileType.SkinDataFile)) + if (_isTemplateFile && _pck.HasFile("Skins.pck", PckFileType.SkinDataFile)) { TreeNode skinsNode = treeViewMain.Nodes.Find("Skins.pck", false).FirstOrDefault(); TreeNode folderNode = CreateNode("Skins"); @@ -304,9 +304,9 @@ namespace PckStudio.Controls foreach (var node in s) { TreeNode parent = treeViewMain.Nodes.Find(node, true)[0]; - if (parent.Tag is PckFile.FileData f && - (f.Filetype is PckFile.FileData.FileType.TexturePackInfoFile || - f.Filetype is PckFile.FileData.FileType.SkinDataFile)) + if (parent.Tag is PckFileData f && + (f.Filetype is PckFileType.TexturePackInfoFile || + f.Filetype is PckFileType.SkinDataFile)) return parent; } @@ -323,17 +323,17 @@ namespace PckStudio.Controls Console.WriteLine(parent.Name); if (parent == null) return; - PckFile.FileData parent_file = parent.Tag as PckFile.FileData; - if (parent_file.Filetype is PckFile.FileData.FileType.TexturePackInfoFile || parent_file.Filetype is PckFile.FileData.FileType.SkinDataFile) + PckFileData parent_file = parent.Tag as PckFileData; + if (parent_file.Filetype is PckFileType.TexturePackInfoFile || parent_file.Filetype is PckFileType.SkinDataFile) { Console.WriteLine("Rebuilding " + parent_file.Filename); - PckFile newPCKFile = new PckFile(3, parent_file.Filetype is PckFile.FileData.FileType.SkinDataFile); + PckFile newPCKFile = new PckFile(3, parent_file.Filetype is PckFileType.SkinDataFile); foreach (TreeNode node in GetAllChildNodes(parent.Nodes)) { - if (node.Tag is PckFile.FileData node_file) + if (node.Tag is PckFileData node_file) { - PckFile.FileData new_file = newPCKFile.CreateNewFile(node_file.Filename.Replace(parent_file.Filename + "/", String.Empty), node_file.Filetype); + PckFileData new_file = newPCKFile.CreateNewFile(node_file.Filename.Replace(parent_file.Filename + "/", String.Empty), node_file.Filetype); foreach (var prop in node_file.Properties) new_file.Properties.Add(prop); new_file.SetData(node_file.Data); } @@ -353,8 +353,8 @@ namespace PckStudio.Controls private bool TryGetLocFile(out LOCFile locFile) { - if (!_pck.TryGetFile("localisation.loc", PckFile.FileData.FileType.LocalisationFile, out PckFile.FileData locdata) && - !_pck.TryGetFile("languages.loc", PckFile.FileData.FileType.LocalisationFile, out locdata)) + if (!_pck.TryGetFile("localisation.loc", PckFileType.LocalisationFile, out PckFileData locdata) && + !_pck.TryGetFile("languages.loc", PckFileType.LocalisationFile, out locdata)) { locFile = null; return false; @@ -379,8 +379,8 @@ namespace PckStudio.Controls private bool TrySetLocFile(in LOCFile locFile) { - if (!_pck.TryGetFile("localisation.loc", PckFile.FileData.FileType.LocalisationFile, out PckFile.FileData locdata) && - !_pck.TryGetFile("languages.loc", PckFile.FileData.FileType.LocalisationFile, out locdata)) + if (!_pck.TryGetFile("localisation.loc", PckFileType.LocalisationFile, out PckFileData locdata) && + !_pck.TryGetFile("languages.loc", PckFileType.LocalisationFile, out locdata)) { return false; } @@ -406,7 +406,7 @@ namespace PckStudio.Controls { treeMeta.Nodes.Clear(); if (treeViewMain.SelectedNode is TreeNode node && - node.Tag is PckFile.FileData file) + node.Tag is PckFileData file) { foreach (var property in file.Properties) { @@ -415,13 +415,13 @@ namespace PckStudio.Controls } } - private static PckFile.FileData CreateNewAudioFile(bool isLittle) + private static PckFileData CreateNewAudioFile(bool isLittle) { PckAudioFile audioPck = new PckAudioFile(); audioPck.AddCategory(PckAudioFile.AudioCategory.EAudioType.Overworld); audioPck.AddCategory(PckAudioFile.AudioCategory.EAudioType.Nether); audioPck.AddCategory(PckAudioFile.AudioCategory.EAudioType.End); - PckFile.FileData pckFileData = new PckFile.FileData("audio.pck", PckFile.FileData.FileType.AudioFile); + PckFileData pckFileData = new PckFileData("audio.pck", PckFileType.AudioFile); pckFileData.SetData(new PckAudioFileWriter(audioPck, isLittle ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian)); return pckFileData; } @@ -452,7 +452,7 @@ namespace PckStudio.Controls using AddFilePrompt diag = new AddFilePrompt("res/" + Path.GetFileName(ofd.FileName)); if (diag.ShowDialog(this) == DialogResult.OK) { - PckFile.FileData file = _pck.CreateNewFile( + PckFileData file = _pck.CreateNewFile( diag.Filepath, diag.Filetype, () => File.ReadAllBytes(ofd.FileName)); @@ -477,7 +477,7 @@ namespace PckStudio.Controls renamePrompt.LabelText = "Path"; if (renamePrompt.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(renamePrompt.NewText)) { - var file = _pck.CreateNewFile(renamePrompt.NewText, PckFile.FileData.FileType.TextureFile); + var file = _pck.CreateNewFile(renamePrompt.NewText, PckFileType.TextureFile); file.SetData(File.ReadAllBytes(fileDialog.FileName)); BuildMainTreeView(); _wasModified = true; @@ -496,7 +496,7 @@ namespace PckStudio.Controls { string skinNameImport = Path.GetFileName(contents.FileName); byte[] data = File.ReadAllBytes(contents.FileName); - PckFile.FileData mfNew = _pck.CreateNewFile(skinNameImport, PckFile.FileData.FileType.SkinFile); + PckFileData mfNew = _pck.CreateNewFile(skinNameImport, PckFileType.SkinFile); mfNew.SetData(data); string propertyFile = Path.GetFileNameWithoutExtension(contents.FileName) + ".txt"; if (File.Exists(propertyFile)) @@ -554,9 +554,9 @@ namespace PckStudio.Controls TreeNodeCollection nodeCollection = treeViewMain.Nodes; if (treeViewMain.SelectedNode is TreeNode node) { - if (node.Tag is PckFile.FileData fd && - (fd.Filetype != PckFile.FileData.FileType.TexturePackInfoFile && - fd.Filetype != PckFile.FileData.FileType.SkinDataFile)) + if (node.Tag is PckFileData fd && + (fd.Filetype != PckFileType.TexturePackInfoFile && + fd.Filetype != PckFileType.SkinDataFile)) { if (node.Parent is TreeNode parentNode) { @@ -569,9 +569,9 @@ namespace PckStudio.Controls } } - private void setFileType_Click(object sender, EventArgs e, PckFile.FileData.FileType type) + private void setFileType_Click(object sender, EventArgs e, PckFileType type) { - if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFile.FileData file) + if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFileData file) { Debug.WriteLine($"Setting {file.Filetype} to {type}"); file.Filetype = type; @@ -587,7 +587,7 @@ namespace PckStudio.Controls buttonEdit.Visible = false; previewPictureBox.Image = Resources.NoImageFound; viewFileInfoToolStripMenuItem.Visible = false; - if (e.Node is TreeNode t && t.Tag is PckFile.FileData file) + if (e.Node is TreeNode t && t.Tag is PckFileData file) { viewFileInfoToolStripMenuItem.Visible = true; if (file.Properties.HasProperty("BOX")) @@ -596,7 +596,7 @@ namespace PckStudio.Controls buttonEdit.Visible = true; } else if (file.Properties.HasProperty("ANIM") && - file.Properties.GetPropertyValue("ANIM", s => SkinANIM.FromString(s) == (SkinAnimFlag.RESOLUTION_64x64 | SkinAnimFlag.SLIM_MODEL))) + file.Properties.GetPropertyValue("ANIM", s => SkinANIM.FromString(s) == (SkinAnimMask.RESOLUTION_64x64 | SkinAnimMask.SLIM_MODEL))) { buttonEdit.Text = "View Skin"; buttonEdit.Visible = true; @@ -604,9 +604,9 @@ namespace PckStudio.Controls switch (file.Filetype) { - case PckFile.FileData.FileType.SkinFile: - case PckFile.FileData.FileType.CapeFile: - case PckFile.FileData.FileType.TextureFile: + case PckFileType.SkinFile: + case PckFileType.CapeFile: + case PckFileType.TextureFile: { // TODO: Add tga support if (Path.GetExtension(file.Filename) == ".tga") break; @@ -634,7 +634,7 @@ namespace PckStudio.Controls if ((file.Filename.StartsWith("res/textures/blocks/") || file.Filename.StartsWith("res/textures/items/")) && - file.Filetype == PckFile.FileData.FileType.TextureFile + file.Filetype == PckFileType.TextureFile && !file.IsMipmappedFile()) { buttonEdit.Text = "EDIT TILE ANIMATION"; @@ -643,22 +643,22 @@ namespace PckStudio.Controls } break; - case PckFile.FileData.FileType.LocalisationFile: + case PckFileType.LocalisationFile: buttonEdit.Text = "EDIT LOC"; buttonEdit.Visible = true; break; - case PckFile.FileData.FileType.AudioFile: + case PckFileType.AudioFile: buttonEdit.Text = "EDIT MUSIC CUES"; buttonEdit.Visible = true; break; - case PckFile.FileData.FileType.ColourTableFile when file.Filename == "colours.col": + case PckFileType.ColourTableFile when file.Filename == "colours.col": buttonEdit.Text = "EDIT COLORS"; buttonEdit.Visible = true; break; - case PckFile.FileData.FileType.BehavioursFile when file.Filename == "behaviours.bin": + case PckFileType.BehavioursFile when file.Filename == "behaviours.bin": buttonEdit.Text = "EDIT BEHAVIOURS"; buttonEdit.Visible = true; break; @@ -671,7 +671,7 @@ namespace PckStudio.Controls private void treeViewMain_DoubleClick(object sender, EventArgs e) { - if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFile.FileData file) + if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFileData file) { pckFileTypeHandler[file.Filetype]?.Invoke(file); } @@ -688,39 +688,39 @@ namespace PckStudio.Controls if (add.ShowDialog() == DialogResult.OK) { - if (_pck.HasFile("Skins.pck", PckFile.FileData.FileType.SkinDataFile)) // Prioritize Skins.pck + if (_pck.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; - SetPckFileIcon(newNode, PckFile.FileData.FileType.SkinFile); + SetPckFileIcon(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 - _pck.Files.Add(add.SkinFile); + _pck.AddFile(add.SkinFile); } if (add.HasCape) { - if (_pck.HasFile("Skins.pck", PckFile.FileData.FileType.SkinDataFile)) // Prioritize Skins.pck + if (_pck.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; - SetPckFileIcon(newNode, PckFile.FileData.FileType.SkinFile); + SetPckFileIcon(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 - _pck.Files.Add(add.CapeFile); + _pck.AddFile(add.CapeFile); } } @@ -736,15 +736,16 @@ namespace PckStudio.Controls if (diag.ShowDialog(this) != DialogResult.OK) return; - var file = new PckFile.FileData( + var file = new PckFileData( $"res/textures/{Animation.GetCategoryName(diag.Category)}/{diag.SelectedTile}.png", - PckFile.FileData.FileType.TextureFile); + PckFileType.TextureFile); - using AnimationEditor animationEditor = new AnimationEditor(file); + var animation = AnimationHelper.GetAnimationFromFile(file); + using AnimationEditor animationEditor = new AnimationEditor(animation, Path.GetFileNameWithoutExtension(file.Filename)); if (animationEditor.ShowDialog() == DialogResult.OK) { _wasModified = true; - _pck.Files.Add(file); + _pck.AddFile(file); BuildMainTreeView(); ReloadMetaTreeView(); } @@ -752,15 +753,10 @@ namespace PckStudio.Controls private void audiopckToolStripMenuItem_Click(object sender, EventArgs e) { - //if (pck.Files.Contains(file => file.Filetype == PckFile.FileData.FileType.AudioFile) != -1) - //{ - // MessageBox.Show("There is already an music cues PCK present in this PCK!", "Can't create audio.pck"); - // return; - //} - if (_pck.Files.Contains("audio.pck", PckFile.FileData.FileType.AudioFile)) + if (_pck.Contains("audio.pck", PckFileType.AudioFile)) { // the chance of this happening is really really slim but just in case - MessageBox.Show($"There is already a file of type \"{nameof(PckFile.FileData.FileType.AudioFile)}\" and name \"audio.pck\" in this PCK!", "Can't create audio.pck"); + MessageBox.Show($"There is already a file of type \"{nameof(PckFileType.AudioFile)}\" and name \"audio.pck\" in this PCK!", "Can't create audio.pck"); return; } if (string.IsNullOrEmpty(_location)) @@ -773,7 +769,7 @@ namespace PckStudio.Controls AudioEditor diag = new AudioEditor(file, LittleEndianCheckBox.Checked); if (diag.ShowDialog(this) == DialogResult.OK) { - _pck.Files.Add(file); + _pck.AddFile(file); } diag.Dispose(); BuildMainTreeView(); @@ -781,25 +777,25 @@ namespace PckStudio.Controls private void colourscolToolStripMenuItem_Click(object sender, EventArgs e) { - if (_pck.TryGetFile("colours.col", PckFile.FileData.FileType.ColourTableFile, out _)) + if (_pck.TryGetFile("colours.col", PckFileType.ColourTableFile, out _)) { MessageBox.Show("A color table file already exists in this PCK and a new one cannot be created.", "Operation aborted"); return; } - var newColorFile = _pck.CreateNewFile("colours.col", PckFile.FileData.FileType.ColourTableFile); + var newColorFile = _pck.CreateNewFile("colours.col", PckFileType.ColourTableFile); newColorFile.SetData(Resources.tu69colours); BuildMainTreeView(); } private void CreateSkinsPCKToolStripMenuItem1_Click(object sender, EventArgs e) { - if (_pck.TryGetFile("Skins.pck", PckFile.FileData.FileType.SkinDataFile, out _)) + if (_pck.TryGetFile("Skins.pck", PckFileType.SkinDataFile, out _)) { MessageBox.Show("A Skins.pck file already exists in this PCK and a new one cannot be created.", "Operation aborted"); return; } - _pck.CreateNewFile("Skins.pck", PckFile.FileData.FileType.SkinDataFile, () => + _pck.CreateNewFile("Skins.pck", PckFileType.SkinDataFile, () => { using var stream = new MemoryStream(); var writer = new PckFileWriter(new PckFile(3, true), GetEndianess()); @@ -818,24 +814,24 @@ namespace PckStudio.Controls private void behavioursbinToolStripMenuItem_Click(object sender, EventArgs e) { - if (_pck.TryGetFile("behaviours.bin", PckFile.FileData.FileType.BehavioursFile, out _)) + if (_pck.TryGetFile("behaviours.bin", PckFileType.BehavioursFile, out _)) { MessageBox.Show("A behaviours file already exists in this PCK and a new one cannot be created.", "Operation aborted"); return; } - _pck.CreateNewFile("behaviours.bin", PckFile.FileData.FileType.BehavioursFile, BehaviourResources.BehaviourFileInitializer); + _pck.CreateNewFile("behaviours.bin", PckFileType.BehavioursFile, BehaviourResources.BehaviourFileInitializer); BuildMainTreeView(); } private void entityMaterialsbinToolStripMenuItem_Click(object sender, EventArgs e) { - if (_pck.TryGetFile("entityMaterials.bin", PckFile.FileData.FileType.MaterialFile, out _)) + if (_pck.TryGetFile("entityMaterials.bin", PckFileType.MaterialFile, out _)) { MessageBox.Show("A behaviours file already exists in this PCK and a new one cannot be created.", "Operation aborted"); return; } - _pck.CreateNewFile("entityMaterials.bin", PckFile.FileData.FileType.MaterialFile, MaterialResources.MaterialsFileInitializer); + _pck.CreateNewFile("entityMaterials.bin", PckFileType.MaterialFile, MaterialResources.MaterialsFileInitializer); BuildMainTreeView(); } @@ -851,19 +847,19 @@ namespace PckStudio.Controls return; } // creates variable to indicate wether current pck skin structure is mashup or regular skin - bool hasSkinsPck = _pck.HasFile("Skins.pck", PckFile.FileData.FileType.SkinDataFile); + bool hasSkinsPck = _pck.HasFile("Skins.pck", PckFileType.SkinDataFile); foreach (var fullfilename in Directory.GetFiles(contents.SelectedPath, "*.png")) { string filename = Path.GetFileNameWithoutExtension(fullfilename); // sets file type based on wether its a cape or skin - PckFile.FileData.FileType pckfiletype = filename.StartsWith("dlccape", StringComparison.OrdinalIgnoreCase) - ? PckFile.FileData.FileType.CapeFile - : PckFile.FileData.FileType.SkinFile; + PckFileType pckfiletype = filename.StartsWith("dlccape", StringComparison.OrdinalIgnoreCase) + ? PckFileType.CapeFile + : PckFileType.SkinFile; string pckfilepath = (hasSkinsPck ? "Skins/" : string.Empty) + filename + ".png"; - PckFile.FileData newFile = new PckFile.FileData(pckfilepath, pckfiletype); + PckFileData newFile = new PckFileData(pckfilepath, pckfiletype); byte[] filedata = File.ReadAllBytes(fullfilename); newFile.SetData(filedata); @@ -897,7 +893,7 @@ namespace PckStudio.Controls } if (hasSkinsPck) { - var skinsfile = _pck.GetFile("Skins.pck", PckFile.FileData.FileType.SkinDataFile); + var skinsfile = _pck.GetFile("Skins.pck", PckFileType.SkinDataFile); using (var ms = new MemoryStream(skinsfile.Data)) { //var reader = new PckFileReader(LittleEndianCheckBox.Checked ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian); @@ -910,7 +906,7 @@ namespace PckStudio.Controls } continue; } - _pck.Files.Add(newFile); + _pck.AddFile(newFile); } BuildMainTreeView(); _wasModified = true; @@ -920,8 +916,8 @@ namespace PckStudio.Controls private void as3DSTextureFileToolStripMenuItem_Click(object sender, EventArgs e) { if (treeViewMain.SelectedNode is TreeNode node && - node.Tag is PckFile.FileData file && - file.Filetype == PckFile.FileData.FileType.SkinFile) + node.Tag is PckFileData file && + file.Filetype == PckFileType.SkinFile) { SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.Filter = "3DS Texture|*.3dst"; @@ -940,7 +936,7 @@ namespace PckStudio.Controls private void generateMipMapTextureToolStripMenuItem_Click(object sender, EventArgs e) { - if (treeViewMain.SelectedNode.Tag is PckFile.FileData file && file.Filetype == PckFile.FileData.FileType.TextureFile) + if (treeViewMain.SelectedNode.Tag is PckFileData file && file.Filetype == PckFileType.TextureFile) { string textureDirectory = Path.GetDirectoryName(file.Filename); string textureName = Path.GetFileNameWithoutExtension(file.Filename); @@ -966,9 +962,9 @@ namespace PckStudio.Controls { string mippedPath = $"{textureDirectory}/{textureName}MipMapLevel{i}{textureExtension}"; Debug.WriteLine(mippedPath); - if (_pck.HasFile(mippedPath, PckFile.FileData.FileType.TextureFile)) - _pck.Files.Remove(_pck.GetFile(mippedPath, PckFile.FileData.FileType.TextureFile)); - PckFile.FileData MipMappedFile = new PckFile.FileData(mippedPath, PckFile.FileData.FileType.TextureFile); + if (_pck.HasFile(mippedPath, PckFileType.TextureFile)) + _pck.RemoveFile(_pck.GetFile(mippedPath, PckFileType.TextureFile)); + PckFileData MipMappedFile = new PckFileData(mippedPath, PckFileType.TextureFile); Image originalTexture = Image.FromStream(new MemoryStream(file.Data)); @@ -989,7 +985,7 @@ namespace PckStudio.Controls MipMappedFile.SetData(texStream.ToArray()); texStream.Dispose(); - _pck.Files.Insert(_pck.Files.IndexOf(file) + i - 1, MipMappedFile); + _pck.InsertFile(_pck.IndexOfFile(file) + i - 1, MipMappedFile); } BuildMainTreeView(); } @@ -998,7 +994,7 @@ namespace PckStudio.Controls private void viewFileInfoToolStripMenuItem_Click(object sender, EventArgs e) { - if (treeViewMain.SelectedNode.Tag is PckFile.FileData file) + if (treeViewMain.SelectedNode.Tag is PckFileData file) { MessageBox.Show( "File path: " + file.Filename + @@ -1011,7 +1007,7 @@ namespace PckStudio.Controls private void correctSkinDecimalsToolStripMenuItem_Click(object sender, EventArgs e) { - if (treeViewMain.SelectedNode is TreeNode node && node.Tag is PckFile.FileData file && file.Filetype == PckFile.FileData.FileType.SkinFile) + if (treeViewMain.SelectedNode is TreeNode node && node.Tag is PckFileData file && file.Filetype == PckFileType.SkinFile) { foreach (var p in file.Properties.FindAll(s => s.Key == "BOX" || s.Key == "OFFSET")) { @@ -1027,7 +1023,7 @@ namespace PckStudio.Controls { var node = treeViewMain.SelectedNode; if (node == null) return; - if (node.Tag is PckFile.FileData file) + if (node.Tag is PckFileData file) { using SaveFileDialog exFile = new SaveFileDialog(); exFile.FileName = Path.GetFileName(file.Filename); @@ -1058,7 +1054,7 @@ namespace PckStudio.Controls { GetAllChildNodes(node.Nodes).ForEach(fileNode => { - if (fileNode.Tag is PckFile.FileData file) + if (fileNode.Tag is PckFileData file) { Directory.CreateDirectory($"{dialog.SelectedPath}/{Path.GetDirectoryName(file.Filename)}"); File.WriteAllBytes($"{dialog.SelectedPath}/{file.Filename}", file.Data); @@ -1073,7 +1069,7 @@ namespace PckStudio.Controls } else { - foreach (var _file in _pck.Files) + foreach (var _file in _pck.GetFiles()) { if (_file.Filename.StartsWith(selectedFolder)) { @@ -1104,11 +1100,11 @@ namespace PckStudio.Controls if (diag.ShowDialog(this) == DialogResult.OK) { - if (node.Tag is PckFile.FileData file) + if (node.Tag is PckFileData file) { TreeNode newNode = new TreeNode(); newNode.Text = Path.GetFileName(diag.NewText); - var NewFile = new PckFile.FileData(diag.NewText, file.Filetype); + var NewFile = new PckFileData(diag.NewText, file.Filetype); file.Properties.ForEach(p => NewFile.Properties.Add(p)); NewFile.SetData(file.Data); NewFile.Filename = diag.NewText; @@ -1129,7 +1125,7 @@ namespace PckStudio.Controls if (node.Parent == null) treeViewMain.Nodes.Insert(node.Index + 1, newNode); //adds generated file node else node.Parent.Nodes.Insert(node.Index + 1, newNode);//adds generated file node to selected folder - if (!IsSubPCKNode(node.FullPath)) _pck.Files.Insert(node.Index + 1, NewFile); + if (!IsSubPCKNode(node.FullPath)) _pck.InsertFile(node.Index + 1, NewFile); else RebuildSubPCK(node.FullPath); BuildMainTreeView(); _wasModified = true; @@ -1147,7 +1143,7 @@ namespace PckStudio.Controls if (diag.ShowDialog(this) == DialogResult.OK) { - if (node.Tag is PckFile.FileData file) + if (node.Tag is PckFileData file) { file.Filename = diag.NewText; } @@ -1156,7 +1152,7 @@ namespace PckStudio.Controls node.Text = diag.NewText; foreach (var childNode in GetAllChildNodes(node.Nodes)) { - if (childNode.Tag is PckFile.FileData folderFile) + if (childNode.Tag is PckFileData folderFile) { folderFile.Filename = childNode.FullPath; } @@ -1170,7 +1166,7 @@ namespace PckStudio.Controls private void replaceToolStripMenuItem_Click(object sender, EventArgs e) { - if (treeViewMain.SelectedNode.Tag is PckFile.FileData file) + if (treeViewMain.SelectedNode.Tag is PckFileData file) { using var ofd = new OpenFileDialog(); // Suddenly, and randomly, this started throwing an exception because it wasn't formatted correctly? So now it's formatted correctly and now displays the file type name in the dialog. @@ -1179,7 +1175,7 @@ namespace PckStudio.Controls switch (file.Filetype) { - case PckFile.FileData.FileType.TextureFile: + case PckFileType.TextureFile: if (Path.GetExtension(file.Filename) == ".png") extra_extensions = ";*.tga"; else if (Path.GetExtension(file.Filename) == ".tga") extra_extensions = ";*.png"; break; @@ -1209,14 +1205,14 @@ namespace PckStudio.Controls string path = node.FullPath; - if (node.Tag is PckFile.FileData) + if (node.Tag is PckFileData) { - PckFile.FileData file = node.Tag as PckFile.FileData; + PckFileData file = node.Tag as PckFileData; string itemPath = "res/textures/items/"; // warn the user about deleting compass.png and clock.png - if (file.Filetype == PckFile.FileData.FileType.TextureFile && + if (file.Filetype == PckFileType.TextureFile && (file.Filename == itemPath + "compass.png" || file.Filename == itemPath + "clock.png")) { if (MessageBox.Show("Are you sure want to delete this file? If \"compass.png\" or \"clock.png\" are missing, your game will crash upon loading this pack.", "Warning", @@ -1224,7 +1220,7 @@ namespace PckStudio.Controls } // remove loc key if its a skin/cape - if (file.Filetype == PckFile.FileData.FileType.SkinFile || file.Filetype == PckFile.FileData.FileType.CapeFile) + if (file.Filetype == PckFileType.SkinFile || file.Filetype == PckFileType.CapeFile) { if (TryGetLocFile(out LOCFile locFile)) { @@ -1236,7 +1232,7 @@ namespace PckStudio.Controls TrySetLocFile(locFile); } } - if (_pck.Files.Remove(file)) + if (_pck.RemoveFile(file)) { node.Remove(); _wasModified = true; @@ -1246,7 +1242,7 @@ namespace PckStudio.Controls MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { string pckFolderDir = node.FullPath; - _pck.Files.RemoveAll(file => file.Filename.StartsWith(pckFolderDir)); + _pck.RemoveAll(file => file.Filename.StartsWith(pckFolderDir)); node.Remove(); _wasModified = true; } @@ -1279,14 +1275,14 @@ namespace PckStudio.Controls { // for now name edits are done through the 'rename' context menu item // TODO: add folder renaming - //e.CancelEdit = e.Node.Tag is PckFile.FileData; + //e.CancelEdit = e.Node.Tag is PckFileData; e.CancelEdit = true; } private void editAllEntriesToolStripMenuItem_Click(object sender, EventArgs e) { if (treeViewMain.SelectedNode is TreeNode node && - node.Tag is PckFile.FileData file) + node.Tag is PckFileData file) { var props = file.Properties.Select(p => p.Key + " " + p.Value); using (var input = new MultiTextPrompt(props.ToArray())) @@ -1312,14 +1308,14 @@ namespace PckStudio.Controls private void treeMeta_DoubleClick(object sender, EventArgs e) { if (treeMeta.SelectedNode is TreeNode subnode && subnode.Tag is KeyValuePair property && - treeViewMain.SelectedNode is TreeNode node && node.Tag is PckFile.FileData file) + treeViewMain.SelectedNode is TreeNode node && node.Tag is PckFileData file) { int i = file.Properties.IndexOf(property); if (i != -1) { switch (property.Key) { - case "ANIM" when file.Filetype == PckFile.FileData.FileType.SkinFile: + case "ANIM" when file.Filetype == PckFileType.SkinFile: try { using ANIMEditor diag = new ANIMEditor(property.Value); @@ -1339,7 +1335,7 @@ namespace PckStudio.Controls } break; - case "BOX" when file.Filetype == PckFile.FileData.FileType.SkinFile: + case "BOX" when file.Filetype == PckFileType.SkinFile: try { using BoxEditor diag = new BoxEditor(property.Value, IsSubPCKNode(treeViewMain.SelectedNode.FullPath)); @@ -1387,7 +1383,7 @@ namespace PckStudio.Controls private void addMultipleEntriesToolStripMenuItem1_Click(object sender, EventArgs e) { if (treeViewMain.SelectedNode is TreeNode node && - node.Tag is PckFile.FileData file) + node.Tag is PckFileData file) { using (var input = new MultiTextPrompt()) { @@ -1410,7 +1406,7 @@ namespace PckStudio.Controls private void addBOXEntryToolStripMenuItem1_Click(object sender, EventArgs e) { - if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFile.FileData file) + if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFileData file) { using BoxEditor diag = new BoxEditor(SkinBOX.Empty, IsSubPCKNode(treeViewMain.SelectedNode.FullPath)); if (diag.ShowDialog(this) == DialogResult.OK) @@ -1426,7 +1422,7 @@ namespace PckStudio.Controls private void addANIMEntryToolStripMenuItem1_Click(object sender, EventArgs e) { - if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFile.FileData file) + if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFileData file) { using ANIMEditor diag = new ANIMEditor(SkinANIM.Empty); if (diag.ShowDialog(this) == DialogResult.OK) @@ -1443,7 +1439,7 @@ namespace PckStudio.Controls private void deleteEntryToolStripMenuItem_Click(object sender, EventArgs e) { if (treeMeta.SelectedNode is TreeNode t && t.Tag is KeyValuePair property && - treeViewMain.SelectedNode is TreeNode main && main.Tag is PckFile.FileData file && + treeViewMain.SelectedNode is TreeNode main && main.Tag is PckFileData file && file.Properties.Remove(property)) { treeMeta.SelectedNode.Remove(); @@ -1455,7 +1451,7 @@ namespace PckStudio.Controls private void addEntryToolStripMenuItem_Click(object sender, EventArgs e) { if (treeViewMain.SelectedNode is TreeNode t && - t.Tag is PckFile.FileData file) + t.Tag is PckFileData file) { using AddPropertyPrompt addProperty = new AddPropertyPrompt(); if (addProperty.ShowDialog() == DialogResult.OK) @@ -1468,9 +1464,9 @@ namespace PckStudio.Controls } } - private void HandleTextureFile(PckFile.FileData file) + private void HandleTextureFile(PckFileData file) { - _ = file.IsMipmappedFile() && _pck.Files.TryGetValue(file.GetNormalPath(), PckFile.FileData.FileType.TextureFile, out file); + _ = file.IsMipmappedFile() && _pck.TryGetValue(file.GetNormalPath(), PckFileType.TextureFile, out file); if (file.Size <= 0) { @@ -1501,7 +1497,8 @@ namespace PckStudio.Controls if (file.Filename.StartsWith("res/textures/blocks/") || file.Filename.StartsWith("res/textures/items/")) { - using (AnimationEditor animationEditor = new AnimationEditor(file)) + var animation = AnimationHelper.GetAnimationFromFile(file); + using (AnimationEditor animationEditor = new AnimationEditor(animation, Path.GetFileNameWithoutExtension(file.Filename))) { if (animationEditor.ShowDialog(this) == DialogResult.OK) { @@ -1513,28 +1510,28 @@ namespace PckStudio.Controls } } - private void HandleGameRuleFile(PckFile.FileData file) + private void HandleGameRuleFile(PckFileData file) { using GameRuleFileEditor grfEditor = new GameRuleFileEditor(file); _wasModified = grfEditor.ShowDialog(this) == DialogResult.OK; UpdateRichPresence(); } - private void HandleAudioFile(PckFile.FileData file) + private void HandleAudioFile(PckFileData file) { using AudioEditor audioEditor = new AudioEditor(file, LittleEndianCheckBox.Checked); _wasModified = audioEditor.ShowDialog(this) == DialogResult.OK; UpdateRichPresence(); } - private void HandleLocalisationFile(PckFile.FileData file) + private void HandleLocalisationFile(PckFileData file) { using LOCEditor locedit = new LOCEditor(file); _wasModified = locedit.ShowDialog(this) == DialogResult.OK; UpdateRichPresence(); } - private void HandleColourFile(PckFile.FileData file) + private void HandleColourFile(PckFileData file) { if (file.Size == 0) { @@ -1546,7 +1543,7 @@ namespace PckStudio.Controls _wasModified = diag.ShowDialog(this) == DialogResult.OK; } - public void HandleSkinFile(PckFile.FileData file) + public void HandleSkinFile(PckFileData file) { if (file.Size <= 0) return; @@ -1572,18 +1569,18 @@ namespace PckStudio.Controls } } - public void HandleModelsFile(PckFile.FileData file) + public void HandleModelsFile(PckFileData file) { MessageBox.Show("Models.bin support has not been implemented. You can use the Spark Editor for the time being to edit these files.", "Not implemented yet."); } - public void HandleBehavioursFile(PckFile.FileData file) + public void HandleBehavioursFile(PckFileData file) { using BehaviourEditor edit = new BehaviourEditor(file); _wasModified = edit.ShowDialog(this) == DialogResult.OK; } - public void HandleMaterialFile(PckFile.FileData file) + public void HandleMaterialFile(PckFileData file) { using MaterialsEditor edit = new MaterialsEditor(file); _wasModified = edit.ShowDialog(this) == DialogResult.OK; diff --git a/PCK-Studio/Extensions/EnumerableExtensions.cs b/PCK-Studio/Extensions/EnumerableExtensions.cs index 44f18f28..2608b500 100644 --- a/PCK-Studio/Extensions/EnumerableExtensions.cs +++ b/PCK-Studio/Extensions/EnumerableExtensions.cs @@ -15,6 +15,16 @@ namespace PckStudio.Extensions yield break; } + public static bool EqualsAny(this T type, params T[] items) + { + foreach (var item in items) + { + if (item.Equals(type)) + return true; + } + return false; + } + public static bool ContainsAny(this IEnumerable array, params T[] items) { foreach (var item in array) diff --git a/PCK-Studio/Extensions/PckFileDataExtensions.cs b/PCK-Studio/Extensions/PckFileDataExtensions.cs index c781b3a5..9488fdcb 100644 --- a/PCK-Studio/Extensions/PckFileDataExtensions.cs +++ b/PCK-Studio/Extensions/PckFileDataExtensions.cs @@ -16,30 +16,32 @@ namespace PckStudio.Extensions { private const string MipMap = "MipMapLevel"; - internal static Image GetTexture(this PckFile.FileData file) + private static Image EmptyImage = new Bitmap(1, 1, PixelFormat.Format32bppArgb); + + internal static Image GetTexture(this PckFileData file) { - if (file.Filetype != PckFile.FileData.FileType.SkinFile && - file.Filetype != PckFile.FileData.FileType.CapeFile && - file.Filetype != PckFile.FileData.FileType.TextureFile) + if (file.Filetype != PckFileType.SkinFile && + file.Filetype != PckFileType.CapeFile && + file.Filetype != PckFileType.TextureFile) { - return null; + throw new Exception("File is not suitable to contain image data."); } - Image image = null; using (var stream = new MemoryStream(file.Data)) { try { - image = Image.FromStream(stream); + 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; } - internal static void SetData(this PckFile.FileData file, IDataFormatWriter writer) + internal static void SetData(this PckFileData file, IDataFormatWriter writer) { using (var stream = new MemoryStream()) { @@ -48,14 +50,13 @@ namespace PckStudio.Extensions } } - internal static void SetData(this PckFile.FileData file, Image image, ImageFormat imageFormat) + internal static void SetData(this PckFileData file, Image image, ImageFormat imageFormat) { - if (file.Filetype != PckFile.FileData.FileType.SkinFile && - file.Filetype != PckFile.FileData.FileType.CapeFile && - file.Filetype != PckFile.FileData.FileType.TextureFile) + if (file.Filetype != PckFileType.SkinFile && + file.Filetype != PckFileType.CapeFile && + file.Filetype != PckFileType.TextureFile) { - Debug.WriteLine($"{file.Filename} can't contain image data"); - return; + throw new Exception("File is not suitable to contain image data."); } using (var stream = new MemoryStream()) @@ -65,7 +66,7 @@ namespace PckStudio.Extensions } } - internal static bool IsMipmappedFile(this PckFile.FileData file) + internal static bool IsMipmappedFile(this PckFileData file) { // We only want to test the file name itself. ex: "terrainMipMapLevel2" string name = Path.GetFileNameWithoutExtension(file.Filename); @@ -80,7 +81,7 @@ namespace PckStudio.Extensions return true; } - internal static string GetNormalPath(this PckFile.FileData file) + internal static string GetNormalPath(this PckFileData file) { if (!file.IsMipmappedFile()) return file.Filename; diff --git a/PCK-Studio/Extensions/PckFileExtensions.cs b/PCK-Studio/Extensions/PckFileExtensions.cs index f24df276..45eca6a9 100644 --- a/PCK-Studio/Extensions/PckFileExtensions.cs +++ b/PCK-Studio/Extensions/PckFileExtensions.cs @@ -11,7 +11,7 @@ namespace PckStudio.Extensions { internal static class PckFileExtensions { - internal static PckFile.FileData CreateNewFileIf(this PckFile pck, bool condition, string filename, PckFile.FileData.FileType filetype, IDataFormatWriter writer) + internal static PckFileData CreateNewFileIf(this PckFile pck, bool condition, string filename, PckFileType filetype, IDataFormatWriter writer) { if (condition) { @@ -20,7 +20,7 @@ namespace PckStudio.Extensions return null; } - internal static PckFile.FileData CreateNewFile(this PckFile pck, string filename, PckFile.FileData.FileType filetype, IDataFormatWriter writer) + internal static PckFileData CreateNewFile(this PckFile pck, string filename, PckFileType filetype, IDataFormatWriter writer) { var file = pck.CreateNewFile(filename, filetype); file.SetData(writer); diff --git a/PCK-Studio/Extensions/TreeNodeExtensions.cs b/PCK-Studio/Extensions/TreeNodeExtensions.cs new file mode 100644 index 00000000..0fab1fcd --- /dev/null +++ b/PCK-Studio/Extensions/TreeNodeExtensions.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace PckStudio.Extensions +{ + internal static class TreeNodeExtensions + { + internal static bool IsTagOfType(this TreeNode node) where T : class + { + return node.Tag is T; + } + + internal static bool TryGetTagData(this TreeNode node, out TOut tagData) where TOut : class + { + if (node?.Tag is TOut _data) + { + tagData = _data; + return true; + } + tagData = default; + return false; + } + + internal static bool Contains(this TreeNode thisNode, TreeNode childNode) + { + if (childNode.Parent == null) + return false; + if (thisNode.Equals(childNode.Parent)) + return true; + // If the parent node is not equal to the first node, + // call the TreeNode.Contains recursively using the parent of the node. + return thisNode.Contains(childNode.Parent); + } + + internal static List GetChildNodes(this TreeNode thisNode) + { + List nodes = new List(thisNode.Nodes.Count); + foreach (TreeNode node in thisNode.Nodes) + { + nodes.Add(node); + nodes.AddRange(node.GetChildNodes()); + } + return nodes; + } + + } +} \ No newline at end of file diff --git a/PCK-Studio/Features/WiiUPanel.cs b/PCK-Studio/Features/WiiUPanel.cs index d81bae23..ab6aa5f4 100644 --- a/PCK-Studio/Features/WiiUPanel.cs +++ b/PCK-Studio/Features/WiiUPanel.cs @@ -236,7 +236,7 @@ namespace PckStudio.Features var reader = new PckFileReader(); currentPCK = reader.FromFile(filepath); if (currentPCK is null) return string.Empty; - return currentPCK.TryGetFile("0", PckFile.FileData.FileType.InfoFile, out var file) + return currentPCK.TryGetFile("0", PckFileType.InfoFile, out var file) ? file.Properties.GetPropertyValue("PACKID") : string.Empty; } @@ -273,7 +273,7 @@ namespace PckStudio.Features client.UploadFile(ms, GetGameContentPath() + "/Common/Media/MediaWiiU.arc"); } archive.Clear(); - currentPCK?.Files.Clear(); + //currentPCK?.Files.Clear(); currentPCK = null; } GC.Collect(); diff --git a/PCK-Studio/Forms/Additional-Popups/AddFilePrompt.cs b/PCK-Studio/Forms/Additional-Popups/AddFilePrompt.cs index 0a1d9d73..29d80d57 100644 --- a/PCK-Studio/Forms/Additional-Popups/AddFilePrompt.cs +++ b/PCK-Studio/Forms/Additional-Popups/AddFilePrompt.cs @@ -12,7 +12,7 @@ namespace PckStudio.Popups /// otherwise /// public string Filepath => DialogResult == DialogResult.OK ? InputTextBox.Text : string.Empty; - public PckFile.FileData.FileType Filetype => (PckFile.FileData.FileType)FileTypeComboBox.SelectedIndex; + public PckFileType Filetype => (PckFileType)FileTypeComboBox.SelectedIndex; public AddFilePrompt(string initialText) : this(initialText, -1) { } diff --git a/PCK-Studio/Forms/Additional-Popups/Animation/ChangeTile.cs b/PCK-Studio/Forms/Additional-Popups/Animation/ChangeTile.cs index 35988534..0bef8b08 100644 --- a/PCK-Studio/Forms/Additional-Popups/Animation/ChangeTile.cs +++ b/PCK-Studio/Forms/Additional-Popups/Animation/ChangeTile.cs @@ -12,10 +12,10 @@ namespace PckStudio.Forms.Additional_Popups.Animation internal partial class ChangeTile : MetroForm { string selectedTile = ""; - Internal.Animation.AnimationCategory category = Internal.Animation.AnimationCategory.Blocks; + Internal.AnimationCategory category = Internal.AnimationCategory.Blocks; public string SelectedTile => selectedTile; - public Internal.Animation.AnimationCategory Category => category; + public Internal.AnimationCategory Category => category; List treeViewBlockCache = new List(); List treeViewItemCache = new List(); @@ -31,57 +31,46 @@ namespace PckStudio.Forms.Additional_Popups.Animation private void InitializeTreeviews() { Profiler.Start(); - GetTileDataToView("blocks", treeViewBlocks.Nodes, treeViewBlockCache.Add); - GetTileDataToView("items", treeViewItems.Nodes, treeViewItemCache.Add); + GetTileDataToView(Internal.AnimationCategory.Blocks, treeViewBlocks.Nodes, treeViewBlockCache.Add); + GetTileDataToView(Internal.AnimationCategory.Items, treeViewItems.Nodes, treeViewItemCache.Add); Profiler.Stop(); } private void treeViews_AfterSelect(object sender, TreeViewEventArgs e) { - if (e.Node.Tag is string tileData) + if (e.Node.Tag is JsonTileInfo tileData) { - selectedTile = tileData; + selectedTile = tileData.InternalName; Debug.WriteLine(selectedTile); category = e.Node.TreeView == treeViewItems - ? Internal.Animation.AnimationCategory.Items - : Internal.Animation.AnimationCategory.Blocks; + ? Internal.AnimationCategory.Items + : Internal.AnimationCategory.Blocks; } } - private void GetTileDataToView(string key, TreeNodeCollection collection, Action additinalAction) + private void GetTileDataToView(Internal.AnimationCategory key, TreeNodeCollection collection, Action additinalAction) { List textureInfos = key switch { - "blocks" => Tiles.BlockTileInfos, - "items" => Tiles.ItemTileInfos, - _ => throw new InvalidOperationException(key) + Internal.AnimationCategory.Blocks => Tiles.BlockTileInfos, + Internal.AnimationCategory.Items => Tiles.ItemTileInfos, + _ => throw new InvalidOperationException(nameof(key)) }; Profiler.Start(); - try + if (textureInfos is not null) { - if (textureInfos is not null) - { - foreach ((int i, var content) in textureInfos.enumerate()) + foreach ((int i, var content) in textureInfos.enumerate()) + { + if (string.IsNullOrEmpty(content.InternalName) || collection.ContainsKey(content.InternalName)) + continue; + TreeNode tileNode = new TreeNode(content.DisplayName, i, i) { - if (!string.IsNullOrEmpty(content.InternalName) && !collection.ContainsKey(content.InternalName)) - { - TreeNode tileNode = new TreeNode(content.DisplayName) - { - Name = content.InternalName, - Tag = content.DisplayName, - ImageIndex = i, - SelectedImageIndex = i, - }; - collection.Add(tileNode); - additinalAction(tileNode); - } - } - } - } - catch (Newtonsoft.Json.JsonException j_ex) - { - MessageBox.Show(j_ex.Message, "Error"); - return; + Name = content.InternalName, + Tag = content + }; + collection.Add(tileNode); + additinalAction(tileNode); + } } Profiler.Stop(); } diff --git a/PCK-Studio/Forms/Additional-Popups/Grf/AddParameter.Designer.cs b/PCK-Studio/Forms/Additional-Popups/Grf/AddParameter.Designer.cs index f17df864..78eb47ae 100644 --- a/PCK-Studio/Forms/Additional-Popups/Grf/AddParameter.Designer.cs +++ b/PCK-Studio/Forms/Additional-Popups/Grf/AddParameter.Designer.cs @@ -35,6 +35,7 @@ this.ValueTextBox = new MetroFramework.Controls.MetroTextBox(); this.CancelBtn = new MetroFramework.Controls.MetroButton(); this.ConfirmBtn = new MetroFramework.Controls.MetroButton(); + this.availableComboBox = new MetroFramework.Controls.MetroComboBox(); metroLabel1 = new MetroFramework.Controls.MetroLabel(); metroLabel2 = new MetroFramework.Controls.MetroLabel(); this.SuspendLayout(); @@ -147,12 +148,27 @@ this.ConfirmBtn.UseSelectable = true; this.ConfirmBtn.Click += new System.EventHandler(this.ConfirmButton_Click); // + // availableComboBox + // + this.availableComboBox.FormattingEnabled = true; + this.availableComboBox.ItemHeight = 23; + this.availableComboBox.Location = new System.Drawing.Point(72, 21); + this.availableComboBox.Name = "availableComboBox"; + this.availableComboBox.Size = new System.Drawing.Size(165, 29); + this.availableComboBox.Style = MetroFramework.MetroColorStyle.Silver; + this.availableComboBox.TabIndex = 6; + this.availableComboBox.Theme = MetroFramework.MetroThemeStyle.Dark; + this.availableComboBox.UseSelectable = true; + this.availableComboBox.Visible = false; + this.availableComboBox.SelectedIndexChanged += new System.EventHandler(this.availableComboBox_SelectedIndexChanged); + // // AddParameter // this.AcceptButton = this.ConfirmBtn; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(257, 126); + this.ClientSize = new System.Drawing.Size(264, 126); + this.Controls.Add(this.availableComboBox); this.Controls.Add(this.ConfirmBtn); this.Controls.Add(this.CancelBtn); this.Controls.Add(this.ValueTextBox); @@ -176,5 +192,6 @@ private MetroFramework.Controls.MetroButton CancelBtn; private MetroFramework.Controls.MetroButton ConfirmBtn; private MetroFramework.Controls.MetroTextBox NameTextBox; + private MetroFramework.Controls.MetroComboBox availableComboBox; } } \ No newline at end of file diff --git a/PCK-Studio/Forms/Additional-Popups/Grf/AddParameter.cs b/PCK-Studio/Forms/Additional-Popups/Grf/AddParameter.cs index 620f66ed..36f3884b 100644 --- a/PCK-Studio/Forms/Additional-Popups/Grf/AddParameter.cs +++ b/PCK-Studio/Forms/Additional-Popups/Grf/AddParameter.cs @@ -1,5 +1,7 @@ using System; using System.Windows.Forms; +using OMI.Formats.GameRule; +using PckStudio.Properties; namespace PckStudio.Forms.Additional_Popups.Grf { @@ -7,19 +9,35 @@ namespace PckStudio.Forms.Additional_Popups.Grf { public string ParameterName => NameTextBox.Text; public string ParameterValue => ValueTextBox.Text; + + private bool _useComboBox + { + get + { + return availableComboBox.Visible && !NameTextBox.Visible; + } + set + { + NameTextBox.Visible = !value; + availableComboBox.Visible = value; + } + } + public AddParameter() { InitializeComponent(); + availableComboBox.Items.Clear(); + availableComboBox.Items.AddRange(GameRuleFile.GameRule.ValidParameters); + _useComboBox = Settings.Default.UseComboBoxForGRFParameter; } - public AddParameter(string parameterName, string parameterValue) : this() + + public AddParameter(string parameterName, string parameterValue, bool isKeyReadonly = true) : this() { NameTextBox.Text = parameterName; ValueTextBox.Text = parameterValue; - } - - public AddParameter(string parameterName, string parameterValue, bool parameterNameBoxEnabled = true) : this(parameterName, parameterValue) - { - NameTextBox.Enabled = parameterNameBoxEnabled; + NameTextBox.Enabled = isKeyReadonly; + availableComboBox.Enabled = isKeyReadonly; + availableComboBox.SelectedItem = parameterName; } private void ConfirmButton_Click(object sender, EventArgs e) @@ -30,7 +48,11 @@ namespace PckStudio.Forms.Additional_Popups.Grf return; } DialogResult = DialogResult.OK; - Close(); + } + + private void availableComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + NameTextBox.Text = availableComboBox.SelectedItem.ToString(); } } } diff --git a/PCK-Studio/Forms/Additional-Popups/ItemSelectionPopUp.Designer.cs b/PCK-Studio/Forms/Additional-Popups/ItemSelectionPopUp.Designer.cs index 5ab86c78..85edf085 100644 --- a/PCK-Studio/Forms/Additional-Popups/ItemSelectionPopUp.Designer.cs +++ b/PCK-Studio/Forms/Additional-Popups/ItemSelectionPopUp.Designer.cs @@ -89,8 +89,8 @@ #endregion private System.Windows.Forms.Button cancelButton; - public System.Windows.Forms.Label label2; - public System.Windows.Forms.Button okBtn; private MetroFramework.Controls.MetroComboBox ComboBox; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button okBtn; } } \ No newline at end of file diff --git a/PCK-Studio/Forms/Additional-Popups/ItemSelectionPopUp.cs b/PCK-Studio/Forms/Additional-Popups/ItemSelectionPopUp.cs index efb7f1b0..c5017b39 100644 --- a/PCK-Studio/Forms/Additional-Popups/ItemSelectionPopUp.cs +++ b/PCK-Studio/Forms/Additional-Popups/ItemSelectionPopUp.cs @@ -7,6 +7,17 @@ namespace PckStudio.Forms.Additional_Popups { public string SelectedItem => DialogResult == DialogResult.OK ? ComboBox.Text : string.Empty; + public string LabelText + { + get => label2.Text; + set => label2.Text = value; + } + public string ButtonText + { + get => okBtn.Text; + set => okBtn.Text = value; + } + public ItemSelectionPopUp(params string[] items) { InitializeComponent(); diff --git a/PCK-Studio/Forms/AppSettingsForm.Designer.cs b/PCK-Studio/Forms/AppSettingsForm.Designer.cs index e9191a50..d651aef3 100644 --- a/PCK-Studio/Forms/AppSettingsForm.Designer.cs +++ b/PCK-Studio/Forms/AppSettingsForm.Designer.cs @@ -35,6 +35,7 @@ this.autoUpdateCheckBox = new MetroFramework.Controls.MetroCheckBox(); this.autoLoadPckCheckBox = new MetroFramework.Controls.MetroCheckBox(); this.showPresenceCheckBox = new MetroFramework.Controls.MetroCheckBox(); + this.grf_paramKeyComboBoxCheckBox = new MetroFramework.Controls.MetroCheckBox(); this.SuspendLayout(); // // autoSaveCheckBox @@ -114,11 +115,26 @@ this.showPresenceCheckBox.UseSelectable = true; this.showPresenceCheckBox.CheckedChanged += new System.EventHandler(this.showPresenceCheckBox_CheckedChanged); // + // grf_paramKeyComboBoxCheckBox + // + this.grf_paramKeyComboBoxCheckBox.AutoSize = true; + this.grf_paramKeyComboBoxCheckBox.Location = new System.Drawing.Point(23, 169); + this.grf_paramKeyComboBoxCheckBox.Name = "grf_paramKeyComboBoxCheckBox"; + this.grf_paramKeyComboBoxCheckBox.Size = new System.Drawing.Size(100, 15); + this.grf_paramKeyComboBoxCheckBox.Style = MetroFramework.MetroColorStyle.White; + this.grf_paramKeyComboBoxCheckBox.TabIndex = 5; + this.grf_paramKeyComboBoxCheckBox.Text = "Select GRF Key"; + this.grf_paramKeyComboBoxCheckBox.Theme = MetroFramework.MetroThemeStyle.Dark; + this.SettingToolTip.SetToolTip(this.grf_paramKeyComboBoxCheckBox, "Use a combobox instead of typing the parameter key name"); + this.grf_paramKeyComboBoxCheckBox.UseSelectable = true; + this.grf_paramKeyComboBoxCheckBox.CheckedChanged += new System.EventHandler(this.grf_paramKeyComboBoxCheckBox_CheckedChanged); + // // AppSettingsForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(527, 270); + this.Controls.Add(this.grf_paramKeyComboBoxCheckBox); this.Controls.Add(this.showPresenceCheckBox); this.Controls.Add(this.autoLoadPckCheckBox); this.Controls.Add(this.autoUpdateCheckBox); @@ -147,5 +163,6 @@ private MetroFramework.Controls.MetroCheckBox autoUpdateCheckBox; private MetroFramework.Controls.MetroCheckBox autoLoadPckCheckBox; private MetroFramework.Controls.MetroCheckBox showPresenceCheckBox; + private MetroFramework.Controls.MetroCheckBox grf_paramKeyComboBoxCheckBox; } } \ No newline at end of file diff --git a/PCK-Studio/Forms/AppSettingsForm.cs b/PCK-Studio/Forms/AppSettingsForm.cs index 53995275..71725fb0 100644 --- a/PCK-Studio/Forms/AppSettingsForm.cs +++ b/PCK-Studio/Forms/AppSettingsForm.cs @@ -40,6 +40,11 @@ namespace PckStudio.Forms Settings.Default.ShowRichPresence = showPresenceCheckBox.Checked; } + private void grf_paramKeyComboBoxCheckBox_CheckedChanged(object sender, EventArgs e) + { + Settings.Default.UseComboBoxForGRFParameter = grf_paramKeyComboBoxCheckBox.Checked; + } + private void LoadCheckboxState(CheckBox checkBox, EventHandler eventHandler, bool state) { checkBox.CheckedChanged -= eventHandler; @@ -53,6 +58,7 @@ namespace PckStudio.Forms LoadCheckboxState(endianCheckBox, endianCheckBox_CheckedChanged, Settings.Default.UseLittleEndianAsDefault); LoadCheckboxState(autoLoadPckCheckBox, autoLoadPckCheckBox_CheckedChanged, Settings.Default.LoadSubPcks); LoadCheckboxState(showPresenceCheckBox, showPresenceCheckBox_CheckedChanged, Settings.Default.ShowRichPresence); + LoadCheckboxState(grf_paramKeyComboBoxCheckBox, grf_paramKeyComboBoxCheckBox_CheckedChanged, Settings.Default.UseComboBoxForGRFParameter); } private void AppBehaviorSettingsForm_FormClosing(object sender, FormClosingEventArgs e) diff --git a/PCK-Studio/Forms/Editor/ANIMEditor.cs b/PCK-Studio/Forms/Editor/ANIMEditor.cs index 3718dcd8..3184ae33 100644 --- a/PCK-Studio/Forms/Editor/ANIMEditor.cs +++ b/PCK-Studio/Forms/Editor/ANIMEditor.cs @@ -182,6 +182,7 @@ namespace PckStudio.Forms.Editor public ANIMEditor(SkinANIM skinANIM) : this() { + initialANIM = skinANIM; setDisplayAnim(skinANIM); ruleset.ApplyAnim(skinANIM); } @@ -328,25 +329,25 @@ namespace PckStudio.Forms.Editor setDisplayAnim((SkinANIM)initialANIM.Clone()); } - static readonly Dictionary Templates = new Dictionary() + static readonly Dictionary Templates = new Dictionary() { - { "Steve (64x32)", SkinAnimFlag.NONE }, - { "Steve (64x64)", SkinAnimFlag.RESOLUTION_64x64 }, - { "Alex (64x64)", SkinAnimFlag.SLIM_MODEL }, - { "Zombie Skins", SkinAnimFlag.ZOMBIE_ARMS }, - { "Cetacean Skins", SkinAnimFlag.SYNCED_ARMS | SkinAnimFlag.SYNCED_LEGS }, - { "Ski Skins", SkinAnimFlag.SYNCED_ARMS | SkinAnimFlag.STATIC_LEGS }, - { "Ghost Skins", SkinAnimFlag.STATIC_LEGS | SkinAnimFlag.ZOMBIE_ARMS }, - { "Medusa (Greek Myth.)", SkinAnimFlag.SYNCED_LEGS }, - { "Librarian (Halo)", SkinAnimFlag.STATIC_LEGS }, - { "Grim Reaper (Halloween)", SkinAnimFlag.STATIC_LEGS | SkinAnimFlag.STATIC_ARMS } + { "Steve (64x32)", SkinAnimMask.NONE }, + { "Steve (64x64)", SkinAnimMask.RESOLUTION_64x64 }, + { "Alex (64x64)", SkinAnimMask.SLIM_MODEL }, + { "Zombie Skins", SkinAnimMask.ZOMBIE_ARMS }, + { "Cetacean Skins", SkinAnimMask.SYNCED_ARMS | SkinAnimMask.SYNCED_LEGS }, + { "Ski Skins", SkinAnimMask.SYNCED_ARMS | SkinAnimMask.STATIC_LEGS }, + { "Ghost Skins", SkinAnimMask.STATIC_LEGS | SkinAnimMask.ZOMBIE_ARMS }, + { "Medusa (Greek Myth.)", SkinAnimMask.SYNCED_LEGS }, + { "Librarian (Halo)", SkinAnimMask.STATIC_LEGS }, + { "Grim Reaper (Halloween)", SkinAnimMask.STATIC_LEGS | SkinAnimMask.STATIC_ARMS } }; private void templateButton_Click(object sender, EventArgs e) { var diag = new ItemSelectionPopUp(Templates.Keys.ToArray()); - diag.label2.Text = "Presets"; - diag.okBtn.Text = "Load"; + diag.ButtonText = "Presets"; + diag.ButtonText = "Load"; if (diag.ShowDialog() != DialogResult.OK) return; diff --git a/PCK-Studio/Forms/Editor/AnimationEditor.cs b/PCK-Studio/Forms/Editor/AnimationEditor.cs index 91c6025d..aae1adf3 100644 --- a/PCK-Studio/Forms/Editor/AnimationEditor.cs +++ b/PCK-Studio/Forms/Editor/AnimationEditor.cs @@ -1,4 +1,21 @@ -using System; +/* Copyright (c) 2023-present miku-666, MattNL + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1.The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +**/ +using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; @@ -11,27 +28,27 @@ using MetroFramework.Forms; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OMI.Formats.Pck; - using PckStudio.Forms.Additional_Popups.Animation; using PckStudio.Extensions; using PckStudio.Properties; using PckStudio.Internal; using PckStudio.Internal.Json; using PckStudio.Helper; +using AnimatedGif; namespace PckStudio.Forms.Editor { public partial class AnimationEditor : MetroForm { - private PckFile.FileData animationFile; - private Animation currentAnimation; + private Animation _animation; - private string TileName = string.Empty; + private string _tileName = string.Empty; - private bool IsSpecialTile(string tileName) + private static readonly string[] specialTileNames = { "clock", "compass" }; + + private static bool IsSpecialTile(string name) { - return tileName == "clock" || tileName == "compass"; + return name.ToLower().EqualsAny(specialTileNames); } private AnimationEditor() @@ -40,89 +57,71 @@ namespace PckStudio.Forms.Editor toolStripSeparator1.Visible = saveToolStripMenuItem1.Visible = !Settings.Default.AutoSaveChanges; } - internal AnimationEditor(Animation animation, string tileName) + internal AnimationEditor(Animation animation, string name) : this() { - currentAnimation = animation; - TileName = tileName; + _ = animation ?? throw new ArgumentNullException(nameof(animation)); + _animation = animation; + _tileName = name; } - public AnimationEditor(PckFile.FileData file) - : this() + internal AnimationEditor(Animation animation, string name, Color blendColor) + : this(animation, name) { - animationFile = file; - TileName = Path.GetFileNameWithoutExtension(animationFile.Filename); - } - - public AnimationEditor(PckFile.FileData file, Color blendColor) - : this(file) - { animationPictureBox.UseBlendColor = true; animationPictureBox.BlendColor = blendColor; } - private void AnimationEditor_Load(object sender, EventArgs e) { bulkAnimationSpeedToolStripMenuItem.Enabled = importToolStripMenuItem.Enabled = exportAsToolStripMenuItem.Enabled = - InterpolationCheckbox.Visible = !IsSpecialTile(TileName); + InterpolationCheckbox.Visible = !IsSpecialTile(_tileName); - if (currentAnimation is null) - CreateAnimation(); SetTileLabel(); LoadAnimationTreeView(); } - private void CreateAnimation() - { - currentAnimation = new Animation(Array.Empty()); - if (animationFile is not null && animationFile.Size > 0) - { - var texture = animationFile.GetTexture(); - var frameTextures = texture.Split(ImageLayoutDirection.Vertical); - currentAnimation = animationFile.Properties.HasProperty("ANIM") - ? new Animation(frameTextures, animationFile.Properties.GetPropertyValue("ANIM")) - : new Animation(frameTextures, string.Empty); - } - currentAnimation.Category = animationFile.Filename.Split('/').Contains("items") - ? Animation.AnimationCategory.Items - : Animation.AnimationCategory.Blocks; - } - private void LoadAnimationTreeView() - { - if (currentAnimation is null) - { + { + if (_animation is null) + { AnimationStartStopBtn.Enabled = false; return; - } - AnimationStartStopBtn.Enabled = true; - InterpolationCheckbox.Checked = currentAnimation.Interpolate; - frameTreeView.Nodes.Clear(); - TextureIcons.Images.Clear(); - TextureIcons.Images.AddRange(currentAnimation.GetTextures().ToArray()); - foreach (var frame in currentAnimation.GetFrames()) - { - var imageIndex = currentAnimation.GetTextureIndex(frame.Texture); - frameTreeView.Nodes.Add(new TreeNode($"for {frame.Ticks} ticks") - { - ImageIndex = imageIndex, - SelectedImageIndex = imageIndex, - }); } - if (currentAnimation.FrameCount > 0) - { - animationPictureBox.SelectFrame(currentAnimation, 0); - } - } + AnimationStartStopBtn.Enabled = true; + InterpolationCheckbox.Checked = _animation.Interpolate; + TextureIcons.Images.Clear(); + TextureIcons.Images.AddRange(_animation.GetTextures().ToArray()); - private void frameTreeView_AfterSelect(object sender, TreeViewEventArgs e) + UpdateTreeView(); + + if (_animation.FrameCount > 0) + { + animationPictureBox.SelectFrame(_animation, 0); + } + } + + private void UpdateTreeView() + { + frameTreeView.Nodes.Clear(); + frameTreeView.Nodes.AddRange( + _animation.GetFrames() + .Select(frame => + { + var imageIndex = _animation.GetTextureIndex(frame.Texture); + return new TreeNode($"for {frame.Ticks} ticks", imageIndex, imageIndex); + }) + .ToArray() + ); + } + + private void frameTreeView_AfterSelect(object sender, TreeViewEventArgs e) { if (animationPictureBox.IsPlaying) AnimationStartStopBtn.Text = "Play Animation"; - animationPictureBox.SelectFrame(currentAnimation, frameTreeView.SelectedNode.Index); + animationPictureBox.SelectFrame(_animation, frameTreeView.SelectedNode.Index); } private void AnimationStartStopBtn_Click(object sender, EventArgs e) @@ -133,9 +132,9 @@ namespace PckStudio.Forms.Editor AnimationStartStopBtn.Text = "Play Animation"; return; } - if (currentAnimation.FrameCount > 1) + if (_animation.FrameCount > 1) { - animationPictureBox.Start(currentAnimation); + animationPictureBox.Start(_animation); AnimationStartStopBtn.Text = "Stop Animation"; } } @@ -158,13 +157,8 @@ namespace PckStudio.Forms.Editor private void saveToolStripMenuItem1_Click(object sender, EventArgs e) { - if (!IsSpecialTile(TileName) && currentAnimation is not null && animationFile is not null) + if (!IsSpecialTile(_tileName) && _animation is not null) { - string anim = currentAnimation.BuildAnim(); - animationFile.Properties.SetProperty("ANIM", anim); - var texture = currentAnimation.BuildTexture(); - animationFile.SetData(texture, ImageFormat.Png); - animationFile.Filename = $"res/textures/{currentAnimation.CategoryString}/{TileName}.png"; DialogResult = DialogResult.OK; return; } @@ -228,8 +222,8 @@ namespace PckStudio.Forms.Editor { int draggedIndex = draggedNode.Index; int targetIndex = targetNode.Index; - currentAnimation.GetFrames().Swap(draggedIndex, targetIndex); - LoadAnimationTreeView(); + _animation.SwapFrames(draggedIndex, targetIndex); + UpdateTreeView(); } } } @@ -250,8 +244,8 @@ namespace PckStudio.Forms.Editor private void treeView1_doubleClick(object sender, EventArgs e) { - var frame = currentAnimation.GetFrame(frameTreeView.SelectedNode.Index); - using FrameEditor diag = new FrameEditor(frame.Ticks, currentAnimation.GetTextureIndex(frame.Texture), TextureIcons); + var frame = _animation.GetFrame(frameTreeView.SelectedNode.Index); + using FrameEditor diag = new FrameEditor(frame.Ticks, _animation.GetTextureIndex(frame.Texture), TextureIcons); if (diag.ShowDialog(this) == DialogResult.OK) { /* Found a bug here. When passing the frame variable, @@ -261,8 +255,8 @@ namespace PckStudio.Forms.Editor * - Matt */ - currentAnimation.SetFrame(frameTreeView.SelectedNode.Index, diag.FrameTextureIndex, diag.FrameTime); - LoadAnimationTreeView(); + _animation.SetFrame(frameTreeView.SelectedNode.Index, diag.FrameTextureIndex, diag.FrameTime); + UpdateTreeView(); } } @@ -272,17 +266,17 @@ namespace PckStudio.Forms.Editor diag.SaveBtn.Text = "Add"; if (diag.ShowDialog(this) == DialogResult.OK) { - currentAnimation.AddFrame(diag.FrameTextureIndex, TileName == "clock" || TileName == "compass" ? 1 : diag.FrameTime); - LoadAnimationTreeView(); + _animation.AddFrame(diag.FrameTextureIndex, IsSpecialTile(_tileName) ? Animation.MinimumFrameTime : diag.FrameTime); + UpdateTreeView(); } } private void removeFrameToolStripMenuItem_Click(object sender, EventArgs e) { - if (frameTreeView.SelectedNode is TreeNode t && - currentAnimation.RemoveFrame(t.Index)) + if (frameTreeView.SelectedNode is TreeNode t && _animation.RemoveFrame(t.Index)) + { frameTreeView.SelectedNode.Remove(); - + } } private void bulkAnimationSpeedToolStripMenuItem_Click(object sender, EventArgs e) @@ -290,13 +284,14 @@ namespace PckStudio.Forms.Editor SetBulkSpeed diag = new SetBulkSpeed(); if (diag.ShowDialog(this) == DialogResult.OK) { - currentAnimation.GetFrames().ForEach(frame => frame.Ticks = diag.Ticks); - LoadAnimationTreeView(); + if (animationPictureBox.IsPlaying) + animationPictureBox.Stop(); + _animation.SetFrameTicks(diag.Ticks); + UpdateTreeView(); } diag.Dispose(); } - // Reworked import tool with new Animation classes by Miku private void importJavaAnimationToolStripMenuItem_Click(object sender, EventArgs e) { if (MessageBox.Show( @@ -324,51 +319,13 @@ namespace PckStudio.Forms.Editor MessageBox.Show(textureFile + " was not found", "Texture not found"); return; } - var textures = Image.FromFile(textureFile).Split(ImageLayoutDirection.Vertical); - var new_animation = new Animation(textures); - new_animation.Category = currentAnimation.Category; try { + var img = Image.FromFile(textureFile); JObject mcmeta = JObject.Parse(File.ReadAllText(fileDialog.FileName)); - if (mcmeta["animation"] is JToken animation) - { - int frameTime = Animation.MinimumFrameTime; - // Some if statements to ensure that the animation is valid. - if (animation["frametime"] is JToken frametime_token && frametime_token.Type == JTokenType.Integer) - frameTime = (int)frametime_token; - if (animation["interpolate"] is JToken interpolate_token && interpolate_token.Type == JTokenType.Boolean) - new_animation.Interpolate = (bool)interpolate_token; - if (animation["frames"] is JToken frames_token && - frames_token.Type == JTokenType.Array) - { - foreach (JToken frame in frames_token.Children()) - { - if (frame.Type == JTokenType.Object) - { - if (frame["index"] is JToken frame_index && frame_index.Type == JTokenType.Integer && - frame["time"] is JToken frame_time && frame_time.Type == JTokenType.Integer) - { - Debug.WriteLine("{0}*{1}", (int)frame["index"], (int)frame["time"]); - new_animation.AddFrame((int)frame["index"], (int)frame["time"]); - } - } - else if (frame.Type == JTokenType.Integer) - { - Debug.WriteLine("{0}*{1}", (int)frame, frameTime); - new_animation.AddFrame((int)frame, frameTime); - } - } - } - else - { - for (int i = 0; i < new_animation.TextureCount; i++) - { - new_animation.AddFrame(i, frameTime); - } - } - } - - currentAnimation = new_animation; + Animation javaAnimation = AnimationHelper.GetAnimationFromJavaAnimation(mcmeta, img); + javaAnimation.Category = _animation.Category; + _animation = javaAnimation; LoadAnimationTreeView(); } catch (JsonException j_ex) @@ -381,48 +338,45 @@ namespace PckStudio.Forms.Editor private void changeTileToolStripMenuItem_Click(object sender, EventArgs e) { using (ChangeTile diag = new ChangeTile()) - if (diag.ShowDialog(this) == DialogResult.OK) - { - Debug.WriteLine(diag.SelectedTile); - currentAnimation.Category = diag.Category; - TileName = diag.SelectedTile; + { + if (diag.ShowDialog(this) != DialogResult.OK) + return; + + Debug.WriteLine(diag.SelectedTile); + _animation.Category = diag.Category; + _tileName = diag.SelectedTile; - bulkAnimationSpeedToolStripMenuItem.Enabled = - importToolStripMenuItem.Enabled = - exportAsToolStripMenuItem.Enabled = - InterpolationCheckbox.Visible = !IsSpecialTile(TileName); + bulkAnimationSpeedToolStripMenuItem.Enabled = + importToolStripMenuItem.Enabled = + exportAsToolStripMenuItem.Enabled = + InterpolationCheckbox.Visible = !IsSpecialTile(_tileName); - SetTileLabel(); - } + SetTileLabel(); + } } private void SetTileLabel() { - var textureInfos = currentAnimation.Category switch + var textureInfos = _animation.Category switch { - Animation.AnimationCategory.Blocks => Tiles.BlockTileInfos, - Animation.AnimationCategory.Items => Tiles.ItemTileInfos, - _ => throw new ArgumentOutOfRangeException(currentAnimation.Category.ToString()) + AnimationCategory.Blocks => Tiles.BlockTileInfos, + AnimationCategory.Items => Tiles.ItemTileInfos, + _ => throw new ArgumentOutOfRangeException(_animation.Category.ToString()) }; + tileLabel.Text = textureInfos.FirstOrDefault(p => p.InternalName == _tileName)?.DisplayName ?? _tileName; - if (textureInfos.FirstOrDefault(p => p.InternalName == TileName) is JsonTileInfo textureInfo) - { - tileLabel.Text = textureInfo.DisplayName; - return; - } - - switch (MessageBox.Show(this, - $"{TileName} is not a valid tile for animation, and will not play in game. Would you like to choose a new tile?", - "Not a valid tile", - MessageBoxButtons.YesNo)) - { - case DialogResult.Yes: - changeTileToolStripMenuItem_Click(null, EventArgs.Empty); - break; - default: - DialogResult = DialogResult.Abort; - break; - } + //switch (MessageBox.Show(this, + // $"{TileName} is not a valid tile for animation, and will not play in game. Would you like to choose a new tile?", + // "Not a valid tile", + // MessageBoxButtons.YesNo)) + //{ + // case DialogResult.Yes: + // changeTileToolStripMenuItem_Click(null, EventArgs.Empty); + // break; + // default: + // DialogResult = DialogResult.Abort; + // break; + //} } private void exportJavaAnimationToolStripMenuItem_Click(object sender, EventArgs e) @@ -432,13 +386,15 @@ namespace PckStudio.Forms.Editor fileDialog.Filter = "Animation Scripts (*.mcmeta)|*.png.mcmeta"; if (fileDialog.ShowDialog(this) == DialogResult.OK) { - JObject mcmeta = currentAnimation.ConvertToJavaAnimation(); + JObject mcmeta = _animation.ConvertToJavaAnimation(); string jsondata = JsonConvert.SerializeObject(mcmeta, Formatting.Indented); string filename = fileDialog.FileName; File.WriteAllText(filename, jsondata); - var finalTexture = currentAnimation.BuildTexture(); - finalTexture.Save(Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename))); // removes ".mcmeta" from filename! - MessageBox.Show("Animation was successfully exported to " + filename, "Export successful!"); + var finalTexture = _animation.BuildTexture(); + // removes ".mcmeta" from filename + string texturePath = Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename)); + finalTexture.Save(texturePath); + MessageBox.Show("Animation was successfully exported as " + Path.GetFileName(filename), "Export successful!"); } } @@ -466,8 +422,8 @@ namespace PckStudio.Forms.Editor private void InterpolationCheckbox_CheckedChanged(object sender, EventArgs e) { - if (currentAnimation is not null) - currentAnimation.Interpolate = InterpolationCheckbox.Checked; + if (_animation is not null) + _animation.Interpolate = InterpolationCheckbox.Checked; } private void AnimationEditor_FormClosing(object sender, FormClosingEventArgs e) @@ -508,7 +464,7 @@ namespace PckStudio.Forms.Editor gif.SelectActiveFrame(dimension, i); textures.Add(new Bitmap(gif)); } - currentAnimation = new Animation(textures, string.Empty); + _animation = new Animation(textures, string.Empty); LoadAnimationTreeView(); } @@ -523,44 +479,27 @@ namespace PckStudio.Forms.Editor return; Image img = Image.FromFile(ofd.FileName); var textures = img.Split(ImageLayoutDirection.Vertical); - currentAnimation = new Animation(textures, string.Empty); + _animation = new Animation(textures, string.Empty); LoadAnimationTreeView(); } - //[System.Runtime.InteropServices.DllImport("gdi32.dll")] - //public static extern bool DeleteObject(IntPtr hObject); - - private void gifToolStripMenuItem_Click(object sender, EventArgs e) + private void gifToolStripMenuItem_Click(object sender, EventArgs e) { - MessageBox.Show(this, "This feature is still under development", "Coming soon"); - return; + var fileDialog = new SaveFileDialog() + { + FileName = _tileName, + Filter = "GIF file|*.gif" + }; + if (fileDialog.ShowDialog(this) != DialogResult.OK) + return; - // TODO - //var fileDialog = new SaveFileDialog() - //{ - // Filter = "GIF file|*.gif" - //}; - //if (fileDialog.ShowDialog(this) != DialogResult.OK) - // return; - - //GifBitmapEncoder gifBitmapEncoder = new GifBitmapEncoder(); - - //foreach (Bitmap texture in currentAnimation.GetTextures()) - //{ - // var bmp = texture.GetHbitmap(); - // var src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( - // bmp, - // IntPtr.Zero, - // System.Windows.Int32Rect.Empty, - // BitmapSizeOptions.FromWidthAndHeight(texture.Width, texture.Height)); - // gifBitmapEncoder.Frames.Add(BitmapFrame.Create(src)); - // DeleteObject(bmp); // recommended, handle memory leak - //} - - //using (var fs = fileDialog.OpenFile()) - //{ - // gifBitmapEncoder.Save(fs); - //} + using (var gifWriter = AnimatedGif.AnimatedGif.Create(fileDialog.FileName, Animation.GameTickInMilliseconds, repeat: 0)) + { + foreach (var frame in _animation.GetInterpolatedFrames()) + { + gifWriter.AddFrame(frame.Texture, frame.Ticks * Animation.GameTickInMilliseconds, GifQuality.Bit8); + } + } } private void frameTimeandTicksToolStripMenuItem_Click(object sender, EventArgs e) diff --git a/PCK-Studio/Forms/Editor/AudioEditor.cs b/PCK-Studio/Forms/Editor/AudioEditor.cs index 9ed7e8dd..3ac86ddc 100644 --- a/PCK-Studio/Forms/Editor/AudioEditor.cs +++ b/PCK-Studio/Forms/Editor/AudioEditor.cs @@ -29,7 +29,7 @@ namespace PckStudio.Forms.Editor { public string defaultType = "yes"; PckAudioFile audioFile = null; - PckFile.FileData audioPCK; + PckFileData audioPCK; bool _isLittleEndian = false; MainForm parent = null; @@ -63,7 +63,7 @@ namespace PckStudio.Forms.Editor return (PckAudioFile.AudioCategory.EAudioType)Categories.IndexOf(category); } - public AudioEditor(PckFile.FileData file, bool isLittleEndian) + public AudioEditor(PckFileData file, bool isLittleEndian) { InitializeComponent(); @@ -577,7 +577,7 @@ namespace PckStudio.Forms.Editor if (available.Length > 0) { using ItemSelectionPopUp add = new ItemSelectionPopUp(available); - add.okBtn.Text = "Save"; + add.ButtonText = "Save"; if (add.ShowDialog() != DialogResult.OK) return; audioFile.RemoveCategory(category.audioType); diff --git a/PCK-Studio/Forms/Editor/BehaviourEditor.cs b/PCK-Studio/Forms/Editor/BehaviourEditor.cs index 2342f4e5..5e4dddde 100644 --- a/PCK-Studio/Forms/Editor/BehaviourEditor.cs +++ b/PCK-Studio/Forms/Editor/BehaviourEditor.cs @@ -20,7 +20,7 @@ namespace PckStudio.Forms.Editor public partial class BehaviourEditor : MetroForm { // Behaviours File Format research by Miku and MattNL - private readonly PckFile.FileData _file; + private readonly PckFileData _file; BehaviourFile behaviourFile; private readonly JObject EntityJSONData = JObject.Parse(Properties.Resources.entityData); @@ -61,7 +61,7 @@ namespace PckStudio.Forms.Editor treeView1.EndUpdate(); } - public BehaviourEditor(PckFile.FileData file) + public BehaviourEditor(PckFileData file) { InitializeComponent(); diff --git a/PCK-Studio/Forms/Editor/COLEditor.cs b/PCK-Studio/Forms/Editor/COLEditor.cs index f7f38fc1..611eaafb 100644 --- a/PCK-Studio/Forms/Editor/COLEditor.cs +++ b/PCK-Studio/Forms/Editor/COLEditor.cs @@ -19,14 +19,14 @@ namespace PckStudio.Forms.Editor ColorContainer colourfile; ColorContainer.Color clipboard_color; - private readonly PckFile.FileData _file; + private readonly PckFileData _file; List colorCache = new List(); List waterCache = new List(); List underwaterCache = new List(); List fogCache = new List(); - public COLEditor(PckFile.FileData file) + public COLEditor(PckFileData file) { InitializeComponent(); diff --git a/PCK-Studio/Forms/Editor/GameRuleFileEditor.cs b/PCK-Studio/Forms/Editor/GameRuleFileEditor.cs index cdee39b6..d4daade9 100644 --- a/PCK-Studio/Forms/Editor/GameRuleFileEditor.cs +++ b/PCK-Studio/Forms/Editor/GameRuleFileEditor.cs @@ -36,10 +36,15 @@ namespace PckStudio.Forms.Editor { public partial class GameRuleFileEditor : MetroFramework.Forms.MetroForm { - private PckFile.FileData _pckfile; + private PckFileData _pckfile; private GameRuleFile _file; private GameRuleFile.CompressionType compressionType; private GameRuleFile.CompressionLevel compressionLevel; + + private const string use_zlib = "Wii U, PS Vita"; + private const string use_deflate = "PS3"; + private const string use_xmem = "Xbox 360"; + public GameRuleFileEditor() { InitializeComponent(); @@ -49,27 +54,27 @@ namespace PckStudio.Forms.Editor private void PromptForCompressionType() { - ItemSelectionPopUp dialog = new ItemSelectionPopUp("Wii U, PS Vita", "PS3", "Xbox 360"); - dialog.label2.Text = "Type"; - dialog.okBtn.Text = "Ok"; + ItemSelectionPopUp dialog = new ItemSelectionPopUp(use_zlib, use_deflate, use_xmem); + dialog.LabelText = "Type"; + dialog.ButtonText = "Ok"; if (dialog.ShowDialog() == DialogResult.OK) { switch(dialog.SelectedItem) { - case "Wii U, PS Vita": + case use_zlib: wiiUPSVitaToolStripMenuItem.Checked = true; break; - case "PS3": + case use_deflate: pS3ToolStripMenuItem.Checked = true; break; - case "Xbox 360": + case use_xmem: xbox360ToolStripMenuItem.Checked = true; break; } } } - public GameRuleFileEditor(PckFile.FileData file) : this() + public GameRuleFileEditor(PckFileData file) : this() { _pckfile = file; using (var stream = new MemoryStream(file.Data)) @@ -127,7 +132,7 @@ namespace PckStudio.Forms.Editor private void SetCompressionLevel() { - switch (_file.FileHeader.CompressionLevel) + switch (_file.Header.CompressionLevel) { case GameRuleFile.CompressionLevel.None: noneToolStripMenuItem.Checked = true; @@ -258,7 +263,7 @@ namespace PckStudio.Forms.Editor private void saveToolStripMenuItem_Click(object sender, EventArgs e) { - if (_file.FileHeader.unknownData[3] != 0) + if (_file.Header.unknownData[3] != 0) { MessageBox.Show("World grf saving is currently unsupported"); return; diff --git a/PCK-Studio/Forms/Editor/LOCEditor.cs b/PCK-Studio/Forms/Editor/LOCEditor.cs index d830e9ea..d19c067c 100644 --- a/PCK-Studio/Forms/Editor/LOCEditor.cs +++ b/PCK-Studio/Forms/Editor/LOCEditor.cs @@ -19,9 +19,9 @@ namespace PckStudio.Forms.Editor { DataTable tbl; LOCFile currentLoc; - PckFile.FileData _file; + PckFileData _file; - public LOCEditor(PckFile.FileData file) + public LOCEditor(PckFileData file) { InitializeComponent(); _file = file; diff --git a/PCK-Studio/Forms/Editor/MaterialsEditor.cs b/PCK-Studio/Forms/Editor/MaterialsEditor.cs index 24623953..88bb11e8 100644 --- a/PCK-Studio/Forms/Editor/MaterialsEditor.cs +++ b/PCK-Studio/Forms/Editor/MaterialsEditor.cs @@ -18,7 +18,7 @@ namespace PckStudio.Forms.Editor public partial class MaterialsEditor : MetroForm { // Materials File Format research by PhoenixARC - private readonly PckFile.FileData _file; + private readonly PckFileData _file; MaterialContainer materialFile; private readonly JObject EntityJSONData = JObject.Parse(Properties.Resources.entityData); @@ -50,7 +50,7 @@ namespace PckStudio.Forms.Editor treeView1.EndUpdate(); } - public MaterialsEditor(PckFile.FileData file) + public MaterialsEditor(PckFileData file) { InitializeComponent(); _file = file; diff --git a/PCK-Studio/Forms/Editor/TextureAtlasEditor.cs b/PCK-Studio/Forms/Editor/TextureAtlasEditor.cs index a35b9cc1..1d71098f 100644 --- a/PCK-Studio/Forms/Editor/TextureAtlasEditor.cs +++ b/PCK-Studio/Forms/Editor/TextureAtlasEditor.cs @@ -107,7 +107,7 @@ namespace PckStudio.Forms.Editor private bool AcquireColorTable(PckFile pckFile) { - if (pckFile.TryGetFile("colours.col", PckFile.FileData.FileType.ColourTableFile, out var colFile) && + if (pckFile.TryGetFile("colours.col", PckFileType.ColourTableFile, out var colFile) && colFile.Size > 0) { using var ms = new MemoryStream(colFile.Data); @@ -137,7 +137,7 @@ namespace PckStudio.Forms.Editor if (_tiles is null || !_tiles.IndexInRange(index) || (_selectedTile = _tiles[index]) is null) return; - if(String.IsNullOrEmpty(_selectedTile.Tile.DisplayName)) + if(string.IsNullOrEmpty(_selectedTile.Tile.DisplayName)) { // changes the selected tile to the base flowing tile (carries all properties over) - Matt _selectedTile = _tiles.Find(t => t.Tile.InternalName == _selectedTile.Tile.InternalName); @@ -148,7 +148,7 @@ namespace PckStudio.Forms.Editor selectTilePictureBox.UseBlendColor = applyColorMaskToolStripMenuItem.Checked; bool hasAnimation = - _pckFile.Files.TryGetValue($"res/textures/{_atlasType}/{_selectedTile.Tile.InternalName}.png", PckFile.FileData.FileType.TextureFile, out var animationFile); + _pckFile.TryGetValue($"res/textures/{_atlasType}/{_selectedTile.Tile.InternalName}.png", PckFileType.TextureFile, out var animationFile); animationButton.Text = hasAnimation ? "Edit Animation" : "Create Animation"; replaceButton.Enabled = !hasAnimation; @@ -156,15 +156,12 @@ namespace PckStudio.Forms.Editor hasAnimation && animationFile.Size > 0) { - using var ms = new MemoryStream(animationFile.Data); - var img = Image.FromStream(ms); - var textures = img.Split(ImageLayoutDirection.Vertical); - var animation = new Internal.Animation(textures, animationFile.Properties.GetPropertyValue("ANIM")); + var animation = AnimationHelper.GetAnimationFromFile(animationFile); selectTilePictureBox.Start(animation); return; } - if (variantLabel.Visible = variantComboBox.Visible = _selectedTile.Tile.HasColourEntry && _selectedTile.Tile.ColourEntry.Variants.Length > 1) + if (variantComboBox.Enabled = variantLabel.Visible = variantComboBox.Visible = _selectedTile.Tile.HasColourEntry && _selectedTile.Tile.ColourEntry.Variants.Length > 1) { variantComboBox.Items.AddRange(_selectedTile.Tile.ColourEntry.Variants); variantComboBox.SelectedItem = _selectedTile.Tile.ColourEntry.DefaultName; @@ -292,15 +289,6 @@ namespace PckStudio.Forms.Editor originalPictureBox.Invalidate(); } - private void ApplyBlend(string colorKey, Image image) - { - if (variantComboBox.Enabled = _selectedTile.Tile.ColourEntry.Variants.Length > 1) - { - selectTilePictureBox.BlendColor = FindBlendColorByKey(colorKey); - selectTilePictureBox.Image = image; - } - } - private Color GetBlendColor() { if (_selectedTile.Tile.HasColourEntry && _selectedTile.Tile.ColourEntry is not null) @@ -406,20 +394,20 @@ namespace PckStudio.Forms.Editor private void animationButton_Click(object sender, EventArgs e) { - bool isNewFile; - if (isNewFile = !_pckFile.Files.TryGetValue( + var file = _pckFile.GetOrCreate( $"res/textures/{_atlasType}/{_selectedTile.Tile.InternalName}.png", - PckFile.FileData.FileType.TextureFile, out var file - )) + PckFileType.TextureFile + ); + + var animation = AnimationHelper.GetAnimationFromFile(file); + + var animationEditor = new AnimationEditor(animation, _selectedTile.Tile.InternalName, GetBlendColor()); + if (animationEditor.ShowDialog() != DialogResult.OK) { - file = new PckFile.FileData($"res/textures/{_atlasType}/{_selectedTile.Tile.InternalName}.png", PckFile.FileData.FileType.TextureFile); + return; } - var animationEditor = new AnimationEditor(file, GetBlendColor()); - if (animationEditor.ShowDialog() == DialogResult.OK && isNewFile) - { - _pckFile.Files.Add(file); - } + AnimationHelper.SaveAnimationToFile(file, animation); } private void extractTileToolStripMenuItem_Click(object sender, EventArgs e) @@ -440,7 +428,9 @@ namespace PckStudio.Forms.Editor if (_selectedTile.Tile.ColourEntry is not null && _selectedTile.Tile.ColourEntry.Variants.IndexInRange(variantComboBox.SelectedIndex)) { - ApplyBlend(_selectedTile.Tile.ColourEntry.Variants[variantComboBox.SelectedIndex], _selectedTile.Texture); + string colorKey = _selectedTile.Tile.ColourEntry.Variants[variantComboBox.SelectedIndex]; + selectTilePictureBox.BlendColor = FindBlendColorByKey(colorKey); + selectTilePictureBox.Image = _selectedTile.Texture; } } diff --git a/PCK-Studio/Forms/Skins-And-Textures/AddNewSkin.cs b/PCK-Studio/Forms/Skins-And-Textures/AddNewSkin.cs index ba43f2f8..3f34cb3d 100644 --- a/PCK-Studio/Forms/Skins-And-Textures/AddNewSkin.cs +++ b/PCK-Studio/Forms/Skins-And-Textures/AddNewSkin.cs @@ -17,13 +17,13 @@ namespace PckStudio.Popups { public partial class AddNewSkin : MetroFramework.Forms.MetroForm { - public PckFile.FileData SkinFile => skin; - public PckFile.FileData CapeFile => cape; + public PckFileData SkinFile => skin; + public PckFileData CapeFile => cape; public bool HasCape => cape is not null; private LOCFile currentLoc; - private PckFile.FileData skin = new PckFile.FileData("dlcskinXYXYXYXY", PckFile.FileData.FileType.SkinFile); - private PckFile.FileData cape; + private PckFileData skin = new PckFileData("dlcskinXYXYXYXY", PckFileType.SkinFile); + private PckFileData cape; private SkinANIM anim = new SkinANIM(); private Random rng = new Random(); @@ -224,7 +224,7 @@ namespace PckStudio.Popups return; } capePictureBox.Image = Image.FromFile(ofd.FileName); - cape ??= new PckFile.FileData("dlccapeXYXYXYXY", PckFile.FileData.FileType.CapeFile); + cape ??= new PckFileData("dlccapeXYXYXYXY", PckFileType.CapeFile); cape.SetData(File.ReadAllBytes(ofd.FileName)); contextMenuCape.Items[0].Text = "Replace"; capeLabel.Visible = false; diff --git a/PCK-Studio/Forms/Skins-And-Textures/AdvancedOptions.cs b/PCK-Studio/Forms/Skins-And-Textures/AdvancedOptions.cs index 9c9f3071..65f15c65 100644 --- a/PCK-Studio/Forms/Skins-And-Textures/AdvancedOptions.cs +++ b/PCK-Studio/Forms/Skins-And-Textures/AdvancedOptions.cs @@ -37,26 +37,26 @@ namespace PckStudio.Popups { if (fileTypeComboBox.SelectedIndex >= 0 && fileTypeComboBox.SelectedIndex <= 13) { - applyBulkProperties(_pckFile.Files, fileTypeComboBox.SelectedIndex - 1); + applyBulkProperties(_pckFile.GetFiles(), fileTypeComboBox.SelectedIndex - 1); DialogResult = DialogResult.OK; return; } MessageBox.Show("Please select a filetype before applying"); } - private void applyBulkProperties(FileCollection files, int index) + private void applyBulkProperties(IReadOnlyCollection files, int index) { - foreach (PckFile.FileData file in files) + foreach (PckFileData file in files) { - if (file.Filetype == PckFile.FileData.FileType.TexturePackInfoFile || - file.Filetype == PckFile.FileData.FileType.SkinDataFile) + if (file.Filetype == PckFileType.TexturePackInfoFile || + file.Filetype == PckFileType.SkinDataFile) { try { var reader = new PckFileReader(_endianness); using var ms = new MemoryStream(file.Data); PckFile subPCK = reader.FromStream(ms); - applyBulkProperties(subPCK.Files, index); + applyBulkProperties(subPCK.GetFiles(), index); file.SetData(new PckFileWriter(subPCK, _endianness)); } catch (OverflowException ex) @@ -65,15 +65,15 @@ namespace PckStudio.Popups } } - if (index == -1 || (Enum.IsDefined(typeof(PckFile.FileData.FileType), index) && (int)file.Filetype == index)) + if (index == -1 || (Enum.IsDefined(typeof(PckFileType), index) && (int)file.Filetype == index)) { file.Properties.Add(propertyKeyTextBox.Text, propertyValueTextBox.Text); } } - if (Enum.IsDefined(typeof(PckFile.FileData.FileType), index)) + if (Enum.IsDefined(typeof(PckFileType), index)) { - MessageBox.Show($"Data added to {(PckFile.FileData.FileType)index} entries"); + MessageBox.Show($"Data added to {(PckFileType)index} entries"); return; } MessageBox.Show("Data added to all entries"); diff --git a/PCK-Studio/Forms/Skins-And-Textures/generateModel.cs b/PCK-Studio/Forms/Skins-And-Textures/generateModel.cs index aa9a0d93..64e7dadb 100644 --- a/PCK-Studio/Forms/Skins-And-Textures/generateModel.cs +++ b/PCK-Studio/Forms/Skins-And-Textures/generateModel.cs @@ -39,7 +39,7 @@ namespace PckStudio.Forms left, } - private PckFile.FileData _file; + private PckFileData _file; private SkinANIM _ANIM; private static Color _backgroundColor = Color.FromArgb(0xff, 0x50, 0x50, 0x50); @@ -126,7 +126,7 @@ namespace PckStudio.Forms } } - public generateModel(PckFile.FileData file) + public generateModel(PckFileData file) { MessageBox.Show(this, "This feature is now considered deprecated and will no longer recieve updates. A better alternative is currently under development. Use at your own risk.", "Deprecated Feature", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); InitializeComponent(); @@ -149,7 +149,7 @@ namespace PckStudio.Forms return sWhitespace.Replace(input, replacement); } - private void LoadData(PckFile.PCKProperties properties) + private void LoadData(PckFileProperties properties) { comboParent.Enabled = properties.GetProperties("BOX").All(kv => { var box = SkinBOX.FromString(kv.Value); diff --git a/PCK-Studio/Forms/Utilities/PckCenterBeta.cs b/PCK-Studio/Forms/Utilities/PckCenterBeta.cs index d96eab9b..a5f68a1d 100644 --- a/PCK-Studio/Forms/Utilities/PckCenterBeta.cs +++ b/PCK-Studio/Forms/Utilities/PckCenterBeta.cs @@ -11,9 +11,6 @@ using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Media.Imaging; using MetroFramework.Forms; -using PckStudio.Classes.Misc; -using PckStudio.Classes.Networking; -using PckStudio.Classes.IO; using PckStudio.API.PCKCenter.model; using PckStudio.API.PCKCenter; diff --git a/PCK-Studio/Forms/Utilities/TextureConverterUtility.cs b/PCK-Studio/Forms/Utilities/TextureConverterUtility.cs index 336f0045..f5e9f12b 100644 --- a/PCK-Studio/Forms/Utilities/TextureConverterUtility.cs +++ b/PCK-Studio/Forms/Utilities/TextureConverterUtility.cs @@ -314,13 +314,13 @@ namespace PckStudio.Forms.Utilities switch (tn.Text) { case ("terrain.png"): - Terrain = Image.FromStream(new MemoryStream(((PckFile.FileData)(tn.Tag)).Data)); + Terrain = Image.FromStream(new MemoryStream(((PckFileData)(tn.Tag)).Data)); break; case ("items.png"): - Items = Image.FromStream(new MemoryStream(((PckFile.FileData)(tn.Tag)).Data)); + Items = Image.FromStream(new MemoryStream(((PckFileData)(tn.Tag)).Data)); break; case ("art"): - painting = Image.FromStream(new MemoryStream(((PckFile.FileData)(tn.Nodes[0].Tag)).Data)); + painting = Image.FromStream(new MemoryStream(((PckFileData)(tn.Nodes[0].Tag)).Data)); break; case ("mob"): EntityNode = tn; @@ -426,7 +426,7 @@ namespace PckStudio.Forms.Utilities string Outpath = "assets\\minecraft\\textures\\"; - foreach (PckFile.FileData mf in Pck.Files) + foreach (PckFileData mf in Pck.GetFiles()) { FileInfo file = new FileInfo(Environment.CurrentDirectory + "\\Temp\\" + @"\" + mf.Filename); file.Directory.Create(); // If the directory already exists, this method does nothing. @@ -451,7 +451,7 @@ namespace PckStudio.Forms.Utilities string Outpath = "assets\\minecraft\\textures\\"; - foreach (PckFile.FileData mf in Pck.Files) + foreach (PckFileData mf in Pck.GetFiles()) { FileInfo file = new FileInfo(Environment.CurrentDirectory + "\\Temp\\" + @"\" + mf.Filename); file.Directory.Create(); // If the directory already exists, this method does nothing. @@ -476,7 +476,7 @@ namespace PckStudio.Forms.Utilities string Outpath = "assets\\minecraft\\textures\\"; - foreach (PckFile.FileData mf in Pck.Files) + foreach (PckFileData mf in Pck.GetFiles()) { FileInfo file = new FileInfo(Environment.CurrentDirectory + "\\Temp\\" + @"\" + mf.Filename); file.Directory.Create(); // If the directory already exists, this method does nothing. diff --git a/PCK-Studio/Forms/Utilities/pckCenterOpen.cs b/PCK-Studio/Forms/Utilities/pckCenterOpen.cs index 0afe5b6e..3bdc4fd7 100644 --- a/PCK-Studio/Forms/Utilities/pckCenterOpen.cs +++ b/PCK-Studio/Forms/Utilities/pckCenterOpen.cs @@ -137,11 +137,11 @@ namespace PckStudio.Forms //MessageBox.Show(root);//debug thingy to make sure filepath is correct //add all skins to a list - List skinsList = new List(); - List capesList = new List(); + List skinsList = new List(); + List capesList = new List(); var reader = new PckFileReader(); PckFile currentPCK = reader.FromFile(Program.AppData + "/PCK-Center/myPcks/" + mod + ".pck"); - foreach (PckFile.FileData skin in currentPCK.Files) + foreach (PckFileData skin in currentPCK.GetFiles()) { if (skin.Filename.Count() == 19) { @@ -175,7 +175,7 @@ namespace PckStudio.Forms writeSkins.WriteLine(" \"skins\": ["); int skinAmount = 0; - foreach (PckFile.FileData newSkin in skinsList) + foreach (PckFileData newSkin in skinsList) { skinAmount += 1; string skinName = "skinName"; @@ -232,7 +232,7 @@ namespace PckStudio.Forms { writeSkins.WriteLine("{"); int newSkinCount = 0; - foreach (PckFile.FileData newSkin in skinsList) + foreach (PckFileData newSkin in skinsList) { newSkinCount += 1; @@ -1020,7 +1020,7 @@ namespace PckStudio.Forms } //adds skin textures - foreach (PckFile.FileData skinTexture in skinsList) + foreach (PckFileData skinTexture in skinsList) { var ms = new MemoryStream(skinTexture.Data); Bitmap saveSkin = new Bitmap(Image.FromStream(ms)); @@ -1049,7 +1049,7 @@ namespace PckStudio.Forms } //adds cape textures - foreach (PckFile.FileData capeTexture in capesList) + foreach (PckFileData capeTexture in capesList) { File.WriteAllBytes(root + "/" + capeTexture.Filename, capeTexture.Data); } diff --git a/PCK-Studio/Helper/AnimationHelper.cs b/PCK-Studio/Helper/AnimationHelper.cs new file mode 100644 index 00000000..a9714d15 --- /dev/null +++ b/PCK-Studio/Helper/AnimationHelper.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using OMI.Formats.Pck; +using PckStudio.Extensions; +using PckStudio.Internal; + +namespace PckStudio.Helper +{ + internal static class AnimationHelper + { + internal static void SaveAnimationToFile(PckFileData file, Animation animation) + { + string anim = animation.BuildAnim(); + file.Properties.SetProperty("ANIM", anim); + var texture = animation.BuildTexture(); + file.SetData(texture, ImageFormat.Png); + } + + internal static Animation GetAnimationFromFile(PckFileData file) + { + _ = file ?? throw new ArgumentNullException(nameof(file)); + if (file.Size > 0) + { + var texture = file.GetTexture(); + var frameTextures = texture.Split(ImageLayoutDirection.Vertical); + var _animation = new Animation(frameTextures, file.Properties.GetPropertyValue("ANIM")); + _animation.Category = file.Filename.Split('/').Contains("items") + ? AnimationCategory.Items + : AnimationCategory.Blocks; + return _animation; + } + return Animation.Empty(file.Filename.Split('/').Contains("items") + ? AnimationCategory.Items + : AnimationCategory.Blocks); + } + + internal static Animation GetAnimationFromJavaAnimation(JObject jsonObject, Image texture) + { + var textures = texture.Split(ImageLayoutDirection.Vertical); + Animation result = new Animation(textures); + if (jsonObject["animation"] is not JToken animation) + return result; + + int frameTime = Animation.MinimumFrameTime; + + if (animation["frametime"] is JToken frametime_token && frametime_token.Type == JTokenType.Integer) + frameTime = (int)frametime_token; + + if (animation["interpolate"] is JToken interpolate_token && interpolate_token.Type == JTokenType.Boolean) + result.Interpolate = (bool)interpolate_token; + + if (animation["frames"] is JToken frames_token && frames_token.Type == JTokenType.Array) + { + foreach (JToken frame in frames_token.Children()) + { + if (frame.Type == JTokenType.Object && + frame["index"] is JToken frame_index && + frame_index.Type == JTokenType.Integer && + frame["time"] is JToken frame_time && + frame_time.Type == JTokenType.Integer) + { + Debug.WriteLine("Index: {0}, Time: {1}", frame_index, frame_time); + result.AddFrame((int)frame_index, (int)frame_time); + } + else if (frame.Type == JTokenType.Integer) + { + Debug.WriteLine("Index: {0}, Time: {1}", frame, frameTime); + result.AddFrame((int)frame, frameTime); + } + } + return result; + } + + for (int i = 0; i < result.TextureCount; i++) + { + result.AddFrame(i, frameTime); + } + + return result; + } + } +} diff --git a/PCK-Studio/Internal/Animation.cs b/PCK-Studio/Internal/Animation.cs index 4d9c972b..2b877380 100644 --- a/PCK-Studio/Internal/Animation.cs +++ b/PCK-Studio/Internal/Animation.cs @@ -20,24 +20,30 @@ using System.Collections.Generic; using System.Drawing; using PckStudio.Extensions; using System.Text; +using System.Collections.ObjectModel; +using System.Linq; namespace PckStudio.Internal { - internal sealed class Animation + internal sealed class Animation { public const int MinimumFrameTime = 1; + public const int GameTickInMilliseconds = 50; + + public static Animation Empty(AnimationCategory category) + { + var animation = new Animation(Array.Empty(), string.Empty); + animation.Category = category; + return animation; + } + public int FrameCount => frames.Count; public int TextureCount => textures.Count; public bool Interpolate { get; set; } = false; - public enum AnimationCategory - { - Items, - Blocks - } public AnimationCategory Category { get; set; } @@ -55,8 +61,7 @@ namespace PckStudio.Internal private readonly List textures; - private readonly List frames = new List(); - + private readonly IList frames = new List(); public Animation(IEnumerable textures) { @@ -72,7 +77,23 @@ namespace PckStudio.Internal public class Frame { public readonly Image Texture; - public int Ticks; + public int Ticks + { + get + { + return _ticks; + } + set + { + lock(l_ticks) + { + _ticks = value; + } + } + } + + private int _ticks; + private object l_ticks = new object(); public Frame(Image texture) : this(texture, MinimumFrameTime) { } @@ -149,12 +170,39 @@ namespace PckStudio.Internal public Frame GetFrame(int index) => frames[index]; - public List GetFrames() + public IReadOnlyCollection GetFrames() { - return frames; + return new ReadOnlyCollection(frames); } - public List GetTextures() + public IReadOnlyCollection GetInterpolatedFrames() + { + if (Interpolate) + { + return new ReadOnlyCollection(InternalGetInterpolatedFrames().ToList()); + } + return GetFrames(); + } + + private IEnumerable InternalGetInterpolatedFrames() + { + for (int i = 0; i < FrameCount; i++) + { + Frame currentFrame = frames[i]; + Frame nextFrame = frames[0]; + if (i + 1 < FrameCount) + nextFrame = frames[i + 1]; + for (int tick = 0; tick < currentFrame.Ticks; tick++) + { + double delta = 1.0f - tick / (double)currentFrame.Ticks; + yield return new Frame(currentFrame.Texture.Interpolate(nextFrame.Texture, delta)); + } + } + yield break; + } + + + public IReadOnlyCollection GetTextures() { return textures; } @@ -167,7 +215,10 @@ namespace PckStudio.Internal public void SetFrame(int frameIndex, Frame frame) { - frames[frameIndex] = frame; + lock(frames) + { + frames[frameIndex] = frame; + } } public void SetFrame(int frameIndex, int textureIndex, int frameTime = MinimumFrameTime) @@ -191,5 +242,24 @@ namespace PckStudio.Internal return textures.Combine(ImageLayoutDirection.Vertical); } - } + + internal void SetFrameTicks(int ticks) + { + lock(frames) + { + foreach (var frame in frames) + { + frame.Ticks = ticks; + } + } + } + + internal void SwapFrames(int sourceIndex, int destinationIndex) + { + lock(frames) + { + frames.Swap(sourceIndex, destinationIndex); + } + } + } } diff --git a/PCK-Studio/Internal/AnimationCategory.cs b/PCK-Studio/Internal/AnimationCategory.cs new file mode 100644 index 00000000..8cae76b2 --- /dev/null +++ b/PCK-Studio/Internal/AnimationCategory.cs @@ -0,0 +1,26 @@ +/* Copyright (c) 2023-present miku-666 + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +**/ + +namespace PckStudio.Internal +{ + internal enum AnimationCategory + { + Items, + Blocks + } +} diff --git a/PCK-Studio/Internal/ApplicationScope.cs b/PCK-Studio/Internal/ApplicationScope.cs index 171abb74..361ad2e4 100644 --- a/PCK-Studio/Internal/ApplicationScope.cs +++ b/PCK-Studio/Internal/ApplicationScope.cs @@ -19,7 +19,6 @@ namespace PckStudio.Internal internal static void Initialize() { - Profiler.Configure(Debug.Listeners[0]); Profiler.Start(); { _entityImages ??= Resources.entities_sheet.SplitHorizontal(32).ToArray(); diff --git a/PCK-Studio/Internal/PckNodeSorter.cs b/PCK-Studio/Internal/PckNodeSorter.cs new file mode 100644 index 00000000..03cfd111 --- /dev/null +++ b/PCK-Studio/Internal/PckNodeSorter.cs @@ -0,0 +1,56 @@ +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Windows.Forms; + +using OMI.Formats.Pck; +using PckStudio.Extensions; + +namespace PckStudio.Internal +{ + public class PckNodeSorter : IComparer, IComparer + { + public object SortingOptions { get; set; } = null; + public bool Descending { get; set; } = false; + + private bool CheckForSkinAndCapeFiles(TreeNode node) + { + return node.TryGetTagData(out PckFileData file) && + (file.Filetype == PckFileType.SkinFile || file.Filetype == PckFileType.CapeFile); + } + + public int Compare(object x, object y) + { + TreeNode tx = x as TreeNode; + TreeNode ty = y as TreeNode; + return Compare(tx, ty); + } + + public int Compare(TreeNode x, TreeNode y) + { + int result = InternalCompare(x, y); + //Debug.WriteLine(result); + if (Descending && result != 0) + { + result = 2 % result + 1; + } + return result; + } + + private int InternalCompare(TreeNode first, TreeNode second) + { + if (first.IsTagOfType() && !second.IsTagOfType()) + return -1; + if (!first.IsTagOfType() && second.IsTagOfType()) + return 1; + + if (CheckForSkinAndCapeFiles(first)) + return 1; + if (CheckForSkinAndCapeFiles(second)) + return 1; + + return first.Text.CompareTo(second.Text); + } + } + +} \ No newline at end of file diff --git a/PCK-Studio/Internal/Profiler.cs b/PCK-Studio/Internal/Profiler.cs index 1c503d26..f9daff27 100644 --- a/PCK-Studio/Internal/Profiler.cs +++ b/PCK-Studio/Internal/Profiler.cs @@ -28,19 +28,12 @@ namespace PckStudio.Internal internal static class Profiler { private static Stopwatch _stopwatch = new Stopwatch(); - private static TraceListener _listener; - - [Conditional("DEBUG")] - internal static void Configure(TraceListener listener) - { - _listener = listener; - } [Conditional("DEBUG")] internal static void Start([CallerMemberName] string caller = default!, [CallerFilePath] string source = default!, [CallerLineNumber] int line = default!) { - _listener?.WriteLine($"Stopwatch starts", category: nameof(Profiler)); - _listener?.WriteLine($"{source}@{caller}:{line}", category: nameof(Profiler)); + Debug.WriteLine($"Stopwatch starts", category: nameof(Profiler)); + Debug.WriteLine($"{source}@{caller}:{line}", category: nameof(Profiler)); _stopwatch.Restart(); } @@ -48,7 +41,7 @@ namespace PckStudio.Internal internal static void Stop([CallerMemberName] string caller = default!, [CallerFilePath] string source = default!, [CallerLineNumber] int line = default!) { _stopwatch.Stop(); - _listener?.WriteLine($"{caller} took {_stopwatch.ElapsedMilliseconds}ms", category: nameof(Profiler)); + Debug.WriteLine($"{caller} took {_stopwatch.ElapsedMilliseconds}ms", category: nameof(Profiler)); } } diff --git a/PCK-Studio/Internal/SkinANIM.cs b/PCK-Studio/Internal/SkinANIM.cs index 784ff3f1..c152efa8 100644 --- a/PCK-Studio/Internal/SkinANIM.cs +++ b/PCK-Studio/Internal/SkinANIM.cs @@ -16,118 +16,72 @@ * 3. This notice may not be removed or altered from any source distribution. **/ using System; +using System.Collections.Specialized; using System.Text.RegularExpressions; namespace PckStudio.Internal { - /// - /// For usage see - /// - [Flags] - public enum SkinAnimFlag : int - { - NONE = 0, // 0x00 - STATIC_ARMS = 1 << 0, // 0x01 - ZOMBIE_ARMS = 1 << 1, // 0x02 - STATIC_LEGS = 1 << 2, // 0x04 - BAD_SANTA = 1 << 3, // 0x08 - // - __BIT_4 = 1 << 4, // 0x10 - Unused?? - SYNCED_LEGS = 1 << 5, // 0x20 - SYNCED_ARMS = 1 << 6, // 0x40 - STATUE_OF_LIBERTY = 1 << 7, // 0x80 - - ALL_ARMOR_DISABLED = 1 << 8, // 0x100 - HEAD_BOBBING_DISABLED = 1 << 9, // 0x200 - HEAD_DISABLED = 1 << 10, // 0x400 - RIGHT_ARM_DISABLED = 1 << 11, // 0x800 - - LEFT_ARM_DISABLED = 1 << 12, // 0x1000 - BODY_DISABLED = 1 << 13, // 0x2000 - RIGHT_LEG_DISABLED = 1 << 14, // 0x4000 - LEFT_LEG_DISABLED = 1 << 15, // 0x8000 - - HEAD_OVERLAY_DISABLED = 1 << 16, // 0x10000 - DO_BACKWARDS_CROUCH = 1 << 17, // 0x20000 - RESOLUTION_64x64 = 1 << 18, // 0x40000 - SLIM_MODEL = 1 << 19, // 0x80000 - - LEFT_ARM_OVERLAY_DISABLED = 1 << 20, // 0x100000 - RIGHT_ARM_OVERLAY_DISABLED = 1 << 21, // 0x200000 - LEFT_LEG_OVERLAY_DISABLED = 1 << 22, // 0x400000 - RIGHT_LEG_OVERLAY_DISABLED = 1 << 23, // 0x800000 - - BODY_OVERLAY_DISABLED = 1 << 24, // 0x1000000 - FORCE_HEAD_ARMOR = 1 << 25, // 0x2000000 - FORCE_RIGHT_ARM_ARMOR = 1 << 26, // 0x4000000 - FORCE_LEFT_ARM_ARMOR = 1 << 27, // 0x8000000 - - FORCE_BODY_ARMOR = 1 << 28, // 0x10000000 - FORCE_RIGHT_LEG_ARMOR = 1 << 29, // 0x20000000 - FORCE_LEFT_LEG_ARMOR = 1 << 30, // 0x40000000 - DINNERBONE = 1 << 31, // 0x80000000 - } - /// /// Represents a Skin Anim value where flags can be set /// - public class SkinANIM : ICloneable, IEquatable, IEquatable + public class SkinANIM : ICloneable, IEquatable, IEquatable { public static readonly SkinANIM Empty = new SkinANIM(); - private SkinAnimFlag _flags; + private BitVector32 _flags; private static readonly Regex _validator = new Regex(@"^0x[0-9a-f]{1,8}\b", RegexOptions.IgnoreCase); public SkinANIM() - : this(SkinAnimFlag.NONE) + : this(SkinAnimMask.NONE) { } - public SkinANIM(SkinAnimFlag mask) + public SkinANIM(SkinAnimMask mask) + : this((int)mask) { - _flags = mask; } - public override string ToString() => "0x" + ((int)_flags).ToString("x8"); + private SkinANIM(int mask) + { + _flags = new BitVector32(mask); + } + + public override string ToString() => "0x" + _flags.Data.ToString("x8"); public static bool IsValidANIM(string anim) { - if (anim is not null) - return _validator.IsMatch(anim); - return false; + return !string.IsNullOrWhiteSpace(anim) && _validator.IsMatch(anim); } public static SkinANIM FromString(string value) => IsValidANIM(value) - ? new SkinANIM((SkinAnimFlag)Convert.ToInt32(value.TrimEnd(' ', '\n', '\r'), 16)) + ? new SkinANIM(Convert.ToInt32(value.TrimEnd(' ', '\n', '\r'), 16)) : new SkinANIM(); - public void SetMask(SkinAnimFlag mask) => _flags = mask; - - public static SkinANIM operator |(SkinANIM _this, SkinANIM other) => new SkinANIM(_this._flags | other._flags); + public static SkinANIM operator |(SkinANIM _this, SkinANIM other) => new SkinANIM(_this._flags.Data | other._flags.Data); - public static SkinANIM operator |(SkinANIM _this, SkinAnimFlag mask) => new SkinANIM(_this._flags | mask); + public static SkinANIM operator |(SkinANIM _this, SkinAnimMask mask) => new SkinANIM(_this._flags.Data | (int)mask); - public static implicit operator SkinANIM(SkinAnimFlag mask) => new SkinANIM(mask); + public static implicit operator SkinANIM(SkinAnimMask mask) => new SkinANIM(mask); - public static bool operator ==(SkinANIM _this, SkinAnimFlag mask) => _this.Equals(mask); - public static bool operator !=(SkinANIM _this, SkinAnimFlag mask) => !_this.Equals(mask); + public static bool operator ==(SkinANIM _this, SkinAnimMask mask) => _this.Equals(mask); + public static bool operator !=(SkinANIM _this, SkinAnimMask mask) => !_this.Equals(mask); public static bool operator ==(SkinANIM _this, SkinANIM other) => _this.Equals(other); public static bool operator !=(SkinANIM _this, SkinANIM other) => !_this.Equals(other); public bool Equals(SkinANIM other) { - return _flags == other._flags; + return _flags.Data == other._flags.Data; } - public bool Equals(SkinAnimFlag other) + public bool Equals(SkinAnimMask other) { - return _flags == other; + return _flags.Data == (int)other; } public override bool Equals(object obj) => obj is SkinANIM a && Equals(a); - public override int GetHashCode() => (int)_flags; + public override int GetHashCode() => _flags.Data; /// /// Sets the desired flag in the bitfield @@ -136,18 +90,21 @@ namespace PckStudio.Internal /// State of the flag public void SetFlag(SkinAnimFlag flag, bool state) { - if (state) _flags |= flag; - else _flags &= ~flag; + if (!Enum.IsDefined(typeof(SkinAnimFlag), flag)) + throw new ArgumentOutOfRangeException(nameof(flag)); + _flags[1 << (int)flag] = state; } /// - /// Gets a desired flags state + /// Gets flag state /// /// Flag to check /// True if flag is set, otherwise false public bool GetFlag(SkinAnimFlag flag) { - return (_flags & flag) != 0; + if (!Enum.IsDefined(typeof(SkinAnimFlag), flag)) + throw new ArgumentOutOfRangeException(nameof(flag)); + return _flags[1 << (int)flag]; } public object Clone() diff --git a/PCK-Studio/Internal/SkinAnimFlag.cs b/PCK-Studio/Internal/SkinAnimFlag.cs new file mode 100644 index 00000000..456c4c91 --- /dev/null +++ b/PCK-Studio/Internal/SkinAnimFlag.cs @@ -0,0 +1,67 @@ +/* Copyright (c) 2022-present miku-666, MattNL + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +**/ +using System; + +namespace PckStudio.Internal +{ + /// + /// For usage see + /// + public enum SkinAnimFlag : int + { + STATIC_ARMS = 0, // 0x01 + ZOMBIE_ARMS = 1, // 0x02 + STATIC_LEGS = 2, // 0x04 + BAD_SANTA = 3, // 0x08 + // + __BIT_4 = 4, // 0x10 - Unused?? + SYNCED_LEGS = 5, // 0x20 + SYNCED_ARMS = 6, // 0x40 + STATUE_OF_LIBERTY = 7, // 0x80 + + ALL_ARMOR_DISABLED = 8, // 0x100 + HEAD_BOBBING_DISABLED = 9, // 0x200 + HEAD_DISABLED = 10, // 0x400 + RIGHT_ARM_DISABLED = 11, // 0x800 + + LEFT_ARM_DISABLED = 12, // 0x1000 + BODY_DISABLED = 13, // 0x2000 + RIGHT_LEG_DISABLED = 14, // 0x4000 + LEFT_LEG_DISABLED = 15, // 0x8000 + + HEAD_OVERLAY_DISABLED = 16, // 0x10000 + DO_BACKWARDS_CROUCH = 17, // 0x20000 + RESOLUTION_64x64 = 18, // 0x40000 + SLIM_MODEL = 19, // 0x80000 + + LEFT_ARM_OVERLAY_DISABLED = 20, // 0x100000 + RIGHT_ARM_OVERLAY_DISABLED = 21, // 0x200000 + LEFT_LEG_OVERLAY_DISABLED = 22, // 0x400000 + RIGHT_LEG_OVERLAY_DISABLED = 23, // 0x800000 + + BODY_OVERLAY_DISABLED = 24, // 0x1000000 + FORCE_HEAD_ARMOR = 25, // 0x2000000 + FORCE_RIGHT_ARM_ARMOR = 26, // 0x4000000 + FORCE_LEFT_ARM_ARMOR = 27, // 0x8000000 + + FORCE_BODY_ARMOR = 28, // 0x10000000 + FORCE_RIGHT_LEG_ARMOR = 29, // 0x20000000 + FORCE_LEFT_LEG_ARMOR = 30, // 0x40000000 + DINNERBONE = 31, // 0x80000000 + } +} diff --git a/PCK-Studio/Internal/SkinAnimMask.cs b/PCK-Studio/Internal/SkinAnimMask.cs new file mode 100644 index 00000000..05a3b097 --- /dev/null +++ b/PCK-Studio/Internal/SkinAnimMask.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PckStudio.Internal +{ + [Flags] + public enum SkinAnimMask : int + { + NONE = 0, // 0x00 + STATIC_ARMS = 1 << 0, // 0x01 + ZOMBIE_ARMS = 1 << 1, // 0x02 + STATIC_LEGS = 1 << 2, // 0x04 + BAD_SANTA = 1 << 3, // 0x08 + + __BIT_4 = 1 << 4, // 0x10 - Unused?? + SYNCED_LEGS = 1 << 5, // 0x20 + SYNCED_ARMS = 1 << 6, // 0x40 + STATUE_OF_LIBERTY = 1 << 7, // 0x80 + + ALL_ARMOR_DISABLED = 1 << 8, // 0x100 + HEAD_BOBBING_DISABLED = 1 << 9, // 0x200 + HEAD_DISABLED = 1 << 10, // 0x400 + RIGHT_ARM_DISABLED = 1 << 11, // 0x800 + + LEFT_ARM_DISABLED = 1 << 12, // 0x1000 + BODY_DISABLED = 1 << 13, // 0x2000 + RIGHT_LEG_DISABLED = 1 << 14, // 0x4000 + LEFT_LEG_DISABLED = 1 << 15, // 0x8000 + + HEAD_OVERLAY_DISABLED = 1 << 16, // 0x10000 + DO_BACKWARDS_CROUCH = 1 << 17, // 0x20000 + RESOLUTION_64x64 = 1 << 18, // 0x40000 + SLIM_MODEL = 1 << 19, // 0x80000 + + LEFT_ARM_OVERLAY_DISABLED = 1 << 20, // 0x100000 + RIGHT_ARM_OVERLAY_DISABLED = 1 << 21, // 0x200000 + LEFT_LEG_OVERLAY_DISABLED = 1 << 22, // 0x400000 + RIGHT_LEG_OVERLAY_DISABLED = 1 << 23, // 0x800000 + + BODY_OVERLAY_DISABLED = 1 << 24, // 0x1000000 + FORCE_HEAD_ARMOR = 1 << 25, // 0x2000000 + FORCE_RIGHT_ARM_ARMOR = 1 << 26, // 0x4000000 + FORCE_LEFT_ARM_ARMOR = 1 << 27, // 0x8000000 + + FORCE_BODY_ARMOR = 1 << 28, // 0x10000000 + FORCE_RIGHT_LEG_ARMOR = 1 << 29, // 0x20000000 + FORCE_LEFT_LEG_ARMOR = 1 << 30, // 0x40000000 + DINNERBONE = 1 << 31, // 0x80000000 + } +} diff --git a/PCK-Studio/MainForm.Designer.cs b/PCK-Studio/MainForm.Designer.cs index c1150a02..32b0ea7a 100644 --- a/PCK-Studio/MainForm.Designer.cs +++ b/PCK-Studio/MainForm.Designer.cs @@ -98,6 +98,21 @@ this.openPckCenterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.joinDevelopmentDiscordToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.trelloBoardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.videosToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.howToMakeABasicSkinPackToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.howToMakeACustomSkinModelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.howToMakeCustomSkinModelsbedrockToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.howToMakeCustomMusicToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.howToInstallPcksDirectlyToWiiUToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.pckCenterReleaseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.howPCKsWorkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.donateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toNobledezJackToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toPhoenixARCDeveloperToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.forMattNLContributorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.contextMenuMetaTree = new System.Windows.Forms.ContextMenuStrip(this.components); this.addEntryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addEntryToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); @@ -120,10 +135,7 @@ this.toNobledezJackToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toPhoenixARCDeveloperToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.forMattNLContributorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); - this.installationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.fAQToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.editorTab = new MetroFramework.Controls.MetroTabPage(); this.label11 = new MetroFramework.Controls.MetroLabel(); @@ -675,27 +687,6 @@ resources.ApplyResources(this.forMattNLContributorToolStripMenuItem, "forMattNLContributorToolStripMenuItem"); this.forMattNLContributorToolStripMenuItem.Click += new System.EventHandler(this.forMattNLContributorToolStripMenuItem_Click); // - // toolStripSeparator2 - // - this.toolStripSeparator2.Name = "toolStripSeparator2"; - resources.ApplyResources(this.toolStripSeparator2, "toolStripSeparator2"); - // - // installationToolStripMenuItem - // - resources.ApplyResources(this.installationToolStripMenuItem, "installationToolStripMenuItem"); - this.installationToolStripMenuItem.Name = "installationToolStripMenuItem"; - // - // fAQToolStripMenuItem1 - // - resources.ApplyResources(this.fAQToolStripMenuItem1, "fAQToolStripMenuItem1"); - this.fAQToolStripMenuItem1.Name = "fAQToolStripMenuItem1"; - this.fAQToolStripMenuItem1.Click += new System.EventHandler(this.fAQToolStripMenuItem1_Click); - // - // toolStripSeparator3 - // - this.toolStripSeparator3.Name = "toolStripSeparator3"; - resources.ApplyResources(this.toolStripSeparator3, "toolStripSeparator3"); - // // settingsToolStripMenuItem // this.settingsToolStripMenuItem.Image = global::PckStudio.Properties.Resources.ranch; @@ -819,9 +810,6 @@ this.toolStripSeparator1, this.videosToolStripMenuItem, this.donateToolStripMenuItem, - this.toolStripSeparator2, - this.installationToolStripMenuItem, - this.fAQToolStripMenuItem1, this.toolStripSeparator3, this.settingsToolStripMenuItem}); this.helpToolStripMenuItem.ForeColor = System.Drawing.Color.Silver; @@ -952,14 +940,11 @@ private System.Windows.Forms.ToolStripMenuItem addBOXEntryToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem addANIMEntryToolStripMenuItem1; private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; private System.Windows.Forms.ToolStripMenuItem donateToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem toNobledezJackToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem toPhoenixARCDeveloperToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem forMattNLContributorToolStripMenuItem; - private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; - private System.Windows.Forms.ToolStripMenuItem installationToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem fAQToolStripMenuItem1; - private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem; private System.Windows.Forms.PictureBox logoPictureBox; private System.Windows.Forms.ToolStripMenuItem closeAllToolStripMenuItem; diff --git a/PCK-Studio/MainForm.cs b/PCK-Studio/MainForm.cs index b57a6ba0..752567a2 100644 --- a/PCK-Studio/MainForm.cs +++ b/PCK-Studio/MainForm.cs @@ -120,15 +120,6 @@ namespace PckStudio #region drag and drop for main tree node - public static void getChildren(List Nodes, TreeNode Node) - { - foreach (TreeNode thisNode in Node.Nodes) - { - Nodes.Add(thisNode); - getChildren(Nodes, thisNode); - } - } - // Most of the code below is modified code from this link: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.treeview.itemdrag?view=windowsdesktop-6.0 // - MattNL @@ -156,35 +147,22 @@ namespace PckStudio } - // Determine whether one node is a parent - // or ancestor of a second node. - private bool ContainsNode(TreeNode node1, TreeNode node2) - { - // Check the parent node of the second node. - if (node2.Parent == null) return false; - if (node2.Parent.Equals(node1)) return true; - // If the parent node is not null or equal to the first node, - // call the ContainsNode method recursively using the parent of - // the second node. - return ContainsNode(node1, node2.Parent); - } - #endregion private PckFile InitializePack(int packId, int packVersion, string packName, bool createSkinsPCK) { var pack = new PckFile(3); - - var zeroFile = pack.CreateNewFile("0", PckFile.FileData.FileType.InfoFile); + + var zeroFile = pack.CreateNewFile("0", PckFileType.InfoFile); zeroFile.Properties.Add("PACKID", packId.ToString()); zeroFile.Properties.Add("PACKVERSION", packVersion.ToString()); var locFile = new LOCFile(); locFile.InitializeDefault(packName); - pack.CreateNewFile("localisation.loc", PckFile.FileData.FileType.LocalisationFile, new LOCFileWriter(locFile, 2)); + pack.CreateNewFile("localisation.loc", PckFileType.LocalisationFile, new LOCFileWriter(locFile, 2)); - PckFile.FileData skinsPCKFile = pack.CreateNewFileIf(createSkinsPCK, "Skins.pck", PckFile.FileData.FileType.SkinDataFile, new PckFileWriter(new PckFile(3, true), - Settings.Default.UseLittleEndianAsDefault + pack.CreateNewFileIf(createSkinsPCK, "Skins.pck", PckFileType.SkinDataFile, new PckFileWriter(new PckFile(3, true), + LittleEndianCheckBox.Checked ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian)); @@ -196,13 +174,13 @@ namespace PckStudio var pack = InitializePack(packId, packVersion, packName, createSkinsPCK); PckFile infoPCK = new PckFile(3); - var icon = infoPCK.CreateNewFile("icon.png", PckFile.FileData.FileType.TextureFile); + var icon = infoPCK.CreateNewFile("icon.png", PckFileType.TextureFile); icon.SetData(Resources.TexturePackIcon, ImageFormat.Png); - var comparison = infoPCK.CreateNewFile("comparison.png", PckFile.FileData.FileType.TextureFile); + var comparison = infoPCK.CreateNewFile("comparison.png", PckFileType.TextureFile); comparison.SetData(Resources.Comparison, ImageFormat.Png); - var texturepackInfo = pack.CreateNewFile($"{res}/{res}Info.pck", PckFile.FileData.FileType.TexturePackInfoFile); + var texturepackInfo = pack.CreateNewFile($"{res}/{res}Info.pck", PckFileType.TexturePackInfoFile); texturepackInfo.Properties.Add("PACKID", "0"); texturepackInfo.Properties.Add("DATAPATH", $"{res}Data.pck"); @@ -215,7 +193,7 @@ namespace PckStudio private PckFile InitializeMashUpPack(int packId, int packVersion, string packName, string res) { var pack = InitializeTexturePack(packId, packVersion, packName, res, true); - var gameRuleFile = pack.CreateNewFile("GameRules.grf", PckFile.FileData.FileType.GameRulesFile); + var gameRuleFile = pack.CreateNewFile("GameRules.grf", PckFileType.GameRulesFile); var grfFile = new GameRuleFile(); grfFile.AddRule("MapOptions", new KeyValuePair("seed", "0"), @@ -286,10 +264,11 @@ namespace PckStudio using (var ofd = new OpenFileDialog()) { ofd.CheckFileExists = true; + ofd.Multiselect = true; ofd.Filter = "PCK (Minecraft Console Package)|*.pck"; if (ofd.ShowDialog() == DialogResult.OK) { - LoadPckFromFile(ofd.FileName); + LoadPckFromFile(ofd.FileNames); } } } @@ -357,12 +336,13 @@ namespace PckStudio } catch (OverflowException ex) { + Debug.WriteLine(ex.Message); + Trace.WriteLine("Failed to open " + ofd.FileName); MessageBox.Show("Error", "Failed to open pck\nTry checking the 'Open/Save as Switch/Vita/PS4 pck' check box in the upper right corner.", MessageBoxButtons.OK, MessageBoxIcon.Error); - Debug.WriteLine(ex.Message); } } - foreach (PckFile.FileData file in pckfile.Files) + foreach (PckFileData file in pckfile.GetFiles()) { string filepath = $"{sfd.SelectedPath}/{file.Filename}"; Directory.CreateDirectory(filepath); @@ -566,7 +546,7 @@ namespace PckStudio switch (prompt) { case DialogResult.Yes: - if (!editor.Pck.TryGetFile("0", PckFile.FileData.FileType.InfoFile, out PckFile.FileData file) || + if (!editor.Pck.TryGetFile("0", PckFileType.InfoFile, out PckFileData file) || string.IsNullOrEmpty(file.Properties.GetPropertyValue("PACKID"))) { MessageBox.Show(this, @@ -619,7 +599,7 @@ namespace PckStudio case DialogResult.No: case DialogResult.Cancel: default: - MessageBox.Show(this, "Operation cancelled"); + Trace.WriteLine("Operation cancelled", category: nameof(addCustomPackIconToolStripMenuItem_Click)); return; } } @@ -645,7 +625,10 @@ namespace PckStudio e.Cancel = true; }; if (!PckManager.Visible) + { PckManager.Show(); + PckManager.BringToFront(); + } if (PckManager.Focus()) PckManager.BringToFront(); } diff --git a/PCK-Studio/MainForm.resx b/PCK-Studio/MainForm.resx index 23893334..fcfe8a5f 100644 --- a/PCK-Studio/MainForm.resx +++ b/PCK-Studio/MainForm.resx @@ -1386,6 +1386,9 @@ About + + False + 177, 6 @@ -19171,629 +19174,31 @@ For MattNL (Other Developer) - - 180, 22 + + 298, 17 + + + 182, 92 - - Buy a coffee + + contextMenuMetaTree - - 177, 6 + + System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - False - - + - 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 - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAA6Omvv/4HGziIzB1SPPcAAAAASUVORK5CYII= + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xMkMEa+wAAABSSURBVDhP5c0x + DsAgDENRxt7/wmkNSpRGf0CCCZAegxNMM7MlGMp3dIU6dxhKf/QMNxRogeQC8ivw5Vn7C0heJlFA+kL5 + jWAohxRkde4wnGftBS90axNmphIGAAAAAElFTkSuQmCC - - 180, 22 + + 181, 22 - - Installation - - - False + + Add Entry @@ -22672,57 +22077,6 @@ 9 - - 4, 38 - - - 5, 50, 5, 7 - - - 1016, 558 - - - 0 - - - editorTab - - - MetroFramework.Controls.MetroTabPage, MetroFramework, Version=1.4.0.0, Culture=neutral, PublicKeyToken=5f91a84759bf584a - - - tabControl - - - 1 - - - Fill - - - 20, 30 - - - 0, 0, 0, 0 - - - 1024, 600 - - - 0 - - - tabControl - - - MetroFramework.Controls.MetroTabControl, MetroFramework, Version=1.4.0.0, Culture=neutral, PublicKeyToken=5f91a84759bf584a - - - $this - - - 4 - Top, Right @@ -25667,54 +25021,12 @@ System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - addEntryToolStripMenuItem + + helpToolStripMenuItem - + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - addEntryToolStripMenuItem1 - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - addBOXEntryToolStripMenuItem1 - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - addANIMEntryToolStripMenuItem1 - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - addMultipleEntriesToolStripMenuItem1 - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - deleteEntryToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - editAllEntriesToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - imageList - - - System.Windows.Forms.ImageList, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - aboutToolStripMenuItem @@ -25799,24 +25111,6 @@ System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - toolStripSeparator2 - - - System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - installationToolStripMenuItem - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - fAQToolStripMenuItem1 - - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - toolStripSeparator3 @@ -25829,12 +25123,54 @@ System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - helpToolStripMenuItem + + addEntryToolStripMenuItem - + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + addEntryToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + addBOXEntryToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + addANIMEntryToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + addMultipleEntriesToolStripMenuItem1 + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + deleteEntryToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + editAllEntriesToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + imageList + + + System.Windows.Forms.ImageList, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + MainForm diff --git a/PCK-Studio/PckNodeSorter.cs b/PCK-Studio/PckNodeSorter.cs deleted file mode 100644 index b2568055..00000000 --- a/PCK-Studio/PckNodeSorter.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Windows.Forms; - -using OMI.Formats.Pck; - -namespace PckStudio -{ - public class PckNodeSorter : IComparer, IComparer - { - private bool CheckForSkinAndCapeFiles(TreeNode node) - { - if (IsPckFile(node, out PckFile.FileData file)) - return file.Filetype == PckFile.FileData.FileType.SkinFile || file.Filetype == PckFile.FileData.FileType.CapeFile; - return false; - } - - private bool IsPckFile(TreeNode node) => IsPckFile(node, out _); - private bool IsPckFile(TreeNode node, out PckFile.FileData file) - { - if (node.Tag is PckFile.FileData _file) - { - file = _file; - return true; - } - file = null; - return false; - } - - public int Compare(TreeNode first, TreeNode second) - { - if (IsPckFile(first) && !IsPckFile(second)) - return -1; - if (!IsPckFile(first) && IsPckFile(second)) - return 1; - - if (CheckForSkinAndCapeFiles(first)) - return -1; - if (CheckForSkinAndCapeFiles(second)) - return 1; - - return first.Text.CompareTo(second.Text); - // weird fail save - //return first.ImageIndex.CompareTo(second.ImageIndex); - } - - int IComparer.Compare(object x, object y) - { - if (x is not TreeNode NodeX) - return -1; - if (y is not TreeNode NodeY) - return 1; - return Compare(NodeX, NodeY); - } - } -} \ No newline at end of file diff --git a/PCK-Studio/PckStudio.csproj b/PCK-Studio/PckStudio.csproj index ef801685..08283f13 100644 --- a/PCK-Studio/PckStudio.csproj +++ b/PCK-Studio/PckStudio.csproj @@ -133,6 +133,11 @@ + + + + + Component @@ -196,8 +201,6 @@ - - @@ -447,7 +450,7 @@ TextureConverterUtility.cs - + Form @@ -702,6 +705,9 @@ + + 1.0.5 + 5.8.0-alpha0098 compile; runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/PCK-Studio/Program.cs b/PCK-Studio/Program.cs index fc4ac6e6..505a7e27 100644 --- a/PCK-Studio/Program.cs +++ b/PCK-Studio/Program.cs @@ -25,6 +25,7 @@ namespace PckStudio static void Main(string[] args) { ApplicationScope.Initialize(); + Trace.TraceInformation("Startup"); RPC.Initialize(); MainInstance = new MainForm(); if (args.Length > 0) diff --git a/PCK-Studio/Properties/Settings.Designer.cs b/PCK-Studio/Properties/Settings.Designer.cs index c154a761..e6d87129 100644 --- a/PCK-Studio/Properties/Settings.Designer.cs +++ b/PCK-Studio/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace PckStudio.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -58,7 +58,7 @@ namespace PckStudio.Properties { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] + [global::System.Configuration.DefaultSettingValueAttribute("False")] public bool LoadSubPcks { get { return ((bool)(this["LoadSubPcks"])); @@ -79,5 +79,17 @@ namespace PckStudio.Properties { this["ShowRichPresence"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseComboBoxForGRFParameter { + get { + return ((bool)(this["UseComboBoxForGRFParameter"])); + } + set { + this["UseComboBoxForGRFParameter"] = value; + } + } } } diff --git a/PCK-Studio/Properties/Settings.settings b/PCK-Studio/Properties/Settings.settings index 1bbfe4fc..8724beec 100644 --- a/PCK-Studio/Properties/Settings.settings +++ b/PCK-Studio/Properties/Settings.settings @@ -17,5 +17,8 @@ True + + False + \ No newline at end of file diff --git a/PCK-Studio/ToolboxItems/AnimationPictureBox.cs b/PCK-Studio/ToolboxItems/AnimationPictureBox.cs index 5903520f..666650f6 100644 --- a/PCK-Studio/ToolboxItems/AnimationPictureBox.cs +++ b/PCK-Studio/ToolboxItems/AnimationPictureBox.cs @@ -25,6 +25,7 @@ using System.Runtime.CompilerServices; using PckStudio.Extensions; using PckStudio.Internal; +using System.Drawing; namespace PckStudio.ToolboxItems { @@ -39,10 +40,15 @@ namespace PckStudio.ToolboxItems return _isPlaying; } } + private set + { + lock (l_playing) + { + _isPlaying = value; + } + } } - private const int TickInMillisecond = 50; // 1 InGame tick - private bool _isPlaying = false; private int currentAnimationFrameIndex = 0; private Animation.Frame currentFrame; @@ -55,14 +61,17 @@ namespace PckStudio.ToolboxItems { _animation = animation; cts = new CancellationTokenSource(); - Task.Run(DoAnimate, cts.Token); + IsPlaying = true; + Task.Run(DoAnimate, cts.Token); } public void Stop([CallerMemberName] string callerName = default!) { Debug.WriteLine($"{nameof(AnimationPictureBox.Stop)} called from {callerName}!"); + IsPlaying = false; cts.Cancel(); - } + cts.Token.WaitHandle.WaitOne(500); + } public void SelectFrame(Animation animation, int index) { @@ -73,15 +82,11 @@ namespace PckStudio.ToolboxItems currentFrame = SetAnimationFrame(index); } - private async void DoAnimate() + private void DoAnimate() { _ = _animation ?? throw new ArgumentNullException(nameof(_animation)); - lock (l_playing) - { - _isPlaying = true; - } Animation.Frame nextFrame; - while (!cts.IsCancellationRequested) + while (!cts.IsCancellationRequested && IsPlaying) { if (currentAnimationFrameIndex >= _animation.FrameCount) { @@ -100,53 +105,29 @@ namespace PckStudio.ToolboxItems currentFrame = _animation.GetFrame(currentAnimationFrameIndex++); if (_animation.Interpolate) { - await InterpolateFrame(currentFrame, nextFrame); + InterpolateFrame(currentFrame, nextFrame); continue; } SetAnimationFrame(currentFrame); - if (!await DelayAsync(TickInMillisecond * currentFrame.Ticks, cts.Token)) + if (cts.Token.WaitHandle.WaitOne(Animation.GameTickInMilliseconds * currentFrame.Ticks)) + { + IsPlaying = false; break; - } - lock (l_playing) - { - _isPlaying = false; + } } } - private async Task InterpolateFrame(Animation.Frame currentFrame, Animation.Frame nextFrame) + private void InterpolateFrame(Animation.Frame currentFrame, Animation.Frame nextFrame) { for (int tick = 0; tick < currentFrame.Ticks && !cts.IsCancellationRequested; tick++) { double delta = 1.0f - tick / (double)currentFrame.Ticks; - if (!IsHandleCreated) - break; - lock (l_dispose) - { - Invoke(() => - { - Image = currentFrame.Texture.Interpolate(nextFrame.Texture, delta); - }); - } - - if (!await DelayAsync(TickInMillisecond, cts.Token)) + SetTexture(currentFrame.Texture.Interpolate(nextFrame.Texture, delta)); + if (cts.Token.WaitHandle.WaitOne(Animation.GameTickInMilliseconds)) break; } } - private async Task DelayAsync(int millisecondsDelay, CancellationToken cancellationToken, [CallerMemberName] string caller = default!) - { - try - { - await Task.Delay(millisecondsDelay, cancellationToken); - } - catch - { - Debug.WriteLine($"Stoping {caller}"); - return false; - } - return true; - } - private Animation.Frame SetAnimationFrame(int frameIndex) { var frame = _animation.GetFrame(frameIndex); @@ -154,25 +135,23 @@ namespace PckStudio.ToolboxItems return frame; } + private void SetTexture(Image texture) + { + if (!IsHandleCreated || Disposing) + return; + Invoke(() => Image = texture); + } + private void SetAnimationFrame(Animation.Frame frame) { - if (!IsHandleCreated) - return; - lock (l_dispose) - { - Invoke(() => - { - Image = frame.Texture; - }); - } + SetTexture(frame.Texture); } protected override void Dispose(bool disposing) { - lock(l_dispose) - { - base.Dispose(disposing); - } + Stop(); + cts.Token.WaitHandle.WaitOne(500); + base.Dispose(disposing); } } } diff --git a/Vendor/OMI-Lib b/Vendor/OMI-Lib index dae99efe..c33a6ada 160000 --- a/Vendor/OMI-Lib +++ b/Vendor/OMI-Lib @@ -1 +1 @@ -Subproject commit dae99efef9dc9fd2c98b82abb5ceca0a7aa4dd70 +Subproject commit c33a6adafe24d7dbbbc2503e5470d348b6bdb1c4