Files
PCK-Studio/PCK-Studio/MainForm.cs
2025-12-14 05:00:02 +01:00

567 lines
23 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using MetroFramework.Forms;
using OMI.Formats.GameRule;
using OMI.Formats.Languages;
using OMI.Formats.Pck;
using OMI.Workers.GameRule;
using OMI.Workers.Language;
using OMI.Workers.Pck;
using PckStudio.Controls;
using PckStudio.Core;
using PckStudio.Core.App;
using PckStudio.Core.DLC;
using PckStudio.Core.Extensions;
using PckStudio.Core.Interfaces;
using PckStudio.External.API.Miles;
using PckStudio.Forms;
using PckStudio.Forms.Additional_Popups;
using PckStudio.Forms.Features;
using PckStudio.Interfaces;
using PckStudio.Internal;
using PckStudio.Internal.App;
using PckStudio.Popups;
using PckStudio.Properties;
namespace PckStudio
{
public partial class MainForm : ImmersiveForm
{
private PckManager PckManager = null;
private Dictionary<string, TabPage> openTabPages = new Dictionary<string, TabPage>();
private DLCManager _dlcManager;
public MainForm()
{
InitializeComponent();
Text = Application.ProductName;
labelVersion.Text = $"{Application.ProductName}: {ApplicationScope.CurrentVersion}";
ChangelogRichTextBox.Text = Resources.CHANGELOG;
pckOpen.AllowDrop = true;
_dlcManager = new DLCManager(Settings.Default.Platform, Settings.Default.UserLanguage);
Internal.SettingsManager.Default.RegisterPropertyChangedCallback<AppLanguage>(nameof(Settings.Default.UserLanguage), _dlcManager.SetPreferredLanguage);
Internal.SettingsManager.Default.RegisterPropertyChangedCallback<ConsolePlatform>(nameof(Settings.Default.Platform), _dlcManager.SetPlatform);
}
public void LoadPckFromFile(IEnumerable<string> filepaths)
{
foreach (string filepath in filepaths)
{
LoadPckFromFile(filepath);
}
}
public void LoadPckFromFile(string filepath)
{
IDLCPackage dlcPackage = _dlcManager.OpenDLCPackage(filepath);
Debug.WriteLine(dlcPackage?.GetDLCPackageType());
if (dlcPackage is null)
{
MessageBox.Show($"Failed to open '{Path.GetFileName(filepath)}'");
return;
}
SaveToRecentFiles(filepath);
AddEditorPage(dlcPackage);
}
internal void OpenNewPckTab(string caption, string identifier, RawAssetDLCPackage packInfo, ISaveContext<RawAssetDLCPackage> saveContext)
{
if (openTabPages.ContainsKey(identifier))
{
tabControl.SelectTab(openTabPages[identifier]);
return;
}
var editor = new RawAssetsEditor(packInfo, saveContext);
AddPage(caption, identifier, editor);
}
private void AddEditorPage(string caption, string identifier, RawAssetDLCPackage packInfo, ISaveContext<RawAssetDLCPackage> saveContext = null)
{
saveContext ??= GetDefaultSaveContext("./new.pck", "PCK (Minecraft Console Package)");
var editor = new RawAssetsEditor(packInfo, saveContext);
AddPage(caption, identifier, editor);
}
private void AddEditorPage(IDLCPackage dlcPackage)
{
switch (dlcPackage.GetDLCPackageType())
{
case DLCPackageType.RawAssets:
ISaveContext<RawAssetDLCPackage> saveContext = GetDefaultSaveContext("", "PCK (Minecraft Console Package)");
var editor = new RawAssetsEditor(dlcPackage as RawAssetDLCPackage, saveContext);
TabPage page = AddPage(dlcPackage.Name, dlcPackage.Name, editor);
break;
case DLCPackageType.SkinPack:
break;
case DLCPackageType.TexturePack:
break;
case DLCPackageType.MashUpPack:
break;
case DLCPackageType.MG01:
case DLCPackageType.MG02:
case DLCPackageType.MG03:
MessageBox.Show("Not Implemented...");
break;
default:
Trace.TraceError("Invalid Package Type");
break;
}
}
private static ISaveContext<RawAssetDLCPackage> GetDefaultSaveContext(string filepath, string description)
{
return new DelegatedFileSaveContext<RawAssetDLCPackage>(filepath, false, new FileDialogFilter(description, "*" + Path.GetExtension(filepath)), (packInfo, stream) => new PckFileWriter(packInfo.PckFile, packInfo.ByteOrder).WriteToStream(stream));
}
private TabPage AddPage(string caption, string identifier, Control control)
{
identifier ??= caption;
if (openTabPages.ContainsKey(identifier))
return openTabPages[identifier];
control.Dock = DockStyle.Fill;
var page = new TabPage(caption);
page.Name = identifier;
page.Controls.Add(control);
tabControl.TabPages.Add(page);
tabControl.SelectTab(page);
return page;
}
private bool TryGetEditor(TabPage page, out IEditor<RawAssetDLCPackage> editor)
{
if (page.Controls[0] is IEditor<RawAssetDLCPackage> outEditor)
{
editor = outEditor;
return true;
}
editor = default;
return false;
}
private bool TryGetCurrentEditor(out IEditor<RawAssetDLCPackage> editor) => TryGetEditor(tabControl.SelectedTab, out editor);
private void MainForm_Load(object sender, EventArgs e)
{
LoadRecentFileList();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
PckManager?.Close();
closeAllToolStripMenuItem_Click(sender, e);
}
private void closeAllToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (TabPage tab in tabControl.TabPages)
{
CloseTab(tabControl.TabPages, tab);
}
closeAllToolStripMenuItem.Visible = false;
}
private void CloseTab(TabControl.TabPageCollection collection, TabPage page)
{
if (TryGetEditor(page, out IEditor<RawAssetDLCPackage> editor))
{
editor.Close();
RemoveOpenFile(page);
collection.Remove(page);
}
}
private void RemoveOpenFile()
{
RemoveOpenFile(tabControl.SelectedTab);
}
private void RemoveOpenFile(TabPage page)
{
if (openTabPages.ContainsKey(page.Name))
{
openTabPages.Remove(page.Name);
}
}
private void LoadRecentFileList()
{
Settings.Default.RecentFiles ??= new System.Collections.Specialized.StringCollection();
recentlyOpenToolStripMenuItem.DropDownItems.Clear();
for (int i = 0; i < Settings.Default.RecentFiles.Count && i < 5; i++)
{
string filepath = Settings.Default.RecentFiles[i];
if (!string.IsNullOrWhiteSpace(filepath) && File.Exists(filepath))
{
string displayFilepath = Regex.Replace(filepath, @"([A-Z]{1}\:\\[Uu]sers\\)([^\\]*\\)(.*)", "~\\$3");
ToolStripItem item = recentlyOpenToolStripMenuItem.DropDownItems.Add(displayFilepath, null, HandleOpenFile);
item.Tag = filepath;
}
}
if (recentlyOpenToolStripMenuItem.DropDownItems.Count > 0)
{
void clearRecentFileList(object sender, EventArgs e)
{
Settings.Default.RecentFiles.Clear();
LoadRecentFileList();
}
recentlyOpenToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator());
recentlyOpenToolStripMenuItem.DropDownItems.Add("Clear", null, clearRecentFileList);
}
}
private void HandleOpenFile(object sender, EventArgs e)
{
if (sender is ToolStripItem menuItem && menuItem.Tag is string filepath)
{
if (!File.Exists(filepath))
{
menuItem.Available = false;
return;
}
LoadPckFromFile(filepath);
}
}
private void SaveToRecentFiles(string filepath)
{
if (filepath is null || string.IsNullOrWhiteSpace(filepath))
return;
if (Settings.Default.RecentFiles.Contains(filepath))
Settings.Default.RecentFiles.Remove(filepath);
Settings.Default.RecentFiles.Insert(0, filepath);
for (int i = Settings.Default.RecentFiles.Count - 1; i >= 5; i--)
{
Settings.Default.RecentFiles.RemoveAt(i);
}
Settings.Default.Save();
LoadRecentFileList();
}
private void openPckManagerToolStripMenuItem_Click(object sender, EventArgs e)
{
PckManager ??= new PckManager();
PckManager.FormClosing += (s, e) => PckManager = null;
if (!PckManager.Visible)
{
// Passing in a parent form will make it stay on top of every other form. -miku
PckManager.Show();
}
if (PckManager.Focus())
PckManager.BringToFront();
}
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(this) == DialogResult.OK)
{
LoadPckFromFile(ofd.FileName);
}
}
private void tabControl_PageClosing(object sender, PageClosingEventArgs e)
{
if (TryGetEditor(e.Page, out IEditor<RawAssetDLCPackage> editor))
{
editor.Close();
_dlcManager.CloseDLCPackage(editor.EditorValue.Identifier);
RemoveOpenFile();
}
}
private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
{
closeToolStripMenuItem.Visible = tabControl.SelectedIndex > 0;
closeAllToolStripMenuItem.Visible = tabControl.SelectedIndex == 0 && tabControl.TabCount > 1;
saveToolStripMenuItem.Visible = tabControl.SelectedIndex > 0;
saveAsToolStripMenuItem.Visible = tabControl.SelectedIndex > 0;
if (tabControl.SelectedIndex == 0)
{
RPC.SetPresence("An Open Source .PCK File Editor");
}
}
private static PckFile InitializePack(int packId, int packVersion, string packName)
{
var pack = new PckFile(3);
PckAsset zeroAsset = pack.CreateNewAsset("0", PckAssetType.InfoFile);
zeroAsset.AddProperty("PACKID", packId);
zeroAsset.AddProperty("PACKVERSION", packVersion);
var locFile = new LOCFile();
locFile.InitializeDefault(packName);
pack.CreateNewAsset("localisation.loc", PckAssetType.LocalisationFile, new LOCFileWriter(locFile, 2));
return pack;
}
private static PckFile InitializeTexturePack(int packId, int packVersion, string packName, string res)
{
PckFile pack = InitializePack(packId, packVersion, packName);
PckFile infoPCK = new PckFile(3);
pack.GetAsset("localisation.loc", PckAssetType.LocalisationFile).Filename = "languages.loc";
PckAsset iconAsset = infoPCK.CreateNewAsset("icon.png", PckAssetType.TextureFile);
iconAsset.SetTexture(Core.Properties.Resources.TexturePackIcon);
PckAsset comparisonAsset = infoPCK.CreateNewAsset("comparison.png", PckAssetType.TextureFile);
comparisonAsset.SetTexture(Core.Properties.Resources.Comparison);
PckAsset texturepackInfoAsset = pack.CreateNewAsset($"{res}/{res}Info.pck", PckAssetType.TexturePackInfoFile);
texturepackInfoAsset.AddProperty("PACKID", "0");
texturepackInfoAsset.AddProperty("DATAPATH", $"{res}Data.pck");
texturepackInfoAsset.SetData(new PckFileWriter(infoPCK, OMI.ByteOrder.BigEndian));
return pack;
}
private static PckFile InitializeMashUpPack(int packId, int packVersion, string packName, string res)
{
PckFile pack = InitializeTexturePack(packId, packVersion, packName, res);
PckAsset gameRuleAsset = pack.CreateNewAsset("GameRules.grf", PckAssetType.GameRulesFile);
GameRuleFile grfFile = new GameRuleFile();
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")
);
gameRuleAsset.SetData(new GameRuleFileWriter(grfFile));
return pack;
}
private void skinPackToolStripMenuItem_Click(object sender, EventArgs e)
{
TextPrompt namePrompt = new TextPrompt();
namePrompt.OKButtonText = "Ok";
if (namePrompt.ShowDialog(this) == DialogResult.OK)
{
PckFile skinPck = InitializePack(new Random().Next(8000, GameConstants.MAX_PACK_ID), 0, namePrompt.NewText);
RawAssetDLCPackage newRawAssetDLCPackage = new RawAssetDLCPackage("New pack", skinPck, OMI.ByteOrder.BigEndian);
AddEditorPage(newRawAssetDLCPackage.Name, "Unsaved texture pack", newRawAssetDLCPackage);
}
}
private void texturePackToolStripMenuItem_Click(object sender, EventArgs e)
{
var packPrompt = new CreateTexturePackPrompt();
if (packPrompt.ShowDialog() == DialogResult.OK)
{
PckFile texturePackPck = InitializeTexturePack(new Random().Next(8000, GameConstants.MAX_PACK_ID), 0, packPrompt.PackName, packPrompt.PackRes);
RawAssetDLCPackage newRawAssetDLCPackage = new RawAssetDLCPackage("New pack", texturePackPck, OMI.ByteOrder.BigEndian);
AddEditorPage(newRawAssetDLCPackage.Name, "Unsaved texture pack", newRawAssetDLCPackage);
}
}
private void mashUpPackToolStripMenuItem_Click(object sender, EventArgs e)
{
var packPrompt = new CreateTexturePackPrompt();
if (packPrompt.ShowDialog() == DialogResult.OK)
{
PckFile mashUpPck = InitializeMashUpPack(new Random().Next(8000, GameConstants.MAX_PACK_ID), 0, packPrompt.PackName, packPrompt.PackRes);
RawAssetDLCPackage newRawAssetDLCPackage = new RawAssetDLCPackage("New pack", mashUpPck, OMI.ByteOrder.BigEndian);
AddEditorPage(newRawAssetDLCPackage.Name, "Unsaved texture pack", newRawAssetDLCPackage);
}
}
private void quickChangeToolStripMenuItem_Click(object sender, EventArgs e)
{
if (TryGetCurrentEditor(out IEditor<RawAssetDLCPackage> editor))
{
using AdvancedOptions advanced = new AdvancedOptions(editor.EditorValue.PckFile);
advanced.IsLittleEndian = editor.EditorValue.ByteOrder == OMI.ByteOrder.LittleEndian;
if (advanced.ShowDialog() == DialogResult.OK)
{
editor.UpdateView();
}
}
}
private void closeToolStripMenuItem_Click(object sender, EventArgs e)
{
CloseTab(tabControl.TabPages, tabControl.SelectedTab);
}
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
using ContributorsForm info = new ContributorsForm();
info.ShowDialog(this);
}
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 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 forMattNLContributorToolStripMenuItem_Click(object sender, EventArgs e) => Process.Start("https://ko-fi.com/mattnl");
private void joinDevelopmentDiscordToolStripMenuItem_Click(object sender, EventArgs e) => Process.Start("https://discord.gg/aJtZNFVQTv");
private void trelloBoardToolStripMenuItem_Click(object sender, EventArgs e) => Process.Start("https://trello.com/b/0XLNOEbe/pck-studio");
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 OpenPck_DragEnter(object sender, DragEventArgs e)
{
pckOpen.Image = Resources.pckDrop;
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop) ?? Array.Empty<string>();
e.Effect = files.Any(file => Path.GetExtension(file).Equals(".pck", StringComparison.CurrentCultureIgnoreCase))
? DragDropEffects.Copy
: DragDropEffects.None;
}
private void OpenPck_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetData(DataFormats.FileDrop, false) is string[] filepaths)
LoadPckFromFile(filepaths);
}
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 saveToolStripMenuItem_Click(object sender, EventArgs e)
{
if (TryGetCurrentEditor(out IEditor<RawAssetDLCPackage> editor))
{
editor.Save();
}
}
private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
{
if (TryGetCurrentEditor(out IEditor<RawAssetDLCPackage> editor))
{
editor.SaveAs();
}
}
private void wavBinkaToolStripMenuItem_Click(object sender, EventArgs e)
{
using OpenFileDialog fileDialog = new OpenFileDialog
{
Multiselect = true,
Filter = "WAV files (*.wav)|*.wav",
Title = "Please choose WAV files to convert to BINKA"
};
if (fileDialog.ShowDialog(this) == DialogResult.OK)
{
using ItemSelectionPopUp dialog = new ItemSelectionPopUp(
"Level 1 (Best Quality)", "Level 2", "Level 3", "Level 4", "Level 5",
"Level 6", "Level 7", "Level 8", "Level 9 (Worst Quality)")
{
LabelText = "Compression",
ButtonText = "OK"
};
if (dialog.ShowDialog(this) == DialogResult.OK)
{
BinkaConverter.ToBinka(
fileDialog.FileNames,
new DirectoryInfo(Path.GetDirectoryName(fileDialog.FileName)),
dialog.SelectedIndex + 1 // compression level
);
}
}
}
private void binkaWavToolStripMenuItem_Click(object sender, EventArgs e)
{
using OpenFileDialog fileDialog = new OpenFileDialog
{
Multiselect = true,
Filter = "BINKA files (*.binka)|*.binka",
Title = "Please choose BINKA files to convert to WAV"
};
if (fileDialog.ShowDialog(this) == DialogResult.OK)
{
BinkaConverter.ToWav(fileDialog.FileNames, new DirectoryInfo(Path.GetDirectoryName(fileDialog.FileName)));
}
}
private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
{
var appSettings = new AppSettingsForm();
appSettings.ShowDialog(this);
}
private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
{
Updater.UpdateToLatest();
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
PckManager?.Close();
Application.Exit();
}
private void packSettingsToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog fileDialog = new OpenFileDialog()
{
Filter = "Minecraft texturepack|*.zip"
};
if (fileDialog.ShowDialog() != DialogResult.OK)
return;
new JavaTextFormatForm(new FileInfo(fileDialog.FileName)).ShowDialog();
}
}
}