UltimateZoneBuilder/Source/Core/Windows/ResourceOptionsForm.cs
2023-06-09 11:16:21 +02:00

540 lines
No EOL
15 KiB
C#
Executable file
Raw Blame History

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Windows.Forms;
using System.IO;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Config;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using CodeImp.DoomBuilder.ZDoom;
using System.Threading;
#endregion
namespace CodeImp.DoomBuilder.Windows
{
internal partial class ResourceOptionsForm : DelayedForm
{
// Variables
private DataLocation res;
private string startPath;
private Controls.FolderSelectDialog dirdialog;
private List<string> requiredarchives;
// Properties
public DataLocation ResourceLocation { get { return res; } }
public GameConfiguration GameConfiguration { get; set; }
//
private bool _ischeckingrequiredarchives = false;
private bool IsCheckingRequiredArchives
{
get
{
return _ischeckingrequiredarchives;
}
set
{
if (value)
{
apply.Enabled = false;
cancel.Enabled = false;
notfortesting.Enabled = false;
dir_textures.Enabled = false;
dir_flats.Enabled = false;
ControlBox = false;
checkingloader.Visible = true;
}
else
{
apply.Enabled = true;
cancel.Enabled = true;
notfortesting.Enabled = true;
dir_textures.Enabled = true;
dir_flats.Enabled = true;
ControlBox = true;
checkingloader.Visible = false;
}
_ischeckingrequiredarchives = value;
}
}
// Constructor
public ResourceOptionsForm(DataLocation settings, string caption, string startPath) //mxd. added startPath
{
// Initialize
InitializeComponent();
#if NO_WIN32
// No easy way to have case-insesitivity for non-Windows platforms
wadfiledialog.Filter = "Doom WAD Files (*.wad)|*.wad;*.Wad;*.wAd;*.WAd;*.waD;*.WaD;*.wAD;*.WAD";
pk3filedialog.Filter = "Doom PK3/PKE/PK7 Files (*.pk3;*.pke;*.pk7;*.ipk3;*.ipk7)|" +
"*.pk3;*.Pk3;*.pK3;*.PK3;" +
"*.pke;*.Pke;*.pKe;*.PKe;*.pkE;*.PkE;*.pKE;*.PKE;" +
"*.pk7;*.Pk7;*.pK7;*.PK7;" +
"*.ipk3;*.iPk3;*.ipK3;*.iPK3;*.Ipk3;*.IPk3;*.IpK3;*.IPK3;" +
"*.ipk7;*.iPk7;*.ipK7;*.iPK7;*.Ipk7;*.IPk7;*.IpK7;*.IPK7";
#endif
// Set caption
this.Text = caption;
//
this.requiredarchives = new List<string>();
// Apply settings from ResourceLocation
this.res = settings;
switch(res.type)
{
// Setup for WAD File
case DataLocation.RESOURCE_WAD:
wadlocation.Text = res.location;
strictpatches.Checked = res.option1;
break;
// Setup for Directory
case DataLocation.RESOURCE_DIRECTORY:
dirlocation.Text = res.location;
dir_textures.Checked = res.option1;
dir_flats.Checked = res.option2;
break;
// Setup for PK3 File
case DataLocation.RESOURCE_PK3:
pk3location.Text = res.location;
break;
}
// Select appropriate tab
tabs.SelectedIndex = res.type;
// Checkbox
notfortesting.Checked = res.notfortesting;
this.startPath = startPath;
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
if (IsCheckingRequiredArchives) e.Cancel = true;
}
public static List<string> CheckRequiredArchives(GameConfiguration config, DataLocation loc, CancellationToken token)
{
#if DEBUG
General.WriteLogLine(string.Format("CheckRequiredArchives (Config = {0})", config?.Name));
#endif
if (config == null)
return new List<string>();
try
{
DataReader dr = null;
List<string> requiredarchives = new List<string>();
HashSet<string> classes = null;
switch (loc.type)
{
case DataLocation.RESOURCE_WAD:
dr = new WADReader(loc, config, true) { Silent = true };
break;
case DataLocation.RESOURCE_DIRECTORY:
dr = new DirectoryReader(loc, config, true) { Silent = true };
break;
case DataLocation.RESOURCE_PK3:
dr = new PK3Reader(loc, config, true) { Silent = true };
break;
}
token.ThrowIfCancellationRequested();
foreach (var arc in config.RequiredArchives)
{
bool found = true;
token.ThrowIfCancellationRequested();
foreach (RequiredArchiveEntry e in arc.Entries)
{
token.ThrowIfCancellationRequested();
if (e.Class != null)
{
if (classes == null)
{
classes = new HashSet<string>();
// load ZScript
var zscript = new ZScriptParser {
NoWarnings = true,
OnInclude = (parser, location) => {
IEnumerable<TextResourceData> includeStreams = dr.GetZScriptData(location);
foreach (TextResourceData data in includeStreams)
{
// Parse this data
parser.Parse(data, false);
//mxd. DECORATE lumps are interdepandable. Can't carry on...
if (parser.HasError)
break;
}
}
};
foreach (TextResourceData data in dr.GetZScriptData("ZSCRIPT"))
{
// Parse the data
data.Stream.Seek(0, SeekOrigin.Begin);
zscript.Parse(data, true);
if (zscript.HasError)
break;
foreach (string cls in zscript.LastClasses)
classes.Add(cls.ToLowerInvariant());
}
#if DEBUG
if (zscript.HasError)
General.WriteLogLine(string.Format("CRA({0}): ZScript error: {1}", loc.location, zscript.ErrorDescription));
#endif
// load DECORATE
var decorate = new DecorateParser(zscript.AllActorsByClass) {
NoWarnings = true,
OnInclude = (parser, location) => {
IEnumerable<TextResourceData> includeStreams = dr.GetDecorateData(location);
foreach (TextResourceData data in includeStreams)
{
// Parse this data
parser.Parse(data, false);
//mxd. DECORATE lumps are interdepandable. Can't carry on...
if (parser.HasError)
break;
}
}
};
foreach (TextResourceData data in dr.GetDecorateData("DECORATE"))
{
// Parse the data
data.Stream.Seek(0, SeekOrigin.Begin);
decorate.Parse(data, true);
if (decorate.HasError)
break;
foreach (string cls in decorate.LastClasses)
classes.Add(cls.ToLowerInvariant());
}
#if DEBUG
if (decorate.HasError)
General.WriteLogLine(string.Format("CRA({0}): DECORATE error: {1}", loc.location, decorate.ErrorDescription));
#endif
}
if (!classes.Contains(e.Class.ToLowerInvariant()))
{
#if DEBUG
General.WriteLogLine(string.Format("CRA({2}): Does not contain class: {1} <- {0}", string.Join(",", classes), e.Class, loc.location));
#endif
found = false;
break;
}
}
if (e.Lump != null && !dr.FileExists(e.Lump))
{
#if DEBUG
General.WriteLogLine(string.Format("CRA({1}): Does not contain lump: {0}", e.Lump, loc.location));
#endif
found = false;
break;
}
}
if (found)
{
requiredarchives.Add(arc.ID);
}
}
dr.Dispose();
return requiredarchives;
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception e)
{
General.WriteLogLine(e.ToString());
return new List<string>();
}
}
private List<string> RunCheckRequiredArchives()
{
// thanks ms for making this a struct
CancellationTokenSource dummySource = new CancellationTokenSource();
List<string> output = CheckRequiredArchives(GameConfiguration, ToDataLocation(), dummySource.Token);
dummySource.Dispose();
return output;
}
private async void StartRequiredArchivesCheck()
{
IsCheckingRequiredArchives = true;
try
{
requiredarchives = await Task.Run(() => RunCheckRequiredArchives());
}
catch
{
requiredarchives = new List<string>();
}
ApplyDefaultRequiredArchivesSetting();
IsCheckingRequiredArchives = false;
}
private void ApplyDefaultRequiredArchivesSetting()
{
dir_textures.Checked = false;
dir_flats.Checked = false;
notfortesting.Checked = false;
// if any of the detected required archives implies "not for testing" <20> disable it by default
foreach (var arc in GameConfiguration.RequiredArchives)
{
if (requiredarchives.Contains(arc.ID) && arc.ExcludeFromTesting)
notfortesting.Checked = true;
}
}
private DataLocation ToDataLocation()
{
DataLocation res = new DataLocation();
res.location = "";
res.requiredarchives = requiredarchives;
// Apply settings to ResourceLocation
switch (tabs.SelectedIndex)
{
// Setup WAD File
case DataLocation.RESOURCE_WAD:
// Check if file is specified
if ((wadlocation.Text.Length == 0) ||
(!File.Exists(wadlocation.Text)))
{
break;
}
else
{
// Apply settings
res.type = DataLocation.RESOURCE_WAD;
res.location = wadlocation.Text;
res.option1 = strictpatches.Checked;
res.option2 = false;
res.notfortesting = notfortesting.Checked;
}
break;
// Setup Directory
case DataLocation.RESOURCE_DIRECTORY:
// Check if directory is specified
if ((dirlocation.Text.Length == 0) ||
(!Directory.Exists(dirlocation.Text)))
{
break;
}
else
{
// Apply settings
res.type = DataLocation.RESOURCE_DIRECTORY;
res.location = dirlocation.Text;
res.option1 = dir_textures.Checked;
res.option2 = dir_flats.Checked;
res.notfortesting = notfortesting.Checked;
}
break;
// Setup PK3 File
case DataLocation.RESOURCE_PK3:
// Check if file is specified
if ((pk3location.Text.Length == 0) ||
(!File.Exists(pk3location.Text)))
{
break;
}
else
{
// Apply settings
res.type = DataLocation.RESOURCE_PK3;
res.location = pk3location.Text;
res.option1 = false;
res.option2 = false;
res.notfortesting = notfortesting.Checked;
}
break;
}
return res;
}
// OK clicked
private void apply_Click(object sender, EventArgs e)
{
res = ToDataLocation();
if (res.location == "")
{
switch (tabs.SelectedIndex)
{
case DataLocation.RESOURCE_WAD:
MessageBox.Show(this, "Please select a valid WAD File resource.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
break;
case DataLocation.RESOURCE_PK3:
MessageBox.Show(this, "Please select a valid PK3, PKE, or PK7 File resource.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
break;
case DataLocation.RESOURCE_DIRECTORY:
MessageBox.Show(this, "Please select a valid directory resource.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
break;
}
}
else
{
this.DialogResult = DialogResult.OK;
this.Close();
}
}
// Cancel clicked
private void cancel_Click(object sender, EventArgs e)
{
// Just hide
this.DialogResult = DialogResult.Cancel;
this.Close();
}
// Browse WAD File clicked
private void browsewad_Click(object sender, EventArgs e)
{
// Browse for WAD File
if(wadfiledialog.ShowDialog(this) == DialogResult.OK)
{
// Use this file
wadlocation.Text = wadfiledialog.FileName;
StartRequiredArchivesCheck();
}
}
// Browse Directory clicked
private void browsedir_Click(object sender, EventArgs e)
{
// Browse for Directory
dirdialog = new Controls.FolderSelectDialog();
dirdialog.Title = "Select Resource Folder";
if (string.IsNullOrEmpty(dirlocation.Text) || !Directory.Exists(dirlocation.Text))
{
//mxd
if (!string.IsNullOrEmpty(startPath))
{
string startDir = Path.GetDirectoryName(startPath);
if (Directory.Exists(startDir)) dirdialog.InitialDirectory = startDir + '\\';
}
}
else
{
dirdialog.InitialDirectory = dirlocation.Text;
}
if (dirdialog.ShowDialog(this.Handle))
{
// Use this directory
dirlocation.Text = dirdialog.FileName;
StartRequiredArchivesCheck();
dirdialog = null;
}
}
// Browse PK3 File clicked
private void browsepk3_Click(object sender, EventArgs e)
{
// Browse for PK3 File
if(pk3filedialog.ShowDialog(this) == DialogResult.OK)
{
// Use this file
pk3location.Text = pk3filedialog.FileName;
StartRequiredArchivesCheck();
}
}
// Link clicked
private void pk3_link_Click(object sender, LinkLabelLinkClickedEventArgs e)
{
General.OpenWebsite("http://www.zdoom.org/wiki/Using_ZIPs_as_WAD_replacement");
}
private void pke_link_Click(object sender, LinkLabelLinkClickedEventArgs e)
{
General.OpenWebsite("https://eternity.youfailit.net/wiki/ZIP");
}
// Help
private void ResourceOptionsForm_HelpRequested(object sender, HelpEventArgs hlpevent)
{
General.ShowHelp("w_resourceoptions.html");
hlpevent.Handled = true;
}
private void wadlocation_Enter(object sender, EventArgs e)
{
// Stop textbox control from getting focus
browsewad.Focus();
}
private void dirlocation_Enter(object sender, EventArgs e)
{
// Stop textbox control from getting focus
browsedir.Focus();
}
private void pk3location_Enter(object sender, EventArgs e)
{
// Stop textbox control from getting focus
browsepk3.Focus();
}
}
}