Files
PCK-Studio/PCK-Studio/Forms/Features/CemuPanel.cs

364 lines
14 KiB
C#

/* 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.
**/
using System;
using System.IO;
using System.Xml;
using System.Linq;
using System.Diagnostics;
using System.Windows.Forms;
using PckStudio.Core.Misc;
using PckStudio.Properties;
using PckStudio.Core.Extensions;
namespace PckStudio.Forms.Features
{
/// <summary>
/// Wishlist:
/// - add the ability to save the currently open pck file to the desired folder destination.
/// (even if the pck file has not yet be saved to disk)
/// </summary>
public partial class CemuPanel : UserControl
{
private const string TitleId_EUR = "101d7500";
private const string TitleId_USA = "101d9d00";
private const string TitleId_JPN = "101dbe00";
public CemuPanel()
{
InitializeComponent();
var imgList = new ImageList();
imgList.ColorDepth = ColorDepth.Depth32Bit;
imgList.ImageSize = new System.Drawing.Size(28, 28);
imgList.Images.Add(Resources.SKIN_ICON);
imgList.Images.Add(Resources.TEXTURE_ICON);
DLCTreeView.ImageList = imgList;
if (!TryApplyPermanentCemuConfig() &&
MessageBox.Show(this, "Failed to get Cemu perma settings\nDo you want to open your local settings.xml file?",
"Cemu mlc path not found",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning) == DialogResult.Yes
)
{
OpenFileDialog fileDialog = new OpenFileDialog()
{
Filter = "Cemu Settings|settings.xml",
};
if (fileDialog.ShowDialog(this) == DialogResult.OK)
{
TryApplyCemuConfig(fileDialog.FileName);
}
}
}
private bool TryApplyCemuConfig(string settingsPath)
{
string cemuPath = Path.Combine(Path.GetDirectoryName(settingsPath), "Cemu.exe");
if (File.Exists(settingsPath) && File.Exists(cemuPath))
{
try
{
var xml = new XmlDocument();
xml.Load(settingsPath);
GameDirectoryTextBox.Text = xml.SelectSingleNode("content").SelectSingleNode("mlc_path").InnerText;
GameDirectoryTextBox.Enabled = false;
BrowseDirectoryBtn.Enabled = false;
}
catch (Exception ex)
{
Debug.WriteLine(ex, category: $"{nameof(CemuPanel)}:{nameof(TryApplyCemuConfig)}");
return false;
}
}
return false;
}
private bool TryApplyPermanentCemuConfig()
{
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Cemu");
string filepath = Path.Combine(path, "perm_setting.xml");
if (Directory.Exists(path) && File.Exists(filepath))
{
try
{
var xml = new XmlDocument();
using (var reader = new StreamReader(filepath))
{
reader.ReadLine();
xml.Load(reader);
}
XmlNode configNode = xml.SelectSingleNode("config");
XmlNode mlcpathNode = configNode.SelectSingleNode("MlcPath");
GameDirectoryTextBox.Text = mlcpathNode.InnerText;
GameDirectoryTextBox.Enabled = false;
BrowseDirectoryBtn.Enabled = false;
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex, category: $"{nameof(CemuPanel)}:{nameof(TryApplyPermanentCemuConfig)}");
return false;
}
}
return false;
}
private string GetSelectedRegionTitleId()
{
if (radioButtonEur.Checked)
{
return TitleId_EUR;
}
if (radioButtonUs.Checked)
{
return TitleId_USA;
}
if (radioButtonJap.Checked)
{
return TitleId_JPN;
}
throw new Exception("how did you get here ?");
}
private string GetGameContentPath(string region)
{
return $"{GameDirectoryTextBox.Text}/usr/title/0005000e/{region}/content";
}
private string GetGameContentPath()
{
string region = GetSelectedRegionTitleId();
return GetGameContentPath(region);
}
private string GetContentSubDirectory(params string[] subpaths)
{
return Path.Combine(GetGameContentPath(), Path.Combine(subpaths));
}
private void BrowseDirectoryBtn_Click(object sender, EventArgs e)
{
OpenFolderDialog openFolderDialog = new OpenFolderDialog
{
Title = "Select Cemu mlc01 Directory"
};
if (openFolderDialog.ShowDialog(Handle) == true && Directory.Exists(openFolderDialog.ResultPath))
{
GameDirectoryTextBox.Text = openFolderDialog.ResultPath;
}
}
private class DLCDirectoryInfo
{
private readonly bool _hasTexturePack;
private readonly string _basePckPath;
private readonly string _texturePackPath;
public bool HasTexturePack => _hasTexturePack;
public string PackPath => _basePckPath;
public string TexturePackPath => _texturePackPath;
public DLCDirectoryInfo(DirectoryInfo directory)
{
_basePckPath = directory.GetFiles().FirstOrDefault(f => f.Name.EndsWith(".pck")).FullName;
_ = _basePckPath ?? throw new NullReferenceException($"Could not find any '.pck' inside {directory.Name}");
if (TryGetDataDirectory(directory, out DirectoryInfo dataDir))
{
FileInfo tpFileInfo = dataDir.GetFiles().FirstOrDefault(f => !f.Name.Equals("audio.pck") && f.Name.EndsWith(".pck"));
_hasTexturePack = tpFileInfo is not null;
_texturePackPath = _hasTexturePack ? tpFileInfo.FullName : string.Empty;
}
}
public DLCDirectoryInfo(string path)
: this(new DirectoryInfo(path))
{
}
private bool TryGetDataDirectory(DirectoryInfo directory, out DirectoryInfo dataDirectory)
{
DirectoryInfo[] dirs = directory.GetDirectories("Data", SearchOption.TopDirectoryOnly);
dataDirectory = dirs.Length != 0 ? dirs[0] : null;
return dirs.Length != 0;
}
}
private void ListDLCs()
{
DLCTreeView.Nodes.Clear();
if (!IsValidInstallDirectory())
{
MessageBox.Show(this, "Please select a valid Game Directory!", "Invalid Directory Specified",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!IsValidGameDirectory())
{
MessageBox.Show(this, $"Could not find '{GetGameContentPath()}'!", "Not Found",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
string dirPath = GetContentSubDirectory("WiiU", "DLC");
DirectoryInfo dlcDirectory = new DirectoryInfo(dirPath);
if (!dlcDirectory.Exists)
{
MessageBox.Show(this, $"'{dirPath}' does not exist!", "Not Found",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
foreach (DirectoryInfo directoryInfo in dlcDirectory.GetDirectories())
{
if (directoryInfo.GetFileSystemInfos().Length != 0)
{
DLCDirectoryInfo dlcDirectoryInfo = new DLCDirectoryInfo(directoryInfo);
TreeNode node = DLCTreeView.Nodes.Add(directoryInfo.Name);
node.ImageIndex = dlcDirectoryInfo.HasTexturePack ? 1 : 0;
node.Tag = dlcDirectoryInfo;
}
}
}
private bool IsValidInstallDirectory()
{
return !string.IsNullOrWhiteSpace(GameDirectoryTextBox.Text) && Directory.Exists(GameDirectoryTextBox.Text);
}
private void radioButton_Click(object sender, EventArgs e)
{
ListDLCs();
}
private bool IsValidGameDirectory()
{
return IsValidInstallDirectory() && Directory.Exists(GetGameContentPath());
}
private void openSkinPackToolStripMenuItem_Click(object sender, EventArgs e)
{
if (DLCTreeView.SelectedNode?.Tag is DLCDirectoryInfo dlcDir)
{
Program.MainInstance.LoadPckFromFile(dlcDir.PackPath);
}
}
private void openTexturePackToolStripMenuItem_Click(object sender, EventArgs e)
{
if (DLCTreeView.SelectedNode?.Tag is DLCDirectoryInfo dlcDir && dlcDir.HasTexturePack)
{
Program.MainInstance.LoadPckFromFile(dlcDir.TexturePackPath);
}
}
private void DLCTreeView_AfterSelect(object sender, TreeViewEventArgs e)
{
openTexturePackToolStripMenuItem.Visible = e.Node.Tag is DLCDirectoryInfo dlcDir && dlcDir.HasTexturePack;
}
private void addCustomPckToolStripMenuItem_Click(object sender, EventArgs e)
{
TextPrompt prompt = new TextPrompt();
prompt.OKButtonText = "OK";
prompt.LabelText = "Folder:";
if (prompt.ShowDialog(this) != DialogResult.OK)
return;
if (prompt.NewText.ContainsAny(Path.GetInvalidPathChars()))
{
MessageBox.Show(this, "Invalid Folder name entered!", "Invalid Folder Name", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
string directoryPath = GetContentSubDirectory("WiiU", "DLC", prompt.NewText);
if (Directory.Exists(directoryPath))
{
MessageBox.Show(this, "A Folder with the same name already exists!", "Folder Name taken", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
Directory.CreateDirectory(directoryPath);
using OpenFileDialog fileDialog = new OpenFileDialog
{
Filter = "PCK (Minecraft Console Package)|*.pck"
};
if (fileDialog.ShowDialog(this) == DialogResult.OK)
{
File.Copy(fileDialog.FileName, Path.Combine(directoryPath, fileDialog.SafeFileName));
}
}
private void removePckToolStripMenuItem_Click(object sender, EventArgs e)
{
string pckName = DLCTreeView.SelectedNode.Text;
DialogResult result = MessageBox.Show(this, $"Are you sure you want to permanently delete '{pckName}'?", "Hold up!", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
string directoryPath = GetContentSubDirectory("WiiU", "DLC", pckName);
Directory.Delete(directoryPath, recursive: true);
DLCTreeView.SelectedNode.Remove();
}
}
private void DLCTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
openSkinPackToolStripMenuItem_Click(sender, e);
}
private void GameDirectoryTextBox_TextChanged(object sender, EventArgs e)
{
if (IsValidInstallDirectory())
{
radioButtonEur.Enabled = Directory.Exists(GetGameContentPath(TitleId_EUR));
radioButtonUs.Enabled = Directory.Exists(GetGameContentPath(TitleId_USA));
radioButtonJap.Enabled = Directory.Exists(GetGameContentPath(TitleId_JPN));
}
ListDLCs();
}
private void DLCTreeView_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Enter && DLCTreeView.SelectedNode is not null)
{
openSkinPackToolStripMenuItem_Click(sender, EventArgs.Empty);
}
base.OnPreviewKeyDown(e);
}
private void DLCTreeView_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = true;
}
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start(GetContentSubDirectory("WiiU", "DLC"));
}
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
{
Clipboard.SetText(GameDirectoryTextBox.Text);
}
}
}