#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 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(); // 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 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(); try { DataReader dr = null; List requiredarchives = new List(); HashSet 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(); // load ZScript var zscript = new ZScriptParser { NoWarnings = true, OnInclude = (parser, location) => { IEnumerable 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 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(); } } private List RunCheckRequiredArchives() { // thanks ms for making this a struct CancellationTokenSource dummySource = new CancellationTokenSource(); List 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(); } 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" � 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(); } } }