Files
PCK-Studio/PCK-Studio/MainForm.cs

2277 lines
77 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Drawing.Imaging;
using PckStudio.Properties;
using PckStudio.Classes.FileTypes;
using PckStudio.Classes.IO.LOC;
using PckStudio.Classes.IO.PCK;
using PckStudio.Classes.IO.GRF;
using PckStudio.Classes.Utils;
using PckStudio.Classes.Utils.ARC;
using PckStudio.Classes._3ds.Utils;
using PckStudio.Forms;
using PckStudio.Forms.Utilities.Skins;
using PckStudio.Forms.Utilities;
using PckStudio.Forms.Editor;
using PckStudio.Forms.Additional_Popups.Animation;
using PckStudio.Forms.Additional_Popups;
using PckStudio.Classes.Misc;
namespace PckStudio
{
public partial class MainForm : MetroFramework.Forms.MetroForm
{
string saveLocation = string.Empty;
PCKFile currentPCK = null;
bool needsUpdate = false;
bool saved = true;
bool isTemplateFile = false;
bool isSelectingTab = false;
readonly Dictionary<PCKFile.FileData.FileType, Action<PCKFile.FileData>> pckFileTypeHandler;
public MainForm()
{
InitializeComponent();
treeViewMain.TreeViewNodeSorter = new PckNodeSorter();
imageList.Images.Add(Resources.ZZFolder); // Icon for folders
imageList.Images.Add(Resources.BINKA_ICON); // Icon for music cue file (audio.pck)
imageList.Images.Add(Resources.IMAGE_ICON); // Icon for images (unused for now)
imageList.Images.Add(Resources.LOC_ICON); // Icon for string localization files (languages.loc;localisation.loc)
imageList.Images.Add(Resources.PCK_ICON); // Icon for generic PCK files (*.pck)
imageList.Images.Add(Resources.ZUnknown); // Icon for Unknown formats
imageList.Images.Add(Resources.COL_ICON); // Icon for color palette files (colours.col)
imageList.Images.Add(Resources.SKINS_ICON); // Icon for Skin.pck archives (skins.pck)
imageList.Images.Add(Resources.MODELS_ICON); // Icon for Model files (models.bin)
imageList.Images.Add(Resources.GRF_ICON); // Icon for Game Rule files (*.grf)
imageList.Images.Add(Resources.GRH_ICON); // Icon for Game Rule Header files (*.grh)
imageList.Images.Add(Resources.INFO_ICON); // Icon for Info files (0)
imageList.Images.Add(Resources.SKIN_ICON); // Icon for Skin files (*.png)
imageList.Images.Add(Resources.CAPE_ICON); // Icon for Cape files (*.png)
imageList.Images.Add(Resources.TEXTURE_ICON); // Icon for Texture files (*.png;*.tga)
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)
pckOpen.AllowDrop = true;
isSelectingTab = true;
tabControl.SelectTab(0);
isSelectingTab = false;
labelVersion.Text = "PCK Studio: " + Application.ProductVersion;
ChangelogRichTextBox.Text = Resources.CHANGELOG;
#if DEBUG
DateTime date = DateTime.UtcNow.Date;
System.Globalization.Calendar cal = new System.Globalization.CultureInfo("en-US").Calendar;
string n = "a"; // this is to specify which build release this is. This is manually updated for now
labelVersion.Text += $" (Debug build {date.ToString("yy")}w{cal.GetWeekOfYear(date, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Monday)}{n})";
//adopted Minecraft Java Edition Snapshot format (YYwWWn) to keep better track of work in progress features and builds
#endif
pckFileTypeHandler = new Dictionary<PCKFile.FileData.FileType, Action<PCKFile.FileData>>(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] = null,
};
}
public void LoadPck(string filepath)
{
treeViewMain.Nodes.Clear();
if (currentPCK is not null && !saved)
{
checkSaveState();
}
currentPCK = openPck(filepath);
if (currentPCK == null)
{
MessageBox.Show(string.Format("Failed to load {0}", Path.GetFileName(filepath)), "Error");
return;
}
if (addPasswordToolStripMenuItem.Enabled = checkForPassword())
{
LoadEditorTab();
}
}
private void Form1_Load(object sender, EventArgs e)
{
RPC.Initialize();
if (currentPCK == null)
RPC.SetPresence("An Open Source .PCK File Editor", "Program by PhoenixARC");
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);
try
{
Directory.CreateDirectory(Program.AppDataCache + "\\mods\\");
}
catch (UnauthorizedAccessException ex)
{
MessageBox.Show("Could not Create directory due to Unauthorized Access");
Debug.WriteLine(ex.Message);
}
}
private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
checkSaveState();
RPC.Deinitialize();
}
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
using (var ofd = new OpenFileDialog())
{
ofd.CheckFileExists = true;
ofd.Filter = "PCK (Minecraft Console Package)|*.pck";
if (ofd.ShowDialog() == DialogResult.OK)
{
LoadPck(ofd.FileName);
}
}
}
private PCKFile openPck(string filePath)
{
PCKFile pck = null;
using (var fileStream = File.OpenRead(filePath))
{
isTemplateFile = false;
saveLocation = filePath;
try
{
pck = PCKFileReader.Read(fileStream, LittleEndianCheckBox.Checked);
metroLabel3.Text = "Current PCK File: " + Path.GetFileName(filePath);
}
catch (OverflowException ex)
{
MessageBox.Show("Failed to open pck\n" +
$"Try {(LittleEndianCheckBox.Checked ? "unchecking" : "checking")} the 'Open/Save as Vita/PS4 pck' check box in the upper right corner.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(ex.Message);
}
catch (Exception ex)
{
MessageBox.Show("Failed to open pck\n" +
"If this is an Audio/Music Cues pck, please use the specialized editor while inside of the parent pck.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine("Can't open pck file of type: " + pck?.type.ToString());
}
}
return pck;
}
private bool checkForPassword()
{
if (currentPCK.TryGetFile("0", PCKFile.FileData.FileType.InfoFile, out PCKFile.FileData file))
{
if (file.Properties.Contains("LOCK"))
return new LockPrompt(file.Properties.GetPropertyValue("LOCK")).ShowDialog() == DialogResult.OK;
}
return true;
}
private void LoadEditorTab()
{
fileEntryCountLabel.Text = "Files:" + currentPCK.Files.Count;
treeViewMain.Enabled = treeMeta.Enabled = true;
closeToolStripMenuItem.Visible = true;
saveToolStripMenuItem.Enabled = true;
saveToolStripMenuItem1.Enabled = true;
metaToolStripMenuItem.Enabled = true;
advancedMetaAddingToolStripMenuItem.Enabled = true;
convertToBedrockToolStripMenuItem.Enabled = true;
addCustomPackImageToolStripMenuItem.Enabled = true;
BuildMainTreeView();
isSelectingTab = true;
tabControl.SelectTab(1);
isSelectingTab = false;
UpdateRPC();
}
private void CloseEditorTab()
{
isSelectingTab = true;
tabControl.SelectTab(0);
isSelectingTab = false;
currentPCK = null;
saved = true;
isTemplateFile = false;
saveLocation = string.Empty;
pictureBoxImagePreview.Image = Resources.NoImageFound;
treeViewMain.Nodes.Clear();
treeMeta.Nodes.Clear();
treeViewMain.Enabled = false;
treeMeta.Enabled = false;
saveToolStripMenuItem.Enabled = false;
saveToolStripMenuItem1.Enabled = false;
metaToolStripMenuItem.Enabled = false;
addPasswordToolStripMenuItem.Enabled = false;
advancedMetaAddingToolStripMenuItem.Enabled = false;
closeToolStripMenuItem.Visible = false;
convertToBedrockToolStripMenuItem.Enabled = false;
addCustomPackImageToolStripMenuItem.Enabled = false;
fileEntryCountLabel.Text = string.Empty;
UpdateRPC();
}
/// <summary>
/// wrapper that allows the use of <paramref name="name"/> in <code>TreeNode.Nodes.Find(<paramref name="name"/>, ...)</code> and <code>TreeNode.Nodes.ContainsKey(<paramref name="name"/>)</code>
/// </summary>
/// <param name="name"></param>
/// <param name="tag"></param>
/// <returns>new Created TreeNode</returns>
public static TreeNode CreateNode(string name, object tag = null)
{
TreeNode node = new TreeNode(name);
node.Name = name;
node.Tag = tag;
return node;
}
private TreeNode BuildNodeTreeBySeperator(TreeNodeCollection root, string path, char seperator)
{
_ = root ?? throw new ArgumentNullException(nameof(root));
if (!path.Contains(seperator))
{
var finalNode = CreateNode(path);
root.Add(finalNode);
return finalNode;
}
string nodeText = path.Substring(0, path.IndexOf(seperator));
string subPath = path.Substring(path.IndexOf(seperator) + 1);
bool alreadyExists = root.ContainsKey(nodeText);
TreeNode subNode = alreadyExists ? root[nodeText] : CreateNode(nodeText);
if (!alreadyExists) root.Add(subNode);
return BuildNodeTreeBySeperator(subNode.Nodes, subPath, seperator);
}
private void BuildPckTreeView(TreeNodeCollection root, PCKFile pckFile, string parentPath = "")
{
foreach (var file in pckFile.Files)
{
// fix any file paths that may be incorrect
if (file.Filename.StartsWith(parentPath))
file.Filename = file.Filename.Remove(0, parentPath.Length);
TreeNode node = BuildNodeTreeBySeperator(root, file.Filename, '/');
node.Tag = file;
switch (file.Filetype)
{
case PCKFile.FileData.FileType.SkinDataFile:
case PCKFile.FileData.FileType.TexturePackInfoFile:
using (var stream = new MemoryStream(file.Data))
{
try
{
PCKFile subPCKfile = PCKFileReader.Read(stream, LittleEndianCheckBox.Checked);
// passes parent path to remove from sub pck filepaths
BuildPckTreeView(node.Nodes, subPCKfile, file.Filename + "/");
}
catch (OverflowException ex)
{
MessageBox.Show("Failed to open pck\n" +
"Try checking the 'Open/Save as Vita/PS4 pck' checkbox in the upper right corner.",
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(ex.Message);
}
}
break;
default: // unknown file format
//throw new InvalidDataException(nameof(file.filetype));
break;
}
SetPckFileIcon(node, file.Filetype);
};
}
private void BuildMainTreeView()
{
// In case the Rename function was just used and the selected node name no longer matches the file name
string filepath = string.Empty;
if(treeViewMain.SelectedNode is TreeNode node && node.Tag is PCKFile.FileData file)
filepath = file.Filename;
treeViewMain.Nodes.Clear();
BuildPckTreeView(treeViewMain.Nodes, currentPCK);
if (isTemplateFile && currentPCK.HasFile("Skins.pck", PCKFile.FileData.FileType.SkinDataFile))
{
TreeNode skinsNode = treeViewMain.Nodes.Find("Skins.pck", false).FirstOrDefault();
TreeNode folderNode = CreateNode("Skins");
folderNode.ImageIndex = 0;
folderNode.SelectedImageIndex = 0;
if (!skinsNode.Nodes.ContainsKey("Skins"))
skinsNode.Nodes.Add(folderNode);
}
if (!string.IsNullOrEmpty(filepath))
{
// Looks kinda nuts but this line of code is responsible for finding the correct node that was originally selected
treeViewMain.SelectedNode =
treeViewMain.Nodes.Find(Path.GetFileName(filepath), true).ToList()
.Find(t => (t.Tag as PCKFile.FileData).Filename == filepath);
}
}
bool IsFilePathMipMapped(string filepath)
{
// We only want to test the file name itself. ex: "terrainMipMapLevel2"
string name = Path.GetFileNameWithoutExtension(filepath);
// check if last character is a digit (0-9). If not return false
if (!char.IsDigit(name[name.Length - 1])) return false;
// If string does not end with MipMapLevel, then it's not MipMapped
if (!name.Remove(name.Length - 1, 1).EndsWith("MipMapLevel")) return false;
return true;
}
private void HandleTextureFile(PCKFile.FileData file)
{
if (IsFilePathMipMapped(file.Filename) &&
currentPCK.Files.Find(pckfile =>
// todo write cleaner ?
pckfile.Filename.Equals(
file.Filename.Remove(file.Filename.Length - 12 - Path.GetExtension(file.Filename).Length)
+ Path.GetExtension(file.Filename)))
is PCKFile.FileData originalAnimationFile)
{
file = originalAnimationFile;
}
if (!(file.Filename.StartsWith("res/textures/blocks/") || file.Filename.StartsWith("res/textures/items/"))) return;
using (AnimationEditor animationEditor = new AnimationEditor(file))
{
if (animationEditor.ShowDialog(this) == DialogResult.OK)
{
file.Filename = animationEditor.TileName;
BuildMainTreeView();
}
}
}
private void HandleGameRuleFile(PCKFile.FileData file)
{
using GRFEditor grfEditor = new GRFEditor(file);
if (grfEditor.ShowDialog(this) == DialogResult.OK)
saved = false;
UpdateRPC();
}
private void UpdateRPC()
{
if (currentPCK == null)
{
RPC.SetPresence("An Open Source .PCK File Editor", "Program by PhoenixARC");
return;
};
if (TryGetLocFile(out LOCFile locfile) &&
locfile.HasLocEntry("IDS_DISPLAY_NAME") &&
locfile.Languages.Contains("en-EN"))
{
RPC.SetPresence($"Editing a Pack: {locfile.GetLocEntry("IDS_DISPLAY_NAME", "en-EN")}", "Program by PhoenixARC");
return;
}
// default
RPC.SetPresence("An Open Source .PCK File Editor", "Program by PhoenixARC");
}
private void HandleAudioFile(PCKFile.FileData file)
{
if (!TryGetLocFile(out LOCFile locFile))
throw new Exception("No .loc File found.");
using AudioEditor audioEditor = new AudioEditor(file, locFile, LittleEndianCheckBox.Checked);
if (audioEditor.ShowDialog(this) == DialogResult.OK)
{
saved = false;
TrySetLocFile(locFile);
}
}
private void HandleLocalisationFile(PCKFile.FileData file)
{
using LOCEditor locedit = new LOCEditor(file);
if (locedit.ShowDialog(this) == DialogResult.OK)
saved = false;
UpdateRPC();
}
private void HandleColourFile(PCKFile.FileData file)
{
if (file.Size == 0)
{
MessageBox.Show("No Color data found.", "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
using COLEditor diag = new COLEditor(file);
if (diag.ShowDialog(this) == DialogResult.OK)
saved = false;
}
public void HandleSkinFile(PCKFile.FileData file)
{
if (file.Properties.HasProperty("BOX"))
{
using (generateModel generate = new generateModel(file.Properties, new PictureBox()))
if (generate.ShowDialog() == DialogResult.OK)
{
entryDataTextBox.Text = entryTypeTextBox.Text = string.Empty;
saved = false;
ReloadMetaTreeView();
}
}
else
{
using (var ms = new MemoryStream(file.Data))
{
var texture = Image.FromStream(ms);
SkinPreview frm = new SkinPreview(texture, file.Properties.GetPropertyValue("ANIM", s => new SkinANIM(s)));
frm.ShowDialog(this);
frm.Dispose();
}
}
}
public void HandleModelsFile(PCKFile.FileData 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.");
//throw new NotImplementedException();
}
public void HandleBehavioursFile(PCKFile.FileData file)
{
using BehaviourEditor locedit = new BehaviourEditor(file);
if (locedit.ShowDialog(this) == DialogResult.OK)
saved = false;
//throw new NotImplementedException();
}
private void selectNode(object sender, TreeViewEventArgs e)
{
ReloadMetaTreeView();
entryTypeTextBox.Text = entryDataTextBox.Text = labelImageSize.Text = string.Empty;
buttonEdit.Visible = false;
pictureBoxImagePreview.Image = Resources.NoImageFound;
var node = e.Node;
viewFileInfoToolStripMenuItem.Visible = false;
if (node is TreeNode t && t.Tag is PCKFile.FileData file)
{
viewFileInfoToolStripMenuItem.Visible = true;
if (file.Properties.HasProperty("BOX"))
{
buttonEdit.Text = "EDIT BOXES";
buttonEdit.Visible = true;
}
else if (file.Properties.HasProperty("ANIM") &&
file.Properties.GetPropertyValue("ANIM", s => new SkinANIM(s)) == (ANIM_EFFECTS.RESOLUTION_64x64 | ANIM_EFFECTS.SLIM_MODEL))
{
buttonEdit.Text = "View Skin";
buttonEdit.Visible = true;
}
switch (file.Filetype)
{
case PCKFile.FileData.FileType.SkinFile:
case PCKFile.FileData.FileType.CapeFile:
case PCKFile.FileData.FileType.TextureFile:
// TODO: Add tga support
if (Path.GetExtension(file.Filename) == ".tga") break;
using (MemoryStream stream = new MemoryStream(file.Data))
{
try
{
pictureBoxImagePreview.Image = Image.FromStream(stream);
labelImageSize.Text = $"{pictureBoxImagePreview.Image.Size.Width}x{pictureBoxImagePreview.Image.Size.Height}";
}
catch (Exception ex)
{
labelImageSize.Text = "";
pictureBoxImagePreview.Image = Resources.NoImageFound;
Debug.WriteLine("Not a supported image format. Setting back to default");
Debug.WriteLine(string.Format("An error occured of type: {0} with message: {1}", ex.GetType(), ex.Message), "Exception");
}
}
if ((file.Filename.StartsWith("res/textures/blocks/") || file.Filename.StartsWith("res/textures/items/")) &&
!file.Filename.EndsWith("clock.png") && !file.Filename.EndsWith("compass.png") &&
file.Filetype == PCKFile.FileData.FileType.TextureFile
&& !IsFilePathMipMapped(file.Filename))
{
buttonEdit.Text = "EDIT TEXTURE ANIMATION";
buttonEdit.Visible = true;
}
break;
case PCKFile.FileData.FileType.LocalisationFile:
buttonEdit.Text = "EDIT LOC";
buttonEdit.Visible = true;
break;
case PCKFile.FileData.FileType.AudioFile:
buttonEdit.Text = "EDIT MUSIC CUES";
buttonEdit.Visible = true;
break;
case PCKFile.FileData.FileType.ColourTableFile when file.Filename == "colours.col":
buttonEdit.Text = "EDIT COLORS";
buttonEdit.Visible = true;
break;
case PCKFile.FileData.FileType.BehavioursFile when file.Filename == "behaviours.bin":
buttonEdit.Text = "EDIT BEHAVIOURS";
buttonEdit.Visible = true;
break;
default:
buttonEdit.Visible = false;
break;
}
}
}
private void extractToolStripMenuItem_Click(object sender, EventArgs e)
{
var node = treeViewMain.SelectedNode;
if (node == null) return;
if (node.Tag is PCKFile.FileData file)
{
using SaveFileDialog exFile = new SaveFileDialog();
exFile.FileName = Path.GetFileName(file.Filename);
exFile.Filter = Path.GetExtension(file.Filename).Replace(".", string.Empty) + " File|*" + Path.GetExtension(file.Filename);
if (exFile.ShowDialog() != DialogResult.OK ||
// Makes sure chosen directory isn't null or whitespace AKA makes sure its usable
string.IsNullOrWhiteSpace(Path.GetDirectoryName(exFile.FileName))) return;
string extractFilePath = exFile.FileName;
File.WriteAllBytes(extractFilePath, file.Data);
if (file.Properties.Count > 0)
{
using var fs = File.CreateText($"{extractFilePath}.txt");
file.Properties.ForEach(property => fs.WriteLine($"{property.Item1}: {property.Item2}"));
}
// Verification that file extraction path was successful
MessageBox.Show("File Extracted");
return;
}
string selectedFolder = node.FullPath;
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
{
dialog.Description = @"Select destination folder";
if (dialog.ShowDialog() == DialogResult.OK)
{
if(IsSubPCKNode(node.FullPath) && node.Tag == null)
{
GetAllChildNodes(node.Nodes).ForEach(fileNode =>
{
if(fileNode.Tag is PCKFile.FileData file)
{
Directory.CreateDirectory($"{dialog.SelectedPath}/{Path.GetDirectoryName(file.Filename)}");
File.WriteAllBytes($"{dialog.SelectedPath}/{file.Filename}", file.Data);
if (file.Properties.Count > 0)
{
using var fs = File.CreateText($"{dialog.SelectedPath}/{file.Filename}.txt");
file.Properties.ForEach(property => fs.WriteLine($"{property.Item1}: {property.Item2}"));
}
}
}
);
}
else
{
currentPCK.Files.ForEach(file =>
{
if (file.Filename.StartsWith(selectedFolder))
{
Directory.CreateDirectory($"{dialog.SelectedPath}/{Path.GetDirectoryName(file.Filename)}");
File.WriteAllBytes($"{dialog.SelectedPath}/{file.Filename}", file.Data);
if (file.Properties.Count > 0)
{
using var fs = File.CreateText($"{dialog.SelectedPath}/{file.Filename}.txt");
file.Properties.ForEach(property => fs.WriteLine($"{property.Item1}: {property.Item2}"));
}
}
});
}
MessageBox.Show("Folder Extracted");
}
}
}
private void SaveTemplate()
{
using SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "PCK (Minecraft Console Package)|*.pck";
saveFileDialog.DefaultExt = ".pck";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
Save(saveFileDialog.FileName);
saveLocation = saveFileDialog.FileName;
isTemplateFile = false;
}
}
private void Save(string filePath)
{
bool isSkinsPCK = false;
if (!currentPCK.TryGetFile("0", PCKFile.FileData.FileType.InfoFile, out PCKFile.FileData _))
{
switch(MessageBox.Show(this, "The info file, \"0\", was not detected. Would you like to save as a Skins.pck archive?", "Save as Skins archive?", MessageBoxButtons.YesNoCancel))
{
case DialogResult.Yes:
isSkinsPCK = true;
break;
case DialogResult.No:
isSkinsPCK = false;
break;
case DialogResult.Cancel:
default:
return; // Cancel operation
}
}
using (var fs = File.OpenWrite(filePath))
{
PCKFileWriter.Write(fs, currentPCK, LittleEndianCheckBox.Checked, isSkinsPCK);
}
saved = true;
MessageBox.Show("Saved Pck file", "File Saved");
}
private void replaceToolStripMenuItem_Click(object sender, EventArgs e)
{
if (treeViewMain.SelectedNode.Tag is PCKFile.FileData 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.
ofd.Filter = file.Filetype.ToString() + " (*" + Path.GetExtension(file.Filename) + ")|*" + Path.GetExtension(file.Filename);
if (ofd.ShowDialog() == DialogResult.OK)
{
file.SetData(File.ReadAllBytes(ofd.FileName));
if (IsSubPCKNode(treeViewMain.SelectedNode.FullPath)) RebuildSubPCK(treeViewMain.SelectedNode);
saved = false;
}
return;
}
//deleteEntryToolStripMenuItem_Click(sender, e);
//using FolderBrowserDialog folderDialog = new FolderBrowserDialog();
//folderDialog.Description = "Select Folder";
//if (folderDialog.ShowDialog() == DialogResult.OK)
//{
// string[] FilePaths = Directory.GetFiles(folderDialog.SelectedPath, "*.png");
// Array.ForEach(FilePaths, filePath => currentPCK.Files.Add(new PCKFile.FileData(filePath, 2)));
//}
// should never happen unless its a folder
MessageBox.Show("Can't replace a folder.");
}
private void deleteFileToolStripMenuItem_Click(object sender, EventArgs e)
{
var node = treeViewMain.SelectedNode;
if (node == null) return;
string path = node.FullPath;
if (node.Tag is PCKFile.FileData)
{
PCKFile.FileData file = node.Tag as PCKFile.FileData;
// remove loc key if its a skin/cape
if (file.Filetype == PCKFile.FileData.FileType.SkinFile || file.Filetype == PCKFile.FileData.FileType.CapeFile)
{
if (TryGetLocFile(out LOCFile locFile))
{
foreach (var property in file.Properties)
{
if (property.Item1 == "THEMENAMEID" || property.Item1 == "DISPLAYNAMEID")
locFile.RemoveLocKey(property.Item2);
}
TrySetLocFile(locFile);
}
}
currentPCK.Files.Remove(file);
node.Remove();
saved = false;
}
else if (MessageBox.Show("Are you sure want to delete this folder? All contents will be deleted", "Warning",
MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
{
string pckFolderDir = node.FullPath;
currentPCK.Files.RemoveAll(file => file.Filename.StartsWith(pckFolderDir));
node.Remove();
saved = false;
}
if (IsSubPCKNode(path)) RebuildSubPCK(node);
}
private void renameFileToolStripMenuItem_Click(object sender, EventArgs e)
{
TreeNode node = treeViewMain.SelectedNode;
if (node == null) return;
string path = node.FullPath;
using RenamePrompt diag = new RenamePrompt(node.FullPath);
if (diag.ShowDialog(this) == DialogResult.OK)
{
if (node.Tag is PCKFile.FileData file
&& file.Filetype is not PCKFile.FileData.FileType.TexturePackInfoFile
&& file.Filetype is not PCKFile.FileData.FileType.SkinDataFile)
{
file.Filename = diag.NewText;
}
else if(!IsSubPCKNode(path)) // folder
{
foreach (var childNode in GetAllChildNodes(node.Nodes))
{
if (childNode.Tag is PCKFile.FileData folderFile &&
childNode.FullPath.StartsWith(childNode.FullPath))
{
folderFile.Filename = diag.NewText + childNode.FullPath.Substring(childNode.FullPath.Length);
}
}
}
else
{
currentPCK.Files.ForEach(file =>
{
if (file.Filename.StartsWith(node.FullPath))
file.Filename = diag.NewText + file.Filename.Substring(node.FullPath.Length);
});
}
saved = false;
if (IsSubPCKNode(path)) RebuildSubPCK(node);
BuildMainTreeView();
}
}
private void createSkinToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!TryGetLocFile(out LOCFile locFile))
{
MessageBox.Show("No .loc file found", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
using (addNewSkin add = new addNewSkin(locFile))
if (add.ShowDialog() == DialogResult.OK)
{
if (currentPCK.HasFile("Skins.pck", PCKFile.FileData.FileType.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);
subPCK.Nodes.Add(newNode);
RebuildSubPCK(newNode);
}
else
{
if (treeViewMain.Nodes.ContainsKey("Skins")) add.SkinFile.Filename = add.SkinFile.Filename.Insert(0, "Skins/"); // Then Skins folder
currentPCK.Files.Add(add.SkinFile);
}
if (add.HasCape)
{
if (currentPCK.HasFile("Skins.pck", PCKFile.FileData.FileType.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);
subPCK.Nodes.Add(newNode);
RebuildSubPCK(newNode);
}
else
{
if (treeViewMain.Nodes.ContainsKey("Skins")) add.CapeFile.Filename = add.CapeFile.Filename.Insert(0, "Skins/"); // Then Skins folder
currentPCK.Files.Add(add.CapeFile);
}
}
TrySetLocFile(locFile);
saved = false;
//BuildMainTreeView();
}
}
private static PCKFile.FileData CreateNewAudioFile(bool isLittle)
{
// create actual valid pck file structure
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);
using (var stream = new MemoryStream())
{
PCKAudioFileWriter.Write(stream, audioPck, isLittle);
pckFileData.SetData(stream.ToArray());
}
return pckFileData;
}
private void audiopckToolStripMenuItem_Click(object sender, EventArgs e)
{
if (currentPCK.Files.FindIndex(file => file.Filename.Contains("audio.pck")) != -1)
{
MessageBox.Show("There is already an audio.pck present in this file!", "Can't create audio.pck");
return;
}
if (!TryGetLocFile(out LOCFile locFile))
throw new Exception("No .loc file found.");
var file = CreateNewAudioFile(LittleEndianCheckBox.Checked);
currentPCK.Files.Add(file);
AudioEditor diag = new AudioEditor(file, locFile, LittleEndianCheckBox.Checked);
if (diag.ShowDialog(this) == DialogResult.OK)
TrySetLocFile(locFile);
diag.Dispose();
}
private void createAnimatedTextureToolStripMenuItem_Click(object sender, EventArgs e)
{
using (var ofd = new OpenFileDialog())
{
ofd.Filter = "PNG Files | *.png";
ofd.Title = "Select a PNG File";
if (ofd.ShowDialog() == DialogResult.OK)
{
using ChangeTile diag = new ChangeTile();
if (diag.ShowDialog(this) == DialogResult.OK)
{
using Image img = new Bitmap(ofd.FileName);
var file = AnimationUtil.CreateNewAnimationFile(img, diag.SelectedTile, diag.IsItem);
using AnimationEditor animationEditor = new AnimationEditor(file);
if (animationEditor.ShowDialog() == DialogResult.OK)
{
file.Filename = animationEditor.TileName;
currentPCK.Files.Add(file);
ReloadMetaTreeView();
BuildMainTreeView();
saved = false;
}
}
}
}
}
bool IsSubPCKNode(string nodePath, string extention = ".pck")
{
// written by miku, implemented and modified by me - MNL
if (nodePath.EndsWith(extention)) return false;
string[] subpaths = nodePath.Split('/');
var conditions = subpaths.Select(s => Path.GetExtension(s).Equals(extention));
bool isSubFile = conditions.Contains(true);
if(isSubFile) Console.WriteLine($"{nodePath} is a Sub-PCK File");
return isSubFile;
}
List<TreeNode> GetAllChildNodes(TreeNodeCollection root)
{
List<TreeNode> childNodes = new List<TreeNode>();
foreach(TreeNode node in root)
{
childNodes.Add(node);
if (node.Nodes.Count > 0)
{
childNodes.AddRange(GetAllChildNodes(node.Nodes));
}
}
return childNodes;
}
TreeNode GetSubPCK(TreeNode child)
{
TreeNode parent = child;
while (parent.Parent != null)
{
parent = parent.Parent;
Console.WriteLine(parent.Text);
if (parent.Tag is PCKFile.FileData f &&
(f.Filetype is PCKFile.FileData.FileType.TexturePackInfoFile ||
f.Filetype is PCKFile.FileData.FileType.SkinDataFile))
return parent;
}
return null;
}
void RebuildSubPCK(TreeNode childNode)
{
// Support for if a file is edited within a PCK File
TreeNode parent = GetSubPCK(childNode);
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)
{
Console.WriteLine("Rebuilding " + parent_file.Filename);
PCKFile newPCKFile = new PCKFile(3);
foreach (TreeNode node in GetAllChildNodes(parent.Nodes))
{
if (node.Tag is PCKFile.FileData node_file)
{
PCKFile.FileData new_file = new PCKFile.FileData(node_file.Filename, node_file.Filetype);
foreach (var prop in node_file.Properties) new_file.Properties.Add(prop);
new_file.SetData(node_file.Data);
newPCKFile.Files.Add(new_file);
}
}
// Bool to add the XMLVersion property
bool isSkinsPCK = parent_file.Filetype is PCKFile.FileData.FileType.SkinDataFile;
using (MemoryStream ms = new MemoryStream())
{
PCKFileWriter.Write(ms, newPCKFile, LittleEndianCheckBox.Checked, isSkinsPCK);
parent_file.SetData(ms.ToArray());
parent.Tag = parent_file;
}
BuildMainTreeView();
}
}
private void treeViewMain_DoubleClick(object sender, EventArgs e)
{
if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PCKFile.FileData file)
{
pckFileTypeHandler[file.Filetype]?.Invoke(file);
}
}
private void treeMeta_AfterSelect(object sender, TreeViewEventArgs e)
{
if (e.Node is TreeNode t && t.Tag is ValueTuple<string, string> property)
{
entryTypeTextBox.Text = property.Item1;
entryDataTextBox.Text = property.Item2;
}
}
private void treeMeta_DoubleClick(object sender, EventArgs e)
{
if (treeMeta.SelectedNode is TreeNode subnode && subnode.Tag is ValueTuple<string, string> property &&
treeViewMain.SelectedNode is TreeNode node && node.Tag is PCKFile.FileData file)
{
int i = file.Properties.IndexOf(property);
if (i != -1)
{
switch (property.Item1)
{
case "ANIM" when file.Filetype == PCKFile.FileData.FileType.SkinFile:
try
{
using ANIMEditor diag = new ANIMEditor(property.Item2);
if (diag.ShowDialog(this) == DialogResult.OK && diag.saved)
{
file.Properties[i] = ("ANIM", diag.outANIM);
if (IsSubPCKNode(treeViewMain.SelectedNode.FullPath))
RebuildSubPCK(treeViewMain.SelectedNode);
ReloadMetaTreeView();
saved = false;
}
return;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
MessageBox.Show("Failed to parse ANIM value, aborting to normal functionality. Please make sure the value only includes hexadecimal characters (0-9,A-F) and has no more than 8 characters. It can have an optional prefix of \"0x\".");
}
break;
case "BOX" when file.Filetype == PCKFile.FileData.FileType.SkinFile:
try
{
using BoxEditor diag = new BoxEditor(property.Item2, IsSubPCKNode(treeViewMain.SelectedNode.FullPath));
if (diag.ShowDialog(this) == DialogResult.OK)
{
file.Properties[i] = ("BOX", diag.Result);
if (IsSubPCKNode(treeViewMain.SelectedNode.FullPath))
RebuildSubPCK(treeViewMain.SelectedNode);
ReloadMetaTreeView();
saved = false;
}
return;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
MessageBox.Show("Failed to parse BOX value, aborting to normal functionality.");
}
break;
default:
break;
}
using (addMeta addDialog = new addMeta(property.Item1, property.Item2))
{
if (addDialog.ShowDialog() == DialogResult.OK)
{
file.Properties[i] = (addDialog.PropertyName, addDialog.PropertyValue);
if (IsSubPCKNode(treeViewMain.SelectedNode.FullPath))
RebuildSubPCK(treeViewMain.SelectedNode);
ReloadMetaTreeView();
saved = false;
}
}
}
}
}
private void cloneFileToolStripMenuItem_Click(object sender, EventArgs e)
{
TreeNode node = treeViewMain.SelectedNode;
PCKFile.FileData mfO = node.Tag as PCKFile.FileData;
// Creates new empty file entry
PCKFile.FileData mf = new PCKFile.FileData(string.Empty, mfO.Filetype);
mf.SetData(mfO.Data); // adds file data to minefile
string dirName = Path.GetDirectoryName(mfO.Filename);
int clone_number = 0;
string prev_clone_str = "_clone1";
string nameWithoutExt = Path.GetFileNameWithoutExtension(mfO.Filename);
string newFileName = mfO.Filename;
do // Checks for existing clones and names it accordingly
{
clone_number++;
string clone_str = "_clone" + clone_number.ToString();
bool isClone = nameWithoutExt.Contains("_clone");
if (isClone) newFileName = nameWithoutExt.Remove(nameWithoutExt.Length - 7) + clone_str + Path.GetExtension(mfO.Filename);
else newFileName = nameWithoutExt + clone_str + Path.GetExtension(mfO.Filename);
prev_clone_str = clone_str;
}
while (currentPCK.HasFile(dirName + (string.IsNullOrEmpty(dirName) ? "" : "/") + newFileName, mf.Filetype));
mf.Filename = dirName + (string.IsNullOrEmpty(dirName) ? "" : "/") + newFileName; //sets minfile name to file name
foreach (var entry in mfO.Properties)
{
var property = (ValueTuple<string, string>)entry;
mf.Properties.Add(property);
}
TreeNode newNode = new TreeNode();
newNode.Text = newFileName;
newNode.Tag = mf;
newNode.ImageIndex = node.ImageIndex;
newNode.SelectedImageIndex = node.SelectedImageIndex;
if (node.Parent == null) treeViewMain.Nodes.Insert(node.Index + 1, newNode); //adds generated minefile node
else node.Parent.Nodes.Insert(node.Index + 1, newNode);//adds generated minefile node to selected folder
if (!IsSubPCKNode(node.FullPath)) currentPCK.Files.Insert(node.Index + 1, mf);
else RebuildSubPCK(node);
}
private void deleteEntryToolStripMenuItem_Click(object sender, EventArgs e)
{
if (treeMeta.SelectedNode is TreeNode t && t.Tag is ValueTuple<string, string> property &&
treeViewMain.SelectedNode is TreeNode main && main.Tag is PCKFile.FileData file &&
file.Properties.Remove(property))
{
treeMeta.SelectedNode.Remove();
if (IsSubPCKNode(treeViewMain.SelectedNode.FullPath)) RebuildSubPCK(treeViewMain.SelectedNode);
saved = false;
}
}
private void ReloadMetaTreeView()
{
treeMeta.Nodes.Clear();
if (treeViewMain.SelectedNode is TreeNode node &&
node.Tag is PCKFile.FileData file)
{
foreach (var property in file.Properties)
{
treeMeta.Nodes.Add(CreateNode(property.Item1, property));
}
}
}
private void addEntryToolStripMenuItem_Click_1(object sender, EventArgs e)
{
if (treeViewMain.SelectedNode is TreeNode t &&
t.Tag is PCKFile.FileData file)
{
using addMeta add = new addMeta();
if (add.ShowDialog() == DialogResult.OK)
{
file.Properties.Add((add.PropertyName, add.PropertyValue));
if (IsSubPCKNode(treeViewMain.SelectedNode.FullPath)) RebuildSubPCK(treeViewMain.SelectedNode);
ReloadMetaTreeView();
saved = false;
}
}
}
#region drag and drop for main tree node
public static void getChildren(List<TreeNode> 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
private void treeViewMain_ItemDrag(object sender, ItemDragEventArgs e)
{
}
// Set the target drop effect to the effect
// specified in the ItemDrag event handler.
private void treeViewMain_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.AllowedEffect;
}
// Select the node under the mouse pointer to indicate the
// expected drop location.
private void treeViewMain_DragOver(object sender, DragEventArgs e)
{
}
private void treeViewMain_DragDrop(object sender, DragEventArgs e)
{
}
// 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 void metaToolStripMenuItem_Click(object sender, EventArgs e)
{
MetaList edit = new MetaList(currentPCK.GetPropertyList());
edit.TopMost = true;
edit.TopLevel = true;
edit.Show();
}
private PCKFile InitializePack(int packId, int packVersion, string packName, bool createSkinsPCK)
{
var newPck = new PCKFile(3);
var zeroFile = new PCKFile.FileData("0", PCKFile.FileData.FileType.InfoFile);
zeroFile.Properties.Add(("PACKID", packId.ToString()));
zeroFile.Properties.Add(("PACKVERSION", packVersion.ToString()));
var loc = new PCKFile.FileData("localisation.loc", PCKFile.FileData.FileType.LocalisationFile);
var locFile = new LOCFile();
locFile.InitializeDefault(packName);
using (var stream = new MemoryStream())
{
LOCFileWriter.Write(stream, locFile);
loc.SetData(stream.ToArray());
}
newPck.Files.Add(zeroFile);
newPck.Files.Add(loc);
if (createSkinsPCK)
{
PCKFile.FileData skinsPCKFile = new PCKFile.FileData("Skins.pck", PCKFile.FileData.FileType.SkinDataFile);
using (var stream = new MemoryStream())
{
PCKFileWriter.Write(stream, new PCKFile(3), LittleEndianCheckBox.Checked, true);
skinsPCKFile.SetData(stream.ToArray());
}
newPck.Files.Add(skinsPCKFile);
}
return newPck;
}
private PCKFile InitializeTexturePack(int packId, int packVersion, string packName, string res, bool createSkinsPCK = false)
{
var newPck = InitializePack(packId, packVersion, packName, createSkinsPCK);
var texturepackInfo = new PCKFile.FileData($"{res}/{res}Info.pck", PCKFile.FileData.FileType.TexturePackInfoFile);
texturepackInfo.Properties.Add(("PACKID", "0"));
texturepackInfo.Properties.Add(("DATAPATH", $"{res}Data.pck"));
PCKFile infoPCK = new PCKFile(3);
using (var ms = new MemoryStream())
{
var icon = new PCKFile.FileData("icon.png", PCKFile.FileData.FileType.TextureFile);
Resources.TexturePackIcon.Save(ms, ImageFormat.Png);
icon.SetData(ms.ToArray());
infoPCK.Files.Add(icon);
}
using (var ms = new MemoryStream())
{
var comparison = new PCKFile.FileData("comparison.png", PCKFile.FileData.FileType.TextureFile);
Resources.Comparison.Save(ms, ImageFormat.Png);
comparison.SetData(ms.ToArray());
infoPCK.Files.Add(comparison);
}
using (var ms = new MemoryStream())
{
PCKFileWriter.Write(ms, infoPCK, LittleEndianCheckBox.Checked);
texturepackInfo.SetData(ms.ToArray());
}
newPck.Files.Add(texturepackInfo);
return newPck;
}
private PCKFile InitializeMashUpPack(int packId, int packVersion, string packName, string res)
{
var newPck = InitializeTexturePack(packId, packVersion, packName, res, true);
var gameRuleFile = new PCKFile.FileData("GameRules.grf", PCKFile.FileData.FileType.GameRulesFile);
var grfFile = new GRFFile();
grfFile.AddRule("MapOptions",
new KeyValuePair<string, string>("seed", "0"),
new KeyValuePair<string, string>("baseSaveName", string.Empty),
new KeyValuePair<string, string>("flatworld", "false"),
new KeyValuePair<string, string>("texturePackId", packId.ToString())
);
grfFile.AddRule("LevelRules")
.AddRule("UpdatePlayer",
new KeyValuePair<string, string>("yRot", "0"),
new KeyValuePair<string, string>("xRot", "0"),
new KeyValuePair<string, string>("spawnX", "0"),
new KeyValuePair<string, string>("spawnY", "0"),
new KeyValuePair<string, string>("spawnZ", "0")
);
using (var stream = new MemoryStream())
{
GRFFileWriter.Write(stream, grfFile, GRFFile.eCompressionType.ZlibRleCrc);
gameRuleFile.SetData(stream.ToArray());
}
newPck.Files.Add(gameRuleFile);
return newPck;
}
private void skinPackToolStripMenuItem_Click(object sender, EventArgs e)
{
checkSaveState();
RenamePrompt namePrompt = new RenamePrompt("");
namePrompt.OKButton.Text = "Ok";
if (namePrompt.ShowDialog() == DialogResult.OK)
{
currentPCK = InitializePack(new Random().Next(8000, int.MaxValue), 0, namePrompt.NewText, true);
isTemplateFile = true;
saved = false;
LoadEditorTab();
}
}
private void texturePackToolStripMenuItem_Click(object sender, EventArgs e)
{
checkSaveState();
CreateTexturePack packPrompt = new CreateTexturePack("");
if (packPrompt.ShowDialog() == DialogResult.OK)
{
currentPCK = InitializeTexturePack(new Random().Next(8000, int.MaxValue), 0, packPrompt.packName, packPrompt.packRes);
isTemplateFile = true;
saved = false;
LoadEditorTab();
}
}
private void mashUpPackToolStripMenuItem_Click(object sender, EventArgs e)
{
checkSaveState();
CreateTexturePack packPrompt = new CreateTexturePack("");
if (packPrompt.ShowDialog() == DialogResult.OK)
{
currentPCK = InitializeMashUpPack(new Random().Next(8000, int.MaxValue), 0, packPrompt.packName, packPrompt.packRes);
isTemplateFile = true;
saved = false;
LoadEditorTab();
}
}
private void advancedMetaAddingToolStripMenuItem_Click(object sender, EventArgs e)
{
//opens dialog for bulk minefile editing
using AdvancedOptions advanced = new AdvancedOptions(currentPCK);
if (advanced.ShowDialog() == DialogResult.OK)
saved = false;
}
private void closeToolStripMenuItem_Click(object sender, EventArgs e)
{
checkSaveState();
CloseEditorTab();
}
private void programInfoToolStripMenuItem_Click(object sender, EventArgs e)
{
using programInfo info = new programInfo();
info.ShowDialog();
}
private void treeViewMain_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Delete:
deleteFileToolStripMenuItem_Click(sender, e);
break;
case Keys.F2:
renameFileToolStripMenuItem_Click(sender, e);
break;
}
}
private void treeViewMain_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
{
// 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 = true;
}
private void extractToolStripMenuItem1_Click(object sender, EventArgs e)
{
try
{
//Extracts a chosen pck file to a chosen destincation
using OpenFileDialog ofd = new OpenFileDialog();
using FolderBrowserDialog sfd = new FolderBrowserDialog();
ofd.CheckFileExists = true;
ofd.Filter = "PCK (Minecraft Console Package)|*.pck";
if (ofd.ShowDialog() == DialogResult.OK && sfd.ShowDialog() == DialogResult.OK)
{
PCKFile pckfile = null;
using (var fs = File.OpenRead(ofd.FileName))
{
try
{
pckfile = PCKFileReader.Read(fs, LittleEndianCheckBox.Checked);
}
catch (OverflowException ex)
{
MessageBox.Show("Error", "Failed to open pck\nTry checking the 'Open/Save as Vita/PS4 pck' check box in the upper right corner.",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(ex.Message);
}
}
if (pckfile.HasFile("0", PCKFile.FileData.FileType.InfoFile) &&
pckfile.GetFile("0", PCKFile.FileData.FileType.InfoFile).Properties.HasProperty("LOCK") &&
new LockPrompt(pckfile.GetFile("0", PCKFile.FileData.FileType.InfoFile).Properties.GetProperty("LOCK").Item2).ShowDialog() != DialogResult.OK)
return; // cancel extraction if password not provided
foreach (PCKFile.FileData file in pckfile.Files)
{
string filepath = $"{sfd.SelectedPath}/{file.Filename}";
FileInfo fileinfo = new FileInfo(filepath);
fileinfo.Directory.Create();
File.WriteAllBytes(filepath, file.Data); // writes data to file
//attempts to generate reimportable metadata file out of minefiles metadata
string metaData = "";
foreach (var entry in file.Properties)
{
metaData += $"{entry.Item1}: {entry.Item2}{Environment.NewLine}";
}
File.WriteAllText(sfd.SelectedPath + @"\" + file.Filename + ".txt", metaData);
}
}
}
catch (Exception)
{
MessageBox.Show("An Error occured while extracting data");
}
}
public string GetDataPath()
{
return Path.Combine(Path.GetDirectoryName(saveLocation), "Data");
}
public bool HasDataFolder()
{
return Directory.Exists(GetDataPath());
}
public bool CreateDataFolder()
{
if (!HasDataFolder())
{
DialogResult result = MessageBox.Show("There is not a \"Data\" folder present in the pack folder. Would you like to create one?", "Folder missing", MessageBoxButtons.YesNo);
if (result == DialogResult.No) return false;
else Directory.CreateDirectory(GetDataPath());
}
return true;
}
private void treeMeta_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Delete)
deleteEntryToolStripMenuItem_Click(sender, e);
}
#region imports a folder of skins to pck
private void importExtractedSkinsFolder(object sender, EventArgs e)
{
using FolderBrowserDialog contents = new FolderBrowserDialog();
if (contents.ShowDialog() == DialogResult.OK)
{
//checks to make sure selected path exist
if (!Directory.Exists(contents.SelectedPath))
{
MessageBox.Show("Directory Lost");
return;
}
// creates variable to indicate wether current pck skin structure is mashup or regular skin
bool hasSkinsPck = currentPCK.HasFile("Skins.pck", PCKFile.FileData.FileType.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;
string pckfilepath = (hasSkinsPck ? "Skins/" : string.Empty) + filename + ".png";
PCKFile.FileData newFile = new PCKFile.FileData(pckfilepath, pckfiletype);
byte[] filedata = File.ReadAllBytes(fullfilename);
newFile.SetData(filedata);
if (File.Exists(fullfilename + ".txt"))
{
string[] properties = File.ReadAllText(fullfilename + ".txt").Split(new string[]{ Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (string property in properties)
{
string[] param = property.Split(':');
if (param.Length < 2) continue;
newFile.Properties.Add((param[0], param[1]));
//switch (param[0])
//{
// case "DISPLAYNAMEID":
// locNameId = param[1];
// continue;
// case "DISPLAYNAME":
// locName = param[1];
// continue;
// case "THEMENAMEID":
// locThemeId = param[1];
// continue;
// case "THEMENAME":
// locTheme = param[1];
// continue;
//}
}
}
if (hasSkinsPck)
{
var skinsfile = currentPCK.GetFile("Skins.pck", PCKFile.FileData.FileType.SkinDataFile);
using (var ms = new MemoryStream(skinsfile.Data))
{
var skinspck = PCKFileReader.Read(ms, LittleEndianCheckBox.Checked);
skinspck.Files.Add(newFile);
ms.Position = 0;
PCKFileWriter.Write(ms, skinspck, LittleEndianCheckBox.Checked);
skinsfile.SetData(ms.ToArray());
}
continue;
}
currentPCK.Files.Add(newFile);
}
BuildMainTreeView();
saved = false;
}
}
#endregion
private bool TryGetLocFile(out LOCFile locFile)
{
if (!currentPCK.TryGetFile("localisation.loc", PCKFile.FileData.FileType.LocalisationFile, out PCKFile.FileData locdata) &&
!currentPCK.TryGetFile("languages.loc", PCKFile.FileData.FileType.LocalisationFile, out locdata))
{
locFile = null;
return false;
}
try
{
using (var stream = new MemoryStream(locdata.Data))
{
locFile = LOCFileReader.Read(stream);
}
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
locFile = null;
return false;
}
private bool TrySetLocFile(in LOCFile locFile)
{
if (!currentPCK.TryGetFile("localisation.loc", PCKFile.FileData.FileType.LocalisationFile, out PCKFile.FileData locdata) &&
!currentPCK.TryGetFile("languages.loc", PCKFile.FileData.FileType.LocalisationFile, out locdata))
{
return false;
}
try
{
using (var stream = new MemoryStream())
{
LOCFileWriter.Write(stream, locFile);
locdata.SetData(stream.ToArray());
}
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return false;
}
private void importSkin(object sender, EventArgs e)
{
using (OpenFileDialog contents = new OpenFileDialog())
{
contents.Title = "Select Extracted Skin File";
contents.Filter = "Skin File (*.png)|*.png";
if (contents.ShowDialog() == DialogResult.OK)
{
string skinNameImport = Path.GetFileName(contents.FileName);
byte[] data = File.ReadAllBytes(contents.FileName);
PCKFile.FileData mfNew = new PCKFile.FileData(skinNameImport, 0);
mfNew.SetData(data);
string propertyFile = Path.GetFileNameWithoutExtension(contents.FileName) + ".txt";
if (File.Exists(propertyFile))
{
string[] txtProperties = File.ReadAllLines(propertyFile);
if ((txtProperties.Contains("DISPLAYNAMEID") && txtProperties.Contains("DISPLAYNAME")) ||
txtProperties.Contains("THEMENAMEID") && txtProperties.Contains("THEMENAME") &&
TryGetLocFile(out LOCFile locFile))
{
// do stuff
//l.AddLocKey(locThemeId, locTheme);
//using (var stream = new MemoryStream())
//{
// LOCFileWriter.Write(stream, locFile);
// locdata.SetData(stream.ToArray());
//}
}
try
{
foreach (string prop in txtProperties)
{
string[] arg = prop.Split(':');
if (arg.Length < 2) continue;
string key = arg[0];
string value = arg[1];
if (key == "DISPLNAMEID" || key == "THEMENAMEID")
{
}
mfNew.Properties.Add(new ValueTuple<string, string>(key, value));
}
saved = false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
}
private void folderToolStripMenuItem_Click(object sender, EventArgs e)
{
RenamePrompt folderNamePrompt = new RenamePrompt("");
folderNamePrompt.OKButton.Text = "Add";
if (folderNamePrompt.ShowDialog() == DialogResult.OK)
{
TreeNode folerNode = CreateNode(folderNamePrompt.NewText);
folerNode.ImageIndex = 0;
folerNode.SelectedImageIndex = 0;
TreeNodeCollection nodeCollection = treeViewMain.Nodes;
if (treeViewMain.SelectedNode is TreeNode node)
{
if (node.Tag is PCKFile.FileData)
{
if (node.Parent is TreeNode parentNode)
{
nodeCollection = parentNode.Nodes;
}
}
else
nodeCollection = node.Nodes;
}
nodeCollection.Add(folerNode);
}
}
private void addPasswordToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!currentPCK.HasFile("0", PCKFile.FileData.FileType.InfoFile)) throw new FileNotFoundException("0 file not found");
PCKFile.FileData file = currentPCK.GetFile("0", PCKFile.FileData.FileType.InfoFile);
if (checkForPassword())
{
AddPCKPassword add = new AddPCKPassword();
if (add.ShowDialog() == DialogResult.OK)
file.Properties.SetProperty("LOCK", add.Password);
add.Dispose();
ReloadMetaTreeView();
saved = false;
}
}
private void binkaConversionToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.youtube.com/watch?v=v6EYr4zc7rI");
}
private void fAQToolStripMenuItem1_Click(object sender, EventArgs e)
{
//System.Diagnostics.Process.Start(hosturl + "pckStudio#faq");
}
private void convertToBedrockToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("This feature is currently being reworked.", "Currently unavailable", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
private void openToolStripMenuItem1_Click(object sender, EventArgs e)
{
DateTime Begin = DateTime.Now;
//pckCenter open = new pckCenter();
PckCenterBeta open = new PckCenterBeta();
open.Show();
TimeSpan duration = new TimeSpan(DateTime.Now.Ticks - Begin.Ticks);
Debug.WriteLine("Completed in: " + duration);
}
private void wiiUPCKInstallerToolStripMenuItem_Click(object sender, EventArgs e)
{
installWiiU install = new installWiiU(null);
install.ShowDialog();
}
private void howToMakeABasicSkinPackToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.youtube.com/watch?v=A43aHRHkKxk");
}
private void howToMakeACustomSkinModelToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.youtube.com/watch?v=pEC_ug55lag");
}
private void howToMakeCustomSkinModelsbedrockToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.youtube.com/watch?v=6z8NTogw5x4");
}
private void howToMakeCustomMusicToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.youtube.com/watch?v=v6EYr4zc7rI");
}
private void howToInstallPcksDirectlyToWiiUToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.youtube.com/watch?v=hRQagnEplec");
}
private void pCKCenterReleaseToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.youtube.com/watch?v=E_6bXSh6yqw");
}
private void howPCKsWorkToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.youtube.com/watch?v=hTlImrRrCKQ");
}
private void PS3PCKInstallerToolStripMenuItem_Click(object sender, EventArgs e)
{
installPS3 install = new installPS3(null);
install.ShowDialog();
}
private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
{
Pref setting = new Pref();
setting.Show();
}
private void administrativeToolsToolStripMenuItem_Click(object sender, EventArgs e)
{
PCK_Manager pckm = new PCK_Manager();
pckm.Show();
}
private void VitaPCKInstallerToolStripMenuItem_Click(object sender, EventArgs e)
{
installVita install = new installVita(null);
install.ShowDialog();
}
private void toPhoenixARCDeveloperToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://cash.app/$PhoenixARC");
}
private void toNobledezJackToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://www.paypal.me/realnobledez");
}
private void joinDevelopmentDiscordToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://discord.gg/aJtZNFVQTv");
}
private void convertPCTextrurePackToolStripMenuItem_Click(object sender, EventArgs e)
{
TextureConverterUtility tex = new TextureConverterUtility(treeViewMain, currentPCK);
tex.ShowDialog();
}
private void OpenPck_MouseEnter(object sender, EventArgs e)
{
pckOpen.Image = Resources.pckOpen;
}
private void OpenPck_MouseLeave(object sender, EventArgs e)
{
pckOpen.Image = Resources.pckClosed;
}
private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
{
if (needsUpdate && File.Exists(Program.AppData + @"\nobleUpdater.exe"))
{
Process.Start(Program.AppData + @"\nobleUpdater.exe"); // starts updater
Application.Exit(); // closes PCK Studio to let updatear finish the job
}
}
private void checkSaveState()
{
if (currentPCK is not null &&
!saved &&
MessageBox.Show("Save PCK?", "Unsaved PCK", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
{
if (isTemplateFile || string.IsNullOrEmpty(saveLocation))
{
SaveTemplate();
return;
}
Save(saveLocation);
}
}
private void OpenPck_DragEnter(object sender, DragEventArgs e)
{
pckOpen.Image = Resources.pckDrop;
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (var file in files)
{
var ext = Path.GetExtension(file);
if (ext.Equals(".pck", StringComparison.CurrentCultureIgnoreCase))
e.Effect = DragDropEffects.Copy;
return;
}
}
private void OpenPck_DragDrop(object sender, DragEventArgs e)
{
string[] Filepaths = (string[])e.Data.GetData(DataFormats.FileDrop, false);
if (Filepaths.Length > 1)
MessageBox.Show("Only one pck file at a time is currently supported");
LoadPck(Filepaths[0]);
}
private void OpenPck_DragLeave(object sender, EventArgs e)
{
pckOpen.Image = Resources.pckClosed;
}
private void OpenPck_Click(object sender, EventArgs e)
{
openToolStripMenuItem_Click(sender, e);
}
private void savePCK(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(saveLocation))
Save(saveLocation);
}
private void saveAsPCK(object sender, EventArgs e)
{
SaveTemplate();
}
private void forMattNLContributorToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start("https://ko-fi.com/mattnl");
}
private void SetPckFileIcon(TreeNode node, PCKFile.FileData.FileType type)
{
switch (type)
{
case PCKFile.FileData.FileType.AudioFile:
node.ImageIndex = 1;
node.SelectedImageIndex = 1;
break;
case PCKFile.FileData.FileType.LocalisationFile:
node.ImageIndex = 3;
node.SelectedImageIndex = 3;
break;
case PCKFile.FileData.FileType.TexturePackInfoFile:
node.ImageIndex = 4;
node.SelectedImageIndex = 4;
break;
case PCKFile.FileData.FileType.ColourTableFile:
node.ImageIndex = 6;
node.SelectedImageIndex = 6;
break;
case PCKFile.FileData.FileType.ModelsFile:
node.ImageIndex = 8;
node.SelectedImageIndex = 8;
break;
case PCKFile.FileData.FileType.SkinDataFile:
node.ImageIndex = 7;
node.SelectedImageIndex = 7;
break;
case PCKFile.FileData.FileType.GameRulesFile:
node.ImageIndex = 9;
node.SelectedImageIndex = 9;
break;
case PCKFile.FileData.FileType.GameRulesHeader:
node.ImageIndex = 10;
node.SelectedImageIndex = 10;
break;
case PCKFile.FileData.FileType.InfoFile:
node.ImageIndex = 11;
node.SelectedImageIndex = 11;
break;
case PCKFile.FileData.FileType.SkinFile:
node.ImageIndex = 12;
node.SelectedImageIndex = 12;
break;
case PCKFile.FileData.FileType.CapeFile:
node.ImageIndex = 13;
node.SelectedImageIndex = 13;
break;
case PCKFile.FileData.FileType.TextureFile:
node.ImageIndex = 14;
node.SelectedImageIndex = 14;
break;
case PCKFile.FileData.FileType.BehavioursFile:
node.ImageIndex = 15;
node.SelectedImageIndex = 15;
break;
case PCKFile.FileData.FileType.MaterialFile:
node.ImageIndex = 16;
node.SelectedImageIndex = 16;
break;
default: // unknown file format
node.ImageIndex = 5;
node.SelectedImageIndex = 5;
break;
}
}
private void setFileType_Click(object sender, EventArgs e, PCKFile.FileData.FileType type)
{
if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PCKFile.FileData file)
{
Debug.WriteLine($"Setting {file.Filetype} to {type}");
file.Filetype = type;
SetPckFileIcon(t, type);
if (IsSubPCKNode(treeViewMain.SelectedNode.FullPath)) RebuildSubPCK(treeViewMain.SelectedNode);
}
}
private void addTextureToolStripMenuItem_Click(object sender, EventArgs e)
{
using OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Filter = "Texture File(*.png)|*.png";
if (fileDialog.ShowDialog() == DialogResult.OK)
{
using RenamePrompt renamePrompt = new RenamePrompt(Path.GetFileName(fileDialog.FileName));
renamePrompt.TextLabel.Text = "Path";
if (renamePrompt.ShowDialog() == DialogResult.OK && !string.IsNullOrEmpty(renamePrompt.NewText))
{
var file = new PCKFile.FileData(renamePrompt.NewText, PCKFile.FileData.FileType.TextureFile);
file.SetData(File.ReadAllBytes(fileDialog.FileName));
currentPCK.Files.Add(file);
BuildMainTreeView();
saved = false;
}
}
}
private void viewFileInfoToolStripMenuItem_Click(object sender, EventArgs e)
{
if (treeViewMain.SelectedNode.Tag is PCKFile.FileData file)
{
MessageBox.Show(
"File path: " + file.Filename +
"\nAssigned File type: " + (int)file.Filetype + " (" + file.Filetype + ")" +
"\nFile size: " + file.Size +
"\nProperties count: " + file.Properties.Count
, Path.GetFileName(file.Filename) + " file info");
}
}
private void generateMipMapTextureToolStripMenuItem_Click(object sender, EventArgs e)
{
if (treeViewMain.SelectedNode.Tag is PCKFile.FileData file && file.Filetype == PCKFile.FileData.FileType.TextureFile)
{
string textureDirectory = Path.GetDirectoryName(file.Filename);
string textureName = Path.GetFileNameWithoutExtension(file.Filename);
if (IsFilePathMipMapped(textureName)) return;
string textureExtension = Path.GetExtension(file.Filename);
// TGA is not yet supported
if (textureExtension == ".tga") return;
using MipMapPrompt diag = new MipMapPrompt();
if (diag.ShowDialog(this) == DialogResult.OK)
{
for (int i = 2; i < 2 + diag.Levels; i++)
{
string mippedPath = textureDirectory + "/" + textureName + "MipMapLevel" + i + textureExtension;
Debug.WriteLine(mippedPath);
if (currentPCK.HasFile(mippedPath, PCKFile.FileData.FileType.TextureFile))
currentPCK.Files.Remove(currentPCK.GetFile(mippedPath, PCKFile.FileData.FileType.TextureFile));
PCKFile.FileData MipMappedFile = new PCKFile.FileData(mippedPath, PCKFile.FileData.FileType.TextureFile);
Image originalTexture = Image.FromStream(new MemoryStream(file.Data));
int NewWidth = originalTexture.Width / (int)Math.Pow(2,i - 1);
int NewHeight = originalTexture.Height / (int)Math.Pow(2, i - 1);
Rectangle tileArea = new Rectangle(0, 0,
NewWidth < 1 ? 1 : NewWidth,
NewHeight < 1 ? 1 : NewHeight);
Image mippedTexture = new Bitmap(NewWidth, NewHeight);
using (Graphics gfx = Graphics.FromImage(mippedTexture))
{
gfx.SmoothingMode = SmoothingMode.None;
gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
gfx.DrawImage(originalTexture, tileArea);
}
MemoryStream texStream = new MemoryStream();
mippedTexture.Save(texStream, ImageFormat.Png);
MipMappedFile.SetData(texStream.ToArray());
currentPCK.Files.Insert(currentPCK.Files.IndexOf(file) + i - 1, MipMappedFile);
}
BuildMainTreeView();
}
}
}
private void colourscolToolStripMenuItem_Click(object sender, EventArgs e)
{
PCKFile.FileData NewColorFile;
if (currentPCK.TryGetFile("colours.col", PCKFile.FileData.FileType.ColourTableFile, out NewColorFile))
{
MessageBox.Show("A color table file already exists in this PCK and a new one cannot be created.", "Operation aborted");
return;
}
NewColorFile = new PCKFile.FileData("colours.col", PCKFile.FileData.FileType.ColourTableFile);
NewColorFile.SetData(Resources.tu69colours);
currentPCK.Files.Add(NewColorFile);
BuildMainTreeView();
}
private void tabControl_Selecting(object sender, TabControlCancelEventArgs e)
{
if (!isSelectingTab) e.Cancel = true;
}
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)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "3DS Texture | *.3dst";
saveFileDialog.DefaultExt = ".3dst";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
using (var fs = saveFileDialog.OpenFile())
{
using var ms = new MemoryStream(file.Data);
Image img = Image.FromStream(ms);
_3DSUtil.SetImageTo3DST(fs, img);
}
}
}
}
private void addMultipleEntriesToolStripMenuItem1_Click(object sender, EventArgs e)
{
if (treeViewMain.SelectedNode is TreeNode node &&
node.Tag is PCKFile.FileData file)
{
using (var input = new TextPrompt())
{
if (input.ShowDialog(this) == DialogResult.OK)
{
foreach (var line in input.TextOutput)
{
int idx = line.IndexOf(' ');
if (idx == -1 || line.Length - 1 == idx)
continue;
file.Properties.Add((line.Substring(0, idx), line.Substring(idx + 1)));
}
ReloadMetaTreeView();
if (IsSubPCKNode(node.FullPath)) RebuildSubPCK(node);
saved = false;
}
}
}
}
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)
{
foreach(var p in file.Properties.FindAll(s => s.property == "BOX" || s.property == "OFFSET"))
{
file.Properties[file.Properties.IndexOf(p)] = (p.property, p.value.Replace(',','.'));
}
ReloadMetaTreeView();
if (IsSubPCKNode(node.FullPath)) RebuildSubPCK(node);
saved = false;
}
}
private void addCustomPackIconToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!currentPCK.TryGetFile("0", PCKFile.FileData.FileType.InfoFile, out PCKFile.FileData file) ||
string.IsNullOrEmpty(file.Properties.GetPropertyValue("PACKID"))
)
{
MessageBox.Show("No PackID is present in this pack. To avoid this error, please open a PCK with a PackID before trying again.", "Operation Aborted", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "Minecraft Archive|*.arc";
if (dialog.ShowDialog(this) == DialogResult.OK)
{
string filepath = dialog.FileName;
dialog.Filter = "Pack Icon|*.png";
if (dialog.ShowDialog(this) == DialogResult.OK)
{
using (var fs = File.OpenRead(filepath))
{
ARCUtil.Inject(fs, (
string.Format("Graphics\\PackGraphics\\{0}.png", file.Properties.GetPropertyValue("PACKID")),
File.ReadAllBytes(dialog.FileName))
);
MessageBox.Show("Successfully added Pack Icon to Archive!", "Successfully Added", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
private void CreateSkinsPCKToolStripMenuItem1_Click(object sender, EventArgs e)
{
if (currentPCK.TryGetFile("Skins.pck", PCKFile.FileData.FileType.SkinDataFile, out _))
{
MessageBox.Show("A Skins.pck file already exists in this PCK and a new one cannot be created.", "Operation aborted");
return;
}
PCKFile.FileData newSkinsPCKFile = new PCKFile.FileData("Skins.pck", PCKFile.FileData.FileType.SkinDataFile);
using (var stream = new MemoryStream())
{
PCKFileWriter.Write(stream, new PCKFile(3), LittleEndianCheckBox.Checked, true);
newSkinsPCKFile.SetData(stream.ToArray());
}
currentPCK.Files.Add(newSkinsPCKFile);
BuildMainTreeView();
TreeNode skinsNode = treeViewMain.Nodes.Find("Skins.pck", false).FirstOrDefault();
TreeNode folderNode = CreateNode("Skins");
folderNode.ImageIndex = 0;
folderNode.SelectedImageIndex = 0;
skinsNode.Nodes.Add(folderNode);
}
private void editAllEntriesToolStripMenuItem_Click(object sender, EventArgs e)
{
if (treeViewMain.SelectedNode is TreeNode node &&
node.Tag is PCKFile.FileData file)
{
var props = file.Properties.Select(l => l.property + " " + l.value);
using (var input = new TextPrompt(props.ToArray()))
{
if (input.ShowDialog(this) == DialogResult.OK)
{
file.Properties.Clear();
foreach (var line in input.TextOutput)
{
int idx = line.IndexOf(' ');
if (idx == -1 || line.Length - 1 == idx)
continue;
file.Properties.Add((line.Substring(0, idx), line.Substring(idx + 1)));
}
ReloadMetaTreeView();
if (IsSubPCKNode(node.FullPath)) RebuildSubPCK(node);
saved = false;
}
}
}
}
private void addFileToolStripMenuItem_Click(object sender, EventArgs e)
{
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.
ofd.Filter = "All files (*.*)|*.*";
ofd.Multiselect = false;
if (ofd.ShowDialog() == DialogResult.OK)
{
using AddFilePrompt diag = new AddFilePrompt("res/" + Path.GetFileName(ofd.FileName));
if (diag.ShowDialog(this) == DialogResult.OK)
{
PCKFile.FileData file = new PCKFile.FileData(diag.filepath, (PCKFile.FileData.FileType)diag.filetype);
file.SetData(File.ReadAllBytes(ofd.FileName));
currentPCK.Files.Add(file);
if (IsSubPCKNode(treeViewMain.SelectedNode.FullPath)) RebuildSubPCK(treeViewMain.SelectedNode);
//else treeViewMain.Nodes.Add();
BuildMainTreeView();
saved = false;
}
}
return;
}
}
public class PckNodeSorter : System.Collections.IComparer
{
int System.Collections.IComparer.Compare(Object x, Object y)
{
TreeNode NodeX = x as TreeNode;
TreeNode NodeY = y as TreeNode;
if (NodeX.Tag is PCKFile.FileData file)
{
switch (file.Filetype)
{
case PCKFile.FileData.FileType.SkinFile:
case PCKFile.FileData.FileType.CapeFile:
return 0; // ignore these files in order to preserve skin files
}
}
int result = NodeX.Text.CompareTo(NodeY.Text);
if (result != 0) return result;
return NodeX.ImageIndex.CompareTo(NodeY.ImageIndex);
}
}
}